Jump to content

R&D AI Improvements


Amineri

Recommended Posts

I've discovered that it's quite easy to make Sectoids into full psionic aliens with the simple DGC.ini edit of giving them the appropriate ability (e.g. eAbility_PsiPanic, eAbility_MindFray). Upgrading them to have psionic abilities partway through the campaign is trickier, but I'm working on it. I've also worked on tweaking out MindMerge so that the amount of health gained is related to the Will of the caster, which helps keep the ability relevant towards midgame (and also makes it more interesting as a psi perk for the player). I've also made it so that it adds bonus Will to the target, again based on the caster's Will.

 

The problem I'm having right now is in the way that the AI uses psionic abilities. It doesn't appear to use the usual prioritization scheme but instead treats psionic abilities with the "Predetermined Ability" section of code, which basically means that the AI always uses the ability instead of something else, even when the chance to hit is very low.

 

In particular my goal is to make it so that only MindMerged Sectoids have enough Will for a reasonable chance at the psi ability working. I'd also like it if sectoids would still prioritize things like moving and taking flanking shots over using Psi Panic (I've seen them not do so).

Link to comment
Share on other sites

  • Replies 144
  • Created
  • Last Reply

Top Posters In This Topic

You're absolutely correct about it using available psi abilities automatically in the predetermined functions. What happens is that if a Sectoid doesn't have a mind merge available, its HasPredeterminedAbility calls HasPredeterminedAbility in its parent class XGAIBehavior_Psi. That function then checks to see if the unit has any of the other psionic abilities available, and if not calls HasPredeterminedAbility in its parent class XGAIBehavior. Normally for a Sectoid that would mean it would fail to find anything in XGAIBehavior_Psi.HasPredeterminedAbility and call XGAIBehavior.HasPredeterminedAbility and ultimately, more than likely, lead to normal priority behavior. However, since you've added the other psi abilities to the Sectoid, XGAIBehavior_Psi.HasPredeterminedAbility returns true whenever any of those abilities is available for use and the Sectoid defaults to a psi ability. Fixing that seems easy enough, if I can find room to actually add the clause to skip XGAIBehavior_Psi.HasPredeterminedAbility if the unit isn't an Ethereal.

 

I'll have to think about the second part about mindmerged Sectoids a little more.

Link to comment
Share on other sites

If you use either wghost's expand tool or UPKmodder you can increase the size of the function, which makes it easier to slide in new stuff.

 

Here's the UPKmodder file I was using to experiment with the XGAIBehavior_Sectoid.HasPredeterminedAbility :

 

 

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=HasPredeterminedAbility@XGAIBehavior_Sectoid
//RESIZE=2D

// alter behavior so sectoids preferentially try and mindmerge before psi panic or mindfray

[BEFORE_HEX]
[HEADER]
7D 02 00 00 C9 01 00 00 
[/HEADER]
[CODE]
//m_kPredeterminedAbility = none
0F 01 58 9A 00 00 2A 

//if(FindPriorityAbility(m_kPredeterminedAbility))
07 23 00 1B FF 32 00 00 00 00 00 00 01 58 9A 00 00 16 

	//return true
	04 27 

//if(m_iMimicBeaconIndex < 0)
07 67 02 96 01 70 9A 00 00 25 16 

	//m_arrPsiAbilities.Length = 0
	0F 36 01 80 A0 00 00 25 

	//if(!m_kUnit.IsPsiLinked() && m_kPlayer.CanMindMerge() || (XComTacticalGRI(class'Engine'.static.GetCurrentWorldInfo().GRI).m_kBattle.m_kDesc.m_iDifficulty != 0) && m_kPlayer.HasUnshieldedMechtoids())
	07 A1 01 82 81 19 01 8F 9A 00 00 0B 00 BE 3A 00 00 00 1B 72 45 00 00 00 00 00 00 4A 16 16 18 C8 00 84 19 01 90 9A 00 00 0A 00 4A AA 00 00 00 1B 80 11 00 00 00 00 00 00 16 18 A4 00 82 9B 19 19 19 2E BD 32 00 00 19 12 20 36 FE FF FF 0A 00 9E F9 FF FF 00 1C DE FB FF FF 16 09 00 5C F9 FF FF 00 01 5C F9 FF FF 09 00 C9 32 00 00 00 01 C9 32 00 00 09 00 AE AE 00 00 00 01 AE AE 00 00 09 00 99 B2 00 00 00 01 99 B2 00 00 25 16 18 20 00 19 01 90 9A 00 00 0A 00 13 AB 00 00 00 1B 53 3D 00 00 00 00 00 00 16 16 16 16 

		//AddBestPsiAbilityToList(m_arrPsiAbilities, 34, false)
		1B E2 01 00 00 00 00 00 00 01 80 A0 00 00 2C 22 28 16 

		//if((m_arrPsiAbilities.Length > 0) && m_arrPsiAbilities[0].GetPrimaryTarget().GetCharType() == 21)
		07 A1 01 82 97 36 01 80 A0 00 00 25 16 18 3C 00 9A 19 19 10 25 01 80 A0 00 00 0A 00 D2 89 00 00 00 1B 9A 3A 00 00 00 00 00 00 16 0A 00 16 3B 00 00 00 1B 9E 37 00 00 00 00 00 00 16 2C 15 16 16 

			//bTargetIsMechtoid = true
			14 2D 00 21 A3 00 00 27 

	//if((((m_arrPsiAbilities.Length > 0) && m_kPlayer.IsAcceptableMindMergeLocation(m_kUnit.Location, m_kUnit, bTargetIsMechtoid)) && m_kUnit.IsInCover()) && !m_kUnit.IsFlankedByOpposingTeam())
	07 67 02 82 82 82 97 36 01 80 A0 00 00 25 16 18 51 00 19 01 90 9A 00 00 3B 00 DE A9 00 00 00 1B D2 43 00 00 00 00 00 00 19 01 8F 9A 00 00 09 00 9E F8 FF FF 00 01 9E F8 FF FF 01 8F 9A 00 00 2D 00 21 A3 00 00 16 16 18 20 00 19 01 8F 9A 00 00 0A 00 2B 36 00 00 00 1C 2C 36 00 00 16 16 18 22 00 81 19 01 8F 9A 00 00 0A 00 79 C5 00 00 00 1B 8D 44 00 00 00 00 00 00 16 16 16 

		//m_kPredeterminedAbility = m_arrPsiAbilities[0]
		0F 01 58 9A 00 00 10 25 01 80 A0 00 00 

		//return true
		04 27 

//return super.HasPredeterminedAbility()
04 1C B6 A0 00 00 16 

//return ReturnValue
04 3A 22 A3 00 00 

//EOS
53 

[/CODE]
[/BEFORE_HEX]


[AFTER_HEX]
[HEADER]
7D 02 00 00 C9 01 00 00 
[/HEADER]
[CODE]
//m_kPredeterminedAbility = none
0F 01 58 9A 00 00 2A 

//if(m_iMimicBeaconIndex < 0)
07 4F 02 96 01 70 9A 00 00 25 16 

	//m_arrPsiAbilities.Length = 0
	0F 36 01 80 A0 00 00 25 

	//if(!m_kUnit.IsPsiLinked() && m_kPlayer.CanMindMerge() || (XComTacticalGRI(class'Engine'.static.GetCurrentWorldInfo().GRI).m_kBattle.m_kDesc.m_iDifficulty >= 0) && m_kPlayer.HasUnshieldedMechtoids())
	07 89 01 82 81 19 01 8F 9A 00 00 0B 00 BE 3A 00 00 00 1B 72 45 00 00 00 00 00 00 4A 16 16 18 C8 00 84 19 01 90 9A 00 00 0A 00 4A AA 00 00 00 1B 80 11 00 00 00 00 00 00 16 18 A4 00 82 99 19 19 19 2E BD 32 00 00 19 12 20 36 FE FF FF 0A 00 9E F9 FF FF 00 1C DE FB FF FF 16 09 00 5C F9 FF FF 00 01 5C F9 FF FF 09 00 C9 32 00 00 00 01 C9 32 00 00 09 00 AE AE 00 00 00 01 AE AE 00 00 09 00 99 B2 00 00 00 01 99 B2 00 00 25 16 18 20 00 19 01 90 9A 00 00 0A 00 13 AB 00 00 00 1B 53 3D 00 00 00 00 00 00 16 16 16 16 

		//AddBestPsiAbilityToList(m_arrPsiAbilities, 34, false)
		1B E2 01 00 00 00 00 00 00 01 80 A0 00 00 2C 22 28 16 

		//if((m_arrPsiAbilities.Length > 0) && m_arrPsiAbilities[0].GetPrimaryTarget().GetCharType() == 21)
		07 89 01 82 97 36 01 80 A0 00 00 25 16 18 3C 00 9A 19 19 10 25 01 80 A0 00 00 0A 00 D2 89 00 00 00 1B 9A 3A 00 00 00 00 00 00 16 0A 00 16 3B 00 00 00 1B 9E 37 00 00 00 00 00 00 16 2C 15 16 16 

			//bTargetIsMechtoid = true
			14 2D 00 21 A3 00 00 27 

	//if((((m_arrPsiAbilities.Length > 0) && m_kPlayer.IsAcceptableMindMergeLocation(m_kUnit.Location, m_kUnit, bTargetIsMechtoid)) && m_kUnit.IsInCover()) && !m_kUnit.IsFlankedByOpposingTeam())
	07 4F 02 82 82 82 97 36 01 80 A0 00 00 25 16 18 51 00 19 01 90 9A 00 00 3B 00 DE A9 00 00 00 1B D2 43 00 00 00 00 00 00 19 01 8F 9A 00 00 09 00 9E F8 FF FF 00 01 9E F8 FF FF 01 8F 9A 00 00 2D 00 21 A3 00 00 16 16 18 20 00 19 01 8F 9A 00 00 0A 00 2B 36 00 00 00 1C 2C 36 00 00 16 16 18 22 00 81 19 01 8F 9A 00 00 0A 00 79 C5 00 00 00 1B 8D 44 00 00 00 00 00 00 16 16 16 

		//m_kPredeterminedAbility = m_arrPsiAbilities[0]
		0F 01 58 9A 00 00 10 25 01 80 A0 00 00 

		//return true
		04 27 

//if(FindPriorityAbility(m_kPredeterminedAbility))
07 67 02 1B FF 32 00 00 00 00 00 00 01 58 9A 00 00 16 

	//return true
	04 27 

//return super.HasPredeterminedAbility()
04 1C B6 A0 00 00 16 

//return ReturnValue
04 3A 22 A3 00 00 

//EOS
53 

[/CODE]
[/AFTER_HEX] 

 

 

 

At one point I had the function resized up, but the most recent version I didn't need it.

 

The question I have about removing the psi abilities from being predetermined : will it still calculate a priority for the ability? As best I can see there's no priority calcuation code for the psi abilities -- it assumes that they will be triggered as predetermined.

Link to comment
Share on other sites

I was following the thread about the expanded functions for a while and got the impression that there were too many concerns with it that it might not be currently viable. Is that not the case anymore? If not, I'll definitely start using it.

 

Actually, psi abilities have default priority scores set with every other ability in XGAIAbilityRules.BuildScores, and some are then modified in the AI_OffenseScore, AI_DefenseScore, AI_IntangiblesScore functions in XGAIAbilityDM. For example, mind control (ability type 39/60) is set in XGAIAbilityRules.BuildScores with the following:

ScoreAbility(39,,1.0,0.0)
ScoreAbility(60,,1.0,0.0)

That sets the default values of mind control to -999 offense score, 1.0 defense score, 0.0 intangibles score. Then its offense score is set in AI_OffenseScore with the following:

case 39:
// End:0x2E4
case 60:
 fScore = 0.0;
 kTarget = XGAbility_Targeted(kAbility).GetPrimaryTarget();
 iWillDiff = m_kUnit.GetWill() - kTarget.GetWill();
 fScore += (float(iWillDiff) / 15.0);
 // End:0x502
 break;
Link to comment
Share on other sites

 

I was following the thread about the expanded functions for a while and got the impression that there were too many concerns with it that it might not be currently viable. Is that not the case anymore? If not, I'll definitely start using it.

 

There are some potential downfalls, so I avoid using it carelessly, but the advantages of being able to increase function size are just to great to pass up entirely. I'd say that out of the ~250-300 functions modified so far for Long War EW, about 20 have been increased in size.

 

The biggest drawback (with how UPKmodder does it) is that it will completely destroy any ability to use absolute file offsets (from beginning of file). I know that older versions of Toolboks used this, but am not sure if the newer versions have upgraded to parsing the UPK header and using relative offsets (relative to the beginning of the function object).

 

-------------

 

 

That sets the default values of mind control to -999 offense score, 1.0 defense score, 0.0 intangibles score. Then its offense score is set in AI_OffenseScore with the following:

 

Right ... so there are priority scores for Psi Control / Mind Control, but currently not any for Psi Panic and MindFray, correct?

 

Psi Panic has an additional negative in that it requires 2 separate will tests ... the first which is caster will vs target will, and the second is a regular panic test. I'm planning to alter this to skip the 2nd panic test if possible -- otherwise the ability is fairly useless.

 

I think if we could simply add in a new line in XGAIAbilityRules.BuildScores for eAbility_PsiPanic (40) , since eAbility_MindFray (57) already has a line, it might work out.

 

The add two new cases preceding the existing will check :

case 40:
case 57:
case 39:
// End:0x2E4
case 60:
 fScore = 0.0;
 kTarget = XGAbility_Targeted(kAbility).GetPrimaryTarget();
 iWillDiff = m_kUnit.GetWill() - kTarget.GetWill();
 fScore += (float(iWillDiff) / 15.0);
 // End:0x502
 break; 

I'm thinking that this would be a better approach than trying to set these as pre-determined abilities, particularly since with the other psi changes will values may vary considerably.

 

Mindmerge as predetermined makes sense since it can't be resisted, but Psi Panic and Mindfray make less sense to me.

Link to comment
Share on other sites

 

I was following the thread about the expanded functions for a while and got the impression that there were too many concerns with it that it might not be currently viable. Is that not the case anymore? If not, I'll definitely start using it.

Within the known limitations, the expanded functions appear to work fine (as reported by several modders). The caveat is that attempting to provide a pseudo-native function replacement might not be called in all situations (such as when a native function calls the replaced native function. Only testing will tell.) The discussion then fell into debating the merits of providing a mechanism to revert changes to their previous state, without a consensus.

 

-Dubious-

Link to comment
Share on other sites

I think if we could simply add in a new line in XGAIAbilityRules.BuildScores for eAbility_PsiPanic (40) , since eAbility_MindFray (57) already has a line, it might work out.

 

Ah, you're right, I didn't notice that there wasn't a ScoreAbility for PsiPanic. Yeah, adding a ScoreAbility for it in BuildScores and then setting it up to use the same case logic as mind control would work. Also, did you notice this strangeness with Psi Lance in AI_OffenseScore?

            case 35:
            // End:0xDD
            case 37:
            // End:0x102
            case 7:
                fScore = GetBaseShotOffenseScore(kAbility);
                // End:0x502
                break;
 
            case 35:
                fScore = 1.0;
                kTarget = XGAbility_Targeted(kAbility).GetPrimaryTarget();
                iWillDiff = m_kUnit.GetWill() - kTarget.GetWill();
                fScore -= (float(iWillDiff) / 40.0);
                // End:0x502
                break;

The way that's written, eAbility_PsiLance (35) will always fall into the first case and exit and the second case will never be used, right? It also appears that Psi Inspiration does not have a ScoreAbility and is handled entirely by the predetermined ability code.

Link to comment
Share on other sites

 

I think if we could simply add in a new line in XGAIAbilityRules.BuildScores for eAbility_PsiPanic (40) , since eAbility_MindFray (57) already has a line, it might work out.

 

Ah, you're right, I didn't notice that there wasn't a ScoreAbility for PsiPanic. Yeah, adding a ScoreAbility for it in BuildScores and then setting it up to use the same case logic as mind control would work. Also, did you notice this strangeness with Psi Lance in AI_OffenseScore?

            case 35:
            // End:0xDD
            case 37:
            // End:0x102
            case 7:
                fScore = GetBaseShotOffenseScore(kAbility);
                // End:0x502
                break;
 
            case 35:
                fScore = 1.0;
                kTarget = XGAbility_Targeted(kAbility).GetPrimaryTarget();
                iWillDiff = m_kUnit.GetWill() - kTarget.GetWill();
                fScore -= (float(iWillDiff) / 40.0);
                // End:0x502
                break;

The way that's written, eAbility_PsiLance (35) will always fall into the first case and exit and the second case will never be used, right? It also appears that Psi Inspiration does not have a ScoreAbility and is handled entirely by the predetermined ability code.

 

 

That is pretty weird, especially seeing the same case twice in a single switch statement. Definitely seems like a bug. The other ability is eAbility_PsiBomb (37), which appears to no longer be active in the code. Neither of the two should be getting based on the basic to-hit chance. Would be worth activating the second case statement and see how it affects Ethereal behavior.

 

However I seem to rarely see Ethereals in single-player use it, probably because Psi Control and and Mind Fray are predetermined abilities that the AI takes in preference, and I try to not let Ethereals live long enough to get to the 3rd or so turn required in order to use it :)

 

Do aliens even get access to Psi Inspiration? I thought is a only an XCOM ability -- it's primarily defensive in nature, boosting defensive will and removing MindFray penalties. It's also a point-blank AoE -- all of which I'd think would require some special situational logic to apply correctly, similar to MindMerge.

Link to comment
Share on other sites

I've seen Ethereals use it occasionally, but like you say, only when I somehow allow one to actually live three rounds.

 

You're right that Psi Inspiration isn't currently used by any aliens; I think I was more hinting that it might be interesting to allow them to use it and add some logic for it. Oh, and I was wrong, it's not included in the predetermined ability code; I can't seem to keep the ability types straight in my head, so I keep mistaking them when I see them used. Anyway, one of my pet peeves with the late game AI is that it doesn't handle enemy psionics very well. For example, the AI will often immediately turn on a friendly unit that has been mind controlled by the player, even if they have a good shot at the unit doing the mind control. Not very smart, but maybe you could argue that it fits with the story.

Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.

×
×
  • Create New...