Ticket #1910: 1910-death-damage-5.patch
File 1910-death-damage-5.patch, 13.4 KB (added by , 7 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
138 138 // true if undefined 139 139 "friendlyFire": template.Attack[type].Splash.FriendlyFire != "false" 140 140 }; 141 142 if (type == "Death") 143 ret.attack.Death = { 144 "hack": getAttackStat("Hack"), 145 "pierce": getAttackStat("Pierce"), 146 "crush": getAttackStat("Crush"), 147 // true if undefined 148 "friendlyFire": template.Attack.Death.FriendlyFire != "false" 149 }; 141 150 } 142 151 } 143 152 -
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 destroyed then it inflicts damage to nearby units.'>" + 195 Attack.prototype.splashSchema + 196 "</element>" + 180 197 "</optional>"; 181 198 182 199 Attack.prototype.Init = function() … … 605 622 cmpUnitAI.UpdateRangeQueries(); 606 623 }; 607 624 625 Attack.prototype.CauseDeathDamage = function() 626 { 627 if (!this.template.Death) 628 return; 629 630 let cmpPosition = Engine.QueryInterface(this.entity, IID_Position); 631 if (!cmpPosition || !cmpPosition.IsInWorld()) 632 return; 633 634 let pos = cmpPosition.GetPosition(); 635 let cmpDamage = Engine.QueryInterface(SYSTEM_ENTITY, IID_Damage); 636 let owner = Engine.QueryInterface(this.entity, IID_Ownership).GetOwner(); 637 let playersToDamage = cmpDamage.GetPlayersToDamage(this.template.Death.FriendlyFire, owner); 638 639 cmpDamage.CauseSplashDamage({ 640 "attacker": this.entity, 641 "origin": Vector2D.from3D(pos), 642 "radius": this.template.Death.Range, 643 "shape": this.template.Death.Shape, 644 "strengths": this.GetAttackStrengths("Death"), 645 "direction": 0, 646 "playersToDamage": playersToDamage, 647 "type": this.template.Type, 648 "attackerOwner": owner 649 }); 650 }; 651 608 652 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 * @return {list of numbers} - the ids of players need to be damaged 72 */ 73 Damage.prototype.GetPlayersToDamage = function(friendlyFire, attackerOwner) 74 { 75 if (!friendlyFire) 76 return QueryPlayerIDInterface(attackerOwner).GetEnemies(); 77 78 let playersToDamage = []; 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(); 247 251 } 248 252 249 253 } -
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": 1000.0, 101 "Pierce": 1000.0, 102 "Crush": 500.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 let 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": 1000, "pierce": 1000, "crush": 500}, 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>