Index: ps/trunk/binaries/data/mods/public/simulation/components/Auras.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/Auras.js (revision 20299) +++ ps/trunk/binaries/data/mods/public/simulation/components/Auras.js (revision 20300) @@ -1,477 +1,481 @@ function Auras() {} Auras.prototype.Schema = "" + "tokens" + "" + ""; Auras.prototype.Init = function() { let cmpDataTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_DataTemplateManager); this.auras = {}; this.affectedPlayers = {}; for (let name of this.GetAuraNames()) { this.affectedPlayers[name] = []; this.auras[name] = cmpDataTemplateManager.GetAuraTemplate(name); } // In case of autogarrisoning, this component can be called before ownership is set. // So it needs to be completely initialised from the start. this.Clean(); }; // We can modify identifier if we want stackable auras in some case. Auras.prototype.GetModifierIdentifier = function(name) { if (this.auras[name].stackable) return name + this.entity; return name; }; Auras.prototype.GetDescriptions = function() { var ret = {}; for (let auraID of this.GetAuraNames()) { let aura = this.auras[auraID]; ret[auraID] = { "name": aura.auraName, "description": aura.auraDescription || null, "radius": this.GetRange(auraID) || null }; } return ret; }; Auras.prototype.GetAuraNames = function() { return this.template._string.split(/\s+/); }; Auras.prototype.GetOverlayIcon = function(name) { return this.auras[name].overlayIcon || ""; }; Auras.prototype.GetAffectedEntities = function(name) { return this[name].targetUnits; }; Auras.prototype.GetRange = function(name) { if (this.IsRangeAura(name)) return +this.auras[name].radius; return undefined; }; /** * Return the names of any range auras - used to render their ranges. */ Auras.prototype.GetVisualAuraRangeNames = function() { return this.GetAuraNames().filter(auraName => this.IsRangeAura(auraName) && this[auraName].isApplied); }; Auras.prototype.GetLineTexture = function(name) { return this.auras[name].rangeOverlay ? this.auras[name].rangeOverlay.lineTexture : "outline_border.png"; }; Auras.prototype.GetLineTextureMask = function(name) { return this.auras[name].rangeOverlay ? this.auras[name].rangeOverlay.lineTextureMask : "outline_border_mask.png"; }; Auras.prototype.GetLineThickness = function(name) { return this.auras[name].rangeOverlay ? this.auras[name].rangeOverlay.lineThickness : 0.2; }; Auras.prototype.GetClasses = function(name) { return this.auras[name].affects; }; Auras.prototype.GetModifications = function(name) { return this.auras[name].modifications; }; Auras.prototype.GetAffectedPlayers = function(name) { return this.affectedPlayers[name]; }; Auras.prototype.CalculateAffectedPlayers = function(name) { var affectedPlayers = this.auras[name].affectedPlayers || ["Player"]; this.affectedPlayers[name] = []; var cmpPlayer = Engine.QueryInterface(this.entity, IID_Player); if (!cmpPlayer) cmpPlayer = QueryOwnerInterface(this.entity); if (!cmpPlayer || cmpPlayer.GetState() == "defeated") return; var numPlayers = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetNumPlayers(); for (var i = 0; i < numPlayers; ++i) { for (let p of affectedPlayers) { if (p == "Player" ? cmpPlayer.GetPlayerID() == i : cmpPlayer["Is" + p](i)) { this.affectedPlayers[name].push(i); break; } } } }; Auras.prototype.CanApply = function(name) { if (!this.auras[name].requiredTechnology) return true; let cmpTechnologyManager = QueryOwnerInterface(this.entity, IID_TechnologyManager); if (!cmpTechnologyManager) return false; return cmpTechnologyManager.IsTechnologyResearched(this.auras[name].requiredTechnology); }; Auras.prototype.HasFormationAura = function() { return this.GetAuraNames().some(n => this.IsFormationAura(n)); }; Auras.prototype.HasGarrisonAura = function() { return this.GetAuraNames().some(n => this.IsGarrisonAura(n)); }; Auras.prototype.HasGarrisonedUnitsAura = function() { return this.GetAuraNames().some(n => this.IsGarrisonedUnitsAura(n)); }; Auras.prototype.GetType = function(name) { return this.auras[name].type; }; Auras.prototype.IsFormationAura = function(name) { return this.GetType(name) == "formation"; }; Auras.prototype.IsGarrisonAura = function(name) { return this.GetType(name) == "garrison"; }; Auras.prototype.IsGarrisonedUnitsAura = function(name) { return this.GetType(name) == "garrisonedUnits"; }; Auras.prototype.IsRangeAura = function(name) { return this.GetType(name) == "range"; }; Auras.prototype.IsGlobalAura = function(name) { return this.GetType(name) == "global" || this.GetType(name) == "player"; }; Auras.prototype.IsPlayerAura = function(name) { return this.GetType(name) == "player"; }; /** * clean all bonuses. Remove the old ones and re-apply the new ones */ Auras.prototype.Clean = function() { var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); var auraNames = this.GetAuraNames(); let targetUnitsClone = {}; let needVisualizationUpdate = false; // remove all bonuses for (let name of auraNames) { targetUnitsClone[name] = []; if (!this[name]) continue; if (this.IsRangeAura(name)) needVisualizationUpdate = true; if (this[name].targetUnits) targetUnitsClone[name] = this[name].targetUnits.slice(); if (this.IsGlobalAura(name)) this.RemoveTemplateBonus(name); this.RemoveBonus(name, this[name].targetUnits); if (this[name].rangeQuery) cmpRangeManager.DestroyActiveQuery(this[name].rangeQuery); } for (let name of auraNames) { // only calculate the affected players on re-applying the bonuses // this makes sure the template bonuses are removed from the correct players this.CalculateAffectedPlayers(name); // initialise range query this[name] = {}; this[name].targetUnits = []; this[name].isApplied = this.CanApply(name); var affectedPlayers = this.GetAffectedPlayers(name); if (!affectedPlayers.length) continue; if (this.IsGlobalAura(name)) { for (let player of affectedPlayers) { this.ApplyTemplateBonus(name, affectedPlayers); if (this.IsPlayerAura(name)) { let cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); let playerEnts = affectedPlayers.map(player => cmpPlayerManager.GetPlayerByID(player)); this.ApplyBonus(name, playerEnts); } else this.ApplyBonus(name, cmpRangeManager.GetEntitiesByPlayer(player)); } continue; } if (!this.IsRangeAura(name)) { this.ApplyBonus(name, targetUnitsClone[name]); continue; } needVisualizationUpdate = true; - this[name].rangeQuery = cmpRangeManager.CreateActiveQuery( - this.entity, - 0, - this.GetRange(name), - affectedPlayers, - IID_Identity, - cmpRangeManager.GetEntityFlagMask("normal") - ); - cmpRangeManager.EnableActiveQuery(this[name].rangeQuery); + + if (this[name].isApplied) + { + this[name].rangeQuery = cmpRangeManager.CreateActiveQuery( + this.entity, + 0, + this.GetRange(name), + affectedPlayers, + IID_Identity, + cmpRangeManager.GetEntityFlagMask("normal") + ); + cmpRangeManager.EnableActiveQuery(this[name].rangeQuery); + } } if (needVisualizationUpdate) { let cmpRangeVisualization = Engine.QueryInterface(this.entity, IID_RangeVisualization); if (cmpRangeVisualization) { cmpRangeVisualization.UpdateVisualAuraRanges(); cmpRangeVisualization.RegenerateRangeVisualizations(false); } } }; Auras.prototype.GiveMembersWithValidClass = function(auraName, entityList) { var match = this.GetClasses(auraName); return entityList.filter(ent => { let cmpIdentity = Engine.QueryInterface(ent, IID_Identity); return cmpIdentity && MatchesClassList(cmpIdentity.GetClassesList(), match); }); }; Auras.prototype.OnRangeUpdate = function(msg) { for (let name of this.GetAuraNames().filter(n => this[n] && msg.tag == this[n].rangeQuery)) { this.ApplyBonus(name, msg.added); this.RemoveBonus(name, msg.removed); } }; Auras.prototype.OnGarrisonedUnitsChanged = function(msg) { for (let name of this.GetAuraNames().filter(n => this.IsGarrisonedUnitsAura(n))) { this.ApplyBonus(name, msg.added); this.RemoveBonus(name, msg.removed); } }; Auras.prototype.RegisterGlobalOwnershipChanged = function(msg) { for (let name of this.GetAuraNames().filter(n => this.IsGlobalAura(n))) { let affectedPlayers = this.GetAffectedPlayers(name); let wasApplied = affectedPlayers.indexOf(msg.from) != -1; let willBeApplied = affectedPlayers.indexOf(msg.to) != -1; if (wasApplied && !willBeApplied) this.RemoveBonus(name, [msg.entity]); if (willBeApplied && !wasApplied) this.ApplyBonus(name, [msg.entity]); } }; Auras.prototype.ApplyFormationBonus = function(memberList) { for (let name of this.GetAuraNames().filter(n => this.IsFormationAura(n))) this.ApplyBonus(name, memberList); }; Auras.prototype.ApplyGarrisonBonus = function(structure) { for (let name of this.GetAuraNames().filter(n => this.IsGarrisonAura(n))) this.ApplyBonus(name, [structure]); }; Auras.prototype.ApplyTemplateBonus = function(name, players) { if (!this[name].isApplied) return; if (!this.IsGlobalAura(name)) return; var modifications = this.GetModifications(name); var cmpAuraManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_AuraManager); var classes = this.GetClasses(name); cmpAuraManager.RegisterGlobalAuraSource(this.entity); for (let mod of modifications) for (let player of players) cmpAuraManager.ApplyTemplateBonus(mod.value, player, classes, mod, this.GetModifierIdentifier(name)); }; Auras.prototype.RemoveFormationBonus = function(memberList) { for (let name of this.GetAuraNames().filter(n => this.IsFormationAura(n))) this.RemoveBonus(name, memberList); }; Auras.prototype.RemoveGarrisonBonus = function(structure) { for (let name of this.GetAuraNames().filter(n => this.IsGarrisonAura(n))) this.RemoveBonus(name, [structure]); }; Auras.prototype.RemoveTemplateBonus = function(name) { if (!this[name].isApplied) return; if (!this.IsGlobalAura(name)) return; var cmpAuraManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_AuraManager); cmpAuraManager.UnregisterGlobalAuraSource(this.entity); var modifications = this.GetModifications(name); var classes = this.GetClasses(name); var players = this.GetAffectedPlayers(name); for (let mod of modifications) for (let player of players) cmpAuraManager.RemoveTemplateBonus(mod.value, player, classes, this.GetModifierIdentifier(name)); }; Auras.prototype.ApplyBonus = function(name, ents) { var validEnts = this.GiveMembersWithValidClass(name, ents); if (!validEnts.length) return; this[name].targetUnits = this[name].targetUnits.concat(validEnts); if (!this[name].isApplied) return; var modifications = this.GetModifications(name); var cmpAuraManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_AuraManager); for (let mod of modifications) cmpAuraManager.ApplyBonus(mod.value, validEnts, mod, this.GetModifierIdentifier(name)); // update status bars if this has an icon if (!this.GetOverlayIcon(name)) return; for (let ent of validEnts) { var cmpStatusBars = Engine.QueryInterface(ent, IID_StatusBars); if (cmpStatusBars) cmpStatusBars.AddAuraSource(this.entity, name); } }; Auras.prototype.RemoveBonus = function(name, ents) { var validEnts = this.GiveMembersWithValidClass(name, ents); if (!validEnts.length) return; this[name].targetUnits = this[name].targetUnits.filter(v => validEnts.indexOf(v) == -1); if (!this[name].isApplied) return; var modifications = this.GetModifications(name); var cmpAuraManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_AuraManager); for (let mod of modifications) cmpAuraManager.RemoveBonus(mod.value, validEnts, this.GetModifierIdentifier(name)); // update status bars if this has an icon if (!this.GetOverlayIcon(name)) return; for (let ent of validEnts) { var cmpStatusBars = Engine.QueryInterface(ent, IID_StatusBars); if (cmpStatusBars) cmpStatusBars.RemoveAuraSource(this.entity, name); } }; Auras.prototype.OnOwnershipChanged = function(msg) { this.Clean(); }; Auras.prototype.OnDiplomacyChanged = function(msg) { var cmpPlayer = Engine.QueryInterface(this.entity, IID_Player); if (cmpPlayer && (cmpPlayer.GetPlayerID() == msg.player || cmpPlayer.GetPlayerID() == msg.otherPlayer) || IsOwnedByPlayer(msg.player, this.entity) || IsOwnedByPlayer(msg.otherPlayer, this.entity)) this.Clean(); }; Auras.prototype.OnGlobalResearchFinished = function(msg) { var cmpPlayer = Engine.QueryInterface(this.entity, IID_Player); if ((!cmpPlayer || cmpPlayer.GetPlayerID() != msg.player) && !IsOwnedByPlayer(msg.player, this.entity)) return; for (let name of this.GetAuraNames()) { let requiredTech = this.auras[name].requiredTechnology; if (requiredTech && requiredTech == msg.tech) { this.Clean(); return; } } }; Auras.prototype.OnPlayerDefeated = function(msg) { this.Clean(); }; Engine.RegisterComponentType(IID_Auras, "Auras", Auras); Index: ps/trunk/binaries/data/mods/public/simulation/components/RangeVisualization.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/RangeVisualization.js (revision 20299) +++ ps/trunk/binaries/data/mods/public/simulation/components/RangeVisualization.js (revision 20300) @@ -1,110 +1,112 @@ function RangeVisualization() {} RangeVisualization.prototype.Schema = ""; RangeVisualization.prototype.Init = function() { this.enabled = false; this.enabledRangeTypes = { "Aura": false, "Heal": false }; this.rangeVisualizations = new Map(); }; // The GUI enables visualizations RangeVisualization.prototype.Serialize = null; RangeVisualization.prototype.Deserialize = function(data) { this.Init(); }; RangeVisualization.prototype.UpdateVisualAuraRanges = function() { let cmpAuras = Engine.QueryInterface(this.entity, IID_Auras); if (!cmpAuras) return; this.rangeVisualizations.set("Aura", []); for (let auraName of cmpAuras.GetVisualAuraRangeNames()) this.rangeVisualizations.get("Aura").push({ "radius": cmpAuras.GetRange(auraName), "texture": cmpAuras.GetLineTexture(auraName), "textureMask": cmpAuras.GetLineTextureMask(auraName), "thickness": cmpAuras.GetLineThickness(auraName), }); }; RangeVisualization.prototype.UpdateVisualHealRanges = function() { let cmpHeal = Engine.QueryInterface(this.entity, IID_Heal); if (!cmpHeal) return; this.rangeVisualizations.set("Heal", [{ "radius": cmpHeal.GetRange().max, "texture": cmpHeal.GetLineTexture(), "textureMask": cmpHeal.GetLineTextureMask(), "thickness": cmpHeal.GetLineThickness(), }]); }; RangeVisualization.prototype.SetEnabled = function(enabled, enabledRangeTypes, forceUpdate) { this.enabled = enabled; this.enabledRangeTypes = enabledRangeTypes; this.RegenerateRangeVisualizations(forceUpdate); }; RangeVisualization.prototype.RegenerateRangeVisualizations = function(forceUpdate) { let cmpSelectable = Engine.QueryInterface(this.entity, IID_Selectable); if (!cmpSelectable) return; cmpSelectable.ResetRangeOverlays(); if (!this.enabled && !forceUpdate) return; // Only render individual range types that have been enabled for (let rangeOverlayType of this.rangeVisualizations.keys()) if (this.enabledRangeTypes[rangeOverlayType]) for (let rangeOverlay of this.rangeVisualizations.get(rangeOverlayType)) cmpSelectable.AddRangeOverlay( rangeOverlay.radius, rangeOverlay.texture, rangeOverlay.textureMask, rangeOverlay.thickness); }; RangeVisualization.prototype.OnOwnershipChanged = function(msg) { if (msg.to == -1) return; for (let type in this.enabledRangeTypes) this["UpdateVisual" + type + "Ranges"](); this.RegenerateRangeVisualizations(false); }; RangeVisualization.prototype.OnValueModification = function(msg) { if (msg.valueNames.indexOf("Heal/Range") == -1) return; this["UpdateVisual" + msg.component + "Ranges"](); this.RegenerateRangeVisualizations(false); }; -/** RangeVisualization component is deserialized before the TechnologyManager, so need to update the ranges here */ +/** + * RangeVisualization component is deserialized before the TechnologyManager, so need to update the ranges here + */ RangeVisualization.prototype.OnDeserialized = function(msg) { for (let type in this.enabledRangeTypes) this["UpdateVisual" + type + "Ranges"](); }; Engine.RegisterComponentType(IID_RangeVisualization, "RangeVisualization", RangeVisualization);