Ticket #1910: 1910-death-damage-3.patch
File 1910-death-damage-3.patch, 13.3 KB (added by , 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
134 134 // true if undefined 135 135 "friendlyFire": template.Attack[type].Splash.FriendlyFire != "false" 136 136 }; 137 138 if (type == "Death") 139 ret.attack.Death = { 140 "hack": getAttackStat("Hack"), 141 "pierce": getAttackStat("Pierce"), 142 "crush": getAttackStat("Crush"), 143 // true if undefined 144 "friendlyFire": template.Attack.Death.FriendlyFire != "false" 145 }; 137 146 } 138 147 } 139 148 -
binaries/data/mods/public/gui/common/tooltips.js
8 8 const g_AttackTypes = { 9 9 "Melee": translate("Melee Attack:"), 10 10 "Ranged": translate("Ranged Attack:"), 11 "Capture": translate("Capture Attack:") 11 "Capture": translate("Capture Attack:"), 12 "Death": translate("Damage on Destroy:") 12 13 }; 13 14 14 15 const g_DamageTypes = { … … 141 142 let attacks = []; 142 143 for (let type in template.attack) 143 144 { 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 144 154 if (type == "Slaughter") 145 155 continue; // Slaughter is used to kill animals, so do not show it. 146 156 -
binaries/data/mods/public/simulation/components/Attack.js
37 37 "<text/>" + 38 38 "</element>" + 39 39 "</optional>"; 40 41 Attack.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>"; 40 50 41 51 Attack.prototype.Schema = 42 52 "<a:help>Controls the attack abilities and strengths of the unit.</a:help>" + … … 94 104 "<Crush>0.0</Crush>" + 95 105 "<MaxRange>4.0</MaxRange>" + 96 106 "</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>" + 97 115 "</a:example>" + 98 116 "<optional>" + 99 117 "<element name='Melee'>" + … … 138 156 "<optional>" + 139 157 "<element name='Splash'>" + 140 158 "<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 + 148 160 "</interleave>" + 149 161 "</element>" + 150 162 "</optional>" + … … 177 189 Attack.prototype.restrictedClassesSchema + 178 190 "</interleave>" + 179 191 "</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>" + 180 197 "</optional>"; 181 198 182 199 Attack.prototype.Init = function() … … 606 623 cmpUnitAI.UpdateRangeQueries(); 607 624 }; 608 625 626 Attack.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 609 653 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_structure_civic_civil_centre.xml
26 26 <Spread>1.5</Spread> 27 27 <PreferredClasses datatype="tokens">Human</PreferredClasses> 28 28 </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> 29 37 </Attack> 30 38 <BuildingAI> 31 39 <DefaultArrowCount>3</DefaultArrowCount> -
binaries/data/mods/public/simulation/templates/template_structure_military_fortress.xml
18 18 <Spread>1.5</Spread> 19 19 <PreferredClasses datatype="tokens">Human</PreferredClasses> 20 20 </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> 21 29 </Attack> 22 30 <BuildingAI> 23 31 <DefaultArrowCount>3</DefaultArrowCount>