2193 lines
108 KiB
Plaintext
2193 lines
108 KiB
Plaintext
|
/*//////////////////////////////////////////////////////////////////////////////
|
||
|
Script: 0i_associates
|
||
|
Programmer: Philos
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
Scripts used for Associates.
|
||
|
*///////////////////////////////////////////////////////////////////////////////
|
||
|
#include "0i_actions"
|
||
|
#include "nw_inc_gff"
|
||
|
// Return TRUE if oCreature can attack based on current modes and actions.
|
||
|
int ai_CanIAttack(object oCreature);
|
||
|
// Returns the nearest locked object from oMaster.
|
||
|
object ai_GetNearestLockedObject(object oCreature);
|
||
|
// Will look for the oTarget or go to the oSpeaker depending on the situation.
|
||
|
void ai_FindTheEnemy(object oCreature, object oSpeaker, object oTarget, int bMonster);
|
||
|
// Selects the correct response base on nCommand from oCommander.
|
||
|
// These are given from either a radial menu option or voice command.
|
||
|
void ai_SelectAssociateCommand(object oCreature, object oCommander, int nCommand);
|
||
|
// Set nAction for the caller to pass to their associates. i.e. For henchmans summons.
|
||
|
void ai_PassActionToAssociates(object oCreature, int nAction, int bStatus = TRUE);
|
||
|
// Set Set the AI Mode to oAssociate and their associates.
|
||
|
void ai_PassAIModeToAssociates(object oAssociate, int nAIMode, int bStatus = TRUE);
|
||
|
// Set oCreature's ai scripts based on its first class or the variable "AI_DEFAULT_SCRIPT".
|
||
|
// bSetBasicAIScript set to TRUE will skip defensive and ambush tactic type scripts.
|
||
|
void ai_SetAssociateAIScript(object oCreature, int bCheckTacticScripts = TRUE);
|
||
|
// Returns TRUE if oCreature can speak.
|
||
|
int ai_CanISpeak(object oCreature);
|
||
|
// Cleansup any henchman actions and then removes them from the PC's faction.
|
||
|
void ai_FireHenchman(object oPC, object oHenchman);
|
||
|
// Will cast defensive spells (Buffs) on oPC's party from oCreature.
|
||
|
void ai_HenchmanCastDefensiveSpells(object oCreature, object oPC);
|
||
|
// Returns TRUE if we are starting combat due to an enemy being near.
|
||
|
// This should be checked after any "is in combat" checks.
|
||
|
int ai_CheckForCombat(object oCreature, int bMonster);
|
||
|
// Checks all perceived creatures to see if we should calculate a combat round
|
||
|
// or start combat for Associates.
|
||
|
void ai_AssociateEvaluateNewThreat(object oCreature, object oLastPerceived, string sPerception);
|
||
|
// Checks all perceived creatures to see if we should calculate a combat round
|
||
|
// or start combat for Monsters.
|
||
|
void ai_MonsterEvaluateNewThreat(object oCreature, object oLastPerceived, string sPerception);
|
||
|
// Copies all int, float, and string variables from oOldObject to oNewObject.
|
||
|
void ai_CopyObjectVariables(object oOldObject, object oNewObject);
|
||
|
//******************************************************************************
|
||
|
//********************* Creature event scripts *********************************
|
||
|
//******************************************************************************
|
||
|
|
||
|
// Add to nw_ch_aca OnRested event script of henchman.
|
||
|
void ai_OnRested(object oCreature);
|
||
|
|
||
|
//******************************************************************************
|
||
|
//******************* Associate AI option scripts ******************************
|
||
|
//******************************************************************************
|
||
|
|
||
|
// Increments/Decrements the following distance of associates.
|
||
|
void ai_FollowIncrement(object oPC, object oAssociate, float fIncrement, string sAssociateType);
|
||
|
// Turns on/off Ranged combat for oAssociate.
|
||
|
void ai_Ranged(object oPC, object oAssociate, string sAssociateType);
|
||
|
// Turns on/off Ignore enemy associates for oAssociate.
|
||
|
void ai_Ignore_Associates(object oPC, object oAssociate, string sAssociateType);
|
||
|
// Turns on/off Ignore floor traps for oAssociate.
|
||
|
void ai_Ignore_Traps(object oPC, object oAssociate, string sAssociateType);
|
||
|
// Turns on/off Search for oAssociate.
|
||
|
void ai_Search(object oPC, object oAssociate, string sAssociateType);
|
||
|
// Turns on/off Stealth for oAssociate.
|
||
|
void ai_Stealth(object oPC, object oAssociate, string sAssociateType);
|
||
|
// Turns on/off Open Doors for oAssociate.
|
||
|
void ai_OpenDoor(object oPC, object oAssociate, string sAssociateType);
|
||
|
// Turns on/off Picking/Bashing locks for oAssociate.
|
||
|
void ai_Locks(object oPC, object oAssociate, string sAssociateType, int nMode);
|
||
|
// Turns on/off Disarming of Traps for oAssociate.
|
||
|
void ai_Traps(object oPC, object oAssociate, string sAssociateType);
|
||
|
// Turns on/off the amount of speaking for oAssociate.
|
||
|
void ai_ReduceSpeech(object oPC, object oAssociate, string sAssociateType);
|
||
|
// Turns on/off use of offensive/defensive spells.
|
||
|
void ai_UseOffensiveMagic(object oPC, object oAssociate, int bDefensive, int bOffensive, string sAssociateType);
|
||
|
// Turns on/off magic use.
|
||
|
void ai_UseMagic(object oPC, object oAssociate, string sAssociateType);
|
||
|
// Turn Magic Item use on/off for oAssociates.
|
||
|
void ai_UseMagicItems(object oPC, object oAssociate, string sAssociateType);
|
||
|
// Adjusts loot options for oAssociate
|
||
|
void ai_Loot(object oPC, object oAssociate, string sAssociateType);
|
||
|
// Adjust loot options for oAssociate
|
||
|
void ai_Spontaneous(object oPC, object oAssociate, string sAssociateType);
|
||
|
// Increments/Decrements the magic use variable for the AI.
|
||
|
void ai_MagicIncrement(object oPC, object oAssociate, int nIncrement, string sAssociateType);
|
||
|
// Increments/Decrements the Loot Range use variable for the AI.
|
||
|
void ai_LootRangeIncrement(object oPC, object oAssociate, float fIncrement, string sAssociateType);
|
||
|
// Increments/Decrements the Lock Range use variable for the AI.
|
||
|
void ai_LockRangeIncrement(object oPC, object oAssociate, float fIncrement, string sAssociateType);
|
||
|
// Increments/Decrements the Trap Range use variable for the AI.
|
||
|
void ai_TrapRangeIncrement(object oPC, object oAssociate, float fIncrement, string sAssociateType);
|
||
|
// Increments/Decrements the Open Door Range use variable for the AI.
|
||
|
void ai_OpenDoorIncrement(object oPC, object oAssociate, float fIncrement, string sAssociateType);
|
||
|
// Saves a new AI script for oAssociate.
|
||
|
void ai_SaveAIScript(object oPC, object oAssociate, int nToken);
|
||
|
// Button action for buffing a PC.
|
||
|
void ai_Buff_Button(object oPC, object oAssociate, int nOption, string sAssociateType);
|
||
|
// Button action for setting healing ranges.
|
||
|
void ai_Heal_Button(object oPC, object oAssociate, int nIncrement, string sVar, string sAssociateType);
|
||
|
// Button action for turning healing on/off.
|
||
|
void ai_Heal_OnOff(object oPC, object oAssociate, string sAssociateType, int nMode);
|
||
|
// Button action for selecting a target to follow.
|
||
|
void ai_FollowTarget(object oPC, object oAssociate);
|
||
|
// Code to make oCreature guard oMaster.
|
||
|
void ai_Philos_Guard(object oMaster, object oCreature);
|
||
|
// Code to make OBJECT_SELF follow oMaster.
|
||
|
void ai_Philos_Follow(object oMaster);
|
||
|
// Code to make OBJECT_SELF hold at their location.
|
||
|
void ai_Philos_StandGround(object oMaster);
|
||
|
// Code to make oCreature attack the nearest enemy.
|
||
|
void ai_Philos_AttackNearest(object oMaster, object oCreature);
|
||
|
// Code to make oCreature turn search mode on.
|
||
|
void ai_Philos_SetSearch(object oMaster, object oCreature, string sAssociateType, int bTurnOn);
|
||
|
// Code to make oCreature turn stealth mode on.
|
||
|
void ai_Philos_SetStealth(object oMaster, object oCreature, string sAssociateType, int bTurnOn);
|
||
|
// Button action for giving commands to associates.
|
||
|
void ai_DoCommand(object oPC, object oAssociate, int nCommand);
|
||
|
// Button action to have associate do an action based on the target via OnPlayer Target event.
|
||
|
void ai_Action(object oPC, object oAssociate);
|
||
|
// Toggles between normal ai script and special tactic ai scripts.
|
||
|
void ai_AIScript(object oPC, object oAssociate, string sAssociate, int nToken);
|
||
|
// Has the PC select a Trap and then place it on the ground from an associate.
|
||
|
void ai_HavePCPlaceTrap(object oPC, object oAssociate);
|
||
|
// Jumps oAssociate to oPC, if oPC == oAssociate it jumps all oAssocites to oPC.
|
||
|
void ai_JumpToPC(object oPC, object oAssociate);
|
||
|
// Allow oAssociate to use no clipping.
|
||
|
void ai_GhostMode(object oPC, object oAssociate, int nToken, string sAssociateType);
|
||
|
// Changes the camera view from either the player to the associate or back.
|
||
|
void ai_ChangeCameraView(object oPC, object oAssociate);
|
||
|
// Checks that the oAssociate is within sight and then opens the inventory.
|
||
|
void ai_OpenInventory(object oAssociate, object oPC);
|
||
|
// Executes an installed plugin.
|
||
|
void ai_Plugin_Execute(object oPC, string sElem, int bUser = 0);
|
||
|
|
||
|
int ai_CanIAttack(object oCreature)
|
||
|
{
|
||
|
if(AI_DEBUG) ai_Debug("0i_associate", "122", "Can I attack? Hold mode: " +
|
||
|
IntToString(ai_GetAIMode(oCreature, AI_MODE_STAND_GROUND)) +
|
||
|
" Follow mode: " + IntToString(ai_GetAIMode(oCreature, AI_MODE_FOLLOW)) +
|
||
|
" Action (19/4): " + IntToString(GetCurrentAction(oCreature)));
|
||
|
if(ai_GetIsCharacter(oCreature)) return TRUE;
|
||
|
int nAction = GetCurrentAction(oCreature);
|
||
|
return (!ai_GetAIMode(oCreature, AI_MODE_STAND_GROUND) &&
|
||
|
!ai_GetAIMode(oCreature, AI_MODE_FOLLOW) &&
|
||
|
nAction != ACTION_ITEMCASTSPELL &&
|
||
|
nAction != ACTION_CASTSPELL);
|
||
|
}
|
||
|
object ai_GetNearestLockedObject(object oCreature)
|
||
|
{
|
||
|
int nCnt = 1;
|
||
|
object oMaster = GetMaster(oCreature);
|
||
|
float fRange = GetLocalFloat(oCreature, AI_TRAP_CHECK_RANGE);
|
||
|
location lCreature = GetLocation(oCreature);
|
||
|
object oObject = GetNearestObjectToLocation(OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE, lCreature, nCnt);
|
||
|
while (oObject != OBJECT_INVALID || GetDistanceBetween(oMaster, oObject) > fRange)
|
||
|
{
|
||
|
if(GetLocked(oObject) && ai_GetIsInLineOfSight(oMaster, oObject)) return oObject;
|
||
|
oObject = GetNearestObjectToLocation(OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE, lCreature, ++nCnt);
|
||
|
}
|
||
|
return OBJECT_INVALID;
|
||
|
}
|
||
|
void ai_FindTheEnemy(object oCreature, object oSpeaker, object oTarget, int bMonster)
|
||
|
{
|
||
|
if(GetLocalInt(oCreature, AI_AM_I_SEARCHING)) return;
|
||
|
if(oSpeaker == oTarget && d100() < 34)
|
||
|
{
|
||
|
// Let them know we heard something in the distance!.
|
||
|
if(!ai_GetAIMode(oCreature, AI_MODE_DO_NOT_SPEAK))
|
||
|
{
|
||
|
string sSpeak = "I heard something!";
|
||
|
int nRoll = d8();
|
||
|
if(nRoll == 1) sSpeak = "Did you hear that?";
|
||
|
if(nRoll == 2) sSpeak = "What was that noise?";
|
||
|
if(nRoll == 3) sSpeak = "Something is moving.";
|
||
|
if(nRoll == 4) sSpeak = "Lookout! I heard a noise.";
|
||
|
if(nRoll == 5) sSpeak = "Listen! We have company.";
|
||
|
AssignCommand(oCreature, ai_HaveCreatureSpeak(oCreature, 0, sSpeak));
|
||
|
}
|
||
|
ai_HaveCreatureSpeak(oCreature, 8, ":43:6:9:10:23:42:");
|
||
|
}
|
||
|
if(GetLocalString(oCreature, AI_COMBAT_SCRIPT) == "ai_a_peaceful" ||
|
||
|
GetLocalString(oCreature, AI_COMBAT_SCRIPT) == "ai_coward") return;
|
||
|
float fDistance, fPerceptionDistance;
|
||
|
if(bMonster)
|
||
|
{
|
||
|
// Check distance from the creature hearing this and the target.
|
||
|
fDistance = GetDistanceBetween(oCreature, oTarget);
|
||
|
fPerceptionDistance = GetLocalFloat(GetModule(), AI_RULE_PERCEPTION_DISTANCE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// We want to use the distance between the PC and target not us.
|
||
|
fDistance = GetDistanceBetween(GetMaster(), oTarget);
|
||
|
fPerceptionDistance = GetLocalFloat(oCreature, AI_ASSOC_PERCEPTION_DISTANCE);
|
||
|
}
|
||
|
if(AI_DEBUG) ai_Debug("0i_associates", "175", " fDistance: " + FloatToString(fDistance, 0, 2) +
|
||
|
" fPerceptionDistance: " + FloatToString(fPerceptionDistance, 0, 2) +
|
||
|
" Hiding? " + IntToString(GetStealthMode(oTarget)));
|
||
|
if(fDistance <= fPerceptionDistance)
|
||
|
{
|
||
|
SetLocalInt(oCreature, AI_AM_I_SEARCHING, TRUE);
|
||
|
if(LineOfSightObject(oCreature, oTarget))
|
||
|
{
|
||
|
if(fDistance > AI_RANGE_CLOSE)
|
||
|
{
|
||
|
int bMoveForward = TRUE;
|
||
|
// We check this because if the enemy is moving or has not
|
||
|
// started acting then we don't want to move up on them as they
|
||
|
// might move towards us! Just attack! Only sneak attack if they are busy.
|
||
|
int nAction = GetCurrentAction(oTarget);
|
||
|
if(AI_DEBUG) ai_Debug("0i_associates", "189", GetName(oTarget) + " current action: " + IntToString(nAction));
|
||
|
if(nAction == ACTION_MOVETOPOINT ||
|
||
|
nAction == ACTION_INVALID ||
|
||
|
nAction == ACTION_RANDOMWALK) bMoveForward = FALSE;
|
||
|
// If they are attacking make sure it is in melee?
|
||
|
// If not then don't move since they might be moving toward us.
|
||
|
if(nAction == ACTION_ATTACKOBJECT)
|
||
|
{
|
||
|
if(!ai_GetNumOfEnemiesInRange(oTarget)) bMoveForward = FALSE;
|
||
|
}
|
||
|
if(bMoveForward)
|
||
|
{
|
||
|
if(AI_DEBUG) ai_Debug("0i_associates", "201", "Running towards combat to engage " + GetName(oTarget));
|
||
|
ActionMoveToObject(oTarget, TRUE, AI_RANGE_CLOSE);
|
||
|
AssignCommand(oCreature, ActionDoCommand(DeleteLocalInt(oCreature, AI_AM_I_SEARCHING)));
|
||
|
return;
|
||
|
}
|
||
|
if(AI_DEBUG) ai_Debug("0i_associates", "207", "Searching for " + GetName(oTarget) + " while moving towards " + GetName(oSpeaker));
|
||
|
SetActionMode(oCreature, ACTION_MODE_DETECT, TRUE);
|
||
|
ActionMoveToObject(oSpeaker);
|
||
|
return;
|
||
|
}
|
||
|
if(AI_DEBUG) ai_Debug("0i_associates", "176", "Moving and searching for " + GetName(oTarget));
|
||
|
SetActionMode(oCreature, ACTION_MODE_DETECT, TRUE);
|
||
|
ActionMoveToLocation(GetLocation(oTarget), FALSE);
|
||
|
//ActionMoveToObject(oTarget, FALSE, AI_RANGE_MELEE);
|
||
|
AssignCommand(oCreature, ActionDoCommand(DeleteLocalInt(oCreature, AI_AM_I_SEARCHING)));
|
||
|
return;
|
||
|
}
|
||
|
if(AI_DEBUG) ai_Debug("0i_associates", "218", "No line of sight for " + GetName(oTarget) + ". Moving towards " + GetName(oSpeaker));
|
||
|
ActionMoveToObject(oSpeaker, TRUE);
|
||
|
AssignCommand(oCreature, ActionDoCommand(DeleteLocalInt(oCreature, AI_AM_I_SEARCHING)));
|
||
|
}
|
||
|
|
||
|
}
|
||
|
void ai_ReactToAssociate(object oCreature, object oCommander, int bMonster)
|
||
|
{
|
||
|
object oTarget = GetLocalObject(oCommander, AI_MY_TARGET);
|
||
|
if (oTarget == OBJECT_INVALID) return;
|
||
|
if(ai_GetIsInCombat(oCreature))
|
||
|
{
|
||
|
if(oCommander == GetMaster(oCreature) && ai_GetAIMode(oCreature, AI_MODE_DEFEND_MASTER))
|
||
|
{
|
||
|
ai_DoAssociateCombatRound(oCreature, oTarget);
|
||
|
}
|
||
|
else ai_DoAssociateCombatRound(oCreature);
|
||
|
return;
|
||
|
}
|
||
|
ai_FindTheEnemy(oCreature, oCommander, oTarget, bMonster);
|
||
|
}
|
||
|
void ai_SelectAssociateCommand(object oCreature, object oCommander, int nCommand)
|
||
|
{
|
||
|
object oMaster = GetMaster(oCreature);
|
||
|
// These nCommands can be issued even when the caller is busy.
|
||
|
switch(nCommand)
|
||
|
{
|
||
|
// Master is being attacked by the enemy.
|
||
|
case ASSOCIATE_COMMAND_MASTERGOINGTOBEATTACKED:
|
||
|
{
|
||
|
object oAttacker = GetGoingToBeAttackedBy(oMaster);
|
||
|
if(AI_DEBUG) ai_Debug("0i_associate", "120", GetName(oMaster) + " has been attack by " +
|
||
|
GetName(GetGoingToBeAttackedBy(oMaster)) + "!");
|
||
|
// Used to set who monsters are attacking.
|
||
|
int nAction = GetCurrentAction(oAttacker);
|
||
|
if(nAction == ACTION_ATTACKOBJECT) SetLocalObject(oAttacker, AI_ATTACKED_PHYSICAL, oMaster);
|
||
|
else if(nAction == ACTION_CASTSPELL || nAction == ACTION_ITEMCASTSPELL)
|
||
|
{
|
||
|
SetLocalObject(oAttacker, AI_ATTACKED_SPELL, oMaster);
|
||
|
}
|
||
|
if(!ai_GetIsBusy(oCreature) && ai_CanIAttack(oCreature))
|
||
|
{
|
||
|
if(ai_GetIsInCombat(oCreature)) ai_DoAssociateCombatRound(oCreature);
|
||
|
else ai_FindTheEnemy(oCreature, oCommander, oAttacker, FALSE);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
// Menu used by a player to have the henchman follow them.
|
||
|
case ASSOCIATE_COMMAND_FOLLOWMASTER:
|
||
|
{
|
||
|
if(AI_DEBUG) ai_Debug("0i_associate", "135", GetName(oMaster) + " has commanded " +
|
||
|
GetName(oCreature) + " to FOLLOW.");
|
||
|
AssignCommand(oCreature, ai_Philos_Follow(oMaster));
|
||
|
return;
|
||
|
}
|
||
|
// Menu used by a player to have the henchman go into NORMAL MODE.
|
||
|
// We also attack the nearest, this keeps henchman going into combat quickly.
|
||
|
case ASSOCIATE_COMMAND_ATTACKNEAREST:
|
||
|
{
|
||
|
if(AI_DEBUG) ai_Debug("0i_associates", "158", GetName(oMaster) + " has commanded " +
|
||
|
GetName(oCreature) + " to attack nearest(NORMAL MODE).");
|
||
|
ai_Philos_AttackNearest(oMaster, oCreature);
|
||
|
return;
|
||
|
}
|
||
|
// Menu used by a player to have the henchman stay where they are standing.
|
||
|
case ASSOCIATE_COMMAND_STANDGROUND:
|
||
|
{
|
||
|
if(AI_DEBUG) ai_Debug("0i_associate", "189", GetName(oMaster) + " has commanded " +
|
||
|
GetName(OBJECT_SELF) + " to STANDGROUND.");
|
||
|
AssignCommand(oCreature, ai_Philos_StandGround(oMaster));
|
||
|
return;
|
||
|
}
|
||
|
// Menu used by a player to have the henchman attack anyone who attacks them.
|
||
|
case ASSOCIATE_COMMAND_GUARDMASTER:
|
||
|
{
|
||
|
if(AI_DEBUG) ai_Debug("0i_associate", "211", GetName(oMaster) + " has commanded " +
|
||
|
GetName(oCreature) + " to GAURDMASTER.");
|
||
|
ai_Philos_Guard(oMaster, oCreature);
|
||
|
return;
|
||
|
}
|
||
|
// Menu used by a player to have the henchman heal them as soon as possible.
|
||
|
case ASSOCIATE_COMMAND_HEALMASTER:
|
||
|
{
|
||
|
// Player will be stuck with this variable if they are not using the AI.
|
||
|
DeleteLocalInt(oCommander, "AI_I_AM_BEING_HEALED");
|
||
|
if(ai_GetIsInCombat(oCreature)) ai_TryHealingTalent(oCreature, ai_GetNumOfEnemiesInRange(oCreature), oCommander);
|
||
|
else AssignCommand(oCreature, ai_ActionTryHealing(oCreature, oCommander));
|
||
|
return;
|
||
|
}
|
||
|
// Menu used by a player to toggle a henchmans casting options.
|
||
|
case ASSOCIATE_COMMAND_TOGGLECASTING:
|
||
|
{
|
||
|
if(ai_GetMagicMode(oCreature, AI_MAGIC_NO_MAGIC))
|
||
|
{
|
||
|
ai_SetMagicMode(oCreature, AI_MAGIC_NO_MAGIC, FALSE);
|
||
|
ai_SetMagicMode(oCreature, AI_MAGIC_DEFENSIVE_CASTING, TRUE);
|
||
|
ai_SetMagicMode(oCreature, AI_MAGIC_OFFENSIVE_CASTING, FALSE);
|
||
|
ai_SendMessages(GetName(oCreature) + " will now cast defensive spells only.", AI_COLOR_GRAY, oCommander);
|
||
|
}
|
||
|
else if(ai_GetMagicMode(oCreature, AI_MAGIC_DEFENSIVE_CASTING))
|
||
|
{
|
||
|
ai_SetMagicMode(oCreature, AI_MAGIC_NO_MAGIC, FALSE);
|
||
|
ai_SetMagicMode(oCreature, AI_MAGIC_DEFENSIVE_CASTING, FALSE);
|
||
|
ai_SetMagicMode(oCreature, AI_MAGIC_OFFENSIVE_CASTING, TRUE);
|
||
|
ai_SendMessages(GetName(oCreature) + " will now cast offensive spells only.", AI_COLOR_GRAY, oCommander);
|
||
|
}
|
||
|
else if(ai_GetMagicMode(oCreature, AI_MAGIC_OFFENSIVE_CASTING))
|
||
|
{
|
||
|
ai_SetMagicMode(oCreature, AI_MAGIC_NO_MAGIC, FALSE);
|
||
|
ai_SetMagicMode(oCreature, AI_MAGIC_DEFENSIVE_CASTING, FALSE);
|
||
|
ai_SetMagicMode(oCreature, AI_MAGIC_OFFENSIVE_CASTING, FALSE);
|
||
|
ai_SendMessages(GetName(oCreature) + " will now cast any spell.", AI_COLOR_GRAY, oCommander);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ai_SetMagicMode(oCreature, AI_MAGIC_NO_MAGIC, TRUE);
|
||
|
ai_SetMagicMode(oCreature, AI_MAGIC_DEFENSIVE_CASTING, FALSE);
|
||
|
ai_SetMagicMode(oCreature, AI_MAGIC_OFFENSIVE_CASTING, FALSE);
|
||
|
ai_SendMessages(GetName(oCreature) + " will not use any magic.", AI_COLOR_GRAY, oCommander);
|
||
|
}
|
||
|
aiSaveAssociateModesToDb(oMaster, oCreature);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
// If we are busy then these nCommands are ignored.
|
||
|
if(!ai_GetIsBusy(oCreature))
|
||
|
{
|
||
|
// Respond to shouts from friendly non-PCs only.
|
||
|
if (ai_CanIAttack(oCreature))
|
||
|
{
|
||
|
if(nCommand == AI_ALLY_IS_WOUNDED)
|
||
|
{
|
||
|
if(ai_TryHealing(oCreature, oCommander)) return;
|
||
|
}
|
||
|
else if(nCommand == AI_ALLY_IS_DISEASED ||
|
||
|
nCommand == AI_ALLY_IS_POISONED ||
|
||
|
nCommand == AI_ALLY_IS_WEAK)
|
||
|
{
|
||
|
if(ai_HealSickness(oCreature, oCommander, oMaster, nCommand)) return;
|
||
|
}
|
||
|
// A friend sees an enemy. If we are not in combat lets seek them out too!
|
||
|
else if(nCommand == AI_ALLY_SEES_AN_ENEMY ||
|
||
|
nCommand == AI_ALLY_HEARD_AN_ENEMY)
|
||
|
{
|
||
|
if(AI_DEBUG) ai_Debug("0i_associates", "282", GetName(oCreature) + " receives notice that " +
|
||
|
GetName(oCommander) + " has seen1/heard2(" + IntToString(nCommand) + " an enemy: " +
|
||
|
GetName(GetLocalObject(oCommander, AI_MY_TARGET)) + "!");
|
||
|
ai_ReactToAssociate(oCreature, oCommander, FALSE);
|
||
|
return;
|
||
|
}
|
||
|
// A friend is in combat. Make some checks to see if we should help.
|
||
|
else if(nCommand == AI_ALLY_ATKED_BY_WEAPON ||
|
||
|
nCommand == AI_ALLY_ATKED_BY_SPELL)
|
||
|
{
|
||
|
if(AI_DEBUG) ai_Debug("0i_associates", "291", GetName(oCreature) + " receives notice that " +
|
||
|
GetName(oCommander) + " was attacked by an enemy!" +
|
||
|
GetName(GetLocalObject(oCommander, AI_MY_TARGET)) + "!");
|
||
|
ai_ReactToAssociate(oCreature, oCommander, FALSE);
|
||
|
return;
|
||
|
}
|
||
|
else if(nCommand == AI_ALLY_IS_DEAD)
|
||
|
{ // Nothing at the moment.
|
||
|
if(AI_DEBUG) ai_Debug("0i_associates", "298", GetName(oCreature) + " receives notice that " +
|
||
|
GetName(oCommander) + " has died!");
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
switch(nCommand)
|
||
|
{
|
||
|
case ASSOCIATE_COMMAND_MASTERATTACKEDOTHER:
|
||
|
{
|
||
|
if(AI_DEBUG) ai_Debug("0i_associate", "307", GetName(oMaster) + " has attacked!");
|
||
|
if(ai_CanIAttack(oCreature))
|
||
|
{
|
||
|
if(ai_GetIsInCombat(oCreature)) ai_DoAssociateCombatRound(oCreature);
|
||
|
else ai_FindTheEnemy(oCreature, oCommander, ai_GetAttackedTarget(oCommander, TRUE, TRUE), FALSE);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
// Master tried to open a door or chest that is locked.
|
||
|
case ASSOCIATE_COMMAND_MASTERFAILEDLOCKPICK:
|
||
|
{
|
||
|
// In command mode we let the player tell us what to do.
|
||
|
if(ai_CanIAttack(oCreature))
|
||
|
{
|
||
|
object oLock = ai_GetNearestLockedObject(oMaster);
|
||
|
//Check and see if our master want's us to open locks.
|
||
|
if(ai_GetAIMode(oCreature, AI_MODE_PICK_LOCKS) ||
|
||
|
ai_GetAIMode(oCreature, AI_MODE_BASH_LOCKS))
|
||
|
{
|
||
|
ai_SetAIMode(oCreature, AI_MODE_SCOUT_AHEAD, FALSE);
|
||
|
ai_SetAIMode(oCreature, AI_MODE_STAND_GROUND, FALSE);
|
||
|
ai_AttemptToByPassLock(oCreature, oLock);
|
||
|
}
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
// Master saw a trap.
|
||
|
case ASSOCIATE_COMMAND_MASTERSAWTRAP:
|
||
|
{
|
||
|
// In command mode we let the player tell us what to do.
|
||
|
if(ai_CanIAttack(oCreature))
|
||
|
{
|
||
|
object oTrap = GetLastTrapDetected(oMaster);
|
||
|
// Sometimes GetLastTrapDetected seems to fail.
|
||
|
if(oTrap == OBJECT_INVALID) oTrap = GetNearestTrapToObject(oMaster, TRUE);
|
||
|
//Check and see if our master want's us to disarm the trap.
|
||
|
ai_SetAIMode(oCreature, AI_MODE_SCOUT_AHEAD, FALSE);
|
||
|
ai_SetAIMode(oCreature, AI_MODE_STAND_GROUND, FALSE);
|
||
|
SetTrapDetectedBy(oTrap, oCreature);
|
||
|
ai_ReactToTrap(oCreature, oTrap);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
// Menu used by a player to toggle henchmans search on and off.
|
||
|
case ASSOCIATE_COMMAND_TOGGLESEARCH:
|
||
|
{
|
||
|
int bTurnOn = !ai_GetAIMode(oCreature, AI_MODE_AGGRESSIVE_SEARCH);
|
||
|
string sAssociateType = ai_GetAssociateType(oMaster, oCreature);
|
||
|
ai_Philos_SetSearch(oMaster, oCreature, sAssociateType, bTurnOn);
|
||
|
return;
|
||
|
}
|
||
|
// Menu used by a player to toggle henchmans stealth on and off.
|
||
|
case ASSOCIATE_COMMAND_TOGGLESTEALTH:
|
||
|
{
|
||
|
int bTurnOn = !ai_GetAIMode(oCreature, AI_MODE_AGGRESSIVE_STEALTH);
|
||
|
string sAssociateType = ai_GetAssociateType(oMaster, oCreature);
|
||
|
ai_Philos_SetStealth(oMaster, oCreature, sAssociateType, bTurnOn);
|
||
|
return;
|
||
|
}
|
||
|
// Menu used by a player to have the henchman try to bypass the nearest lock.
|
||
|
case ASSOCIATE_COMMAND_PICKLOCK:
|
||
|
{
|
||
|
ai_SetAIMode(oCreature, AI_MODE_DEFEND_MASTER, FALSE);
|
||
|
ai_SetAIMode(oCreature, AI_MODE_SCOUT_AHEAD, FALSE);
|
||
|
ai_SetAIMode(oCreature, AI_MODE_STAND_GROUND, FALSE);
|
||
|
ai_SetAIMode(oCreature, AI_MODE_FOLLOW, FALSE);
|
||
|
object oLock = ai_GetNearestLockedObject(oMaster);
|
||
|
// Clear locked variable incase we tried already.
|
||
|
string sID = ObjectToString(oCreature);
|
||
|
SetLocalInt(oLock, "AI_LOCKED_" + sID, FALSE);
|
||
|
ai_AttemptToByPassLock(oCreature, oLock);
|
||
|
aiSaveAssociateModesToDb(oMaster, oCreature);
|
||
|
return;
|
||
|
}
|
||
|
// Menu used by a player to have the henchman try to disarm the nearest trap.
|
||
|
case ASSOCIATE_COMMAND_DISARMTRAP:
|
||
|
{
|
||
|
ai_SetAIMode(oCreature, AI_MODE_DEFEND_MASTER, FALSE);
|
||
|
ai_SetAIMode(oCreature, AI_MODE_SCOUT_AHEAD, FALSE);
|
||
|
ai_SetAIMode(oCreature, AI_MODE_STAND_GROUND, FALSE);
|
||
|
ai_SetAIMode(oCreature, AI_MODE_FOLLOW, FALSE);
|
||
|
object oTrap = GetNearestTrapToObject(oMaster);
|
||
|
// Clear trapped variable incase we tried already.
|
||
|
string sID = ObjectToString(oCreature);
|
||
|
ai_ReactToTrap(oCreature, oTrap, TRUE);
|
||
|
aiSaveAssociateModesToDb(oMaster, oCreature);
|
||
|
return;
|
||
|
}
|
||
|
// Menu used by a player to open a henchmans inventory to give, move, or take.
|
||
|
case ASSOCIATE_COMMAND_INVENTORY:
|
||
|
{
|
||
|
if(AI_OPEN_INVENTORY)
|
||
|
{
|
||
|
ai_HaveCreatureSpeak(oCreature, 4, ":29:46:35:");
|
||
|
OpenInventory(oCreature, oCommander);
|
||
|
}
|
||
|
// Can't look at an associate's inventory.
|
||
|
else
|
||
|
{
|
||
|
ai_HaveCreatureSpeak(oCreature, 6, ":47:30:36:8:48:");
|
||
|
ai_SendMessages("You cannot open " + GetName(oCreature) + "'s inventory.", AI_COLOR_GRAY, oMaster);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
case ASSOCIATE_COMMAND_LEAVEPARTY:
|
||
|
{
|
||
|
if(AI_REMOVE_HENCHMAN_ON)
|
||
|
{
|
||
|
ai_ClearCreatureActions();
|
||
|
ai_FireHenchman (GetPCSpeaker(), oCreature);
|
||
|
PlayVoiceChat (VOICE_CHAT_GOODBYE, oCreature);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
void ai_PassActionToAssociates(object oCreature, int nAction, int bStatus = TRUE)
|
||
|
{
|
||
|
int nAssociateType;
|
||
|
object oAssociate;
|
||
|
for(nAssociateType = 2; nAssociateType < 6; nAssociateType ++)
|
||
|
{
|
||
|
oAssociate = GetAssociate(nAssociateType);
|
||
|
if(oAssociate != OBJECT_INVALID) SetActionMode(oAssociate, nAction, bStatus);
|
||
|
}
|
||
|
}
|
||
|
void ai_PassToAssociate(object oAssociate, int nAIMode, int bStatus)
|
||
|
{
|
||
|
ai_ClearCreatureActions(TRUE);
|
||
|
ai_SetAIMode(oAssociate, nAIMode, bStatus);
|
||
|
}
|
||
|
void ai_PassAIModeToAssociates(object oAssociate, int nAIMode, int bStatus = TRUE)
|
||
|
{
|
||
|
ai_SetAIMode(oAssociate, nAIMode, bStatus);
|
||
|
int nAssociateType;
|
||
|
object oAssoc;
|
||
|
for(nAssociateType = 2; nAssociateType < 6; nAssociateType ++)
|
||
|
{
|
||
|
oAssoc = GetAssociate(nAssociateType, oAssociate);
|
||
|
if(oAssoc != OBJECT_INVALID) AssignCommand(oAssoc, ai_PassToAssociate(oAssoc, nAIMode, bStatus));
|
||
|
}
|
||
|
}
|
||
|
void ai_SetAssociateAIScript(object oCreature, int bCheckTacticScripts = TRUE)
|
||
|
{
|
||
|
string sCombatAI;
|
||
|
object oMaster = GetMaster();
|
||
|
if(ai_GetIsCharacter(oMaster))
|
||
|
{
|
||
|
string sAssociateType = ai_GetAssociateType(oMaster, oCreature);
|
||
|
json jAIData = ai_GetAssociateDbJson(oMaster, sAssociateType, "aidata");
|
||
|
sCombatAI = JsonGetString(JsonArrayGet(jAIData, 8));
|
||
|
}
|
||
|
else sCombatAI = GetLocalString(oCreature, AI_DEFAULT_SCRIPT);
|
||
|
int nAssociateType = GetAssociateType(oCreature);
|
||
|
if (nAssociateType == ASSOCIATE_TYPE_FAMILIAR && sCombatAI == "")
|
||
|
{
|
||
|
sCombatAI = "ai_a_default";
|
||
|
}
|
||
|
else if(sCombatAI == "ai_coward")
|
||
|
{
|
||
|
SetLocalString(oCreature, AI_COMBAT_SCRIPT, sCombatAI);
|
||
|
return;
|
||
|
}
|
||
|
else if(bCheckTacticScripts && GetLocalInt(GetModule(), AI_RULE_AMBUSH))
|
||
|
{
|
||
|
// They should have a skill ranks equal to their level + 1 to use a special AI.
|
||
|
int nSkillNeeded = GetHitDice(oCreature) + 1;
|
||
|
if(sCombatAI == "" || sCombatAI == "ai_a_ambusher")
|
||
|
{
|
||
|
// Ambusher: requires either Improved Invisibility or Invisibility.
|
||
|
if(GetHasSpell(SPELL_IMPROVED_INVISIBILITY, oCreature) ||
|
||
|
GetHasSpell(SPELL_INVISIBILITY, oCreature))
|
||
|
{
|
||
|
int bCast = ai_TryToCastSpell(oCreature, SPELL_IMPROVED_INVISIBILITY, oCreature);
|
||
|
if(!bCast) bCast = ai_TryToCastSpell(oCreature, SPELL_INVISIBILITY, oCreature);
|
||
|
if(bCast)
|
||
|
{
|
||
|
SetLocalString(oCreature, AI_COMBAT_SCRIPT, "ai_a_ambusher");
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
// Ambusher: Requires a Hide and Move silently skill equal to your level + 1.
|
||
|
else if(GetSkillRank(SKILL_HIDE, oCreature) >= nSkillNeeded &&
|
||
|
GetSkillRank(SKILL_MOVE_SILENTLY, oCreature) >= nSkillNeeded)
|
||
|
{
|
||
|
SetLocalString(oCreature, AI_COMBAT_SCRIPT, "ai_a_ambusher");
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
// Defensive : requires Parry skill equal to your level or Expertise.
|
||
|
else if(sCombatAI == "ai_a_defensive" ||
|
||
|
(sCombatAI == "" &&
|
||
|
(GetSkillRank(SKILL_PARRY, oCreature) >= nSkillNeeded ||
|
||
|
GetHasFeat(FEAT_EXPERTISE, oCreature) ||
|
||
|
GetHasFeat(FEAT_IMPROVED_EXPERTISE, oCreature))))
|
||
|
{
|
||
|
SetLocalString(oCreature, AI_COMBAT_SCRIPT, "ai_a_defensive");
|
||
|
return;
|
||
|
}
|
||
|
else if(sCombatAI == "ai_cntrspell" || GetHasSpell(SPELL_LESSER_DISPEL, oCreature) ||
|
||
|
GetHasSpell(SPELL_DISPEL_MAGIC, oCreature) || GetHasSpell(SPELL_GREATER_DISPELLING, oCreature))
|
||
|
{
|
||
|
SetLocalString(oCreature, AI_COMBAT_SCRIPT, "ai_cntrspell");
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
if(sCombatAI == "")
|
||
|
{
|
||
|
// Select the best ai for this henchmen based on class.
|
||
|
int nClass = GetClassByPosition(1, oCreature);
|
||
|
// If they have more than one class use the default ai.
|
||
|
if(GetClassByPosition(2, oCreature) != CLASS_TYPE_INVALID) sCombatAI = "ai_a_default";
|
||
|
else if(nClass == CLASS_TYPE_BARBARIAN) sCombatAI = "ai_a_barbarian";
|
||
|
else if(nClass == CLASS_TYPE_BARD) sCombatAI = "ai_a_bard";
|
||
|
else if(nClass == CLASS_TYPE_CLERIC) sCombatAI = "ai_a_cleric";
|
||
|
else if(nClass == CLASS_TYPE_DRUID) sCombatAI = "ai_a_druid";
|
||
|
else if(nClass == CLASS_TYPE_FIGHTER) sCombatAI = "ai_a_fighter";
|
||
|
else if(nClass == CLASS_TYPE_MONK) sCombatAI = "ai_a_monk";
|
||
|
else if(nClass == CLASS_TYPE_PALADIN) sCombatAI = "ai_a_paladin";
|
||
|
else if(nClass == CLASS_TYPE_RANGER) sCombatAI = "ai_a_ranger";
|
||
|
else if(nClass == CLASS_TYPE_ROGUE) sCombatAI = "ai_a_rogue";
|
||
|
else if(nClass == CLASS_TYPE_SORCERER) sCombatAI = "ai_a_sorcerer";
|
||
|
else if(nClass == CLASS_TYPE_WIZARD) sCombatAI = "ai_a_wizard";
|
||
|
//else if(nClass == CLASS_TYPE_ABERRATION) sCombatAI = "ai_a_default";
|
||
|
//else if(nClass == CLASS_TYPE_ANIMAL) sCombatAI = "ai_a_animal";
|
||
|
//else if(nClass == CLASS_TYPE_CONSTRUCT) sCombatAI = "ai_a_animal";
|
||
|
//else if(nClass == CLASS_TYPE_DRAGON) sCombatAI = "ai_a_dragon";
|
||
|
//else if(nClass == CLASS_TYPE_ELEMENTAL) sCombatAI = "ai_a_default";
|
||
|
//else if(nClass == CLASS_TYPE_FEY) sCombatAI = "ai_a_default";
|
||
|
//else if(nClass == CLASS_TYPE_GIANT) sCombatAI = "ai_a_default";
|
||
|
//else if(nClass == CLASS_TYPE_HUMANOID) sCombatAI = "ai_a_default";
|
||
|
//else if(nClass == CLASS_TYPE_MAGICAL_BEAST) sCombatAI = "ai_a_default";
|
||
|
//else if(nClass == CLASS_TYPE_MONSTROUS) sCombatAI = "ai_a_default";
|
||
|
//else if(nClass == CLASS_TYPE_OOZE) sCombatAI = "ai_a_default";
|
||
|
//else if(nClass == CLASS_TYPE_OUTSIDER) sCombatAI = "ai_a_default";
|
||
|
//else if(nClass == CLASS_TYPE_UNDEAD) sCombatAI = "ai_a_default";
|
||
|
//else if(nClass == CLASS_TYPE_VERMIN) sCombatAI = "ai_a_animal";
|
||
|
else sCombatAI = "ai_a_default";
|
||
|
}
|
||
|
if(AI_DEBUG) ai_Debug("0i_associates", "530", GetName(oCreature) + " is setting AI to " + sCombatAI);
|
||
|
SetLocalString(oCreature, AI_COMBAT_SCRIPT, sCombatAI);
|
||
|
SetLocalString(oCreature, AI_DEFAULT_SCRIPT, sCombatAI);
|
||
|
}
|
||
|
int ai_CanISpeak (object oCreature)
|
||
|
{
|
||
|
int nRace = GetRacialType (oCreature);
|
||
|
if (nRace == RACIAL_TYPE_ANIMAL || nRace == RACIAL_TYPE_BEAST ||
|
||
|
nRace == RACIAL_TYPE_CONSTRUCT || nRace == RACIAL_TYPE_OOZE) return FALSE;
|
||
|
return (GetAbilityScore (oCreature, ABILITY_INTELLIGENCE) > 7);
|
||
|
}
|
||
|
void ai_FireHenchman(object oPC, object oHenchman)
|
||
|
{
|
||
|
if(oPC == OBJECT_INVALID || oHenchman == OBJECT_INVALID) return;
|
||
|
// Now double-check that this is actually our master
|
||
|
if(GetMaster(oHenchman) != oPC) return;
|
||
|
// Turn off stealth mode
|
||
|
SetActionMode(oHenchman, ACTION_MODE_STEALTH, FALSE);
|
||
|
// Remove the henchman
|
||
|
RemoveHenchman (oPC, oHenchman);
|
||
|
ChangeToStandardFaction(oHenchman, STANDARD_FACTION_DEFENDER);
|
||
|
}
|
||
|
void ai_HenchmanCastDefensiveSpells (object oCreature, object oPC)
|
||
|
{
|
||
|
ai_CastBuffs(oCreature, 3, 0, oPC);
|
||
|
}
|
||
|
int ai_CheckForCombat(object oCreature, int bMonster)
|
||
|
{
|
||
|
object oEnemy = ai_GetNearestEnemy(oCreature, 1, 7, 7, 7, 5, TRUE);
|
||
|
//object oEnemy = ai_GetNearestEnemy(oCreature, 1, -1, -1, -1, -1, TRUE);
|
||
|
if(AI_DEBUG) ai_Debug("0i_associate", "586", "Checking for Combat: oEnemy is " + GetName(oEnemy) +
|
||
|
" Distance: " + FloatToString(GetDistanceBetween(oEnemy, oCreature), 0, 2));
|
||
|
if(oEnemy != OBJECT_INVALID)
|
||
|
{
|
||
|
float fPerceptionDistance, fDistance;
|
||
|
if(bMonster)
|
||
|
{
|
||
|
fDistance = GetDistanceBetween(oCreature, oEnemy);
|
||
|
fPerceptionDistance = GetLocalFloat(GetModule(), AI_RULE_PERCEPTION_DISTANCE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// We want to use the distance between the PC and target not us.
|
||
|
object oMaster = GetMaster();
|
||
|
if(oMaster != OBJECT_INVALID) fDistance = GetDistanceBetween(oMaster, oEnemy);
|
||
|
else fDistance = GetDistanceBetween(oCreature, oEnemy);
|
||
|
fPerceptionDistance = GetLocalFloat(oCreature, AI_ASSOC_PERCEPTION_DISTANCE);
|
||
|
if(fPerceptionDistance == 0.0) fPerceptionDistance = 20.0;
|
||
|
}
|
||
|
if(fDistance < fPerceptionDistance)
|
||
|
{
|
||
|
ai_HaveCreatureSpeak(oCreature, 5, ":0:1:2:3:6:");
|
||
|
SetLocalObject (oCreature, AI_MY_TARGET, oEnemy);
|
||
|
SpeakString(AI_I_SEE_AN_ENEMY, TALKVOLUME_SILENT_TALK);
|
||
|
if(bMonster) ai_StartMonsterCombat(oCreature);
|
||
|
else if(ai_CanIAttack(oCreature)) ai_StartAssociateCombat(oCreature);
|
||
|
return TRUE;
|
||
|
}
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
void ai_AssociateEvaluateNewThreat(object oCreature, object oLastPerceived, string sPerception)
|
||
|
{
|
||
|
if(!ai_CanIAttack(oCreature)) return;
|
||
|
int nAction = GetCurrentAction(oCreature);
|
||
|
if(AI_DEBUG) ai_Debug("0i_associates", "775", "Our current action: " + IntToString(nAction));
|
||
|
switch(nAction)
|
||
|
{
|
||
|
// These actions are uninteruptable.
|
||
|
case ACTION_CASTSPELL :
|
||
|
case ACTION_ITEMCASTSPELL :
|
||
|
case ACTION_COUNTERSPELL : return;
|
||
|
// Might be doing a special action that is not a defined action.
|
||
|
case ACTION_INVALID :
|
||
|
{
|
||
|
int nCombatWait = GetLocalInt(oCreature, AI_COMBAT_WAIT_IN_SECONDS);
|
||
|
if(AI_DEBUG) ai_Debug("0i_associate", "761", "Doing a special action (nCombatWait): " + IntToString(nCombatWait));
|
||
|
if(nCombatWait)
|
||
|
{
|
||
|
if(ai_IsInCombatRound(oCreature, nCombatWait)) return;
|
||
|
DeleteLocalInt(oCreature, AI_COMBAT_WAIT_IN_SECONDS);
|
||
|
}
|
||
|
}
|
||
|
// We need to reevaluate combat during these actions when we see a new enemy.
|
||
|
//case ACTION_ATTACKOBJECT :
|
||
|
//case ACTION_MOVETOPOINT :
|
||
|
}
|
||
|
if(ai_GetIsInCombat(oCreature))
|
||
|
{
|
||
|
object oTarget = ai_GetAttackedTarget(oCreature);
|
||
|
if(AI_DEBUG) ai_Debug("0i_associates", "775", "Should we recalculate our combat round? oTarget: " + GetName(oTarget) +
|
||
|
" oTarget Distance: " + FloatToString(GetDistanceBetween(oCreature, oTarget), 0, 2) +
|
||
|
" oLastPerceived Distance: " + FloatToString(GetDistanceBetween(oCreature, oLastPerceived), 0, 2));
|
||
|
// If the LastPerceived is our target then don't recalculate.
|
||
|
if(oTarget == oLastPerceived) return;
|
||
|
// If we don't have a target or the lastperceived is closer than our
|
||
|
// target then recalculate.
|
||
|
if(oTarget == OBJECT_INVALID ||
|
||
|
GetDistanceBetween(oCreature, oTarget) > GetDistanceBetween(oCreature, oLastPerceived))
|
||
|
{
|
||
|
// We should clear any skill cooldowns that are at at max since that means they were skipped.
|
||
|
if(GetLocalInt(oCreature, "AI_EMPATHY_COOLDOWN") == AI_EMPATHY_COOLDOWN)
|
||
|
{ DeleteLocalInt(oCreature, "AI_EMPATHY_COOLDOWN"); }
|
||
|
else if (GetLocalInt(oCreature, "AI_TAUNT_COOLDOWN") == AI_TAUNT_COOLDOWN)
|
||
|
{ DeleteLocalInt(oCreature, "AI_EMPATHY_COOLDOWN"); }
|
||
|
ai_DoAssociateCombatRound(oCreature);
|
||
|
return;
|
||
|
}
|
||
|
// Lets only reevaluate combat if the new enemy is more powerful
|
||
|
// than the average enemies we already know about.
|
||
|
int nPower = ai_GetCharacterLevels(oLastPerceived) / 2;
|
||
|
int nEnemyPower = GetLocalInt(oCreature, AI_ENEMY_POWER) / (GetLocalInt(oCreature, AI_ENEMY_NUMBERS) + 1);
|
||
|
if(AI_DEBUG) ai_Debug("0i_associates", "797", " Is the new opponent more powerful? " +
|
||
|
GetName(oLastPerceived) + " nPower: " + IntToString(nPower) +
|
||
|
" nEnemyPower: " + IntToString(nEnemyPower));
|
||
|
if(nEnemyPower < nPower) ai_DoAssociateCombatRound(oCreature);
|
||
|
return;
|
||
|
}
|
||
|
// Heard fires first, but Heard and Seen are both set at the same time.
|
||
|
// So lets skip the hearing code if they are also seen.
|
||
|
if(sPerception == AI_I_SEE_AN_ENEMY || GetObjectSeen(oLastPerceived, oCreature))
|
||
|
{
|
||
|
// We are not in combat and we see the enemy so alert our allies!
|
||
|
ai_HaveCreatureSpeak(oCreature, 5, ":0:1:2:3:6:");
|
||
|
SetLocalObject (oCreature, AI_MY_TARGET, oLastPerceived);
|
||
|
SpeakString(sPerception, TALKVOLUME_SILENT_TALK);
|
||
|
ai_StartAssociateCombat(oCreature);
|
||
|
}
|
||
|
else ai_FindTheEnemy(oCreature, oLastPerceived, oLastPerceived, FALSE);
|
||
|
}
|
||
|
void ai_MonsterEvaluateNewThreat(object oCreature, object oLastPerceived, string sPerception)
|
||
|
{
|
||
|
if(!ai_CanIAttack(oCreature)) return;
|
||
|
int nAction = GetCurrentAction(oCreature);
|
||
|
if(AI_DEBUG) ai_Debug("0i_associates", "672", "nAction: " + IntToString(nAction));
|
||
|
switch(nAction)
|
||
|
{
|
||
|
// These actions are uninteruptable.
|
||
|
case ACTION_CASTSPELL :
|
||
|
case ACTION_ITEMCASTSPELL :
|
||
|
case ACTION_COUNTERSPELL : return;
|
||
|
// Might be doing a special action that is not a defined action.
|
||
|
case ACTION_INVALID :
|
||
|
{
|
||
|
int nCombatWait = GetLocalInt(oCreature, AI_COMBAT_WAIT_IN_SECONDS);
|
||
|
if(AI_DEBUG) ai_Debug("0i_associates", "683", "nCombatWait: " + IntToString(nCombatWait));
|
||
|
if(nCombatWait)
|
||
|
{
|
||
|
if(ai_IsInCombatRound(oCreature, nCombatWait)) return;
|
||
|
DeleteLocalInt(oCreature, AI_COMBAT_WAIT_IN_SECONDS);
|
||
|
}
|
||
|
}
|
||
|
// We need to reevaluate combat during these actions when we see a new enemy.
|
||
|
//case ACTION_ATTACKOBJECT :
|
||
|
//case ACTION_MOVETOPOINT :
|
||
|
}
|
||
|
if(ai_GetIsInCombat(oCreature))
|
||
|
{
|
||
|
object oTarget = ai_GetAttackedTarget(oCreature);
|
||
|
if(AI_DEBUG) ai_Debug("0i_associates", "697", "oTarget: " + GetName(oTarget) +
|
||
|
" oTarget Distance: " + FloatToString(GetDistanceBetween(oCreature, oTarget), 0, 2) +
|
||
|
" oLastPerceived Distance: " + FloatToString(GetDistanceBetween(oCreature, oLastPerceived), 0, 2));
|
||
|
// If the LastPerceived is our target then don't recalculate.
|
||
|
if(oTarget == oLastPerceived) return;
|
||
|
// If we don't have a target or the lastperceived is closer than our
|
||
|
// target then recalculate.
|
||
|
if(oTarget == OBJECT_INVALID ||
|
||
|
GetDistanceBetween(oCreature, oTarget) > GetDistanceBetween(oCreature, oLastPerceived))
|
||
|
{
|
||
|
ai_DoMonsterCombatRound(oCreature);
|
||
|
return;
|
||
|
}
|
||
|
// Now only reevaluate combat if the new enemy is more powerful
|
||
|
// than the average enemies we already know about.
|
||
|
int nPower = ai_GetCharacterLevels(oLastPerceived) / 2;
|
||
|
int nEnemyPower = GetLocalInt(oCreature, AI_ENEMY_POWER) / (GetLocalInt(oCreature, AI_ENEMY_NUMBERS) + 1);
|
||
|
if(AI_DEBUG) ai_Debug("0i_associates", "714", GetName(oLastPerceived) + " nPower: " + IntToString(nPower) +
|
||
|
" nEnemyPower: " + IntToString(nEnemyPower));
|
||
|
if(nEnemyPower < nPower) ai_DoMonsterCombatRound(oCreature);
|
||
|
return;
|
||
|
}
|
||
|
if(sPerception == AI_I_SEE_AN_ENEMY)
|
||
|
{
|
||
|
if(d100() < 34)
|
||
|
{
|
||
|
// We are not in combat so alert our allies!
|
||
|
ai_HaveCreatureSpeak(oCreature, 5, ":0:1:2:3:6:");
|
||
|
}
|
||
|
SetLocalObject(oCreature, AI_MY_TARGET, oLastPerceived);
|
||
|
SpeakString(sPerception, TALKVOLUME_SILENT_TALK);
|
||
|
ai_StartMonsterCombat(oCreature);
|
||
|
}
|
||
|
else ai_FindTheEnemy(oCreature, oLastPerceived, oLastPerceived, TRUE);
|
||
|
}
|
||
|
void ai_CopyObjectVariables(object oOldObject, object oNewObject)
|
||
|
{
|
||
|
json jObject = ObjectToJson(oOldObject, TRUE);
|
||
|
json jVarTable = GffGetList(jObject, "VarTable");
|
||
|
string sVariable, sName;
|
||
|
int nIndex, nVarType;
|
||
|
json jVar = JsonArrayGet(jVarTable, nIndex);
|
||
|
while(JsonGetType(jVar) != JSON_TYPE_NULL)
|
||
|
{
|
||
|
sName = JsonGetString(GffGetString(jVar, "Name"));
|
||
|
nVarType = JsonGetInt(GffGetDword(jVar, "Type"));
|
||
|
if(nVarType == 1) SetLocalInt(oNewObject, sName, JsonGetInt(GffGetInt(jVar, "Value")));
|
||
|
else if(nVarType == 2) SetLocalFloat(oNewObject, sName, JsonGetFloat(GffGetFloat(jVar, "Value")));
|
||
|
else if(nVarType == 3) SetLocalString(oNewObject, sName, JsonGetString(GffGetString(jVar, "Value")));
|
||
|
jVar = JsonArrayGet(jVarTable, ++nIndex);
|
||
|
}
|
||
|
}
|
||
|
//******************************************************************************
|
||
|
//********************* Creature event scripts *********************************
|
||
|
//******************************************************************************
|
||
|
|
||
|
void ai_OnRested(object oCreature)
|
||
|
{
|
||
|
if(ai_GetMagicMode(oCreature, AI_MAGIC_BUFF_AFTER_REST))
|
||
|
{
|
||
|
int nLevel = ai_GetCharacterLevels(oCreature);
|
||
|
float fDelay = StringToFloat(Get2DAString("restduration", "DURATION", nLevel));
|
||
|
fDelay = (fDelay / 1000.0f) + 2.0f;
|
||
|
DelayCommand(fDelay, ai_HenchmanCastDefensiveSpells(oCreature, GetMaster()));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//******************************************************************************
|
||
|
//******************* Associate AI option scripts ******************************
|
||
|
//******************************************************************************
|
||
|
void ai_UpdateToolTipUI(object oPC, string sWindowID1, string sWindowID2, string sToolTipBind, string sText)
|
||
|
{
|
||
|
int nMenuToken = NuiFindWindow(oPC, sWindowID1);
|
||
|
if(nMenuToken) NuiSetBind (oPC, nMenuToken, sToolTipBind, JsonString (sText));
|
||
|
if(sWindowID2 != "")
|
||
|
{
|
||
|
int nWidgetToken = NuiFindWindow(oPC, sWindowID2);
|
||
|
if(nWidgetToken) NuiSetBind (oPC, nWidgetToken, sToolTipBind, JsonString (sText));
|
||
|
}
|
||
|
}
|
||
|
void ai_FollowIncrement(object oPC, object oAssociate, float fIncrement, string sAssociateType)
|
||
|
{
|
||
|
float fAdjustment = GetLocalFloat(oAssociate, AI_FOLLOW_RANGE) + fIncrement;
|
||
|
if(fAdjustment > 10.0) fAdjustment = 10.0;
|
||
|
else if(fAdjustment < 1.0) fAdjustment = 1.0;
|
||
|
SetLocalFloat(oAssociate, AI_FOLLOW_RANGE, fAdjustment);
|
||
|
json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata");
|
||
|
jAIData = JsonArraySet(jAIData, 6, JsonFloat(fAdjustment));
|
||
|
ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData);
|
||
|
string sName;
|
||
|
object oTarget = GetLocalObject(oAssociate, AI_FOLLOW_TARGET);
|
||
|
string sTarget;
|
||
|
if(oTarget != OBJECT_INVALID) sTarget = GetName(oTarget);
|
||
|
else
|
||
|
{
|
||
|
if(ai_GetIsCharacter(oAssociate)) sTarget = "nobody";
|
||
|
else sTarget = GetName(oPC);
|
||
|
}
|
||
|
float fRange = fAdjustment +
|
||
|
StringToFloat(Get2DAString("appearance", "PREFATCKDIST", GetAppearanceType(oAssociate)));
|
||
|
string sRange = FloatToString(fRange, 0, 0);
|
||
|
if(oPC == oAssociate)
|
||
|
{
|
||
|
sName = " All associates";
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_cmd_follow_tooltip", sName + " enter follow mode ");
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_follow_target_tooltip", " " + GetName(oAssociate) + " following " + sTarget + " [" + sRange + " meters]");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sName = " " + GetName(oAssociate);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_cmd_follow_tooltip", sName + " enter follow mode [" + sRange + " meters]");
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_follow_target_tooltip", " " + GetName(oAssociate) + " following " + sTarget + " [" + sRange + " meters]");
|
||
|
}
|
||
|
}
|
||
|
void ai_Ranged(object oPC, object oAssociate, string sAssociateType)
|
||
|
{
|
||
|
//ai_ClearCreatureActions();
|
||
|
if(ai_GetAIMode(oAssociate, AI_MODE_STOP_RANGED))
|
||
|
{
|
||
|
ai_SendMessages(GetName(oAssociate) + " is using ranged combat.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_ranged_tooltip", " Ranged On");
|
||
|
ai_SetAIMode(oAssociate, AI_MODE_STOP_RANGED, FALSE);
|
||
|
ai_EquipBestRangedWeapon(oAssociate);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ai_SendMessages(GetName(oAssociate) + " is using melee combat only.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_ranged_tooltip", " Ranged Off");
|
||
|
ai_SetAIMode(oAssociate, AI_MODE_STOP_RANGED, TRUE);
|
||
|
ai_EquipBestMeleeWeapon(oAssociate);
|
||
|
}
|
||
|
aiSaveAssociateModesToDb(oPC, oAssociate);
|
||
|
}
|
||
|
void ai_EquipWeapons(object oPC, object oAssociate, string sAssociateType)
|
||
|
{
|
||
|
if(ai_GetAIMode(oAssociate, AI_MODE_EQUIP_WEAPON_OFF))
|
||
|
{
|
||
|
ai_SendMessages(GetName(oAssociate) + " will be equiping their best weapons.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_equip_weapon_tooltip", " Equiping Best Weapons On");
|
||
|
ai_SetAIMode(oAssociate, AI_MODE_EQUIP_WEAPON_OFF, FALSE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ai_SendMessages(GetName(oAssociate) + " will not equip their best weapons.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_equip_weapon_tooltip", " Equiping Best Weapons Off");
|
||
|
ai_SetAIMode(oAssociate, AI_MODE_EQUIP_WEAPON_OFF, TRUE);
|
||
|
}
|
||
|
aiSaveAssociateModesToDb(oPC, oAssociate);
|
||
|
}
|
||
|
void ai_Search(object oPC, object oAssociate, string sAssociateType)
|
||
|
{
|
||
|
if(ai_GetAIMode(oAssociate, AI_MODE_AGGRESSIVE_SEARCH))
|
||
|
{
|
||
|
ai_SendMessages(GetName(oAssociate) + " is turning search off.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_search_tooltip", " Search mode Off");
|
||
|
SetActionMode(oAssociate, ACTION_MODE_DETECT, FALSE);
|
||
|
ai_SetAIMode(oAssociate, AI_MODE_AGGRESSIVE_SEARCH, FALSE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ai_SendMessages(GetName(oAssociate) + " is turning search on.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_search_tooltip", " Search mode On");
|
||
|
SetActionMode(oAssociate, ACTION_MODE_DETECT, TRUE);
|
||
|
ai_SetAIMode(oAssociate, AI_MODE_AGGRESSIVE_SEARCH, TRUE);
|
||
|
}
|
||
|
aiSaveAssociateModesToDb(oPC, oAssociate);
|
||
|
}
|
||
|
void ai_Stealth(object oPC, object oAssociate, string sAssociateType)
|
||
|
{
|
||
|
if(ai_GetAIMode(oAssociate, AI_MODE_AGGRESSIVE_STEALTH))
|
||
|
{
|
||
|
ai_SendMessages(GetName(oAssociate) + " is turning stealth off.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_stealth_tooltip", " Stealth mode Off");
|
||
|
SetActionMode(oAssociate, ACTION_MODE_STEALTH, FALSE);
|
||
|
ai_SetAIMode(oAssociate, AI_MODE_AGGRESSIVE_STEALTH, FALSE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ai_SendMessages(GetName(oAssociate) + " is turning stealth on.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_stealth_tooltip", " Stealth mode On");
|
||
|
SetActionMode(oAssociate, ACTION_MODE_STEALTH, TRUE);
|
||
|
ai_SetAIMode(oAssociate, AI_MODE_AGGRESSIVE_STEALTH, TRUE);
|
||
|
}
|
||
|
aiSaveAssociateModesToDb(oPC, oAssociate);
|
||
|
}
|
||
|
void ai_OpenDoor(object oPC, object oAssociate, string sAssociateType)
|
||
|
{
|
||
|
string sRange = FloatToString(GetLocalFloat(oAssociate, AI_OPEN_DOORS_RANGE), 0, 0);
|
||
|
if(ai_GetAIMode(oAssociate, AI_MODE_OPEN_DOORS))
|
||
|
{
|
||
|
ai_SendMessages(GetName(oAssociate) + " is turning open doors off.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_open_door_tooltip", " Open Doors Off [" + sRange + " meters]");
|
||
|
ai_SetAIMode(oAssociate, AI_MODE_OPEN_DOORS, FALSE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ai_SendMessages(GetName(oAssociate) + " is turning open doors on.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_open_door_tooltip", " Open Doors On [" + sRange + " meters]");
|
||
|
ai_SetAIMode(oAssociate, AI_MODE_OPEN_DOORS, TRUE);
|
||
|
}
|
||
|
aiSaveAssociateModesToDb(oPC, oAssociate);
|
||
|
}
|
||
|
void ai_Locks(object oPC, object oAssociate, string sAssociateType, int nMode)
|
||
|
{
|
||
|
string sRange = FloatToString(GetLocalFloat(oAssociate, AI_LOCK_CHECK_RANGE), 0, 0);
|
||
|
if(nMode == 1)
|
||
|
{
|
||
|
if(ai_GetAIMode(oAssociate, AI_MODE_PICK_LOCKS))
|
||
|
{
|
||
|
ai_SendMessages(GetName(oAssociate) + " will stop picking locks.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_pick_locks_tooltip", " Pick Locks Off [" + sRange + " meters]");
|
||
|
ai_SetAIMode(oAssociate, AI_MODE_PICK_LOCKS, FALSE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ai_SendMessages(GetName(oAssociate) + " will now pick locks.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_pick_locks_tooltip", " Pick Locks On [" + sRange + " meters]");
|
||
|
ai_SetAIMode(oAssociate, AI_MODE_PICK_LOCKS, TRUE);
|
||
|
}
|
||
|
}
|
||
|
else if(nMode == 2)
|
||
|
{
|
||
|
if(ai_GetAIMode(oAssociate, AI_MODE_BASH_LOCKS))
|
||
|
{
|
||
|
ai_SendMessages(GetName(oAssociate) + " will stop bashing.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_bash_locks_tooltip", " Bash Locks Off [" + sRange + " meters]");
|
||
|
ai_SetAIMode(oAssociate, AI_MODE_BASH_LOCKS, FALSE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ai_SendMessages(GetName(oAssociate) + " will now bash things.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_bash_locks_tooltip", " Bash Locks On [" + sRange + " meters]");
|
||
|
ai_SetAIMode(oAssociate, AI_MODE_BASH_LOCKS, TRUE);
|
||
|
}
|
||
|
}
|
||
|
aiSaveAssociateModesToDb(oPC, oAssociate);
|
||
|
}
|
||
|
void ai_Traps(object oPC, object oAssociate, string sAssociateType)
|
||
|
{
|
||
|
string sRange = FloatToString(GetLocalFloat(oAssociate, AI_TRAP_CHECK_RANGE), 0, 0);
|
||
|
if(ai_GetAIMode(oAssociate, AI_MODE_DISARM_TRAPS))
|
||
|
{
|
||
|
ai_SendMessages(GetName(oAssociate) + " will stop disarming traps.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_traps_tooltip", " Disable Traps Off [" + sRange + " meters]");
|
||
|
ai_SetAIMode(oAssociate, AI_MODE_DISARM_TRAPS, FALSE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ai_SendMessages(GetName(oAssociate) + " will now disarm traps.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_traps_tooltip", " Disable Traps On [" + sRange + " meters]");
|
||
|
ai_SetAIMode(oAssociate, AI_MODE_DISARM_TRAPS, TRUE);
|
||
|
}
|
||
|
aiSaveAssociateModesToDb(oPC, oAssociate);
|
||
|
}
|
||
|
void ai_ReduceSpeech(object oPC, object oAssociate, string sAssociateType)
|
||
|
{
|
||
|
if(ai_GetAIMode(oAssociate, AI_MODE_DO_NOT_SPEAK))
|
||
|
{
|
||
|
ai_SendMessages(GetName(oAssociate) + " will increase speech.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_quiet_tooltip", " Reduced Speech Off");
|
||
|
ai_SetAIMode(oAssociate, AI_MODE_DO_NOT_SPEAK, FALSE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ai_SendMessages(GetName(oAssociate) + " will reduce speech.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_quiet_tooltip", " Reduced Speech On");
|
||
|
ai_SetAIMode(oAssociate, AI_MODE_DO_NOT_SPEAK, TRUE);
|
||
|
}
|
||
|
aiSaveAssociateModesToDb(oPC, oAssociate);
|
||
|
}
|
||
|
void ai_UseOffensiveMagic(object oPC, object oAssociate, int bDefensive, int bOffensive, string sAssociateType)
|
||
|
{
|
||
|
if(bOffensive)
|
||
|
{
|
||
|
if(ai_GetMagicMode(oAssociate, AI_MAGIC_OFFENSIVE_CASTING))
|
||
|
{
|
||
|
ai_SendMessages(GetName(oAssociate) + " has stopped using offensive magic in combat.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_off_magic_tooltip", " Offensive Magic Off");
|
||
|
ai_SetMagicMode(oAssociate, AI_MAGIC_OFFENSIVE_CASTING, FALSE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ai_SendMessages(GetName(oAssociate) + " is now using offensive magic in combat.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_off_magic_tooltip", " Offensive Magic On");
|
||
|
ai_SetMagicMode(oAssociate, AI_MAGIC_OFFENSIVE_CASTING, TRUE);
|
||
|
}
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_def_magic_tooltip", " Defensive Magic Off");
|
||
|
ai_SetMagicMode(oAssociate, AI_MAGIC_DEFENSIVE_CASTING, FALSE);
|
||
|
}
|
||
|
else if(bDefensive)
|
||
|
{
|
||
|
if(ai_GetMagicMode(oAssociate, AI_MAGIC_DEFENSIVE_CASTING))
|
||
|
{
|
||
|
ai_SendMessages(GetName(oAssociate) + " has stopped using defensive magic in combat.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_def_magic_tooltip", " Defensive Magic Off");
|
||
|
ai_SetMagicMode(oAssociate, AI_MAGIC_DEFENSIVE_CASTING, FALSE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ai_SendMessages(GetName(oAssociate) + " is now using defensive magic in combat.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_def_magic_tooltip", " Defensive Magic On");
|
||
|
ai_SetMagicMode(oAssociate, AI_MAGIC_DEFENSIVE_CASTING, TRUE);
|
||
|
}
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_off_magic_tooltip", " Offensive Magic Off");
|
||
|
ai_SetMagicMode(oAssociate, AI_MAGIC_OFFENSIVE_CASTING, FALSE);
|
||
|
}
|
||
|
aiSaveAssociateModesToDb(oPC, oAssociate);
|
||
|
}
|
||
|
void ai_UseMagic(object oPC, object oAssociate, string sAssociateType)
|
||
|
{
|
||
|
if(ai_GetMagicMode(oAssociate, AI_MAGIC_NO_MAGIC))
|
||
|
{
|
||
|
ai_SendMessages(GetName(oAssociate) + " is now using magic in combat.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_magic_tooltip", " Magic On");
|
||
|
ai_SetMagicMode(oAssociate, AI_MAGIC_NO_MAGIC, FALSE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ai_SendMessages(GetName(oAssociate) + " has stopped using magic in combat.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_magic_tooltip", " Magic Off");
|
||
|
ai_SetMagicMode(oAssociate, AI_MAGIC_NO_MAGIC, TRUE);
|
||
|
}
|
||
|
aiSaveAssociateModesToDb(oPC, oAssociate);
|
||
|
}
|
||
|
void ai_UseMagicItems(object oPC, object oAssociate, string sAssociateType)
|
||
|
{
|
||
|
if(ai_GetMagicMode(oAssociate, AI_MAGIC_NO_MAGIC_ITEMS))
|
||
|
{
|
||
|
ai_SendMessages(GetName(oAssociate) + " is now using magic items in combat.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_magic_items_tooltip", " Magic Items On");
|
||
|
ai_SetMagicMode(oAssociate, AI_MAGIC_NO_MAGIC_ITEMS, FALSE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ai_SendMessages(GetName(oAssociate) + " has stopped using magic items in combat.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_magic_items_tooltip", " Magic Items Off");
|
||
|
ai_SetMagicMode(oAssociate, AI_MAGIC_NO_MAGIC_ITEMS, TRUE);
|
||
|
}
|
||
|
aiSaveAssociateModesToDb(oPC, oAssociate);
|
||
|
}
|
||
|
void ai_Loot(object oPC, object oAssociate, string sAssociateType)
|
||
|
{
|
||
|
int bLooting = !ai_GetAIMode(oAssociate, AI_MODE_PICKUP_ITEMS);
|
||
|
string sRange = FloatToString(GetLocalFloat(oAssociate, AI_LOOT_CHECK_RANGE), 0, 0);
|
||
|
string sMessage, sText;
|
||
|
if(bLooting)
|
||
|
{
|
||
|
sMessage = " is picking up items.";
|
||
|
sText = " Looting On [" + sRange + " meters]";
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sMessage = " is not picking up items.";
|
||
|
sText = " Looting Off [" + sRange + " meters]";
|
||
|
}
|
||
|
ai_SendMessages(GetName(oAssociate) + sMessage, AI_COLOR_YELLOW, oPC);
|
||
|
ai_SetAIMode(oAssociate, AI_MODE_PICKUP_ITEMS, bLooting);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_loot_tooltip", sText);
|
||
|
aiSaveAssociateModesToDb(oPC, oAssociate);
|
||
|
}
|
||
|
void ai_Spontaneous(object oPC, object oAssociate, string sAssociateType)
|
||
|
{
|
||
|
int bSpontaneous = !ai_GetMagicMode(oAssociate, AI_MAGIC_NO_SPONTANEOUS_CURE);
|
||
|
string sMessage, sText;
|
||
|
|
||
|
if(bSpontaneous)
|
||
|
{
|
||
|
sMessage = " has stop casting spontaneous healing spells.";
|
||
|
sText = " Spontaneous casting Off";
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sMessage = " will now cast spontaneous healing spells.";
|
||
|
sText = " Spontaneous casting On";
|
||
|
}
|
||
|
ai_SendMessages(GetName(oAssociate) + sMessage, AI_COLOR_YELLOW, oPC);
|
||
|
ai_SetMagicMode(oAssociate, AI_MAGIC_NO_SPONTANEOUS_CURE, bSpontaneous);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_spontaneous_tooltip", sText);
|
||
|
aiSaveAssociateModesToDb(oPC, oAssociate);
|
||
|
}
|
||
|
void ai_MagicIncrement(object oPC, object oAssociate, int nIncrement, string sAssociateType)
|
||
|
{
|
||
|
int nAdjustment = GetLocalInt(oAssociate, AI_DIFFICULTY_ADJUSTMENT) + nIncrement;
|
||
|
if(nAdjustment > 100) nAdjustment = 100;
|
||
|
else if(nAdjustment < -100) nAdjustment = -100;
|
||
|
SetLocalInt(oAssociate, AI_DIFFICULTY_ADJUSTMENT, nAdjustment);
|
||
|
json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata");
|
||
|
jAIData = JsonArraySet(jAIData, 0, JsonInt(nAdjustment));
|
||
|
ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData);
|
||
|
string sMagic = IntToString(nAdjustment);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_magic_level_tooltip", " Magic Level [" + sMagic + "]");
|
||
|
}
|
||
|
void ai_LootRangeIncrement(object oPC, object oAssociate, float fIncrement, string sAssociateType)
|
||
|
{
|
||
|
float fAdjustment = GetLocalFloat(oAssociate, AI_LOOT_CHECK_RANGE) + fIncrement;
|
||
|
if(fAdjustment > 40.0) fAdjustment = 40.0;
|
||
|
else if(fAdjustment < 0.0) fAdjustment = 0.0;
|
||
|
SetLocalFloat(oAssociate, AI_LOOT_CHECK_RANGE, fAdjustment);
|
||
|
json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata");
|
||
|
jAIData = JsonArraySet(jAIData, 3, JsonFloat(fAdjustment));
|
||
|
ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData);
|
||
|
string sRange = FloatToString(fAdjustment, 0, 0);
|
||
|
string sLoot = " Looting Off [" + sRange + " meters]";
|
||
|
if(ai_GetAIMode(oAssociate, AI_MODE_PICKUP_ITEMS)) sLoot = " Looting On [" + sRange + " meters]";
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_loot_tooltip", sLoot);
|
||
|
}
|
||
|
void ai_LockRangeIncrement(object oPC, object oAssociate, float fIncrement, string sAssociateType)
|
||
|
{
|
||
|
float fAdjustment = GetLocalFloat(oAssociate, AI_LOCK_CHECK_RANGE) + fIncrement;
|
||
|
if(fAdjustment > 40.0) fAdjustment = 40.0;
|
||
|
else if(fAdjustment < 0.0) fAdjustment = 0.0;
|
||
|
SetLocalFloat(oAssociate, AI_LOCK_CHECK_RANGE, fAdjustment);
|
||
|
json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata");
|
||
|
jAIData = JsonArraySet(jAIData, 4, JsonFloat(fAdjustment));
|
||
|
ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData);
|
||
|
string sRange = FloatToString(fAdjustment, 0, 0);
|
||
|
string sPick = " Pick Locks Off [" + sRange + " meters]";
|
||
|
string sBash = " Bash Off [" + sRange + " meters]";
|
||
|
if(ai_GetAIMode(oAssociate, AI_MODE_PICK_LOCKS)) sPick = " Pick Locks On [" + sRange + " meters]";
|
||
|
if(ai_GetAIMode(oAssociate, AI_MODE_BASH_LOCKS)) sBash = " Bash On [" + sRange + " meters]";
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_pick_locks_tooltip", sPick);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_bash_locks_tooltip", sBash);
|
||
|
}
|
||
|
void ai_TrapRangeIncrement(object oPC, object oAssociate, float fIncrement, string sAssociateType)
|
||
|
{
|
||
|
float fAdjustment = GetLocalFloat(oAssociate, AI_TRAP_CHECK_RANGE) + fIncrement;
|
||
|
if(fAdjustment > 40.0) fAdjustment = 40.0;
|
||
|
else if(fAdjustment < 0.0) fAdjustment = 0.0;
|
||
|
SetLocalFloat(oAssociate, AI_TRAP_CHECK_RANGE, fAdjustment);
|
||
|
json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata");
|
||
|
jAIData = JsonArraySet(jAIData, 5, JsonFloat(fAdjustment));
|
||
|
ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData);
|
||
|
string sRange = FloatToString(fAdjustment, 0, 0);
|
||
|
string sText = " Disable Traps Off [" + sRange + " meters]";
|
||
|
if(ai_GetAIMode(oAssociate, AI_MODE_DISARM_TRAPS)) sText = " Disable Traps On [" + sRange + " meters]";
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_traps_tooltip", sText);
|
||
|
}
|
||
|
void ai_OpenDoorIncrement(object oPC, object oAssociate, float fIncrement, string sAssociateType)
|
||
|
{
|
||
|
float fAdjustment = GetLocalFloat(oAssociate, AI_OPEN_DOORS_RANGE) + fIncrement;
|
||
|
if(fAdjustment > 40.0) fAdjustment = 40.0;
|
||
|
else if(fAdjustment < 0.0) fAdjustment = 0.0;
|
||
|
SetLocalFloat(oAssociate, AI_OPEN_DOORS_RANGE, fAdjustment);
|
||
|
json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata");
|
||
|
jAIData = JsonArraySet(jAIData, 9, JsonFloat(fAdjustment));
|
||
|
ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData);
|
||
|
string sRange = FloatToString(fAdjustment, 0, 0);
|
||
|
string sText = " Open Doors Off [" + sRange + " meters]";
|
||
|
if(ai_GetAIMode(oAssociate, AI_MODE_OPEN_DOORS)) sText = " Open Doors On [" + sRange + " meters]";
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_open_door_tooltip", sText);
|
||
|
}
|
||
|
void ai_SaveAIScript(object oPC, object oAssociate, int nToken)
|
||
|
{
|
||
|
string sScript = JsonGetString(NuiGetBind(oPC, nToken, "txt_ai_script"));
|
||
|
string sOldScript = GetLocalString(oAssociate, AI_COMBAT_SCRIPT);
|
||
|
if(GetStringLeft(sScript, 5) != "ai_a_") ai_SendMessages(sScript + " does not have correct prefix it must have ai_a_ for associates! Did not change AI script.", AI_COLOR_RED, oPC);
|
||
|
else if(ResManGetAliasFor(sScript, RESTYPE_NCS) == "")
|
||
|
{
|
||
|
ai_SendMessages(sScript + " not found by ResMan! This is not a valid AI script.", AI_COLOR_RED, oPC);
|
||
|
}
|
||
|
else if(sScript != sOldScript)
|
||
|
{
|
||
|
SetLocalString(oAssociate, AI_COMBAT_SCRIPT, sScript);
|
||
|
SetLocalString(oAssociate, AI_DEFAULT_SCRIPT, sScript);
|
||
|
string sAssociateType = ai_GetAssociateType(oPC, oAssociate);
|
||
|
json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata");
|
||
|
if(JsonGetType(JsonArrayGet(jAIData, 8)) == JSON_TYPE_NULL) jAIData = JsonArrayInsert(jAIData, JsonString(sScript));
|
||
|
else jAIData = JsonArraySet(jAIData, 8, JsonString(sScript));
|
||
|
ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData);
|
||
|
ai_SendMessages(GetName(oAssociate) + " is now using " + sScript + " AI script!", AI_COLOR_GREEN, oPC);
|
||
|
}
|
||
|
else ai_SendMessages(GetName(oAssociate) + " is already using this script! Did not change AI script.", AI_COLOR_RED, oPC);
|
||
|
}
|
||
|
void ai_Buff_Button(object oPC, object oAssociate, int nOption, string sAssociateType)
|
||
|
{
|
||
|
if(nOption == 0)
|
||
|
{
|
||
|
int bRestBuff = !ai_GetMagicMode(oAssociate, AI_MAGIC_BUFF_AFTER_REST);
|
||
|
ai_SetMagicMode(oAssociate, AI_MAGIC_BUFF_AFTER_REST, bRestBuff);
|
||
|
if(bRestBuff)
|
||
|
{
|
||
|
ai_SendMessages(GetName(oAssociate) + " will cast long buffs after resting.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_buff_rest_tooltip", " [On] Turn buffing after resting off.");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ai_SendMessages(GetName(oAssociate) + " will not cast long buffs after resting.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_buff_rest_tooltip", " [Off] Turn buffing after resting on.");
|
||
|
}
|
||
|
aiSaveAssociateModesToDb(oPC, oAssociate);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if(!GetIsPossessedFamiliar(oAssociate))
|
||
|
{
|
||
|
object oEnemy = GetNearestEnemy(oAssociate);
|
||
|
//ai_Debug("0e_nui", "865", "oEnemy: " + GetName(oEnemy) + " fDistance: " +
|
||
|
// FloatToString(GetDistanceBetween(oAssociate, oEnemy), 0, 2));
|
||
|
if(GetDistanceBetween(oAssociate, oEnemy) > 30.0 ||
|
||
|
oEnemy == OBJECT_INVALID)
|
||
|
{
|
||
|
ai_CastBuffs(oAssociate, nOption, 0, oPC);
|
||
|
}
|
||
|
else ai_SendMessages("You cannot buff while there are enemies nearby.", AI_COLOR_RED, oPC);
|
||
|
}
|
||
|
else ai_SendMessages("You cannot buff while possessing your familiar.", AI_COLOR_RED, oPC);
|
||
|
}
|
||
|
}
|
||
|
void ai_Heal_Button(object oPC, object oAssociate, int nIncrement, string sVar, string sAssociateType)
|
||
|
{
|
||
|
int nHeal = GetLocalInt(oAssociate, sVar);
|
||
|
if(nIncrement > 0 && nHeal > 100 - nIncrement) nHeal = 100 - nIncrement;
|
||
|
if(nIncrement < 0 && nHeal < abs(nIncrement)) nHeal = abs(nIncrement);
|
||
|
nHeal += nIncrement;
|
||
|
SetLocalInt(oAssociate, sVar, nHeal);
|
||
|
string sHeal = IntToString(nHeal);
|
||
|
json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata");
|
||
|
if(sVar == AI_HEAL_OUT_OF_COMBAT_LIMIT)
|
||
|
{
|
||
|
string sText = " Will heal at or below [" + sHeal + "%] health out of combat";
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_heal_out_tooltip", sText);
|
||
|
jAIData = JsonArraySet(jAIData, 1, JsonInt(nHeal));
|
||
|
}
|
||
|
else if(sVar == AI_HEAL_IN_COMBAT_LIMIT)
|
||
|
{
|
||
|
string sText = " Will heal at or below [" + sHeal + "%] health in combat";
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_heal_in_tooltip", sText);
|
||
|
jAIData = JsonArraySet(jAIData, 2, JsonInt(nHeal));
|
||
|
}
|
||
|
ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData);
|
||
|
}
|
||
|
void ai_Heal_OnOff(object oPC, object oAssociate, string sAssociateType, int nMode)
|
||
|
{
|
||
|
string sText, sText2;
|
||
|
if(nMode == 1)
|
||
|
{
|
||
|
if(ai_GetAIMode(oAssociate, AI_MODE_SELF_HEALING_OFF))
|
||
|
{
|
||
|
ai_SetAIMode(oAssociate, AI_MODE_SELF_HEALING_OFF, FALSE);
|
||
|
sText = " Self healing On";
|
||
|
sText2 = " will now use healing on themselves.";
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ai_SetAIMode(oAssociate, AI_MODE_SELF_HEALING_OFF, TRUE);
|
||
|
sText = " Self healing Off";
|
||
|
sText2 = " will stop using healing on themselves.";
|
||
|
}
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_heals_onoff_tooltip", sText);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if(ai_GetAIMode(oAssociate, AI_MODE_PARTY_HEALING_OFF))
|
||
|
{
|
||
|
ai_SetAIMode(oAssociate, AI_MODE_PARTY_HEALING_OFF, FALSE);
|
||
|
sText = " Party healing On";
|
||
|
sText2 = " will now use healing on party members.";
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ai_SetAIMode(oAssociate, AI_MODE_PARTY_HEALING_OFF, TRUE);
|
||
|
sText = " Party healing Off";
|
||
|
sText2 = " will stop using healing on party members.";
|
||
|
}
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_healp_onoff_tooltip", sText);
|
||
|
}
|
||
|
ai_SendMessages(GetName(oAssociate) + sText2, AI_COLOR_YELLOW, oPC);
|
||
|
aiSaveAssociateModesToDb(oPC, oAssociate);
|
||
|
}
|
||
|
void ai_Cure_OnOff(object oPC, object oAssociate, string sAssociateType)
|
||
|
{
|
||
|
if(ai_GetMagicMode(oAssociate, AI_MAGIC_CURE_SPELLS_OFF))
|
||
|
{
|
||
|
ai_SendMessages(GetName(oAssociate) + " will now cast cure spells.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_cure_onoff_tooltip", " Cast Cure Spells On");
|
||
|
ai_SetMagicMode(oAssociate, AI_MAGIC_CURE_SPELLS_OFF, FALSE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ai_SendMessages(GetName(oAssociate) + " will stop casting cure spells.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_cure_onoff_tooltip", " Cast Cure Spells Off");
|
||
|
ai_SetMagicMode(oAssociate, AI_MAGIC_CURE_SPELLS_OFF, TRUE);
|
||
|
}
|
||
|
aiSaveAssociateModesToDb(oPC, oAssociate);
|
||
|
}
|
||
|
void ai_Ignore_Associates(object oPC, object oAssociate, string sAssociateType)
|
||
|
{
|
||
|
if(ai_GetAIMode(oAssociate, AI_MODE_IGNORE_ASSOCIATES))
|
||
|
{
|
||
|
ai_SendMessages(GetName(oAssociate) + " will stop ignoring henchman's associates and enemy associates.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_ignore_assoc_tooltip", " Ignore Enemy Associates Off");
|
||
|
ai_SetAIMode(oAssociate, AI_MODE_IGNORE_ASSOCIATES, FALSE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ai_SendMessages(GetName(oAssociate) + " will now ignore henchman's associates and enemy associates.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_ignore_assoc_tooltip", " Ignore Enemy Associates On");
|
||
|
ai_SetAIMode(oAssociate, AI_MODE_IGNORE_ASSOCIATES, TRUE);
|
||
|
}
|
||
|
aiSaveAssociateModesToDb(oPC, oAssociate);
|
||
|
}
|
||
|
void ai_Ignore_Traps(object oPC, object oAssociate, string sAssociateType)
|
||
|
{
|
||
|
if(ai_GetAIMode(oAssociate, AI_MODE_IGNORE_TRAPS))
|
||
|
{
|
||
|
ai_SendMessages(GetName(oAssociate) + " will stop ignoring traps on the floor and will stop moving when one is seen.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_ignore_traps_tooltip", " Ignore Floor Traps Off");
|
||
|
ai_SetAIMode(oAssociate, AI_MODE_IGNORE_TRAPS, FALSE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ai_SendMessages(GetName(oAssociate) + " will now ignore traps on the floor and will continue with their actions.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_ignore_traps_tooltip", " Ignore Floor Traps On");
|
||
|
ai_SetAIMode(oAssociate, AI_MODE_IGNORE_TRAPS, TRUE);
|
||
|
}
|
||
|
aiSaveAssociateModesToDb(oPC, oAssociate);
|
||
|
}
|
||
|
void ai_FollowTarget(object oPC, object oAssociate)
|
||
|
{
|
||
|
SetLocalObject(oPC, AI_TARGET_ASSOCIATE, oAssociate);
|
||
|
SetLocalString(oPC, AI_TARGET_MODE, "ASSOCIATE_FOLLOW_TARGET");
|
||
|
EnterTargetingMode(oPC, OBJECT_TYPE_CREATURE, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK);
|
||
|
}
|
||
|
void ai_Original_Guard()
|
||
|
{
|
||
|
ResetHenchmenState();
|
||
|
//Companions will only attack the Masters Last Attacker
|
||
|
SetAssociateState(NW_ASC_MODE_DEFEND_MASTER);
|
||
|
SetAssociateState(NW_ASC_MODE_STAND_GROUND, FALSE);
|
||
|
object oMaster = GetMaster();
|
||
|
object oLastAttacker = GetLastHostileActor(oMaster);
|
||
|
// * for some reason this is too often invalid. still the routine
|
||
|
// * works corrrectly
|
||
|
SetLocalInt(OBJECT_SELF, "X0_BATTLEJOINEDMASTER", TRUE);
|
||
|
HenchmenCombatRound(oLastAttacker);
|
||
|
ai_SendMessages(GetName(OBJECT_SELF) + " is now guarding you!", AI_COLOR_YELLOW, oMaster);
|
||
|
}
|
||
|
void ai_Original_Follow()
|
||
|
{
|
||
|
ResetHenchmenState();
|
||
|
SetAssociateState(NW_ASC_MODE_STAND_GROUND, FALSE);
|
||
|
DelayCommand(2.5, VoiceCanDo());
|
||
|
object oMaster = GetMaster();
|
||
|
ActionForceFollowObject(oMaster, GetFollowDistance());
|
||
|
SetAssociateState(NW_ASC_IS_BUSY);
|
||
|
DelayCommand(5.0, SetAssociateState(NW_ASC_IS_BUSY, FALSE));
|
||
|
ai_SendMessages(GetName(OBJECT_SELF) + " is now following You!", AI_COLOR_YELLOW, oMaster);
|
||
|
}
|
||
|
void ai_Original_StandGround()
|
||
|
{
|
||
|
SetAssociateState(NW_ASC_MODE_STAND_GROUND);
|
||
|
SetAssociateState(NW_ASC_MODE_DEFEND_MASTER, FALSE);
|
||
|
DelayCommand(2.0, VoiceCanDo());
|
||
|
ActionAttack(OBJECT_INVALID);
|
||
|
ClearActions(CLEAR_X0_INC_HENAI_RespondToShout1);
|
||
|
ai_SendMessages(GetName(OBJECT_SELF) + " is now standing their ground!", AI_COLOR_YELLOW, GetMaster());
|
||
|
}
|
||
|
void ai_Original_AttackNearest()
|
||
|
{
|
||
|
ResetHenchmenState();
|
||
|
SetAssociateState(NW_ASC_MODE_DEFEND_MASTER, FALSE);
|
||
|
SetAssociateState(NW_ASC_MODE_STAND_GROUND, FALSE);
|
||
|
DetermineCombatRound();
|
||
|
// * bonus feature. If master is attacking a door or container, issues VWE Attack Nearest
|
||
|
// * will make henchman join in on the fun
|
||
|
object oMaster = GetMaster();
|
||
|
object oTarget = GetAttackTarget(oMaster);
|
||
|
if (GetIsObjectValid(oTarget) == TRUE)
|
||
|
{
|
||
|
if (GetObjectType(oTarget) == OBJECT_TYPE_PLACEABLE || GetObjectType(oTarget) == OBJECT_TYPE_DOOR)
|
||
|
{
|
||
|
ActionAttack(oTarget);
|
||
|
}
|
||
|
}
|
||
|
ai_SendMessages(GetName(OBJECT_SELF) + " is now in normal mode!", AI_COLOR_YELLOW, oMaster);
|
||
|
}
|
||
|
void ai_Original_SetSearch(object oAssociate, int bTurnOn)
|
||
|
{
|
||
|
if(GetRacialType(oAssociate) != RACIAL_TYPE_ELF) SetActionMode(oAssociate, ACTION_MODE_DETECT, bTurnOn);
|
||
|
}
|
||
|
void ai_Original_SetStealth(object oAssociate, int bTurnOn)
|
||
|
{
|
||
|
SetActionMode(oAssociate, ACTION_MODE_STEALTH, bTurnOn);
|
||
|
}
|
||
|
void ai_Philos_Guard(object oMaster, object oCreature)
|
||
|
{
|
||
|
ai_PassAIModeToAssociates(oCreature, AI_MODE_SCOUT_AHEAD, FALSE);
|
||
|
ai_PassAIModeToAssociates(oCreature, AI_MODE_DEFEND_MASTER, TRUE);
|
||
|
ai_PassAIModeToAssociates(oCreature, AI_MODE_STAND_GROUND, FALSE);
|
||
|
ai_PassAIModeToAssociates(oCreature, AI_MODE_FOLLOW, FALSE);
|
||
|
ai_SetAIMode(oCreature, AI_MODE_COMMANDED, FALSE);
|
||
|
int nToken = NuiFindWindow(oMaster, ai_GetAssociateType(oMaster, oCreature) + AI_WIDGET_NUI);
|
||
|
ai_HighlightWidgetMode(oMaster, oCreature, nToken);
|
||
|
if(!ai_GetIsBusy(oCreature) && ai_GetIsInCombat(oCreature))
|
||
|
{
|
||
|
object oLastAttacker = GetLastHostileActor(oMaster);
|
||
|
if(oLastAttacker != OBJECT_INVALID) ai_DoAssociateCombatRound(oCreature, oLastAttacker);
|
||
|
else AssignCommand(oCreature, ActionMoveToObject(oMaster, TRUE));
|
||
|
}
|
||
|
ai_SendMessages(GetName(oCreature) + " is now guarding you!", AI_COLOR_YELLOW, oMaster);
|
||
|
aiSaveAssociateModesToDb(oMaster, oCreature);
|
||
|
}
|
||
|
void ai_Philos_Follow(object oMaster)
|
||
|
{
|
||
|
object oCreature = OBJECT_SELF;
|
||
|
ai_PassAIModeToAssociates(oCreature, AI_MODE_SCOUT_AHEAD, FALSE);
|
||
|
ai_PassAIModeToAssociates(oCreature, AI_MODE_STAND_GROUND, FALSE);
|
||
|
ai_PassAIModeToAssociates(oCreature, AI_MODE_FOLLOW, TRUE);
|
||
|
ai_SetAIMode(oCreature, AI_MODE_COMMANDED, FALSE);
|
||
|
int nToken = NuiFindWindow(oMaster, ai_GetAssociateType(oMaster, oCreature) + AI_WIDGET_NUI);
|
||
|
ai_HighlightWidgetMode(oMaster, oCreature, nToken);
|
||
|
aiSaveAssociateModesToDb(oMaster, oCreature);
|
||
|
// To follow we probably should be running and not searching or hiding.
|
||
|
if(GetDetectMode(oCreature) && !GetHasFeat(FEAT_KEEN_SENSE, oCreature)) SetActionMode(oCreature, ACTION_MODE_DETECT, FALSE);
|
||
|
if(GetStealthMode(oCreature)) SetActionMode(oCreature, ACTION_MODE_STEALTH, FALSE);
|
||
|
ai_PassActionToAssociates(oCreature, ACTION_FOLLOW);
|
||
|
if(ai_IsInCombatRound(oCreature)) ai_ClearCombatState(oCreature);
|
||
|
ai_ClearCreatureActions(TRUE);
|
||
|
object oTarget = GetLocalObject(oCreature, AI_FOLLOW_TARGET);
|
||
|
if(oTarget == OBJECT_INVALID) oTarget = oMaster;
|
||
|
ActionMoveToObject(oTarget, TRUE, ai_GetFollowDistance(oCreature));
|
||
|
ai_SendMessages(GetName(oCreature) + " is now following " + GetName(oTarget) + "!", AI_COLOR_YELLOW, oMaster);
|
||
|
}
|
||
|
void ai_Philos_StandGround(object oMaster)
|
||
|
{
|
||
|
object oCreature = OBJECT_SELF;
|
||
|
ai_PassAIModeToAssociates(oCreature, AI_MODE_SCOUT_AHEAD, FALSE);
|
||
|
ai_PassAIModeToAssociates(oCreature, AI_MODE_STAND_GROUND, TRUE);
|
||
|
ai_PassAIModeToAssociates(oCreature, AI_MODE_DEFEND_MASTER, FALSE);
|
||
|
ai_PassAIModeToAssociates(oCreature, AI_MODE_FOLLOW, FALSE);
|
||
|
ai_PassActionToAssociates(oCreature, ACTION_FOLLOW, FALSE);
|
||
|
ai_SetAIMode(oCreature, AI_MODE_COMMANDED, FALSE);
|
||
|
int nToken = NuiFindWindow(oMaster, ai_GetAssociateType(oMaster, oCreature) + AI_WIDGET_NUI);
|
||
|
ai_HighlightWidgetMode(oMaster, oCreature, nToken);
|
||
|
if(ai_IsInCombatRound(oCreature))
|
||
|
{
|
||
|
ai_ClearCombatState(oCreature);
|
||
|
DeleteLocalObject(oCreature, AI_ATTACKED_PHYSICAL);
|
||
|
DeleteLocalObject(oCreature, AI_ATTACKED_SPELL);
|
||
|
}
|
||
|
ai_ClearCreatureActions(TRUE);
|
||
|
ai_SendMessages(GetName(oCreature) + " is now standing their ground!", AI_COLOR_YELLOW, oMaster);
|
||
|
aiSaveAssociateModesToDb(oMaster, oCreature);
|
||
|
}
|
||
|
void ai_Philos_AttackNearest(object oMaster, object oCreature)
|
||
|
{
|
||
|
ai_PassAIModeToAssociates(oCreature, AI_MODE_SCOUT_AHEAD, FALSE);
|
||
|
ai_PassAIModeToAssociates(oCreature, AI_MODE_STAND_GROUND, FALSE);
|
||
|
ai_PassAIModeToAssociates(oCreature, AI_MODE_DEFEND_MASTER, FALSE);
|
||
|
ai_PassAIModeToAssociates(oCreature, AI_MODE_FOLLOW, FALSE);
|
||
|
ai_PassActionToAssociates(oCreature, ACTION_FOLLOW, FALSE);
|
||
|
ai_SetAIMode(oCreature, AI_MODE_COMMANDED, FALSE);
|
||
|
int nToken = NuiFindWindow(oMaster, ai_GetAssociateType(oMaster, oCreature) + AI_WIDGET_NUI);
|
||
|
ai_HighlightWidgetMode(oMaster, oCreature, nToken);
|
||
|
// Removes any targets the PC may have given the associate.
|
||
|
DeleteLocalObject(oCreature, AI_PC_LOCKED_TARGET);
|
||
|
// This resets a henchmens failed Moral save in combat.
|
||
|
string sScript = GetLocalString(oCreature, AI_COMBAT_SCRIPT);
|
||
|
if(sScript == "ai_coward")
|
||
|
{
|
||
|
sScript = GetLocalString(oCreature, AI_DEFAULT_SCRIPT);
|
||
|
SetLocalString(oCreature, AI_COMBAT_SCRIPT, sScript);
|
||
|
}
|
||
|
if(!ai_GetIsBusy(oCreature))
|
||
|
{
|
||
|
object oEnemy = ai_GetNearestEnemy(oCreature, 1, 7, 7);
|
||
|
if(oEnemy != OBJECT_INVALID && GetDistanceBetween(oCreature, oEnemy) < AI_RANGE_BATTLEFIELD)
|
||
|
{
|
||
|
ai_HaveCreatureSpeak(oCreature, 5, ":0:1:2:3:6:");
|
||
|
// If master is attacking a target we will attack them too!
|
||
|
if(!ai_GetIsInCombat(oCreature)) ai_StartAssociateCombat(oCreature);
|
||
|
object oTarget = ai_GetAttackedTarget(oMaster);
|
||
|
if(oTarget == OBJECT_INVALID) ai_DoAssociateCombatRound(oCreature);
|
||
|
else ai_DoAssociateCombatRound(oCreature, oTarget);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
object oTarget = GetLocalObject(oCreature, AI_FOLLOW_TARGET);
|
||
|
if(oTarget == OBJECT_INVALID) oTarget = oMaster;
|
||
|
AssignCommand(oCreature, ActionMoveToObject(oMaster, TRUE, ai_GetFollowDistance(oCreature)));
|
||
|
}
|
||
|
}
|
||
|
ai_SendMessages(GetName(oCreature) + " is now in normal mode!", AI_COLOR_YELLOW, oMaster);
|
||
|
aiSaveAssociateModesToDb(oMaster, oCreature);
|
||
|
}
|
||
|
void ai_Philos_SetSearch(object oMaster, object oCreature, string sAssociateType, int bTurnOn)
|
||
|
{
|
||
|
if(bTurnOn)
|
||
|
{
|
||
|
ai_SetAIMode(oCreature, AI_MODE_AGGRESSIVE_SEARCH, TRUE);
|
||
|
SetActionMode(oCreature, ACTION_MODE_DETECT, TRUE);
|
||
|
ai_PassActionToAssociates(oCreature, ACTION_MODE_DETECT, TRUE);
|
||
|
//ai_PassActionToAssociates(oCreature, ACTION_MODE_DETECT, TRUE);
|
||
|
ai_UpdateToolTipUI(oMaster, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_search_tooltip", " Search mode On");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ai_SetAIMode(oCreature, AI_MODE_AGGRESSIVE_SEARCH, FALSE);
|
||
|
SetActionMode(oCreature, ACTION_MODE_DETECT, FALSE);
|
||
|
ai_PassActionToAssociates(oCreature, ACTION_MODE_DETECT, FALSE);
|
||
|
//ai_PassActionToAssociates(oCreature, ACTION_MODE_DETECT, FALSE);
|
||
|
ai_UpdateToolTipUI(oMaster, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_search_tooltip", " Search mode Off");
|
||
|
}
|
||
|
aiSaveAssociateModesToDb(oMaster, oCreature);
|
||
|
}
|
||
|
void ai_Philos_SetStealth(object oMaster, object oCreature, string sAssociateType, int bTurnOn)
|
||
|
{
|
||
|
if(bTurnOn)
|
||
|
{
|
||
|
ai_SetAIMode(oCreature, AI_MODE_AGGRESSIVE_STEALTH);
|
||
|
SetActionMode(oCreature, ACTION_MODE_STEALTH, TRUE);
|
||
|
ai_PassActionToAssociates(oCreature, ACTION_MODE_STEALTH, TRUE);
|
||
|
ai_UpdateToolTipUI(oMaster, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_stealth_tooltip", " Stealth mode On");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ai_SetAIMode(oCreature, AI_MODE_AGGRESSIVE_STEALTH, FALSE);
|
||
|
SetActionMode(oCreature, ACTION_MODE_STEALTH, FALSE);
|
||
|
ai_PassActionToAssociates(oCreature, ACTION_MODE_STEALTH, FALSE);
|
||
|
//ai_PassActionToAssociates(oCreature, ACTION_MODE_STEALTH, FALSE);
|
||
|
ai_UpdateToolTipUI(oMaster, sAssociateType + AI_NUI, sAssociateType + AI_WIDGET_NUI, "btn_stealth_tooltip", " Stealth mode Off");
|
||
|
}
|
||
|
aiSaveAssociateModesToDb(oMaster, oCreature);
|
||
|
}
|
||
|
void ai_DoCommand(object oPC, object oAssociate, int nCommand)
|
||
|
{
|
||
|
int nIndex = 1;
|
||
|
if(oPC == oAssociate)
|
||
|
{
|
||
|
if(nCommand == 1) // Guard PC.
|
||
|
{
|
||
|
// Not using Philos Henchman AI. Use vanilla commands.
|
||
|
if(ResManGetAliasFor("ai_a_default", RESTYPE_NCS) == "")
|
||
|
{
|
||
|
for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++)
|
||
|
{
|
||
|
oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex);
|
||
|
if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_Original_Guard());
|
||
|
}
|
||
|
for(nIndex = 2; nIndex < 6; nIndex++)
|
||
|
{
|
||
|
oAssociate = GetAssociate(nIndex, oPC);
|
||
|
if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_Original_Guard());
|
||
|
}
|
||
|
}
|
||
|
// Use Philos AI commands.
|
||
|
else
|
||
|
{
|
||
|
for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++)
|
||
|
{
|
||
|
oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex);
|
||
|
if(oAssociate != OBJECT_INVALID) ai_Philos_Guard(oPC, oAssociate);
|
||
|
}
|
||
|
for(nIndex = 2; nIndex < 6; nIndex++)
|
||
|
{
|
||
|
oAssociate = GetAssociate(nIndex, oPC);
|
||
|
if(oAssociate != OBJECT_INVALID) ai_Philos_Guard(oPC, oAssociate);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if(nCommand == 2) // Follow PC.
|
||
|
{
|
||
|
// Not using Philos Henchman AI. Use vanilla commands.
|
||
|
if(ResManGetAliasFor("ai_a_default", RESTYPE_NCS) == "")
|
||
|
{
|
||
|
for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++)
|
||
|
{
|
||
|
oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex);
|
||
|
if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_Original_Follow());
|
||
|
}
|
||
|
for(nIndex = 2; nIndex < 6; nIndex++)
|
||
|
{
|
||
|
oAssociate = GetAssociate(nIndex, oPC);
|
||
|
if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_Original_Follow());
|
||
|
}
|
||
|
}
|
||
|
// Use Philos AI commands.
|
||
|
else
|
||
|
{
|
||
|
for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++)
|
||
|
{
|
||
|
oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex);
|
||
|
if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_Philos_Follow(oPC));
|
||
|
}
|
||
|
for(nIndex = 2; nIndex < 6; nIndex++)
|
||
|
{
|
||
|
oAssociate = GetAssociate(nIndex, oPC);
|
||
|
if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_Philos_Follow(oPC));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if(nCommand == 3) // Standground.
|
||
|
{
|
||
|
// Not using Philos Henchman AI. Use vanilla commands.
|
||
|
if(ResManGetAliasFor("ai_a_default", RESTYPE_NCS) == "")
|
||
|
{
|
||
|
for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++)
|
||
|
{
|
||
|
oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex);
|
||
|
if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_Original_StandGround());
|
||
|
}
|
||
|
for(nIndex = 2; nIndex < 6; nIndex++)
|
||
|
{
|
||
|
oAssociate = GetAssociate(nIndex, oPC);
|
||
|
if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_Original_StandGround());
|
||
|
}
|
||
|
}
|
||
|
// Use Philos AI commands.
|
||
|
else
|
||
|
{
|
||
|
for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++)
|
||
|
{
|
||
|
oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex);
|
||
|
if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_Philos_StandGround(oPC));
|
||
|
}
|
||
|
for(nIndex = 2; nIndex < 6; nIndex++)
|
||
|
{
|
||
|
oAssociate = GetAssociate(nIndex, oPC);
|
||
|
if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_Philos_StandGround(oPC));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if(nCommand == 4) // Normal mode - i.e. Attack nearest.
|
||
|
{
|
||
|
// Not using Philos Henchman AI. Use vanilla commands.
|
||
|
if(ResManGetAliasFor("ai_a_default", RESTYPE_NCS) == "")
|
||
|
{
|
||
|
for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++)
|
||
|
{
|
||
|
oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex);
|
||
|
if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_Original_AttackNearest());
|
||
|
}
|
||
|
for(nIndex = 2; nIndex < 6; nIndex++)
|
||
|
{
|
||
|
oAssociate = GetAssociate(nIndex, oPC);
|
||
|
if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_Original_AttackNearest());
|
||
|
}
|
||
|
}
|
||
|
// Use Philos AI commands.
|
||
|
else
|
||
|
{
|
||
|
for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++)
|
||
|
{
|
||
|
oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex);
|
||
|
if(oAssociate != OBJECT_INVALID) ai_Philos_AttackNearest(oPC, oAssociate);
|
||
|
}
|
||
|
for(nIndex = 2; nIndex < 6; nIndex++)
|
||
|
{
|
||
|
oAssociate = GetAssociate(nIndex, oPC);
|
||
|
if(oAssociate != OBJECT_INVALID) ai_Philos_AttackNearest(oPC, oAssociate);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if(nCommand == 5) // All associates toggle search mode
|
||
|
{
|
||
|
int bTurnOn = !ai_GetAIMode(oPC, AI_MODE_AGGRESSIVE_SEARCH);
|
||
|
// Not using Philos Henchman AI. Use vanilla commands.
|
||
|
if(ResManGetAliasFor("ai_a_default", RESTYPE_NCS) == "")
|
||
|
{
|
||
|
ai_Original_SetSearch(oPC, bTurnOn);
|
||
|
for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++)
|
||
|
{
|
||
|
oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex);
|
||
|
if(oAssociate != OBJECT_INVALID) ai_Original_SetSearch(oAssociate, bTurnOn);
|
||
|
}
|
||
|
for(nIndex = 2; nIndex < 6; nIndex++)
|
||
|
{
|
||
|
oAssociate = GetAssociate(nIndex, oPC);
|
||
|
if(oAssociate != OBJECT_INVALID) ai_Original_SetSearch(oAssociate, bTurnOn);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ai_Philos_SetSearch(oPC, oPC, "pc", bTurnOn);
|
||
|
string sAssociateType;
|
||
|
for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++)
|
||
|
{
|
||
|
oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex);
|
||
|
if(oAssociate != OBJECT_INVALID)
|
||
|
{
|
||
|
sAssociateType = ai_GetAssociateType(oPC, oAssociate);
|
||
|
ai_Philos_SetSearch(oPC, oAssociate, sAssociateType, bTurnOn);
|
||
|
}
|
||
|
}
|
||
|
for(nIndex = 2; nIndex < 6; nIndex++)
|
||
|
{
|
||
|
oAssociate = GetAssociate(nIndex, oPC);
|
||
|
if(oAssociate != OBJECT_INVALID)
|
||
|
{
|
||
|
sAssociateType = ai_GetAssociateType(oPC, oAssociate);
|
||
|
ai_Philos_SetSearch(oPC, oAssociate, sAssociateType, bTurnOn);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if(bTurnOn)
|
||
|
{
|
||
|
ai_SendMessages("Everyone is now in search mode!", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, "pc" + AI_COMMAND_NUI, "pc" + AI_WIDGET_NUI, "btn_cmd_search_tooltip", " Everyone leave search mode");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ai_SendMessages("Everyone has left search mode!", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, "pc" + AI_COMMAND_NUI, "pc" + AI_WIDGET_NUI, "btn_cmd_search_tooltip", " Everyone enter search mode");
|
||
|
}
|
||
|
}
|
||
|
if(nCommand == 6) // All associate use stealth mode
|
||
|
{
|
||
|
int bTurnOn = !ai_GetAIMode(oPC, AI_MODE_AGGRESSIVE_STEALTH);
|
||
|
// Not using Philos Henchman AI. Use vanilla commands.
|
||
|
if(ResManGetAliasFor("ai_a_default", RESTYPE_NCS) == "")
|
||
|
{
|
||
|
ai_Original_SetStealth(oPC, bTurnOn);
|
||
|
for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++)
|
||
|
{
|
||
|
oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex);
|
||
|
if(oAssociate != OBJECT_INVALID) ai_Original_SetStealth(oAssociate, bTurnOn);
|
||
|
}
|
||
|
for(nIndex = 2; nIndex < 6; nIndex++)
|
||
|
{
|
||
|
oAssociate = GetAssociate(nIndex, oPC);
|
||
|
if(oAssociate != OBJECT_INVALID) ai_Original_SetStealth(oAssociate, bTurnOn);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ai_Philos_SetStealth(oPC, oPC, "pc", bTurnOn);
|
||
|
string sAssociateType;
|
||
|
for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++)
|
||
|
{
|
||
|
oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex);
|
||
|
if(oAssociate != OBJECT_INVALID)
|
||
|
{
|
||
|
sAssociateType = ai_GetAssociateType(oPC, oAssociate);
|
||
|
ai_Philos_SetStealth(oPC, oAssociate, sAssociateType, bTurnOn);
|
||
|
}
|
||
|
}
|
||
|
for(nIndex = 2; nIndex < 6; nIndex++)
|
||
|
{
|
||
|
oAssociate = GetAssociate(nIndex, oPC);
|
||
|
if(oAssociate != OBJECT_INVALID)
|
||
|
{
|
||
|
sAssociateType = ai_GetAssociateType(oPC, oAssociate);
|
||
|
ai_Philos_SetStealth(oPC, oAssociate, sAssociateType, bTurnOn);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if(bTurnOn)
|
||
|
{
|
||
|
ai_SendMessages("Everyone is now in stealth mode.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, "pc" + AI_COMMAND_NUI, "pc" + AI_WIDGET_NUI, "btn_cmd_stealth_tooltip", " Everyone leave stealth mode");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ai_SendMessages("Everyone has left stealth mode.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, "pc" + AI_COMMAND_NUI, "pc" + AI_WIDGET_NUI, "btn_cmd_stealth_tooltip", " Everyone enter stealth mode");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if(nCommand == 1)
|
||
|
{
|
||
|
// Not using Philos Henchman AI. Use vanilla commands.
|
||
|
if(ResManGetAliasFor("ai_a_default", RESTYPE_NCS) == "")
|
||
|
{
|
||
|
AssignCommand(oAssociate, ai_Original_Guard());
|
||
|
}
|
||
|
else ai_Philos_Guard(oPC, oAssociate);
|
||
|
}
|
||
|
else if(nCommand == 2)
|
||
|
{
|
||
|
// Not using Philos Henchman AI. Use vanilla commands.
|
||
|
if(ResManGetAliasFor("ai_a_default", RESTYPE_NCS) == "")
|
||
|
{
|
||
|
AssignCommand(oAssociate, ai_Original_Follow());
|
||
|
}
|
||
|
else AssignCommand(oAssociate, ai_Philos_Follow(oPC));
|
||
|
}
|
||
|
else if(nCommand == 3)
|
||
|
{
|
||
|
// Not using Philos Henchman AI. Use vanilla commands.
|
||
|
if(ResManGetAliasFor("ai_a_default", RESTYPE_NCS) == "")
|
||
|
{
|
||
|
AssignCommand(oAssociate, ai_Original_StandGround());
|
||
|
}
|
||
|
else AssignCommand(oAssociate, ai_Philos_StandGround(oPC));
|
||
|
}
|
||
|
else if(nCommand == 4)
|
||
|
{
|
||
|
// Not using Philos Henchman AI. Use vanilla commands.
|
||
|
if(ResManGetAliasFor("ai_a_default", RESTYPE_NCS) == "")
|
||
|
{
|
||
|
AssignCommand(oAssociate, ai_Original_AttackNearest());
|
||
|
}
|
||
|
else ai_Philos_AttackNearest(oPC, oAssociate);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
void ai_Action(object oPC, object oAssociate)
|
||
|
{
|
||
|
if(oPC == oAssociate)
|
||
|
{
|
||
|
DeleteLocalObject(oPC, "NW_ASSOCIATE_COMMAND");
|
||
|
SetLocalString(oPC, AI_TARGET_MODE, "ASSOCIATE_ACTION_ALL");
|
||
|
ai_SendMessages("Select an action for the party.", AI_COLOR_YELLOW, oPC);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SetLocalObject(oPC, AI_TARGET_ASSOCIATE, oAssociate);
|
||
|
SetLocalString(oPC, AI_TARGET_MODE, "ASSOCIATE_ACTION");
|
||
|
ai_SendMessages("Select an action for " + GetName(oAssociate) + ".", AI_COLOR_YELLOW, oPC);
|
||
|
}
|
||
|
EnterTargetingMode(oPC, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK);
|
||
|
}
|
||
|
void ai_AIScript(object oPC, object oAssociate, string sAssociateType, int nToken)
|
||
|
{
|
||
|
if(ResManGetAliasFor("ai_a_default", RESTYPE_NCS) != "")
|
||
|
{
|
||
|
string sScript = GetLocalString(oAssociate, AI_COMBAT_SCRIPT);
|
||
|
string sIcon = "ir_scommand";
|
||
|
if(sScript == "ai_a_ambusher")
|
||
|
{
|
||
|
sScript = "ai_a_flanker";
|
||
|
sIcon = "ir_invite";
|
||
|
SetLocalString(oAssociate, AI_DEFAULT_SCRIPT, sScript);
|
||
|
SetLocalString(oAssociate, AI_COMBAT_SCRIPT, sScript);
|
||
|
ai_SendMessages(GetName(oAssociate) + " is now using flanking tactics in combat.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_cmd_ai_script_tooltip", " Flanker: Attacks enemies engaged with allies");
|
||
|
}
|
||
|
else if(sScript == "ai_a_flanker")
|
||
|
{
|
||
|
sScript = "ai_a_peaceful";
|
||
|
sIcon = "ir_ignore";
|
||
|
SetLocalString(oAssociate, AI_DEFAULT_SCRIPT, sScript);
|
||
|
SetLocalString(oAssociate, AI_COMBAT_SCRIPT, sScript);
|
||
|
ai_SendMessages(GetName(oAssociate) + " is now using peaceful tactics in combat.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_cmd_ai_script_tooltip", " Peaceful: Avoids attacking any enemies if possible");
|
||
|
}
|
||
|
else if(sScript == "ai_a_peaceful")
|
||
|
{
|
||
|
sScript = "ai_a_defensive";
|
||
|
sIcon = "ir_knockdwn";
|
||
|
SetLocalString(oAssociate, AI_DEFAULT_SCRIPT, sScript);
|
||
|
SetLocalString(oAssociate, AI_COMBAT_SCRIPT, sScript);
|
||
|
ai_SendMessages(GetName(oAssociate) + " is now using defensive tactics in combat.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_cmd_ai_script_tooltip", " Defensive: Attacks then uses Expertise/Parry");
|
||
|
}
|
||
|
else if(sScript == "ai_a_defensive")
|
||
|
{
|
||
|
sScript = "ai_a_ranged";
|
||
|
sIcon = "ir_ranger";
|
||
|
SetLocalString(oAssociate, AI_DEFAULT_SCRIPT, sScript);
|
||
|
SetLocalString(oAssociate, AI_COMBAT_SCRIPT, sScript);
|
||
|
ai_SendMessages(GetName(oAssociate) + " is now using ranged tactics in combat.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_cmd_ai_script_tooltip", " Ranged: Attacks from range as much as possible");
|
||
|
}
|
||
|
else if(sScript == "ai_a_ranged")
|
||
|
{
|
||
|
sScript = "ai_a_cntrspell";
|
||
|
sIcon = "ir_dcaster";
|
||
|
SetLocalString(oAssociate, AI_DEFAULT_SCRIPT, sScript);
|
||
|
SetLocalString(oAssociate, AI_COMBAT_SCRIPT, sScript);
|
||
|
ai_SendMessages(GetName(oAssociate) + " is now using counter spell tactics in combat.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_cmd_ai_script_tooltip", " Counter Spell: Tries to counter enemy spells");
|
||
|
}
|
||
|
else if(sScript == "ai_a_cntrspell")
|
||
|
{
|
||
|
DeleteLocalString(oAssociate, AI_DEFAULT_SCRIPT);
|
||
|
ai_SetAssociateAIScript(oAssociate, FALSE);
|
||
|
sScript = GetLocalString(oAssociate, AI_DEFAULT_SCRIPT);
|
||
|
ai_SendMessages(GetName(oAssociate) + " is now using default tactics in combat.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_cmd_ai_script_tooltip", " Default tactics: Using the creatures base AI script");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sScript = "ai_a_ambusher";
|
||
|
sIcon = "ir_rogue";
|
||
|
SetLocalString(oAssociate, AI_DEFAULT_SCRIPT, sScript);
|
||
|
SetLocalString(oAssociate, AI_COMBAT_SCRIPT, sScript);
|
||
|
ai_SendMessages(GetName(oAssociate) + " is now using ambush tactics in combat.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_cmd_ai_script_tooltip", " Ambusher: Attacks from a hidden position");
|
||
|
}
|
||
|
NuiSetBind(oPC, nToken, "btn_cmd_ai_script_image", JsonString(sIcon));
|
||
|
NuiSetBind(oPC, nToken, "btn_cmd_ai_script_label", JsonString("Tactics: " + sScript));
|
||
|
json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata");
|
||
|
if(JsonGetType(JsonArrayGet(jAIData, 8)) == JSON_TYPE_NULL) jAIData = JsonArrayInsert(jAIData, JsonString(sScript));
|
||
|
else jAIData = JsonArraySet(jAIData, 8, JsonString(sScript));
|
||
|
ai_SetAssociateDbJson(oPC, sAssociateType, "aidata", jAIData);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if(GetCombatCondition(X0_COMBAT_FLAG_AMBUSHER, oAssociate))
|
||
|
{
|
||
|
SetCombatCondition(X0_COMBAT_FLAG_AMBUSHER, FALSE, oAssociate);
|
||
|
SetCombatCondition(X0_COMBAT_FLAG_COWARDLY, TRUE, oAssociate);
|
||
|
SetCombatCondition(X0_COMBAT_FLAG_DEFENSIVE, FALSE, oAssociate);
|
||
|
SetCombatCondition(X0_COMBAT_FLAG_RANGED, FALSE, oAssociate);
|
||
|
ai_SendMessages(GetName(oAssociate) + " is now using coward tactics in combat.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_cmd_ai_script_tooltip", " Using coward tactics");
|
||
|
}
|
||
|
else if(GetCombatCondition(X0_COMBAT_FLAG_COWARDLY, oAssociate))
|
||
|
{
|
||
|
SetCombatCondition(X0_COMBAT_FLAG_AMBUSHER, FALSE, oAssociate);
|
||
|
SetCombatCondition(X0_COMBAT_FLAG_COWARDLY, FALSE, oAssociate);
|
||
|
SetCombatCondition(X0_COMBAT_FLAG_DEFENSIVE, TRUE, oAssociate);
|
||
|
SetCombatCondition(X0_COMBAT_FLAG_RANGED, FALSE, oAssociate);
|
||
|
ai_SendMessages(GetName(oAssociate) + " is now using defensive tactics in combat.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_cmd_ai_script_tooltip", " Using defensive tactics");
|
||
|
}
|
||
|
else if(GetCombatCondition(X0_COMBAT_FLAG_DEFENSIVE, oAssociate))
|
||
|
{
|
||
|
SetCombatCondition(X0_COMBAT_FLAG_AMBUSHER, FALSE, oAssociate);
|
||
|
SetCombatCondition(X0_COMBAT_FLAG_COWARDLY, FALSE, oAssociate);
|
||
|
SetCombatCondition(X0_COMBAT_FLAG_DEFENSIVE, FALSE, oAssociate);
|
||
|
SetCombatCondition(X0_COMBAT_FLAG_RANGED, TRUE, oAssociate);
|
||
|
ai_SendMessages(GetName(oAssociate) + " is now using ranged tactics in combat.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_cmd_ai_script_tooltip", " Using ranged tactics");
|
||
|
}
|
||
|
else if(GetCombatCondition(X0_COMBAT_FLAG_RANGED, oAssociate))
|
||
|
{
|
||
|
SetCombatCondition(X0_COMBAT_FLAG_AMBUSHER, FALSE, oAssociate);
|
||
|
SetCombatCondition(X0_COMBAT_FLAG_COWARDLY, FALSE, oAssociate);
|
||
|
SetCombatCondition(X0_COMBAT_FLAG_DEFENSIVE, FALSE, oAssociate);
|
||
|
SetCombatCondition(X0_COMBAT_FLAG_RANGED, FALSE, oAssociate);
|
||
|
ai_SendMessages(GetName(oAssociate) + " is now using normal tactics in combat.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_cmd_ai_script_tooltip", " Using ambush tactics");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SetCombatCondition(X0_COMBAT_FLAG_AMBUSHER, TRUE, oAssociate);
|
||
|
SetCombatCondition(X0_COMBAT_FLAG_COWARDLY, FALSE, oAssociate);
|
||
|
SetCombatCondition(X0_COMBAT_FLAG_DEFENSIVE, FALSE, oAssociate);
|
||
|
SetCombatCondition(X0_COMBAT_FLAG_RANGED, FALSE, oAssociate);
|
||
|
ai_SendMessages(GetName(oAssociate) + " is now using ambush tactics in combat.", AI_COLOR_YELLOW, oPC);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_cmd_ai_script_tooltip", " Using ambush tactics");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
void ai_HavePCPlaceTrap(object oPC, object oAssociate)
|
||
|
{
|
||
|
SetLocalObject(oPC, AI_TARGET_ASSOCIATE, oAssociate);
|
||
|
SetLocalString(oPC, AI_TARGET_MODE, "ASSOCIATE_GET_TRAP");
|
||
|
ai_SendMessages(GetName(oAssociate) + " select a trap to place.", AI_COLOR_YELLOW, oPC);
|
||
|
OpenInventory(oAssociate, oPC);
|
||
|
EnterTargetingMode(oPC, OBJECT_TYPE_ITEM, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK);
|
||
|
}
|
||
|
void ai_JumpAssociateToPC(object oPC)
|
||
|
{
|
||
|
ai_ClearCreatureActions(TRUE);
|
||
|
JumpToObject(oPC);
|
||
|
}
|
||
|
void ai_JumpToPC(object oPC, object oAssociate)
|
||
|
{
|
||
|
int nAssociateType, nHenchman, nHenchAssociate;
|
||
|
object oHenchman, oHenchmanAssociate;
|
||
|
if(oPC != oAssociate)
|
||
|
{
|
||
|
if(nAssociateType == ASSOCIATE_TYPE_HENCHMAN)
|
||
|
{
|
||
|
for(nHenchAssociate = 2; nHenchAssociate <= 5; nHenchAssociate++)
|
||
|
{
|
||
|
oHenchmanAssociate = GetAssociate(nHenchAssociate, oHenchman, 1);
|
||
|
if(oHenchmanAssociate != OBJECT_INVALID)
|
||
|
{
|
||
|
AssignCommand(oHenchmanAssociate, ai_JumpAssociateToPC(oPC));
|
||
|
}
|
||
|
}
|
||
|
AssignCommand(oHenchman, ai_JumpAssociateToPC(oPC));
|
||
|
}
|
||
|
else AssignCommand(oAssociate, ai_JumpAssociateToPC(oPC));
|
||
|
return;
|
||
|
}
|
||
|
for(nAssociateType = 1; nAssociateType <= 5; nAssociateType++)
|
||
|
{
|
||
|
if(nAssociateType == ASSOCIATE_TYPE_HENCHMAN)
|
||
|
{
|
||
|
for(nHenchman = 1; nHenchman <= AI_MAX_HENCHMAN; nHenchman++)
|
||
|
{
|
||
|
oHenchman = GetAssociate(nAssociateType, oPC, nHenchman);
|
||
|
if(oHenchman != OBJECT_INVALID)
|
||
|
{
|
||
|
for(nHenchAssociate = 2; nHenchAssociate <= 5; nHenchAssociate++)
|
||
|
{
|
||
|
oHenchmanAssociate = GetAssociate(nHenchAssociate, oHenchman, 1);
|
||
|
if(oHenchmanAssociate != OBJECT_INVALID)
|
||
|
{
|
||
|
AssignCommand(oHenchmanAssociate, ai_JumpAssociateToPC(oPC));
|
||
|
}
|
||
|
}
|
||
|
AssignCommand(oHenchman, ai_JumpAssociateToPC(oPC));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
oHenchman = GetAssociate(nAssociateType, oPC, 1);
|
||
|
if(oHenchman != OBJECT_INVALID) AssignCommand(oHenchman, ai_JumpAssociateToPC(oPC));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
void ai_GhostMode(object oPC, object oAssociate, int nToken, string sAssociateType)
|
||
|
{
|
||
|
string sText;
|
||
|
if(ai_GetAIMode(oAssociate, AI_MODE_GHOST))
|
||
|
{
|
||
|
ai_SetAIMode(oAssociate, AI_MODE_GHOST, FALSE);
|
||
|
ai_RemoveASpecificEffect(oAssociate, EFFECT_TYPE_CUTSCENEGHOST);
|
||
|
sText = " Turn On clipping through creatures for " + GetName(oAssociate);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_ghost_mode_tooltip", sText);
|
||
|
ai_SendMessages(GetName(oAssociate) + " is not in Ghost Mode and will run into creatures.", AI_COLOR_YELLOW, oPC);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ai_SetAIMode(oAssociate, AI_MODE_GHOST, TRUE);
|
||
|
effect eGhost = EffectCutsceneGhost();
|
||
|
eGhost = UnyieldingEffect(eGhost);
|
||
|
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eGhost, oAssociate);
|
||
|
sText = " Turn Off clipping through creatures for " + GetName(oAssociate);
|
||
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_ghost_mode_tooltip", sText);
|
||
|
ai_SendMessages(GetName(oAssociate) + " is now in Ghost Mode and will clip through creatures.", AI_COLOR_YELLOW, oPC);
|
||
|
}
|
||
|
}
|
||
|
void ai_ChangeCameraView(object oPC, object oAssociate)
|
||
|
{
|
||
|
object oCamAssociate = GetLocalObject(oPC, "AI_CAMERA_ON_ASSOCIATE");
|
||
|
if(oCamAssociate == oAssociate)
|
||
|
{
|
||
|
DeleteLocalObject(oPC, "AI_CAMERA_ON_ASSOCIATE");
|
||
|
AttachCamera(oPC, oPC);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SetLocalObject(oPC, "AI_CAMERA_ON_ASSOCIATE", oAssociate);
|
||
|
AttachCamera(oPC, oAssociate);
|
||
|
}
|
||
|
}
|
||
|
void ai_SelectCameraView(object oPC)
|
||
|
{
|
||
|
SetLocalString(oPC, AI_TARGET_MODE, "DM_SELECT_CAMERA_VIEW");
|
||
|
ai_SendMessages(GetName(oPC) + " select an object to change the camera view to.", AI_COLOR_YELLOW, oPC);
|
||
|
EnterTargetingMode(oPC, OBJECT_TYPE_ALL, MOUSECURSOR_CREATE, MOUSECURSOR_NOCREATE);
|
||
|
}
|
||
|
void ai_OpenInventory(object oAssociate, object oPC)
|
||
|
{
|
||
|
// Funny things happen when you open associate inventories when they are not
|
||
|
// within sight.
|
||
|
if(LineOfSightObject(oPC, oAssociate))
|
||
|
{
|
||
|
OpenInventory(oAssociate, oPC);
|
||
|
}
|
||
|
else ai_SendMessages(GetName(oAssociate) + " is not within sight!", AI_COLOR_RED, oPC);
|
||
|
}
|
||
|
void ai_SelectOpenInventory(object oPC)
|
||
|
{
|
||
|
SetLocalString(oPC, AI_TARGET_MODE, "DM_SELECT_OPEN_INVENTORY");
|
||
|
ai_SendMessages(GetName(oPC) + " select an object to open its inventory.", AI_COLOR_YELLOW, oPC);
|
||
|
EnterTargetingMode(oPC, OBJECT_TYPE_CREATURE, MOUSECURSOR_EXAMINE, MOUSECURSOR_NOEXAMINE);
|
||
|
}
|
||
|
void ai_Plugin_Execute(object oPC, string sElem, int bUser = 0)
|
||
|
{
|
||
|
int nIndex = StringToInt(GetStringRight(sElem, 1));
|
||
|
json jPlugins, jPlugin;
|
||
|
if(bUser == 1) // From DM command menu.
|
||
|
{
|
||
|
string sName = ai_RemoveIllegalCharacters(GetName(oPC));
|
||
|
jPlugins = ai_GetCampaignDbJson("plugins", sName, AI_DM_TABLE);
|
||
|
}
|
||
|
else if(bUser == 2) // From DM plugin menu, master plugin list.
|
||
|
{
|
||
|
jPlugins = ai_GetCampaignDbJson("plugins");
|
||
|
}
|
||
|
else jPlugins = ai_GetAssociateDbJson(oPC, "pc", "plugins");
|
||
|
jPlugin = JsonArrayGet(jPlugins, nIndex);
|
||
|
string sScript = JsonGetString(JsonArrayGet(jPlugin, 0));
|
||
|
if(ResManGetAliasFor(sScript, RESTYPE_NCS) == "")
|
||
|
{
|
||
|
ai_SendMessages(sScript + " not found by ResMan!", AI_COLOR_RED, oPC);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
string sName = JsonGetString(JsonArrayGet(jPlugin, 2));
|
||
|
ai_SendMessages("Executing plugin " + sName + ".", AI_COLOR_GREEN, oPC);
|
||
|
ExecuteScript(sScript, oPC);
|
||
|
}
|
||
|
}
|