Index: ps/trunk/binaries/data/mods/public/globalscripts/AttackEffects.js =================================================================== --- ps/trunk/binaries/data/mods/public/globalscripts/AttackEffects.js +++ ps/trunk/binaries/data/mods/public/globalscripts/AttackEffects.js @@ -1,5 +1,5 @@ // TODO: could be worth putting this in json files someday -const g_EffectTypes = ["Damage", "Capture", "GiveStatus"]; +const g_EffectTypes = ["Damage", "Capture", "ApplyStatus"]; const g_EffectReceiver = { "Damage": { "IID": "IID_Health", @@ -9,8 +9,8 @@ "IID": "IID_Capturable", "method": "Capture" }, - "GiveStatus": { + "ApplyStatus": { "IID": "IID_StatusEffectsReceiver", - "method": "GiveStatus" + "method": "ApplyStatus" } }; Index: ps/trunk/binaries/data/mods/public/globalscripts/ModificationTemplates.js =================================================================== --- ps/trunk/binaries/data/mods/public/globalscripts/ModificationTemplates.js +++ ps/trunk/binaries/data/mods/public/globalscripts/ModificationTemplates.js @@ -43,3 +43,160 @@ global.AuraTemplates = new ModificationTemplates("simulation/data/auras/"); global.TechnologyTemplates = new ModificationTemplates("simulation/data/technologies/"); } + +/** + * Derives modifications (to be applied to entities) from a given aura/technology. + * + * @param {Object} techTemplate - The aura/technology template to derive the modifications from. + * @return {Object} - An object containing the relevant modifications. + */ +function DeriveModificationsFromTech(techTemplate) +{ + if (!techTemplate.modifications) + return {}; + + let techMods = {}; + let techAffects = []; + if (techTemplate.affects && techTemplate.affects.length) + techAffects = techTemplate.affects.map(affected => affected.split(/\s+/)); + else + techAffects.push([]); + + for (let mod of techTemplate.modifications) + { + let affects = techAffects.slice(); + if (mod.affects) + { + let specAffects = mod.affects.split(/\s+/); + for (let a in affects) + affects[a] = affects[a].concat(specAffects); + } + + let newModifier = { "affects": affects }; + for (let idx in mod) + if (idx !== "value" && idx !== "affects") + newModifier[idx] = mod[idx]; + + if (!techMods[mod.value]) + techMods[mod.value] = []; + techMods[mod.value].push(newModifier); + } + return techMods; +} + +/** + * Derives modifications (to be applied to entities) from a provided array + * of aura/technology template data. + * + * @param {Object[]} techsDataArray + * @return {Object} - The combined relevant modifications of all the technologies. + */ +function DeriveModificationsFromTechnologies(techsDataArray) +{ + if (!techsDataArray.length) + return {}; + + let derivedModifiers = {}; + for (let technology of techsDataArray) + { + if (!technology.reqs) + continue; + + let modifiers = DeriveModificationsFromTech(technology); + for (let modPath in modifiers) + { + if (!derivedModifiers[modPath]) + derivedModifiers[modPath] = []; + derivedModifiers[modPath] = derivedModifiers[modPath].concat(modifiers[modPath]); + } + } + return derivedModifiers; +} + +/** + * Common definition of the XML schema for in-template modifications. + */ +const ModificationSchema = +"" + + "" + + "" + + "tokens" + + "" + + "" + + "" + + "" + + "" + + "tokens" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + +""; + +const ModificationsSchema = +"" + + "" + + "" + + "" + + ModificationSchema + + "" + + "" + +""; + +/** + * Derives a single modification (to be applied to entities) from a given XML template. + * + * @param {Object} techTemplate - The XML template node to derive the modification from. + * @return {Object} containing the relevant modification. + */ +function DeriveModificationFromXMLTemplate(template) +{ + let effect = {}; + if (template.Add) + effect.add = +template.Add; + if (template.Multiply) + effect.multiply = +template.Multiply; + if (template.Replace) + effect.replace = template.Replace; + effect.affects = template.Affects ? template.Affects._string.split(/\s/) : []; + + let ret = {}; + for (let path of template.Paths._string.split(/\s/)) + { + ret[path] = [effect]; + } + + return ret; +} + +/** + * Derives all modifications (to be applied to entities) from a given XML template. + * + * @param {Object} techTemplate - The XML template node to derive the modifications from. + * @return {Object} containing the combined modifications. + */ +function DeriveModificationsFromXMLTemplate(template) +{ + let ret = {}; + for (let name in template) + { + let modification = DeriveModificationFromXMLTemplate(template[name]); + for (let path in modification) + { + if (!ret[path]) + ret[path] = []; + ret[path].push(modification[path][0]); + } + } + return ret; +} Index: ps/trunk/binaries/data/mods/public/globalscripts/Technologies.js =================================================================== --- ps/trunk/binaries/data/mods/public/globalscripts/Technologies.js +++ ps/trunk/binaries/data/mods/public/globalscripts/Technologies.js @@ -40,74 +40,6 @@ } /** - * Derives modifications (to be applied to entities) from a given technology. - * - * @param {Object} techTemplate - The technology template to derive the modifications from. - * @return {Object} containing the relevant modifications. - */ -function DeriveModificationsFromTech(techTemplate) -{ - if (!techTemplate.modifications) - return {}; - - let techMods = {}; - let techAffects = []; - if (techTemplate.affects && techTemplate.affects.length) - for (let affected of techTemplate.affects) - techAffects.push(affected.split(/\s+/)); - else - techAffects.push([]); - - for (let mod of techTemplate.modifications) - { - let affects = techAffects.slice(); - if (mod.affects) - { - let specAffects = mod.affects.split(/\s+/); - for (let a in affects) - affects[a] = affects[a].concat(specAffects); - } - - let newModifier = { "affects": affects }; - for (let idx in mod) - if (idx !== "value" && idx !== "affects") - newModifier[idx] = mod[idx]; - - if (!techMods[mod.value]) - techMods[mod.value] = []; - techMods[mod.value].push(newModifier); - } - return techMods; -} - -/** - * Derives modifications (to be applied to entities) from a provided array - * of technology template data. - * - * @param {array} techsDataArray - * @return {object} containing the combined relevant modifications of all - * the technologies. - */ -function DeriveModificationsFromTechnologies(techsDataArray) -{ - let derivedModifiers = {}; - for (let technology of techsDataArray) - { - if (!technology.reqs) - continue; - - let modifiers = DeriveModificationsFromTech(technology); - for (let modPath in modifiers) - { - if (!derivedModifiers[modPath]) - derivedModifiers[modPath] = []; - derivedModifiers[modPath] = derivedModifiers[modPath].concat(modifiers[modPath]); - } - } - return derivedModifiers; -} - -/** * Returns whether the given modification applies to the entity containing the given class list */ function DoesModificationApply(modification, classes) Index: ps/trunk/binaries/data/mods/public/globalscripts/Templates.js =================================================================== --- ps/trunk/binaries/data/mods/public/globalscripts/Templates.js +++ ps/trunk/binaries/data/mods/public/globalscripts/Templates.js @@ -180,7 +180,9 @@ effects.Damage[damageType] = getEntityValue(path + "/Damage/" + damageType); } - // TODO: status effects + if (temp.ApplyStatus) + effects.ApplyStatus = temp.ApplyStatus; + return effects; }; Index: ps/trunk/binaries/data/mods/public/gui/common/tooltips.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/common/tooltips.js +++ ps/trunk/binaries/data/mods/public/gui/common/tooltips.js @@ -275,13 +275,13 @@ }); } -function giveStatusDetails(giveStatusTemplate) +function applyStatusDetails(applyStatusTemplate) { - if (!giveStatusTemplate) + if (!applyStatusTemplate) return ""; return sprintf(translate("gives %(name)s"), { - "name": Object.keys(giveStatusTemplate).map(x => unitFont(translateWithContext("status effect", x))).join(', '), + "name": Object.keys(applyStatusTemplate).map(x => unitFont(translateWithContext("status effect", applyStatusTemplate[x].Name))).join(commaFont(translate(", "))), }); } @@ -293,7 +293,7 @@ let effects = [ captureDetails(attackTypeTemplate.Capture || undefined), damageDetails(attackTypeTemplate.Damage || undefined), - giveStatusDetails(attackTypeTemplate.GiveStatus || undefined) + applyStatusDetails(attackTypeTemplate.ApplyStatus || undefined) ]; return effects.filter(effect => effect).join(commaFont(translate(", "))); } @@ -323,9 +323,9 @@ // Show the effects of status effects below let statusEffectsDetails = []; - if (attackTypeTemplate.GiveStatus) - for (let status in attackTypeTemplate.GiveStatus) - statusEffectsDetails.push("\n " + getStatusEffectsTooltip(status, attackTypeTemplate.GiveStatus[status])); + if (attackTypeTemplate.ApplyStatus) + for (let status in attackTypeTemplate.ApplyStatus) + statusEffectsDetails.push("\n " + getStatusEffectsTooltip(attackTypeTemplate.ApplyStatus[status])); statusEffectsDetails = statusEffectsDetails.join(""); tooltips.push(sprintf(translate("%(attackLabel)s: %(effects)s, %(range)s, %(rate)s%(statusEffects)s"), { @@ -371,20 +371,48 @@ return tooltips.join("\n"); } -function getStatusEffectsTooltip(name, template) +function getStatusEffectsTooltip(template) { + let tooltipAttributes = []; + let tooltipString = ""; + if (template.Tooltip) + { + tooltipAttributes.push("%(tooltip)s"); + tooltipString = translate(template.Tooltip); + } + + let attackEffectsString = ""; + if (template.Damage || template.Capture) + { + tooltipAttributes.push("%(effects)s"); + attackEffectsString = attackEffectsDetails(template); + } + + let intervalString = ""; + if (template.Interval) + { + tooltipAttributes.push("%(rate)s"); + intervalString = sprintf(translate("%(interval)s"), { + "interval": attackRateDetails(+template.Interval) + }); + } + let durationString = ""; if (template.Duration) - durationString = sprintf(translate(", %(durName)s: %(duration)s"), { + { + tooltipAttributes.push("%(duration)s"); + durationString = sprintf(translate("%(durName)s: %(duration)s"), { "durName": headerFont(translate("Duration")), - "duration": getSecondsString((template.TimeElapsed ? +template.Duration - template.TimeElapsed : +template.Duration) / 1000), + "duration": getSecondsString((template._timeElapsed ? +template.Duration - template._timeElapsed : +template.Duration) / 1000), }); + } - return sprintf(translate("%(statusName)s: %(effects)s, %(rate)s%(durationString)s"), { - "statusName": headerFont(translateWithContext("status effect", name)), - "effects": attackEffectsDetails(template), - "rate": attackRateDetails(+template.Interval), - "durationString": durationString + return sprintf(translate("%(statusName)s: " + tooltipAttributes.join(translate(commaFont(", ")))), { + "statusName": headerFont(translateWithContext("status effect", template.Name)), + "tooltip": tooltipString, + "effects": attackEffectsString, + "rate": intervalString, + "duration": durationString }); } Index: ps/trunk/binaries/data/mods/public/gui/session/selection_details.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/session/selection_details.js +++ ps/trunk/binaries/data/mods/public/gui/session/selection_details.js @@ -94,14 +94,16 @@ if (entState.statusEffects) { - let statusIcons = Engine.GetGUIObjectByName("statusEffectsIcons").children; + let statusEffectsSection = Engine.GetGUIObjectByName("statusEffectsIcons"); + statusEffectsSection.hidden = false; + let statusIcons = statusEffectsSection.children; let i = 0; for (let effectName in entState.statusEffects) { let effect = entState.statusEffects[effectName]; statusIcons[i].hidden = false; statusIcons[i].sprite = "stretched:session/icons/status_effects/" + (effect.Icon || "default") + ".png"; - statusIcons[i].tooltip = getStatusEffectsTooltip(effectName, effect); + statusIcons[i].tooltip = getStatusEffectsTooltip(effect); let size = statusIcons[i].size; size.top = i * 18; size.bottom = i * 18 + 16; @@ -111,6 +113,8 @@ for (; i < statusIcons.length; ++i) statusIcons[i].hidden = true; } + else + Engine.GetGUIObjectByName("statusEffectsIcons").hidden = true; let showHealth = entState.hitpoints; let showResource = entState.resourceSupply; Index: ps/trunk/binaries/data/mods/public/simulation/components/StatusEffectsReceiver.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/StatusEffectsReceiver.js +++ ps/trunk/binaries/data/mods/public/simulation/components/StatusEffectsReceiver.js @@ -1,69 +1,145 @@ function StatusEffectsReceiver() {} +StatusEffectsReceiver.prototype.DefaultInterval = 1000; + +/** + * Initialises the status effects. + */ StatusEffectsReceiver.prototype.Init = function() { this.activeStatusEffects = {}; }; +/** + * Which status effects are active on this entity. + * + * @return {Object} - An object containing the status effects which currently affect the entity. + */ StatusEffectsReceiver.prototype.GetActiveStatuses = function() { return this.activeStatusEffects; }; -// Called by attacking effects. -StatusEffectsReceiver.prototype.GiveStatus = function(effectData, attacker, attackerOwner, bonusMultiplier) +/** + * Called by Attacking effects. Adds status effects for each entry in the effectData. + * + * @param {Object} effectData - An object containing the status effects to give to the entity. + * @param {number} attacker - The entity ID of the attacker. + * @param {number} attackerOwner - The player ID of the attacker. + * @param {number} bonusMultiplier - A value to multiply the damage with (not implemented yet for SE). + * + * @return {Object} - The names of the status effects which were processed. + */ +StatusEffectsReceiver.prototype.ApplyStatus = function(effectData, attacker, attackerOwner, bonusMultiplier) { + let attackerData = { "entity": attacker, "owner": attackerOwner }; for (let effect in effectData) - this.AddStatus(effect, effectData[effect]); + this.AddStatus(effect, effectData[effect], attackerData); // TODO: implement loot / resistance. return { "inflictedStatuses": Object.keys(effectData) }; }; -StatusEffectsReceiver.prototype.AddStatus = function(statusName, data) +/** + * Adds a status effect to the entity. + * + * @param {string} statusName - The name of the status effect. + * @param {object} data - The various effects and timings. + */ +StatusEffectsReceiver.prototype.AddStatus = function(statusName, data, attackerData) { if (this.activeStatusEffects[statusName]) + { + // TODO: implement different behaviour when receiving the same status multiple times. + // For now, these are ignored. return; + } this.activeStatusEffects[statusName] = {}; let status = this.activeStatusEffects[statusName]; Object.assign(status, data); - status.Interval = +data.Interval; - status.TimeElapsed = 0; - status.FirstTime = true; + + if (status.Modifiers) + { + let modifications = DeriveModificationsFromXMLTemplate(status.Modifiers); + let cmpModifiersManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ModifiersManager); + cmpModifiersManager.AddModifiers(statusName, modifications, this.entity); + } + + // With neither an interval nor a duration, there is no point in starting a timer. + if (!status.Duration && !status.Interval) + return; + + // We need this to prevent Status Effects from giving XP + // to the entity that applied them. + status.StatusEffect = true; + + // We want an interval to update the GUI to show how much time of the status effect + // is left even if the status effect itself has no interval. + if (!status.Interval) + status._interval = this.DefaultInterval; + + status._timeElapsed = 0; + status._firstTime = true; + status.source = attackerData; let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); - status.Timer = cmpTimer.SetInterval(this.entity, IID_StatusEffectsReceiver, "ExecuteEffect", 0, +status.Interval, statusName); + status._timer = cmpTimer.SetInterval(this.entity, IID_StatusEffectsReceiver, "ExecuteEffect", 0, +(status.Interval || status._interval), statusName); }; +/** + * Removes a status effect from the entity. + * + * @param {string} statusName - The status effect to be removed. + */ StatusEffectsReceiver.prototype.RemoveStatus = function(statusName) { - if (!this.activeStatusEffects[statusName]) + let statusEffect = this.activeStatusEffects[statusName]; + if (!statusEffect) return; - let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); - cmpTimer.CancelTimer(this.activeStatusEffects[statusName].Timer); + if (statusEffect.Modifiers) + { + let cmpModifiersManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ModifiersManager); + cmpModifiersManager.RemoveAllModifiers(statusName, this.entity); + } + + if (statusEffect._timer) + { + let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); + cmpTimer.CancelTimer(statusEffect._timer); + } delete this.activeStatusEffects[statusName]; }; +/** + * Called by the timers. Executes a status effect. + * + * @param {string} statusName - The name of the status effect to be executed. + * @param {number} lateness - The delay between the calling of the function and the actual execution (turn time?). + */ StatusEffectsReceiver.prototype.ExecuteEffect = function(statusName, lateness) { let status = this.activeStatusEffects[statusName]; if (!status) return; - if (status.FirstTime) + if (status.Damage || status.Capture) + Attacking.HandleAttackEffects(statusName, status, this.entity, status.source.entity, status.source.owner); + + if (!status.Duration) + return; + + if (status._firstTime) { - status.FirstTime = false; - status.TimeElapsed += lateness; + status._firstTime = false; + status._timeElapsed += lateness; } else - status.TimeElapsed += status.Interval + lateness; - - Attacking.HandleAttackEffects(statusName, status, this.entity, -1, -1); + status._timeElapsed += +(status.Interval || status._interval) + lateness; - if (status.Duration && status.TimeElapsed >= +status.Duration) + if (status._timeElapsed >= +status.Duration) this.RemoveStatus(statusName); }; Index: ps/trunk/binaries/data/mods/public/simulation/components/UnitAI.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/UnitAI.js +++ ps/trunk/binaries/data/mods/public/simulation/components/UnitAI.js @@ -4099,6 +4099,9 @@ UnitAI.prototype.OnAttacked = function(msg) { + if (msg.fromStatusEffect) + return; + this.UnitFsm.ProcessMessage(this, {"type": "Attacked", "data": msg}); }; Index: ps/trunk/binaries/data/mods/public/simulation/components/tests/test_Damage.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/tests/test_Damage.js +++ ps/trunk/binaries/data/mods/public/simulation/components/tests/test_Damage.js @@ -103,7 +103,16 @@ Engine.PostMessage = function(ent, iid, message) { - TS_ASSERT_UNEVAL_EQUALS({ "type": type, "target": target, "attacker": attacker, "attackerOwner": attackerOwner, "damage": damage, "capture": 0, "statusEffects": [] }, message); + TS_ASSERT_UNEVAL_EQUALS({ + "type": type, + "target": target, + "attacker": attacker, + "attackerOwner": attackerOwner, + "damage": damage, + "capture": 0, + "statusEffects": [], + "fromStatusEffect": false + }, message); }; AddMock(target, IID_Footprint, { Index: ps/trunk/binaries/data/mods/public/simulation/components/tests/test_Pack.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/tests/test_Pack.js +++ ps/trunk/binaries/data/mods/public/simulation/components/tests/test_Pack.js @@ -11,6 +11,7 @@ Engine.LoadComponentScript("interfaces/Player.js"); Engine.LoadComponentScript("interfaces/Promotion.js"); Engine.LoadComponentScript("interfaces/ResourceGatherer.js"); +Engine.LoadComponentScript("interfaces/StatusEffectsReceiver.js"); Engine.LoadComponentScript("interfaces/Timer.js"); Engine.LoadComponentScript("interfaces/UnitAI.js"); Engine.LoadComponentScript("Pack.js"); Index: ps/trunk/binaries/data/mods/public/simulation/components/tests/test_StatusEffectsReceiver.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/tests/test_StatusEffectsReceiver.js +++ ps/trunk/binaries/data/mods/public/simulation/components/tests/test_StatusEffectsReceiver.js @@ -7,6 +7,8 @@ var cmpStatusReceiver; var cmpTimer; var dealtDamage; +var enemyEntity = 4; +var enemy = 2; function setup() { @@ -31,6 +33,10 @@ "Damage": { [statusName]: 1 } + }, + { + "entity": enemyEntity, + "owner": enemy, }); cmpTimer.OnUpdate({ "turnLength": 1 }); @@ -63,7 +69,7 @@ Engine.RegisterGlobal("Attacking", Attacking); // damage scheduled: 0, 1, 2, 10 sec - cmpStatusReceiver.GiveStatus({ + cmpStatusReceiver.ApplyStatus({ "Burn": { "Duration": 20000, "Interval": 10000, @@ -111,6 +117,10 @@ "Damage": { [statusName]: 1 } + }, + { + "entity": enemyEntity, + "owner": enemy, }); cmpTimer.OnUpdate({ "turnLength": 1 }); Index: ps/trunk/binaries/data/mods/public/simulation/helpers/Attacking.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/helpers/Attacking.js +++ ps/trunk/binaries/data/mods/public/simulation/helpers/Attacking.js @@ -3,50 +3,70 @@ */ function Attacking() {} +const DirectEffectsSchema = + "" + + "" + + "" + + "" + + // Armour requires Foundation to not be a damage type. + "Foundation" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + ""; + +const StatusEffectsSchema = + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + DirectEffectsSchema + + "" + + "" + + "" + + "" + + "" + + ModificationsSchema + + "" + + "" + + "" + + "" + + ""; + /** * Builds a RelaxRNG schema of possible attack effects. * See globalscripts/AttackEffects.js for possible elements. * Attacks may also have a "Bonuses" element. * - * @return {string} - RelaxNG schema string + * @return {string} - RelaxNG schema string. */ -const DamageSchema = "" + - "" + - "" + - "" + - // Armour requires Foundation to not be a damage type. - "Foundation" + - "" + - "" + - "" + - ""; - Attacking.prototype.BuildAttackEffectsSchema = function() { return "" + "" + "" + - "" + - DamageSchema + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + DamageSchema + "" + - "" + - "" + - "" + - "" + + DirectEffectsSchema + + StatusEffectsSchema + "" + "" + "" + @@ -85,8 +105,8 @@ if (template.Capture) ret.Capture = ApplyValueModificationsToEntity(valueModifRoot + "/Capture", +(template.Capture || 0), entity); - if (template.GiveStatus) - ret.GiveStatus = template.GiveStatus; + if (template.ApplyStatus) + ret.ApplyStatus = template.ApplyStatus; if (template.Bonuses) ret.Bonuses = template.Bonuses; @@ -250,10 +270,6 @@ Object.assign(targetState, cmpReceiver[receiver.method](attackData[effectType], attacker, attackerOwner, bonusMultiplier)); } - let cmpPromotion = Engine.QueryInterface(attacker, IID_Promotion); - if (cmpPromotion && targetState.xp) - cmpPromotion.IncreaseXp(targetState.xp); - if (targetState.killed) this.TargetKilled(attacker, target, attackerOwner); @@ -265,7 +281,16 @@ "damage": -(targetState.HPchange || 0), "capture": targetState.captureChange || 0, "statusEffects": targetState.inflictedStatuses || [], + "fromStatusEffect": !!attackData.StatusEffect, }); + + // We do not want an entity to get XP from active Status Effects. + if (!!attackData.StatusEffect) + return; + + let cmpPromotion = Engine.QueryInterface(attacker, IID_Promotion); + if (cmpPromotion && targetState.xp) + cmpPromotion.IncreaseXp(targetState.xp); }; /** Index: ps/trunk/binaries/data/mods/public/simulation/helpers/Transform.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/helpers/Transform.js +++ ps/trunk/binaries/data/mods/public/simulation/helpers/Transform.js @@ -110,6 +110,20 @@ } } + let cmpStatusEffectsReceiver = Engine.QueryInterface(oldEnt, IID_StatusEffectsReceiver); + let cmpNewStatusEffectsReceiver = Engine.QueryInterface(newEnt, IID_StatusEffectsReceiver); + if (cmpStatusEffectsReceiver && cmpNewStatusEffectsReceiver) + { + let activeStatus = cmpStatusEffectsReceiver.GetActiveStatuses(); + for (let status in activeStatus) + { + let newStatus = activeStatus[status]; + if (newStatus.Duration) + newStatus.Duration -= newStatus._timeElapsed; + cmpNewStatusEffectsReceiver.ApplyStatus({ [status]: newStatus }, newStatus.source.entity, newStatus.source.owner); + } + } + TransferGarrisonedUnits(oldEnt, newEnt); Engine.PostMessage(oldEnt, MT_EntityRenamed, { "entity": oldEnt, "newentity": newEnt });