Compare commits
64 Commits
alpha-0.0.
...
alpha-0.1.
| Author | SHA1 | Date | |
|---|---|---|---|
| f70661694b | |||
| 36a1ab0572 | |||
| 409268b604 | |||
| 174d144d26 | |||
| 360258e4cf | |||
| 2148c0050c | |||
| 61166023df | |||
| e7bf22cea6 | |||
| f09f79198d | |||
| 8c19d3cc3c | |||
| 208892d266 | |||
| 4384806cf0 | |||
| 1a091baeaf | |||
| 43f21ffd44 | |||
| 24617c539f | |||
| 4611a198c9 | |||
| d40ffe8f6c | |||
| 384b7ad51e | |||
| fd9b448fa4 | |||
| c4a2b08416 | |||
| a241d7691b | |||
| 0af4cd506c | |||
| 6b5c56b37d | |||
| 1474220a77 | |||
| a802b5cef5 | |||
| 7e91b863da | |||
| 129bb47286 | |||
| e4a9c5f763 | |||
| 78cf2d0f18 | |||
| 00d2b5394f | |||
| 00414abe61 | |||
| cb847d0edd | |||
| 2da0bd9b79 | |||
| cf9633c061 | |||
| 7a6fdc30b6 | |||
| 657fe761ab | |||
| 687944bf51 | |||
| 93e079ddb3 | |||
| 8171a91ed5 | |||
| 0c1824e40b | |||
| f91ab4bc83 | |||
| 1843fc6f4d | |||
| 6a51638b7e | |||
| 8351c5d09f | |||
| ee94c811e1 | |||
| 50a8269773 | |||
| 2996c88d94 | |||
| 55263fa582 | |||
| adeddee09d | |||
| f0115166c0 | |||
| c1affc36c0 | |||
| 6c4ef25abd | |||
| fd0a5e5848 | |||
| 7fb357e9c8 | |||
| 1d7a8cc66b | |||
| 070749e685 | |||
| a716e46c64 | |||
| 2e2aeb44b9 | |||
| 2df76248be | |||
| 36f1430ff2 | |||
| 2e14827f13 | |||
| c7d81de60e | |||
| f48af51dc7 | |||
| 0f328e6f81 |
@@ -12,14 +12,28 @@ enum class GameState : std::uint8_t {
|
|||||||
Game,
|
Game,
|
||||||
EndGame,
|
EndGame,
|
||||||
Disconnected,
|
Disconnected,
|
||||||
|
Closed
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::map<std::uint8_t, Player> PlayerList;
|
typedef std::map<std::uint8_t, Player> PlayerList;
|
||||||
|
|
||||||
class Game {
|
class GameListener {
|
||||||
|
public:
|
||||||
|
virtual void OnPlayerJoin(PlayerID player) {}
|
||||||
|
virtual void OnPlayerLeave(PlayerID player) {}
|
||||||
|
|
||||||
|
virtual void OnGameStateUpdate(GameState newState) {}
|
||||||
|
virtual void OnGameBegin() {}
|
||||||
|
virtual void OnGameEnd() {}
|
||||||
|
virtual void OnGameClose() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef utils::ObjectNotifier<GameListener> GameNotifier;
|
||||||
|
|
||||||
|
class Game : public GameNotifier {
|
||||||
protected:
|
protected:
|
||||||
World* m_World;
|
World* m_World;
|
||||||
std::array<Team, 2> m_Teams = { Team{TeamColor::Red}, Team{TeamColor::Blue} };
|
TeamList m_Teams = { Team{TeamColor::Red}, Team{TeamColor::Blue} };
|
||||||
GameState m_GameState = GameState::Lobby;
|
GameState m_GameState = GameState::Lobby;
|
||||||
PlayerList m_Players;
|
PlayerList m_Players;
|
||||||
public:
|
public:
|
||||||
@@ -46,8 +60,10 @@ 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);
|
||||||
|
|
||||||
|
const TeamList& getTeams() const { return m_Teams; }
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,13 +2,20 @@
|
|||||||
|
|
||||||
#include "Towers.h"
|
#include "Towers.h"
|
||||||
#include "Types.h"
|
#include "Types.h"
|
||||||
|
#include "Team.h"
|
||||||
|
|
||||||
|
#include "misc/ObjectNotifier.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
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,
|
||||||
@@ -41,37 +48,39 @@ class MobStats {
|
|||||||
private:
|
private:
|
||||||
float m_Damage;
|
float m_Damage;
|
||||||
float m_Speed;
|
float m_Speed;
|
||||||
|
glm::vec2 m_Size;
|
||||||
std::uint16_t m_MoneyCost;
|
std::uint16_t m_MoneyCost;
|
||||||
std::uint16_t m_ExpCost;
|
std::uint16_t m_ExpCost;
|
||||||
std::uint16_t m_MaxLife;
|
std::uint16_t m_MaxLife;
|
||||||
std::uint16_t m_ExpReward;
|
std::uint16_t m_ExpReward;
|
||||||
public:
|
public:
|
||||||
MobStats(float damage, float speed, std::uint16_t moneyCost,
|
MobStats(float damage, float speed, glm::vec2 size, std::uint16_t moneyCost,
|
||||||
std::uint16_t expCost, std::uint16_t expReward,
|
std::uint16_t expCost, std::uint16_t expReward,
|
||||||
std::uint16_t maxLife) : m_Damage(damage), m_Speed(speed),
|
std::uint16_t maxLife) : m_Damage(damage), m_Speed(speed),
|
||||||
m_MoneyCost(moneyCost), m_ExpCost(expCost), m_MaxLife(maxLife),
|
m_Size(size), m_MoneyCost(moneyCost), m_ExpCost(expCost),
|
||||||
m_ExpReward(expReward) {
|
m_MaxLife(maxLife), m_ExpReward(expReward) {
|
||||||
}
|
}
|
||||||
|
|
||||||
float getDamage() const { return m_Damage; }
|
float getDamage() const { return m_Damage; }
|
||||||
float getMovementSpeed() const { return m_Speed; }
|
float getMovementSpeed() const { return m_Speed; }
|
||||||
|
const glm::vec2& getSize() const { return m_Size; }
|
||||||
std::uint16_t getMoneyCost() const { return m_MoneyCost; }
|
std::uint16_t getMoneyCost() const { return m_MoneyCost; }
|
||||||
std::uint16_t getExpCost() const { return m_ExpCost; }
|
std::uint16_t getExpCost() const { return m_ExpCost; }
|
||||||
std::uint16_t getExpReward() const { return m_ExpReward; }
|
std::uint16_t getExpReward() const { return m_ExpReward; }
|
||||||
std::uint16_t getMaxLife() const { return m_MaxLife; }
|
std::uint16_t getMaxLife() const { return m_MaxLife; }
|
||||||
};
|
};
|
||||||
|
|
||||||
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);
|
||||||
const TowerImmunities& getMobTowerImmunities(MobType type, std::uint8_t level);
|
const TowerImmunities& getMobTowerImmunities(MobType type, std::uint8_t level);
|
||||||
const EffectImmunities& getMobEffectImmunities(MobType type, std::uint8_t level);
|
const EffectImmunities& getMobEffectImmunities(MobType type, std::uint8_t level);
|
||||||
|
|
||||||
class Mob {
|
class Mob : public utils::shape::Rectangle {
|
||||||
protected:
|
protected:
|
||||||
float m_Health;
|
float m_Health;
|
||||||
private:
|
private:
|
||||||
@@ -80,24 +89,27 @@ private:
|
|||||||
MobLevel m_Level;
|
MobLevel m_Level;
|
||||||
Direction m_Direction;
|
Direction m_Direction;
|
||||||
std::vector<EffectDuration> m_Effects;
|
std::vector<EffectDuration> m_Effects;
|
||||||
const Tower* m_LastDamage; // the tower that damaged the mob
|
const Tower* m_LastDamage; // the last tower that damaged the mob
|
||||||
|
|
||||||
utils::Timer m_EffectFireTimer;
|
utils::Timer m_EffectFireTimer;
|
||||||
utils::Timer m_EffectPoisonTimer;
|
utils::Timer m_EffectPoisonTimer;
|
||||||
utils::Timer m_EffectHealTimer;
|
utils::Timer m_EffectHealTimer;
|
||||||
|
|
||||||
float m_X = 0, m_Y = 0;
|
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_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 MobType getType() const = 0;
|
||||||
|
|
||||||
virtual void tick(std::uint64_t delta);
|
virtual void tick(std::uint64_t delta, World* world);
|
||||||
|
|
||||||
virtual void OnDeath(World* world){}
|
virtual bool OnDeath(World* world) { return true; }
|
||||||
|
|
||||||
const TowerImmunities& getTowerImmunities() const { return getMobTowerImmunities(getType(), m_Level); }
|
const TowerImmunities& getTowerImmunities() const { return getMobTowerImmunities(getType(), m_Level); }
|
||||||
const EffectImmunities& getEffectImmunities() const { return getMobEffectImmunities(getType(), m_Level); }
|
const EffectImmunities& getEffectImmunities() const { return getMobEffectImmunities(getType(), m_Level); }
|
||||||
@@ -108,29 +120,36 @@ 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_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((float)getStats()->getMaxLife(), m_Health + heal); }
|
void heal(float heal) { m_Health = std::min(static_cast<float>(getStats()->getMaxLife()), m_Health + heal); }
|
||||||
|
void setMobReachedCastle(TeamCastle* castle) { m_CastleTarget = castle; } // used when mob is in front of the castle
|
||||||
float getX() const { return m_X; }
|
|
||||||
void setX(float x) { m_X = x; }
|
|
||||||
|
|
||||||
float getY() const { return m_Y; }
|
|
||||||
void setY(float y) { m_Y = y; }
|
|
||||||
|
|
||||||
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 getTileY() { return getCenterY() - static_cast<float>(static_cast<std::int32_t>(getCenterY())); } // returns a float between 0 and 1 excluded
|
||||||
|
|
||||||
Direction getDirection() const { return m_Direction; }
|
Direction getDirection() const { return m_Direction; }
|
||||||
void setDirection(Direction dir) { m_Direction = dir; }
|
void setDirection(Direction dir) { m_Direction = dir; }
|
||||||
protected:
|
protected:
|
||||||
void initHealth() { m_Health = (float)getStats()->getMaxLife(); }
|
void initMob() {
|
||||||
|
m_Health = static_cast<float>(getStats()->getMaxLife());
|
||||||
|
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);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -138,70 +157,70 @@ typedef std::shared_ptr<Mob> MobPtr;
|
|||||||
|
|
||||||
class Zombie : public Mob {
|
class Zombie : public Mob {
|
||||||
public:
|
public:
|
||||||
Zombie(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { initHealth(); }
|
Zombie(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { initMob(); }
|
||||||
|
|
||||||
virtual MobType getType() const { return MobType::Zombie; }
|
virtual MobType getType() const { return MobType::Zombie; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class Spider : public Mob {
|
class Spider : public Mob {
|
||||||
public:
|
public:
|
||||||
Spider(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { initHealth(); }
|
Spider(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { initMob(); }
|
||||||
|
|
||||||
virtual MobType getType() const { return MobType::Spider; }
|
virtual MobType getType() const { return MobType::Spider; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class Skeleton : public Mob {
|
class Skeleton : public Mob {
|
||||||
public:
|
public:
|
||||||
Skeleton(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { initHealth(); }
|
Skeleton(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { initMob(); }
|
||||||
|
|
||||||
virtual MobType getType() const { return MobType::Skeleton; }
|
virtual MobType getType() const { return MobType::Skeleton; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class PigMan : public Mob {
|
class PigMan : public Mob {
|
||||||
public:
|
public:
|
||||||
PigMan(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { initHealth(); }
|
PigMan(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { initMob(); }
|
||||||
|
|
||||||
virtual MobType getType() const { return MobType::Pigman; }
|
virtual MobType getType() const { return MobType::Pigman; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class Creeper : public Mob {
|
class Creeper : public Mob {
|
||||||
public:
|
public:
|
||||||
Creeper(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { initHealth(); }
|
Creeper(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { initMob(); }
|
||||||
|
|
||||||
virtual MobType getType() const { return MobType::Creeper; }
|
virtual MobType getType() const { return MobType::Creeper; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class Silverfish : public Mob {
|
class Silverfish : public Mob {
|
||||||
public:
|
public:
|
||||||
Silverfish(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { initHealth(); }
|
Silverfish(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { initMob(); }
|
||||||
|
|
||||||
virtual MobType getType() const { return MobType::Silverfish; }
|
virtual MobType getType() const { return MobType::Silverfish; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class Blaze : public Mob {
|
class Blaze : public Mob {
|
||||||
public:
|
public:
|
||||||
Blaze(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { initHealth(); }
|
Blaze(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { initMob(); }
|
||||||
|
|
||||||
virtual MobType getType() const { return MobType::Blaze; }
|
virtual MobType getType() const { return MobType::Blaze; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class Witch : public Mob {
|
class Witch : public Mob {
|
||||||
public:
|
public:
|
||||||
Witch(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { initHealth(); }
|
Witch(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { initMob(); }
|
||||||
|
|
||||||
virtual MobType getType() const { return MobType::Witch; }
|
virtual MobType getType() const { return MobType::Witch; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class Slime : public Mob {
|
class Slime : public Mob {
|
||||||
public:
|
public:
|
||||||
Slime(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { initHealth(); }
|
Slime(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { initMob(); }
|
||||||
|
|
||||||
virtual MobType getType() const { return MobType::Slime; }
|
virtual MobType getType() const { return MobType::Slime; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class Giant : public Mob {
|
class Giant : public Mob {
|
||||||
public:
|
public:
|
||||||
Giant(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { initHealth(); }
|
Giant(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { initMob(); }
|
||||||
|
|
||||||
virtual MobType getType() const { return MobType::Giant; }
|
virtual MobType getType() const { return MobType::Giant; }
|
||||||
};
|
};
|
||||||
@@ -211,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
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ public:
|
|||||||
std::uint8_t getGoldPerSecond() const { return m_GoldPerSecond; }
|
std::uint8_t getGoldPerSecond() const { return m_GoldPerSecond; }
|
||||||
void setGoldPerSecond(std::uint8_t goldPerSecond) { m_GoldPerSecond = goldPerSecond; }
|
void setGoldPerSecond(std::uint8_t goldPerSecond) { m_GoldPerSecond = goldPerSecond; }
|
||||||
|
|
||||||
std::uint32_t getGold() const { return m_Gold;}
|
std::uint32_t getGold() const { return m_Gold; }
|
||||||
void setGold(std::uint32_t gold) { m_GoldChanged = true; m_Gold = gold; }
|
void setGold(std::uint32_t gold) { m_GoldChanged = true; m_Gold = gold; }
|
||||||
void addGold(std::uint32_t gold) { m_GoldChanged = true; m_Gold += gold; }
|
void addGold(std::uint32_t gold) { m_GoldChanged = true; m_Gold += gold; }
|
||||||
void removeGold(std::uint32_t gold) { m_GoldChanged = true; m_Gold -= gold; }
|
void removeGold(std::uint32_t gold) { m_GoldChanged = true; m_Gold -= gold; }
|
||||||
|
|||||||
@@ -2,9 +2,11 @@
|
|||||||
|
|
||||||
#include "Types.h"
|
#include "Types.h"
|
||||||
|
|
||||||
|
#include "misc/Shapes.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <algorithm>
|
#include <cmath>
|
||||||
|
|
||||||
namespace td {
|
namespace td {
|
||||||
namespace game {
|
namespace game {
|
||||||
@@ -17,14 +19,48 @@ enum class TeamColor : std::int8_t {
|
|||||||
Blue
|
Blue
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Spawn {
|
class Spawn : public utils::shape::Rectangle {
|
||||||
Direction direction;
|
private:
|
||||||
std::int32_t x, y;
|
Direction m_Direction;
|
||||||
|
public:
|
||||||
|
Spawn() {
|
||||||
|
setWidth(5);
|
||||||
|
setHeight(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
Direction getDirection() const { return m_Direction; }
|
||||||
|
|
||||||
|
void setDirection(Direction direction) { m_Direction = direction; }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TeamCastle {
|
class Team;
|
||||||
std::int32_t x, y;
|
|
||||||
std::uint16_t life = 1000;
|
class TeamCastle : public utils::shape::Rectangle {
|
||||||
|
private:
|
||||||
|
const Team* m_Team;
|
||||||
|
float m_Life;
|
||||||
|
public:
|
||||||
|
static constexpr int CastleMaxLife = 1000;
|
||||||
|
|
||||||
|
TeamCastle(const Team* team) : m_Team(team), m_Life(CastleMaxLife) {
|
||||||
|
setWidth(5);
|
||||||
|
setHeight(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
TeamCastle() : TeamCastle(nullptr) {}
|
||||||
|
|
||||||
|
float getLife() const { return m_Life; }
|
||||||
|
|
||||||
|
const Team* getTeam() const { return m_Team; }
|
||||||
|
void setTeam(const Team* team) { m_Team = team; }
|
||||||
|
|
||||||
|
void setLife(float life) { m_Life = life; }
|
||||||
|
void damage(float damage) { m_Life = std::max(0.0f, m_Life - damage); }
|
||||||
|
|
||||||
|
void setShape(utils::shape::Rectangle rect) {
|
||||||
|
setCenter(rect.getCenter());
|
||||||
|
setSize(rect.getSize());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class Team {
|
class Team {
|
||||||
@@ -50,5 +86,7 @@ public:
|
|||||||
std::uint8_t getPlayerCount() const;
|
std::uint8_t getPlayerCount() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef std::array<Team, 2> TeamList;
|
||||||
|
|
||||||
} // namespace game
|
} // namespace game
|
||||||
} // namespace td
|
} // namespace td
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "misc/Time.h"
|
#include "misc/Time.h"
|
||||||
|
#include "misc/Shapes.h"
|
||||||
|
|
||||||
#include "game/Types.h"
|
#include "game/Types.h"
|
||||||
|
|
||||||
namespace td {
|
namespace td {
|
||||||
@@ -85,19 +87,18 @@ const TowerStats* getTowerStats(TowerType type, TowerLevel level);
|
|||||||
|
|
||||||
typedef std::uint16_t TowerID;
|
typedef std::uint16_t TowerID;
|
||||||
|
|
||||||
class Tower {
|
class Tower : public utils::shape::Circle {
|
||||||
private:
|
private:
|
||||||
TowerID m_ID;
|
TowerID m_ID;
|
||||||
TowerType m_Type;
|
TowerType m_Type;
|
||||||
std::int32_t m_X, m_Y;
|
|
||||||
TowerLevel m_Level{};
|
TowerLevel m_Level{};
|
||||||
PlayerID m_Builder;
|
PlayerID m_Builder;
|
||||||
protected:
|
protected:
|
||||||
utils::CooldownTimer m_Timer;
|
utils::CooldownTimer m_Timer;
|
||||||
public:
|
public:
|
||||||
Tower(TowerID id, TowerType type, std::int32_t x, std::int32_t y, PlayerID builder) : m_ID(id), m_Type(type), m_X(x), m_Y(y), m_Builder(builder),
|
Tower(TowerID id, TowerType type, std::int32_t x, std::int32_t y, PlayerID builder) : utils::shape::Circle(x + 0.5f, y + 0.5f, 0), m_ID(id), m_Type(type), m_Builder(builder),
|
||||||
m_Timer(getStats()->getDamageRate() * 1000) { // converting seconds to millis
|
m_Timer(getStats()->getDamageRate() * 1000) { // converting seconds to millis
|
||||||
|
setRadius(getStats()->getRange());
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual TowerType getType() const = 0;
|
virtual TowerType getType() const = 0;
|
||||||
@@ -109,11 +110,10 @@ public:
|
|||||||
m_Level.setPath(path);
|
m_Level.setPath(path);
|
||||||
m_Timer.setCooldown(getStats()->getDamageRate() * 1000); // converting seconds to millis
|
m_Timer.setCooldown(getStats()->getDamageRate() * 1000); // converting seconds to millis
|
||||||
m_Timer.reset();
|
m_Timer.reset();
|
||||||
|
setRadius(getStats()->getRange());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::uint16_t getID() const { return m_ID; }
|
std::uint16_t getID() const { return m_ID; }
|
||||||
std::int32_t getX() const { return m_X; }
|
|
||||||
std::int32_t getY() const { return m_Y; }
|
|
||||||
const TowerLevel& getLevel() const { return m_Level; }
|
const TowerLevel& getLevel() const { return m_Level; }
|
||||||
const TowerStats* getStats() const { return getTowerStats(m_Type, m_Level); }
|
const TowerStats* getStats() const { return getTowerStats(m_Type, m_Level); }
|
||||||
PlayerID getBuilder() const { return m_Builder; }
|
PlayerID getBuilder() const { return m_Builder; }
|
||||||
@@ -163,6 +163,9 @@ class ArcherTower : public LittleTower {
|
|||||||
public:
|
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 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);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -56,6 +56,13 @@ struct Color {
|
|||||||
std::uint8_t r, g, b;
|
std::uint8_t r, g, b;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static constexpr Color BLACK{ 0, 0, 0 };
|
||||||
|
static constexpr Color WHITE{ 255, 255, 255 };
|
||||||
|
|
||||||
|
static constexpr Color RED{ 255, 0, 0 };
|
||||||
|
static constexpr Color GREEN{ 0, 255, 0 };
|
||||||
|
static constexpr Color BLUE{ 0, 0, 255 };
|
||||||
|
|
||||||
struct Tile {
|
struct Tile {
|
||||||
virtual TileType getType() const = 0;
|
virtual TileType getType() const = 0;
|
||||||
};
|
};
|
||||||
@@ -114,11 +121,27 @@ typedef std::array<Color, 2> SpawnColorPalette;
|
|||||||
|
|
||||||
typedef std::vector<TowerPtr> TowerList;
|
typedef std::vector<TowerPtr> TowerList;
|
||||||
|
|
||||||
class World {
|
class WorldListener {
|
||||||
|
public:
|
||||||
|
WorldListener() {}
|
||||||
|
|
||||||
|
virtual void OnTowerAdd(TowerPtr tower) {}
|
||||||
|
virtual void OnTowerRemove(TowerPtr tower) {}
|
||||||
|
|
||||||
|
virtual void OnArcherTowerShot(MobPtr target, ArcherTower* shooter) {}
|
||||||
|
|
||||||
|
virtual void OnArrowShot(MobPtr target, bool fire, Tower* shooter) {}
|
||||||
|
virtual void OnExplosion(utils::shape::Circle explosion, float centerDamage, Tower* shooter) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef utils::ObjectNotifier<WorldListener> WorldNotifier;
|
||||||
|
|
||||||
|
class World : public WorldListener, public MobListener {
|
||||||
protected:
|
protected:
|
||||||
TowerTileColorPalette m_TowerPlacePalette;
|
TowerTileColorPalette m_TowerPlacePalette;
|
||||||
Color m_WalkablePalette;
|
Color m_WalkablePalette;
|
||||||
std::vector<Color> m_DecorationPalette;
|
std::vector<Color> m_DecorationPalette;
|
||||||
|
Color m_Background;
|
||||||
|
|
||||||
std::unordered_map<ChunkCoord, ChunkPtr> m_Chunks;
|
std::unordered_map<ChunkCoord, ChunkPtr> m_Chunks;
|
||||||
|
|
||||||
@@ -131,6 +154,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);
|
||||||
|
|
||||||
@@ -145,12 +171,14 @@ 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;
|
||||||
|
|
||||||
const TowerTileColorPalette& getTowerTileColorPalette() const { return m_TowerPlacePalette; }
|
const TowerTileColorPalette& getTowerTileColorPalette() const { return m_TowerPlacePalette; }
|
||||||
const Color& getWalkableTileColor() const { return m_WalkablePalette; }
|
const Color& getWalkableTileColor() const { return m_WalkablePalette; }
|
||||||
const std::vector<Color>& getDecorationPalette() const { return m_DecorationPalette; }
|
const std::vector<Color>& getDecorationPalette() const { return m_DecorationPalette; }
|
||||||
|
const Color& getBackgroundColor() const { return m_Background; }
|
||||||
|
|
||||||
const TilePalette& getTilePalette() const { return m_TilePalette; }
|
const TilePalette& getTilePalette() const { return m_TilePalette; }
|
||||||
|
|
||||||
@@ -184,13 +212,29 @@ public:
|
|||||||
Team& getTeam(TeamColor team);
|
Team& getTeam(TeamColor team);
|
||||||
const Team& getTeam(TeamColor team) const;
|
const Team& getTeam(TeamColor team) const;
|
||||||
|
|
||||||
|
const TeamList& getTeams() const;
|
||||||
|
|
||||||
const TowerList& getTowers() const { return m_Towers; };
|
const TowerList& getTowers() const { return m_Towers; };
|
||||||
TowerPtr getTowerById(TowerID tower);
|
TowerPtr getTowerById(TowerID tower);
|
||||||
|
|
||||||
// Archer Tower
|
const Player* getPlayerById(PlayerID id) const;
|
||||||
virtual void OnArrowShot(MobPtr target, Tower* shooter);
|
|
||||||
|
WorldNotifier& getWorldNotifier() { return m_WorldNotifier; }
|
||||||
|
MobNotifier& getMobNotifier() { return m_MobNotifier; }
|
||||||
|
|
||||||
|
// WorldListener
|
||||||
|
|
||||||
|
virtual void OnArcherTowerShot(MobPtr target, ArcherTower* shooter);
|
||||||
|
|
||||||
|
virtual void OnArrowShot(MobPtr target, bool fire, Tower* shooter);
|
||||||
|
virtual void OnExplosion(utils::shape::Circle explosion, float centerDamage, Tower* shooter);
|
||||||
|
|
||||||
|
// MobListener
|
||||||
|
|
||||||
|
virtual void OnMobDamage(Mob* target, float damage, Tower* source);
|
||||||
|
virtual void OnMobCastleDamage(Mob* damager, TeamCastle* enemyCastle, float damage);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void moveMobs(std::uint64_t delta);
|
|
||||||
void tickMobs(std::uint64_t delta);
|
void tickMobs(std::uint64_t delta);
|
||||||
void cleanDeadMobs();
|
void cleanDeadMobs();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,16 +19,16 @@ class Client {
|
|||||||
private:
|
private:
|
||||||
render::Renderer* m_Renderer;
|
render::Renderer* m_Renderer;
|
||||||
ClientConnexion m_Connexion;
|
ClientConnexion m_Connexion;
|
||||||
ClientGame m_Game;
|
std::unique_ptr<ClientGame> m_Game;
|
||||||
bool m_Connected;
|
bool m_Connected;
|
||||||
public:
|
public:
|
||||||
Client(render::Renderer* renderer) : m_Renderer(renderer), m_Game(this), m_Connected(false) {}
|
Client(render::Renderer* renderer) : m_Renderer(renderer), m_Game(std::make_unique<ClientGame>(this)), m_Connected(false) {}
|
||||||
|
|
||||||
const ClientGame& getGame() const { return m_Game; }
|
const ClientGame& getGame() const { return *m_Game; }
|
||||||
const ClientConnexion& getConnexion() const { return m_Connexion; }
|
const ClientConnexion& getConnexion() const { return m_Connexion; }
|
||||||
render::Renderer* getRenderer() const { return m_Renderer; }
|
render::Renderer* getRenderer() const { return m_Renderer; }
|
||||||
|
|
||||||
ClientGame& getGame() { return m_Game; }
|
ClientGame& getGame() { return *m_Game; }
|
||||||
ClientConnexion& getConnexion() { return m_Connexion; }
|
ClientConnexion& getConnexion() { return m_Connexion; }
|
||||||
|
|
||||||
void tick(std::uint64_t delta);
|
void tick(std::uint64_t delta);
|
||||||
@@ -44,6 +44,9 @@ 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);
|
||||||
|
private:
|
||||||
|
void reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace client
|
} // namespace client
|
||||||
|
|||||||
@@ -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& getWorldClient() { 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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -61,12 +61,11 @@ private:
|
|||||||
bool m_ServerRunning;
|
bool m_ServerRunning;
|
||||||
public:
|
public:
|
||||||
Server(const std::string& worldFilePath);
|
Server(const std::string& worldFilePath);
|
||||||
virtual ~Server() {}
|
virtual ~Server();
|
||||||
|
|
||||||
bool start(std::uint16_t port);
|
bool start(std::uint16_t port);
|
||||||
void stop();
|
void stop(); // force the server to stop
|
||||||
|
void close(); // at the end of a game
|
||||||
void lauchGame();
|
|
||||||
|
|
||||||
void removeConnexion(std::uint8_t connexionID);
|
void removeConnexion(std::uint8_t connexionID);
|
||||||
|
|
||||||
@@ -74,6 +73,8 @@ public:
|
|||||||
|
|
||||||
float getTPS() const { return m_TickCounter.getTPS(); }
|
float getTPS() const { return m_TickCounter.getTPS(); }
|
||||||
|
|
||||||
|
bool isRunning() { return m_ServerRunning; }
|
||||||
|
|
||||||
const ServerGame& getGame() const { return m_Game; }
|
const ServerGame& getGame() const { return m_Game; }
|
||||||
ServerGame& getGame() { return m_Game; }
|
ServerGame& getGame() { return m_Game; }
|
||||||
|
|
||||||
@@ -88,6 +89,7 @@ private:
|
|||||||
void accept();
|
void accept();
|
||||||
void updateSockets();
|
void updateSockets();
|
||||||
|
|
||||||
|
void clean();
|
||||||
void startThread();
|
void startThread();
|
||||||
void stopThread();
|
void stopThread();
|
||||||
void tick(std::uint64_t delta);
|
void tick(std::uint64_t delta);
|
||||||
|
|||||||
@@ -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; }
|
||||||
|
|||||||
@@ -9,11 +9,12 @@ namespace server {
|
|||||||
|
|
||||||
class Server;
|
class Server;
|
||||||
|
|
||||||
class ServerGame : public game::Game {
|
class ServerGame : public game::Game, public game::GameListener {
|
||||||
private:
|
private:
|
||||||
Server* m_Server;
|
Server* m_Server;
|
||||||
ServerWorld m_ServerWorld;
|
ServerWorld m_ServerWorld;
|
||||||
utils::AutoTimer m_GoldMineTimer{ 1000, std::bind(&ServerGame::updateGoldMines, this) };
|
utils::AutoTimer m_GoldMineTimer{ 1000, std::bind(&ServerGame::updateGoldMines, this) };
|
||||||
|
utils::CooldownTimer m_EndGameCooldown{ 1000 * 10 };
|
||||||
public:
|
public:
|
||||||
ServerGame(Server* server);
|
ServerGame(Server* server);
|
||||||
~ServerGame() {}
|
~ServerGame() {}
|
||||||
@@ -22,6 +23,13 @@ public:
|
|||||||
|
|
||||||
virtual void tick(std::uint64_t delta);
|
virtual void tick(std::uint64_t delta);
|
||||||
void startGame();
|
void startGame();
|
||||||
|
|
||||||
|
// GameListener
|
||||||
|
|
||||||
|
virtual void OnGameStateUpdate(game::GameState newState);
|
||||||
|
virtual void OnGameBegin();
|
||||||
|
virtual void OnGameEnd();
|
||||||
|
virtual void OnGameClose();
|
||||||
private:
|
private:
|
||||||
void balanceTeams();
|
void balanceTeams();
|
||||||
void updateGoldMines();
|
void updateGoldMines();
|
||||||
|
|||||||
@@ -14,12 +14,15 @@ private:
|
|||||||
game::TowerID m_CurrentTowerID;
|
game::TowerID m_CurrentTowerID;
|
||||||
Server* m_Server;
|
Server* m_Server;
|
||||||
public:
|
public:
|
||||||
|
static constexpr float MobSpawnBorder = 0.01f;
|
||||||
|
|
||||||
ServerWorld(Server* server, ServerGame* game);
|
ServerWorld(Server* server, ServerGame* game);
|
||||||
|
|
||||||
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;
|
virtual void OnMobDie(game::Mob* mob);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace server
|
} // namespace server
|
||||||
|
|||||||
4465
include/misc/Backward.hpp
Normal file
4465
include/misc/Backward.hpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include "misc/DataBuffer.h"
|
#include "DataBuffer.h"
|
||||||
|
|
||||||
namespace td {
|
namespace td {
|
||||||
namespace utils {
|
namespace utils {
|
||||||
|
|||||||
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
|
||||||
@@ -18,7 +18,7 @@ enum class Architecture {
|
|||||||
Unknown,
|
Unknown,
|
||||||
};
|
};
|
||||||
|
|
||||||
inline Os getSystemOs(){
|
inline Os getSystemOs() {
|
||||||
#if defined(_WIN32) || defined(_WIN64)
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
return Os::Windows;
|
return Os::Windows;
|
||||||
#elif defined(__ANDROID__)
|
#elif defined(__ANDROID__)
|
||||||
@@ -26,12 +26,12 @@ inline Os getSystemOs(){
|
|||||||
#elif defined(__unix__)
|
#elif defined(__unix__)
|
||||||
return Os::Linux;
|
return Os::Linux;
|
||||||
#else
|
#else
|
||||||
#pragma message ("Target OS unknown or unsupported !")
|
#pragma message ("Target OS unknown or unsupported !")
|
||||||
return Os::Unknown;
|
return Os::Unknown;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Architecture getSystemArchitecture(){
|
inline Architecture getSystemArchitecture() {
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
return Architecture::x86_64;
|
return Architecture::x86_64;
|
||||||
#elif defined(_WIN32)
|
#elif defined(_WIN32)
|
||||||
@@ -45,7 +45,7 @@ inline Architecture getSystemArchitecture(){
|
|||||||
#elif defined(__arm__) || defined(_M_ARM)
|
#elif defined(__arm__) || defined(_M_ARM)
|
||||||
return Architecture::Armhf;
|
return Architecture::Armhf;
|
||||||
#else
|
#else
|
||||||
#pragma message ("Target CPU architecture unknown or unsupported !")
|
#pragma message ("Target CPU architecture unknown or unsupported !")
|
||||||
return Architecture::Unknown;
|
return Architecture::Unknown;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace td {
|
|||||||
namespace utils {
|
namespace utils {
|
||||||
|
|
||||||
template<typename NumberType>
|
template<typename NumberType>
|
||||||
NumberType getRandomInt(NumberType min, NumberType max){
|
NumberType getRandomInt(NumberType min, NumberType max) {
|
||||||
std::random_device randomDevice;
|
std::random_device randomDevice;
|
||||||
std::mt19937 generator(randomDevice());
|
std::mt19937 generator(randomDevice());
|
||||||
std::uniform_int_distribution<NumberType> distrib(min, max);
|
std::uniform_int_distribution<NumberType> distrib(min, max);
|
||||||
@@ -14,7 +14,7 @@ NumberType getRandomInt(NumberType min, NumberType max){
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename NumberType>
|
template<typename NumberType>
|
||||||
NumberType getRandomReal(NumberType min, NumberType max){
|
NumberType getRandomReal(NumberType min, NumberType max) {
|
||||||
std::random_device randomDevice;
|
std::random_device randomDevice;
|
||||||
std::mt19937 generator(randomDevice());
|
std::mt19937 generator(randomDevice());
|
||||||
std::uniform_real_distribution<NumberType> distrib(min, max);
|
std::uniform_real_distribution<NumberType> distrib(min, max);
|
||||||
|
|||||||
96
include/misc/Shapes.h
Normal file
96
include/misc/Shapes.h
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace td {
|
||||||
|
namespace utils {
|
||||||
|
namespace shape {
|
||||||
|
|
||||||
|
class Point {
|
||||||
|
private:
|
||||||
|
float m_X, m_Y;
|
||||||
|
public:
|
||||||
|
Point() : m_X(0), m_Y(0) {}
|
||||||
|
Point(float x, float y) : m_X(x), m_Y(y) {}
|
||||||
|
|
||||||
|
float getX() const { return m_X; }
|
||||||
|
float getY() const { return m_Y; }
|
||||||
|
|
||||||
|
void setX(float x) { m_X = x; }
|
||||||
|
void setY(float y) { m_Y = y; }
|
||||||
|
|
||||||
|
float distance(const Point& point) const;
|
||||||
|
float distanceSquared(const Point& point) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Circle;
|
||||||
|
|
||||||
|
class Rectangle {
|
||||||
|
private:
|
||||||
|
Point m_Center;
|
||||||
|
float m_Width, m_Height;
|
||||||
|
public:
|
||||||
|
Rectangle() {}
|
||||||
|
|
||||||
|
const Point& getCenter() const { return m_Center; }
|
||||||
|
float getCenterX() const { return m_Center.getX(); }
|
||||||
|
float getCenterY() const { return m_Center.getY(); }
|
||||||
|
|
||||||
|
float getWidth() const { return m_Width; }
|
||||||
|
float getHeight() const { return m_Height; }
|
||||||
|
|
||||||
|
Point getTopLeft() const { return { m_Center.getX() - (m_Width / 2.0f), m_Center.getY() - (m_Height / 2.0f) }; }
|
||||||
|
Point getBottomRight() const { return { m_Center.getX() + (m_Width / 2.0f), m_Center.getY() + (m_Height / 2.0f) }; }
|
||||||
|
|
||||||
|
void setCenter(const Point& center) { m_Center = center; }
|
||||||
|
void setCenterX(float x) { m_Center.setX(x); }
|
||||||
|
void setCenterY(float y) { m_Center.setY(y); }
|
||||||
|
|
||||||
|
void setSize(float width, float height) { setWidth(width); setHeight(height); }
|
||||||
|
void setSize(Point size) { setSize(size.getX(), size.getY()); }
|
||||||
|
Point getSize() { return { m_Width, m_Height }; }
|
||||||
|
|
||||||
|
void setWidth(float width) { m_Width = width; }
|
||||||
|
void setHeight(float height) { m_Height = height; }
|
||||||
|
|
||||||
|
bool collidesWith(const Point& point) const;
|
||||||
|
bool collidesWith(const Rectangle& rect) const;
|
||||||
|
bool collidesWith(const Circle& circle) const;
|
||||||
|
|
||||||
|
// distance from the closest side of the rectangle
|
||||||
|
float distance(const Circle& circle) const;
|
||||||
|
float distanceSquared(const Circle& circle) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Circle {
|
||||||
|
private:
|
||||||
|
Point m_Center;
|
||||||
|
float m_Radius;
|
||||||
|
public:
|
||||||
|
Circle(float x, float y, float radius) : m_Center(x, y), m_Radius(radius) {}
|
||||||
|
Circle() : m_Radius(0) {}
|
||||||
|
|
||||||
|
const Point& getCenter() const { return m_Center; }
|
||||||
|
float getCenterX() const { return m_Center.getX(); }
|
||||||
|
float getCenterY() const { return m_Center.getY(); }
|
||||||
|
|
||||||
|
float getRadius() const { return m_Radius; }
|
||||||
|
|
||||||
|
void setCenter(const Point& center) { m_Center = center; }
|
||||||
|
void setCenterX(float x) { m_Center.setX(x); }
|
||||||
|
void setCenterY(float y) { m_Center.setY(y); }
|
||||||
|
|
||||||
|
void setRadius(float radius) { m_Radius = radius; }
|
||||||
|
|
||||||
|
bool collidesWith(const Point& point) const;
|
||||||
|
bool collidesWith(const Rectangle& rect) const;
|
||||||
|
bool collidesWith(const Circle& circle) const;
|
||||||
|
|
||||||
|
// distance from the closest side of the rectangle
|
||||||
|
float distance(const Rectangle& rect) const;
|
||||||
|
float distanceSquared(const Rectangle& rect) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace shape
|
||||||
|
} // namespace utils
|
||||||
|
} // namespace td
|
||||||
@@ -39,7 +39,7 @@ public:
|
|||||||
// utililty class to call function at regular period of time
|
// utililty class to call function at regular period of time
|
||||||
class Timer {
|
class Timer {
|
||||||
private:
|
private:
|
||||||
std::uint64_t m_Interval;
|
std::uint64_t m_Interval; // in millis
|
||||||
std::uint64_t m_InternalTime = 0;
|
std::uint64_t m_InternalTime = 0;
|
||||||
public:
|
public:
|
||||||
Timer() : m_Interval(0) {}
|
Timer() : m_Interval(0) {}
|
||||||
@@ -53,10 +53,10 @@ 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;
|
std::uint64_t m_Cooldown; // in millis
|
||||||
std::uint64_t m_CooldownTime;
|
std::uint64_t m_CooldownTime;
|
||||||
public:
|
public:
|
||||||
CooldownTimer() : m_Cooldown(0), m_CooldownTime(0) {}
|
CooldownTimer() : m_Cooldown(0), m_CooldownTime(0) {}
|
||||||
|
|||||||
@@ -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) {}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -33,18 +33,19 @@ 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 {
|
||||||
game::TowerTileColorPalette m_TowerPlacePalette;
|
game::TowerTileColorPalette m_TowerPlacePalette;
|
||||||
game::Color m_WalkablePalette;
|
game::Color m_WalkablePalette;
|
||||||
std::vector<game::Color> m_DecorationPalette;
|
std::vector<game::Color> m_DecorationPalette;
|
||||||
|
game::Color m_Background;
|
||||||
|
|
||||||
game::SpawnColorPalette m_SpawnColorPalette;
|
game::SpawnColorPalette m_SpawnColorPalette;
|
||||||
|
|
||||||
@@ -128,6 +129,7 @@ public:
|
|||||||
const game::TowerTileColorPalette& getTowerTilePalette() const { return m_Header.m_TowerPlacePalette; }
|
const game::TowerTileColorPalette& getTowerTilePalette() const { return m_Header.m_TowerPlacePalette; }
|
||||||
const game::Color& getWalkableTileColor() const { return m_Header.m_WalkablePalette; }
|
const game::Color& getWalkableTileColor() const { return m_Header.m_WalkablePalette; }
|
||||||
const std::vector<game::Color>& getDecorationPalette() const { return m_Header.m_DecorationPalette; }
|
const std::vector<game::Color>& getDecorationPalette() const { return m_Header.m_DecorationPalette; }
|
||||||
|
const game::Color& getBackgroundColor() const { return m_Header.m_Background; }
|
||||||
|
|
||||||
const game::Spawn& getRedSpawn() const { return m_Header.m_RedSpawn; }
|
const game::Spawn& getRedSpawn() const { return m_Header.m_RedSpawn; }
|
||||||
const game::Spawn& getBlueSpawn() const { return m_Header.m_BlueSpawn; }
|
const game::Spawn& getBlueSpawn() const { return m_Header.m_BlueSpawn; }
|
||||||
@@ -477,13 +479,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 +493,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 {
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ private:
|
|||||||
std::unique_ptr<WorldShader> m_WorldShader;
|
std::unique_ptr<WorldShader> m_WorldShader;
|
||||||
std::unique_ptr<EntityShader> m_EntityShader;
|
std::unique_ptr<EntityShader> m_EntityShader;
|
||||||
|
|
||||||
|
glm::vec3 m_BackgroundColor;
|
||||||
|
|
||||||
bool m_IsometricView = true;
|
bool m_IsometricView = true;
|
||||||
float m_IsometricShade = m_IsometricView;
|
float m_IsometricShade = m_IsometricView;
|
||||||
glm::vec2 m_CamPos{};
|
glm::vec2 m_CamPos{};
|
||||||
@@ -49,6 +51,8 @@ public:
|
|||||||
void setCamPos(const glm::vec2& newPos);
|
void setCamPos(const glm::vec2& newPos);
|
||||||
void setIsometricView(bool isometric); // false = 2D true = Isometric
|
void setIsometricView(bool isometric); // false = 2D true = Isometric
|
||||||
|
|
||||||
|
void setBackgroundColor(const glm::vec3& color) { m_BackgroundColor = color; }
|
||||||
|
|
||||||
glm::vec2 getCursorWorldPos(const glm::vec2& cursorPos, float aspectRatio, float zoom, float windowWidth, float windowHeight);
|
glm::vec2 getCursorWorldPos(const glm::vec2& cursorPos, float aspectRatio, float zoom, float windowWidth, float windowHeight);
|
||||||
private:
|
private:
|
||||||
void updateIsometricView();
|
void updateIsometricView();
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "render/gui/TowerPlacePopup.h"
|
#include "render/gui/TowerPlacePopup.h"
|
||||||
#include "render/gui/MobTooltip.h"
|
#include "render/gui/MobTooltip.h"
|
||||||
|
#include "render/gui/CastleTooltip.h"
|
||||||
|
|
||||||
#include "render/gui/imgui/imgui.h"
|
#include "render/gui/imgui/imgui.h"
|
||||||
|
|
||||||
@@ -23,7 +24,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;
|
||||||
@@ -40,15 +41,13 @@ private:
|
|||||||
|
|
||||||
std::unique_ptr<gui::TowerPlacePopup> m_TowerPlacePopup;
|
std::unique_ptr<gui::TowerPlacePopup> m_TowerPlacePopup;
|
||||||
std::unique_ptr<gui::MobTooltip> m_MobTooltip;
|
std::unique_ptr<gui::MobTooltip> m_MobTooltip;
|
||||||
|
std::unique_ptr<gui::CastleTooltip> m_CastleTooltip;
|
||||||
public:
|
public:
|
||||||
WorldRenderer(game::World* world, client::ClientGame* client);
|
WorldRenderer(game::World* world, client::ClientGame* client);
|
||||||
~WorldRenderer();
|
~WorldRenderer();
|
||||||
|
|
||||||
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 +57,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;
|
||||||
@@ -67,8 +71,12 @@ private:
|
|||||||
void renderPopups();
|
void renderPopups();
|
||||||
void renderTowerUpgradePopup();
|
void renderTowerUpgradePopup();
|
||||||
void renderMobTooltip() const;
|
void renderMobTooltip() const;
|
||||||
|
void renderCastleTooltip() const;
|
||||||
void detectClick();
|
void detectClick();
|
||||||
void detectMobHovering() const;
|
void detectMobHovering() const;
|
||||||
|
void detectCastleHovering() const;
|
||||||
|
void renderTooltips() const;
|
||||||
|
void removeTower();
|
||||||
glm::vec2 getCursorWorldPos() const;
|
glm::vec2 getCursorWorldPos() const;
|
||||||
glm::vec2 getClickWorldPos() const;
|
glm::vec2 getClickWorldPos() const;
|
||||||
|
|
||||||
|
|||||||
28
include/render/gui/CastleTooltip.h
Normal file
28
include/render/gui/CastleTooltip.h
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "GuiWidget.h"
|
||||||
|
|
||||||
|
namespace td {
|
||||||
|
|
||||||
|
namespace game {
|
||||||
|
|
||||||
|
class TeamCastle;
|
||||||
|
|
||||||
|
} // namespace game
|
||||||
|
|
||||||
|
namespace gui {
|
||||||
|
|
||||||
|
class CastleTooltip : public GuiWidget {
|
||||||
|
private:
|
||||||
|
const game::TeamCastle* m_Castle;
|
||||||
|
public:
|
||||||
|
CastleTooltip(client::Client* client);
|
||||||
|
|
||||||
|
virtual void render();
|
||||||
|
|
||||||
|
void setCastle(const game::TeamCastle* castle) { m_Castle = castle; }
|
||||||
|
bool isShown() { return m_Castle != nullptr; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace gui
|
||||||
|
} // namespace td
|
||||||
@@ -10,15 +10,17 @@ namespace gui {
|
|||||||
|
|
||||||
class GuiManager {
|
class GuiManager {
|
||||||
private:
|
private:
|
||||||
std::vector<std::shared_ptr<GuiWidget>> m_Widgets;
|
std::vector<std::unique_ptr<GuiWidget>> m_Widgets;
|
||||||
public:
|
public:
|
||||||
void renderWidgets(){
|
GuiManager(){}
|
||||||
for(auto widget : m_Widgets){
|
|
||||||
|
void renderWidgets() {
|
||||||
|
for (auto& widget : m_Widgets) {
|
||||||
widget->render();
|
widget->render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void addWidgets(const std::shared_ptr<GuiWidget>& widget){
|
void addWidget(std::unique_ptr<GuiWidget>&& widget) {
|
||||||
m_Widgets.push_back(std::move(widget));
|
m_Widgets.push_back(std::move(widget));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ public:
|
|||||||
virtual void render();
|
virtual void render();
|
||||||
|
|
||||||
void setMob(const game::Mob* mob) { m_Mob = mob; }
|
void setMob(const game::Mob* mob) { m_Mob = mob; }
|
||||||
|
bool isShown() { return m_Mob != nullptr; }
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace gui
|
} // namespace gui
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
namespace td {
|
namespace td {
|
||||||
namespace gui {
|
namespace gui {
|
||||||
|
|
||||||
class SummonMenu : public GuiWidget{
|
class SummonMenu : public GuiWidget {
|
||||||
private:
|
private:
|
||||||
bool m_MenuOpened;
|
bool m_MenuOpened;
|
||||||
int m_ImageWidth = 100;
|
int m_ImageWidth = 100;
|
||||||
|
|||||||
@@ -9,8 +9,10 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#include "render/gui/GuiManager.h"
|
||||||
|
|
||||||
struct SDL_Window;
|
struct SDL_Window;
|
||||||
typedef void *SDL_GLContext;
|
typedef void* SDL_GLContext;
|
||||||
|
|
||||||
namespace td {
|
namespace td {
|
||||||
|
|
||||||
@@ -20,15 +22,6 @@ class Client;
|
|||||||
|
|
||||||
} // namespace client
|
} // namespace client
|
||||||
|
|
||||||
namespace gui {
|
|
||||||
|
|
||||||
class MainMenu;
|
|
||||||
class GameMenu;
|
|
||||||
class FrameMenu;
|
|
||||||
class UpdateMenu;
|
|
||||||
|
|
||||||
} // namespace gui
|
|
||||||
|
|
||||||
namespace render {
|
namespace render {
|
||||||
|
|
||||||
class Renderer;
|
class Renderer;
|
||||||
@@ -38,11 +31,8 @@ private:
|
|||||||
SDL_Window* m_Window;
|
SDL_Window* m_Window;
|
||||||
SDL_GLContext m_GlContext;
|
SDL_GLContext m_GlContext;
|
||||||
td::render::Renderer* m_Renderer;
|
td::render::Renderer* m_Renderer;
|
||||||
|
td::gui::GuiManager m_GuiManager;
|
||||||
std::unique_ptr<td::client::Client> m_Client;
|
std::unique_ptr<td::client::Client> m_Client;
|
||||||
std::unique_ptr<td::gui::MainMenu> m_MainMenu;
|
|
||||||
std::unique_ptr<td::gui::GameMenu> m_GameMenu;
|
|
||||||
std::unique_ptr<td::gui::FrameMenu> m_FrameMenu;
|
|
||||||
std::unique_ptr<td::gui::UpdateMenu> m_UpdateMenu;
|
|
||||||
public:
|
public:
|
||||||
TowerGui(SDL_Window* wndow, SDL_GLContext glContext, td::render::Renderer* renderer);
|
TowerGui(SDL_Window* wndow, SDL_GLContext glContext, td::render::Renderer* renderer);
|
||||||
~TowerGui();
|
~TowerGui();
|
||||||
|
|||||||
@@ -17,11 +17,11 @@ public:
|
|||||||
|
|
||||||
void setClickPos(const glm::vec2& worldPos);
|
void setClickPos(const glm::vec2& worldPos);
|
||||||
private:
|
private:
|
||||||
static constexpr float m_TowerPopupTileWidth = 250.0f;
|
static constexpr float m_TowerPopupTileWidth = 200.0f;
|
||||||
static constexpr float m_TowerPopupTileHeight = 250.0f;
|
static constexpr float m_TowerPopupTileHeight = 200.0f;
|
||||||
|
|
||||||
static constexpr float m_PlaceTowerButtonWidth = 200.0f;
|
static constexpr float m_PlaceTowerButtonWidth = 150.0f;
|
||||||
static constexpr float m_PlaceTowerButtonHeight = 45.0f;
|
static constexpr float m_PlaceTowerButtonHeight = 35.0f;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace gui
|
} // namespace gui
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include "GuiWidget.h"
|
#include "GuiWidget.h"
|
||||||
|
|
||||||
#include "misc/Updater.h"
|
#include "updater/Updater.h"
|
||||||
|
|
||||||
#include <future>
|
#include <future>
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,7 @@
|
|||||||
|
|
||||||
#include "misc/DataBuffer.h"
|
#include "misc/DataBuffer.h"
|
||||||
|
|
||||||
#include <ctime>
|
#define TD_VERSION "alpha-0.1.2"
|
||||||
|
|
||||||
#define TD_VERSION "alpha-0.0.1"
|
|
||||||
|
|
||||||
namespace td {
|
namespace td {
|
||||||
namespace utils {
|
namespace utils {
|
||||||
@@ -34,7 +32,7 @@ public:
|
|||||||
static std::string getLocalFilePath();
|
static std::string getLocalFilePath();
|
||||||
static void removeOldFile();
|
static void removeOldFile();
|
||||||
|
|
||||||
static std::string getCurrentVersion(){ return TD_VERSION; }
|
static std::string getCurrentVersion() { return TD_VERSION; }
|
||||||
std::string getLastVersion() { return m_LastVersion; }
|
std::string getLastVersion() { return m_LastVersion; }
|
||||||
|
|
||||||
bool canUpdate();
|
bool canUpdate();
|
||||||
@@ -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 "misc/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();
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -14,11 +16,11 @@ bool Mob::isImmuneTo(EffectType type) {
|
|||||||
return std::find(getEffectImmunities().begin(), getEffectImmunities().end(), type) != getEffectImmunities().end();
|
return std::find(getEffectImmunities().begin(), getEffectImmunities().end(), type) != getEffectImmunities().end();
|
||||||
}
|
}
|
||||||
|
|
||||||
EffectDuration& Mob::getEffect(EffectType effectType){
|
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,29 +32,194 @@ 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];
|
||||||
effect.duration -= deltaSec;
|
effect.duration -= deltaSec;
|
||||||
if (effect.duration < 0){ // effect has gone
|
if (effect.duration < 0) { // effect has gone
|
||||||
m_Effects.erase(m_Effects.begin() + i);
|
m_Effects.erase(m_Effects.begin() + i);
|
||||||
switch(effect.type){
|
switch (effect.type) {
|
||||||
case EffectType::Fire:{
|
case EffectType::Fire: {
|
||||||
m_EffectFireTimer.reset();
|
m_EffectFireTimer.reset();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case EffectType::Poison:{
|
case EffectType::Poison: {
|
||||||
m_EffectPoisonTimer.reset();
|
m_EffectPoisonTimer.reset();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case EffectType::Heal:{
|
case EffectType::Heal: {
|
||||||
m_EffectHealTimer.reset();
|
m_EffectHealTimer.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,18 +228,18 @@ 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)) {
|
||||||
if(m_EffectFireTimer.update(delta)){
|
if (m_EffectFireTimer.update(delta)) {
|
||||||
heal(10);
|
heal(10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -88,66 +255,66 @@ bool Mob::hasEffect(EffectType type) {
|
|||||||
typedef std::pair<MobType, std::uint8_t> MobKey;
|
typedef std::pair<MobType, std::uint8_t> MobKey;
|
||||||
|
|
||||||
const std::map<MobKey, MobStats> MobConstants = {
|
const std::map<MobKey, MobStats> MobConstants = {
|
||||||
// damage speed money_cost exp_cost exp_reward max_health
|
// damage speed size money_cost exp_cost exp_reward max_health
|
||||||
{{MobType::Zombie, 1},{MobStats{1, 1.6, 15, 0, 7, 40}}},
|
{{MobType::Zombie, 1},{MobStats{1, 1.6, {1, 1}, 15, 0, 7, 40}}},
|
||||||
{{MobType::Zombie, 2},{MobStats{1, 1.6, 18, 88, 9, 56}}},
|
{{MobType::Zombie, 2},{MobStats{1, 1.6, {1, 1}, 18, 88, 9, 56}}},
|
||||||
{{MobType::Zombie, 3},{MobStats{1, 1.6, 22, 153, 10, 78}}},
|
{{MobType::Zombie, 3},{MobStats{1, 1.6, {1, 1}, 22, 153, 10, 78}}},
|
||||||
{{MobType::Zombie, 4},{MobStats{1.5, 1.6, 26, 268, 13, 110}}},
|
{{MobType::Zombie, 4},{MobStats{1.5, 1.6, {1, 1}, 26, 268, 13, 110}}},
|
||||||
{{MobType::Zombie, 5},{MobStats{2, 1.6, 31, 469, 15, 154}}},
|
{{MobType::Zombie, 5},{MobStats{2, 1.6, {1, 1}, 31, 469, 15, 154}}},
|
||||||
|
|
||||||
{{MobType::Spider, 1},{MobStats{1, 1.6, 25, 100, 15, 80}}},
|
{{MobType::Spider, 1},{MobStats{1, 1.6, {1, 1}, 25, 100, 15, 80}}},
|
||||||
{{MobType::Spider, 2},{MobStats{1, 2, 30, 175, 16, 112}}},
|
{{MobType::Spider, 2},{MobStats{1, 2, {1, 1}, 30, 175, 16, 112}}},
|
||||||
{{MobType::Spider, 3},{MobStats{1.5, 2, 36, 306, 18, 157}}},
|
{{MobType::Spider, 3},{MobStats{1.5, 2, {1, 1}, 36, 306, 18, 157}}},
|
||||||
{{MobType::Spider, 4},{MobStats{2.5, 2, 43, 536, 19, 222}}},
|
{{MobType::Spider, 4},{MobStats{2.5, 2, {1, 1}, 43, 536, 19, 222}}},
|
||||||
{{MobType::Spider, 5},{MobStats{1.5, 2.5, 52, 938, 22, 307}}},
|
{{MobType::Spider, 5},{MobStats{1.5, 2.5, {1, 1}, 52, 938, 22, 307}}},
|
||||||
|
|
||||||
{{MobType::Skeleton, 1},{MobStats{1, 1.6, 120, 200, 30, 350}}},
|
{{MobType::Skeleton, 1},{MobStats{1, 1.6, {1, 1}, 120, 200, 30, 350}}},
|
||||||
{{MobType::Skeleton, 2},{MobStats{1, 1.6, 144, 350, 33, 490}}},
|
{{MobType::Skeleton, 2},{MobStats{1, 1.6, {1, 1}, 144, 350, 33, 490}}},
|
||||||
{{MobType::Skeleton, 3},{MobStats{1, 1.6, 173, 613, 36, 686}}},
|
{{MobType::Skeleton, 3},{MobStats{1, 1.6, {1, 1}, 173, 613, 36, 686}}},
|
||||||
{{MobType::Skeleton, 4},{MobStats{1.5, 1.6, 225, 1072, 40, 960}}},
|
{{MobType::Skeleton, 4},{MobStats{1.5, 1.6, {1, 1}, 225, 1072, 40, 960}}},
|
||||||
{{MobType::Skeleton, 5},{MobStats{2, 1.6, 255, 1876, 43, 1345}}},
|
{{MobType::Skeleton, 5},{MobStats{2, 1.6, {1, 1}, 255, 1876, 43, 1345}}},
|
||||||
|
|
||||||
{{MobType::Pigman, 1},{MobStats{1, 2, 100, 150, 22, 150}}},
|
{{MobType::Pigman, 1},{MobStats{1, 2, {1, 1}, 100, 150, 22, 150}}},
|
||||||
{{MobType::Pigman, 2},{MobStats{1, 2, 120, 263, 24, 210}}},
|
{{MobType::Pigman, 2},{MobStats{1, 2, {1, 1}, 120, 263, 24, 210}}},
|
||||||
{{MobType::Pigman, 3},{MobStats{1, 2, 144, 459, 25, 297}}},
|
{{MobType::Pigman, 3},{MobStats{1, 2, {1, 1}, 144, 459, 25, 297}}},
|
||||||
{{MobType::Pigman, 4},{MobStats{1, 2, 173, 804, 25, 412}}},
|
{{MobType::Pigman, 4},{MobStats{1, 2, {1, 1}, 173, 804, 25, 412}}},
|
||||||
{{MobType::Pigman, 5},{MobStats{1.5, 2, 207, 1407, 27, 576}}},
|
{{MobType::Pigman, 5},{MobStats{1.5, 2, {1, 1}, 207, 1407, 27, 576}}},
|
||||||
|
|
||||||
{{MobType::Creeper, 1},{MobStats{1, 2, 250, 325, 46, 350}}},
|
{{MobType::Creeper, 1},{MobStats{1, 2, {1, 1}, 250, 325, 46, 350}}},
|
||||||
{{MobType::Creeper, 2},{MobStats{1, 2, 290, 550, 49, 460}}},
|
{{MobType::Creeper, 2},{MobStats{1, 2, {1, 1}, 290, 550, 49, 460}}},
|
||||||
{{MobType::Creeper, 3},{MobStats{2, 2, 350, 800, 55, 630}}},
|
{{MobType::Creeper, 3},{MobStats{2, 2, {1, 1}, 350, 800, 55, 630}}},
|
||||||
{{MobType::Creeper, 4},{MobStats{3, 2, 420, 1300, 61, 900}}},
|
{{MobType::Creeper, 4},{MobStats{3, 2, {1, 1}, 420, 1300, 61, 900}}},
|
||||||
{{MobType::Creeper, 5},{MobStats{4, 2, 510, 1850, 67, 1250}}},
|
{{MobType::Creeper, 5},{MobStats{4, 2, {1, 1}, 510, 1850, 67, 1250}}},
|
||||||
|
|
||||||
{{MobType::Silverfish, 1},{MobStats{1, 1.6, 38, 125, 18, 120}}},
|
{{MobType::Silverfish, 1},{MobStats{1, 1.6, {1, 1}, 38, 125, 18, 120}}},
|
||||||
{{MobType::Silverfish, 2},{MobStats{1, 1.6, 50, 230, 19, 170}}},
|
{{MobType::Silverfish, 2},{MobStats{1, 1.6, {1, 1}, 50, 230, 19, 170}}},
|
||||||
{{MobType::Silverfish, 3},{MobStats{1, 1.6, 75, 340, 25, 225}}},
|
{{MobType::Silverfish, 3},{MobStats{1, 1.6, {1, 1}, 75, 340, 25, 225}}},
|
||||||
{{MobType::Silverfish, 4},{MobStats{1.5, 1.6, 170, 700, 33, 310}}},
|
{{MobType::Silverfish, 4},{MobStats{1.5, 1.6, {1, 1}, 170, 700, 33, 310}}},
|
||||||
{{MobType::Silverfish, 5},{MobStats{1.5, 1.6, 200, 1800, 36, 390}}},
|
{{MobType::Silverfish, 5},{MobStats{1.5, 1.6, {1, 1}, 200, 1800, 36, 390}}},
|
||||||
|
|
||||||
{{MobType::Blaze, 1},{MobStats{1, 1.6, 500, 500, 105, 410}}},
|
{{MobType::Blaze, 1},{MobStats{1, 1.6, {1, 1}, 500, 500, 105, 410}}},
|
||||||
{{MobType::Blaze, 2},{MobStats{1, 1.6, 600, 875, 111, 574}}},
|
{{MobType::Blaze, 2},{MobStats{1, 1.6, {1, 1}, 600, 875, 111, 574}}},
|
||||||
{{MobType::Blaze, 3},{MobStats{1, 1.6, 720, 1531, 115, 804}}},
|
{{MobType::Blaze, 3},{MobStats{1, 1.6, {1, 1}, 720, 1531, 115, 804}}},
|
||||||
{{MobType::Blaze, 4},{MobStats{1.5, 1.6, 864, 2680, 121, 1125}}},
|
{{MobType::Blaze, 4},{MobStats{1.5, 1.6, {1, 1}, 864, 2680, 121, 1125}}},
|
||||||
{{MobType::Blaze, 5},{MobStats{2, 1.6, 1037, 4689, 127, 1575}}},
|
{{MobType::Blaze, 5},{MobStats{2, 1.6, {1, 1}, 1037, 4689, 127, 1575}}},
|
||||||
|
|
||||||
{{MobType::Witch, 1},{MobStats{1, 1.6, 150, 300, 37, 300}}},
|
{{MobType::Witch, 1},{MobStats{1, 1.6, {1, 1}, 150, 300, 37, 300}}},
|
||||||
{{MobType::Witch, 2},{MobStats{1, 1.6, 165, 525, 39, 405}}},
|
{{MobType::Witch, 2},{MobStats{1, 1.6, {1, 1}, 165, 525, 39, 405}}},
|
||||||
{{MobType::Witch, 3},{MobStats{1, 1.6, 182, 918, 42, 547}}},
|
{{MobType::Witch, 3},{MobStats{1, 1.6, {1, 1}, 182, 918, 42, 547}}},
|
||||||
{{MobType::Witch, 4},{MobStats{1.5, 1.6, 200, 1606, 43, 738}}},
|
{{MobType::Witch, 4},{MobStats{1.5, 1.6, {1, 1}, 200, 1606, 43, 738}}},
|
||||||
{{MobType::Witch, 5},{MobStats{2, 1.6, 220, 2810, 45, 996}}},
|
{{MobType::Witch, 5},{MobStats{2, 1.6, {1, 1}, 220, 2810, 45, 996}}},
|
||||||
|
|
||||||
{{MobType::Slime, 1},{MobStats{1, 0.8, 1500, 1000, 300, 800}}},
|
{{MobType::Slime, 1},{MobStats{1, 0.8, {1, 1}, 1500, 1000, 300, 800}}},
|
||||||
{{MobType::Slime, 2},{MobStats{1.5, 0.8, 1800, 1750, 314, 880}}},
|
{{MobType::Slime, 2},{MobStats{1.5, 0.8, {1, 1}, 1800, 1750, 314, 880}}},
|
||||||
{{MobType::Slime, 3},{MobStats{2, 0.8, 2160, 3063, 330, 968}}},
|
{{MobType::Slime, 3},{MobStats{2, 0.8, {1, 1}, 2160, 3063, 330, 968}}},
|
||||||
{{MobType::Slime, 4},{MobStats{2.5, 0.8, 2592, 5359, 348, 1065}}},
|
{{MobType::Slime, 4},{MobStats{2.5, 0.8, {1, 1}, 2592, 5359, 348, 1065}}},
|
||||||
{{MobType::Slime, 5},{MobStats{3, 0.8, 3110, 9379, 366, 1171}}},
|
{{MobType::Slime, 5},{MobStats{3, 0.8, {1, 1}, 3110, 9379, 366, 1171}}},
|
||||||
|
|
||||||
{{MobType::Giant, 1},{MobStats{10, 0.8, 4000, 2250, 600, 6250}}},
|
{{MobType::Giant, 1},{MobStats{10, 0.8, {1, 1}, 4000, 2250, 600, 6250}}},
|
||||||
{{MobType::Giant, 2},{MobStats{20, 0.8, 4500, 4000, 612, 9375}}},
|
{{MobType::Giant, 2},{MobStats{20, 0.8, {1, 1}, 4500, 4000, 612, 9375}}},
|
||||||
{{MobType::Giant, 3},{MobStats{30, 0.8, 5062, 7250, 624, 14062}}},
|
{{MobType::Giant, 3},{MobStats{30, 0.8, {1, 1}, 5062, 7250, 624, 14062}}},
|
||||||
{{MobType::Giant, 4},{MobStats{40, 0.8, 5695, 12500, 636, 21093}}},
|
{{MobType::Giant, 4},{MobStats{40, 0.8, {1, 1}, 5695, 12500, 636, 21093}}},
|
||||||
{{MobType::Giant, 5},{MobStats{50, 0.8, 6407, 22000, 648, 31640}}},
|
{{MobType::Giant, 5},{MobStats{50, 0.8, {1, 1}, 6407, 22000, 648, 31640}}},
|
||||||
};
|
};
|
||||||
|
|
||||||
const MobStats* getMobStats(MobType type, std::uint8_t level) {
|
const MobStats* getMobStats(MobType type, std::uint8_t level) {
|
||||||
@@ -235,7 +402,7 @@ const std::map<MobKey, EffectImmunities> MobsEffectImmunities = {
|
|||||||
|
|
||||||
{{MobType::Skeleton, 1},{}},
|
{{MobType::Skeleton, 1},{}},
|
||||||
{{MobType::Skeleton, 2},{}},
|
{{MobType::Skeleton, 2},{}},
|
||||||
{{MobType::Skeleton, 3},{EffectType::Fire}},
|
{{MobType::Skeleton, 3},{}},
|
||||||
{{MobType::Skeleton, 4},{EffectType::Fire}},
|
{{MobType::Skeleton, 4},{EffectType::Fire}},
|
||||||
{{MobType::Skeleton, 5},{EffectType::Fire}},
|
{{MobType::Skeleton, 5},{EffectType::Fire}},
|
||||||
|
|
||||||
@@ -305,8 +472,8 @@ MobPtr MobFactory::createMob(MobID id, MobType type, std::uint8_t level, PlayerI
|
|||||||
return mobFactory[type](id, level, sender);
|
return mobFactory[type](id, level, sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string MobFactory::getMobName(MobType type){
|
std::string MobFactory::getMobName(MobType type) {
|
||||||
switch(type){
|
switch (type) {
|
||||||
case MobType::Zombie:
|
case MobType::Zombie:
|
||||||
return "Zombie";
|
return "Zombie";
|
||||||
case MobType::Spider:
|
case MobType::Spider:
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
#include "game/Player.h"
|
#include "game/Player.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
namespace td {
|
namespace td {
|
||||||
namespace game {
|
namespace game {
|
||||||
|
|
||||||
Team::Team(TeamColor color) : m_Color(color) {}
|
Team::Team(TeamColor color) : m_Color(color), m_TeamCastle(this) {}
|
||||||
|
|
||||||
void Team::addPlayer(Player* newPlayer) {
|
void Team::addPlayer(Player* newPlayer) {
|
||||||
m_Players.push_back(newPlayer);
|
m_Players.push_back(newPlayer);
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace game {
|
|||||||
bool Tower::isMobInRange(MobPtr mob) {
|
bool Tower::isMobInRange(MobPtr mob) {
|
||||||
if (mob->isDead())
|
if (mob->isDead())
|
||||||
return false;
|
return false;
|
||||||
return (m_X - mob->getX()) * (m_X - mob->getX()) + (m_Y - mob->getY()) * (m_Y - mob->getY()) < (getStats()->getRange() * getStats()->getRange());
|
return mob->collidesWith(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::map<std::pair<TowerType, TowerLevel>, TowerStats> TowerConstants = {
|
const std::map<std::pair<TowerType, TowerLevel>, TowerStats> TowerConstants = {
|
||||||
@@ -21,8 +21,8 @@ const std::map<std::pair<TowerType, TowerLevel>, TowerStats> TowerConstants = {
|
|||||||
{{TowerType::Archer, {4, TowerPath::Top}}, {0.8, 0, 15}},
|
{{TowerType::Archer, {4, TowerPath::Top}}, {0.8, 0, 15}},
|
||||||
|
|
||||||
{{TowerType::Archer, {2, TowerPath::Bottom}}, {2, 10, 12}},
|
{{TowerType::Archer, {2, TowerPath::Bottom}}, {2, 10, 12}},
|
||||||
{{TowerType::Archer, {3, TowerPath::Bottom}}, {2, 0, 13}},
|
{{TowerType::Archer, {3, TowerPath::Bottom}}, {2, 10, 13}},
|
||||||
{{TowerType::Archer, {4, TowerPath::Bottom}}, {2, 0, 15}},
|
{{TowerType::Archer, {4, TowerPath::Bottom}}, {2, 10, 15}},
|
||||||
|
|
||||||
//-----------------------------------------------------------------
|
//-----------------------------------------------------------------
|
||||||
|
|
||||||
@@ -116,8 +116,8 @@ const std::map<std::pair<TowerType, TowerLevel>, TowerStats> TowerConstants = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const TowerStats* getTowerStats(TowerType type, TowerLevel level) {
|
const TowerStats* getTowerStats(TowerType type, TowerLevel level) {
|
||||||
auto it = TowerConstants.find({type, level});
|
auto it = TowerConstants.find({ type, level });
|
||||||
if(it == TowerConstants.end()) return nullptr;
|
if (it == TowerConstants.end()) return nullptr;
|
||||||
return &it->second;
|
return &it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,11 +213,11 @@ 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->OnArrowShot(mob, this);
|
world->getWorldNotifier().notifyListeners(&WorldListener::OnArcherTowerShot, mob, this);
|
||||||
|
m_Timer.applyCooldown();
|
||||||
arrowsShot++;
|
arrowsShot++;
|
||||||
if (arrowsShot >= arrows)
|
if (arrowsShot >= arrows)
|
||||||
break;
|
break;
|
||||||
m_Timer.applyCooldown();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -229,7 +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
|
||||||
mob->damage(damage, this);
|
if (damage > 0)
|
||||||
|
world->getMobNotifier().notifyListeners(&MobListener::OnMobDamage, mob.get(), damage, this);
|
||||||
m_Timer.applyCooldown();
|
m_Timer.applyCooldown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -251,11 +252,11 @@ void PoisonTower::tick(std::uint64_t delta, World* world) {
|
|||||||
if (m_Timer.update(delta)) {
|
if (m_Timer.update(delta)) {
|
||||||
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->getMobNotifier().notifyListeners(&MobListener::OnMobDamage, mob.get(), getStats()->getDamage(), this);
|
||||||
}else{
|
} else {
|
||||||
float durationSec;
|
float durationSec;
|
||||||
switch(getLevel().getLevel()){
|
switch (getLevel().getLevel()) {
|
||||||
case 1:
|
case 1:
|
||||||
durationSec = 5;
|
durationSec = 5;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -13,15 +13,16 @@ namespace td {
|
|||||||
namespace game {
|
namespace game {
|
||||||
|
|
||||||
World::World(Game* game) : m_Game(game) {
|
World::World(Game* game) : m_Game(game) {
|
||||||
|
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 {
|
||||||
std::int16_t chunkX = x / Chunk::ChunkWidth;
|
std::int16_t chunkX = x / Chunk::ChunkWidth;
|
||||||
std::int16_t chunkY = y / Chunk::ChunkHeight;
|
std::int16_t chunkY = y / Chunk::ChunkHeight;
|
||||||
|
|
||||||
std::uint16_t subChunkX = x % Chunk::ChunkWidth;
|
std::uint16_t subChunkX = std::abs(x % Chunk::ChunkWidth);
|
||||||
std::uint16_t subChunkY = y % Chunk::ChunkHeight;
|
std::uint16_t subChunkY = std::abs(y % Chunk::ChunkHeight);
|
||||||
|
|
||||||
auto chunkIt = m_Chunks.find({ chunkX, chunkY });
|
auto chunkIt = m_Chunks.find({ chunkX, chunkY });
|
||||||
if (chunkIt == m_Chunks.end())
|
if (chunkIt == m_Chunks.end())
|
||||||
@@ -36,6 +37,7 @@ bool World::loadMap(const protocol::WorldBeginDataPacket* worldHeader) {
|
|||||||
m_TowerPlacePalette = worldHeader->getTowerTilePalette();
|
m_TowerPlacePalette = worldHeader->getTowerTilePalette();
|
||||||
m_WalkablePalette = worldHeader->getWalkableTileColor();
|
m_WalkablePalette = worldHeader->getWalkableTileColor();
|
||||||
m_DecorationPalette = worldHeader->getDecorationPalette();
|
m_DecorationPalette = worldHeader->getDecorationPalette();
|
||||||
|
m_Background = worldHeader->getBackgroundColor();
|
||||||
|
|
||||||
getRedTeam().getSpawn() = worldHeader->getRedSpawn();
|
getRedTeam().getSpawn() = worldHeader->getRedSpawn();
|
||||||
getBlueTeam().getSpawn() = worldHeader->getBlueSpawn();
|
getBlueTeam().getSpawn() = worldHeader->getBlueSpawn();
|
||||||
@@ -43,7 +45,10 @@ bool World::loadMap(const protocol::WorldBeginDataPacket* worldHeader) {
|
|||||||
m_SpawnColorPalette = worldHeader->getSpawnPalette();
|
m_SpawnColorPalette = worldHeader->getSpawnPalette();
|
||||||
|
|
||||||
getRedTeam().getCastle() = worldHeader->getRedCastle();
|
getRedTeam().getCastle() = worldHeader->getRedCastle();
|
||||||
|
getRedTeam().getCastle().setTeam(&getRedTeam());
|
||||||
|
|
||||||
getBlueTeam().getCastle() = worldHeader->getBlueCastle();
|
getBlueTeam().getCastle() = worldHeader->getBlueCastle();
|
||||||
|
getBlueTeam().getCastle().setTeam(&getBlueTeam());
|
||||||
|
|
||||||
m_TilePalette = worldHeader->getTilePalette();
|
m_TilePalette = worldHeader->getTilePalette();
|
||||||
|
|
||||||
@@ -101,7 +106,8 @@ bool World::saveMap(const std::string& fileName) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void World::tick(std::uint64_t delta) {
|
void World::tick(std::uint64_t delta) {
|
||||||
moveMobs(delta);
|
if (m_Game->getGameState() != GameState::Game) return;
|
||||||
|
|
||||||
tickMobs(delta);
|
tickMobs(delta);
|
||||||
for (TowerPtr tower : m_Towers) {
|
for (TowerPtr tower : m_Towers) {
|
||||||
tower->tick(delta, this);
|
tower->tick(delta, this);
|
||||||
@@ -111,10 +117,10 @@ void World::tick(std::uint64_t delta) {
|
|||||||
|
|
||||||
void World::spawnMobAt(MobID id, MobType type, std::uint8_t level, PlayerID sender, float x, float y, Direction dir) {
|
void World::spawnMobAt(MobID id, MobType type, std::uint8_t level, PlayerID sender, float x, float y, Direction dir) {
|
||||||
MobPtr mob = MobFactory::createMob(id, type, level, sender);
|
MobPtr mob = MobFactory::createMob(id, type, level, sender);
|
||||||
mob->setX(x);
|
mob->setCenter({ x, y });
|
||||||
mob->setY(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) {
|
||||||
@@ -123,46 +129,20 @@ TowerPtr World::placeTowerAt(TowerID id, TowerType type, std::int32_t x, std::in
|
|||||||
return tower;
|
return tower;
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::tickMobs(std::uint64_t delta) {
|
TowerPtr World::removeTower(TowerID towerId) {
|
||||||
for (MobPtr mob : m_Mobs) {
|
auto it = std::find_if(m_Towers.begin(), m_Towers.end(), [towerId](TowerPtr tower) { return tower->getID() == towerId;});
|
||||||
mob->tick(delta);
|
if (it == m_Towers.end()) return nullptr;
|
||||||
}
|
|
||||||
|
TowerPtr tower = *it;
|
||||||
|
|
||||||
|
m_Towers.erase(it);
|
||||||
|
|
||||||
|
return tower;
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::moveMobs(std::uint64_t delta) {
|
void World::tickMobs(std::uint64_t delta) {
|
||||||
for (MobPtr mob : m_Mobs) {
|
for (MobPtr mob : m_Mobs) {
|
||||||
TilePtr tile = getTile(mob->getX(), mob->getY());
|
mob->tick(delta, this);
|
||||||
|
|
||||||
if (tile != nullptr && tile->getType() == TileType::Walk) {
|
|
||||||
WalkableTile* walkTile = dynamic_cast<WalkableTile*>(tile.get());
|
|
||||||
mob->setDirection(walkTile->direction);
|
|
||||||
}
|
|
||||||
|
|
||||||
float mobWalkSpeed = mob->getStats()->getMovementSpeed();
|
|
||||||
|
|
||||||
float walkAmount = mobWalkSpeed * ((float)delta / 1000.0f);
|
|
||||||
|
|
||||||
if (mob->hasEffect(EffectType::Slowness))
|
|
||||||
walkAmount *= 0.70;
|
|
||||||
|
|
||||||
switch (mob->getDirection()) {
|
|
||||||
case Direction::NegativeX: {
|
|
||||||
mob->setX(mob->getX() - walkAmount);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Direction::PositiveX: {
|
|
||||||
mob->setX(mob->getX() + walkAmount);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Direction::NegativeY: {
|
|
||||||
mob->setY(mob->getY() - walkAmount);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Direction::PositiveY: {
|
|
||||||
mob->setY(mob->getY() + walkAmount);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,18 +222,10 @@ bool World::CanPlaceBigTower(const glm::vec2& worldPos, PlayerID playerID) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
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()) {
|
||||||
mob->OnDeath(this);
|
|
||||||
|
|
||||||
//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);
|
m_Mobs.erase(m_Mobs.begin() + i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -262,13 +234,13 @@ void World::cleanDeadMobs() {
|
|||||||
TowerPtr World::getTower(const glm::vec2& position) const {
|
TowerPtr World::getTower(const glm::vec2& position) const {
|
||||||
for (TowerPtr tower : m_Towers) {
|
for (TowerPtr tower : m_Towers) {
|
||||||
if (tower->getSize() == TowerSize::Big) {
|
if (tower->getSize() == TowerSize::Big) {
|
||||||
if (tower->getX() - 2 <= static_cast<std::int32_t>(position.x) && tower->getX() + 2 >= static_cast<std::int32_t>(position.x) &&
|
if (tower->getCenterX() - 2.5f < position.x && tower->getCenterX() + 2.5f > position.x &&
|
||||||
tower->getY() - 2 <= static_cast<std::int32_t>(position.y) && tower->getY() + 2 >= static_cast<std::int32_t>(position.y)) {
|
tower->getCenterY() - 2.5f < position.y && tower->getCenterY() + 2.5f > position.y) {
|
||||||
return tower;
|
return tower;
|
||||||
}
|
}
|
||||||
} else {
|
} else { // little tower
|
||||||
if (tower->getX() - 1 <= static_cast<std::int32_t>(position.x) && tower->getX() + 1 >= static_cast<std::int32_t>(position.x) &&
|
if (tower->getCenterX() - 1.5f < position.x && tower->getCenterX() + 1.5f > position.x &&
|
||||||
tower->getY() - 1 <= static_cast<std::int32_t>(position.y) && tower->getY() + 1 >= static_cast<std::int32_t>(position.y)) {
|
tower->getCenterY() - 1.5f < position.y && tower->getCenterY() + 1.5f > position.y) {
|
||||||
return tower;
|
return tower;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -278,16 +250,48 @@ TowerPtr World::getTower(const glm::vec2& position) const {
|
|||||||
|
|
||||||
TowerPtr World::getTowerById(TowerID towerID) {
|
TowerPtr World::getTowerById(TowerID towerID) {
|
||||||
auto it = std::find_if(m_Towers.begin(), m_Towers.end(), [towerID](TowerPtr tower) { return tower->getID() == 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;
|
if (it == m_Towers.end()) return nullptr;
|
||||||
return *it;
|
return *it;
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::OnArrowShot(MobPtr target, Tower* shooter) {
|
void World::OnArcherTowerShot(MobPtr target, ArcherTower* shooter) {
|
||||||
bool explosiveArrows = shooter->getLevel().getPath() == TowerPath::Bottom;
|
bool fireArrows = shooter->getLevel().getPath() == TowerPath::Bottom;
|
||||||
|
bool explosiveArrows = shooter->getLevel().getLevel() == 4 && fireArrows;
|
||||||
|
|
||||||
|
getWorldNotifier().notifyListeners(&WorldListener::OnArrowShot, target, fireArrows, shooter);
|
||||||
if (explosiveArrows) {
|
if (explosiveArrows) {
|
||||||
// aoe damage
|
getWorldNotifier().notifyListeners(&WorldListener::OnExplosion, utils::shape::Circle{ target->getCenterX(), target->getCenterY(), ArcherTower::ExplosionRadius }, shooter->getStats()->getDamage(), shooter);
|
||||||
} else {
|
}
|
||||||
target->damage(shooter->getStats()->getDamage(), shooter);
|
}
|
||||||
|
|
||||||
|
void World::OnArrowShot(MobPtr target, bool fireArrow, Tower* 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) {
|
||||||
|
for (MobPtr mob : m_Mobs) {
|
||||||
|
if (mob->isAlive() && mob->collidesWith(explosion)) {
|
||||||
|
// linear distance damage reduction
|
||||||
|
float explosionDamage = mob->distance(explosion) / explosion.getRadius() * centerDamage;
|
||||||
|
getMobNotifier().notifyListeners(&MobListener::OnMobDamage, mob.get(), explosionDamage, shooter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void World::OnMobCastleDamage(Mob* damager, TeamCastle* enemyCastle, float damage) {
|
||||||
|
enemyCastle->damage(damage);
|
||||||
|
if (enemyCastle->getLife() <= 0) {
|
||||||
|
m_Game->notifyListeners(&GameListener::OnGameEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void World::OnMobDamage(Mob* target, float damage, Tower* source) {
|
||||||
|
target->damage(damage, source);
|
||||||
|
if (target->isDead()) {
|
||||||
|
getMobNotifier().notifyListeners(&MobListener::OnMobDie, target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -315,5 +319,13 @@ 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
const TeamList& World::getTeams() const {
|
||||||
|
return m_Game->getTeams();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace game
|
} // namespace game
|
||||||
} // namespace td
|
} // namespace td
|
||||||
|
|||||||
@@ -40,16 +40,22 @@ void Client::tick(std::uint64_t delta) {
|
|||||||
m_Connected = m_Connexion.updateSocket();
|
m_Connected = m_Connexion.updateSocket();
|
||||||
if (!m_Connected) {
|
if (!m_Connected) {
|
||||||
std::cout << "Disconnected ! (Reason : " << m_Connexion.getDisconnectReason() << ")\n";
|
std::cout << "Disconnected ! (Reason : " << m_Connexion.getDisconnectReason() << ")\n";
|
||||||
|
reset();
|
||||||
} else {
|
} else {
|
||||||
m_Game.tick(delta);
|
m_Game->tick(delta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::render() {
|
void Client::render() {
|
||||||
m_Game.renderWorld();
|
m_Game->renderWorld();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::sendMobs(const std::vector<protocol::MobSend>& mobSends){
|
void Client::reset() {
|
||||||
|
m_Game.reset(0);
|
||||||
|
m_Game = std::make_unique<ClientGame>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::sendMobs(const std::vector<protocol::MobSend>& mobSends) {
|
||||||
protocol::SendMobsPacket packet(mobSends);
|
protocol::SendMobsPacket packet(mobSends);
|
||||||
m_Connexion.sendPacket(&packet);
|
m_Connexion.sendPacket(&packet);
|
||||||
}
|
}
|
||||||
@@ -59,10 +65,15 @@ void Client::placeTower(game::TowerType type, const glm::vec2& position) {
|
|||||||
m_Connexion.sendPacket(&packet);
|
m_Connexion.sendPacket(&packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::upgradeTower(game::TowerID tower, game::TowerLevel level){
|
void Client::upgradeTower(game::TowerID tower, game::TowerLevel level) {
|
||||||
protocol::UpgradeTowerPacket packet(tower, level);
|
protocol::UpgradeTowerPacket packet(tower, 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
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
namespace td {
|
namespace td {
|
||||||
namespace client {
|
namespace client {
|
||||||
|
|
||||||
ClientConnexion::ClientConnexion() : Connexion(&m_Dispatcher) {
|
ClientConnexion::ClientConnexion() : Connexion(&m_Dispatcher), m_ServerTPS(0) {
|
||||||
registerHandlers();
|
registerHandlers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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() {
|
||||||
@@ -97,13 +93,14 @@ void ClientGame::HandlePacket(const protocol::UpdateMoneyPacket* packet) {
|
|||||||
|
|
||||||
void ClientGame::HandlePacket(const protocol::DisconnectPacket* packet) {
|
void ClientGame::HandlePacket(const protocol::DisconnectPacket* packet) {
|
||||||
m_GameState = game::GameState::Disconnected;
|
m_GameState = game::GameState::Disconnected;
|
||||||
|
m_Renderer->setBackgroundColor({ 0, 0, 0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientGame::HandlePacket(const protocol::WorldDataPacket* packet) {
|
void ClientGame::HandlePacket(const protocol::WorldDataPacket* packet) {
|
||||||
m_WorldRenderer.loadModels();
|
m_WorldRenderer.loadModels();
|
||||||
// set cam pos to player spawn
|
// set cam pos to player spawn
|
||||||
const game::Spawn& spawn = m_World->getTeam(m_Player->getTeamColor()).getSpawn();
|
const game::Spawn& spawn = m_World->getTeam(m_Player->getTeamColor()).getSpawn();
|
||||||
m_WorldRenderer.setCamPos(spawn.x + 0.5, spawn.y + 0.5);
|
m_WorldRenderer.setCamPos(spawn.getCenterX(), spawn.getCenterY());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientGame::renderWorld() {
|
void ClientGame::renderWorld() {
|
||||||
@@ -112,9 +109,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
|
||||||
|
|||||||
@@ -7,14 +7,21 @@ 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) {
|
||||||
loadMap(packet);
|
loadMap(packet);
|
||||||
|
if (m_Game->getGameState() == game::GameState::Game) {
|
||||||
|
const game::Color& backgroundColor = getBackgroundColor();
|
||||||
|
m_Game->getRenderer()->setBackgroundColor({ static_cast<float>(backgroundColor.r / 255.0f), static_cast<float>(backgroundColor.g / 255.0f),
|
||||||
|
static_cast<float>(backgroundColor.b / 255.0f) });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldClient::HandlePacket(const protocol::WorldDataPacket* packet) {
|
void WorldClient::HandlePacket(const protocol::WorldDataPacket* packet) {
|
||||||
@@ -28,12 +35,22 @@ void WorldClient::HandlePacket(const protocol::SpawnMobPacket* packet) {
|
|||||||
|
|
||||||
void WorldClient::HandlePacket(const protocol::UpgradeTowerPacket* packet) {
|
void WorldClient::HandlePacket(const protocol::UpgradeTowerPacket* packet) {
|
||||||
game::TowerPtr tower = getTowerById(packet->getTowerID());
|
game::TowerPtr tower = getTowerById(packet->getTowerID());
|
||||||
if(tower == nullptr) return; // this should not happen but who knows ?
|
if (tower == nullptr) return; // this should not happen but who knows ?
|
||||||
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
|
||||||
|
|||||||
@@ -37,10 +37,8 @@ void Lobby::tick() {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (utils::getTime() - m_StartTimerTime >= LobbyWaitingTime) {
|
if (utils::getTime() - m_StartTimerTime >= LobbyWaitingTime) {
|
||||||
protocol::UpdateGameStatePacket packet(game::GameState::Game);
|
m_Server->getGame().notifyListeners(&game::GameListener::OnGameBegin);
|
||||||
m_Server->broadcastPacket(&packet);
|
|
||||||
m_GameStarted = true;
|
m_GameStarted = true;
|
||||||
m_Server->lauchGame();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,9 @@ Server::Server(const std::string& worldFilePath) : m_ServerRunning(false) {
|
|||||||
m_Game.getWorld()->loadMapFromFile(worldFilePath);
|
m_Game.getWorld()->loadMapFromFile(worldFilePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::lauchGame() {
|
Server::~Server() {
|
||||||
m_Game.startGame();
|
if (m_Thread.joinable())
|
||||||
|
m_Thread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::startThread() {
|
void Server::startThread() {
|
||||||
@@ -29,13 +30,16 @@ void Server::startThread() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
clean();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::stopThread(){
|
void Server::close() {
|
||||||
|
stopThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::stopThread() {
|
||||||
m_ServerRunning = false;
|
m_ServerRunning = false;
|
||||||
if(m_Thread.joinable())
|
|
||||||
m_Thread.join();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Server::start(std::uint16_t port) {
|
bool Server::start(std::uint16_t port) {
|
||||||
@@ -54,19 +58,24 @@ bool Server::start(std::uint16_t port) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::stop() {
|
void Server::clean() {
|
||||||
if(!m_ServerRunning)
|
|
||||||
return;
|
|
||||||
stopThread();
|
|
||||||
|
|
||||||
protocol::DisconnectPacket packet("Server closed");
|
|
||||||
broadcastPacket(&packet);
|
|
||||||
|
|
||||||
m_Listener.close();
|
m_Listener.close();
|
||||||
m_Listener.destroy();
|
m_Listener.destroy();
|
||||||
|
|
||||||
m_Connections.clear();
|
m_Connections.clear();
|
||||||
getPlayers().clear();
|
getPlayers().clear();
|
||||||
|
|
||||||
|
std::cout << "Server successfully stopped !\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::stop() {
|
||||||
|
if (!m_ServerRunning)
|
||||||
|
return;
|
||||||
|
|
||||||
|
protocol::DisconnectPacket packet("Server closed");
|
||||||
|
broadcastPacket(&packet);
|
||||||
|
|
||||||
|
stopThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::tick(std::uint64_t delta) {
|
void Server::tick(std::uint64_t delta) {
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
@@ -168,12 +171,18 @@ void ServerConnexion::HandlePacket(const protocol::SendMobsPacket* packet) {
|
|||||||
|
|
||||||
//TODO: verify the packet
|
//TODO: verify the packet
|
||||||
|
|
||||||
for(protocol::MobSend mobSend : mobSent){
|
for (protocol::MobSend mobSend : mobSent) {
|
||||||
m_Server->getGame().getServerWorld()->spawnMobs(mobSend.mobType, mobSend.mobLevel, m_ID, mobSend.mobCount);
|
m_Server->getGame().getServerWorld()->spawnMobs(mobSend.mobType, mobSend.mobLevel, m_ID, mobSend.mobCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerConnexion::HandlePacket(const protocol::UpgradeTowerPacket* packet){
|
void ServerConnexion::HandlePacket(const protocol::UpgradeTowerPacket* packet) {
|
||||||
|
//TODO: verify the packet
|
||||||
|
|
||||||
|
m_Server->broadcastPacket(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerConnexion::HandlePacket(const protocol::RemoveTowerPacket* packet) {
|
||||||
//TODO: verify the packet
|
//TODO: verify the packet
|
||||||
|
|
||||||
m_Server->broadcastPacket(packet);
|
m_Server->broadcastPacket(packet);
|
||||||
|
|||||||
@@ -5,13 +5,17 @@ namespace td {
|
|||||||
namespace server {
|
namespace server {
|
||||||
|
|
||||||
ServerGame::ServerGame(server::Server* server) : game::Game(&m_ServerWorld), m_Server(server), m_ServerWorld(server, this) {
|
ServerGame::ServerGame(server::Server* server) : game::Game(&m_ServerWorld), m_Server(server), m_ServerWorld(server, this) {
|
||||||
|
bindListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerGame::tick(std::uint64_t delta) {
|
void ServerGame::tick(std::uint64_t delta) {
|
||||||
if(m_GameState == game::GameState::Game){
|
if (m_GameState == game::GameState::Game) {
|
||||||
Game::tick(delta);
|
Game::tick(delta);
|
||||||
updatePlayerStats();
|
updatePlayerStats();
|
||||||
|
} else if (m_GameState == game::GameState::EndGame) {
|
||||||
|
if (m_EndGameCooldown.update(delta)) {
|
||||||
|
notifyListeners(&game::GameListener::OnGameClose);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,16 +31,16 @@ void ServerGame::startGame() {
|
|||||||
m_GameState = game::GameState::Game;
|
m_GameState = game::GameState::Game;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerGame::updatePlayerStats(){
|
void ServerGame::updatePlayerStats() {
|
||||||
m_GoldMineTimer.update();
|
m_GoldMineTimer.update();
|
||||||
for (auto& pair : m_Server->getPlayers()) {
|
for (auto& pair : m_Server->getPlayers()) {
|
||||||
game::Player& player = pair.second;
|
game::Player& player = pair.second;
|
||||||
if(player.hasGoldChanged()){
|
if (player.hasGoldChanged()) {
|
||||||
protocol::UpdateMoneyPacket packet(player.getGold());
|
protocol::UpdateMoneyPacket packet(player.getGold());
|
||||||
m_Server->getConnexions()[player.getID()].sendPacket(&packet);
|
m_Server->getConnexions()[player.getID()].sendPacket(&packet);
|
||||||
player.updateGold();
|
player.updateGold();
|
||||||
}
|
}
|
||||||
if(player.hasExpChanged()){
|
if (player.hasExpChanged()) {
|
||||||
protocol::UpdateExpPacket packet(player.getExp());
|
protocol::UpdateExpPacket packet(player.getExp());
|
||||||
m_Server->getConnexions()[player.getID()].sendPacket(&packet);
|
m_Server->getConnexions()[player.getID()].sendPacket(&packet);
|
||||||
player.updateExp();
|
player.updateExp();
|
||||||
@@ -70,5 +74,31 @@ void ServerGame::balanceTeams() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ServerGame::OnGameStateUpdate(game::GameState newState) {
|
||||||
|
setGameState(newState);
|
||||||
|
protocol::UpdateGameStatePacket packet(newState);
|
||||||
|
m_Server->broadcastPacket(&packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerGame::OnGameBegin() {
|
||||||
|
notifyListeners(&game::GameListener::OnGameStateUpdate, game::GameState::Game);
|
||||||
|
startGame();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerGame::OnGameEnd() {
|
||||||
|
notifyListeners(&game::GameListener::OnGameStateUpdate, game::GameState::EndGame);
|
||||||
|
m_EndGameCooldown.applyCooldown();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerGame::OnGameClose() {
|
||||||
|
notifyListeners(&game::GameListener::OnGameStateUpdate, game::GameState::Closed);
|
||||||
|
// Disconnect clients
|
||||||
|
protocol::DisconnectPacket packet("Game finished");
|
||||||
|
m_Server->broadcastPacket(&packet);
|
||||||
|
|
||||||
|
// Closing server
|
||||||
|
m_Server->close();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace game
|
} // namespace game
|
||||||
} // namespace td
|
} // namespace td
|
||||||
|
|||||||
@@ -22,21 +22,27 @@ 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->x;
|
float spawnWidth = enemyMobSpawn->getWidth();
|
||||||
std::int32_t spawnCenterY = enemyMobSpawn->y;
|
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 mobX = utils::getRandomReal<float>(minSpawnX, maxSpawnX);
|
float minSpawnX = spawnCenterX - spawnWidth / 2.0f + mobSize.x / 2.0f;
|
||||||
float mobY = utils::getRandomReal<float>(minSpawnY, maxSpawnY);
|
float maxSpawnX = spawnCenterX + spawnWidth / 2.0f - mobSize.x / 2.0f;
|
||||||
|
|
||||||
spawnMobAt(m_CurrentMobID, type, level, sender, mobX, mobY, enemyMobSpawn->direction);
|
float minSpawnY = spawnCenterY - spawnHeight / 2.0f + mobSize.y / 2.0f;
|
||||||
|
float maxSpawnY = spawnCenterY + spawnHeight / 2.0f - mobSize.y / 2.0f;
|
||||||
|
|
||||||
protocol::SpawnMobPacket packet(m_CurrentMobID, type, level, sender, mobX, mobY, enemyMobSpawn->direction);
|
float mobX = utils::getRandomReal<float>(minSpawnX + MobSpawnBorder, maxSpawnX - MobSpawnBorder);
|
||||||
|
float mobY = utils::getRandomReal<float>(minSpawnY + MobSpawnBorder, maxSpawnY - MobSpawnBorder);
|
||||||
|
|
||||||
|
spawnMobAt(m_CurrentMobID, type, level, sender, mobX, mobY, enemyMobSpawn->getDirection());
|
||||||
|
|
||||||
|
protocol::SpawnMobPacket packet(m_CurrentMobID, type, level, sender, mobX, mobY, enemyMobSpawn->getDirection());
|
||||||
m_Server->broadcastPacket(&packet);
|
m_Server->broadcastPacket(&packet);
|
||||||
|
|
||||||
m_CurrentMobID++;
|
m_CurrentMobID++;
|
||||||
@@ -49,8 +55,15 @@ 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) {
|
void ServerWorld::OnMobDie(game::Mob* mob) {
|
||||||
World::OnArrowShot(target, shooter);
|
if (mob->OnDeath(this)) { // check if the mob is actually dead (slimes ...)
|
||||||
|
//reward players
|
||||||
|
game::Player* sender = m_Game->getPlayerById(mob->getSender());
|
||||||
|
sender->addExp(mob->getStats()->getExpReward());
|
||||||
|
|
||||||
|
game::Player* killer = m_Game->getPlayerById(mob->getLastDamageTower()->getBuilder());
|
||||||
|
killer->addGold(mob->getStats()->getMoneyCost());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace server
|
} // namespace server
|
||||||
|
|||||||
84
src/misc/Shapes.cpp
Normal file
84
src/misc/Shapes.cpp
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
#include "misc/Shapes.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
namespace td {
|
||||||
|
namespace utils {
|
||||||
|
namespace shape {
|
||||||
|
|
||||||
|
float Point::distance(const Point& point) const {
|
||||||
|
return std::sqrt(distanceSquared(point));
|
||||||
|
}
|
||||||
|
|
||||||
|
float Point::distanceSquared(const Point& point) const {
|
||||||
|
return (m_X - point.getX()) * (m_X - point.getX()) + (m_Y - point.getY()) * (m_Y - point.getY());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Rectangle::collidesWith(const Point& point) const {
|
||||||
|
return point.getX() > getTopLeft().getX() && point.getX() < getBottomRight().getX() &&
|
||||||
|
point.getY() > getTopLeft().getY() && point.getY() < getBottomRight().getY();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Rectangle::collidesWith(const Rectangle& rect) const {
|
||||||
|
Point point1{ rect.getTopLeft().getX(), rect.getTopLeft().getY() };
|
||||||
|
Point point2{ rect.getTopLeft().getX(), rect.getBottomRight().getY() };
|
||||||
|
Point point3{ rect.getBottomRight().getX(), rect.getTopLeft().getY() };
|
||||||
|
Point point4{ rect.getBottomRight().getX(), rect.getBottomRight().getY() };
|
||||||
|
|
||||||
|
if (collidesWith(point1)) return true;
|
||||||
|
if (collidesWith(point2)) return true;
|
||||||
|
if (collidesWith(point3)) return true;
|
||||||
|
if (collidesWith(point4)) return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Rectangle::collidesWith(const Circle& circle) const {
|
||||||
|
return circle.collidesWith(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
float Rectangle::distance(const Circle& circle) const {
|
||||||
|
return circle.distance(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
float Rectangle::distanceSquared(const Circle& circle) const {
|
||||||
|
return circle.distanceSquared(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool Circle::collidesWith(const Point& point) const {
|
||||||
|
return m_Radius * m_Radius > m_Center.distanceSquared(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Circle::collidesWith(const Rectangle& rect) const {
|
||||||
|
float distanceSquared_ = distanceSquared(rect);
|
||||||
|
|
||||||
|
return distanceSquared_ < m_Radius* m_Radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Circle::collidesWith(const Circle& circle) const {
|
||||||
|
return m_Radius + circle.getRadius() > m_Center.distanceSquared(circle.getCenter());
|
||||||
|
}
|
||||||
|
|
||||||
|
float Circle::distance(const Rectangle& rect) const {
|
||||||
|
return std::sqrt(distanceSquared(rect));
|
||||||
|
}
|
||||||
|
|
||||||
|
float Circle::distanceSquared(const Rectangle& rect) const {
|
||||||
|
float closestX = std::clamp(m_Center.getX(), rect.getTopLeft().getX(), rect.getBottomRight().getX());
|
||||||
|
float closestY = std::clamp(m_Center.getY(), rect.getTopLeft().getY(), rect.getBottomRight().getY());
|
||||||
|
|
||||||
|
float distanceX = m_Center.getX() - closestX;
|
||||||
|
float distanceY = m_Center.getY() - closestY;
|
||||||
|
|
||||||
|
return (distanceX * distanceX) + (distanceY * distanceY);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace shape
|
||||||
|
} // namespace utils
|
||||||
|
} // namespace td
|
||||||
@@ -55,7 +55,7 @@ void CooldownTimer::reset() {
|
|||||||
m_Cooldown = 0; // let the timer active once at the beginning
|
m_Cooldown = 0; // let the timer active once at the beginning
|
||||||
}
|
}
|
||||||
|
|
||||||
void CooldownTimer::applyCooldown(){
|
void CooldownTimer::applyCooldown() {
|
||||||
m_Cooldown = m_CooldownTime;
|
m_Cooldown = m_CooldownTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace td {
|
|||||||
namespace protocol {
|
namespace protocol {
|
||||||
namespace PacketFactory {
|
namespace PacketFactory {
|
||||||
|
|
||||||
using PacketCreator = std::function<PacketPtr ()>;
|
using PacketCreator = std::function<PacketPtr()>;
|
||||||
|
|
||||||
static std::map<PacketType, PacketCreator> packets = {
|
static std::map<PacketType, PacketCreator> packets = {
|
||||||
{PacketType::PlayerLogin, []() -> PacketPtr {return std::make_unique<PlayerLoginPacket>();} },
|
{PacketType::PlayerLogin, []() -> PacketPtr {return std::make_unique<PlayerLoginPacket>();} },
|
||||||
@@ -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>(); } },
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -91,14 +91,16 @@ DataBuffer WorldBeginDataPacket::SerializeCustom() const {
|
|||||||
|
|
||||||
memcpy((std::uint8_t*)data.data() + bufferSize, decoTilePalette.data(), decoTilePalette.size() * sizeof(game::Color));
|
memcpy((std::uint8_t*)data.data() + bufferSize, decoTilePalette.data(), decoTilePalette.size() * sizeof(game::Color));
|
||||||
|
|
||||||
|
data << m_Header.m_Background;
|
||||||
|
|
||||||
const game::Spawn& redSpawn = m_Header.m_RedSpawn, blueSpawn = m_Header.m_BlueSpawn;
|
const game::Spawn& redSpawn = m_Header.m_RedSpawn, blueSpawn = m_Header.m_BlueSpawn;
|
||||||
const game::TeamCastle& redCastle = m_Header.m_RedCastle, blueCastle = m_Header.m_BlueCastle;
|
const game::TeamCastle& redCastle = m_Header.m_RedCastle, blueCastle = m_Header.m_BlueCastle;
|
||||||
|
|
||||||
data << redSpawn << redCastle;
|
data << redSpawn << static_cast<utils::shape::Rectangle>(redCastle);
|
||||||
data << blueSpawn << blueCastle;
|
data << blueSpawn << static_cast<utils::shape::Rectangle>(blueCastle);
|
||||||
|
|
||||||
// tile palette
|
// tile palette
|
||||||
data << m_Header.m_TilePalette.size();
|
data << static_cast<std::uint64_t>(m_Header.m_TilePalette.size());
|
||||||
|
|
||||||
for (game::TilePtr tile : m_Header.m_TilePalette) {
|
for (game::TilePtr tile : m_Header.m_TilePalette) {
|
||||||
data << tile;
|
data << tile;
|
||||||
@@ -123,14 +125,16 @@ DataBuffer WorldBeginDataPacket::Serialize() const {
|
|||||||
|
|
||||||
memcpy((std::uint8_t*)data.data() + bufferSize, decoTilePalette.data(), decoTilePalette.size() * sizeof(game::Color));
|
memcpy((std::uint8_t*)data.data() + bufferSize, decoTilePalette.data(), decoTilePalette.size() * sizeof(game::Color));
|
||||||
|
|
||||||
|
data << m_Header.m_World->getBackgroundColor();
|
||||||
|
|
||||||
const game::Spawn& redSpawn = m_Header.m_World->getRedTeam().getSpawn(), blueSpawn = m_Header.m_World->getBlueTeam().getSpawn();
|
const game::Spawn& redSpawn = m_Header.m_World->getRedTeam().getSpawn(), blueSpawn = m_Header.m_World->getBlueTeam().getSpawn();
|
||||||
const game::TeamCastle& redCastle = m_Header.m_World->getRedTeam().getCastle(), blueCastle = m_Header.m_World->getBlueTeam().getCastle();
|
const game::TeamCastle& redCastle = m_Header.m_World->getRedTeam().getCastle(), blueCastle = m_Header.m_World->getBlueTeam().getCastle();
|
||||||
|
|
||||||
data << redSpawn << redCastle;
|
data << redSpawn << static_cast<utils::shape::Rectangle>(redCastle);
|
||||||
data << blueSpawn << blueCastle;
|
data << blueSpawn << static_cast<utils::shape::Rectangle>(blueCastle);
|
||||||
|
|
||||||
// tile palette
|
// tile palette
|
||||||
data << m_Header.m_World->getTilePalette().size();
|
data << static_cast<std::uint64_t>(m_Header.m_World->getTilePalette().size());
|
||||||
|
|
||||||
for (game::TilePtr tile : m_Header.m_World->getTilePalette()) {
|
for (game::TilePtr tile : m_Header.m_World->getTilePalette()) {
|
||||||
data << tile;
|
data << tile;
|
||||||
@@ -155,8 +159,15 @@ void WorldBeginDataPacket::Deserialize(DataBuffer& data) {
|
|||||||
|
|
||||||
data.SetReadOffset(data.GetReadOffset() + decoPalletteSizeByte);
|
data.SetReadOffset(data.GetReadOffset() + decoPalletteSizeByte);
|
||||||
|
|
||||||
data >> m_Header.m_RedSpawn >> m_Header.m_RedCastle;
|
data >> m_Header.m_Background;
|
||||||
data >> m_Header.m_BlueSpawn >> m_Header.m_BlueCastle;
|
|
||||||
|
utils::shape::Rectangle redCastle, blueCastle;
|
||||||
|
|
||||||
|
data >> m_Header.m_RedSpawn >> redCastle;
|
||||||
|
data >> m_Header.m_BlueSpawn >> blueCastle;
|
||||||
|
|
||||||
|
m_Header.m_RedCastle.setShape(redCastle);
|
||||||
|
m_Header.m_BlueCastle.setShape(blueCastle);
|
||||||
|
|
||||||
std::uint64_t tilePaletteSize;
|
std::uint64_t tilePaletteSize;
|
||||||
data >> tilePaletteSize;
|
data >> tilePaletteSize;
|
||||||
@@ -485,20 +496,20 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
DataBuffer SendMobsPacket::Serialize() const {
|
DataBuffer SendMobsPacket::Serialize() const {
|
||||||
DataBuffer data;
|
DataBuffer data;
|
||||||
data << getID() << static_cast<std::uint8_t>(m_MobSends.size());
|
data << getID() << static_cast<std::uint8_t>(m_MobSends.size());
|
||||||
for(const MobSend& mobSend : m_MobSends){
|
for (const MobSend& mobSend : m_MobSends) {
|
||||||
data << mobSend;
|
data << mobSend;
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
@@ -508,7 +519,7 @@ void SendMobsPacket::Deserialize(DataBuffer& data) {
|
|||||||
std::uint8_t mobSendCount;
|
std::uint8_t mobSendCount;
|
||||||
data >> mobSendCount;
|
data >> mobSendCount;
|
||||||
protocol::MobSend mobSend;
|
protocol::MobSend mobSend;
|
||||||
for(int i = 0; i < mobSendCount; i++){
|
for (int i = 0; i < mobSendCount; i++) {
|
||||||
data >> mobSend;
|
data >> mobSend;
|
||||||
m_MobSends.push_back(mobSend);
|
m_MobSends.push_back(mobSend);
|
||||||
}
|
}
|
||||||
@@ -543,7 +554,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);
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
namespace td {
|
namespace td {
|
||||||
namespace render {
|
namespace render {
|
||||||
|
|
||||||
Renderer::Renderer() {
|
Renderer::Renderer() : m_BackgroundColor(0, 0, 0) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,9 +43,9 @@ void Renderer::initShader() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Renderer::init() {
|
bool Renderer::init() {
|
||||||
#ifndef ANDROID
|
#ifndef ANDROID
|
||||||
glbinding::Binding::initialize();
|
glbinding::Binding::initialize();
|
||||||
#endif
|
#endif
|
||||||
glEnable(GL_TEXTURE_2D);
|
glEnable(GL_TEXTURE_2D);
|
||||||
glEnable(GL_BLEND);
|
glEnable(GL_BLEND);
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
@@ -86,7 +86,7 @@ void Renderer::updateIsometricFade() {
|
|||||||
|
|
||||||
void Renderer::prepare() {
|
void Renderer::prepare() {
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
glClearColor(0, 0, 0, 0);
|
glClearColor(m_BackgroundColor.r, m_BackgroundColor.g, m_BackgroundColor.b, 0);
|
||||||
updateIsometricFade();
|
updateIsometricFade();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ 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_CastleTooltip = std::make_unique<gui::CastleTooltip>(m_Client->getClient());
|
||||||
|
m_Client->getWorldClient().getWorldNotifier().bindListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldRenderer::updateCursorPos() {
|
void WorldRenderer::updateCursorPos() {
|
||||||
@@ -64,6 +66,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 {
|
||||||
@@ -74,7 +88,7 @@ void WorldRenderer::renderMobs() const {
|
|||||||
for (game::MobPtr mob : m_World->getMobList()) {
|
for (game::MobPtr mob : m_World->getMobList()) {
|
||||||
Renderer::Model model;
|
Renderer::Model model;
|
||||||
model.vao = m_MobVao.get();
|
model.vao = m_MobVao.get();
|
||||||
model.positon = { mob->getX(), mob->getY() };
|
model.positon = { mob->getCenterX(), mob->getCenterY() };
|
||||||
m_Renderer->renderModel(model);
|
m_Renderer->renderModel(model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,7 +99,9 @@ void WorldRenderer::renderTowers() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WorldRenderer::renderTileSelect() const {
|
void WorldRenderer::renderTileSelect() const {
|
||||||
if(ImGui::IsAnyItemHovered()) return;
|
if (ImGui::IsAnyItemHovered()) return;
|
||||||
|
|
||||||
|
if(m_MobTooltip->isShown() || m_CastleTooltip->isShown()) return;
|
||||||
|
|
||||||
Renderer::Model tileSelectModel;
|
Renderer::Model tileSelectModel;
|
||||||
tileSelectModel.vao = m_SelectTileVao.get();
|
tileSelectModel.vao = m_SelectTileVao.get();
|
||||||
@@ -106,7 +122,7 @@ void WorldRenderer::render() {
|
|||||||
renderMobs();
|
renderMobs();
|
||||||
renderTowers();
|
renderTowers();
|
||||||
renderTileSelect();
|
renderTileSelect();
|
||||||
renderMobTooltip();
|
renderTooltips();
|
||||||
renderPopups();
|
renderPopups();
|
||||||
detectClick();
|
detectClick();
|
||||||
}
|
}
|
||||||
@@ -115,6 +131,11 @@ WorldRenderer::~WorldRenderer() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WorldRenderer::renderTooltips() const {
|
||||||
|
renderMobTooltip();
|
||||||
|
renderCastleTooltip();
|
||||||
|
}
|
||||||
|
|
||||||
void WorldRenderer::moveCam(float relativeX, float relativeY, float aspectRatio) {
|
void WorldRenderer::moveCam(float relativeX, float relativeY, float aspectRatio) {
|
||||||
if (m_WorldVao == nullptr)
|
if (m_WorldVao == nullptr)
|
||||||
return;
|
return;
|
||||||
@@ -180,8 +201,8 @@ void WorldRenderer::renderTowerUpgradePopup() {
|
|||||||
bool alreadyUpgraded = currentLevel <= towerLevel;
|
bool alreadyUpgraded = currentLevel <= towerLevel;
|
||||||
bool canUpgrade = (towerLevel + 1) == currentLevel;
|
bool canUpgrade = (towerLevel + 1) == currentLevel;
|
||||||
|
|
||||||
if(canUpgrade && towerPath != game::TowerPath::Base){
|
if (canUpgrade && towerPath != game::TowerPath::Base) {
|
||||||
if(currentPath != towerPath){
|
if (currentPath != towerPath) {
|
||||||
canUpgrade = false;
|
canUpgrade = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -199,7 +220,7 @@ void WorldRenderer::renderTowerUpgradePopup() {
|
|||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
} else if (canUpgrade) {
|
} else if (canUpgrade) {
|
||||||
if (ImGui::Button("Upgrade", ImVec2(100, 100))) {
|
if (ImGui::Button("Upgrade", ImVec2(100, 100))) {
|
||||||
m_Client->getClient()->upgradeTower(tower->getID(), {currentLevel, currentPath});
|
m_Client->getClient()->upgradeTower(tower->getID(), { currentLevel, currentPath });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ImGui::BeginDisabled();
|
ImGui::BeginDisabled();
|
||||||
@@ -226,20 +247,23 @@ void WorldRenderer::detectClick() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WorldRenderer::renderMobTooltip() const {
|
void WorldRenderer::renderMobTooltip() const {
|
||||||
if(ImGui::IsAnyItemHovered()) return;
|
if (ImGui::IsAnyItemHovered()) return;
|
||||||
|
|
||||||
detectMobHovering();
|
detectMobHovering();
|
||||||
m_MobTooltip->render();
|
m_MobTooltip->render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WorldRenderer::renderCastleTooltip() const {
|
||||||
|
if (ImGui::IsAnyItemHovered()) return;
|
||||||
|
|
||||||
|
detectCastleHovering();
|
||||||
|
m_CastleTooltip->render();
|
||||||
|
}
|
||||||
|
|
||||||
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()) {
|
||||||
// mob size is currently 1x1 for all mobs
|
if (mob->collidesWith({ cursorWorldPos.x, cursorWorldPos.y })) {
|
||||||
float mobCenterX = mob->getX();
|
|
||||||
float mobCenterY = mob->getY();
|
|
||||||
if (cursorWorldPos.x > mobCenterX - 0.5f && cursorWorldPos.x < mobCenterX + 0.5f
|
|
||||||
&& cursorWorldPos.y > mobCenterY - 0.5f && cursorWorldPos.y < mobCenterY + 0.5f) {
|
|
||||||
m_MobTooltip->setMob(mob.get());
|
m_MobTooltip->setMob(mob.get());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -247,13 +271,24 @@ void WorldRenderer::detectMobHovering() const {
|
|||||||
m_MobTooltip->setMob(nullptr);
|
m_MobTooltip->setMob(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldRenderer::addTower(game::TowerPtr tower) {
|
void WorldRenderer::detectCastleHovering() const {
|
||||||
const WorldLoader::RenderData& renderData = WorldLoader::loadTowerModel(tower);
|
glm::vec2 cursorWorldPos = getCursorWorldPos();
|
||||||
|
for (const game::Team& team : m_World->getTeams()) {
|
||||||
|
if (team.getCastle().collidesWith({ cursorWorldPos.x, cursorWorldPos.y })) {
|
||||||
|
m_CastleTooltip->setCastle(&team.getCastle());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_CastleTooltip->setCastle(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldRenderer::OnTowerAdd(game::TowerPtr 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();
|
||||||
}
|
}
|
||||||
|
|||||||
27
src/render/gui/CastleTooltip.cpp
Normal file
27
src/render/gui/CastleTooltip.cpp
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#include "render/gui/CastleTooltip.h"
|
||||||
|
#include "render/gui/imgui/imgui.h"
|
||||||
|
|
||||||
|
#include "render/WorldRenderer.h"
|
||||||
|
|
||||||
|
#include "game/client/Client.h"
|
||||||
|
|
||||||
|
namespace td {
|
||||||
|
namespace gui {
|
||||||
|
|
||||||
|
CastleTooltip::CastleTooltip(client::Client* client) : GuiWidget(client) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void CastleTooltip::render() {
|
||||||
|
if (m_Castle == nullptr) return;
|
||||||
|
|
||||||
|
ImGui::BeginTooltip();
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, render::WorldRenderer::getImGuiTeamColor(m_Castle->getTeam()->getColor()));
|
||||||
|
ImGui::Text("Castle : ");
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
ImGui::Text("\tCastle HP : %i/%i", static_cast<int>(m_Castle->getLife()), game::TeamCastle::CastleMaxLife);
|
||||||
|
ImGui::EndTooltip();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace gui
|
||||||
|
} // namespace td
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
namespace td {
|
namespace td {
|
||||||
namespace gui {
|
namespace gui {
|
||||||
|
|
||||||
FrameMenu::FrameMenu(client::Client* client) : GuiWidget(client), m_VSync(true), m_IsometricView(true), m_ShowDemoWindow(false){
|
FrameMenu::FrameMenu(client::Client* client) : GuiWidget(client), m_VSync(true), m_IsometricView(true), m_ShowDemoWindow(false) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ void FrameMenu::render() {
|
|||||||
ImGui::Checkbox("Demo Window", &m_ShowDemoWindow);
|
ImGui::Checkbox("Demo Window", &m_ShowDemoWindow);
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
|
|
||||||
if(m_ShowDemoWindow)
|
if (m_ShowDemoWindow)
|
||||||
ImGui::ShowDemoWindow(&m_ShowDemoWindow);
|
ImGui::ShowDemoWindow(&m_ShowDemoWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ GameMenu::GameMenu(client::Client* client) : GuiWidget(client), m_SummonMenu(std
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameMenu::render(){
|
void GameMenu::render() {
|
||||||
|
if(!m_Client->isConnected()) return;
|
||||||
|
|
||||||
if (getClient()->getGame().getGameState() == td::game::GameState::Lobby) {
|
if (getClient()->getGame().getGameState() == td::game::GameState::Lobby) {
|
||||||
ImGui::Begin("Lobby");
|
ImGui::Begin("Lobby");
|
||||||
|
|
||||||
|
|||||||
@@ -12,12 +12,18 @@ MainMenu::MainMenu(client::Client* client) : GuiWidget(client), m_ConnectPort(25
|
|||||||
m_ConnectAddress.reserve(256);
|
m_ConnectAddress.reserve(256);
|
||||||
}
|
}
|
||||||
|
|
||||||
MainMenu::~MainMenu(){
|
MainMenu::~MainMenu() {
|
||||||
if(m_Server != nullptr)
|
if (m_Server != nullptr)
|
||||||
m_Server->stop();
|
m_Server->stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainMenu::render() {
|
void MainMenu::render() {
|
||||||
|
if (m_Server != nullptr && !m_Server->isRunning()) {
|
||||||
|
m_Server.reset(0); // destroying server if it stoped
|
||||||
|
}
|
||||||
|
|
||||||
|
if(m_Client->isConnected()) return;
|
||||||
|
|
||||||
ImGui::Begin("Main Menu");
|
ImGui::Begin("Main Menu");
|
||||||
if (ImGui::Button("Rejoindre une partie##join")) {
|
if (ImGui::Button("Rejoindre une partie##join")) {
|
||||||
ImGui::OpenPopup("Rejoindre une partie##join_popup");
|
ImGui::OpenPopup("Rejoindre une partie##join_popup");
|
||||||
@@ -71,7 +77,7 @@ void MainMenu::render() {
|
|||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MainMenu::startServer(){
|
bool MainMenu::startServer() {
|
||||||
if (m_WorldFilePath.empty())
|
if (m_WorldFilePath.empty())
|
||||||
return false;
|
return false;
|
||||||
m_Server = std::make_unique<td::server::Server>(m_WorldFilePath);
|
m_Server = std::make_unique<td::server::Server>(m_WorldFilePath);
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -5,11 +5,11 @@
|
|||||||
namespace td {
|
namespace td {
|
||||||
namespace gui {
|
namespace gui {
|
||||||
|
|
||||||
SummonMenu::SummonMenu(client::Client* client) : GuiWidget(client), m_MenuOpened(true){
|
SummonMenu::SummonMenu(client::Client* client) : GuiWidget(client), m_MenuOpened(true) {
|
||||||
m_Values.fill(0);
|
m_Values.fill(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SummonMenu::render(){
|
void SummonMenu::render() {
|
||||||
if (m_MenuOpened) {
|
if (m_MenuOpened) {
|
||||||
ImGui::Begin("Summon", &m_MenuOpened);
|
ImGui::Begin("Summon", &m_MenuOpened);
|
||||||
ImTextureID my_tex_id = ImGui::GetIO().Fonts->TexID;
|
ImTextureID my_tex_id = ImGui::GetIO().Fonts->TexID;
|
||||||
@@ -51,8 +51,8 @@ void SummonMenu::render(){
|
|||||||
if (ImGui::Button("Send")) {
|
if (ImGui::Button("Send")) {
|
||||||
std::vector<protocol::MobSend> mobSent;
|
std::vector<protocol::MobSend> mobSent;
|
||||||
protocol::MobSend mobSend;
|
protocol::MobSend mobSend;
|
||||||
for(int i = 0; i < m_MobTypeCount; i++){
|
for (int i = 0; i < m_MobTypeCount; i++) {
|
||||||
if(m_Values[i] != 0){
|
if (m_Values[i] != 0) {
|
||||||
mobSend.mobCount = m_Values[i];
|
mobSend.mobCount = m_Values[i];
|
||||||
mobSend.mobLevel = 1; // TODO: add mob levels
|
mobSend.mobLevel = 1; // TODO: add mob levels
|
||||||
mobSend.mobType = td::game::MobType(i);
|
mobSend.mobType = td::game::MobType(i);
|
||||||
@@ -66,7 +66,7 @@ void SummonMenu::render(){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SummonMenu::setSummonMax(int valueIndex){
|
void SummonMenu::setSummonMax(int valueIndex) {
|
||||||
int& value = m_Values[valueIndex];
|
int& value = m_Values[valueIndex];
|
||||||
value = std::max(0, value);
|
value = std::max(0, value);
|
||||||
value = std::min(12, value);
|
value = std::min(12, value);
|
||||||
|
|||||||
@@ -22,14 +22,14 @@ namespace td {
|
|||||||
namespace render {
|
namespace render {
|
||||||
|
|
||||||
void TowerGui::initWidgets() {
|
void TowerGui::initWidgets() {
|
||||||
m_MainMenu = std::make_unique<td::gui::MainMenu>(m_Client.get());
|
m_GuiManager.addWidget(std::make_unique<td::gui::MainMenu>(m_Client.get()));
|
||||||
m_GameMenu = std::make_unique<td::gui::GameMenu>(m_Client.get());
|
m_GuiManager.addWidget(std::make_unique<td::gui::GameMenu>(m_Client.get()));
|
||||||
m_FrameMenu = std::make_unique<td::gui::FrameMenu>(m_Client.get());
|
m_GuiManager.addWidget(std::make_unique<td::gui::FrameMenu>(m_Client.get()));
|
||||||
m_UpdateMenu = std::make_unique<td::gui::UpdateMenu>(m_Client.get());
|
m_GuiManager.addWidget(std::make_unique<td::gui::UpdateMenu>(m_Client.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TowerGui::TowerGui(SDL_Window* sdl_window, SDL_GLContext glContext, td::render::Renderer* renderer) : m_Window(sdl_window),
|
TowerGui::TowerGui(SDL_Window* sdl_window, SDL_GLContext glContext, td::render::Renderer* renderer) : m_Window(sdl_window),
|
||||||
m_GlContext(glContext), m_Renderer(renderer), m_Client(std::make_unique<client::Client>(m_Renderer)) {
|
m_GlContext(glContext), m_Renderer(renderer), m_Client(std::make_unique<client::Client>(m_Renderer)) {
|
||||||
IMGUI_CHECKVERSION();
|
IMGUI_CHECKVERSION();
|
||||||
ImGui::CreateContext();
|
ImGui::CreateContext();
|
||||||
ImGui::StyleColorsDark();
|
ImGui::StyleColorsDark();
|
||||||
@@ -69,13 +69,8 @@ void TowerGui::render() {
|
|||||||
beginFrame();
|
beginFrame();
|
||||||
|
|
||||||
m_Client->render();
|
m_Client->render();
|
||||||
if (m_Client->isConnected())
|
|
||||||
m_GameMenu->render();
|
|
||||||
else
|
|
||||||
m_MainMenu->render();
|
|
||||||
|
|
||||||
m_FrameMenu->render();
|
m_GuiManager.renderWidgets();
|
||||||
m_UpdateMenu->render();
|
|
||||||
|
|
||||||
endFrame();
|
endFrame();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ void TowerPlacePopup::render() {
|
|||||||
ImGui::SetCursorPosY(m_TowerPopupTileHeight - m_PlaceTowerButtonHeight - 10);
|
ImGui::SetCursorPosY(m_TowerPopupTileHeight - m_PlaceTowerButtonHeight - 10);
|
||||||
ImGui::SetCursorPosX(m_TowerPopupTileWidth / 2.0f - m_PlaceTowerButtonWidth / 2.0f);
|
ImGui::SetCursorPosX(m_TowerPopupTileWidth / 2.0f - m_PlaceTowerButtonWidth / 2.0f);
|
||||||
|
|
||||||
if(ImGui::Button(buyText.c_str(), ImVec2(m_PlaceTowerButtonWidth, m_PlaceTowerButtonHeight))){
|
if (ImGui::Button(buyText.c_str(), ImVec2(m_PlaceTowerButtonWidth, m_PlaceTowerButtonHeight))) {
|
||||||
getClient()->placeTower(towerType, m_ClickWorldPos);
|
getClient()->placeTower(towerType, m_ClickWorldPos);
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,18 +20,18 @@ void UpdateMenu::render() {
|
|||||||
bool updateAvailable = m_UpdateAvailable.get();
|
bool updateAvailable = m_UpdateAvailable.get();
|
||||||
if (updateAvailable) {
|
if (updateAvailable) {
|
||||||
|
|
||||||
if(m_Updater.isFileWrited()){
|
if (m_Updater.isFileWrited()) {
|
||||||
ImGui::Text("The update is now installed");
|
ImGui::Text("The update is now installed");
|
||||||
ImGui::Text("The game needs to be restarted");
|
ImGui::Text("The game needs to be restarted");
|
||||||
}else if (m_Updater.isDownloadComplete()) {
|
} else if (m_Updater.isDownloadComplete()) {
|
||||||
ImGui::Text("Download done!");
|
ImGui::Text("Download done!");
|
||||||
if(ImGui::Button("Install")){
|
if (ImGui::Button("Install")) {
|
||||||
if(!m_Updater.writeFile()){
|
if (!m_Updater.writeFile()) {
|
||||||
m_Error = "Failed to write file !\n";
|
m_Error = "Failed to write file !\n";
|
||||||
ImGui::OpenPopup("UpdateError");
|
ImGui::OpenPopup("UpdateError");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(ImGui::Button("Cancel")){
|
if (ImGui::Button("Cancel")) {
|
||||||
m_Updater.cancelDownload();
|
m_Updater.cancelDownload();
|
||||||
m_Updater.clearCache();
|
m_Updater.clearCache();
|
||||||
}
|
}
|
||||||
@@ -50,13 +50,13 @@ void UpdateMenu::render() {
|
|||||||
|
|
||||||
bool canDownloadFile = m_Updater.canUpdate();
|
bool canDownloadFile = m_Updater.canUpdate();
|
||||||
|
|
||||||
if(!canDownloadFile) ImGui::BeginDisabled();
|
if (!canDownloadFile) ImGui::BeginDisabled();
|
||||||
|
|
||||||
if (ImGui::Button("Download")) {
|
if (ImGui::Button("Download")) {
|
||||||
m_Updater.downloadUpdate();
|
m_Updater.downloadUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!canDownloadFile) ImGui::EndDisabled();
|
if (!canDownloadFile) ImGui::EndDisabled();
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (ImGui::Button("Cancel")) {
|
if (ImGui::Button("Cancel")) {
|
||||||
@@ -77,8 +77,8 @@ void UpdateMenu::render() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateMenu::renderErrorPopup(){
|
void UpdateMenu::renderErrorPopup() {
|
||||||
if(ImGui::BeginPopup("UpdateError")){
|
if (ImGui::BeginPopup("UpdateError")) {
|
||||||
ImGui::Text("Error : %s", m_Error.c_str());
|
ImGui::Text("Error : %s", m_Error.c_str());
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,8 +97,8 @@ GL::VertexArray loadWorldModel(const td::game::World* world) {
|
|||||||
|
|
||||||
for (int spawnColor = 0; spawnColor < 2; spawnColor++) {
|
for (int spawnColor = 0; spawnColor < 2; spawnColor++) {
|
||||||
const game::Spawn& spawn = world->getTeam(game::TeamColor(spawnColor)).getSpawn();
|
const game::Spawn& spawn = world->getTeam(game::TeamColor(spawnColor)).getSpawn();
|
||||||
float fromX = spawn.x - 2, toX = spawn.x + 3;
|
float fromX = spawn.getTopLeft().getX() , toX = spawn.getBottomRight().getX();
|
||||||
float fromY = spawn.y - 2, toY = spawn.y + 3;
|
float fromY = spawn.getTopLeft().getY() , toY = spawn.getBottomRight().getY();
|
||||||
|
|
||||||
positions.insert(positions.end(), {
|
positions.insert(positions.end(), {
|
||||||
fromX, fromY,
|
fromX, fromY,
|
||||||
@@ -125,8 +125,8 @@ GL::VertexArray loadWorldModel(const td::game::World* world) {
|
|||||||
|
|
||||||
for (int castleColor = 0; castleColor < 2; castleColor++) {
|
for (int castleColor = 0; castleColor < 2; castleColor++) {
|
||||||
const game::TeamCastle& castle = world->getTeam(game::TeamColor(castleColor)).getCastle();
|
const game::TeamCastle& castle = world->getTeam(game::TeamColor(castleColor)).getCastle();
|
||||||
float fromX = castle.x - 2, toX = castle.x + 3;
|
float fromX = castle.getTopLeft().getX(), toX = castle.getBottomRight().getX();
|
||||||
float fromY = castle.y - 2, toY = castle.y + 3;
|
float fromY = castle.getTopLeft().getY(), toY = castle.getBottomRight().getY();
|
||||||
|
|
||||||
positions.insert(positions.end(), {
|
positions.insert(positions.end(), {
|
||||||
fromX, fromY,
|
fromX, fromY,
|
||||||
@@ -203,17 +203,17 @@ RenderData loadTowerModel(game::TowerPtr tower) {
|
|||||||
float towerY, towerDY;
|
float towerY, towerDY;
|
||||||
|
|
||||||
if (tower->getSize() == game::TowerSize::Little) {
|
if (tower->getSize() == game::TowerSize::Little) {
|
||||||
towerX = tower->getX() - 1;
|
towerX = tower->getCenterX() - 1.5f;
|
||||||
towerDX = tower->getX() + 2;
|
towerDX = tower->getCenterX() + 1.5f;
|
||||||
|
|
||||||
towerY = tower->getY() - 1;
|
towerY = tower->getCenterY() - 1.5f;
|
||||||
towerDY = tower->getY() + 2;
|
towerDY = tower->getCenterY() + 1.5f;
|
||||||
} else {
|
} else {
|
||||||
towerX = tower->getX() - 2;
|
towerX = tower->getCenterX() - 2.5f;
|
||||||
towerDX = tower->getX() + 3;
|
towerDX = tower->getCenterX() + 2.5f;
|
||||||
|
|
||||||
towerY = tower->getY() - 2;
|
towerY = tower->getCenterY() - 2.5f;
|
||||||
towerDY = tower->getY() + 3;
|
towerDY = tower->getCenterY() + 2.5f;
|
||||||
}
|
}
|
||||||
std::vector<float> positions = {
|
std::vector<float> positions = {
|
||||||
towerX, towerY,
|
towerX, towerY,
|
||||||
|
|||||||
@@ -1,10 +1,3 @@
|
|||||||
/*
|
|
||||||
* EntityShader.cpp
|
|
||||||
*
|
|
||||||
* Created on: 4 nov. 2020
|
|
||||||
* Author: simon
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "render/shaders/EntityShader.h"
|
#include "render/shaders/EntityShader.h"
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
|
|||||||
@@ -116,9 +116,9 @@ int ShaderProgram::loadShader(const std::string& source, GLenum type) {
|
|||||||
char error[size];
|
char error[size];
|
||||||
glGetShaderInfoLog(shaderID, size, &size, error);
|
glGetShaderInfoLog(shaderID, size, &size, error);
|
||||||
std::cout << error << std::endl;
|
std::cout << error << std::endl;
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "TRACKERS", "Could not compile shader !\n %s", error);
|
__android_log_print(ANDROID_LOG_ERROR, "TRACKERS", "Could not compile shader !\n %s", error);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
return shaderID;
|
return shaderID;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "misc/Updater.h"
|
#include "updater/Updater.h"
|
||||||
|
|
||||||
#include "misc/Platform.h"
|
#include "misc/Platform.h"
|
||||||
#include "misc/DataBuffer.h"
|
#include "misc/DataBuffer.h"
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@ target("TowerDefense")
|
|||||||
add_files("src/*.cpp", "src/*/*.cpp", "src/*/*/*.cpp", "src/*/*/*/*.cpp")
|
add_files("src/*.cpp", "src/*/*.cpp", "src/*/*/*.cpp", "src/*/*/*/*.cpp")
|
||||||
add_cxflags("-pthread")
|
add_cxflags("-pthread")
|
||||||
|
|
||||||
set_languages("c++14")
|
set_languages("c++17")
|
||||||
|
|
||||||
add_links("glbinding", "z", "SDL2")
|
add_links("glbinding", "z", "SDL2")
|
||||||
|
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user