wghost81 Posted January 26, 2014 Share Posted January 26, 2014 (edited) monju125, that's interesting, because CNH dynamic units have Hunter = false property (default is true). I believe, on covert extraction missions all EXALT units were supposed to actively search for covert operative and on capture and hold missions they were supposed to make a run for capture zone. Edited January 26, 2014 by wghost81 Link to comment Share on other sites More sharing options...
Amineri Posted January 26, 2014 Author Share Posted January 26, 2014 I decided to take a look into why the EXALT AI seems so driven to push soldiers into the target zone, and I thought I'd share what I've found. First off, a new concept of a "Capper" was introduced with Enemy Within, which I think only applies to EXALT. The variable m_bCapper is set true for those units that the XGAIPlayer designates to push into the target zone. The cappers are updated in XGAIPlayer.UpdateCappers. Basically all current units are sorted according to distance to the target zone, and the two current closest units are designated as the cappers. I think that this is the start of the problem, as even if there are only 1 or 2 units active, those 1 or 2 units are designated as cappers and will be pushed into the target zone, even though it can be suicidal to do so. There's no concept of awaiting reinforcements before attempting to push in. At the least I'd think that no more than 1/3 to 1/2 of the units should be designated Cappers. If there's only 1 or 2 units, they shouldn't be a Capper. If there are three units, then designating 1 as Capper make sense, as it can dash forward with cover from his mates. Ideally there would be some additional tests to ensure that the extra units are close enough to actually provide support. How the capper info is used is the next part, and has to deal the DestinationScore. DestinationScoreis a function defined in XGAIBehavior that XGAIPlayer uses to determine "how good" each potential move destination. DestinationScore measures several different variables :Range AdvantageDistance OverwatchProximity Flank Angle Visibility Cover Each AI unit type can weight some of these variable differently in order to provide consistent AI behavioral difference between different units. For example Sectoids have their Distance and Flank Weights at 0.50, while Mutons have their Flank weight at 1.50. Range measures "Range Advantage". I'm not entirely sure what this is, but I suspect it is to provide bonuses in assymetric range conditions (e.g. if alien can shoot at XCOM but XCOM can't shoot back, that's a range advantage). A score of up to 100 can be achieved from Range. This is modified by the unit's Range weight. Overwatch measures how many units can overwatch shoot at the location. If Overwatch Danger < 2, then the location gets an Overwatch score of +400, otherwise -250 * (iDangerLevel - 1). iDangerLevel can be no greater than 3, so this can be +400, -250, or -500. If the location flanks any units, it gets a score of 200 + ((25 * iNumFlanking) - 1). If the location flanks the current priority target, it gets an additional +200. This is modified by the unit's Flank Weight. Distance is computed via int(((Square(GetMaxAttackRange()) - fDistance) * float(100)) / Square(GetMaxAttackRange())). fDistance is actually the square of the distance, so this value ranges from 100 down to 0, with higher values at closer range. There is an additional -50 if the unit is over 75% of its max attack range distant from the closest target. Angle tries to move the unit closer to a flanking position. Possible values range from 50 to -50 (50 being if the unit is actually flanking the target). Proximity grants a bonus of ~100 on terror maps for being close to civilian targets. Otherwise it score higher for staying at least 2 meters away from friendly units (presumably to avoid AoE attacks) Visibility applies a penalty of -300 if an enemy unit isn't visible from a piece of cover. Cover adds a -1000 modifier if cover location is exposed, +110 if an enemy is visible from the cover, +110 for low cover, and +170 for full cover. In summary the components of the score and approximate values:Range Advantage -- 0 - 100Overwatch -- -500, -250, or 400Flanking -- 0 - ~450Distance -- -50 - 100Angle -- -50 - 50Proximity -- 0 - 150Visibility -- -300 or 0Cover -- -1000 (exposed) up to +280 Finally, there is a catchall additional score retrieved from XGAIBehavior.ScoreLocation. ScoreLocation is defined in XGAIBehavior, and is also overridden in XGAIBehavior_Exalt. In XGAIBehavior.ScoreLocation is the following Exalt-specific chunk: if(m_bCapper && !bInPriorityVolume) { return 0; } // End:0x70 else { // End:0x70 if(bInPriorityVolume) { iBonus = 5000; } } In addition to the +5000 bonus for Cappers moving into the target area, in XGAIBehavior_Exalt.ScoreLocation is : iBonus = super.ScoreLocation(kScore, fDistance); if(!m_bCapper && m_kPlayer.m_kPriorityVolume != none) { vTestLoc = kScore.vLoc; vTestLoc.Z += 96.0; if(IsInsidePriorityVolume(vTestLoc)) { iBonus = 1500; } else { vToCapturePoint = m_kPlayer.m_kPriorityVolume.Location - m_kUnit.GetPawn().Location; vToTestLoc = vTestLoc - m_kUnit.GetPawn().Location; fDot = Normal(vToCapturePoint) Dot Normal(vToTestLoc); if(fDot > float(0)) { fBonusPercentage = FMin(1.0, VSize(vToTestLoc) / (float(7) * 96.0)); fBonusPercentage *= fDot; iBonus += int(float(1500) * fBonusPercentage); } } } return iBonus; For non-Cappers, this overrides the bonus set in the super function, but still grants a +1500 bonus to other units to move into the target area. In comparison, the penalty for moving to an exposed position is only -1000. If the location isn't within the target area, instead fitness is measure by how much closer the position is to the capture point, with up to +1500 for moving directly toward the capture point. Link to comment Share on other sites More sharing options...
monju125 Posted January 26, 2014 Share Posted January 26, 2014 monju125, that's interesting, because CNH dynamic units have Hunter = false property (default is true). I believe, on covert extraction missions all EXALT units were supposed to actively search for covert operative and on capture and hold missions they were supposed to make a run for capture zone. They normally do, but as the code I posted a few posts back shows, that's because they're hardcoded to use DirectHunt. I changed Roll(100) to Roll(50) in that function to allow them to use WideHunt and FlankHunt, but it doesn't really work well because they choose a hunt path every single turn, making them switch randomly between the three from turn to turn. Link to comment Share on other sites More sharing options...
wghost81 Posted January 26, 2014 Share Posted January 26, 2014 (edited) monju125, may be random tactics selection should be moved to init code instead of turn code? So every hunter will get a random tactics and will follow that tactics every turn. Edited January 26, 2014 by wghost81 Link to comment Share on other sites More sharing options...
Amineri Posted January 27, 2014 Author Share Posted January 27, 2014 Another thing I want to look into is that the AI doesn't very well take into account possible kill shots when prioritizing actions. There is a function HasPotentialKillShot(), but it is only used in HasPredeterminedAbility() as a possible factor as to whether a predetermined ability should be used. Also, HasPotentialKillShot does not take into account bonus critical damage, but only uses the XGWeapon.GetPossibleDamage() function. Instead the effective to-hit is increased slightly for low HP targets according to the formula : iHP = kAbility.GetPrimaryTarget().GetUnitHP(); if(iHP < 5) { return 7.0 * float(5 - iHP); } return 0.0; This works well in the early game, but in the late game doesn't work at all, when alien weapon damage are in the 8 to 9 range. Link to comment Share on other sites More sharing options...
monju125 Posted January 28, 2014 Share Posted January 28, 2014 monju125, may be random tactics selection should be moved to init code instead of turn code? So every hunter will get a random tactics and will follow that tactics every turn. The only problem with that is that the tactic includes the hunt target, so if its set in the init code, it would have to be updated later on when a target is no longer valid (for example, one cap point is taken, and the other becomes active). It's probably as easy as adding some code to BeginTurn to check for a valid XGTactic_Hunt before it resets a unit's tactic. Link to comment Share on other sites More sharing options...
wghost81 Posted February 6, 2014 Share Posted February 6, 2014 monju125, Amineri, do any of you by any chance know where to enable patrolling mode for Council mission pods? Haven't had much luck in finding this yet. Link to comment Share on other sites More sharing options...
Amineri Posted February 6, 2014 Author Share Posted February 6, 2014 I was just updating the mod that disables alien pods from warping around in the Fog of War before being activated, which is fairly related. It appears that the function XComAlienPathManager.InitTrippedPatrol is the function that starts a pod patrolling. This can be called from XComAlienPod.InitTrippedPatrol, but requires that m_kDynamicAI be initialized. m_kDynamicAI is a just a weirdly name instance of XComAlienPathHandler, which is created during XComAlienPod.Init via: if(HasPath()) { m_kDynamicAI = Spawn(class'XComAlienPathHandler'); m_kDynamicAI.InitRoute(self); } which would seem to require that the pod have a path defined. HasPath returns 'return PathNodes.Length > 1;', which could be randomized. The other place InitTrippedPatrol is called is in XComAlienPodManager.RevealNextPod in the lines : if((m_iActivePodIdx < m_arrActivation.Length) && m_arrActivation[m_iActivePodIdx].IsActive()) { if(m_arrTripped.Find(m_arrActivation[m_iActivePodIdx]) != -1) { if(!m_arrActivation[m_iActivePodIdx].HasBegunTrippedPatrol()) { m_arrActivation[m_iActivePodIdx].InitTrippedPatrol(); } } Link to comment Share on other sites More sharing options...
Amineri Posted February 6, 2014 Author Share Posted February 6, 2014 Tracing back what I had to do in EU to remove aliens from teleporting in the fog of war, I discovered that Firaxis had turned on the Astar search algorithm for pathfinding for pod patrol movement (which was the main thing the prevented them from skipping around really badly). This means that only the "allowed" pod teleporting which allows for pod teleporting when out of sight has to be removed, which can be removed with a quite small change: MODFILEVERSION=4 UPKFILE=XComGame.upk GUID=5B 06 B8 18 67 22 12 44 85 9B A8 5B 9D 57 1D 4B // XComGame_EW_patch1.upk FUNCTION=InitMoveManeuver@XComAlienPod //disables alien pod teleporting even when not revealed and in fog of war // Astar change was incorporated into vanilla Enemy Within code [BEFORE_HEX] [HEADER] [/HEADER] [CODE] //bReveal = WalkPodToLocation(vDestination, bAllowWarping) 14 2D 00 C4 40 00 00 1B 05 7E 00 00 00 00 00 00 00 C1 40 00 00 2D 00 B1 40 00 00 16 [/CODE] [/BEFORE_HEX] [AFTER_HEX] [HEADER] [/HEADER] [CODE] //bReveal = WalkPodToLocation(vDestination, false) 14 2D 00 C4 40 00 00 1B 05 7E 00 00 00 00 00 00 00 C1 40 00 00 28 16 //null ops 00 C1 40 00 00 [/CODE] [/AFTER_HEX] Link to comment Share on other sites More sharing options...
wghost81 Posted February 6, 2014 Share Posted February 6, 2014 (edited) m_kDynamicAI is used for predefined patrol routes, embedded in map pods. Is is defined for UFO maps, for example. But not defined for abductions. But. It isn't used in actual games at all, as far as I can tell. Only for debug missions with no overmind control. XComAlienPodManager.OvermindSpawn calls OnSpawn function which eventually calls for XGOvermind.OnPodSpawned. This function checks if all pods are spawned and if so calls for BeginMission. BeginMission calls for DetermineTactics and DetermineTactics sets pod tactics, based on mission type: DetermineHQAssaultTactics, DetermineCaptureAndHoldTactics, DetermineCovertExtractionTactics, DetermineAbductionTactics, DetermineUFOTactics. There is no specific entry for Council missions, but last else should handle it. General tactics work like this: if pod MustLurk (true for UFO/Base Commanders), it spawns XGTactic_Lurk. For UFO and abduction it checks for ini lurk percentage (ABDUCTION_LURK_PCT, etc) and spawns XGTactic_Lurk and XGTactic_Patrol accordingly. For EXALT missions it creates hunt targets. The last else in DetermineTactics spawns XGTactic_Lurk for Commanders and XGTactic_Patrol for the others. But if XGTactic_Patrol.InitPatrol fails, it spawns XGTactic_Lurk. XGTactic_Patrol actually creates random patrol routes. It fails when BuildPatrolRect fails to FitRectToMap. And I don't think it's the case, as abduction pods on the same map are patrolling normally. At this point I don't know where to find Council mission specific tactic. I traced function calls and couldn't find anything, which could prevent pods from patrolling. But. There is some code in XGTactic_Patrol I can't figure out yet, it's related to m_fParameter calculation and usage. It is used by Overmind too and as it seems tries to switch pods from patrolling state to hunting state. I don't understand how it works, but when I disabled it nothing changed. So at this point I don't know where else I should look. :sad: Edited February 6, 2014 by wghost81 Link to comment Share on other sites More sharing options...
Recommended Posts