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