#include "game/World.h" #include "protocol/PacketDispatcher.h" #include "protocol/Protocol.h" #include "game/BaseGame.h" #include "misc/Random.h" #include #include "misc/Compression.h" #include namespace td { namespace game { World::World(Game* game) : m_Game(game) { } 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(); getRedTeam().getSpawn() = worldHeader->getRedSpawn(); getBlueTeam().getSpawn() = worldHeader->getBlueSpawn(); m_SpawnColorPalette = worldHeader->getSpawnPalette(); getRedTeam().getCastle() = worldHeader->getRedCastle(); getBlueTeam().getCastle() = worldHeader->getBlueCastle(); 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)) { std::cerr << "Failed to load map from file " << fileName << " !\n"; return false; } std::cout << "Read file : " << fileName << " (File size : " << buffer.GetSize() << ")" << std::endl; DataBuffer mapHeaderPacketBuffer = utils::Decompress(buffer); DataBuffer mapDataPacketBuffer = utils::Decompress(buffer); protocol::PacketType packetType; mapHeaderPacketBuffer >> packetType; protocol::WorldBeginDataPacket headerPacket; headerPacket.Deserialize(mapHeaderPacketBuffer); mapDataPacketBuffer >> packetType; 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()); DataBuffer mapDataCompressed = utils::Compress(dataPacket.Serialize()); std::cout << "Header Packet Size : " << mapHeaderCompressed.GetSize() << std::endl; std::cout << "World Data Packet Size : " << mapDataCompressed.GetSize() << std::endl; DataBuffer buffer = mapHeaderCompressed << mapDataCompressed; std::cout << "Total Size : " << buffer.GetSize() << std::endl; return buffer.WriteFile(fileName); } void World::tick(std::uint64_t delta) { moveMobs(delta); 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); } 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; } void World::tickMobs(std::uint64_t delta) { for (MobPtr mob : m_Mobs) { mob->tick(delta); } } void World::moveMobs(std::uint64_t delta) { for (MobPtr mob : m_Mobs) { TilePtr tile = getTile(mob->getCenter().getX(), mob->getCenter().getY()); if (tile != nullptr && tile->getType() == TileType::Walk) { WalkableTile* walkTile = dynamic_cast(tile.get()); mob->setDirection(walkTile->direction); } moveMob(mob, delta); TeamColor mobTeam = m_Game->getPlayerById(mob->getSender()).getTeamColor(); const TeamCastle* enemyCastle; if (mobTeam == TeamColor::Red) { enemyCastle = &getBlueTeam().getCastle(); } else if (mobTeam == TeamColor::Blue) { enemyCastle = &getRedTeam().getCastle(); } if (isMobTouchingCastle(mob, *enemyCastle)) { moveBackMob(mob, *enemyCastle); } } } void World::moveMob(MobPtr mob, std::uint64_t delta) { float mobWalkSpeed = mob->getStats()->getMovementSpeed(); float walkAmount = mobWalkSpeed * ((float)delta / 1000.0f); if (mob->hasEffect(EffectType::Slowness)) walkAmount *= 0.70; // walk 30% slower switch (mob->getDirection()) { case Direction::NegativeX: { mob->setCenterX(mob->getCenterX() - walkAmount); break; } case Direction::PositiveX: { mob->setCenterX(mob->getCenterX() + walkAmount); break; } case Direction::NegativeY: { mob->setCenterY(mob->getCenterY() - walkAmount); break; } case Direction::PositiveY: { mob->setCenterY(mob->getCenterY() + walkAmount); break; } } } bool World::isMobTouchingCastle(MobPtr mob, const TeamCastle& enemyCastle) const { return enemyCastle.collidesWith(*mob); } void World::moveBackMob(MobPtr mob, const TeamCastle& enemyCastle) { switch (mob->getDirection()) { case Direction::NegativeX: { mob->setCenterX(enemyCastle.getBottomRight().getX() + mob->getWidth() / 2.0f); break; } case Direction::PositiveX: { mob->setCenterX(enemyCastle.getTopLeft().getX() - mob->getWidth() / 2.0f); break; } case Direction::NegativeY: { mob->setCenterY(enemyCastle.getBottomRight().getY() + mob->getHeight() / 2.0f); break; } case Direction::PositiveY: { mob->setCenterY(enemyCastle.getTopLeft().getY() - mob->getHeight() / 2.0f); break; } } } const Color* World::getTileColor(TilePtr tile) const { switch (tile->getType()) { case TileType::Tower: { TowerTile* towerTile = (TowerTile*)tile.get(); return &m_TowerPlacePalette[towerTile->color_palette_ref]; } case TileType::Walk: { return &m_WalkablePalette; } case TileType::Decoration: { DecorationTile* towerTile = (DecorationTile*)tile.get(); return &m_DecorationPalette[towerTile->color_palette_ref]; break; } case TileType::None: { return nullptr; } } return nullptr; } bool World::CanPlaceLittleTower(const glm::vec2& 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 = (const 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 glm::vec2& 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 = (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() { for (std::size_t i = 0; i < m_Mobs.size(); i++) { MobPtr mob = m_Mobs[i]; if (mob->isDead()) { if (mob->OnDeath(this)) { //reward players Player& sender = m_Game->getPlayerById(mob->getSender()); sender.addExp(mob->getStats()->getExpReward()); Player& killer = m_Game->getPlayerById(mob->getLastDamageTower()->getBuilder()); killer.addGold(mob->getStats()->getMoneyCost()); m_Mobs.erase(m_Mobs.begin() + i); } } } } TowerPtr World::getTower(const glm::vec2& 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::OnArrowShot(MobPtr target, Tower* shooter) { bool explosiveArrows = shooter->getLevel().getPath() == TowerPath::Bottom; if (explosiveArrows) { // aoe damage } else { target->damage(shooter->getStats()->getDamage(), shooter); } } 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); } } // namespace game } // namespace td