1210 lines
44 KiB
Plaintext
1210 lines
44 KiB
Plaintext
//::///////////////////////////////////////////////
|
||
//:: Beholder AI and Attack Include
|
||
//:: x2_inc_behai
|
||
//:: Copyright (c) 2003 Bioware Corp.
|
||
//:://////////////////////////////////////////////
|
||
/*
|
||
|
||
Include file for several beholder AI functions
|
||
|
||
*/
|
||
//:://////////////////////////////////////////////
|
||
//:: Created By: Georg Zoeller
|
||
//:: Created On: August, 2003
|
||
//:://////////////////////////////////////////////
|
||
// Modified by Tony K for NWN2 2008-05-17
|
||
// Modified by Tony K for NWN1 2008-07-22
|
||
|
||
|
||
#include "x2_inc_beholder"
|
||
|
||
|
||
const int BEHOLDER_RAY_TYPES_STANDARD = 0;
|
||
const int BEHOLDER_RAY_TYPES_GAUTH = 1;
|
||
const int BEHOLDER_RAY_TYPES_STANDARD_MISSING14 = 2;
|
||
const int BEHOLDER_RAY_TYPES_SPECIFIED = 3;
|
||
const int BEHOLDER_RAY_TYPES_EYEBALL = 4;
|
||
const int BEHOLDER_RAY_TYPES_HIVE_MOTHER = 5;
|
||
const int BEHOLDER_RAY_TYPES_BEHOLDER_MAGE = 6;
|
||
// user created ray types
|
||
const int BEHOLDER_RAY_TYPES_USER1 = 20;
|
||
const int BEHOLDER_RAY_TYPES_USER2 = 21;
|
||
const int BEHOLDER_RAY_TYPES_USER3 = 22;
|
||
|
||
// internal AI local variables
|
||
const string BEHOLDER_THREAT_RATING = "BeholderThreatRating";
|
||
const string BEHOLDER_TOUCH_CHANCE = "BeholderTouchChance";
|
||
const string BEHOLDER_RAY_TARGET_QUAD = "BeholderRayQuad";
|
||
const string BEHOLDER_RAY_QUAD_COUNT = "BeholderRayQuadCount";
|
||
const string BEHOLDER_RAY_MESSAGE_SENT = "BeholderMessageSent";
|
||
const string BEHOLDER_EYE_IS_OPEN = "BeholderEyeOpen";
|
||
const string BEHOLDER_AI_ACTION_IN_ROUND_USED = "BeholderActionInRoundUsed";
|
||
const string BEHOLDER_AI_AGILE_TYRANT_USE = "BeholderAgileTyrantUse";
|
||
|
||
const int BEHOLDER_QUADRANT_FRONT = 1;
|
||
const int BEHOLDER_QUADRANT_RIGHT = 2;
|
||
const int BEHOLDER_QUADRANT_BACK = 3;
|
||
const int BEHOLDER_QUADRANT_LEFT = 4;
|
||
|
||
int gHenchRayTargetObjects;
|
||
|
||
|
||
// initializes list of targets into quadrants for the eyestalk rays
|
||
void InitRayTargets(object oTarget, int bFromSpellScript)
|
||
{
|
||
object oCurTarget = GetLocalObject(OBJECT_SELF, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
vector vSelf = GetPosition(OBJECT_SELF);
|
||
vector vTarget = GetPosition(oTarget);
|
||
float fAngle1 = VectorToAngle(vTarget - vSelf);
|
||
|
||
while (GetIsObjectValid(oCurTarget))
|
||
{
|
||
// Jug_Debug(GetName(OBJECT_SELF) + " init ray target " + GetName(oTarget));
|
||
float fCurThreatRating = GetRawThreatRating(oCurTarget);
|
||
|
||
if (GetIsDisabled(oCurTarget))
|
||
{
|
||
fCurThreatRating *= 0.1;
|
||
}
|
||
SetLocalFloat(oCurTarget, BEHOLDER_THREAT_RATING, fCurThreatRating);
|
||
if (bFromSpellScript && GetIsPC(oCurTarget))
|
||
{
|
||
SetLocalInt(oCurTarget, BEHOLDER_RAY_MESSAGE_SENT, TRUE);
|
||
}
|
||
else
|
||
{
|
||
DeleteLocalInt(oCurTarget, BEHOLDER_RAY_MESSAGE_SENT);
|
||
}
|
||
float fTouchChance;
|
||
if (GetObjectSeen(oCurTarget))
|
||
{
|
||
int index;
|
||
int hitCount;
|
||
for (index = 0; index < 20; index++)
|
||
{
|
||
hitCount += TouchAttackRanged(oCurTarget, FALSE) ? 1 : 0;
|
||
}
|
||
// Jug_Debug(GetName(OBJECT_SELF) + " target " + GetName(oCurTarget) + " raw touch chance " + IntToString(hitCount));
|
||
if (!hitCount)
|
||
{
|
||
fTouchChance = 0.05;
|
||
}
|
||
else if (hitCount == 20)
|
||
{
|
||
fTouchChance = 0.95;
|
||
}
|
||
else
|
||
{
|
||
fTouchChance = hitCount / 20.0;
|
||
}
|
||
vector vCurTarget = GetPositionFromLocation(GetLocation(oCurTarget));
|
||
float fAngle2 = VectorToAngle(vCurTarget - vSelf);
|
||
|
||
float fRelAngel = fAngle1 - fAngle2;
|
||
if (fRelAngel < 0.0)
|
||
{
|
||
fAngle1 += 360.0;
|
||
}
|
||
int quadrant;
|
||
if (fRelAngel > 315.0 || fRelAngel <= 45.0)
|
||
{
|
||
quadrant = BEHOLDER_QUADRANT_FRONT;
|
||
}
|
||
else if (fRelAngel <= 135.0)
|
||
{
|
||
quadrant = BEHOLDER_QUADRANT_RIGHT;
|
||
}
|
||
else if (fRelAngel <= 225.0)
|
||
{
|
||
quadrant = BEHOLDER_QUADRANT_BACK;
|
||
}
|
||
else
|
||
{
|
||
quadrant = BEHOLDER_QUADRANT_LEFT;
|
||
}
|
||
SetLocalInt(oCurTarget, BEHOLDER_RAY_TARGET_QUAD, quadrant);
|
||
// Jug_Debug(GetName(oTarget) + " quadrant " + IntToString(quadrant));
|
||
}
|
||
// else
|
||
// {
|
||
// Jug_Debug("*****" + GetName(OBJECT_SELF) + " target " + GetName(oCurTarget) + " is not seen");
|
||
// }
|
||
SetLocalFloat(oCurTarget, BEHOLDER_TOUCH_CHANCE, fTouchChance);
|
||
|
||
oCurTarget = GetLocalObject(oCurTarget, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
}
|
||
int curQuadCount;
|
||
int nMaxRaysPerQuadrant = GetBeholderMaxRaysPerQuadrant();
|
||
for (curQuadCount = 1; curQuadCount <= 4; curQuadCount++)
|
||
{
|
||
SetIntArray(OBJECT_SELF, BEHOLDER_RAY_QUAD_COUNT, curQuadCount, nMaxRaysPerQuadrant);
|
||
}
|
||
|
||
if (GetBeholderAgileTyrant())
|
||
{
|
||
SetLocalInt(OBJECT_SELF, BEHOLDER_AI_AGILE_TYRANT_USE, TRUE);
|
||
}
|
||
}
|
||
|
||
|
||
// get death ray attack chances
|
||
void BeholderCheckDeathRay()
|
||
{
|
||
int nSaveDC = GetBeholderEyeStalkSpellDC();
|
||
|
||
float damageAmount = IntToFloat(11 + GetBeholderEyeStalkSpellCasterLevel());
|
||
|
||
object oCurTarget = GetLocalObject(OBJECT_SELF, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
while (GetIsObjectValid(oCurTarget))
|
||
{
|
||
// Jug_Debug(GetName(OBJECT_SELF) + " check death ray for " + GetName(oCurTarget));
|
||
float ratio = GetLocalFloat(oCurTarget, BEHOLDER_TOUCH_CHANCE);
|
||
if (GetIsImmune(oCurTarget, IMMUNITY_TYPE_DEATH, OBJECT_SELF))
|
||
{
|
||
ratio *= CalculateDamageWeight(damageAmount, oCurTarget);
|
||
}
|
||
else
|
||
{
|
||
float saveChance = Getd20ChanceLimited(nSaveDC - GetFortitudeSavingThrow(oCurTarget));
|
||
ratio *= saveChance /* * 1.0 */ +
|
||
(1.0 - saveChance) * CalculateDamageWeight(damageAmount, oCurTarget);
|
||
}
|
||
SetFloatArray(oCurTarget, BEHOLDER_TOUCH_CHANCE, BEHOLDER_RAY_DEATH, ratio);
|
||
// Jug_Debug(GetName(OBJECT_SELF) + " ratio " + FloatToString(ratio));
|
||
|
||
oCurTarget = GetLocalObject(oCurTarget, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
}
|
||
}
|
||
|
||
|
||
// get telekinesis thrust chances
|
||
void BeholderCheckTKThrustRay()
|
||
{
|
||
int nSaveDC = GetBeholderEyeStalkSpellDC();
|
||
|
||
object oCurTarget = GetLocalObject(OBJECT_SELF, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
while (GetIsObjectValid(oCurTarget))
|
||
{
|
||
float ratio = GetLocalFloat(oCurTarget, BEHOLDER_TOUCH_CHANCE);
|
||
if (GetIsImmune(oCurTarget, IMMUNITY_TYPE_KNOCKDOWN, OBJECT_SELF) ||
|
||
(GetAdjustedCreatureSize(oCurTarget) > CREATURE_SIZE_MEDIUM) ||
|
||
GetIsDisabled(oCurTarget))
|
||
{
|
||
ratio = 0.0;
|
||
}
|
||
else
|
||
{
|
||
ratio *= Getd20ChanceLimited(nSaveDC - GetWillSavingThrow(oCurTarget));
|
||
}
|
||
SetFloatArray(oCurTarget, BEHOLDER_TOUCH_CHANCE, BEHOLDER_RAY_TK_THRUST, ratio);
|
||
|
||
oCurTarget = GetLocalObject(oCurTarget, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
}
|
||
}
|
||
|
||
|
||
// get telekinesis throw rock chances
|
||
void BeholderCheckTKThrowRocksRay()
|
||
{
|
||
float damageAmount = 2.0 * GetBeholderEyeStalkSpellCasterLevel();
|
||
// Jug_Debug(GetName(OBJECT_SELF) + " rock damage is " + FloatToString(damageAmount));
|
||
|
||
object oCurTarget = GetLocalObject(OBJECT_SELF, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
while (GetIsObjectValid(oCurTarget))
|
||
{
|
||
float ratio = Getd20ChanceLimited(GetBaseAttackBonus(OBJECT_SELF) + GetAbilityModifier(ABILITY_CHARISMA) - GetAC(oCurTarget));
|
||
// Jug_Debug(GetName(OBJECT_SELF) + " throw rock chance on " + GetName(oCurTarget) + " is " + FloatToString(ratio));
|
||
ratio *= CalculateDamageWeight(damageAmount, oCurTarget);
|
||
|
||
SetFloatArray(oCurTarget, BEHOLDER_TOUCH_CHANCE, BEHOLDER_RAY_TK_THROW_ROCKS, ratio);
|
||
|
||
oCurTarget = GetLocalObject(oCurTarget, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
}
|
||
}
|
||
|
||
|
||
// get petrification attack chances
|
||
void BeholderCheckPetriRay()
|
||
{
|
||
int nSaveDC = GetBeholderEyeStalkSpellDC();
|
||
|
||
object oCurTarget = GetLocalObject(OBJECT_SELF, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
while (GetIsObjectValid(oCurTarget))
|
||
{
|
||
float ratio = GetLocalFloat(oCurTarget, BEHOLDER_TOUCH_CHANCE);
|
||
if (GetHasEffect(EFFECT_TYPE_PETRIFY, oCurTarget))
|
||
{
|
||
ratio = 0.0;
|
||
}
|
||
else
|
||
{
|
||
ratio *= Getd20ChanceLimited(nSaveDC - GetFortitudeSavingThrow(oCurTarget));
|
||
}
|
||
SetFloatArray(oCurTarget, BEHOLDER_TOUCH_CHANCE, BEHOLDER_RAY_PETRI, ratio);
|
||
|
||
oCurTarget = GetLocalObject(oCurTarget, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
}
|
||
}
|
||
|
||
|
||
// get charm attack chances
|
||
void BeholderCheckCharmRay(int nRay)
|
||
{
|
||
int nSaveDC = GetBeholderEyeStalkSpellDC();
|
||
|
||
object oCurTarget = GetLocalObject(OBJECT_SELF, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
while (GetIsObjectValid(oCurTarget))
|
||
{
|
||
float ratio = GetLocalFloat(oCurTarget, BEHOLDER_TOUCH_CHANCE);
|
||
if (GetIsImmune(oCurTarget, IMMUNITY_TYPE_MIND_SPELLS, OBJECT_SELF) ||
|
||
GetIsImmune(oCurTarget, IMMUNITY_TYPE_CHARM, OBJECT_SELF) || GetIsDisabled(oCurTarget) ||
|
||
((nRay == BEHOLDER_RAY_CHARM_PER) && !GetIsHumanoid(GetRacialType(oCurTarget))))
|
||
{
|
||
ratio = 0.0;
|
||
}
|
||
else
|
||
{
|
||
ratio *= Getd20ChanceLimited(nSaveDC - GetWillSavingThrow(oCurTarget));
|
||
if (nRay == BEHOLDER_RAY_CHARM_PER)
|
||
{
|
||
ratio *= 0.31;
|
||
}
|
||
else
|
||
{
|
||
ratio *= 0.3;
|
||
}
|
||
}
|
||
SetFloatArray(oCurTarget, BEHOLDER_TOUCH_CHANCE, nRay, ratio);
|
||
|
||
oCurTarget = GetLocalObject(oCurTarget, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
}
|
||
}
|
||
|
||
|
||
// get slow attack chances
|
||
void BeholderCheckSlowRay()
|
||
{
|
||
int nSaveDC = GetBeholderEyeStalkSpellDC();
|
||
|
||
object oCurTarget = GetLocalObject(OBJECT_SELF, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
while (GetIsObjectValid(oCurTarget))
|
||
{
|
||
float ratio = GetLocalFloat(oCurTarget, BEHOLDER_TOUCH_CHANCE);
|
||
if (GetHasEffect(EFFECT_TYPE_SLOW, oCurTarget))
|
||
{
|
||
ratio = 0.0;
|
||
}
|
||
else
|
||
{
|
||
ratio *= 0.3 * Getd20ChanceLimited(nSaveDC - GetWillSavingThrow(oCurTarget));
|
||
}
|
||
SetFloatArray(oCurTarget, BEHOLDER_TOUCH_CHANCE, BEHOLDER_RAY_SLOW, ratio);
|
||
|
||
oCurTarget = GetLocalObject(oCurTarget, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
}
|
||
}
|
||
|
||
|
||
// get inflict wounds attack chances
|
||
void BeholderCheckWoundRay(int nRay)
|
||
{
|
||
int nSaveDC = GetBeholderEyeStalkSpellDC();
|
||
|
||
float damageAmount = IntToFloat(GetBeholderEyeStalkSpellCasterLevel());
|
||
if (nRay == BEHOLDER_RAY_WOUND)
|
||
{
|
||
if (damageAmount > 10.0)
|
||
{
|
||
damageAmount = 10.0;
|
||
}
|
||
damageAmount += 9.0;
|
||
}
|
||
else
|
||
{
|
||
if (damageAmount > 20.0)
|
||
{
|
||
damageAmount = 20.0;
|
||
}
|
||
damageAmount += 18.0;
|
||
}
|
||
|
||
object oCurTarget = GetLocalObject(OBJECT_SELF, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
while (GetIsObjectValid(oCurTarget))
|
||
{
|
||
// Jug_Debug(GetName(OBJECT_SELF) + " check wounds ray for " + GetName(oCurTarget));
|
||
float ratio = GetLocalFloat(oCurTarget, BEHOLDER_TOUCH_CHANCE);
|
||
if (GetRacialType(oCurTarget) == RACIAL_TYPE_UNDEAD)
|
||
{
|
||
ratio = 0.0;
|
||
}
|
||
else
|
||
{
|
||
float saveChance = Getd20ChanceLimited(nSaveDC - GetWillSavingThrow(oCurTarget));
|
||
ratio *= saveChance * CalculateDamageWeight(damageAmount, oCurTarget) +
|
||
(1.0 - saveChance) * CalculateDamageWeight(damageAmount / 2.0, oCurTarget);
|
||
}
|
||
SetFloatArray(oCurTarget, BEHOLDER_TOUCH_CHANCE, nRay, ratio);
|
||
// Jug_Debug(GetName(OBJECT_SELF) + " ratio " + FloatToString(ratio));
|
||
|
||
oCurTarget = GetLocalObject(oCurTarget, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
}
|
||
}
|
||
|
||
|
||
// get fear attack chances
|
||
void BeholderCheckFearRay(int nRay)
|
||
{
|
||
int nSaveDC = GetBeholderEyeStalkSpellDC();
|
||
int checkHitDice = nRay == BEHOLDER_RAY_CAUSE_FEAR;
|
||
|
||
object oCurTarget = GetLocalObject(OBJECT_SELF, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
while (GetIsObjectValid(oCurTarget))
|
||
{
|
||
float ratio = GetLocalFloat(oCurTarget, BEHOLDER_TOUCH_CHANCE);
|
||
if (GetIsImmune(oCurTarget, IMMUNITY_TYPE_FEAR, OBJECT_SELF) ||
|
||
GetIsImmune(oCurTarget, IMMUNITY_TYPE_MIND_SPELLS, OBJECT_SELF) ||
|
||
GetIsDisabled(oCurTarget) ||
|
||
(checkHitDice && (GetHitDice(oCurTarget) >= 5)))
|
||
{
|
||
ratio = 0.0;
|
||
}
|
||
else
|
||
{
|
||
ratio *= 0.95 * Getd20ChanceLimited(nSaveDC - GetWillSavingThrow(oCurTarget));
|
||
}
|
||
SetFloatArray(oCurTarget, BEHOLDER_TOUCH_CHANCE, nRay, ratio);
|
||
|
||
oCurTarget = GetLocalObject(oCurTarget, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
}
|
||
}
|
||
|
||
|
||
// get sleep attack chances
|
||
void BeholderCheckSleepRay()
|
||
{
|
||
int nSaveDC = GetBeholderEyeStalkSpellDC();
|
||
|
||
object oCurTarget = GetLocalObject(OBJECT_SELF, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
while (GetIsObjectValid(oCurTarget))
|
||
{
|
||
float ratio = GetLocalFloat(oCurTarget, BEHOLDER_TOUCH_CHANCE);
|
||
if (GetIsImmune(oCurTarget, IMMUNITY_TYPE_SLEEP, OBJECT_SELF) ||
|
||
GetIsImmune(oCurTarget, IMMUNITY_TYPE_MIND_SPELLS, OBJECT_SELF) || GetIsDisabled(oCurTarget))
|
||
{
|
||
ratio = 0.0;
|
||
}
|
||
else
|
||
{
|
||
ratio *= 0.95 * Getd20ChanceLimited(nSaveDC - GetWillSavingThrow(oCurTarget));
|
||
}
|
||
SetFloatArray(oCurTarget, BEHOLDER_TOUCH_CHANCE, BEHOLDER_RAY_SLEEP, ratio);
|
||
|
||
oCurTarget = GetLocalObject(oCurTarget, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
}
|
||
}
|
||
|
||
|
||
// get disintegrate attack chances
|
||
void BeholderCheckDisintegrateRay()
|
||
{
|
||
int nSaveDC = GetBeholderEyeStalkSpellDC();
|
||
|
||
float damageAmount = 3.5 * IntToFloat(GetBeholderEyeStalkSpellCasterLevel());
|
||
|
||
object oCurTarget = GetLocalObject(OBJECT_SELF, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
while (GetIsObjectValid(oCurTarget))
|
||
{
|
||
// Jug_Debug(GetName(OBJECT_SELF) + " check disintegrate ray for " + GetName(oCurTarget));
|
||
float ratio = GetLocalFloat(oCurTarget, BEHOLDER_TOUCH_CHANCE);
|
||
float saveChance = Getd20ChanceLimited(nSaveDC - GetFortitudeSavingThrow(oCurTarget));
|
||
ratio *= saveChance * CalculateDamageWeight(damageAmount, oCurTarget) +
|
||
(1.0 - saveChance) * CalculateDamageWeight(18.0, oCurTarget);
|
||
SetFloatArray(oCurTarget, BEHOLDER_TOUCH_CHANCE, BEHOLDER_RAY_DISINTEGRATE, ratio);
|
||
// Jug_Debug(GetName(OBJECT_SELF) + " ratio " + FloatToString(ratio));
|
||
|
||
oCurTarget = GetLocalObject(oCurTarget, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
}
|
||
}
|
||
|
||
|
||
// get dispel attack chances
|
||
void BeholderCheckDispelRay()
|
||
{
|
||
int casterLevel = GetBeholderEyeStalkSpellCasterLevel();
|
||
if (casterLevel > 10)
|
||
{
|
||
casterLevel = 10;
|
||
}
|
||
giBestDispelCastingLevel = casterLevel;
|
||
|
||
object oCurTarget = GetLocalObject(OBJECT_SELF, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
while (GetIsObjectValid(oCurTarget))
|
||
{
|
||
// Jug_Debug(GetName(OBJECT_SELF) + " check dispel ray for " + GetName(oCurTarget));
|
||
float ratio = GetLocalFloat(oCurTarget, BEHOLDER_TOUCH_CHANCE);
|
||
|
||
if (GetIsDisabled(oCurTarget))
|
||
{
|
||
ratio = 0.0;
|
||
}
|
||
else
|
||
{
|
||
ratio *= Jug_GetHasBeneficialEnhancement(oCurTarget).dispel;
|
||
}
|
||
|
||
SetFloatArray(oCurTarget, BEHOLDER_TOUCH_CHANCE, BEHOLDER_RAY_DISPEL, ratio);
|
||
// Jug_Debug(GetName(OBJECT_SELF) + " ratio " + FloatToString(ratio));
|
||
|
||
oCurTarget = GetLocalObject(oCurTarget, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
}
|
||
}
|
||
|
||
|
||
// get damage attack chances
|
||
void BeholderCheckDamageRay(int nRay)
|
||
{
|
||
float damage;
|
||
if (nRay == BEHOLDER_RAY_SCORCHING)
|
||
{
|
||
damage = 14.0;
|
||
}
|
||
else
|
||
{
|
||
damage = 2.0;
|
||
}
|
||
object oCurTarget = GetLocalObject(OBJECT_SELF, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
while (GetIsObjectValid(oCurTarget))
|
||
{
|
||
// Jug_Debug(GetName(OBJECT_SELF) + " check damage ray for " + GetName(oCurTarget));
|
||
float ratio = GetLocalFloat(oCurTarget, BEHOLDER_TOUCH_CHANCE);
|
||
ratio *= CalculateDamageWeight(damage, oCurTarget);
|
||
SetFloatArray(oCurTarget, BEHOLDER_TOUCH_CHANCE, nRay, ratio);
|
||
// Jug_Debug(GetName(OBJECT_SELF) + " ratio " + FloatToString(ratio));
|
||
|
||
oCurTarget = GetLocalObject(oCurTarget, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
}
|
||
}
|
||
|
||
|
||
// get paralysis attack chances
|
||
void BeholderCheckParalysisRay()
|
||
{
|
||
int nSaveDC = GetBeholderEyeStalkSpellDC() - 4;
|
||
|
||
object oCurTarget = GetLocalObject(OBJECT_SELF, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
while (GetIsObjectValid(oCurTarget))
|
||
{
|
||
float ratio = GetLocalFloat(oCurTarget, BEHOLDER_TOUCH_CHANCE);
|
||
if (GetIsImmune(oCurTarget, IMMUNITY_TYPE_PARALYSIS, OBJECT_SELF) ||
|
||
GetIsImmune(oCurTarget, IMMUNITY_TYPE_MIND_SPELLS, OBJECT_SELF) ||
|
||
GetIsDisabled(oCurTarget))
|
||
{
|
||
ratio = 0.0;
|
||
}
|
||
else
|
||
{
|
||
ratio *= 0.45 * Getd20ChanceLimited(nSaveDC - GetFortitudeSavingThrow(oCurTarget));
|
||
}
|
||
SetFloatArray(oCurTarget, BEHOLDER_TOUCH_CHANCE, BEHOLDER_RAY_PARALYSIS, ratio);
|
||
|
||
oCurTarget = GetLocalObject(oCurTarget, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
}
|
||
}
|
||
|
||
|
||
// get exhaust attack chances
|
||
void BeholderCheckExhaustionRay()
|
||
{
|
||
object oCurTarget = GetLocalObject(OBJECT_SELF, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
while (GetIsObjectValid(oCurTarget))
|
||
{
|
||
float ratio = GetLocalFloat(oCurTarget, BEHOLDER_TOUCH_CHANCE);
|
||
if (GetIsImmune(oCurTarget, IMMUNITY_TYPE_ABILITY_DECREASE, OBJECT_SELF) ||
|
||
GetHasSpellEffect(BEHOLDER_EXHAUSTION_SPELLID , oCurTarget) ||
|
||
GetIsDisabled(oCurTarget))
|
||
{
|
||
ratio = 0.0;
|
||
}
|
||
else
|
||
{
|
||
ratio *= 0.35;
|
||
}
|
||
SetFloatArray(oCurTarget, BEHOLDER_TOUCH_CHANCE, BEHOLDER_RAY_EXHAUSTION, ratio);
|
||
|
||
oCurTarget = GetLocalObject(oCurTarget, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
}
|
||
}
|
||
|
||
|
||
// get daze attack chances
|
||
void BeholderCheckDazeRay()
|
||
{
|
||
int nSaveDC = GetBeholderEyeStalkSpellDC();
|
||
|
||
object oCurTarget = GetLocalObject(OBJECT_SELF, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
while (GetIsObjectValid(oCurTarget))
|
||
{
|
||
float ratio = GetLocalFloat(oCurTarget, BEHOLDER_TOUCH_CHANCE);
|
||
if (GetIsImmune(oCurTarget, IMMUNITY_TYPE_MIND_SPELLS, OBJECT_SELF) ||
|
||
GetIsImmune(oCurTarget, IMMUNITY_TYPE_CHARM, OBJECT_SELF) || GetIsDisabled(oCurTarget) ||
|
||
!GetIsHumanoid(GetRacialType(oCurTarget)) || (GetHitDice(oCurTarget) >= 5))
|
||
{
|
||
ratio = 0.0;
|
||
}
|
||
else
|
||
{
|
||
ratio *= Getd20ChanceLimited(nSaveDC - GetWillSavingThrow(oCurTarget));
|
||
ratio *= 0.3;
|
||
}
|
||
SetFloatArray(oCurTarget, BEHOLDER_TOUCH_CHANCE, BEHOLDER_RAY_DAZE, ratio);
|
||
|
||
oCurTarget = GetLocalObject(oCurTarget, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
}
|
||
}
|
||
|
||
|
||
// BEHOLDER_RAY_USER add custom ray spell targeting here, copy the best matching ray and modify
|
||
|
||
|
||
const string sCustomStringName = "<CUSTOM0>";
|
||
|
||
// find the best ray to use against possible targets, the target priority is lowered based
|
||
// on the attack chance
|
||
int CheckRayToUse(int bUseAntiMagic, int nRaysToUseMask)
|
||
{
|
||
int nBestRay;
|
||
float fBestTargetWeight;
|
||
object oBestTarget;
|
||
|
||
int nCurRay = 1;
|
||
int nCurRayMask = nRaysToUseMask;
|
||
while (nCurRayMask)
|
||
{
|
||
if (nCurRayMask & 1)
|
||
{
|
||
// Jug_Debug(GetName(OBJECT_SELF) + " testing ray " + IntToHexString(nCurRay));
|
||
object oCurTarget = GetLocalObject(OBJECT_SELF, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
while (GetIsObjectValid(oCurTarget))
|
||
{
|
||
int targetQuadrant = GetLocalInt(oCurTarget, BEHOLDER_RAY_TARGET_QUAD);
|
||
// check quadrant limit
|
||
if (!(bUseAntiMagic && (targetQuadrant == BEHOLDER_QUADRANT_FRONT) &&
|
||
(nCurRay != BEHOLDER_RAY_TK_THROW_ROCKS)) &&
|
||
GetIntArray(OBJECT_SELF, BEHOLDER_RAY_QUAD_COUNT, targetQuadrant) ||
|
||
GetLocalInt(OBJECT_SELF, BEHOLDER_AI_AGILE_TYRANT_USE))
|
||
{
|
||
float fCurTargetWeight = GetFloatArray(oCurTarget, BEHOLDER_TOUCH_CHANCE, nCurRay);
|
||
|
||
// Jug_Debug(GetName(OBJECT_SELF) + " test target " + GetName(oCurTarget) + " weight " + FloatToString(fCurTargetWeight));
|
||
|
||
if (fCurTargetWeight > 0.0)
|
||
{
|
||
fCurTargetWeight *= GetLocalFloat(oCurTarget, BEHOLDER_THREAT_RATING);
|
||
|
||
if (fCurTargetWeight > fBestTargetWeight)
|
||
{
|
||
nBestRay = nCurRay;
|
||
fBestTargetWeight = fCurTargetWeight;
|
||
oBestTarget = oCurTarget;
|
||
}
|
||
}
|
||
}
|
||
oCurTarget = GetLocalObject(oCurTarget, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
}
|
||
}
|
||
|
||
nCurRayMask = nCurRayMask >> 1;
|
||
nCurRay = nCurRay << 1;
|
||
}
|
||
|
||
if (fBestTargetWeight <= 0.0)
|
||
{
|
||
nRaysToUseMask = 0;
|
||
}
|
||
else
|
||
{
|
||
if (GetIsPC(oBestTarget) && !GetLocalInt(oBestTarget, BEHOLDER_RAY_MESSAGE_SENT))
|
||
{
|
||
string sLocalizedText = GetStringByStrRef(83839);
|
||
int totalLen = GetStringLength(sLocalizedText);
|
||
int customLen = GetStringLength(sCustomStringName);
|
||
int startPosition = FindSubString(sLocalizedText, sCustomStringName);
|
||
string sDisplayText = GetStringLeft(sLocalizedText, startPosition);
|
||
sDisplayText += GetName(OBJECT_SELF);
|
||
sDisplayText += GetStringRight(sLocalizedText, totalLen - customLen);
|
||
SendMessageToPC(oBestTarget, "<c<>p<EFBFBD>>" + sDisplayText + "</c>");
|
||
SetLocalInt(oBestTarget, BEHOLDER_RAY_MESSAGE_SENT, TRUE);
|
||
}
|
||
|
||
BehDoFireBeam(nBestRay, oBestTarget);
|
||
|
||
nRaysToUseMask = RemoveEyebalRayType(nRaysToUseMask, nBestRay);
|
||
|
||
float fBestChance = 1.0 - GetFloatArray(oBestTarget, BEHOLDER_TOUCH_CHANCE, nBestRay);
|
||
fBestChance *= GetLocalFloat(oBestTarget, BEHOLDER_THREAT_RATING);
|
||
SetLocalFloat(oBestTarget, BEHOLDER_THREAT_RATING, fBestChance);
|
||
|
||
int quadrant = GetLocalInt(oBestTarget, BEHOLDER_RAY_TARGET_QUAD);
|
||
int raysLeftInQuadrant = GetIntArray(OBJECT_SELF, BEHOLDER_RAY_QUAD_COUNT, quadrant);
|
||
if (!raysLeftInQuadrant)
|
||
{
|
||
// remove agile tyrant use (one extra ray in one quadrant)
|
||
DeleteLocalInt(OBJECT_SELF, BEHOLDER_AI_AGILE_TYRANT_USE);
|
||
}
|
||
else
|
||
{
|
||
SetIntArray(OBJECT_SELF, BEHOLDER_RAY_QUAD_COUNT, quadrant, raysLeftInQuadrant - 1);
|
||
}
|
||
}
|
||
return nRaysToUseMask;
|
||
}
|
||
|
||
|
||
// check and fire all eyestalk rays
|
||
void CheckRays(int bUseAntiMagic)
|
||
{
|
||
int rayTypes = GetLocalInt(OBJECT_SELF, "beholder_ray_types");
|
||
|
||
int nRaysToUseMask;
|
||
switch (rayTypes)
|
||
{
|
||
case BEHOLDER_RAY_TYPES_GAUTH:
|
||
nRaysToUseMask = GAUTH_STANDARD_EYE_RAYS;
|
||
break;
|
||
case BEHOLDER_RAY_TYPES_STANDARD_MISSING14:
|
||
nRaysToUseMask = BEHOLDER_STANDARD_EYE_RAYS;
|
||
{
|
||
// Jug_Debug(GetName(OBJECT_SELF) + " starting rays " + IntToHexString(nRaysToUseMask));
|
||
int randomCount = d4();
|
||
while ((randomCount > 0) && nRaysToUseMask)
|
||
{
|
||
int eyeStalkToRemove = 1 << d10();
|
||
if (eyeStalkToRemove & nRaysToUseMask)
|
||
{
|
||
// Jug_Debug(GetName(OBJECT_SELF) + " removing ray " + IntToHexString(eyeStalkToRemove));
|
||
nRaysToUseMask = RemoveEyebalRayType(nRaysToUseMask, eyeStalkToRemove);
|
||
randomCount--;
|
||
}
|
||
}
|
||
// Jug_Debug(GetName(OBJECT_SELF) + " ending rays " + IntToHexString(nRaysToUseMask));
|
||
}
|
||
SetLocalInt(OBJECT_SELF, "beholder_ray_custom", nRaysToUseMask);
|
||
SetLocalInt(OBJECT_SELF, "beholder_ray_types", BEHOLDER_RAY_TYPES_SPECIFIED);
|
||
break;
|
||
case BEHOLDER_RAY_TYPES_SPECIFIED:
|
||
nRaysToUseMask = GetLocalInt(OBJECT_SELF, "beholder_ray_custom");
|
||
break;
|
||
case BEHOLDER_RAY_TYPES_EYEBALL:
|
||
nRaysToUseMask = EYEBALL_STANDARD_EYE_RAYS;
|
||
break;
|
||
case BEHOLDER_RAY_TYPES_HIVE_MOTHER:
|
||
nRaysToUseMask = HIVE_MOTHER_STANDARD_EYE_RAYS;
|
||
break;
|
||
case BEHOLDER_RAY_TYPES_BEHOLDER_MAGE:
|
||
nRaysToUseMask = BEHOLDER_MAGE_EYE_RAYS;
|
||
break;
|
||
|
||
// BEHOLDER_RAY_TYPES_USER a custom set of ray types can be added here
|
||
|
||
default:
|
||
nRaysToUseMask = BEHOLDER_STANDARD_EYE_RAYS;
|
||
break;
|
||
}
|
||
// Jug_Debug(GetName(OBJECT_SELF) + " in check rays types " + IntToHexString(nRaysToUseMask));
|
||
|
||
nRaysToUseMask &= ~GetLocalInt(OBJECT_SELF, BEHOLDER_RAY_SUPPRESS);
|
||
|
||
// Jug_Debug(GetName(OBJECT_SELF) + " in check rays types after suppress " + IntToHexString(nRaysToUseMask));
|
||
|
||
if (nRaysToUseMask & BEHOLDER_RAY_DEATH)
|
||
{
|
||
BeholderCheckDeathRay();
|
||
}
|
||
if (nRaysToUseMask & BEHOLDER_RAY_TK_THRUST)
|
||
{
|
||
BeholderCheckTKThrustRay();
|
||
}
|
||
if (nRaysToUseMask & BEHOLDER_RAY_TK_THROW_ROCKS)
|
||
{
|
||
BeholderCheckTKThrowRocksRay();
|
||
}
|
||
if (nRaysToUseMask & BEHOLDER_RAY_PETRI)
|
||
{
|
||
BeholderCheckPetriRay();
|
||
}
|
||
if (nRaysToUseMask & BEHOLDER_RAY_CHARM_PER)
|
||
{
|
||
BeholderCheckCharmRay(BEHOLDER_RAY_CHARM_PER);
|
||
}
|
||
if (nRaysToUseMask & BEHOLDER_RAY_CHARM_MON)
|
||
{
|
||
BeholderCheckCharmRay(BEHOLDER_RAY_CHARM_MON);
|
||
}
|
||
if (nRaysToUseMask & BEHOLDER_RAY_SLOW)
|
||
{
|
||
BeholderCheckSlowRay();
|
||
}
|
||
if (nRaysToUseMask & BEHOLDER_RAY_WOUND)
|
||
{
|
||
BeholderCheckWoundRay(BEHOLDER_RAY_WOUND);
|
||
}
|
||
if (nRaysToUseMask & BEHOLDER_RAY_CRITICAL_WOUND)
|
||
{
|
||
BeholderCheckWoundRay(BEHOLDER_RAY_CRITICAL_WOUND);
|
||
}
|
||
if (nRaysToUseMask & BEHOLDER_RAY_FEAR)
|
||
{
|
||
BeholderCheckFearRay(BEHOLDER_RAY_FEAR);
|
||
}
|
||
if (nRaysToUseMask & BEHOLDER_RAY_CAUSE_FEAR)
|
||
{
|
||
BeholderCheckFearRay(BEHOLDER_RAY_CAUSE_FEAR);
|
||
}
|
||
if (nRaysToUseMask & BEHOLDER_RAY_SLEEP)
|
||
{
|
||
BeholderCheckSleepRay();
|
||
}
|
||
if (nRaysToUseMask & BEHOLDER_RAY_DISINTEGRATE)
|
||
{
|
||
BeholderCheckDisintegrateRay();
|
||
}
|
||
|
||
if (nRaysToUseMask & BEHOLDER_RAY_DISPEL)
|
||
{
|
||
BeholderCheckDispelRay();
|
||
}
|
||
if (nRaysToUseMask & BEHOLDER_RAY_SCORCHING)
|
||
{
|
||
BeholderCheckDamageRay(BEHOLDER_RAY_SCORCHING);
|
||
}
|
||
if (nRaysToUseMask & BEHOLDER_RAY_FROST)
|
||
{
|
||
BeholderCheckDamageRay(BEHOLDER_RAY_FROST);
|
||
}
|
||
if (nRaysToUseMask & BEHOLDER_RAY_PARALYSIS)
|
||
{
|
||
BeholderCheckParalysisRay();
|
||
}
|
||
if (nRaysToUseMask & BEHOLDER_RAY_EXHAUSTION)
|
||
{
|
||
BeholderCheckExhaustionRay();
|
||
}
|
||
if (nRaysToUseMask & BEHOLDER_RAY_DAZE)
|
||
{
|
||
BeholderCheckDazeRay();
|
||
}
|
||
|
||
// BEHOLDER_RAY_USER add custom ray spell here
|
||
|
||
int useAllRays = !GetLocalInt(OBJECT_SELF, BEHOLDER_AI_ONE_RAY);
|
||
|
||
do
|
||
{
|
||
nRaysToUseMask = CheckRayToUse(bUseAntiMagic, nRaysToUseMask);
|
||
} while (useAllRays && nRaysToUseMask);
|
||
}
|
||
|
||
|
||
const string sCreatureSaveResultStr = "BEHOLDER_CREATURE_SAVE";
|
||
const string sCreatureMagicUserStr = "BEHOLDER_CREATURE_MAGIC_USER_ONLY";
|
||
|
||
// get the best antimagic target
|
||
object GetBestAntiMagicTarget(object oTarget)
|
||
{
|
||
// reset flag on nearby creatures
|
||
location testTargetLoc = GetLocation(OBJECT_SELF);
|
||
object oTestTarget = GetFirstObjectInShape(SHAPE_SPHERE, 25.0, testTargetLoc, FALSE, OBJECT_TYPE_CREATURE);
|
||
while (GetIsObjectValid(oTestTarget))
|
||
{
|
||
SetLocalInt(oTestTarget, sCreatureSaveResultStr, -1);
|
||
oTestTarget = GetNextObjectInShape(SHAPE_SPHERE, 25.0, testTargetLoc, FALSE, OBJECT_TYPE_CREATURE);
|
||
}
|
||
|
||
int bRequireTarget;
|
||
if (GetDistanceToObject(oTarget) <= 5.0)
|
||
{
|
||
bRequireTarget = TRUE;
|
||
}
|
||
|
||
object oBestTarget = OBJECT_INVALID;
|
||
int nBestMagicTarget = 0;
|
||
|
||
object oCurTarget = GetLocalObject(OBJECT_SELF, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
while (GetIsObjectValid(oCurTarget))
|
||
{
|
||
if (GetDistanceToObject(oCurTarget) > (bRequireTarget ? 5.0 : 25.0))
|
||
{
|
||
oCurTarget = GetLocalObject(oCurTarget, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
continue;
|
||
}
|
||
testTargetLoc = GetLocation(oCurTarget);
|
||
int nAntiMagicCount;
|
||
int nMageCount;
|
||
|
||
oTestTarget = GetFirstObjectInShape(SHAPE_SPELLCONE, 25.0, testTargetLoc, TRUE, OBJECT_TYPE_CREATURE | OBJECT_TYPE_AREA_OF_EFFECT);
|
||
//Cycle through the targets within the spell shape until an invalid object is captured.
|
||
while (GetIsObjectValid(oTestTarget))
|
||
{
|
||
if (GetObjectType(oTestTarget) == OBJECT_TYPE_AREA_OF_EFFECT)
|
||
{
|
||
object oCreator = GetAreaOfEffectCreator(oTestTarget);
|
||
if (GetObjectType(oCreator) == OBJECT_TYPE_CREATURE && GetIsEnemy(oCreator))
|
||
{
|
||
nAntiMagicCount += 5;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (oTestTarget != OBJECT_SELF && !GetIsDead(oTestTarget))
|
||
{
|
||
if (GetIsFriend(oTestTarget))
|
||
{
|
||
nAntiMagicCount = -1000;
|
||
break;
|
||
}
|
||
int nSaveResult = GetLocalInt(oTestTarget, sCreatureSaveResultStr);
|
||
int nMag;
|
||
if (nSaveResult == -1)
|
||
{
|
||
if ((oTestTarget != oTarget) && !GetObjectSeen(oTestTarget) && !GetObjectHeard(oTestTarget))
|
||
{
|
||
nSaveResult = 0;
|
||
nMag = 0;
|
||
}
|
||
// already antimagic or dead?
|
||
else if (!GetHasSpellEffect(BEHOLDER_EYE_SPELLID, oTestTarget) && !GetIsDead(oTestTarget))
|
||
{
|
||
nSaveResult = 0;
|
||
effect eCheck = GetFirstEffect(oTestTarget);
|
||
int bContinueLoop = TRUE;
|
||
while (bContinueLoop && GetIsEffectValid(eCheck))
|
||
{
|
||
int iSpell = GetEffectSpellId(eCheck);
|
||
if (iSpell != -1 && GetEffectSubType(eCheck) == SUBTYPE_MAGICAL)
|
||
{
|
||
// Found an effect applied by a spell script - check the effect type
|
||
// Ignore invisibility effects since that's a special case taken
|
||
// care of elsewhere
|
||
int iType = GetEffectType(eCheck);
|
||
|
||
switch(iType)
|
||
{
|
||
case EFFECT_TYPE_REGENERATE:
|
||
case EFFECT_TYPE_SANCTUARY:
|
||
case EFFECT_TYPE_IMMUNITY:
|
||
case EFFECT_TYPE_INVULNERABLE:
|
||
case EFFECT_TYPE_HASTE:
|
||
case EFFECT_TYPE_ELEMENTALSHIELD:
|
||
case EFFECT_TYPE_SPELL_IMMUNITY:
|
||
case EFFECT_TYPE_SPELLLEVELABSORPTION:
|
||
case EFFECT_TYPE_DAMAGE_IMMUNITY_INCREASE:
|
||
case EFFECT_TYPE_DAMAGE_INCREASE:
|
||
case EFFECT_TYPE_DAMAGE_REDUCTION:
|
||
case EFFECT_TYPE_DAMAGE_RESISTANCE:
|
||
case EFFECT_TYPE_POLYMORPH:
|
||
case EFFECT_TYPE_DOMINATED:
|
||
nSaveResult += 10;
|
||
break;
|
||
case EFFECT_TYPE_ABILITY_INCREASE:
|
||
case EFFECT_TYPE_AC_INCREASE:
|
||
case EFFECT_TYPE_ATTACK_INCREASE:
|
||
case EFFECT_TYPE_CONCEALMENT:
|
||
case EFFECT_TYPE_ENEMY_ATTACK_BONUS:
|
||
case EFFECT_TYPE_MOVEMENT_SPEED_INCREASE:
|
||
case EFFECT_TYPE_SAVING_THROW_INCREASE:
|
||
case EFFECT_TYPE_SEEINVISIBLE:
|
||
case EFFECT_TYPE_SKILL_INCREASE:
|
||
case EFFECT_TYPE_SPELL_RESISTANCE_INCREASE:
|
||
case EFFECT_TYPE_TEMPORARY_HITPOINTS:
|
||
case EFFECT_TYPE_TRUESEEING:
|
||
case EFFECT_TYPE_ULTRAVISION:
|
||
nSaveResult++;
|
||
break;
|
||
case EFFECT_TYPE_SLOW:
|
||
// sometimes ignore slow
|
||
if (d2() == 1)
|
||
{
|
||
break;
|
||
}
|
||
case EFFECT_TYPE_PARALYZE:
|
||
case EFFECT_TYPE_STUNNED:
|
||
case EFFECT_TYPE_FRIGHTENED:
|
||
case EFFECT_TYPE_SLEEP:
|
||
case EFFECT_TYPE_DAZED:
|
||
case EFFECT_TYPE_CHARMED:
|
||
case EFFECT_TYPE_CONFUSED:
|
||
case EFFECT_TYPE_TURNED:
|
||
// if disabled don't dispel
|
||
nSaveResult = -1000;
|
||
bContinueLoop = FALSE;
|
||
break;
|
||
case EFFECT_TYPE_PETRIFY:
|
||
// ignore this target
|
||
nSaveResult = 0;
|
||
bContinueLoop = FALSE;
|
||
break;
|
||
}
|
||
}
|
||
eCheck = GetNextEffect(oTestTarget);
|
||
}
|
||
if (nSaveResult < 0)
|
||
{
|
||
nAntiMagicCount = -1000;
|
||
break;
|
||
}
|
||
|
||
nMag = GetLevelByClass(CLASS_TYPE_WIZARD, oTestTarget) + GetLevelByClass(CLASS_TYPE_SORCERER, oTestTarget) +
|
||
GetLevelByClass(CLASS_TYPE_BARD, oTestTarget) + GetLevelByClass(CLASS_TYPE_CLERIC, oTestTarget) +
|
||
GetLevelByClass(CLASS_TYPE_DRUID, oTestTarget);
|
||
if (nSaveResult > 0)
|
||
{
|
||
nSaveResult += nMag / 3;
|
||
nMag = 0;
|
||
}
|
||
else if (nMag > 3)
|
||
{
|
||
nSaveResult += nMag / 3;
|
||
nMag = 1;
|
||
|
||
}
|
||
else
|
||
{
|
||
nMag = 0;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
nSaveResult = 0;
|
||
nMag = 0;
|
||
}
|
||
SetLocalInt(oTestTarget, sCreatureSaveResultStr, nSaveResult);
|
||
SetLocalInt(oTestTarget, sCreatureMagicUserStr, nMag);
|
||
}
|
||
else
|
||
{
|
||
nMag = GetLocalInt(oTestTarget, sCreatureMagicUserStr);
|
||
}
|
||
nAntiMagicCount += nSaveResult;
|
||
nMageCount += nMag;
|
||
}
|
||
}
|
||
//Select the next target within the spell shape.
|
||
oTestTarget = GetNextObjectInShape(SHAPE_SPELLCONE, 25.0, testTargetLoc, TRUE, OBJECT_TYPE_CREATURE | OBJECT_TYPE_AREA_OF_EFFECT);
|
||
}
|
||
// Jug_Debug(GetName(OBJECT_SELF) + " mage count " + IntToString(nMageCount) + " total " + IntToString(gHenchSpellTargetObjects));
|
||
if ((nAntiMagicCount > nBestMagicTarget) && (nMageCount < gHenchSpellTargetObjects))
|
||
{
|
||
nBestMagicTarget = nAntiMagicCount;
|
||
oBestTarget = oCurTarget;
|
||
}
|
||
oCurTarget = GetLocalObject(oCurTarget, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
}
|
||
// ignore minor magic sometimes
|
||
if (nBestMagicTarget <= d4(2))
|
||
{
|
||
oBestTarget = OBJECT_INVALID;
|
||
}
|
||
return oBestTarget;
|
||
}
|
||
|
||
|
||
// get the best stun target
|
||
object GetBestStunTarget(object oTarget)
|
||
{
|
||
// reset flag on nearby creatures
|
||
location testTargetLoc = GetLocation(OBJECT_SELF);
|
||
object oTestTarget = GetFirstObjectInShape(SHAPE_SPHERE, 25.0, testTargetLoc, FALSE, OBJECT_TYPE_CREATURE);
|
||
while (GetIsObjectValid(oTestTarget))
|
||
{
|
||
SetLocalInt(oTestTarget, sCreatureSaveResultStr, -1);
|
||
oTestTarget = GetNextObjectInShape(SHAPE_SPHERE, 25.0, testTargetLoc, FALSE, OBJECT_TYPE_CREATURE);
|
||
}
|
||
|
||
int bRequireTarget;
|
||
if (GetDistanceToObject(oTarget) <= 5.0)
|
||
{
|
||
bRequireTarget = TRUE;
|
||
}
|
||
|
||
object oBestTarget = OBJECT_INVALID;
|
||
int nBestStunCount;
|
||
|
||
object oCurTarget = GetLocalObject(OBJECT_SELF, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
while (GetIsObjectValid(oCurTarget))
|
||
{
|
||
if (GetDistanceToObject(oCurTarget) > (bRequireTarget ? 5.0 : 25.0))
|
||
{
|
||
oCurTarget = GetLocalObject(oCurTarget, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
continue;
|
||
}
|
||
testTargetLoc = GetLocation(oCurTarget);
|
||
int nStunCount;
|
||
|
||
oTestTarget = GetFirstObjectInShape(SHAPE_SPELLCONE, 25.0, testTargetLoc, TRUE, OBJECT_TYPE_CREATURE);
|
||
//Cycle through the targets within the spell shape until an invalid object is captured.
|
||
while (GetIsObjectValid(oTestTarget))
|
||
{
|
||
if (oTestTarget != OBJECT_SELF && !GetIsDead(oTestTarget))
|
||
{
|
||
if (GetIsFriend(oTestTarget))
|
||
{
|
||
nStunCount = -1000;
|
||
break;
|
||
}
|
||
int nSaveResult = GetLocalInt(oTestTarget, sCreatureSaveResultStr);
|
||
if (nSaveResult == -1)
|
||
{
|
||
if ((oTestTarget != oTarget) && !GetObjectSeen(oTestTarget) && !GetObjectHeard(oTestTarget))
|
||
{
|
||
nSaveResult = 0;
|
||
}
|
||
else if (!GetIsDisabled(oTestTarget) && !GetIsDead(oTestTarget) &&
|
||
!GetIsImmune(oTestTarget, IMMUNITY_TYPE_MIND_SPELLS, OBJECT_SELF) &&
|
||
!GetIsImmune(oTestTarget, IMMUNITY_TYPE_STUN, OBJECT_SELF))
|
||
{
|
||
nSaveResult = 1;
|
||
}
|
||
else
|
||
{
|
||
nSaveResult = 0;
|
||
}
|
||
SetLocalInt(oTestTarget, sCreatureSaveResultStr, nSaveResult);
|
||
}
|
||
nStunCount += nSaveResult;
|
||
}
|
||
//Select the next target within the spell shape.
|
||
oTestTarget = GetNextObjectInShape(SHAPE_SPELLCONE, 25.0, testTargetLoc, TRUE, OBJECT_TYPE_CREATURE);
|
||
}
|
||
// Jug_Debug(GetName(OBJECT_SELF) + " stun total " + IntToString(nStunCount));
|
||
if (nStunCount > nBestStunCount)
|
||
{
|
||
nBestStunCount = nStunCount;
|
||
oBestTarget = oCurTarget;
|
||
}
|
||
oCurTarget = GetLocalObject(oCurTarget, BEHOLDER_SPELL_TARGET_OBJECTS);
|
||
}
|
||
if (nBestStunCount <= 0)
|
||
{
|
||
oBestTarget = OBJECT_INVALID;
|
||
}
|
||
// Jug_Debug(GetName(OBJECT_SELF) + " stun " + GetName(oBestTarget));
|
||
return oBestTarget;
|
||
}
|
||
|
||
|
||
// run AI for all eyestalk ray attacks
|
||
void RunBeholderSmallEyeAttacks(object oTarget, int bFromSpellScript, int bAntiMagicUsed)
|
||
{
|
||
// if in antimagic or blind, can't use eye ray attacks
|
||
if (GetHasEffect(EFFECT_TYPE_SPELL_FAILURE) || GetHasEffect(EFFECT_TYPE_BLINDNESS))
|
||
{
|
||
return;
|
||
}
|
||
|
||
// find targets - up to three eyes can be used in a quadrant
|
||
InitRayTargets(oTarget, bFromSpellScript);
|
||
|
||
CheckRays(bAntiMagicUsed);
|
||
}
|
||
|
||
|
||
// checks main eye and eyestalk AI for what to do
|
||
// returns antimagic or stun target if any
|
||
object RunBeholderEyeAttacks(object oTarget, int bFromSpellScript)
|
||
{
|
||
// Jug_Debug(GetName(OBJECT_SELF) + " run beholder attack spell");
|
||
|
||
// Only if we are beholders and not beholder mages
|
||
int nMainEyeAttack = GetLocalInt(OBJECT_SELF, BEHOLDER_MAIN_EYE_TYPE);
|
||
if (nMainEyeAttack == BEHOLDER_MAIN_EYE_ANTI_MAGIC_RANDOM)
|
||
{
|
||
nMainEyeAttack = (d3() == 1) ? BEHOLDER_MAIN_EYE_ANTI_MAGIC : BEHOLDER_MAIN_EYE_NONE;
|
||
SetLocalInt(OBJECT_SELF, BEHOLDER_MAIN_EYE_TYPE, nMainEyeAttack);
|
||
}
|
||
|
||
DeleteLocalInt(OBJECT_SELF, BEHOLDER_EYE_IS_OPEN);
|
||
DeleteLocalInt(OBJECT_SELF, BEHOLDER_AI_ACTION_IN_ROUND_USED);
|
||
|
||
// need that to make them not drop out of combat
|
||
SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, 736));
|
||
|
||
HenchInitSpellTargetObjects(oTarget);
|
||
|
||
// first determine where anti magic eye should be used (if at all)
|
||
int bUseMainEye;
|
||
if (nMainEyeAttack == BEHOLDER_MAIN_EYE_ANTI_MAGIC)
|
||
{
|
||
object oTestTarget = GetBestAntiMagicTarget(oTarget);
|
||
bUseMainEye = GetIsObjectValid(oTestTarget);
|
||
if (bUseMainEye)
|
||
{
|
||
oTarget = oTestTarget;
|
||
}
|
||
}
|
||
else if (nMainEyeAttack == BEHOLDER_MAIN_EYE_STUN)
|
||
{
|
||
object oTestTarget = GetBestStunTarget(oTarget);
|
||
bUseMainEye = GetIsObjectValid(oTestTarget);
|
||
if (bUseMainEye)
|
||
{
|
||
oTarget = oTestTarget;
|
||
SetLocalInt(OBJECT_SELF, BEHOLDER_AI_ACTION_IN_ROUND_USED, TRUE);
|
||
}
|
||
}
|
||
|
||
DelayCommand(0.01, RunBeholderSmallEyeAttacks(oTarget, bFromSpellScript,
|
||
bUseMainEye && (nMainEyeAttack == BEHOLDER_MAIN_EYE_ANTI_MAGIC)));
|
||
|
||
// Jug_Debug(GetName(OBJECT_SELF) + " use main eye " + IntToString(bUseMainEye));
|
||
|
||
// Only if we are beholders and not beholder mages
|
||
if (bUseMainEye)
|
||
{
|
||
// Jug_Debug(GetName(OBJECT_SELF) + " use anti magic on " + GetName(oTarget));
|
||
SetLocalInt(OBJECT_SELF, BEHOLDER_EYE_IS_OPEN, TRUE);
|
||
OpenAntiMagicEye(oTarget);
|
||
return oTarget;
|
||
}
|
||
return OBJECT_INVALID;
|
||
}
|
||
|
||
|
||
const string beholderCombatRoundStr = "BeholderCombatRound";
|
||
const string beholderLastCombatTime = "BeholderLastCombatRound";
|
||
|
||
// test if a new combat round has started
|
||
// since eyestalk attacks are free actions, this allows their use even if moving
|
||
int CheckIfNewCombatRound()
|
||
{
|
||
int combatRoundCount = GetLocalInt(OBJECT_SELF, beholderCombatRoundStr);
|
||
int combatRoundIncremented;
|
||
|
||
int currentTimeSec = GetTimeSecond();
|
||
|
||
if (combatRoundCount == 0)
|
||
{
|
||
combatRoundCount ++;
|
||
SetLocalInt(OBJECT_SELF, beholderLastCombatTime, currentTimeSec);
|
||
SetLocalInt(OBJECT_SELF, beholderCombatRoundStr, combatRoundCount);
|
||
combatRoundIncremented = TRUE;
|
||
}
|
||
else
|
||
{
|
||
int lastCombatTime = GetLocalInt(OBJECT_SELF, beholderLastCombatTime);
|
||
int lastCombatTimeDiff = currentTimeSec + 1 - lastCombatTime;
|
||
if (lastCombatTimeDiff < 0)
|
||
{
|
||
lastCombatTimeDiff += 60;
|
||
}
|
||
if (lastCombatTimeDiff > 5)
|
||
{
|
||
// Jug_Debug(GetName(OBJECT_SELF) + " setting combat round count value " + IntToString(lastCombatTimeDiff));
|
||
combatRoundCount ++;
|
||
combatRoundIncremented = TRUE;
|
||
SetLocalInt(OBJECT_SELF, beholderCombatRoundStr, combatRoundCount);
|
||
SetLocalInt(OBJECT_SELF, beholderLastCombatTime, currentTimeSec);
|
||
}
|
||
}
|
||
|
||
return combatRoundIncremented;
|
||
} |