703 lines
25 KiB
Plaintext
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;
|
|
}
|