Index: binaries/data/mods/public/maps/random/polar_sea_triggers.js =================================================================== --- binaries/data/mods/public/maps/random/polar_sea_triggers.js +++ binaries/data/mods/public/maps/random/polar_sea_triggers.js @@ -38,8 +38,8 @@ if (!attackerPos) continue; - // The returned entities are sorted by RangeManager already - let targets = Attacking.EntitiesNearPoint(attackerPos, 200, players).filter(ent => { + // The returned entities are sorted by RangeManager already. + let targets = Attacking.EntitiesNearPoint(attackerPos, 0, 200, players).filter(ent => { let cmpIdentity = Engine.QueryInterface(ent, IID_Identity); return cmpIdentity && MatchesClassList(cmpIdentity.GetClassesList(), targetClasses); }); Index: binaries/data/mods/public/simulation/components/Attack.js =================================================================== --- binaries/data/mods/public/simulation/components/Attack.js +++ binaries/data/mods/public/simulation/components/Attack.js @@ -139,6 +139,9 @@ "" + "" + "" + + "" + + "" + + "" + "" + "" + Attacking.BuildAttackEffectsSchema() + @@ -422,7 +425,8 @@ return { "attackData": this.GetAttackEffectsData(type, true), "friendlyFire": this.template[type].Splash.FriendlyFire == "true", - "radius": ApplyValueModificationsToEntity("Attack/" + type + "/Splash/Range", +this.template[type].Splash.Range, this.entity), + "minRange": ApplyValueModificationsToEntity("Attack/" + type + "/Splash/MinRange", +(this.template[type].Splash.MinRange || 0), this.entity), + "maxRange": ApplyValueModificationsToEntity("Attack/" + type + "/Splash/Range", +this.template[type].Splash.Range, this.entity), "shape": this.template[type].Splash.Shape, }; }; Index: binaries/data/mods/public/simulation/components/DeathDamage.js =================================================================== --- binaries/data/mods/public/simulation/components/DeathDamage.js +++ binaries/data/mods/public/simulation/components/DeathDamage.js @@ -31,6 +31,9 @@ "" + "" + "" + + "" + + "" + + "" + "" + "" + Attacking.BuildAttackEffectsSchema(); @@ -58,7 +61,8 @@ if (owner == INVALID_PLAYER) warn("Unit causing death damage does not have any owner."); - let radius = ApplyValueModificationsToEntity("DeathDamage/Range", +this.template.Range, this.entity); + let minRange = ApplyValueModificationsToEntity("DeathDamage/MinRange", +(this.template.MinRange || 0), this.entity); + let maxRange = ApplyValueModificationsToEntity("DeathDamage/Range", +this.template.Range, this.entity); Attacking.CauseDamageOverArea({ "type": "Death", @@ -66,7 +70,8 @@ "attacker": this.entity, "attackerOwner": owner, "origin": pos, - "radius": radius, + "minRange": minRange, + "maxRange": maxRange, "shape": this.template.Shape, "friendlyFire": this.template.FriendlyFire == "true", }); Index: binaries/data/mods/public/simulation/components/DelayedDamage.js =================================================================== --- binaries/data/mods/public/simulation/components/DelayedDamage.js +++ binaries/data/mods/public/simulation/components/DelayedDamage.js @@ -45,7 +45,8 @@ "attacker": data.attacker, "attackerOwner": data.attackerOwner, "origin": Vector2D.from3D(data.position), - "radius": data.splash.radius, + "minRange": data.splash.minRange, + "maxRange": data.splash.maxRange, "shape": data.splash.shape, "direction": data.direction, "friendlyFire": data.splash.friendlyFire @@ -69,7 +70,7 @@ return; // If we didn't hit the main target look for nearby units. - let ents = Attacking.EntitiesNearPoint(Vector2D.from3D(data.position), + let ents = Attacking.EntitiesNearPoint(Vector2D.from3D(data.position), 0, targetPosition.horizDistanceTo(data.position) * 2, Attacking.GetPlayersToDamage(data.attackerOwner, data.friendlyFire)); Index: binaries/data/mods/public/simulation/components/tests/test_Attack.js =================================================================== --- binaries/data/mods/public/simulation/components/tests/test_Attack.js +++ binaries/data/mods/public/simulation/components/tests/test_Attack.js @@ -200,7 +200,8 @@ } }, "friendlyFire": false, - "radius": 10, + "minRange": 0, + "maxRange": 10, "shape": "Circular" }); }); Index: binaries/data/mods/public/simulation/components/tests/test_Damage.js =================================================================== --- binaries/data/mods/public/simulation/components/tests/test_Damage.js +++ binaries/data/mods/public/simulation/components/tests/test_Damage.js @@ -176,7 +176,8 @@ "attacker": attacker, "attackerOwner": attackerOwner, "origin": origin, - "radius": 10, + "minRange": 0, + "maxRange": 10, "shape": "Linear", "direction": new Vector3D(1, 747, 0), "friendlyFire": false, @@ -184,7 +185,7 @@ let fallOff = function(x, y) { - return (1 - x * x / (data.radius * data.radius)) * (1 - 25 * y * y / (data.radius * data.radius)); + return (1 - x * x / (data.maxRange * data.maxRange)) * (1 - 25 * y * y / (data.maxRange * data.maxRange)); }; let hitEnts = new Set(); @@ -350,7 +351,8 @@ "attacker": 50, "attackerOwner": attackerOwner, "origin": new Vector2D(3, 4), - "radius": radius, + "minRange": 0, + "maxRange": radius, "shape": "Circular", "friendlyFire": false, }); @@ -481,7 +483,8 @@ // Add a splash damage. data.splash = {}; data.splash.friendlyFire = false; - data.splash.radius = 10; + data.splash.minRange = 0; + data.splash.maxRange = 10; data.splash.shape = "Circular"; data.splash.attackData = { "Damage": { "Hack": 0, "Pierce": 0, "Crush": 200 } }; @@ -576,7 +579,8 @@ // Test splash damage with friendly fire. data.splash = {}; data.splash.friendlyFire = true; - data.splash.radius = 10; + data.splash.minRange = 10; + data.splash.maxRange = 10; data.splash.shape = "Circular"; data.splash.attackData = { "Damage": { "Pierce": 0, "Crush": 200 } }; Index: binaries/data/mods/public/simulation/components/tests/test_DeathDamage.js =================================================================== --- binaries/data/mods/public/simulation/components/tests/test_DeathDamage.js +++ binaries/data/mods/public/simulation/components/tests/test_DeathDamage.js @@ -45,7 +45,8 @@ "attacker": deadEnt, "attackerOwner": player, "origin": pos, - "radius": template.Range, + "minRange": 0, + "maxRange": template.Range, "shape": template.Shape, "friendlyFire": false }; Index: binaries/data/mods/public/simulation/helpers/Attacking.js =================================================================== --- binaries/data/mods/public/simulation/helpers/Attacking.js +++ binaries/data/mods/public/simulation/helpers/Attacking.js @@ -205,14 +205,15 @@ * @param {number} data.attacker - The entity id of the attacker. * @param {number} data.attackerOwner - The player id of the attacker. * @param {Vector2D} data.origin - The origin of the projectile hit. - * @param {number} data.radius - The radius of the splash damage. + * @param {number} data.minRange - The minimal radius of the area damage. + * @param {number} data.maxRange - The maximum radius of the area damage. * @param {string} data.shape - The shape of the radius. * @param {Vector3D} [data.direction] - The unit vector defining the direction. Needed for linear splash damage. * @param {boolean} data.friendlyFire - A flag indicating if allied entities also ought to be damaged. */ Attacking.prototype.CauseDamageOverArea = function(data) { - let nearEnts = this.EntitiesNearPoint(data.origin, data.radius, + let nearEnts = this.EntitiesNearPoint(data.origin, data.minRange, data.maxRange, this.GetPlayersToDamage(data.attackerOwner, data.friendlyFire)); let damageMultiplier = 1; @@ -221,7 +222,7 @@ { let entityPosition = Engine.QueryInterface(ent, IID_Position).GetPosition2D(); if (data.shape == 'Circular') // circular effect with quadratic falloff in every direction - damageMultiplier = 1 - data.origin.distanceToSquared(entityPosition) / (data.radius * data.radius); + damageMultiplier = 1 - data.origin.distanceToSquared(entityPosition) / (data.maxRange * data.maxRange); else if (data.shape == 'Linear') // linear effect with quadratic falloff in two directions (only used for certain missiles) { // Get position of entity relative to splash origin. @@ -233,12 +234,12 @@ let perpPos = relativePos.cross(direction); // The width of linear splash is one fifth of the normal splash radius. - let width = data.radius / 5; + let width = data.maxRange / 5; // Check that the unit is within the distance splash width of the line starting at the missile's // landing point which extends in the direction of the missile for length splash radius. if (parallelPos >= 0 && Math.abs(perpPos) < width) // If in radius, quadratic falloff in both directions - damageMultiplier = (1 - parallelPos * parallelPos / (data.radius * data.radius)) * + damageMultiplier = (1 - parallelPos * parallelPos / (data.maxRange * data.maxRange)) * (1 - perpPos * perpPos / (width * width)); else damageMultiplier = 0; @@ -296,14 +297,15 @@ /** * Gets entities near a give point for given players. * @param {Vector2D} origin - The point to check around. - * @param {number} radius - The radius around the point to check. + * @param {number} minRange - The minimum distance the entities may have from the point to check. + * @param {number} maxRange - The maximum distance the entities may have from the point to check. * @param {number[]} players - The players of which we need to check entities. * @return {number[]} The id's of the entities in range of the given point. */ -Attacking.prototype.EntitiesNearPoint = function(origin, radius, players) +Attacking.prototype.EntitiesNearPoint = function(origin, minRange, maxRange, players) { // If there is insufficient data return an empty array. - if (!origin || !radius || !players || !players.length) + if (!origin || !maxRange || !players || !players.length) return []; let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); @@ -314,12 +316,12 @@ if (gaiaIndex !== -1) { // splice() modifies players in-place and returns [0] - gaiaEntities = gaiaEntities.concat(cmpRangeManager.ExecuteQueryAroundPos(origin, 0, radius, players.splice(gaiaIndex, 1), IID_Health)); + gaiaEntities = gaiaEntities.concat(cmpRangeManager.ExecuteQueryAroundPos(origin, minRange, maxRange, players.splice(gaiaIndex, 1), IID_Health)); if (!players.length) return gaiaEntities; } - return cmpRangeManager.ExecuteQueryAroundPos(origin, 0, radius, players, 0).concat(gaiaEntities); + return cmpRangeManager.ExecuteQueryAroundPos(origin, minRange, maxRange, players, 0).concat(gaiaEntities); }; /**