diff --git a/binaries/data/mods/public/simulation/components/GuiInterface.js b/binaries/data/mods/public/simulation/components/GuiInterface.js
index b4137b1ae8..e56334e176 100644
--- a/binaries/data/mods/public/simulation/components/GuiInterface.js
+++ b/binaries/data/mods/public/simulation/components/GuiInterface.js
@@ -331,8 +331,10 @@ GuiInterface.prototype.SetBuildingPlacementPreview = function(player, cmd)
if (this.placementEntity)
{
+ var ent = this.placementEntity[1];
+
// Move the preview into the right location
- var pos = Engine.QueryInterface(this.placementEntity[1], IID_Position);
+ var pos = Engine.QueryInterface(ent, IID_Position);
if (pos)
{
pos.JumpTo(cmd.x, cmd.z);
@@ -340,21 +342,26 @@ GuiInterface.prototype.SetBuildingPlacementPreview = function(player, cmd)
}
// Check whether it's obstructed by other entities
- var cmpObstruction = Engine.QueryInterface(this.placementEntity[1], IID_Obstruction);
+ var cmpObstruction = Engine.QueryInterface(ent, IID_Obstruction);
var colliding = (cmpObstruction && cmpObstruction.CheckCollisions());
- // Set it to a red shade if this is an obstructed location
- var cmpVisual = Engine.QueryInterface(this.placementEntity[1], IID_Visual);
+ // Check whether it's in a visible region
+ var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
+ var visible = (cmpRangeManager.GetLosVisibility(ent, player) == "visible");
+
+ var ok = (!colliding && visible);
+
+ // Set it to a red shade if this is an invalid location
+ var cmpVisual = Engine.QueryInterface(ent, IID_Visual);
if (cmpVisual)
{
- if (colliding)
+ if (!ok)
cmpVisual.SetShadingColour(1.4, 0.4, 0.4, 1);
else
cmpVisual.SetShadingColour(1, 1, 1, 1);
}
- if (!colliding)
- return true;
+ return ok;
}
return false;
diff --git a/binaries/data/mods/public/simulation/helpers/Commands.js b/binaries/data/mods/public/simulation/helpers/Commands.js
index 8470610b97..db90e2d744 100644
--- a/binaries/data/mods/public/simulation/helpers/Commands.js
+++ b/binaries/data/mods/public/simulation/helpers/Commands.js
@@ -72,6 +72,7 @@ function ProcessCommand(player, cmd)
cmpPosition.JumpTo(cmd.x, cmd.z);
cmpPosition.SetYRotation(cmd.angle);
+ // Check whether it's obstructed by other entities
var cmpObstruction = Engine.QueryInterface(ent, IID_Obstruction);
if (cmpObstruction && cmpObstruction.CheckCollisions())
{
@@ -83,14 +84,21 @@ function ProcessCommand(player, cmd)
break;
}
+ // Check whether it's in a visible region
+ var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
+ var visible = (cmpRangeManager.GetLosVisibility(ent, player) == "visible");
+ if (!visible)
+ {
+ // TODO: report error to player (the building site was not visible)
+ Engine.DestroyEntity(ent);
+ break;
+ }
+
var cmpCost = Engine.QueryInterface(ent, IID_Cost);
if (!cmpPlayer.TrySubtractResources(cmpCost.GetResourceCosts()))
{
// TODO: report error to player (they ran out of resources)
-
- // Remove the foundation because the construction was aborted
Engine.DestroyEntity(ent);
-
break;
}
diff --git a/binaries/data/mods/public/simulation/templates/template_gaia.xml b/binaries/data/mods/public/simulation/templates/template_gaia.xml
index e0070c6c68..49b6fd1c86 100644
--- a/binaries/data/mods/public/simulation/templates/template_gaia.xml
+++ b/binaries/data/mods/public/simulation/templates/template_gaia.xml
@@ -7,6 +7,7 @@
0
true
+ false
2.0
diff --git a/binaries/data/mods/public/simulation/templates/template_structure.xml b/binaries/data/mods/public/simulation/templates/template_structure.xml
index e7313b0bc9..a376637812 100644
--- a/binaries/data/mods/public/simulation/templates/template_structure.xml
+++ b/binaries/data/mods/public/simulation/templates/template_structure.xml
@@ -52,6 +52,7 @@
36
true
+ false
diff --git a/binaries/data/mods/public/simulation/templates/template_structure_gaia_settlement.xml b/binaries/data/mods/public/simulation/templates/template_structure_gaia_settlement.xml
index 8ed49c6dc6..97495e870a 100644
--- a/binaries/data/mods/public/simulation/templates/template_structure_gaia_settlement.xml
+++ b/binaries/data/mods/public/simulation/templates/template_structure_gaia_settlement.xml
@@ -20,6 +20,8 @@
+ 0
true
+ false
diff --git a/binaries/data/mods/public/simulation/templates/template_unit.xml b/binaries/data/mods/public/simulation/templates/template_unit.xml
index 328207aa9f..0356e318ca 100644
--- a/binaries/data/mods/public/simulation/templates/template_unit.xml
+++ b/binaries/data/mods/public/simulation/templates/template_unit.xml
@@ -70,5 +70,6 @@
24
false
+ false
diff --git a/source/simulation2/components/CCmpRangeManager.cpp b/source/simulation2/components/CCmpRangeManager.cpp
index 8a1102bf5f..10677e4cb4 100644
--- a/source/simulation2/components/CCmpRangeManager.cpp
+++ b/source/simulation2/components/CCmpRangeManager.cpp
@@ -663,13 +663,17 @@ private:
{
// (We can't use m_EntityData since this needs to handle LOCAL entities too)
+ // Entities not with positions in the world are never visible
CmpPtr cmpPosition(GetSimContext(), ent);
if (cmpPosition.null() || !cmpPosition->IsInWorld())
return VIS_HIDDEN;
+ // Global flag makes all positioned entities visible
if (m_LosRevealAll)
return VIS_VISIBLE;
+ // Visible if within a visible region
+
CFixedVector2D pos = cmpPosition->GetPosition2D();
CLosQuerier los(player, m_LosState, m_TerrainVerticesPerSide);
@@ -680,6 +684,7 @@ private:
if (los.IsVisible(i, j))
return VIS_VISIBLE;
+ // Fogged if the 'retain in fog' flag is set, and in a non-visible explored region
if (los.IsExplored(i, j))
{
CmpPtr cmpVision(GetSimContext(), ent);
@@ -687,6 +692,7 @@ private:
return VIS_FOGGED;
}
+ // Otherwise not visible
return VIS_HIDDEN;
}
diff --git a/source/simulation2/components/CCmpTemplateManager.cpp b/source/simulation2/components/CCmpTemplateManager.cpp
index e5a2b7f2f6..87d7ce6dc1 100644
--- a/source/simulation2/components/CCmpTemplateManager.cpp
+++ b/source/simulation2/components/CCmpTemplateManager.cpp
@@ -435,8 +435,8 @@ void CCmpTemplateManager::CopyPreviewSubset(CParamNode& out, const CParamNode& i
if (!corpse)
{
- // Previews should always be visible in fog-of-war
- CParamNode::LoadXMLString(out, "0true");
+ // Previews should always be visible in fog-of-war/etc
+ CParamNode::LoadXMLString(out, "0falsetrue");
}
if (corpse)
diff --git a/source/simulation2/components/CCmpVision.cpp b/source/simulation2/components/CCmpVision.cpp
index 6f153b8eea..47fd194be9 100644
--- a/source/simulation2/components/CCmpVision.cpp
+++ b/source/simulation2/components/CCmpVision.cpp
@@ -31,6 +31,7 @@ public:
entity_pos_t m_Range;
bool m_RetainInFog;
+ bool m_AlwaysVisible;
static std::string GetSchema()
{
@@ -40,6 +41,9 @@ public:
""
""
""
+ ""
+ ""
+ ""
"";
}
@@ -47,6 +51,7 @@ public:
{
m_Range = paramNode.GetChild("Range").ToFixed();
m_RetainInFog = paramNode.GetChild("RetainInFog").ToBool();
+ m_AlwaysVisible = paramNode.GetChild("AlwaysVisible").ToBool();
}
virtual void Deinit(const CSimContext& UNUSED(context))
@@ -71,6 +76,11 @@ public:
{
return m_RetainInFog;
}
+
+ virtual bool GetAlwaysVisible()
+ {
+ return m_AlwaysVisible;
+ }
};
REGISTER_COMPONENT_TYPE(Vision)
diff --git a/source/simulation2/components/CCmpVisualActor.cpp b/source/simulation2/components/CCmpVisualActor.cpp
index 20705b6bfb..eb5bbefe1c 100644
--- a/source/simulation2/components/CCmpVisualActor.cpp
+++ b/source/simulation2/components/CCmpVisualActor.cpp
@@ -54,6 +54,8 @@ public:
std::wstring m_ActorName;
CUnit* m_Unit;
+ fixed m_R, m_G, m_B; // shading colour
+
ICmpRangeManager::ELosVisibility m_Visibility; // only valid between Interpolate and RenderSubmit
// Current animation state
@@ -106,6 +108,8 @@ public:
else
m_ActorName = paramNode.GetChild("Actor").ToString();
+ m_R = m_G = m_B = fixed::FromInt(1);
+
std::set selections;
m_Unit = context.GetUnitManager().CreateUnit(m_ActorName, selections);
if (!m_Unit)
@@ -280,10 +284,10 @@ public:
virtual void SetShadingColour(fixed r, fixed g, fixed b, fixed a)
{
- if (!m_Unit)
- return;
-
- m_Unit->GetModel().SetShadingColor(CColor(r.ToFloat(), g.ToFloat(), b.ToFloat(), a.ToFloat()));
+ m_R = r;
+ m_G = g;
+ m_B = b;
+ UNUSED2(a); // TODO: why is this even an argument?
}
virtual void Hotload(const std::wstring& name)
@@ -368,8 +372,18 @@ void CCmpVisualActor::Interpolate(float frameTime, float frameOffset)
return;
}
- CmpPtr cmpRangeManager(GetSimContext(), SYSTEM_ENTITY);
- m_Visibility = cmpRangeManager->GetLosVisibility(GetEntityId(), GetSimContext().GetCurrentDisplayedPlayer());
+ // The 'always visible' flag means we should always render the unit
+ // (regardless of whether the LOS system thinks it's visible)
+ CmpPtr cmpVision(GetSimContext(), GetEntityId());
+ if (!cmpVision.null() && cmpVision->GetAlwaysVisible())
+ {
+ m_Visibility = ICmpRangeManager::VIS_VISIBLE;
+ }
+ else
+ {
+ CmpPtr cmpRangeManager(GetSimContext(), SYSTEM_ENTITY);
+ m_Visibility = cmpRangeManager->GetLosVisibility(GetEntityId(), GetSimContext().GetCurrentDisplayedPlayer());
+ }
// Even if HIDDEN due to LOS, we need to set up the transforms
// so that projectiles will be launched from the right place
@@ -378,8 +392,21 @@ void CCmpVisualActor::Interpolate(float frameTime, float frameOffset)
CMatrix3D transform(cmpPosition->GetInterpolatedTransform(frameOffset, floating));
- m_Unit->GetModel().SetTransform(transform);
+ CModel& model = m_Unit->GetModel();
+
+ model.SetTransform(transform);
m_Unit->UpdateModel(frameTime);
+
+ // If not hidden, then we need to set up some extra state for rendering
+ if (m_Visibility != ICmpRangeManager::VIS_HIDDEN)
+ {
+ model.ValidatePosition();
+
+ if (m_Visibility == ICmpRangeManager::VIS_FOGGED)
+ model.SetShadingColor(CColor(0.7f * m_R.ToFloat(), 0.7f * m_G.ToFloat(), 0.7f * m_B.ToFloat(), 1.0f));
+ else
+ model.SetShadingColor(CColor(m_R.ToFloat(), m_G.ToFloat(), m_B.ToFloat(), 1.0f));
+ }
}
void CCmpVisualActor::RenderSubmit(SceneCollector& collector, const CFrustum& frustum, bool culling)
@@ -392,15 +419,8 @@ void CCmpVisualActor::RenderSubmit(SceneCollector& collector, const CFrustum& fr
CModel& model = m_Unit->GetModel();
- model.ValidatePosition();
-
if (culling && !frustum.IsBoxVisible(CVector3D(0, 0, 0), model.GetBounds()))
return;
- if (m_Visibility == ICmpRangeManager::VIS_FOGGED)
- model.SetShadingColor(CColor(0.7f, 0.7f, 0.7f, 1.0f));
- else
- model.SetShadingColor(CColor(1.0f, 1.0f, 1.0f, 1.0f));
-
collector.SubmitRecursive(&model);
}
diff --git a/source/simulation2/components/ICmpRangeManager.cpp b/source/simulation2/components/ICmpRangeManager.cpp
index 746d70bf73..d5303dd0b9 100644
--- a/source/simulation2/components/ICmpRangeManager.cpp
+++ b/source/simulation2/components/ICmpRangeManager.cpp
@@ -21,6 +21,18 @@
#include "simulation2/system/InterfaceScripted.h"
+std::string ICmpRangeManager::GetLosVisibility_wrapper(entity_id_t ent, int player)
+{
+ ELosVisibility visibility = GetLosVisibility(ent, player);
+ switch (visibility)
+ {
+ case VIS_HIDDEN: return "hidden";
+ case VIS_FOGGED: return "fogged";
+ case VIS_VISIBLE: return "visible";
+ default: return "error"; // should never happen
+ }
+}
+
BEGIN_INTERFACE_WRAPPER(RangeManager)
DEFINE_INTERFACE_METHOD_4("ExecuteQuery", std::vector, ICmpRangeManager, ExecuteQuery, entity_id_t, entity_pos_t, std::vector, int)
DEFINE_INTERFACE_METHOD_4("CreateActiveQuery", ICmpRangeManager::tag_t, ICmpRangeManager, CreateActiveQuery, entity_id_t, entity_pos_t, std::vector, int)
@@ -31,4 +43,5 @@ DEFINE_INTERFACE_METHOD_1("ResetActiveQuery", std::vector, ICmpRang
DEFINE_INTERFACE_METHOD_1("GetEntitiesByPlayer", std::vector, ICmpRangeManager, GetEntitiesByPlayer, int)
DEFINE_INTERFACE_METHOD_1("SetDebugOverlay", void, ICmpRangeManager, SetDebugOverlay, bool)
DEFINE_INTERFACE_METHOD_1("SetLosRevealAll", void, ICmpRangeManager, SetLosRevealAll, bool)
+DEFINE_INTERFACE_METHOD_2("GetLosVisibility", std::string, ICmpRangeManager, GetLosVisibility_wrapper, entity_id_t, int)
END_INTERFACE_WRAPPER(RangeManager)
diff --git a/source/simulation2/components/ICmpRangeManager.h b/source/simulation2/components/ICmpRangeManager.h
index ed24f0a97a..e2946a3887 100644
--- a/source/simulation2/components/ICmpRangeManager.h
+++ b/source/simulation2/components/ICmpRangeManager.h
@@ -228,6 +228,12 @@ public:
*/
virtual ELosVisibility GetLosVisibility(entity_id_t ent, int player) = 0;
+ /**
+ * GetLosVisibility wrapped for script calls.
+ * Returns "hidden", "fogged" or "visible".
+ */
+ std::string GetLosVisibility_wrapper(entity_id_t ent, int player);
+
/**
* Set globally whether the whole map should be made visible.
*/
diff --git a/source/simulation2/components/ICmpVision.cpp b/source/simulation2/components/ICmpVision.cpp
index dcd1604682..a1361153b3 100644
--- a/source/simulation2/components/ICmpVision.cpp
+++ b/source/simulation2/components/ICmpVision.cpp
@@ -24,4 +24,5 @@
BEGIN_INTERFACE_WRAPPER(Vision)
DEFINE_INTERFACE_METHOD_0("GetRange", entity_pos_t, ICmpVision, GetRange)
DEFINE_INTERFACE_METHOD_0("GetRetainInFog", bool, ICmpVision, GetRetainInFog)
+DEFINE_INTERFACE_METHOD_0("GetAlwaysVisible", bool, ICmpVision, GetAlwaysVisible)
END_INTERFACE_WRAPPER(Vision)
diff --git a/source/simulation2/components/ICmpVision.h b/source/simulation2/components/ICmpVision.h
index 2f4174cbd6..02bd9fa3b8 100644
--- a/source/simulation2/components/ICmpVision.h
+++ b/source/simulation2/components/ICmpVision.h
@@ -32,6 +32,8 @@ public:
virtual bool GetRetainInFog() = 0;
+ virtual bool GetAlwaysVisible() = 0;
+
DECLARE_INTERFACE_TYPE(Vision)
};
diff --git a/source/simulation2/tests/test_CmpTemplateManager.h b/source/simulation2/tests/test_CmpTemplateManager.h
index 8352f8ca20..17fd919202 100644
--- a/source/simulation2/tests/test_CmpTemplateManager.h
+++ b/source/simulation2/tests/test_CmpTemplateManager.h
@@ -83,7 +83,7 @@ public:
TS_ASSERT(preview != NULL);
TS_ASSERT_WSTR_EQUALS(preview->ToXML(),
L"0uprightfalse"
- L"0true"
+ L"true0false"
L"example");
const CParamNode* previewobstruct = tempMan->LoadTemplate(ent2, "preview|unitobstruct", -1);
@@ -92,14 +92,14 @@ public:
L"1.0"
L""
L"0uprightfalse"
- L"0true"
+ L"true0false"
L"example");
const CParamNode* previewactor = tempMan->LoadTemplate(ent2, "preview|actor|example2", -1);
TS_ASSERT(previewactor != NULL);
TS_ASSERT_WSTR_EQUALS(previewactor->ToXML(),
L"0uprightfalse"
- L"0true"
+ L"true0false"
L"example2");
}