Ticket #1910: 1910-death-damage-2.patch
File 1910-death-damage-2.patch, 9.7 KB (added by , 8 years ago) |
---|
-
binaries/data
-
binaries/data/mods/public/simulation/components/Attack.js
Property changes on: binaries/data ___________________________________________________________________ Modified: svn:ignore ## -1,3 +1,4 ## -*.xmb *.db *.jbf +*.xmb +cache
94 94 "<Crush>0.0</Crush>" + 95 95 "<MaxRange>4.0</MaxRange>" + 96 96 "</Slaughter>" + 97 "<Death>" + 98 "<Shape>Circular</Shape>" + 99 "<Range>20</Range>" + 100 "<FriendlyFire>false</FriendlyFire>" + 101 "<Hack>0.0</Hack>" + 102 "<Pierce>10.0</Pierce>" + 103 "<Crush>50.0</Crush>" + 104 "</Death>" + 97 105 "</a:example>" + 98 106 "<optional>" + 99 107 "<element name='Melee'>" + … … 177 185 Attack.prototype.restrictedClassesSchema + 178 186 "</interleave>" + 179 187 "</element>" + 188 "</optional>" + 189 "<optional>" + 190 "<element name='Death' a:help='When a unit dies it inflicts damage on nearby units.'>" + 191 "<interleave>" + 192 "<element name='Shape' a:help='Shape of the splash damage, can be circular or linear'><text/></element>" + 193 "<element name='Range' a:help='Size of the area affected by the splash'><ref name='nonNegativeDecimal'/></element>" + 194 "<element name='FriendlyFire' a:help='Whether the splash damage can hurt non enemy units'><data type='boolean'/></element>" + 195 "<element name='Hack' a:help='Hack damage strength'><ref name='nonNegativeDecimal'/></element>" + 196 "<element name='Pierce' a:help='Pierce damage strength'><ref name='nonNegativeDecimal'/></element>" + 197 "<element name='Crush' a:help='Crush damage strength'><ref name='nonNegativeDecimal'/></element>" + 198 Attack.prototype.bonusesSchema + 199 Attack.prototype.preferredClassesSchema + 200 Attack.prototype.restrictedClassesSchema + 201 "</interleave>" + 202 "</element>" + 180 203 "</optional>"; 181 204 182 205 Attack.prototype.Init = function() … … 606 629 cmpUnitAI.UpdateRangeQueries(); 607 630 }; 608 631 632 Attack.prototype.CauseDeathDamage = function() 633 { 634 if (!this.template.Death) 635 return; 636 637 let cmpPosition = Engine.QueryInterface(this.entity, IID_Position); 638 if (!cmpPosition.IsInWorld()) 639 return; 640 641 let pos = cmpPosition.GetPosition(); 642 let cmpDamage = Engine.QueryInterface(SYSTEM_ENTITY, IID_Damage); 643 let owner = Engine.QueryInterface(this.entity, IID_Ownership).GetOwner(); 644 let playersToDamage = cmpDamage.GetPlayersToDamage(this.template.Death.FriendlyFire, owner); 645 646 cmpDamage.CauseSplashDamage({ 647 "attacker": this.entity, 648 "origin": Vector2D.from3D(pos), 649 "radius": this.template.Death.Range, 650 "shape": this.template.Death.Shape, 651 "strengths": this.GetAttackStrengths("Death"), 652 "direction": 0, //does directed death damage make sense? 653 "playersToDamage": playersToDamage, 654 "type": this.template.Type, 655 "attackerOwner": owner 656 }); 657 658 }; 659 609 660 Engine.RegisterComponentType(IID_Attack, "Attack", Attack); -
binaries/data/mods/public/simulation/components/Damage.js
65 65 }; 66 66 67 67 /** 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 */ 72 Damage.prototype.GetPlayersToDamage = function(friendlyFire, attackerOwner) 73 { 74 var playersToDamage = []; 75 if (!friendlyFire) 76 playersToDamage = QueryPlayerIDInterface(attackerOwner).GetEnemies(); 77 else 78 { 79 let numPlayers = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetNumPlayers(); 80 for (let i = 0; i < numPlayers; ++i) 81 playersToDamage.push(i); 82 } 83 return playersToDamage; 84 } 85 86 /** 68 87 * Handles hit logic after the projectile travel time has passed. 69 88 * @param {Object} data - the data sent by the caller. 70 89 * @param {number} data.attacker - the entity id of the attacker. … … 90 109 // Do this first in case the direct hit kills the target 91 110 if (data.isSplash) 92 111 { 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 } 112 let playersToDamage = this.GetPlayersToDamage(data.friendlyFire, data.attackerOwner); 102 113 103 114 this.CauseSplashDamage({ 104 115 "attacker": data.attacker, -
binaries/data/mods/public/simulation/components/Health.js
244 244 245 245 this.hitpoints = 0; 246 246 this.RegisterHealthChanged(oldHitpoints); 247 248 let cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); 249 if (cmpAttack) 250 cmpAttack.CauseDeathDamage(); 251 247 252 } 248 253 249 254 } -
binaries/data/mods/public/simulation/components/tests/test_Attack.js
6 6 Engine.LoadComponentScript("interfaces/TechnologyManager.js"); 7 7 Engine.LoadComponentScript("interfaces/Formation.js"); 8 8 Engine.LoadComponentScript("interfaces/Attack.js"); 9 Engine.LoadComponentScript("interfaces/Damage.js"); 10 Engine.LoadComponentScript("interfaces/Timer.js"); 9 11 Engine.LoadComponentScript("Attack.js"); 12 Engine.LoadComponentScript("Timer.js"); 10 13 11 14 let entityID = 903; 15 let cmpTimer = ConstructComponent(SYSTEM_ENTITY, "Timer"); 12 16 13 17 function attackComponentTest(defenderClass, test_function) 14 18 { … … 30 34 31 35 AddMock(attacker, IID_Position, { 32 36 "IsInWorld": () => true, 33 "GetHeightOffset": () => 5 37 "GetHeightOffset": () => 5, 38 "GetPosition": () => new Vector3D(1, 2, 3) 34 39 }); 35 40 36 41 AddMock(attacker, IID_Ownership, { … … 66 71 "MaxRange": 80, 67 72 "PrepareTime": 300, 68 73 "RepeatTime": 500, 74 "ProjectileSpeed": 50, 75 "Spread": 2.5, 69 76 "PreferredClasses": { 70 77 "_string": "Archer" 71 78 }, 72 79 "RestrictedClasses": { 73 80 "_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 74 89 } 75 90 }, 76 91 "Capture" : { … … 77 92 "Value": 8, 78 93 "MaxRange": 10, 79 94 }, 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 } 81 104 }); 82 105 83 106 let defender = ++entityID; … … 119 142 "prepare": 0, 120 143 "repeat": 1000 121 144 }); 145 146 TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetSplashDamage("Ranged"), {hack: 0, pierce: 15, crush: 35, friendlyFire: false}); 122 147 }); 123 148 124 149 for (let className of ["Infantry", "Cavalry"]) … … 161 186 testGetBestAttackAgainst("Archer", "Ranged"); 162 187 testGetBestAttackAgainst("Domestic", "Slaughter"); 163 188 testGetBestAttackAgainst("Structure", "Capture", true); 189 190 attackComponentTest(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
53 53 54 54 AddMock(SYSTEM_ENTITY, IID_PlayerManager, { 55 55 GetPlayerByID: (id) => atkPlayerEntity, 56 GetNumPlayers: () => 4 56 57 }); 57 58 58 59 AddMock(SYSTEM_ENTITY, IID_RangeManager, { … … 122 123 cmpAttack.PerformAttack("Ranged", target); 123 124 Engine.DestroyEntity(attacker); 124 125 TestDamage(); 126 127 atkPlayerEntity = 1; 128 AddMock(atkPlayerEntity, IID_Player, { 129 GetEnemies: () => [2, 3], 130 }); 131 TS_ASSERT_UNEVAL_EQUALS(cmpDamage.GetPlayersToDamage(true, atkPlayerEntity), [0, 1, 2, 3]); 132 TS_ASSERT_UNEVAL_EQUALS(cmpDamage.GetPlayersToDamage(false, atkPlayerEntity), [2, 3]); -
binaries/data/mods/public/simulation/templates/template_unit_infantry_ranged_slinger.xml
16 16 <RepeatTime>1000</RepeatTime> 17 17 <Spread>1.4</Spread> 18 18 </Ranged> 19 <Death> 20 <Shape>Circular</Shape> 21 <Range>15</Range> 22 <FriendlyFire>true</FriendlyFire> 23 <Hack>50.0</Hack> 24 <Pierce>60.0</Pierce> 25 <Crush>70.0</Crush> 26 </Death> 19 27 </Attack> 20 28 <Cost> 21 29 <Resources>