restructure project

This commit is contained in:
2023-08-13 11:59:13 +02:00
parent b4836847f5
commit 50c17e8ed1
210 changed files with 471 additions and 422 deletions

37
src/td/game/BaseGame.cpp Normal file
View File

@@ -0,0 +1,37 @@
#include "td/game/BaseGame.h"
namespace td {
namespace game {
Game::Game(World* world) : m_World(world) {
}
Game::~Game() {
}
void Game::Tick(std::uint64_t delta) {
if (m_GameState == GameState::Game) {
m_World->Tick(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 td

511
src/td/game/Mobs.cpp Normal file
View File

@@ -0,0 +1,511 @@
#include "td/game/Mobs.h"
#include "td/game/Player.h"
#include "td/game/World.h"
#include <map>
#include <algorithm>
namespace td {
namespace game {
bool Mob::IsImmuneTo(TowerType type) {
return std::find(GetTowerImmunities().begin(), GetTowerImmunities().end(), type) != GetTowerImmunities().end();
}
bool Mob::IsImmuneTo(EffectType type) {
return std::find(GetEffectImmunities().begin(), GetEffectImmunities().end(), type) != GetEffectImmunities().end();
}
EffectDuration& Mob::GetEffect(EffectType 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, Tower* tower) {
if (IsImmuneTo(effectType))
return;
if (HasEffect(effectType)) {
EffectDuration& effect = GetEffect(effectType);
if (effect.duration < durationSec)
effect.duration = durationSec; // Setting new duration if it's greater then the actual
} else {
m_Effects.push_back({ effectType, durationSec, tower });
}
}
void Mob::AttackCastle(std::uint64_t delta, World* world) {
if (!HasReachedEnemyCastle()) return;
if (m_AttackTimer.Update(delta)) {
world->GetMobNotifier().NotifyListeners(&MobListener::OnMobCastleDamage, this, m_CastleTarget, GetStats()->GetDamage());
m_AttackTimer.ApplyCooldown();
}
}
void Mob::Walk(std::uint64_t delta, World* world) {
float mobWalkSpeed = GetStats()->GetMovementSpeed();
float walkAmount = mobWalkSpeed * (static_cast<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;
}
default:
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;
}
default:
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;
}
default:
break;
}
}
bool Mob::IsTouchingCastle(const TeamCastle& enemyCastle) const {
return enemyCastle.CollidesWith(*this);
}
void Mob::Tick(std::uint64_t delta, World* world) {
m_HitCooldown = std::max(0.0f, m_HitCooldown - static_cast<float>(delta / 1000.0f));
UpdateEffects(delta, world);
Move(delta, world);
AttackCastle(delta, world);
}
void Mob::UpdateEffects(std::uint64_t delta, World* world) {
float deltaSec = static_cast<float>(delta / 1000.0f);
for (std::size_t i = 0; i < m_Effects.size(); i++) {
EffectDuration& effect = m_Effects[i];
effect.duration -= deltaSec;
if (effect.duration < 0) { // effect has gone
m_Effects.erase(m_Effects.begin() + static_cast<int>(i));
switch (effect.type) {
case EffectType::Fire: {
m_EffectFireTimer.Reset();
break;
}
case EffectType::Poison: {
m_EffectPoisonTimer.Reset();
break;
}
case EffectType::Heal: {
m_EffectHealTimer.Reset();
}
default:
break;
}
}
}
if (HasEffect(EffectType::Fire)) {
if (m_EffectFireTimer.Update(delta)) {
world->GetMobNotifier().NotifyListeners(&MobListener::OnMobDamage, this, 3.0f, GetEffect(EffectType::Fire).tower);
}
}
if (HasEffect(EffectType::Poison)) {
if (m_EffectPoisonTimer.Update(delta)) {
world->GetMobNotifier().NotifyListeners(&MobListener::OnMobDamage, this, 1.0f, GetEffect(EffectType::Poison).tower);
}
}
if (HasEffect(EffectType::Heal)) {
if (m_EffectFireTimer.Update(delta)) {
Heal(10);
}
}
}
bool Mob::HasEffect(EffectType type) {
return std::find_if(m_Effects.begin(), m_Effects.end(), [&type](EffectDuration effect) { return effect.type == type;}) != m_Effects.end();
}
typedef std::pair<MobType, std::uint8_t> MobKey;
static const std::map<MobKey, MobStats> MobConstants = {
// damage speed size money_cost exp_cost exp_reward max_health
{{MobType::Zombie, 1},{MobStats{1, 1.6, {1, 1}, 15, 0, 7, 40}}},
{{MobType::Zombie, 2},{MobStats{1, 1.6, {1, 1}, 18, 88, 9, 56}}},
{{MobType::Zombie, 3},{MobStats{1, 1.6, {1, 1}, 22, 153, 10, 78}}},
{{MobType::Zombie, 4},{MobStats{1.5, 1.6, {1, 1}, 26, 268, 13, 110}}},
{{MobType::Zombie, 5},{MobStats{2, 1.6, {1, 1}, 31, 469, 15, 154}}},
{{MobType::Spider, 1},{MobStats{1, 1.6, {1, 1}, 25, 100, 15, 80}}},
{{MobType::Spider, 2},{MobStats{1, 2, {1, 1}, 30, 175, 16, 112}}},
{{MobType::Spider, 3},{MobStats{1.5, 2, {1, 1}, 36, 306, 18, 157}}},
{{MobType::Spider, 4},{MobStats{2.5, 2, {1, 1}, 43, 536, 19, 222}}},
{{MobType::Spider, 5},{MobStats{1.5, 2.5, {1, 1}, 52, 938, 22, 307}}},
{{MobType::Skeleton, 1},{MobStats{1, 1.6, {1, 1}, 120, 200, 30, 350}}},
{{MobType::Skeleton, 2},{MobStats{1, 1.6, {1, 1}, 144, 350, 33, 490}}},
{{MobType::Skeleton, 3},{MobStats{1, 1.6, {1, 1}, 173, 613, 36, 686}}},
{{MobType::Skeleton, 4},{MobStats{1.5, 1.6, {1, 1}, 225, 1072, 40, 960}}},
{{MobType::Skeleton, 5},{MobStats{2, 1.6, {1, 1}, 255, 1876, 43, 1345}}},
{{MobType::Pigman, 1},{MobStats{1, 2, {1, 1}, 100, 150, 22, 150}}},
{{MobType::Pigman, 2},{MobStats{1, 2, {1, 1}, 120, 263, 24, 210}}},
{{MobType::Pigman, 3},{MobStats{1, 2, {1, 1}, 144, 459, 25, 297}}},
{{MobType::Pigman, 4},{MobStats{1, 2, {1, 1}, 173, 804, 25, 412}}},
{{MobType::Pigman, 5},{MobStats{1.5, 2, {1, 1}, 207, 1407, 27, 576}}},
{{MobType::Creeper, 1},{MobStats{1, 2, {1, 1}, 250, 325, 46, 350}}},
{{MobType::Creeper, 2},{MobStats{1, 2, {1, 1}, 290, 550, 49, 460}}},
{{MobType::Creeper, 3},{MobStats{2, 2, {1, 1}, 350, 800, 55, 630}}},
{{MobType::Creeper, 4},{MobStats{3, 2, {1, 1}, 420, 1300, 61, 900}}},
{{MobType::Creeper, 5},{MobStats{4, 2, {1, 1}, 510, 1850, 67, 1250}}},
{{MobType::Silverfish, 1},{MobStats{1, 1.6, {1, 1}, 38, 125, 18, 120}}},
{{MobType::Silverfish, 2},{MobStats{1, 1.6, {1, 1}, 50, 230, 19, 170}}},
{{MobType::Silverfish, 3},{MobStats{1, 1.6, {1, 1}, 75, 340, 25, 225}}},
{{MobType::Silverfish, 4},{MobStats{1.5, 1.6, {1, 1}, 170, 700, 33, 310}}},
{{MobType::Silverfish, 5},{MobStats{1.5, 1.6, {1, 1}, 200, 1800, 36, 390}}},
{{MobType::Blaze, 1},{MobStats{1, 1.6, {1, 1}, 500, 500, 105, 410}}},
{{MobType::Blaze, 2},{MobStats{1, 1.6, {1, 1}, 600, 875, 111, 574}}},
{{MobType::Blaze, 3},{MobStats{1, 1.6, {1, 1}, 720, 1531, 115, 804}}},
{{MobType::Blaze, 4},{MobStats{1.5, 1.6, {1, 1}, 864, 2680, 121, 1125}}},
{{MobType::Blaze, 5},{MobStats{2, 1.6, {1, 1}, 1037, 4689, 127, 1575}}},
{{MobType::Witch, 1},{MobStats{1, 1.6, {1, 1}, 150, 300, 37, 300}}},
{{MobType::Witch, 2},{MobStats{1, 1.6, {1, 1}, 165, 525, 39, 405}}},
{{MobType::Witch, 3},{MobStats{1, 1.6, {1, 1}, 182, 918, 42, 547}}},
{{MobType::Witch, 4},{MobStats{1.5, 1.6, {1, 1}, 200, 1606, 43, 738}}},
{{MobType::Witch, 5},{MobStats{2, 1.6, {1, 1}, 220, 2810, 45, 996}}},
{{MobType::Slime, 1},{MobStats{1, 0.8, {1, 1}, 1500, 1000, 300, 800}}},
{{MobType::Slime, 2},{MobStats{1.5, 0.8, {1, 1}, 1800, 1750, 314, 880}}},
{{MobType::Slime, 3},{MobStats{2, 0.8, {1, 1}, 2160, 3063, 330, 968}}},
{{MobType::Slime, 4},{MobStats{2.5, 0.8, {1, 1}, 2592, 5359, 348, 1065}}},
{{MobType::Slime, 5},{MobStats{3, 0.8, {1, 1}, 3110, 9379, 366, 1171}}},
{{MobType::Giant, 1},{MobStats{10, 0.8, {1, 1}, 4000, 2250, 600, 6250}}},
{{MobType::Giant, 2},{MobStats{20, 0.8, {1, 1}, 4500, 4000, 612, 9375}}},
{{MobType::Giant, 3},{MobStats{30, 0.8, {1, 1}, 5062, 7250, 624, 14062}}},
{{MobType::Giant, 4},{MobStats{40, 0.8, {1, 1}, 5695, 12500, 636, 21093}}},
{{MobType::Giant, 5},{MobStats{50, 0.8, {1, 1}, 6407, 22000, 648, 31640}}},
};
const MobStats* GetMobStats(MobType type, std::uint8_t level) {
return &MobConstants.at(MobKey{ type, level });
}
static const std::map<MobKey, TowerImmunities> MobsTowerImmunities = {
{{MobType::Zombie, 1},{}},
{{MobType::Zombie, 2},{}},
{{MobType::Zombie, 3},{}},
{{MobType::Zombie, 4},{}},
{{MobType::Zombie, 5},{}},
{{MobType::Spider, 1},{}},
{{MobType::Spider, 2},{}},
{{MobType::Spider, 3},{}},
{{MobType::Spider, 4},{TowerType::Archer, TowerType::Artillery, TowerType::Leach, TowerType::Necromancer, TowerType::Poison, TowerType::Quake, TowerType::Sorcerer, TowerType::Turret, TowerType::Zeus}},
{{MobType::Spider, 5},{TowerType::Archer, TowerType::Artillery, TowerType::Leach, TowerType::Necromancer, TowerType::Poison, TowerType::Quake, TowerType::Sorcerer, TowerType::Turret, TowerType::Zeus}},
{{MobType::Skeleton, 1},{}},
{{MobType::Skeleton, 2},{}},
{{MobType::Skeleton, 3},{}},
{{MobType::Skeleton, 4},{}},
{{MobType::Skeleton, 5},{}},
{{MobType::Pigman, 1},{TowerType::Zeus}},
{{MobType::Pigman, 2},{TowerType::Zeus}},
{{MobType::Pigman, 3},{TowerType::Zeus}},
{{MobType::Pigman, 4},{TowerType::Zeus}},
{{MobType::Pigman, 5},{TowerType::Zeus}},
{{MobType::Creeper, 1},{}},
{{MobType::Creeper, 2},{}},
{{MobType::Creeper, 3},{}},
{{MobType::Creeper, 4},{}},
{{MobType::Creeper, 5},{}},
{{MobType::Silverfish, 1},{}},
{{MobType::Silverfish, 2},{}},
{{MobType::Silverfish, 3},{}},
{{MobType::Silverfish, 4},{}},
{{MobType::Silverfish, 5},{}},
{{MobType::Blaze, 1},{}},
{{MobType::Blaze, 2},{}},
{{MobType::Blaze, 3},{}},
{{MobType::Blaze, 4},{}},
{{MobType::Blaze, 5},{}},
{{MobType::Witch, 1},{TowerType::Zeus, TowerType::Mage, TowerType::Poison}},
{{MobType::Witch, 2},{TowerType::Zeus, TowerType::Mage, TowerType::Poison}},
{{MobType::Witch, 3},{TowerType::Zeus, TowerType::Mage, TowerType::Poison}},
{{MobType::Witch, 4},{TowerType::Zeus, TowerType::Mage, TowerType::Poison}},
{{MobType::Witch, 5},{TowerType::Zeus, TowerType::Mage, TowerType::Poison}},
{{MobType::Slime, 1},{}},
{{MobType::Slime, 2},{}},
{{MobType::Slime, 3},{}},
{{MobType::Slime, 4},{}},
{{MobType::Slime, 5},{}},
{{MobType::Giant, 1},{}},
{{MobType::Giant, 2},{}},
{{MobType::Giant, 3},{}},
{{MobType::Giant, 4},{}},
{{MobType::Giant, 5},{}},
};
const TowerImmunities& GetMobTowerImmunities(MobType type, std::uint8_t level) {
return MobsTowerImmunities.at({ type, level });
}
static const std::map<MobKey, EffectImmunities> MobsEffectImmunities = {
{{MobType::Zombie, 1},{}},
{{MobType::Zombie, 2},{}},
{{MobType::Zombie, 3},{}},
{{MobType::Zombie, 4},{}},
{{MobType::Zombie, 5},{}},
{{MobType::Spider, 1},{}},
{{MobType::Spider, 2},{}},
{{MobType::Spider, 3},{}},
{{MobType::Spider, 4},{}},
{{MobType::Spider, 5},{}},
{{MobType::Skeleton, 1},{}},
{{MobType::Skeleton, 2},{}},
{{MobType::Skeleton, 3},{}},
{{MobType::Skeleton, 4},{EffectType::Fire}},
{{MobType::Skeleton, 5},{EffectType::Fire}},
{{MobType::Pigman, 1},{EffectType::Fire}},
{{MobType::Pigman, 2},{EffectType::Fire}},
{{MobType::Pigman, 3},{EffectType::Fire}},
{{MobType::Pigman, 4},{EffectType::Fire}},
{{MobType::Pigman, 5},{EffectType::Fire}},
{{MobType::Creeper, 1},{}},
{{MobType::Creeper, 2},{}},
{{MobType::Creeper, 3},{}},
{{MobType::Creeper, 4},{}},
{{MobType::Creeper, 5},{}},
{{MobType::Silverfish, 1},{}},
{{MobType::Silverfish, 2},{}},
{{MobType::Silverfish, 3},{}},
{{MobType::Silverfish, 4},{EffectType::Fire}},
{{MobType::Silverfish, 5},{EffectType::Fire}},
{{MobType::Blaze, 1},{EffectType::Fire}},
{{MobType::Blaze, 2},{EffectType::Fire}},
{{MobType::Blaze, 3},{EffectType::Fire}},
{{MobType::Blaze, 4},{EffectType::Fire}},
{{MobType::Blaze, 5},{EffectType::Fire}},
{{MobType::Witch, 1},{}},
{{MobType::Witch, 2},{}},
{{MobType::Witch, 3},{}},
{{MobType::Witch, 4},{}},
{{MobType::Witch, 5},{}},
{{MobType::Slime, 1},{}},
{{MobType::Slime, 2},{}},
{{MobType::Slime, 3},{}},
{{MobType::Slime, 4},{EffectType::Fire}},
{{MobType::Slime, 5},{EffectType::Fire}},
{{MobType::Giant, 1},{EffectType::Stun}},
{{MobType::Giant, 2},{EffectType::Stun}},
{{MobType::Giant, 3},{EffectType::Stun}},
{{MobType::Giant, 4},{EffectType::Stun}},
{{MobType::Giant, 5},{EffectType::Stun}},
};
const EffectImmunities& GetMobEffectImmunities(MobType type, std::uint8_t level) {
return MobsEffectImmunities.at({ type, level });
}
MobPtr MobFactory::CreateMob(MobID mobId, MobType mobType, std::uint8_t mobLevel, PlayerID mobSender) {
using MobCreator = std::function<std::shared_ptr<Mob>(MobID, std::uint8_t, PlayerID)>;
static const std::map<MobType, MobCreator> mobFactory = {
{MobType::Zombie, [](MobID id, std::uint8_t level, PlayerID sender) -> MobPtr {return std::make_shared<Zombie>(id, level, sender);} },
{MobType::Spider, [](MobID id, std::uint8_t level, PlayerID sender) -> MobPtr {return std::make_shared<Spider>(id, level, sender);} },
{MobType::Skeleton, [](MobID id, std::uint8_t level, PlayerID sender) -> MobPtr {return std::make_shared<Skeleton>(id, level, sender);} },
{MobType::Pigman, [](MobID id, std::uint8_t level, PlayerID sender) -> MobPtr {return std::make_shared<PigMan>(id, level, sender);} },
{MobType::Creeper, [](MobID id, std::uint8_t level, PlayerID sender) -> MobPtr {return std::make_shared<Creeper>(id, level, sender);} },
{MobType::Silverfish, [](MobID id, std::uint8_t level, PlayerID sender) -> MobPtr {return std::make_shared<Silverfish>(id, level, sender);} },
{MobType::Blaze, [](MobID id, std::uint8_t level, PlayerID sender) -> MobPtr {return std::make_shared<Blaze>(id, level, sender);} },
{MobType::Witch, [](MobID id, std::uint8_t level, PlayerID sender) -> MobPtr {return std::make_shared<Witch>(id, level, sender);} },
{MobType::Slime, [](MobID id, std::uint8_t level, PlayerID sender) -> MobPtr {return std::make_shared<Slime>(id, level, sender);} },
{MobType::Giant, [](MobID id, std::uint8_t level, PlayerID sender) -> MobPtr {return std::make_shared<Giant>(id, level, sender);} },
};
return mobFactory.at(mobType)(mobId, mobLevel, mobSender);
}
std::string MobFactory::GetMobName(MobType type) {
switch (type) {
case MobType::Zombie:
return "Zombie";
case MobType::Spider:
return "Spider";
case MobType::Skeleton:
return "Skeleton";
case MobType::Pigman:
return "Pigman";
case MobType::Creeper:
return "Creeper";
case MobType::Silverfish:
return "Silverfish";
case MobType::Blaze:
return "Blaze";
case MobType::Witch:
return "Witch";
case MobType::Slime:
return "Slime";
case MobType::Giant:
return "Giant";
default:
return "Unknow";
}
}
} // namespace game
} // namespace td

28
src/td/game/Team.cpp Normal file
View File

@@ -0,0 +1,28 @@
#include "td/game/Player.h"
#include <algorithm>
namespace td {
namespace game {
Team::Team(TeamColor color) : m_Color(color), m_TeamCastle(this) {}
void Team::AddPlayer(Player* newPlayer) {
m_Players.push_back(newPlayer);
}
void Team::RemovePlayer(const Player* player) {
m_Players.erase(std::find(m_Players.begin(), m_Players.end(), player));
}
TeamColor Team::GetColor() const {
return m_Color;
}
std::uint8_t Team::GetPlayerCount() const {
return m_Players.size();
}
} // namespace game
} // namespace td

205
src/td/game/Towers.cpp Normal file
View File

@@ -0,0 +1,205 @@
#include "td/game/Towers.h"
#include "td/game/World.h"
#include <map>
namespace td {
namespace game {
bool Tower::IsMobInRange(MobPtr mob) {
if (mob->IsDead())
return false;
return mob->CollidesWith(*this);
}
const std::map<std::pair<TowerType, TowerLevel>, TowerStats> TowerConstants = {
// // rate damage range
{{TowerType::Archer, {1, TowerPath::Base}}, {2, 5, 10}},
{{TowerType::Archer, {2, TowerPath::Top}}, {1, 0, 12}},
{{TowerType::Archer, {3, TowerPath::Top}}, {1, 0, 13}},
{{TowerType::Archer, {4, TowerPath::Top}}, {0.8, 0, 15}},
{{TowerType::Archer, {2, TowerPath::Bottom}}, {2, 10, 12}},
{{TowerType::Archer, {3, TowerPath::Bottom}}, {2, 10, 13}},
{{TowerType::Archer, {4, TowerPath::Bottom}}, {2, 10, 15}},
//-----------------------------------------------------------------
{{TowerType::Ice, {1, TowerPath::Base}}, {1, 0, 10}},
{{TowerType::Ice, {2, TowerPath::Base}}, {1, 0, 12}},
{{TowerType::Ice, {3, TowerPath::Base}}, {1, 0, 13}},
{{TowerType::Ice, {4, TowerPath::Base}}, {1, 1, 15}},
//-----------------------------------------------------------------
{{TowerType::Sorcerer, {1, TowerPath::Base}}, {5, 0, 10}},
{{TowerType::Sorcerer, {2, TowerPath::Base}}, {4, 0, 12}},
{{TowerType::Sorcerer, {3, TowerPath::Top}}, {4, 0, 14}},
{{TowerType::Sorcerer, {4, TowerPath::Top}}, {4, 0, 15}},
{{TowerType::Sorcerer, {3, TowerPath::Bottom}}, {4, 0, 14}},
{{TowerType::Sorcerer, {4, TowerPath::Bottom}}, {4, 0, 15}},
//-----------------------------------------------------------------
{{TowerType::Zeus, {1, TowerPath::Base}}, {5, 0, 10}},
{{TowerType::Zeus, {2, TowerPath::Base}}, {4, 0, 12}},
{{TowerType::Zeus, {3, TowerPath::Top}}, {0, 0, 0}},
{{TowerType::Zeus, {4, TowerPath::Top}}, {0, 0, 0}},
{{TowerType::Zeus, {3, TowerPath::Bottom}}, {1.2, 0, 14}},
{{TowerType::Zeus, {4, TowerPath::Bottom}}, {0.8, 0, 15}},
//-----------------------------------------------------------------
{{TowerType::Mage, {1, TowerPath::Base}}, {5, 0, 10}},
{{TowerType::Mage, {2, TowerPath::Base}}, {4, 0, 12}},
{{TowerType::Mage, {3, TowerPath::Base}}, {3, 0, 13}},
{{TowerType::Mage, {4, TowerPath::Base}}, {1, 30, 15}},
//-----------------------------------------------------------------
{{TowerType::Artillery, {1, TowerPath::Base}}, {7, 0, 10}},
{{TowerType::Artillery, {2, TowerPath::Base}}, {7, 0, 12}},
{{TowerType::Artillery, {3, TowerPath::Top}}, {7, 0, 13}},
{{TowerType::Artillery, {4, TowerPath::Top}}, {7, 0, 15}},
{{TowerType::Artillery, {3, TowerPath::Bottom}}, {5, 0, 13}},
{{TowerType::Artillery, {4, TowerPath::Bottom}}, {4, 0, 15}},
//-----------------------------------------------------------------
{{TowerType::Quake, {1, TowerPath::Base}}, {5, 5, 10}},
{{TowerType::Quake, {2, TowerPath::Base}}, {4, 7, 12}},
{{TowerType::Quake, {3, TowerPath::Base}}, {3, 9, 13}},
{{TowerType::Quake, {4, TowerPath::Base}}, {2, 11, 15}},
//-----------------------------------------------------------------
{{TowerType::Poison, {1, TowerPath::Base}}, {5, 0, 10}},
{{TowerType::Poison, {2, TowerPath::Base}}, {5, 0, 12}},
{{TowerType::Poison, {3, TowerPath::Top}}, {6, 0, 13}},
{{TowerType::Poison, {4, TowerPath::Top}}, {5, 0, 15}},
{{TowerType::Poison, {3, TowerPath::Bottom}}, {5, 10, 13}},
{{TowerType::Poison, {4, TowerPath::Bottom}}, {6, 20, 15}},
//-----------------------------------------------------------------
{{TowerType::Leach, {1, TowerPath::Base}}, {0, 0, 0}},
{{TowerType::Leach, {2, TowerPath::Base}}, {0, 0, 0}},
{{TowerType::Leach, {3, TowerPath::Base}}, {0, 0, 0}},
//-----------------------------------------------------------------
{{TowerType::Turret, {1, TowerPath::Base}}, {0.5, 0, 0}},
{{TowerType::Turret, {2, TowerPath::Top}}, {0, 0, 0}},
{{TowerType::Turret, {3, TowerPath::Top}}, {0, 0, 0}},
{{TowerType::Turret, {2, TowerPath::Bottom}}, {0, 0, 0}},
{{TowerType::Turret, {3, TowerPath::Bottom}}, {0, 0, 0}},
//-----------------------------------------------------------------
{{TowerType::Necromancer, {1, TowerPath::Base}}, {2, 0, 11}},
{{TowerType::Necromancer, {2, TowerPath::Base}}, {1, 0, 14}},
{{TowerType::Necromancer, {3, TowerPath::Top}}, {1, 0, 15}},
{{TowerType::Necromancer, {3, TowerPath::Bottom}}, {0, 30, 0}},
};
const TowerStats* GetTowerStats(TowerType type, TowerLevel level) {
auto it = TowerConstants.find({ type, level });
if (it == TowerConstants.end()) return nullptr;
return &it->second;
}
static const std::map<TowerType, TowerInfo> TowerInfoConstants = {
{TowerType::Archer, {"Archer", "Shoot projectiles", false}},
{TowerType::Artillery, {"Artillery", "Explosion", false}},
{TowerType::Ice, {"Ice", "Slow down enemies", false}},
{TowerType::Leach, {"Leach", "Shoot projectiles", true}},
{TowerType::Mage, {"Mage", "Set enemies on fire", false}},
{TowerType::Necromancer, {"Necromancer", "Summon troops", true}},
{TowerType::Poison, {"Poison", "Poison enemies", false}},
{TowerType::Quake, {"Quake", "Shoot projectiles", false}},
{TowerType::Sorcerer, {"Sorcerer", "Summon friendly troops", false}},
{TowerType::Turret, {"Turret", "Shoot arrow very fast", true}},
{TowerType::Zeus, {"Zeus", "Strike lightning", false}},
};
const TowerInfo& GetTowerInfo(TowerType type) {
return TowerInfoConstants.at(type);
}
namespace TowerFactory {
using TowerCreator = std::function<std::shared_ptr<Tower>(TowerID, std::int32_t, std::int32_t, PlayerID)>;
static const std::map<TowerType, TowerCreator> towerFactory = {
{TowerType::Archer, [](TowerID id, std::int32_t x, std::int32_t y, PlayerID builder) -> TowerPtr {return std::make_shared<ArcherTower>(id, x, y , builder);} },
{TowerType::Artillery, [](TowerID id, std::int32_t x, std::int32_t y, PlayerID builder) -> TowerPtr {return std::make_shared<ArtilleryTower>(id, x, y , builder);} },
{TowerType::Ice, [](TowerID id, std::int32_t x, std::int32_t y, PlayerID builder) -> TowerPtr {return std::make_shared<IceTower>(id, x, y , builder);} },
{TowerType::Mage, [](TowerID id, std::int32_t x, std::int32_t y, PlayerID builder) -> TowerPtr {return std::make_shared<MageTower>(id, x, y , builder);} },
{TowerType::Poison, [](TowerID id, std::int32_t x, std::int32_t y, PlayerID builder) -> TowerPtr {return std::make_shared<PoisonTower>(id, x, y , builder);} },
{TowerType::Quake, [](TowerID id, std::int32_t x, std::int32_t y, PlayerID builder) -> TowerPtr {return std::make_shared<QuakeTower>(id, x, y , builder);} },
{TowerType::Sorcerer, [](TowerID id, std::int32_t x, std::int32_t y, PlayerID builder) -> TowerPtr {return std::make_shared<SorcererTower>(id, x, y , builder);} },
{TowerType::Zeus, [](TowerID id, std::int32_t x, std::int32_t y, PlayerID builder) -> TowerPtr {return std::make_shared<ZeusTower>(id, x, y , builder);} },
{TowerType::Leach, [](TowerID id, std::int32_t x, std::int32_t y, PlayerID builder) -> TowerPtr {return std::make_shared<LeachTower>(id, x, y , builder);} },
{TowerType::Necromancer, [](TowerID id, std::int32_t x, std::int32_t y, PlayerID builder) -> TowerPtr {return std::make_shared<NecromancerTower>(id, x, y , builder);} },
{TowerType::Turret, [](TowerID id, std::int32_t x, std::int32_t y, PlayerID builder) -> TowerPtr {return std::make_shared<TurretTower>(id, x, y , builder);} },
};
TowerPtr CreateTower(TowerType type, TowerID id, std::int32_t x, std::int32_t y, PlayerID builder) {
return towerFactory.at(type)(id, x, y, builder);
}
std::string GetTowerName(TowerType type) {
switch (type) {
case TowerType::Archer:
return "Archer";
case TowerType::Artillery:
return "Artillery";
case TowerType::Ice:
return "Ice";
case TowerType::Mage:
return "Mage";
case TowerType::Poison:
return "Poison";
case TowerType::Quake:
return "Quake";
case TowerType::Sorcerer:
return "Sorcerer";
case TowerType::Zeus:
return "Zeus";
case TowerType::Leach:
return "Leach";
case TowerType::Necromancer:
return "Necromancer";
case TowerType::Turret:
return "Turret";
default:
return "Unknow";
}
}
} // namespace TowerFactory
} // namespace game
} // namespace td

326
src/td/game/World.cpp Normal file
View File

@@ -0,0 +1,326 @@
#include "td/game/World.h"
#include "td/protocol/PacketDispatcher.h"
#include "td/protocol/Protocol.h"
#include "td/protocol/packets/WorldBeginDataPacket.h"
#include "td/protocol/packets/WorldDataPacket.h"
#include "td/game/BaseGame.h"
#include "td/misc/Random.h"
#include "td/misc/Compression.h"
#include "td/misc/Format.h"
#include <cmath>
namespace td {
namespace 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 {
std::int16_t chunkX = x / Chunk::ChunkWidth;
std::int16_t chunkY = y / Chunk::ChunkHeight;
std::uint16_t subChunkX = std::abs(x % Chunk::ChunkWidth);
std::uint16_t subChunkY = std::abs(y % Chunk::ChunkHeight);
auto chunkIt = m_Chunks.find({ chunkX, chunkY });
if (chunkIt == m_Chunks.end())
return nullptr;
ChunkPtr chunk = chunkIt->second;
return GetTilePtr(chunk->GetTileIndex(subChunkY * Chunk::ChunkWidth + subChunkX));
}
bool World::LoadMap(const protocol::WorldBeginDataPacket* worldHeader) {
m_TowerPlacePalette = worldHeader->GetTowerTilePalette();
m_WalkablePalette = worldHeader->GetWalkableTileColor();
m_DecorationPalette = worldHeader->GetDecorationPalette();
m_Background = worldHeader->GetBackgroundColor();
GetRedTeam().GetSpawn() = worldHeader->GetRedSpawn();
GetBlueTeam().GetSpawn() = worldHeader->GetBlueSpawn();
m_SpawnColorPalette = worldHeader->GetSpawnPalette();
GetRedTeam().GetCastle() = worldHeader->GetRedCastle();
GetRedTeam().GetCastle().SetTeam(&GetRedTeam());
GetBlueTeam().GetCastle() = worldHeader->GetBlueCastle();
GetBlueTeam().GetCastle().SetTeam(&GetBlueTeam());
m_TilePalette = worldHeader->GetTilePalette();
return true;
}
bool World::LoadMap(const protocol::WorldDataPacket* worldData) {
m_Chunks = worldData->GetChunks();
return true;
}
bool World::LoadMapFromFile(const std::string& fileName) {
DataBuffer buffer;
if (!buffer.ReadFile(fileName)) {
utils::LOGE(utils::format("Failed to load map from file %s", fileName.c_str()));
return false;
}
utils::LOG(utils::format("Read file : %s (File size : %u)", fileName.c_str(), buffer.GetSize()));
DataBuffer mapHeaderPacketBuffer = utils::Decompress(buffer);
DataBuffer mapDataPacketBuffer = utils::Decompress(buffer);
protocol::WorldBeginDataPacket headerPacket;
headerPacket.Deserialize(mapHeaderPacketBuffer);
protocol::WorldDataPacket dataPacket;
dataPacket.Deserialize(mapDataPacketBuffer);
LoadMap(&headerPacket);
LoadMap(&dataPacket);
return true;
}
bool World::SaveMap(const std::string& fileName) const {
protocol::WorldBeginDataPacket headerPacket(this);
protocol::WorldDataPacket dataPacket(this);
DataBuffer mapHeaderCompressed = utils::Compress(headerPacket.Serialize(false));
DataBuffer mapDataCompressed = utils::Compress(dataPacket.Serialize(false));
utils::LOG(utils::format("Header Packet Size : %u", mapHeaderCompressed.GetSize()));
utils::LOG(utils::format("World Data Packet Size : %u", mapDataCompressed.GetSize()));
DataBuffer buffer = mapHeaderCompressed << mapDataCompressed;
utils::LOG(utils::format("Total Size : %u", buffer.GetSize()));
return buffer.WriteFile(fileName);
}
void World::Tick(std::uint64_t delta) {
if (m_Game->GetGameState() != GameState::Game) return;
TickMobs(delta);
for (TowerPtr tower : m_Towers) {
tower->Tick(delta, this);
}
CleanDeadMobs();
}
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);
mob->SetCenter({ x, y });
mob->SetDirection(dir);
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 tower = TowerFactory::CreateTower(type, id, x, y, builder);
m_Towers.push_back(tower);
return tower;
}
TowerPtr World::RemoveTower(TowerID towerId) {
auto it = std::find_if(m_Towers.begin(), m_Towers.end(), [towerId](TowerPtr tower) { return tower->GetID() == towerId;});
if (it == m_Towers.end()) return nullptr;
TowerPtr tower = *it;
m_Towers.erase(it);
return tower;
}
void World::TickMobs(std::uint64_t delta) {
for (MobPtr mob : m_Mobs) {
mob->Tick(delta, this);
}
}
const Color* World::GetTileColor(TilePtr tile) const {
switch (tile->GetType()) {
case TileType::Tower: {
TowerTile* towerTile = dynamic_cast<TowerTile*>(tile.get());
return &m_TowerPlacePalette[towerTile->color_palette_ref];
}
case TileType::Walk: {
return &m_WalkablePalette;
}
case TileType::Decoration: {
DecorationTile* towerTile = dynamic_cast<DecorationTile*>(tile.get());
return &m_DecorationPalette[towerTile->color_palette_ref];
break;
}
default: {
return nullptr;
}
}
return nullptr;
}
bool World::CanPlaceLittleTower(const Vec2f& worldPos, PlayerID playerID) const {
TilePtr tile = GetTile(worldPos.x, worldPos.y);
const Player& player = m_Game->GetPlayers()[playerID];
if (tile == nullptr) {
return false;
}
if (tile->GetType() == game::TileType::Tower) {
const TowerTile* towerTile = dynamic_cast<TowerTile*>(tile.get());
if (towerTile->team_owner != player.GetTeamColor())
return false;
for (int x = -1; x < 2; x++) {
for (int y = -1; y < 2; y++) {
game::TilePtr adjacentTile = GetTile(worldPos.x + x, worldPos.y + y);
if (adjacentTile == nullptr || adjacentTile->GetType() != game::TileType::Tower || GetTower({ worldPos.x + x, worldPos.y + y }) != nullptr) {
return false;
}
}
}
return true;
}
return false;
}
bool World::CanPlaceBigTower(const Vec2f& worldPos, PlayerID playerID) const {
if (!CanPlaceLittleTower(worldPos, playerID)) return false;
TilePtr tile = GetTile(worldPos.x, worldPos.y);
const Player& player = m_Game->GetPlayers()[playerID];
if (tile == nullptr) {
return false;
}
if (tile->GetType() == game::TileType::Tower) {
const TowerTile* towerTile = dynamic_cast<const TowerTile*>(tile.get());
if (towerTile->team_owner != player.GetTeamColor())
return false;
for (int x = -2; x < 3; x++) {
for (int y = -2; y < 3; y++) {
game::TilePtr adjacentTile = GetTile(worldPos.x + x, worldPos.y + y);
if (adjacentTile == nullptr || adjacentTile->GetType() != game::TileType::Tower || GetTower({ worldPos.x + x, worldPos.y + y }) != nullptr) {
return false;
}
}
}
return true;
}
return false;
}
void World::CleanDeadMobs() {
// safely remove mobs when unused
for (std::size_t i = 0; i < m_Mobs.size(); i++) {
MobPtr mob = m_Mobs[i];
if (mob->IsDead()) {
m_Mobs.erase(m_Mobs.begin() + static_cast<int>(i));
}
}
}
TowerPtr World::GetTower(const Vec2f& position) const {
for (TowerPtr tower : m_Towers) {
if (tower->GetSize() == TowerSize::Big) {
if (tower->GetCenterX() - 2.5f < position.x && tower->GetCenterX() + 2.5f > position.x &&
tower->GetCenterY() - 2.5f < position.y && tower->GetCenterY() + 2.5f > position.y) {
return tower;
}
} else { // little tower
if (tower->GetCenterX() - 1.5f < position.x && tower->GetCenterX() + 1.5f > position.x &&
tower->GetCenterY() - 1.5f < position.y && tower->GetCenterY() + 1.5f > position.y) {
return tower;
}
}
}
return nullptr;
}
TowerPtr World::GetTowerById(TowerID towerID) {
auto it = std::find_if(m_Towers.begin(), m_Towers.end(), [towerID](TowerPtr tower) { return tower->GetID() == towerID;});
if (it == m_Towers.end()) return nullptr;
return *it;
}
void World::OnArcherTowerShot(MobPtr tarGet, ArcherTower* shooter) {
bool fireArrows = shooter->GetLevel().GetPath() == TowerPath::Bottom;
bool explosiveArrows = shooter->GetLevel().GetLevel() == 4 && fireArrows;
GetWorldNotifier().NotifyListeners(&WorldListener::OnArrowShot, tarGet, fireArrows, shooter);
if (explosiveArrows) {
GetWorldNotifier().NotifyListeners(&WorldListener::OnExplosion, utils::shape::Circle{ tarGet->GetCenterX(), tarGet->GetCenterY(), ArcherTower::ExplosionRadius }, 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);
}
}
Team& World::GetRedTeam() {
return m_Game->GetRedTeam();
}
const Team& World::GetRedTeam() const {
return m_Game->GetRedTeam();
}
Team& World::GetBlueTeam() {
return m_Game->GetBlueTeam();
}
const Team& World::GetBlueTeam() const {
return m_Game->GetBlueTeam();
}
Team& World::GetTeam(TeamColor team) {
return m_Game->GetTeam(team);
}
const Team& World::GetTeam(TeamColor team) const {
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 td

View File

@@ -0,0 +1,25 @@
#include "td/game/Towers.h"
#include "td/game/World.h"
namespace td {
namespace game {
void ArcherTower::Tick(std::uint64_t delta, World* world) {
if (m_Timer.Update(delta)) {
std::uint8_t arrowsShot = 0;
bool explosiveArrows = GetLevel().GetPath() == TowerPath::Bottom;
std::uint8_t arrows = explosiveArrows ? 2 : GetLevel().GetLevel();
for (MobPtr mob : world->GetMobList()) {
if (IsMobInRange(mob)) {
world->GetWorldNotifier().NotifyListeners(&WorldListener::OnArcherTowerShot, mob, this);
m_Timer.ApplyCooldown();
arrowsShot++;
if (arrowsShot >= arrows)
break;
}
}
}
}
} // namespace game
} // namespace td

View File

@@ -0,0 +1,12 @@
#include "td/game/Towers.h"
#include "td/game/World.h"
namespace td {
namespace game {
void ArtilleryTower::Tick(std::uint64_t delta, World* world) {
}
} // namespace game
} // namespace td

View File

@@ -0,0 +1,22 @@
#include "td/game/Towers.h"
#include "td/game/World.h"
namespace td {
namespace game {
void IceTower::Tick(std::uint64_t delta, World* world) {
if (m_Timer.Update(delta)) {
float damage = GetStats()->GetDamage();
for (MobPtr mob : world->GetMobList()) {
if (IsMobInRange(mob)) {
mob->AddEffect(EffectType::Slowness, 1, this); // slowness for 1s every second
if (damage > 0)
world->GetMobNotifier().NotifyListeners(&MobListener::OnMobDamage, mob.get(), damage, this);
m_Timer.ApplyCooldown();
}
}
}
}
} // namespace game
} // namespace td

View File

@@ -0,0 +1,12 @@
#include "td/game/Towers.h"
#include "td/game/World.h"
namespace td {
namespace game {
void LeachTower::Tick(std::uint64_t delta, World* world) {
}
} // namespace game
} // namespace td

View File

@@ -0,0 +1,19 @@
#include "td/game/Towers.h"
#include "td/game/World.h"
namespace td {
namespace game {
void MageTower::Tick(std::uint64_t delta, World* world) {
if (m_Timer.Update(delta)) {
for (MobPtr mob : world->GetMobList()) {
if (IsMobInRange(mob)) {
mob->AddEffect(EffectType::Fire, GetLevel().GetLevel() * 3, this);
m_Timer.ApplyCooldown();
}
}
}
}
} // namespace game
} // namespace td

View File

@@ -0,0 +1,12 @@
#include "td/game/Towers.h"
#include "td/game/World.h"
namespace td {
namespace game {
void NecromancerTower::Tick(std::uint64_t delta, World* world) {
}
} // namespace game
} // namespace td

View File

@@ -0,0 +1,45 @@
#include "td/game/Towers.h"
#include "td/game/World.h"
namespace td {
namespace game {
void PoisonTower::Tick(std::uint64_t delta, World* world) {
if (m_Timer.Update(delta)) {
for (MobPtr mob : world->GetMobList()) {
if (IsMobInRange(mob)) {
if (GetLevel().GetPath() == TowerPath::Bottom) {
world->GetMobNotifier().NotifyListeners(&MobListener::OnMobDamage, mob.get(), GetStats()->GetDamage(), this);
} else {
float durationSec;
switch (GetLevel().GetLevel()) {
case 1:
durationSec = 5;
break;
case 2:
durationSec = 15;
break;
case 3:
durationSec = 30;
break;
case 4:
durationSec = 1e10; // about 3 million hours. It should be enough
break;
default:
durationSec = 0; // how did we get there ?
break;
}
mob->AddEffect(EffectType::Poison, durationSec, this);
}
m_Timer.ApplyCooldown();
}
}
}
}
} // namespace game
} // namespace td

View File

@@ -0,0 +1,12 @@
#include "td/game/Towers.h"
#include "td/game/World.h"
namespace td {
namespace game {
void QuakeTower::Tick(std::uint64_t delta, World* world) {
}
} // namespace game
} // namespace td

View File

@@ -0,0 +1,12 @@
#include "td/game/Towers.h"
#include "td/game/World.h"
namespace td {
namespace game {
void SorcererTower::Tick(std::uint64_t delta, World* world) {
}
} // namespace game
} // namespace td

View File

@@ -0,0 +1,12 @@
#include "td/game/Towers.h"
#include "td/game/World.h"
namespace td {
namespace game {
void TurretTower::Tick(std::uint64_t delta, World* world) {
}
} // namespace game
} // namespace td

View File

@@ -0,0 +1,12 @@
#include "td/game/Towers.h"
#include "td/game/World.h"
namespace td {
namespace game {
void ZeusTower::Tick(std::uint64_t delta, World* world) {
}
} // namespace game
} // namespace td