#pragma once #include #include #include #include #include #include namespace td { using Vec2fp = Vec2; namespace game { struct WalkableTile; enum class EffectType : std::uint8_t { Slowness = 0, Stun, Fire, Poison, Heal, }; enum class MobType : std::uint8_t { Zombie = 0, Spider, Skeleton, Pigman, Creeper, Silverfish, Blaze, Witch, Slime, Giant, MOB_COUNT }; typedef std::uint32_t MobID; typedef std::uint8_t MobLevel; typedef std::vector TowerImmunities; typedef std::vector EffectImmunities; struct MobStats { float m_Damage; float m_Speed; Vec2f m_Size; std::uint16_t m_MoneyCost; std::uint16_t m_ExpCost; std::uint16_t m_MaxLife; std::uint16_t m_ExpReward; }; struct EffectDuration { EffectType type; float duration; // in seconds Tower* tower; // the tower that gived the effect }; const MobStats* GetMobStats(MobType type, std::uint8_t level); const TowerImmunities& GetMobTowerImmunities(MobType type, std::uint8_t level); const EffectImmunities& GetMobEffectImmunities(MobType type, std::uint8_t level); class Mob { protected: float m_Health; private: Vec2fp m_Position; MobID m_ID; PlayerID m_Sender; MobLevel m_Level; Direction m_Direction; std::vector m_Effects; const Tower* m_LastDamage; // the last tower that damaged the mob float m_HitCooldown; // utils::Timer m_EffectFireTimer; // utils::Timer m_EffectPoisonTimer; // utils::Timer m_EffectHealTimer; TeamCastle* m_CastleTarget; // utils::CooldownTimer m_AttackTimer; public: Mob(MobID id, MobLevel level, PlayerID sender) : m_Sender(sender), m_Level(level), m_HitCooldown(0), m_CastleTarget(nullptr) {} virtual ~Mob() {} virtual MobType GetType() const = 0; virtual bool OnDeath(World* world) { return true; } Vec2fp& GetPosition() { return m_Position; } MobID GetMobID() const { return m_ID; } const TowerImmunities& GetTowerImmunities() const { return GetMobTowerImmunities(GetType(), m_Level); } const EffectImmunities& GetEffectImmunities() const { return GetMobEffectImmunities(GetType(), m_Level); } PlayerID GetSender() const { return m_Sender; } MobLevel GetLevel() const { return m_Level; } const MobStats* GetStats() const { return GetMobStats(GetType(), m_Level); } void SetHealth(float newHealth) { m_Health = newHealth; } float GetHealth() const { return m_Health; } bool IsDead() const { return m_Health <= 0; } bool IsAlive() const { return m_Health > 0; } 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; m_HitCooldown = 0.1; } void Heal(float heal) { m_Health = std::min(static_cast(GetStats()->m_MaxLife), m_Health + heal); } void SetMobReachedCastle(TeamCastle* castle) { m_CastleTarget = castle; } // used when mob is in front of the castle bool IsImmuneTo(TowerType type); bool IsImmuneTo(EffectType type); void AddEffect(EffectType type, float durationSec, Tower* tower); bool HasEffect(EffectType type); bool HasTakenDamage() { return m_HitCooldown > 0; } // // returns a float between 0 and 1 excluded // float GetTileX() { // return GetCenterX() - static_cast(static_cast(GetCenterX())); // } // // returns a float between 0 and 1 excluded // float GetTileY() { // return GetCenterY() - static_cast(static_cast(GetCenterY())); // } Direction GetDirection() const { return m_Direction; } void SetDirection(Direction dir) { m_Direction = dir; } protected: void InitMob() { m_Health = static_cast(GetStats()->m_MaxLife); // SetSize(GetStats()->m_Size.x, GetStats()->m_Size.y); } private: 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); }; typedef std::shared_ptr MobPtr; template class ConcreteMob : public Mob { public: ConcreteMob(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); } virtual ~ConcreteMob() {} virtual constexpr MobType GetType() const override { return MT; } }; using Zombie = ConcreteMob; using Spider = ConcreteMob; using Skeleton = ConcreteMob; using PigMan = ConcreteMob; using Creeper = ConcreteMob; using Silverfish = ConcreteMob; using Blaze = ConcreteMob; using Witch = ConcreteMob; using Slime = ConcreteMob; using Giant = ConcreteMob; namespace MobFactory { MobPtr CreateMob(MobID id, MobType type, std::uint8_t level, PlayerID sender); std::string GetMobName(MobType type); } // namespace MobFactory 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 MobNotifier; } // namespace game } // namespace td