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