Ticket #3610: DamageAfterDeathOfAtacker_v3.patch
File DamageAfterDeathOfAtacker_v3.patch, 24.7 KB (added by , 8 years ago) |
---|
-
binaries/data/mods/public/gui/credits/texts/programming.json
108 108 {"nick": "kingadami", "name": "Adam Winsor"}, 109 109 {"nick": "kingbasil", "name": "Giannis Fafalios"}, 110 110 {"nick": "lafferjm", "name": "Justin Lafferty"}, 111 {"nick": "LeanderH", "name": "Leander Hemelhof"}, 111 112 {"nick": "leper", "name": "Georg Kilzer"}, 112 113 {"nick": "LittleDev"}, 113 114 {"nick": "livingaftermidnight", "name": "Will Dull"}, -
binaries/data/mods/public/simulation/components/Attack.js
486 486 */ 487 487 Attack.prototype.PerformAttack = function(type, target) 488 488 { 489 let cmpDamage = Engine.QueryInterface(SYSTEM_ENTITY, IID_Damage); 489 490 // If this is a ranged attack, then launch a projectile 490 491 if (type == "Ranged") 491 492 { … … 555 556 556 557 let playerId = Engine.QueryInterface(this.entity, IID_Ownership).GetOwner(); 557 558 cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 558 cmpTimer.SetTimeout(this.entity, IID_Attack, "MissileHit", timeToTarget * 1000, { 559 "type": type, 560 "target": target, 561 "position": realTargetPosition, 562 "direction": missileDirection, 563 "projectileId": id, 564 "playerId":playerId 565 }); 559 let data = { 560 "type": type, 561 "attacker": this.entity, 562 "target": target, 563 "strengths": this.GetAttackStrengths(type), 564 "position": realTargetPosition, 565 "direction": missileDirection, 566 "projectileId": id, 567 "playerId": playerId, 568 "multiplier": this.GetAttackBonus(type, target), 569 "isSplash": false, 570 "attackerOwner": Engine.QueryInterface(this.entity, IID_Ownership).GetOwner() 571 } 572 if (this.template.Ranged.Splash) 573 { 574 data.friendlyFire = this.template.Ranged.Splash.FriendlyFire; 575 data.range = this.template.Ranged.Splash.Range; 576 data.shape = this.template.Ranged.Splash.Shape; 577 data.isSplash = true; 578 } 579 cmpTimer.SetTimeout(SYSTEM_ENTITY, IID_Damage, "MissileHit", timeToTarget * 1000, data); 566 580 } 567 581 else if (type == "Capture") 568 582 { … … 593 607 else 594 608 { 595 609 // Melee attack - hurt the target immediately 596 Damage.CauseDamage({610 cmpDamage.CauseDamage({ 597 611 "strengths": this.GetAttackStrengths(type), 598 612 "target": target, 599 613 "attacker": this.entity, … … 604 618 // TODO: charge attacks (need to design how they work) 605 619 }; 606 620 607 Attack.prototype.InterpolatedLocation = function(ent, lateness)608 {609 let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);610 let turnLength = cmpTimer.GetLatestTurnLength()/1000;611 let cmpTargetPosition = Engine.QueryInterface(ent, IID_Position);612 if (!cmpTargetPosition || !cmpTargetPosition.IsInWorld()) // TODO: handle dead target properly613 return undefined;614 let curPos = cmpTargetPosition.GetPosition();615 let prevPos = cmpTargetPosition.GetPreviousPosition();616 lateness /= 1000;617 return new Vector3D((curPos.x * (turnLength - lateness) + prevPos.x * lateness) / turnLength,618 0,619 (curPos.z * (turnLength - lateness) + prevPos.z * lateness) / turnLength);620 };621 622 // Tests whether it point is inside of ent's footprint623 Attack.prototype.testCollision = function(ent, point, lateness)624 {625 let targetPosition = this.InterpolatedLocation(ent, lateness);626 if (!targetPosition)627 return false;628 let cmpFootprint = Engine.QueryInterface(ent, IID_Footprint);629 if (!cmpFootprint)630 return false;631 let targetShape = cmpFootprint.GetShape();632 633 if (!targetShape || !targetPosition)634 return false;635 636 if (targetShape.type === 'circle')637 {638 // Use VectorDistanceSquared and square targetShape.radius to avoid square roots.639 return (targetPosition.horizDistanceTo(point) < (targetShape.radius * targetShape.radius));640 }641 else642 {643 let angle = Engine.QueryInterface(ent, IID_Position).GetRotation().y;644 645 let d = Vector3D.sub(point, targetPosition);646 d = Vector2D.from3D(d).rotate(-angle);647 648 return d.x < Math.abs(targetShape.width/2) && d.y < Math.abs(targetShape.depth/2);649 }650 };651 652 Attack.prototype.MissileHit = function(data, lateness)653 {654 let targetPosition = this.InterpolatedLocation(data.target, lateness);655 if (!targetPosition)656 return;657 658 // Do this first in case the direct hit kills the target659 if (this.template.Ranged.Splash)660 {661 let playersToDamage;662 663 if (this.template.Ranged.Splash.FriendlyFire == "false")664 {665 let cmpPlayer = QueryPlayerIDInterface(data.playerId);666 playersToDamage = cmpPlayer.GetEnemies();667 }668 669 Damage.CauseSplashDamage({670 "attacker": this.entity,671 "origin": Vector2D.from3D(data.position),672 "radius": this.template.Ranged.Splash.Range,673 "shape": this.template.Ranged.Splash.Shape,674 "strengths": this.GetAttackStrengths(data.type),675 "direction": data.direction,676 "playersToDamage": playersToDamage,677 "type": data.type678 });679 }680 681 if (this.testCollision(data.target, data.position, lateness))682 {683 data.attacker = this.entity;684 data.multiplier = this.GetAttackBonus(data.type, data.target);685 data.strengths = this.GetAttackStrengths(data.type);686 // Hit the primary target687 Damage.CauseDamage(data);688 689 // Remove the projectile690 let cmpProjectileManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ProjectileManager);691 cmpProjectileManager.RemoveProjectile(data.projectileId);692 }693 else694 {695 // If we didn't hit the main target look for nearby units696 let cmpPlayer = QueryPlayerIDInterface(data.playerId);697 let ents = Damage.EntitiesNearPoint(Vector2D.from3D(data.position), targetPosition.horizDistanceTo(data.position) * 2, cmpPlayer.GetEnemies());698 699 for (let i = 0; i < ents.length; ++i)700 {701 if (!this.testCollision(ents[i], data.position, lateness))702 continue;703 704 Damage.CauseDamage({705 "strengths": this.GetAttackStrengths(data.type),706 "target": ents[i],707 "attacker": this.entity,708 "multiplier": this.GetAttackBonus(data.type, ents[i]),709 "type": data.type710 });711 712 // Remove the projectile713 let cmpProjectileManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ProjectileManager);714 cmpProjectileManager.RemoveProjectile(data.projectileId);715 }716 }717 };718 719 621 Attack.prototype.OnValueModification = function(msg) 720 622 { 721 623 if (msg.component != "Attack") -
binaries/data/mods/public/simulation/components/AttackDetection.js
63 63 if (cmpTargetOwnership.GetOwner() != cmpPlayer.GetPlayerID()) 64 64 return; 65 65 var cmpAttackerOwnership = Engine.QueryInterface(attacker, IID_Ownership); 66 let attackerOwner = (cmpAttackOwnership ? cmpAttackerOwnership.GetOwner() : Engine.QueryInterface(SYSTEM_ENTITY, IID_Damage).GetDeadEntityOwner(attacker)); 67 66 68 // Don't register attacks dealt by myself 67 if ( cmpAttackerOwnership.GetOwner()== cmpPlayer.GetPlayerID())69 if (attackerOwner == cmpPlayer.GetPlayerID()) 68 70 return; 69 71 70 72 // Since livestock can be attacked/gathered by other players … … 122 124 "type": "attack", 123 125 "target": target, 124 126 "players": [cmpPlayer.GetPlayerID()], 125 "attacker": cmpAttackerOwnership.GetOwner(),127 "attacker": attackerOwner, 126 128 "targetIsDomesticAnimal": targetIsDomesticAnimal 127 129 }); 128 130 PlaySound("attacked", target); -
binaries/data/mods/public/simulation/components/Damage.js
1 function Damage() {} 2 3 Damage.prototype.Schema = 4 "<a:component type='system'/><empty/>"; 5 6 Damage.prototype.Init = function() 7 { 8 this.entityOwners = new Map(); 9 } 10 11 Damage.prototype.InterpolatedLocation = function(ent, lateness) 12 { 13 let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 14 let turnLength = cmpTimer.GetLatestTurnLength()/1000; 15 let cmpTargetPosition = Engine.QueryInterface(ent, IID_Position); 16 if (!cmpTargetPosition || !cmpTargetPosition.IsInWorld()) // TODO: handle dead target properly 17 return undefined; 18 let curPos = cmpTargetPosition.GetPosition(); 19 let prevPos = cmpTargetPosition.GetPreviousPosition(); 20 lateness /= 1000; 21 return new Vector3D((curPos.x * (turnLength - lateness) + prevPos.x * lateness) / turnLength, 22 0, 23 (curPos.z * (turnLength - lateness) + prevPos.z * lateness) / turnLength); 24 }; 25 /** 26 * Test if a point is inside of an entities footprint 27 * ent = <entity id> 28 * point = <Vector2D> 29 * lateness = <int> 30 */ 31 Damage.prototype.TestCollision = function(ent, point, lateness) 32 { 33 let targetPosition = this.InterpolatedLocation(ent, lateness); 34 if (!targetPosition) 35 return false; 36 let cmpFootprint = Engine.QueryInterface(ent, IID_Footprint); 37 if (!cmpFootprint) 38 return false; 39 let targetShape = cmpFootprint.GetShape(); 40 41 if (!targetShape || !targetPosition) 42 return false; 43 44 if (targetShape.type === 'circle') 45 { 46 // Use VectorDistanceSquared and square targetShape.radius to avoid square roots. 47 return (targetPosition.horizDistanceTo(point) < (targetShape.radius * targetShape.radius)); 48 } 49 else 50 { 51 let angle = Engine.QueryInterface(ent, IID_Position).GetRotation().y; 52 53 let d = Vector3D.sub(point, targetPosition); 54 d = Vector2D.from3D(d).rotate(-angle); 55 56 return d.x < Math.abs(targetShape.width/2) && d.y < Math.abs(targetShape.depth/2); 57 } 58 }; 59 60 Damage.prototype.MissileHit = function(data, lateness) 61 { 62 let targetPosition = this.InterpolatedLocation(data.target, lateness); 63 if (!targetPosition) 64 return; 65 66 // Do this first in case the direct hit kills the target 67 if (data.isSplash) 68 { 69 let playersToDamage; 70 71 if (!data.friendlyFire) 72 { 73 let cmpPlayer = QueryPlayerIDInterface(data.playerId); 74 playersToDamage = cmpPlayer.GetEnemies(); 75 } 76 77 this.CauseSplashDamage({ 78 "attacker": data.attacker, 79 "origin": Vector2D.from3D(data.position), 80 "radius": data.radius, 81 "shape": data.shape, 82 "strengths": data.strengths, 83 "direction": data.direction, 84 "playersToDamage": playersToDamage, 85 "type": data.type 86 }); 87 } 88 89 if (this.TestCollision(data.target, data.position, lateness)) 90 { 91 // Hit the primary target 92 this.CauseDamage(data); 93 94 // Remove the projectile 95 let cmpProjectileManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ProjectileManager); 96 cmpProjectileManager.RemoveProjectile(data.projectileId); 97 } 98 else 99 { 100 // If we didn't hit the main target look for nearby units 101 let cmpPlayer = QueryPlayerIDInterface(data.playerId); 102 let ents = this.EntitiesNearPoint(Vector2D.from3D(data.position), targetPosition.horizDistanceTo(data.position) * 2, cmpPlayer.GetEnemies()); 103 104 for (let i = 0; i < ents.length; ++i) 105 { 106 if (!this.TestCollision(ents[i], data.position, lateness)) 107 continue; 108 109 this.CauseDamage({ 110 "strengths": data.strengths, 111 "target": ents[i], 112 "attacker": data.attacker, 113 "multiplier": data.multiplier, 114 "type": data.type 115 }); 116 117 // Remove the projectile 118 let cmpProjectileManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ProjectileManager); 119 cmpProjectileManager.RemoveProjectile(data.projectileId); 120 } 121 } 122 let cmpHealth = Engine.QueryInterface(data.attacker, IID_Health); 123 if (!cmpHealth || cmpHealth.GetHitpoints() == 0) // Add killer data if it's already dead. 124 { 125 this.entityOwners.set(data.attacker, data.attackerOwner); 126 } 127 }; 128 129 /** 130 * Damages units around a given origin. 131 * data.attacker = <entity id> 132 * data.origin = <Vector2D> 133 * data.radius = <int> 134 * data.shape = <string> 135 * data.strengths = {'hack':<float>, 'pierce':<float>, 'crush':<float>} 136 * data.type = <string> 137 * ***Optional Variables*** 138 * data.direction = <unit vector> 139 * data.playersToDamage = <array of player ids> 140 */ 141 Damage.prototype.CauseSplashDamage = function(data) 142 { 143 // Get nearby entities and define variables 144 var nearEnts = this.EntitiesNearPoint(data.origin, data.radius, data.playersToDamage); 145 var damageMultiplier = 1; 146 // Cycle through all the nearby entities and damage it appropriately based on its distance from the origin. 147 for (let i = 0; i < nearEnts.length; ++i) 148 { 149 var entityPosition = Engine.QueryInterface(nearEnts[i], IID_Position).GetPosition2D(); 150 if(data.shape == 'Circular') // circular effect with quadratic falloff in every direction 151 { 152 var squaredDistanceFromOrigin = data.origin.distanceToSquared(entityPosition); 153 damageMultiplier = 1 - squaredDistanceFromOrigin / (data.radius * data.radius); 154 } 155 else if(data.shape == 'Linear') // linear effect with quadratic falloff in two directions (only used for certain missiles) 156 { 157 // Get position of entity relative to splash origin. 158 var relativePos = entityPosition.sub(data.origin); 159 160 // The width of linear splash is one fifth of the normal splash radius. 161 var width = data.radius/5; 162 163 // Effectivly rotate the axis to align with the missile direction. 164 var parallelDist = relativePos.dot(data.direction); // z axis 165 var perpDist = Math.abs(relativePos.cross(data.direction)); // y axis 166 167 // Check that the unit is within the distance at which it will get damaged. 168 if (parallelDist > -width && perpDist < width) // If in radius, quadratic falloff in both directions 169 damageMultiplier = (data.radius * data.radius - parallelDist * parallelDist) / (data.radius * data.radius) 170 * (width * width - perpDist * perpDist) / (width * width); 171 else 172 damageMultiplier = 0; 173 } 174 else // In case someone calls this function with an invalid shape. 175 { 176 warn("The " + data.shape + " splash damage shape is not implemented!"); 177 } 178 // Call CauseDamage which reduces the hitpoints, posts network command, plays sounds.... 179 this.CauseDamage({"strengths":data.strengths, "target":nearEnts[i], "attacker":data.attacker, "multiplier":damageMultiplier, "type":data.type + ".Splash"}); 180 } 181 }; 182 183 /** 184 * Causes damage on a given unit 185 * data.strengths = {'hack':<float>, 'pierce':<float>, 'crush':<float>} 186 * data.target = <entity id> 187 * data.attacker = <entity id> 188 * data.multiplier = <float between 1 and 0> 189 * data.type = <string> 190 */ 191 Damage.prototype.CauseDamage = function(data) 192 { 193 // Check the target can be damaged otherwise don't do anything. 194 var cmpDamageReceiver = Engine.QueryInterface(data.target, IID_DamageReceiver); 195 var cmpHealth = Engine.QueryInterface(data.target, IID_Health); 196 if (!cmpDamageReceiver || !cmpHealth) 197 return; 198 199 // Damage the target 200 var targetState = cmpDamageReceiver.TakeDamage(data.strengths.hack * data.multiplier, data.strengths.pierce * data.multiplier, data.strengths.crush * data.multiplier); 201 202 var cmpPromotion = Engine.QueryInterface(data.attacker, IID_Promotion); 203 var cmpLoot = Engine.QueryInterface(data.target, IID_Loot); 204 if (cmpPromotion && cmpLoot && cmpLoot.GetXp() > 0) 205 cmpPromotion.IncreaseXp(cmpLoot.GetXp() * -targetState.change / cmpHealth.GetMaxHitpoints()); 206 207 // If the target was killed run some cleanup 208 if (targetState.killed) 209 this.TargetKilled(data.attacker, data.target); 210 211 // Post the network command (make it work in multiplayer) 212 Engine.PostMessage(data.target, MT_Attacked, {"attacker":data.attacker, "target":data.target, "type":data.type, "damage":-targetState.change}); 213 214 // Play attacking sounds 215 PlaySound("attack_impact", data.attacker); 216 }; 217 218 /** 219 * Gets entities near a give point for given players. 220 * origin = <Vector2D> 221 * radius = <int> 222 * players = <array> 223 * If players is not included, entities from all players are used. 224 */ 225 Damage.prototype.EntitiesNearPoint = function(origin, radius, players) 226 { 227 // If there is insufficient data return an empty array. 228 if (!origin || !radius) 229 return []; 230 231 // If the players parameter is not specified use all players. 232 if (!players) 233 { 234 var playerEntities = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetAllPlayerEntities(); 235 players = []; 236 for (let i = 0; i < playerEntities.length; ++i) 237 players.push(Engine.QueryInterface(playerEntities[i], IID_Player).GetPlayerID()); 238 } 239 240 // Call RangeManager with dummy entity and return the result. 241 var rangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); 242 var rangeQuery = rangeManager.ExecuteQueryAroundPos(origin, 0, radius, players, IID_DamageReceiver); 243 return rangeQuery; 244 }; 245 246 /** 247 * Called when some units kills something (another unit, building, animal etc) 248 * killerEntity = <entity id> 249 * targetEntity = <entity id> 250 */ 251 Damage.prototype.TargetKilled = function(killerEntity, targetEntity) 252 { 253 // Add to killer statistics. 254 var cmpKillerPlayerStatisticsTracker = QueryOwnerInterface(killerEntity, IID_StatisticsTracker); 255 if (cmpKillerPlayerStatisticsTracker) 256 cmpKillerPlayerStatisticsTracker.KilledEntity(targetEntity); 257 // Add to loser statistics. 258 var cmpTargetPlayerStatisticsTracker = QueryOwnerInterface(targetEntity, IID_StatisticsTracker); 259 if (cmpTargetPlayerStatisticsTracker) 260 cmpTargetPlayerStatisticsTracker.LostEntity(targetEntity); 261 262 // If killer can collect loot, let's try to collect it. 263 var cmpLooter = Engine.QueryInterface(killerEntity, IID_Looter); 264 if (cmpLooter) 265 cmpLooter.Collect(targetEntity); 266 }; 267 268 269 Damage.prototype.GetDeadEntityOwner = function(entityId) 270 { 271 var cmpOwner = this.entityOwners.get(entityId); 272 this.entityOwners.delete(entityId); 273 return cmpOwner; 274 }; 275 276 Engine.RegisterSystemComponentType(IID_Damage, "Damage", Damage); -
binaries/data/mods/public/simulation/components/interfaces/Damage.js
Property changes on: binaries/data/mods/public/simulation/components/Damage.js ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property
1 Engine.RegisterInterface("Damage"); -
binaries/data/mods/public/simulation/helpers/Damage.js
Property changes on: binaries/data/mods/public/simulation/components/interfaces/Damage.js ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property
1 // Create global Damage object.2 var Damage = {};3 4 /**5 * Damages units around a given origin.6 * data.attacker = <entity id>7 * data.origin = <Vector2D>8 * data.radius = <int>9 * data.shape = <string>10 * data.strengths = {'hack':<float>, 'pierce':<float>, 'crush':<float>}11 * data.type = <string>12 * ***Optional Variables***13 * data.direction = <unit vector>14 * data.playersToDamage = <array of player ids>15 */16 Damage.CauseSplashDamage = function(data)17 {18 // Get nearby entities and define variables19 var nearEnts = Damage.EntitiesNearPoint(data.origin, data.radius, data.playersToDamage);20 var damageMultiplier = 1;21 // Cycle through all the nearby entities and damage it appropriately based on its distance from the origin.22 for each (var entity in nearEnts)23 {24 var entityPosition = Engine.QueryInterface(entity, IID_Position).GetPosition2D();25 if(data.shape == 'Circular') // circular effect with quadratic falloff in every direction26 {27 var squaredDistanceFromOrigin = data.origin.distanceToSquared(entityPosition);28 damageMultiplier = 1 - squaredDistanceFromOrigin / (data.radius * data.radius);29 }30 else if(data.shape == 'Linear') // linear effect with quadratic falloff in two directions (only used for certain missiles)31 {32 // Get position of entity relative to splash origin.33 var relativePos = entityPosition.sub(data.origin);34 35 // The width of linear splash is one fifth of the normal splash radius.36 var width = data.radius/5;37 38 // Effectivly rotate the axis to align with the missile direction.39 var parallelDist = relativePos.dot(data.direction); // z axis40 var perpDist = Math.abs(relativePos.cross(data.direction)); // y axis41 42 // Check that the unit is within the distance at which it will get damaged.43 if (parallelDist > -width && perpDist < width) // If in radius, quadratic falloff in both directions44 damageMultiplier = (data.radius * data.radius - parallelDist * parallelDist) / (data.radius * data.radius)45 * (width * width - perpDist * perpDist) / (width * width);46 else47 damageMultiplier = 0;48 }49 else // In case someone calls this function with an invalid shape.50 {51 warn("The " + data.shape + " splash damage shape is not implemented!");52 }53 // Call CauseDamage which reduces the hitpoints, posts network command, plays sounds....54 Damage.CauseDamage({"strengths":data.strengths, "target":entity, "attacker":data.attacker, "multiplier":damageMultiplier, "type":data.type + ".Splash"});55 }56 };57 58 /**59 * Causes damage on a given unit60 * data.strengths = {'hack':<float>, 'pierce':<float>, 'crush':<float>}61 * data.target = <entity id>62 * data.attacker = <entity id>63 * data.multiplier = <float between 1 and 0>64 * data.type = <string>65 */66 Damage.CauseDamage = function(data)67 {68 // Check the target can be damaged otherwise don't do anything.69 var cmpDamageReceiver = Engine.QueryInterface(data.target, IID_DamageReceiver);70 var cmpHealth = Engine.QueryInterface(data.target, IID_Health);71 if (!cmpDamageReceiver || !cmpHealth)72 return;73 74 // Damage the target75 var targetState = cmpDamageReceiver.TakeDamage(data.strengths.hack * data.multiplier, data.strengths.pierce * data.multiplier, data.strengths.crush * data.multiplier);76 77 var cmpPromotion = Engine.QueryInterface(data.attacker, IID_Promotion);78 var cmpLoot = Engine.QueryInterface(data.target, IID_Loot);79 if (cmpPromotion && cmpLoot && cmpLoot.GetXp() > 0)80 cmpPromotion.IncreaseXp(cmpLoot.GetXp() * -targetState.change / cmpHealth.GetMaxHitpoints());81 82 // If the target was killed run some cleanup83 if (targetState.killed)84 Damage.TargetKilled(data.attacker, data.target);85 86 // Post the network command (make it work in multiplayer)87 Engine.PostMessage(data.target, MT_Attacked, {"attacker":data.attacker, "target":data.target, "type":data.type, "damage":-targetState.change});88 89 // Play attacking sounds90 PlaySound("attack_impact", data.attacker);91 };92 93 /**94 * Gets entities near a give point for given players.95 * origin = <Vector2D>96 * radius = <int>97 * players = <array>98 * If players is not included, entities from all players are used.99 */100 Damage.EntitiesNearPoint = function(origin, radius, players)101 {102 // If there is insufficient data return an empty array.103 if (!origin || !radius)104 return [];105 106 // If the players parameter is not specified use all players.107 if (!players)108 {109 var playerEntities = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetAllPlayerEntities();110 players = [];111 for each (var entity in playerEntities)112 players.push(Engine.QueryInterface(entity, IID_Player).GetPlayerID());113 }114 115 // Call RangeManager with dummy entity and return the result.116 var rangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);117 var rangeQuery = rangeManager.ExecuteQueryAroundPos(origin, 0, radius, players, IID_DamageReceiver);118 return rangeQuery;119 };120 121 /**122 * Called when some units kills something (another unit, building, animal etc)123 * killerEntity = <entity id>124 * targetEntity = <entity id>125 */126 Damage.TargetKilled = function(killerEntity, targetEntity)127 {128 // Add to killer statistics.129 var cmpKillerPlayerStatisticsTracker = QueryOwnerInterface(killerEntity, IID_StatisticsTracker);130 if (cmpKillerPlayerStatisticsTracker)131 cmpKillerPlayerStatisticsTracker.KilledEntity(targetEntity);132 // Add to loser statistics.133 var cmpTargetPlayerStatisticsTracker = QueryOwnerInterface(targetEntity, IID_StatisticsTracker);134 if (cmpTargetPlayerStatisticsTracker)135 cmpTargetPlayerStatisticsTracker.LostEntity(targetEntity);136 137 // If killer can collect loot, let's try to collect it.138 var cmpLooter = Engine.QueryInterface(killerEntity, IID_Looter);139 if (cmpLooter)140 cmpLooter.Collect(targetEntity);141 };142 143 Engine.RegisterGlobal("Damage", Damage);144