diff --git a/binaries/data/mods/public/globalscripts/Templates.js b/binaries/data/mods/public/globalscripts/Templates.js index 36d88c3bc5..5fd20049e8 100644 --- a/binaries/data/mods/public/globalscripts/Templates.js +++ b/binaries/data/mods/public/globalscripts/Templates.js @@ -98,28 +98,37 @@ function GetTemplateDataHelper(template, player, auraTemplates) if (template.Attack) { - let getAttackStat = function(type, stat) - { - return getEntityValue("Attack/" + type + "/"+stat); - }; - ret.attack = {}; for (let type in template.Attack) { + let getAttackStat = function(stat) { + return getEntityValue("Attack/" + type + "/" + stat); + }; + if (type == "Capture") ret.attack.Capture = { - "value": getAttackStat(type,"Value"), + "value": getAttackStat("Value") }; else ret.attack[type] = { - "hack": getAttackStat(type, "Hack"), - "pierce": getAttackStat(type, "Pierce"), - "crush": getAttackStat(type, "Crush"), - "minRange": getAttackStat(type, "MinRange"), - "maxRange": getAttackStat(type, "MaxRange"), - "elevationBonus": getAttackStat(type, "ElevationBonus"), + "hack": getAttackStat("Hack"), + "pierce": getAttackStat("Pierce"), + "crush": getAttackStat("Crush"), + "minRange": getAttackStat("MinRange"), + "maxRange": getAttackStat("MaxRange"), + "elevationBonus": getAttackStat("ElevationBonus") + }; + + ret.attack[type].repeatTime = getAttackStat("RepeatTime"); + + if (template.Attack[type].Splash) + ret.attack[type].splash = { + "hack": getAttackStat("Splash/Hack"), + "pierce": getAttackStat("Splash/Pierce"), + "crush": getAttackStat("Splash/Crush"), + // true if undefined + "friendlyFire": template.Attack[type].Splash.FriendlyFire != "false" }; - ret.attack[type].repeatTime = +(template.Attack[type].RepeatTime || 0); } } diff --git a/binaries/data/mods/public/gui/common/tooltips.js b/binaries/data/mods/public/gui/common/tooltips.js index 55e04477b6..95d5ef36e2 100644 --- a/binaries/data/mods/public/gui/common/tooltips.js +++ b/binaries/data/mods/public/gui/common/tooltips.js @@ -137,7 +137,7 @@ function damageTypesToText(dmg) })).join(commaFont(translate(", "))); } -// TODO: should also show minRange and splash damage +// TODO: should also show minRange function getAttackTooltip(template) { if (!template.attack) @@ -198,6 +198,33 @@ function getAttackTooltip(template) return attacks.join("\n"); } +function getSplashDamageTooltip(template) +{ + if (!template.attack) + return ""; + + let tooltips = []; + for (let type in template.attack) + { + let splash = template.attack[type].splash; + if (!splash) + continue; + + tooltips.push([ + sprintf(translate("%(attackLabel)s %(damageTypes)s"), { + "attackLabel": headerFont(translate("Splash Damage:")), + "damageTypes": damageTypesToText(splash) + }), + sprintf(translate("Friendly Fire: %(enabled)s"), { + "enabled": splash.friendlyFire ? translate("Yes") : translate("No") + }) + ].join(commaFont(translate(", ")))); + } + + // If multiple attack types deal splash damage, the attack type should be shown to differentiate. + return tooltips.join("\n"); +} + function getGarrisonTooltip(template) { if (!template.garrisonHolder) diff --git a/binaries/data/mods/public/gui/session/selection_details.js b/binaries/data/mods/public/gui/session/selection_details.js index 141cb2c87d..6b6ab41a22 100644 --- a/binaries/data/mods/public/gui/session/selection_details.js +++ b/binaries/data/mods/public/gui/session/selection_details.js @@ -284,6 +284,7 @@ function displaySingle(entState) Engine.GetGUIObjectByName("attackAndArmorStats").tooltip = [ getAttackTooltip, + getSplashDamageTooltip, getHealerTooltip, getArmorTooltip, getRepairRateTooltip, diff --git a/binaries/data/mods/public/gui/session/selection_panels.js b/binaries/data/mods/public/gui/session/selection_panels.js index 028225026f..19558f4e81 100644 --- a/binaries/data/mods/public/gui/session/selection_panels.js +++ b/binaries/data/mods/public/gui/session/selection_panels.js @@ -975,15 +975,16 @@ g_SelectionPanels.Training = { tooltips.push(formatLimitString(limits.entLimit, limits.entCount, limits.entLimitChangers)); if (Engine.ConfigDB_GetValue("user", "showdetailedtooltips") === "true") - tooltips.push( - getHealthTooltip(template), - getAttackTooltip(template), - getHealerTooltip(template), - getArmorTooltip(template), - getGarrisonTooltip(template), - getProjectilesTooltip(template), - getSpeedTooltip(template) - ); + tooltips = tooltips.concat([ + getHealthTooltip, + getAttackTooltip, + getSplashDamageTooltip, + getHealerTooltip, + getArmorTooltip, + getGarrisonTooltip, + getProjectilesTooltip, + getSpeedTooltip + ].map(func => func(template))); tooltips.push( "[color=\"" + g_HotkeyColor + "\"]" + diff --git a/binaries/data/mods/public/gui/structree/draw.js b/binaries/data/mods/public/gui/structree/draw.js index 870d63e62b..92c60ce433 100644 --- a/binaries/data/mods/public/gui/structree/draw.js +++ b/binaries/data/mods/public/gui/structree/draw.js @@ -8,6 +8,7 @@ var g_TooltipFunctions = [ getHealthTooltip, getHealerTooltip, getAttackTooltip, + getSplashDamageTooltip, getArmorTooltip, getGarrisonTooltip, getProjectilesTooltip, diff --git a/binaries/data/mods/public/simulation/components/Attack.js b/binaries/data/mods/public/simulation/components/Attack.js index c997aad87d..978a36d76a 100644 --- a/binaries/data/mods/public/simulation/components/Attack.js +++ b/binaries/data/mods/public/simulation/components/Attack.js @@ -407,6 +407,17 @@ Attack.prototype.GetAttackStrengths = function(type) }; }; +Attack.prototype.GetSplashDamage = function(type) +{ + if (!this.template[type].Splash) + return false; + + return { + "friendlyFire": this.template[type].Splash.FriendlyFire, + "attackStrengths": this.GetAttackStrengths(type + ".Splash") + }; +}; + Attack.prototype.GetRange = function(type) { let max = +this.template[type].MaxRange; @@ -645,19 +656,20 @@ Attack.prototype.MissileHit = function(data, lateness) if (!targetPosition) return; - if (this.template.Ranged.Splash) // splash damage, do this first in case the direct hit kills the target + // Do this first in case the direct hit kills the target + if (this.template.Ranged.Splash) { let friendlyFire = this.template.Ranged.Splash.FriendlyFire; let splashRadius = this.template.Ranged.Splash.Range; let splashShape = this.template.Ranged.Splash.Shape; let playersToDamage; - // If friendlyFire isn't enabled, get all player enemies to pass to "Damage.CauseSplashDamage". + if (friendlyFire == "false") { let cmpPlayer = QueryPlayerIDInterface(data.playerId); playersToDamage = cmpPlayer.GetEnemies(); } - // Damage the units. + Damage.CauseSplashDamage({ "attacker": this.entity, "origin": Vector2D.from3D(data.position), @@ -666,7 +678,7 @@ Attack.prototype.MissileHit = function(data, lateness) "strengths": this.GetAttackStrengths(data.type), "direction": data.direction, "playersToDamage": playersToDamage, - "type":data.type + "type": data.type }); } @@ -690,19 +702,20 @@ Attack.prototype.MissileHit = function(data, lateness) for (let i = 0; i < ents.length; ++i) { - if (this.testCollision(ents[i], data.position, lateness)) - { - Damage.CauseDamage({ - "strengths": this.GetAttackStrengths(data.type), - "target": ents[i], - "attacker": this.entity, - "multiplier": this.GetAttackBonus(data.type, ents[i]), - "type": data.type - }); - // Remove the projectile - let cmpProjectileManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ProjectileManager); - cmpProjectileManager.RemoveProjectile(data.projectileId); - } + if (!this.testCollision(ents[i], data.position, lateness)) + continue; + + Damage.CauseDamage({ + "strengths": this.GetAttackStrengths(data.type), + "target": ents[i], + "attacker": this.entity, + "multiplier": this.GetAttackBonus(data.type, ents[i]), + "type": data.type + }); + + // Remove the projectile + let cmpProjectileManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ProjectileManager); + cmpProjectileManager.RemoveProjectile(data.projectileId); } } }; diff --git a/binaries/data/mods/public/simulation/components/GuiInterface.js b/binaries/data/mods/public/simulation/components/GuiInterface.js index 1f99e4b980..4f0ed5d6fe 100644 --- a/binaries/data/mods/public/simulation/components/GuiInterface.js +++ b/binaries/data/mods/public/simulation/components/GuiInterface.js @@ -329,12 +329,9 @@ GuiInterface.prototype.GetEntityState = function(player, ent) let cmpFogging = Engine.QueryInterface(ent, IID_Fogging); if (cmpFogging) - { - if (cmpFogging.IsMiraged(player)) - ret.fogging = { "mirage": cmpFogging.GetMirage(player) }; - else - ret.fogging = { "mirage": null }; - } + ret.fogging = { + "mirage": cmpFogging.IsMiraged(player) ? cmpFogging.GetMirage(player) : null + }; let cmpFoundation = QueryMiragedInterface(ent, IID_Foundation); if (cmpFoundation) @@ -444,12 +441,16 @@ GuiInterface.prototype.GetExtendedEntityState = function(player, ent) 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 @@ -459,12 +460,14 @@ GuiInterface.prototype.GetExtendedEntityState = function(player, ent) } ret.attack[type].elevationBonus = range.elevationBonus; + let cmpPosition = Engine.QueryInterface(ent, IID_Position); let cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI); let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); + if (cmpUnitAI && cmpPosition && cmpPosition.IsInWorld()) { - // For units, take the rage in front of it, no spread. So angle = 0 + // 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); } else if(cmpPosition && cmpPosition.IsInWorld()) diff --git a/binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_onager.xml b/binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_onager.xml index f42a38d104..21c997c686 100644 --- a/binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_onager.xml +++ b/binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_onager.xml @@ -45,7 +45,6 @@ Siege Catapult - Causes splash damage. Ranged Catapult