Ticket #1496: Damagev2_notest.diff
File Damagev2_notest.diff, 15.5 KB (added by , 11 years ago) |
---|
-
0ad/binaries/data/mods/public/simulation/components/Attack.js
486 486 else 487 487 { 488 488 // Melee attack - hurt the target immediately 489 this.CauseDamage({"type": type, "target": target}); 489 var cmpDamage = Engine.QueryInterface(SYSTEM_ENTITY, IID_Damage); 490 cmpDamage.CauseDamage({"strengths":this.GetAttackStrengths(type), "target":target, "attacker":this.entity, "multiplier":this.GetAttackBonus(type, target), "type":type}); 490 491 } 491 492 // TODO: charge attacks (need to design how they work) 492 493 }; 493 494 494 /**495 * Called when some units kills something (another unit, building, animal etc)496 */497 Attack.prototype.TargetKilled = function(killerEntity, targetEntity)498 {499 var cmpKillerPlayerStatisticsTracker = QueryOwnerInterface(killerEntity, IID_StatisticsTracker);500 if (cmpKillerPlayerStatisticsTracker) cmpKillerPlayerStatisticsTracker.KilledEntity(targetEntity);501 var cmpTargetPlayerStatisticsTracker = QueryOwnerInterface(targetEntity, IID_StatisticsTracker);502 if (cmpTargetPlayerStatisticsTracker) cmpTargetPlayerStatisticsTracker.LostEntity(targetEntity);503 504 // if unit can collect loot, lets try to collect it505 var cmpLooter = Engine.QueryInterface(killerEntity, IID_Looter);506 if (cmpLooter)507 {508 cmpLooter.Collect(targetEntity);509 }510 };511 512 495 Attack.prototype.InterpolatedLocation = function(ent, lateness) 513 496 { 514 497 var cmpTargetPosition = Engine.QueryInterface(ent, IID_Position); … … (this hunk was shorter than expected) 530 513 { 531 514 return (p1.x * p2.x + p1.z * p2.z); 532 515 }; 533 534 Attack.prototype.VectorCross = function(p1, p2)535 {536 return (p1.x * p2.z - p1.z * p2.x);537 };538 539 Attack.prototype.VectorLength = function(p)540 {541 return Math.sqrt(p.x*p.x + p.z*p.z);542 };543 516 544 517 // Tests whether it point is inside of ent's footprint 545 518 Attack.prototype.testCollision = function(ent, point, lateness) … … 578 556 var targetPosition = this.InterpolatedLocation(data.target, lateness); 579 557 if (!targetPosition) 580 558 return; 581 559 var cmpDamage = Engine.QueryInterface(SYSTEM_ENTITY, IID_Damage); 582 560 if (this.template.Ranged.Splash) // splash damage, do this first in case the direct hit kills the target 583 561 { 584 562 var friendlyFire = this.template.Ranged.Splash.FriendlyFire; 585 563 var splashRadius = this.template.Ranged.Splash.Range; 586 564 var splashShape = this.template.Ranged.Splash.Shape; 587 588 var ents = this.GetNearbyEntities(data.target, this.VectorDistance(data.position, targetPosition) * 2 + splashRadius, friendlyFire); 589 ents.push(data.target); // Add the original unit to the list of splash damage targets 590 591 for (var i = 0; i < ents.length; i++) 592 { 593 var entityPosition = this.InterpolatedLocation(ents[i], lateness); 594 var radius = this.VectorDistance(data.position, entityPosition); 595 596 if (radius < splashRadius) 597 { 598 var multiplier = 1; 599 if (splashShape == "Circular") // quadratic falloff 600 { 601 multiplier *= 1 - ((radius * radius) / (splashRadius * splashRadius)); 602 } 603 else if (splashShape == "Linear") 604 { 605 // position of entity relative to where the missile hit 606 var relPos = {"x": entityPosition.x - data.position.x, "z": entityPosition.z - data.position.z}; 607 608 var splashWidth = splashRadius / 5; 609 var parallelDist = this.VectorDot(relPos, data.direction); 610 var perpDist = Math.abs(this.VectorCross(relPos, data.direction)); 611 612 // Check that the unit is within the distance splashWidth of the line starting at the missile's 613 // landing point which extends in the direction of the missile for length splashRadius. 614 if (parallelDist > -splashWidth && perpDist < splashWidth) 615 { 616 // Use a quadratic falloff in both directions 617 multiplier = (splashRadius*splashRadius - parallelDist*parallelDist) / (splashRadius*splashRadius) 618 * (splashWidth*splashWidth - perpDist*perpDist) / (splashWidth*splashWidth); 619 } 620 else 621 { 622 multiplier = 0; 623 } 624 } 625 var newData = {"type": data.type + ".Splash", "target": ents[i], "damageMultiplier": multiplier}; 626 this.CauseDamage(newData); 627 } 628 } 565 cmpDamage.CauseSplashDamage({"attacker":this.entity, "origin":data.position, "radius":splashRadius, "shape":splashShape, "strengths":this.GetAttackStrengths(data.type), "direction":data.direction, "playersToDamage":undefined}); 629 566 } 630 567 631 568 if (this.testCollision(data.target, data.position, lateness)) 632 569 { 570 data.attacker=this.entity 571 data.multiplier=this.GetAttackBonus(data.type, data.target) 572 data.strengths=this.GetAttackStrengths(data.type) 633 573 // Hit the primary target 634 this.CauseDamage(data);574 cmpDamage.CauseDamage(data); 635 575 636 576 // Remove the projectile 637 577 var cmpProjectileManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ProjectileManager); … … 640 580 else 641 581 { 642 582 // If we didn't hit the main target look for nearby units 643 var ents = this.GetNearbyEntities(data.target, this.VectorDistance(data.position, targetPosition) * 2);583 var ents = cmpDamage.EntitiesNearPoint(data.position, this.VectorDistance(data.position, targetPosition) * 2); 644 584 645 585 for (var i = 0; i < ents.length; i++) 646 586 { 647 587 if (this.testCollision(ents[i], data.position, lateness)) 648 588 { 649 var newData = {"type": data.type, "target": ents[i]}; 650 this.CauseDamage(newData); 589 var newData = {"strengths":this.GetAttackStrengths(data.type), "target":ents[i], "attacker":this.entity, "multiplier":this.GetAttackBonus(data.type, ents[i]), "type":data.type}; 651 590 591 cmpDamage.CauseDamage(newData); 592 652 593 // Remove the projectile 653 594 var cmpProjectileManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ProjectileManager); 654 595 cmpProjectileManager.RemoveProjectile(data.projectileId); … … 657 598 } 658 599 }; 659 600 660 Attack.prototype.GetNearbyEntities = function(startEnt, range, friendlyFire)661 {662 var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);663 var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);664 var owner = cmpOwnership.GetOwner();665 var cmpPlayer = Engine.QueryInterface(cmpPlayerManager.GetPlayerByID(owner), IID_Player);666 var numPlayers = cmpPlayerManager.GetNumPlayers();667 var players = [];668 669 for (var i = 1; i < numPlayers; ++i)670 {671 // Only target enemies unless friendly fire is on672 if (cmpPlayer.IsEnemy(i) || friendlyFire)673 players.push(i);674 }675 676 var rangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);677 return rangeManager.ExecuteQuery(startEnt, 0, range, players, IID_DamageReceiver);678 }679 680 /**681 * Inflict damage on the target682 */683 Attack.prototype.CauseDamage = function(data)684 {685 var strengths = this.GetAttackStrengths(data.type);686 687 var damageMultiplier = this.GetAttackBonus(data.type, data.target);688 if (data.damageMultiplier !== undefined)689 damageMultiplier *= data.damageMultiplier;690 691 var cmpDamageReceiver = Engine.QueryInterface(data.target, IID_DamageReceiver);692 if (!cmpDamageReceiver)693 return;694 var targetState = cmpDamageReceiver.TakeDamage(strengths.hack * damageMultiplier, strengths.pierce * damageMultiplier, strengths.crush * damageMultiplier);695 // if target killed pick up loot and credit experience696 if (targetState.killed == true)697 {698 this.TargetKilled(this.entity, data.target);699 }700 701 Engine.PostMessage(data.target, MT_Attacked,702 { "attacker": this.entity, "target": data.target, "type": data.type, "damage": -targetState.change });703 704 PlaySound("attack_impact", this.entity);705 };706 707 601 Attack.prototype.OnUpdate = function(msg) 708 602 { 709 603 this.turnLength = msg.turnLength; -
0ad/binaries/data/mods/public/simulation/components/PlayerManager.js
46 46 this.playerEntities = []; 47 47 }; 48 48 49 PlayerManager.prototype.GetAllPlayers = function() 50 { 51 return this.playerEntities; 52 }; 49 53 Engine.RegisterComponentType(IID_PlayerManager, "PlayerManager", PlayerManager); -
0ad/binaries/data/mods/public/simulation/components/interfaces/Damage.js
1 Engine.RegisterInterface("Damage"); -
0ad/binaries/data/mods/public/simulation/components/Damage.js
1 function Damage() {} 2 3 Damage.prototype.Schema = "<a:component type='system'/><empty/>"; 4 5 Damage.prototype.Init = function() 6 { 7 // Create dummy entity for EntitiesNearPoint 8 this.dummyTargetEntity = Engine.AddEntity('special/dummy'); 9 } 10 11 /**************************************** 12 * Damages units around a given origin. 13 * data.attacker = <entity id> 14 * data.origin = {'x':<int>, 'z':<int>} 15 * data.radius = <int> 16 * data.shape = <string> 17 * data.strengths = {'hack':<float>, 'pierce':<float>, 'crush':<float>} 18 * ***Optional Variables*** 19 * data.direction = <unit vector> 20 * data.playersToDamage = <array of player ids> 21 */ 22 Damage.prototype.CauseSplashDamage = function(data) 23 { 24 // Get nearby entities and define variables 25 var nearEnts = this.EntitiesNearPoint(data.origin, data.radius, data.playersToDamage); 26 var damageMultiplier = 1; 27 // Cycle through all the nearby entities and damage it appropriately based on its distance from the origin. 28 for each (var entity in nearEnts) 29 { 30 var entityPosition = Engine.QueryInterface(entity, IID_Position).GetPosition(); 31 if(data.shape=='Circular') // circular effect with quadratic falloff in every direction 32 { 33 var squaredDistanceFromOrigin = this.VectorDistanceSquared(data.origin, entityPosition); 34 damageMultiplier == 1 - ((squaredDistanceFromOrigin) / (data.radius * data.radius)); 35 } 36 else if(data.shape=='Linear') // linear effect with quadratic falloff in two directions (only used for certain missiles) 37 { 38 // Get position of entity relative to splash origin. 39 var relativePos = {"x": entityPosition.x - data.origin.x, "z": entityPosition.z - data.origin.z}; 40 41 // The width of linear splash is one fifth of the normal splash radius. 42 var width = data.radius/5; 43 44 // Effectivly rotate the axis to align with the missile direction. 45 var parallelDist = this.VectorDot(relativePos, data.direction);//x axis 46 var perpDist = Math.abs(this.VectorCross(relativePos, data.direction));//y axis 47 48 // Check that the unit is within the distance at which it will get damaged. 49 if (parallelDist > -width && perpDist < width) // If in radius, quadratic falloff in both directions 50 multiplier = (data.radius*data.radius - parallelDist*parallelDist) / (data.radius*data.radius) 51 * (width*width - perpDist*perpDist) / (width*width); 52 else 53 multiplier = 0; 54 } 55 else // In case someone calls this function with an invalid shape. 56 { 57 warn("The "+data.shape+" splash damage shape is not implemented!"); 58 } 59 // Call CauseDamage which reduces the hitpoints, posts network command, plays sounds.... 60 this.CauseDamage({"strengths":data.strengths, "target":entity, "attacker":data.attacker, "multiplier":damageMultiplier, "type":"Splash"}) 61 } 62 }; 63 64 /**************************************** 65 * Causes damage on a given unit 66 * data.strengths = {'hack':<float>, 'pierce':<float>, 'crush':<float>} 67 * data.target = <entity id> 68 * data.attacker = <entity id> 69 * data.multiplier = <float between 1 and 0> 70 * data.type = <string> 71 */ 72 Damage.prototype.CauseDamage = function(data) 73 { 74 // Check the target can be damaged otherwise don't do anything. 75 var cmpDamageReceiver = Engine.QueryInterface(data.target, IID_DamageReceiver); 76 if (!cmpDamageReceiver) 77 return; 78 79 // Damage the target 80 var targetState = cmpDamageReceiver.TakeDamage(data.strengths.hack * data.multiplier, data.strengths.pierce * data.multiplier, data.strengths.crush * data.multiplier); 81 82 // If the target was killed run some cleanup 83 if (targetState.killed == true) 84 this.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 sounds 90 PlaySound("attack_impact", data.attacker); 91 }; 92 93 /**************************************** 94 * Gets entities near a give point for given players. 95 * origin = {'x':<int>, 'z':<int>} 96 * radius = <int> 97 * players = <array> 98 * If players is not included, entities from all players are used. 99 */ 100 Damage.prototype.EntitiesNearPoint = function(origin, radius, players) 101 { 102 // If there is insufficient data return an empty array. 103 if(!origin || !radius) 104 return []; 105 106 // Move the dummy entity to the origin of the query. 107 var cmpDummyPosition = Engine.QueryInterface(this.dummyTargetEntity, IID_Position); 108 cmpDummyPosition.JumpTo(origin.x, origin.z); 109 110 // If the players parameter is not specified use all players. 111 if(!players) 112 players = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetAllPlayers(); 113 114 // Call RangeManager with dummy entity and return the result. 115 var rangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); 116 var rangeQuery = rangeManager.ExecuteQuery(this.dummyTargetEntity, 0, radius, players, IID_DamageReceiver); 117 return rangeQuery; 118 }; 119 120 /**************************************** 121 * Called when some units kills something (another unit, building, animal etc) 122 * killerEntity = <entity id> 123 * targetEntity = <entity id> 124 */ 125 Damage.prototype.TargetKilled = function(killerEntity, targetEntity) 126 { 127 // Add to killer statistics. 128 var cmpKillerPlayerStatisticsTracker = QueryOwnerInterface(killerEntity, IID_StatisticsTracker); 129 if (cmpKillerPlayerStatisticsTracker) 130 cmpKillerPlayerStatisticsTracker.KilledEntity(targetEntity); 131 // Add to loser statistics. 132 var cmpTargetPlayerStatisticsTracker = QueryOwnerInterface(targetEntity, IID_StatisticsTracker); 133 if (cmpTargetPlayerStatisticsTracker) 134 cmpTargetPlayerStatisticsTracker.LostEntity(targetEntity); 135 136 // If killer can collect loot, lets try to collect it. 137 var cmpLooter = Engine.QueryInterface(killerEntity, IID_Looter); 138 if (cmpLooter) 139 cmpLooter.Collect(targetEntity); 140 }; 141 142 // Gets the straight line distance between p1 and p2 143 Damage.prototype.VectorDistanceSquared = function(p1, p2) 144 { 145 return ((p1.x - p2.x)*(p1.x - p2.x) + (p1.z - p2.z)*(p1.z - p2.z)); 146 }; 147 148 // Gets the dot product of two vectors. 149 Damage.prototype.VectorDot = function(p1, p2) 150 { 151 return (p1.x * p2.x + p1.z * p2.z); 152 }; 153 154 // Gets the 2D interpreted version of the cross product of two vectors. 155 Damage.prototype.VectorCross = function(p1, p2) 156 { 157 return (p1.x * p2.z - p1.z * p2.x); 158 }; 159 160 Engine.RegisterComponentType(IID_Damage, "Damage", Damage); -
0ad/source/simulation2/Simulation2.cpp
127 127 LOAD_SCRIPTED_COMPONENT("PlayerManager"); 128 128 LOAD_SCRIPTED_COMPONENT("TechnologyTemplateManager"); 129 129 LOAD_SCRIPTED_COMPONENT("Timer"); 130 LOAD_SCRIPTED_COMPONENT("Damage"); 130 131 131 132 #undef LOAD_SCRIPTED_COMPONENT