Updated Release Archive. Fixed Mage-killer prereqs. Removed old LETO & ConvoCC related files. Added organized spell scroll store. Fixed Gloura spellbook. Various TLK fixes. Reorganized Repo. Removed invalid user folders. Added DocGen back in.
685 lines
28 KiB
Plaintext
685 lines
28 KiB
Plaintext
|
|
// * Various functions to determine sneak dice.
|
|
|
|
// * Used to find the total sneak dice a character is capable of.
|
|
int GetTotalSneakAttackDice(object oPC);
|
|
|
|
// * Used to find the total rogue sneak dice a character is capable of.
|
|
// -----------------------------------------------------------------------------------------
|
|
// Future PRC's go here. DO NOT ADD ROGUE/BLACKGUARD/ASSASSIN SNEAK ATTACKS AS CLASS FEATS.
|
|
// Placeholder feats are fine, even encouraged. Example: "Ranged Sneak Attack +1d6".
|
|
// The feat should do nothing, just show that you have the bonus.
|
|
// -----------------------------------------------------------------------------------------
|
|
int GetRogueSneak(object oPC);
|
|
|
|
// * Used to find the total blackguard sneak dice a character is capable of.
|
|
int GetBlackguardSneak(object oPC);
|
|
|
|
// * Used to find the total assassin sneak dice a character is capable of.
|
|
int GetAssassinSneak(object oPC);
|
|
|
|
// * Used to find how much a character has taken "Improved Sneak Attack".
|
|
int GetEpicFeatSneak(object oPC);
|
|
|
|
//:://////////////////////////////////////////////
|
|
//:: Sneak Attack Functions
|
|
//:://////////////////////////////////////////////
|
|
|
|
// Checks if attacker is flanking the defender or not
|
|
int GetIsFlanked(object oDefender, object oAttacker);
|
|
|
|
// Checks if an AoE spell is flanking the defender
|
|
int GetIsAOEFlanked(object oDefender, object oAttacker);
|
|
|
|
// Determines if a creature is helpless.
|
|
// (effective dex modifier of 0, and can be Coup De Graced).
|
|
int GetIsHelpless(object oDefender);
|
|
|
|
// Returns if oDefender is denied dex bonus to AC from spells
|
|
// int nIgnoreUD - ignores Uncanny Dodge
|
|
int GetIsDeniedDexBonusToAC(object oDefender, object oAttacker, int nIgnoreUD = FALSE);
|
|
|
|
// Returns FALSE if oDefender has no concealment
|
|
// or the int amount of concealment on the defender.
|
|
int GetIsConcealed(object oDefender, object oAttacker);
|
|
|
|
// Returns true if the Attacker can Sneak Attack the target
|
|
int GetCanSneakAttack(object oDefender, object oAttacker);
|
|
|
|
// Returns Sneak Attack Damage
|
|
int GetSneakAttackDamage(int iSneakAttackDice);
|
|
|
|
//Returns applicable elemental type for Dragonfire Strike
|
|
int GetDragonfireDamageType(object oPC);
|
|
|
|
// * Used to find the total favoured enemy bonus a character is capable of.
|
|
int GetFavouredEnemyBonus(object oPC);
|
|
|
|
//:://////////////////////////////////////////////
|
|
//:: Includes
|
|
//:://////////////////////////////////////////////
|
|
|
|
//#include "prc_class_const"
|
|
//#include "prc_feat_const"
|
|
#include "tob_move_const"
|
|
#include "prc_x2_itemprop"
|
|
|
|
//:://////////////////////////////////////////////
|
|
//:: Definitions
|
|
//:://////////////////////////////////////////////
|
|
|
|
int GetTotalSneakAttackDice(object oPC)
|
|
{
|
|
int iSneakAttackDice = GetRogueSneak(oPC) + GetBlackguardSneak(oPC) +
|
|
GetAssassinSneak(oPC) + GetEpicFeatSneak(oPC);
|
|
return iSneakAttackDice;
|
|
}
|
|
|
|
int GetRogueSneak(object oPC)
|
|
{
|
|
object oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC);
|
|
int nWeaponType = GetBaseItemType(oWeapon);
|
|
|
|
int iClassLevel;
|
|
int iRogueSneak = 0;
|
|
|
|
// Rogue
|
|
iClassLevel = GetLevelByClass(CLASS_TYPE_ROGUE, oPC);
|
|
// Daring Outlaw
|
|
if (iClassLevel && GetHasFeat(FEAT_DARING_OUTLAW, oPC))
|
|
iClassLevel += GetLevelByClass(CLASS_TYPE_SWASHBUCKLER, oPC);
|
|
if (iClassLevel) iRogueSneak += (iClassLevel + 1) / 2;
|
|
|
|
// Arcane Trickster (Epic)
|
|
iClassLevel = GetLevelByClass(CLASS_TYPE_ARCTRICK, oPC);
|
|
if (iClassLevel >= 12) iRogueSneak += (iClassLevel - 10) / 2;
|
|
|
|
// Black Flame Zealot
|
|
iClassLevel = GetLevelByClass(CLASS_TYPE_BFZ, oPC);
|
|
if (iClassLevel) iRogueSneak += iClassLevel / 3;
|
|
|
|
// Nightshade
|
|
iClassLevel = GetLevelByClass(CLASS_TYPE_NIGHTSHADE, oPC);
|
|
if (iClassLevel) iRogueSneak += iClassLevel / 3;
|
|
|
|
// Outlaw Crimson Road
|
|
//iClassLevel = GetLevelByClass(CLASS_TYPE_OUTLAW_CRIMSON_ROAD, oPC);
|
|
//if (iClassLevel) iRogueSneak += (iClassLevel + 1) / 2;
|
|
|
|
// Temple Raider
|
|
//iClassLevel = GetLevelByClass(CLASS_TYPE_TEMPLE_RAIDER, oPC);
|
|
//if (iClassLevel>= 2) iRogueSneak += (iClassLevel + 1) / 3;
|
|
|
|
// Ghost-Faced Killer
|
|
iClassLevel = GetLevelByClass(CLASS_TYPE_GHOST_FACED_KILLER, oPC);
|
|
if (iClassLevel >= 2) iRogueSneak += ((iClassLevel + 1) / 3);
|
|
|
|
// Ninja
|
|
iClassLevel = GetLevelByClass(CLASS_TYPE_NINJA, oPC);
|
|
if (iClassLevel) iRogueSneak += (iClassLevel + 1) / 2;
|
|
|
|
// Shadow Thief of Amn
|
|
iClassLevel = GetLevelByClass(CLASS_TYPE_SHADOW_THIEF_AMN, oPC);
|
|
if (iClassLevel) iRogueSneak += (iClassLevel + 1) / 2;
|
|
|
|
// Slayer of Domiel
|
|
iClassLevel = GetLevelByClass(CLASS_TYPE_SLAYER_OF_DOMIEL, oPC);
|
|
if (iClassLevel) iRogueSneak += (iClassLevel + 1) / 2;
|
|
|
|
// Crinti Shadow Marauder
|
|
iClassLevel = GetLevelByClass(CLASS_TYPE_CRINTI_SHADOW_MARAUDER, oPC);
|
|
if (iClassLevel) iRogueSneak += iClassLevel / 2;
|
|
|
|
// Cultist of the Shattered Peak
|
|
iClassLevel = GetLevelByClass(CLASS_TYPE_CULTIST_SHATTERED_PEAK, oPC);
|
|
if (iClassLevel) iRogueSneak += iClassLevel / 2;
|
|
|
|
// Skullclan Hunter
|
|
iClassLevel = GetLevelByClass(CLASS_TYPE_SKULLCLAN_HUNTER, oPC);
|
|
if (iClassLevel) iRogueSneak += iClassLevel / 3;
|
|
|
|
// Shadowmind
|
|
iClassLevel = GetLevelByClass(CLASS_TYPE_SHADOWMIND, oPC);
|
|
if (iClassLevel) iRogueSneak += (iClassLevel + 1) / 2;
|
|
|
|
// Psychic Rogue
|
|
iClassLevel = GetLevelByClass(CLASS_TYPE_PSYCHIC_ROGUE, oPC);
|
|
if (iClassLevel) iRogueSneak += (iClassLevel + 2) / 3;
|
|
|
|
// Unseen Seer
|
|
iClassLevel = GetLevelByClass(CLASS_TYPE_UNSEEN_SEER, oPC);
|
|
if (iClassLevel) iRogueSneak += (iClassLevel + 2) / 3;
|
|
|
|
// Fist of Dal Quor
|
|
iClassLevel = GetLevelByClass(CLASS_TYPE_FIST_DAL_QUOR, oPC);
|
|
if (iClassLevel) iRogueSneak += (iClassLevel + 1) / 2;
|
|
|
|
// Umbral Disciple
|
|
iClassLevel = GetLevelByClass(CLASS_TYPE_UMBRAL_DISCIPLE, oPC);
|
|
if (iClassLevel) iRogueSneak += (iClassLevel + 1) / 3;
|
|
|
|
//Dragon Devotee and Hand of the Winged Masters
|
|
int nBonusFeatDice = 0;
|
|
int nCount;
|
|
for(nCount = FEAT_SPECIAL_SNEAK_ATTACK_5D6; nCount >= FEAT_SPECIAL_SNEAK_ATTACK_1D6; nCount--)
|
|
{
|
|
if (GetHasFeat(nCount,oPC))
|
|
{
|
|
nBonusFeatDice = nCount - FEAT_SPECIAL_SNEAK_ATTACK_1D6 + 1;
|
|
//if (DEBUG) DoDebug("prc_inc_sneak: Bonus Sneak Dice: " + IntToString(nBonusFeatDice));
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Shadowbane Inquisitor
|
|
iClassLevel = GetLevelByClass(CLASS_TYPE_SHADOWBANE_INQUISITOR, oPC);
|
|
if (iClassLevel >= 4) iRogueSneak++;
|
|
if (iClassLevel >= 7) iRogueSneak++;
|
|
if (iClassLevel >= 10) iRogueSneak++;
|
|
|
|
// Shadowbane Stalker
|
|
iClassLevel = GetLevelByClass(CLASS_TYPE_SHADOWBANE_STALKER, oPC);
|
|
if (iClassLevel) iRogueSneak += iClassLevel / 3;
|
|
|
|
//Naztharune Rakshasa racial sneak attack
|
|
if(GetHasFeat(FEAT_RACIAL_SNEAK_6D6)) iRogueSneak += 6;
|
|
|
|
if (GetRacialType(oPC) == RACIAL_TYPE_MARRULURK) iRogueSneak += 2;
|
|
|
|
if(nWeaponType == BASE_ITEM_LONGBOW || nWeaponType == BASE_ITEM_SHORTBOW)
|
|
{
|
|
// Peerless Archer
|
|
iClassLevel = GetLevelByClass(CLASS_TYPE_PEERLESS, oPC);
|
|
if (iClassLevel) iRogueSneak += (iClassLevel + 2) / 3;
|
|
}
|
|
else if(nWeaponType == BASE_ITEM_SLING)
|
|
{
|
|
// Halfling Warslinger
|
|
iClassLevel = GetLevelByClass(CLASS_TYPE_HALFLING_WARSLINGER, oPC);
|
|
if (iClassLevel) iRogueSneak += (iClassLevel + 1) / 2;
|
|
}
|
|
else if(nWeaponType == BASE_ITEM_WHIP)
|
|
{
|
|
// Lasher
|
|
iClassLevel = GetLevelByClass(CLASS_TYPE_LASHER, oPC);
|
|
if (iClassLevel > 0) iRogueSneak += ((iClassLevel - 1) / 4) + 1;
|
|
}
|
|
|
|
//Justice of Weald and Woe
|
|
iClassLevel = GetLevelByClass(CLASS_TYPE_JUSTICEWW, oPC);
|
|
if(iClassLevel > 1) iRogueSneak++;
|
|
if(iClassLevel > 6) iRogueSneak++;
|
|
|
|
//Shadowblade
|
|
iClassLevel = GetLevelByClass(CLASS_TYPE_SHADOWBLADE, oPC);
|
|
if (iClassLevel) iRogueSneak += (iClassLevel + 1) / 2;
|
|
|
|
if(GetHasSpellEffect(MOVE_SH_ASSASSINS_STANCE, oPC))
|
|
{
|
|
iRogueSneak += 2;
|
|
}
|
|
|
|
if(GetLocalInt(oPC, "SacredStrike"))
|
|
{
|
|
iRogueSneak += GetLocalInt(oPC, "SacredStrike");
|
|
}
|
|
if(GetLocalInt(oPC, "PsyRogueSneak"))
|
|
{
|
|
iRogueSneak += GetLocalInt(oPC, "PsyRogueSneak");
|
|
}
|
|
if(GetLocalInt(oPC, "CunningStrike"))
|
|
{
|
|
iRogueSneak += 1;
|
|
}
|
|
if(GetLocalInt(oPC, "UmbralSneak"))
|
|
{
|
|
iRogueSneak += GetLocalInt(oPC, "UmbralSneak");
|
|
}
|
|
if(GetLocalInt(oPC, "MalphasSneak"))
|
|
{
|
|
iRogueSneak += GetLocalInt(oPC, "MalphasSneak");
|
|
}
|
|
if(GetLocalInt(oPC, "AndroSneak"))
|
|
{
|
|
iRogueSneak += GetLocalInt(oPC, "AndroSneak");
|
|
}
|
|
if (GetLocalInt(oPC, "FactotumSneak"))
|
|
{
|
|
iRogueSneak += (GetLevelByClass(CLASS_TYPE_FACTOTUM, oPC) + 1) / 2;
|
|
}
|
|
|
|
if(iRogueSneak > 0) //the feats only apply if you already have Sneak Attack
|
|
iRogueSneak += nBonusFeatDice;
|
|
|
|
// -----------------------------------------------------------------------------------------
|
|
// Future PRC's go here. DO NOT ADD ROGUE/BLACKGUARD/ASSASSIN SNEAK ATTACKS AS CLASS FEATS.
|
|
// Placeholder feats are fine, even encouraged. Example: "Ranged Sneak Attack +1d6".
|
|
// The feat should do nothing, just show that you have the bonus.
|
|
// -----------------------------------------------------------------------------------------
|
|
|
|
//if (DEBUG) DoDebug("prc_inc_sneak: Rogue Sneak Dice: " + IntToString(iRogueSneak));
|
|
return iRogueSneak;
|
|
}
|
|
|
|
// --------------------------------------------------
|
|
// PLEASE DO NOT ADD ANY NEW CLASSES TO THIS FUNCTION
|
|
// --------------------------------------------------
|
|
int GetBlackguardSneak(object oPC)
|
|
{
|
|
int iClassLevel;
|
|
int iBlackguardSneak = 0;
|
|
|
|
// Blackguard
|
|
iClassLevel = GetLevelByClass(CLASS_TYPE_BLACKGUARD, oPC);
|
|
if (iClassLevel) iBlackguardSneak += (iClassLevel - 1) / 3;
|
|
if ((iClassLevel) && (GetLevelByClass(CLASS_TYPE_PALADIN) >= 5)) iBlackguardSneak++; // bonus for pal/bg
|
|
|
|
// Ninja Spy
|
|
iClassLevel = GetLevelByClass(CLASS_TYPE_NINJA_SPY, oPC);
|
|
if (iClassLevel) iBlackguardSneak += (iClassLevel + 1) / 3;
|
|
|
|
// Arcane Trickster (Pre-Epic)
|
|
iClassLevel = GetLevelByClass(CLASS_TYPE_ARCTRICK, oPC);
|
|
if ((iClassLevel >= 2) && (iClassLevel < 11)) iBlackguardSneak += iClassLevel / 2;
|
|
if (iClassLevel >= 11) iBlackguardSneak += 5;
|
|
|
|
// Disciple of Baalzebul
|
|
iClassLevel = GetLevelByClass(CLASS_TYPE_DISC_BAALZEBUL, oPC);
|
|
if ((iClassLevel >= 2) && (iClassLevel < 5)) iBlackguardSneak++;
|
|
if ((iClassLevel >= 5) && (iClassLevel < 8)) iBlackguardSneak += 2;
|
|
if (iClassLevel >= 8) iBlackguardSneak += 3;
|
|
|
|
//if (DEBUG) DoDebug("prc_inc_sneak: Blackguard Sneak Dice: " + IntToString(iBlackguardSneak));
|
|
return iBlackguardSneak;
|
|
}
|
|
|
|
// --------------------------------------------------
|
|
// PLEASE DO NOT ADD ANY NEW CLASSES TO THIS FUNCTION
|
|
// --------------------------------------------------
|
|
int GetAssassinSneak(object oPC)
|
|
{
|
|
int iClassLevel;
|
|
int iAssassinSneakDice = 0;
|
|
|
|
// Assassin
|
|
iClassLevel = GetLevelByClass(CLASS_TYPE_ASSASSIN, oPC);
|
|
if (iClassLevel) iAssassinSneakDice += (iClassLevel + 1) / 2;
|
|
|
|
// Telflammar Shadowlord
|
|
if(GetLevelByClass(CLASS_TYPE_SHADOWLORD, oPC) > 5) iAssassinSneakDice++;
|
|
|
|
//if (DEBUG) DoDebug("prc_inc_sneak: Assassin Sneak Dice: " + IntToString(iAssassinSneakDice));
|
|
return iAssassinSneakDice;
|
|
}
|
|
|
|
int GetEpicFeatSneak(object oPC)
|
|
{
|
|
int iEpicFeatDice = 0;
|
|
int iCount;
|
|
|
|
// Basically searches top-down for improved sneak attack feats until it finds one.
|
|
for(iCount = FEAT_EPIC_IMPROVED_SNEAK_ATTACK_10; iCount >= FEAT_EPIC_IMPROVED_SNEAK_ATTACK_1; iCount--)
|
|
{
|
|
if (GetHasFeat(iCount,oPC))
|
|
{
|
|
iEpicFeatDice = (iCount + 1) - FEAT_EPIC_IMPROVED_SNEAK_ATTACK_1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//if (DEBUG) DoDebug("prc_inc_sneak: Epic Sneak Dice: " + IntToString(iEpicFeatDice));
|
|
return iEpicFeatDice;
|
|
}
|
|
|
|
//:://////////////////////////////////////////////
|
|
//:: Sneak Attack Function Definitions
|
|
//:://////////////////////////////////////////////
|
|
|
|
int GetIsFlanked(object oDefender, object oAttacker)
|
|
{
|
|
int bReturnVal = FALSE;
|
|
//if (DEBUG) DoDebug("Starting GetIsFlanked");
|
|
|
|
if(GetIsObjectValid(oAttacker) && GetIsObjectValid(oDefender))
|
|
{
|
|
// I am assuming that if the Defender is facing away from the
|
|
// Attacker then the Defender is flanked, as NWN "turns" an
|
|
// attacker towards the defender
|
|
|
|
vector vDefender = AngleToVector(GetFacing(oDefender));
|
|
vector vAttacker = AngleToVector(GetFacing(oAttacker));
|
|
vector vResult = vDefender + vAttacker;
|
|
//if (DEBUG) DoDebug("GetIsFlanked: End Section #1");
|
|
float iMagDefender = VectorMagnitude(vDefender);
|
|
float iMagResult = VectorMagnitude(vResult);
|
|
|
|
// If the magnitude of the Defenders facing vector is greater than the
|
|
// result of the magnitude of the vector addition of the Attackers and
|
|
// Defenders facing then the Defender is flanked.
|
|
|
|
if(iMagDefender < iMagResult)
|
|
{
|
|
bReturnVal = TRUE;
|
|
}
|
|
}
|
|
//if (DEBUG) DoDebug("GetIsFlanked: End Section #2");
|
|
return bReturnVal;
|
|
}
|
|
|
|
// Checks if an AoE spell is against someone distracted in meleee combat
|
|
int GetIsAOEFlanked(object oDefender, object oAttacker)
|
|
{
|
|
int bReturnVal = TRUE;
|
|
|
|
// if they are not in combat then they are automatically flanked (surprise round)
|
|
if(!PRCGetIsFighting(oDefender) || !GetIsInCombat(oDefender) )
|
|
{
|
|
// checks if they are attacking something other than the caster
|
|
object oTarget = GetAttackTarget(oDefender);
|
|
if(oTarget == oAttacker) bReturnVal = FALSE;
|
|
}
|
|
|
|
return bReturnVal;
|
|
}
|
|
|
|
int GetIsHelpless(object oDefender)
|
|
{
|
|
// Does not apply when grappled
|
|
if (GetLocalInt(oDefender, "IsGrappled") && !GetLocalInt(oDefender, "UnconsciousGrapple")) return FALSE;
|
|
|
|
// PnP describes a helpless defender as
|
|
// A helpless foe - one who is bound, held, sleeping, paralyzed,
|
|
// unconscious, or otherwise at your mercy - is an easy target.
|
|
return ( PRCGetHasEffect(EFFECT_TYPE_PARALYZE, oDefender)
|
|
|| PRCGetHasEffect(EFFECT_TYPE_SLEEP, oDefender)
|
|
|| PRCGetHasEffect(EFFECT_TYPE_PETRIFY, oDefender)
|
|
|| PRCGetHasEffect(EFFECT_TYPE_CUTSCENE_PARALYZE, oDefender) );
|
|
}
|
|
|
|
int GetIsDeniedDexBonusToAC(object oDefender, object oAttacker, int nIgnoreUD = FALSE)
|
|
{
|
|
int bIsDeniedDex = FALSE;
|
|
int bDefenderHasTrueSight = PRCGetHasEffect(EFFECT_TYPE_TRUESEEING, oDefender);
|
|
int bDefenderCanSeeInvisble = PRCGetHasEffect(EFFECT_TYPE_SEEINVISIBLE, oDefender);
|
|
int bDefenderIsKnockedDown = GetHasFeatEffect(FEAT_KNOCKDOWN, oDefender) || GetHasFeatEffect(FEAT_IMPROVED_KNOCKDOWN, oDefender);
|
|
|
|
// if the player is helpess, they are automatically denied dex bonus.
|
|
if( GetIsHelpless(oDefender) ) return TRUE;
|
|
|
|
// Forces it
|
|
if (GetLocalInt(oAttacker, "PRC_SB_UNEXPECTED")) return TRUE;
|
|
|
|
// if the player is not fighting, then this is the "surprise round"
|
|
if( !PRCGetIsFighting(oDefender) || !GetIsInCombat(oDefender) )
|
|
{
|
|
bIsDeniedDex = TRUE;
|
|
}
|
|
|
|
// In NwN, knocked down targets are counted as denied dex bonus to AC.
|
|
if( bDefenderIsKnockedDown ) bIsDeniedDex = TRUE;
|
|
|
|
// if defender has spell effect on them causing them to be denied dex bonus to AC.
|
|
if( PRCGetHasEffect(EFFECT_TYPE_BLINDNESS, oDefender) ) bIsDeniedDex = TRUE;
|
|
else if( PRCGetHasEffect(EFFECT_TYPE_ENTANGLE, oDefender) ) bIsDeniedDex = TRUE;
|
|
else if( PRCGetHasEffect(EFFECT_TYPE_FRIGHTENED, oDefender) ) bIsDeniedDex = TRUE;
|
|
else if( PRCGetHasEffect(EFFECT_TYPE_STUNNED, oDefender) ) bIsDeniedDex = TRUE;
|
|
|
|
// Note: This is wrong by PnP rules... but Bioware allows auto sneaks on Dazed targets.
|
|
// to keep in tune with the game engine I'll leave this active.
|
|
else if( PRCGetHasEffect(EFFECT_TYPE_DAZED, oDefender) ) bIsDeniedDex = TRUE;
|
|
|
|
// if attacker is invisvisible/hiding/etc.
|
|
else if( PRCGetHasEffect(EFFECT_TYPE_INVISIBILITY, oAttacker) && !bDefenderHasTrueSight && !bDefenderCanSeeInvisble )
|
|
{
|
|
bIsDeniedDex = TRUE;
|
|
}
|
|
else if( PRCGetHasEffect(EFFECT_TYPE_IMPROVEDINVISIBILITY, oAttacker) && !bDefenderHasTrueSight && !bDefenderCanSeeInvisble )
|
|
{
|
|
bIsDeniedDex = TRUE;
|
|
}
|
|
else if( !GetObjectSeen(oAttacker, oDefender) )
|
|
{
|
|
bIsDeniedDex = TRUE;
|
|
}
|
|
|
|
// Check for Uncanny Dodge Vs. Sneak Attack.
|
|
if( GetHasFeat(FEAT_UNCANNY_DODGE_2, oDefender) && !nIgnoreUD )
|
|
{
|
|
if(GetLevelByClass(CLASS_TYPE_DWARVENDEFENDER, oDefender))
|
|
return FALSE;
|
|
|
|
// +4 because a rogue has to be 4 levels higher to flank
|
|
int iUncannyDodgeLevels = GetLevelByClass(CLASS_TYPE_ASSASSIN , oDefender)
|
|
+ GetLevelByClass(CLASS_TYPE_BARBARIAN , oDefender)
|
|
+ GetLevelByClass(CLASS_TYPE_PSYCHIC_ROGUE , oDefender)
|
|
+ GetLevelByClass(CLASS_TYPE_ROGUE , oDefender)
|
|
+ GetLevelByClass(CLASS_TYPE_SHADOWDANCER, oDefender)
|
|
+ 4;
|
|
|
|
int iSneakAttackLevels = GetLevelByClass(CLASS_TYPE_BOWMAN , oAttacker)
|
|
+ GetLevelByClass(CLASS_TYPE_NINJA , oAttacker)
|
|
+ GetLevelByClass(CLASS_TYPE_PSYCHIC_ROGUE , oAttacker)
|
|
+ GetLevelByClass(CLASS_TYPE_ROGUE , oAttacker)
|
|
|
|
// add other sneak attacking PrC's here
|
|
+ GetLevelByClass(CLASS_TYPE_ARCTRICK , oAttacker)
|
|
+ GetLevelByClass(CLASS_TYPE_ASSASSIN , oAttacker)
|
|
+ GetLevelByClass(CLASS_TYPE_BFZ , oAttacker)
|
|
+ GetLevelByClass(CLASS_TYPE_BLACKGUARD , oAttacker)
|
|
+ GetLevelByClass(CLASS_TYPE_BLARCHER , oAttacker)
|
|
+ GetLevelByClass(CLASS_TYPE_DISC_BAALZEBUL , oAttacker)
|
|
+ GetLevelByClass(CLASS_TYPE_FIST_DAL_QUOR , oAttacker)
|
|
+ GetLevelByClass(CLASS_TYPE_GHOST_FACED_KILLER , oAttacker)
|
|
+ GetLevelByClass(CLASS_TYPE_HALFLING_WARSLINGER, oAttacker)
|
|
+ GetLevelByClass(CLASS_TYPE_JUSTICEWW , oAttacker)
|
|
+ GetLevelByClass(CLASS_TYPE_LASHER , oAttacker)
|
|
+ GetLevelByClass(CLASS_TYPE_NIGHTSHADE , oAttacker)
|
|
+ GetLevelByClass(CLASS_TYPE_NINJA_SPY , oAttacker)
|
|
+ GetLevelByClass(CLASS_TYPE_PEERLESS , oAttacker)
|
|
+ GetLevelByClass(CLASS_TYPE_SHADOWBLADE , oAttacker)
|
|
+ GetLevelByClass(CLASS_TYPE_SHADOWLORD , oAttacker)
|
|
+ GetLevelByClass(CLASS_TYPE_SHADOWMIND , oAttacker)
|
|
+ GetLevelByClass(CLASS_TYPE_SKULLCLAN_HUNTER , oAttacker)
|
|
+ GetLevelByClass(CLASS_TYPE_SLAYER_OF_DOMIEL , oAttacker);
|
|
|
|
if(iUncannyDodgeLevels > iSneakAttackLevels)
|
|
{
|
|
bIsDeniedDex = FALSE;
|
|
}
|
|
}
|
|
|
|
return bIsDeniedDex;
|
|
}
|
|
|
|
int GetIsConcealed(object oDefender, object oAttacker)
|
|
{
|
|
int bIsConcealed = FALSE;
|
|
|
|
int bAttackerHasTrueSight = PRCGetHasEffect(EFFECT_TYPE_TRUESEEING, oAttacker);
|
|
int bAttackerCanSeeInvisble = PRCGetHasEffect(EFFECT_TYPE_SEEINVISIBLE, oAttacker);
|
|
int bAttackerUltraVision = PRCGetHasEffect(EFFECT_TYPE_ULTRAVISION, oAttacker);
|
|
|
|
if(GetHasFeat(FEAT_EPIC_SELF_CONCEALMENT_50, oDefender) ) bIsConcealed = 50;
|
|
else if(GetHasFeat(FEAT_EPIC_SELF_CONCEALMENT_40, oDefender) ) bIsConcealed = 40;
|
|
else if(GetHasFeat(FEAT_EPIC_SELF_CONCEALMENT_30, oDefender) ) bIsConcealed = 30;
|
|
else if(GetHasFeat(FEAT_EPIC_SELF_CONCEALMENT_20, oDefender) ) bIsConcealed = 20;
|
|
else if(GetHasFeat(FEAT_EPIC_SELF_CONCEALMENT_10, oDefender) ) bIsConcealed = 10;
|
|
|
|
// darkness, invisible, imp invisible
|
|
else if(GetStealthMode(oDefender) == STEALTH_MODE_ACTIVATED && !GetObjectSeen(oDefender, oAttacker) ) bIsConcealed = TRUE;
|
|
else if(PRCGetHasEffect(EFFECT_TYPE_SANCTUARY, oDefender) && !bAttackerHasTrueSight )
|
|
{
|
|
// if they player is hidden you know enough to try attacking, give 50% miss chance
|
|
// as that is the highest concealment normally allowed.
|
|
// couldn't find any rules that governed this though.
|
|
bIsConcealed = 50;
|
|
}
|
|
else if(PRCGetHasEffect(EFFECT_TYPE_INVISIBILITY, oDefender) && !bAttackerHasTrueSight && !bAttackerCanSeeInvisble )
|
|
{
|
|
bIsConcealed = 50;
|
|
}
|
|
else if(PRCGetHasEffect(EFFECT_TYPE_IMPROVEDINVISIBILITY, oDefender) && !bAttackerHasTrueSight && !bAttackerCanSeeInvisble )
|
|
{
|
|
bIsConcealed = 50;
|
|
}
|
|
else if(PRCGetHasEffect(EFFECT_TYPE_DARKNESS, oDefender) && !bAttackerHasTrueSight && !bAttackerUltraVision)
|
|
{
|
|
bIsConcealed = 50;
|
|
}
|
|
else if(GetHasFeatEffect(FEAT_EMPTY_BODY, oDefender) )
|
|
{
|
|
bIsConcealed = 50;
|
|
}
|
|
//else if(PRCGetHasEffect(EFFECT_TYPE_ETHEREAL, oDefender) && !bAttackerHasTrueSight && !bAttackerCanSeeInvisble )
|
|
//{
|
|
// bIsConcealed = TRUE;
|
|
//}
|
|
|
|
// spell effects
|
|
else if(GetHasSpellEffect(1764 , oDefender) && !bAttackerHasTrueSight) // blur spell
|
|
{
|
|
bIsConcealed = 20;
|
|
}
|
|
else if(GetHasSpellEffect(SPELL_DISPLACEMENT , oDefender) && !bAttackerHasTrueSight)
|
|
{
|
|
bIsConcealed = 50;
|
|
}
|
|
else if(GetHasSpellEffect(SPELL_SHADOW_EVADE , oDefender) && !bAttackerHasTrueSight)
|
|
{
|
|
int iSDlevel = GetLevelByClass(CLASS_TYPE_SHADOWDANCER, oDefender);
|
|
if(iSDlevel <= 4) bIsConcealed = 5;
|
|
if(iSDlevel <= 6) bIsConcealed = 10;
|
|
if(iSDlevel <= 8) bIsConcealed = 15;
|
|
if(iSDlevel <= 10) bIsConcealed = 20;
|
|
}
|
|
|
|
// this is the catch-all effect
|
|
else if(PRCGetHasEffect(EFFECT_TYPE_CONCEALMENT, oDefender) && !bAttackerHasTrueSight)
|
|
{
|
|
if(bIsConcealed == FALSE) bIsConcealed = TRUE;
|
|
}
|
|
|
|
if(GetLocalInt(oAttacker, "PRC_SB_UNERRING"))
|
|
{
|
|
bIsConcealed = FALSE;
|
|
return bIsConcealed;
|
|
}
|
|
return bIsConcealed;
|
|
}
|
|
|
|
int GetCanSneakAttack(object oDefender, object oAttacker)
|
|
{
|
|
//cant sneak non-creatures
|
|
if(GetObjectType(oDefender) != OBJECT_TYPE_CREATURE)
|
|
return FALSE;
|
|
|
|
// Can't sneak attack if you're in a grapple
|
|
if(GetLocalInt(oAttacker, "IsGrappled"))
|
|
return FALSE;
|
|
|
|
int bReturnVal = FALSE;
|
|
int bIsInRange = FALSE;
|
|
int bIsFlanked = GetIsFlanked(oDefender, oAttacker);
|
|
int bIsDeniedDex = GetIsDeniedDexBonusToAC(oDefender, oAttacker);
|
|
|
|
float fDistance = GetDistanceBetween(oAttacker, oDefender);
|
|
if(fDistance <= FeetToMeters(30.0f) ) bIsInRange = TRUE;
|
|
|
|
// Is only run if enemy is indeed flanked or denied dex bonus to AC
|
|
// otherwise there is no reason to check further
|
|
if(bIsFlanked || bIsDeniedDex && bIsInRange)
|
|
{
|
|
// so far they can be sneaked
|
|
bReturnVal = TRUE;
|
|
|
|
// checking for other factors that remove sneak attack
|
|
if( GetIsImmune(oDefender, IMMUNITY_TYPE_CRITICAL_HIT, OBJECT_INVALID) ) bReturnVal = FALSE;
|
|
if( GetIsImmune(oDefender, IMMUNITY_TYPE_SNEAK_ATTACK, OBJECT_INVALID) ) bReturnVal = FALSE;
|
|
// Skullclan Hunters can sneak attack undead, so they return true here.
|
|
if( GetLevelByClass(CLASS_TYPE_SKULLCLAN_HUNTER, oAttacker) && GetRacialType(oDefender) == RACIAL_TYPE_UNDEAD) bReturnVal = TRUE;
|
|
|
|
if( GetIsConcealed(oDefender, oAttacker) )
|
|
bReturnVal = FALSE;
|
|
}
|
|
|
|
return bReturnVal;
|
|
}
|
|
|
|
int GetSneakAttackDamage(int iSneakAttackDice)
|
|
{
|
|
int iSneakAttackDamage = d6(iSneakAttackDice);
|
|
return iSneakAttackDamage;
|
|
}
|
|
|
|
int GetDragonfireDamageType(object oPC)
|
|
{
|
|
//Elemental Immunities for various dragon types.
|
|
int iType = GetHasFeat(FEAT_BLACK_DRAGON, oPC) ? DAMAGE_TYPE_ACID :
|
|
GetHasFeat(FEAT_BROWN_DRAGON, oPC) ? DAMAGE_TYPE_ACID :
|
|
GetHasFeat(FEAT_COPPER_DRAGON, oPC) ? DAMAGE_TYPE_ACID :
|
|
GetHasFeat(FEAT_GREEN_DRAGON, oPC) ? DAMAGE_TYPE_ACID :
|
|
GetHasFeat(FEAT_BRASS_DRAGON, oPC) ? DAMAGE_TYPE_FIRE :
|
|
GetHasFeat(FEAT_GOLD_DRAGON, oPC) ? DAMAGE_TYPE_FIRE :
|
|
GetHasFeat(FEAT_RED_DRAGON, oPC) ? DAMAGE_TYPE_FIRE :
|
|
GetHasFeat(FEAT_LUNG_WANG_DRAGON, oPC) ? DAMAGE_TYPE_FIRE :
|
|
GetHasFeat(FEAT_BATTLE_DRAGON, oPC) ? DAMAGE_TYPE_SONIC :
|
|
GetHasFeat(FEAT_EMERALD_DRAGON, oPC) ? DAMAGE_TYPE_SONIC :
|
|
GetHasFeat(FEAT_HOWLING_DRAGON, oPC) ? DAMAGE_TYPE_SONIC :
|
|
GetHasFeat(FEAT_BLUE_DRAGON, oPC) ? DAMAGE_TYPE_ELECTRICAL :
|
|
GetHasFeat(FEAT_BRONZE_DRAGON, oPC) ? DAMAGE_TYPE_ELECTRICAL :
|
|
GetHasFeat(FEAT_OCEANUS_DRAGON, oPC) ? DAMAGE_TYPE_ELECTRICAL :
|
|
GetHasFeat(FEAT_SAPPHIRE_DRAGON, oPC) ? DAMAGE_TYPE_ELECTRICAL :
|
|
GetHasFeat(FEAT_SONG_DRAGON, oPC) ? DAMAGE_TYPE_ELECTRICAL :
|
|
GetHasFeat(FEAT_SHEN_LUNG_DRAGON, oPC) ? DAMAGE_TYPE_ELECTRICAL :
|
|
GetHasFeat(FEAT_CRYSTAL_DRAGON, oPC) ? DAMAGE_TYPE_COLD :
|
|
GetHasFeat(FEAT_TOPAZ_DRAGON, oPC) ? DAMAGE_TYPE_COLD :
|
|
GetHasFeat(FEAT_SILVER_DRAGON, oPC) ? DAMAGE_TYPE_COLD :
|
|
GetHasFeat(FEAT_WHITE_DRAGON, oPC) ? DAMAGE_TYPE_COLD :
|
|
GetHasFeat(FEAT_DRACONIC_HERITAGE_BK, oPC) ? DAMAGE_TYPE_ACID :
|
|
GetHasFeat(FEAT_DRACONIC_HERITAGE_CP, oPC) ? DAMAGE_TYPE_ACID :
|
|
GetHasFeat(FEAT_DRACONIC_HERITAGE_GR, oPC) ? DAMAGE_TYPE_ACID :
|
|
GetHasFeat(FEAT_DRACONIC_HERITAGE_BS, oPC) ? DAMAGE_TYPE_FIRE :
|
|
GetHasFeat(FEAT_DRACONIC_HERITAGE_GD, oPC) ? DAMAGE_TYPE_FIRE :
|
|
GetHasFeat(FEAT_DRACONIC_HERITAGE_RD, oPC) ? DAMAGE_TYPE_FIRE :
|
|
GetHasFeat(FEAT_DRACONIC_HERITAGE_EM, oPC) ? DAMAGE_TYPE_SONIC :
|
|
GetHasFeat(FEAT_DRACONIC_HERITAGE_BL, oPC) ? DAMAGE_TYPE_ELECTRICAL :
|
|
GetHasFeat(FEAT_DRACONIC_HERITAGE_BZ, oPC) ? DAMAGE_TYPE_ELECTRICAL :
|
|
GetHasFeat(FEAT_DRACONIC_HERITAGE_SA, oPC) ? DAMAGE_TYPE_ELECTRICAL :
|
|
GetHasFeat(FEAT_DRACONIC_HERITAGE_CR, oPC) ? DAMAGE_TYPE_COLD :
|
|
GetHasFeat(FEAT_DRACONIC_HERITAGE_TP, oPC) ? DAMAGE_TYPE_COLD :
|
|
GetHasFeat(FEAT_DRACONIC_HERITAGE_SR, oPC) ? DAMAGE_TYPE_COLD :
|
|
GetHasFeat(FEAT_DRACONIC_HERITAGE_WH, oPC) ? DAMAGE_TYPE_COLD :
|
|
GetHasFeat(FEAT_KOB_DRAGONWROUGHT_BK, oPC) ? DAMAGE_TYPE_ACID :
|
|
GetHasFeat(FEAT_KOB_DRAGONWROUGHT_CP, oPC) ? DAMAGE_TYPE_ACID :
|
|
GetHasFeat(FEAT_KOB_DRAGONWROUGHT_GR, oPC) ? DAMAGE_TYPE_ACID :
|
|
GetHasFeat(FEAT_KOB_DRAGONWROUGHT_BS, oPC) ? DAMAGE_TYPE_FIRE :
|
|
GetHasFeat(FEAT_KOB_DRAGONWROUGHT_GD, oPC) ? DAMAGE_TYPE_FIRE :
|
|
GetHasFeat(FEAT_KOB_DRAGONWROUGHT_RD, oPC) ? DAMAGE_TYPE_FIRE :
|
|
GetHasFeat(FEAT_KOB_DRAGONWROUGHT_EM, oPC) ? DAMAGE_TYPE_SONIC :
|
|
GetHasFeat(FEAT_KOB_DRAGONWROUGHT_BL, oPC) ? DAMAGE_TYPE_ELECTRICAL :
|
|
GetHasFeat(FEAT_KOB_DRAGONWROUGHT_BZ, oPC) ? DAMAGE_TYPE_ELECTRICAL :
|
|
GetHasFeat(FEAT_KOB_DRAGONWROUGHT_SA, oPC) ? DAMAGE_TYPE_ELECTRICAL :
|
|
GetHasFeat(FEAT_KOB_DRAGONWROUGHT_CR, oPC) ? DAMAGE_TYPE_COLD :
|
|
GetHasFeat(FEAT_KOB_DRAGONWROUGHT_TP, oPC) ? DAMAGE_TYPE_COLD :
|
|
GetHasFeat(FEAT_KOB_DRAGONWROUGHT_SR, oPC) ? DAMAGE_TYPE_COLD :
|
|
GetHasFeat(FEAT_KOB_DRAGONWROUGHT_WH, oPC) ? DAMAGE_TYPE_COLD :
|
|
DAMAGE_TYPE_FIRE; // If none match, make the itemproperty invalid
|
|
|
|
return iType;
|
|
}
|
|
|
|
int GetFavouredEnemyBonus(object oPC)
|
|
{
|
|
object oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oPC);
|
|
int nWeaponType = GetBaseItemType(oWeapon);
|
|
|
|
int nClass;
|
|
int nFE = 0;
|
|
|
|
// Ranger
|
|
nClass = GetLevelByClass(CLASS_TYPE_RANGER, oPC);
|
|
if (nClass) nFE += nClass/5 + 1;
|
|
|
|
if (DEBUG) DoDebug("prc_inc_sneak: Favoured Enemy Bonus: " + IntToString(nFE));
|
|
return nFE;
|
|
} |