926 lines
34 KiB
Plaintext
926 lines
34 KiB
Plaintext
//::///////////////////////////////////////////////
|
|
//:: Beholder main eye and eyestalk spells
|
|
//:: x2_inc_beholder
|
|
//:: Copyright (c) 2003 Bioware Corp.
|
|
//:://////////////////////////////////////////////
|
|
/*
|
|
|
|
Include file for several beholder 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_behcommon"
|
|
|
|
|
|
const int BEHOLDER_EYE_SPELLID = 727; // main eye spell id
|
|
const int BEHOLDER_TK_SPELLID = 777; // telekinesis spell id
|
|
const int BEHOLDER_DISINTEGRATE_SPELLID = 787;
|
|
const int BEHOLDER_EXHAUSTION_SPELLID = 776; // exhaustion spell id
|
|
|
|
const int BEHOLDER_RAY_DEATH = 0x00000001;
|
|
const int BEHOLDER_RAY_TK_THRUST = 0x00000002;
|
|
const int BEHOLDER_RAY_PETRI = 0x00000004;
|
|
const int BEHOLDER_RAY_CHARM_PER = 0x00000008;
|
|
const int BEHOLDER_RAY_SLOW = 0x00000010;
|
|
const int BEHOLDER_RAY_WOUND = 0x00000020;
|
|
const int BEHOLDER_RAY_FEAR = 0x00000040;
|
|
// added beholder rays
|
|
const int BEHOLDER_RAY_CHARM_MON = 0x00000080;
|
|
const int BEHOLDER_RAY_SLEEP = 0x00000100;
|
|
const int BEHOLDER_RAY_DISINTEGRATE = 0x00000200;
|
|
// gauth rays
|
|
const int BEHOLDER_RAY_DISPEL = 0x00000400;
|
|
const int BEHOLDER_RAY_SCORCHING = 0x00000800;
|
|
const int BEHOLDER_RAY_PARALYSIS = 0x00001000;
|
|
const int BEHOLDER_RAY_EXHAUSTION = 0x00002000;
|
|
// extra telekinesis
|
|
const int BEHOLDER_RAY_TK_THROW_ROCKS = 0x00004000;
|
|
// eyeball
|
|
const int BEHOLDER_RAY_FROST = 0x00008000; // after use, must wait two rounds before next use of frost or daze
|
|
const int BEHOLDER_RAY_DAZE = 0x00010000; // after use, must wait two rounds before next use of frost or daze
|
|
const int BEHOLDER_RAY_CAUSE_FEAR = 0x00020000;
|
|
// hive mother
|
|
const int BEHOLDER_RAY_CRITICAL_WOUND = 0x00040000;
|
|
|
|
// user created rays
|
|
const int BEHOLDER_RAY_USER1 = 0x00400000;
|
|
const int BEHOLDER_RAY_USER2 = 0x00800000;
|
|
const int BEHOLDER_RAY_USER3 = 0x01000000;
|
|
const int BEHOLDER_RAY_USER4 = 0x02000000;
|
|
const int BEHOLDER_RAY_USER5 = 0x04000000;
|
|
const int BEHOLDER_RAY_USER6 = 0x08000000;
|
|
const int BEHOLDER_RAY_USER7 = 0x10000000;
|
|
const int BEHOLDER_RAY_USER8 = 0x20000000;
|
|
const int BEHOLDER_RAY_USER9 = 0x40000000;
|
|
const int BEHOLDER_RAY_USER10 = 0x80000000;
|
|
|
|
|
|
const int BEHOLDER_STANDARD_EYE_RAYS = 0x000043ff;
|
|
const int GAUTH_STANDARD_EYE_RAYS = 0x00003d20;
|
|
const int EYEBALL_STANDARD_EYE_RAYS = 0x00038000;
|
|
const int HIVE_MOTHER_STANDARD_EYE_RAYS = 0x000443df;
|
|
const int BEHOLDER_MAGE_EYE_RAYS = 0x00004227;
|
|
|
|
const int BEHOLDER_RAY_TK_ALL_TYPES = 0x00004002; // all telekinesis types or'ed together
|
|
|
|
|
|
const int BEHOLDER_MAIN_EYE_ANTI_MAGIC = 0;
|
|
const int BEHOLDER_MAIN_EYE_NONE = 1;
|
|
const int BEHOLDER_MAIN_EYE_STUN = 2;
|
|
const int BEHOLDER_MAIN_EYE_ANTI_MAGIC_RANDOM = 3;
|
|
|
|
const string BEHOLDER_MAIN_EYE_TYPE = "beholder_main_eye";
|
|
const string BEHOLDER_AI_ONE_RAY = "beholder_one_ray";
|
|
|
|
|
|
// returns eyestalk caster level
|
|
int GetBeholderEyeStalkSpellCasterLevel()
|
|
{
|
|
int returnValue = GetLocalInt(OBJECT_SELF, "beholder_spell_level");
|
|
if (returnValue < 1)
|
|
{
|
|
returnValue = 13;
|
|
}
|
|
return returnValue;
|
|
}
|
|
|
|
|
|
// returns eyestalk DC, normally 10 + HD/2 + Cha modifier
|
|
int GetBeholderEyeStalkSpellDC()
|
|
{
|
|
int returnValue = GetLocalInt(OBJECT_SELF, "beholder_spell_dc");
|
|
if (returnValue < 1)
|
|
{
|
|
returnValue = 10 + GetHitDice(OBJECT_SELF) / 2 + GetAbilityModifier(ABILITY_CHARISMA);
|
|
}
|
|
else
|
|
{
|
|
// adjust for possible charisma bonus or penalty
|
|
returnValue += GetAbilityModifier(ABILITY_CHARISMA) - GetAbilityScore(OBJECT_SELF, ABILITY_CHARISMA, TRUE) / 2 + 5;
|
|
}
|
|
return returnValue;
|
|
}
|
|
|
|
|
|
// return maximum number of eyestalk rays per quadrant
|
|
int GetBeholderMaxRaysPerQuadrant()
|
|
{
|
|
int returnValue = GetLocalInt(OBJECT_SELF, "beholder_rays_per_quad");
|
|
if (returnValue < 1)
|
|
{
|
|
returnValue = 3;
|
|
}
|
|
return returnValue;
|
|
}
|
|
|
|
|
|
// TRUE if beholder has agile tyrant feat (once extra ray in one quadrant)
|
|
int GetBeholderAgileTyrant()
|
|
{
|
|
return GetLocalInt(OBJECT_SELF, "beholder_agile_tyrant");
|
|
}
|
|
|
|
|
|
// remove nRayToRemove from bitmask nCurRays, returns new bitmask
|
|
int RemoveEyebalRayType(int nCurRays, int nRayToRemove)
|
|
{
|
|
int bitMask;
|
|
if (nRayToRemove & BEHOLDER_RAY_TK_ALL_TYPES)
|
|
{
|
|
bitMask = ~BEHOLDER_RAY_TK_ALL_TYPES;
|
|
}
|
|
else
|
|
{
|
|
bitMask = ~nRayToRemove;
|
|
}
|
|
nCurRays &= bitMask;
|
|
return nCurRays;
|
|
}
|
|
|
|
|
|
// applies beholder petrification effects
|
|
void DoBeholderPetrify(int nDuration, object oSource, object oTarget, int nSpellID)
|
|
{
|
|
if(!GetIsReactionTypeFriendly(oTarget) && !GetIsDead(oTarget))
|
|
{
|
|
// * exit if creature is immune to petrification
|
|
if (spellsIsImmuneToPetrification(oTarget) == TRUE)
|
|
{
|
|
return;
|
|
}
|
|
float fDifficulty = 0.0;
|
|
int bIsPC = GetIsPC(oTarget);
|
|
int bShowPopup = FALSE;
|
|
|
|
// * calculate Duration based on difficulty settings
|
|
int nGameDiff = GetGameDifficulty();
|
|
switch (nGameDiff)
|
|
{
|
|
case GAME_DIFFICULTY_VERY_EASY:
|
|
case GAME_DIFFICULTY_EASY:
|
|
case GAME_DIFFICULTY_NORMAL:
|
|
fDifficulty = RoundsToSeconds(nDuration); // One Round per hit-die or caster level
|
|
break;
|
|
case GAME_DIFFICULTY_CORE_RULES:
|
|
case GAME_DIFFICULTY_DIFFICULT:
|
|
if (!GetPlotFlag(oTarget))
|
|
{
|
|
bShowPopup = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
effect ePetrify = EffectPetrify();
|
|
effect eDur = EffectVisualEffect(VFX_DUR_CESSATE_NEGATIVE);
|
|
effect eLink = EffectLinkEffects(eDur, ePetrify);
|
|
|
|
|
|
/// * The duration is permanent against NPCs but only temporary against PCs
|
|
if (bIsPC == TRUE)
|
|
{
|
|
if (bShowPopup == TRUE)
|
|
{
|
|
// * under hardcore rules or higher, this is an instant death
|
|
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLink, oTarget);
|
|
DelayCommand(2.75, PopUpDeathGUIPanel(oTarget, FALSE , TRUE, 40579));
|
|
// if in hardcore, treat the player as an NPC
|
|
bIsPC = FALSE;
|
|
}
|
|
else
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, fDifficulty);
|
|
}
|
|
else
|
|
{
|
|
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLink, oTarget);
|
|
// * Feb 11 2003 BK I don't think this is necessary anymore
|
|
//if the target was an NPC - make him uncommandable until Stone to Flesh is cast
|
|
//SetCommandable(FALSE, oTarget);
|
|
|
|
// Feb 5 2004 - Jon
|
|
// Added kick-henchman-out-of-party code from generic petrify script
|
|
if (GetAssociateType(oTarget) == ASSOCIATE_TYPE_HENCHMAN)
|
|
{
|
|
FireHenchman(GetMaster(oTarget),oTarget);
|
|
}
|
|
}
|
|
// April 2003: Clearing actions to kick them out of conversation when petrified
|
|
AssignCommand(oTarget, ClearAllActions());
|
|
}
|
|
}
|
|
|
|
|
|
const string BEHOLDER_RAY_SUPPRESS = "BeholderRaySuppress";
|
|
|
|
// resets ray use
|
|
void TurnOffSuppress(int nRayMask)
|
|
{
|
|
SetLocalInt(OBJECT_SELF, BEHOLDER_RAY_SUPPRESS, ~nRayMask & GetLocalInt(OBJECT_SELF, BEHOLDER_RAY_SUPPRESS));
|
|
}
|
|
|
|
// temporarily supresses use of given rays for nRounds time
|
|
void SuppressUseOfRays(int nRayMask, int nRounds)
|
|
{
|
|
// int invertedMask = ~nRayMask;
|
|
SetLocalInt(OBJECT_SELF, BEHOLDER_RAY_SUPPRESS, nRayMask | GetLocalInt(OBJECT_SELF, BEHOLDER_RAY_SUPPRESS));
|
|
DelayCommand(6.0 * nRounds + 3.0, TurnOffSuppress(nRayMask));
|
|
}
|
|
|
|
|
|
// get location on ground that rocks can be thrown from for oTarget
|
|
location GetBeholderStartThrowRocksLocation(object oTarget)
|
|
{
|
|
vector vSelf = GetPosition(OBJECT_SELF);
|
|
vector vCurTarget = GetPosition(oTarget);
|
|
float fAngle2 = VectorToAngle(vCurTarget - vSelf);
|
|
float fAngleDifference = GetFacing(OBJECT_SELF) - VectorToAngle(vCurTarget - vSelf);
|
|
location lRockThrowStart;
|
|
// check if in front or back quadrant
|
|
if (fabs(sin(fAngleDifference)) <= 0.5)
|
|
{
|
|
// check if behind
|
|
if (cos(fAngleDifference) < 0.0)
|
|
{
|
|
switch (d2())
|
|
{
|
|
case 1:
|
|
lRockThrowStart = GetStepRightLocation(OBJECT_SELF);
|
|
break;
|
|
default:
|
|
lRockThrowStart = GetStepLeftLocation(OBJECT_SELF);
|
|
break;
|
|
}
|
|
}
|
|
else if (GetDistanceToObject(oTarget) < 7.5)
|
|
{
|
|
switch (d2())
|
|
{
|
|
case 1:
|
|
lRockThrowStart = GetFlankingRightLocation(OBJECT_SELF);
|
|
break;
|
|
default:
|
|
lRockThrowStart = GetFlankingLeftLocation(OBJECT_SELF);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (d4())
|
|
{
|
|
case 1:
|
|
lRockThrowStart = GetFlankingRightLocation(OBJECT_SELF);
|
|
break;
|
|
case 2:
|
|
lRockThrowStart = GetStepRightLocation(OBJECT_SELF);
|
|
break;
|
|
case 3:
|
|
lRockThrowStart = GetFlankingLeftLocation(OBJECT_SELF);
|
|
break;
|
|
default:
|
|
lRockThrowStart = GetStepLeftLocation(OBJECT_SELF);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lRockThrowStart = GetBehindLocation(OBJECT_SELF);
|
|
}
|
|
|
|
return lRockThrowStart;
|
|
}
|
|
|
|
|
|
|
|
const int OVERRIDE_ATTACK_RESULT_DEFAULT = 0;
|
|
const int OVERRIDE_ATTACK_RESULT_HIT_SUCCESSFUL = 1;
|
|
const int OVERRIDE_ATTACK_RESULT_PARRIED = 2;
|
|
const int OVERRIDE_ATTACK_RESULT_CRITICAL_HIT = 3;
|
|
const int OVERRIDE_ATTACK_RESULT_MISS = 4;
|
|
|
|
|
|
// throw rocks using telekinesis, nRockWeight * 25 lb rock is thrown
|
|
void DoTKThrowRock(object oSource, object oTarget, int nRockWeight)
|
|
{
|
|
int attackMods = GetBaseAttackBonus(OBJECT_SELF) + GetAbilityModifier(ABILITY_CHARISMA);
|
|
int attackRoll = d20();
|
|
int hitResult;
|
|
if (attackRoll == 1)
|
|
{
|
|
hitResult = OVERRIDE_ATTACK_RESULT_MISS;
|
|
}
|
|
else if (attackRoll == 20)
|
|
{
|
|
hitResult = OVERRIDE_ATTACK_RESULT_CRITICAL_HIT;
|
|
}
|
|
else if ((attackRoll + attackMods) >= GetAC(oTarget))
|
|
{
|
|
hitResult = OVERRIDE_ATTACK_RESULT_HIT_SUCCESSFUL;
|
|
}
|
|
else
|
|
{
|
|
hitResult = OVERRIDE_ATTACK_RESULT_MISS;
|
|
}
|
|
if (hitResult == OVERRIDE_ATTACK_RESULT_CRITICAL_HIT)
|
|
{
|
|
// check immunity and reroll attack
|
|
if (GetIsImmune(oTarget, IMMUNITY_TYPE_CRITICAL_HIT) ||
|
|
((d20() + attackMods) < GetAC(oTarget)))
|
|
{
|
|
// critical hit missed
|
|
hitResult = OVERRIDE_ATTACK_RESULT_HIT_SUCCESSFUL;
|
|
}
|
|
}
|
|
// Jug_Debug(GetName(OBJECT_SELF) + " throw rock result " + IntToString(hitResult) + " total is " + IntToString(nRockWeight));
|
|
|
|
location lRockThrowStart = GetLocation(oSource);
|
|
location lTargetLoc = GetLocation(oTarget);
|
|
vector positionRandom = GetPositionFromLocation(lRockThrowStart);
|
|
positionRandom.x += IntToFloat(Random(100)) / 1000 - 0.1;
|
|
positionRandom.y += IntToFloat(Random(100)) / 1000 - 0.1;
|
|
location lSourceLocRandom = Location(GetArea(oTarget),positionRandom,0.0f);
|
|
|
|
AssignCommand( oSource,
|
|
ActionCastFakeSpellAtObject(SPELL_TRAP_SHURIKEN,
|
|
oTarget,
|
|
PROJECTILE_PATH_TYPE_HOMING));
|
|
|
|
// SpawnItemProjectile(oSource, oTarget, lSourceLocRandom, lTargetLoc,
|
|
// BASE_ITEM_SLING, PROJECTILE_PATH_TYPE_HOMING, hitResult, 0);
|
|
if (hitResult != OVERRIDE_ATTACK_RESULT_MISS)
|
|
{
|
|
int damageAmount = d4(nRockWeight);
|
|
if (hitResult == OVERRIDE_ATTACK_RESULT_CRITICAL_HIT)
|
|
{
|
|
damageAmount *= 2;
|
|
}
|
|
effect eBlunt = EffectDamage(damageAmount, DAMAGE_TYPE_BLUDGEONING);
|
|
float fTravelTime = 1.5; //GetProjectileTravelTime(lSourceLocRandom, lTargetLoc, PROJECTILE_PATH_TYPE_HOMING);
|
|
DelayCommand(fTravelTime, ApplyEffectToObject(DURATION_TYPE_INSTANT, eBlunt, oTarget) );
|
|
}
|
|
}
|
|
|
|
|
|
// throw rocks using telekinesis, sizes nTotalWeight * 25 lb rocks
|
|
void DoTKThrowRocks(object oSource, object oTarget, int nTotalWeight)
|
|
{
|
|
int nTotalWeight = GetBeholderEyeStalkSpellCasterLevel();
|
|
int nCurRock = d4();
|
|
while (nCurRock > 0)
|
|
{
|
|
int nCurRockWeight = nTotalWeight / nCurRock;
|
|
|
|
float fDelay = GetRandomDelay(0.75, 1.25);
|
|
DelayCommand(fDelay, DoTKThrowRock(oSource, oTarget, nCurRockWeight));
|
|
|
|
nTotalWeight -= nCurRockWeight;
|
|
nCurRock --;
|
|
}
|
|
}
|
|
|
|
|
|
// do beholder ray attack, both beam and effects of ray are done
|
|
void DoBeholderRayAttack(int nRay, object oTarget, int bCritical)
|
|
{
|
|
int nSave, bSave;
|
|
int nSaveDC = GetBeholderEyeStalkSpellDC();
|
|
int nDamage;
|
|
effect e1, eLink, eVis, eDur /*, eBeam */;
|
|
|
|
// BEHOLDER_RAY_USER add custom ray save types
|
|
switch (nRay)
|
|
{
|
|
case BEHOLDER_RAY_DEATH:
|
|
case BEHOLDER_RAY_PETRI:
|
|
case BEHOLDER_RAY_DISINTEGRATE:
|
|
case BEHOLDER_RAY_PARALYSIS:
|
|
nSave = SAVING_THROW_FORT;
|
|
break;
|
|
|
|
case BEHOLDER_RAY_DISPEL:
|
|
case BEHOLDER_RAY_SCORCHING:
|
|
case BEHOLDER_RAY_EXHAUSTION:
|
|
case BEHOLDER_RAY_TK_THROW_ROCKS:
|
|
case BEHOLDER_RAY_FROST:
|
|
nSave = -1; // no save
|
|
break;
|
|
|
|
default:
|
|
nSave = SAVING_THROW_WILL;
|
|
break;
|
|
}
|
|
|
|
if ((nRay == BEHOLDER_RAY_CHARM_PER) && !GetIsHumanoid(GetRacialType(oTarget)))
|
|
{
|
|
bSave = SAVING_THROW_CHECK_IMMUNE;
|
|
}
|
|
else if ((nRay == BEHOLDER_RAY_DAZE) && (!GetIsHumanoid(GetRacialType(oTarget)) || (GetHitDice(oTarget) >= 5)))
|
|
{
|
|
bSave = SAVING_THROW_CHECK_IMMUNE;
|
|
}
|
|
else if ((nRay == BEHOLDER_RAY_TK_THRUST) && (GetAdjustedCreatureSize(oTarget) > CREATURE_SIZE_MEDIUM))
|
|
{
|
|
// can't move more than 325 pounds
|
|
bSave = SAVING_THROW_CHECK_IMMUNE;
|
|
}
|
|
else if ((nRay == BEHOLDER_RAY_CAUSE_FEAR) && (GetHitDice(oTarget) >= 5))
|
|
{
|
|
bSave = SAVING_THROW_CHECK_IMMUNE;
|
|
}
|
|
else
|
|
{
|
|
if (nSave == SAVING_THROW_WILL)
|
|
{
|
|
bSave = WillSave(oTarget, nSaveDC, SAVING_THROW_TYPE_NONE);
|
|
}
|
|
else if (nSave == SAVING_THROW_FORT)
|
|
{
|
|
bSave = FortitudeSave(oTarget, nSaveDC, SAVING_THROW_TYPE_NONE);
|
|
}
|
|
}
|
|
|
|
switch (nRay)
|
|
{
|
|
case BEHOLDER_RAY_DEATH:
|
|
if (bSave == SAVING_THROW_CHECK_FAILED)
|
|
{
|
|
e1 = EffectDeath(TRUE);
|
|
eVis = EffectVisualEffect(VFX_IMP_DEATH);
|
|
eLink = EffectLinkEffects(e1,eVis);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT,eLink,oTarget);
|
|
}
|
|
else
|
|
{
|
|
nDamage = d6(3) + GetBeholderEyeStalkSpellCasterLevel();
|
|
if (bCritical)
|
|
{
|
|
nDamage *= 2;
|
|
}
|
|
e1 = EffectDamage(nDamage, DAMAGE_TYPE_NEGATIVE);
|
|
eVis = EffectVisualEffect(VFX_IMP_NEGATIVE_ENERGY);
|
|
eLink = EffectLinkEffects(e1,eVis);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT,eLink,oTarget);
|
|
}
|
|
break;
|
|
|
|
case BEHOLDER_RAY_TK_THRUST:
|
|
// no matching spell
|
|
if (bSave == SAVING_THROW_CHECK_FAILED)
|
|
{
|
|
e1 = EffectKnockdown();
|
|
eVis = EffectVisualEffect(VFX_IMP_STUN);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT,eVis,oTarget);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY,e1,oTarget,6.0f);
|
|
}
|
|
break;
|
|
|
|
// Petrify for one round per SaveDC
|
|
case BEHOLDER_RAY_PETRI:
|
|
if (bSave == SAVING_THROW_CHECK_FAILED)
|
|
{
|
|
eVis = EffectVisualEffect(VFX_IMP_POLYMORPH);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT,eVis,oTarget);
|
|
DoBeholderPetrify(nSaveDC,OBJECT_SELF, oTarget, SPELL_FLESH_TO_STONE);
|
|
}
|
|
break;
|
|
|
|
case BEHOLDER_RAY_CHARM_MON:
|
|
if (bSave == SAVING_THROW_CHECK_FAILED)
|
|
{
|
|
e1 = EffectCharmed();
|
|
eVis = EffectVisualEffect(VFX_IMP_CHARM);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT,eVis,oTarget);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY,e1,oTarget,24.0f);
|
|
}
|
|
break;
|
|
|
|
case BEHOLDER_RAY_SLOW:
|
|
if (bSave == SAVING_THROW_CHECK_FAILED)
|
|
{
|
|
e1 = EffectSlow();
|
|
eVis = EffectVisualEffect(VFX_IMP_SLOW);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT,eVis,oTarget);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY,e1,oTarget,RoundsToSeconds(6));
|
|
}
|
|
break;
|
|
|
|
case BEHOLDER_RAY_WOUND:
|
|
case BEHOLDER_RAY_CRITICAL_WOUND:
|
|
nDamage = GetBeholderEyeStalkSpellCasterLevel();
|
|
if (nRay == BEHOLDER_RAY_WOUND)
|
|
{
|
|
if (nDamage > 10)
|
|
{
|
|
nDamage = 10;
|
|
}
|
|
nDamage += d8(2);
|
|
}
|
|
else
|
|
{
|
|
if (nDamage > 20)
|
|
{
|
|
nDamage = 20;
|
|
}
|
|
nDamage += d8(4);
|
|
}
|
|
if (bCritical)
|
|
{
|
|
nDamage *= 2;
|
|
}
|
|
if (bSave != SAVING_THROW_CHECK_FAILED)
|
|
{
|
|
// cause serious - save is 1/2
|
|
nDamage /= 2;
|
|
}
|
|
e1 = EffectDamage(nDamage, DAMAGE_TYPE_NEGATIVE);
|
|
eVis = EffectVisualEffect(VFX_COM_BLOOD_REG_RED);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT,eVis,oTarget);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT,e1,oTarget);
|
|
break;
|
|
|
|
case BEHOLDER_RAY_FEAR:
|
|
case BEHOLDER_RAY_CAUSE_FEAR:
|
|
if (bSave == SAVING_THROW_CHECK_FAILED)
|
|
{
|
|
e1 = EffectFrightened();
|
|
eVis = EffectVisualEffect(VFX_IMP_FEAR_S);
|
|
eDur = EffectVisualEffect(VFX_DUR_MIND_AFFECTING_FEAR);
|
|
e1 = EffectLinkEffects(eDur,e1);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT,eVis,oTarget);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY,e1,oTarget,RoundsToSeconds(1+d4()));
|
|
}
|
|
break;
|
|
|
|
case BEHOLDER_RAY_CHARM_PER:
|
|
if (bSave == SAVING_THROW_CHECK_FAILED)
|
|
{
|
|
e1 = EffectCharmed();
|
|
eVis = EffectVisualEffect(VFX_IMP_CHARM);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT,eVis,oTarget);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY,e1,oTarget,24.0f);
|
|
}
|
|
break;
|
|
|
|
case BEHOLDER_RAY_SLEEP:
|
|
if (bSave == SAVING_THROW_CHECK_FAILED)
|
|
{
|
|
e1 = EffectSleep();
|
|
eDur = EffectVisualEffect(VFX_DUR_CESSATE_NEGATIVE);
|
|
eDur = EffectLinkEffects(eDur, EffectVisualEffect(VFX_DUR_MIND_AFFECTING_NEGATIVE));
|
|
eVis = EffectVisualEffect(VFX_IMP_SLEEP);
|
|
eDur = EffectLinkEffects(eVis, eDur);
|
|
|
|
//ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oLowest);
|
|
if (GetIsImmune(oTarget, IMMUNITY_TYPE_SLEEP, OBJECT_SELF) == FALSE)
|
|
{
|
|
e1 = EffectLinkEffects(e1, eDur);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, e1, oTarget, RoundsToSeconds(1+d4()));
|
|
}
|
|
else
|
|
// * even though I am immune apply just the sleep effect for the immunity message
|
|
{
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, e1, oTarget, RoundsToSeconds(1+d4()));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case BEHOLDER_RAY_DISINTEGRATE:
|
|
if (bSave == SAVING_THROW_CHECK_FAILED)
|
|
{
|
|
string nName = GetName(oTarget);
|
|
if (nName == "Mordenkainen's Sword")
|
|
{
|
|
effect eKill = EffectDamage(GetCurrentHitPoints(oTarget) + 1,DAMAGE_TYPE_MAGICAL,DAMAGE_POWER_NORMAL);
|
|
eVis = EffectVisualEffect(VFX_IMP_DEATH);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, eKill, oTarget);
|
|
break; // out of case
|
|
}
|
|
nDamage = d6(GetBeholderEyeStalkSpellCasterLevel());
|
|
}
|
|
else
|
|
{
|
|
nDamage = d6(5);
|
|
}
|
|
if (bCritical)
|
|
{
|
|
nDamage *= 2;
|
|
}
|
|
{
|
|
e1 = EffectDamage(nDamage, DAMAGE_TYPE_MAGICAL);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, e1, oTarget);
|
|
if (GetCurrentHitPoints(oTarget) <= 0)
|
|
{
|
|
// If they are at or below 0 hit points, disintegrate them!
|
|
eVis = EffectVisualEffect(VFX_IMP_DEATH);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case BEHOLDER_RAY_DISPEL:
|
|
{
|
|
eVis = EffectVisualEffect(VFX_IMP_BREACH);
|
|
effect eImpact = EffectVisualEffect(VFX_FNF_DISPEL);
|
|
int nCasterLevel = GetBeholderEyeStalkSpellCasterLevel();
|
|
//--------------------------------------------------------------------------
|
|
// Dispel Magic is capped at caster level 10
|
|
//--------------------------------------------------------------------------
|
|
if (nCasterLevel > 10)
|
|
{
|
|
nCasterLevel = 10;
|
|
}
|
|
// Jug_Debug(GetName(OBJECT_SELF) + " doing dispel " + IntToString(nCasterLevel));
|
|
// spellsDispelMagic(oTarget, nCasterLevel, eVis, eImpact);
|
|
|
|
e1 = EffectDispelMagicAll(nCasterLevel);
|
|
// ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, e1, oTarget);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, eImpact, oTarget);
|
|
}
|
|
break;
|
|
|
|
case BEHOLDER_RAY_SCORCHING:
|
|
nDamage = d6(4);
|
|
if (bCritical)
|
|
{
|
|
nDamage *= 2;
|
|
}
|
|
e1 = EffectDamage(nDamage, DAMAGE_TYPE_FIRE);
|
|
eVis = EffectVisualEffect(VFX_IMP_FLAME_S);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT,eVis,oTarget);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT,e1,oTarget);
|
|
break;
|
|
|
|
case BEHOLDER_RAY_PARALYSIS:
|
|
if (bSave == SAVING_THROW_CHECK_FAILED)
|
|
{
|
|
int nDuration = GetBeholderEyeStalkSpellCasterLevel();
|
|
nDuration = GetScaledDuration(nDuration, oTarget);
|
|
e1 = EffectParalyze();
|
|
eVis = EffectVisualEffect(82);
|
|
eDur = EffectVisualEffect(VFX_DUR_CESSATE_NEGATIVE);
|
|
effect eDur2 = EffectVisualEffect(VFX_DUR_PARALYZED);
|
|
effect eDur3 = EffectVisualEffect(VFX_DUR_PARALYZE_HOLD);
|
|
|
|
eLink = EffectLinkEffects(eDur2, eDur);
|
|
eLink = EffectLinkEffects(eLink, e1);
|
|
eLink = EffectLinkEffects(eLink, eVis);
|
|
eLink = EffectLinkEffects(eLink, eDur3);
|
|
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, RoundsToSeconds(nDuration));
|
|
}
|
|
break;
|
|
|
|
case BEHOLDER_RAY_EXHAUSTION:
|
|
if (bSave == SAVING_THROW_CHECK_FAILED && !GetHasSpellEffect(BEHOLDER_EXHAUSTION_SPELLID , oTarget))
|
|
{
|
|
effect eStrPenalty = EffectAbilityDecrease(ABILITY_STRENGTH, 6);
|
|
effect eDexPenalty = EffectAbilityDecrease(ABILITY_DEXTERITY, 6);
|
|
effect eMovePenalty = EffectMovementSpeedDecrease(50); // 50% decrease
|
|
|
|
eLink = EffectLinkEffects (eStrPenalty, eDexPenalty);
|
|
eLink = EffectLinkEffects(eLink, eMovePenalty);
|
|
eLink = ExtraordinaryEffect(eLink);
|
|
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY,eLink,oTarget,TurnsToSeconds(GetBeholderEyeStalkSpellCasterLevel()));
|
|
}
|
|
break;
|
|
|
|
case BEHOLDER_RAY_FROST:
|
|
nDamage = d3();
|
|
if (bCritical)
|
|
{
|
|
nDamage *= 2;
|
|
}
|
|
e1 = EffectDamage(nDamage, DAMAGE_TYPE_COLD);
|
|
eVis = EffectVisualEffect(VFX_IMP_FROST_S);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT,eVis,oTarget);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT,e1,oTarget);
|
|
// eyeball can't use daze or frost for next 2 rounds
|
|
SuppressUseOfRays(BEHOLDER_RAY_FROST | BEHOLDER_RAY_DAZE, 2);
|
|
break;
|
|
|
|
case BEHOLDER_RAY_DAZE:
|
|
if (bSave == SAVING_THROW_CHECK_FAILED)
|
|
{
|
|
effect eMind = EffectVisualEffect(VFX_DUR_MIND_AFFECTING_NEGATIVE);
|
|
e1 = EffectDazed();
|
|
effect eDur = EffectVisualEffect(VFX_DUR_CESSATE_NEGATIVE);
|
|
|
|
eLink = EffectLinkEffects(eMind, e1);
|
|
eLink = EffectLinkEffects(eLink, eDur);
|
|
|
|
eVis = EffectVisualEffect(VFX_IMP_DAZED_S);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT,eVis,oTarget);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY,eLink,oTarget,6.0f);
|
|
}
|
|
// eyeball can't use daze or frost for next 2 rounds
|
|
SuppressUseOfRays(BEHOLDER_RAY_FROST | BEHOLDER_RAY_DAZE, 2);
|
|
break;
|
|
|
|
// BEHOLDER_RAY_USER add custom ray effects here
|
|
}
|
|
}
|
|
|
|
|
|
// open antimagic eye at target
|
|
void OpenAntiMagicEye (object oTarget)
|
|
{
|
|
ClearAllActions();
|
|
|
|
int bInstant = GetLocalInt(OBJECT_SELF, BEHOLDER_MAIN_EYE_TYPE) != BEHOLDER_MAIN_EYE_STUN;
|
|
int nSpell = bInstant ? BEHOLDER_EYE_SPELLID : SPELLABILITY_GAZE_STUNNED;
|
|
if (GetObjectSeen(oTarget))
|
|
{
|
|
ActionCastSpellAtObject(nSpell , oTarget, METAMAGIC_ANY, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, bInstant);
|
|
}
|
|
else
|
|
{
|
|
ActionCastSpellAtLocation(nSpell, GetLocation(oTarget), METAMAGIC_ANY, TRUE, PROJECTILE_PATH_TYPE_DEFAULT, bInstant);
|
|
}
|
|
}
|
|
|
|
|
|
// being a badass beholder, we close our antimagic eye only to attack with our eye rays
|
|
// and then reopen it...
|
|
void CloseAntiMagicEye(object oTarget)
|
|
{
|
|
RemoveSpellEffects(BEHOLDER_EYE_SPELLID, OBJECT_SELF, oTarget);
|
|
}
|
|
|
|
|
|
const float BEHOLDER_BEAM_EFFECT_TIME = 0.33;
|
|
|
|
void DoBeamEffect(int nRay, object oTarget, int bMiss)
|
|
{
|
|
effect eBeam;
|
|
|
|
switch (nRay)
|
|
{
|
|
case BEHOLDER_RAY_DEATH:
|
|
SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, SPELL_FINGER_OF_DEATH, TRUE));
|
|
eBeam = EffectBeam(VFX_BEAM_BLACK, OBJECT_SELF, BODY_NODE_MONSTER_0, bMiss);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eBeam, oTarget, BEHOLDER_BEAM_EFFECT_TIME);
|
|
break;
|
|
|
|
case BEHOLDER_RAY_TK_THRUST:
|
|
// no matching spell
|
|
SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, BEHOLDER_TK_SPELLID, TRUE));
|
|
eBeam = EffectBeam(VFX_BEAM_SILENT_ODD, OBJECT_SELF, BODY_NODE_MONSTER_1, bMiss);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eBeam, oTarget, BEHOLDER_BEAM_EFFECT_TIME);
|
|
break;
|
|
|
|
case BEHOLDER_RAY_TK_THROW_ROCKS:
|
|
{
|
|
location lRockThrowTest = GetBeholderStartThrowRocksLocation(oTarget);
|
|
object oIpoint = CreateObject(OBJECT_TYPE_PLACEABLE, "plc_weathmark", lRockThrowTest);
|
|
location lRockThrowStart = GetLocation(oIpoint);
|
|
|
|
SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, BEHOLDER_TK_SPELLID, TRUE));
|
|
eBeam = EffectBeam(VFX_BEAM_SILENT_ODD, OBJECT_SELF, BODY_NODE_MONSTER_1, FALSE);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eBeam, oIpoint, 1.0 /*BEHOLDER_BEAM_EFFECT_TIME*/);
|
|
|
|
DestroyObject(oIpoint, 1.0);
|
|
|
|
DoTKThrowRocks(oIpoint, oTarget, GetBeholderEyeStalkSpellCasterLevel());
|
|
|
|
break;
|
|
}
|
|
|
|
// Petrify for one round per SaveDC
|
|
case BEHOLDER_RAY_PETRI:
|
|
SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, SPELL_FLESH_TO_STONE, TRUE));
|
|
eBeam = EffectBeam(VFX_BEAM_SILENT_HOLY, OBJECT_SELF, BODY_NODE_MONSTER_2, bMiss);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eBeam, oTarget, BEHOLDER_BEAM_EFFECT_TIME);
|
|
break;
|
|
|
|
case BEHOLDER_RAY_CHARM_MON:
|
|
SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, SPELL_CHARM_MONSTER, TRUE));
|
|
eBeam = EffectBeam(VFX_BEAM_SILENT_MIND, OBJECT_SELF, BODY_NODE_MONSTER_3, bMiss);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eBeam, oTarget, BEHOLDER_BEAM_EFFECT_TIME);
|
|
break;
|
|
|
|
case BEHOLDER_RAY_SLOW:
|
|
SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, SPELL_SLOW, TRUE));
|
|
eBeam = EffectBeam(VFX_BEAM_SILENT_COLD, OBJECT_SELF, BODY_NODE_MONSTER_4, bMiss);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eBeam, oTarget, BEHOLDER_BEAM_EFFECT_TIME);
|
|
break;
|
|
|
|
case BEHOLDER_RAY_WOUND:
|
|
case BEHOLDER_RAY_CRITICAL_WOUND:
|
|
SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, SPELL_INFLICT_MODERATE_WOUNDS, TRUE));
|
|
eBeam = EffectBeam(VFX_BEAM_SILENT_EVIL, OBJECT_SELF, BODY_NODE_MONSTER_3, bMiss);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eBeam, oTarget, BEHOLDER_BEAM_EFFECT_TIME);
|
|
break;
|
|
|
|
case BEHOLDER_RAY_FEAR:
|
|
case BEHOLDER_RAY_CAUSE_FEAR:
|
|
SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, SPELL_FEAR, TRUE));
|
|
eBeam = EffectBeam(VFX_BEAM_SILENT_MIND, OBJECT_SELF, BODY_NODE_MONSTER_0, bMiss);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eBeam, oTarget, BEHOLDER_BEAM_EFFECT_TIME);
|
|
break;
|
|
|
|
case BEHOLDER_RAY_CHARM_PER:
|
|
SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, SPELL_CHARM_PERSON, TRUE));
|
|
eBeam = EffectBeam(VFX_BEAM_SILENT_MIND, OBJECT_SELF, BODY_NODE_MONSTER_4, bMiss);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eBeam, oTarget, BEHOLDER_BEAM_EFFECT_TIME);
|
|
break;
|
|
|
|
case BEHOLDER_RAY_SLEEP:
|
|
SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, SPELL_SLEEP, TRUE));
|
|
eBeam = EffectBeam(VFX_BEAM_SILENT_MIND, OBJECT_SELF, BODY_NODE_MONSTER_1, bMiss);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eBeam, oTarget, BEHOLDER_BEAM_EFFECT_TIME);
|
|
break;
|
|
|
|
case BEHOLDER_RAY_DISINTEGRATE:
|
|
SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, BEHOLDER_DISINTEGRATE_SPELLID, TRUE));
|
|
eBeam = EffectBeam(VFX_BEAM_DISINTEGRATE, OBJECT_SELF, BODY_NODE_MONSTER_0, bMiss);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eBeam, oTarget, BEHOLDER_BEAM_EFFECT_TIME);
|
|
break;
|
|
|
|
case BEHOLDER_RAY_DISPEL:
|
|
SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, SPELL_DISPEL_MAGIC, TRUE));
|
|
eBeam = EffectBeam(VFX_BEAM_SILENT_ODD, OBJECT_SELF, BODY_NODE_MONSTER_0, bMiss);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY,eBeam,oTarget,1.0);
|
|
break;
|
|
|
|
case BEHOLDER_RAY_SCORCHING:
|
|
SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, 1056, TRUE));
|
|
eBeam = EffectBeam(VFX_BEAM_FIRE, OBJECT_SELF, BODY_NODE_MONSTER_2, bMiss);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY,eBeam,oTarget,1.0);
|
|
break;
|
|
|
|
case BEHOLDER_RAY_PARALYSIS:
|
|
SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, SPELL_HOLD_PERSON, TRUE));
|
|
eBeam = EffectBeam(VFX_BEAM_SILENT_MIND, OBJECT_SELF, BODY_NODE_MONSTER_4, bMiss);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY,eBeam,oTarget,1.0);
|
|
break;
|
|
|
|
case BEHOLDER_RAY_EXHAUSTION:
|
|
SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, BEHOLDER_EXHAUSTION_SPELLID, TRUE));
|
|
eBeam = EffectBeam(VFX_BEAM_SILENT_EVIL, OBJECT_SELF, BODY_NODE_MONSTER_4, bMiss);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY,eBeam,oTarget,1.0);
|
|
break;
|
|
|
|
case BEHOLDER_RAY_FROST:
|
|
SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, SPELL_RAY_OF_FROST, TRUE));
|
|
eBeam = EffectBeam(VFX_BEAM_SILENT_COLD, OBJECT_SELF, BODY_NODE_MONSTER_1, bMiss);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY,eBeam,oTarget,1.0);
|
|
break;
|
|
|
|
case BEHOLDER_RAY_DAZE:
|
|
SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, SPELL_DAZE, TRUE));
|
|
eBeam = EffectBeam(VFX_BEAM_SILENT_MIND, OBJECT_SELF, BODY_NODE_MONSTER_2, bMiss);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY,eBeam,oTarget,1.0);
|
|
break;
|
|
// BEHOLDER_RAY_USER add custom ray effects here
|
|
}
|
|
}
|
|
|
|
|
|
// try to fire beam at target, checks for ranged touch hit or miss
|
|
void DelayedBehFireBeam(int nRay, object oTarget)
|
|
{
|
|
if (!GetIsDead(OBJECT_SELF) && !GetIsDisabled(OBJECT_SELF))
|
|
{
|
|
int bHit;
|
|
if (nRay == BEHOLDER_RAY_TK_THROW_ROCKS)
|
|
{
|
|
// no touch attack needed
|
|
bHit = TOUCH_ATTACK_RESULT_HIT;
|
|
}
|
|
else
|
|
{
|
|
bHit = TouchAttackRanged(oTarget, FALSE);
|
|
}
|
|
|
|
DoBeamEffect(nRay, oTarget, bHit == TOUCH_ATTACK_RESULT_MISS);
|
|
|
|
if (bHit != TOUCH_ATTACK_RESULT_MISS)
|
|
{
|
|
// Jug_Debug(GetName(OBJECT_SELF) + " hit ray " + IntToHexString(nRay) + " on " + GetName(oTarget));
|
|
DelayCommand(0.15, DoBeholderRayAttack(nRay, oTarget, (bHit == TOUCH_ATTACK_RESULT_CRITICAL) &&
|
|
!GetIsImmune(oTarget, IMMUNITY_TYPE_CRITICAL_HIT)));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
float fCurRayDelay;
|
|
|
|
// fire beam at target, delay is done so all eyestalk rays don't fire at once
|
|
void BehDoFireBeam(int nRay, object oTarget)
|
|
{
|
|
// Jug_Debug(GetName(OBJECT_SELF) + " fire ray " + IntToHexString(nRay) + " on " + GetName(oTarget));
|
|
DelayCommand(fCurRayDelay, DelayedBehFireBeam(nRay, oTarget));
|
|
fCurRayDelay += 0.35;
|
|
} |