restructure project
This commit is contained in:
37
src/td/game/BaseGame.cpp
Normal file
37
src/td/game/BaseGame.cpp
Normal 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
511
src/td/game/Mobs.cpp
Normal 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
28
src/td/game/Team.cpp
Normal 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
205
src/td/game/Towers.cpp
Normal 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
326
src/td/game/World.cpp
Normal 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
|
||||
25
src/td/game/towers/ArcherTower.cpp
Normal file
25
src/td/game/towers/ArcherTower.cpp
Normal 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
|
||||
12
src/td/game/towers/ArtilleryTower.cpp
Normal file
12
src/td/game/towers/ArtilleryTower.cpp
Normal 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
|
||||
22
src/td/game/towers/IceTower.cpp
Normal file
22
src/td/game/towers/IceTower.cpp
Normal 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
|
||||
12
src/td/game/towers/LeachTower.cpp
Normal file
12
src/td/game/towers/LeachTower.cpp
Normal 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
|
||||
19
src/td/game/towers/MageTower.cpp
Normal file
19
src/td/game/towers/MageTower.cpp
Normal 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
|
||||
12
src/td/game/towers/Necromancer.cpp
Normal file
12
src/td/game/towers/Necromancer.cpp
Normal 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
|
||||
45
src/td/game/towers/PoisonTower.cpp
Normal file
45
src/td/game/towers/PoisonTower.cpp
Normal 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
|
||||
12
src/td/game/towers/QuakeTower.cpp
Normal file
12
src/td/game/towers/QuakeTower.cpp
Normal 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
|
||||
12
src/td/game/towers/SorcererTower.cpp
Normal file
12
src/td/game/towers/SorcererTower.cpp
Normal 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
|
||||
12
src/td/game/towers/TurretTower.cpp
Normal file
12
src/td/game/towers/TurretTower.cpp
Normal 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
|
||||
12
src/td/game/towers/ZeusTower.cpp
Normal file
12
src/td/game/towers/ZeusTower.cpp
Normal 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
|
||||
88
src/td/misc/Compression.cpp
Normal file
88
src/td/misc/Compression.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
#include "td/misc/Compression.h"
|
||||
|
||||
#include <zlib.h>
|
||||
#include <cassert>
|
||||
|
||||
#define COMPRESSION_THRESHOLD 64
|
||||
|
||||
namespace td {
|
||||
namespace utils {
|
||||
|
||||
std::uint64_t Inflate(const std::string& source, std::string& dest) {
|
||||
uLongf size = dest.size();
|
||||
uncompress(reinterpret_cast<Bytef*>(dest.data()), reinterpret_cast<uLongf*>(&size), reinterpret_cast<const Bytef*>(source.c_str()), source.length());
|
||||
return size;
|
||||
}
|
||||
|
||||
std::uint64_t Deflate(const std::string& source, std::string& dest) {
|
||||
uLongf size = source.length();
|
||||
dest.resize(source.size()); // Resize for the compressed data to fit into
|
||||
compress(reinterpret_cast<Bytef*>(dest.data()), reinterpret_cast<uLongf*>(&size), reinterpret_cast<const Bytef*>(source.c_str()), source.length());
|
||||
dest.resize(size); // Resize to cut useless data
|
||||
return size;
|
||||
}
|
||||
|
||||
DataBuffer Compress(const DataBuffer& buffer) {
|
||||
std::string compressedData;
|
||||
DataBuffer packet;
|
||||
|
||||
if (buffer.GetSize() < COMPRESSION_THRESHOLD) {
|
||||
// Don't compress since it's a small packet
|
||||
std::uint64_t dataLength = 0;
|
||||
std::uint64_t packetLength = buffer.GetSize() + sizeof(dataLength);
|
||||
|
||||
packet << packetLength;
|
||||
packet << dataLength;
|
||||
packet << buffer;
|
||||
return packet;
|
||||
}
|
||||
|
||||
Deflate(buffer.ToString(), compressedData);
|
||||
|
||||
std::uint64_t dataLength = buffer.GetSize();
|
||||
std::uint64_t packetLength = compressedData.length() + sizeof(dataLength);
|
||||
|
||||
packet << packetLength;
|
||||
packet << dataLength;
|
||||
packet << compressedData;
|
||||
return packet;
|
||||
}
|
||||
|
||||
DataBuffer Decompress(DataBuffer& buffer, std::uint64_t packetLength) {
|
||||
std::uint64_t uncompressedLength;
|
||||
|
||||
buffer >> uncompressedLength;
|
||||
|
||||
std::uint64_t compressedLength = packetLength - sizeof(uncompressedLength);
|
||||
|
||||
if (uncompressedLength == 0) {
|
||||
// Uncompressed
|
||||
DataBuffer ret;
|
||||
buffer.ReadSome(ret, compressedLength);
|
||||
return ret;
|
||||
}
|
||||
|
||||
assert(buffer.GetReadOffset() + compressedLength <= buffer.GetSize());
|
||||
|
||||
std::string deflatedData;
|
||||
buffer.ReadSome(deflatedData, compressedLength);
|
||||
|
||||
std::string inflated;
|
||||
inflated.resize(uncompressedLength);
|
||||
|
||||
Inflate(deflatedData, inflated);
|
||||
|
||||
assert(inflated.length() == uncompressedLength);
|
||||
return DataBuffer(inflated);
|
||||
}
|
||||
|
||||
DataBuffer Decompress(DataBuffer& buffer) {
|
||||
std::uint64_t packetLength;
|
||||
|
||||
buffer >> packetLength;
|
||||
|
||||
return Decompress(buffer, packetLength);
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
} // namespace td
|
||||
95
src/td/misc/DataBuffer.cpp
Normal file
95
src/td/misc/DataBuffer.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
#include "td/misc/DataBuffer.h"
|
||||
|
||||
#include <iomanip>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
namespace td {
|
||||
|
||||
DataBuffer::DataBuffer() : m_ReadOffset(0) {}
|
||||
DataBuffer::DataBuffer(const DataBuffer& other) : m_Buffer(other.m_Buffer), m_ReadOffset(other.m_ReadOffset) {}
|
||||
DataBuffer::DataBuffer(DataBuffer&& other) : m_Buffer(std::move(other.m_Buffer)), m_ReadOffset(std::move(other.m_ReadOffset)) {}
|
||||
DataBuffer::DataBuffer(const std::string& str) : m_Buffer(str.begin(), str.end()), m_ReadOffset(0) {}
|
||||
|
||||
DataBuffer::DataBuffer(const DataBuffer& other, Data::difference_type offset) : m_ReadOffset(0) {
|
||||
m_Buffer.reserve(other.GetSize() - static_cast<std::size_t>(offset));
|
||||
std::copy(other.m_Buffer.begin() + offset, other.m_Buffer.end(), std::back_inserter(m_Buffer));
|
||||
}
|
||||
|
||||
DataBuffer& DataBuffer::operator=(const DataBuffer& other) {
|
||||
m_Buffer = other.m_Buffer;
|
||||
m_ReadOffset = other.m_ReadOffset;
|
||||
return *this;
|
||||
}
|
||||
|
||||
DataBuffer& DataBuffer::operator=(DataBuffer&& other) {
|
||||
m_Buffer = std::move(other.m_Buffer);
|
||||
m_ReadOffset = std::move(other.m_ReadOffset);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void DataBuffer::SetReadOffset(std::size_t pos) {
|
||||
assert(pos <= GetSize());
|
||||
m_ReadOffset = pos;
|
||||
}
|
||||
|
||||
std::string DataBuffer::ToString() const {
|
||||
return std::string(m_Buffer.begin(), m_Buffer.end());
|
||||
}
|
||||
|
||||
std::size_t DataBuffer::GetSize() const { return m_Buffer.size(); }
|
||||
bool DataBuffer::IsEmpty() const { return m_Buffer.empty(); }
|
||||
std::size_t DataBuffer::GetRemaining() const {
|
||||
return m_Buffer.size() - m_ReadOffset;
|
||||
}
|
||||
|
||||
DataBuffer::iterator DataBuffer::begin() {
|
||||
return m_Buffer.begin();
|
||||
}
|
||||
DataBuffer::iterator DataBuffer::end() {
|
||||
return m_Buffer.end();
|
||||
}
|
||||
DataBuffer::const_iterator DataBuffer::begin() const {
|
||||
return m_Buffer.begin();
|
||||
}
|
||||
DataBuffer::const_iterator DataBuffer::end() const {
|
||||
return m_Buffer.end();
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const DataBuffer& buffer) {
|
||||
for (unsigned char u : buffer)
|
||||
os << std::hex << std::setfill('0') << std::setw(2) << static_cast<int>(u) << " ";
|
||||
os << std::dec;
|
||||
return os;
|
||||
}
|
||||
|
||||
bool DataBuffer::ReadFile(const std::string& fileName) {
|
||||
try {
|
||||
std::ifstream file(fileName, std::istream::binary);
|
||||
std::ostringstream ss;
|
||||
ss << file.rdbuf();
|
||||
const std::string& s = ss.str();
|
||||
m_Buffer = DataBuffer::Data(s.begin(), s.end());
|
||||
m_ReadOffset = 0;
|
||||
} catch (std::exception& e) {
|
||||
std::cerr << "Failed to read file \"" << fileName << "\" reason : " << e.what() << std::endl;
|
||||
return false;
|
||||
}
|
||||
return m_Buffer.size() > 0;
|
||||
}
|
||||
|
||||
bool DataBuffer::WriteFile(const std::string& fileName) {
|
||||
try {
|
||||
std::ofstream file(fileName, std::ostream::binary);
|
||||
file.write(reinterpret_cast<const char*>(m_Buffer.data()), static_cast<std::streamsize>(m_Buffer.size()));
|
||||
file.flush();
|
||||
} catch (std::exception& e) {
|
||||
std::cerr << "Failed to write file \"" << fileName << "\" reason : " << e.what() << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // td
|
||||
|
||||
205
src/td/misc/Easing.cpp
Normal file
205
src/td/misc/Easing.cpp
Normal file
@@ -0,0 +1,205 @@
|
||||
#include "td/misc/Easing.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace td {
|
||||
namespace utils {
|
||||
|
||||
// Found at https://easings.net/
|
||||
|
||||
/* Sine functions */
|
||||
|
||||
float EaseInSine(float x) {
|
||||
return 1.0f - std::cos((x * PI) / 2.0f);
|
||||
}
|
||||
|
||||
float EaseOutSine(float x) {
|
||||
return std::sin((x * PI) / 2.0f);
|
||||
}
|
||||
|
||||
float EaseInOutSine(float x) {
|
||||
return -(std::cos(PI * x) - 1.0f) / 2.0f;
|
||||
}
|
||||
|
||||
/* Cubic functions */
|
||||
|
||||
float EaseInCubic(float x) {
|
||||
return x * EaseInQuad(x);
|
||||
}
|
||||
|
||||
float EaseOutCubic(float x) {
|
||||
return 1 - std::pow(1 - x, 3);
|
||||
}
|
||||
|
||||
float EaseInOutCubic(float x) {
|
||||
return x < 0.5 ? 4 * EaseInCubic(x) : 1 - std::pow(-2 * x + 2, 3) / 2.0f;
|
||||
}
|
||||
|
||||
/* Quint functions */
|
||||
|
||||
float EaseInQuint(float x) {
|
||||
return x * EaseInQuart(x);
|
||||
}
|
||||
|
||||
float EaseOutQuint(float x) {
|
||||
return 1 - std::pow(1 - x, 5);
|
||||
}
|
||||
|
||||
float EaseInOutQuint(float x) {
|
||||
return x < 0.5 ? 16 * EaseInQuint(x) : 1 - std::pow(-2 * x + 2, 5) / 2.0f;
|
||||
}
|
||||
|
||||
/* Circ functions */
|
||||
|
||||
float EaseInCirc(float x) {
|
||||
return 1 - std::sqrt(1 - std::pow(x, 2));
|
||||
}
|
||||
|
||||
float EaseOutCirc(float x) {
|
||||
return std::sqrt(1 - std::pow(x - 1, 2));
|
||||
}
|
||||
|
||||
float EaseInOutCirc(float x) {
|
||||
return x < 0.5
|
||||
? (1 - std::sqrt(1 - std::pow(2 * x, 2))) / 2.0f
|
||||
: (std::sqrt(1 - std::pow(-2 * x + 2, 2)) + 1) / 2.0f;
|
||||
}
|
||||
|
||||
/* Elastic functions */
|
||||
|
||||
float EaseInElastic(float x) {
|
||||
const float c4 = (2 * PI) / 3.0f;
|
||||
|
||||
return x == 0
|
||||
? 0
|
||||
: x == 1
|
||||
? 1
|
||||
: -std::pow(2, 10 * x - 10) * std::sin((x * 10 - 10.75) * c4);
|
||||
}
|
||||
|
||||
float EaseOutElastic(float x) {
|
||||
const float c4 = (2 * PI) / 3.0f;
|
||||
|
||||
return x == 0
|
||||
? 0
|
||||
: x == 1
|
||||
? 1
|
||||
: std::pow(2, -10 * x) * std::sin((x * 10 - 0.75) * c4) + 1;
|
||||
}
|
||||
|
||||
float EaseInOutElastic(float x) {
|
||||
const float c5 = (2 * PI) / 4.5;
|
||||
|
||||
return x == 0
|
||||
? 0
|
||||
: x == 1
|
||||
? 1
|
||||
: x < 0.5
|
||||
? -(std::pow(2, 20 * x - 10) * std::sin((20 * x - 11.125) * c5)) / 2.0f
|
||||
: (std::pow(2, -20 * x + 10) * std::sin((20 * x - 11.125) * c5)) / 2.0f + 1;
|
||||
}
|
||||
|
||||
/* Quad functions */
|
||||
|
||||
float EaseInQuad(float x) {
|
||||
return x * x;
|
||||
}
|
||||
|
||||
float EaseOutQuad(float x) {
|
||||
return 1 - (1 - x) * (1 - x);
|
||||
}
|
||||
|
||||
float EaseInOutQuad(float x) {
|
||||
return x < 0.5 ? 2 * x * x : 1 - std::pow(-2 * x + 2, 2) / 2.0f;
|
||||
}
|
||||
|
||||
/* Quart functions */
|
||||
|
||||
float EaseInQuart(float x) {
|
||||
return x * EaseInCubic(x);
|
||||
}
|
||||
|
||||
float EaseOutQuart(float x) {
|
||||
return 1 - std::pow(1 - x, 4);
|
||||
}
|
||||
|
||||
float EaseInOutQuart(float x) {
|
||||
return x < 0.5 ? 8 * EaseInQuart(x) : 1 - std::pow(-2 * x + 2, 4) / 2.0f;
|
||||
}
|
||||
|
||||
/* Expo functions */
|
||||
|
||||
float EaseInExpo(float x) {
|
||||
return x == 0 ? 0 : std::pow(2, 10 * x - 10);
|
||||
}
|
||||
|
||||
float EaseOutExpo(float x) {
|
||||
return x == 1 ? 1 : 1 - std::pow(2, -10 * x);
|
||||
}
|
||||
|
||||
float EaseInOutExpo(float x) {
|
||||
return x == 0
|
||||
? 0
|
||||
: x == 1
|
||||
? 1
|
||||
: x < 0.5 ? std::pow(2, 20 * x - 10) / 2.0f
|
||||
: (2 - std::pow(2, -20 * x + 10)) / 2.0f;
|
||||
}
|
||||
|
||||
/* Back functions */
|
||||
|
||||
float EaseInBack(float x) {
|
||||
const float c1 = 1.70158;
|
||||
const float c3 = c1 + 1;
|
||||
|
||||
return c3 * EaseInCubic(x) - c1 * EaseInQuad(x);
|
||||
}
|
||||
|
||||
float EaseOutBack(float x) {
|
||||
const float c1 = 1.70158;
|
||||
const float c3 = c1 + 1;
|
||||
|
||||
return 1 + c3 * std::pow(x - 1, 3) + c1 * std::pow(x - 1, 2);
|
||||
}
|
||||
|
||||
float EaseInOutBack(float x) {
|
||||
const float c1 = 1.70158;
|
||||
const float c2 = c1 * 1.525;
|
||||
|
||||
return x < 0.5
|
||||
? (std::pow(2 * x, 2) * ((c2 + 1) * 2 * x - c2)) / 2.0f
|
||||
: (std::pow(2 * x - 2, 2) * ((c2 + 1) * (x * 2 - 2) + c2) + 2) / 2.0f;
|
||||
}
|
||||
|
||||
/* Bounce functions */
|
||||
|
||||
float EaseInBounce(float x) {
|
||||
return 1 - EaseOutBounce(1 - x);
|
||||
}
|
||||
|
||||
float EaseOutBounce(float x) {
|
||||
const float n1 = 7.5625;
|
||||
const float d1 = 2.75;
|
||||
|
||||
if (x < 1 / d1) {
|
||||
return n1 * EaseInQuad(x);
|
||||
} else if (x < 2 / d1) {
|
||||
x -= 1.5;
|
||||
return n1 * (x / d1) * x + 0.75;
|
||||
} else if (x < 2.5 / d1) {
|
||||
x -= 2.25;
|
||||
return n1 * (x / d1) * x + 0.9375;
|
||||
} else {
|
||||
x -= 2.625;
|
||||
return n1 * (x / d1) * x + 0.984375;
|
||||
}
|
||||
}
|
||||
|
||||
float EaseInOutBounce(float x) {
|
||||
return x < 0.5
|
||||
? (1 - EaseOutBounce(1 - 2 * x)) / 2.0f
|
||||
: (1 + EaseOutBounce(2 * x - 1)) / 2.0f;
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
} // namespace td
|
||||
35
src/td/misc/Log.cpp
Normal file
35
src/td/misc/Log.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#include "td/misc/Log.h"
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <android/log.h>
|
||||
#else
|
||||
#include <iostream>
|
||||
#endif
|
||||
|
||||
namespace td {
|
||||
namespace utils {
|
||||
|
||||
void LOG(const std::string& msg) {
|
||||
#ifdef __ANDROID__
|
||||
__android_log_print(ANDROID_LOG_INFO, "TRACKERS", "%s", msg.c_str());
|
||||
#else
|
||||
std::cout << msg << "\n";
|
||||
#endif
|
||||
}
|
||||
|
||||
void LOGD(const std::string& msg) {
|
||||
#if !defined(NDEBUG)
|
||||
LOG(msg);
|
||||
#endif
|
||||
}
|
||||
|
||||
void LOGE(const std::string& err) {
|
||||
#ifdef __ANDROID__
|
||||
__android_log_print(ANDROID_LOG_ERROR, "TRACKERS", "%s", err.c_str());
|
||||
#else
|
||||
std::cerr << err << "\n";
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace td
|
||||
90
src/td/misc/Maths.cpp
Normal file
90
src/td/misc/Maths.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
#include "td/misc/Maths.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace td {
|
||||
namespace maths {
|
||||
|
||||
Mat4f Perspective(float fovY, float aspectRatio, float zNear, float zFar) {
|
||||
const float tanHalfFovy = std::tan(fovY / 2.0f);
|
||||
|
||||
Mat4f result{};
|
||||
result.x0 = 1.0f / (aspectRatio * tanHalfFovy);
|
||||
result.y1 = 1.0f / (tanHalfFovy);
|
||||
result.z2 = -(zFar + zNear) / (zFar - zNear);
|
||||
result.z3 = -1.0f;
|
||||
result.w2 = -(2.0f * zFar * zNear) / (zFar - zNear);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Mat4f Look(const Vec3f& eye, const Vec3f& front, const Vec3f& up) {
|
||||
const Vec3f f = Normalize(front);
|
||||
const Vec3f s = Normalize(Cross(f, up));
|
||||
const Vec3f u = Cross(s, f);
|
||||
|
||||
Mat4f result = Identity<float>();
|
||||
result.x0 = s.x;
|
||||
result.y0 = s.y;
|
||||
result.z0 = s.z;
|
||||
result.x1 = u.x;
|
||||
result.y1 = u.y;
|
||||
result.z1 = u.z;
|
||||
result.x2 = -f.x;
|
||||
result.y2 = -f.y;
|
||||
result.z2 = -f.z;
|
||||
result.w0 = -Dot(s, eye);
|
||||
result.w1 = -Dot(u, eye);
|
||||
result.w2 = Dot(f, eye);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Mat4f Inverse(const Mat4f& mat) {
|
||||
float s0 = mat.at(0, 0) * mat.at(1, 1) - mat.at(1, 0) * mat.at(0, 1);
|
||||
float s1 = mat.at(0, 0) * mat.at(1, 2) - mat.at(1, 0) * mat.at(0, 2);
|
||||
float s2 = mat.at(0, 0) * mat.at(1, 3) - mat.at(1, 0) * mat.at(0, 3);
|
||||
float s3 = mat.at(0, 1) * mat.at(1, 2) - mat.at(1, 1) * mat.at(0, 2);
|
||||
float s4 = mat.at(0, 1) * mat.at(1, 3) - mat.at(1, 1) * mat.at(0, 3);
|
||||
float s5 = mat.at(0, 2) * mat.at(1, 3) - mat.at(1, 2) * mat.at(0, 3);
|
||||
|
||||
float c5 = mat.at(2, 2) * mat.at(3, 3) - mat.at(3, 2) * mat.at(2, 3);
|
||||
float c4 = mat.at(2, 1) * mat.at(3, 3) - mat.at(3, 1) * mat.at(2, 3);
|
||||
float c3 = mat.at(2, 1) * mat.at(3, 2) - mat.at(3, 1) * mat.at(2, 2);
|
||||
float c2 = mat.at(2, 0) * mat.at(3, 3) - mat.at(3, 0) * mat.at(2, 3);
|
||||
float c1 = mat.at(2, 0) * mat.at(3, 2) - mat.at(3, 0) * mat.at(2, 2);
|
||||
float c0 = mat.at(2, 0) * mat.at(3, 1) - mat.at(3, 0) * mat.at(2, 1);
|
||||
|
||||
float det = s0 * c5 - s1 * c4 + s2 * c3 + s3 * c2 - s4 * c1 + s5 * c0;
|
||||
|
||||
assert(det != 0 && "Determinant equals 0 !");
|
||||
|
||||
float invdet = 1.0 / det;
|
||||
|
||||
Mat4f result;
|
||||
|
||||
result.at(0, 0) = ( mat.at(1, 1) * c5 - mat.at(1, 2) * c4 + mat.at(1, 3) * c3) * invdet;
|
||||
result.at(0, 1) = (-mat.at(0, 1) * c5 + mat.at(0, 2) * c4 - mat.at(0, 3) * c3) * invdet;
|
||||
result.at(0, 2) = ( mat.at(3, 1) * s5 - mat.at(3, 2) * s4 + mat.at(3, 3) * s3) * invdet;
|
||||
result.at(0, 3) = (-mat.at(2, 1) * s5 + mat.at(2, 2) * s4 - mat.at(2, 3) * s3) * invdet;
|
||||
|
||||
result.at(1, 0) = (-mat.at(1, 0) * c5 + mat.at(1, 2) * c2 - mat.at(1, 3) * c1) * invdet;
|
||||
result.at(1, 1) = ( mat.at(0, 0) * c5 - mat.at(0, 2) * c2 + mat.at(0, 3) * c1) * invdet;
|
||||
result.at(1, 2) = (-mat.at(3, 0) * s5 + mat.at(3, 2) * s2 - mat.at(3, 3) * s1) * invdet;
|
||||
result.at(1, 3) = ( mat.at(2, 0) * s5 - mat.at(2, 2) * s2 + mat.at(2, 3) * s1) * invdet;
|
||||
|
||||
result.at(2, 0) = ( mat.at(1, 0) * c4 - mat.at(1, 1) * c2 + mat.at(1, 3) * c0) * invdet;
|
||||
result.at(2, 1) = (-mat.at(0, 0) * c4 + mat.at(0, 1) * c2 - mat.at(0, 3) * c0) * invdet;
|
||||
result.at(2, 2) = ( mat.at(3, 0) * s4 - mat.at(3, 1) * s2 + mat.at(3, 3) * s0) * invdet;
|
||||
result.at(2, 3) = (-mat.at(2, 0) * s4 + mat.at(2, 1) * s2 - mat.at(2, 3) * s0) * invdet;
|
||||
|
||||
result.at(3, 0) = (-mat.at(1, 0) * c3 + mat.at(1, 1) * c1 - mat.at(1, 2) * c0) * invdet;
|
||||
result.at(3, 1) = ( mat.at(0, 0) * c3 - mat.at(0, 1) * c1 + mat.at(0, 2) * c0) * invdet;
|
||||
result.at(3, 2) = (-mat.at(3, 0) * s3 + mat.at(3, 1) * s1 - mat.at(3, 2) * s0) * invdet;
|
||||
result.at(3, 3) = ( mat.at(2, 0) * s3 - mat.at(2, 1) * s1 + mat.at(2, 2) * s0) * invdet;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace maths
|
||||
} // namespace td
|
||||
85
src/td/misc/Shapes.cpp
Normal file
85
src/td/misc/Shapes.cpp
Normal file
@@ -0,0 +1,85 @@
|
||||
#include "td/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
|
||||
67
src/td/misc/Time.cpp
Normal file
67
src/td/misc/Time.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include "td/misc/Time.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <algorithm>
|
||||
|
||||
namespace td {
|
||||
namespace utils {
|
||||
|
||||
void AutoTimer::Update() {
|
||||
m_InternalTime += GetTime() - m_LastTime;
|
||||
if (m_InternalTime >= m_Interval) {
|
||||
if (m_Function != nullptr)
|
||||
m_Function();
|
||||
m_InternalTime %= m_Interval;
|
||||
}
|
||||
m_LastTime = GetTime();
|
||||
}
|
||||
|
||||
void AutoTimer::Update(std::uint64_t delta) {
|
||||
m_InternalTime += delta;
|
||||
if (m_InternalTime >= m_Interval) {
|
||||
if (m_Function != nullptr)
|
||||
m_Function();
|
||||
m_InternalTime %= m_Interval;
|
||||
}
|
||||
m_LastTime = GetTime();
|
||||
}
|
||||
|
||||
void AutoTimer::Reset() {
|
||||
m_InternalTime = 0;
|
||||
m_LastTime = GetTime();
|
||||
}
|
||||
|
||||
bool Timer::Update(std::uint64_t delta) {
|
||||
m_InternalTime += delta;
|
||||
if (m_InternalTime >= m_Interval) {
|
||||
m_InternalTime %= m_Interval;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Timer::Reset() {
|
||||
m_InternalTime = 0; // let the timer active once at the beginning
|
||||
}
|
||||
|
||||
bool CooldownTimer::Update(std::uint64_t delta) {
|
||||
if (m_Cooldown > 0) {
|
||||
m_Cooldown = static_cast<std::uint64_t>(std::max(static_cast<std::int64_t>(0), static_cast<std::int64_t>(m_Cooldown - delta)));
|
||||
}
|
||||
return m_Cooldown == 0;
|
||||
}
|
||||
|
||||
void CooldownTimer::Reset() {
|
||||
m_Cooldown = 0; // let the timer active once at the beginning
|
||||
}
|
||||
|
||||
void CooldownTimer::ApplyCooldown() {
|
||||
m_Cooldown = m_CooldownTime;
|
||||
}
|
||||
|
||||
std::uint64_t GetTime() {
|
||||
return static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock().now().time_since_epoch()).count());
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
} // namespace td
|
||||
76
src/td/network/Connexion.cpp
Normal file
76
src/td/network/Connexion.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
#include "td/network/Connexion.h"
|
||||
#include "td/protocol/PacketDispatcher.h"
|
||||
#include "td/protocol/PacketFactory.h"
|
||||
#include "td/misc/Compression.h"
|
||||
|
||||
#include "td/misc/Time.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
Connexion::Connexion() : protocol::PacketHandler(nullptr) {
|
||||
|
||||
}
|
||||
|
||||
Connexion::Connexion(Connexion&& move) : protocol::PacketHandler(&m_Dispatcher), m_Socket(std::move(move.m_Socket)) {
|
||||
|
||||
}
|
||||
|
||||
Connexion::Connexion(protocol::PacketDispatcher* dispatcher) : protocol::PacketHandler(dispatcher) {
|
||||
|
||||
}
|
||||
|
||||
Connexion::Connexion(protocol::PacketDispatcher* dispatcher, network::TCPSocket& socket) : protocol::PacketHandler(dispatcher), m_Socket(std::move(socket)) {
|
||||
|
||||
}
|
||||
|
||||
bool Connexion::UpdateSocket() {
|
||||
if (m_Socket.GetStatus() != network::Socket::Connected)
|
||||
return false;
|
||||
|
||||
while (true) {
|
||||
DataBuffer buffer;
|
||||
m_Socket.Receive(buffer, sizeof(std::uint64_t));
|
||||
if (buffer.GetSize() == 0)
|
||||
break;
|
||||
std::uint64_t packetLenght;
|
||||
buffer >> packetLenght;
|
||||
|
||||
m_Socket.Receive(buffer, packetLenght);
|
||||
|
||||
DataBuffer decompressed = utils::Decompress(buffer, packetLenght);
|
||||
|
||||
protocol::PacketType packetType;
|
||||
decompressed >> packetType;
|
||||
|
||||
PacketPtr packet = protocol::PacketFactory::CreatePacket(packetType, decompressed);
|
||||
GetDispatcher()->Dispatch(packet);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Connexion::Connect(const std::string& address, std::uint16_t port) {
|
||||
if (!m_Socket.Connect(address, port)) {
|
||||
return false;
|
||||
}
|
||||
m_Socket.SetBlocking(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Connexion::SendPacket(const protocol::Packet* packet) {
|
||||
network::SendPacket(packet->Serialize(), m_Socket);
|
||||
}
|
||||
|
||||
void Connexion::CloseConnection() {
|
||||
m_Socket.Disconnect();
|
||||
}
|
||||
|
||||
Connexion::~Connexion() {
|
||||
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
} // namespace td
|
||||
125
src/td/network/IPAddress.cpp
Normal file
125
src/td/network/IPAddress.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
#include "td/network/IPAddress.h"
|
||||
|
||||
#include <regex>
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
|
||||
namespace {
|
||||
|
||||
const std::regex IPRegex(R":(^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$):");
|
||||
const std::wregex IPRegexW(LR":(^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$):");
|
||||
|
||||
} // ns
|
||||
|
||||
namespace td {
|
||||
namespace network {
|
||||
|
||||
/* Create an invalid address */
|
||||
IPAddress::IPAddress() noexcept
|
||||
: m_Address(0), m_Valid(false) {
|
||||
|
||||
}
|
||||
|
||||
/* Initialize by string IP */
|
||||
IPAddress::IPAddress(const std::string& ip)
|
||||
: m_Address(0), m_Valid(false) {
|
||||
|
||||
std::sregex_iterator begin(ip.begin(), ip.end(), IPRegex);
|
||||
std::sregex_iterator end;
|
||||
|
||||
if (begin == end) return; // m_Valid = false
|
||||
|
||||
std::smatch match = *begin;
|
||||
|
||||
std::uint8_t octet1 = std::stoul(std::string(match[1]));
|
||||
std::uint8_t octet2 = std::stoul(std::string(match[2]));
|
||||
std::uint8_t octet3 = std::stoul(std::string(match[3]));
|
||||
std::uint8_t octet4 = std::stoul(std::string(match[4]));
|
||||
|
||||
m_Address = static_cast<std::uint32_t>((octet1 << 24) | (octet2 << 16) | (octet3 << 8) | octet4);
|
||||
m_Valid = true;
|
||||
}
|
||||
|
||||
IPAddress::IPAddress(const std::wstring& ip)
|
||||
: m_Address(0), m_Valid(false) {
|
||||
|
||||
std::wsregex_iterator begin(ip.begin(), ip.end(), IPRegexW);
|
||||
std::wsregex_iterator end;
|
||||
|
||||
if (begin == end) return; // m_Valid = false
|
||||
|
||||
std::wsmatch match = *begin;
|
||||
|
||||
std::uint8_t octet1 = std::stoul(match[1]);
|
||||
std::uint8_t octet2 = std::stoul(match[2]);
|
||||
std::uint8_t octet3 = std::stoul(match[3]);
|
||||
std::uint8_t octet4 = std::stoul(match[4]);
|
||||
|
||||
m_Address = static_cast<std::uint32_t>((octet1 << 24) | (octet2 << 16) | (octet3 << 8) | octet4);
|
||||
m_Valid = true;
|
||||
}
|
||||
|
||||
/* Initialize by octets */
|
||||
IPAddress::IPAddress(uint8_t octet1, uint8_t octet2, uint8_t octet3, uint8_t octet4) noexcept
|
||||
: m_Valid(true) {
|
||||
m_Address = static_cast<std::uint32_t>((octet1 << 24) | (octet2 << 16) | (octet3 << 8) | octet4);
|
||||
}
|
||||
|
||||
/* Get the specific octet */
|
||||
uint8_t IPAddress::GetOctet(uint8_t num) const {
|
||||
if (num == 0 || num > 4) throw std::invalid_argument("Invalid argument in IPAddress:GetOctet.");
|
||||
|
||||
return (m_Address >> (8 * (4 - num))) & 0xFF;
|
||||
}
|
||||
|
||||
/* Set the specific octet. 1-4 */
|
||||
void IPAddress::SetOctet(uint8_t num, uint8_t value) {
|
||||
if (num == 0 || num > 4) throw std::invalid_argument("Invalid argument in IPAddress:GetOctet.");
|
||||
uint8_t octets[4];
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
octets[i] = (m_Address >> ((3 - i) * 8)) & 0xFF;
|
||||
|
||||
octets[num - 1] = value;
|
||||
|
||||
m_Address = static_cast<std::uint32_t>((octets[0] << 24) | (octets[1] << 16) | (octets[2] << 8) | octets[3]);
|
||||
}
|
||||
|
||||
IPAddress IPAddress::LocalAddress() {
|
||||
return IPAddress(127, 0, 0, 1);
|
||||
}
|
||||
|
||||
std::string IPAddress::ToString() const {
|
||||
std::stringstream ss;
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (i != 0) ss << ".";
|
||||
ss << static_cast<unsigned int>(GetOctet(i + 1));
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
bool IPAddress::operator==(const IPAddress& right) {
|
||||
return m_Address == right.m_Address;
|
||||
}
|
||||
|
||||
bool IPAddress::operator!=(const IPAddress& right) {
|
||||
return !(*this == right);
|
||||
}
|
||||
|
||||
bool IPAddress::operator==(bool b) {
|
||||
return IsValid() == b;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const IPAddress& addr) {
|
||||
return os << addr.ToString();
|
||||
}
|
||||
|
||||
std::wostream& operator<<(std::wostream& os, const IPAddress& addr) {
|
||||
std::string str = addr.ToString();
|
||||
return os << std::wstring(str.begin(), str.end());
|
||||
}
|
||||
|
||||
} // ns network
|
||||
} // ns mc
|
||||
68
src/td/network/Network.cpp
Normal file
68
src/td/network/Network.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#include "td/network/Network.h"
|
||||
|
||||
namespace td {
|
||||
namespace network {
|
||||
|
||||
class NetworkInitializer {
|
||||
public:
|
||||
NetworkInitializer();
|
||||
~NetworkInitializer();
|
||||
|
||||
NetworkInitializer(const NetworkInitializer& rhs) = delete;
|
||||
NetworkInitializer& operator=(const NetworkInitializer& rhs) = delete;
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
NetworkInitializer::NetworkInitializer() {
|
||||
WSADATA wsaData;
|
||||
WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
}
|
||||
NetworkInitializer::~NetworkInitializer() {
|
||||
WSACleanup();
|
||||
}
|
||||
#else
|
||||
NetworkInitializer::NetworkInitializer() {
|
||||
|
||||
}
|
||||
NetworkInitializer::~NetworkInitializer() {
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
NetworkInitializer initializer;
|
||||
|
||||
IPAddresses Dns::Resolve(const std::string& host) {
|
||||
IPAddresses list;
|
||||
addrinfo hints{};
|
||||
addrinfo* addresses = nullptr;
|
||||
|
||||
//hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
|
||||
getaddrinfo(host.c_str(), NULL, &hints, &addresses);
|
||||
|
||||
for (addrinfo* p = addresses; p != NULL; p = p->ai_next) {
|
||||
#ifdef _WIN32
|
||||
//wchar_t straddr[35];
|
||||
//char straddr[512];
|
||||
//DWORD len;
|
||||
//WSAAddressToStringA(p->ai_addr, p->ai_addrlen, NULL, straddr, &len);
|
||||
|
||||
char* straddr = inet_ntoa(((sockaddr_in*)p->ai_addr)->sin_addr);
|
||||
|
||||
#else
|
||||
char straddr[512];
|
||||
|
||||
inet_ntop(p->ai_family, &(reinterpret_cast<sockaddr_in*>(p->ai_addr))->sin_addr, straddr, sizeof(straddr));
|
||||
#endif
|
||||
|
||||
list.push_back(IPAddress(straddr));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
} // ns network
|
||||
} // ns mc
|
||||
80
src/td/network/Socket.cpp
Normal file
80
src/td/network/Socket.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
#include "td/network/Socket.h"
|
||||
|
||||
#include "td/network/IPAddress.h"
|
||||
#include "td/network/Network.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define ioctl ioctlsocket
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace td {
|
||||
namespace network {
|
||||
|
||||
Socket::Socket(Type type)
|
||||
: m_Blocking(false),
|
||||
m_Type(type),
|
||||
m_Status(Disconnected),
|
||||
m_Handle(static_cast<SocketHandle>(INVALID_SOCKET)) {
|
||||
|
||||
}
|
||||
|
||||
Socket::~Socket() {
|
||||
Disconnect();
|
||||
}
|
||||
|
||||
bool Socket::SetBlocking(bool block) {
|
||||
unsigned long mode = block ? 0 : 1;
|
||||
|
||||
if (ioctl(m_Handle, FIONBIO, &mode) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_Blocking = block;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Socket::IsBlocking() const noexcept {
|
||||
return m_Blocking;
|
||||
}
|
||||
|
||||
Socket::Type Socket::GetType() const noexcept {
|
||||
return m_Type;
|
||||
}
|
||||
|
||||
SocketHandle Socket::GetHandle() const noexcept {
|
||||
return m_Handle;
|
||||
}
|
||||
|
||||
void Socket::SetStatus(Socket::Status status) {
|
||||
m_Status = status;
|
||||
}
|
||||
|
||||
Socket::Status Socket::GetStatus() const noexcept {
|
||||
return m_Status;
|
||||
}
|
||||
|
||||
bool Socket::Connect(const std::string& ip, uint16_t port) {
|
||||
IPAddress addr(ip);
|
||||
return Connect(addr, port);
|
||||
}
|
||||
|
||||
std::size_t Socket::Send(const std::string& data) {
|
||||
return this->Send(reinterpret_cast<const unsigned char*>(data.c_str()), data.length());
|
||||
}
|
||||
|
||||
std::size_t Socket::Send(DataBuffer& buffer) {
|
||||
std::string data = buffer.ToString();
|
||||
return this->Send(reinterpret_cast<const unsigned char*>(data.c_str()), data.length());
|
||||
}
|
||||
|
||||
void Socket::Disconnect() {
|
||||
if (m_Handle < 0)
|
||||
closesocket(m_Handle);
|
||||
m_Status = Disconnected;
|
||||
}
|
||||
|
||||
} // ns network
|
||||
} // ns mc
|
||||
71
src/td/network/TCPListener.cpp
Normal file
71
src/td/network/TCPListener.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
#include "td/network/TCPListener.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
#define closesocket close
|
||||
#define SD_BOTH SHUT_RDWR
|
||||
#define ioctlsocket ioctl
|
||||
#endif
|
||||
|
||||
namespace td {
|
||||
namespace network {
|
||||
|
||||
TCPListener::TCPListener() {}
|
||||
|
||||
TCPListener::~TCPListener() {
|
||||
Destroy();
|
||||
}
|
||||
|
||||
bool TCPListener::Listen(uint16_t port, int maxConnections) {
|
||||
if ((m_Handle = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
|
||||
return false;
|
||||
|
||||
struct sockaddr_in address;
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_addr.s_addr = INADDR_ANY;
|
||||
address.sin_port = htons(port);
|
||||
|
||||
if (::bind(m_Handle, reinterpret_cast<sockaddr*>(&address), sizeof(address)) < 0)
|
||||
return false;
|
||||
|
||||
if (::listen(m_Handle, maxConnections) < 0)
|
||||
return false;
|
||||
|
||||
m_Port = port;
|
||||
m_MaxConnections = maxConnections;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TCPListener::Accept(TCPSocket& newSocket) {
|
||||
int addrlen = sizeof(newSocket.m_RemoteAddr);
|
||||
if ((newSocket.m_Handle = ::accept(m_Handle, reinterpret_cast<sockaddr*>(&newSocket.m_RemoteAddr),
|
||||
reinterpret_cast<socklen_t*>(&addrlen))) < 0)
|
||||
return false;
|
||||
newSocket.SetStatus(Socket::Status::Connected);
|
||||
newSocket.SetBlocking(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
void TCPListener::Destroy() {
|
||||
if (m_Handle < 0)
|
||||
::closesocket(m_Handle);
|
||||
}
|
||||
|
||||
bool TCPListener::Close() {
|
||||
if (::shutdown(m_Handle, SD_BOTH) == 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TCPListener::SetBlocking(bool blocking) {
|
||||
unsigned long mode = blocking ? 0 : 1;
|
||||
|
||||
if (::ioctlsocket(m_Handle, FIONBIO, &mode) < 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace network
|
||||
} // namespace td
|
||||
147
src/td/network/TCPSocket.cpp
Normal file
147
src/td/network/TCPSocket.cpp
Normal file
@@ -0,0 +1,147 @@
|
||||
#include "td/network/TCPSocket.h"
|
||||
#include "td/misc/Compression.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WOULDBLOCK WSAEWOULDBLOCK
|
||||
#define MSG_DONTWAIT 0
|
||||
#else
|
||||
#define WOULDBLOCK EWOULDBLOCK
|
||||
#endif
|
||||
|
||||
namespace td {
|
||||
namespace network {
|
||||
|
||||
TCPSocket::TCPSocket() : Socket(Socket::TCP), m_Port(0) {
|
||||
m_Handle = static_cast<SocketHandle>(INVALID_SOCKET);
|
||||
}
|
||||
|
||||
bool TCPSocket::Connect(const IPAddress& address, unsigned short port) {
|
||||
if (this->GetStatus() == Connected)
|
||||
return true;
|
||||
|
||||
struct addrinfo hints {};
|
||||
memset(&hints, 0, sizeof(addrinfo));
|
||||
|
||||
struct addrinfo* result = nullptr;
|
||||
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
|
||||
if (::getaddrinfo(address.ToString().c_str(), std::to_string(static_cast<int>(port)).c_str(), &hints, &result) != 0) {
|
||||
std::cerr << "Failed to get address info !\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((m_Handle = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
|
||||
std::cerr << "Failed to create socket !\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
struct addrinfo* ptr = nullptr;
|
||||
for (ptr = result; ptr != nullptr; ptr = ptr->ai_next) {
|
||||
struct sockaddr* sockaddr = ptr->ai_addr;
|
||||
if (::connect(m_Handle, sockaddr, sizeof(sockaddr_in)) != 0) {
|
||||
std::cerr << "Failed to connect with this address !\n";
|
||||
continue;
|
||||
}
|
||||
m_RemoteAddr = *sockaddr;
|
||||
break;
|
||||
}
|
||||
|
||||
freeaddrinfo(result);
|
||||
|
||||
if (!ptr) {
|
||||
std::cerr << "Could not find a suitable interface for connecting !\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
this->SetStatus(Connected);
|
||||
m_RemoteIP = address;
|
||||
m_Port = port;
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t TCPSocket::Send(const unsigned char* data, size_t size) {
|
||||
if (this->GetStatus() != Connected)
|
||||
return 0;
|
||||
|
||||
size_t sent = 0;
|
||||
|
||||
while (sent < size) {
|
||||
int cur = ::send(m_Handle, reinterpret_cast<const char*>(data + sent), size - sent, 0);
|
||||
if (cur <= 0) {
|
||||
Disconnect();
|
||||
return 0;
|
||||
}
|
||||
sent += static_cast<std::size_t>(cur);
|
||||
}
|
||||
|
||||
return sent;
|
||||
}
|
||||
|
||||
std::size_t TCPSocket::Receive(DataBuffer& buffer, std::size_t amount) {
|
||||
buffer.Resize(amount);
|
||||
buffer.SetReadOffset(0);
|
||||
|
||||
int recvAmount = ::recv(m_Handle, reinterpret_cast<char*>(buffer.data()), amount, 0);
|
||||
if (recvAmount <= 0) {
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
int err = WSAGetLastError();
|
||||
#else
|
||||
int err = errno;
|
||||
#endif
|
||||
if (err == WOULDBLOCK) {
|
||||
buffer.Clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Disconnect();
|
||||
buffer.Clear();
|
||||
return 0;
|
||||
}
|
||||
buffer.Resize(static_cast<std::size_t>(recvAmount));
|
||||
return static_cast<std::size_t>(recvAmount);
|
||||
}
|
||||
|
||||
DataBuffer TCPSocket::Receive(std::size_t amount) {
|
||||
std::unique_ptr<char[]> buf(new char[amount]);
|
||||
|
||||
int received = ::recv(m_Handle, buf.get(), amount, 0);
|
||||
|
||||
if (received <= 0) {
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
int err = WSAGetLastError();
|
||||
#else
|
||||
int err = errno;
|
||||
#endif
|
||||
if (err == WOULDBLOCK)
|
||||
return DataBuffer();
|
||||
|
||||
Disconnect();
|
||||
return DataBuffer();
|
||||
}
|
||||
|
||||
return DataBuffer(std::string(buf.get(), static_cast<std::size_t>(received)));
|
||||
}
|
||||
|
||||
TCPSocket::TCPSocket(TCPSocket&& other) : Socket(TCP) {
|
||||
m_Handle = other.m_Handle;
|
||||
m_Port = other.m_Port;
|
||||
m_RemoteAddr = other.m_RemoteAddr;
|
||||
m_RemoteIP = other.m_RemoteIP;
|
||||
SetStatus(other.GetStatus());
|
||||
SetBlocking(other.IsBlocking());
|
||||
other.m_Handle = static_cast<SocketHandle>(INVALID_SOCKET);
|
||||
}
|
||||
|
||||
void SendPacket(const DataBuffer& data, network::TCPSocket& socket) {
|
||||
DataBuffer compressed = utils::Compress(data);
|
||||
socket.Send(reinterpret_cast<const std::uint8_t*>(compressed.ToString().data()), compressed.GetSize());
|
||||
}
|
||||
|
||||
|
||||
} // ns network
|
||||
} // ns mc
|
||||
103
src/td/network/UDPSocket.cpp
Normal file
103
src/td/network/UDPSocket.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
#include "td/network/UDPSocket.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WOULDBLOCK WSAEWOULDBLOCK
|
||||
#else
|
||||
#define WOULDBLOCK EWOULDBLOCK
|
||||
#endif
|
||||
|
||||
namespace td {
|
||||
namespace network {
|
||||
|
||||
UDPSocket::UDPSocket()
|
||||
: Socket(Socket::UDP), m_Port(0) {
|
||||
m_Handle = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
}
|
||||
|
||||
bool UDPSocket::Connect(const IPAddress& address, unsigned short port) {
|
||||
if (this->GetStatus() == Connected)
|
||||
return true;
|
||||
|
||||
m_RemoteAddr.sin_port = htons(port);
|
||||
m_RemoteAddr.sin_family = AF_INET;
|
||||
m_RemoteAddr.sin_addr.s_addr = inet_addr(address.ToString().c_str());
|
||||
|
||||
this->SetStatus(Connected);
|
||||
m_RemoteIP = IPAddress(address);
|
||||
m_Port = port;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::size_t UDPSocket::Send(const unsigned char* data, std::size_t size) {
|
||||
std::size_t sent = 0;
|
||||
|
||||
if (this->GetStatus() != Connected)
|
||||
return 0;
|
||||
|
||||
while (sent < size) {
|
||||
int cur = ::sendto(m_Handle, reinterpret_cast<const char*>(data + sent), size - sent, 0,
|
||||
reinterpret_cast<sockaddr*>(&m_RemoteAddr), sizeof(sockaddr_in));
|
||||
if (cur <= 0) {
|
||||
Disconnect();
|
||||
return 0;
|
||||
}
|
||||
sent += static_cast<std::size_t>(cur);
|
||||
}
|
||||
|
||||
return sent;
|
||||
}
|
||||
|
||||
DataBuffer UDPSocket::Receive(std::size_t amount) {
|
||||
std::unique_ptr<char[]> buf(new char[amount]);
|
||||
socklen_t slen = sizeof(sockaddr_in);
|
||||
|
||||
int received = ::recvfrom(m_Handle, buf.get(), amount, 0,
|
||||
reinterpret_cast<sockaddr*>(&m_RemoteAddr), &slen);
|
||||
|
||||
if (received <= 0) {
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
int err = WSAGetLastError();
|
||||
#else
|
||||
int err = errno;
|
||||
#endif
|
||||
if (err == WOULDBLOCK)
|
||||
return DataBuffer(std::string(buf.get(), static_cast<std::size_t>(received)));
|
||||
|
||||
Disconnect();
|
||||
return DataBuffer();
|
||||
}
|
||||
|
||||
return DataBuffer(std::string(buf.get(), static_cast<std::size_t>(received)));
|
||||
}
|
||||
|
||||
std::size_t UDPSocket::Receive(DataBuffer& buffer, std::size_t amount) {
|
||||
buffer.Resize(amount);
|
||||
buffer.SetReadOffset(0);
|
||||
|
||||
socklen_t slen = sizeof(sockaddr_in);
|
||||
|
||||
int recvAmount = ::recvfrom(m_Handle, reinterpret_cast<char*>(buffer.data()), amount, 0,
|
||||
reinterpret_cast<sockaddr*>(&m_RemoteAddr), &slen);
|
||||
if (recvAmount <= 0) {
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
int err = WSAGetLastError();
|
||||
#else
|
||||
int err = errno;
|
||||
#endif
|
||||
if (err == WOULDBLOCK) {
|
||||
buffer.Clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Disconnect();
|
||||
buffer.Clear();
|
||||
return 0;
|
||||
}
|
||||
buffer.Resize(static_cast<std::size_t>(recvAmount));
|
||||
return static_cast<std::size_t>(recvAmount);
|
||||
}
|
||||
|
||||
} // ns network
|
||||
} // ns mc
|
||||
37
src/td/protocol/PacketDispatcher.cpp
Normal file
37
src/td/protocol/PacketDispatcher.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#include "td/protocol/PacketDispatcher.h"
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
void PacketDispatcher::RegisterHandler(PacketType type, PacketHandler* handler) {
|
||||
auto found = std::find(m_Handlers[type].begin(), m_Handlers[type].end(), handler);
|
||||
if (found == m_Handlers[type].end())
|
||||
m_Handlers[type].push_back(handler);
|
||||
}
|
||||
|
||||
void PacketDispatcher::UnregisterHandler(PacketType type, PacketHandler* handler) {
|
||||
auto found = std::find(m_Handlers[type].begin(), m_Handlers[type].end(), handler);
|
||||
if (found != m_Handlers[type].end())
|
||||
m_Handlers[type].erase(found);
|
||||
}
|
||||
|
||||
void PacketDispatcher::UnregisterHandler(PacketHandler* handler) {
|
||||
for (auto& pair : m_Handlers) {
|
||||
if (pair.second.empty()) continue;
|
||||
|
||||
PacketType type = pair.first;
|
||||
|
||||
m_Handlers[type].erase(std::remove(m_Handlers[type].begin(), m_Handlers[type].end(), handler), m_Handlers[type].end());
|
||||
}
|
||||
}
|
||||
|
||||
void PacketDispatcher::Dispatch(const PacketPtr& packet) {
|
||||
if (!packet) return;
|
||||
|
||||
PacketType type = packet->GetType();
|
||||
for (PacketHandler* handler : m_Handlers[type])
|
||||
packet->Dispatch(handler);
|
||||
}
|
||||
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
49
src/td/protocol/PacketFactory.cpp
Normal file
49
src/td/protocol/PacketFactory.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#include "td/protocol/PacketFactory.h"
|
||||
#include "td/protocol/Packets.h"
|
||||
#include <map>
|
||||
#include <functional>
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
namespace PacketFactory {
|
||||
|
||||
using PacketCreator = std::function<PacketPtr()>;
|
||||
|
||||
static std::map<PacketType, PacketCreator> packets = {
|
||||
{PacketType::PlayerLogin, []() -> PacketPtr {return std::make_unique<PlayerLoginPacket>();} },
|
||||
{PacketType::WorldBeginData, []() -> PacketPtr {return std::make_unique<WorldBeginDataPacket>();} },
|
||||
{PacketType::WorldData, []() -> PacketPtr {return std::make_unique<WorldDataPacket>();} },
|
||||
{PacketType::KeepAlive, []() -> PacketPtr {return std::make_unique<KeepAlivePacket>();} },
|
||||
{PacketType::UpdateMoney, []() -> PacketPtr {return std::make_unique<UpdateMoneyPacket>();} },
|
||||
{PacketType::UpdateEXP, []() -> PacketPtr {return std::make_unique<UpdateExpPacket>();} },
|
||||
{PacketType::UpdateLobbyTime, []() -> PacketPtr {return std::make_unique<UpdateLobbyTimePacket>(); } },
|
||||
{PacketType::UpdateGameState, []() -> PacketPtr {return std::make_unique<UpdateGameStatePacket>(); } },
|
||||
{PacketType::PlayerList, []() -> PacketPtr {return std::make_unique<PlayerListPacket>(); } },
|
||||
{PacketType::PlayerJoin, []() -> PacketPtr {return std::make_unique<PlayerJoinPacket>(); } },
|
||||
{PacketType::PlayerLeave, []() -> PacketPtr {return std::make_unique<PlayerLeavePacket>(); } },
|
||||
{PacketType::ConnectionInfo, []() -> PacketPtr {return std::make_unique<ConnexionInfoPacket>(); } },
|
||||
{PacketType::SelectTeam, []() -> PacketPtr {return std::make_unique<SelectTeamPacket>(); } },
|
||||
{PacketType::UpdatePlayerTeam, []() -> PacketPtr {return std::make_unique<UpdatePlayerTeamPacket>(); } },
|
||||
{PacketType::Disconnect, []() -> PacketPtr {return std::make_unique<DisconnectPacket>(); } },
|
||||
{PacketType::ServerTps, []() -> PacketPtr {return std::make_unique<ServerTpsPacket>(); } },
|
||||
{PacketType::SpawnMob, []() -> PacketPtr {return std::make_unique<SpawnMobPacket>(); } },
|
||||
{PacketType::PlaceTower, []() -> PacketPtr {return std::make_unique<PlaceTowerPacket>(); } },
|
||||
{PacketType::WorldAddTower, []() -> PacketPtr {return std::make_unique<WorldAddTowerPacket>(); } },
|
||||
{PacketType::RemoveTower, []() -> PacketPtr {return std::make_unique<RemoveTowerPacket>(); } },
|
||||
{PacketType::SendMobs, []() -> PacketPtr {return std::make_unique<SendMobsPacket>(); } },
|
||||
{PacketType::UpgradeTower, []() -> PacketPtr {return std::make_unique<UpgradeTowerPacket>(); } },
|
||||
{PacketType::UpdateCastleLife, []() -> PacketPtr {return std::make_unique<UpdateCastleLifePacket>(); } },
|
||||
{PacketType::UpdateMobStates, []() -> PacketPtr {return std::make_unique<UpdateMobStatesPacket>(); } },
|
||||
{PacketType::PlayerBuyItem, []() -> PacketPtr {return std::make_unique<PlayerBuyItemPacket>(); } },
|
||||
{PacketType::PlayerBuyMobUpgrade, []() -> PacketPtr {return std::make_unique<PlayerBuyMobUpgradePacket>(); } },
|
||||
};
|
||||
|
||||
PacketPtr CreatePacket(PacketType type, DataBuffer& buffer) {
|
||||
PacketPtr packet = packets[type]();
|
||||
packet->Deserialize(buffer);
|
||||
return packet;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
44
src/td/protocol/Protocol.cpp
Normal file
44
src/td/protocol/Protocol.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#include "td/protocol/PacketHandler.h"
|
||||
#include "td/protocol/Packets.h"
|
||||
|
||||
#define REGISTER_DISPATCH_CLASS(className) void className::Dispatch(PacketHandler* handler) const { \
|
||||
handler->HandlePacket(this);\
|
||||
}
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
void Packet::WritePacketID(DataBuffer& data, bool packetID) const {
|
||||
if (packetID)
|
||||
data << GetID();
|
||||
}
|
||||
|
||||
REGISTER_DISPATCH_CLASS(PlayerLoginPacket)
|
||||
REGISTER_DISPATCH_CLASS(WorldBeginDataPacket)
|
||||
REGISTER_DISPATCH_CLASS(WorldDataPacket)
|
||||
REGISTER_DISPATCH_CLASS(KeepAlivePacket)
|
||||
REGISTER_DISPATCH_CLASS(UpdateExpPacket)
|
||||
REGISTER_DISPATCH_CLASS(UpdateMoneyPacket)
|
||||
REGISTER_DISPATCH_CLASS(UpdateLobbyTimePacket)
|
||||
REGISTER_DISPATCH_CLASS(UpdateGameStatePacket)
|
||||
REGISTER_DISPATCH_CLASS(PlayerListPacket)
|
||||
REGISTER_DISPATCH_CLASS(PlayerJoinPacket)
|
||||
REGISTER_DISPATCH_CLASS(PlayerLeavePacket)
|
||||
REGISTER_DISPATCH_CLASS(ConnexionInfoPacket)
|
||||
REGISTER_DISPATCH_CLASS(SelectTeamPacket)
|
||||
REGISTER_DISPATCH_CLASS(UpdatePlayerTeamPacket)
|
||||
REGISTER_DISPATCH_CLASS(DisconnectPacket)
|
||||
REGISTER_DISPATCH_CLASS(ServerTpsPacket)
|
||||
REGISTER_DISPATCH_CLASS(SpawnMobPacket)
|
||||
REGISTER_DISPATCH_CLASS(PlaceTowerPacket)
|
||||
REGISTER_DISPATCH_CLASS(WorldAddTowerPacket)
|
||||
REGISTER_DISPATCH_CLASS(RemoveTowerPacket)
|
||||
REGISTER_DISPATCH_CLASS(SendMobsPacket)
|
||||
REGISTER_DISPATCH_CLASS(UpgradeTowerPacket)
|
||||
REGISTER_DISPATCH_CLASS(UpdateCastleLifePacket)
|
||||
REGISTER_DISPATCH_CLASS(UpdateMobStatesPacket)
|
||||
REGISTER_DISPATCH_CLASS(PlayerBuyItemPacket)
|
||||
REGISTER_DISPATCH_CLASS(PlayerBuyMobUpgradePacket)
|
||||
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
19
src/td/protocol/packets/ConnectionInfoPacket.cpp
Normal file
19
src/td/protocol/packets/ConnectionInfoPacket.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "td/protocol/packets/ConnectionInfoPacket.h"
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
DataBuffer ConnexionInfoPacket::Serialize(bool packetID) const {
|
||||
DataBuffer data;
|
||||
|
||||
WritePacketID(data, packetID);
|
||||
data << m_ConnectionID;
|
||||
return data;
|
||||
}
|
||||
|
||||
void ConnexionInfoPacket::Deserialize(DataBuffer& data) {
|
||||
data >> m_ConnectionID;
|
||||
}
|
||||
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
19
src/td/protocol/packets/DisconnectPacket.cpp
Normal file
19
src/td/protocol/packets/DisconnectPacket.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "td/protocol/packets/DisconnectPacket.h"
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
DataBuffer DisconnectPacket::Serialize(bool packetID) const {
|
||||
DataBuffer data;
|
||||
|
||||
WritePacketID(data, packetID);
|
||||
data << m_Reason;
|
||||
return data;
|
||||
}
|
||||
|
||||
void DisconnectPacket::Deserialize(DataBuffer& data) {
|
||||
data >> m_Reason;
|
||||
}
|
||||
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
19
src/td/protocol/packets/KeepAlivePacket.cpp
Normal file
19
src/td/protocol/packets/KeepAlivePacket.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "td/protocol/packets/KeepAlivePacket.h"
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
DataBuffer KeepAlivePacket::Serialize(bool packetID) const {
|
||||
DataBuffer data;
|
||||
|
||||
WritePacketID(data, packetID);
|
||||
data << m_AliveID;
|
||||
return data;
|
||||
}
|
||||
|
||||
void KeepAlivePacket::Deserialize(DataBuffer& data) {
|
||||
data >> m_AliveID;
|
||||
}
|
||||
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
19
src/td/protocol/packets/PlaceTowerPacket.cpp
Normal file
19
src/td/protocol/packets/PlaceTowerPacket.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "td/protocol/packets/PlaceTowerPacket.h"
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
DataBuffer PlaceTowerPacket::Serialize(bool packetID) const {
|
||||
DataBuffer data;
|
||||
|
||||
WritePacketID(data, packetID);
|
||||
data << m_TowerX << m_TowerY << m_TowerType;
|
||||
return data;
|
||||
}
|
||||
|
||||
void PlaceTowerPacket::Deserialize(DataBuffer& data) {
|
||||
data >> m_TowerX >> m_TowerY >> m_TowerType;
|
||||
}
|
||||
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
19
src/td/protocol/packets/PlayerBuyItemPacket.cpp
Normal file
19
src/td/protocol/packets/PlayerBuyItemPacket.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "td/protocol/packets/PlayerBuyItemPacket.h"
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
DataBuffer PlayerBuyItemPacket::Serialize(bool packetID) const {
|
||||
DataBuffer data;
|
||||
|
||||
WritePacketID(data, packetID);
|
||||
data << m_ItemType << m_Count;
|
||||
return data;
|
||||
}
|
||||
|
||||
void PlayerBuyItemPacket::Deserialize(DataBuffer& data) {
|
||||
data >> m_ItemType >> m_Count;
|
||||
}
|
||||
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
19
src/td/protocol/packets/PlayerBuyMobUpgradePacket.cpp
Normal file
19
src/td/protocol/packets/PlayerBuyMobUpgradePacket.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "td/protocol/packets/PlayerBuyMobUpgradePacket.h"
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
DataBuffer PlayerBuyMobUpgradePacket::Serialize(bool packetID) const {
|
||||
DataBuffer data;
|
||||
|
||||
WritePacketID(data, packetID);
|
||||
data << m_MobType << m_MobLevel;
|
||||
return data;
|
||||
}
|
||||
|
||||
void PlayerBuyMobUpgradePacket::Deserialize(DataBuffer& data) {
|
||||
data >> m_MobType >> m_MobLevel;
|
||||
}
|
||||
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
19
src/td/protocol/packets/PlayerJoinPacket.cpp
Normal file
19
src/td/protocol/packets/PlayerJoinPacket.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "td/protocol/packets/PlayerJoinPacket.h"
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
DataBuffer PlayerJoinPacket::Serialize(bool packetID) const {
|
||||
DataBuffer data;
|
||||
|
||||
WritePacketID(data, packetID);
|
||||
data << m_PlayerID << m_PlayerName;
|
||||
return data;
|
||||
}
|
||||
|
||||
void PlayerJoinPacket::Deserialize(DataBuffer& data) {
|
||||
data >> m_PlayerID >> m_PlayerName;
|
||||
}
|
||||
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
20
src/td/protocol/packets/PlayerLeavePacket.cpp
Normal file
20
src/td/protocol/packets/PlayerLeavePacket.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#include "td/protocol/packets/PlayerLeavePacket.h"
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
DataBuffer PlayerLeavePacket::Serialize(bool packetID) const {
|
||||
DataBuffer data;
|
||||
|
||||
WritePacketID(data, packetID);
|
||||
data << m_PlayerID;
|
||||
return data;
|
||||
}
|
||||
|
||||
void PlayerLeavePacket::Deserialize(DataBuffer& data) {
|
||||
data >> m_PlayerID;
|
||||
}
|
||||
|
||||
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
30
src/td/protocol/packets/PlayerListPacket.cpp
Normal file
30
src/td/protocol/packets/PlayerListPacket.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#include "td/protocol/packets/PlayerListPacket.h"
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
DataBuffer PlayerListPacket::Serialize(bool packetID) const {
|
||||
DataBuffer data;
|
||||
|
||||
WritePacketID(data, packetID);
|
||||
data << static_cast<std::uint8_t>(m_Players.size());
|
||||
for (auto [playerID, playerInfo] : m_Players) {
|
||||
data << playerID << playerInfo.name << playerInfo.team;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
void PlayerListPacket::Deserialize(DataBuffer& data) {
|
||||
std::uint8_t playerCount;
|
||||
data >> playerCount;
|
||||
|
||||
for (int i = 0; i < playerCount; i++) {
|
||||
std::uint8_t playerID;
|
||||
PlayerInfo playerInfo;
|
||||
data >> playerID >> playerInfo.name >> playerInfo.team;
|
||||
m_Players.insert({ playerID, playerInfo });
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
19
src/td/protocol/packets/PlayerLoginPacket.cpp
Normal file
19
src/td/protocol/packets/PlayerLoginPacket.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "td/protocol/packets/PlayerLoginPacket.h"
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
DataBuffer PlayerLoginPacket::Serialize(bool packetID) const {
|
||||
DataBuffer data;
|
||||
|
||||
WritePacketID(data, packetID);
|
||||
data << m_PlayerName;
|
||||
return data;
|
||||
}
|
||||
|
||||
void PlayerLoginPacket::Deserialize(DataBuffer& data) {
|
||||
data >> m_PlayerName;
|
||||
}
|
||||
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
19
src/td/protocol/packets/RemoveTowerPacket.cpp
Normal file
19
src/td/protocol/packets/RemoveTowerPacket.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "td/protocol/packets/RemoveTowerPacket.h"
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
DataBuffer RemoveTowerPacket::Serialize(bool packetID) const {
|
||||
DataBuffer data;
|
||||
|
||||
WritePacketID(data, packetID);
|
||||
data << m_TowerID;
|
||||
return data;
|
||||
}
|
||||
|
||||
void RemoveTowerPacket::Deserialize(DataBuffer& data) {
|
||||
data >> m_TowerID;
|
||||
}
|
||||
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
19
src/td/protocol/packets/SelectTeamPacket.cpp
Normal file
19
src/td/protocol/packets/SelectTeamPacket.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "td/protocol/packets/SelectTeamPacket.h"
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
DataBuffer SelectTeamPacket::Serialize(bool packetID) const {
|
||||
DataBuffer data;
|
||||
|
||||
WritePacketID(data, packetID);
|
||||
data << m_SelectedTeam;
|
||||
return data;
|
||||
}
|
||||
|
||||
void SelectTeamPacket::Deserialize(DataBuffer& data) {
|
||||
data >> m_SelectedTeam;
|
||||
}
|
||||
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
26
src/td/protocol/packets/SendMobsPacket.cpp
Normal file
26
src/td/protocol/packets/SendMobsPacket.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#include "td/protocol/packets/SendMobsPacket.h"
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
DataBuffer SendMobsPacket::Serialize(bool packetID) const {
|
||||
DataBuffer data;
|
||||
|
||||
WritePacketID(data, packetID);
|
||||
data << static_cast<std::uint8_t>(m_MobSends.size());
|
||||
|
||||
data.WriteSome(reinterpret_cast<const std::uint8_t*>(m_MobSends.data()), m_MobSends.size() * sizeof(m_MobSends));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void SendMobsPacket::Deserialize(DataBuffer& data) {
|
||||
std::uint8_t mobSendCount;
|
||||
data >> mobSendCount;
|
||||
|
||||
m_MobSends.resize(mobSendCount);
|
||||
data.ReadSome(reinterpret_cast<std::uint8_t*>(m_MobSends.data()), mobSendCount * sizeof(MobSend));
|
||||
}
|
||||
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
19
src/td/protocol/packets/ServerTpsPacket.cpp
Normal file
19
src/td/protocol/packets/ServerTpsPacket.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "td/protocol/packets/ServerTpsPacket.h"
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
DataBuffer ServerTpsPacket::Serialize(bool packetID) const {
|
||||
DataBuffer data;
|
||||
|
||||
WritePacketID(data, packetID);
|
||||
data << m_TPS << m_MSPT << m_PacketSendTime;
|
||||
return data;
|
||||
}
|
||||
|
||||
void ServerTpsPacket::Deserialize(DataBuffer& data) {
|
||||
data >> m_TPS >> m_MSPT >> m_PacketSendTime;
|
||||
}
|
||||
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
21
src/td/protocol/packets/SpawnMobPacket.cpp
Normal file
21
src/td/protocol/packets/SpawnMobPacket.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#include "td/protocol/packets/SpawnMobPacket.h"
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
DataBuffer SpawnMobPacket::Serialize(bool packetID) const {
|
||||
DataBuffer data;
|
||||
|
||||
WritePacketID(data, packetID);
|
||||
data << m_MobID << m_MobType << m_MobLevel << m_MobDirection
|
||||
<< m_Sender << m_MobX << m_MobY;
|
||||
return data;
|
||||
}
|
||||
|
||||
void SpawnMobPacket::Deserialize(DataBuffer& data) {
|
||||
data >> m_MobID >> m_MobType >> m_MobLevel >> m_MobDirection
|
||||
>> m_Sender >> m_MobX >> m_MobY;
|
||||
}
|
||||
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
19
src/td/protocol/packets/UpdateCastleLifePacket.cpp
Normal file
19
src/td/protocol/packets/UpdateCastleLifePacket.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "td/protocol/packets/UpdateCastleLifePacket.h"
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
DataBuffer UpdateCastleLifePacket::Serialize(bool packetID) const {
|
||||
DataBuffer data;
|
||||
|
||||
WritePacketID(data, packetID);
|
||||
data << m_CastleLife << m_Team;
|
||||
return data;
|
||||
}
|
||||
|
||||
void UpdateCastleLifePacket::Deserialize(DataBuffer& data) {
|
||||
data >> m_CastleLife >> m_Team;
|
||||
}
|
||||
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
19
src/td/protocol/packets/UpdateExpPacket.cpp
Normal file
19
src/td/protocol/packets/UpdateExpPacket.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "td/protocol/packets/UpdateExpPacket.h"
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
DataBuffer UpdateExpPacket::Serialize(bool packetID) const {
|
||||
DataBuffer data;
|
||||
|
||||
WritePacketID(data, packetID);
|
||||
data << m_NewAmount;
|
||||
return data;
|
||||
}
|
||||
|
||||
void UpdateExpPacket::Deserialize(DataBuffer& data) {
|
||||
data >> m_NewAmount;
|
||||
}
|
||||
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
19
src/td/protocol/packets/UpdateGameStatePacket.cpp
Normal file
19
src/td/protocol/packets/UpdateGameStatePacket.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "td/protocol/packets/UpdateGameStatePacket.h"
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
DataBuffer UpdateGameStatePacket::Serialize(bool packetID) const {
|
||||
DataBuffer data;
|
||||
|
||||
WritePacketID(data, packetID);
|
||||
data << m_GameState;
|
||||
return data;
|
||||
}
|
||||
|
||||
void UpdateGameStatePacket::Deserialize(DataBuffer& data) {
|
||||
data >> m_GameState;
|
||||
}
|
||||
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
19
src/td/protocol/packets/UpdateLobbyTimePacket.cpp
Normal file
19
src/td/protocol/packets/UpdateLobbyTimePacket.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "td/protocol/packets/UpdateLobbyTimePacket.h"
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
DataBuffer UpdateLobbyTimePacket::Serialize(bool packetID) const {
|
||||
DataBuffer data;
|
||||
|
||||
WritePacketID(data, packetID);
|
||||
data << m_RemainingTime;
|
||||
return data;
|
||||
}
|
||||
|
||||
void UpdateLobbyTimePacket::Deserialize(DataBuffer& data) {
|
||||
data >> m_RemainingTime;
|
||||
}
|
||||
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
25
src/td/protocol/packets/UpdateMobStatesPacket.cpp
Normal file
25
src/td/protocol/packets/UpdateMobStatesPacket.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include "td/protocol/packets/UpdateMobStatesPacket.h"
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
DataBuffer UpdateMobStatesPacket::Serialize(bool packetID) const {
|
||||
DataBuffer data;
|
||||
|
||||
WritePacketID(data, packetID);
|
||||
data << static_cast<std::uint64_t>(m_MobStates.size());
|
||||
|
||||
data.WriteSome(reinterpret_cast<const std::uint8_t*>(m_MobStates.data()), m_MobStates.size() * sizeof(MobState));
|
||||
return data;
|
||||
}
|
||||
|
||||
void UpdateMobStatesPacket::Deserialize(DataBuffer& data) {
|
||||
std::uint64_t mobCount;
|
||||
data >> mobCount;
|
||||
m_MobStates.resize(mobCount);
|
||||
|
||||
data.ReadSome(reinterpret_cast<std::uint8_t*>(m_MobStates.data()), mobCount * sizeof(MobState));
|
||||
}
|
||||
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
19
src/td/protocol/packets/UpdateMoneyPacket.cpp
Normal file
19
src/td/protocol/packets/UpdateMoneyPacket.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "td/protocol/packets/UpdateMoneyPacket.h"
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
DataBuffer UpdateMoneyPacket::Serialize(bool packetID) const {
|
||||
DataBuffer data;
|
||||
|
||||
WritePacketID(data, packetID);
|
||||
data << m_NewAmount;
|
||||
return data;
|
||||
}
|
||||
|
||||
void UpdateMoneyPacket::Deserialize(DataBuffer& data) {
|
||||
data >> m_NewAmount;
|
||||
}
|
||||
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
20
src/td/protocol/packets/UpdatePlayerTeamPacket.cpp
Normal file
20
src/td/protocol/packets/UpdatePlayerTeamPacket.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#include "td/protocol/packets/UpdatePlayerTeamPacket.h"
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
|
||||
DataBuffer UpdatePlayerTeamPacket::Serialize(bool packetID) const {
|
||||
DataBuffer data;
|
||||
|
||||
WritePacketID(data, packetID);
|
||||
data << m_PlayerID << m_SelectedTeam;
|
||||
return data;
|
||||
}
|
||||
|
||||
void UpdatePlayerTeamPacket::Deserialize(DataBuffer& data) {
|
||||
data >> m_PlayerID >> m_SelectedTeam;
|
||||
}
|
||||
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
19
src/td/protocol/packets/UpgradeTowerPacket.cpp
Normal file
19
src/td/protocol/packets/UpgradeTowerPacket.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "td/protocol/packets/UpgradeTowerPacket.h"
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
DataBuffer UpgradeTowerPacket::Serialize(bool packetID) const {
|
||||
DataBuffer data;
|
||||
|
||||
WritePacketID(data, packetID);
|
||||
data << m_TowerID << m_TowerLevel;
|
||||
return data;
|
||||
}
|
||||
|
||||
void UpgradeTowerPacket::Deserialize(DataBuffer& data) {
|
||||
data >> m_TowerID >> m_TowerLevel;
|
||||
}
|
||||
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
19
src/td/protocol/packets/WorldAddTowerPacket.cpp
Normal file
19
src/td/protocol/packets/WorldAddTowerPacket.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "td/protocol/packets/WorldAddTowerPacket.h"
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
DataBuffer WorldAddTowerPacket::Serialize(bool packetID) const {
|
||||
DataBuffer data;
|
||||
|
||||
WritePacketID(data, packetID);
|
||||
data << m_TowerID << m_TowerX << m_TowerY << m_TowerType << m_Builder;
|
||||
return data;
|
||||
}
|
||||
|
||||
void WorldAddTowerPacket::Deserialize(DataBuffer& data) {
|
||||
data >> m_TowerID >> m_TowerX >> m_TowerY >> m_TowerType >> m_Builder;
|
||||
}
|
||||
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
139
src/td/protocol/packets/WorldBeginDataPacket.cpp
Normal file
139
src/td/protocol/packets/WorldBeginDataPacket.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
#include "td/protocol/packets/WorldBeginDataPacket.h"
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
static DataBuffer& operator<<(DataBuffer& buffer, game::TilePtr tile) {
|
||||
buffer << tile->GetType();
|
||||
|
||||
switch (tile->GetType()) {
|
||||
|
||||
case game::TileType::Tower: {
|
||||
const game::TowerTile* towerTile = dynamic_cast<const game::TowerTile*>(tile.get());
|
||||
buffer << towerTile->color_palette_ref << towerTile->team_owner;
|
||||
break;
|
||||
}
|
||||
|
||||
case game::TileType::Walk: {
|
||||
const game::WalkableTile* walkTile = dynamic_cast<const game::WalkableTile*>(tile.get());
|
||||
buffer << walkTile->direction;
|
||||
break;
|
||||
}
|
||||
|
||||
case game::TileType::Decoration: {
|
||||
const game::DecorationTile* decoTile = dynamic_cast<const game::DecorationTile*>(tile.get());
|
||||
buffer << decoTile->color_palette_ref;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static DataBuffer& operator>>(DataBuffer& buffer, game::TilePtr& tile) {
|
||||
game::TileType tileType;
|
||||
buffer >> tileType;
|
||||
switch (tileType) {
|
||||
case game::TileType::Tower: {
|
||||
auto tilePtr = std::make_shared<game::TowerTile>();
|
||||
buffer >> tilePtr->color_palette_ref >> tilePtr->team_owner;
|
||||
tile = tilePtr;
|
||||
break;
|
||||
}
|
||||
case game::TileType::Walk: {
|
||||
auto tilePtr = std::make_shared<game::WalkableTile>();
|
||||
buffer >> tilePtr->direction;
|
||||
tile = tilePtr;
|
||||
break;
|
||||
}
|
||||
case game::TileType::Decoration: {
|
||||
auto tilePtr = std::make_shared<game::DecorationTile>();
|
||||
buffer >> tilePtr->color_palette_ref;
|
||||
tile = tilePtr;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
DataBuffer WorldBeginDataPacket::Serialize(bool packetID) const {
|
||||
DataBuffer data;
|
||||
const game::TowerTileColorPalette& towerTilePalette = m_Header.m_World->GetTowerTileColorPalette();
|
||||
const std::vector<Color>& decoTilePalette = m_Header.m_World->GetDecorationPalette();
|
||||
|
||||
WritePacketID(data, packetID);
|
||||
|
||||
data << towerTilePalette << m_Header.m_World->GetWalkableTileColor()
|
||||
<< static_cast<std::uint16_t>(decoTilePalette.size());
|
||||
|
||||
// deco color palette
|
||||
std::size_t bufferSize = data.GetSize();
|
||||
data.Resize(bufferSize + decoTilePalette.size() * sizeof(Color));
|
||||
|
||||
memcpy(reinterpret_cast<std::uint8_t*>(data.data()) + bufferSize, decoTilePalette.data(), decoTilePalette.size() * sizeof(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::TeamCastle& redCastle = m_Header.m_World->GetRedTeam().GetCastle(), blueCastle = m_Header.m_World->GetBlueTeam().GetCastle();
|
||||
|
||||
data << redSpawn << static_cast<utils::shape::Rectangle>(redCastle);
|
||||
data << blueSpawn << static_cast<utils::shape::Rectangle>(blueCastle);
|
||||
|
||||
// tile palette
|
||||
data << static_cast<std::uint64_t>(m_Header.m_World->GetTilePalette().size());
|
||||
|
||||
for (game::TilePtr tile : m_Header.m_World->GetTilePalette()) {
|
||||
data << tile;
|
||||
}
|
||||
|
||||
data << m_Header.m_World->GetSpawnColors();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void WorldBeginDataPacket::Deserialize(DataBuffer& data) {
|
||||
data >> m_Header.m_TowerPlacePalette >> m_Header.m_WalkablePalette;
|
||||
|
||||
std::uint16_t decoPaletteSize;
|
||||
data >> decoPaletteSize;
|
||||
|
||||
std::size_t decoPalletteSizeByte = decoPaletteSize * sizeof(Color);
|
||||
|
||||
m_Header.m_DecorationPalette.resize(decoPaletteSize);
|
||||
|
||||
memcpy(reinterpret_cast<std::uint8_t*>(m_Header.m_DecorationPalette.data()), data.data() + data.GetReadOffset(), decoPalletteSizeByte);
|
||||
|
||||
data.SetReadOffset(data.GetReadOffset() + decoPalletteSizeByte);
|
||||
|
||||
data >> m_Header.m_Background;
|
||||
|
||||
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;
|
||||
data >> tilePaletteSize;
|
||||
|
||||
m_Header.m_TilePalette.reserve(tilePaletteSize);
|
||||
|
||||
for (std::uint64_t tileNumber = 0; tileNumber < tilePaletteSize; tileNumber++) {
|
||||
game::TilePtr tile;
|
||||
data >> tile;
|
||||
m_Header.m_TilePalette.push_back(tile);
|
||||
}
|
||||
|
||||
data >> m_Header.m_SpawnColorPalette;
|
||||
}
|
||||
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
115
src/td/protocol/packets/WorldDataPacket.cpp
Normal file
115
src/td/protocol/packets/WorldDataPacket.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
#include "td/protocol/packets/WorldDataPacket.h"
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
const int BITS_IN_BYTE = 8;
|
||||
const int BITS_IN_LONG = BITS_IN_BYTE * sizeof(std::uint64_t);
|
||||
|
||||
static unsigned int countBits(unsigned int number) {
|
||||
// log function in base 2
|
||||
// take only integer part
|
||||
return static_cast<unsigned int>(std::log2(number) + 1);
|
||||
}
|
||||
|
||||
typedef std::vector<uint64_t> ChunkPackedData;
|
||||
|
||||
DataBuffer WorldDataPacket::Serialize(bool packetID) const {
|
||||
DataBuffer data;
|
||||
|
||||
WritePacketID(data, packetID);
|
||||
|
||||
data << m_World->GetChunks().size();
|
||||
for (const auto& pair : m_World->GetChunks()) {
|
||||
game::ChunkCoord coords = pair.first;
|
||||
game::ChunkPtr chunk = pair.second;
|
||||
|
||||
data << coords.x << coords.y << static_cast<std::uint64_t>(chunk->palette.size());
|
||||
|
||||
std::size_t bufferSize = data.GetSize();
|
||||
data.Resize(data.GetSize() + chunk->palette.size() * sizeof(game::ChunkPalette::value_type));
|
||||
memcpy(reinterpret_cast<std::uint8_t*>(data.data()) + bufferSize, chunk->palette.data(), chunk->palette.size() * sizeof(game::ChunkPalette::value_type));
|
||||
|
||||
std::uint8_t bitsPerTile = countBits(chunk->palette.size());
|
||||
|
||||
game::Chunk::ChunkData::value_type individualValueMask = ((1 << bitsPerTile) - 1);
|
||||
|
||||
ChunkPackedData chunkData(game::Chunk::ChunkSize / (BITS_IN_BYTE * sizeof(ChunkPackedData::value_type) / bitsPerTile), 0);
|
||||
|
||||
for (unsigned int tileNumber = 0; tileNumber < game::Chunk::ChunkSize; tileNumber++) {
|
||||
std::size_t startLong = static_cast<std::size_t>((tileNumber * bitsPerTile) / BITS_IN_LONG);
|
||||
std::size_t startOffset = static_cast<std::size_t>((tileNumber * bitsPerTile) % BITS_IN_LONG);
|
||||
std::size_t endLong = static_cast<std::size_t>(((tileNumber + 1) * bitsPerTile - 1) / BITS_IN_LONG);
|
||||
|
||||
std::uint64_t value = static_cast<std::uint64_t>(chunk->tiles[tileNumber]);
|
||||
|
||||
value &= individualValueMask;
|
||||
|
||||
chunkData[startLong] |= (value << startOffset);
|
||||
|
||||
if (startLong != endLong) {
|
||||
chunkData[endLong] = (value >> (BITS_IN_LONG - startOffset));
|
||||
}
|
||||
}
|
||||
|
||||
bufferSize = data.GetSize();
|
||||
data.Resize(data.GetSize() + chunkData.size() * sizeof(ChunkPackedData::value_type));
|
||||
memcpy(reinterpret_cast<std::uint8_t*>(data.data()) + bufferSize, chunkData.data(), chunkData.size() * sizeof(ChunkPackedData::value_type));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
void WorldDataPacket::Deserialize(DataBuffer& data) {
|
||||
std::uint64_t chunkCount;
|
||||
data >> chunkCount;
|
||||
|
||||
for (std::uint64_t chunkNumber = 0; chunkNumber < chunkCount; chunkNumber++) {
|
||||
game::ChunkPtr chunk = std::make_shared<game::Chunk>();
|
||||
|
||||
decltype(game::ChunkCoord::x) chunkX, chunkY;
|
||||
data >> chunkX >> chunkY;
|
||||
|
||||
std::uint64_t chunkPaletteSize;
|
||||
data >> chunkPaletteSize;
|
||||
|
||||
game::ChunkPalette chunkPalette(chunkPaletteSize);
|
||||
|
||||
memcpy(reinterpret_cast<void*>(chunkPalette.data()), data.data() + data.GetReadOffset(), chunkPaletteSize * sizeof(game::ChunkPalette::value_type));
|
||||
data.SetReadOffset(data.GetReadOffset() + chunkPaletteSize * sizeof(game::ChunkPalette::value_type));
|
||||
|
||||
chunk->palette = chunkPalette;
|
||||
|
||||
std::uint8_t bitsPerTile = countBits(chunkPaletteSize);
|
||||
|
||||
// A bitmask that contains bitsPerTile set bits
|
||||
game::Chunk::ChunkData::value_type individualValueMask = ((1 << bitsPerTile) - 1);
|
||||
|
||||
ChunkPackedData chunkData(game::Chunk::ChunkSize / (BITS_IN_BYTE * sizeof(ChunkPackedData::value_type) / bitsPerTile), 0);
|
||||
|
||||
memcpy(reinterpret_cast<void*>(chunkData.data()), data.data() + data.GetReadOffset(), chunkData.size() * sizeof(ChunkPackedData::value_type));
|
||||
data.SetReadOffset(data.GetReadOffset() + chunkData.size() * sizeof(ChunkPackedData::value_type));
|
||||
|
||||
for (unsigned int tileNumber = 0; tileNumber < game::Chunk::ChunkSize; tileNumber++) {
|
||||
std::size_t startLong = (tileNumber * bitsPerTile) / BITS_IN_LONG;
|
||||
std::size_t startOffset = (tileNumber * bitsPerTile) % BITS_IN_LONG;
|
||||
std::size_t endLong = ((tileNumber + 1) * bitsPerTile - 1) / BITS_IN_LONG;
|
||||
|
||||
game::Chunk::ChunkData::value_type value;
|
||||
if (startLong == endLong) {
|
||||
value = (chunkData[startLong] >> startOffset);
|
||||
} else {
|
||||
int endOffset = BITS_IN_LONG - startOffset;
|
||||
value = (chunkData[startLong] >> startOffset | chunkData[endLong] << endOffset);
|
||||
}
|
||||
value &= individualValueMask;
|
||||
|
||||
chunk->tiles[tileNumber] = value;
|
||||
}
|
||||
|
||||
|
||||
m_WorldData.m_Chunks.insert({ {chunkX, chunkY}, chunk });
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
Reference in New Issue
Block a user