Amineri Posted January 17, 2014 Author Share Posted January 17, 2014 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 More sharing options...
monju125 Posted January 17, 2014 Share Posted January 17, 2014 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 More sharing options...
Amineri Posted January 17, 2014 Author Share Posted January 17, 2014 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 More sharing options...
monju125 Posted January 17, 2014 Share Posted January 17, 2014 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 More sharing options...
Amineri Posted January 17, 2014 Author Share Posted January 17, 2014 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 More sharing options...
dubiousintent Posted January 17, 2014 Share Posted January 17, 2014 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 More sharing options...
monju125 Posted January 18, 2014 Share Posted January 18, 2014 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 More sharing options...
monju125 Posted January 18, 2014 Share Posted January 18, 2014 I think what I had noticed that had soured me on the expanding functions idea had to do with expanding multiple functions and potential pitfalls combining mods? This isn't really the place for this though. I'll just go reread that thread. Link to comment Share on other sites More sharing options...
Amineri Posted January 18, 2014 Author Share Posted January 18, 2014 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 More sharing options...
monju125 Posted January 18, 2014 Share Posted January 18, 2014 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 More sharing options...
Recommended Posts