Amon_PRC8/_module/nss/x2_ai_behold.nss
Jaysyn904 c5cffc37af Initial Commit
Initial Commit [v1.01]
2025-04-03 19:00:46 -04:00

447 lines
12 KiB
Plaintext

//::///////////////////////////////////////////////
//:: Custom Beholder AI
//:: x2_ai_behold
//:: Copyright (c) 2003 Bioware Corp.
//:://////////////////////////////////////////////
/*
This is the Hordes of the Underdark campaign
Mini AI run on the beholders.
It does not use any spells assigned to the
beholder, if you want to make a custom beholder
you need to deactivate this AI by removing the
approriate variable from your beholder creature
in the toolset
Short overview:
Beholder will always use its eyes, unless
a) If threatened in melee, it will try to move away or float away
to the nearest waypoint tagged X2_WP_BEHOLDER_TUNNEL
b) If threatened in melee and below 1/5 hp it will always try to float
b) If affected by antimagic itself, it melee attack
c) If target is affected by petrification, it will melee attack
Logic for eye ray usage are in the appropriate spellscripts
setting X2_BEHOLDER_AI_NOJUMP to 1 will prevent the beholders from
jumping, even if there are jump points nearby
*/
//:://////////////////////////////////////////////
//:: Created By: Georg Zoeller
//:: Created On: 2003-08-21
//:://////////////////////////////////////////////
#include "x2_inc_behai"
void BehClearFleeState()
{
DeleteLocalInt(OBJECT_SELF,"X2_BEHOLDER_AI_FLEEING");
}
void BehClearState()
{
ClearAllActions();
}
// leviate (jump) either closer to or away from oTargetFrom
int TryToLevitateAway(object oTargetFrom, int bCloseIn = FALSE)
{
if (GetLocalInt(OBJECT_SELF,"X2_BEHOLDER_AI_NOJUMP"))
{
return FALSE;
}
object oExit;
if (bCloseIn)
{
oExit = GetNearestObjectByTag("X2_WP_BEHOLDER_TUNNEL",oTargetFrom);
}
else
{
oExit = GetNearestObjectByTag("X2_WP_BEHOLDER_TUNNEL");
}
if (!GetIsObjectValid(oExit))
{
return FALSE;
}
float fDist = GetDistanceBetween(oExit,oTargetFrom);
int bJump;
if (bCloseIn)
{
if ((fDist >= 8.0f) && (fDist <= 20.0f))
{
bJump = TRUE;
}
}
else
{
if ((fDist >= 10.0f) && (fDist <= 40.0f))
{
bJump = TRUE;
}
}
if (!bJump)
{
if (bCloseIn)
{
oExit = GetNearestObjectByTag("X2_WP_BEHOLDER_TUNNEL",oTargetFrom,2);
}
else
{
oExit = GetNearestObjectByTag("X2_WP_BEHOLDER_TUNNEL",OBJECT_SELF,2);
}
if (!GetIsObjectValid(oExit))
{
return FALSE;
}
else
{
fDist = GetDistanceBetween(oExit,oTargetFrom);
if (bCloseIn)
{
if ((fDist >= 6.0f) && (fDist <= 15.0f))
{
bJump = TRUE;
}
}
else
{
if ((fDist >= 8.0f) && (fDist <= 50.0f))
{
bJump = TRUE;
}
}
}
}
if (bJump)
{
if (!GetIsDead(OBJECT_SELF))
{
int bImmortal = GetImmortal(OBJECT_SELF);
int nAni = GetLocalInt(oExit,"X2_L_BEH_USE_ANI");
if (nAni ==0)
{
nAni = 1;
}
effect eAppear = EffectDisappearAppear(GetLocation(oExit), nAni) ;
eAppear = SupernaturalEffect(eAppear);
object oSelf = OBJECT_SELF;
ApplyEffectToObject(DURATION_TYPE_TEMPORARY,eAppear,oSelf,4.0f);
// make the beholder enter combat again
DelayCommand(4.1f, ActionCastSpellAtObject(736,oTargetFrom,METAMAGIC_ANY,TRUE,0,PROJECTILE_PATH_TYPE_DEFAULT,TRUE));
}
return TRUE;
}
else
{
return FALSE;
}
return TRUE;
}
const int BEHOLDER_TACTIC_ATTACK = 0;
const int BEHOLDER_TACTIC_FLEE = 1;
const int BEHOLDER_TACTIC_LEVITATE_AWAY = 2;
const int BEHOLDER_TACTIC_LEVITATE_CLOSE = 3;
const int BEHOLDER_TACTIC_DO_NOTHING = 5;
// returns AI tactic for this round
int GetBeholderTactic(object oTarget, int bStartOfRound)
{
int nRet = BEHOLDER_TACTIC_ATTACK;
int nCurHP = GetCurrentHitPoints(OBJECT_SELF);
int nMaxHP = GetMaxHitPoints(OBJECT_SELF);
if (GetIsMeleeAttacker(oTarget))
{
// some attack randomization
if (nCurHP < nMaxHP / 3)
{
nRet = BEHOLDER_TACTIC_LEVITATE_AWAY;
}
else if (nCurHP < nMaxHP / 2)
{
nRet = BEHOLDER_TACTIC_FLEE;
}
}
else if ((GetDistanceBetween(oTarget, OBJECT_SELF) > 25.0) && (nCurHP > nMaxHP / 2))
{
nRet = BEHOLDER_TACTIC_LEVITATE_CLOSE;
}
if(GetIsRangedAttacker(oTarget) && (nCurHP < nMaxHP / 3))
{
if (d2()==1)
{
nRet = BEHOLDER_TACTIC_LEVITATE_AWAY;
}
else
{
nRet = BEHOLDER_TACTIC_FLEE;
}
}
if (!bStartOfRound)
{
if (nRet == BEHOLDER_TACTIC_LEVITATE_AWAY)
{
nRet = BEHOLDER_TACTIC_FLEE;
}
else if (nRet == BEHOLDER_TACTIC_LEVITATE_CLOSE)
{
nRet = BEHOLDER_TACTIC_ATTACK;
}
}
return nRet;
}
void main()
{
// Jug_Debug(GetName(OBJECT_SELF) + " run beholder ai script " + IntToString(GetTimeSecond()));
int testBeholderSpellLevel = GetLocalInt(OBJECT_SELF, "beholder_spell_level");
if (!testBeholderSpellLevel)
{
switch (GetAppearanceType(OBJECT_SELF))
{
case APPEARANCE_TYPE_BEHOLDER_MOTHER:
SetLocalInt(OBJECT_SELF, "beholder_spell_level", 20);
SetLocalInt(OBJECT_SELF, "beholder_rays_per_quad", 6);
SetLocalInt(OBJECT_SELF, "beholder_ray_types", BEHOLDER_RAY_TYPES_HIVE_MOTHER);
break;
case 299 /* Beholder_GZhorb */:
SetLocalInt(OBJECT_SELF, "beholder_spell_level", 13);
SetLocalInt(OBJECT_SELF, "beholder_rays_per_quad", 3);
SetLocalInt(OBJECT_SELF, "beholder_ray_types", BEHOLDER_RAY_TYPES_STANDARD);
SetLocalInt(OBJECT_SELF, "beholder_agile_tyrant", TRUE);
break;
case APPEARANCE_TYPE_BEHOLDER_MAGE:
SetLocalInt(OBJECT_SELF, "beholder_spell_level", 13);
SetLocalInt(OBJECT_SELF, "beholder_rays_per_quad", 3);
SetLocalInt(OBJECT_SELF, "beholder_ray_types", BEHOLDER_RAY_TYPES_BEHOLDER_MAGE);
SetLocalInt(OBJECT_SELF, "beholder_main_eye", BEHOLDER_MAIN_EYE_NONE);
break;
/* Gauth :
SetLocalInt(OBJECT_SELF, "beholder_spell_level", 8);
SetLocalInt(OBJECT_SELF, "beholder_rays_per_quad", 2);
SetLocalInt(OBJECT_SELF, "beholder_ray_types", BEHOLDER_RAY_TYPES_GAUTH);
SetLocalInt(OBJECT_SELF, "beholder_main_eye", BEHOLDER_MAIN_EYE_STUN);
break; */
/* Eyeball :
SetLocalInt(OBJECT_SELF, "beholder_spell_level", 1);
SetLocalInt(OBJECT_SELF, "beholder_rays_per_quad", 3);
SetLocalInt(OBJECT_SELF, "beholder_ray_types", BEHOLDER_RAY_TYPES_EYEBALL);
SetLocalInt(OBJECT_SELF, "beholder_main_eye", BEHOLDER_MAIN_EYE_NONE);
SetLocalInt(OBJECT_SELF, "beholder_one_ray", TRUE);
break; */
default /* APPEARANCE_TYPE_BEHOLDER */:
SetLocalInt(OBJECT_SELF, "beholder_spell_level", 13);
SetLocalInt(OBJECT_SELF, "beholder_rays_per_quad", 3);
SetLocalInt(OBJECT_SELF, "beholder_ray_types", BEHOLDER_RAY_TYPES_STANDARD);
break;
}
}
//The following two lines should not be touched
object oIntruder = GetCreatureOverrideAIScriptTarget();
ClearCreatureOverrideAIScriptTarget();
SetCreatureOverrideAIScriptFinished();
/*if (GetLocalString(OBJECT_SELF,"UID") =="")
{
SetLocalString(OBJECT_SELF,"UID",RandomName());
}
string UID = GetLocalString(OBJECT_SELF,"UID");
if (GetLocalInt(GetModule(),"TEST")>0 )
{
if (GetLocalInt(GetModule(),"TEST") ==2)
{
if (!GetLocalInt (OBJECT_SELF,"IN_DEBUG") ==0)
{
return;
}
}
SetLocalInt(GetModule(),"TEST",2);
SpawnScriptDebugger();
SetLocalInt (OBJECT_SELF,"IN_DEBUG",TRUE);
}
*/
// ********************* Start of custom AI script ****************************
// Here you can write your own AI to run in place of DetermineCombatRound.
// The minimalistic approach would be something like
//
// TalentFlee(oTarget); // flee on any combat activity
if(GetAssociateState(NW_ASC_IS_BUSY))
{
return;
}
if (BashDoorCheck(oIntruder)) {return;}
// * BK: stop fighting if something bizarre that shouldn't happen, happens
if (bkEvaluationSanityCheck(oIntruder, GetFollowDistance()))
{
oIntruder = OBJECT_INVALID;
}
if (!GetIsObjectValid(oIntruder))
{
oIntruder = bkAcquireTarget();
}
if (!GetIsObjectValid(oIntruder))
{
oIntruder = GetNearestSeenOrHeardEnemy();
}
if (!GetIsObjectValid(oIntruder))
{
oIntruder = GetLastAttacker();
}
if (GetIsObjectValid(oIntruder) && GetIsDead(oIntruder))
{
DeleteLocalInt(OBJECT_SELF, beholderLastCombatTime);
return;
}
int newCombatRound = CheckIfNewCombatRound();
object oAntiMagicTarget;
int combatTactic = GetBeholderTactic(oIntruder, newCombatRound);
// Jug_Debug(GetName(OBJECT_SELF) + " combat tactic " + IntToString(combatTactic));
if (newCombatRound)
{
if (combatTactic == BEHOLDER_TACTIC_LEVITATE_AWAY)
{
if (TryToLevitateAway(oIntruder))
{
return; // nothing more to do
// combatTactic = BEHOLDER_TACTIC_DO_NOTHING;
}
else
{
combatTactic = BEHOLDER_TACTIC_FLEE;
}
}
else if (combatTactic == BEHOLDER_TACTIC_LEVITATE_CLOSE)
{
if (TryToLevitateAway(oIntruder, TRUE))
{
return; // nothing more to do
// combatTactic = BEHOLDER_TACTIC_DO_NOTHING;
}
else
{
combatTactic = BEHOLDER_TACTIC_ATTACK;
}
}
oAntiMagicTarget = RunBeholderEyeAttacks(oIntruder, FALSE);
}
if (__InCombatRound())
{
return;
}
if (GetIsObjectValid(oAntiMagicTarget))
{
// Jug_Debug(GetName(OBJECT_SELF) + " beholder eye open attack");
// check for stun since that uses a standard action
if (!GetLocalInt(OBJECT_SELF, BEHOLDER_AI_ACTION_IN_ROUND_USED))
{
DelayCommand(0.5, ActionAttack(oAntiMagicTarget, GetDistanceToObject(oAntiMagicTarget) >= 5.0));
/* // unable to back away
{
vector vTarget = GetPosition(oAntiMagicTarget);
vector vSource = GetPosition(OBJECT_SELF);
vector vDirection = vTarget - vSource;
// DelayCommand(0.5, SetFacing(VectorToAngle(vDirection), TRUE));
DelayCommand(0.5, ActionMoveAwayFromObject(oAntiMagicTarget, TRUE, 13.0f));
} */
}
combatTactic = BEHOLDER_TACTIC_DO_NOTHING;
}
else if (GetLocalInt(OBJECT_SELF, BEHOLDER_EYE_IS_OPEN))
{
// Jug_Debug(GetName(OBJECT_SELF) + " beholder eye open, do nothing " + GetName(oIntruder));
combatTactic = BEHOLDER_TACTIC_DO_NOTHING;
}
else if (GetLocalInt(OBJECT_SELF, BEHOLDER_AI_ACTION_IN_ROUND_USED))
{
// Jug_Debug(GetName(OBJECT_SELF) + " beholder action used, do nothing " + GetName(oIntruder));
// ClearAllActions();
combatTactic = BEHOLDER_TACTIC_DO_NOTHING;
}
if (combatTactic == BEHOLDER_TACTIC_DO_NOTHING)
{
__TurnCombatRoundOn(TRUE);
__TurnCombatRoundOn(FALSE);
return;
}
if (combatTactic == BEHOLDER_TACTIC_FLEE)
{
if (GetDistanceToObject(oIntruder) <= 13.0f)
{
// Jug_Debug(GetName(OBJECT_SELF) + " beholder move away from " + GetName(oIntruder));
__TurnCombatRoundOn(TRUE);
ClearAllActions();
ActionMoveAwayFromObject(oIntruder,TRUE, 13.0f);
__TurnCombatRoundOn(FALSE);
return;
}
// make a coward or ambusher?
}
else
{
// unmake a coward or ambusher?
}
// allow default AI to do something
SetLocalInt(OBJECT_SELF, "X2_SPECIAL_COMBAT_AI_SCRIPT_OK", FALSE);
}