We altered targeting behavior by modifying the target selection functions called by DetermineCombatRound. We included this script in x0_inc_generic to do so:
object bkAcquireTarget() {
object oLastTarget = GetAttackTarget();
if (GetIsObjectValid(oLastTarget) &&
!GetIsDead(oLastTarget) &&
!GetHasSpellEffect(SPELL_ETHEREALNESS, oLastTarget)) {
object oPriority = GetLocalObject(OBJECT_SELF, "TargetPriorityOverride");
if (GetIsObjectValid(oPriority)) {
return oPriority;
} else if (GetResRef(oLastTarget) == "x2_s_bblade") {//skip bbod if boss or smart enough, unless caster with Mord
if (GetHasSpell(SPELL_MORDENKAINENS_DISJUNCTION, OBJECT_SELF))
return oLastTarget;
if (GetLocalInt(OBJECT_SELF, "IgnoreBBoD") || (GetAbilityScore(OBJECT_SELF, ABILITY_INTELLIGENCE) > Random(100)))
oLastTarget = ChooseNewTarget(oLastTarget);
} else if (GetHasSpellEffect(HGEFFECT_TAUNT)) {
object oEffectSource = GetLocalObject(OBJECT_SELF, "TauntSource");
if (!GetIsObjectValid(oEffectSource))//safety check
return oLastTarget;
int nType = GetLocalInt(OBJECT_SELF, "TauntSkill");
if (nType == SKILL_BLUFF) {
/*
Bluffed creatures switch away from bluffer 75% of the time, and never choose them as
their new target unless there are none others available.
*/
if ((oEffectSource == oLastTarget) && Random(4))
oLastTarget = ChooseNewTarget(oEffectSource);
else
return oLastTarget;
} else if (nType == SKILL_TAUNT) {
/*
Taunted creatures switch to attacking the taunter 75% of the time
*/
if ((oEffectSource != oLastTarget) && Random(4))
return oEffectSource;
else
return oLastTarget;
} else
return oLastTarget;
} else
return oLastTarget;
} else
oLastTarget = ChooseNewTarget();
if (!GetIsObjectValid(oLastTarget) && //non-henchmen swap to ranged weapons
!GetIsObjectValid(GetMaster(OBJECT_SELF)))
ActionEquipMostDamagingRanged();
return oLastTarget; // valid or not, return it
}
int GetConcealment(object oTarget) {
int nEff, nConceal = 0;
effect eEff;
for (eEff = GetFirstEffect(oTarget);
GetIsEffectValid(eEff);
eEff = GetNextEffect(oTarget)) {
if (GetEffectType(eEff) == EFFECT_TYPE_CONCEALMENT) {
if ((nEff = GetEffectInteger(eEff, 0)) > nConceal)
nConceal = nEff;
}
}
return nConceal;
}
int GetIsSquishyCaster (object oCreature) {
if (GetLevelByclass(class_TYPE_CLERIC, oCreature) > 10)
return TRUE;
if (GetLevelByclass(class_TYPE_SORCERER, oCreature) > 10)
return TRUE;
if (GetLevelByclass(class_TYPE_WIZARD, oCreature) > 10)
return TRUE;
if ((GetLevelByclass(class_TYPE_DRUID, oCreature) > 10) &&
(GetLevelByclass(class_TYPE_SHIFTER, oCreature) < 5))
return TRUE;
if (GetAbilityScore(oCreature, ABILITY_CHARISMA, TRUE) >= 30 &&
(GetLevelByclass(class_TYPE_BARD, oCreature) > 10))
return TRUE;
return FALSE;
}
void DeleteAIVariables(object oCreature, int nTargetsConceal) {
DeleteLocalObject(oCreature, "GEN_AI_TARGET_NEAREST");//clear fallback
DeleteLocalObject(oCreature, "GEN_AI_TARGET_HATED");//clear hated
if (nTargetsConceal)
DeleteLocalObject(oCreature, "GEN_AI_TARGET_HIGH_CONCEAL");//clear highest conceal
}
object ChooseNewTarget(object oTargetToExclude = OBJECT_INVALID) {
object oSource = OBJECT_SELF, oTargetToExclude2;
object oEffectSource = GetLocalObject(oSource, "TauntSource");
int nType = GetLocalInt(oSource, "TauntSkill");
if (GetIsObjectValid(oEffectSource)) {
if ((nType == SKILL_TAUNT) && !GetIsDead(oEffectSource) && !GetHasSpellEffect(SPELL_ETHEREALNESS, oEffectSource) && Random(4))
return oEffectSource;
else if ((nType == SKILL_BLUFF) && Random(4)) {
if (oTargetToExclude == OBJECT_INVALID) //oTargetToExclude can be either a bluffer or a bbod
oTargetToExclude = oEffectSource;
else if (oTargetToExclude != oEffectSource)//if excluded is not bluffer, it's bbod
oTargetToExclude2 = oEffectSource;//makes bluffer TargetToExclude2, after bbod
}
}
int nNth = 1, nConceal, nHighConceal, nCrit;
float fRange = GetLocalFloat(GetModule(), "AIRange");
if (fRange < 1.0)
fRange = 10.0;
if (GetLocalInt(oSource, "X2_L_BEH_MAGIC") || GetItemIsRangedWeapon(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oSource)))
fRange += 20.0;
int nTargetsCasters = GetLocalInt(oSource, "AttackCasters");
int nTargetsConceal = (GetHasFeat(FEAT_SKILL_FOCUS_LISTEN, oSource) || GetHasFeat(FEAT_EPIC_SKILL_FOCUS_LISTEN, oSource));
int nTargetsCritVuln = GetHasFeat(GetWeaponDevastatingCriticalFeat(GetBaseItemType(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oSource))), oSource);
//retrieve hated class, set if not
int nHatedclass = GetLocalInt(oSource, "NW_L_BEHAVIOUR1") - 1;
if (nHatedclass == -1) {
nHatedclass = Random(10);
SetLocalInt(oSource, "NW_L_BEHAVIOUR1", nHatedclass+1);
}
object oTarget;
string sRes;
while (GetIsObjectValid(oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY,
oSource, nNth++,
CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN,
CREATURE_TYPE_DOES_NOT_HAVE_SPELL_EFFECT, SPELL_ETHEREALNESS))) {
if (GetIsDead(oTarget)) {
nHatedclass = Random(10);
SetLocalInt(oSource, "NW_L_BEHAVIOUR1", nHatedclass+1);//pick a new hated class when any target is dying
continue;
}
if (oTarget == oTargetToExclude || oTarget == oTargetToExclude2)//ignore these targets unless there are none others
continue;
sRes = GetResRef(oTarget);
if (sRes == "x2_s_bblade") {//skip bbod if boss or smart enough, unless caster with Mord
if (GetHasSpell(SPELL_MORDENKAINENS_DISJUNCTION, oSource)) {
DeleteAIVariables(oSource, nTargetsConceal);
return oTarget;
}
if (GetLocalInt(oSource, "IgnoreBBoD") || (GetAbilityScore(oSource, ABILITY_INTELLIGENCE) > Random(100)))
continue;
}
if (!GetIsObjectValid(GetLocalObject(oSource, "GEN_AI_TARGET_NEAREST")))
SetLocalObject(oSource, "GEN_AI_TARGET_NEAREST", oTarget);//store nearest enemy for fallback
if ((GetLevelByclass(nHatedclass, oTarget) > 0) &&
(!GetIsObjectValid(GetLocalObject(oSource, "GEN_AI_TARGET_HATED"))))
SetLocalObject(oSource, "GEN_AI_TARGET_HATED", oTarget);//store nearest hated enemy
if (nTargetsCritVuln &&
!GetIsImmune(oTarget, IMMUNITY_TYPE_CRITICAL_HIT)) {
if (GetDistanceBetween(oSource, oTarget) <= fRange) {
DeleteAIVariables(oSource, nTargetsConceal);
return oTarget;//ret nearest non-crit enemy if in range
}
break;//end loop - if nearest crit vuln out of range, just going to attack default target
}
if (nTargetsCasters && GetIsSquishyCaster(oTarget)) {
DeleteAIVariables(oSource, nTargetsConceal);
return oTarget;//return nearest caster if they AttackCasters (ignoring range)
}
if (nTargetsConceal) {//find the highest conceal target in range
nConceal = GetConcealment(oTarget);
if (nConceal > nHighConceal) {
if (GetDistanceBetween(oSource, oTarget) <= fRange) {
nHighConceal = nConceal;
SetLocalObject(oSource, "GEN_AI_TARGET_HIGH_CONCEAL", oTarget);//store highest conceal enemy in range
} else break;//stop if out of range
}
}
}
if (nTargetsConceal) {
oTarget = GetLocalObject(oSource, "GEN_AI_TARGET_HIGH_CONCEAL");
if (GetIsObjectValid(oTarget) && Random(4)) {//target highest conceal 75% of the time
DeleteAIVariables(oSource, nTargetsConceal);
return oTarget;
}
}
oTarget = GetLocalObject(oSource, "GEN_AI_TARGET_HATED");
if (GetIsObjectValid(oTarget) && (GetDistanceBetween(oSource, oTarget) <= fRange)) {
DeleteAIVariables(oSource, nTargetsConceal);
return oTarget;
}
oTarget = GetLocalObject(oSource, "GEN_AI_TARGET_NEAREST");
DeleteAIVariables(oSource, nTargetsConceal);
if (!GetIsObjectValid(oTarget))
oTarget = oTargetToExclude2;//target of last resort
if (!GetIsObjectValid(oTarget))
oTarget = oTargetToExclude;//target of last resort- means bluffers get targeted before bbods
return oTarget;//ok if invalid
}
As you can see, it allows us to fine-tune target selection. There may be an easier way that's equally effective, but I'm unaware of it if so.
[EDIT] Use Lightfoot's way, it's far simpler than making deep ai edits.
Funky
Modifié par FunkySwerve, 23 juillet 2011 - 07:01 .