Ticket #1910: 1910-death-damage-8.patch
File 1910-death-damage-8.patch, 14.9 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 = 42 "<interleave>" + 43 "<element name='Shape' a:help='Shape of the splash damage, can be circular or linear'><text/></element>" + 44 "<element name='Range' a:help='Size of the area affected by the splash'><ref name='nonNegativeDecimal'/></element>" + 45 "<element name='FriendlyFire' a:help='Whether the splash damage can hurt non enemy units'><data type='boolean'/></element>" + 46 "<element name='Hack' a:help='Hack damage strength'><ref name='nonNegativeDecimal'/></element>" + 47 "<element name='Pierce' a:help='Pierce damage strength'><ref name='nonNegativeDecimal'/></element>" + 48 "<element name='Crush' a:help='Crush damage strength'><ref name='nonNegativeDecimal'/></element>" + 49 Attack.prototype.bonusesSchema + 50 "</interleave>"; 40 51 41 52 Attack.prototype.Schema = 42 53 "<a:help>Controls the attack abilities and strengths of the unit.</a:help>" + … … 94 105 "<Crush>0.0</Crush>" + 95 106 "<MaxRange>4.0</MaxRange>" + 96 107 "</Slaughter>" + 108 "<Death>" + 109 "<Shape>Circular</Shape>" + 110 "<Range>20</Range>" + 111 "<FriendlyFire>false</FriendlyFire>" + 112 "<Hack>0.0</Hack>" + 113 "<Pierce>10.0</Pierce>" + 114 "<Crush>50.0</Crush>" + 115 "</Death>" + 97 116 "</a:example>" + 98 117 "<optional>" + 99 118 "<element name='Melee'>" + … … 138 157 "<optional>" + 139 158 "<element name='Splash'>" + 140 159 "<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 + 160 Attack.prototype.splashSchema + 148 161 "</interleave>" + 149 162 "</element>" + 150 163 "</optional>" + … … 177 190 Attack.prototype.restrictedClassesSchema + 178 191 "</interleave>" + 179 192 "</element>" + 193 "</optional>" + 194 "<optional>" + 195 "<element name='Death' a:help='When a unit or building is destroyed then it inflicts damage to nearby units.'>" + 196 Attack.prototype.splashSchema + 197 "</element>" + 180 198 "</optional>"; 181 199 182 200 Attack.prototype.Init = function() … … 605 623 cmpUnitAI.UpdateRangeQueries(); 606 624 }; 607 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 || !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, 647 "playersToDamage": playersToDamage, 648 "type": this.template.Type, 649 "attackerOwner": owner 650 }); 651 }; 652 608 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 * @return {list of numbers} - the ids of players need to be damaged 72 */ 73 Damage.prototype.GetPlayersToDamage = function(friendlyFire, attackerOwner) 74 { 75 let player = QueryPlayerIDInterface(attackerOwner); 76 let nonAllies = player.GetEnemies().concat(player.GetNeutrals()); 77 78 return friendlyFire ? nonAllies.concat(player.GetAllies()) : nonAllies; 79 } 80 81 /** 68 82 * Handles hit logic after the projectile travel time has passed. 69 83 * @param {Object} data - the data sent by the caller. 70 84 * @param {number} data.attacker - the entity id of the attacker. … … 90 104 // Do this first in case the direct hit kills the target 91 105 if (data.isSplash) 92 106 { 93 let playersToDamage = [];94 if (!data.friendlyFire)95 playersToDamage = QueryPlayerIDInterface(data.attackerOwner).GetEnemies();96 else97 {98 let numPlayers = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetNumPlayers();99 for (let i = 0; i < numPlayers; ++i)100 playersToDamage.push(i);101 }102 103 107 this.CauseSplashDamage({ 104 108 "attacker": data.attacker, 105 109 "origin": Vector2D.from3D(data.position), … … 107 111 "shape": data.shape, 108 112 "strengths": data.splashStrengths, 109 113 "direction": data.direction, 110 "playersToDamage": playersToDamage,114 "playersToDamage": this.GetPlayersToDamage(data.friendlyFire, data.attackerOwner), 111 115 "type": data.type, 112 116 "attackerOwner": data.attackerOwner 113 117 }); -
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/Player.js
646 646 return this.GetPlayersByDiplomacy("IsEnemy"); 647 647 }; 648 648 649 Player.prototype.GetNeutrals = function() 650 { 651 return this.GetPlayersByDiplomacy("IsNeutral"); 652 }; 653 649 654 Player.prototype.SetNeutral = function(id) 650 655 { 651 656 this.SetDiplomacyIndex(id, 0); -
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
49 49 50 50 AddMock(atkPlayerEntity, IID_Player, { 51 51 GetEnemies: () => [targetOwner], 52 GetNeutrals: () => [] 52 53 }); 53 54 54 55 AddMock(SYSTEM_ENTITY, IID_PlayerManager, { 55 56 GetPlayerByID: (id) => atkPlayerEntity, 57 GetNumPlayers: () => 5 56 58 }); 57 59 58 60 AddMock(SYSTEM_ENTITY, IID_RangeManager, { … … 122 124 cmpAttack.PerformAttack("Ranged", target); 123 125 Engine.DestroyEntity(attacker); 124 126 TestDamage(); 127 128 atkPlayerEntity = 1; 129 AddMock(atkPlayerEntity, IID_Player, { 130 GetEnemies: () => [2, 3], 131 GetNeutrals: () => [4], 132 GetAllies: () => [0, 1] 133 }); 134 TS_ASSERT_UNEVAL_EQUALS(cmpDamage.GetPlayersToDamage(true, atkPlayerEntity), [2, 3, 4, 0, 1]); 135 TS_ASSERT_UNEVAL_EQUALS(cmpDamage.GetPlayersToDamage(false, atkPlayerEntity), [2, 3, 4]); -
binaries/data/mods/public/simulation/components/tests/test_Player.js
16 16 cmpPlayer.SetDiplomacy([-1, 1, 0, 1, -1]); 17 17 TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetAllies(), [1, 3]); 18 18 TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetEnemies(), [0, 4]); 19 TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetNeutrals(), [2]); 19 20 20 21 var diplo = cmpPlayer.GetDiplomacy(); 21 22 diplo[0] = 1; -
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>23</Range> 32 <FriendlyFire>true</FriendlyFire> 33 <Hack>10.0</Hack> 34 <Pierce>10.0</Pierce> 35 <Crush>20.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>23</Range> 24 <FriendlyFire>true</FriendlyFire> 25 <Hack>10.0</Hack> 26 <Pierce>10.0</Pierce> 27 <Crush>20.0</Crush> 28 </Death> 21 29 </Attack> 22 30 <BuildingAI> 23 31 <DefaultArrowCount>3</DefaultArrowCount>