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"); }