Updated Jasperre's AI
Updated Jasperre's AI to 1.4, fixed a few other coding bugs & fully compiled module.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/************************ [Include - Other AI Functions] ***********************
|
||||
Filename: j_inc_other_ai
|
||||
************************* [Include - Other AI Functions] ***********************
|
||||
/*/////////////////////// [Include - Other AI Functions] ///////////////////////
|
||||
Filename: J_INC_Other_AI
|
||||
///////////////////////// [Include - Other AI Functions] ///////////////////////
|
||||
This contains fuctions and calls for these scripts:
|
||||
nw_c2_default2 - Percieve
|
||||
nw_c2_default3 - On Combat round End (For DetermineCombatRound() only)
|
||||
@@ -9,43 +9,44 @@
|
||||
nw_c2_default6 - Damaged
|
||||
nw_c2_default8 - Disturbed
|
||||
nw_c2_defaultb - Spell cast at
|
||||
Ones that don't use this use different/No includes.
|
||||
|
||||
Ones that don't use this use different or no includes.
|
||||
|
||||
HOPEFULLY it will make them faster, if they don't run combat.
|
||||
|
||||
They use Execute Script to initiate combat. (With the override ones
|
||||
initiating the override version, the normal initiateing the normal).
|
||||
************************* [History] ********************************************
|
||||
///////////////////////// [History] ////////////////////////////////////////////
|
||||
1.3 - Added to speed up compilings and gather non-combat, or other workings
|
||||
in one place.
|
||||
************************* [Workings] *******************************************
|
||||
This is included, by #include "J_INC_OTHER_AI" in other AI files.
|
||||
1.4 - TO DO:
|
||||
-
|
||||
///////////////////////// [Workings] ///////////////////////////////////////////
|
||||
This is included in other AI files.
|
||||
|
||||
They then use these functions in them scripts.
|
||||
************************* [Arguments] ******************************************
|
||||
///////////////////////// [Arguments] //////////////////////////////////////////
|
||||
Arguments: N/A
|
||||
************************* Include - Other AI Functions] ***********************/
|
||||
///////////////////////// [Include - Other AI Functions] /////////////////////*/
|
||||
|
||||
// All constants.
|
||||
#include "j_inc_constants"
|
||||
#include "J_INC_CONSTANTS"
|
||||
|
||||
// Responds to it (like makinging the callers attacker thier target)
|
||||
// Responds to it (like makinging the callers attacker thier target)
|
||||
// Called in OnConversation, and thats it. Use "ShouterFriend" To stop repeated GetIsFriend calls.
|
||||
void RespondToShout(object oShouter, int nShoutIndex);
|
||||
// Gets the attacker or attakee of the target, which should be a friend
|
||||
// Gets any possible target which is attacking oShouter (and isn't an ally)
|
||||
// or who oShouter is attacking. oShouter should be a ally.
|
||||
object GetIntruderFromShout(object oShouter);
|
||||
|
||||
// Shouts, or really brings all people in 60.0M(by default) to the "shouter"
|
||||
void ShoutBossShout(object oEnemy);
|
||||
// Checks the target for a specific EFFECT_TYPE constant value
|
||||
// Returns TRUE or FALSE. Used On Damaged for polymorph checking.
|
||||
int GetHasEffect(int nEffectType, object oTarget = OBJECT_SELF);
|
||||
// This sets a morale penalty, to the exsisting one, if there is one.
|
||||
// It will reduce itself after fDuration (or if we die, ETC, it is deleted).
|
||||
// It is deleted at the end of combat as well.
|
||||
void SetMoralePenalty(int iPenalty, float fDuration = 0.0);
|
||||
// Removes iPenalty amount if it can.
|
||||
void RemoveMoralePenalty(int iPenalty);
|
||||
void SetMoralePenalty(int nPenalty, float fDuration = 0.0);
|
||||
// Removes nPenalty amount if it can.
|
||||
void RemoveMoralePenalty(int nPenalty);
|
||||
// At 5+ intelligence, we fire off any dispells at oPlaceables location
|
||||
void SearchDispells(object oPlaceable);
|
||||
|
||||
@@ -66,6 +67,18 @@ void ActionMoveToEnemy(object oEnemy);
|
||||
// - They then run! (Badly)
|
||||
int PerceptionFleeFrom(object oEnemy);
|
||||
|
||||
// This wrappers commonly used code for a "Call to arms" type response.
|
||||
// * We know of no enemy, so we will move to oAlly, who either called to
|
||||
// us, or, well, we know of.
|
||||
// * Calls out AI_SHOUT_CALL_TO_ARMS too.
|
||||
void CallToArmsResponse(object oAlly);
|
||||
// This wrappers commonly used code for a "I was attacked" type response.
|
||||
// * We know there will be an enemy - or should be - and if we find one to attack
|
||||
// (using GetIntruderFromShout()) - we attack it (and call another I was attacked)
|
||||
// else, this will run CallToArmsResponse(oAlly);
|
||||
// * Calls out AI_SHOUT_I_WAS_ATTACKED, or AI_SHOUT_CALL_TO_ARMS too.
|
||||
void IWasAttackedResponse(object oAlly);
|
||||
|
||||
/*::///////////////////////////////////////////////
|
||||
//:: Name: ShoutBossShout
|
||||
//::///////////////////////////////////////////////
|
||||
@@ -74,16 +87,19 @@ int PerceptionFleeFrom(object oEnemy);
|
||||
//:://///////////////////////////////////////////*/
|
||||
void ShoutBossShout(object oEnemy)
|
||||
{
|
||||
if(GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_BOSS_MONSTER_SHOUT, AI_OTHER_COMBAT_MASTER))
|
||||
// 1.4 - Added a 5 minute cooldown timer for this. Thusly, if the boss lingers,
|
||||
// so will the big shout they do.
|
||||
if(GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_BOSS_MONSTER_SHOUT, AI_OTHER_COMBAT_MASTER) &&
|
||||
!GetLocalTimer(AI_TIMER_BOSS_SHOUT_COOLDOWN))
|
||||
{
|
||||
// Get the range (and default to 60.0 M)
|
||||
float fRange = IntToFloat(GetBoundriedAIInteger(AI_BOSS_MONSTER_SHOUT_RANGE, i60, 370));
|
||||
float fRange = IntToFloat(GetBoundriedAIInteger(AI_BOSS_MONSTER_SHOUT_RANGE, 60, 370));
|
||||
// We loop through nearest not-seen, not-heard allies and get them
|
||||
// to attack the person.
|
||||
int Cnt = i1;
|
||||
int nCnt = 1;
|
||||
// Not seen, not heard...
|
||||
object oAlly = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND,
|
||||
OBJECT_SELF, Cnt, CREATURE_TYPE_IS_ALIVE, TRUE,
|
||||
OBJECT_SELF, nCnt, CREATURE_TYPE_IS_ALIVE, TRUE,
|
||||
CREATURE_TYPE_PERCEPTION, PERCEPTION_NOT_SEEN_AND_NOT_HEARD);
|
||||
// Get who thier target is.
|
||||
object oThierTarget;
|
||||
@@ -103,13 +119,19 @@ void ShoutBossShout(object oEnemy)
|
||||
ExecuteScript(COMBAT_FILE, oAlly);
|
||||
}
|
||||
}
|
||||
Cnt++;
|
||||
nCnt++;
|
||||
oAlly = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND,
|
||||
OBJECT_SELF, Cnt, CREATURE_TYPE_IS_ALIVE, TRUE,
|
||||
OBJECT_SELF, nCnt, CREATURE_TYPE_IS_ALIVE, TRUE,
|
||||
CREATURE_TYPE_PERCEPTION, PERCEPTION_NOT_SEEN_AND_NOT_HEARD);
|
||||
}
|
||||
// Remove it :-)
|
||||
DeleteSpawnInCondition(AI_FLAG_OTHER_COMBAT_BOSS_MONSTER_SHOUT, AI_OTHER_COMBAT_MASTER);
|
||||
// * Don't speak when dead. 1.4 change (an obvious one to make)
|
||||
if(CanSpeak())
|
||||
{
|
||||
// Speak a string associated with this action being carried out
|
||||
SpeakArrayString(AI_TALK_ON_LEADER_BOSS_SHOUT);
|
||||
}
|
||||
// Remove it for 5 minutes.
|
||||
SetLocalTimer(AI_TIMER_BOSS_SHOUT_COOLDOWN, 300.0);
|
||||
}
|
||||
}
|
||||
// This MAY make us set a local timer to turn off hiding.
|
||||
@@ -122,7 +144,7 @@ void TurnOffHiding(object oIntruder)
|
||||
(GetObjectSeen(OBJECT_SELF, oIntruder) ||
|
||||
GetObjectHeard(OBJECT_SELF, oIntruder)))
|
||||
{
|
||||
SetLocalTimer(AI_TIMER_TURN_OFF_HIDE, f18);
|
||||
SetLocalTimer(AI_TIMER_TURN_OFF_HIDE, 18.0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +158,8 @@ void HideOrClear()
|
||||
GetActionMode(OBJECT_SELF, ACTION_MODE_STEALTH) == FALSE)
|
||||
{
|
||||
// Need skill or force on
|
||||
if((GetSkillRank(SKILL_HIDE) - i4 >= GetHitDice(OBJECT_SELF)) ||
|
||||
int nRank = GetSkillRank(SKILL_HIDE);
|
||||
if((nRank - 4 >= GetHitDice(OBJECT_SELF) && nRank >= 7) ||
|
||||
GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_FORCE_HIDING, AI_OTHER_COMBAT_MASTER))
|
||||
{
|
||||
// Use hide
|
||||
@@ -173,12 +196,17 @@ void HideOrClear()
|
||||
I_WAS_ATTACKED = 1;
|
||||
|
||||
If not in combat, attack the attackee of the shouter. Basically the best
|
||||
way to get people to come and help us.
|
||||
way to get people to come and help us, if we know of an attacker!
|
||||
* Call this after we call DetermineCombatRound() to make sure that any
|
||||
responses know of the attackers. It doesn't matter in actual fact, but
|
||||
useful anyway.
|
||||
|
||||
CALL_TO_ARMS = 3;
|
||||
|
||||
If not in combat, determine combat round. By default, it should check any
|
||||
allies it can see/hear for thier targets and help them too.
|
||||
* Better if we do not know of a target (and thusly our allies wouldn't know
|
||||
of them as well) so the allies will move to us.
|
||||
|
||||
HELP_MY_FRIEND = 4;
|
||||
|
||||
@@ -208,179 +236,254 @@ void HideOrClear()
|
||||
//::///////////////////////////////////////////////
|
||||
// Modified almost completely: Jasperre
|
||||
//:://///////////////////////////////////////////*/
|
||||
// Gets the attacker or attakee of the target, which should be a friend
|
||||
// Gets any possible target which is attacking oShouter (and isn't an ally)
|
||||
// or who oShouter is attacking. oShouter should be a ally.
|
||||
object GetIntruderFromShout(object oShouter)
|
||||
{
|
||||
object oIntruder = GetAttackTarget(oShouter);
|
||||
if(!GetIsObjectValid(oIntruder) ||
|
||||
GetIgnoreNoFriend(oIntruder))
|
||||
// First, get who they specifically want to attack (IE: Input target the shout
|
||||
// is usually for)
|
||||
object oIntruder = GetLocalObject(oShouter, AI_OBJECT + AI_ATTACK_SPECIFIC_OBJECT);
|
||||
if(GetIgnoreNoFriend(oIntruder) || (!GetObjectSeen(oShouter) && !GetObjectHeard(oShouter)))
|
||||
{
|
||||
oIntruder = GetLastHostileActor(oShouter);
|
||||
if(GetIgnoreNoFriend(oIntruder))
|
||||
// Or, we look for the last melee target (which, at least, will be set)
|
||||
oIntruder = GetLocalObject(oShouter, AI_OBJECT + AI_LAST_MELEE_TARGET);
|
||||
if(GetIgnoreNoFriend(oIntruder) || (!GetObjectSeen(oShouter) && !GetObjectHeard(oShouter)))
|
||||
{
|
||||
return OBJECT_INVALID;
|
||||
// Current actual attack target of the shouter
|
||||
oIntruder = GetAttackTarget(oShouter);
|
||||
if(GetIgnoreNoFriend(oIntruder) || (!GetObjectSeen(oShouter) && !GetObjectHeard(oShouter)))
|
||||
{
|
||||
// Last hostile actor of the shouter
|
||||
oIntruder = GetLastHostileActor(oShouter);
|
||||
if(GetIgnoreNoFriend(oIntruder) || (!GetObjectSeen(oShouter) && !GetObjectHeard(oShouter)))
|
||||
{
|
||||
return OBJECT_INVALID;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return oIntruder;
|
||||
}
|
||||
|
||||
// Responds to it (like makinging the callers attacker thier target)
|
||||
// Called in OnConversation, and thats it. Use "ShouterFriend" To stop repeated GetIsFriend calls.
|
||||
void RespondToShout(object oShouter, int nShoutIndex)
|
||||
{
|
||||
// We use oIntruder to set who to attack.
|
||||
object oIntruder;
|
||||
// Ones we don't care about if we are in combat...
|
||||
if(nShoutIndex == i6) // "Attack specific object"
|
||||
// Check nShoutIndex against known constants
|
||||
switch(nShoutIndex)
|
||||
{
|
||||
// If a leader, we set it as a local object, nothing more
|
||||
if(GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_GROUP_LEADER, AI_OTHER_COMBAT_MASTER, oShouter))
|
||||
// Note: Not checked in sequential order (especially as they are constants).
|
||||
// Instead, it is "Ones which if we are in combat, we still check" first.
|
||||
|
||||
// Attack a specific object which the leader shouted about.
|
||||
case AI_SHOUT_LEADER_ATTACK_TARGET_CONSTANT:
|
||||
{
|
||||
oIntruder = GetLocalObject(oShouter, AI_ATTACK_SPECIFIC_OBJECT);
|
||||
if(GetObjectSeen(oIntruder))
|
||||
// If a leader, we set it as a local object, nothing more
|
||||
if(GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_GROUP_LEADER, AI_OTHER_COMBAT_MASTER, oShouter))
|
||||
{
|
||||
// Set local object to use in next DetermineCombatRound.
|
||||
// We do not interrupt current acition (EG: Life saving stoneskins!) to re-direct.
|
||||
SetAIObject(AI_ATTACK_SPECIFIC_OBJECT, oIntruder);
|
||||
// 6 second delay.
|
||||
SetLocalTimer(AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID, f6);
|
||||
// 70. "[Shout] Reacting To Shout. [ShoutNo.] " + IntToString(iInput) + " [Shouter] " + GetName(oInput)
|
||||
DebugActionSpeakByInt(70, oShouter, nShoutIndex);
|
||||
|
||||
oIntruder = GetLocalObject(oShouter, AI_ATTACK_SPECIFIC_OBJECT);
|
||||
if(GetObjectSeen(oIntruder))
|
||||
{
|
||||
// Set local object to use in next DetermineCombatRound.
|
||||
// We do not interrupt current acition (EG: Life saving stoneskins!) to re-direct.
|
||||
SetAIObject(AI_ATTACK_SPECIFIC_OBJECT, oIntruder);
|
||||
// 6 second delay.
|
||||
SetLocalTimer(AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID, 6.0);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if(nShoutIndex == i5)// "leader flee now"
|
||||
{
|
||||
// If a leader, we set it as a local object, nothing more
|
||||
if(GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_GROUP_LEADER, AI_OTHER_COMBAT_MASTER, oShouter))
|
||||
break;
|
||||
// Leader flee now - mass retreat to those who hear it.
|
||||
case AI_SHOUT_LEADER_FLEE_NOW_CONSTANT:
|
||||
{
|
||||
oIntruder = GetLocalObject(oShouter, AI_FLEE_TO);
|
||||
// RUN! If intruder set is over 5.0M or no valid intruder
|
||||
ClearAllActions();
|
||||
// If a leader, we set it as a local object, nothing more
|
||||
if(GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_GROUP_LEADER, AI_OTHER_COMBAT_MASTER, oShouter))
|
||||
{
|
||||
// Get who we are going to run too
|
||||
oIntruder = GetLocalObject(oShouter, AI_FLEE_TO);
|
||||
|
||||
// RUN! If intruder set is over 5.0M or no valid intruder
|
||||
ClearAllActions();
|
||||
|
||||
// 70. "[Shout] Reacting To Shout. [ShoutNo.] " + IntToString(iInput) + " [Shouter] " + GetName(oInput)
|
||||
DebugActionSpeakByInt(70, oShouter, nShoutIndex);
|
||||
|
||||
// Set to run
|
||||
SetCurrentAction(AI_SPECIAL_ACTIONS_FLEE);
|
||||
// Turn on fleeing visual effect
|
||||
ApplyFleeingVisual();
|
||||
|
||||
// Ignore talk for 12 seconds
|
||||
SetLocalTimer(AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID, 12.0);
|
||||
|
||||
// If valid, we run to the intruder
|
||||
if(GetIsObjectValid(oIntruder))
|
||||
{
|
||||
SetAIObject(AI_FLEE_TO, oIntruder);
|
||||
ActionMoveToObject(oIntruder);
|
||||
}
|
||||
else // Else, we will just follow our leader!
|
||||
{
|
||||
SetAIObject(AI_FLEE_TO, oShouter);
|
||||
ActionForceFollowObject(oShouter, 3.0);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
// All others (IE: We need to not be in combat for these)
|
||||
// Anything that requires "DetermineCombatRound()" is here.
|
||||
|
||||
// If the shout is number 8, it is "I was opened" and so can only be a
|
||||
// placeable or door.
|
||||
case AI_SHOUT_I_WAS_OPENED_CONSTANT:
|
||||
{
|
||||
// If we are already attacking, we ignore this shout.
|
||||
if(CannotPerformCombatRound()) return;
|
||||
|
||||
// We need somewhat complexe here - to get thier opener.
|
||||
int nType = GetObjectType(oShouter);
|
||||
// Check object type. If not a placeable nor door - stop script.
|
||||
if(nType == OBJECT_TYPE_PLACEABLE ||
|
||||
nType == OBJECT_TYPE_DOOR)
|
||||
{
|
||||
// 70. "[Shout] Reacting To Shout. [ShoutNo.] " + IntToString(iInput) + " [Shouter] " + GetName(oInput)
|
||||
DebugActionSpeakByInt(70, oShouter, nShoutIndex);
|
||||
|
||||
// Now, we assign the placeable/door to set thier opener.
|
||||
// We do this by just executing a script that does it.
|
||||
ExecuteScript(FILE_SET_OPENER, oShouter);
|
||||
// We can immediantly get this would-be attacker!
|
||||
oIntruder = GetLocalObject(oShouter, AI_PLACEABLE_LAST_OPENED_BY);
|
||||
if(GetIsObjectValid(oIntruder))
|
||||
{
|
||||
// Attack
|
||||
ClearAllActions();
|
||||
DetermineCombatRound(oShouter);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Move to the object who shouted in detect mode
|
||||
SetActionMode(OBJECT_SELF, ACTION_MODE_DETECT, TRUE);
|
||||
ActionMoveToObject(oShouter, TRUE);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
// Call to arms requires nothing special. It is only called if
|
||||
// There is no target the shouter has to attack specifically, rather then
|
||||
// "I_WAS_ATTACKED" which would have.
|
||||
case AI_SHOUT_CALL_TO_ARMS_CONSTANT:
|
||||
{
|
||||
// If we are already attacking, we ignore this shout.
|
||||
if(CannotPerformCombatRound()) return;
|
||||
|
||||
// Ignore for 6 seconds
|
||||
SetLocalTimer(AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID, 6.0);
|
||||
|
||||
// 70. "[Shout] Reacting To Shout. [ShoutNo.] " + IntToString(iInput) + " [Shouter] " + GetName(oInput)
|
||||
DebugActionSpeakByInt(70, oShouter, nShoutIndex);
|
||||
SetCurrentAction(AI_SPECIAL_ACTIONS_FLEE);
|
||||
SetLocalTimer(AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID, f12);
|
||||
if(GetIsObjectValid(oIntruder))
|
||||
{
|
||||
SetAIObject(AI_FLEE_TO, oIntruder);
|
||||
ActionMoveToObject(oIntruder);
|
||||
}
|
||||
else // Else, we will just follow our leader!
|
||||
{
|
||||
SetAIObject(AI_FLEE_TO, oShouter);
|
||||
ActionForceFollowObject(oShouter, f3);
|
||||
}
|
||||
|
||||
// do standard Call to Arms response - IE: Move to oShouter
|
||||
CallToArmsResponse(oShouter);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
// If the shout is number 8, it is "I was opened" and so can only be a
|
||||
// placeable or door.
|
||||
else if(nShoutIndex == i8)// "I was opened"
|
||||
{
|
||||
// We need somewhat complexe here - to get thier opener.
|
||||
int nType = GetObjectType(oShouter);
|
||||
// Check object type. If not a placeable nor door - stop script.
|
||||
if(nType == OBJECT_TYPE_PLACEABLE ||
|
||||
nType == OBJECT_TYPE_DOOR)
|
||||
{
|
||||
// Now, we assign the placeable/door to set thier opener.
|
||||
// - Need to check it works.
|
||||
AssignCommand(oShouter, SetLocalObject(oShouter, PLACEABLE_LAST_OPENED_BY, GetLastOpenedBy()));
|
||||
oIntruder = GetLocalObject(oShouter, PLACEABLE_LAST_OPENED_BY);
|
||||
if(GetIsObjectValid(oIntruder))
|
||||
{
|
||||
// Attack
|
||||
ClearAllActions();
|
||||
DetermineCombatRound(oShouter);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Else, we must not be in combat for the rest
|
||||
else if(!CannotPerformCombatRound())
|
||||
{
|
||||
// Call to arms requires nothing special
|
||||
if(nShoutIndex == i3)// "Call to arms"
|
||||
{
|
||||
SetLocalTimer(AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID, f6);
|
||||
// 70. "[Shout] Reacting To Shout. [ShoutNo.] " + IntToString(iInput) + " [Shouter] " + GetName(oInput)
|
||||
DebugActionSpeakByInt(70, oShouter, nShoutIndex);
|
||||
DetermineCombatRound();
|
||||
}
|
||||
// Ones we can GetIntruderFromShout(oShouter);
|
||||
if(nShoutIndex == i1 || // "I was attacked"
|
||||
nShoutIndex == i4 || // "Help my friend"
|
||||
nShoutIndex == i7) // "I was killed"
|
||||
{
|
||||
// Am not already fighting, and we don't ignore the intruder
|
||||
oIntruder = GetIntruderFromShout(oShouter);
|
||||
if(!GetIsObjectValid(oIntruder))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(nShoutIndex == i1 ||
|
||||
nShoutIndex == i7)
|
||||
{
|
||||
// Morale penalty if they were killed
|
||||
if(nShoutIndex == i7)
|
||||
{
|
||||
SetMoralePenalty((GetHitDice(oShouter)/i4), f18);
|
||||
}
|
||||
// Get intruder
|
||||
// 70. "[Shout] Reacting To Shout. [ShoutNo.] " + IntToString(iInput) + " [Shouter] " + GetName(oInput)
|
||||
DebugActionSpeakByInt(70, oShouter, nShoutIndex);
|
||||
if(GetObjectSeen(oIntruder))
|
||||
{
|
||||
// Stop, and attack, if we can see them!
|
||||
ClearAllActions();
|
||||
SetLocalTimer(AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID, f9);
|
||||
DetermineCombatRound(oIntruder);
|
||||
DelayCommand(f2, AISpeakString(I_WAS_ATTACKED));
|
||||
}
|
||||
else // Else the enemy is not seen
|
||||
{
|
||||
// If I can see neither the shouter nor the enemy
|
||||
// stop what I am doing, and move to the attacker.
|
||||
// - 1.3 change. They move to the attackers location (IE directed by ally)
|
||||
ClearAllActions();
|
||||
SetLocalTimer(AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID, f6);
|
||||
// This will move to oIntruder if nothing else
|
||||
DetermineCombatRound(oIntruder);
|
||||
// Shout to other allies, after a second.
|
||||
DelayCommand(f2, AISpeakString(HELP_MY_FRIEND));
|
||||
}
|
||||
}
|
||||
else if(nShoutIndex == i4)// "Help my friend"
|
||||
break;
|
||||
// "Help my friend" is when a runner is running off (sorta fleeing) to
|
||||
// get help. This will move to the location set on them to reinforce.
|
||||
case AI_SHOUT_HELP_MY_FRIEND_CONSTANT:
|
||||
{
|
||||
// If we are already attacking, we ignore this shout.
|
||||
if(CannotPerformCombatRound()) return;
|
||||
|
||||
// Ignore things for 6 seconds
|
||||
SetLocalTimer(AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID, 6.0);
|
||||
|
||||
// We move to where the runner/shouter wants us.
|
||||
location lMoveTo = GetLocalLocation(oShouter, AI_HELP_MY_FRIEND_LOCATION);
|
||||
location lMoveTo = GetLocalLocation(oShouter, AI_LOCATION + AI_HELP_MY_FRIEND_LOCATION);
|
||||
|
||||
// 70. "[Shout] Reacting To Shout. [ShoutNo.] " + IntToString(iInput) + " [Shouter] " + GetName(oInput)
|
||||
DebugActionSpeakByInt(70, oShouter, nShoutIndex);
|
||||
SetLocalTimer(AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID, f6);
|
||||
|
||||
// If the location is valid
|
||||
if(GetIsObjectValid(GetAreaFromLocation(lMoveTo)))
|
||||
{
|
||||
// New special action, but one that is overrided by combat
|
||||
SetCurrentAction(AI_SPECIAL_ACTIONS_MOVE_TO_COMBAT);
|
||||
SetAIObject(AI_MOVE_TO_COMBAT_OBJECT, oShouter);
|
||||
SetAILocation(AI_MOVE_TO_COMBAT_LOCATION, lMoveTo);
|
||||
|
||||
// Move to the location of the fight, attack.
|
||||
ClearAllActions();
|
||||
// Move to the fights location
|
||||
ActionMoveToLocation(lMoveTo, TRUE);
|
||||
ActionDoCommand(DetermineCombatRound());
|
||||
// When we see someone fighting, we'll DCR
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we do not know of the friend attacker, we will follow them
|
||||
// Else, if we do not know of the friends attackers, or the location
|
||||
// they are at, we will follow them without casting any preperation
|
||||
// spells.
|
||||
ClearAllActions();
|
||||
SetSpawnInCondition(AI_FLAG_COMBAT_FLAG_FAST_BUFF_ENEMY, AI_COMBAT_MASTER);
|
||||
ActionForceFollowObject(oShouter, f3);
|
||||
ActionDoCommand(DetermineCombatRound());
|
||||
ActionForceFollowObject(oShouter, 3.0);
|
||||
// When we see an enemy, we'll attack!
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
// "I was attacked" is called when a creature is hurt or sees an enemy,
|
||||
// and starts to attack them. This means they know who the enemy is -
|
||||
// and thusly we can get it from them (Ususally GetLastHostileActor()
|
||||
// "I was killed" is the same, but applies a morale penalty too
|
||||
case AI_SHOUT_I_WAS_ATTACKED_CONSTANT:
|
||||
case AI_SHOUT_I_WAS_KILLED_CONSTANT:
|
||||
{
|
||||
// If it was "I was killed", we apply a short morale penatly
|
||||
// Penalty is "Hit dice / 4 + 1" (so always 1 minimum) for 18 seconds.
|
||||
if(nShoutIndex == AI_SHOUT_I_WAS_KILLED_CONSTANT)
|
||||
{
|
||||
SetMoralePenalty(GetHitDice(oShouter)/4 + 1, 18.0);
|
||||
}
|
||||
|
||||
// If we are already attacking, we ignore this shout.
|
||||
if(CannotPerformCombatRound()) return;
|
||||
|
||||
// 70. "[Shout] Reacting To Shout. [ShoutNo.] " + IntToString(iInput) + " [Shouter] " + GetName(oInput)
|
||||
DebugActionSpeakByInt(70, oShouter, nShoutIndex);
|
||||
|
||||
// Ignore for 6 seconds
|
||||
SetLocalTimer(AI_TIMER_SHOUT_IGNORE_ANYTHING_SAID, 6.0);
|
||||
|
||||
// Respond to oShouter's request for help - find thier target, and
|
||||
// attack!
|
||||
IWasAttackedResponse(oShouter);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// At 5+ intelligence, we fire off any dispells at oPlaceables location
|
||||
void SearchDispells(object oPlaceable)
|
||||
{
|
||||
// No dispelling at low intelligence.
|
||||
if(GetBoundriedAIInteger(AI_INTELLIGENCE) < i5) return;
|
||||
if(GetBoundriedAIInteger(AI_INTELLIGENCE) < 5) return;
|
||||
location lPlace = GetLocation(oPlaceable);
|
||||
// Move closer if not seen.
|
||||
if(!GetObjectSeen(oPlaceable))
|
||||
{
|
||||
// Move nearer - 6 M is out of the dispell range
|
||||
ActionMoveToObject(oPlaceable, TRUE, f6);
|
||||
ActionMoveToObject(oPlaceable, TRUE, 6.0);
|
||||
}
|
||||
// Dispell if we have any - at the location of oPlaceable.
|
||||
if(GetHasSpell(SPELL_LESSER_DISPEL))
|
||||
@@ -401,41 +504,22 @@ void SearchDispells(object oPlaceable)
|
||||
}
|
||||
}
|
||||
|
||||
// Get Has Effect
|
||||
// Checks to see if the target has a given
|
||||
// effect, usually from a spell. Really useful this is.
|
||||
int GetHasEffect(int nEffectType, object oTarget = OBJECT_SELF)
|
||||
{
|
||||
effect eCheck = GetFirstEffect(oTarget);
|
||||
while(GetIsEffectValid(eCheck))
|
||||
{
|
||||
if(GetEffectType(eCheck) == nEffectType)
|
||||
{
|
||||
return TRUE;
|
||||
break;
|
||||
}
|
||||
eCheck = GetNextEffect(oTarget);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// This sets a morale penalty, to the exsisting one, if there is one.
|
||||
// It will reduce itself (by the penalty) after fDuration (or if we die, ETC, it is deleted).
|
||||
// It is deleted at the end of combat as well.
|
||||
void SetMoralePenalty(int iPenalty, float fDuration = 0.0)
|
||||
void SetMoralePenalty(int nPenalty, float fDuration = 0.0)
|
||||
{
|
||||
int iOriginal = GetAIInteger(AI_MORALE_PENALTY);
|
||||
int iNew = iOriginal + iPenalty;
|
||||
SetAIInteger(AI_MORALE_PENALTY, iNew);
|
||||
DelayCommand(fDuration, RemoveMoralePenalty(iPenalty));
|
||||
int nNewPenalty = GetAIInteger(AI_MORALE_PENALTY) + nPenalty;
|
||||
SetAIInteger(AI_MORALE_PENALTY, nNewPenalty);
|
||||
DelayCommand(fDuration, RemoveMoralePenalty(nPenalty));
|
||||
}
|
||||
void RemoveMoralePenalty(int iPenalty)
|
||||
// Removes nPenalty amount if it can.
|
||||
void RemoveMoralePenalty(int nPenalty)
|
||||
{
|
||||
int iOriginal = GetAIInteger(AI_MORALE_PENALTY);
|
||||
int iNew = iOriginal - iPenalty;
|
||||
if(iNew > 0 && !GetIsDead(OBJECT_SELF))
|
||||
int nNewPenalty = GetAIInteger(AI_MORALE_PENALTY) - nPenalty;
|
||||
if(nNewPenalty > 0 && !GetIsDead(OBJECT_SELF))
|
||||
{
|
||||
SetAIInteger(AI_MORALE_PENALTY, iNew);
|
||||
SetAIInteger(AI_MORALE_PENALTY, nNewPenalty);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -478,10 +562,10 @@ int PerceptionFleeFrom(object oEnemy)
|
||||
// Valid run from target
|
||||
if(!GetIsObjectValid(oRunTarget))
|
||||
{
|
||||
oRunTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, i1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE);
|
||||
oRunTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE);
|
||||
if(!GetIsObjectValid(oRunTarget))
|
||||
{
|
||||
oRunTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, i1, CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD, CREATURE_TYPE_IS_ALIVE, TRUE);
|
||||
oRunTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD, CREATURE_TYPE_IS_ALIVE, TRUE);
|
||||
if(!GetIsObjectValid(oRunTarget))
|
||||
{
|
||||
oRunTarget = GetLastHostileActor();
|
||||
@@ -495,12 +579,140 @@ int PerceptionFleeFrom(object oEnemy)
|
||||
}
|
||||
// Run from enemy
|
||||
ClearAllActions();
|
||||
ActionMoveAwayFromObject(oRunTarget, TRUE, f50);
|
||||
ActionMoveAwayFromObject(oRunTarget, TRUE, 50.0);
|
||||
return TRUE;
|
||||
}
|
||||
// 0 or more morale.
|
||||
return FALSE;
|
||||
}
|
||||
// This wrappers commonly used code for a "Call to arms" type response.
|
||||
// * We know of no enemy, so we will move to oAlly, who either called to
|
||||
// us, or, well, we know of.
|
||||
// * Calls out AI_SHOUT_CALL_TO_ARMS too.
|
||||
void CallToArmsResponse(object oAlly)
|
||||
{
|
||||
// Shout to allies to attack, or be prepared.
|
||||
AISpeakString(AI_SHOUT_CALL_TO_ARMS);
|
||||
|
||||
// If we are over 2 meters away from oShouter, we move to them using
|
||||
// the special action
|
||||
if(GetDistanceToObject(oAlly) > 2.0 || !GetObjectSeen(oAlly))
|
||||
{
|
||||
// New special action, but one that is overrided by combat
|
||||
location lAlly = GetLocation(oAlly);
|
||||
SetCurrentAction(AI_SPECIAL_ACTIONS_MOVE_TO_COMBAT);
|
||||
SetAIObject(AI_MOVE_TO_COMBAT_OBJECT, oAlly);
|
||||
SetAILocation(AI_MOVE_TO_COMBAT_LOCATION, lAlly);
|
||||
|
||||
// Move to the location of the fight, attack.
|
||||
ClearAllActions();
|
||||
// Move to the fights location
|
||||
ActionMoveToLocation(lAlly, TRUE);
|
||||
// When we see someone fighting, we'll DCR
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Determine it anyway - we will search around oShouter
|
||||
// if nothing is found...but we are near to the shouter
|
||||
DetermineCombatRound(oAlly);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// This wrappers commonly used code for a "I was attacked" type response.
|
||||
// * We know there will be an enemy - or should be - and if we find one to attack
|
||||
// (using GetIntruderFromShout()) - we attack it (and call another I was attacked)
|
||||
// else, this will run CallToArmsResponse(oAlly);
|
||||
// * Calls out AI_SHOUT_I_WAS_ATTACKED, or AI_SHOUT_CALL_TO_ARMS too.
|
||||
void IWasAttackedResponse(object oAlly)
|
||||
{
|
||||
// Get the indruder. This is either who oShouter is currently attacking,
|
||||
// or the last attacker of them.
|
||||
object oIntruder = GetIntruderFromShout(oAlly);
|
||||
|
||||
// If valid, of course attack!
|
||||
if(GetIsObjectValid(oIntruder))
|
||||
{
|
||||
// 1.4 Note:
|
||||
// * It used to check "Are they seen". Basically, this is redudant
|
||||
// with the checks used in DetermineCombatRound(). It will do the
|
||||
// searching using oIntruder whatever.
|
||||
|
||||
// Stop, and attack
|
||||
ClearAllActions();
|
||||
DetermineCombatRound(oIntruder);
|
||||
|
||||
// Shout I was attacked - we've set our intruder now
|
||||
AISpeakString(AI_SHOUT_I_WAS_ATTACKED);
|
||||
return;
|
||||
}
|
||||
// If invalid, we act as if it was "Call to arms" type thing.
|
||||
// Call to arms is better to use normally, of course.
|
||||
else
|
||||
{
|
||||
// Shout to allies to attack, or be prepared.
|
||||
AISpeakString(AI_SHOUT_CALL_TO_ARMS);
|
||||
|
||||
// We see if they are attacking anything:
|
||||
oIntruder = GetAttackTarget(oAlly);
|
||||
if(!GetIsObjectValid(oIntruder))
|
||||
{
|
||||
oIntruder = GetLocalObject(oAlly, AI_OBJECT + AI_LAST_MELEE_TARGET);
|
||||
}
|
||||
|
||||
// If valid, we will move to a point bisecting the intruder and oAlly, or
|
||||
// move to oAlly. Should get interrupted once we see the attack target.
|
||||
// * NEED TO TEST
|
||||
if(GetIsObjectValid(oIntruder))
|
||||
{
|
||||
// New special action, but one that is overrided by combat
|
||||
vector vTarget = GetPosition(oIntruder);
|
||||
vector vSource = GetPosition(OBJECT_SELF);
|
||||
vector vDirection = vTarget - vSource;
|
||||
float fDistance = VectorMagnitude(vDirection) / 2.0;
|
||||
vector vPoint = VectorNormalize(vDirection) * fDistance + vSource;
|
||||
location lTarget = Location(GetArea(OBJECT_SELF), vPoint, DIRECTION_NORTH);
|
||||
|
||||
SetCurrentAction(AI_SPECIAL_ACTIONS_MOVE_TO_COMBAT);
|
||||
SetAIObject(AI_MOVE_TO_COMBAT_OBJECT, oAlly);
|
||||
SetAILocation(AI_MOVE_TO_COMBAT_LOCATION, lTarget);
|
||||
|
||||
// Move to the location of the fight, attack.
|
||||
ClearAllActions();
|
||||
// Move to the fights location
|
||||
ActionMoveToLocation(lTarget, TRUE);
|
||||
// When we see someone fighting, we'll DCR
|
||||
return;
|
||||
}
|
||||
// If we are over 2 meters away from oShouter, we move to them using
|
||||
// the special action
|
||||
else if(GetDistanceToObject(oAlly) > 2.0 || !GetObjectSeen(oAlly))
|
||||
{
|
||||
// New special action, but one that is overrided by combat
|
||||
location lAlly = GetLocation(oAlly);
|
||||
SetCurrentAction(AI_SPECIAL_ACTIONS_MOVE_TO_COMBAT);
|
||||
SetAIObject(AI_MOVE_TO_COMBAT_OBJECT, oAlly);
|
||||
SetAILocation(AI_MOVE_TO_COMBAT_LOCATION, lAlly);
|
||||
|
||||
// Move to the location of the fight, attack.
|
||||
ClearAllActions();
|
||||
// Move to the fights location
|
||||
ActionMoveToLocation(lAlly, TRUE);
|
||||
// When we see someone fighting, we'll DCR
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Determine it anyway - we will search around oShouter
|
||||
// if nothing is found...but we are near to the shouter
|
||||
DetermineCombatRound(oAlly);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Debug: To compile this script full, uncomment all of the below.
|
||||
/* - Add two "/"'s at the start of this line
|
||||
|
Reference in New Issue
Block a user