Amon_PRC8/_module/nss/x2_inc_beholder.nss

926 lines
34 KiB
Plaintext
Raw Permalink Normal View History

2025-04-03 19:00:46 -04:00
//::///////////////////////////////////////////////
//:: 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;
}