Ticket #1910: 1910-death-damage-4.patch

File 1910-death-damage-4.patch, 13.3 KB (added by Mate-86, 8 years ago)
  • binaries/data

  • binaries/data/mods/public/globalscripts/Templates.js

    Property changes on: binaries/data
    ___________________________________________________________________
    Modified: svn:ignore
    ## -1,3 +1,4 ##
    -*.xmb
     *.db
     *.jbf
    +*.xmb
    +cache
     
    137137                    // true if undefined
    138138                    "friendlyFire": template.Attack[type].Splash.FriendlyFire != "false"
    139139                };
     140
     141            if (type == "Death")
     142                ret.attack.Death = {
     143                    "hack": getAttackStat("Hack"),
     144                    "pierce": getAttackStat("Pierce"),
     145                    "crush": getAttackStat("Crush"),
     146                    // true if undefined
     147                    "friendlyFire": template.Attack.Death.FriendlyFire != "false"
     148                };
    140149        }
    141150    }
    142151
  • binaries/data/mods/public/gui/common/tooltips.js

     
    88const g_AttackTypes = {
    99    "Melee": translate("Melee Attack:"),
    1010    "Ranged": translate("Ranged Attack:"),
    11     "Capture": translate("Capture Attack:")
     11    "Capture": translate("Capture Attack:"),
     12    "Death": translate("Damage on Destroy:")
    1213};
    1314
    1415const g_DamageTypes = {
     
    141142    let attacks = [];
    142143    for (let type in template.attack)
    143144    {
     145        if (type == "Death") {
     146            attacks.push(sprintf(translate("%(label)s %(details)s Friendly Fire: %(enabled)s"), {
     147                "label": headerFont(g_AttackTypes[type]),
     148                "details": damageTypesToText(template.attack.Death),
     149                "enabled": template.attack.Death.friendlyFire ? translate("Yes") : translate("No")
     150            }));
     151            continue;
     152        }
     153
    144154        if (type == "Slaughter")
    145155            continue; // Slaughter is used to kill animals, so do not show it.
    146156
  • binaries/data/mods/public/simulation/components/Attack.js

     
    3737            "<text/>" +
    3838        "</element>" +
    3939    "</optional>";
     40   
     41Attack.prototype.splashSchema = "<interleave>" +
     42                "<element name='Shape' a:help='Shape of the splash damage, can be circular or linear'><text/></element>" +
     43                "<element name='Range' a:help='Size of the area affected by the splash'><ref name='nonNegativeDecimal'/></element>" +
     44                "<element name='FriendlyFire' a:help='Whether the splash damage can hurt non enemy units'><data type='boolean'/></element>" +
     45                "<element name='Hack' a:help='Hack damage strength'><ref name='nonNegativeDecimal'/></element>" +
     46                "<element name='Pierce' a:help='Pierce damage strength'><ref name='nonNegativeDecimal'/></element>" +
     47                "<element name='Crush' a:help='Crush damage strength'><ref name='nonNegativeDecimal'/></element>" +
     48                Attack.prototype.bonusesSchema +
     49            "</interleave>";
    4050
    4151Attack.prototype.Schema =
    4252    "<a:help>Controls the attack abilities and strengths of the unit.</a:help>" +
     
    94104            "<Crush>0.0</Crush>" +
    95105            "<MaxRange>4.0</MaxRange>" +
    96106        "</Slaughter>" +
     107        "<Death>" +
     108            "<Shape>Circular</Shape>" +
     109            "<Range>20</Range>" +
     110            "<FriendlyFire>false</FriendlyFire>" +
     111            "<Hack>0.0</Hack>" +
     112            "<Pierce>10.0</Pierce>" +
     113            "<Crush>50.0</Crush>" +
     114        "</Death>" +
    97115    "</a:example>" +
    98116    "<optional>" +
    99117        "<element name='Melee'>" +
     
    138156                "<optional>" +
    139157                    "<element name='Splash'>" +
    140158                        "<interleave>" +
    141                             "<element name='Shape' a:help='Shape of the splash damage, can be circular or linear'><text/></element>" +
    142                             "<element name='Range' a:help='Size of the area affected by the splash'><ref name='nonNegativeDecimal'/></element>" +
    143                             "<element name='FriendlyFire' a:help='Whether the splash damage can hurt non enemy units'><data type='boolean'/></element>" +
    144                             "<element name='Hack' a:help='Hack damage strength'><ref name='nonNegativeDecimal'/></element>" +
    145                             "<element name='Pierce' a:help='Pierce damage strength'><ref name='nonNegativeDecimal'/></element>" +
    146                             "<element name='Crush' a:help='Crush damage strength'><ref name='nonNegativeDecimal'/></element>" +
    147                             Attack.prototype.bonusesSchema +
     159                            Attack.prototype.splashSchema +
    148160                        "</interleave>" +
    149161                    "</element>" +
    150162                "</optional>" +
     
    177189                Attack.prototype.restrictedClassesSchema +
    178190            "</interleave>" +
    179191        "</element>" +
     192    "</optional>" +
     193    "<optional>" +
     194        "<element name='Death' a:help='When a unit or building is detroyed then it inflicts damage to nearby units.'>" +
     195            Attack.prototype.splashSchema +
     196        "</element>" +
    180197    "</optional>";
    181198
    182199Attack.prototype.Init = function()
     
    606623        cmpUnitAI.UpdateRangeQueries();
    607624};
    608625
     626Attack.prototype.CauseDeathDamage = function()
     627{
     628    if (!this.template.Death)
     629        return;
     630
     631    let cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
     632    if (!cmpPosition.IsInWorld())
     633        return;
     634
     635    let pos = cmpPosition.GetPosition();
     636    let cmpDamage = Engine.QueryInterface(SYSTEM_ENTITY, IID_Damage);
     637    let owner = Engine.QueryInterface(this.entity, IID_Ownership).GetOwner();
     638    let playersToDamage = cmpDamage.GetPlayersToDamage(this.template.Death.FriendlyFire, owner);
     639
     640    cmpDamage.CauseSplashDamage({
     641        "attacker": this.entity,
     642        "origin": Vector2D.from3D(pos),
     643        "radius": this.template.Death.Range,
     644        "shape": this.template.Death.Shape,
     645        "strengths": this.GetAttackStrengths("Death"),
     646        "direction": 0, //does directed death damage make sense?
     647        "playersToDamage": playersToDamage,
     648        "type": this.template.Type,
     649        "attackerOwner": owner
     650    });
     651};
     652
    609653Engine.RegisterComponentType(IID_Attack, "Attack", Attack);
  • binaries/data/mods/public/simulation/components/Damage.js

     
    6565};
    6666
    6767/**
     68 * Get the list of players affected by the damage.
     69 * @param {boolean} friendlyFire - a flag indicating if allied entities are also damaged.
     70 * @param {number}  attackerOwner - the player id of the attacker.
     71 */
     72Damage.prototype.GetPlayersToDamage = function(friendlyFire, attackerOwner)
     73{
     74    if (!friendlyFire)
     75        return QueryPlayerIDInterface(attackerOwner).GetEnemies();
     76
     77    var playersToDamage = [];
     78    let numPlayers = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetNumPlayers();
     79    for (let i = 0; i < numPlayers; ++i)
     80        playersToDamage.push(i);
     81
     82    return playersToDamage;
     83}
     84
     85/**
    6886 * Handles hit logic after the projectile travel time has passed.
    6987 * @param {Object}   data - the data sent by the caller.
    7088 * @param {number}   data.attacker - the entity id of the attacker.
     
    90108    // Do this first in case the direct hit kills the target
    91109    if (data.isSplash)
    92110    {
    93         let playersToDamage = [];
    94         if (!data.friendlyFire)
    95             playersToDamage = QueryPlayerIDInterface(data.attackerOwner).GetEnemies();
    96         else
    97         {
    98             let numPlayers = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetNumPlayers();
    99             for (let i = 0; i < numPlayers; ++i)
    100                 playersToDamage.push(i);
    101         }
     111        let playersToDamage = this.GetPlayersToDamage(data.friendlyFire, data.attackerOwner);
    102112
    103113        this.CauseSplashDamage({
    104114            "attacker": data.attacker,
  • binaries/data/mods/public/simulation/components/Health.js

     
    244244
    245245            this.hitpoints = 0;
    246246            this.RegisterHealthChanged(oldHitpoints);
     247
     248            let cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
     249            if (cmpAttack)
     250                cmpAttack.CauseDeathDamage();
    247251        }
    248252
    249253    }
  • binaries/data/mods/public/simulation/components/tests/test_Attack.js

     
    66Engine.LoadComponentScript("interfaces/TechnologyManager.js");
    77Engine.LoadComponentScript("interfaces/Formation.js");
    88Engine.LoadComponentScript("interfaces/Attack.js");
     9Engine.LoadComponentScript("interfaces/Damage.js");
     10Engine.LoadComponentScript("interfaces/Timer.js");
    911Engine.LoadComponentScript("Attack.js");
     12Engine.LoadComponentScript("Timer.js");
    1013
    1114let entityID = 903;
     15let cmpTimer = ConstructComponent(SYSTEM_ENTITY, "Timer");
    1216
    1317function attackComponentTest(defenderClass, test_function)
    1418{
     
    3034
    3135    AddMock(attacker, IID_Position, {
    3236        "IsInWorld": () => true,
    33         "GetHeightOffset": () => 5
     37        "GetHeightOffset": () => 5,
     38        "GetPosition": () => new Vector3D(1, 2, 3)
    3439    });
    3540
    3641    AddMock(attacker, IID_Ownership, {
     
    6671            "MaxRange": 80,
    6772            "PrepareTime": 300,
    6873            "RepeatTime": 500,
     74            "ProjectileSpeed": 50,
     75            "Spread": 2.5,
    6976            "PreferredClasses": {
    7077                "_string": "Archer"
    7178            },
    7279            "RestrictedClasses": {
    7380                "_string": "Elephant"
     81            },
     82            "Splash" : {
     83                "Shape": "Circular",
     84                "Range": 10,
     85                "FriendlyFire": "false",
     86                "Hack": 0.0,
     87                "Pierce": 15.0,
     88                "Crush": 35.0
    7489            }
    7590        },
    7691        "Capture" : {
     
    7792            "Value": 8,
    7893            "MaxRange": 10,
    7994        },
    80         "Slaughter": {}
     95        "Slaughter": {},
     96        "Death": {
     97            "Shape": "Circular",
     98            "Range": 20,
     99            "FriendlyFire": false,
     100            "Hack": 0.0,
     101            "Pierce": 10.0,
     102            "Crush": 50.0
     103        }
    81104    });
    82105
    83106    let defender = ++entityID;
     
    119142        "prepare": 0,
    120143        "repeat": 1000
    121144    });
     145
     146    TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetSplashDamage("Ranged"), {hack: 0, pierce: 15, crush: 35, friendlyFire: false});
    122147});
    123148
    124149for (let className of ["Infantry", "Cavalry"])
     
    161186testGetBestAttackAgainst("Archer", "Ranged");
    162187testGetBestAttackAgainst("Domestic", "Slaughter");
    163188testGetBestAttackAgainst("Structure", "Capture", true);
     189
     190attackComponentTest(undefined, (attacker, cmpAttack, defender) => {
     191
     192    var causeSplashDamageArg = "";
     193
     194    AddMock(SYSTEM_ENTITY, IID_Damage, {
     195        "CauseDamage": () => false,
     196        "SetTimeout": () => false,
     197        "GetPlayersToDamage": () => [1, 2],
     198        "CauseSplashDamage": function (arg) { causeSplashDamageArg = arg; }
     199    });
     200
     201    cmpAttack.CauseDeathDamage();
     202
     203    TS_ASSERT_UNEVAL_EQUALS(causeSplashDamageArg, {
     204        attacker: attacker,
     205        origin: {x:1, y:3},
     206        radius: 20,
     207        shape: "Circular",
     208        strengths: {hack:0, pierce:10, crush:50},
     209        direction: 0,
     210        playersToDamage: [1, 2],
     211        type: void(0),
     212        attackerOwner: 1
     213    });
     214});
  • binaries/data/mods/public/simulation/components/tests/test_Damage.js

     
    5353
    5454AddMock(SYSTEM_ENTITY, IID_PlayerManager, {
    5555    GetPlayerByID: (id) => atkPlayerEntity,
     56    GetNumPlayers: () => 4
    5657});
    5758
    5859AddMock(SYSTEM_ENTITY, IID_RangeManager, {
     
    122123cmpAttack.PerformAttack("Ranged", target);
    123124Engine.DestroyEntity(attacker);
    124125TestDamage();
     126
     127atkPlayerEntity = 1;
     128AddMock(atkPlayerEntity, IID_Player, {
     129    GetEnemies: () => [2, 3],
     130});
     131TS_ASSERT_UNEVAL_EQUALS(cmpDamage.GetPlayersToDamage(true, atkPlayerEntity), [0, 1, 2, 3]);
     132TS_ASSERT_UNEVAL_EQUALS(cmpDamage.GetPlayersToDamage(false, atkPlayerEntity), [2, 3]);
  • binaries/data/mods/public/simulation/templates/template_structure_civic_civil_centre.xml

     
    2626      <Spread>1.5</Spread>
    2727      <PreferredClasses datatype="tokens">Human</PreferredClasses>
    2828    </Ranged>
     29    <Death>
     30      <Shape>Circular</Shape>
     31      <Range>15</Range>
     32      <FriendlyFire>true</FriendlyFire>
     33      <Hack>50.0</Hack>
     34      <Pierce>60.0</Pierce>
     35      <Crush>170.0</Crush>
     36    </Death>
    2937  </Attack>
    3038  <BuildingAI>
    3139    <DefaultArrowCount>3</DefaultArrowCount>
  • binaries/data/mods/public/simulation/templates/template_structure_military_fortress.xml

     
    1818      <Spread>1.5</Spread>
    1919      <PreferredClasses datatype="tokens">Human</PreferredClasses>
    2020    </Ranged>
     21    <Death>
     22      <Shape>Circular</Shape>
     23      <Range>15</Range>
     24      <FriendlyFire>true</FriendlyFire>
     25      <Hack>0.0</Hack>
     26      <Pierce>0.0</Pierce>
     27      <Crush>270.0</Crush>
     28    </Death>
    2129  </Attack>
    2230  <BuildingAI>
    2331    <DefaultArrowCount>3</DefaultArrowCount>