I created a Paladin henchman and configured a package in which he gets Divine Might. I can make him use it trough conversation, but not combat. In conversation I used these scripts:
This is to see if he has the feat on the “text appears”. I copied from others just changed the feat:
int StartingConditional()
{
int iResult;
iResult = GetHasFeat(FEAT_DIVINE_MIGHT, OBJECT_SELF);
return iResult;
}
This I made myself, it is to use the feat in the “action taken” part.
There is a known bug in feat.2da where Divine Might has a value of **** in the category column where it should be 22. That bug prevents all NPCs from using Divine Might. If you’re curious, look in categories.2da and you’ll find entry 22 is TALENT_CATEGORY_HARMFUL_MELEE. There are several other feats with no value for category or the wrong value. Those bugs are in the 1.69 as well as the EE versions of NWN.
However Divine Might isn’t a directly harmful feat (@Grymlorde), so I’m leaning towards TALENT_CATEGORY_BENEFICIAL_ENHANCEMENT_SELF (10) here.
However the problem is - and probably the reason why BW didn’t allow NPCs to use it (see message in the log) - that there is no way to bind one feat with another (i.e. can’t use X when you spent all Y for today). And this is what happens with Divine Might. So at worst you could end up with NPC constantly trying to use this feat (it has unlimited uses).
It is probably for the best to use this feat directly through custom combat AI.
Or - if someone is really determined - change Divine Might to have 10 uses per day (read: more than max Turn Undead) and decrement the remaining ones inside X0_S2_DivMight once Turn Undead is depleted. This may confuse players.
Scripting a (good) AI is one of the hardest things to do. There is a few projects in the Vault that do this, but they are full packages which may not do what you want (or do too much other stuff). But do check them out.
BW did let us hook into DetermineCombatRound, but of course they turned it into a binary choice: either you handle whole combat yourself or you let vanilla handle whole combat. They didn’t provide a “do something yourself first but let vanilla finish it” third option.
Putting Divine Might to offensive talent category indeed causes the NPC to deadlock. However I made a quick and dirty hack allowing injecting this feat (or any feat really) into the vanilla DCR pipeline. This was actually fun (and it works).
Save this script as nwsh_dcr - this is my DCR hack library
/*
CUSTOM COMBAT OVERRIDE FOR VANILLA DETERMINE COMBAT ROUND
Allows vanilla AI to be called from custom AI
By NWShacker, 2021-01-09
Based on nw_i0_generic
*/
#include "nw_i0_generic"
#include "x2_inc_switches"
// wrapper for SetCreatureOverrideAIScript
void NWSH_SetCreatureOverrideAIScript(string sScript, object oCreature=OBJECT_SELF);
void NWSH_SetCreatureOverrideAIScript(string sScript, object oCreature=OBJECT_SELF)
{
SetCreatureOverrideAIScript(oCreature, sScript);
}
// initializes combat round override - MUST be called first
// * returns TRUE - can proceed with override script
// * returns FALSE - do not proceed with override script
int NWSH_DetermineCombatRoundInit();
int NWSH_DetermineCombatRoundInit()
{
if(__InCombatRound() || GetAssociateState(NW_ASC_IS_BUSY))
{
SetCreatureOverrideAIScriptFinished();
return FALSE;
}
__TurnCombatRoundOn(TRUE);
return TRUE;
}
// validates current attack target (passed to DetermineCombatRound())
// or obtains new attack target (nearest hostile creature)
object NWSH_DetermineCombatRoundTarget();
object NWSH_DetermineCombatRoundTarget()
{
object oIntruder = GetCreatureOverrideAIScriptTarget();
if(BashDoorCheck(oIntruder)) return OBJECT_INVALID;
if(bkEvaluationSanityCheck(oIntruder, 0.0)) return OBJECT_INVALID;
if(!GetIsObjectValid(oIntruder))
{
oIntruder = bkAcquireTarget();
}
return oIntruder;
}
// tries vanilla AI tactics against oIntruder
// DO NOT call this function directly
void NWSH_DetermineCombatRoundTactics(object oIntruder=OBJECT_INVALID)
{
if(TalentPersistentAbilities() || chooseTactics(oIntruder) == 99)
{
__TurnCombatRoundOn(FALSE);
}
else
{
DeleteLocalObject(OBJECT_SELF, "NW_GENERIC_LAST_ATTACK_TARGET");
ClearActions(CLEAR_NW_I0_GENERIC_658);
__TurnCombatRoundOn(FALSE);
WalkWayPoints();
}
}
// finishes combat round override - MUST be called last
// tries vanilla AI tactics against oIntruder (as action if iAsAction is TRUE)
void NWSH_DetermineCombatRoundExit(object oIntruder=OBJECT_INVALID, int iAsAction=FALSE);
void NWSH_DetermineCombatRoundExit(object oIntruder=OBJECT_INVALID, int iAsAction=FALSE)
{
if(iAsAction)
{
ActionDoCommand(NWSH_DetermineCombatRoundTactics(oIntruder));
}
else
{
NWSH_DetermineCombatRoundTactics(oIntruder);
}
SetCreatureOverrideAIScriptFinished();
}
Save this script as your_custom_ai or something - this is the custom AI
Wow, thanks very much, you’re awesome. Let me make a noob question though: how do I use it? I create all 3 scripts in the module and slot the third in OnUserDefined on the henchman?
I’ve had no issues with henchman using TonyK’s HenchAI and Divine Might (after I fixed the category). Of course, it is rare for low level parties to have several fights before resting so that may be why I never had a problem.
As for whether Divine Might is a Beneficial Enhancement or Harmful Melee, I don’t think it really makes any difference from an AI perspective.
You can run the last script anywhere or as a part of any other script (except the first two, obviously). OnSpawn makes sense so the creature is set from the beginning.
It seems to make a difference for vanilla AI. It expects harmful melee talents to be instantaneous, while DM is a buff (and has different target), so the AI keeps “attacking” the enemy with it, essentially locking itself in a loop.
That’s the name of a variable that is going to hold the in-game reference to the NPC in that script. To be precise, it can hold a reference to any object, but we’re going to assign there the result of GetObjectByTag which returns an object with matching tag.
You could rename oYourNPC to MalfattosHenchman and it will be alright as long as you rename it everywhere in the script or you’ll get compilation error.
We want to make Sir Norik use that custom AI script, so we write:
#include "nwsh_dcr"
void main()
{
// put correct tag below
object oYourNPC = GetObjectByTag("TAG_OF_SIR_NORIK_HERE");
NWSH_SetCreatureOverrideAIScript("your_custom_ai", oYourNPC);
}
If Sir Norik is going to run this script, i.e. this script is his OnSpawn, you can reduce it to:
I checked them (copied back from here) and they work.
Order and naming is important:
save first script as nwsh_dcr; this is a “library” script - it is not compiled (and cannot be compiled - no main()) - other scripts that have #include "nwsh_dcr" access code from it when they are compiled
save second script as your_custom_ai - it should compile; if not, post the error message
save third script as anything - this one enables the second as custom AI, just make sure something is going to run it at least once (you could even put it in a conversation with the paladin, at least for testing)
I don’t remember if there is a problem with Divine Might used by AI in NWN 2 because it’s been a long time since I have played. What you could do is try to find a trainer module and see if it happens.
Yes, Beamdog is still releasing patches for the Enhanced Edition.