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);
 | |
|     }
 | |
| }
 |