Index: binaries/data/mods/public/simulation/components/Armour.js =================================================================== --- binaries/data/mods/public/simulation/components/Armour.js +++ binaries/data/mods/public/simulation/components/Armour.js @@ -19,6 +19,7 @@ Armour.prototype.Init = function() { this.invulnerable = false; + this.UpdateProperties(); }; Armour.prototype.IsInvulnerable = function() @@ -57,11 +58,12 @@ return cmpHealth.Reduce(total); }; -Armour.prototype.GetArmourStrengths = function() +// Cache some properties that are expensive to compute. +Armour.prototype.UpdateProperties = function() { // Work out the armour values with technology effects - var applyMods = (type, foundation) => { - var strength; + let applyMods = (type, foundation) => { + let strength; if (foundation) { strength = +this.template.Foundation[type]; @@ -73,13 +75,43 @@ return ApplyValueModificationsToEntity("Armour/" + type, strength, this.entity); }; - var foundation = Engine.QueryInterface(this.entity, IID_Foundation) && this.template.Foundation; + let foundation = Engine.QueryInterface(this.entity, IID_Foundation) && this.template.Foundation; - let ret = {}; + this.armourStrengths = {}; for (let damageType of DamageTypes.GetTypes()) - ret[damageType] = applyMods(damageType, foundation); + this.armourStrengths[damageType] = applyMods(damageType, foundation); +} + +Armour.prototype.GetArmourStrengths = function() +{ + return this.armourStrengths; +}; + +Armour.prototype.OnValueModification = function(msg) +{ + if (msg.component != "Armour") + return; + + this.UpdateProperties(); +}; - return ret; +Armour.prototype.OnOwnershipChanged = function(msg) +{ + if (msg.to == INVALID_PLAYER) + return; + + this.UpdateProperties(); +}; + +Armour.prototype.OnGlobalInitGame = function(msg) +{ + this.UpdateProperties(); +}; + +Armour.prototype.OnMultiplierChanged = function(msg) +{ + if (msg.player == QueryOwnerInterface(this.entity, IID_Player).GetPlayerID()) + this.UpdateProperties(); }; Engine.RegisterComponentType(IID_DamageReceiver, "Armour", Armour); Index: binaries/data/mods/public/simulation/components/Attack.js =================================================================== --- binaries/data/mods/public/simulation/components/Attack.js +++ binaries/data/mods/public/simulation/components/Attack.js @@ -218,6 +218,7 @@ Attack.prototype.Init = function() { + this.UpdateProperties(); }; Attack.prototype.Serialize = null; // we have no dynamic state to save @@ -405,39 +406,97 @@ return aPreference - bPreference; }; -Attack.prototype.GetTimers = function(type) +// Cache some properties that are expensive to compute. +Attack.prototype.UpdateProperties = function() { - let prepare = +(this.template[type].PrepareTime || 0); - prepare = ApplyValueModificationsToEntity("Attack/" + type + "/PrepareTime", prepare, this.entity); + this.prepareTimes = {}; + this.repeatTimes = {}; + this.maxRanges = {}; + this.minRanges = {}; + this.elevationBonuses = {}; + this.captures = {}; + this.damages = {}; - let repeat = +(this.template[type].RepeatTime || 1000); - repeat = ApplyValueModificationsToEntity("Attack/" + type + "/RepeatTime", repeat, this.entity); + for (let type of this.GetAttackTypes()) + { + let template = this.template[type]; + let splash = ""; - return { "prepare": prepare, "repeat": repeat }; -}; + if (template) + { + let prepare = +(template.PrepareTime || 0); + this.prepareTimes[type] = ApplyValueModificationsToEntity("Attack/" + type + "/PrepareTime", prepare, this.entity); -Attack.prototype.GetAttackStrengths = function(type) -{ - // Work out the attack values with technology effects - let template = this.template[type]; - let splash = ""; - if (!template) + let repeat = +(template.RepeatTime || 1000); + this.repeatTimes[type] = ApplyValueModificationsToEntity("Attack/" + type + "/RepeatTime", repeat, this.entity); + + let max = +template.MaxRange; + this.maxRanges[type] = ApplyValueModificationsToEntity("Attack/" + type + "/MaxRange", max, this.entity); + + let min = +(template.MinRange || 0); + this.minRanges[type] = ApplyValueModificationsToEntity("Attack/" + type + "/MinRange", min, this.entity); + + let elevationBonus = +(template.ElevationBonus || 0); + this.elevationBonuses[type] = ApplyValueModificationsToEntity("Attack/" + type + "/ElevationBonus", elevationBonus, this.entity); + } + else + { + template = this.template[type.split(".")[0]].Splash; + splash = "/Splash"; + } + + let applyMods = damageType => + ApplyValueModificationsToEntity("Attack/" + type + splash + "/" + damageType, +(template[damageType] || 0), this.entity); + + if (type == "Capture") + this.captures[type] = { "value": applyMods("Value") }; + else + { + this.damages[type] = {}; + for (let damageType of DamageTypes.GetTypes()) + this.damages[type][damageType] = applyMods(damageType); + } + } + + let attack = {}; + for (let type of this.GetAttackTypes()) { - template = this.template[type.split(".")[0]].Splash; - splash = "/Splash"; + attack[type] = this.GetAttackStrengths(type); + attack[type].splash = this.GetSplashDamage(type); + + let range = this.GetRange(type); + attack[type].minRange = range.min; + attack[type].maxRange = range.max; + + let timers = this.GetTimers(type); + attack[type].prepareTime = timers.prepare; + attack[type].repeatTime = timers.repeat; + + if (type != "Ranged") + { + // not a ranged attack, set some defaults + attack[type].elevationBonus = 0; + attack[type].elevationAdaptedRange = attack.maxRange; + continue; + } + + attack[type].elevationBonus = range.elevationBonus; } - let applyMods = damageType => - ApplyValueModificationsToEntity("Attack/" + type + splash + "/" + damageType, +(template[damageType] || 0), this.entity); + this.properties = attack; +} - if (type == "Capture") - return { "value": applyMods("Value") }; +Attack.prototype.GetTimers = function(type) +{ + return { "prepare": this.prepareTimes[type], "repeat": this.repeatTimes[type] }; +}; - let ret = {}; - for (let damageType of DamageTypes.GetTypes()) - ret[damageType] = applyMods(damageType); +Attack.prototype.GetAttackStrengths = function(type) +{ + if (type == "Capture") + return this.captures[type]; - return ret; + return this.damages[type]; }; Attack.prototype.GetSplashDamage = function(type) @@ -453,18 +512,14 @@ Attack.prototype.GetRange = function(type) { - let max = +this.template[type].MaxRange; - max = ApplyValueModificationsToEntity("Attack/" + type + "/MaxRange", max, this.entity); - - let min = +(this.template[type].MinRange || 0); - min = ApplyValueModificationsToEntity("Attack/" + type + "/MinRange", min, this.entity); - - let elevationBonus = +(this.template[type].ElevationBonus || 0); - elevationBonus = ApplyValueModificationsToEntity("Attack/" + type + "/ElevationBonus", elevationBonus, this.entity); - - return { "max": max, "min": min, "elevationBonus": elevationBonus }; + return { "max": this.maxRanges[type], "min": this.minRanges[type], "elevationBonus": this.elevationBonuses[type] }; }; +Attack.prototype.GetProperties = function() +{ + return this.properties; +} + Attack.prototype.GetBonusTemplate = function(type) { let template = this.template[type]; @@ -544,7 +599,7 @@ let launchPoint = selfPosition.clone(); // TODO: remove this when all the ranged unit templates are updated with Projectile/Launchpoint launchPoint.y += 3; - + let cmpVisual = Engine.QueryInterface(this.entity, IID_Visual); if (cmpVisual) { @@ -663,6 +718,8 @@ if (msg.component != "Attack") return; + this.UpdateProperties(); + let cmpUnitAI = Engine.QueryInterface(this.entity, IID_UnitAI); if (!cmpUnitAI) return; @@ -672,6 +729,25 @@ cmpUnitAI.UpdateRangeQueries(); }; +Attack.prototype.OnOwnershipChanged = function(msg) +{ + if (msg.to == INVALID_PLAYER) + return; + + this.UpdateProperties(); +}; + +Attack.prototype.OnGlobalInitGame = function(msg) +{ + this.UpdateProperties(); +}; + +Attack.prototype.OnMultiplierChanged = function(msg) +{ + if (msg.player == QueryOwnerInterface(this.entity, IID_Player).GetPlayerID()) + this.UpdateProperties(); +}; + Attack.prototype.GetRangeOverlays = function() { if (!this.template.Ranged || !this.template.Ranged.RangeOverlay) Index: binaries/data/mods/public/simulation/components/GuiInterface.js =================================================================== --- binaries/data/mods/public/simulation/components/GuiInterface.js +++ binaries/data/mods/public/simulation/components/GuiInterface.js @@ -384,45 +384,29 @@ { let types = cmpAttack.GetAttackTypes(); if (types.length) - ret.attack = {}; + ret.attack = cmpAttack.GetProperties(); for (let type of types) { - ret.attack[type] = cmpAttack.GetAttackStrengths(type); - ret.attack[type].splash = cmpAttack.GetSplashDamage(type); - - let range = cmpAttack.GetRange(type); - ret.attack[type].minRange = range.min; - ret.attack[type].maxRange = range.max; - - let timers = cmpAttack.GetTimers(type); - ret.attack[type].prepareTime = timers.prepare; - ret.attack[type].repeatTime = timers.repeat; - if (type != "Ranged") - { - // not a ranged attack, set some defaults - ret.attack[type].elevationBonus = 0; - ret.attack[type].elevationAdaptedRange = ret.attack.maxRange; continue; - } - ret.attack[type].elevationBonus = range.elevationBonus; + let max = ret.attack[type].maxRange; if (cmpUnitAI && cmpPosition && cmpPosition.IsInWorld()) { // For units, take the range in front of it, no spread. So angle = 0 - ret.attack[type].elevationAdaptedRange = cmpRangeManager.GetElevationAdaptedRange(cmpPosition.GetPosition(), cmpPosition.GetRotation(), range.max, range.elevationBonus, 0); + ret.attack[type].elevationAdaptedRange = cmpRangeManager.GetElevationAdaptedRange(cmpPosition.GetPosition(), cmpPosition.GetRotation(), max, ret.attack[type].elevationBonus, 0); } else if(cmpPosition && cmpPosition.IsInWorld()) { // For buildings, take the average elevation around it. So angle = 2*pi - ret.attack[type].elevationAdaptedRange = cmpRangeManager.GetElevationAdaptedRange(cmpPosition.GetPosition(), cmpPosition.GetRotation(), range.max, range.elevationBonus, 2*Math.PI); + ret.attack[type].elevationAdaptedRange = cmpRangeManager.GetElevationAdaptedRange(cmpPosition.GetPosition(), cmpPosition.GetRotation(), max, ret.attack[type].elevationBonus, 2*Math.PI); } else { // not in world, set a default? - ret.attack[type].elevationAdaptedRange = ret.attack.maxRange; + ret.attack[type].elevationAdaptedRange = ret.attack[type].maxRange; } } } Index: binaries/data/mods/public/simulation/components/Loot.js =================================================================== --- binaries/data/mods/public/simulation/components/Loot.js +++ binaries/data/mods/public/simulation/components/Loot.js @@ -8,19 +8,58 @@ "" + Resources.BuildSchema("nonNegativeInteger", ["xp"]); +Loot.prototype.Init = function() +{ + this.UpdateProperties(); +} + Loot.prototype.Serialize = null; // we have no dynamic state to save +// Cache some properties that are expensive to compute. +Loot.prototype.UpdateProperties = function() +{ + this.xp = Math.floor(ApplyValueModificationsToEntity("Loot/xp", +(this.template.xp || 0), this.entity)) + + this.resources = {}; + for (let res of Resources.GetCodes()) + this.resources[res] = Math.floor(ApplyValueModificationsToEntity("Loot/" + res, +(this.template[res] || 0), this.entity)); +}; + Loot.prototype.GetXp = function() { - return Math.floor(ApplyValueModificationsToEntity("Loot/xp", +(this.template.xp || 0), this.entity)); + return this.xp; }; Loot.prototype.GetResources = function() { - let ret = {}; - for (let res of Resources.GetCodes()) - ret[res] = Math.floor(ApplyValueModificationsToEntity("Loot/" + res, +(this.template[res] || 0), this.entity)); - return ret; + return this.resources; +}; + +Loot.prototype.OnValueModification = function(msg) +{ + if (msg.component != "Loot") + return; + + this.UpdateProperties(); +}; + +Loot.prototype.OnOwnershipChanged = function(msg) +{ + if (msg.to == INVALID_PLAYER) + return; + + this.UpdateProperties(); +}; + +Loot.prototype.OnGlobalInitGame = function(msg) +{ + this.UpdateProperties(); +}; + +Loot.prototype.OnMultiplierChanged = function(msg) +{ + if (msg.player == QueryOwnerInterface(this.entity, IID_Player).GetPlayerID()) + this.UpdateProperties(); }; Engine.RegisterComponentType(IID_Loot, "Loot", Loot);