diff --git a/include/td/Maths.h b/include/td/Maths.h index c5cad53..023ea6d 100644 --- a/include/td/Maths.h +++ b/include/td/Maths.h @@ -176,6 +176,11 @@ Mat4f Look(const Vec3f& eye, const Vec3f& center, const Vec3f& up); Mat4f Inverse(const Mat4f& mat); +template +T Lerp(T v0, T v1, T t) { + return (T(1) - t) * v0 + t * v1; +} + } // namespace maths diff --git a/include/td/game/Mobs.h b/include/td/game/Mobs.h index 9f24e5f..43ad263 100644 --- a/include/td/game/Mobs.h +++ b/include/td/game/Mobs.h @@ -9,6 +9,10 @@ #include #include +#include +#include +#include + namespace td { using Vec2fp = Vec2; @@ -65,152 +69,33 @@ const MobStats* GetMobStats(MobType type, std::uint8_t level); const TowerImmunities& GetMobTowerImmunities(MobType type, std::uint8_t level); const EffectImmunities& GetMobEffectImmunities(MobType type, std::uint8_t level); -class Mob { - protected: - float m_Health; +class MobHandler; - private: - Vec2fp m_Position; +struct MobData {}; + +class Mob : public sp::MessageBase { + public: MobID m_ID; - PlayerID m_Sender; MobLevel m_Level; + PlayerID m_Sender; + float m_Health; + Vec2fp m_Position; Direction m_Direction; std::vector m_Effects; const Tower* m_LastDamage; // the last tower that damaged the mob float m_HitCooldown; - - // utils::Timer m_EffectFireTimer; - // utils::Timer m_EffectPoisonTimer; - // utils::Timer m_EffectHealTimer; - TeamCastle* m_CastleTarget; // utils::CooldownTimer m_AttackTimer; - public: - Mob(MobID id, MobLevel level, PlayerID sender) : m_Sender(sender), m_Level(level), m_HitCooldown(0), m_CastleTarget(nullptr) {} + MobPtr m_Next; - virtual ~Mob() {} + Mob() {} - virtual MobType GetType() const = 0; - - virtual bool OnDeath(World* world) { - return true; - } - - Vec2fp& GetPosition() { - return m_Position; - } - - MobID GetMobID() const { - return m_ID; - } - const TowerImmunities& GetTowerImmunities() const { - return GetMobTowerImmunities(GetType(), m_Level); - } - const EffectImmunities& GetEffectImmunities() const { - return GetMobEffectImmunities(GetType(), m_Level); - } - PlayerID GetSender() const { - return m_Sender; - } - MobLevel GetLevel() const { - return m_Level; - } - const MobStats* GetStats() const { - return GetMobStats(GetType(), m_Level); - } - void SetHealth(float newHealth) { - m_Health = newHealth; - } - float GetHealth() const { - return m_Health; - } - bool IsDead() const { - return m_Health <= 0; - } - bool IsAlive() const { - return m_Health > 0; - } - const Tower* GetLastDamageTower() { - return m_LastDamage; - } - bool HasReachedEnemyCastle() { - return m_CastleTarget != nullptr; - } - - void Damage(float dmg, const Tower* damager) { - m_Health = std::max(0.0f, m_Health - dmg); - m_LastDamage = damager; - m_HitCooldown = 0.1; - } - - void Heal(float heal) { - m_Health = std::min(static_cast(GetStats()->m_MaxLife), m_Health + heal); - } - - void SetMobReachedCastle(TeamCastle* castle) { - m_CastleTarget = castle; - } // used when mob is in front of the castle - - bool IsImmuneTo(TowerType type); - - bool IsImmuneTo(EffectType type); - void AddEffect(EffectType type, float durationSec, Tower* tower); - bool HasEffect(EffectType type); - - bool HasTakenDamage() { - return m_HitCooldown > 0; - } - - // // returns a float between 0 and 1 excluded - // float GetTileX() { - // return GetCenterX() - static_cast(static_cast(GetCenterX())); - // } - - // // returns a float between 0 and 1 excluded - // float GetTileY() { - // return GetCenterY() - static_cast(static_cast(GetCenterY())); - // } - - Direction GetDirection() const { - return m_Direction; - } - void SetDirection(Direction dir) { - m_Direction = dir; - } - - protected: - void InitMob() { - m_Health = static_cast(GetStats()->m_MaxLife); - // SetSize(GetStats()->m_Size.x, GetStats()->m_Size.y); - } - - private: - void UpdateEffects(std::uint64_t delta, World* world); - void AttackCastle(std::uint64_t delta, World* world); - void Move(std::uint64_t delta, World* world); - void Walk(std::uint64_t delta, World* world); - void MoveBack(const TeamCastle& castle, World* world); - void ChangeDirection(const WalkableTile& tile, World* world); - bool IsTouchingCastle(const TeamCastle& castle) const; - EffectDuration& GetEffect(EffectType type); + Mob& operator=(const Mob& a_Other) = default; }; -typedef std::shared_ptr MobPtr; - -template -class ConcreteMob : public Mob { - public: - ConcreteMob(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { - InitMob(); - } - - virtual ~ConcreteMob() {} - - virtual constexpr MobType GetType() const override { - return MT; - } -}; +template +using ConcreteMob = sp::ConcreteMessage; using Zombie = ConcreteMob; using Spider = ConcreteMob; @@ -223,16 +108,17 @@ using Witch = ConcreteMob; using Slime = ConcreteMob; using Giant = ConcreteMob; -namespace MobFactory { +using AllMobs = std::tuple; -MobPtr CreateMob(MobID id, MobType type, std::uint8_t level, PlayerID sender); -std::string GetMobName(MobType type); +class MobHandler : public sp::GenericHandler {}; -} // namespace MobFactory +using MobFactory = sp::MessageFactory; class MobListener { public: - virtual void OnMobSpawn(Mob* mob) {} + virtual void OnMobSpawn(Mob* mob) { + MobHandler h; + } virtual void OnMobDie(Mob* mob) {} virtual void OnMobDamage(Mob* target, float damage, Tower* damager) {} diff --git a/include/td/game/World.h b/include/td/game/World.h index da2b1f7..410a04c 100644 --- a/include/td/game/World.h +++ b/include/td/game/World.h @@ -21,6 +21,7 @@ class World { TilePalette m_TilePalette; sim::WorldSnapshot m_CurrentState; + sim::WorldSnapshot m_NextState; private: sim::WorldTicker m_Ticker; diff --git a/include/td/protocol/command/Commands.h b/include/td/protocol/command/Commands.h index 87f5c4e..a0dcdbb 100644 --- a/include/td/protocol/command/Commands.h +++ b/include/td/protocol/command/Commands.h @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include #include @@ -33,7 +33,7 @@ class CommandHandler; using CommandBase = sp::MessageBase; template -using CommandMessage = sp::ConcreteMessage; +using CommandMessage = sp::ConcreteMessage; namespace commands { @@ -51,7 +51,7 @@ using UseItemCommand = CommandMessage; using AllCommands = std::tuple; -class CommandHandler : public sp::MessageHandler {}; +class CommandHandler : public sp::GenericHandler {}; using CommandDispatcher = sp::MessageDispatcher; diff --git a/include/td/protocol/packet/Packets.h b/include/td/protocol/packet/Packets.h index 4be3b2f..544061b 100644 --- a/include/td/protocol/packet/Packets.h +++ b/include/td/protocol/packet/Packets.h @@ -12,7 +12,7 @@ #include #include #include -#include +#include namespace td { namespace protocol { @@ -37,7 +37,7 @@ using PacketBase = sp::MessageBase; template -using PacketMessage = sp::ConcreteMessage; +using PacketMessage = sp::ConcreteMessage; namespace packets { @@ -61,7 +61,7 @@ using AllPackets = std::tuple; -class PacketHandler : public sp::MessageHandler {}; +class PacketHandler : public sp::GenericHandler {}; using PacketDispatcher = sp::MessageDispatcher; diff --git a/include/td/render/Renderer.h b/include/td/render/Renderer.h index fa25234..cb5c23b 100644 --- a/include/td/render/Renderer.h +++ b/include/td/render/Renderer.h @@ -10,7 +10,8 @@ namespace render { class BasicRenderer { public: - virtual void Render() = 0; + virtual void Render(float a_Lerp) = 0; + virtual ~BasicRenderer() {} void Render(const GL::VertexArray& a_Vao); }; @@ -24,6 +25,11 @@ class Renderer : public BasicRenderer { public: Renderer(Camera& a_Camera); virtual ~Renderer() {} + + template + float Lerp(const T& a_Mob, float a_LerpFactor, const std::function& a_MemberGetter) { + return static_cast(maths::Lerp(a_MemberGetter(a_Mob), a_MemberGetter(*a_Mob.m_Next), a_LerpFactor)); + } }; class RenderPipeline { @@ -32,7 +38,7 @@ class RenderPipeline { public: RenderPipeline(); - ~RenderPipeline() = default; + virtual ~RenderPipeline() {} template void AddRenderer(Args&&... args) { @@ -43,9 +49,9 @@ class RenderPipeline { m_Renderers.clear(); } - void Render() { + void Render(float a_Lerp) { for (auto& renderer : m_Renderers) { - renderer->Render(); + renderer->Render(a_Lerp); } } }; diff --git a/include/td/render/renderer/EntityRenderer.h b/include/td/render/renderer/EntityRenderer.h index 4f6562c..eefd91d 100644 --- a/include/td/render/renderer/EntityRenderer.h +++ b/include/td/render/renderer/EntityRenderer.h @@ -16,7 +16,7 @@ class EntityRenderer : public Renderer { EntityRenderer(Camera& a_Camera, const game::World& a_World); virtual ~EntityRenderer(); - virtual void Render() override; + virtual void Render(float a_Lerp) override; }; } // namespace render diff --git a/include/td/render/renderer/WorldRenderer.h b/include/td/render/renderer/WorldRenderer.h index 93dfb8d..ded2b24 100644 --- a/include/td/render/renderer/WorldRenderer.h +++ b/include/td/render/renderer/WorldRenderer.h @@ -10,14 +10,13 @@ namespace render { class WorldRenderer : public Renderer { private: - const game::World& m_World; std::unique_ptr m_WorldVao; public: WorldRenderer(Camera& a_Camera, const game::World& a_World); virtual ~WorldRenderer(); - virtual void Render() override; + virtual void Render(float a_Lerp) override; }; } // namespace render diff --git a/include/td/simulation/CommandApply.h b/include/td/simulation/CommandApply.h index e98c217..b16dfe2 100644 --- a/include/td/simulation/CommandApply.h +++ b/include/td/simulation/CommandApply.h @@ -14,7 +14,7 @@ class CommandApply : public protocol::CommandHandler { public: CommandApply(const game::World& a_World, WorldSnapshot& a_Snapshot); - virtual void Handle(const protocol::cdata::SpawnTroop& a_SpawnTroop) override; + virtual void Handle(const protocol::commands::SpawnTroopCommand& a_SpawnTroop) override; }; } // namespace sim diff --git a/include/td/simulation/RealTimeSimulation.h b/include/td/simulation/RealTimeSimulation.h index a97b03e..694f500 100644 --- a/include/td/simulation/RealTimeSimulation.h +++ b/include/td/simulation/RealTimeSimulation.h @@ -17,9 +17,18 @@ class RealTimeSimulation { std::size_t m_CurrentStep; public: + /** + * \param a_StepTime in ms + */ RealTimeSimulation(game::World& a_World, GameHistory&& a_History, std::uint64_t a_StepTime); - void Update(); + /** + * \return the progress [0-1] between two steps + */ + float Update(); + + private: + void Step(); }; } // namespace sim diff --git a/include/td/simulation/WorldTicker.h b/include/td/simulation/WorldTicker.h index 42555e3..9d6d838 100644 --- a/include/td/simulation/WorldTicker.h +++ b/include/td/simulation/WorldTicker.h @@ -23,12 +23,12 @@ class WorldTicker { WorldTicker(); WorldSnapshot NextStep( - const game::World& a_World, const WorldSnapshot& a_PreviousState, const protocol::LockStep& a_LockStep, FpFloat a_Delta); + const game::World& a_World, 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); + WorldSnapshot CreateNext(WorldSnapshot& a_PreviousState); template void AddSystem() { diff --git a/src/main.cpp b/src/main.cpp index 53f6b90..8b035d4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -20,11 +20,11 @@ class WorldApply : public td::protocol::PacketHandler { public: WorldApply(td::game::World& a_World) : m_World(a_World) {} - void Handle(const td::protocol::pdata::WorldHeader& a_Header) override { - m_World.LoadMap(a_Header); + void Handle(const td::protocol::packets::WorldHeaderPacket& a_Header) override { + m_World.LoadMap(*a_Header); } - void Handle(const td::protocol::pdata::WorldData& a_Data) override { - m_World.LoadMap(a_Data); + void Handle(const td::protocol::packets::WorldDataPacket& a_Data) override { + m_World.LoadMap(*a_Data); } }; @@ -93,15 +93,15 @@ int main(int argc, char** argv) { renderer.AddRenderer(cam, w); renderer.AddRenderer(cam, w); - cam.SetCamPos({77, 25, 13}); + cam.SetCamPos({77, 5, 13}); cam.UpdatePerspective(display.GetAspectRatio()); - td::sim::RealTimeSimulation simulation(w, std::move(gh), 1000); + td::sim::RealTimeSimulation simulation(w, std::move(gh), 2000); while (!display.IsCloseRequested()) { display.PollEvents(); - simulation.Update(); - renderer.Render(); + float lerp = simulation.Update(); + renderer.Render(lerp); display.Update(); } diff --git a/src/td/game/World.cpp b/src/td/game/World.cpp index fefb18b..78c515a 100644 --- a/src/td/game/World.cpp +++ b/src/td/game/World.cpp @@ -5,9 +5,7 @@ namespace td { namespace game { -World::World() : m_CurrentState{.m_Teams{Team{TeamColor::Red}, Team{TeamColor::Blue}}} { - -} +World::World() : m_CurrentState{.m_Teams{Team{TeamColor::Red}, Team{TeamColor::Blue}}}, m_NextState{.m_Teams{Team{TeamColor::Red}, Team{TeamColor::Blue}}} {} const Color* World::GetTileColor(TilePtr tile) const { switch (tile->GetType()) { @@ -58,7 +56,8 @@ bool World::LoadMap(const protocol::pdata::WorldData& a_WorldData) { } void World::Tick(const protocol::LockStep& a_LockStep, FpFloat a_Delta) { - m_CurrentState = m_Ticker.NextStep(*this, m_CurrentState, a_LockStep, a_Delta); + m_CurrentState = m_NextState; + m_NextState = m_Ticker.NextStep(*this, m_NextState, a_LockStep, a_Delta); } } // namespace game diff --git a/src/td/render/renderer/EntityRenderer.cpp b/src/td/render/renderer/EntityRenderer.cpp index 55eaa67..bdfa53b 100644 --- a/src/td/render/renderer/EntityRenderer.cpp +++ b/src/td/render/renderer/EntityRenderer.cpp @@ -13,16 +13,19 @@ EntityRenderer::EntityRenderer(Camera& a_Camera, const game::World& a_World) : R EntityRenderer::~EntityRenderer() {} -void EntityRenderer::Render() { + + +void EntityRenderer::Render(float a_Lerp) { m_Shader->Start(); for (const auto& mob : m_World.GetMobList()) { - const auto mobCoords = mob->GetPosition(); - float x = static_cast(mobCoords.x); - float z = static_cast(mobCoords.y); + + float x = Lerp(*mob, a_Lerp, [](const game::Mob& a_Mob) { return static_cast(a_Mob.m_Position.x); }); + float z = Lerp(*mob, a_Lerp, [](const game::Mob& a_Mob) { return static_cast(a_Mob.m_Position.y); }); + m_Shader->SetModelPos({x, 1, z}); Renderer::Render(*m_EntityVao); } } -} // namespace render +} // namespace render } // namespace td diff --git a/src/td/render/renderer/WorldRenderer.cpp b/src/td/render/renderer/WorldRenderer.cpp index 468aad3..078dc5d 100644 --- a/src/td/render/renderer/WorldRenderer.cpp +++ b/src/td/render/renderer/WorldRenderer.cpp @@ -7,13 +7,13 @@ namespace td { namespace render { -WorldRenderer::WorldRenderer(Camera& a_Camera, const game::World& a_World) : Renderer(a_Camera), m_World(a_World) { - m_WorldVao = std::make_unique(std::move(WorldLoader::LoadWorldModel(&a_World))); +WorldRenderer::WorldRenderer(Camera& a_Camera, const game::World& a_World) : Renderer(a_Camera) { + m_WorldVao = std::make_unique(WorldLoader::LoadWorldModel(&a_World)); } WorldRenderer::~WorldRenderer() {} -void WorldRenderer::Render() { +void WorldRenderer::Render(float a_Lerp) { m_Shader->Start(); Renderer::Render(*m_WorldVao); ImGui::ShowDemoWindow(); diff --git a/src/td/simulation/CommandApply.cpp b/src/td/simulation/CommandApply.cpp index eeaa9e2..852ef6d 100644 --- a/src/td/simulation/CommandApply.cpp +++ b/src/td/simulation/CommandApply.cpp @@ -5,9 +5,9 @@ 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(0, *a_SpawnTroop.m_Level, a_SpawnTroop.m_Sender); - zombie->GetPosition() = a_SpawnTroop.m_Position; +void CommandApply::Handle(const protocol::commands::SpawnTroopCommand& a_SpawnTroop) { + auto zombie = std::make_shared(); + zombie->m_Position = a_SpawnTroop->m_Position; m_Snapshot.m_Mobs.push_back(zombie); } diff --git a/src/td/simulation/RealTimeSimulation.cpp b/src/td/simulation/RealTimeSimulation.cpp index 6abfc42..438c424 100644 --- a/src/td/simulation/RealTimeSimulation.cpp +++ b/src/td/simulation/RealTimeSimulation.cpp @@ -16,16 +16,24 @@ RealTimeSimulation::RealTimeSimulation(game::World& a_World, GameHistory&& a_His m_History(std::move(a_History)), m_CurrentTime(0), m_LastTime(GetTime()), - m_CurrentStep(0) {} + m_CurrentStep(0) { + Step(); +} -void RealTimeSimulation::Update() { +float RealTimeSimulation::Update() { + // TODO: handle freezes (m_CurrentTime > 2 * m_StepTime) 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++; + Step(); m_CurrentTime -= m_StepTime; } + return (float) m_CurrentTime / (float) m_StepTime; +} + +void RealTimeSimulation::Step() { + m_World.Tick(m_History[m_CurrentStep], FpFloat(m_StepTime) / FpFloat(1000)); + m_CurrentStep++; } } // namespace sim diff --git a/src/td/simulation/WorldTicker.cpp b/src/td/simulation/WorldTicker.cpp index 367f90b..ae972a8 100644 --- a/src/td/simulation/WorldTicker.cpp +++ b/src/td/simulation/WorldTicker.cpp @@ -13,9 +13,9 @@ WorldTicker::WorldTicker() { } WorldSnapshot WorldTicker::NextStep( - const game::World& a_World, const WorldSnapshot& a_PreviousState, const protocol::LockStep& a_LockStep, FpFloat a_Delta) { + const game::World& a_World, WorldSnapshot& a_PreviousState, const protocol::LockStep& a_LockStep, FpFloat a_Delta) { WorldSnapshot next = CreateNext(a_PreviousState); - ApplySteps(a_World, next, a_LockStep); + ApplySteps(a_World, next, a_LockStep); // maybe swap with tick ? Tick(a_World, next, a_Delta); return next; } @@ -33,8 +33,21 @@ void WorldTicker::Tick(const game::World& a_World, WorldSnapshot& a_State, FpFlo } } -WorldSnapshot WorldTicker::CreateNext(const WorldSnapshot& a_PreviousState) { - return a_PreviousState; +WorldSnapshot WorldTicker::CreateNext(WorldSnapshot& a_PreviousState) { + static game::MobFactory mobFactory; + + WorldSnapshot next { + .m_Towers = a_PreviousState.m_Towers, + .m_Teams = a_PreviousState.m_Teams + }; + next.m_Mobs.reserve(a_PreviousState.m_Mobs.size()); + for (auto& mob : a_PreviousState.m_Mobs) { + game::MobPtr newMob = std::shared_ptr(mobFactory.CreateMessage(mob->GetId()).release()); + *newMob = *mob; + mob->m_Next = newMob; + next.m_Mobs.push_back(newMob); + } + return next; } } // namespace sim diff --git a/src/td/simulation/system/EntityMove.cpp b/src/td/simulation/system/EntityMove.cpp index 4592762..798a6f8 100644 --- a/src/td/simulation/system/EntityMove.cpp +++ b/src/td/simulation/system/EntityMove.cpp @@ -5,7 +5,7 @@ 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; + mob->m_Position.x += a_Delta; } }