NWNDS/nwnds_module/hench_i0_melee.nss
Jaysyn904 de24f81734 Added NWN Dark Sun module contents
Added NWN Dark Sun module contents.
2021-07-12 21:24:46 -04:00

703 lines
25 KiB
Plaintext

/*
Henchman Inventory And Battle AI
This file contains a modified form of the original TalentMeleeAttack.
Major modification to not randomly pick feats to use.
*/
#include "hench_i0_equip"
// void main() { }
int GetItemAttackBonus(object oTarget, object oItem)
{
int nReturnVal = 0;
itemproperty oProp = GetFirstItemProperty(oItem);
while (GetIsItemPropertyValid(oProp))
{
int bGetSetting = FALSE;
switch (GetItemPropertyType(oProp))
{
case ITEM_PROPERTY_ENHANCEMENT_BONUS:
case ITEM_PROPERTY_ATTACK_BONUS:
bGetSetting = TRUE;
break;
case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_ALIGNMENT_GROUP:
case ITEM_PROPERTY_ATTACK_BONUS_VS_ALIGNMENT_GROUP:
switch (GetItemPropertySubType(oProp))
{
case IP_CONST_ALIGNMENTGROUP_NEUTRAL:
bGetSetting = (GetAlignmentGoodEvil(oTarget) == ALIGNMENT_NEUTRAL) ||
(GetAlignmentLawChaos(oTarget) == ALIGNMENT_NEUTRAL);
break;
case IP_CONST_ALIGNMENTGROUP_LAWFUL:
bGetSetting = GetAlignmentLawChaos(oTarget) == ALIGNMENT_LAWFUL;
break;
case IP_CONST_ALIGNMENTGROUP_CHAOTIC:
bGetSetting = GetAlignmentLawChaos(oTarget) == ALIGNMENT_CHAOTIC;
break;
case IP_CONST_ALIGNMENTGROUP_GOOD:
bGetSetting = GetAlignmentGoodEvil(oTarget) == ALIGNMENT_GOOD;
break;
case IP_CONST_ALIGNMENTGROUP_EVIL:
bGetSetting = GetAlignmentGoodEvil(oTarget) == ALIGNMENT_EVIL;
break;
}
break;
case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_RACIAL_GROUP:
case ITEM_PROPERTY_ATTACK_BONUS_VS_RACIAL_GROUP:
bGetSetting = GetItemPropertySubType(oProp) == GetRacialType(oTarget);
break;
case ITEM_PROPERTY_ENHANCEMENT_BONUS_VS_SPECIFIC_ALIGNEMENT:
case ITEM_PROPERTY_ATTACK_BONUS_VS_SPECIFIC_ALIGNMENT:
{
int iSpecificAlignment = GetItemPropertySubType(oProp);
switch (iSpecificAlignment % 3)
{
case 0:
bGetSetting = GetAlignmentGoodEvil(oTarget) == ALIGNMENT_GOOD;
break;
case 1:
bGetSetting = GetAlignmentGoodEvil(oTarget) == ALIGNMENT_NEUTRAL;
break;
case 2:
bGetSetting = GetAlignmentGoodEvil(oTarget) == ALIGNMENT_EVIL;
break;
}
if (bGetSetting)
{
bGetSetting = FALSE;
switch (iSpecificAlignment / 3)
{
case 0:
bGetSetting = GetAlignmentLawChaos(oTarget) == ALIGNMENT_LAWFUL;
break;
case 1:
bGetSetting = GetAlignmentLawChaos(oTarget) == ALIGNMENT_NEUTRAL;
break;
case 2:
bGetSetting = GetAlignmentLawChaos(oTarget) == ALIGNMENT_CHAOTIC;
break;
}
}
}
break;
}
if (bGetSetting)
{
int itemPropValue = GetItemPropertyCostTableValue(oProp);
if (itemPropValue > nReturnVal)
{
nReturnVal = itemPropValue;
}
}
oProp = GetNextItemProperty(oItem);
}
return nReturnVal;
}
int GetMeleeAttackBonus(object oCreature, object oWeaponRight, object oTarget)
{
int nReturn = 0;
// Finesse only if we are using a proper weapon
int nStrMod = GetAbilityModifier(ABILITY_STRENGTH, oCreature);
int nDexMod = GetAbilityModifier(ABILITY_DEXTERITY, oCreature);
int bCanFinesse = GetHasFeat(FEAT_WEAPON_FINESSE, oCreature) && (nDexMod > nStrMod);
if (GetIsObjectValid(oWeaponRight))
{
nReturn += GetItemAttackBonus(oTarget, oWeaponRight);
if (bCanFinesse)
{
switch (GetBaseItemType(oWeaponRight))
{
// only these weapons can be finessed
case BASE_ITEM_DAGGER:
case BASE_ITEM_HANDAXE:
case BASE_ITEM_KAMA:
case BASE_ITEM_KUKRI:
case BASE_ITEM_LIGHTHAMMER:
case BASE_ITEM_LIGHTMACE:
case BASE_ITEM_RAPIER:
case BASE_ITEM_SHORTSWORD:
case BASE_ITEM_SHURIKEN:
case BASE_ITEM_SICKLE:
case BASE_ITEM_THROWINGAXE:
break;
default:
bCanFinesse = FALSE;
}
}
}
else
{
// note: creature weapons can be finessed
oWeaponRight = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oCreature);
if (GetIsObjectValid(oWeaponRight))
{
nReturn += GetItemAttackBonus(oTarget, oWeaponRight);
}
else
{
oWeaponRight = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oCreature);
if (GetIsObjectValid(oWeaponRight))
{
nReturn += GetItemAttackBonus(oTarget, oWeaponRight);
}
else
{
oWeaponRight = GetItemInSlot(INVENTORY_SLOT_ARMS, oCreature);
if (GetIsObjectValid(oWeaponRight))
{
nReturn += GetItemAttackBonus(oTarget, oWeaponRight);
}
}
}
}
object oWeaponLeft = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oCreature);
int nOffHandWeaponSize = GetMeleeWeaponSize(oWeaponLeft);
if (nOffHandWeaponSize > 0)
{
if (nOffHandWeaponSize == GetCreatureSize(oCreature))
{
nReturn -= 2;
}
else
{
// TODO more could be done here
nReturn -= 4;
}
}
if(bCanFinesse)
{
nReturn += nDexMod;
}
else
{
nReturn += nStrMod;
}
return nReturn;
}
int GetRangeAttackBonus(object oCreature, object oRangedWeapon, object oTarget)
{
int nReturn = GetItemAttackBonus(oTarget, oRangedWeapon);
int nDexMod = GetAbilityModifier(ABILITY_DEXTERITY, oCreature);
int nWisMod = GetAbilityModifier(ABILITY_WISDOM, oCreature);
if (GetHasFeat(FEAT_ZEN_ARCHERY, oCreature) && (nWisMod > nDexMod))
{
nReturn += nWisMod;
}
else
{
nReturn += nDexMod;
}
int itemType = GetBaseItemType(oRangedWeapon);
if ((itemType == BASE_ITEM_LONGBOW) || (itemType == BASE_ITEM_SHORTBOW))
{
int nLevel = GetLevelByClass(CLASS_TYPE_ARCANE_ARCHER, oCreature);
if (nLevel > 0)
{
nReturn += ((nLevel+1)/2);
}
}
return nReturn;
}
int GetAttackBonus(object oCreature, object oTarget)
{
object oRightWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oCreature);
if (GetWeaponRanged(oRightWeapon))
{
return GetRangeAttackBonus(oCreature, oRightWeapon, oTarget);
}
return GetMeleeAttackBonus(oCreature, oRightWeapon, oTarget);
}
void HenchDoTalentMeleeAttack(object oTarget, float fThresholdDistance, int iCreatureType)
{
int iIntCheck = GetAbilityScore(OBJECT_SELF, ABILITY_INTELLIGENCE) - 2;
if (iIntCheck < 6)
{
// have less smart creatures not use best combat feats all the time
if (iIntCheck < 1)
{
iIntCheck = 1;
}
if (iIntCheck < d6())
{
UseCombatAttack(oTarget);
return;
}
}
object oRightWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND);
int bRangedWeapon = GetWeaponRanged(oRightWeapon);
int iAC = GetAC(oTarget);
int iNewBAB = GetBaseAttackBonus(OBJECT_SELF) + 5 + d4(2);
if(bRangedWeapon)
{
// At a -2 to hit, this can disarm the arms or legs...speed or attack bonus
if(d8() == 1 && GetHasFeat(FEAT_CALLED_SHOT) && !GetHasFeatEffect(FEAT_CALLED_SHOT, oTarget)
&& !GetIsImmune(oTarget, IMMUNITY_TYPE_CRITICAL_HIT, OBJECT_SELF) && !GetIsDisabled(oTarget))
{
iNewBAB += GetRangeAttackBonus(OBJECT_SELF, oRightWeapon, oTarget);
if((iNewBAB - 2) >= iAC)
{
UseCombatAttack(oTarget, FEAT_CALLED_SHOT);
return;
}
}
// Always use if present
if(GetHasFeat(FEAT_RAPID_SHOT))
{
int iItemType = GetBaseItemType(oRightWeapon);
if (iItemType == BASE_ITEM_SHORTBOW || iItemType == BASE_ITEM_LONGBOW)
{
UseCombatAttack(oTarget, FEAT_RAPID_SHOT);
return;
}
}
ActionAttack(oTarget);
return;
}
iNewBAB += GetMeleeAttackBonus(OBJECT_SELF, oRightWeapon, oTarget);
float relativeChallenge;
if (iCreatureType == 0)
{
// mosters always use best feat
relativeChallenge = -10.0;
}
else
{
relativeChallenge = iCreatureType == 1 ? IntToFloat(GetHitDice(OBJECT_SELF)) * HENCH_HITDICE_TO_CR : GetChallengeRating(OBJECT_SELF);
relativeChallenge -= GetIsPC(oTarget) || GetAssociateType(oTarget) == ASSOCIATE_TYPE_HENCHMAN ?
IntToFloat(GetHitDice(oTarget)) * HENCH_HITDICE_TO_CR : GetChallengeRating(oTarget);
}
// For use against them evil pests! Top - one use only anyway.
if(GetHasFeat(FEAT_SMITE_EVIL) && GetAlignmentGoodEvil(oTarget) == ALIGNMENT_EVIL &&
relativeChallenge <= 2.0)
{
UseCombatAttack(oTarget, FEAT_SMITE_EVIL);
return;
}
if(GetHasFeat(FEAT_SMITE_GOOD) && GetAlignmentGoodEvil(oTarget) == ALIGNMENT_GOOD &&
relativeChallenge <= 2.0)
{
UseCombatAttack(oTarget, FEAT_SMITE_GOOD);
return;
}
// * only the playable races have whirlwind attack
// * Attempt to Use Whirlwind Attack
if (GetHasFeat(FEAT_WHIRLWIND_ATTACK) && GetOKToWhirl(OBJECT_SELF))
{
int iAttackThreshold;
if (GetCurrentHitPoints(oTarget) <= (GetMeleeAttackBonus(OBJECT_SELF, oRightWeapon, oTarget) + 5))
{
// a single hit is likely to kill target, check if any other targets are available
iAttackThreshold = 2;
}
else
{
// set number of whirlwind targets needed equal to the number of attacks per round
iAttackThreshold = GetBaseAttackBonus(OBJECT_SELF);
if (GetHitDice(OBJECT_SELF) > 20)
{
iAttackThreshold -= GetHitDice(OBJECT_SELF) / 2;
}
iAttackThreshold = (iAttackThreshold + 4) / 5;
}
float fThresholdDistance;
int iSizeThreshold;
if (GetHasFeat(FEAT_IMPROVED_WHIRLWIND))
{
fThresholdDistance = 10.0;
iSizeThreshold = CREATURE_SIZE_HUGE;
}
else
{
fThresholdDistance = 3.0;
iSizeThreshold = CREATURE_SIZE_MEDIUM;
}
int enemyIndex = 1;
object oTestTarget = GetNearestEnemy(OBJECT_SELF, enemyIndex++);
while (GetIsObjectValid(oTestTarget))
{
if ((GetDistanceToObject(oTestTarget) <= fThresholdDistance) &&
(GetCreatureSize(oTestTarget) <= iSizeThreshold))
{
--iAttackThreshold;
if (iAttackThreshold <= 0)
{
break;
}
}
oTestTarget = GetNearestEnemy(OBJECT_SELF, enemyIndex++);
}
// Jug_Debug(GetName(OBJECT_SELF) + " num attacks " + IntToString((GetBaseAttackBonus(OBJECT_SELF) + 1) / 5) + " count is " + IntToString(iAttackThreshold));
if (iAttackThreshold <= 0)
{
UseCombatAttack(OBJECT_SELF, FEAT_WHIRLWIND_ATTACK);
return;
}
}
// TODO update for HotU, dwarven defender (is same as barb rage?)
if (relativeChallenge <= 2.0 && TryKiDamage(oTarget))
{
return;
}
if (d6() == 1 && !GetIsImmune(oTarget, IMMUNITY_TYPE_CRITICAL_HIT, OBJECT_SELF) && (iNewBAB >= iAC))
{
int bHasQuiveringPalm = GetHasFeat(FEAT_QUIVERING_PALM);
int bHasStunningFist = GetHasFeat(FEAT_STUNNING_FIST);
int bHasSAP = GetHasFeat(FEAT_SAP);
int bHasCalledShot = GetHasFeat(FEAT_CALLED_SHOT) && d3() == 1;
if ((bHasQuiveringPalm || bHasStunningFist || bHasSAP || bHasCalledShot) &&
relativeChallenge <= 2.0 &&
(GetCharacterLevel(OBJECT_SELF) / 2 + 10 + GetAbilityModifier(ABILITY_WISDOM) >=
GetFortitudeSavingThrow(oTarget) + 5 + Random(15)))
{
if(!GetIsDisabled(oTarget))
{
if (bHasQuiveringPalm && !GetIsObjectValid(oRightWeapon))
{
UseCombatAttack(oTarget, FEAT_QUIVERING_PALM);
return;
}
if (bHasStunningFist)
{
int iMod = iNewBAB;
if (GetIsObjectValid(oRightWeapon) || (GetLevelByClass(CLASS_TYPE_MONK) == 0))
{
iMod -= 4;
}
if (iMod >= iAC)
{
UseCombatAttack(oTarget, FEAT_STUNNING_FIST);
return;
}
}
// OK, not for PCs, but may be on an NPC. -4 Attack. Above the one below.
if(bHasSAP)
{
if((iNewBAB - 4) >= iAC)
{
UseCombatAttack(oTarget, FEAT_SAP);
return;
}
}
if (bHasCalledShot)
{
// At a -2 to hit, this can disarm the arms or legs...speed or attack bonus
if(((iNewBAB - 2) >= iAC) && !GetHasFeatEffect(FEAT_CALLED_SHOT, oTarget))
{
UseCombatAttack(oTarget, FEAT_CALLED_SHOT);
return;
}
}
}
}
}
int bHasImprovedKnockdown = GetHasFeat(FEAT_IMPROVED_KNOCKDOWN);
if((bHasImprovedKnockdown || GetHasFeat(FEAT_KNOCKDOWN)) &&
!GetIsImmune(oTarget, IMMUNITY_TYPE_KNOCKDOWN, OBJECT_SELF) &&
!GetHasFeatEffect(FEAT_KNOCKDOWN, oTarget) &&
!GetHasFeatEffect(FEAT_IMPROVED_KNOCKDOWN, oTarget))
{
// By far the BEST feat to use - knocking them down lets you freely attack them!
// These return 1-5, based on size.
int iOurSize = GetCreatureSize(OBJECT_SELF);
int iTheirSize = GetCreatureSize(oTarget);
if (bHasImprovedKnockdown)
{
iOurSize++;
}
int bUse = iOurSize > iTheirSize;
if (!bUse && ((iOurSize + 1) >= iTheirSize))
{
int iMod = iNewBAB - 4;
// check if one size larger
if (iOurSize != iTheirSize)
{
iMod -= 4;
}
if(iMod >= iAC)
{
bUse = iMod > GetSkillRank(SKILL_DISCIPLINE, oTarget);
}
}
if(bUse)
{
UseCombatAttack(oTarget, bHasImprovedKnockdown ? FEAT_IMPROVED_KNOCKDOWN : FEAT_KNOCKDOWN);
return;
}
}
// start using expertise if have under 50% hit points
if (GetPercentageHPLoss(OBJECT_SELF) < 50 &&
(GetHasFeat(FEAT_EXPERTISE) || GetHasFeat(FEAT_IMPROVED_EXPERTISE)))
{
// get estimation of opponent attack vs. my AC
int iMyAC = GetAC(OBJECT_SELF);
int iTargetsBAB = GetBaseAttackBonus(oTarget) + 5 + d4(2) + GetAttackBonus(oTarget, OBJECT_SELF);
if(GetHasFeat(FEAT_IMPROVED_EXPERTISE) && (iTargetsBAB - 5) >= iMyAC)
{
UseCombatAttack(oTarget, -1, ACTION_MODE_IMPROVED_EXPERTISE);
return;
}
if(iTargetsBAB >= iMyAC)
{
UseCombatAttack(oTarget, -1, ACTION_MODE_EXPERTISE);
return;
}
}
// Only use parry on an active melee attacker, and
// only if our parry skill > our AC - 10
// JE, Apr.14,2004: Bugfix to make this actually work. Thanks to the message board
// members who investigated this.
if (GetSkillRank(SKILL_PARRY) > (GetAC(OBJECT_SELF) - 10))
{
object oTargetRightWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget);
if (GetAttackTarget(oTarget) == OBJECT_SELF &&
GetIsObjectValid(oTargetRightWeapon) &&
!GetWeaponRanged(oTargetRightWeapon))
{
int iAttackChance = GetBaseAttackBonus(oTarget) + GetAttackBonus(oTarget, OBJECT_SELF) - GetAC(OBJECT_SELF);
if ((iAttackChance > -20) && (GetPercentageHPLoss(OBJECT_SELF) < 65))
{
object oNearestFriend = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE);
if (GetIsObjectValid(oNearestFriend) && GetDistanceToObject(oNearestFriend) <= 5.0)
{
UseCombatAttack(oTarget, -1, ACTION_MODE_PARRY);
return;
}
}
}
}
//
// Auldar: Give 10% chance to Taunt if target is within 3.5 meters and is a challenge, if Skill points have been
// spent in Taunt skill indicating intention to use, and a taunt isn't in effect
//
if ((d10 () == 1) && ((relativeChallenge <= 2.0) ||
(GetCharacterLevel(oTarget) > (GetCharacterLevel(OBJECT_SELF) - 2)))
&& (GetDistanceToObject(oTarget) <= 3.5))
{
// Auldar: Adding a check for the Taunt skill, and ensuring that noone with ONLY a CHA mod to
// Taunt will use the skill.
// This confirms that some points are spent in the skill indicating an intention for the NPC to use them.
// Also using 69MEH69's idea to check for a negative modifier so we don't subtract a negative number (ie add)
// to the skill check
if ((GetSkillRank(SKILL_TAUNT, OBJECT_SELF, TRUE) > 0) && ((GetSkillRank(SKILL_TAUNT) + 5 + d4(2)) > GetSkillRank(SKILL_CONCENTRATION, oTarget)))
{
ActionUseSkill(SKILL_TAUNT, oTarget);
return;
}
}
if(d4() == 1 && (GetHasFeat(FEAT_IMPROVED_DISARM) || GetHasFeat(FEAT_DISARM)) &&
GetIsCreatureDisarmable(oTarget) &&
((GetIsObjectValid(oRightWeapon) && !GetWeaponRanged(oRightWeapon)) || !GetIsObjectValid(oRightWeapon)))
{
object oTargetRightWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget);
if (GetIsObjectValid(oTargetRightWeapon) && !GetWeaponRanged(oTargetRightWeapon))
{
int iWeaponSize;
if (GetIsObjectValid(oRightWeapon))
{
iWeaponSize = GetMeleeWeaponSize(oRightWeapon);
}
else
{
iWeaponSize = GetCreatureSize(OBJECT_SELF);
}
int iTargetWeaponSize = GetMeleeWeaponSize(oTargetRightWeapon);
// No AOO, and only a -4 penalty to hit.
if(GetHasFeat(FEAT_IMPROVED_DISARM))
{
// Apply weapon size penalites/bonuses to check - Use right weapons.
int iMod = iWeaponSize - iTargetWeaponSize;
if(iMod != 0) iMod += (iMod * 4);
if(((iNewBAB - 4 + iMod) >= iAC) && (iMod > GetSkillRank(SKILL_DISCIPLINE, oTarget)))
{
UseCombatAttack(oTarget, FEAT_IMPROVED_DISARM);
return;
}
}
// Provokes an AOO. Improved does not, but this is -6, and bonuses depend on weapons used (sizes)
else if(d2() == 1)
{
// Apply weapon size penalites/bonuses to check - Use right weapons.
int iMod = iWeaponSize - iTargetWeaponSize;
if(iMod != 0) iMod += (iMod * 4);
if(((iNewBAB - 6 + iMod) >= iAC) && (iMod > GetSkillRank(SKILL_DISCIPLINE, oTarget)))
{
UseCombatAttack(oTarget, FEAT_DISARM);
return;
}
}
}
}
// This activates an extra attack, at -2 to hit. Of course, only unarmed and kama
if(GetHasFeat(FEAT_FLURRY_OF_BLOWS) && ((iNewBAB - 2) >= iAC) && (!GetIsObjectValid(oRightWeapon) ||
(GetBaseItemType(oRightWeapon) == BASE_ITEM_KAMA)))
{
UseCombatAttack(oTarget, FEAT_FLURRY_OF_BLOWS);
return;
}
// -10 to hit - make sure by extra 5
if(GetHasFeat(FEAT_IMPROVED_POWER_ATTACK) && ((iNewBAB - 15) >= iAC))
{
UseCombatAttack(oTarget, FEAT_IMPROVED_POWER_ATTACK);
return;
}
// is a -5 to hit - make sure by extra 5
if(GetHasFeat(FEAT_POWER_ATTACK) && ((iNewBAB - 10) >= iAC))
{
UseCombatAttack(oTarget, FEAT_POWER_ATTACK);
return;
}
// extra damage with only one attack
if (GetHasFeat(FEAT_DIRTY_FIGHTING) && GetBaseAttackBonus(OBJECT_SELF) < 8)
{
UseCombatAttack(oTarget, FEAT_DIRTY_FIGHTING);
return;
}
UseCombatAttack(oTarget);
}
void ActionContinueMeleeAttack(object oTarget, float fThresholdDistance, int iCreatureType, int iCallNumber)
{
if (GetLocalInt(OBJECT_SELF, "UseRangedWeapons"))
{
if (!EquipRangedWeapon(oTarget, iCreatureType == 1, iCallNumber))
{
ActionDoCommand(ActionContinueMeleeAttack(oTarget, fThresholdDistance, iCreatureType, iCallNumber + 1));
return;
}
}
else
{
if (!EquipMeleeWeapons(oTarget, iCreatureType == 1, iCallNumber))
{
ActionDoCommand(ActionContinueMeleeAttack(oTarget, fThresholdDistance, iCreatureType, iCallNumber + 1));
return;
}
}
HenchDoTalentMeleeAttack(oTarget, fThresholdDistance, iCreatureType);
}
// MELEE ATTACK OTHERS
/*
ISSUE 1: Talent Melee Attack should set the Last Spell Used to 0 so that melee casters can use
a single special ability.
Auldar: Made changes here to use Taunt when appropriate as well as more accurate calculations for
To-Hit vs Target AC.
Tony: Made major changes in using attack feats. Heavily modified code from Jasperre.
*/
int HenchTalentMeleeAttack(object oIntruder, float fThresholdDistance, int iMeleeAttackers, int iCreatureType, int bPolymorphed)
{
object oTarget = oIntruder;
if (iCreatureType == 0)
{
MonsterBattleCry();
}
else if (iCreatureType == 1)
{
HenchBattleCry();
}
// allow sneak attack to use other feats
if(GetHasFeat(FEAT_SNEAK_ATTACK))
{
//Sneak Attack Flanking attack
float fRange = iMeleeAttackers ? 3.5 : /* bRangedWeapon*/ TRUE ? 50.0 : 5.0;
int iEnemyAC, iAC = 100;
object oRealMaster = GetRealMaster();
int bEnemyMaster = FALSE;
object oLastSneakTarget = GetLocalObject(OBJECT_SELF, "LastSneakTarget");
object oAttackTarget;
int nCnt = 1;
object oEnemy = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, nCnt, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE);
object oBest = OBJECT_INVALID;
while(GetIsObjectValid(oEnemy) && GetDistanceToObject(oEnemy) <= fRange)
{
if(!GetPlotFlag(oEnemy) && !GetIsImmune(oEnemy, IMMUNITY_TYPE_SNEAK_ATTACK, OBJECT_SELF))
{
oAttackTarget = GetAttackTarget(oEnemy);
if(oAttackTarget != OBJECT_SELF)
{
if (oEnemy == oLastSneakTarget)
{
oBest = oEnemy;
break;
}
iEnemyAC = GetAC(oEnemy);
if (oAttackTarget == oRealMaster)
{
if (!bEnemyMaster)
{
bEnemyMaster = TRUE;
iAC = iEnemyAC;
oBest = oEnemy;
}
else if(iAC > iEnemyAC)
{
iAC = iEnemyAC;
oBest = oEnemy;
}
}
else if(iAC > iEnemyAC)
{
iAC = iEnemyAC;
oBest = oEnemy;
}
}
}
nCnt++;
oEnemy = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, nCnt, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE);
}
if (GetIsObjectValid(oBest))
{
oTarget = oBest;
SetLocalObject(OBJECT_SELF, "LastSneakTarget", oBest);
}
}
if (!HenchEquipAppropriateWeapons(oTarget, fThresholdDistance, iCreatureType == 1, bPolymorphed))
{
ActionDoCommand(ActionContinueMeleeAttack(oTarget, fThresholdDistance, iCreatureType, 2));
}
else
{
HenchDoTalentMeleeAttack(oTarget, fThresholdDistance, iCreatureType);
}
return TRUE;
}