Ticket #1496: Damagev5.diff

File Damagev5.diff, 23.1 KB (added by Josh, 11 years ago)

Latest version, fixed issues found by historic_bruno and fixed some whitespace issues in Attack.js.

  • 0ad/binaries/data/mods/public/simulation/templates/special/dummy.xml

     
     1<Entity>
     2    <Position>
     3        <Altitude>0</Altitude>
     4        <Anchor>upright</Anchor>
     5        <Floating>false</Floating>
     6        <TurnRate>6.0</TurnRate>
     7    </Position>
     8</Entity>
  • 0ad/binaries/data/mods/public/simulation/helpers/Damage.js

     
     1//Create global Damage object.
     2var Damage = {};
     3
     4/****************************************
     5 * Damages units around a given origin.
     6 * data.attacker = <entity id>
     7 * data.origin = {'x':<int>, 'z':<int>}
     8 * data.radius = <int>
     9 * data.shape = <string>
     10 * data.strengths = {'hack':<float>, 'pierce':<float>, 'crush':<float>}
     11 * ***Optional Variables***
     12 * data.direction = <unit vector>
     13 * data.playersToDamage = <array of player ids>
     14 */
     15Damage.CauseSplashDamage = function(data)
     16{
     17    // Get nearby entities and define variables
     18    var nearEnts = Damage.EntitiesNearPoint(data.origin, data.radius, data.playersToDamage);
     19    var damageMultiplier = 1;
     20    // Cycle through all the nearby entities and damage it appropriately based on its distance from the origin.
     21    for each (var entity in nearEnts)
     22    {
     23        var entityPosition = Engine.QueryInterface(entity, IID_Position).GetPosition();
     24        if(data.shape == 'Circular') // circular effect with quadratic falloff in every direction
     25        {
     26            var squaredDistanceFromOrigin = Damage.VectorDistanceSquared(data.origin, entityPosition);
     27            damageMultiplier == 1 - squaredDistanceFromOrigin / (data.radius * data.radius);
     28        }
     29        else if(data.shape == 'Linear') // linear effect with quadratic falloff in two directions (only used for certain missiles)
     30        {
     31            // Get position of entity relative to splash origin.
     32            var relativePos = {"x":entityPosition.x - data.origin.x, "z":entityPosition.z - data.origin.z};
     33
     34            // The width of linear splash is one fifth of the normal splash radius.
     35            var width = data.radius/5;
     36
     37            // Effectivly rotate the axis to align with the missile direction.
     38            var parallelDist = Damage.VectorDot(relativePos, data.direction); // z axis
     39            var perpDist = Math.abs(Damage.VectorCross(relativePos, data.direction)); // y axis
     40
     41            // Check that the unit is within the distance at which it will get damaged.
     42            if (parallelDist > -width && perpDist < width) // If in radius, quadratic falloff in both directions
     43                damageMultiplier = (data.radius * data.radius - parallelDist * parallelDist) / (data.radius * data.radius)
     44                                     * (width * width - perpDist * perpDist) / (width * width);
     45            else
     46                damageMultiplier = 0;
     47        }
     48        else // In case someone calls this function with an invalid shape.
     49        {
     50            warn("The " + data.shape + " splash damage shape is not implemented!");
     51        }
     52        // Call CauseDamage which reduces the hitpoints, posts network command, plays sounds....
     53        Damage.CauseDamage({"strengths":data.strengths, "target":entity, "attacker":data.attacker, "multiplier":damageMultiplier, "type":"Splash"})
     54    }
     55};
     56
     57/****************************************
     58 * Causes damage on a given unit
     59 * data.strengths = {'hack':<float>, 'pierce':<float>, 'crush':<float>}
     60 * data.target = <entity id>
     61 * data.attacker = <entity id>
     62 * data.multiplier = <float between 1 and 0>
     63 * data.type = <string>
     64 */
     65Damage.CauseDamage = function(data)
     66{
     67    // Check the target can be damaged otherwise don't do anything.
     68    var cmpDamageReceiver = Engine.QueryInterface(data.target, IID_DamageReceiver);
     69    if (!cmpDamageReceiver)
     70        return;
     71
     72    // Damage the target
     73    var targetState = cmpDamageReceiver.TakeDamage(data.strengths.hack * data.multiplier, data.strengths.pierce * data.multiplier, data.strengths.crush * data.multiplier);
     74
     75    // If the target was killed run some cleanup
     76    if (targetState.killed)
     77        Damage.TargetKilled(data.attacker, data.target);
     78
     79    // Post the network command (make it work in multiplayer)
     80    Engine.PostMessage(data.target, MT_Attacked, {"attacker":data.attacker, "target":data.target, "type":data.type, "damage":-targetState.change});
     81
     82    // Play attacking sounds
     83    PlaySound("attack_impact", data.attacker);
     84};
     85
     86/****************************************
     87 * Gets entities near a give point for given players.
     88 * origin = {'x':<int>, 'z':<int>}
     89 * radius = <int>
     90 * players = <array>
     91 * If players is not included, entities from all players are used.
     92 */
     93Damage.EntitiesNearPoint = function(origin, radius, players)
     94{
     95    // If there is insufficient data return an empty array.
     96    if (!origin || !radius)
     97        return [];
     98    //Create the dummy entity used for range calculations if it doesn't exist.
     99    if (!Damage.dummyTargetEntity)
     100        Damage.dummyTargetEntity = Engine.AddEntity('special/dummy');
     101    // Move the dummy entity to the origin of the query.
     102    var cmpDummyPosition = Engine.QueryInterface(Damage.dummyTargetEntity, IID_Position);
     103    cmpDummyPosition.JumpTo(origin.x, origin.z);
     104
     105    // If the players parameter is not specified use all players.
     106    if (!players)
     107        players = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetAllPlayers();
     108
     109    // Call RangeManager with dummy entity and return the result.
     110    var rangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
     111    var rangeQuery = rangeManager.ExecuteQuery(Damage.dummyTargetEntity, 0, radius, players, IID_DamageReceiver);
     112    return rangeQuery;
     113};
     114
     115/****************************************
     116 * Called when some units kills something (another unit, building, animal etc)
     117 * killerEntity = <entity id>
     118 * targetEntity = <entity id>
     119 */
     120Damage.TargetKilled = function(killerEntity, targetEntity)
     121{
     122    // Add to killer statistics.
     123    var cmpKillerPlayerStatisticsTracker = QueryOwnerInterface(killerEntity, IID_StatisticsTracker);
     124    if (cmpKillerPlayerStatisticsTracker)
     125        cmpKillerPlayerStatisticsTracker.KilledEntity(targetEntity);
     126    // Add to loser statistics.
     127    var cmpTargetPlayerStatisticsTracker = QueryOwnerInterface(targetEntity, IID_StatisticsTracker);
     128    if (cmpTargetPlayerStatisticsTracker)
     129        cmpTargetPlayerStatisticsTracker.LostEntity(targetEntity);
     130
     131    // If killer can collect loot, let's try to collect it.
     132    var cmpLooter = Engine.QueryInterface(killerEntity, IID_Looter);
     133    if (cmpLooter)
     134        cmpLooter.Collect(targetEntity);
     135};
     136
     137// Gets the straight line distance between p1 and p2
     138Damage.VectorDistanceSquared = function(p1, p2)
     139{
     140    return (p1.x - p2.x) * (p1.x - p2.x) + (p1.z - p2.z) * (p1.z - p2.z);
     141};
     142
     143// Gets the dot product of two vectors.
     144Damage.VectorDot = function(p1, p2)
     145{
     146    return p1.x * p2.x + p1.z * p2.z;
     147};
     148
     149// Gets the 2D interpreted version of the cross product of two vectors.
     150Damage.VectorCross = function(p1, p2)
     151{
     152    return p1.x * p2.z - p1.z * p2.x;
     153};
     154
     155Engine.RegisterGlobal("Damage", Damage);
  • 0ad/binaries/data/mods/public/simulation/components/Player.js

     
    447447/**
    448448 * Check if given player is our enemy
    449449 */
     450Player.prototype.GetEnemies = function()
     451{
     452    var enemies = [];
     453    for (var i = 0; i != this.diplomacy.length; i++) {if (this.diplomacy[i] < 0){enemies.push(i);}}
     454    return enemies;
     455};
     456
     457/**
     458 * Check if given player is our enemy
     459 */
    450460Player.prototype.IsEnemy = function(id)
    451461{
    452462    return this.diplomacy[id] < 0;
  • 0ad/binaries/data/mods/public/simulation/components/Attack.js

     
    257257        return undefined;
    258258
    259259    const targetClasses = cmpIdentity.GetClassesList();
    260    
     260
    261261    var minPref = null;
    262262    for each (var type in this.GetAttackTypes())
    263263    {
     
    296296    const isAllowed = function (value, i, a) { return !attack.GetRestrictedClasses(value).some(isTargetClass); }
    297297    const isPreferred = function (value, i, a) { return attack.GetPreferredClasses(value).some(isTargetClass); }
    298298    const byPreference = function (a, b) { return (types.indexOf(a) + (isPreferred(a) ? types.length : 0) ) - (types.indexOf(b) + (isPreferred(b) ? types.length : 0) ); }
    299    
     299
    300300    // Always slaughter domestic animals instead of using a normal attack
    301301    if (isTargetClass("Domestic") && this.template.Slaughter)
    302302        return "Slaughter";
     
    319319{
    320320    var prepare = +(this.template[type].PrepareTime || 0);
    321321    prepare = ApplyTechModificationsToEntity("Attack/" + type + "/PrepareTime", prepare, this.entity);
    322    
     322
    323323    var repeat = +(this.template[type].RepeatTime || 1000);
    324324    repeat = ApplyTechModificationsToEntity("Attack/" + type + "/RepeatTime", repeat, this.entity);
    325325
     
    330330{
    331331    // Work out the attack values with technology effects
    332332    var self = this;
    333    
     333
    334334    var template = this.template[type];
    335335    var splash = "";
    336336    if (!template)
     
    338338        template = this.template[type.split(".")[0]].Splash;
    339339        splash = "/Splash";
    340340    }
    341    
     341
    342342    var applyTechs = function(damageType)
    343343    {
    344344        // All causes caching problems so disable it for now.
    345345        //var allComponent = ApplyTechModificationsToEntity("Attack/" + type + splash + "/All", +(template[damageType] || 0), self.entity) - self.template[type][damageType];
    346346        return ApplyTechModificationsToEntity("Attack/" + type + splash + "/" + damageType, +(template[damageType] || 0), self.entity);
    347347    };
    348    
     348
    349349    return {
    350350        hack: applyTechs("Hack"),
    351351        pierce: applyTechs("Pierce"),
     
    357357{
    358358    var max = +this.template[type].MaxRange;
    359359    max = ApplyTechModificationsToEntity("Attack/" + type + "/MaxRange", max, this.entity);
    360    
     360
    361361    var min = +(this.template[type].MinRange || 0);
    362362    min = ApplyTechModificationsToEntity("Attack/" + type + "/MinRange", min, this.entity);
    363    
     363
    364364    return { "max": max, "min": min };
    365365};
    366366
     
    371371    var template = this.template[type];
    372372    if (!template)
    373373        template = this.template[type.split(".")[0]].Splash;
    374    
     374
    375375    if (template.Bonuses)
    376376    {
    377377        var cmpIdentity = Engine.QueryInterface(target, IID_Identity);
    378378        if (!cmpIdentity)
    379379            return 1;
    380        
     380
    381381        // Multiply the bonuses for all matching classes
    382382        for (var key in template.Bonuses)
    383383        {
    384384            var bonus = template.Bonuses[key];
    385            
     385
    386386            var hasClasses = true;
    387387            if (bonus.Classes){
    388388                var classes = bonus.Classes.split(/\s+/);
     
    394394                attackBonus *= bonus.Multiplier;
    395395        }
    396396    }
    397    
     397
    398398    return attackBonus;
    399399};
    400400
    401401// Returns a 2d random distribution scaled for a spread of scale 1.
    402402// The current implementation is a 2d gaussian with sigma = 1
    403403Attack.prototype.GetNormalDistribution = function(){
    404    
     404
    405405    // Use the Box-Muller transform to get a gaussian distribution
    406406    var a = Math.random();
    407407    var b = Math.random();
    408    
     408
    409409    var c = Math.sqrt(-2*Math.log(a)) * Math.cos(2*Math.PI*b);
    410410    var d = Math.sqrt(-2*Math.log(a)) * Math.sin(2*Math.PI*b);
    411    
     411
    412412    return [c, d];
    413413};
    414414
     
    429429        // Get some data about the entity
    430430        var horizSpeed = +this.template[type].ProjectileSpeed;
    431431        var gravity = 9.81; // this affects the shape of the curve; assume it's constant for now
    432        
     432
    433433        var spread = this.template.Ranged.Spread;
    434434        spread = ApplyTechModificationsToEntity("Attack/Ranged/Spread", spread, this.entity);
    435        
     435
    436436        //horizSpeed /= 2; gravity /= 2; // slow it down for testing
    437        
     437
    438438        var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
    439439        if (!cmpPosition || !cmpPosition.IsInWorld())
    440440            return;
     
    443443        if (!cmpTargetPosition || !cmpTargetPosition.IsInWorld())
    444444            return;
    445445        var targetPosition = cmpTargetPosition.GetPosition();
    446        
     446
    447447        var relativePosition = {"x": targetPosition.x - selfPosition.x, "z": targetPosition.z - selfPosition.z}
    448448        var previousTargetPosition = Engine.QueryInterface(target, IID_Position).GetPreviousPosition();
    449        
     449
    450450        var targetVelocity = {"x": (targetPosition.x - previousTargetPosition.x) / this.turnLength, "z": (targetPosition.z - previousTargetPosition.z) / this.turnLength}
    451451        // the component of the targets velocity radially away from the archer
    452452        var radialSpeed = this.VectorDot(relativePosition, targetVelocity) / this.VectorLength(relativePosition);
    453        
     453
    454454        var horizDistance = this.VectorDistance(targetPosition, selfPosition);
    455        
     455
    456456        // This is an approximation of the time ot the target, it assumes that the target has a constant radial
    457457        // velocity, but since units move in straight lines this is not true.  The exact value would be more
    458458        // difficult to calculate and I think this is sufficiently accurate.  (I tested and for cavalry it was
     
    462462        // Predict where the unit is when the missile lands.
    463463        var predictedPosition = {"x": targetPosition.x + targetVelocity.x * timeToTarget,
    464464                                 "z": targetPosition.z + targetVelocity.z * timeToTarget};
    465        
     465
    466466        // Compute the real target point (based on spread and target speed)
    467467        var randNorm = this.GetNormalDistribution();
    468468        var offsetX = randNorm[0] * spread * (1 + this.VectorLength(targetVelocity) / 20);
     
    481481        // Launch the graphical projectile
    482482        var cmpProjectileManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ProjectileManager);
    483483        var id = cmpProjectileManager.LaunchProjectileAtPoint(this.entity, realTargetPosition, horizSpeed, gravity);
    484        
     484
     485        var playerId = Engine.QueryInterface(this.entity, IID_Ownership).GetOwner()
    485486        var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
    486         cmpTimer.SetTimeout(this.entity, IID_Attack, "MissileHit", timeToTarget*1000, {"type": type, "target": target, "position": realTargetPosition, "direction": missileDirection, "projectileId": id});
     487        cmpTimer.SetTimeout(this.entity, IID_Attack, "MissileHit", timeToTarget*1000, {"type": type, "target": target, "position": realTargetPosition, "direction": missileDirection, "projectileId": id, "playerId":playerId});
    487488    }
    488489    else
    489490    {
    490491        // Melee attack - hurt the target immediately
    491         this.CauseDamage({"type": type, "target": target});
     492        Damage.CauseDamage({"strengths":this.GetAttackStrengths(type), "target":target, "attacker":this.entity, "multiplier":this.GetAttackBonus(type, target), "type":type});
    492493    }
    493494    // TODO: charge attacks (need to design how they work)
    494495};
    495496
    496 /**
    497  * Called when some units kills something (another unit, building, animal etc)
    498  */
    499 Attack.prototype.TargetKilled = function(killerEntity, targetEntity)
    500 {
    501     var cmpKillerPlayerStatisticsTracker = QueryOwnerInterface(killerEntity, IID_StatisticsTracker);
    502     if (cmpKillerPlayerStatisticsTracker) cmpKillerPlayerStatisticsTracker.KilledEntity(targetEntity);
    503     var cmpTargetPlayerStatisticsTracker = QueryOwnerInterface(targetEntity, IID_StatisticsTracker);
    504     if (cmpTargetPlayerStatisticsTracker) cmpTargetPlayerStatisticsTracker.LostEntity(targetEntity);
    505 
    506     // if unit can collect loot, lets try to collect it
    507     var cmpLooter = Engine.QueryInterface(killerEntity, IID_Looter);
    508     if (cmpLooter)
    509     {
    510         cmpLooter.Collect(targetEntity);
    511     }
    512 };
    513 
    514497Attack.prototype.InterpolatedLocation = function(ent, lateness)
    515498{
    516499    var cmpTargetPosition = Engine.QueryInterface(ent, IID_Position);
     
    533516    return (p1.x * p2.x + p1.z * p2.z);
    534517};
    535518
    536 Attack.prototype.VectorCross = function(p1, p2)
    537 {
    538     return (p1.x * p2.z - p1.z * p2.x);
    539 };
    540 
    541519Attack.prototype.VectorLength = function(p)
    542520{
    543521    return Math.sqrt(p.x*p.x + p.z*p.z);
     
    553531    if (!cmpFootprint)
    554532        return false;
    555533    var targetShape = cmpFootprint.GetShape();
    556    
     534
    557535    if (!targetShape || !targetPosition)
    558536        return false;
    559    
     537
    560538    if (targetShape.type === 'circle')
    561539    {
    562540        return (this.VectorDistance(point, targetPosition) < targetShape.radius);
     
    564542    else
    565543    {
    566544        var targetRotation = Engine.QueryInterface(ent, IID_Position).GetRotation().y;
    567        
     545
    568546        var dx = point.x - targetPosition.x;
    569547        var dz = point.z - targetPosition.z;
    570        
     548
    571549        var dxr = Math.cos(targetRotation) * dx - Math.sin(targetRotation) * dz;
    572550        var dzr = Math.sin(targetRotation) * dx + Math.cos(targetRotation) * dz;
    573        
     551
    574552        return (-targetShape.width/2 <= dxr && dxr < targetShape.width/2 && -targetShape.depth/2 <= dzr && dzr < targetShape.depth/2);
    575553    }
    576554};
     
    586564        var friendlyFire = this.template.Ranged.Splash.FriendlyFire;
    587565        var splashRadius = this.template.Ranged.Splash.Range;
    588566        var splashShape = this.template.Ranged.Splash.Shape;
    589        
    590         var ents = this.GetNearbyEntities(data.target, this.VectorDistance(data.position, targetPosition) * 2 + splashRadius, friendlyFire);
    591         ents.push(data.target); // Add the original unit to the list of splash damage targets
    592        
    593         for (var i = 0; i < ents.length; i++)
     567        var playersToDamage;
     568
     569        // If friendlyFire isn't enabled, get all player enemies to pass to "Damage.CauseSplashDamage".
     570        if (friendlyFire == false)
    594571        {
    595             var entityPosition = this.InterpolatedLocation(ents[i], lateness);
    596             var radius = this.VectorDistance(data.position, entityPosition);
    597            
    598             if (radius < splashRadius)
    599             {
    600                 var multiplier = 1;
    601                 if (splashShape == "Circular") // quadratic falloff
    602                 {
    603                     multiplier *= 1 - ((radius * radius) / (splashRadius * splashRadius));
    604                 }
    605                 else if (splashShape == "Linear")
    606                 {
    607                     // position of entity relative to where the missile hit
    608                     var relPos = {"x": entityPosition.x - data.position.x, "z": entityPosition.z - data.position.z};
    609                    
    610                     var splashWidth = splashRadius / 5;
    611                     var parallelDist = this.VectorDot(relPos, data.direction);
    612                     var perpDist = Math.abs(this.VectorCross(relPos, data.direction));
    613                    
    614                     // Check that the unit is within the distance splashWidth of the line starting at the missile's
    615                     // landing point which extends in the direction of the missile for length splashRadius.
    616                     if (parallelDist > -splashWidth && perpDist < splashWidth)
    617                     {
    618                         // Use a quadratic falloff in both directions
    619                         multiplier = (splashRadius*splashRadius - parallelDist*parallelDist) / (splashRadius*splashRadius)
    620                                      * (splashWidth*splashWidth - perpDist*perpDist) / (splashWidth*splashWidth);
    621                     }
    622                     else
    623                     {
    624                         multiplier = 0;
    625                     }
    626                 }
    627                 var newData = {"type": data.type + ".Splash", "target": ents[i], "damageMultiplier": multiplier};
    628                 this.CauseDamage(newData);
    629             }
     572            var cmpPlayer = Engine.QueryInterface(Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetPlayerByID(data.playerId), IID_Player)
     573            playersToDamage = cmpPlayer.GetEnemies();
    630574        }
     575        // Damage the units.
     576        Damage.CauseSplashDamage({"attacker":this.entity, "origin":data.position, "radius":splashRadius, "shape":splashShape, "strengths":this.GetAttackStrengths(data.type), "direction":data.direction, "playersToDamage":undefined});
    631577    }
    632    
     578
    633579    if (this.testCollision(data.target, data.position, lateness))
    634580    {
     581        data.attacker = this.entity
     582        data.multiplier = this.GetAttackBonus(data.type, data.target)
     583        data.strengths = this.GetAttackStrengths(data.type)
     584
    635585        // Hit the primary target
    636         this.CauseDamage(data);
    637        
     586        Damage.CauseDamage(data);
     587
    638588        // Remove the projectile
    639589        var cmpProjectileManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ProjectileManager);
    640590        cmpProjectileManager.RemoveProjectile(data.projectileId);
     
    642592    else
    643593    {
    644594        // If we didn't hit the main target look for nearby units
    645         var ents = this.GetNearbyEntities(data.target, this.VectorDistance(data.position, targetPosition) * 2);
    646        
     595        var cmpPlayer = Engine.QueryInterface(Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetPlayerByID(data.playerId), IID_Player)
     596        var ents = Damage.EntitiesNearPoint(data.position, this.VectorDistance(data.position, targetPosition) * 2, cmpPlayer.GetEnemies());
     597
    647598        for (var i = 0; i < ents.length; i++)
    648599        {
    649600            if (this.testCollision(ents[i], data.position, lateness))
    650601            {
    651                 var newData = {"type": data.type, "target": ents[i]};
    652                 this.CauseDamage(newData);
     602                var newData = {"strengths":this.GetAttackStrengths(data.type), "target":ents[i], "attacker":this.entity, "multiplier":this.GetAttackBonus(data.type, ents[i]), "type":data.type};
     603                Damage.CauseDamage(newData);
    653604               
    654605                // Remove the projectile
    655606                var cmpProjectileManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ProjectileManager);
     
    659610    }
    660611};
    661612
    662 Attack.prototype.GetNearbyEntities = function(startEnt, range, friendlyFire)
    663 {
    664     var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
    665     var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
    666     var owner = cmpOwnership.GetOwner();
    667     var cmpPlayer = Engine.QueryInterface(cmpPlayerManager.GetPlayerByID(owner), IID_Player);
    668     var numPlayers = cmpPlayerManager.GetNumPlayers();
    669     var players = [];
    670    
    671     for (var i = 1; i < numPlayers; ++i)
    672     {   
    673         // Only target enemies unless friendly fire is on
    674         if (cmpPlayer.IsEnemy(i) || friendlyFire)
    675             players.push(i);
    676     }
    677    
    678     var rangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
    679     return rangeManager.ExecuteQuery(startEnt, 0, range, players, IID_DamageReceiver);
    680 }
    681 
    682 /**
    683  * Inflict damage on the target
    684  */
    685 Attack.prototype.CauseDamage = function(data)
    686 {
    687     var strengths = this.GetAttackStrengths(data.type);
    688    
    689     var damageMultiplier = this.GetAttackBonus(data.type, data.target);
    690     if (data.damageMultiplier !== undefined)
    691         damageMultiplier *= data.damageMultiplier;
    692    
    693     var cmpDamageReceiver = Engine.QueryInterface(data.target, IID_DamageReceiver);
    694     if (!cmpDamageReceiver)
    695         return;
    696     var targetState = cmpDamageReceiver.TakeDamage(strengths.hack * damageMultiplier, strengths.pierce * damageMultiplier, strengths.crush * damageMultiplier);
    697     // if target killed pick up loot and credit experience
    698     if (targetState.killed == true)
    699     {
    700         this.TargetKilled(this.entity, data.target);
    701     }
    702 
    703     Engine.PostMessage(data.target, MT_Attacked,
    704         { "attacker": this.entity, "target": data.target, "type": data.type, "damage": -targetState.change });
    705 
    706     PlaySound("attack_impact", this.entity);
    707 };
    708 
    709613Attack.prototype.OnUpdate = function(msg)
    710614{
    711615    this.turnLength = msg.turnLength;
  • 0ad/binaries/data/mods/public/simulation/components/PlayerManager.js

     
    4646    this.playerEntities = [];
    4747};
    4848
     49PlayerManager.prototype.GetAllPlayers = function()
     50{
     51    return this.playerEntities;
     52};
    4953Engine.RegisterComponentType(IID_PlayerManager, "PlayerManager", PlayerManager);