14 Commits

48 changed files with 540 additions and 320 deletions

View File

@@ -2,10 +2,13 @@
#include "Towers.h" #include "Towers.h"
#include "Types.h" #include "Types.h"
#include "Team.h"
#include <vector> #include <vector>
#include <memory> #include <memory>
#include <glm/glm.hpp>
namespace td { namespace td {
namespace game { namespace game {
@@ -41,20 +44,22 @@ 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; }
@@ -71,7 +76,7 @@ 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,13 +85,12 @@ 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;
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) {
@@ -97,7 +101,7 @@ public:
virtual void tick(std::uint64_t delta); virtual void tick(std::uint64_t delta);
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); }
@@ -110,13 +114,7 @@ public:
const Tower* getLastDamageTower() { return m_LastDamage; } const Tower* getLastDamageTower() { return m_LastDamage; }
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); }
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);
@@ -128,7 +126,10 @@ public:
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);
EffectDuration& getEffect(EffectType type); EffectDuration& getEffect(EffectType type);
@@ -138,70 +139,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; }
}; };

View File

@@ -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,33 @@ 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 { struct TeamCastle : public utils::shape::Rectangle{
std::int32_t x, y; private:
std::uint16_t life = 1000; float m_Life;
public:
TeamCastle() : m_Life(1000) {
setWidth(5);
setHeight(5);
}
float getLife() const { return m_Life; }
void setLife(float life) { m_Life = life; }
void damage(float damage) { m_Life = std::max(0.0f, m_Life - damage); }
}; };
class Team { class Team {

View File

@@ -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; }

View File

@@ -134,6 +134,8 @@ protected:
public: public:
World(Game* game); World(Game* game);
static constexpr std::uint8_t CastleWidth = 5, CastleHeight = 5;
bool loadMap(const protocol::WorldBeginDataPacket* worldHeader); bool loadMap(const protocol::WorldBeginDataPacket* worldHeader);
bool loadMap(const protocol::WorldDataPacket* worldData); bool loadMap(const protocol::WorldDataPacket* worldData);
@@ -191,6 +193,9 @@ public:
virtual void OnArrowShot(MobPtr target, Tower* shooter); virtual void OnArrowShot(MobPtr target, Tower* shooter);
private: private:
void moveMobs(std::uint64_t delta); void moveMobs(std::uint64_t delta);
void moveMob(MobPtr mob, std::uint64_t delta);
void moveBackMob(MobPtr mob, const TeamCastle& castle);
bool isMobTouchingCastle(MobPtr mob, const TeamCastle& castle) const;
void tickMobs(std::uint64_t delta); void tickMobs(std::uint64_t delta);
void cleanDeadMobs(); void cleanDeadMobs();
}; };

View File

@@ -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 {

85
include/misc/Shapes.h Normal file
View File

@@ -0,0 +1,85 @@
#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 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;
};
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;
};
} // namespace shape
} // namespace utils
} // namespace td

View File

@@ -1,10 +1,10 @@
#pragma once #pragma once
#include "misc/DataBuffer.h" #include "DataBuffer.h"
#include <ctime> #include <ctime>
#define TD_VERSION "alpha-0.0.1" #define TD_VERSION "alpha-0.0.2"
namespace td { namespace td {
namespace utils { namespace utils {

View File

@@ -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

View File

@@ -88,66 +88,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) {

View File

@@ -1,5 +1,7 @@
#include "game/Player.h" #include "game/Player.h"
#include <algorithm>
namespace td { namespace td {
namespace game { namespace game {

View File

@@ -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 = {

View File

@@ -20,8 +20,8 @@ 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())
@@ -111,8 +111,7 @@ 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);
} }
@@ -131,39 +130,82 @@ void World::tickMobs(std::uint64_t delta) {
void World::moveMobs(std::uint64_t delta) { void World::moveMobs(std::uint64_t delta) {
for (MobPtr mob : m_Mobs) { for (MobPtr mob : m_Mobs) {
TilePtr tile = getTile(mob->getX(), mob->getY()); TilePtr tile = getTile(mob->getCenter().getX(), mob->getCenter().getY());
if (tile != nullptr && tile->getType() == TileType::Walk) { if (tile != nullptr && tile->getType() == TileType::Walk) {
WalkableTile* walkTile = dynamic_cast<WalkableTile*>(tile.get()); WalkableTile* walkTile = dynamic_cast<WalkableTile*>(tile.get());
mob->setDirection(walkTile->direction); mob->setDirection(walkTile->direction);
} }
moveMob(mob, delta);
TeamColor mobTeam = m_Game->getPlayerById(mob->getSender()).getTeamColor();
const TeamCastle* enemyCastle;
if (mobTeam == TeamColor::Red) {
enemyCastle = &getBlueTeam().getCastle();
} else if (mobTeam == TeamColor::Blue) {
enemyCastle = &getRedTeam().getCastle();
}
if (isMobTouchingCastle(mob, *enemyCastle)) {
moveBackMob(mob, *enemyCastle);
}
}
}
void World::moveMob(MobPtr mob, std::uint64_t delta) {
float mobWalkSpeed = mob->getStats()->getMovementSpeed(); float mobWalkSpeed = mob->getStats()->getMovementSpeed();
float walkAmount = mobWalkSpeed * ((float)delta / 1000.0f); float walkAmount = mobWalkSpeed * ((float)delta / 1000.0f);
if (mob->hasEffect(EffectType::Slowness)) if (mob->hasEffect(EffectType::Slowness))
walkAmount *= 0.70; walkAmount *= 0.70; // walk 30% slower
switch (mob->getDirection()) { switch (mob->getDirection()) {
case Direction::NegativeX: { case Direction::NegativeX: {
mob->setX(mob->getX() - walkAmount); mob->setCenterX(mob->getCenterX() - walkAmount);
break; break;
} }
case Direction::PositiveX: { case Direction::PositiveX: {
mob->setX(mob->getX() + walkAmount); mob->setCenterX(mob->getCenterX() + walkAmount);
break; break;
} }
case Direction::NegativeY: { case Direction::NegativeY: {
mob->setY(mob->getY() - walkAmount); mob->setCenterY(mob->getCenterY() - walkAmount);
break; break;
} }
case Direction::PositiveY: { case Direction::PositiveY: {
mob->setY(mob->getY() + walkAmount); mob->setCenterY(mob->getCenterY() + walkAmount);
break; break;
} }
} }
} }
bool World::isMobTouchingCastle(MobPtr mob, const TeamCastle& enemyCastle) const {
return enemyCastle.collidesWith(*mob);
}
void World::moveBackMob(MobPtr mob, const TeamCastle& enemyCastle) {
switch (mob->getDirection()) {
case Direction::NegativeX: {
mob->setCenterX(enemyCastle.getBottomRight().getX() + mob->getWidth() / 2.0f);
break;
}
case Direction::PositiveX: {
mob->setCenterX(enemyCastle.getTopLeft().getX() - mob->getWidth() / 2.0f);
break;
}
case Direction::NegativeY: {
mob->setCenterY(enemyCastle.getBottomRight().getY() + mob->getHeight() / 2.0f);
break;
}
case Direction::PositiveY: {
mob->setCenterY(enemyCastle.getTopLeft().getY() - mob->getHeight() / 2.0f);
break;
}
}
} }
const Color* World::getTileColor(TilePtr tile) const { const Color* World::getTileColor(TilePtr tile) const {
@@ -245,8 +287,7 @@ void World::cleanDeadMobs() {
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); if (mob->OnDeath(this)) {
//reward players //reward players
Player& sender = m_Game->getPlayerById(mob->getSender()); Player& sender = m_Game->getPlayerById(mob->getSender());
sender.addExp(mob->getStats()->getExpReward()); sender.addExp(mob->getStats()->getExpReward());
@@ -258,17 +299,18 @@ 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;
} }
} }

View File

@@ -103,7 +103,7 @@ 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() {

View File

@@ -22,8 +22,8 @@ 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; std::int32_t spawnCenterX = enemyMobSpawn->getCenterX();
std::int32_t spawnCenterY = enemyMobSpawn->y; std::int32_t spawnCenterY = enemyMobSpawn->getCenterY();
std::int32_t minSpawnY = spawnCenterY - 2; std::int32_t minSpawnY = spawnCenterY - 2;
std::int32_t maxSpawnY = spawnCenterY + 2; std::int32_t maxSpawnY = spawnCenterY + 2;
@@ -34,9 +34,9 @@ void ServerWorld::spawnMobs(game::MobType type, std::uint8_t level, game::Player
float mobX = utils::getRandomReal<float>(minSpawnX, maxSpawnX); float mobX = utils::getRandomReal<float>(minSpawnX, maxSpawnX);
float mobY = utils::getRandomReal<float>(minSpawnY, maxSpawnY); float mobY = utils::getRandomReal<float>(minSpawnY, maxSpawnY);
spawnMobAt(m_CurrentMobID, type, level, sender, mobX, mobY, enemyMobSpawn->direction); spawnMobAt(m_CurrentMobID, type, level, sender, mobX, mobY, enemyMobSpawn->getDirection());
protocol::SpawnMobPacket packet(m_CurrentMobID, type, level, sender, mobX, mobY, enemyMobSpawn->direction); protocol::SpawnMobPacket packet(m_CurrentMobID, type, level, sender, mobX, mobY, enemyMobSpawn->getDirection());
m_Server->broadcastPacket(&packet); m_Server->broadcastPacket(&packet);
m_CurrentMobID++; m_CurrentMobID++;

68
src/misc/Shapes.cpp Normal file
View File

@@ -0,0 +1,68 @@
#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);
}
bool Circle::collidesWith(const Point& point) const {
return m_Radius * m_Radius > m_Center.distanceSquared(point);
}
bool Circle::collidesWith(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;
float distanceSquared = (distanceX * distanceX) + (distanceY * distanceY);
return distanceSquared < m_Radius * m_Radius;
}
bool Circle::collidesWith(const Circle& circle) const {
return m_Radius + circle.getRadius() > m_Center.distanceSquared(circle.getCenter());
}
} // namespace shape
} // namespace utils
} // namespace td

View File

@@ -74,7 +74,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);
} }
} }
@@ -235,11 +235,7 @@ void WorldRenderer::renderMobTooltip() const {
void WorldRenderer::detectMobHovering() const { void WorldRenderer::detectMobHovering() const {
glm::vec2 cursorWorldPos = getCursorWorldPos(); glm::vec2 cursorWorldPos = getCursorWorldPos();
for (game::MobPtr mob : m_World->getMobList()) { for (game::MobPtr mob : m_World->getMobList()) {
// 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;
} }

View File

@@ -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,

View File

@@ -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")