diff --git a/binaries/data/mods/public/gui/session/selection_details.js b/binaries/data/mods/public/gui/session/selection_details.js
index 32a2a81d96..482682ec88 100644
--- a/binaries/data/mods/public/gui/session/selection_details.js
+++ b/binaries/data/mods/public/gui/session/selection_details.js
@@ -164,7 +164,7 @@ function displaySingle(entState, template)
Engine.GetGUIObjectByName("resourceCarryingIcon").tooltip = sprintf(translate("Gain: %(amount)s"), { amount: getTradingTooltip(entState.trader.goods.amount) });
}
// And for number of workers
- else if (entState.foundation && !entState.mirage)
+ else if (entState.foundation && entState.visibility == "visible")
{
Engine.GetGUIObjectByName("resourceCarryingIcon").hidden = false;
Engine.GetGUIObjectByName("resourceCarryingText").hidden = false;
@@ -172,7 +172,7 @@ function displaySingle(entState, template)
Engine.GetGUIObjectByName("resourceCarryingText").caption = entState.foundation.numBuilders + " ";
Engine.GetGUIObjectByName("resourceCarryingIcon").tooltip = sprintf(translate("Number of builders.\nTasking another to this foundation would speed construction up by %(numb)s%%"), { numb : Math.round((Math.pow((entState.foundation.numBuilders+1)/entState.foundation.numBuilders, 0.7) - 1.0)*100) });
}
- else if (entState.resourceSupply && (!entState.resourceSupply.killBeforeGather || !entState.hitpoints) && !entState.mirage)
+ else if (entState.resourceSupply && (!entState.resourceSupply.killBeforeGather || !entState.hitpoints) && entState.visibility == "visible")
{
Engine.GetGUIObjectByName("resourceCarryingIcon").hidden = false;
Engine.GetGUIObjectByName("resourceCarryingText").hidden = false;
diff --git a/binaries/data/mods/public/simulation/components/Fogging.js b/binaries/data/mods/public/simulation/components/Fogging.js
index a8647f2534..53003e3725 100644
--- a/binaries/data/mods/public/simulation/components/Fogging.js
+++ b/binaries/data/mods/public/simulation/components/Fogging.js
@@ -5,17 +5,18 @@ const VIS_VISIBLE = 2;
function Fogging() {}
Fogging.prototype.Schema =
- "Allows this entity to be replaced by mirages entities in the fog-of-war." +
+ "Allows this entity to be replaced by mirage entities in the fog-of-war." +
"";
Fogging.prototype.Init = function()
{
+ this.activated = false;
this.mirages = [];
this.miraged = [];
this.seen = [];
- var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
- for (var player = 0; player < cmpPlayerManager.GetNumPlayers(); ++player)
+ let cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
+ for (let player = 0; player < cmpPlayerManager.GetNumPlayers(); ++player)
{
this.mirages.push(INVALID_ENTITY);
this.miraged.push(false);
@@ -23,8 +24,36 @@ Fogging.prototype.Init = function()
}
};
+Fogging.prototype.Activate = function()
+{
+ let mustUpdate = !this.activated;
+ this.activated = true;
+
+ if (mustUpdate)
+ {
+ // Load a mirage for each player who has already seen the entity
+ let cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
+ for (let player = 0; player < cmpPlayerManager.GetNumPlayers(); ++player)
+ {
+ if (this.seen[player])
+ this.LoadMirage(player);
+ }
+ }
+};
+
+Fogging.prototype.IsActivated = function()
+{
+ return this.activated;
+}
+
Fogging.prototype.LoadMirage = function(player)
{
+ if (!this.activated)
+ {
+ error("LoadMirage called for an entity with fogging deactivated");
+ return;
+ }
+
this.miraged[player] = true;
if (this.mirages[player] == INVALID_ENTITY)
@@ -108,10 +137,17 @@ Fogging.prototype.LoadMirage = function(player)
// Notify the GUI the entity has been replaced by a mirage, in case it is selected at this moment
var cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
cmpGuiInterface.AddMiragedEntity(player, this.entity, this.mirages[player]);
+
+ // Notify the range manager the visibility of this entity must be updated
+ let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
+ cmpRangeManager.RequestVisibilityUpdate(this.entity);
};
Fogging.prototype.ForceMiraging = function(player)
{
+ if (!this.activated)
+ return;
+
this.seen[player] = true;
this.LoadMirage(player);
};
@@ -140,21 +176,6 @@ Fogging.prototype.WasSeen = function(player)
return this.seen[player];
};
-Fogging.prototype.OnVisibilityChanged = function(msg)
-{
- if (msg.player >= this.mirages.length)
- return;
-
- if (msg.newVisibility == VIS_VISIBLE)
- {
- this.miraged[msg.player] = false;
- this.seen[msg.player] = true;
- }
-
- if (msg.newVisibility == VIS_FOGGED)
- this.LoadMirage(msg.player);
-};
-
Fogging.prototype.OnDestroy = function(msg)
{
for (var player = 0; player < this.mirages.length; ++player)
@@ -172,4 +193,26 @@ Fogging.prototype.OnDestroy = function(msg)
}
};
+Fogging.prototype.OnOwnershipChanged = function(msg)
+{
+ // Always activate fogging for non-Gaia entities
+ if (msg.to > 0)
+ this.Activate();
+};
+
+Fogging.prototype.OnVisibilityChanged = function(msg)
+{
+ if (msg.player >= this.mirages.length)
+ return;
+
+ if (msg.newVisibility == VIS_VISIBLE)
+ {
+ this.miraged[msg.player] = false;
+ this.seen[msg.player] = true;
+ }
+
+ if (msg.newVisibility == VIS_FOGGED && this.activated)
+ this.LoadMirage(msg.player);
+};
+
Engine.RegisterComponentType(IID_Fogging, "Fogging", Fogging);
diff --git a/binaries/data/mods/public/simulation/components/Health.js b/binaries/data/mods/public/simulation/components/Health.js
index c310cfa5b3..568be9bcad 100644
--- a/binaries/data/mods/public/simulation/components/Health.js
+++ b/binaries/data/mods/public/simulation/components/Health.js
@@ -70,6 +70,11 @@ Health.prototype.SetHitpoints = function(value)
if (this.hitpoints == 0)
return;
+ // Before changing the value, activate Fogging if necessary to hide changes
+ let cmpFogging = Engine.QueryInterface(this.entity, IID_Fogging);
+ if (cmpFogging)
+ cmpFogging.Activate();
+
var old = this.hitpoints;
this.hitpoints = Math.max(1, Math.min(this.GetMaxHitpoints(), value));
@@ -150,6 +155,11 @@ Health.prototype.Kill = function()
*/
Health.prototype.Reduce = function(amount)
{
+ // Before changing the value, activate Fogging if necessary to hide changes
+ let cmpFogging = Engine.QueryInterface(this.entity, IID_Fogging);
+ if (cmpFogging)
+ cmpFogging.Activate();
+
var state = { "killed": false };
if (amount >= 0 && this.hitpoints == this.GetMaxHitpoints())
{
@@ -208,6 +218,11 @@ Health.prototype.Reduce = function(amount)
Health.prototype.Increase = function(amount)
{
+ // Before changing the value, activate Fogging if necessary to hide changes
+ let cmpFogging = Engine.QueryInterface(this.entity, IID_Fogging);
+ if (cmpFogging)
+ cmpFogging.Activate();
+
if (this.hitpoints == this.GetMaxHitpoints())
return {"old": this.hitpoints, "new":this.hitpoints};
diff --git a/binaries/data/mods/public/simulation/components/ResourceSupply.js b/binaries/data/mods/public/simulation/components/ResourceSupply.js
index d239b08e09..a0008e5424 100644
--- a/binaries/data/mods/public/simulation/components/ResourceSupply.js
+++ b/binaries/data/mods/public/simulation/components/ResourceSupply.js
@@ -97,6 +97,11 @@ ResourceSupply.prototype.GetDiminishingReturns = function()
ResourceSupply.prototype.TakeResources = function(rate)
{
+ // Before changing the amount, activate Fogging if necessary to hide changes
+ let cmpFogging = Engine.QueryInterface(this.entity, IID_Fogging);
+ if (cmpFogging)
+ cmpFogging.Activate();
+
if (this.infinite)
return { "amount": rate, "exhausted": false };
diff --git a/binaries/data/mods/public/simulation/components/StatusBars.js b/binaries/data/mods/public/simulation/components/StatusBars.js
index 8a4ac7b3f0..7b8bde3ba8 100644
--- a/binaries/data/mods/public/simulation/components/StatusBars.js
+++ b/binaries/data/mods/public/simulation/components/StatusBars.js
@@ -101,23 +101,23 @@ StatusBars.prototype.RegenerateSprites = function()
yoffset -= height * 1.2;
};
+ var cmpMirage = Engine.QueryInterface(this.entity, IID_Mirage);
+
var cmpPack = Engine.QueryInterface(this.entity, IID_Pack);
if (cmpPack && cmpPack.IsPacking())
- {
AddBar("pack", cmpPack.GetProgress());
- }
var cmpHealth = Engine.QueryInterface(this.entity, IID_Health);
if (cmpHealth && cmpHealth.GetHitpoints() > 0)
- {
AddBar("health", cmpHealth.GetHitpoints() / cmpHealth.GetMaxHitpoints());
- }
+ else if (cmpMirage && cmpMirage.Health())
+ AddBar("health", cmpMirage.GetHitpoints() / cmpMirage.GetMaxHitpoints());
var cmpResourceSupply = Engine.QueryInterface(this.entity, IID_ResourceSupply);
if (cmpResourceSupply)
- {
AddBar("supply", cmpResourceSupply.IsInfinite() ? 1 : cmpResourceSupply.GetCurrentAmount() / cmpResourceSupply.GetMaxAmount());
- }
+ else if (cmpMirage && cmpMirage.ResourceSupply())
+ AddBar("supply", cmpMirage.IsInfinite() ? 1 : cmpMirage.GetAmount() / cmpMirage.GetMaxAmount());
/*
// Rank icon disabled for now - see discussion around
diff --git a/source/ps/TemplateLoader.cpp b/source/ps/TemplateLoader.cpp
index 3eb71ced3c..604ca27784 100644
--- a/source/ps/TemplateLoader.cpp
+++ b/source/ps/TemplateLoader.cpp
@@ -433,8 +433,11 @@ void CTemplateLoader::CopyMirageSubset(CParamNode& out, const CParamNode& in)
permittedComponentTypes.insert("Footprint");
permittedComponentTypes.insert("Minimap");
permittedComponentTypes.insert("Ownership");
+ permittedComponentTypes.insert("OverlayRenderer");
permittedComponentTypes.insert("Position");
permittedComponentTypes.insert("Selectable");
+ permittedComponentTypes.insert("StatusBars");
+ permittedComponentTypes.insert("Visibility");
permittedComponentTypes.insert("VisualActor");
CParamNode::LoadXMLString(out, "");
@@ -456,7 +459,6 @@ void CTemplateLoader::CopyMirageSubset(CParamNode& out, const CParamNode& in)
// Set the entity as mirage entity
CParamNode::LoadXMLString(out, "");
- CParamNode::LoadXMLString(out, "truefalsefalse");
}
void CTemplateLoader::CopyFoundationSubset(CParamNode& out, const CParamNode& in)
diff --git a/source/simulation2/components/CCmpRangeManager.cpp b/source/simulation2/components/CCmpRangeManager.cpp
index e9f5511d5c..fb7c6f25d3 100644
--- a/source/simulation2/components/CCmpRangeManager.cpp
+++ b/source/simulation2/components/CCmpRangeManager.cpp
@@ -1454,12 +1454,15 @@ public:
return VIS_HIDDEN;
}
- // Fogged entities must not disappear while the mirage is not ready
+ // Fogged entities are hidden in two cases:
+ // - They were not scouted
+ // - A mirage replaces them
CmpPtr cmpFogging(ent);
- if (cmpFogging && cmpFogging->WasSeen(player) && !cmpFogging->IsMiraged(player))
- return VIS_FOGGED;
+ if (cmpFogging && cmpFogging->IsActivated() &&
+ (!cmpFogging->WasSeen(player) || cmpFogging->IsMiraged(player)))
+ return VIS_HIDDEN;
- return VIS_HIDDEN;
+ return VIS_FOGGED;
}
ELosVisibility ComputeLosVisibility(entity_id_t ent, player_id_t player)
@@ -1557,13 +1560,21 @@ public:
for (std::vector::iterator it = m_ModifiedEntities.begin(); it != m_ModifiedEntities.end(); ++it)
{
- UpdateVisibility(*it);
+ // Don't bother updating if we already did it in a global update
+ if (!m_GlobalVisibilityUpdate)
+ UpdateVisibility(*it);
}
m_ModifiedEntities.clear();
m_GlobalVisibilityUpdate = false;
}
+ virtual void RequestVisibilityUpdate(entity_id_t ent)
+ {
+ if (std::find(m_ModifiedEntities.begin(), m_ModifiedEntities.end(), ent) == m_ModifiedEntities.end())
+ m_ModifiedEntities.push_back(ent);
+ }
+
void UpdateVisibility(entity_id_t ent)
{
// Warning: Code related to fogging (like posting VisibilityChanged messages)
@@ -1594,10 +1605,6 @@ public:
{
if (oldVisibilities[player-1] == newVisibilities[player-1])
continue;
-
- // Another visibility update can be necessary to take in account new mirages
- if (std::find(m_ModifiedEntities.begin(), m_ModifiedEntities.end(), ent) == m_ModifiedEntities.end())
- m_ModifiedEntities.push_back(ent);
CMessageVisibilityChanged msg(player, ent, oldVisibilities[player-1], newVisibilities[player-1]);
GetSimContext().GetComponentManager().PostMessage(ent, msg);
diff --git a/source/simulation2/components/ICmpFogging.cpp b/source/simulation2/components/ICmpFogging.cpp
index 083e8bede3..ce858f4b0f 100644
--- a/source/simulation2/components/ICmpFogging.cpp
+++ b/source/simulation2/components/ICmpFogging.cpp
@@ -1,4 +1,4 @@
-/* Copyright (C) 2014 Wildfire Games.
+/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -30,6 +30,11 @@ class CCmpFoggingScripted : public ICmpFogging
public:
DEFAULT_SCRIPT_WRAPPER(FoggingScripted)
+ virtual bool IsActivated()
+ {
+ return m_Script.Call("IsActivated");
+ }
+
virtual bool WasSeen(player_id_t player)
{
return m_Script.Call("WasSeen", player);
diff --git a/source/simulation2/components/ICmpFogging.h b/source/simulation2/components/ICmpFogging.h
index e8ea51a3e0..cde8d4d902 100644
--- a/source/simulation2/components/ICmpFogging.h
+++ b/source/simulation2/components/ICmpFogging.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2014 Wildfire Games.
+/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -30,6 +30,7 @@
class ICmpFogging : public IComponent
{
public:
+ virtual bool IsActivated() = 0;
virtual bool WasSeen(player_id_t player) = 0;
virtual bool IsMiraged(player_id_t player) = 0;
virtual void ForceMiraging(player_id_t player) = 0;
diff --git a/source/simulation2/components/ICmpRangeManager.cpp b/source/simulation2/components/ICmpRangeManager.cpp
index dacb19f9fb..fc678babc4 100644
--- a/source/simulation2/components/ICmpRangeManager.cpp
+++ b/source/simulation2/components/ICmpRangeManager.cpp
@@ -52,6 +52,7 @@ DEFINE_INTERFACE_METHOD_2("SetLosRevealAll", void, ICmpRangeManager, SetLosRevea
DEFINE_INTERFACE_METHOD_1("GetLosRevealAll", bool, ICmpRangeManager, GetLosRevealAll, player_id_t)
DEFINE_INTERFACE_METHOD_5("GetElevationAdaptedRange", entity_pos_t, ICmpRangeManager, GetElevationAdaptedRange, CFixedVector3D, CFixedVector3D, entity_pos_t, entity_pos_t, entity_pos_t)
DEFINE_INTERFACE_METHOD_2("GetLosVisibility", std::string, ICmpRangeManager, GetLosVisibility_wrapper, entity_id_t, player_id_t)
+DEFINE_INTERFACE_METHOD_1("RequestVisibilityUpdate", void, ICmpRangeManager, RequestVisibilityUpdate, entity_id_t)
DEFINE_INTERFACE_METHOD_1("SetLosCircular", void, ICmpRangeManager, SetLosCircular, bool)
DEFINE_INTERFACE_METHOD_0("GetLosCircular", bool, ICmpRangeManager, GetLosCircular)
DEFINE_INTERFACE_METHOD_2("SetSharedLos", void, ICmpRangeManager, SetSharedLos, player_id_t, std::vector)
diff --git a/source/simulation2/components/ICmpRangeManager.h b/source/simulation2/components/ICmpRangeManager.h
index e9dda3ed13..acdc8475f4 100644
--- a/source/simulation2/components/ICmpRangeManager.h
+++ b/source/simulation2/components/ICmpRangeManager.h
@@ -323,6 +323,12 @@ public:
virtual ELosVisibility GetLosVisibility(CEntityHandle ent, player_id_t player) = 0;
virtual ELosVisibility GetLosVisibility(entity_id_t ent, player_id_t player) = 0;
+ /**
+ * Request the update of the visibility cache of ent at next turn.
+ * Typically used for fogging.
+ */
+ virtual void RequestVisibilityUpdate(entity_id_t ent) = 0;
+
/**
* GetLosVisibility wrapped for script calls.