309 lines
10 KiB
Plaintext
309 lines
10 KiB
Plaintext
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Olander's AI
|
|
// nw_c2_default2
|
|
// by Don Anderson
|
|
// dandersonru@msn.com
|
|
//
|
|
// OnPerception
|
|
// Original script by Bioware
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "oai_inc_ai"
|
|
|
|
object NearestAdjoiningDoorTo(object oTarget)
|
|
{
|
|
object oArea = GetArea(oTarget);
|
|
object oDoor;
|
|
int nClosest = 1;
|
|
|
|
// Target may not have arrived yet
|
|
if (!GetIsObjectValid(oArea))
|
|
return OBJECT_INVALID;
|
|
|
|
// Search for nearest adjoining door/trigger
|
|
while (GetIsObjectValid(oDoor = GetNearestObject(OBJECT_TYPE_DOOR|OBJECT_TYPE_TRIGGER, OBJECT_SELF, nClosest))) {
|
|
if (GetArea(GetTransitionTarget(oDoor)) == oArea)
|
|
return oDoor;
|
|
|
|
nClosest++;
|
|
}
|
|
|
|
return OBJECT_INVALID;
|
|
}
|
|
|
|
// Pursue a area changing enemy
|
|
void OAI_Pursue(object oTarget, int nPursueCnt=10)
|
|
{
|
|
object oDoor;
|
|
|
|
//Is target valid
|
|
if (!GetIsObjectValid(oTarget)) return;
|
|
|
|
//Can only do this if no enemy nearby
|
|
if(GetIsObjectValid(oDoor = GetNearestSeenEnemy()))
|
|
{
|
|
if(GetDistanceToObject(oDoor) < 20.0) return;
|
|
}
|
|
|
|
//If target area is available then find the correct trigger/door
|
|
if(GetIsObjectValid(oDoor = NearestAdjoiningDoorTo(oTarget)))
|
|
{
|
|
//Get the distance to the door and delay arrival 1 second for every 8m
|
|
DelayCommand(GetDistanceToObject(oDoor) / 8.0, JumpToObject(GetTransitionTarget(oDoor)));
|
|
return;
|
|
}
|
|
|
|
//Here we haven't found the target yet, try again later
|
|
if(nPursueCnt > 0) DelayCommand(1.5, OAI_Pursue(oTarget, nPursueCnt - 1));
|
|
}
|
|
|
|
void main()
|
|
{
|
|
// dk: 10-Dec-2005 skip if performing special action
|
|
if(GetAIOff()) return;
|
|
|
|
|
|
/******************************************************************************/
|
|
//: CUSTOM PERCEIVED ADDITONS BELOW HERE
|
|
|
|
|
|
//Custom Scripts you Need to Execute
|
|
ExecuteScript("oai_cust_pcv", OBJECT_SELF);
|
|
|
|
//Olander's Pack Animals
|
|
int nPA = GetLocalInt(OBJECT_SELF,"PACKANIMAL");
|
|
if(nPA == 1) return;
|
|
|
|
//Olander's Horses
|
|
int nHorse = GetLocalInt(OBJECT_SELF,"HORSE");
|
|
if(nHorse == 1) return;
|
|
|
|
|
|
//: CUSTOM PERCEIVED ADDITONS ABOVE HERE
|
|
/******************************************************************************/
|
|
|
|
object oPerceived = GetLastPerceived();
|
|
if(!GetIsObjectValid(oPerceived)) return;
|
|
|
|
//Don't want to know about opponents that suddenly go silent
|
|
if(GetLastPerceptionInaudible()) return;
|
|
|
|
//Get some useful details here once
|
|
object oMyTarget = GetLocalObject(OBJECT_SELF,"OAI_ATTACK_TARGET");
|
|
int bEnemy = GetIsEnemy(oPerceived);
|
|
location oLoc;
|
|
|
|
/******************************************************************************/
|
|
//: SPECIAL CAN NOT SEE CONDITIONS
|
|
|
|
// dk: 29-Dec-2005 Chase my enemy if leaving the map
|
|
// Note: Fleeing creatures should be uncommandable and not need to be considered here
|
|
// Um... what about special effects like sleep, confusion etc
|
|
//
|
|
// My Enemy has vanished go have a look around
|
|
if(bEnemy && GetLastPerceptionVanished())
|
|
{
|
|
//Don't care is not a problem to us
|
|
if((oMyTarget != oPerceived && GetIsObjectValid(oMyTarget) && GetObjectSeen(oMyTarget))
|
|
|| (GetBehaviorState(NW_FLAG_BEHAVIOR_OMNIVORE && d4() > 1)) // Omnivores usually give up
|
|
|| GetBehaviorState(NW_FLAG_BEHAVIOR_HERBIVORE))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//They have changed areas, then try to follow them
|
|
if(GetArea(oPerceived) != GetArea(OBJECT_SELF))
|
|
{
|
|
ClearAllActions(TRUE);
|
|
OAI_Pursue(oPerceived);
|
|
}
|
|
|
|
//Our (living) target vanished, go look in last location and retarget someone
|
|
else if(!GetIsDead(oPerceived))
|
|
{
|
|
ClearAllActions();
|
|
ActionMoveToLocation(oLoc = GetLocation(oPerceived), TRUE);
|
|
ActionDoCommand(OAI_DetermineCombatRound());
|
|
|
|
//Denote we are chasing someone for a while (convonces heartbeat to continue chase)
|
|
SetLocalInt(OBJECT_SELF, "OAI_SEARCHING", 20); // search > 10 means run
|
|
SetLocalLocation(OBJECT_SELF, "OAI_SEARCH_LOC", oLoc);
|
|
}
|
|
return;
|
|
}
|
|
|
|
//Did I hear someone I can not see (and I am not currently in combat
|
|
if(bEnemy && GetLastPerceptionHeard()
|
|
&& !GetObjectSeen(oPerceived)
|
|
&& !GetIsInCombat()
|
|
&& !IsInConversation(OBJECT_SELF)
|
|
&& (GetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS)
|
|
|| GetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS_AVIAN)
|
|
|| GetIsEncounterCreature()))
|
|
{
|
|
//Goto location of noise
|
|
ClearAllActions();
|
|
ActionMoveToLocation(oLoc = GetLocation(oPerceived), FALSE);
|
|
ActionDoCommand(OAI_DetermineCombatRound());
|
|
|
|
//Denote we are searching (used in hearbeat, cleared in on perceive enemy)
|
|
SetLocalInt(OBJECT_SELF, "OAI_SEARCHING", 10); // Serch 10 or less means walk there
|
|
SetLocalLocation(OBJECT_SELF, "OAI_SEARCH_LOC", oLoc);
|
|
|
|
//Some nice semi-random sounds
|
|
switch (d4())
|
|
{
|
|
case 1: ActionDoCommand(PlayVoiceChat(VOICE_CHAT_LOOKHERE)); break;
|
|
case 2: ActionDoCommand(PlayVoiceChat(VOICE_CHAT_ENEMIES)); break;
|
|
default: ActionDoCommand(PlayVoiceChat(VOICE_CHAT_SEARCH));
|
|
}
|
|
|
|
//Some nice semi-random animations
|
|
if (d3() > 1) ActionPlayAnimation(ANIMATION_FIREFORGET_HEAD_TURN_LEFT, 0.5);
|
|
if (d3() > 1) ActionPlayAnimation(ANIMATION_FIREFORGET_HEAD_TURN_RIGHT, 0.5);
|
|
if (d2() == 1) ActionPlayAnimation(ANIMATION_FIREFORGET_PAUSE_SCRATCH_HEAD);
|
|
else if (d2() == 1) ActionPlayAnimation(ANIMATION_LOOPING_LOOK_FAR, 0.5, 2.0);
|
|
return;
|
|
}
|
|
|
|
//If I can not see anything here we have no more to consider
|
|
//We could just be here de to a noise stopping
|
|
if(!GetLastPerceptionSeen()) return;
|
|
|
|
/******************************************************************************/
|
|
//: CAN SEE PERCEIVED CONDITIONS
|
|
|
|
|
|
// What do we do if we see dead people
|
|
//
|
|
// We will either ignore it OR
|
|
// stopwhat we are doing and add a whole bunch of actions to go look and recontinue walking
|
|
// waypoints (if we have them). This relys on any serious combat things will clear all these
|
|
// actions and react properly. Also we go into SEARCH mode to ignore other carcasses and stuff.
|
|
if (GetIsDead(oPerceived))
|
|
{
|
|
//Ignore if in combat of any kind
|
|
if(GetAttemptedAttackTarget() != OBJECT_INVALID
|
|
|| GetAttackTarget() != OBJECT_INVALID
|
|
|| GetAttemptedSpellTarget() != OBJECT_INVALID
|
|
|| GetIsObjectValid(GetNearestSeenEnemy()))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//We have noticed a carcass, how often do we ignore it?
|
|
if(GetLocalInt(OBJECT_SELF, "OAI_SEARCHING") // Already searching for something
|
|
|| (!bEnemy && d6() != 1) // Friendly carcass, ignore 5 in 6 chance
|
|
|| (bEnemy && d3() == 1)) // Enemy carcass, ignore 1 in 3 chance
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Otherwise stop what we are doing and go have a look
|
|
ClearAllActions();
|
|
ActionMoveToLocation(oLoc = GetLocation(oPerceived));
|
|
SetLocalInt(OBJECT_SELF, "OAI_SEARCHING", 6); // Search 10 or less means walk there
|
|
SetLocalLocation(OBJECT_SELF, "OAI_SEARCH_LOC", GetLocation(oPerceived));
|
|
|
|
//Some nice semi-random animations
|
|
if (d3() > 1)
|
|
{
|
|
ActionPlayAnimation(ANIMATION_FIREFORGET_HEAD_TURN_LEFT, 0.5);
|
|
ActionPlayAnimation(ANIMATION_FIREFORGET_HEAD_TURN_RIGHT, 0.5);
|
|
}
|
|
if (d2() == 1) ActionPlayAnimation(ANIMATION_FIREFORGET_PAUSE_SCRATCH_HEAD);
|
|
else if (d2() == 1) ActionPlayAnimation(ANIMATION_LOOPING_LOOK_FAR, 0.5, 2.0);
|
|
|
|
//Finally return to current waypoint if one
|
|
object oWayPt = GetLocalObject(OBJECT_SELF, "WP_" + IntToString(GetLocalInt(OBJECT_SELF, "WP_CUR")));
|
|
if(GetIsObjectValid(oWayPt)) ActionMoveToObject(oWayPt);
|
|
return;
|
|
}
|
|
|
|
//This is the equivalent of a force conversation bubble, should only be used if you want an NPC
|
|
//to say something while he is already engaged in combat.
|
|
if(GetSpawnInCondition(NW_FLAG_SPECIAL_COMBAT_CONVERSATION) && GetIsPC(oPerceived))
|
|
{
|
|
SpeakOneLinerConversation();
|
|
}
|
|
|
|
//Linked up to the special conversation check to initiate a special one-off conversation
|
|
//to get the PCs attention
|
|
if(GetSpawnInCondition(NW_FLAG_SPECIAL_CONVERSATION) && GetIsPC(oPerceived))
|
|
{
|
|
ActionStartConversation(OBJECT_SELF);
|
|
}
|
|
|
|
if(bEnemy)
|
|
{
|
|
if(GetLocalInt(oPerceived, "OAI_ILLUSION_DC") > 0)
|
|
{
|
|
if(BreakIllusion(oPerceived)) {}
|
|
}
|
|
}
|
|
|
|
//If I am not involved in fighting right now
|
|
if(!GetIsInCombat(OBJECT_SELF))
|
|
{
|
|
if(bEnemy)
|
|
{
|
|
if(!GetHasEffect(EFFECT_TYPE_SLEEP)
|
|
|| !GetHasEffect(EFFECT_TYPE_BLINDNESS)
|
|
|| !GetHasEffect(EFFECT_TYPE_CONFUSED)
|
|
|| !GetHasEffect(EFFECT_TYPE_DAZED)
|
|
|| !GetHasEffect(EFFECT_TYPE_DOMINATED)
|
|
|| !GetHasEffect(EFFECT_TYPE_FRIGHTENED)
|
|
|| !GetHasEffect(EFFECT_TYPE_PARALYZE)
|
|
|| !GetHasEffect(EFFECT_TYPE_PETRIFY)
|
|
|| !GetHasEffect(EFFECT_TYPE_STUNNED)
|
|
|| !GetHasEffect(EFFECT_TYPE_TURNED))
|
|
{
|
|
if(GetBehaviorState(NW_FLAG_BEHAVIOR_OMNIVORE) || GetBehaviorState(NW_FLAG_BEHAVIOR_HERBIVORE))
|
|
{
|
|
OAI_DetermineSpecialBehavior();
|
|
}
|
|
else
|
|
{
|
|
SetFacingPoint(GetPosition(oPerceived));
|
|
SpeakString("NW_I_WAS_ATTACKED", TALKVOLUME_SILENT_TALK);
|
|
OAI_DetermineCombatRound();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(GetBehaviorState(NW_FLAG_BEHAVIOR_OMNIVORE) || GetBehaviorState(NW_FLAG_BEHAVIOR_HERBIVORE))
|
|
{
|
|
OAI_DetermineSpecialBehavior();
|
|
}
|
|
else if(GetSpawnInCondition(NW_FLAG_SPECIAL_CONVERSATION) && GetIsPC(oPerceived))
|
|
{
|
|
ActionStartConversation(OBJECT_SELF);
|
|
}
|
|
|
|
//Activate ambient animations or walk waypoints if appropriate
|
|
if(!IsInConversation(OBJECT_SELF))
|
|
{
|
|
if(GetIsPostOrWalking())
|
|
{
|
|
WalkWayPoints();
|
|
}
|
|
else if(GetIsPC(oPerceived) && (GetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS)
|
|
|| GetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS_AVIAN)
|
|
|| GetSpawnInCondition(NW_FLAG_IMMOBILE_AMBIENT_ANIMATIONS)
|
|
|| GetIsEncounterCreature()))
|
|
{
|
|
SetAnimationCondition(NW_ANIM_FLAG_IS_ACTIVE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(GetSpawnInCondition(NW_FLAG_PERCIEVE_EVENT))
|
|
{
|
|
SignalEvent(OBJECT_SELF, EventUserDefined(1002));
|
|
}
|
|
}
|