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.