Index: source/simulation2/components/CCmpCinemaManager.h
===================================================================
--- /dev/null
+++ source/simulation2/components/CCmpCinemaManager.h
@@ -0,0 +1,91 @@
+/* Copyright (C) 2022 Wildfire Games.
+ * This file is part of 0 A.D.
+ *
+ * 0 A.D. is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D. If not, see .
+ */
+
+#ifndef INCLUDED_CCMPCINEMAMANAGER
+#define INCLUDED_CCMPCINEMAMANAGER
+
+#include "ICmpCinemaManager.h"
+
+class CCmpCinemaManager final : public ICmpCinemaManager
+{
+public:
+ static constexpr int typeId{CID_CinemaManager};
+
+ static void ClassInit(CComponentManager& componentManager);
+
+ static IComponent* Allocate(const ScriptInterface&, JS::HandleValue);
+ static void Deallocate(IComponent* cmp);
+
+ int GetComponentTypeId() const override;
+
+ static std::string GetSchema();
+
+ void Init(const CParamNode& UNUSED(paramNode)) override;
+
+ void Deinit() override;
+
+ void Serialize(ISerializer& serializer) override;
+
+ void Deserialize(const CParamNode& UNUSED(paramNode), IDeserializer& deserializer) override;
+
+ void HandleMessage(const CMessage& msg, bool UNUSED(global)) override;
+
+ void AddPath(const CCinemaPath& path) override;
+
+ void AddCinemaPathToQueue(const CStrW& name) override;
+
+ void Play() override;
+
+ void Stop() override;
+
+ bool HasPath(const CStrW& name) const override;
+
+ void ClearQueue() override;
+
+ void DeletePath(const CStrW& name) override;
+
+ const std::map& GetPaths() const override;
+
+ void SetPaths(const std::map& newPaths) override;
+
+ const std::list& GetQueue() const override;
+
+ bool IsEnabled() const override;
+
+ void SetEnabled(bool enabled) override;
+
+ void PlayQueue(const float deltaRealTime, CCamera* camera) override;
+
+private:
+
+ void SerializePath(const CCinemaPath& path, ISerializer& serializer);
+
+ CCinemaPath DeserializePath(IDeserializer& deserializer);
+
+ bool m_Enabled;
+ std::map m_Paths;
+ std::list m_PathQueue;
+
+ // States before playing
+ bool m_MapRevealed;
+
+ fixed m_ElapsedTime;
+ fixed m_TotalTime;
+ fixed m_CurrentPathElapsedTime;
+};
+
+#endif // INCLUDED_CCMPCINEMAMANAGER
Index: source/simulation2/components/CCmpCinemaManager.cpp
===================================================================
--- source/simulation2/components/CCmpCinemaManager.cpp
+++ source/simulation2/components/CCmpCinemaManager.cpp
@@ -17,8 +17,9 @@
#include "precompiled.h"
+#include "CCmpCinemaManager.h"
+
#include "simulation2/system/Component.h"
-#include "ICmpCinemaManager.h"
#include "ps/CLogger.h"
#include "simulation2/components/ICmpOverlayRenderer.h"
@@ -29,293 +30,319 @@
#include "simulation2/Simulation2.h"
-class CCmpCinemaManager final : public ICmpCinemaManager
+
+void CCmpCinemaManager::ClassInit(CComponentManager& componentManager)
{
-public:
- static void ClassInit(CComponentManager& componentManager)
- {
- componentManager.SubscribeToMessageType(MT_Update);
- }
+ componentManager.SubscribeToMessageType(MT_Update);
+}
- DEFAULT_COMPONENT_ALLOCATOR(CinemaManager)
+IComponent* CCmpCinemaManager::Allocate(const ScriptInterface&, JS::HandleValue)
+{
+ return nullptr;
+}
- static std::string GetSchema()
- {
- return "";
- }
+void CCmpCinemaManager::Deallocate(IComponent*)
+{}
+
+int CCmpCinemaManager::GetComponentTypeId() const
+{
+ return CID_CinemaManager;
+}
+
+std::string CCmpCinemaManager::GetSchema()
+{
+ return "";
+}
+
+void CCmpCinemaManager::Init(const CParamNode& UNUSED(paramNode))
+{
+ m_Enabled = false;
+ m_MapRevealed = false;
+ m_ElapsedTime = fixed::Zero();
+ m_TotalTime = fixed::Zero();
+ m_CurrentPathElapsedTime = fixed::Zero();
+}
+
+void CCmpCinemaManager::Deinit()
+{}
+
+void CCmpCinemaManager::Serialize(ISerializer& serializer)
+{
+ serializer.Bool("Enabled", m_Enabled);
+ serializer.NumberFixed_Unbounded("ElapsedTime", m_ElapsedTime);
+ serializer.NumberFixed_Unbounded("CurrentPathElapsedTime", m_CurrentPathElapsedTime);
+ serializer.Bool("MapRevealed", m_MapRevealed);
+
+ serializer.NumberU32_Unbounded("NumberOfPaths", m_Paths.size());
+ for (const std::pair& it : m_Paths)
+ SerializePath(it.second, serializer);
+
+ serializer.NumberU32_Unbounded("NumberOfQueuedPaths", m_PathQueue.size());
+ for (const CCinemaPath& path : m_PathQueue)
+ serializer.String("PathName", path.GetName(), 1, 128);
+}
- void Init(const CParamNode& UNUSED(paramNode)) override
+void CCmpCinemaManager::Deserialize(const CParamNode& UNUSED(paramNode), IDeserializer& deserializer)
+{
+ deserializer.Bool("Enabled", m_Enabled);
+ deserializer.NumberFixed_Unbounded("ElapsedTime", m_ElapsedTime);
+ deserializer.NumberFixed_Unbounded("CurrentPathElapsedTime", m_CurrentPathElapsedTime);
+ deserializer.Bool("MapRevealed", m_MapRevealed);
+
+ uint32_t numberOfPaths = 0;
+ deserializer.NumberU32_Unbounded("NumberOfPaths", numberOfPaths);
+ for (uint32_t i = 0; i < numberOfPaths; ++i)
{
- m_Enabled = false;
- m_MapRevealed = false;
- m_ElapsedTime = fixed::Zero();
- m_TotalTime = fixed::Zero();
- m_CurrentPathElapsedTime = fixed::Zero();
+ CCinemaPath path = DeserializePath(deserializer);
+ m_Paths[path.GetName()] = path;
}
- void Deinit() override
+ uint32_t numberOfQueuedPaths = 0;
+ deserializer.NumberU32_Unbounded("NumberOfQueuedPaths", numberOfQueuedPaths);
+ for (uint32_t i = 0; i < numberOfQueuedPaths; ++i)
{
+ CStrW pathName;
+ deserializer.String("PathName", pathName, 1, 128);
+ ENSURE(HasPath(pathName));
+ AddCinemaPathToQueue(pathName);
}
- void Serialize(ISerializer& serializer) override
+ if (!m_PathQueue.empty())
{
- serializer.Bool("Enabled", m_Enabled);
- serializer.NumberFixed_Unbounded("ElapsedTime", m_ElapsedTime);
- serializer.NumberFixed_Unbounded("CurrentPathElapsedTime", m_CurrentPathElapsedTime);
- serializer.Bool("MapRevealed", m_MapRevealed);
-
- serializer.NumberU32_Unbounded("NumberOfPaths", m_Paths.size());
- for (const std::pair& it : m_Paths)
- SerializePath(it.second, serializer);
-
- serializer.NumberU32_Unbounded("NumberOfQueuedPaths", m_PathQueue.size());
- for (const CCinemaPath& path : m_PathQueue)
- serializer.String("PathName", path.GetName(), 1, 128);
+ m_PathQueue.front().m_TimeElapsed = m_CurrentPathElapsedTime.ToFloat();
+ m_PathQueue.front().Validate();
}
- void Deserialize(const CParamNode& UNUSED(paramNode), IDeserializer& deserializer) override
+ SetEnabled(m_Enabled);
+}
+
+void CCmpCinemaManager::HandleMessage(const CMessage& msg, bool UNUSED(global))
+{
+ switch (msg.GetType())
{
- deserializer.Bool("Enabled", m_Enabled);
- deserializer.NumberFixed_Unbounded("ElapsedTime", m_ElapsedTime);
- deserializer.NumberFixed_Unbounded("CurrentPathElapsedTime", m_CurrentPathElapsedTime);
- deserializer.Bool("MapRevealed", m_MapRevealed);
-
- uint32_t numberOfPaths = 0;
- deserializer.NumberU32_Unbounded("NumberOfPaths", numberOfPaths);
- for (uint32_t i = 0; i < numberOfPaths; ++i)
- {
- CCinemaPath path = DeserializePath(deserializer);
- m_Paths[path.GetName()] = path;
- }
+ case MT_Update:
+ {
+ const CMessageUpdate &msgData = static_cast(msg);
+ if (!m_Enabled)
+ break;
- uint32_t numberOfQueuedPaths = 0;
- deserializer.NumberU32_Unbounded("NumberOfQueuedPaths", numberOfQueuedPaths);
- for (uint32_t i = 0; i < numberOfQueuedPaths; ++i)
+ m_ElapsedTime += msgData.turnLength;
+ m_CurrentPathElapsedTime += msgData.turnLength;
+ if (m_CurrentPathElapsedTime >= m_PathQueue.front().GetDuration())
{
- CStrW pathName;
- deserializer.String("PathName", pathName, 1, 128);
- ENSURE(HasPath(pathName));
- AddCinemaPathToQueue(pathName);
+ CMessageCinemaPathEnded msgCinemaPathEnded(m_PathQueue.front().GetName());
+ m_PathQueue.pop_front();
+ GetSimContext().GetComponentManager().PostMessage(SYSTEM_ENTITY, msgCinemaPathEnded);
+ m_CurrentPathElapsedTime = fixed::Zero();
+
+ if (!m_PathQueue.empty())
+ m_PathQueue.front().Reset();
}
- if (!m_PathQueue.empty())
+ if (m_ElapsedTime >= m_TotalTime)
{
- m_PathQueue.front().m_TimeElapsed = m_CurrentPathElapsedTime.ToFloat();
- m_PathQueue.front().Validate();
+ m_CurrentPathElapsedTime = fixed::Zero();
+ m_ElapsedTime = fixed::Zero();
+ m_TotalTime = fixed::Zero();
+ SetEnabled(false);
+ GetSimContext().GetComponentManager().PostMessage(SYSTEM_ENTITY, CMessageCinemaQueueEnded());
}
- SetEnabled(m_Enabled);
+ break;
}
+ default:
+ break;
+ }
+}
- void HandleMessage(const CMessage& msg, bool UNUSED(global)) override
+void CCmpCinemaManager::AddPath(const CCinemaPath& path)
+{
+ if (m_Paths.find(path.GetName()) != m_Paths.end())
{
- switch (msg.GetType())
- {
- case MT_Update:
- {
- const CMessageUpdate &msgData = static_cast(msg);
- if (!m_Enabled)
- break;
-
- m_ElapsedTime += msgData.turnLength;
- m_CurrentPathElapsedTime += msgData.turnLength;
- if (m_CurrentPathElapsedTime >= m_PathQueue.front().GetDuration())
- {
- CMessageCinemaPathEnded msgCinemaPathEnded(m_PathQueue.front().GetName());
- m_PathQueue.pop_front();
- GetSimContext().GetComponentManager().PostMessage(SYSTEM_ENTITY, msgCinemaPathEnded);
- m_CurrentPathElapsedTime = fixed::Zero();
-
- if (!m_PathQueue.empty())
- m_PathQueue.front().Reset();
- }
-
- if (m_ElapsedTime >= m_TotalTime)
- {
- m_CurrentPathElapsedTime = fixed::Zero();
- m_ElapsedTime = fixed::Zero();
- m_TotalTime = fixed::Zero();
- SetEnabled(false);
- GetSimContext().GetComponentManager().PostMessage(SYSTEM_ENTITY, CMessageCinemaQueueEnded());
- }
-
- break;
- }
- default:
- break;
- }
+ LOGWARNING("Path with name '%s' already exists", path.GetName().ToUTF8());
+ return;
}
+ m_Paths[path.GetName()] = path;
+}
- void AddPath(const CCinemaPath& path) override
+void CCmpCinemaManager::AddCinemaPathToQueue(const CStrW& name)
+{
+ if (!HasPath(name))
{
- if (m_Paths.find(path.GetName()) != m_Paths.end())
- {
- LOGWARNING("Path with name '%s' already exists", path.GetName().ToUTF8());
- return;
- }
- m_Paths[path.GetName()] = path;
+ LOGWARNING("Path with name '%s' doesn't exist", name.ToUTF8());
+ return;
}
+ m_PathQueue.push_back(m_Paths[name]);
- void AddCinemaPathToQueue(const CStrW& name) override
- {
- if (!HasPath(name))
- {
- LOGWARNING("Path with name '%s' doesn't exist", name.ToUTF8());
- return;
- }
- m_PathQueue.push_back(m_Paths[name]);
+ if (m_PathQueue.size() == 1)
+ m_PathQueue.front().Reset();
+ m_TotalTime += m_Paths[name].GetDuration();
+}
- if (m_PathQueue.size() == 1)
- m_PathQueue.front().Reset();
- m_TotalTime += m_Paths[name].GetDuration();
- }
+void CCmpCinemaManager::Play()
+{
+ SetEnabled(true);
+}
- void Play() override
- {
- SetEnabled(true);
- }
+void CCmpCinemaManager::Stop()
+{
+ SetEnabled(false);
+}
- void Stop() override
- {
- SetEnabled(false);
- }
+bool CCmpCinemaManager::HasPath(const CStrW& name) const
+{
+ return m_Paths.find(name) != m_Paths.end();
+}
- bool HasPath(const CStrW& name) const override
- {
- return m_Paths.find(name) != m_Paths.end();
- }
+void CCmpCinemaManager::ClearQueue()
+{
+ m_PathQueue.clear();
+}
- void ClearQueue() override
+void CCmpCinemaManager::DeletePath(const CStrW& name)
+{
+ if (!HasPath(name))
{
- m_PathQueue.clear();
+ LOGWARNING("Path with name '%s' doesn't exist", name.ToUTF8());
+ return;
}
+ m_PathQueue.remove_if([name](const CCinemaPath& path) { return path.GetName() == name; });
+ m_Paths.erase(name);
+}
- void DeletePath(const CStrW& name) override
- {
- if (!HasPath(name))
- {
- LOGWARNING("Path with name '%s' doesn't exist", name.ToUTF8());
- return;
- }
- m_PathQueue.remove_if([name](const CCinemaPath& path) { return path.GetName() == name; });
- m_Paths.erase(name);
- }
+const std::map& CCmpCinemaManager::GetPaths() const
+{
+ return m_Paths;
+}
- const std::map& GetPaths() const override
- {
- return m_Paths;
- }
+void CCmpCinemaManager::SetPaths(const std::map& newPaths)
+{
+ m_Paths = newPaths;
+}
- void SetPaths(const std::map& newPaths) override
- {
- m_Paths = newPaths;
- }
+const std::list& CCmpCinemaManager::GetQueue() const
+{
+ return m_PathQueue;
+}
- const std::list& GetQueue() const override
- {
- return m_PathQueue;
- }
+bool CCmpCinemaManager::IsEnabled() const
+{
+ return m_Enabled;
+}
- bool IsEnabled() const override
- {
- return m_Enabled;
- }
+void CCmpCinemaManager::SetEnabled(bool enabled)
+{
+ if (m_PathQueue.empty() && enabled)
+ enabled = false;
+
+ if (m_Enabled == enabled)
+ return;
- void SetEnabled(bool enabled) override
+ CmpPtr cmpRangeManager(GetSimContext().GetSystemEntity());
+ CmpPtr cmpTerritoryManager(GetSimContext().GetSystemEntity());
+ if (cmpRangeManager)
{
- if (m_PathQueue.empty() && enabled)
- enabled = false;
+ if (enabled)
+ m_MapRevealed = cmpRangeManager->GetLosRevealAll(-1);
+ // TODO: improve m_MapRevealed state and without fade in
+ cmpRangeManager->SetLosRevealAll(-1, enabled);
+ }
+ if (cmpTerritoryManager)
+ cmpTerritoryManager->SetVisibility(!enabled);
+ ICmpSelectable::SetOverrideVisibility(!enabled);
+ ICmpOverlayRenderer::SetOverrideVisibility(!enabled);
- if (m_Enabled == enabled)
- return;
+ m_Enabled = enabled;
+}
- CmpPtr cmpRangeManager(GetSimContext().GetSystemEntity());
- CmpPtr cmpTerritoryManager(GetSimContext().GetSystemEntity());
- if (cmpRangeManager)
- {
- if (enabled)
- m_MapRevealed = cmpRangeManager->GetLosRevealAll(-1);
- // TODO: improve m_MapRevealed state and without fade in
- cmpRangeManager->SetLosRevealAll(-1, enabled);
- }
- if (cmpTerritoryManager)
- cmpTerritoryManager->SetVisibility(!enabled);
- ICmpSelectable::SetOverrideVisibility(!enabled);
- ICmpOverlayRenderer::SetOverrideVisibility(!enabled);
+void CCmpCinemaManager::PlayQueue(const float deltaRealTime, CCamera* camera)
+{
+ if (m_PathQueue.empty())
+ return;
+ m_PathQueue.front().Play(deltaRealTime, camera);
+}
- m_Enabled = enabled;
+void CCmpCinemaManager::SerializePath(const CCinemaPath& path, ISerializer& serializer)
+{
+ const CCinemaData* data = path.GetData();
+
+ serializer.String("PathName", data->m_Name, 1, 128);
+ serializer.String("PathOrientation", data->m_Orientation, 1, 128);
+ serializer.String("PathMode", data->m_Mode, 1, 128);
+ serializer.String("PathStyle", data->m_Style, 1, 128);
+ serializer.NumberFixed_Unbounded("PathTimescale", data->m_Timescale);
+ serializer.Bool("LookAtTarget", data->m_LookAtTarget);
+
+ serializer.NumberU32("NumberOfNodes", path.GetAllNodes().size(), 1, MAX_SPLINE_NODES);
+ const std::vector& nodes = path.GetAllNodes();
+ for (size_t i = 0; i < nodes.size(); ++i)
+ {
+ if (i > 0)
+ serializer.NumberFixed_Unbounded("NodeDeltaTime", nodes[i - 1].Distance);
+ else
+ serializer.NumberFixed_Unbounded("NodeDeltaTime", fixed::Zero());
+
+ serializer.NumberFixed_Unbounded("PositionX", nodes[i].Position.X);
+ serializer.NumberFixed_Unbounded("PositionY", nodes[i].Position.Y);
+ serializer.NumberFixed_Unbounded("PositionZ", nodes[i].Position.Z);
+
+ serializer.NumberFixed_Unbounded("RotationX", nodes[i].Rotation.X);
+ serializer.NumberFixed_Unbounded("RotationY", nodes[i].Rotation.Y);
+ serializer.NumberFixed_Unbounded("RotationZ", nodes[i].Rotation.Z);
}
- void PlayQueue(const float deltaRealTime, CCamera* camera) override
+ if (!data->m_LookAtTarget)
+ return;
+
+ const std::vector& targetNodes = path.GetTargetSpline().GetAllNodes();
+ serializer.NumberU32("NumberOfTargetNodes", targetNodes.size(), 1, MAX_SPLINE_NODES);
+ for (size_t i = 0; i < targetNodes.size(); ++i)
{
- if (m_PathQueue.empty())
- return;
- m_PathQueue.front().Play(deltaRealTime, camera);
+ if (i > 0)
+ serializer.NumberFixed_Unbounded("NodeDeltaTime", targetNodes[i - 1].Distance);
+ else
+ serializer.NumberFixed_Unbounded("NodeDeltaTime", fixed::Zero());
+ serializer.NumberFixed_Unbounded("PositionX", targetNodes[i].Position.X);
+ serializer.NumberFixed_Unbounded("PositionY", targetNodes[i].Position.Y);
+ serializer.NumberFixed_Unbounded("PositionZ", targetNodes[i].Position.Z);
}
+}
-private:
-
- void SerializePath(const CCinemaPath& path, ISerializer& serializer)
+CCinemaPath CCmpCinemaManager::DeserializePath(IDeserializer& deserializer)
+{
+ CCinemaData data;
+
+ deserializer.String("PathName", data.m_Name, 1, 128);
+ deserializer.String("PathOrientation", data.m_Orientation, 1, 128);
+ deserializer.String("PathMode", data.m_Mode, 1, 128);
+ deserializer.String("PathStyle", data.m_Style, 1, 128);
+ deserializer.NumberFixed_Unbounded("PathTimescale", data.m_Timescale);
+ deserializer.Bool("LookAtTarget", data.m_LookAtTarget);
+
+ TNSpline pathSpline, targetSpline;
+ uint32_t numberOfNodes = 0;
+ deserializer.NumberU32("NumberOfNodes", numberOfNodes, 1, MAX_SPLINE_NODES);
+ for (uint32_t j = 0; j < numberOfNodes; ++j)
{
- const CCinemaData* data = path.GetData();
-
- serializer.String("PathName", data->m_Name, 1, 128);
- serializer.String("PathOrientation", data->m_Orientation, 1, 128);
- serializer.String("PathMode", data->m_Mode, 1, 128);
- serializer.String("PathStyle", data->m_Style, 1, 128);
- serializer.NumberFixed_Unbounded("PathTimescale", data->m_Timescale);
- serializer.Bool("LookAtTarget", data->m_LookAtTarget);
-
- serializer.NumberU32("NumberOfNodes", path.GetAllNodes().size(), 1, MAX_SPLINE_NODES);
- const std::vector& nodes = path.GetAllNodes();
- for (size_t i = 0; i < nodes.size(); ++i)
- {
- if (i > 0)
- serializer.NumberFixed_Unbounded("NodeDeltaTime", nodes[i - 1].Distance);
- else
- serializer.NumberFixed_Unbounded("NodeDeltaTime", fixed::Zero());
-
- serializer.NumberFixed_Unbounded("PositionX", nodes[i].Position.X);
- serializer.NumberFixed_Unbounded("PositionY", nodes[i].Position.Y);
- serializer.NumberFixed_Unbounded("PositionZ", nodes[i].Position.Z);
-
- serializer.NumberFixed_Unbounded("RotationX", nodes[i].Rotation.X);
- serializer.NumberFixed_Unbounded("RotationY", nodes[i].Rotation.Y);
- serializer.NumberFixed_Unbounded("RotationZ", nodes[i].Rotation.Z);
- }
+ SplineData node;
+ deserializer.NumberFixed_Unbounded("NodeDeltaTime", node.Distance);
- if (!data->m_LookAtTarget)
- return;
+ deserializer.NumberFixed_Unbounded("PositionX", node.Position.X);
+ deserializer.NumberFixed_Unbounded("PositionY", node.Position.Y);
+ deserializer.NumberFixed_Unbounded("PositionZ", node.Position.Z);
- const std::vector& targetNodes = path.GetTargetSpline().GetAllNodes();
- serializer.NumberU32("NumberOfTargetNodes", targetNodes.size(), 1, MAX_SPLINE_NODES);
- for (size_t i = 0; i < targetNodes.size(); ++i)
- {
- if (i > 0)
- serializer.NumberFixed_Unbounded("NodeDeltaTime", targetNodes[i - 1].Distance);
- else
- serializer.NumberFixed_Unbounded("NodeDeltaTime", fixed::Zero());
- serializer.NumberFixed_Unbounded("PositionX", targetNodes[i].Position.X);
- serializer.NumberFixed_Unbounded("PositionY", targetNodes[i].Position.Y);
- serializer.NumberFixed_Unbounded("PositionZ", targetNodes[i].Position.Z);
- }
+ deserializer.NumberFixed_Unbounded("RotationX", node.Rotation.X);
+ deserializer.NumberFixed_Unbounded("RotationY", node.Rotation.Y);
+ deserializer.NumberFixed_Unbounded("RotationZ", node.Rotation.Z);
+
+ pathSpline.AddNode(node.Position, node.Rotation, node.Distance);
}
- CCinemaPath DeserializePath(IDeserializer& deserializer)
+ if (data.m_LookAtTarget)
{
- CCinemaData data;
-
- deserializer.String("PathName", data.m_Name, 1, 128);
- deserializer.String("PathOrientation", data.m_Orientation, 1, 128);
- deserializer.String("PathMode", data.m_Mode, 1, 128);
- deserializer.String("PathStyle", data.m_Style, 1, 128);
- deserializer.NumberFixed_Unbounded("PathTimescale", data.m_Timescale);
- deserializer.Bool("LookAtTarget", data.m_LookAtTarget);
-
- TNSpline pathSpline, targetSpline;
- uint32_t numberOfNodes = 0;
- deserializer.NumberU32("NumberOfNodes", numberOfNodes, 1, MAX_SPLINE_NODES);
- for (uint32_t j = 0; j < numberOfNodes; ++j)
+ uint32_t numberOfTargetNodes = 0;
+ deserializer.NumberU32("NumberOfTargetNodes", numberOfTargetNodes, 1, MAX_SPLINE_NODES);
+ for (uint32_t j = 0; j < numberOfTargetNodes; ++j)
{
SplineData node;
deserializer.NumberFixed_Unbounded("NodeDeltaTime", node.Distance);
@@ -324,43 +351,11 @@
deserializer.NumberFixed_Unbounded("PositionY", node.Position.Y);
deserializer.NumberFixed_Unbounded("PositionZ", node.Position.Z);
- deserializer.NumberFixed_Unbounded("RotationX", node.Rotation.X);
- deserializer.NumberFixed_Unbounded("RotationY", node.Rotation.Y);
- deserializer.NumberFixed_Unbounded("RotationZ", node.Rotation.Z);
-
- pathSpline.AddNode(node.Position, node.Rotation, node.Distance);
+ targetSpline.AddNode(node.Position, CFixedVector3D(), node.Distance);
}
-
- if (data.m_LookAtTarget)
- {
- uint32_t numberOfTargetNodes = 0;
- deserializer.NumberU32("NumberOfTargetNodes", numberOfTargetNodes, 1, MAX_SPLINE_NODES);
- for (uint32_t j = 0; j < numberOfTargetNodes; ++j)
- {
- SplineData node;
- deserializer.NumberFixed_Unbounded("NodeDeltaTime", node.Distance);
-
- deserializer.NumberFixed_Unbounded("PositionX", node.Position.X);
- deserializer.NumberFixed_Unbounded("PositionY", node.Position.Y);
- deserializer.NumberFixed_Unbounded("PositionZ", node.Position.Z);
-
- targetSpline.AddNode(node.Position, CFixedVector3D(), node.Distance);
- }
- }
-
- return CCinemaPath(data, pathSpline, targetSpline);
}
- bool m_Enabled;
- std::map m_Paths;
- std::list m_PathQueue;
-
- // States before playing
- bool m_MapRevealed;
-
- fixed m_ElapsedTime;
- fixed m_TotalTime;
- fixed m_CurrentPathElapsedTime;
-};
+ return CCinemaPath(data, pathSpline, targetSpline);
+}
REGISTER_COMPONENT_TYPE(CinemaManager)
Index: source/simulation2/components/CCmpCommandQueue.h
===================================================================
--- /dev/null
+++ source/simulation2/components/CCmpCommandQueue.h
@@ -0,0 +1,54 @@
+/* Copyright (C) 2022 Wildfire Games.
+ * This file is part of 0 A.D.
+ *
+ * 0 A.D. is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D. If not, see .
+ */
+
+#ifndef INCLUDED_CCMPCOMMANDQUE
+#define INCLUDED_CCMPCOMMANDQUE
+
+#include "ICmpCommandQueue.h"
+
+class CCmpCommandQueue final : public ICmpCommandQueue
+{
+public:
+ static constexpr int typeId{CID_CommandQueue};
+
+ static void ClassInit(CComponentManager& UNUSED(componentManager));
+
+ static IComponent* Allocate(const ScriptInterface&, JS::HandleValue);
+ static void Deallocate(IComponent* cmp);
+
+ int GetComponentTypeId() const override;
+
+ std::vector m_LocalQueue;
+
+ static std::string GetSchema();
+
+ void Init(const CParamNode& UNUSED(paramNode)) override;
+
+ void Deinit() override;
+
+ void Serialize(ISerializer& serialize) override;
+
+ void Deserialize(const CParamNode& UNUSED(paramNode), IDeserializer& deserialize) override;
+
+ void PushLocalCommand(player_id_t player, JS::HandleValue cmd) override;
+
+ void PostNetworkCommand(JS::HandleValue cmd1) override;
+
+ void FlushTurn(const std::vector& commands) override;
+};
+
+#endif // INCLUDED_CCMPCOMMONDQUEUE
Index: source/simulation2/components/CCmpCommandQueue.cpp
===================================================================
--- source/simulation2/components/CCmpCommandQueue.cpp
+++ source/simulation2/components/CCmpCommandQueue.cpp
@@ -17,8 +17,9 @@
#include "precompiled.h"
+#include "CCmpCommandQueue.h"
+
#include "simulation2/system/Component.h"
-#include "ICmpCommandQueue.h"
#include "ps/CLogger.h"
#include "ps/Game.h"
@@ -27,102 +28,104 @@
#include "scriptinterface/JSON.h"
#include "simulation2/system/TurnManager.h"
-class CCmpCommandQueue final : public ICmpCommandQueue
+void CCmpCommandQueue::ClassInit(CComponentManager& UNUSED(componentManager))
+{}
+
+IComponent* CCmpCommandQueue::Allocate(const ScriptInterface&, JS::HandleValue)
{
-public:
- static void ClassInit(CComponentManager& UNUSED(componentManager))
- {
- }
+ return nullptr;
+}
- DEFAULT_COMPONENT_ALLOCATOR(CommandQueue)
+void CCmpCommandQueue::Deallocate(IComponent*)
+{}
- std::vector m_LocalQueue;
+int CCmpCommandQueue::GetComponentTypeId() const
+{
+ return CID_CommandQueue;
+}
- static std::string GetSchema()
- {
- return "";
- }
+std::string CCmpCommandQueue::CCmpCommandQueue::GetSchema()
+{
+ return "";
+}
- void Init(const CParamNode& UNUSED(paramNode)) override
- {
- }
+void CCmpCommandQueue::Init(const CParamNode& UNUSED(paramNode))
+{}
- void Deinit() override
- {
- }
+void CCmpCommandQueue::Deinit()
+{}
- void Serialize(ISerializer& serialize) override
- {
- ScriptRequest rq(GetSimContext().GetScriptInterface());
-
- serialize.NumberU32_Unbounded("num commands", (u32)m_LocalQueue.size());
- for (size_t i = 0; i < m_LocalQueue.size(); ++i)
- {
- serialize.NumberI32_Unbounded("player", m_LocalQueue[i].player);
- serialize.ScriptVal("data", &m_LocalQueue[i].data);
- }
- }
+void CCmpCommandQueue::Serialize(ISerializer& serialize)
+{
+ ScriptRequest rq(GetSimContext().GetScriptInterface());
- void Deserialize(const CParamNode& UNUSED(paramNode), IDeserializer& deserialize) override
+ serialize.NumberU32_Unbounded("num commands", (u32)m_LocalQueue.size());
+ for (size_t i = 0; i < m_LocalQueue.size(); ++i)
{
- ScriptRequest rq(GetSimContext().GetScriptInterface());
-
- u32 numCmds;
- deserialize.NumberU32_Unbounded("num commands", numCmds);
- for (size_t i = 0; i < numCmds; ++i)
- {
- i32 player;
- JS::RootedValue data(rq.cx);
- deserialize.NumberI32_Unbounded("player", player);
- deserialize.ScriptVal("data", &data);
- m_LocalQueue.emplace_back(SimulationCommand(player, rq.cx, data));
- }
+ serialize.NumberI32_Unbounded("player", m_LocalQueue[i].player);
+ serialize.ScriptVal("data", &m_LocalQueue[i].data);
}
+}
+
+void CCmpCommandQueue::Deserialize(const CParamNode& UNUSED(paramNode), IDeserializer& deserialize)
+{
+ ScriptRequest rq(GetSimContext().GetScriptInterface());
- void PushLocalCommand(player_id_t player, JS::HandleValue cmd) override
+ u32 numCmds;
+ deserialize.NumberU32_Unbounded("num commands", numCmds);
+ for (size_t i = 0; i < numCmds; ++i)
{
- ScriptRequest rq(GetSimContext().GetScriptInterface());
- m_LocalQueue.emplace_back(SimulationCommand(player, rq.cx, cmd));
+ i32 player;
+ JS::RootedValue data(rq.cx);
+ deserialize.NumberI32_Unbounded("player", player);
+ deserialize.ScriptVal("data", &data);
+ m_LocalQueue.emplace_back(SimulationCommand(player, rq.cx, data));
}
+}
- void PostNetworkCommand(JS::HandleValue cmd1) override
- {
- ScriptRequest rq(GetSimContext().GetScriptInterface());
+void CCmpCommandQueue::PushLocalCommand(player_id_t player, JS::HandleValue cmd)
+{
+ ScriptRequest rq(GetSimContext().GetScriptInterface());
+ m_LocalQueue.emplace_back(SimulationCommand(player, rq.cx, cmd));
+}
- // TODO: This is a workaround because we need to pass a MutableHandle to StringifyJSON.
- JS::RootedValue cmd(rq.cx, cmd1.get());
+void CCmpCommandQueue::PostNetworkCommand(JS::HandleValue cmd1)
+{
+ ScriptRequest rq(GetSimContext().GetScriptInterface());
- PROFILE2_EVENT("post net command");
- PROFILE2_ATTR("command: %s", Script::StringifyJSON(rq, &cmd, false).c_str());
+ // TODO: This is a workaround because we need to pass a MutableHandle to StringifyJSON.
+ JS::RootedValue cmd(rq.cx, cmd1.get());
- // TODO: would be nicer to not use globals
- if (g_Game && g_Game->GetTurnManager())
- g_Game->GetTurnManager()->PostCommand(cmd);
+ PROFILE2_EVENT("post net command");
+ PROFILE2_ATTR("command: %s", Script::StringifyJSON(rq, &cmd, false).c_str());
+
+ // TODO: would be nicer to not use globals
+ if (g_Game && g_Game->GetTurnManager())
+ g_Game->GetTurnManager()->PostCommand(cmd);
+}
+
+void CCmpCommandQueue::FlushTurn(const std::vector& commands)
+{
+ const ScriptInterface& scriptInterface = GetSimContext().GetScriptInterface();
+ ScriptRequest rq(scriptInterface);
+
+ JS::RootedValue global(rq.cx, rq.globalValue());
+ std::vector localCommands;
+ m_LocalQueue.swap(localCommands);
+
+ for (size_t i = 0; i < localCommands.size(); ++i)
+ {
+ bool ok = ScriptFunction::CallVoid(rq, global, "ProcessCommand", localCommands[i].player, localCommands[i].data);
+ if (!ok)
+ LOGERROR("Failed to call ProcessCommand() global script function");
}
- void FlushTurn(const std::vector& commands) override
+ for (size_t i = 0; i < commands.size(); ++i)
{
- const ScriptInterface& scriptInterface = GetSimContext().GetScriptInterface();
- ScriptRequest rq(scriptInterface);
-
- JS::RootedValue global(rq.cx, rq.globalValue());
- std::vector localCommands;
- m_LocalQueue.swap(localCommands);
-
- for (size_t i = 0; i < localCommands.size(); ++i)
- {
- bool ok = ScriptFunction::CallVoid(rq, global, "ProcessCommand", localCommands[i].player, localCommands[i].data);
- if (!ok)
- LOGERROR("Failed to call ProcessCommand() global script function");
- }
-
- for (size_t i = 0; i < commands.size(); ++i)
- {
- bool ok = ScriptFunction::CallVoid(rq, global, "ProcessCommand", commands[i].player, commands[i].data);
- if (!ok)
- LOGERROR("Failed to call ProcessCommand() global script function");
- }
+ bool ok = ScriptFunction::CallVoid(rq, global, "ProcessCommand", commands[i].player, commands[i].data);
+ if (!ok)
+ LOGERROR("Failed to call ProcessCommand() global script function");
}
-};
+}
REGISTER_COMPONENT_TYPE(CommandQueue)
Index: source/simulation2/components/CCmpObstructionManager.h
===================================================================
--- /dev/null
+++ source/simulation2/components/CCmpObstructionManager.h
@@ -0,0 +1,237 @@
+/* Copyright (C) 2022 Wildfire Games.
+ * This file is part of 0 A.D.
+ *
+ * 0 A.D. is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D. If not, see .
+ */
+
+#ifndef INCLUDED_CCMPOBSTRUCTIONMANAGER
+#define INCLUDED_CCMPOBSTRUCTIONMANAGER
+
+#include "ICmpObstructionManager.h"
+
+#include "graphics/Overlay.h"
+#include "renderer/Scene.h"
+#include "simulation2/helpers/Grid.h"
+#include "simulation2/helpers/Spatial.h"
+
+typedef u16 pass_class_t;
+
+class CCmpObstructionManager final : public ICmpObstructionManager
+{
+public:
+ /**
+ * Internal representation of axis-aligned circular shapes for moving units
+ */
+ struct UnitShape
+ {
+ entity_id_t entity;
+ entity_pos_t x, z;
+ entity_pos_t clearance;
+ ICmpObstructionManager::flags_t flags;
+ entity_id_t group; // control group (typically the owner entity, or a formation controller entity) (units ignore collisions with others in the same group)
+ };
+
+ /**
+ * Internal representation of arbitrary-rotation static square shapes for buildings
+ */
+ struct StaticShape
+ {
+ entity_id_t entity;
+ entity_pos_t x, z; // world-space coordinates
+ CFixedVector2D u, v; // orthogonal unit vectors - axes of local coordinate space
+ entity_pos_t hw, hh; // half width/height in local coordinate space
+ ICmpObstructionManager::flags_t flags;
+ entity_id_t group;
+ entity_id_t group2;
+ };
+
+ static constexpr int typeId{CID_ObstructionManager};
+
+ static void ClassInit(CComponentManager& componentManager);
+
+ static IComponent* Allocate(const ScriptInterface&, JS::HandleValue);
+ static void Deallocate(IComponent* cmp);
+
+ int GetComponentTypeId() const override;
+
+ bool m_DebugOverlayEnabled;
+ bool m_DebugOverlayDirty;
+ std::vector m_DebugOverlayLines;
+
+ SpatialSubdivision m_UnitSubdivision;
+ SpatialSubdivision m_StaticSubdivision;
+
+ // TODO: using std::map is a bit inefficient; is there a better way to store these?
+ std::map m_UnitShapes;
+ std::map m_StaticShapes;
+ u32 m_UnitShapeNext; // next allocated id
+ u32 m_StaticShapeNext;
+
+ entity_pos_t m_MaxClearance;
+
+ bool m_PassabilityCircular;
+
+ entity_pos_t m_WorldX0;
+ entity_pos_t m_WorldZ0;
+ entity_pos_t m_WorldX1;
+ entity_pos_t m_WorldZ1;
+
+ static std::string GetSchema();
+
+ void Init(const CParamNode& UNUSED(paramNode)) override;
+
+ void Deinit() override;
+
+ template
+ void SerializeCommon(S& serialize)
+ {
+ Serializer(serialize, "unit subdiv", m_UnitSubdivision);
+ Serializer(serialize, "static subdiv", m_StaticSubdivision);
+
+ serialize.NumberFixed_Unbounded("max clearance", m_MaxClearance);
+
+ Serializer(serialize, "unit shapes", m_UnitShapes);
+ Serializer(serialize, "static shapes", m_StaticShapes);
+ serialize.NumberU32_Unbounded("unit shape next", m_UnitShapeNext);
+ serialize.NumberU32_Unbounded("static shape next", m_StaticShapeNext);
+
+ serialize.Bool("circular", m_PassabilityCircular);
+
+ serialize.NumberFixed_Unbounded("world x0", m_WorldX0);
+ serialize.NumberFixed_Unbounded("world z0", m_WorldZ0);
+ serialize.NumberFixed_Unbounded("world x1", m_WorldX1);
+ serialize.NumberFixed_Unbounded("world z1", m_WorldZ1);
+ }
+
+ void Serialize(ISerializer& serialize) override;
+
+ void Deserialize(const CParamNode& paramNode, IDeserializer& deserialize) override;
+
+ void HandleMessage(const CMessage& msg, bool UNUSED(global)) override;
+
+ // NB: on deserialization, this function is not called after the component is reset.
+ // So anything that happens here should be safely serialized.
+ void SetBounds(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1) override;
+
+ void ResetSubdivisions(entity_pos_t x1, entity_pos_t z1);
+
+ tag_t AddUnitShape(entity_id_t ent, entity_pos_t x, entity_pos_t z, entity_pos_t clearance,
+ flags_t flags, entity_id_t group) override;
+
+ tag_t AddStaticShape(entity_id_t ent, entity_pos_t x, entity_pos_t z, entity_angle_t a,
+ entity_pos_t w, entity_pos_t h, flags_t flags, entity_id_t group,
+ entity_id_t group2 /* = INVALID_ENTITY */) override;
+
+
+ ObstructionSquare GetUnitShapeObstruction(entity_pos_t x, entity_pos_t z, entity_pos_t clearance)
+ const override;
+
+ ObstructionSquare GetStaticShapeObstruction(entity_pos_t x, entity_pos_t z, entity_angle_t a,
+ entity_pos_t w, entity_pos_t h) const override;
+
+ void MoveShape(tag_t tag, entity_pos_t x, entity_pos_t z, entity_angle_t a) override;
+
+ void SetUnitMovingFlag(tag_t tag, bool moving) override;
+
+ void SetUnitControlGroup(tag_t tag, entity_id_t group) override;
+
+ void SetStaticControlGroup(tag_t tag, entity_id_t group, entity_id_t group2) override;
+
+ void RemoveShape(tag_t tag) override;
+
+ ObstructionSquare GetObstruction(tag_t tag) const override;
+
+ fixed DistanceToPoint(entity_id_t ent, entity_pos_t px, entity_pos_t pz) const override;
+ fixed MaxDistanceToPoint(entity_id_t ent, entity_pos_t px, entity_pos_t pz) const override;
+ fixed DistanceToTarget(entity_id_t ent, entity_id_t target) const override;
+ fixed MaxDistanceToTarget(entity_id_t ent, entity_id_t target) const override;
+ fixed DistanceBetweenShapes(const ObstructionSquare& source, const ObstructionSquare& target) const override;
+ fixed MaxDistanceBetweenShapes(const ObstructionSquare& source, const ObstructionSquare& target) const override;
+
+ bool IsInPointRange(entity_id_t ent, entity_pos_t px, entity_pos_t pz, entity_pos_t minRange, entity_pos_t maxRange, bool opposite) const override;
+ bool IsInTargetRange(entity_id_t ent, entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange, bool opposite) const override;
+ bool IsInTargetParabolicRange(entity_id_t ent, entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange, entity_pos_t yOrigin, bool opposite) const override;
+ bool IsPointInPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t px, entity_pos_t pz, entity_pos_t minRange, entity_pos_t maxRange) const override;
+ bool AreShapesInRange(const ObstructionSquare& source, const ObstructionSquare& target, entity_pos_t minRange, entity_pos_t maxRange, bool opposite) const override;
+
+ bool TestLine(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, entity_pos_t r, bool relaxClearanceForUnits = false) const override;
+ bool TestStaticShape(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t a, entity_pos_t w, entity_pos_t h, std::vector* out) const override;
+ bool TestUnitShape(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t r, std::vector* out) const override;
+
+ void Rasterize(Grid& grid, const std::vector& passClasses, bool fullUpdate) override;
+ void GetObstructionsInRange(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, std::vector& squares) const override;
+ void GetUnitObstructionsInRange(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, std::vector& squares) const override;
+ void GetStaticObstructionsInRange(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, std::vector& squares) const override;
+ void GetUnitsOnObstruction(const ObstructionSquare& square, std::vector& out, const IObstructionTestFilter& filter, bool strict = false) const override;
+ void GetStaticObstructionsOnObstruction(const ObstructionSquare& square, std::vector& out, const IObstructionTestFilter& filter) const override;
+
+ void SetPassabilityCircular(bool enabled) override;
+ bool GetPassabilityCircular() const override;
+
+ void SetDebugOverlay(bool enabled) override;
+
+ void RenderSubmit(SceneCollector& collector);
+
+ void UpdateInformations(GridUpdateInformation& informations) override;
+
+private:
+ // Dynamic updates for the long-range pathfinder
+ GridUpdateInformation m_UpdateInformations;
+ // These vectors might contain shapes that were deleted
+ std::vector m_DirtyStaticShapes;
+ std::vector m_DirtyUnitShapes;
+
+ /**
+ * Mark all previous Rasterize()d grids as dirty, and the debug display.
+ * Call this when the world bounds have changed.
+ */
+ void MakeDirtyAll();
+
+ /**
+ * Mark the debug display as dirty.
+ * Call this when nothing has changed except a unit's 'moving' flag.
+ */
+ void MakeDirtyDebug();
+
+ void MarkDirtinessGrid(const entity_pos_t& x, const entity_pos_t& z, const entity_pos_t& r);
+
+ void MarkDirtinessGrid(const entity_pos_t& x, const entity_pos_t& z, const CFixedVector2D& hbox);
+
+ /**
+ * Mark all previous Rasterize()d grids as dirty, if they depend on this shape.
+ * Call this when a static shape has changed.
+ */
+ void MakeDirtyStatic(flags_t flags, u32 index, const StaticShape& shape);
+
+ /**
+ * Mark all previous Rasterize()d grids as dirty, if they depend on this shape.
+ * Call this when a unit shape has changed.
+ */
+ void MakeDirtyUnit(flags_t flags, u32 index, const UnitShape& shape);
+
+ /**
+ * Return whether the given point is within the world bounds by at least r
+ */
+ bool IsInWorld(entity_pos_t x, entity_pos_t z, entity_pos_t r) const;
+
+ /**
+ * Return whether the given point is within the world bounds
+ */
+ bool IsInWorld(const CFixedVector2D& p) const;
+
+ void RasterizeHelper(Grid& grid, ICmpObstructionManager::flags_t requireMask,
+ bool fullUpdate, pass_class_t appliedMask, entity_pos_t clearance = fixed::Zero()) const;
+};
+
+#endif // INCLUDED_CCMPOBSTRUCTIONMANAGER
Index: source/simulation2/components/CCmpObstructionManager.cpp
===================================================================
--- source/simulation2/components/CCmpObstructionManager.cpp
+++ source/simulation2/components/CCmpObstructionManager.cpp
@@ -17,24 +17,21 @@
#include "precompiled.h"
+#include "CCmpObstructionManager.h"
+
#include "simulation2/system/Component.h"
-#include "ICmpObstructionManager.h"
#include "ICmpPosition.h"
#include "ICmpRangeManager.h"
#include "simulation2/MessageTypes.h"
#include "simulation2/helpers/Geometry.h"
-#include "simulation2/helpers/Grid.h"
#include "simulation2/helpers/Rasterize.h"
#include "simulation2/helpers/Render.h"
-#include "simulation2/helpers/Spatial.h"
#include "simulation2/serialization/SerializedTypes.h"
-#include "graphics/Overlay.h"
#include "maths/MathUtil.h"
#include "ps/Profile.h"
-#include "renderer/Scene.h"
#include "ps/CLogger.h"
// Externally, tags are opaque non-zero positive integers.
@@ -47,48 +44,21 @@
#define STATIC_INDEX_TO_TAG(idx) tag_t(((idx) << 1) | 1)
#define TAG_TO_INDEX(tag) ((tag).n >> 1)
-namespace
-{
/**
* Size of each obstruction subdivision square.
* TODO: find the optimal number instead of blindly guessing.
*/
constexpr entity_pos_t OBSTRUCTION_SUBDIVISION_SIZE = entity_pos_t::FromInt(32);
-/**
- * Internal representation of axis-aligned circular shapes for moving units
- */
-struct UnitShape
-{
- entity_id_t entity;
- entity_pos_t x, z;
- entity_pos_t clearance;
- ICmpObstructionManager::flags_t flags;
- entity_id_t group; // control group (typically the owner entity, or a formation controller entity) (units ignore collisions with others in the same group)
-};
-
-/**
- * Internal representation of arbitrary-rotation static square shapes for buildings
- */
-struct StaticShape
-{
- entity_id_t entity;
- entity_pos_t x, z; // world-space coordinates
- CFixedVector2D u, v; // orthogonal unit vectors - axes of local coordinate space
- entity_pos_t hw, hh; // half width/height in local coordinate space
- ICmpObstructionManager::flags_t flags;
- entity_id_t group;
- entity_id_t group2;
-};
-} // anonymous namespace
/**
* Serialization helper template for UnitShape
*/
template<>
-struct SerializeHelper
+struct SerializeHelper
{
template
- void operator()(S& serialize, const char* UNUSED(name), Serialize::qualify value) const
+ void operator()(S& serialize, const char* UNUSED(name),
+ Serialize::qualify value) const
{
serialize.NumberU32_Unbounded("entity", value.entity);
serialize.NumberFixed_Unbounded("x", value.x);
@@ -103,10 +73,11 @@
* Serialization helper template for StaticShape
*/
template<>
-struct SerializeHelper
+struct SerializeHelper
{
template
- void operator()(S& serialize, const char* UNUSED(name), Serialize::qualify value) const
+ void operator()(S& serialize, const char* UNUSED(name),
+ Serialize::qualify value) const
{
serialize.NumberU32_Unbounded("entity", value.entity);
serialize.NumberFixed_Unbounded("x", value.x);
@@ -123,548 +94,461 @@
}
};
-class CCmpObstructionManager final : public ICmpObstructionManager
+void CCmpObstructionManager::ClassInit(CComponentManager& componentManager)
{
-public:
- static void ClassInit(CComponentManager& componentManager)
- {
- componentManager.SubscribeToMessageType(MT_RenderSubmit); // for debug overlays
- }
+ componentManager.SubscribeToMessageType(MT_RenderSubmit); // for debug overlays
+}
- DEFAULT_COMPONENT_ALLOCATOR(ObstructionManager)
+IComponent* CCmpObstructionManager::Allocate(const ScriptInterface&, JS::HandleValue)
+{
+ return nullptr;
+}
- bool m_DebugOverlayEnabled;
- bool m_DebugOverlayDirty;
- std::vector m_DebugOverlayLines;
+void CCmpObstructionManager::Deallocate(IComponent*)
+{}
- SpatialSubdivision m_UnitSubdivision;
- SpatialSubdivision m_StaticSubdivision;
+int CCmpObstructionManager::GetComponentTypeId() const
+{
+ return CID_ObstructionManager;
+}
- // TODO: using std::map is a bit inefficient; is there a better way to store these?
- std::map m_UnitShapes;
- std::map m_StaticShapes;
- u32 m_UnitShapeNext; // next allocated id
- u32 m_StaticShapeNext;
+std::string CCmpObstructionManager::GetSchema()
+{
+ return "";
+}
- entity_pos_t m_MaxClearance;
+void CCmpObstructionManager::Init(const CParamNode& UNUSED(paramNode))
+{
+ m_DebugOverlayEnabled = false;
+ m_DebugOverlayDirty = true;
- bool m_PassabilityCircular;
+ m_UnitShapeNext = 1;
+ m_StaticShapeNext = 1;
- entity_pos_t m_WorldX0;
- entity_pos_t m_WorldZ0;
- entity_pos_t m_WorldX1;
- entity_pos_t m_WorldZ1;
+ m_UpdateInformations.dirty = true;
+ m_UpdateInformations.globallyDirty = true;
- static std::string GetSchema()
- {
- return "";
- }
+ m_PassabilityCircular = false;
- void Init(const CParamNode& UNUSED(paramNode)) override
- {
- m_DebugOverlayEnabled = false;
- m_DebugOverlayDirty = true;
+ m_WorldX0 = m_WorldZ0 = m_WorldX1 = m_WorldZ1 = entity_pos_t::Zero();
- m_UnitShapeNext = 1;
- m_StaticShapeNext = 1;
+ // Initialise with bogus values (these will get replaced when
+ // SetBounds is called)
+ ResetSubdivisions(entity_pos_t::FromInt(1024), entity_pos_t::FromInt(1024));
+}
- m_UpdateInformations.dirty = true;
- m_UpdateInformations.globallyDirty = true;
+void CCmpObstructionManager::Deinit()
+{}
- m_PassabilityCircular = false;
+void CCmpObstructionManager::Serialize(ISerializer& serialize)
+{
+ // TODO: this could perhaps be optimised by not storing all the obstructions,
+ // and instead regenerating them from the other entities on Deserialize
- m_WorldX0 = m_WorldZ0 = m_WorldX1 = m_WorldZ1 = entity_pos_t::Zero();
+ SerializeCommon(serialize);
+}
- // Initialise with bogus values (these will get replaced when
- // SetBounds is called)
- ResetSubdivisions(entity_pos_t::FromInt(1024), entity_pos_t::FromInt(1024));
- }
+void CCmpObstructionManager::Deserialize(const CParamNode& paramNode, IDeserializer& deserialize)
+{
+ Init(paramNode);
- void Deinit() override
- {
- }
+ SerializeCommon(deserialize);
- template
- void SerializeCommon(S& serialize)
- {
- Serializer(serialize, "unit subdiv", m_UnitSubdivision);
- Serializer(serialize, "static subdiv", m_StaticSubdivision);
+ i32 size = ((m_WorldX1-m_WorldX0)/Pathfinding::NAVCELL_SIZE_INT).ToInt_RoundToInfinity();
+ m_UpdateInformations.dirtinessGrid = Grid(size, size);
+}
- serialize.NumberFixed_Unbounded("max clearance", m_MaxClearance);
+void CCmpObstructionManager::HandleMessage(const CMessage& msg, bool UNUSED(global))
+{
+ switch (msg.GetType())
+ {
+ case MT_RenderSubmit:
+ {
+ const CMessageRenderSubmit& msgData = static_cast (msg);
+ RenderSubmit(msgData.collector);
+ break;
+ }
+ }
+}
- Serializer(serialize, "unit shapes", m_UnitShapes);
- Serializer(serialize, "static shapes", m_StaticShapes);
- serialize.NumberU32_Unbounded("unit shape next", m_UnitShapeNext);
- serialize.NumberU32_Unbounded("static shape next", m_StaticShapeNext);
+// NB: on deserialization, this function is not called after the component is reset.
+// So anything that happens here should be safely serialized.
+void CCmpObstructionManager::SetBounds(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1)
+{
+ m_WorldX0 = x0;
+ m_WorldZ0 = z0;
+ m_WorldX1 = x1;
+ m_WorldZ1 = z1;
+ MakeDirtyAll();
+
+ // Subdivision system bounds:
+ ENSURE(x0.IsZero() && z0.IsZero()); // don't bother implementing non-zero offsets yet
+ ResetSubdivisions(x1, z1);
+
+ i32 size = ((m_WorldX1-m_WorldX0)/Pathfinding::NAVCELL_SIZE_INT).ToInt_RoundToInfinity();
+ m_UpdateInformations.dirtinessGrid = Grid(size, size);
+
+ CmpPtr cmpPathfinder(GetSystemEntity());
+ if (cmpPathfinder)
+ m_MaxClearance = cmpPathfinder->GetMaximumClearance();
+}
- serialize.Bool("circular", m_PassabilityCircular);
+void CCmpObstructionManager::ResetSubdivisions(entity_pos_t x1, entity_pos_t z1)
+{
+ m_UnitSubdivision.Reset(x1, z1, OBSTRUCTION_SUBDIVISION_SIZE);
+ m_StaticSubdivision.Reset(x1, z1, OBSTRUCTION_SUBDIVISION_SIZE);
- serialize.NumberFixed_Unbounded("world x0", m_WorldX0);
- serialize.NumberFixed_Unbounded("world z0", m_WorldZ0);
- serialize.NumberFixed_Unbounded("world x1", m_WorldX1);
- serialize.NumberFixed_Unbounded("world z1", m_WorldZ1);
+ for (std::map::iterator it = m_UnitShapes.begin(); it != m_UnitShapes.end(); ++it)
+ {
+ CFixedVector2D center(it->second.x, it->second.z);
+ CFixedVector2D halfSize(it->second.clearance, it->second.clearance);
+ m_UnitSubdivision.Add(it->first, center - halfSize, center + halfSize);
}
- void Serialize(ISerializer& serialize) override
+ for (std::map::iterator it = m_StaticShapes.begin(); it != m_StaticShapes.end(); ++it)
{
- // TODO: this could perhaps be optimised by not storing all the obstructions,
- // and instead regenerating them from the other entities on Deserialize
-
- SerializeCommon(serialize);
+ CFixedVector2D center(it->second.x, it->second.z);
+ CFixedVector2D bbHalfSize = Geometry::GetHalfBoundingBox(it->second.u, it->second.v, CFixedVector2D(it->second.hw, it->second.hh));
+ m_StaticSubdivision.Add(it->first, center - bbHalfSize, center + bbHalfSize);
}
+}
- void Deserialize(const CParamNode& paramNode, IDeserializer& deserialize) override
- {
- Init(paramNode);
+auto CCmpObstructionManager::AddUnitShape(entity_id_t ent, entity_pos_t x, entity_pos_t z,
+ entity_pos_t clearance, flags_t flags, entity_id_t group) -> tag_t
+{
+ UnitShape shape = { ent, x, z, clearance, flags, group };
+ u32 id = m_UnitShapeNext++;
+ m_UnitShapes[id] = shape;
- SerializeCommon(deserialize);
+ m_UnitSubdivision.Add(id, CFixedVector2D(x - clearance, z - clearance), CFixedVector2D(x + clearance, z + clearance));
- i32 size = ((m_WorldX1-m_WorldX0)/Pathfinding::NAVCELL_SIZE_INT).ToInt_RoundToInfinity();
- m_UpdateInformations.dirtinessGrid = Grid(size, size);
- }
+ MakeDirtyUnit(flags, id, shape);
- void HandleMessage(const CMessage& msg, bool UNUSED(global)) override
- {
- switch (msg.GetType())
- {
- case MT_RenderSubmit:
- {
- const CMessageRenderSubmit& msgData = static_cast (msg);
- RenderSubmit(msgData.collector);
- break;
- }
- }
- }
+ return UNIT_INDEX_TO_TAG(id);
+}
- // NB: on deserialization, this function is not called after the component is reset.
- // So anything that happens here should be safely serialized.
- void SetBounds(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1) override
- {
- m_WorldX0 = x0;
- m_WorldZ0 = z0;
- m_WorldX1 = x1;
- m_WorldZ1 = z1;
- MakeDirtyAll();
-
- // Subdivision system bounds:
- ENSURE(x0.IsZero() && z0.IsZero()); // don't bother implementing non-zero offsets yet
- ResetSubdivisions(x1, z1);
-
- i32 size = ((m_WorldX1-m_WorldX0)/Pathfinding::NAVCELL_SIZE_INT).ToInt_RoundToInfinity();
- m_UpdateInformations.dirtinessGrid = Grid(size, size);
-
- CmpPtr cmpPathfinder(GetSystemEntity());
- if (cmpPathfinder)
- m_MaxClearance = cmpPathfinder->GetMaximumClearance();
- }
+auto CCmpObstructionManager::AddStaticShape(entity_id_t ent, entity_pos_t x, entity_pos_t z,
+ entity_angle_t a, entity_pos_t w, entity_pos_t h, flags_t flags, entity_id_t group,
+ entity_id_t group2 /* = INVALID_ENTITY */) -> tag_t
+{
+ fixed s, c;
+ sincos_approx(a, s, c);
+ CFixedVector2D u(c, -s);
+ CFixedVector2D v(s, c);
- void ResetSubdivisions(entity_pos_t x1, entity_pos_t z1)
- {
- m_UnitSubdivision.Reset(x1, z1, OBSTRUCTION_SUBDIVISION_SIZE);
- m_StaticSubdivision.Reset(x1, z1, OBSTRUCTION_SUBDIVISION_SIZE);
+ StaticShape shape = { ent, x, z, u, v, w/2, h/2, flags, group, group2 };
+ u32 id = m_StaticShapeNext++;
+ m_StaticShapes[id] = shape;
- for (std::map::iterator it = m_UnitShapes.begin(); it != m_UnitShapes.end(); ++it)
- {
- CFixedVector2D center(it->second.x, it->second.z);
- CFixedVector2D halfSize(it->second.clearance, it->second.clearance);
- m_UnitSubdivision.Add(it->first, center - halfSize, center + halfSize);
- }
+ CFixedVector2D center(x, z);
+ CFixedVector2D bbHalfSize = Geometry::GetHalfBoundingBox(u, v, CFixedVector2D(w/2, h/2));
+ m_StaticSubdivision.Add(id, center - bbHalfSize, center + bbHalfSize);
- for (std::map::iterator it = m_StaticShapes.begin(); it != m_StaticShapes.end(); ++it)
- {
- CFixedVector2D center(it->second.x, it->second.z);
- CFixedVector2D bbHalfSize = Geometry::GetHalfBoundingBox(it->second.u, it->second.v, CFixedVector2D(it->second.hw, it->second.hh));
- m_StaticSubdivision.Add(it->first, center - bbHalfSize, center + bbHalfSize);
- }
- }
+ MakeDirtyStatic(flags, id, shape);
- tag_t AddUnitShape(entity_id_t ent, entity_pos_t x, entity_pos_t z, entity_pos_t clearance, flags_t flags, entity_id_t group) override
- {
- UnitShape shape = { ent, x, z, clearance, flags, group };
- u32 id = m_UnitShapeNext++;
- m_UnitShapes[id] = shape;
+ return STATIC_INDEX_TO_TAG(id);
+}
- m_UnitSubdivision.Add(id, CFixedVector2D(x - clearance, z - clearance), CFixedVector2D(x + clearance, z + clearance));
+auto CCmpObstructionManager::GetUnitShapeObstruction(entity_pos_t x, entity_pos_t z,
+ entity_pos_t clearance) const -> ObstructionSquare
+{
+ CFixedVector2D u(entity_pos_t::FromInt(1), entity_pos_t::Zero());
+ CFixedVector2D v(entity_pos_t::Zero(), entity_pos_t::FromInt(1));
+ ObstructionSquare o = { x, z, u, v, clearance, clearance };
+ return o;
+}
- MakeDirtyUnit(flags, id, shape);
+auto CCmpObstructionManager::GetStaticShapeObstruction(entity_pos_t x, entity_pos_t z,
+ entity_angle_t a, entity_pos_t w, entity_pos_t h) const -> ObstructionSquare
+{
+ fixed s, c;
+ sincos_approx(a, s, c);
+ CFixedVector2D u(c, -s);
+ CFixedVector2D v(s, c);
- return UNIT_INDEX_TO_TAG(id);
- }
+ ObstructionSquare o = { x, z, u, v, w/2, h/2 };
+ return o;
+}
- tag_t AddStaticShape(entity_id_t ent, entity_pos_t x, entity_pos_t z, entity_angle_t a, entity_pos_t w, entity_pos_t h, flags_t flags, entity_id_t group, entity_id_t group2 /* = INVALID_ENTITY */) override
+void CCmpObstructionManager::MoveShape(tag_t tag, entity_pos_t x, entity_pos_t z, entity_angle_t a)
+{
+ ENSURE(TAG_IS_VALID(tag));
+
+ if (TAG_IS_UNIT(tag))
{
- fixed s, c;
- sincos_approx(a, s, c);
- CFixedVector2D u(c, -s);
- CFixedVector2D v(s, c);
+ UnitShape& shape = m_UnitShapes[TAG_TO_INDEX(tag)];
- StaticShape shape = { ent, x, z, u, v, w/2, h/2, flags, group, group2 };
- u32 id = m_StaticShapeNext++;
- m_StaticShapes[id] = shape;
+ MakeDirtyUnit(shape.flags, TAG_TO_INDEX(tag), shape); // dirty the old shape region
- CFixedVector2D center(x, z);
- CFixedVector2D bbHalfSize = Geometry::GetHalfBoundingBox(u, v, CFixedVector2D(w/2, h/2));
- m_StaticSubdivision.Add(id, center - bbHalfSize, center + bbHalfSize);
+ m_UnitSubdivision.Move(TAG_TO_INDEX(tag),
+ CFixedVector2D(shape.x - shape.clearance, shape.z - shape.clearance),
+ CFixedVector2D(shape.x + shape.clearance, shape.z + shape.clearance),
+ CFixedVector2D(x - shape.clearance, z - shape.clearance),
+ CFixedVector2D(x + shape.clearance, z + shape.clearance));
- MakeDirtyStatic(flags, id, shape);
+ shape.x = x;
+ shape.z = z;
- return STATIC_INDEX_TO_TAG(id);
+ MakeDirtyUnit(shape.flags, TAG_TO_INDEX(tag), shape); // dirty the new shape region
}
-
- ObstructionSquare GetUnitShapeObstruction(entity_pos_t x, entity_pos_t z, entity_pos_t clearance) const override
- {
- CFixedVector2D u(entity_pos_t::FromInt(1), entity_pos_t::Zero());
- CFixedVector2D v(entity_pos_t::Zero(), entity_pos_t::FromInt(1));
- ObstructionSquare o = { x, z, u, v, clearance, clearance };
- return o;
- }
-
- ObstructionSquare GetStaticShapeObstruction(entity_pos_t x, entity_pos_t z, entity_angle_t a, entity_pos_t w, entity_pos_t h) const override
+ else
{
fixed s, c;
sincos_approx(a, s, c);
CFixedVector2D u(c, -s);
CFixedVector2D v(s, c);
- ObstructionSquare o = { x, z, u, v, w/2, h/2 };
- return o;
- }
+ StaticShape& shape = m_StaticShapes[TAG_TO_INDEX(tag)];
- void MoveShape(tag_t tag, entity_pos_t x, entity_pos_t z, entity_angle_t a) override
- {
- ENSURE(TAG_IS_VALID(tag));
+ MakeDirtyStatic(shape.flags, TAG_TO_INDEX(tag), shape); // dirty the old shape region
- if (TAG_IS_UNIT(tag))
- {
- UnitShape& shape = m_UnitShapes[TAG_TO_INDEX(tag)];
+ CFixedVector2D fromBbHalfSize = Geometry::GetHalfBoundingBox(shape.u, shape.v, CFixedVector2D(shape.hw, shape.hh));
+ CFixedVector2D toBbHalfSize = Geometry::GetHalfBoundingBox(u, v, CFixedVector2D(shape.hw, shape.hh));
+ m_StaticSubdivision.Move(TAG_TO_INDEX(tag),
+ CFixedVector2D(shape.x, shape.z) - fromBbHalfSize,
+ CFixedVector2D(shape.x, shape.z) + fromBbHalfSize,
+ CFixedVector2D(x, z) - toBbHalfSize,
+ CFixedVector2D(x, z) + toBbHalfSize);
- MakeDirtyUnit(shape.flags, TAG_TO_INDEX(tag), shape); // dirty the old shape region
+ shape.x = x;
+ shape.z = z;
+ shape.u = u;
+ shape.v = v;
- m_UnitSubdivision.Move(TAG_TO_INDEX(tag),
- CFixedVector2D(shape.x - shape.clearance, shape.z - shape.clearance),
- CFixedVector2D(shape.x + shape.clearance, shape.z + shape.clearance),
- CFixedVector2D(x - shape.clearance, z - shape.clearance),
- CFixedVector2D(x + shape.clearance, z + shape.clearance));
+ MakeDirtyStatic(shape.flags, TAG_TO_INDEX(tag), shape); // dirty the new shape region
+ }
+}
- shape.x = x;
- shape.z = z;
+void CCmpObstructionManager::SetUnitMovingFlag(tag_t tag, bool moving)
+{
+ ENSURE(TAG_IS_VALID(tag) && TAG_IS_UNIT(tag));
- MakeDirtyUnit(shape.flags, TAG_TO_INDEX(tag), shape); // dirty the new shape region
- }
+ if (TAG_IS_UNIT(tag))
+ {
+ UnitShape& shape = m_UnitShapes[TAG_TO_INDEX(tag)];
+ if (moving)
+ shape.flags |= FLAG_MOVING;
else
- {
- fixed s, c;
- sincos_approx(a, s, c);
- CFixedVector2D u(c, -s);
- CFixedVector2D v(s, c);
-
- StaticShape& shape = m_StaticShapes[TAG_TO_INDEX(tag)];
-
- MakeDirtyStatic(shape.flags, TAG_TO_INDEX(tag), shape); // dirty the old shape region
-
- CFixedVector2D fromBbHalfSize = Geometry::GetHalfBoundingBox(shape.u, shape.v, CFixedVector2D(shape.hw, shape.hh));
- CFixedVector2D toBbHalfSize = Geometry::GetHalfBoundingBox(u, v, CFixedVector2D(shape.hw, shape.hh));
- m_StaticSubdivision.Move(TAG_TO_INDEX(tag),
- CFixedVector2D(shape.x, shape.z) - fromBbHalfSize,
- CFixedVector2D(shape.x, shape.z) + fromBbHalfSize,
- CFixedVector2D(x, z) - toBbHalfSize,
- CFixedVector2D(x, z) + toBbHalfSize);
+ shape.flags &= (flags_t)~FLAG_MOVING;
- shape.x = x;
- shape.z = z;
- shape.u = u;
- shape.v = v;
-
- MakeDirtyStatic(shape.flags, TAG_TO_INDEX(tag), shape); // dirty the new shape region
- }
+ MakeDirtyDebug();
}
+}
- void SetUnitMovingFlag(tag_t tag, bool moving) override
- {
- ENSURE(TAG_IS_VALID(tag) && TAG_IS_UNIT(tag));
-
- if (TAG_IS_UNIT(tag))
- {
- UnitShape& shape = m_UnitShapes[TAG_TO_INDEX(tag)];
- if (moving)
- shape.flags |= FLAG_MOVING;
- else
- shape.flags &= (flags_t)~FLAG_MOVING;
+void CCmpObstructionManager::SetUnitControlGroup(tag_t tag, entity_id_t group)
+{
+ ENSURE(TAG_IS_VALID(tag) && TAG_IS_UNIT(tag));
- MakeDirtyDebug();
- }
+ if (TAG_IS_UNIT(tag))
+ {
+ UnitShape& shape = m_UnitShapes[TAG_TO_INDEX(tag)];
+ shape.group = group;
}
+}
- void SetUnitControlGroup(tag_t tag, entity_id_t group) override
- {
- ENSURE(TAG_IS_VALID(tag) && TAG_IS_UNIT(tag));
+void CCmpObstructionManager::SetStaticControlGroup(tag_t tag, entity_id_t group, entity_id_t group2)
+{
+ ENSURE(TAG_IS_VALID(tag) && TAG_IS_STATIC(tag));
- if (TAG_IS_UNIT(tag))
- {
- UnitShape& shape = m_UnitShapes[TAG_TO_INDEX(tag)];
- shape.group = group;
- }
+ if (TAG_IS_STATIC(tag))
+ {
+ StaticShape& shape = m_StaticShapes[TAG_TO_INDEX(tag)];
+ shape.group = group;
+ shape.group2 = group2;
}
+}
- void SetStaticControlGroup(tag_t tag, entity_id_t group, entity_id_t group2) override
+void CCmpObstructionManager::RemoveShape(tag_t tag)
+{
+ ENSURE(TAG_IS_VALID(tag));
+
+ if (TAG_IS_UNIT(tag))
{
- ENSURE(TAG_IS_VALID(tag) && TAG_IS_STATIC(tag));
+ UnitShape& shape = m_UnitShapes[TAG_TO_INDEX(tag)];
+ m_UnitSubdivision.Remove(TAG_TO_INDEX(tag),
+ CFixedVector2D(shape.x - shape.clearance, shape.z - shape.clearance),
+ CFixedVector2D(shape.x + shape.clearance, shape.z + shape.clearance));
- if (TAG_IS_STATIC(tag))
- {
- StaticShape& shape = m_StaticShapes[TAG_TO_INDEX(tag)];
- shape.group = group;
- shape.group2 = group2;
- }
- }
+ MakeDirtyUnit(shape.flags, TAG_TO_INDEX(tag), shape);
- void RemoveShape(tag_t tag) override
+ m_UnitShapes.erase(TAG_TO_INDEX(tag));
+ }
+ else
{
- ENSURE(TAG_IS_VALID(tag));
+ StaticShape& shape = m_StaticShapes[TAG_TO_INDEX(tag)];
- if (TAG_IS_UNIT(tag))
- {
- UnitShape& shape = m_UnitShapes[TAG_TO_INDEX(tag)];
- m_UnitSubdivision.Remove(TAG_TO_INDEX(tag),
- CFixedVector2D(shape.x - shape.clearance, shape.z - shape.clearance),
- CFixedVector2D(shape.x + shape.clearance, shape.z + shape.clearance));
+ CFixedVector2D center(shape.x, shape.z);
+ CFixedVector2D bbHalfSize = Geometry::GetHalfBoundingBox(shape.u, shape.v, CFixedVector2D(shape.hw, shape.hh));
+ m_StaticSubdivision.Remove(TAG_TO_INDEX(tag), center - bbHalfSize, center + bbHalfSize);
- MakeDirtyUnit(shape.flags, TAG_TO_INDEX(tag), shape);
+ MakeDirtyStatic(shape.flags, TAG_TO_INDEX(tag), shape);
- m_UnitShapes.erase(TAG_TO_INDEX(tag));
- }
- else
- {
- StaticShape& shape = m_StaticShapes[TAG_TO_INDEX(tag)];
-
- CFixedVector2D center(shape.x, shape.z);
- CFixedVector2D bbHalfSize = Geometry::GetHalfBoundingBox(shape.u, shape.v, CFixedVector2D(shape.hw, shape.hh));
- m_StaticSubdivision.Remove(TAG_TO_INDEX(tag), center - bbHalfSize, center + bbHalfSize);
+ m_StaticShapes.erase(TAG_TO_INDEX(tag));
+ }
+}
- MakeDirtyStatic(shape.flags, TAG_TO_INDEX(tag), shape);
+auto CCmpObstructionManager::GetObstruction(tag_t tag) const -> ObstructionSquare
+{
+ ENSURE(TAG_IS_VALID(tag));
- m_StaticShapes.erase(TAG_TO_INDEX(tag));
- }
+ if (TAG_IS_UNIT(tag))
+ {
+ const UnitShape& shape = m_UnitShapes.at(TAG_TO_INDEX(tag));
+ CFixedVector2D u(entity_pos_t::FromInt(1), entity_pos_t::Zero());
+ CFixedVector2D v(entity_pos_t::Zero(), entity_pos_t::FromInt(1));
+ ObstructionSquare o = { shape.x, shape.z, u, v, shape.clearance, shape.clearance };
+ return o;
}
-
- ObstructionSquare GetObstruction(tag_t tag) const override
+ else
{
- ENSURE(TAG_IS_VALID(tag));
-
- if (TAG_IS_UNIT(tag))
- {
- const UnitShape& shape = m_UnitShapes.at(TAG_TO_INDEX(tag));
- CFixedVector2D u(entity_pos_t::FromInt(1), entity_pos_t::Zero());
- CFixedVector2D v(entity_pos_t::Zero(), entity_pos_t::FromInt(1));
- ObstructionSquare o = { shape.x, shape.z, u, v, shape.clearance, shape.clearance };
- return o;
- }
- else
- {
- const StaticShape& shape = m_StaticShapes.at(TAG_TO_INDEX(tag));
- ObstructionSquare o = { shape.x, shape.z, shape.u, shape.v, shape.hw, shape.hh };
- return o;
- }
+ const StaticShape& shape = m_StaticShapes.at(TAG_TO_INDEX(tag));
+ ObstructionSquare o = { shape.x, shape.z, shape.u, shape.v, shape.hw, shape.hh };
+ return o;
}
+}
- fixed DistanceToPoint(entity_id_t ent, entity_pos_t px, entity_pos_t pz) const override;
- fixed MaxDistanceToPoint(entity_id_t ent, entity_pos_t px, entity_pos_t pz) const override;
- fixed DistanceToTarget(entity_id_t ent, entity_id_t target) const override;
- fixed MaxDistanceToTarget(entity_id_t ent, entity_id_t target) const override;
- fixed DistanceBetweenShapes(const ObstructionSquare& source, const ObstructionSquare& target) const override;
- fixed MaxDistanceBetweenShapes(const ObstructionSquare& source, const ObstructionSquare& target) const override;
-
- bool IsInPointRange(entity_id_t ent, entity_pos_t px, entity_pos_t pz, entity_pos_t minRange, entity_pos_t maxRange, bool opposite) const override;
- bool IsInTargetRange(entity_id_t ent, entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange, bool opposite) const override;
- bool IsInTargetParabolicRange(entity_id_t ent, entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange, entity_pos_t yOrigin, bool opposite) const override;
- bool IsPointInPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t px, entity_pos_t pz, entity_pos_t minRange, entity_pos_t maxRange) const override;
- bool AreShapesInRange(const ObstructionSquare& source, const ObstructionSquare& target, entity_pos_t minRange, entity_pos_t maxRange, bool opposite) const override;
-
- bool TestLine(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, entity_pos_t r, bool relaxClearanceForUnits = false) const override;
- bool TestStaticShape(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t a, entity_pos_t w, entity_pos_t h, std::vector* out) const override;
- bool TestUnitShape(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t r, std::vector* out) const override;
-
- void Rasterize(Grid& grid, const std::vector& passClasses, bool fullUpdate) override;
- void GetObstructionsInRange(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, std::vector& squares) const override;
- void GetUnitObstructionsInRange(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, std::vector& squares) const override;
- void GetStaticObstructionsInRange(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, std::vector& squares) const override;
- void GetUnitsOnObstruction(const ObstructionSquare& square, std::vector& out, const IObstructionTestFilter& filter, bool strict = false) const override;
- void GetStaticObstructionsOnObstruction(const ObstructionSquare& square, std::vector& out, const IObstructionTestFilter& filter) const override;
-
- void SetPassabilityCircular(bool enabled) override
- {
- m_PassabilityCircular = enabled;
- MakeDirtyAll();
+void CCmpObstructionManager::SetPassabilityCircular(bool enabled)
+{
+ m_PassabilityCircular = enabled;
+ MakeDirtyAll();
- CMessageObstructionMapShapeChanged msg;
- GetSimContext().GetComponentManager().BroadcastMessage(msg);
- }
+ CMessageObstructionMapShapeChanged msg;
+ GetSimContext().GetComponentManager().BroadcastMessage(msg);
+}
- bool GetPassabilityCircular() const override
- {
- return m_PassabilityCircular;
- }
+bool CCmpObstructionManager::GetPassabilityCircular() const
+{
+ return m_PassabilityCircular;
+}
- void SetDebugOverlay(bool enabled) override
- {
- m_DebugOverlayEnabled = enabled;
- m_DebugOverlayDirty = true;
- if (!enabled)
- m_DebugOverlayLines.clear();
- }
+void CCmpObstructionManager::SetDebugOverlay(bool enabled)
+{
+ m_DebugOverlayEnabled = enabled;
+ m_DebugOverlayDirty = true;
+ if (!enabled)
+ m_DebugOverlayLines.clear();
+}
- void RenderSubmit(SceneCollector& collector);
+void CCmpObstructionManager::UpdateInformations(GridUpdateInformation& informations)
+{
+ if (!m_UpdateInformations.dirtinessGrid.blank())
+ informations.MergeAndClear(m_UpdateInformations);
+}
- void UpdateInformations(GridUpdateInformation& informations) override
- {
- if (!m_UpdateInformations.dirtinessGrid.blank())
- informations.MergeAndClear(m_UpdateInformations);
- }
+void CCmpObstructionManager::MakeDirtyAll()
+{
+ m_UpdateInformations.dirty = true;
+ m_UpdateInformations.globallyDirty = true;
+ m_UpdateInformations.dirtinessGrid.reset();
-private:
- // Dynamic updates for the long-range pathfinder
- GridUpdateInformation m_UpdateInformations;
- // These vectors might contain shapes that were deleted
- std::vector m_DirtyStaticShapes;
- std::vector m_DirtyUnitShapes;
-
- /**
- * Mark all previous Rasterize()d grids as dirty, and the debug display.
- * Call this when the world bounds have changed.
- */
- void MakeDirtyAll()
- {
- m_UpdateInformations.dirty = true;
- m_UpdateInformations.globallyDirty = true;
- m_UpdateInformations.dirtinessGrid.reset();
+ m_DebugOverlayDirty = true;
+}
- m_DebugOverlayDirty = true;
- }
+void CCmpObstructionManager::MakeDirtyDebug()
+{
+ m_DebugOverlayDirty = true;
+}
- /**
- * Mark the debug display as dirty.
- * Call this when nothing has changed except a unit's 'moving' flag.
- */
- void MakeDirtyDebug()
- {
- m_DebugOverlayDirty = true;
- }
+void CCmpObstructionManager::MarkDirtinessGrid(const entity_pos_t& x, const entity_pos_t& z,
+ const entity_pos_t& r)
+{
+ MarkDirtinessGrid(x, z, CFixedVector2D(r, r));
+}
- inline void MarkDirtinessGrid(const entity_pos_t& x, const entity_pos_t& z, const entity_pos_t& r)
- {
- MarkDirtinessGrid(x, z, CFixedVector2D(r, r));
- }
+void CCmpObstructionManager::MarkDirtinessGrid(const entity_pos_t& x, const entity_pos_t& z,
+ const CFixedVector2D& hbox)
+{
+ if (m_UpdateInformations.dirtinessGrid.m_W == 0)
+ return;
- inline void MarkDirtinessGrid(const entity_pos_t& x, const entity_pos_t& z, const CFixedVector2D& hbox)
- {
- if (m_UpdateInformations.dirtinessGrid.m_W == 0)
- return;
+ u16 j0, j1, i0, i1;
+ Pathfinding::NearestNavcell(x - hbox.X, z - hbox.Y, i0, j0, m_UpdateInformations.dirtinessGrid.m_W, m_UpdateInformations.dirtinessGrid.m_H);
+ Pathfinding::NearestNavcell(x + hbox.X, z + hbox.Y, i1, j1, m_UpdateInformations.dirtinessGrid.m_W, m_UpdateInformations.dirtinessGrid.m_H);
- u16 j0, j1, i0, i1;
- Pathfinding::NearestNavcell(x - hbox.X, z - hbox.Y, i0, j0, m_UpdateInformations.dirtinessGrid.m_W, m_UpdateInformations.dirtinessGrid.m_H);
- Pathfinding::NearestNavcell(x + hbox.X, z + hbox.Y, i1, j1, m_UpdateInformations.dirtinessGrid.m_W, m_UpdateInformations.dirtinessGrid.m_H);
+ for (int j = j0; j < j1; ++j)
+ for (int i = i0; i < i1; ++i)
+ m_UpdateInformations.dirtinessGrid.set(i, j, 1);
+}
- for (int j = j0; j < j1; ++j)
- for (int i = i0; i < i1; ++i)
- m_UpdateInformations.dirtinessGrid.set(i, j, 1);
- }
+void CCmpObstructionManager::MakeDirtyStatic(flags_t flags, u32 index, const StaticShape& shape)
+{
+ m_DebugOverlayDirty = true;
- /**
- * Mark all previous Rasterize()d grids as dirty, if they depend on this shape.
- * Call this when a static shape has changed.
- */
- void MakeDirtyStatic(flags_t flags, u32 index, const StaticShape& shape)
+ if (flags & (FLAG_BLOCK_PATHFINDING | FLAG_BLOCK_FOUNDATION))
{
- m_DebugOverlayDirty = true;
-
- if (flags & (FLAG_BLOCK_PATHFINDING | FLAG_BLOCK_FOUNDATION))
- {
- m_UpdateInformations.dirty = true;
+ m_UpdateInformations.dirty = true;
- if (std::find(m_DirtyStaticShapes.begin(), m_DirtyStaticShapes.end(), index) == m_DirtyStaticShapes.end())
- m_DirtyStaticShapes.push_back(index);
+ if (std::find(m_DirtyStaticShapes.begin(), m_DirtyStaticShapes.end(), index) == m_DirtyStaticShapes.end())
+ m_DirtyStaticShapes.push_back(index);
- // All shapes overlapping the updated part of the grid should be dirtied too.
- // We are going to invalidate the region of the grid corresponding to the modified shape plus its clearance,
- // and we need to get the shapes whose clearance can overlap this area. So we need to extend the search area
- // by two times the maximum clearance.
+ // All shapes overlapping the updated part of the grid should be dirtied too.
+ // We are going to invalidate the region of the grid corresponding to the modified shape plus its clearance,
+ // and we need to get the shapes whose clearance can overlap this area. So we need to extend the search area
+ // by two times the maximum clearance.
- CFixedVector2D center(shape.x, shape.z);
- CFixedVector2D hbox = Geometry::GetHalfBoundingBox(shape.u, shape.v, CFixedVector2D(shape.hw, shape.hh));
- CFixedVector2D expand(m_MaxClearance, m_MaxClearance);
+ CFixedVector2D center(shape.x, shape.z);
+ CFixedVector2D hbox = Geometry::GetHalfBoundingBox(shape.u, shape.v, CFixedVector2D(shape.hw, shape.hh));
+ CFixedVector2D expand(m_MaxClearance, m_MaxClearance);
- std::vector staticsNear;
- m_StaticSubdivision.GetInRange(staticsNear, center - hbox - expand*2, center + hbox + expand*2);
- for (u32& staticId : staticsNear)
- if (std::find(m_DirtyStaticShapes.begin(), m_DirtyStaticShapes.end(), staticId) == m_DirtyStaticShapes.end())
- m_DirtyStaticShapes.push_back(staticId);
+ std::vector staticsNear;
+ m_StaticSubdivision.GetInRange(staticsNear, center - hbox - expand*2, center + hbox + expand*2);
+ for (u32& staticId : staticsNear)
+ if (std::find(m_DirtyStaticShapes.begin(), m_DirtyStaticShapes.end(), staticId) == m_DirtyStaticShapes.end())
+ m_DirtyStaticShapes.push_back(staticId);
- std::vector unitsNear;
- m_UnitSubdivision.GetInRange(unitsNear, center - hbox - expand*2, center + hbox + expand*2);
- for (u32& unitId : unitsNear)
- if (std::find(m_DirtyUnitShapes.begin(), m_DirtyUnitShapes.end(), unitId) == m_DirtyUnitShapes.end())
- m_DirtyUnitShapes.push_back(unitId);
+ std::vector unitsNear;
+ m_UnitSubdivision.GetInRange(unitsNear, center - hbox - expand*2, center + hbox + expand*2);
+ for (u32& unitId : unitsNear)
+ if (std::find(m_DirtyUnitShapes.begin(), m_DirtyUnitShapes.end(), unitId) == m_DirtyUnitShapes.end())
+ m_DirtyUnitShapes.push_back(unitId);
- MarkDirtinessGrid(shape.x, shape.z, hbox + expand);
- }
+ MarkDirtinessGrid(shape.x, shape.z, hbox + expand);
}
+}
- /**
- * Mark all previous Rasterize()d grids as dirty, if they depend on this shape.
- * Call this when a unit shape has changed.
- */
- void MakeDirtyUnit(flags_t flags, u32 index, const UnitShape& shape)
- {
- m_DebugOverlayDirty = true;
-
- if (flags & (FLAG_BLOCK_PATHFINDING | FLAG_BLOCK_FOUNDATION))
- {
- m_UpdateInformations.dirty = true;
+void CCmpObstructionManager::MakeDirtyUnit(flags_t flags, u32 index, const UnitShape& shape)
+{
+ m_DebugOverlayDirty = true;
- if (std::find(m_DirtyUnitShapes.begin(), m_DirtyUnitShapes.end(), index) == m_DirtyUnitShapes.end())
- m_DirtyUnitShapes.push_back(index);
+ if (flags & (FLAG_BLOCK_PATHFINDING | FLAG_BLOCK_FOUNDATION))
+ {
+ m_UpdateInformations.dirty = true;
- // All shapes overlapping the updated part of the grid should be dirtied too.
- // We are going to invalidate the region of the grid corresponding to the modified shape plus its clearance,
- // and we need to get the shapes whose clearance can overlap this area. So we need to extend the search area
- // by two times the maximum clearance.
+ if (std::find(m_DirtyUnitShapes.begin(), m_DirtyUnitShapes.end(), index) == m_DirtyUnitShapes.end())
+ m_DirtyUnitShapes.push_back(index);
- CFixedVector2D center(shape.x, shape.z);
+ // All shapes overlapping the updated part of the grid should be dirtied too.
+ // We are going to invalidate the region of the grid corresponding to the modified shape plus its clearance,
+ // and we need to get the shapes whose clearance can overlap this area. So we need to extend the search area
+ // by two times the maximum clearance.
- std::vector staticsNear;
- m_StaticSubdivision.GetNear(staticsNear, center, shape.clearance + m_MaxClearance*2);
- for (u32& staticId : staticsNear)
- if (std::find(m_DirtyStaticShapes.begin(), m_DirtyStaticShapes.end(), staticId) == m_DirtyStaticShapes.end())
- m_DirtyStaticShapes.push_back(staticId);
+ CFixedVector2D center(shape.x, shape.z);
- std::vector unitsNear;
- m_UnitSubdivision.GetNear(unitsNear, center, shape.clearance + m_MaxClearance*2);
- for (u32& unitId : unitsNear)
- if (std::find(m_DirtyUnitShapes.begin(), m_DirtyUnitShapes.end(), unitId) == m_DirtyUnitShapes.end())
- m_DirtyUnitShapes.push_back(unitId);
+ std::vector staticsNear;
+ m_StaticSubdivision.GetNear(staticsNear, center, shape.clearance + m_MaxClearance*2);
+ for (u32& staticId : staticsNear)
+ if (std::find(m_DirtyStaticShapes.begin(), m_DirtyStaticShapes.end(), staticId) == m_DirtyStaticShapes.end())
+ m_DirtyStaticShapes.push_back(staticId);
- MarkDirtinessGrid(shape.x, shape.z, shape.clearance + m_MaxClearance);
- }
- }
+ std::vector unitsNear;
+ m_UnitSubdivision.GetNear(unitsNear, center, shape.clearance + m_MaxClearance*2);
+ for (u32& unitId : unitsNear)
+ if (std::find(m_DirtyUnitShapes.begin(), m_DirtyUnitShapes.end(), unitId) == m_DirtyUnitShapes.end())
+ m_DirtyUnitShapes.push_back(unitId);
- /**
- * Return whether the given point is within the world bounds by at least r
- */
- inline bool IsInWorld(entity_pos_t x, entity_pos_t z, entity_pos_t r) const
- {
- return (m_WorldX0+r <= x && x <= m_WorldX1-r && m_WorldZ0+r <= z && z <= m_WorldZ1-r);
+ MarkDirtinessGrid(shape.x, shape.z, shape.clearance + m_MaxClearance);
}
+}
- /**
- * Return whether the given point is within the world bounds
- */
- inline bool IsInWorld(const CFixedVector2D& p) const
- {
- return (m_WorldX0 <= p.X && p.X <= m_WorldX1 && m_WorldZ0 <= p.Y && p.Y <= m_WorldZ1);
- }
+bool CCmpObstructionManager::IsInWorld(entity_pos_t x, entity_pos_t z, entity_pos_t r) const
+{
+ return (m_WorldX0+r <= x && x <= m_WorldX1-r && m_WorldZ0+r <= z && z <= m_WorldZ1-r);
+}
- void RasterizeHelper(Grid& grid, ICmpObstructionManager::flags_t requireMask, bool fullUpdate, pass_class_t appliedMask, entity_pos_t clearance = fixed::Zero()) const;
-};
+bool CCmpObstructionManager::IsInWorld(const CFixedVector2D& p) const
+{
+ return (m_WorldX0 <= p.X && p.X <= m_WorldX1 && m_WorldZ0 <= p.Y && p.Y <= m_WorldZ1);
+}
REGISTER_COMPONENT_TYPE(ObstructionManager)
Index: source/simulation2/components/CCmpParticleManager.h
===================================================================
--- /dev/null
+++ source/simulation2/components/CCmpParticleManager.h
@@ -0,0 +1,53 @@
+/* Copyright (C) 2022 Wildfire Games.
+ * This file is part of 0 A.D.
+ *
+ * 0 A.D. is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D. If not, see .
+ */
+
+#ifndef INCLUDED_CCMPPARTICLEMANAGER
+#define INCLUDED_CCMPPARTICLEMANAGER
+
+#include "ICmpParticleManager.h"
+
+class CCmpParticleManager final : public ICmpParticleManager
+{
+public:
+ static constexpr int typeId{CID_ParticleManager};
+
+ static void ClassInit(CComponentManager& componentManager);
+
+ static IComponent* Allocate(const ScriptInterface&, JS::HandleValue);
+ static void Deallocate(IComponent* cmp);
+
+ int GetComponentTypeId() const override;
+
+ static std::string GetSchema();
+
+ void Init(const CParamNode& UNUSED(paramNode)) override;
+
+ void Deinit() override;
+
+ void Serialize(ISerializer& UNUSED(serialize)) override;
+
+ void Deserialize(const CParamNode& paramNode, IDeserializer& UNUSED(deserialize)) override;
+
+ void HandleMessage(const CMessage& msg, bool UNUSED(global)) override;
+
+ void SetUseSimTime(bool flag) override;
+
+private:
+ bool useSimTime;
+};
+
+#endif // INCLUDED_CCMPPARTICLEMANAGER
Index: source/simulation2/components/CCmpParticleManager.cpp
===================================================================
--- source/simulation2/components/CCmpParticleManager.cpp
+++ source/simulation2/components/CCmpParticleManager.cpp
@@ -17,70 +17,74 @@
#include "precompiled.h"
+#include "CCmpParticleManager.h"
+
#include "simulation2/system/Component.h"
-#include "ICmpParticleManager.h"
#include "graphics/ParticleManager.h"
#include "renderer/Renderer.h"
#include "renderer/SceneRenderer.h"
#include "simulation2/MessageTypes.h"
-class CCmpParticleManager final : public ICmpParticleManager
+void CCmpParticleManager::ClassInit(CComponentManager& componentManager)
{
-public:
- static void ClassInit(CComponentManager& componentManager)
- {
- componentManager.SubscribeToMessageType(MT_Interpolate);
- }
+ componentManager.SubscribeToMessageType(MT_Interpolate);
+}
- DEFAULT_COMPONENT_ALLOCATOR(ParticleManager)
+IComponent* CCmpParticleManager::Allocate(const ScriptInterface&, JS::HandleValue)
+{
+ return nullptr;
+}
- bool useSimTime;
+void CCmpParticleManager::Deallocate(IComponent*)
+{}
- static std::string GetSchema()
- {
- return "";
- }
+int CCmpParticleManager::GetComponentTypeId() const
+{
+ return CID_ParticleManager;
+}
- void Init(const CParamNode& UNUSED(paramNode)) override
- {
- useSimTime = true;
- }
+std::string CCmpParticleManager::GetSchema()
+{
+ return "";
+}
- void Deinit() override
- {
- }
+void CCmpParticleManager::Init(const CParamNode& UNUSED(paramNode))
+{
+ useSimTime = true;
+}
- void Serialize(ISerializer& UNUSED(serialize)) override
- {
- }
+void CCmpParticleManager::Deinit()
+{}
- void Deserialize(const CParamNode& paramNode, IDeserializer& UNUSED(deserialize)) override
- {
- Init(paramNode);
- }
+void CCmpParticleManager::Serialize(ISerializer& UNUSED(serialize))
+{}
- void HandleMessage(const CMessage& msg, bool UNUSED(global)) override
+void CCmpParticleManager::Deserialize(const CParamNode& paramNode, IDeserializer& UNUSED(deserialize))
+{
+ Init(paramNode);
+}
+
+void CCmpParticleManager::HandleMessage(const CMessage& msg, bool UNUSED(global))
+{
+ switch (msg.GetType())
{
- switch (msg.GetType())
- {
- case MT_Interpolate:
+ case MT_Interpolate:
+ {
+ const CMessageInterpolate& msgData = static_cast (msg);
+ if (CRenderer::IsInitialised())
{
- const CMessageInterpolate& msgData = static_cast (msg);
- if (CRenderer::IsInitialised())
- {
- float time = useSimTime ? msgData.deltaSimTime : msgData.deltaRealTime;
- g_Renderer.GetSceneRenderer().GetParticleManager().Interpolate(time);
- }
- break;
- }
+ float time = useSimTime ? msgData.deltaSimTime : msgData.deltaRealTime;
+ g_Renderer.GetSceneRenderer().GetParticleManager().Interpolate(time);
}
+ break;
}
-
- void SetUseSimTime(bool flag) override
- {
- useSimTime = flag;
}
-};
+}
+
+void CCmpParticleManager::SetUseSimTime(bool flag)
+{
+ useSimTime = flag;
+}
REGISTER_COMPONENT_TYPE(ParticleManager)
Index: source/simulation2/components/CCmpPathfinder.cpp
===================================================================
--- source/simulation2/components/CCmpPathfinder.cpp
+++ source/simulation2/components/CCmpPathfinder.cpp
@@ -29,13 +29,11 @@
#include "simulation2/components/ICmpObstructionManager.h"
#include "simulation2/components/ICmpTerrain.h"
#include "simulation2/components/ICmpWaterManager.h"
-#include "simulation2/helpers/HierarchicalPathfinder.h"
-#include "simulation2/helpers/LongPathfinder.h"
#include "simulation2/helpers/MapEdgeTiles.h"
#include "simulation2/helpers/Rasterize.h"
-#include "simulation2/helpers/VertexPathfinder.h"
#include "simulation2/serialization/SerializedPathfinder.h"
#include "simulation2/serialization/SerializedTypes.h"
+#include "simulation2/system/Component.h"
#include "ps/CLogger.h"
#include "ps/CStr.h"
@@ -47,6 +45,28 @@
REGISTER_COMPONENT_TYPE(Pathfinder)
+void CCmpPathfinder::ClassInit(CComponentManager& componentManager)
+{
+ componentManager.SubscribeToMessageType(MT_Deserialized);
+ componentManager.SubscribeToMessageType(MT_RenderSubmit); // for debug overlays
+ componentManager.SubscribeToMessageType(MT_TerrainChanged);
+ componentManager.SubscribeToMessageType(MT_WaterChanged);
+ componentManager.SubscribeToMessageType(MT_ObstructionMapShapeChanged);
+}
+
+IComponent* CCmpPathfinder::Allocate(const ScriptInterface&, JS::HandleValue)
+{
+ return nullptr;
+}
+
+void CCmpPathfinder::Deallocate(IComponent*)
+{}
+
+int CCmpPathfinder::GetComponentTypeId() const
+{
+ return CID_Pathfinder;
+}
+
void CCmpPathfinder::Init(const CParamNode& UNUSED(paramNode))
{
m_GridSize = 0;
Index: source/simulation2/components/CCmpPathfinder_Common.h
===================================================================
--- source/simulation2/components/CCmpPathfinder_Common.h
+++ source/simulation2/components/CCmpPathfinder_Common.h
@@ -27,8 +27,6 @@
* The long-range pathfinding is done by a LongPathfinder object.
*/
-#include "simulation2/system/Component.h"
-
#include "ICmpPathfinder.h"
#include "graphics/Overlay.h"
@@ -39,13 +37,12 @@
#include "renderer/TerrainOverlay.h"
#include "simulation2/components/ICmpObstructionManager.h"
#include "simulation2/helpers/Grid.h"
+#include "simulation2/helpers/HierarchicalPathfinder.h"
+#include "simulation2/helpers/LongPathfinder.h"
+#include "simulation2/helpers/VertexPathfinder.h"
#include
-class HierarchicalPathfinder;
-class LongPathfinder;
-class VertexPathfinder;
-
class SceneCollector;
class AtlasOverlay;
@@ -61,18 +58,16 @@
class CCmpPathfinder final : public ICmpPathfinder
{
public:
- static void ClassInit(CComponentManager& componentManager)
- {
- componentManager.SubscribeToMessageType(MT_Deserialized);
- componentManager.SubscribeToMessageType(MT_RenderSubmit); // for debug overlays
- componentManager.SubscribeToMessageType(MT_TerrainChanged);
- componentManager.SubscribeToMessageType(MT_WaterChanged);
- componentManager.SubscribeToMessageType(MT_ObstructionMapShapeChanged);
- }
+ static constexpr int typeId{CID_Pathfinder};
+
+ static void ClassInit(CComponentManager& componentManager);
~CCmpPathfinder();
- DEFAULT_COMPONENT_ALLOCATOR(Pathfinder)
+ static IComponent* Allocate(const ScriptInterface&, JS::HandleValue);
+ static void Deallocate(IComponent* cmp);
+
+ int GetComponentTypeId() const override;
// Template state:
Index: source/simulation2/components/CCmpProjectileManager.h
===================================================================
--- /dev/null
+++ source/simulation2/components/CCmpProjectileManager.h
@@ -0,0 +1,117 @@
+/* Copyright (C) 2022 Wildfire Games.
+ * This file is part of 0 A.D.
+ *
+ * 0 A.D. is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D. If not, see .
+ */
+
+#ifndef INCLUDED_CCMPPROJECTILEMANAGER
+#define INCLUDED_CCMPPROJECTILEMANAGER
+
+#include "ICmpProjectileManager.h"
+
+#include "graphics/ModelAbstract.h"
+#include "graphics/Unit.h"
+#include "renderer/Scene.h"
+#include "simulation2/helpers/Los.h"
+
+class CCmpProjectileManager final : public ICmpProjectileManager
+{
+public:
+ static constexpr int typeId{CID_ProjectileManager};
+
+ static void ClassInit(CComponentManager& componentManager);
+
+ static IComponent* Allocate(const ScriptInterface&, JS::HandleValue);
+ static void Deallocate(IComponent* cmp);
+
+ int GetComponentTypeId() const override;
+
+ static std::string GetSchema();
+
+ void Init(const CParamNode& UNUSED(paramNode)) override;
+
+ void Deinit() override;
+
+ void Serialize(ISerializer& serialize) override;
+
+ void Deserialize(const CParamNode& paramNode, IDeserializer& deserialize) override;
+
+ void HandleMessage(const CMessage& msg, bool UNUSED(global)) override;
+
+ uint32_t LaunchProjectileAtPoint(const CFixedVector3D& launchPoint, const CFixedVector3D& target,
+ fixed speed, fixed gravity, const std::wstring& actorName, const std::wstring& impactActorName,
+ fixed impactAnimationLifetime) override;
+
+ void RemoveProjectile(uint32_t) override;
+
+ void RenderModel(CModelAbstract& model, const CVector3D& position, SceneCollector& collector,
+ const CFrustum& frustum, bool culling, const CLosQuerier& los, bool losRevealAll) const;
+
+private:
+ struct Projectile
+ {
+ CUnit* unit;
+ CVector3D origin;
+ CVector3D pos;
+ CVector3D v;
+ float time;
+ float timeHit;
+ float gravity;
+ float impactAnimationLifetime;
+ uint32_t id;
+ std::wstring impactActorName;
+ bool isImpactAnimationCreated;
+ bool stopped;
+
+ CVector3D position(float t)
+ {
+ float t2 = t;
+ if (t2 > timeHit)
+ t2 = timeHit + logf(1.f + t2 - timeHit);
+
+ CVector3D ret(origin);
+ ret.X += v.X * t2;
+ ret.Z += v.Z * t2;
+ ret.Y += v.Y * t2 - 0.5f * gravity * t * t;
+ return ret;
+ }
+ };
+
+ struct ProjectileImpactAnimation
+ {
+ CUnit* unit;
+ CVector3D pos;
+ float time;
+ };
+
+ std::vector m_Projectiles;
+
+ std::vector m_ProjectileImpactAnimations;
+
+ uint32_t m_ActorSeed;
+
+ uint32_t m_NextId;
+
+ uint32_t LaunchProjectile(CFixedVector3D launchPoint, CFixedVector3D targetPoint, fixed speed,
+ fixed gravity, const std::wstring& actorName, const std::wstring& impactActorName,
+ fixed impactAnimationLifetime);
+
+ void AdvanceProjectile(Projectile& projectile, float dt) const;
+
+ void Interpolate(float frameTime);
+
+ void RenderSubmit(SceneCollector& collector, const CFrustum& frustum, bool culling) const;
+};
+
+#endif // INCLUDED_CCMPPROJECTILEMANAGER
Index: source/simulation2/components/CCmpProjectileManager.cpp
===================================================================
--- source/simulation2/components/CCmpProjectileManager.cpp
+++ source/simulation2/components/CCmpProjectileManager.cpp
@@ -17,8 +17,9 @@
#include "precompiled.h"
+#include "CCmpProjectileManager.h"
+
#include "simulation2/system/Component.h"
-#include "ICmpProjectileManager.h"
#include "ICmpObstruction.h"
#include "ICmpObstructionManager.h"
@@ -29,148 +30,96 @@
#include "simulation2/MessageTypes.h"
#include "graphics/Model.h"
-#include "graphics/Unit.h"
#include "graphics/UnitManager.h"
#include "maths/Frustum.h"
#include "maths/Matrix3D.h"
#include "maths/Quaternion.h"
#include "maths/Vector3D.h"
#include "ps/CLogger.h"
-#include "renderer/Scene.h"
// Time (in seconds) before projectiles that stuck in the ground are destroyed
const static float PROJECTILE_DECAY_TIME = 30.f;
-class CCmpProjectileManager final : public ICmpProjectileManager
+void CCmpProjectileManager::ClassInit(CComponentManager& componentManager)
{
-public:
- static void ClassInit(CComponentManager& componentManager)
- {
- componentManager.SubscribeToMessageType(MT_Interpolate);
- componentManager.SubscribeToMessageType(MT_RenderSubmit);
- }
+ componentManager.SubscribeToMessageType(MT_Interpolate);
+ componentManager.SubscribeToMessageType(MT_RenderSubmit);
+}
- DEFAULT_COMPONENT_ALLOCATOR(ProjectileManager)
+IComponent* CCmpProjectileManager::Allocate(const ScriptInterface&, JS::HandleValue)
+{
+ return nullptr;
+}
+void CCmpProjectileManager::Deallocate(IComponent*)
+{}
- static std::string GetSchema()
- {
- return "";
- }
+int CCmpProjectileManager::GetComponentTypeId() const
+{
+ return CID_ProjectileManager;
+}
- void Init(const CParamNode& UNUSED(paramNode)) override
- {
- m_ActorSeed = 0;
- m_NextId = 1;
- }
+std::string CCmpProjectileManager::GetSchema()
+{
+ return "";
+}
- void Deinit() override
- {
- for (size_t i = 0; i < m_Projectiles.size(); ++i)
- GetSimContext().GetUnitManager().DeleteUnit(m_Projectiles[i].unit);
- m_Projectiles.clear();
- }
+void CCmpProjectileManager::Init(const CParamNode& UNUSED(paramNode))
+{
+ m_ActorSeed = 0;
+ m_NextId = 1;
+}
- void Serialize(ISerializer& serialize) override
- {
- // Because this is just graphical effects, and because it's all non-deterministic floating point,
- // we don't do much serialization here.
- // (That means projectiles will vanish if you save/load - is that okay?)
+void CCmpProjectileManager::Deinit()
+{
+ for (size_t i = 0; i < m_Projectiles.size(); ++i)
+ GetSimContext().GetUnitManager().DeleteUnit(m_Projectiles[i].unit);
+ m_Projectiles.clear();
+}
- // The attack code stores the id so that the projectile gets deleted when it hits the target
- serialize.NumberU32_Unbounded("next id", m_NextId);
- }
+void CCmpProjectileManager::Serialize(ISerializer& serialize)
+{
+ // Because this is just graphical effects, and because it's all non-deterministic floating point,
+ // we don't do much serialization here.
+ // (That means projectiles will vanish if you save/load - is that okay?)
- void Deserialize(const CParamNode& paramNode, IDeserializer& deserialize) override
- {
- Init(paramNode);
+ // The attack code stores the id so that the projectile gets deleted when it hits the target
+ serialize.NumberU32_Unbounded("next id", m_NextId);
+}
- // The attack code stores the id so that the projectile gets deleted when it hits the target
- deserialize.NumberU32_Unbounded("next id", m_NextId);
- }
+void CCmpProjectileManager::Deserialize(const CParamNode& paramNode, IDeserializer& deserialize)
+{
+ Init(paramNode);
- void HandleMessage(const CMessage& msg, bool UNUSED(global)) override
- {
- switch (msg.GetType())
- {
- case MT_Interpolate:
- {
- const CMessageInterpolate& msgData = static_cast (msg);
- Interpolate(msgData.deltaSimTime);
- break;
- }
- case MT_RenderSubmit:
- {
- const CMessageRenderSubmit& msgData = static_cast (msg);
- RenderSubmit(msgData.collector, msgData.frustum, msgData.culling);
- break;
- }
- }
- }
+ // The attack code stores the id so that the projectile gets deleted when it hits the target
+ deserialize.NumberU32_Unbounded("next id", m_NextId);
+}
- uint32_t LaunchProjectileAtPoint(const CFixedVector3D& launchPoint, const CFixedVector3D& target, fixed speed, fixed gravity, const std::wstring& actorName, const std::wstring& impactActorName, fixed impactAnimationLifetime) override
+void CCmpProjectileManager::HandleMessage(const CMessage& msg, bool UNUSED(global))
+{
+ switch (msg.GetType())
{
- return LaunchProjectile(launchPoint, target, speed, gravity, actorName, impactActorName, impactAnimationLifetime);
- }
-
- void RemoveProjectile(uint32_t) override;
-
- void RenderModel(CModelAbstract& model, const CVector3D& position, SceneCollector& collector, const CFrustum& frustum, bool culling,
- const CLosQuerier& los, bool losRevealAll) const;
-
-private:
- struct Projectile
+ case MT_Interpolate:
{
- CUnit* unit;
- CVector3D origin;
- CVector3D pos;
- CVector3D v;
- float time;
- float timeHit;
- float gravity;
- float impactAnimationLifetime;
- uint32_t id;
- std::wstring impactActorName;
- bool isImpactAnimationCreated;
- bool stopped;
-
- CVector3D position(float t)
- {
- float t2 = t;
- if (t2 > timeHit)
- t2 = timeHit + logf(1.f + t2 - timeHit);
-
- CVector3D ret(origin);
- ret.X += v.X * t2;
- ret.Z += v.Z * t2;
- ret.Y += v.Y * t2 - 0.5f * gravity * t * t;
- return ret;
- }
- };
-
- struct ProjectileImpactAnimation
+ const CMessageInterpolate& msgData = static_cast (msg);
+ Interpolate(msgData.deltaSimTime);
+ break;
+ }
+ case MT_RenderSubmit:
{
- CUnit* unit;
- CVector3D pos;
- float time;
- };
-
- std::vector m_Projectiles;
-
- std::vector m_ProjectileImpactAnimations;
-
- uint32_t m_ActorSeed;
-
- uint32_t m_NextId;
-
- uint32_t LaunchProjectile(CFixedVector3D launchPoint, CFixedVector3D targetPoint, fixed speed, fixed gravity,
- const std::wstring& actorName, const std::wstring& impactActorName, fixed impactAnimationLifetime);
-
- void AdvanceProjectile(Projectile& projectile, float dt) const;
+ const CMessageRenderSubmit& msgData = static_cast (msg);
+ RenderSubmit(msgData.collector, msgData.frustum, msgData.culling);
+ break;
+ }
+ }
+}
- void Interpolate(float frameTime);
+uint32_t CCmpProjectileManager::LaunchProjectileAtPoint(const CFixedVector3D& launchPoint,
+ const CFixedVector3D& target, fixed speed, fixed gravity, const std::wstring& actorName,
+ const std::wstring& impactActorName, fixed impactAnimationLifetime)
+{
+ return LaunchProjectile(launchPoint, target, speed, gravity, actorName, impactActorName, impactAnimationLifetime);
+}
- void RenderSubmit(SceneCollector& collector, const CFrustum& frustum, bool culling) const;
-};
REGISTER_COMPONENT_TYPE(ProjectileManager)
Index: source/simulation2/components/CCmpRangeManager.h
===================================================================
--- /dev/null
+++ source/simulation2/components/CCmpRangeManager.h
@@ -0,0 +1,484 @@
+/* Copyright (C) 2022 Wildfire Games.
+ * This file is part of 0 A.D.
+ *
+ * 0 A.D. is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D. If not, see .
+ */
+
+#ifndef INCLUDED_CCMPRANGEMANAGER
+#define INCLUDED_CCMPRANGEMANAGER
+
+#include "ICmpRangeManager.h"
+
+#include "graphics/Overlay.h"
+#include "renderer/Scene.h"
+#include "simulation2/helpers/Grid.h"
+#include "simulation2/helpers/Los.h"
+#include "simulation2/helpers/Spatial.h"
+#include "simulation2/system/EntityMap.h"
+
+#define DEBUG_RANGE_MANAGER_BOUNDS 0
+
+/**
+ * Range manager implementation.
+ * Maintains a list of all entities (and their positions and owners), which is used for
+ * queries.
+ *
+ * LOS implementation is based on the model described in GPG2.
+ * (TODO: would be nice to make it cleverer, so e.g. mountains and walls
+ * can block vision)
+ */
+class CCmpRangeManager final : public ICmpRangeManager
+{
+public:
+ /**
+ * Representation of a range query.
+ */
+ struct Query
+ {
+ std::vector lastMatch;
+ CEntityHandle source; // TODO: this could crash if an entity is destroyed while a Query is still referencing it
+ entity_pos_t minRange;
+ entity_pos_t maxRange;
+ entity_pos_t yOrigin; // Used for parabolas only.
+ u32 ownersMask;
+ i32 interface;
+ u8 flagsMask;
+ bool enabled;
+ bool parabolic;
+ bool accountForSize; // If true, the query accounts for unit sizes, otherwise it treats all entities as points.
+ };
+
+ /**
+ * Representation of an entity, with the data needed for queries.
+ */
+ enum class FlagMasks
+ {
+ // flags used for queries
+ None = 0x00,
+ Normal = 0x01,
+ Injured = 0x02,
+ AllQuery = Normal | Injured,
+
+ // 0x04 reserved for future use
+
+ // general flags
+ InWorld = 0x08,
+ RetainInFog = 0x10,
+ RevealShore = 0x20,
+ ScriptedVisibility = 0x40,
+ SharedVision = 0x80
+ };
+
+ struct EntityData
+ {
+ EntityData() :
+ visibilities(0), size(0), visionSharing(0),
+ owner(-1), flags{static_cast(FlagMasks::Normal)}
+ { }
+ entity_pos_t x, z;
+ entity_pos_t visionRange;
+ u32 visibilities; // 2-bit visibility, per player
+ u32 size;
+ u16 visionSharing; // 1-bit per player
+ i8 owner;
+ u8 flags; // See the FlagMasks enum
+
+ template
+ bool HasFlag() const { return (flags & static_cast(mask)) != 0; }
+
+ template
+ void SetFlag(bool val) { flags = val ? (flags | static_cast(mask)) :
+ (flags & ~static_cast(mask)); }
+
+ void SetFlag(u8 mask, bool val) { flags = val ? (flags | mask) : (flags & ~mask); }
+ };
+
+ static constexpr int typeId{CID_RangeManager};
+
+ static void ClassInit(CComponentManager& componentManager);
+
+ static IComponent* Allocate(const ScriptInterface&, JS::HandleValue);
+
+ static void Deallocate(IComponent* cmp);
+
+ int GetComponentTypeId() const override;
+
+ bool m_DebugOverlayEnabled;
+ bool m_DebugOverlayDirty;
+ std::vector m_DebugOverlayLines;
+
+ // Deserialization flag. A lot of different functions are called by Deserialize()
+ // and we don't want to pass isDeserializing bool arguments to all of them...
+ bool m_Deserializing;
+
+ // World bounds (entities are expected to be within this range)
+ entity_pos_t m_WorldX0;
+ entity_pos_t m_WorldZ0;
+ entity_pos_t m_WorldX1;
+ entity_pos_t m_WorldZ1;
+
+ // Range query state:
+ tag_t m_QueryNext; // next allocated id
+ std::map m_Queries;
+ EntityMap m_EntityData;
+
+ FastSpatialSubdivision m_Subdivision; // spatial index of m_EntityData
+ std::vector m_SubdivisionResults;
+
+ // LOS state:
+ static const player_id_t MAX_LOS_PLAYER_ID = 16;
+
+ using LosRegion = std::pair;
+
+ std::array m_LosRevealAll;
+ bool m_LosCircular;
+ i32 m_LosVerticesPerSide;
+
+ // Cache for visibility tracking
+ i32 m_LosRegionsPerSide;
+ bool m_GlobalVisibilityUpdate;
+ std::array m_GlobalPlayerVisibilityUpdate;
+ Grid m_DirtyVisibility;
+ Grid> m_LosRegions;
+ // List of entities that must be updated, regardless of the status of their tile
+ std::vector m_ModifiedEntities;
+
+ // Counts of units seeing vertex, per vertex, per player (starting with player 0).
+ // Use u16 to avoid overflows when we have very large (but not infeasibly large) numbers
+ // of units in a very small area.
+ // (Note we use vertexes, not tiles, to better match the renderer.)
+ // Lazily constructed when it's needed, to save memory in smaller games.
+ std::array, MAX_LOS_PLAYER_ID> m_LosPlayerCounts;
+
+ // 2-bit LosState per player, starting with player 1 (not 0!) up to player MAX_LOS_PLAYER_ID (inclusive)
+ Grid m_LosState;
+
+ // Special static visibility data for the "reveal whole map" mode
+ // (TODO: this is usually a waste of memory)
+ Grid m_LosStateRevealed;
+
+ // Shared LOS masks, one per player.
+ std::array m_SharedLosMasks;
+ // Shared dirty visibility masks, one per player.
+ std::array m_SharedDirtyVisibilityMasks;
+
+ // Cache explored vertices per player (not serialized)
+ u32 m_TotalInworldVertices;
+ std::vector m_ExploredVertices;
+
+ static std::string GetSchema();
+
+ void Init(const CParamNode& UNUSED(paramNode)) override;
+
+ void Deinit() override;
+
+ template
+ void SerializeCommon(S& serialize)
+ {
+ serialize.NumberFixed_Unbounded("world x0", m_WorldX0);
+ serialize.NumberFixed_Unbounded("world z0", m_WorldZ0);
+ serialize.NumberFixed_Unbounded("world x1", m_WorldX1);
+ serialize.NumberFixed_Unbounded("world z1", m_WorldZ1);
+
+ serialize.NumberU32_Unbounded("query next", m_QueryNext);
+ Serializer(serialize, "queries", m_Queries, GetSimContext());
+ Serializer(serialize, "entity data", m_EntityData);
+
+ Serializer(serialize, "los reveal all", m_LosRevealAll);
+ serialize.Bool("los circular", m_LosCircular);
+ serialize.NumberI32_Unbounded("los verts per side", m_LosVerticesPerSide);
+
+ serialize.Bool("global visibility update", m_GlobalVisibilityUpdate);
+ Serializer(serialize, "global player visibility update", m_GlobalPlayerVisibilityUpdate);
+ Serializer(serialize, "dirty visibility", m_DirtyVisibility);
+ Serializer(serialize, "modified entities", m_ModifiedEntities);
+
+ // We don't serialize m_Subdivision, m_LosPlayerCounts or m_LosRegions
+ // since they can be recomputed from the entity data when deserializing;
+ // m_LosState must be serialized since it depends on the history of exploration
+
+ Serializer(serialize, "los state", m_LosState);
+ Serializer(serialize, "shared los masks", m_SharedLosMasks);
+ Serializer(serialize, "shared dirty visibility masks", m_SharedDirtyVisibilityMasks);
+ }
+
+ void Serialize(ISerializer& serialize) override;
+
+ void Deserialize(const CParamNode& paramNode, IDeserializer& deserialize) override;
+
+ void HandleMessage(const CMessage& msg, bool UNUSED(global)) override;
+
+ void SetBounds(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1) override;
+
+ void Verify() override;
+
+ FastSpatialSubdivision* GetSubdivision() override;
+
+ // Reinitialise subdivisions and LOS data, based on entity data
+ void ResetDerivedData();
+
+ void ResetSubdivisions(entity_pos_t x1, entity_pos_t z1);
+
+ tag_t CreateActiveQuery(entity_id_t source,
+ entity_pos_t minRange, entity_pos_t maxRange, const std::vector& owners,
+ int requiredInterface, u8 flags, bool accountForSize) override;
+
+ tag_t CreateActiveParabolicQuery(entity_id_t source,
+ entity_pos_t minRange, entity_pos_t maxRange, entity_pos_t yOrigin,
+ const std::vector& owners, int requiredInterface, u8 flags) override;
+
+ void DestroyActiveQuery(tag_t tag) override;
+
+ void EnableActiveQuery(tag_t tag) override;
+
+ void DisableActiveQuery(tag_t tag) override;
+
+ bool IsActiveQueryEnabled(tag_t tag) const override;
+
+ std::vector ExecuteQueryAroundPos(const CFixedVector2D& pos, entity_pos_t minRange,
+ entity_pos_t maxRange, const std::vector& owners, int requiredInterface,
+ bool accountForSize) override;
+
+ std::vector ExecuteQuery(entity_id_t source, entity_pos_t minRange,
+ entity_pos_t maxRange, const std::vector& owners, int requiredInterface,
+ bool accountForSize) override;
+
+ std::vector ResetActiveQuery(tag_t tag) override;
+
+ std::vector GetEntitiesByPlayer(player_id_t player) const override;
+
+ std::vector GetNonGaiaEntities() const override;
+
+ std::vector GetGaiaAndNonGaiaEntities() const override;
+
+ std::vector GetEntitiesByMask(u32 ownerMask) const;
+
+ void SetDebugOverlay(bool enabled) override;
+
+ /**
+ * Update all currently-enabled active queries.
+ */
+ void ExecuteActiveQueries();
+
+ /**
+ * Returns whether the given entity matches the given query (ignoring maxRange)
+ */
+ bool TestEntityQuery(const Query& q, entity_id_t id, const EntityData& entity) const;
+
+ /**
+ * Returns a list of distinct entity IDs that match the given query, sorted by ID.
+ */
+ void PerformQuery(const Query& q, std::vector& r, CFixedVector2D pos);
+
+ entity_pos_t GetEffectiveParabolicRange(entity_id_t source, entity_id_t target, entity_pos_t range,
+ entity_pos_t yOrigin) const override;
+
+ entity_pos_t GetElevationAdaptedRange(const CFixedVector3D& pos1, const CFixedVector3D& rot,
+ entity_pos_t range, entity_pos_t yOrigin, entity_pos_t angle) const override;
+
+ virtual std::vector getParabolicRangeForm(CFixedVector3D pos, entity_pos_t maxRange,
+ entity_pos_t cutoff, entity_pos_t minAngle, entity_pos_t maxAngle, int numberOfSteps) const;
+
+ Query ConstructQuery(entity_id_t source, entity_pos_t minRange, entity_pos_t maxRange,
+ const std::vector& owners, int requiredInterface, u8 flagsMask, bool accountForSize) const;
+
+ Query ConstructParabolicQuery(entity_id_t source, entity_pos_t minRange, entity_pos_t maxRange,
+ entity_pos_t yOrigin, const std::vector& owners, int requiredInterface, u8 flagsMask,
+ bool accountForSize) const;
+
+ void RenderSubmit(SceneCollector& collector);
+
+ u8 GetEntityFlagMask(const std::string& identifier) const override;
+
+ void SetEntityFlag(entity_id_t ent, const std::string& identifier, bool value) override;
+
+ // ****************************************************************
+ // LOS implementation:
+
+ CLosQuerier GetLosQuerier(player_id_t player) const override;
+
+ void ActivateScriptedVisibility(entity_id_t ent, bool status) override;
+
+ LosVisibility ComputeLosVisibility(CEntityHandle ent, player_id_t player) const;
+ LosVisibility ComputeLosVisibility(entity_id_t ent, player_id_t player) const;
+
+ LosVisibility GetLosVisibility(CEntityHandle ent, player_id_t player) const override;
+ LosVisibility GetLosVisibility(entity_id_t ent, player_id_t player) const override;
+ LosVisibility GetLosVisibilityPosition(entity_pos_t x, entity_pos_t z, player_id_t player) const
+ override;
+
+ size_t GetVerticesPerSide() const override;
+
+ LosRegion LosVertexToLosRegionsHelper(u16 x, u16 z) const;
+
+ LosRegion PosToLosRegionsHelper(entity_pos_t x, entity_pos_t z) const;
+
+ void AddToRegion(LosRegion region, entity_id_t ent);
+ void RemoveFromRegion(LosRegion region, entity_id_t ent);
+
+ void UpdateVisibilityData();
+
+ void RequestVisibilityUpdate(entity_id_t ent) override;
+
+ void UpdateVisibility(entity_id_t ent, player_id_t player);
+ void UpdateVisibility(entity_id_t ent);
+
+ void SetLosRevealAll(player_id_t player, bool enabled) override;
+ bool GetLosRevealAll(player_id_t player) const override;
+ void SetLosCircular(bool enabled) override;
+ bool GetLosCircular() const override;
+
+ void SetSharedLos(player_id_t player, const std::vector& players) override;
+ u32 GetSharedLosMask(player_id_t player) const override;
+
+ void ExploreMap(player_id_t p) override;
+
+ void ExploreTerritories() override;
+
+ /**
+ * Force any entity in explored territory to appear for player p.
+ * This is useful for miraging entities inside the territory borders at the beginning of a game,
+ * or if the "Explore Map" option has been set.
+ */
+ void SeeExploredEntities(player_id_t p) const;
+
+ void RevealShore(player_id_t p, bool enable) override;
+
+ /**
+ * Returns whether the given vertex is outside the normal bounds of the world
+ * (i.e. outside the range of a circular map)
+ */
+ bool LosIsOffWorld(ssize_t i, ssize_t j) const;
+
+ /**
+ * Update the LOS state of tiles within a given horizontal strip (i0,j) to (i1,j) (inclusive).
+ */
+ void LosAddStripHelper(u8 owner, i32 i0, i32 i1, i32 j, Grid& counts);
+
+ /**
+ * Update the LOS state of tiles within a given horizontal strip (i0,j) to (i1,j) (inclusive).
+ */
+ void LosRemoveStripHelper(u8 owner, i32 i0, i32 i1, i32 j, Grid& counts);
+
+ void MarkVisibilityDirtyAroundTile(u8 owner, i32 i, i32 j);
+
+ /**
+ * Update the LOS state of tiles within a given circular range,
+ * either adding or removing visibility depending on the template parameter.
+ * Assumes owner is in the valid range.
+ */
+ template
+ void LosUpdateHelper(u8 owner, entity_pos_t visionRange, CFixedVector2D pos)
+ {
+ if (m_LosVerticesPerSide == 0) // do nothing if not initialised yet
+ return;
+
+ PROFILE("LosUpdateHelper");
+
+ Grid& counts = m_LosPlayerCounts.at(owner);
+
+ // Lazy initialisation of counts:
+ if (counts.blank())
+ counts.resize(m_LosVerticesPerSide, m_LosVerticesPerSide);
+
+ // Compute the circular region as a series of strips.
+ // Rather than quantise pos to vertexes, we do more precise sub-tile computations
+ // to get smoother behaviour as a unit moves rather than jumping a whole tile
+ // at once.
+ // To avoid the cost of sqrt when computing the outline of the circle,
+ // we loop from the bottom to the top and estimate the width of the current
+ // strip based on the previous strip, then adjust each end of the strip
+ // inwards or outwards until it's the widest that still falls within the circle.
+
+ // Compute top/bottom coordinates, and clamp to exclude the 1-tile border around the map
+ // (so that we never render the sharp edge of the map)
+ i32 j0 = ((pos.Y - visionRange)/LOS_TILE_SIZE).ToInt_RoundToInfinity();
+ i32 j1 = ((pos.Y + visionRange)/LOS_TILE_SIZE).ToInt_RoundToNegInfinity();
+ i32 j0clamp = std::max(j0, 1);
+ i32 j1clamp = std::min(j1, m_LosVerticesPerSide-2);
+
+ // Translate world coordinates into fractional tile-space coordinates
+ entity_pos_t x = pos.X / LOS_TILE_SIZE;
+ entity_pos_t y = pos.Y / LOS_TILE_SIZE;
+ entity_pos_t r = visionRange / LOS_TILE_SIZE;
+ entity_pos_t r2 = r.Square();
+
+ // Compute the integers on either side of x
+ i32 xfloor = (x - entity_pos_t::Epsilon()).ToInt_RoundToNegInfinity();
+ i32 xceil = (x + entity_pos_t::Epsilon()).ToInt_RoundToInfinity();
+
+ // Initialise the strip (i0, i1) to a rough guess
+ i32 i0 = xfloor;
+ i32 i1 = xceil;
+
+ for (i32 j = j0clamp; j <= j1clamp; ++j)
+ {
+ // Adjust i0 and i1 to be the outermost values that don't exceed
+ // the circle's radius (i.e. require dy^2 + dx^2 <= r^2).
+ // When moving the points inwards, clamp them to xceil+1 or xfloor-1
+ // so they don't accidentally shoot off in the wrong direction forever.
+
+ entity_pos_t dy = entity_pos_t::FromInt(j) - y;
+ entity_pos_t dy2 = dy.Square();
+ while (dy2 + (entity_pos_t::FromInt(i0-1) - x).Square() <= r2)
+ --i0;
+ while (i0 < xceil && dy2 + (entity_pos_t::FromInt(i0) - x).Square() > r2)
+ ++i0;
+ while (dy2 + (entity_pos_t::FromInt(i1+1) - x).Square() <= r2)
+ ++i1;
+ while (i1 > xfloor && dy2 + (entity_pos_t::FromInt(i1) - x).Square() > r2)
+ --i1;
+
+#if DEBUG_RANGE_MANAGER_BOUNDS
+ if (i0 <= i1)
+ {
+ ENSURE(dy2 + (entity_pos_t::FromInt(i0) - x).Square() <= r2);
+ ENSURE(dy2 + (entity_pos_t::FromInt(i1) - x).Square() <= r2);
+ }
+ ENSURE(dy2 + (entity_pos_t::FromInt(i0 - 1) - x).Square() > r2);
+ ENSURE(dy2 + (entity_pos_t::FromInt(i1 + 1) - x).Square() > r2);
+#endif
+
+ // Clamp the strip to exclude the 1-tile border,
+ // then add or remove the strip as requested
+ i32 i0clamp = std::max(i0, 1);
+ i32 i1clamp = std::min(i1, m_LosVerticesPerSide-2);
+ if (adding)
+ LosAddStripHelper(owner, i0clamp, i1clamp, j, counts);
+ else
+ LosRemoveStripHelper(owner, i0clamp, i1clamp, j, counts);
+ }
+ }
+
+ /**
+ * Update the LOS state of tiles within a given circular range,
+ * by removing visibility around the 'from' position
+ * and then adding visibility around the 'to' position.
+ */
+ void LosUpdateHelperIncremental(u8 owner, entity_pos_t visionRange, CFixedVector2D from, CFixedVector2D to);
+
+ void LosAdd(player_id_t owner, entity_pos_t visionRange, CFixedVector2D pos);
+ void SharingLosAdd(u16 visionSharing, entity_pos_t visionRange, CFixedVector2D pos);
+ void LosRemove(player_id_t owner, entity_pos_t visionRange, CFixedVector2D pos);
+ void SharingLosRemove(u16 visionSharing, entity_pos_t visionRange, CFixedVector2D pos);
+ void LosMove(player_id_t owner, entity_pos_t visionRange, CFixedVector2D from, CFixedVector2D to);
+ void SharingLosMove(u16 visionSharing, entity_pos_t visionRange, CFixedVector2D from,
+ CFixedVector2D to);
+
+ u8 GetPercentMapExplored(player_id_t player) const override;
+
+ u8 GetUnionPercentMapExplored(const std::vector& players) const override;
+};
+
+#endif // INCLUDED_CCMPRANGEMANAGER
Index: source/simulation2/components/CCmpRangeManager.cpp
===================================================================
--- source/simulation2/components/CCmpRangeManager.cpp
+++ source/simulation2/components/CCmpRangeManager.cpp
@@ -17,11 +17,10 @@
#include "precompiled.h"
+#include "CCmpRangeManager.h"
#include "simulation2/system/Component.h"
-#include "ICmpRangeManager.h"
#include "ICmpTerrain.h"
-#include "simulation2/system/EntityMap.h"
#include "simulation2/MessageTypes.h"
#include "simulation2/components/ICmpFogging.h"
#include "simulation2/components/ICmpMirage.h"
@@ -32,19 +31,13 @@
#include "simulation2/components/ICmpVisibility.h"
#include "simulation2/components/ICmpVision.h"
#include "simulation2/components/ICmpWaterManager.h"
-#include "simulation2/helpers/Los.h"
#include "simulation2/helpers/MapEdgeTiles.h"
#include "simulation2/helpers/Render.h"
-#include "simulation2/helpers/Spatial.h"
#include "simulation2/serialization/SerializedTypes.h"
-#include "graphics/Overlay.h"
#include "lib/timer.h"
#include "ps/CLogger.h"
#include "ps/Profile.h"
-#include "renderer/Scene.h"
-
-#define DEBUG_RANGE_MANAGER_BOUNDS 0
namespace
{
@@ -149,24 +142,6 @@
return 1 << (player-1);
}
-/**
- * Representation of a range query.
- */
-struct Query
-{
- std::vector lastMatch;
- CEntityHandle source; // TODO: this could crash if an entity is destroyed while a Query is still referencing it
- entity_pos_t minRange;
- entity_pos_t maxRange;
- entity_pos_t yOrigin; // Used for parabolas only.
- u32 ownersMask;
- i32 interface;
- u8 flagsMask;
- bool enabled;
- bool parabolic;
- bool accountForSize; // If true, the query accounts for unit sizes, otherwise it treats all entities as points.
-};
-
/**
* Checks whether v is in a parabolic range of (0,0,0)
* The highest point of the paraboloid is (0,range/2,0)
@@ -201,51 +176,7 @@
static std::map ParabolicRangesOutlines;
-/**
- * Representation of an entity, with the data needed for queries.
- */
-enum FlagMasks
-{
- // flags used for queries
- None = 0x00,
- Normal = 0x01,
- Injured = 0x02,
- AllQuery = Normal | Injured,
-
- // 0x04 reserved for future use
-
- // general flags
- InWorld = 0x08,
- RetainInFog = 0x10,
- RevealShore = 0x20,
- ScriptedVisibility = 0x40,
- SharedVision = 0x80
-};
-
-struct EntityData
-{
- EntityData() :
- visibilities(0), size(0), visionSharing(0),
- owner(-1), flags(FlagMasks::Normal)
- { }
- entity_pos_t x, z;
- entity_pos_t visionRange;
- u32 visibilities; // 2-bit visibility, per player
- u32 size;
- u16 visionSharing; // 1-bit per player
- i8 owner;
- u8 flags; // See the FlagMasks enum
-
- template
- inline bool HasFlag() const { return (flags & mask) != 0; }
-
- template
- inline void SetFlag(bool val) { flags = val ? (flags | mask) : (flags & ~mask); }
-
- inline void SetFlag(u8 mask, bool val) { flags = val ? (flags | mask) : (flags & ~mask); }
-};
-
-static_assert(sizeof(EntityData) == 24);
+static_assert(sizeof(CCmpRangeManager::EntityData) == 24);
/**
* Functor for sorting entities by distance from a source point.
@@ -255,7 +186,8 @@
class EntityDistanceOrdering
{
public:
- EntityDistanceOrdering(const EntityMap& entities, const CFixedVector2D& source) :
+ EntityDistanceOrdering(const EntityMap& entities,
+ const CFixedVector2D& source) :
m_EntityData(entities), m_Source(source)
{
}
@@ -264,14 +196,14 @@
bool operator()(entity_id_t a, entity_id_t b) const
{
- const EntityData& da = m_EntityData.find(a)->second;
- const EntityData& db = m_EntityData.find(b)->second;
+ const CCmpRangeManager::EntityData& da = m_EntityData.find(a)->second;
+ const CCmpRangeManager::EntityData& db = m_EntityData.find(b)->second;
CFixedVector2D vecA = CFixedVector2D(da.x, da.z) - m_Source;
CFixedVector2D vecB = CFixedVector2D(db.x, db.z) - m_Source;
return (vecA.CompareLength(vecB) < 0);
}
- const EntityMap& m_EntityData;
+ const EntityMap& m_EntityData;
CFixedVector2D m_Source;
private:
@@ -283,10 +215,11 @@
* Serialization helper template for Query
*/
template<>
-struct SerializeHelper
+struct SerializeHelper
{
template
- void Common(S& serialize, const char* UNUSED(name), Serialize::qualify value)
+ void Common(S& serialize, const char* UNUSED(name),
+ Serialize::qualify value)
{
serialize.NumberFixed_Unbounded("min range", value.minRange);
serialize.NumberFixed_Unbounded("max range", value.maxRange);
@@ -300,7 +233,8 @@
serialize.Bool("account for size",value.accountForSize);
}
- void operator()(ISerializer& serialize, const char* name, Query& value, const CSimContext& UNUSED(context))
+ void operator()(ISerializer& serialize, const char* name, CCmpRangeManager::Query& value,
+ const CSimContext& UNUSED(context))
{
Common(serialize, name, value);
@@ -308,7 +242,8 @@
serialize.NumberU32_Unbounded("source", id);
}
- void operator()(IDeserializer& deserialize, const char* name, Query& value, const CSimContext& context)
+ void operator()(IDeserializer& deserialize, const char* name, CCmpRangeManager::Query& value,
+ const CSimContext& context)
{
Common(deserialize, name, value);
@@ -324,10 +259,11 @@
* Serialization helper template for EntityData
*/
template<>
-struct SerializeHelper
+struct SerializeHelper
{
template
- void operator()(S& serialize, const char* UNUSED(name), Serialize::qualify value)
+ void operator()(S& serialize, const char* UNUSED(name),
+ Serialize::qualify value)
{
serialize.NumberFixed_Unbounded("x", value.x);
serialize.NumberFixed_Unbounded("z", value.z);
@@ -340,2186 +276,2019 @@
}
};
-/**
- * Range manager implementation.
- * Maintains a list of all entities (and their positions and owners), which is used for
- * queries.
- *
- * LOS implementation is based on the model described in GPG2.
- * (TODO: would be nice to make it cleverer, so e.g. mountains and walls
- * can block vision)
- */
-class CCmpRangeManager final : public ICmpRangeManager
+void CCmpRangeManager::ClassInit(CComponentManager& componentManager)
{
-public:
- static void ClassInit(CComponentManager& componentManager)
- {
- componentManager.SubscribeGloballyToMessageType(MT_Create);
- componentManager.SubscribeGloballyToMessageType(MT_PositionChanged);
- componentManager.SubscribeGloballyToMessageType(MT_OwnershipChanged);
- componentManager.SubscribeGloballyToMessageType(MT_Destroy);
- componentManager.SubscribeGloballyToMessageType(MT_VisionRangeChanged);
- componentManager.SubscribeGloballyToMessageType(MT_VisionSharingChanged);
-
- componentManager.SubscribeToMessageType(MT_Deserialized);
- componentManager.SubscribeToMessageType(MT_Update);
- componentManager.SubscribeToMessageType(MT_RenderSubmit); // for debug overlays
- }
+ componentManager.SubscribeGloballyToMessageType(MT_Create);
+ componentManager.SubscribeGloballyToMessageType(MT_PositionChanged);
+ componentManager.SubscribeGloballyToMessageType(MT_OwnershipChanged);
+ componentManager.SubscribeGloballyToMessageType(MT_Destroy);
+ componentManager.SubscribeGloballyToMessageType(MT_VisionRangeChanged);
+ componentManager.SubscribeGloballyToMessageType(MT_VisionSharingChanged);
+
+ componentManager.SubscribeToMessageType(MT_Deserialized);
+ componentManager.SubscribeToMessageType(MT_Update);
+ componentManager.SubscribeToMessageType(MT_RenderSubmit); // for debug overlays
+}
+
+IComponent* CCmpRangeManager::Allocate(const ScriptInterface&, JS::HandleValue)
+{
+ return nullptr;
+}
- DEFAULT_COMPONENT_ALLOCATOR(RangeManager)
+void CCmpRangeManager::Deallocate(IComponent*)
+{}
- bool m_DebugOverlayEnabled;
- bool m_DebugOverlayDirty;
- std::vector m_DebugOverlayLines;
+int CCmpRangeManager::GetComponentTypeId() const
+{
+ return CID_RangeManager;
+}
- // Deserialization flag. A lot of different functions are called by Deserialize()
- // and we don't want to pass isDeserializing bool arguments to all of them...
- bool m_Deserializing;
+std::string CCmpRangeManager::GetSchema()
+{
+ return "";
+}
- // World bounds (entities are expected to be within this range)
- entity_pos_t m_WorldX0;
- entity_pos_t m_WorldZ0;
- entity_pos_t m_WorldX1;
- entity_pos_t m_WorldZ1;
+void CCmpRangeManager::Init(const CParamNode& UNUSED(paramNode))
+{
+ m_QueryNext = 1;
- // Range query state:
- tag_t m_QueryNext; // next allocated id
- std::map m_Queries;
- EntityMap m_EntityData;
+ m_DebugOverlayEnabled = false;
+ m_DebugOverlayDirty = true;
- FastSpatialSubdivision m_Subdivision; // spatial index of m_EntityData
- std::vector m_SubdivisionResults;
+ m_Deserializing = false;
+ m_WorldX0 = m_WorldZ0 = m_WorldX1 = m_WorldZ1 = entity_pos_t::Zero();
- // LOS state:
- static const player_id_t MAX_LOS_PLAYER_ID = 16;
+ // Initialise with bogus values (these will get replaced when
+ // SetBounds is called)
+ ResetSubdivisions(entity_pos_t::FromInt(1024), entity_pos_t::FromInt(1024));
- using LosRegion = std::pair;
+ m_SubdivisionResults.reserve(4096);
- std::array m_LosRevealAll;
- bool m_LosCircular;
- i32 m_LosVerticesPerSide;
+ // The whole map should be visible to Gaia by default, else e.g. animals
+ // will get confused when trying to run from enemies
+ m_LosRevealAll[0] = true;
- // Cache for visibility tracking
- i32 m_LosRegionsPerSide;
- bool m_GlobalVisibilityUpdate;
- std::array m_GlobalPlayerVisibilityUpdate;
- Grid m_DirtyVisibility;
- Grid> m_LosRegions;
- // List of entities that must be updated, regardless of the status of their tile
- std::vector m_ModifiedEntities;
+ m_GlobalVisibilityUpdate = true;
- // Counts of units seeing vertex, per vertex, per player (starting with player 0).
- // Use u16 to avoid overflows when we have very large (but not infeasibly large) numbers
- // of units in a very small area.
- // (Note we use vertexes, not tiles, to better match the renderer.)
- // Lazily constructed when it's needed, to save memory in smaller games.
- std::array, MAX_LOS_PLAYER_ID> m_LosPlayerCounts;
+ m_LosCircular = false;
+ m_LosVerticesPerSide = 0;
+}
- // 2-bit LosState per player, starting with player 1 (not 0!) up to player MAX_LOS_PLAYER_ID (inclusive)
- Grid m_LosState;
+void CCmpRangeManager::Deinit()
+{}
- // Special static visibility data for the "reveal whole map" mode
- // (TODO: this is usually a waste of memory)
- Grid m_LosStateRevealed;
+void CCmpRangeManager::Serialize(ISerializer& serialize)
+{
+ SerializeCommon(serialize);
+}
- // Shared LOS masks, one per player.
- std::array m_SharedLosMasks;
- // Shared dirty visibility masks, one per player.
- std::array m_SharedDirtyVisibilityMasks;
+void CCmpRangeManager::Deserialize(const CParamNode& paramNode, IDeserializer& deserialize)
+{
+ Init(paramNode);
- // Cache explored vertices per player (not serialized)
- u32 m_TotalInworldVertices;
- std::vector m_ExploredVertices;
+ SerializeCommon(deserialize);
+}
- static std::string GetSchema()
+void CCmpRangeManager::HandleMessage(const CMessage& msg, bool UNUSED(global))
+{
+ switch (msg.GetType())
{
- return "";
- }
-
- void Init(const CParamNode& UNUSED(paramNode)) override
+ case MT_Deserialized:
{
- m_QueryNext = 1;
-
- m_DebugOverlayEnabled = false;
- m_DebugOverlayDirty = true;
-
+ // Reinitialize subdivisions and LOS data after all
+ // other components have been deserialized.
+ m_Deserializing = true;
+ ResetDerivedData();
m_Deserializing = false;
- m_WorldX0 = m_WorldZ0 = m_WorldX1 = m_WorldZ1 = entity_pos_t::Zero();
-
- // Initialise with bogus values (these will get replaced when
- // SetBounds is called)
- ResetSubdivisions(entity_pos_t::FromInt(1024), entity_pos_t::FromInt(1024));
-
- m_SubdivisionResults.reserve(4096);
-
- // The whole map should be visible to Gaia by default, else e.g. animals
- // will get confused when trying to run from enemies
- m_LosRevealAll[0] = true;
-
- m_GlobalVisibilityUpdate = true;
-
- m_LosCircular = false;
- m_LosVerticesPerSide = 0;
+ break;
}
-
- void Deinit() override
- {
- }
-
- template
- void SerializeCommon(S& serialize)
+ case MT_Create:
{
- serialize.NumberFixed_Unbounded("world x0", m_WorldX0);
- serialize.NumberFixed_Unbounded("world z0", m_WorldZ0);
- serialize.NumberFixed_Unbounded("world x1", m_WorldX1);
- serialize.NumberFixed_Unbounded("world z1", m_WorldZ1);
-
- serialize.NumberU32_Unbounded("query next", m_QueryNext);
- Serializer(serialize, "queries", m_Queries, GetSimContext());
- Serializer(serialize, "entity data", m_EntityData);
-
- Serializer(serialize, "los reveal all", m_LosRevealAll);
- serialize.Bool("los circular", m_LosCircular);
- serialize.NumberI32_Unbounded("los verts per side", m_LosVerticesPerSide);
-
- serialize.Bool("global visibility update", m_GlobalVisibilityUpdate);
- Serializer(serialize, "global player visibility update", m_GlobalPlayerVisibilityUpdate);
- Serializer(serialize, "dirty visibility", m_DirtyVisibility);
- Serializer(serialize, "modified entities", m_ModifiedEntities);
-
- // We don't serialize m_Subdivision, m_LosPlayerCounts or m_LosRegions
- // since they can be recomputed from the entity data when deserializing;
- // m_LosState must be serialized since it depends on the history of exploration
-
- Serializer(serialize, "los state", m_LosState);
- Serializer(serialize, "shared los masks", m_SharedLosMasks);
- Serializer(serialize, "shared dirty visibility masks", m_SharedDirtyVisibilityMasks);
- }
+ const CMessageCreate& msgData = static_cast (msg);
+ entity_id_t ent = msgData.entity;
- void Serialize(ISerializer& serialize) override
- {
- SerializeCommon(serialize);
- }
+ // Ignore local entities - we shouldn't let them influence anything
+ if (ENTITY_IS_LOCAL(ent))
+ break;
- void Deserialize(const CParamNode& paramNode, IDeserializer& deserialize) override
- {
- Init(paramNode);
+ // Ignore non-positional entities
+ CmpPtr cmpPosition(GetSimContext(), ent);
+ if (!cmpPosition)
+ break;
- SerializeCommon(deserialize);
- }
+ // The newly-created entity will have owner -1 and position out-of-world
+ // (any initialisation of those values will happen later), so we can just
+ // use the default-constructed EntityData here
+ EntityData entdata;
- void HandleMessage(const CMessage& msg, bool UNUSED(global)) override
- {
- switch (msg.GetType())
- {
- case MT_Deserialized:
+ // Store the LOS data, if any
+ CmpPtr cmpVision(GetSimContext(), ent);
+ if (cmpVision)
{
- // Reinitialize subdivisions and LOS data after all
- // other components have been deserialized.
- m_Deserializing = true;
- ResetDerivedData();
- m_Deserializing = false;
- break;
+ entdata.visionRange = cmpVision->GetRange();
+ entdata.SetFlag(cmpVision->GetRevealShore());
}
- case MT_Create:
- {
- const CMessageCreate& msgData = static_cast (msg);
- entity_id_t ent = msgData.entity;
-
- // Ignore local entities - we shouldn't let them influence anything
- if (ENTITY_IS_LOCAL(ent))
- break;
-
- // Ignore non-positional entities
- CmpPtr cmpPosition(GetSimContext(), ent);
- if (!cmpPosition)
- break;
-
- // The newly-created entity will have owner -1 and position out-of-world
- // (any initialisation of those values will happen later), so we can just
- // use the default-constructed EntityData here
- EntityData entdata;
-
- // Store the LOS data, if any
- CmpPtr cmpVision(GetSimContext(), ent);
- if (cmpVision)
- {
- entdata.visionRange = cmpVision->GetRange();
- entdata.SetFlag(cmpVision->GetRevealShore());
- }
- CmpPtr cmpVisibility(GetSimContext(), ent);
- if (cmpVisibility)
- entdata.SetFlag(cmpVisibility->GetRetainInFog());
+ CmpPtr cmpVisibility(GetSimContext(), ent);
+ if (cmpVisibility)
+ entdata.SetFlag(cmpVisibility->GetRetainInFog());
- // Store the size
- CmpPtr cmpObstruction(GetSimContext(), ent);
- if (cmpObstruction)
- entdata.size = cmpObstruction->GetSize().ToInt_RoundToInfinity();
+ // Store the size
+ CmpPtr cmpObstruction(GetSimContext(), ent);
+ if (cmpObstruction)
+ entdata.size = cmpObstruction->GetSize().ToInt_RoundToInfinity();
- // Remember this entity
- m_EntityData.insert(ent, entdata);
- break;
- }
- case MT_PositionChanged:
- {
- const CMessagePositionChanged& msgData = static_cast (msg);
- entity_id_t ent = msgData.entity;
+ // Remember this entity
+ m_EntityData.insert(ent, entdata);
+ break;
+ }
+ case MT_PositionChanged:
+ {
+ const CMessagePositionChanged& msgData = static_cast (msg);
+ entity_id_t ent = msgData.entity;
- EntityMap::iterator it = m_EntityData.find(ent);
+ EntityMap::iterator it = m_EntityData.find(ent);
- // Ignore if we're not already tracking this entity
- if (it == m_EntityData.end())
- break;
+ // Ignore if we're not already tracking this entity
+ if (it == m_EntityData.end())
+ break;
- if (msgData.inWorld)
+ if (msgData.inWorld)
+ {
+ if (it->second.HasFlag())
{
- if (it->second.HasFlag())
- {
- CFixedVector2D from(it->second.x, it->second.z);
- CFixedVector2D to(msgData.x, msgData.z);
- m_Subdivision.Move(ent, from, to, it->second.size);
- if (it->second.HasFlag())
- SharingLosMove(it->second.visionSharing, it->second.visionRange, from, to);
- else
- LosMove(it->second.owner, it->second.visionRange, from, to);
- LosRegion oldLosRegion = PosToLosRegionsHelper(it->second.x, it->second.z);
- LosRegion newLosRegion = PosToLosRegionsHelper(msgData.x, msgData.z);
- if (oldLosRegion != newLosRegion)
- {
- RemoveFromRegion(oldLosRegion, ent);
- AddToRegion(newLosRegion, ent);
- }
- }
+ CFixedVector2D from(it->second.x, it->second.z);
+ CFixedVector2D to(msgData.x, msgData.z);
+ m_Subdivision.Move(ent, from, to, it->second.size);
+ if (it->second.HasFlag())
+ SharingLosMove(it->second.visionSharing, it->second.visionRange, from, to);
else
+ LosMove(it->second.owner, it->second.visionRange, from, to);
+ LosRegion oldLosRegion = PosToLosRegionsHelper(it->second.x, it->second.z);
+ LosRegion newLosRegion = PosToLosRegionsHelper(msgData.x, msgData.z);
+ if (oldLosRegion != newLosRegion)
{
- CFixedVector2D to(msgData.x, msgData.z);
- m_Subdivision.Add(ent, to, it->second.size);
- if (it->second.HasFlag())
- SharingLosAdd(it->second.visionSharing, it->second.visionRange, to);
- else
- LosAdd(it->second.owner, it->second.visionRange, to);
- AddToRegion(PosToLosRegionsHelper(msgData.x, msgData.z), ent);
+ RemoveFromRegion(oldLosRegion, ent);
+ AddToRegion(newLosRegion, ent);
}
-
- it->second.SetFlag(true);
- it->second.x = msgData.x;
- it->second.z = msgData.z;
}
else
{
- if (it->second.HasFlag())
- {
- CFixedVector2D from(it->second.x, it->second.z);
- m_Subdivision.Remove(ent, from, it->second.size);
- if (it->second.HasFlag())
- SharingLosRemove(it->second.visionSharing, it->second.visionRange, from);
- else
- LosRemove(it->second.owner, it->second.visionRange, from);
- RemoveFromRegion(PosToLosRegionsHelper(it->second.x, it->second.z), ent);
- }
-
- it->second.SetFlag(false);
- it->second.x = entity_pos_t::Zero();
- it->second.z = entity_pos_t::Zero();
+ CFixedVector2D to(msgData.x, msgData.z);
+ m_Subdivision.Add(ent, to, it->second.size);
+ if (it->second.HasFlag())
+ SharingLosAdd(it->second.visionSharing, it->second.visionRange, to);
+ else
+ LosAdd(it->second.owner, it->second.visionRange, to);
+ AddToRegion(PosToLosRegionsHelper(msgData.x, msgData.z), ent);
}
- RequestVisibilityUpdate(ent);
-
- break;
+ it->second.SetFlag(true);
+ it->second.x = msgData.x;
+ it->second.z = msgData.z;
}
- case MT_OwnershipChanged:
+ else
{
- const CMessageOwnershipChanged& msgData = static_cast (msg);
- entity_id_t ent = msgData.entity;
-
- EntityMap::iterator it = m_EntityData.find(ent);
-
- // Ignore if we're not already tracking this entity
- if (it == m_EntityData.end())
- break;
-
if (it->second.HasFlag())
{
- // Entity vision is taken into account in VisionSharingChanged
- // when sharing component activated
- if (!it->second.HasFlag())
- {
- CFixedVector2D pos(it->second.x, it->second.z);
- LosRemove(it->second.owner, it->second.visionRange, pos);
- LosAdd(msgData.to, it->second.visionRange, pos);
- }
-
- if (it->second.HasFlag())
- {
- RevealShore(it->second.owner, false);
- RevealShore(msgData.to, true);
- }
+ CFixedVector2D from(it->second.x, it->second.z);
+ m_Subdivision.Remove(ent, from, it->second.size);
+ if (it->second.HasFlag())
+ SharingLosRemove(it->second.visionSharing, it->second.visionRange, from);
+ else
+ LosRemove(it->second.owner, it->second.visionRange, from);
+ RemoveFromRegion(PosToLosRegionsHelper(it->second.x, it->second.z), ent);
}
- ENSURE(-128 <= msgData.to && msgData.to <= 127);
- it->second.owner = (i8)msgData.to;
-
- break;
+ it->second.SetFlag(false);
+ it->second.x = entity_pos_t::Zero();
+ it->second.z = entity_pos_t::Zero();
}
- case MT_Destroy:
- {
- const CMessageDestroy& msgData = static_cast (msg);
- entity_id_t ent = msgData.entity;
- EntityMap::iterator it = m_EntityData.find(ent);
+ RequestVisibilityUpdate(ent);
- // Ignore if we're not already tracking this entity
- if (it == m_EntityData.end())
- break;
+ break;
+ }
+ case MT_OwnershipChanged:
+ {
+ const CMessageOwnershipChanged& msgData = static_cast (msg);
+ entity_id_t ent = msgData.entity;
- if (it->second.HasFlag())
+ EntityMap::iterator it = m_EntityData.find(ent);
+
+ // Ignore if we're not already tracking this entity
+ if (it == m_EntityData.end())
+ break;
+
+ if (it->second.HasFlag())
+ {
+ // Entity vision is taken into account in VisionSharingChanged
+ // when sharing component activated
+ if (!it->second.HasFlag())
{
- m_Subdivision.Remove(ent, CFixedVector2D(it->second.x, it->second.z), it->second.size);
- RemoveFromRegion(PosToLosRegionsHelper(it->second.x, it->second.z), ent);
+ CFixedVector2D pos(it->second.x, it->second.z);
+ LosRemove(it->second.owner, it->second.visionRange, pos);
+ LosAdd(msgData.to, it->second.visionRange, pos);
}
- // This will be called after Ownership's OnDestroy, so ownership will be set
- // to -1 already and we don't have to do a LosRemove here
- ENSURE(it->second.owner == -1);
+ if (it->second.HasFlag())
+ {
+ RevealShore(it->second.owner, false);
+ RevealShore(msgData.to, true);
+ }
+ }
- m_EntityData.erase(it);
+ ENSURE(-128 <= msgData.to && msgData.to <= 127);
+ it->second.owner = (i8)msgData.to;
- break;
- }
- case MT_VisionRangeChanged:
- {
- const CMessageVisionRangeChanged& msgData = static_cast (msg);
- entity_id_t ent = msgData.entity;
+ break;
+ }
+ case MT_Destroy:
+ {
+ const CMessageDestroy& msgData = static_cast (msg);
+ entity_id_t ent = msgData.entity;
- EntityMap::iterator it = m_EntityData.find(ent);
+ EntityMap::iterator it = m_EntityData.find(ent);
- // Ignore if we're not already tracking this entity
- if (it == m_EntityData.end())
- break;
+ // Ignore if we're not already tracking this entity
+ if (it == m_EntityData.end())
+ break;
- CmpPtr cmpVision(GetSimContext(), ent);
- if (!cmpVision)
- break;
+ if (it->second.HasFlag())
+ {
+ m_Subdivision.Remove(ent, CFixedVector2D(it->second.x, it->second.z), it->second.size);
+ RemoveFromRegion(PosToLosRegionsHelper(it->second.x, it->second.z), ent);
+ }
- entity_pos_t oldRange = it->second.visionRange;
- entity_pos_t newRange = msgData.newRange;
+ // This will be called after Ownership's OnDestroy, so ownership will be set
+ // to -1 already and we don't have to do a LosRemove here
+ ENSURE(it->second.owner == -1);
- // If the range changed and the entity's in-world, we need to manually adjust it
- // but if it's not in-world, we only need to set the new vision range
+ m_EntityData.erase(it);
- it->second.visionRange = newRange;
+ break;
+ }
+ case MT_VisionRangeChanged:
+ {
+ const CMessageVisionRangeChanged& msgData = static_cast(msg);
+ entity_id_t ent = msgData.entity;
- if (it->second.HasFlag())
- {
- CFixedVector2D pos(it->second.x, it->second.z);
- if (it->second.HasFlag())
- {
- SharingLosRemove(it->second.visionSharing, oldRange, pos);
- SharingLosAdd(it->second.visionSharing, newRange, pos);
- }
- else
- {
- LosRemove(it->second.owner, oldRange, pos);
- LosAdd(it->second.owner, newRange, pos);
- }
- }
+ EntityMap::iterator it = m_EntityData.find(ent);
+ // Ignore if we're not already tracking this entity
+ if (it == m_EntityData.end())
+ break;
+
+ CmpPtr cmpVision(GetSimContext(), ent);
+ if (!cmpVision)
break;
- }
- case MT_VisionSharingChanged:
- {
- const CMessageVisionSharingChanged& msgData = static_cast (msg);
- entity_id_t ent = msgData.entity;
- EntityMap::iterator it = m_EntityData.find(ent);
+ entity_pos_t oldRange = it->second.visionRange;
+ entity_pos_t newRange = msgData.newRange;
- // Ignore if we're not already tracking this entity
- if (it == m_EntityData.end())
- break;
+ // If the range changed and the entity's in-world, we need to manually adjust it
+ // but if it's not in-world, we only need to set the new vision range
- ENSURE(msgData.player > 0 && msgData.player < MAX_LOS_PLAYER_ID+1);
- u16 visionChanged = CalcVisionSharingMask(msgData.player);
+ it->second.visionRange = newRange;
- if (!it->second.HasFlag())
+ if (it->second.HasFlag())
+ {
+ CFixedVector2D pos(it->second.x, it->second.z);
+ if (it->second.HasFlag())
{
- // Activation of the Vision Sharing
- ENSURE(it->second.owner == (i8)msgData.player);
- it->second.visionSharing = visionChanged;
- it->second.SetFlag(true);
- break;
+ SharingLosRemove(it->second.visionSharing, oldRange, pos);
+ SharingLosAdd(it->second.visionSharing, newRange, pos);
}
-
- if (it->second.HasFlag())
+ else
{
- entity_pos_t range = it->second.visionRange;
- CFixedVector2D pos(it->second.x, it->second.z);
- if (msgData.add)
- LosAdd(msgData.player, range, pos);
- else
- LosRemove(msgData.player, range, pos);
+ LosRemove(it->second.owner, oldRange, pos);
+ LosAdd(it->second.owner, newRange, pos);
}
+ }
- if (msgData.add)
- it->second.visionSharing |= visionChanged;
- else
- it->second.visionSharing &= ~visionChanged;
+ break;
+ }
+ case MT_VisionSharingChanged:
+ {
+ const auto& msgData = static_cast(msg);
+ entity_id_t ent = msgData.entity;
+
+ EntityMap::iterator it = m_EntityData.find(ent);
+
+ // Ignore if we're not already tracking this entity
+ if (it == m_EntityData.end())
break;
- }
- case MT_Update:
+
+ ENSURE(msgData.player > 0 && msgData.player < MAX_LOS_PLAYER_ID+1);
+ u16 visionChanged = CalcVisionSharingMask(msgData.player);
+
+ if (!it->second.HasFlag())
{
- m_DebugOverlayDirty = true;
- ExecuteActiveQueries();
- UpdateVisibilityData();
+ // Activation of the Vision Sharing
+ ENSURE(it->second.owner == (i8)msgData.player);
+ it->second.visionSharing = visionChanged;
+ it->second.SetFlag(true);
break;
}
- case MT_RenderSubmit:
+
+ if (it->second.HasFlag())
{
- const CMessageRenderSubmit& msgData = static_cast (msg);
- RenderSubmit(msgData.collector);
- break;
- }
+ entity_pos_t range = it->second.visionRange;
+ CFixedVector2D pos(it->second.x, it->second.z);
+ if (msgData.add)
+ LosAdd(msgData.player, range, pos);
+ else
+ LosRemove(msgData.player, range, pos);
}
- }
- void SetBounds(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1) override
+ if (msgData.add)
+ it->second.visionSharing |= visionChanged;
+ else
+ it->second.visionSharing &= ~visionChanged;
+ break;
+ }
+ case MT_Update:
{
- // Don't support rectangular looking maps.
- ENSURE(x1-x0 == z1-z0);
- m_WorldX0 = x0;
- m_WorldZ0 = z0;
- m_WorldX1 = x1;
- m_WorldZ1 = z1;
- m_LosVerticesPerSide = ((x1 - x0) / LOS_TILE_SIZE).ToInt_RoundToZero() + 1;
-
- ResetDerivedData();
+ m_DebugOverlayDirty = true;
+ ExecuteActiveQueries();
+ UpdateVisibilityData();
+ break;
}
-
- void Verify() override
+ case MT_RenderSubmit:
{
- // Ignore if map not initialised yet
- if (m_WorldX1.IsZero())
- return;
+ const CMessageRenderSubmit& msgData = static_cast (msg);
+ RenderSubmit(msgData.collector);
+ break;
+ }
+ }
+}
+
+void CCmpRangeManager::SetBounds(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1)
+{
+ // Don't support rectangular looking maps.
+ ENSURE(x1-x0 == z1-z0);
+ m_WorldX0 = x0;
+ m_WorldZ0 = z0;
+ m_WorldX1 = x1;
+ m_WorldZ1 = z1;
+ m_LosVerticesPerSide = ((x1 - x0) / LOS_TILE_SIZE).ToInt_RoundToZero() + 1;
+
+ ResetDerivedData();
+}
- // Check that calling ResetDerivedData (i.e. recomputing all the state from scratch)
- // does not affect the incrementally-computed state
+void CCmpRangeManager::Verify()
+{
+ // Ignore if map not initialised yet
+ if (m_WorldX1.IsZero())
+ return;
- std::array