From 82a5ab6d1930d7f403067fbc27aff93f44796eb5 Mon Sep 17 00:00:00 2001 From: Angen Date: Mon, 27 Jan 2020 16:51:25 +0000 Subject: [PATCH] Allow status effect to apply modifiers and fix 2333b1814e Status effect can apply modifier to the entity (armour strength, walking speed, ...) This is also hiding status effect icons by default in selection detail. Fixing smaller related bugs. Differential Revision: https://code.wildfiregames.com/D2281 Patch by: @Freagarach Comments by: Stan, wraitii This was SVN commit r23448. --- .../public/globalscripts/AttackEffects.js | 6 +- .../globalscripts/ModificationTemplates.js | 157 ++++++++++++++++++ .../mods/public/globalscripts/Technologies.js | 68 -------- .../mods/public/globalscripts/Templates.js | 4 +- .../data/mods/public/gui/common/tooltips.js | 58 +++++-- .../public/gui/session/selection_details.js | 8 +- .../components/StatusEffectsReceiver.js | 118 ++++++++++--- .../public/simulation/components/UnitAI.js | 3 + .../components/tests/test_Damage.js | 11 +- .../simulation/components/tests/test_Pack.js | 1 + .../tests/test_StatusEffectsReceiver.js | 12 +- .../public/simulation/helpers/Attacking.js | 103 +++++++----- .../public/simulation/helpers/Transform.js | 14 ++ 13 files changed, 412 insertions(+), 151 deletions(-) diff --git a/binaries/data/mods/public/globalscripts/AttackEffects.js b/binaries/data/mods/public/globalscripts/AttackEffects.js index 48d42eb590..0af93a0669 100755 --- a/binaries/data/mods/public/globalscripts/AttackEffects.js +++ b/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 @@ const g_EffectReceiver = { "IID": "IID_Capturable", "method": "Capture" }, - "GiveStatus": { + "ApplyStatus": { "IID": "IID_StatusEffectsReceiver", - "method": "GiveStatus" + "method": "ApplyStatus" } }; diff --git a/binaries/data/mods/public/globalscripts/ModificationTemplates.js b/binaries/data/mods/public/globalscripts/ModificationTemplates.js index 37071475a3..f8d3bc1ee6 100644 --- a/binaries/data/mods/public/globalscripts/ModificationTemplates.js +++ b/binaries/data/mods/public/globalscripts/ModificationTemplates.js @@ -43,3 +43,160 @@ function LoadModificationTemplates() 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; +} diff --git a/binaries/data/mods/public/globalscripts/Technologies.js b/binaries/data/mods/public/globalscripts/Technologies.js index 9ff60fddc5..3701dbf209 100644 --- a/binaries/data/mods/public/globalscripts/Technologies.js +++ b/binaries/data/mods/public/globalscripts/Technologies.js @@ -39,74 +39,6 @@ function GetTechModifiedProperty(modifications, classes, originalValue) return originalValue; } -/** - * 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 */ diff --git a/binaries/data/mods/public/globalscripts/Templates.js b/binaries/data/mods/public/globalscripts/Templates.js index 3c29610c36..f337e96df2 100644 --- a/binaries/data/mods/public/globalscripts/Templates.js +++ b/binaries/data/mods/public/globalscripts/Templates.js @@ -180,7 +180,9 @@ function GetTemplateDataHelper(template, player, auraTemplates, modifiers = {}) effects.Damage[damageType] = getEntityValue(path + "/Damage/" + damageType); } - // TODO: status effects + if (temp.ApplyStatus) + effects.ApplyStatus = temp.ApplyStatus; + return effects; }; diff --git a/binaries/data/mods/public/gui/common/tooltips.js b/binaries/data/mods/public/gui/common/tooltips.js index 35024e3964..f71e7c7cb7 100644 --- a/binaries/data/mods/public/gui/common/tooltips.js +++ b/binaries/data/mods/public/gui/common/tooltips.js @@ -275,13 +275,13 @@ function captureDetails(captureTemplate) }); } -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 @@ function attackEffectsDetails(attackTypeTemplate) 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 @@ function getAttackTooltip(template) // 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 @@ function getSplashDamageTooltip(template) 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 }); } diff --git a/binaries/data/mods/public/gui/session/selection_details.js b/binaries/data/mods/public/gui/session/selection_details.js index ca1b759543..43d77c273e 100644 --- a/binaries/data/mods/public/gui/session/selection_details.js +++ b/binaries/data/mods/public/gui/session/selection_details.js @@ -94,14 +94,16 @@ function displaySingle(entState) 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 @@ function displaySingle(entState) for (; i < statusIcons.length; ++i) statusIcons[i].hidden = true; } + else + Engine.GetGUIObjectByName("statusEffectsIcons").hidden = true; let showHealth = entState.hitpoints; let showResource = entState.resourceSupply; diff --git a/binaries/data/mods/public/simulation/components/StatusEffectsReceiver.js b/binaries/data/mods/public/simulation/components/StatusEffectsReceiver.js index 5d6b68fe69..0a19df0183 100755 --- a/binaries/data/mods/public/simulation/components/StatusEffectsReceiver.js +++ b/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; - let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); - status.Timer = cmpTimer.SetInterval(this.entity, IID_StatusEffectsReceiver, "ExecuteEffect", 0, +status.Interval, statusName); -}; + if (status.Modifiers) + { + let modifications = DeriveModificationsFromXMLTemplate(status.Modifiers); + let cmpModifiersManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ModifiersManager); + cmpModifiersManager.AddModifiers(statusName, modifications, this.entity); + } -StatusEffectsReceiver.prototype.RemoveStatus = function(statusName) -{ - if (!this.activeStatusEffects[statusName]) + // 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); - cmpTimer.CancelTimer(this.activeStatusEffects[statusName].Timer); + 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) +{ + let statusEffect = this.activeStatusEffects[statusName]; + if (!statusEffect) + return; + + 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; + status._timeElapsed += +(status.Interval || status._interval) + lateness; - Attacking.HandleAttackEffects(statusName, status, this.entity, -1, -1); - - if (status.Duration && status.TimeElapsed >= +status.Duration) + if (status._timeElapsed >= +status.Duration) this.RemoveStatus(statusName); }; diff --git a/binaries/data/mods/public/simulation/components/UnitAI.js b/binaries/data/mods/public/simulation/components/UnitAI.js index 155ad0b23e..0d86828601 100644 --- a/binaries/data/mods/public/simulation/components/UnitAI.js +++ b/binaries/data/mods/public/simulation/components/UnitAI.js @@ -4099,6 +4099,9 @@ UnitAI.prototype.OnGlobalEntityRenamed = function(msg) UnitAI.prototype.OnAttacked = function(msg) { + if (msg.fromStatusEffect) + return; + this.UnitFsm.ProcessMessage(this, {"type": "Attacked", "data": msg}); }; diff --git a/binaries/data/mods/public/simulation/components/tests/test_Damage.js b/binaries/data/mods/public/simulation/components/tests/test_Damage.js index 86687dd735..9dfdca4b5b 100644 --- a/binaries/data/mods/public/simulation/components/tests/test_Damage.js +++ b/binaries/data/mods/public/simulation/components/tests/test_Damage.js @@ -103,7 +103,16 @@ function Test_Generic() 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, { diff --git a/binaries/data/mods/public/simulation/components/tests/test_Pack.js b/binaries/data/mods/public/simulation/components/tests/test_Pack.js index 9c9435eefc..4e60bd8b33 100644 --- a/binaries/data/mods/public/simulation/components/tests/test_Pack.js +++ b/binaries/data/mods/public/simulation/components/tests/test_Pack.js @@ -11,6 +11,7 @@ Engine.LoadComponentScript("interfaces/Pack.js"); 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"); diff --git a/binaries/data/mods/public/simulation/components/tests/test_StatusEffectsReceiver.js b/binaries/data/mods/public/simulation/components/tests/test_StatusEffectsReceiver.js index c71ca31661..de38f6adc8 100755 --- a/binaries/data/mods/public/simulation/components/tests/test_StatusEffectsReceiver.js +++ b/binaries/data/mods/public/simulation/components/tests/test_StatusEffectsReceiver.js @@ -7,6 +7,8 @@ var target = 42; var cmpStatusReceiver; var cmpTimer; var dealtDamage; +var enemyEntity = 4; +var enemy = 2; function setup() { @@ -31,6 +33,10 @@ function testInflictEffects() "Damage": { [statusName]: 1 } + }, + { + "entity": enemyEntity, + "owner": enemy, }); cmpTimer.OnUpdate({ "turnLength": 1 }); @@ -63,7 +69,7 @@ function testMultipleEffects() Engine.RegisterGlobal("Attacking", Attacking); // damage scheduled: 0, 1, 2, 10 sec - cmpStatusReceiver.GiveStatus({ + cmpStatusReceiver.ApplyStatus({ "Burn": { "Duration": 20000, "Interval": 10000, @@ -111,6 +117,10 @@ function testRemoveStatus() "Damage": { [statusName]: 1 } + }, + { + "entity": enemyEntity, + "owner": enemy, }); cmpTimer.OnUpdate({ "turnLength": 1 }); diff --git a/binaries/data/mods/public/simulation/helpers/Attacking.js b/binaries/data/mods/public/simulation/helpers/Attacking.js index 861939ba94..d7ae6175a2 100644 --- a/binaries/data/mods/public/simulation/helpers/Attacking.js +++ b/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 @@ Attacking.prototype.GetAttackEffectsData = function(valueModifRoot, template, en 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 @@ Attacking.prototype.HandleAttackEffects = function(attackType, attackData, targe 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 @@ Attacking.prototype.HandleAttackEffects = function(attackType, attackData, targe "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); }; /** diff --git a/binaries/data/mods/public/simulation/helpers/Transform.js b/binaries/data/mods/public/simulation/helpers/Transform.js index 118a66ef1b..645cd31487 100644 --- a/binaries/data/mods/public/simulation/helpers/Transform.js +++ b/binaries/data/mods/public/simulation/helpers/Transform.js @@ -110,6 +110,20 @@ function ChangeEntityTemplate(oldEnt, newTemplate) } } + 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 });