Index: binaries/data/mods/public/simulation/components/Damage.js
===================================================================
--- binaries/data/mods/public/simulation/components/Damage.js
+++ binaries/data/mods/public/simulation/components/Damage.js
@@ -231,6 +231,51 @@
}
};
+Damage.prototype.CauseProximityDamage = function(data)
+{
+ // Get nearby entities and define variables
+ let nearEnts = this.EntitiesNearPoint(data.origin, data.radius, data.playersToDamage);
+ let damageMultiplier = 1;
+
+ // Cycle through all the nearby entities and damage it appropriately based on its distance from the origin.
+ for (let ent of nearEnts)
+ {
+ // Do not damage the attacker itself
+ if (ent == data.attacker)
+ continue;
+
+ // Do not damage restricted classes
+ let cmpProximityDamage = Engine.QueryInterface(data.attacker, IID_ProximityDamage)
+ let restrictedClasses = cmpProximityDamage.GetRestrictedClasses();
+ let cmpIdentity = QueryMiragedInterface(ent, IID_Identity);
+ let targetClasses = cmpIdentity.GetClassesList();
+ if (restrictedClasses.length && MatchesClassList(targetClasses, restrictedClasses))
+ continue;
+
+ // Calculate distance effects
+ let entityPosition = Engine.QueryInterface(ent, IID_Position).GetPosition2D();
+ if (data.shape == 'Circular')
+ damageMultiplier = 1 - data.origin.distanceToSquared(entityPosition) / (data.radius * data.radius);
+ else
+ {
+ warn("The " + data.shape + " proximity damage shape is not implemented!");
+ }
+
+ if (data.proximityBonus)
+ damageMultiplier *= GetDamageBonus(ent, data.proximityBonus);
+
+ // Call CauseDamage which reduces the hitpoints, posts network command, plays sounds....
+ this.CauseDamage({
+ "strengths": data.strengths,
+ "target": ent,
+ "attacker": data.attacker,
+ "multiplier": damageMultiplier,
+ "type": data.type + ".Proximity",
+ "attackerOwner": data.attackerOwner
+ });
+ }
+};
+
/**
* Causes damage on a given unit.
* @param {Object} data - the data passed by the caller.
Index: binaries/data/mods/public/simulation/components/ProximityDamage.js
===================================================================
--- /dev/null
+++ binaries/data/mods/public/simulation/components/ProximityDamage.js
@@ -0,0 +1,146 @@
+function ProximityDamage() {}
+
+ProximityDamage.prototype.bonusesSchema =
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "";
+
+ProximityDamage.prototype.restrictedClassesSchema =
+ "" +
+ "" +
+ "" +
+ "tokens" +
+ "" +
+ "" +
+ "" +
+ "";
+
+ProximityDamage.prototype.Schema =
+ "Whether a unit or building inflicts damage to nearby units." +
+ "" +
+ "Circular" +
+ "10" +
+ "true" +
+ "0.0" +
+ "10.0" +
+ "50.0" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ DamageTypes.BuildSchema("damage strength") +
+ ProximityDamage.prototype.restrictedClassesSchema +
+ ProximityDamage.prototype.bonusesSchema;
+
+ProximityDamage.prototype.Init = function()
+{
+ this.CheckTimer();
+};
+
+ProximityDamage.prototype.GetRestrictedClasses = function()
+{
+ if (this.template.RestrictedClasses &&
+ this.template.RestrictedClasses._string)
+ return this.template.RestrictedClasses._string.split(/\s+/);
+
+ return [];
+};
+
+ProximityDamage.prototype.Serialize = null; // We have no dynamic state to save
+
+ProximityDamage.prototype.GetProximityDamageStrengths = function()
+{
+ // Work out the damage values with technology effects
+ let applyMods = damageType =>
+ ApplyValueModificationsToEntity("ProximityDamage/" + damageType, +(this.template[damageType] || 0), this.entity);
+
+ let ret = {};
+ for (let damageType of DamageTypes.GetTypes())
+ ret[damageType] = applyMods(damageType);
+
+ return ret;
+};
+
+ProximityDamage.prototype.GetBonusTemplate = function()
+{
+ return this.template.Bonuses || null;
+};
+
+ProximityDamage.prototype.CauseProximityDamage = function()
+{
+ let cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
+ if (!cmpPosition || !cmpPosition.IsInWorld())
+ return;
+ let pos = cmpPosition.GetPosition2D();
+
+ let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
+ let owner = cmpOwnership.GetOwner();
+ if (owner == INVALID_PLAYER)
+ warn("Unit causing proximity damage does not have any owner.");
+
+ let cmpDamage = Engine.QueryInterface(SYSTEM_ENTITY, IID_Damage);
+ let playersToDamage = cmpDamage.GetPlayersToDamage(owner, this.template.FriendlyFire);
+
+ let radius = ApplyValueModificationsToEntity("ProximityDamage/Range", +this.template.Range, this.entity);
+
+ cmpDamage.CauseProximityDamage({
+ "attacker": this.entity,
+ "origin": pos,
+ "radius": radius,
+ "shape": this.template.Shape,
+ "strengths": this.GetProximityDamageStrengths(),
+ "proximityBonus": this.GetBonusTemplate(),
+ "playersToDamage": playersToDamage,
+ "type": "Proximity",
+ "attackerOwner": owner
+ });
+};
+
+// Start the damage timer when no timer exists.
+ProximityDamage.prototype.CheckTimer = function()
+{
+ if (this.template.OnlyWhenMoving == "true")
+ {
+ let cmpUnitAI = Engine.QueryInterface(this.entity, IID_UnitAI);
+ if (cmpUnitAI && !cmpUnitAI.IsMoving())
+ {
+ // We don't need a timer, disable if one exists
+ if (this.proximityDamageTimer)
+ {
+ let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
+ cmpTimer.CancelTimer(this.proximityDamageTimer);
+ this.proximityDamageTimer = undefined;
+ }
+ return;
+ }
+ }
+
+ // We need a timer, enable if one doesn't exist
+ if (this.proximityDamageTimer)
+ return;
+
+ let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
+ this.proximityDamageTimer = cmpTimer.SetInterval(this.entity, IID_ProximityDamage, "CauseProximityDamage", 100, 100, null);
+};
+
+// If the entity starts or stops moving check the timer
+ProximityDamage.prototype.OnUnitAIStateChanged = function(msg)
+{
+ this.CheckTimer();
+};
+
+Engine.RegisterComponentType(IID_ProximityDamage, "ProximityDamage", ProximityDamage);
Index: binaries/data/mods/public/simulation/components/UnitAI.js
===================================================================
--- binaries/data/mods/public/simulation/components/UnitAI.js
+++ binaries/data/mods/public/simulation/components/UnitAI.js
@@ -3321,6 +3321,12 @@
return (state == "WALKING");
};
+UnitAI.prototype.IsMoving = function()
+{
+ let state = this.GetCurrentState().split(".").pop();
+ return (state == "WALKING" || state == "APPROACHING" || state == "RUNNING" || state == "CHARGING" || state == "FLEEING");
+};
+
/**
* Return true if the current order is WalkAndFight or Patrol.
*/
Index: binaries/data/mods/public/simulation/components/interfaces/ProximityDamage.js
===================================================================
--- /dev/null
+++ binaries/data/mods/public/simulation/components/interfaces/ProximityDamage.js
@@ -0,0 +1 @@
+Engine.RegisterInterface("ProximityDamage");