too many things

This commit is contained in:
2025-07-18 13:11:18 +02:00
parent b788caafa6
commit 6d0e56eb46
26 changed files with 529 additions and 191 deletions

View File

@@ -18,7 +18,7 @@ struct Vec2 {
T g; T g;
}; };
constexpr Vec2(T X = 0, T Y = 0) : x(X), y(Y) {} constexpr Vec2(T X = T(0), T Y = T(0)) : x(X), y(Y) {}
}; };
template <typename T> template <typename T>

View File

@@ -2,6 +2,11 @@
#include <cstdint> #include <cstdint>
#include <fpm/fixed.hpp> #include <fpm/fixed.hpp>
#include <td/Maths.h>
namespace sp {
class DataBuffer;
} // namespace sp
namespace td { namespace td {
@@ -31,7 +36,7 @@ enum class EntityType : std::uint8_t {
Zombie = 0, Zombie = 0,
Spider, Spider,
Pigman, Pigman,
Skeleton, Skelon,
Creeper, Creeper,
Silverfish, Silverfish,
Blaze, Blaze,
@@ -61,10 +66,7 @@ struct TowerCoords {
std::int16_t y; std::int16_t y;
}; };
struct EntityCoords { using EntityCoords = Vec2<FpFloat>;
FpFloat x;
FpFloat y;
};
using PeerID = std::uint16_t; using PeerID = std::uint16_t;
@@ -75,4 +77,10 @@ enum class Direction : std::uint8_t {
NegativeY = 1 << 3, NegativeY = 1 << 3,
}; };
sp::DataBuffer& operator<<(sp::DataBuffer& a_Buffer, const EntityCoords& a_Coords);
sp::DataBuffer& operator<<(sp::DataBuffer& a_Buffer, const FpFloat& a_Float);
sp::DataBuffer& operator>>(sp::DataBuffer& a_Buffer, EntityCoords& a_Coords);
sp::DataBuffer& operator>>(sp::DataBuffer& a_Buffer, FpFloat& a_Float);
} // namespace td } // namespace td

View File

@@ -10,8 +10,11 @@
#include <vector> #include <vector>
namespace td { namespace td {
using Vec2fp = Vec2<FpFloat>;
namespace game { namespace game {
struct WalkableTile; struct WalkableTile;
enum class EffectType : std::uint8_t { enum class EffectType : std::uint8_t {
@@ -62,11 +65,12 @@ const MobStats* GetMobStats(MobType type, std::uint8_t level);
const TowerImmunities& GetMobTowerImmunities(MobType type, std::uint8_t level); const TowerImmunities& GetMobTowerImmunities(MobType type, std::uint8_t level);
const EffectImmunities& GetMobEffectImmunities(MobType type, std::uint8_t level); const EffectImmunities& GetMobEffectImmunities(MobType type, std::uint8_t level);
class Mob : public utils::shape::Rectangle { class Mob {
protected: protected:
float m_Health; float m_Health;
private: private:
Vec2fp m_Position;
MobID m_ID; MobID m_ID;
PlayerID m_Sender; PlayerID m_Sender;
MobLevel m_Level; MobLevel m_Level;
@@ -89,12 +93,14 @@ class Mob : public utils::shape::Rectangle {
virtual MobType GetType() const = 0; virtual MobType GetType() const = 0;
virtual void Tick(std::uint64_t delta, World* world) {}
virtual bool OnDeath(World* world) { virtual bool OnDeath(World* world) {
return true; return true;
} }
Vec2fp& GetPosition() {
return m_Position;
}
MobID GetMobID() const { MobID GetMobID() const {
return m_ID; return m_ID;
} }
@@ -156,15 +162,15 @@ class Mob : public utils::shape::Rectangle {
return m_HitCooldown > 0; return m_HitCooldown > 0;
} }
// returns a float between 0 and 1 excluded // // returns a float between 0 and 1 excluded
float GetTileX() { // float GetTileX() {
return GetCenterX() - static_cast<float>(static_cast<std::int32_t>(GetCenterX())); // return GetCenterX() - static_cast<float>(static_cast<std::int32_t>(GetCenterX()));
} // }
// returns a float between 0 and 1 excluded // // returns a float between 0 and 1 excluded
float GetTileY() { // float GetTileY() {
return GetCenterY() - static_cast<float>(static_cast<std::int32_t>(GetCenterY())); // return GetCenterY() - static_cast<float>(static_cast<std::int32_t>(GetCenterY()));
} // }
Direction GetDirection() const { Direction GetDirection() const {
return m_Direction; return m_Direction;
@@ -176,7 +182,7 @@ class Mob : public utils::shape::Rectangle {
protected: protected:
void InitMob() { void InitMob() {
m_Health = static_cast<float>(GetStats()->m_MaxLife); m_Health = static_cast<float>(GetStats()->m_MaxLife);
SetSize(GetStats()->m_Size.x, GetStats()->m_Size.y); // SetSize(GetStats()->m_Size.x, GetStats()->m_Size.y);
} }
private: private:
@@ -201,9 +207,7 @@ class ConcreteMob : public Mob {
virtual ~ConcreteMob() {} virtual ~ConcreteMob() {}
virtual void Tick(std::uint64_t delta, World* world) {} virtual constexpr MobType GetType() const override {
virtual constexpr MobType GetType() const {
return MT; return MT;
} }
}; };

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
#include <td/simulation/WorldTicker.h>
#include <td/game/WorldTypes.h> #include <td/game/WorldTypes.h>
#include <td/protocol/packet/Packets.h> #include <td/protocol/packet/Packets.h>
@@ -19,11 +20,10 @@ class World {
TilePalette m_TilePalette; TilePalette m_TilePalette;
MobList m_Mobs; sim::WorldSnapshot m_CurrentState;
TowerList m_Towers; private:
sim::WorldTicker m_Ticker;
TeamList m_Teams;
public: public:
World(); World();
@@ -34,8 +34,6 @@ class World {
bool LoadMapFromFile(const std::string& fileName); bool LoadMapFromFile(const std::string& fileName);
bool SaveMap(const std::string& fileName) const; bool SaveMap(const std::string& fileName) const;
void Tick(std::uint64_t delta);
void SpawnMobAt(MobID id, MobType type, std::uint8_t level, PlayerID sender, float x, float y, Direction dir); void SpawnMobAt(MobID id, MobType type, std::uint8_t level, PlayerID sender, float x, float y, Direction dir);
TowerPtr PlaceTowerAt(TowerID id, TowerType type, std::int32_t x, std::int32_t y, PlayerID builder); TowerPtr PlaceTowerAt(TowerID id, TowerType type, std::int32_t x, std::int32_t y, PlayerID builder);
@@ -83,47 +81,49 @@ class World {
} }
const MobList& GetMobList() const { const MobList& GetMobList() const {
return m_Mobs; return m_CurrentState.m_Mobs;
} }
MobList& GetMobList() { MobList& GetMobList() {
return m_Mobs; return m_CurrentState.m_Mobs;
} }
const Color* GetTileColor(TilePtr tile) const; const Color* GetTileColor(TilePtr tile) const;
Team& GetRedTeam() { Team& GetRedTeam() {
return m_Teams[static_cast<std::uint8_t>(TeamColor::Red)]; return m_CurrentState.m_Teams[static_cast<std::uint8_t>(TeamColor::Red)];
} }
const Team& GetRedTeam() const { const Team& GetRedTeam() const {
return m_Teams[static_cast<std::uint8_t>(TeamColor::Red)]; return m_CurrentState.m_Teams[static_cast<std::uint8_t>(TeamColor::Red)];
} }
Team& GetBlueTeam() { Team& GetBlueTeam() {
return m_Teams[static_cast<std::uint8_t>(TeamColor::Blue)]; return m_CurrentState.m_Teams[static_cast<std::uint8_t>(TeamColor::Blue)];
} }
const Team& GetBlueTeam() const { const Team& GetBlueTeam() const {
return m_Teams[static_cast<std::uint8_t>(TeamColor::Red)]; return m_CurrentState.m_Teams[static_cast<std::uint8_t>(TeamColor::Red)];
} }
Team& GetTeam(TeamColor team) { Team& GetTeam(TeamColor team) {
return m_Teams[static_cast<std::uint8_t>(team)]; return m_CurrentState.m_Teams[static_cast<std::uint8_t>(team)];
} }
const Team& GetTeam(TeamColor team) const { const Team& GetTeam(TeamColor team) const {
return m_Teams[static_cast<std::uint8_t>(team)]; return m_CurrentState.m_Teams[static_cast<std::uint8_t>(team)];
} }
const TeamList& GetTeams() const { const TeamList& GetTeams() const {
return m_Teams; return m_CurrentState.m_Teams;
} }
const TowerList& GetTowers() const { const TowerList& GetTowers() const {
return m_Towers; return m_CurrentState.m_Towers;
} }
TowerPtr GetTowerById(TowerID tower); TowerPtr GetTowerById(TowerID tower);
const Player* GetPlayerById(PlayerID id) const; const Player* GetPlayerById(PlayerID id) const;
void Tick(const protocol::LockStep& a_LockStep, FpFloat a_Delta);
private: private:
void TickMobs(std::uint64_t delta); void TickMobs(std::uint64_t delta);
void CleanDeadMobs(); void CleanDeadMobs();

View File

@@ -102,6 +102,8 @@ typedef std::array<Color, 2> SpawnColorPalette;
typedef std::vector<TowerPtr> TowerList; typedef std::vector<TowerPtr> TowerList;
sp::DataBuffer& operator>>(sp::DataBuffer& buffer, TilePtr& tile);
} // namespace game } // namespace game

View File

@@ -3,11 +3,14 @@
#include <string> #include <string>
#include <SDL3/SDL_video.h> #include <SDL3/SDL_video.h>
#include <td/misc/Signal.h>
namespace td { namespace td {
class Display { class Display {
public: public:
utils::Signal<float> OnAspectRatioChange;
Display(int a_Width, int a_Height, const std::string& a_Title); Display(int a_Width, int a_Height, const std::string& a_Title);
~Display(); ~Display();

View File

@@ -21,8 +21,10 @@ struct UpgradeTower {
sp::BitField<std::uint8_t, 4> m_Upgrade; sp::BitField<std::uint8_t, 4> m_Upgrade;
}; };
using EntityTypeInt = std::uint8_t;
struct SpawnTroop { struct SpawnTroop {
sp::BitField<EntityType, 5> m_Type; sp::BitField<EntityTypeInt, 5> m_Type;
sp::BitField<std::uint8_t, 3> m_Level; sp::BitField<std::uint8_t, 3> m_Level;
EntityCoords m_Position; EntityCoords m_Position;
PlayerID m_Sender; PlayerID m_Sender;

View File

@@ -0,0 +1,18 @@
#pragma once
#include <td/protocol/packet/PacketData.h>
namespace sp {
class DataBuffer;
namespace details {
template <>
void ReadMessage(DataBuffer& a_Buffer, td::protocol::pdata::WorldHeader& a_Header);
template <>
void ReadMessage(DataBuffer& a_Buffer, td::protocol::pdata::WorldData& a_WorldData);
} // namespace details
} // namespace sp

View File

@@ -0,0 +1,21 @@
#pragma once
#include <td/game/World.h>
#include <td/protocol/command/Commands.h>
namespace td {
namespace sim {
class CommandApply : public protocol::CommandHandler {
private:
const game::World& m_World;
WorldSnapshot& m_Snapshot;
public:
CommandApply(const game::World& a_World, WorldSnapshot& a_Snapshot);
virtual void Handle(const protocol::cdata::SpawnTroop& a_SpawnTroop) override;
};
} // namespace sim
} // namespace td

View File

@@ -0,0 +1,26 @@
#pragma once
#include <td/game/World.h>
namespace td {
namespace sim {
using GameHistory = std::vector<td::protocol::LockStep>;
class RealTimeSimulation {
private:
std::uint64_t m_StepTime;
game::World& m_World;
GameHistory m_History;
std::uint64_t m_CurrentTime;
std::uint64_t m_LastTime;
std::size_t m_CurrentStep;
public:
RealTimeSimulation(game::World& a_World, GameHistory&& a_History, std::uint64_t a_StepTime);
void Update();
};
} // namespace sim
} // namespace td

View File

@@ -0,0 +1,17 @@
#pragma once
#include <td/game/WorldTypes.h>
namespace td {
namespace sim {
struct WorldSnapshot {
game::MobList m_Mobs;
game::TowerList m_Towers;
game::TeamList m_Teams;
};
} // namespace sim
} // namespace td

View File

@@ -0,0 +1,40 @@
#pragma once
#include <td/protocol/command/Commands.h>
#include <td/simulation/WorldSnapshot.h>
namespace td {
namespace game {
class World;
}
namespace sim {
class IWorldSystem {
public:
virtual void Tick(const game::World& a_World, WorldSnapshot& a_State, FpFloat a_Delta) = 0;
};
class WorldTicker {
private:
std::vector<std::unique_ptr<IWorldSystem>> m_Systems;
public:
WorldTicker();
WorldSnapshot NextStep(
const game::World& a_World, const WorldSnapshot& a_PreviousState, const protocol::LockStep& a_LockStep, FpFloat a_Delta);
private:
void ApplySteps(const game::World& a_World, WorldSnapshot& a_State, const protocol::LockStep& a_LockStep);
void Tick(const game::World& a_World, WorldSnapshot& a_State, FpFloat a_Delta);
WorldSnapshot CreateNext(const WorldSnapshot& a_PreviousState);
template <typename T>
void AddSystem() {
m_Systems.push_back(std::move(std::make_unique<T>()));
}
};
} // namespace sim
} // namespace td

View File

@@ -0,0 +1,14 @@
#pragma once
#include <td/simulation/WorldTicker.h>
namespace td {
namespace sim {
class EntityMove : public IWorldSystem {
public:
virtual void Tick(const game::World& a_World, WorldSnapshot& a_State, FpFloat a_Delta) override;
};
} // namespace sim
} // namespace td

View File

@@ -5,159 +5,18 @@
#include <td/input/Display.h> #include <td/input/Display.h>
#include <td/game/World.h> #include <td/game/World.h>
#include <td/protocol/packet/PacketSerialize.h>
#include <td/protocol/packet/Packets.h> #include <td/protocol/packet/Packets.h>
#include <td/render/renderer/WorldRenderer.h>
#include <td/render/renderer/EntityRenderer.h> #include <td/render/renderer/EntityRenderer.h>
#include <td/render/renderer/WorldRenderer.h>
namespace td { #include <td/simulation/RealTimeSimulation.h>
namespace game {
sp::DataBuffer& operator>>(sp::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;
}
} // namespace game
} // namespace td
namespace sp {
namespace details {
template <>
void ReadMessage(DataBuffer& a_Buffer, td::protocol::pdata::WorldHeader& a_Header) {
a_Buffer >> a_Header.m_TowerPlacePalette >> a_Header.m_WalkablePalette;
std::uint16_t decoPaletteSize;
a_Buffer >> decoPaletteSize;
std::size_t decoPalletteSizeByte = decoPaletteSize * sizeof(td::Color);
a_Header.m_DecorationPalette.resize(decoPaletteSize);
memcpy(reinterpret_cast<std::uint8_t*>(a_Header.m_DecorationPalette.data()), a_Buffer.data() + a_Buffer.GetReadOffset(),
decoPalletteSizeByte);
a_Buffer.SetReadOffset(a_Buffer.GetReadOffset() + decoPalletteSizeByte);
a_Buffer >> a_Header.m_Background;
td::utils::shape::Rectangle redCastle, blueCastle;
a_Buffer >> a_Header.m_RedSpawn >> redCastle;
a_Buffer >> a_Header.m_BlueSpawn >> blueCastle;
a_Header.m_RedCastle.SetShape(redCastle);
a_Header.m_BlueCastle.SetShape(blueCastle);
std::uint64_t tilePaletteSize;
a_Buffer >> tilePaletteSize;
a_Header.m_TilePalette.reserve(tilePaletteSize);
for (std::uint64_t tileNumber = 0; tileNumber < tilePaletteSize; tileNumber++) {
td::game::TilePtr tile;
a_Buffer >> tile;
a_Header.m_TilePalette.push_back(tile);
}
a_Buffer >> a_Header.m_SpawnColorPalette;
}
typedef std::vector<uint64_t> ChunkPackedData;
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);
}
template <>
void ReadMessage(DataBuffer& a_Buffer, td::protocol::pdata::WorldData& a_WorldData) {
std::uint64_t chunkCount;
a_Buffer >> chunkCount;
for (std::uint64_t chunkNumber = 0; chunkNumber < chunkCount; chunkNumber++) {
td::game::ChunkPtr chunk = std::make_shared<td::game::Chunk>();
td::game::ChunkCoord chunkCoords;
a_Buffer >> chunkCoords.x >> chunkCoords.y;
std::uint64_t chunkPaletteSize;
// std::reverse(reinterpret_cast<std::uint8_t*>(&chunkPaletteSize), reinterpret_cast<std::uint8_t*>(&chunkPaletteSize) + 4);
a_Buffer >> chunkPaletteSize;
td::game::ChunkPalette chunkPalette(chunkPaletteSize);
memcpy(reinterpret_cast<void*>(chunkPalette.data()), a_Buffer.data() + a_Buffer.GetReadOffset(),
chunkPaletteSize * sizeof(td::game::ChunkPalette::value_type));
a_Buffer.SetReadOffset(a_Buffer.GetReadOffset() + chunkPaletteSize * sizeof(td::game::ChunkPalette::value_type));
chunk->palette = chunkPalette;
std::uint8_t bitsPerTile = countBits(chunkPaletteSize);
// A bitmask that contains bitsPerTile set bits
td::game::Chunk::ChunkData::value_type individualValueMask = ((1 << bitsPerTile) - 1);
ChunkPackedData chunkData(td::game::Chunk::ChunkSize / (BITS_IN_BYTE * sizeof(ChunkPackedData::value_type) / bitsPerTile), 0);
memcpy(reinterpret_cast<void*>(chunkData.data()), a_Buffer.data() + a_Buffer.GetReadOffset(),
chunkData.size() * sizeof(ChunkPackedData::value_type));
a_Buffer.SetReadOffset(a_Buffer.GetReadOffset() + chunkData.size() * sizeof(ChunkPackedData::value_type));
for (unsigned int tileNumber = 0; tileNumber < td::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;
td::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;
}
a_WorldData.m_Chunks.insert({chunkCoords, chunk});
}
}
} // namespace details
} // namespace sp
class WorldApply : public td::protocol::PacketHandler { class WorldApply : public td::protocol::PacketHandler {
private: private:
td::game::World& m_World; td::game::World& m_World;
public: public:
WorldApply(td::game::World& a_World) : m_World(a_World) {} WorldApply(td::game::World& a_World) : m_World(a_World) {}
@@ -170,7 +29,7 @@ class WorldApply : public td::protocol::PacketHandler {
}; };
td::game::World GetWorld() { td::game::World GetWorld() {
sp::DataBuffer buffer; sp::DataBuffer buffer;
buffer.ReadFile("test/tdmap.tdmap2"); buffer.ReadFile("test/tdmap.tdmap2");
sp::DataBuffer buffer1 = sp::zlib::Decompress(buffer, 84); sp::DataBuffer buffer1 = sp::zlib::Decompress(buffer, 84);
@@ -196,6 +55,26 @@ sp::DataBuffer buffer;
return w; return w;
} }
void FastForward(td::game::World& a_World, const td::sim::GameHistory& a_LockSteps) {
const td::FpFloat delta = td::FpFloat(1) / td::FpFloat(75);
for (const auto& lockstep : a_LockSteps) {
a_World.Tick(lockstep, delta);
}
}
td::sim::GameHistory GetCustomHistory() {
constexpr std::size_t MAX_COUNT = 20 * 60 * 40;
td::sim::GameHistory gh(MAX_COUNT);
auto spawn = std::make_shared<td::protocol::commands::SpawnTroopCommand>(0, 0, td::Vec2fp{td::FpFloat(77), td::FpFloat(13)}, 0);
gh[0].push_back(spawn);
return gh;
}
int main(int argc, char** argv) { int main(int argc, char** argv) {
td::game::World w = GetWorld(); td::game::World w = GetWorld();
@@ -204,9 +83,11 @@ int main(int argc, char** argv) {
td::render::Camera cam; td::render::Camera cam;
auto mob = std::make_shared<td::game::Zombie>(0, 0, 0); display.OnAspectRatioChange.Connect([&cam](float a_AspectRatio){
mob->SetCenter({77, 13}); cam.UpdatePerspective(a_AspectRatio);
w.GetMobList().push_back(mob); });
td::sim::GameHistory gh = GetCustomHistory();
td::render::RenderPipeline renderer; td::render::RenderPipeline renderer;
renderer.AddRenderer<td::render::WorldRenderer>(cam, w); renderer.AddRenderer<td::render::WorldRenderer>(cam, w);
@@ -215,8 +96,11 @@ int main(int argc, char** argv) {
cam.SetCamPos({77, 25, 13}); cam.SetCamPos({77, 25, 13});
cam.UpdatePerspective(display.GetAspectRatio()); cam.UpdatePerspective(display.GetAspectRatio());
td::sim::RealTimeSimulation simulation(w, std::move(gh), 1000);
while (!display.IsCloseRequested()) { while (!display.IsCloseRequested()) {
display.PollEvents(); display.PollEvents();
simulation.Update();
renderer.Render(); renderer.Render();
display.Update(); display.Update();
} }

30
src/td/Types.cpp Normal file
View File

@@ -0,0 +1,30 @@
#include <td/Types.h>
#include <sp/common/DataBuffer.h>
#include <sp/common/ByteSwapping.h>
namespace td {
sp::DataBuffer& operator<<(sp::DataBuffer& a_Buffer, const EntityCoords& a_Coords) {
return a_Buffer << a_Coords.x << a_Coords.y;
}
sp::DataBuffer& operator<<(sp::DataBuffer& a_Buffer, const FpFloat& a_Float) {
auto raw = a_Float.raw_value();
sp::ToNetwork(raw);
return a_Buffer << raw;
}
sp::DataBuffer& operator>>(sp::DataBuffer& a_Buffer, EntityCoords& a_Coords) {
return a_Buffer >> a_Coords.x >> a_Coords.y;
}
sp::DataBuffer& operator>>(sp::DataBuffer& a_Buffer, FpFloat& a_Float) {
auto raw = a_Float.raw_value();
a_Buffer >> raw;
sp::FromNetwork(raw);
a_Float = FpFloat::from_raw_value(raw);
return a_Buffer;
}
} // namespace td

View File

@@ -1,9 +1,11 @@
#include <td/game/World.h> #include <td/game/World.h>
#include <td/simulation/WorldTicker.h>
namespace td { namespace td {
namespace game { namespace game {
World::World() : m_Teams{Team{TeamColor::Red}, Team{TeamColor::Blue}} { World::World() : m_CurrentState{.m_Teams{Team{TeamColor::Red}, Team{TeamColor::Blue}}} {
} }
@@ -55,5 +57,9 @@ bool World::LoadMap(const protocol::pdata::WorldData& a_WorldData) {
return true; return true;
} }
void World::Tick(const protocol::LockStep& a_LockStep, FpFloat a_Delta) {
m_CurrentState = m_Ticker.NextStep(*this, m_CurrentState, a_LockStep, a_Delta);
}
} // namespace game } // namespace game
} // namespace td } // namespace td

View File

@@ -0,0 +1,44 @@
#include <td/game/WorldTypes.h>
#include <sp/common/DataBuffer.h>
namespace td {
namespace game {
sp::DataBuffer& operator>>(sp::DataBuffer& buffer, TileType& tile) {
std::uint8_t raw;
buffer >> raw;
tile = TileType(raw);
return buffer;
}
sp::DataBuffer& operator>>(sp::DataBuffer& buffer, 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;
}
} // namespace game
} // namespace td

View File

@@ -125,6 +125,7 @@ void Display::PollEvents() {
m_LastWidth = event.window.data1; m_LastWidth = event.window.data1;
m_LastHeight = event.window.data2; m_LastHeight = event.window.data2;
m_AspectRatio = (float)m_LastWidth / m_LastHeight; m_AspectRatio = (float)m_LastWidth / m_LastHeight;
OnAspectRatioChange(m_AspectRatio);
} }
default: default:

View File

@@ -0,0 +1,115 @@
#include <td/protocol/packet/PacketSerialize.h>
#include <sp/io/MessageIO.h>
namespace sp {
namespace details {
template <>
void ReadMessage(DataBuffer& a_Buffer, td::protocol::pdata::WorldHeader& a_Header) {
a_Buffer >> a_Header.m_TowerPlacePalette >> a_Header.m_WalkablePalette;
std::uint16_t decoPaletteSize;
a_Buffer >> decoPaletteSize;
std::size_t decoPalletteSizeByte = decoPaletteSize * sizeof(td::Color);
a_Header.m_DecorationPalette.resize(decoPaletteSize);
memcpy(reinterpret_cast<std::uint8_t*>(a_Header.m_DecorationPalette.data()), a_Buffer.data() + a_Buffer.GetReadOffset(),
decoPalletteSizeByte);
a_Buffer.SetReadOffset(a_Buffer.GetReadOffset() + decoPalletteSizeByte);
a_Buffer >> a_Header.m_Background;
td::utils::shape::Rectangle redCastle, blueCastle;
a_Buffer >> a_Header.m_RedSpawn >> redCastle;
a_Buffer >> a_Header.m_BlueSpawn >> blueCastle;
a_Header.m_RedCastle.SetShape(redCastle);
a_Header.m_BlueCastle.SetShape(blueCastle);
std::uint64_t tilePaletteSize;
a_Buffer >> tilePaletteSize;
a_Header.m_TilePalette.reserve(tilePaletteSize);
for (std::uint64_t tileNumber = 0; tileNumber < tilePaletteSize; tileNumber++) {
td::game::TilePtr tile;
a_Buffer >> tile;
a_Header.m_TilePalette.push_back(tile);
}
a_Buffer >> a_Header.m_SpawnColorPalette;
}
typedef std::vector<uint64_t> ChunkPackedData;
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);
}
template <>
void ReadMessage(DataBuffer& a_Buffer, td::protocol::pdata::WorldData& a_WorldData) {
std::uint64_t chunkCount;
a_Buffer >> chunkCount;
for (std::uint64_t chunkNumber = 0; chunkNumber < chunkCount; chunkNumber++) {
td::game::ChunkPtr chunk = std::make_shared<td::game::Chunk>();
td::game::ChunkCoord chunkCoords;
a_Buffer >> chunkCoords.x >> chunkCoords.y;
std::uint64_t chunkPaletteSize;
// std::reverse(reinterpret_cast<std::uint8_t*>(&chunkPaletteSize), reinterpret_cast<std::uint8_t*>(&chunkPaletteSize) + 4);
a_Buffer >> chunkPaletteSize;
td::game::ChunkPalette chunkPalette(chunkPaletteSize);
memcpy(reinterpret_cast<void*>(chunkPalette.data()), a_Buffer.data() + a_Buffer.GetReadOffset(),
chunkPaletteSize * sizeof(td::game::ChunkPalette::value_type));
a_Buffer.SetReadOffset(a_Buffer.GetReadOffset() + chunkPaletteSize * sizeof(td::game::ChunkPalette::value_type));
chunk->palette = chunkPalette;
std::uint8_t bitsPerTile = countBits(chunkPaletteSize);
// A bitmask that contains bitsPerTile set bits
td::game::Chunk::ChunkData::value_type individualValueMask = ((1 << bitsPerTile) - 1);
ChunkPackedData chunkData(td::game::Chunk::ChunkSize / (BITS_IN_BYTE * sizeof(ChunkPackedData::value_type) / bitsPerTile), 0);
memcpy(reinterpret_cast<void*>(chunkData.data()), a_Buffer.data() + a_Buffer.GetReadOffset(),
chunkData.size() * sizeof(ChunkPackedData::value_type));
a_Buffer.SetReadOffset(a_Buffer.GetReadOffset() + chunkData.size() * sizeof(ChunkPackedData::value_type));
for (unsigned int tileNumber = 0; tileNumber < td::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;
td::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;
}
a_WorldData.m_Chunks.insert({chunkCoords, chunk});
}
}
} // namespace details
} // namespace sp

View File

@@ -16,8 +16,10 @@ EntityRenderer::~EntityRenderer() {}
void EntityRenderer::Render() { void EntityRenderer::Render() {
m_Shader->Start(); m_Shader->Start();
for (const auto& mob : m_World.GetMobList()) { for (const auto& mob : m_World.GetMobList()) {
const auto mobCoords = mob->GetCenter(); const auto mobCoords = mob->GetPosition();
m_Shader->SetModelPos({mobCoords.GetX(), 1, mobCoords.GetY()}); float x = static_cast<float>(mobCoords.x);
float z = static_cast<float>(mobCoords.y);
m_Shader->SetModelPos({x, 1, z});
Renderer::Render(*m_EntityVao); Renderer::Render(*m_EntityVao);
} }
} }

View File

@@ -0,0 +1,15 @@
#include <td/simulation/CommandApply.h>
namespace td {
namespace sim {
CommandApply::CommandApply(const game::World& a_World, WorldSnapshot& a_Snapshot) : m_World(a_World), m_Snapshot(a_Snapshot) {}
void CommandApply::Handle(const protocol::cdata::SpawnTroop& a_SpawnTroop) {
auto zombie = std::make_shared<game::Zombie>(0, *a_SpawnTroop.m_Level, a_SpawnTroop.m_Sender);
zombie->GetPosition() = a_SpawnTroop.m_Position;
m_Snapshot.m_Mobs.push_back(zombie);
}
} // namespace sim
} // namespace td

View File

@@ -1,4 +1,4 @@
#include <td/game/GameHistory.h> #include <td/simulation/GameHistory.h>
namespace td { namespace td {
namespace game { namespace game {

View File

@@ -0,0 +1,32 @@
#include <td/simulation/RealTimeSimulation.h>
#include <chrono>
namespace td {
namespace sim {
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());
}
RealTimeSimulation::RealTimeSimulation(game::World& a_World, GameHistory&& a_History, std::uint64_t a_StepTime) :
m_StepTime(a_StepTime),
m_World(a_World),
m_History(std::move(a_History)),
m_CurrentTime(0),
m_LastTime(GetTime()),
m_CurrentStep(0) {}
void RealTimeSimulation::Update() {
m_CurrentTime += GetTime() - m_LastTime;
m_LastTime = GetTime();
if (m_CurrentTime > m_StepTime) {
m_World.Tick(m_History[m_CurrentStep], FpFloat(m_StepTime) / FpFloat(1000));
m_CurrentStep++;
m_CurrentTime -= m_StepTime;
}
}
} // namespace sim
} // namespace td

View File

@@ -0,0 +1,41 @@
#include <td/simulation/WorldTicker.h>
#include <td/simulation/system/EntityMove.h>
#include <td/simulation/CommandApply.h>
#define ADD_SYSTEM(class) std::move(std::make_unique<class>())
namespace td {
namespace sim {
WorldTicker::WorldTicker() {
AddSystem<EntityMove>();
}
WorldSnapshot WorldTicker::NextStep(
const game::World& a_World, const WorldSnapshot& a_PreviousState, const protocol::LockStep& a_LockStep, FpFloat a_Delta) {
WorldSnapshot next = CreateNext(a_PreviousState);
ApplySteps(a_World, next, a_LockStep);
Tick(a_World, next, a_Delta);
return next;
}
void WorldTicker::ApplySteps(const game::World& a_World, WorldSnapshot& a_State, const protocol::LockStep& a_LockStep) {
CommandApply cmdHandler(a_World, a_State);
for (const auto& cmd : a_LockStep) {
cmd->Dispatch(cmdHandler);
}
}
void WorldTicker::Tick(const game::World& a_World, WorldSnapshot& a_State, FpFloat a_Delta) {
for (const auto& system : m_Systems) {
system->Tick(a_World, a_State, a_Delta);
}
}
WorldSnapshot WorldTicker::CreateNext(const WorldSnapshot& a_PreviousState) {
return a_PreviousState;
}
} // namespace sim
} // namespace td

View File

@@ -0,0 +1,13 @@
#include <td/simulation/system/EntityMove.h>
namespace td {
namespace sim {
void EntityMove::Tick(const game::World& a_World, WorldSnapshot& a_State, FpFloat a_Delta) {
for (auto& mob : a_State.m_Mobs) {
mob->GetPosition().x += a_Delta;
}
}
} // namespace sim
} // namespace td