Ticket #1144: restricted_and_preferred_classes.diff
File restricted_and_preferred_classes.diff, 14.4 KB (added by , 12 years ago) |
---|
-
binaries/data/mods/public/simulation/components/tests/test_UnitAI.js
85 85 AddMock(unit, IID_Attack, { 86 86 GetRange: function() { return 10; }, 87 87 GetBestAttack: function() { return "melee"; }, 88 GetBestAttackAgainst: function(t) { return "melee"; }, 88 89 GetTimers: function() { return { "prepare": 500, "repeat": 1000 }; }, 90 CanAttack: function(v) { return true; }, 91 CompareEntitiesByPreference: function(a, b) { return 0; }, 89 92 }); 90 93 91 94 unitAI.OnCreate(); -
binaries/data/mods/public/simulation/components/UnitAI.js
249 249 } 250 250 251 251 // Work out how to attack the given target 252 var type = this.GetBestAttack ();252 var type = this.GetBestAttackAgainst(this.order.data.target); 253 253 if (!type) 254 254 { 255 255 // Oops, we can't attack at all … … 336 336 if (this.MustKillGatherTarget(this.order.data.target) && this.CheckTargetVisible(this.order.data.target)) 337 337 { 338 338 // Make sure we can attack the target, else we'll get very stuck 339 if (!this.GetBestAttack ())339 if (!this.GetBestAttackAgainst(this.order.data.target)) 340 340 { 341 341 // Oops, we can't attack at all - give up 342 342 // TODO: should do something so the player knows why this failed … … 644 644 if (this.GetStance().targetVisibleEnemies) 645 645 { 646 646 // Start attacking one of the newly-seen enemy (if any) 647 this.RespondToTargetedEntities(msg.data.added); 647 var ents = this.GetAttackableEntitiesByPreference(msg.data.added); 648 this.RespondToTargetedEntities(ents); 648 649 } 649 650 }, 650 651 … … 2437 2438 return cmpAttack.GetBestAttack(); 2438 2439 }; 2439 2440 2441 UnitAI.prototype.GetBestAttackAgainst = function(target) 2442 { 2443 var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); 2444 if (!cmpAttack) 2445 return undefined; 2446 return cmpAttack.GetBestAttackAgainst(target); 2447 }; 2448 2440 2449 /** 2441 2450 * Try to find one of the given entities which can be attacked, 2442 2451 * and start attacking it. … … 2462 2471 */ 2463 2472 UnitAI.prototype.AttackEntityInZone = function(ents) 2464 2473 { 2465 var type = this.GetBestAttack();2466 2474 for each (var target in ents) 2467 2475 { 2468 if (this.CanAttack(target) && this.CheckTargetDistanceFromHeldPosition(target, IID_Attack, type))2476 if (this.CanAttack(target)) 2469 2477 { 2470 this.PushOrderFront("Attack", { "target": target, "force": false }); 2471 return true; 2478 var type = this.GetBestAttackAgainst(target); 2479 if (this.CheckTargetDistanceFromHeldPosition(target, IID_Attack, type)) 2480 { 2481 this.PushOrderFront("Attack", { "target": target, "force": false }); 2482 return true; 2483 } 2472 2484 } 2473 2485 } 2474 2486 return false; … … 2928 2940 if (!this.GetStance().targetVisibleEnemies) 2929 2941 return false; 2930 2942 2931 SortEntitiesByPriority(ents); 2932 return this.RespondToTargetedEntities(ents); 2943 return this.RespondToTargetedEntities(this.GetAttackableEntitiesByPreference(ents)); 2933 2944 }; 2934 2945 2935 2946 /** … … 3023 3034 // Verify that we're able to respond to Attack commands 3024 3035 var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); 3025 3036 if (!cmpAttack) 3037 return false; 3038 3039 if (!cmpAttack.CanAttack(target)) 3026 3040 return false; 3027 3041 3028 3042 // Verify that the target is alive … … 3266 3280 return false; 3267 3281 }; 3268 3282 3283 UnitAI.prototype.GetAttackableEntitiesByPreference = function(ents) 3284 { 3285 var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); 3286 if (!cmpAttack) 3287 return []; 3288 3289 return ents 3290 .filter(function (v, i, a) { return cmpAttack.CanAttack(v); }) 3291 .sort(function (a, b) { return cmpAttack.CompareEntitiesByPreference(a, b); }); 3292 }; 3293 3269 3294 Engine.RegisterComponentType(IID_UnitAI, "UnitAI", UnitAI); -
binaries/data/mods/public/simulation/components/Attack.js
18 18 "</element>" + 19 19 "</optional>"; 20 20 21 var preferredClassesSchema = 22 "<optional>" + 23 "<element name='PreferredClasses' a:help='Space delimited list of classes preferred for attacking. If an entity has any of theses classes, it is preferred.'>" + 24 "<attribute name='datatype'>" + 25 "<value>tokens</value>" + 26 "</attribute>" + 27 "<text/>" + 28 "</element>" + 29 "</optional>"; 30 31 var restrictedClassesSchema = 32 "<optional>" + 33 "<element name='RestrictedClasses' a:help='Space delimited list of classes that cannot be attacked by this entity. If target entity has any of these classes, it cannot be attacked'>" + 34 "<attribute name='datatype'>" + 35 "<value>tokens</value>" + 36 "</attribute>" + 37 "<text/>" + 38 "</element>" + 39 "</optional>"; 40 21 41 Attack.prototype.Schema = 22 42 "<a:help>Controls the attack abilities and strengths of the unit.</a:help>" + 23 43 "<a:example>" + … … 38 58 "<Multiplier>1.5</Multiplier>" + 39 59 "</BonusCavMelee>" + 40 60 "</Bonuses>" + 61 "<RestrictedClasses datatype=\"tokens\">Champion</RestrictedClasses>" + 62 "<PreferredClasses datatype=\"tokens\">Cavalry Infantry</PreferredClasses>" + 41 63 "</Melee>" + 42 64 "<Ranged>" + 43 65 "<Hack>0.0</Hack>" + … … 54 76 "<Multiplier>2</Multiplier>" + 55 77 "</Bonus1>" + 56 78 "</Bonuses>" + 79 "<RestrictedClasses datatype=\"tokens\">Champion</RestrictedClasses>" + 57 80 "</Ranged>" + 58 81 "<Charge>" + 59 82 "<Hack>10.0</Hack>" + … … 74 97 "<data type='positiveInteger'/>" + 75 98 "</element>" + 76 99 bonusesSchema + 100 preferredClassesSchema + 101 restrictedClassesSchema + 77 102 "</interleave>" + 78 103 "</element>" + 79 104 "</optional>" + … … 95 120 "<ref name='nonNegativeDecimal'/>" + 96 121 "</element>" + 97 122 bonusesSchema + 123 preferredClassesSchema + 124 restrictedClassesSchema + 98 125 "</interleave>" + 99 126 "</element>" + 100 127 "</optional>" + … … 107 134 "<element name='MaxRange'><ref name='nonNegativeDecimal'/></element>" + // TODO: how do these work? 108 135 "<element name='MinRange'><ref name='nonNegativeDecimal'/></element>" + 109 136 bonusesSchema + 137 preferredClassesSchema + 138 restrictedClassesSchema + 110 139 "</interleave>" + 111 140 "</element>" + 112 141 "</optional>"; … … 117 146 118 147 Attack.prototype.Serialize = null; // we have no dynamic state to save 119 148 149 Attack.prototype.GetAttackTypes = function() 150 { 151 var ret = []; 152 if (this.template.Charge) ret.push("Charge"); 153 if (this.template.Melee) ret.push("Melee"); 154 if (this.template.Ranged) ret.push("Ranged"); 155 return ret; 156 }; 157 158 Attack.prototype.GetPreferredClasses = function(type) 159 { 160 if (this.template[type] && this.template[type].PreferredClasses) 161 { 162 return this.template[type].PreferredClasses["_string"].split(/\s+/); 163 } 164 return []; 165 }; 166 167 Attack.prototype.GetRestrictedClasses = function(type) 168 { 169 if (this.template[type] && this.template[type].RestrictedClasses) 170 { 171 return this.template[type].RestrictedClasses["_string"].split(/\s+/); 172 } 173 return []; 174 }; 175 176 Attack.prototype.CanAttack = function(target) 177 { 178 const cmpIdentity = Engine.QueryInterface(target, IID_Identity); 179 if (!cmpIdentity) 180 return undefined; 181 182 const targetClasses = cmpIdentity.GetClassesList(); 183 184 for each (var type in this.GetAttackTypes()) 185 { 186 var canAttack = true; 187 var restrictedClasses = this.GetRestrictedClasses(type); 188 189 for each (var targetClass in targetClasses) 190 { 191 if (restrictedClasses.indexOf(targetClass) != -1) 192 { 193 canAttack = false; 194 break; 195 } 196 } 197 if (canAttack) 198 { 199 return true; 200 } 201 } 202 203 return false; 204 }; 205 206 Attack.prototype.GetPreference = function(target) 207 { 208 const cmpIdentity = Engine.QueryInterface(target, IID_Identity); 209 if (!cmpIdentity) 210 return undefined; 211 212 const targetClasses = cmpIdentity.GetClassesList(); 213 214 var minPref = null; 215 for each (var type in this.GetAttackTypes()) 216 { 217 for each (var targetClass in targetClasses) 218 { 219 var pref = this.GetPreferredClasses(type).indexOf(targetClass); 220 if (pref != -1 && (minPref === null || minPref > pref)) 221 { 222 error("preference for target " + target + " with type " + type + " " +pref); 223 minPref = pref; 224 } 225 } 226 } 227 return minPref; 228 }; 229 120 230 /** 121 231 * Return the type of the best attack. 122 232 * TODO: this should probably depend on range, target, etc, … … 124 234 */ 125 235 Attack.prototype.GetBestAttack = function() 126 236 { 127 if (this.template.Ranged)128 return "Ranged";129 else if (this.template.Melee) 130 return "Melee"; 131 else if (this.template.Charge) 132 return "Charge";133 else237 return this.GetAttackTypes().pop(); 238 }; 239 240 Attack.prototype.GetBestAttackAgainst = function(target) 241 { 242 const cmpIdentity = Engine.QueryInterface(target, IID_Identity); 243 if (!cmpIdentity) 134 244 return undefined; 245 246 const targetClasses = cmpIdentity.GetClassesList(); 247 const isTargetClass = function (value, i, a) { return targetClasses.indexOf(value) != -1; }; 248 const types = this.GetAttackTypes(); 249 const attack = this; 250 const isAllowed = function (value, i, a) { return !attack.GetRestrictedClasses(value).some(isTargetClass); } 251 const isPreferred = function (value, i, a) { return attack.GetPreferredClasses(value).some(isTargetClass); } 252 const byPreference = function (a, b) { return (types.indexOf(a) + (isPreferred(a) ? types.length : 0) ) - (types.indexOf(b) + (isPreferred(b) ? types.length : 0) ); } 253 254 return types.filter(isAllowed).sort(byPreference).pop(); 135 255 }; 136 256 257 Attack.prototype.CompareEntitiesByPreference = function(a, b) 258 { 259 var aPreference = this.GetPreference(a); 260 var bPreference = this.GetPreference(b); 261 262 if (aPreference === null && bPreference === null) return 0; 263 if (aPreference === null) return bPreference; 264 if (bPreference === null) return aPreference; 265 return aPreference - bPreference; // 0 is most preferred 266 }; 267 137 268 Attack.prototype.GetTimers = function(type) 138 269 { 139 270 var cmpTechMan = QueryOwnerInterface(this.entity, IID_TechnologyManager); -
binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_aggressive.xml
7 7 <Crush>0.0</Crush> 8 8 <MaxRange>4.0</MaxRange> 9 9 <RepeatTime>1000</RepeatTime> 10 <RestrictedClasses datatype="tokens">Structure</RestrictedClasses> 10 11 </Melee> 11 12 </Attack> 12 13 <Identity> -
binaries/data/mods/public/simulation/templates/template_unit_fauna_wild_violent.xml
7 7 <Crush>0.0</Crush> 8 8 <MaxRange>4.0</MaxRange> 9 9 <RepeatTime>1000</RepeatTime> 10 <RestrictedClasses datatype="tokens">Structure</RestrictedClasses> 10 11 </Melee> 11 12 </Attack> 12 13 <Identity> -
binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_fishing.xml
12 12 <Crush>0.0</Crush> 13 13 <MaxRange>5.0</MaxRange> 14 14 <RepeatTime>1000</RepeatTime> 15 <RestrictedClasses datatype="tokens">Ship</RestrictedClasses> 15 16 </Melee> 16 17 </Attack> 17 18 <Footprint> -
binaries/data/mods/public/simulation/templates/template_unit_fauna_wild_aggressive.xml
7 7 <Crush>0.0</Crush> 8 8 <MaxRange>4.0</MaxRange> 9 9 <RepeatTime>1000</RepeatTime> 10 <RestrictedClasses datatype="tokens">Structure</RestrictedClasses> 10 11 </Melee> 11 12 </Attack> 12 13 <Identity> -
binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_violent.xml
7 7 <Crush>0.0</Crush> 8 8 <MaxRange>4.0</MaxRange> 9 9 <RepeatTime>1000</RepeatTime> 10 <RestrictedClasses datatype="tokens">Structure</RestrictedClasses> 10 11 </Melee> 11 12 </Attack> 12 13 <Identity> -
binaries/data/mods/public/simulation/templates/template_unit_support_female_citizen.xml
12 12 <Crush>0.0</Crush> 13 13 <MaxRange>4.0</MaxRange> 14 14 <RepeatTime>1000</RepeatTime> 15 <RestrictedClasses datatype="tokens">Infantry Cavalry Champion CitizenSoldier</RestrictedClasses> 15 16 </Melee> 16 17 </Attack> 17 18 <Auras> -
binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_ram.xml
21 21 <Multiplier>3.0</Multiplier> 22 22 </BonusGates> 23 23 </Bonuses> 24 <RestrictedClasses datatype="tokens">Organic</RestrictedClasses> 25 <PreferredClasses datatype="tokens">Gates Structure</PreferredClasses> 24 26 </Melee> 25 27 <Charge> 26 28 <Hack>0.0</Hack> … … 38 40 <Multiplier>3.0</Multiplier> 39 41 </BonusGates> 40 42 </Bonuses> 43 <RestrictedClasses datatype="tokens">Organic</RestrictedClasses> 44 <PreferredClasses datatype="tokens">Gates Structure</PreferredClasses> 41 45 </Charge> 42 46 </Attack> 43 47 <Cost>