Ticket #4098: 4098.1.diff
File 4098.1.diff, 20.7 KB (added by , 8 years ago) |
---|
-
binaries/data/mods/public/simulation/components/UnitAI.js
356 356 // Check if we need to move TODO implement a better way to know if we are on the shoreline 357 357 var needToMove = true; 358 358 var cmpPosition = Engine.QueryInterface(this.entity, IID_Position); 359 if (this.lastShorelinePosition && cmpPosition && (this.lastShorelinePosition.x == cmpPosition.GetPosition().x) 360 && (this.lastShorelinePosition.z == cmpPosition.GetPosition().z)) 359 if (this.lastShorelinePosition && cmpPosition && 360 this.lastShorelinePosition.x == cmpPosition.GetPosition().x && 361 this.lastShorelinePosition.z == cmpPosition.GetPosition().z) 361 362 { 362 363 // we were already on the shoreline, and have not moved since 363 364 if (DistanceBetweenEntities(this.entity, this.order.data.target) < 50) … … 1376 1377 1377 1378 "GuardedAttacked": function(msg) { 1378 1379 // do nothing if we have a forced order in queue before the guard order 1379 for ( vari = 0; i < this.orderQueue.length; ++i)1380 for (let i = 0; i < this.orderQueue.length; ++i) 1380 1381 { 1381 1382 if (this.orderQueue[i].type == "Guard") 1382 1383 break; … … 1831 1832 var animationName = "attack_" + this.order.data.attackType.toLowerCase(); 1832 1833 if (this.IsFormationMember()) 1833 1834 { 1834 varcmpFormation = Engine.QueryInterface(this.formationController, IID_Formation);1835 let cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation); 1835 1836 if (cmpFormation) 1836 1837 animationName = cmpFormation.GetFormationAnimation(this.entity, animationName); 1837 1838 } … … 2054 2055 2055 2056 // Try to find another nearby target of the same specific type 2056 2057 // Also don't switch to a different type of huntable animal 2057 var nearby = this.FindNearbyResource(function (ent, type, template) { 2058 return ( 2059 ent != oldTarget 2060 && ((type.generic == "treasure" && oldType.generic == "treasure") 2061 || (type.specific == oldType.specific 2062 && (type.specific != "meat" || oldTemplate == template))) 2063 ); 2064 }, oldTarget); 2058 let nearby = this.FindNearbyResource((ent, type, template) => 2059 ent != oldTarget && 2060 (type.generic == "treasure" && oldType.generic == "treasure" || 2061 (type.specific == oldType.specific && 2062 (type.specific != "meat" || oldTemplate == template))), 2063 oldTarget); 2065 2064 if (nearby) 2066 2065 { 2067 2066 this.PerformGather(nearby, false, false); … … 2082 2081 else 2083 2082 { 2084 2083 // we're kind of stuck here. Return resource. 2085 varnearby = this.FindNearestDropsite(oldType.generic);2084 nearby = this.FindNearestDropsite(oldType.generic); 2086 2085 if (nearby) 2087 2086 { 2088 2087 this.PushOrderFront("ReturnResource", { "target": nearby, "force": false }); … … 2119 2118 2120 2119 // Try to find another nearby target of the same specific type 2121 2120 // Also don't switch to a different type of huntable animal 2122 var nearby = this.FindNearbyResource(function (ent, type, template) { 2123 return ( 2124 ent != oldTarget 2125 && ((type.generic == "treasure" && oldType.generic == "treasure") 2126 || (type.specific == oldType.specific 2127 && (type.specific != "meat" || oldTemplate == template))) 2128 ); 2129 }); 2121 let nearby = this.FindNearbyResource((ent, type, template) => 2122 ent != oldTarget && 2123 (type.generic == "treasure" && oldType.generic == "treasure" || 2124 (type.specific == oldType.specific && 2125 (type.specific != "meat" || oldTemplate == template)))); 2130 2126 if (nearby) 2131 2127 { 2132 2128 this.PerformGather(nearby, false, false); … … 2165 2161 2166 2162 // Try to find another nearby target of the same specific type 2167 2163 // Also don't switch to a different type of huntable animal 2168 var nearby = this.FindNearbyResource(function (ent, type, template) { 2169 return ( 2170 (type.generic == "treasure" && resourceType.generic == "treasure") 2171 || (type.specific == resourceType.specific 2172 && (type.specific != "meat" || resourceTemplate == template)) 2173 ); 2174 }); 2164 let nearby = this.FindNearbyResource((ent, type, template) => 2165 (type.generic == "treasure" && resourceType.generic == "treasure") || 2166 (type.specific == resourceType.specific && 2167 (type.specific != "meat" || resourceTemplate == template))); 2175 2168 2176 2169 // If there is a nearby resource start gathering 2177 2170 if (nearby) … … 2185 2178 return; 2186 2179 2187 2180 // Nothing better to do: go back to dropsite 2188 varnearby = this.FindNearestDropsite(resourceType.generic);2181 nearby = this.FindNearestDropsite(resourceType.generic); 2189 2182 if (nearby) 2190 2183 { 2191 2184 this.PushOrderFront("ReturnResource", { "target": nearby, "force": false }); … … 2289 2282 { 2290 2283 // Gather the resources: 2291 2284 2292 varcmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer);2285 let cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer); 2293 2286 2294 2287 // Try to gather treasure 2295 2288 if (cmpResourceGatherer.TryInstantGather(this.gatheringTarget)) … … 2307 2300 // return to the nearest dropsite 2308 2301 if (status.filled) 2309 2302 { 2310 varnearby = this.FindNearestDropsite(resourceType.generic);2303 let nearby = this.FindNearestDropsite(resourceType.generic); 2311 2304 if (nearby) 2312 2305 { 2313 2306 // (Keep this Gather order on the stack so we'll … … 2357 2350 2358 2351 // Give up on this order and try our next queued order 2359 2352 // but first check what is our next order and, if needed, insert a returnResource order 2360 varcmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer);2353 let cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer); 2361 2354 if (cmpResourceGatherer.IsCarrying(resourceType.generic) && 2362 2355 this.orderQueue.length > 1 && this.orderQueue[1] !== "ReturnResource" && 2363 2356 (this.orderQueue[1].type !== "Gather" || this.orderQueue[1].data.type.generic !== resourceType.generic)) … … 2373 2366 2374 2367 // Try to find a new resource of the same specific type near our current position: 2375 2368 // Also don't switch to a different type of huntable animal 2376 var nearby = this.FindNearbyResource(function (ent, type, template) { 2377 return ( 2378 (type.generic == "treasure" && resourceType.generic == "treasure") 2379 || (type.specific == resourceType.specific 2380 && (type.specific != "meat" || resourceTemplate == template)) 2381 ); 2382 }); 2369 let nearby = this.FindNearbyResource((ent, type, template) => 2370 (type.generic == "treasure" && resourceType.generic == "treasure") || 2371 (type.specific == resourceType.specific && 2372 (type.specific != "meat" || resourceTemplate == template))); 2383 2373 if (nearby) 2384 2374 { 2385 2375 this.PerformGather(nearby, false, false); … … 2397 2387 // drop it off, and if not then we might as well head to the dropsite 2398 2388 // anyway because that's a nice enough place to congregate and idle 2399 2389 2400 varnearby = this.FindNearestDropsite(resourceType.generic);2390 nearby = this.FindNearestDropsite(resourceType.generic); 2401 2391 if (nearby) 2402 2392 { 2403 2393 this.PushOrderFront("ReturnResource", { "target": nearby, "force": false }); … … 2565 2555 // Dump any resources we can 2566 2556 var dropsiteTypes = cmpResourceDropsite.GetTypes(); 2567 2557 2568 varcmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer);2558 let cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer); 2569 2559 cmpResourceGatherer.CommitResources(dropsiteTypes); 2570 2560 2571 2561 // Stop showing the carried resource animation. … … 2581 2571 // The dropsite was destroyed, or we couldn't reach it, or ownership changed 2582 2572 // Look for a new one. 2583 2573 2584 varcmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer);2574 let cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer); 2585 2575 var genericType = cmpResourceGatherer.GetMainCarryingType(); 2586 2576 var nearby = this.FindNearestDropsite(genericType); 2587 2577 if (nearby) … … 2776 2766 // the build command should look for nearby resources to gather 2777 2767 if ((oldData.force || oldData.autoharvest) && this.CanReturnResource(msg.data.newentity, false)) 2778 2768 { 2779 varcmpResourceDropsite = Engine.QueryInterface(msg.data.newentity, IID_ResourceDropsite);2780 vartypes = cmpResourceDropsite.GetTypes();2769 cmpResourceDropsite = Engine.QueryInterface(msg.data.newentity, IID_ResourceDropsite); 2770 let types = cmpResourceDropsite.GetTypes(); 2781 2771 // TODO: Slightly undefined behavior here, we don't know what type of resource will be collected, 2782 2772 // may cause problems for AIs (especially hunting fast animals), but avoid ugly hacks to fix that! 2783 var nearby = this.FindNearbyResource(function (ent, type, template) { 2784 return (types.indexOf(type.generic) != -1); 2785 }); 2773 let nearby = this.FindNearbyResource((ent, type, template) => types.indexOf(type.generic) != -1); 2786 2774 if (nearby) 2787 2775 { 2788 2776 this.PerformGather(nearby, true, false); … … 2861 2849 "GARRISONED": { 2862 2850 "enter": function() { 2863 2851 // Target is not handled the same way with Alert and direct garrisoning 2864 if (this.order.data.target) 2865 var target = this.order.data.target; 2866 else 2852 var target = this.order.data.target; 2853 if (!target) 2867 2854 { 2868 2855 if (!this.alertGarrisoningTarget) 2869 2856 { … … 2871 2858 this.FinishOrder(); 2872 2859 return true; 2873 2860 } 2874 vartarget = this.alertGarrisoningTarget;2861 target = this.alertGarrisoningTarget; 2875 2862 } 2876 2863 2877 2864 // Check that we can garrison here … … 3243 3230 3244 3231 UnitAI.prototype.IsUnderAlert = function() 3245 3232 { 3246 return this.alertRaiser != undefined;3233 return this.alertRaiser !== undefined; 3247 3234 }; 3248 3235 3249 3236 UnitAI.prototype.ResetAlert = function() … … 3367 3354 { 3368 3355 // Switch to a virgin state to let states execute their leave handlers. 3369 3356 // except if garrisoned or cheering or (un)packing, in which case we only clear the order queue 3370 if (this.isGarrisoned || (this.orderQueue[0] && (this.orderQueue[0].type == "Cheering" 3371 ||this.orderQueue[0].type == "Pack" || this.orderQueue[0].type == "Unpack")))3357 if (this.isGarrisoned || (this.orderQueue[0] && (this.orderQueue[0].type == "Cheering" || 3358 this.orderQueue[0].type == "Pack" || this.orderQueue[0].type == "Unpack"))) 3372 3359 { 3373 3360 this.orderQueue.length = Math.min(this.orderQueue.length, 1); 3374 3361 Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); … … 3427 3414 { 3428 3415 if (this.orderQueue[i].type != "PickupUnit" || this.orderQueue[i].data.target != msg.entity) 3429 3416 continue; 3430 if (i == 0)3417 if (i === 0) 3431 3418 this.UnitFsm.ProcessMessage(this, {"type": "PickupCanceled", "data": msg}); 3432 3419 else 3433 3420 this.orderQueue.splice(i, 1); … … 3729 3716 // TODO: maybe a better way of doing this would be to use priority levels 3730 3717 if (this.order && this.order.type == "Cheering") 3731 3718 { 3732 varorder = { "type": type, "data": data };3719 let order = { "type": type, "data": data }; 3733 3720 var cheeringOrder = this.orderQueue.shift(); 3734 3721 this.orderQueue = [cheeringOrder, order]; 3735 3722 } 3736 3723 else if (this.IsPacking() && type != "CancelPack" && type != "CancelUnpack") 3737 3724 { 3738 varorder = { "type": type, "data": data };3725 let order = { "type": type, "data": data }; 3739 3726 var packingOrder = this.orderQueue.shift(); 3740 3727 this.orderQueue = [packingOrder, order]; 3741 3728 } … … 3792 3779 var cmpUnitAI = Engine.QueryInterface(this.formationController, IID_UnitAI); 3793 3780 if (cmpUnitAI) 3794 3781 { 3795 for ( vari = 0; i < cmpUnitAI.orderQueue.length; ++i)3782 for (let i = 0; i < cmpUnitAI.orderQueue.length; ++i) 3796 3783 { 3797 3784 if (isWorkType(cmpUnitAI.orderQueue[i].type)) 3798 3785 { … … 3804 3791 } 3805 3792 3806 3793 // If nothing found, take the unit orders 3807 for ( vari = 0; i < this.orderQueue.length; ++i)3794 for (let i = 0; i < this.orderQueue.length; ++i) 3808 3795 { 3809 3796 if (isWorkType(this.orderQueue[i].type)) 3810 3797 { … … 3816 3803 3817 3804 UnitAI.prototype.BackToWork = function() 3818 3805 { 3819 if ( this.workOrders.length == 0)3806 if (!this.workOrders.length) 3820 3807 return false; 3821 3808 3822 3809 // Clear the order queue considering special orders not to avoid … … 3998 3985 return true; 3999 3986 4000 3987 var cmpHealth = QueryMiragedInterface(ent, IID_Health); 4001 return cmpHealth && cmpHealth.GetHitpoints() != 0;3988 return cmpHealth && cmpHealth.GetHitpoints() !== 0; 4002 3989 }; 4003 3990 4004 3991 /** … … 4336 4323 var t = targetCmpPosition.GetPosition(); 4337 4324 // h is positive when I'm higher than the target 4338 4325 var h = s.y-t.y+range.elevationBonus; 4339 4326 let parabolicMaxRange = 0; 4340 4327 // No negative roots please 4341 if (h>-range.max/2) 4342 var parabolicMaxRange = Math.sqrt(range.max*range.max+2*range.max*h); 4343 else 4344 // return false? Or hope you come close enough? 4345 var parabolicMaxRange = 0; 4346 //return false; 4328 if (h > - range.max / 2) 4329 parabolicMaxRange = Math.sqrt(range.max*range.max+2*range.max*h); 4347 4330 4348 4331 // the parabole changes while walking, take something in the middle 4349 4332 var guessedMaxRange = (range.max + parabolicMaxRange)/2; … … 4408 4391 if (this.IsFormationMember()) 4409 4392 { 4410 4393 var cmpFormationUnitAI = Engine.QueryInterface(this.formationController, IID_UnitAI); 4411 if (cmpFormationUnitAI && cmpFormationUnitAI.IsAttackingAsFormation() 4412 && cmpFormationUnitAI.order.data.target == target) 4394 if (cmpFormationUnitAI && cmpFormationUnitAI.IsAttackingAsFormation() && cmpFormationUnitAI.order.data.target == target) 4413 4395 return true; 4414 4396 } 4415 4397 … … 4588 4570 UnitAI.prototype.AttackEntityInZone = function(ents, forceResponse) 4589 4571 { 4590 4572 var target = ents.find(target => 4591 this.CanAttack(target, forceResponse) 4592 && this.CheckTargetDistanceFromHeldPosition(target, IID_Attack, this.GetBestAttackAgainst(target, true))4593 &&(this.GetStance().respondChaseBeyondVision || this.CheckTargetIsInVisionRange(target))4573 this.CanAttack(target, forceResponse) && 4574 this.CheckTargetDistanceFromHeldPosition(target, IID_Attack, this.GetBestAttackAgainst(target, true)) && 4575 (this.GetStance().respondChaseBeyondVision || this.CheckTargetIsInVisionRange(target)) 4594 4576 ); 4595 4577 if (!target) 4596 4578 return false; … … 5098 5080 5099 5081 // Remember the position of our target, if any, in case it disappears 5100 5082 // later and we want to head to its last known position 5101 var lastPos = undefined;5083 var lastPos; 5102 5084 var cmpPosition = Engine.QueryInterface(target, IID_Position); 5103 5085 if (cmpPosition && cmpPosition.IsInWorld()) 5104 5086 lastPos = cmpPosition.GetPosition(); … … 5225 5207 5226 5208 UnitAI.prototype.MoveToMarket = function(targetMarket) 5227 5209 { 5228 if (this.waypoints && this.waypoints.length > 1) 5229 { 5230 var point = this.waypoints.pop(); 5231 var ok = this.MoveToPoint(point.x, point.z); 5232 if (!ok) 5233 ok = this.MoveToMarket(targetMarket); 5234 } 5235 else 5236 { 5237 this.waypoints = undefined; 5238 var ok = this.MoveToTarget(targetMarket); 5239 } 5240 5241 return ok; 5210 if (this.waypoints && this.waypoints.length > 1) 5211 { 5212 var point = this.waypoints.pop(); 5213 return this.MoveToPoint(point.x, point.z) || this.MoveToMarket(targetMarket); 5214 } 5215 5216 this.waypoints = undefined; 5217 return this.MoveToTarget(targetMarket); 5242 5218 }; 5243 5219 5244 5220 UnitAI.prototype.PerformTradeAndMoveToNextMarket = function(currentMarket) … … 5427 5403 { 5428 5404 var cmpUnitAI; 5429 5405 var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation); 5430 for each (var ent incmpFormation.members)5406 for (let ent of cmpFormation.members) 5431 5407 { 5432 5408 if (!(cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI))) 5433 5409 continue; 5434 vartargets = cmpUnitAI.GetTargetsFromUnit();5435 for ( var targof targets)5410 let targets = cmpUnitAI.GetTargetsFromUnit(); 5411 for (let target of targets) 5436 5412 { 5437 if (!cmpUnitAI.CanAttack(targ ))5413 if (!cmpUnitAI.CanAttack(target)) 5438 5414 continue; 5439 5415 if (this.order.data.targetClasses) 5440 5416 { 5441 var cmpIdentity = Engine.QueryInterface(targ, IID_Identity); 5442 var targetClasses = this.order.data.targetClasses; 5443 if (targetClasses.attack && cmpIdentity 5444 && !MatchesClassList(cmpIdentity.GetClassesList(), targetClasses.attack)) 5417 var cmpIdentity = Engine.QueryInterface(target, IID_Identity); 5418 let targetClasses = this.order.data.targetClasses; 5419 if (targetClasses.attack && cmpIdentity && !MatchesClassList(cmpIdentity.GetClassesList(), targetClasses.attack)) 5445 5420 continue; 5446 if (targetClasses.avoid && cmpIdentity 5447 && MatchesClassList(cmpIdentity.GetClassesList(), targetClasses.avoid)) 5421 if (targetClasses.avoid && cmpIdentity && MatchesClassList(cmpIdentity.GetClassesList(), targetClasses.avoid)) 5448 5422 continue; 5449 5423 // Only used by the AIs to prevent some choices of targets 5450 if (targetClasses.vetoEntities && targetClasses.vetoEntities[targ ])5424 if (targetClasses.vetoEntities && targetClasses.vetoEntities[target]) 5451 5425 continue; 5452 5426 } 5453 this.PushOrderFront("Attack", { "target": targ , "force": true, "allowCapture": true });5427 this.PushOrderFront("Attack", { "target": target, "force": true, "allowCapture": true }); 5454 5428 return true; 5455 5429 } 5456 5430 } … … 5458 5432 } 5459 5433 5460 5434 var targets = this.GetTargetsFromUnit(); 5461 for ( var targof targets)5435 for (let target of targets) 5462 5436 { 5463 if (!this.CanAttack(targ ))5437 if (!this.CanAttack(target)) 5464 5438 continue; 5465 5439 if (this.order.data.targetClasses) 5466 5440 { 5467 var cmpIdentity = Engine.QueryInterface(targ, IID_Identity); 5468 var targetClasses = this.order.data.targetClasses; 5469 if (cmpIdentity && targetClasses.attack 5470 && !MatchesClassList(cmpIdentity.GetClassesList(), targetClasses.attack)) 5441 let cmpIdentity = Engine.QueryInterface(target, IID_Identity); 5442 let targetClasses = this.order.data.targetClasses; 5443 if (cmpIdentity && targetClasses.attack && !MatchesClassList(cmpIdentity.GetClassesList(), targetClasses.attack)) 5471 5444 continue; 5472 if (cmpIdentity && targetClasses.avoid 5473 && MatchesClassList(cmpIdentity.GetClassesList(), targetClasses.avoid)) 5445 if (cmpIdentity && targetClasses.avoid && MatchesClassList(cmpIdentity.GetClassesList(), targetClasses.avoid)) 5474 5446 continue; 5475 5447 // Only used by the AIs to prevent some choices of targets 5476 if (targetClasses.vetoEntities && targetClasses.vetoEntities[targ ])5448 if (targetClasses.vetoEntities && targetClasses.vetoEntities[target]) 5477 5449 continue; 5478 5450 } 5479 this.PushOrderFront("Attack", { "target": targ , "force": true, "allowCapture": true });5451 this.PushOrderFront("Attack", { "target": target, "force": true, "allowCapture": true }); 5480 5452 return true; 5481 5453 } 5482 5454 … … 5533 5505 var ret = { "min": 0, "max": 0 }; 5534 5506 if (this.GetStance().respondStandGround) 5535 5507 { 5536 varcmpRanged = Engine.QueryInterface(this.entity, iid);5508 let cmpRanged = Engine.QueryInterface(this.entity, iid); 5537 5509 if (!cmpRanged) 5538 5510 return ret; 5539 varrange = iid !== IID_Attack ? cmpRanged.GetRange() : cmpRanged.GetFullAttackRange();5511 let range = iid !== IID_Attack ? cmpRanged.GetRange() : cmpRanged.GetFullAttackRange(); 5540 5512 ret.min = range.min; 5541 5513 ret.max = range.max; 5542 5514 } 5543 5515 else if (this.GetStance().respondChase) 5544 5516 { 5545 varcmpVision = Engine.QueryInterface(this.entity, IID_Vision);5517 let cmpVision = Engine.QueryInterface(this.entity, IID_Vision); 5546 5518 if (!cmpVision) 5547 5519 return ret; 5548 var range = cmpVision.GetRange(); 5549 ret.max = range; 5520 ret.max = cmpVision.GetRange(); 5550 5521 } 5551 5522 else if (this.GetStance().respondHoldGround) 5552 5523 { 5553 varcmpRanged = Engine.QueryInterface(this.entity, iid);5524 let cmpRanged = Engine.QueryInterface(this.entity, iid); 5554 5525 if (!cmpRanged) 5555 5526 return ret; 5556 varrange = iid !== IID_Attack ? cmpRanged.GetRange() : cmpRanged.GetFullAttackRange();5557 varcmpVision = Engine.QueryInterface(this.entity, IID_Vision);5527 let range = iid !== IID_Attack ? cmpRanged.GetRange() : cmpRanged.GetFullAttackRange(); 5528 let cmpVision = Engine.QueryInterface(this.entity, IID_Vision); 5558 5529 if (!cmpVision) 5559 5530 return ret; 5560 5531 var halfvision = cmpVision.GetRange() / 2; … … 5564 5535 // but as it is the default for healers we need to set it to something sane. 5565 5536 else if (iid === IID_Heal) 5566 5537 { 5567 varcmpVision = Engine.QueryInterface(this.entity, IID_Vision);5538 let cmpVision = Engine.QueryInterface(this.entity, IID_Vision); 5568 5539 if (!cmpVision) 5569 5540 return ret; 5570 var range = cmpVision.GetRange();5571 ret.max = range;5541 5542 ret.max = cmpVision.GetRange(); 5572 5543 } 5573 5544 return ret; 5574 5545 }; … … 5865 5836 UnitAI.prototype.IsAttackingAsFormation = function() 5866 5837 { 5867 5838 var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); 5868 return cmpAttack && cmpAttack.CanAttackAsFormation() 5869 && this.GetCurrentState() == "FORMATIONCONTROLLER.COMBAT.ATTACKING"; 5839 return cmpAttack && cmpAttack.CanAttackAsFormation() && this.GetCurrentState() == "FORMATIONCONTROLLER.COMBAT.ATTACKING"; 5870 5840 }; 5871 5841 5872 5842 //// Animal specific functions ////