Added PEPS AI. Updated module name. Set all henchmen to have a random race &/or class based name using a custom version of Markshire's Nomeclature scripts, as well as appearance. Set Constructs, Undead, Outsiders & Elementals to not require food or drink. Full compile.
794 lines
34 KiB
Plaintext
794 lines
34 KiB
Plaintext
/*//////////////////////////////////////////////////////////////////////////////
|
|
// Script Name: 0i_player_target
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
Include script for handling player targeting functions.
|
|
|
|
*///////////////////////////////////////////////////////////////////////////////
|
|
#include "0i_associates"
|
|
#include "0i_menus"
|
|
// Setup an AI OnPlayerTarget Event script while allowing any module onplayer
|
|
// target event script to still work.
|
|
void ai_SetupPlayerTarget(object oCreature);
|
|
// Selects a target for oAssocite to follow.
|
|
void ai_AllSelectTarget(object oPC, object oAssociate, object oTarget);
|
|
// Removes the Cutscene ghosts and variables from all associates. For original AI scripts.
|
|
void ai_OriginalRemoveAllActionMode(object oPC);
|
|
// Removes the Cutscene ghosts and Command mode from all associates.
|
|
void ai_RemoveAllActionMode(object oPC);
|
|
// Once a trap has been selected from the associates inventory move to placing the trap.
|
|
void ai_SelectTrap(object oPC, object oAssociate, object oItem);
|
|
// Place the selected trap at the location selected by the player for OBJECT_SELF.
|
|
void ai_PlaceTrap(object oPC, location lLocation);
|
|
// Adds a creature to nGroup for oDM
|
|
void ai_AddToGroup(object oDM, object oTarget, string sTargetMode);
|
|
// Has nGroup perform an action based on the selected target or location.
|
|
void ai_DMAction(object oDM, object oTarget, location lLocation, string sTargetMode);
|
|
// Get oPC to select a spell target for oAssociate.
|
|
void ai_SelectWidgetSpellTarget(object oPC, object oAssociate, string sElem);
|
|
// Updates oAssociates widget by destroying the current one and rebuilding.
|
|
void ai_UpdateAssociateWidget(object oPC, object oAssociate);
|
|
// Sets oAssociates action mode for nFeat from the quick widget menu
|
|
int ai_SetActionMode(object oAssociate, int nFeat);
|
|
|
|
void ai_SetupPlayerTarget(object oCreature)
|
|
{
|
|
object oModule = GetModule();
|
|
string sModuleTargetEvent = GetEventScript(oModule, EVENT_SCRIPT_MODULE_ON_PLAYER_TARGET);
|
|
if(sModuleTargetEvent != "")
|
|
{
|
|
if(sModuleTargetEvent != "0e_player_target") SetLocalString(oModule, AI_MODULE_TARGET_EVENT, sModuleTargetEvent);
|
|
}
|
|
SetEventScript(oModule, EVENT_SCRIPT_MODULE_ON_PLAYER_TARGET, "0e_player_target");
|
|
}
|
|
void ai_OriginalActionAssociate(object oPC, object oTarget, location lLocation)
|
|
{
|
|
object oAssociate = OBJECT_SELF;
|
|
if(!GetLocalInt(oAssociate, sGhostModeVarname) && GetLocalInt(oPC, sGhostModeVarname))
|
|
{
|
|
effect eGhost = EffectCutsceneGhost();
|
|
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eGhost, oAssociate);
|
|
SetLocalInt(oAssociate, sGhostModeVarname, TRUE);
|
|
}
|
|
int nObjectType = GetObjectType(oTarget);
|
|
ai_ClearCreatureActions(TRUE);
|
|
if(oTarget == GetArea(oPC))
|
|
{
|
|
ActionMoveToLocation(lLocation, TRUE);
|
|
if(GetLocalObject(oPC, AI_FOLLOW_TARGET) == oAssociate)
|
|
{
|
|
float fFollowDistance = 3.0;
|
|
AssignCommand(oPC, ai_ClearCreatureActions());
|
|
AssignCommand(oPC, ActionForceFollowObject(oAssociate, fFollowDistance));
|
|
}
|
|
}
|
|
else if(nObjectType == OBJECT_TYPE_CREATURE)
|
|
{
|
|
if(oTarget != GetLocalObject(oPC, AI_TARGET_ASSOCIATE))
|
|
{
|
|
if(GetMaster(oTarget) == oPC)
|
|
{
|
|
SetLocalString(oPC, AI_TARGET_MODE, "ASSOCIATE_ACTION");
|
|
SetLocalObject(oPC, AI_TARGET_ASSOCIATE, oTarget);
|
|
ai_SendMessages(GetName(oTarget) + " is now in Action Mode.", AI_COLOR_YELLOW, oPC);
|
|
}
|
|
else ActionMoveToObject(oTarget, TRUE);
|
|
}
|
|
}
|
|
else if(nObjectType == OBJECT_TYPE_DOOR)
|
|
{
|
|
if(GetIsTrapped(oTarget) && GetAssociateState(NW_ASC_DISARM_TRAPS, oAssociate))
|
|
{
|
|
if(GetTrapDetectedBy(oTarget, oPC)) SetTrapDetectedBy(oTarget, oAssociate);
|
|
if(GetTrapDetectedBy(oTarget, oAssociate))
|
|
{
|
|
bkAttemptToDisarmTrap(oTarget);
|
|
EnterTargetingMode(oPC, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK);
|
|
return;
|
|
}
|
|
}
|
|
if(GetLocked(oTarget)) bkAttemptToOpenLock(oTarget);
|
|
if(GetIsOpen(oTarget))
|
|
{
|
|
ActionCloseDoor(oTarget, TRUE);
|
|
}
|
|
else ActionOpenDoor(oTarget, TRUE);
|
|
}
|
|
else if(nObjectType == OBJECT_TYPE_ITEM)
|
|
{
|
|
ActionPickUpItem(oTarget);
|
|
}
|
|
else if(nObjectType == OBJECT_TYPE_PLACEABLE)
|
|
{
|
|
ActionMoveToObject(oTarget, TRUE);
|
|
if(GetHasInventory(oTarget))
|
|
{
|
|
if(GetIsTrapped(oTarget) && GetAssociateState(NW_ASC_RETRY_OPEN_LOCKS, oAssociate))
|
|
{
|
|
if(GetTrapDetectedBy(oTarget, oPC)) SetTrapDetectedBy(oTarget, oAssociate);
|
|
if(GetTrapDetectedBy(oTarget, oAssociate))
|
|
{
|
|
bkAttemptToDisarmTrap(oTarget);
|
|
EnterTargetingMode(oPC, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK);
|
|
return;
|
|
}
|
|
if(GetLocked(oTarget))
|
|
{
|
|
if(GetAssociateState(NW_ASC_RETRY_OPEN_LOCKS, oAssociate))
|
|
{
|
|
bkAttemptToOpenLock(oTarget);
|
|
}
|
|
else AssignCommand(oAssociate, ai_HaveCreatureSpeak(oAssociate, 0, "This " + GetName(oTarget) + " is locked!"));
|
|
EnterTargetingMode(oPC, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK);
|
|
return;
|
|
}
|
|
DoPlaceableObjectAction(oTarget, PLACEABLE_ACTION_USE);
|
|
}
|
|
else if(GetLocked(oTarget))
|
|
{
|
|
if(GetAssociateState(NW_ASC_RETRY_OPEN_LOCKS, oAssociate))
|
|
{
|
|
bkAttemptToOpenLock(oTarget);
|
|
}
|
|
else AssignCommand(oAssociate, ai_HaveCreatureSpeak(oAssociate, 0, "This " + GetName(oTarget) + " is locked!"));
|
|
EnterTargetingMode(oPC, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK);
|
|
return;
|
|
}
|
|
DoPlaceableObjectAction(oTarget, PLACEABLE_ACTION_USE);
|
|
}
|
|
DoPlaceableObjectAction(oTarget, PLACEABLE_ACTION_USE);
|
|
}
|
|
else if(nObjectType == OBJECT_TYPE_TRIGGER)
|
|
{
|
|
if(GetIsTrapped(oTarget) && GetAssociateState(NW_ASC_RETRY_OPEN_LOCKS, oAssociate))
|
|
{
|
|
if(GetTrapDetectedBy(oTarget, oPC)) SetTrapDetectedBy(oTarget, oAssociate);
|
|
if(GetTrapDetectedBy(oTarget, oAssociate)) bkAttemptToDisarmTrap(oTarget);
|
|
}
|
|
}
|
|
EnterTargetingMode(oPC, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK);
|
|
}
|
|
void ai_OriginalActionAllAssociates(object oPC, object oTarget, location lLocation)
|
|
{
|
|
object oAssociate;
|
|
int nIndex;
|
|
for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++)
|
|
{
|
|
oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex);
|
|
if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_OriginalActionAssociate(oPC, oTarget, lLocation));
|
|
}
|
|
for(nIndex = 2; nIndex < 6; nIndex++)
|
|
{
|
|
oAssociate = GetAssociate(nIndex, oPC);
|
|
if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_OriginalActionAssociate(oPC, oTarget, lLocation));
|
|
}
|
|
}
|
|
void ai_ActionAssociate(object oPC, object oTarget, location lLocation)
|
|
{
|
|
object oAssociate = OBJECT_SELF;
|
|
if(ai_GetAIMode(oPC, AI_MODE_ACTION_GHOST) &&
|
|
!ai_GetAIMode(oAssociate, AI_MODE_GHOST) &&
|
|
!GetLocalInt(oAssociate, sGhostModeVarname))
|
|
{
|
|
effect eGhost = EffectCutsceneGhost();
|
|
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eGhost, oAssociate);
|
|
SetLocalInt(oAssociate, sGhostModeVarname, TRUE);
|
|
}
|
|
int nObjectType = GetObjectType(oTarget);
|
|
ai_SetAIMode(oAssociate, AI_MODE_COMMANDED, TRUE);
|
|
ai_ClearCreatureActions(TRUE);
|
|
if(oTarget == GetArea(oPC))
|
|
{
|
|
ActionMoveToLocation(lLocation, TRUE);
|
|
if(GetLocalObject(oPC, AI_FOLLOW_TARGET) == oAssociate)
|
|
{
|
|
float fFollowDistance = ai_GetFollowDistance(oPC);
|
|
if(GetDistanceBetween(oAssociate, oPC) <= fFollowDistance)
|
|
{
|
|
DelayCommand(fFollowDistance, AssignCommand(oPC, ActionMoveToObject(oAssociate, TRUE, fFollowDistance)));
|
|
}
|
|
else AssignCommand(oPC, ActionMoveToObject(oAssociate, TRUE, fFollowDistance));
|
|
}
|
|
}
|
|
else if(nObjectType == OBJECT_TYPE_CREATURE)
|
|
{
|
|
if(GetIsDead(oTarget))
|
|
{
|
|
AssignCommand(oAssociate, ActionDoCommand(ai_SearchObject(oAssociate, oTarget, oPC, TRUE)));
|
|
}
|
|
else if(GetIsEnemy(oTarget, oAssociate))
|
|
{
|
|
// Lock them into attacking this target only.
|
|
SetLocalObject(oAssociate, AI_PC_LOCKED_TARGET, oTarget);
|
|
// This resets a henchmens failed Moral save in combat.
|
|
if(GetLocalString(oAssociate, AI_COMBAT_SCRIPT) == "ai_coward")
|
|
{
|
|
SetLocalString(oAssociate, AI_COMBAT_SCRIPT, GetLocalString(oAssociate, AI_DEFAULT_SCRIPT));
|
|
}
|
|
if(ai_GetIsInCombat(oAssociate)) ai_DoAssociateCombatRound(oAssociate, oTarget);
|
|
else
|
|
{
|
|
ai_HaveCreatureSpeak(oAssociate, 5, ":0:1:2:3:6:");
|
|
ai_StartAssociateCombat(oAssociate, oTarget);
|
|
}
|
|
ai_SendMessages(GetName(oAssociate) + " is attacking " + GetName(oTarget), AI_COLOR_RED, oPC);
|
|
}
|
|
else
|
|
{
|
|
ActionMoveToObject(oTarget, TRUE);
|
|
// Player will be stuck with this variable if they are not using the AI.
|
|
DeleteLocalInt(oTarget, "AI_I_AM_BEING_HEALED");
|
|
ActionDoCommand(ai_ActionTryHealing(oAssociate, oTarget));
|
|
}
|
|
}
|
|
else if(nObjectType == OBJECT_TYPE_DOOR)
|
|
{
|
|
if(GetIsTrapped(oTarget))
|
|
{
|
|
if(GetTrapDetectedBy(oTarget, oPC)) SetTrapDetectedBy(oTarget, oAssociate);
|
|
if(GetTrapDetectedBy(oTarget, oAssociate))
|
|
{
|
|
int bStopAction = !GetLocalInt(oTarget, "AI_CANNOT_TRAP_" + GetTag(oAssociate));
|
|
if(ai_ReactToTrap(oAssociate, oTarget, TRUE)) bStopAction = TRUE;
|
|
if(bStopAction)
|
|
{
|
|
EnterTargetingMode(oPC, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if(GetLocked(oTarget)) ai_AttemptToByPassLock(oAssociate, oTarget, TRUE);
|
|
else if(GetIsOpen(oTarget)) ActionCloseDoor(oTarget, TRUE);
|
|
else ActionOpenDoor(oTarget, TRUE);
|
|
}
|
|
else if(nObjectType == OBJECT_TYPE_ITEM)
|
|
{
|
|
ActionPickUpItem(oTarget);
|
|
}
|
|
else if(nObjectType == OBJECT_TYPE_PLACEABLE)
|
|
{
|
|
ActionMoveToObject(oTarget, TRUE);
|
|
if(GetHasInventory(oTarget))
|
|
{
|
|
if(GetIsTrapped(oTarget))
|
|
{
|
|
if(GetTrapDetectedBy(oTarget, oPC)) SetTrapDetectedBy(oTarget, oAssociate);
|
|
if(GetTrapDetectedBy(oTarget, oAssociate))
|
|
{
|
|
if(ai_ReactToTrap(oAssociate, oTarget, TRUE))
|
|
{
|
|
EnterTargetingMode(oPC, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK);
|
|
return;
|
|
}
|
|
|
|
}
|
|
}
|
|
if(GetLocked(oTarget)) ai_AttemptToByPassLock(oAssociate, oTarget, TRUE);
|
|
else ActionDoCommand(ai_SearchObject(oAssociate, oTarget, oPC, TRUE));
|
|
}
|
|
else
|
|
{
|
|
if(ai_GetAIMode(oAssociate, AI_MODE_BASH_LOCKS))
|
|
{
|
|
AssignCommand(oAssociate, ai_ClearCreatureActions());
|
|
// Check to make sure we are using a melee weapon.
|
|
if(ai_GetIsMeleeWeapon(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oAssociate)) ||
|
|
ai_EquipBestMeleeWeapon(oAssociate))
|
|
{
|
|
AssignCommand(oAssociate, ActionWait(1.0));
|
|
AssignCommand(oAssociate, ActionAttack(oTarget));
|
|
}
|
|
}
|
|
else AssignCommand(oAssociate, DoPlaceableObjectAction(oTarget, PLACEABLE_ACTION_USE));
|
|
}
|
|
}
|
|
else if(nObjectType == OBJECT_TYPE_TRIGGER)
|
|
{
|
|
if(GetIsTrapped(oTarget))
|
|
{
|
|
if(GetTrapDetectedBy(oTarget, oPC)) SetTrapDetectedBy(oTarget, oAssociate);
|
|
if(GetTrapDetectedBy(oTarget, oAssociate)) ai_ReactToTrap(oAssociate, oTarget, TRUE);
|
|
}
|
|
}
|
|
EnterTargetingMode(oPC, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK);
|
|
}
|
|
void ai_ActionAllAssociates(object oPC, object oTarget, location lLocation)
|
|
{
|
|
object oAssociate;
|
|
int nIndex;
|
|
for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++)
|
|
{
|
|
oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex);
|
|
if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_ActionAssociate(oPC, oTarget, lLocation));
|
|
}
|
|
for(nIndex = 2; nIndex < 6; nIndex++)
|
|
{
|
|
oAssociate = GetAssociate(nIndex, oPC);
|
|
if(oAssociate != OBJECT_INVALID) AssignCommand(oAssociate, ai_ActionAssociate(oPC, oTarget, lLocation));
|
|
}
|
|
}
|
|
void ai_SelectFollowTarget(object oPC, object oAssociate, object oTarget)
|
|
{
|
|
string sAssociateType = ai_GetAssociateType(oPC, oAssociate);
|
|
int nToken = NuiFindWindow(oPC, sAssociateType + AI_WIDGET_NUI);
|
|
float fRange = GetLocalFloat(oAssociate, AI_FOLLOW_RANGE) +
|
|
StringToFloat(Get2DAString("appearance", "PREFATCKDIST", GetAppearanceType(oAssociate)));
|
|
string sRange = FloatToString(fRange, 0, 0);
|
|
if(oAssociate == oTarget)
|
|
{
|
|
ai_SetAIMode(oAssociate, AI_MODE_FOLLOW, FALSE);
|
|
DeleteLocalObject(oAssociate, AI_FOLLOW_TARGET);
|
|
string sTarget;
|
|
if(ai_GetIsCharacter(oAssociate))
|
|
{
|
|
sTarget = "nobody";
|
|
ai_SendMessages(GetName(oAssociate) + " is not following anyone now!", AI_COLOR_YELLOW, oPC);
|
|
}
|
|
else
|
|
{
|
|
sTarget = GetName(oPC);
|
|
ai_SendMessages(GetName(oAssociate) + " is now following " + sTarget + "!", AI_COLOR_YELLOW, oPC);
|
|
}
|
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_follow_target_tooltip", " " + GetName(oAssociate) + " following " + sTarget + " [" + sRange + " meters]");
|
|
}
|
|
else
|
|
{
|
|
ai_SetAIMode(oAssociate, AI_MODE_FOLLOW, TRUE);
|
|
SetLocalObject(oAssociate, AI_FOLLOW_TARGET, oTarget);
|
|
ai_SendMessages(GetName(oAssociate) + " is now following " + GetName(oTarget) + ".", AI_COLOR_YELLOW, oPC);
|
|
AssignCommand(oAssociate, ActionMoveToObject(oTarget, TRUE, ai_GetFollowDistance(oAssociate)));
|
|
ai_UpdateToolTipUI(oPC, sAssociateType + AI_COMMAND_NUI, sAssociateType + AI_WIDGET_NUI, "btn_follow_target_tooltip", " " + GetName(oAssociate) + " following " + GetName(oTarget) + " [" + sRange + " meters]");
|
|
}
|
|
aiSaveAssociateModesToDb(oPC, oAssociate);
|
|
}
|
|
void ai_OriginalRemoveAllActionMode(object oPC)
|
|
{
|
|
if(!ai_GetAIMode(oPC, AI_MODE_ACTION_GHOST)) return;
|
|
object oAssociate;
|
|
int nIndex;
|
|
for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++)
|
|
{
|
|
oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex);
|
|
if(oAssociate != OBJECT_INVALID &&
|
|
!ai_GetAIMode(oAssociate, AI_MODE_GHOST) &&
|
|
GetLocalInt(oAssociate, sGhostModeVarname))
|
|
{
|
|
ai_RemoveASpecificEffect(oAssociate, EFFECT_TYPE_CUTSCENEGHOST);
|
|
DeleteLocalInt(oAssociate, sGhostModeVarname);
|
|
}
|
|
}
|
|
for(nIndex = 2; nIndex < 6; nIndex++)
|
|
{
|
|
oAssociate = GetAssociate(nIndex, oPC);
|
|
if(oAssociate != OBJECT_INVALID &&
|
|
!ai_GetAIMode(oAssociate, AI_MODE_GHOST) &&
|
|
GetLocalInt(oAssociate, sGhostModeVarname))
|
|
{
|
|
ai_RemoveASpecificEffect(oAssociate, EFFECT_TYPE_CUTSCENEGHOST);
|
|
DeleteLocalInt(oAssociate, sGhostModeVarname);
|
|
}
|
|
}
|
|
}
|
|
void ai_RemoveAllActionMode(object oPC)
|
|
{
|
|
object oAssociate;
|
|
int nIndex;
|
|
for(nIndex = 1; nIndex <= AI_MAX_HENCHMAN; nIndex++)
|
|
{
|
|
oAssociate = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oPC, nIndex);
|
|
if(oAssociate != OBJECT_INVALID)
|
|
{
|
|
ai_SetAIMode(oAssociate, AI_MODE_COMMANDED, FALSE);
|
|
if(ai_GetAIMode(oPC, AI_MODE_ACTION_GHOST) &&
|
|
!ai_GetAIMode(oAssociate, AI_MODE_GHOST) &&
|
|
GetLocalInt(oAssociate, sGhostModeVarname))
|
|
{
|
|
ai_RemoveASpecificEffect(oAssociate, EFFECT_TYPE_CUTSCENEGHOST);
|
|
DeleteLocalInt(oAssociate, sGhostModeVarname);
|
|
}
|
|
ExecuteScript("nw_ch_ac1", oAssociate);
|
|
}
|
|
}
|
|
for(nIndex = 2; nIndex < 6; nIndex++)
|
|
{
|
|
oAssociate = GetAssociate(nIndex, oPC);
|
|
if(oAssociate != OBJECT_INVALID)
|
|
{
|
|
ai_SetAIMode(oAssociate, AI_MODE_COMMANDED, FALSE);
|
|
if(ai_GetAIMode(oPC, AI_MODE_ACTION_GHOST) &&
|
|
!ai_GetAIMode(oAssociate, AI_MODE_GHOST) &&
|
|
GetLocalInt(oAssociate, sGhostModeVarname))
|
|
{
|
|
ai_RemoveASpecificEffect(oAssociate, EFFECT_TYPE_CUTSCENEGHOST);
|
|
DeleteLocalInt(oAssociate, sGhostModeVarname);
|
|
}
|
|
ExecuteScript("nw_ch_ac1", oAssociate);
|
|
}
|
|
}
|
|
}
|
|
void ai_SelectTrap(object oPC, object oAssociate, object oItem)
|
|
{
|
|
if(GetBaseItemType(oItem) != BASE_ITEM_TRAPKIT)
|
|
{
|
|
ai_SendMessages("A trap kit was not selected.", AI_COLOR_YELLOW, oPC);
|
|
return;
|
|
}
|
|
ai_SendMessages("Now select a location to place the trap.", AI_COLOR_YELLOW, oPC);
|
|
SetLocalObject(oAssociate, "AI_TRAP_KIT", oItem);
|
|
SetLocalString(oPC, AI_TARGET_MODE, "ASSOCIATE_PLACE_TRAP");
|
|
OpenInventory(oAssociate, oPC);
|
|
EnterTargetingMode(oPC, OBJECT_TYPE_TILE, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK);
|
|
}
|
|
void ai_PlaceTrap(object oPC, location lLocation)
|
|
{
|
|
object oItem = GetLocalObject(OBJECT_SELF, "AI_TRAP_KIT");
|
|
itemproperty ipTrap = GetFirstItemProperty(oItem);
|
|
if(GetItemPropertyType(ipTrap) == ITEM_PROPERTY_TRAP)
|
|
{
|
|
ActionUseItemAtLocation(oItem, ipTrap, lLocation);
|
|
}
|
|
else ai_SendMessages("This trap kit does not have a trap property!", AI_COLOR_YELLOW, oPC);
|
|
}
|
|
void ai_AddToGroup(object oDM, object oTarget, string sTargetMode)
|
|
{
|
|
string sGroup = GetStringRight(sTargetMode, 1);
|
|
if(oDM == oTarget)
|
|
{
|
|
ai_SendMessages("Group " + sGroup + " has been cleared.", AI_COLOR_YELLOW, oDM);
|
|
string sText = "Group " + sGroup;
|
|
NuiSetBind(oDM, NuiFindWindow(oDM, "dm" + AI_WIDGET_NUI), "btn_cmd_group" + sGroup + "_tooltip", JsonString(sText + " (Left Action/Right Add)"));
|
|
NuiSetBind(oDM, NuiFindWindow(oDM, "dm" + AI_COMMAND_NUI), "btn_cmd_group" + sGroup + "_tooltip", JsonString(sText + " (Left Action/Right Add)"));
|
|
NuiSetBind(oDM, NuiFindWindow(oDM, "dm" + AI_COMMAND_NUI), "btn_cmd_group" + sGroup + "_label", JsonString(sText));
|
|
DeleteLocalJson(oDM, "DM_GROUP" + sGroup);
|
|
return;
|
|
}
|
|
string sName = GetName(oTarget);
|
|
json jGroup = GetLocalJson(oDM, "DM_GROUP" + sGroup);
|
|
if(JsonGetType(jGroup) == JSON_TYPE_NULL)
|
|
{
|
|
string sText = sName + "'s group";
|
|
NuiSetBind(oDM, NuiFindWindow(oDM, "dm" + AI_WIDGET_NUI), "btn_cmd_group" + sGroup + "_tooltip", JsonString(sText + " [Run]"));
|
|
NuiSetBind(oDM, NuiFindWindow(oDM, "dm" + AI_COMMAND_NUI), "btn_cmd_group" + sGroup + "_tooltip", JsonString(sText + " [Run]"));
|
|
NuiSetBind(oDM, NuiFindWindow(oDM, "dm" + AI_COMMAND_NUI), "btn_cmd_group" + sGroup + "_label", JsonString(sText));
|
|
jGroup = JsonArrayInsert(JsonArray(), JsonInt(1));
|
|
}
|
|
string sUUID = GetObjectUUID(oTarget);
|
|
int nIndex = 1;
|
|
string sListUUID = JsonGetString(JsonArrayGet(jGroup, nIndex));
|
|
while(sListUUID != "")
|
|
{
|
|
if(sListUUID == sUUID)
|
|
{
|
|
ai_SendMessages("This creature is already in the group!", AI_COLOR_RED, oDM);
|
|
return;
|
|
}
|
|
sListUUID = JsonGetString(JsonArrayGet(jGroup, ++nIndex));
|
|
}
|
|
jGroup = JsonArrayInsert(jGroup, JsonString(sUUID));
|
|
ai_SendMessages(sName + " has been saved to group" + sGroup, AI_COLOR_YELLOW, oDM);
|
|
SetLocalJson(oDM, "DM_GROUP" + sGroup, jGroup);
|
|
EnterTargetingMode(oDM, OBJECT_TYPE_CREATURE, MOUSECURSOR_PICKUP, MOUSECURSOR_PICKUP_DOWN);
|
|
}
|
|
void ai_MonsterAction(object oDM, object oTarget, location lLocation, int bRun, int nIndex)
|
|
{
|
|
object oCreature = OBJECT_SELF;
|
|
int nObjectType = GetObjectType(oTarget);
|
|
ai_ClearCreatureActions(TRUE);
|
|
if(oTarget == GetArea(oDM))
|
|
{
|
|
ActionMoveToLocation(lLocation, bRun);
|
|
}
|
|
else if(nObjectType == OBJECT_TYPE_CREATURE)
|
|
{
|
|
if(GetIsDead(oTarget)) return;
|
|
else if(GetIsEnemy(oTarget, oCreature))
|
|
{
|
|
// Lock them into attacking this target only.
|
|
SetLocalObject(oCreature, AI_PC_LOCKED_TARGET, oTarget);
|
|
// This resets a creatures failed Moral save in combat.
|
|
if(GetLocalString(oCreature, AI_COMBAT_SCRIPT) == "ai_coward")
|
|
{
|
|
SetLocalString(oCreature, AI_COMBAT_SCRIPT, GetLocalString(oCreature, AI_DEFAULT_SCRIPT));
|
|
}
|
|
if(ai_GetIsInCombat(oCreature)) ai_DoMonsterCombatRound(oCreature);
|
|
else
|
|
{
|
|
ai_HaveCreatureSpeak(oCreature, 5, ":0:1:2:3:6:");
|
|
ai_StartMonsterCombat(oCreature);
|
|
}
|
|
if(nIndex == 1)
|
|
{
|
|
ai_SendMessages(GetName(oCreature) + "'s group is attacking " + GetName(oTarget), AI_COLOR_RED, oDM);
|
|
}
|
|
}
|
|
else if(oTarget == oDM)
|
|
{
|
|
if(GetLocalInt(oCreature, "AI_FOLLOWING_DM"))
|
|
{
|
|
ClearAllActions(FALSE, oCreature);
|
|
DeleteLocalInt(oCreature, "AI_FOLLOWING_DM");
|
|
if(nIndex == 1)
|
|
{
|
|
ai_SendMessages(GetName(oCreature) + "'s group has stopped following you.", AI_COLOR_RED, oDM);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ActionForceFollowObject(oDM, 4.0);
|
|
SetLocalInt(oCreature, "AI_FOLLOWING_DM", TRUE);
|
|
if(nIndex == 1)
|
|
{
|
|
ai_SendMessages(GetName(oCreature) + "'s group is following you.", AI_COLOR_RED, oDM);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ActionMoveToObject(oTarget, TRUE);
|
|
// Player will be stuck with this variable if they are not using the AI.
|
|
DeleteLocalInt(oTarget, "AI_I_AM_BEING_HEALED");
|
|
ActionDoCommand(ai_ActionTryHealing(oCreature, oTarget));
|
|
if(nIndex == 1)
|
|
{
|
|
ai_SendMessages(GetName(oCreature) + "'s group is moving to and attempting to heal " + GetName(oTarget), AI_COLOR_RED, oDM);
|
|
}
|
|
}
|
|
}
|
|
else if(nObjectType == OBJECT_TYPE_DOOR)
|
|
{
|
|
if(GetIsTrapped(oTarget))
|
|
{
|
|
if(GetTrapDetectedBy(oTarget, oDM)) SetTrapDetectedBy(oTarget, oCreature);
|
|
if(GetTrapDetectedBy(oTarget, oCreature))
|
|
{
|
|
ai_ReactToTrap(oCreature, oTarget, TRUE);
|
|
EnterTargetingMode(oDM, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK);
|
|
return;
|
|
}
|
|
else if(GetLocked(oTarget)) ai_AttemptToByPassLock(oCreature, oTarget);
|
|
}
|
|
else if(GetLocked(oTarget)) ai_AttemptToByPassLock(oCreature, oTarget);
|
|
else if(GetIsOpen(oTarget))
|
|
{
|
|
ActionCloseDoor(oTarget, TRUE);
|
|
}
|
|
else ActionOpenDoor(oTarget, TRUE);
|
|
}
|
|
else if(nObjectType == OBJECT_TYPE_ITEM)
|
|
{
|
|
ActionPickUpItem(oTarget);
|
|
}
|
|
else if(nObjectType == OBJECT_TYPE_PLACEABLE)
|
|
{
|
|
ActionMoveToObject(oTarget, TRUE);
|
|
if(GetHasInventory(oTarget))
|
|
{
|
|
if(GetIsTrapped(oTarget))
|
|
{
|
|
if(GetTrapDetectedBy(oTarget, oDM)) SetTrapDetectedBy(oTarget, oCreature);
|
|
if(GetTrapDetectedBy(oTarget, oCreature))
|
|
{
|
|
ai_ReactToTrap(oCreature, oTarget, TRUE);
|
|
EnterTargetingMode(oDM, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK);
|
|
return;
|
|
}
|
|
if(GetLocked(oTarget))
|
|
{
|
|
if(!ai_AttemptToByPassLock(oCreature, oTarget))
|
|
{
|
|
AssignCommand(oCreature, ai_HaveCreatureSpeak(oCreature, 0, "This " + GetName(oTarget) + " is locked!"));
|
|
}
|
|
EnterTargetingMode(oDM, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK);
|
|
return;
|
|
}
|
|
DoPlaceableObjectAction(oTarget, PLACEABLE_ACTION_USE);
|
|
}
|
|
else if(GetLocked(oTarget))
|
|
{
|
|
if(ai_AttemptToByPassLock(oCreature, oTarget))
|
|
{
|
|
AssignCommand(oCreature, ai_HaveCreatureSpeak(oCreature, 0, "This " + GetName(oTarget) + " is locked!"));
|
|
}
|
|
EnterTargetingMode(oDM, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK);
|
|
return;
|
|
}
|
|
ActionDoCommand(ai_SearchObject(oCreature, oTarget, oDM, TRUE));
|
|
}
|
|
DoPlaceableObjectAction(oTarget, PLACEABLE_ACTION_USE);
|
|
}
|
|
else if(nObjectType == OBJECT_TYPE_TRIGGER)
|
|
{
|
|
if(GetIsTrapped(oTarget))
|
|
{
|
|
if(GetTrapDetectedBy(oTarget, oDM)) SetTrapDetectedBy(oTarget, oCreature);
|
|
if(GetTrapDetectedBy(oTarget, oCreature)) ai_ReactToTrap(oCreature, oTarget, TRUE);
|
|
}
|
|
}
|
|
EnterTargetingMode(oDM, OBJECT_TYPE_ALL, MOUSECURSOR_ACTION, MOUSECURSOR_NOWALK);
|
|
}
|
|
void ai_DMAction(object oDM, object oTarget, location lLocation, string sTargetMode)
|
|
{
|
|
string sGroup = GetStringRight(sTargetMode, 1);
|
|
json jGroup = GetLocalJson(oDM, "DM_GROUP" + sGroup);
|
|
int bRun = JsonGetInt(JsonArrayGet(jGroup, 0));
|
|
int nIndex = 1;
|
|
string sUUID = JsonGetString(JsonArrayGet(jGroup, nIndex));
|
|
object oCreature;
|
|
while(sUUID != "")
|
|
{
|
|
oCreature = GetObjectByUUID(sUUID);
|
|
AssignCommand(oCreature, ai_MonsterAction(oDM, oTarget, lLocation, bRun, nIndex));
|
|
sUUID = JsonGetString(JsonArrayGet(jGroup, ++nIndex));
|
|
}
|
|
if(nIndex == 0) ai_SendMessages("Group" + sGroup + " is empty!", AI_COLOR_RED, oDM);
|
|
}
|
|
void ai_SelectWidgetSpellTarget(object oPC, object oAssociate, string sElem)
|
|
{
|
|
int nIndex;
|
|
if(GetStringLength(sElem) == 13) nIndex = StringToInt(GetStringRight(sElem, 2));
|
|
else nIndex = StringToInt(GetStringRight(sElem, 1));
|
|
SetLocalInt(oAssociate, "AI_WIDGET_SPELL_INDEX", nIndex);
|
|
string sAssociateType = ai_GetAssociateType(oPC, oAssociate);
|
|
json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata");
|
|
json jSpells = JsonArrayGet(jAIData, 10);
|
|
json jWidget = JsonArrayGet(jSpells, 2);
|
|
json jSpell = JsonArrayGet(jWidget, nIndex);
|
|
int nSpell = JsonGetInt(JsonArrayGet(jSpell, 0));
|
|
int nClass = JsonGetInt(JsonArrayGet(jSpell, 1));
|
|
if(nClass == -1) // This is an Item.
|
|
{
|
|
object oItem = GetObjectByUUID(JsonGetString(JsonArrayGet(jSpell, 5)));
|
|
int nBaseItemType = GetBaseItemType(oItem);
|
|
if(Get2DAString("spells", "Range", nSpell) == "P" || // Self
|
|
nBaseItemType == BASE_ITEM_ENCHANTED_POTION ||
|
|
nBaseItemType == BASE_ITEM_POTIONS)
|
|
{
|
|
int nIprpSubType = JsonGetInt(JsonArrayGet(jSpell, 4));
|
|
itemproperty ipProperty = GetFirstItemProperty(oItem);
|
|
while(GetIsItemPropertyValid(ipProperty))
|
|
{
|
|
if(nIprpSubType == GetItemPropertySubType(ipProperty)) break;
|
|
ipProperty = GetNextItemProperty(oItem);
|
|
}
|
|
if(ai_GetIsInCombat(oAssociate)) AssignCommand(oAssociate, ai_ClearCreatureActions(TRUE));
|
|
AssignCommand(oAssociate, ActionUseItemOnObject(oItem, ipProperty, oAssociate));
|
|
DelayCommand(6.0, ai_UpdateAssociateWidget(oPC, oAssociate));
|
|
return;
|
|
}
|
|
SetLocalString(oPC, AI_TARGET_MODE, "ASSOCIATE_USE_ITEM");
|
|
if(nSpell == SPELL_HEALINGKIT)
|
|
{
|
|
EnterTargetingMode(oPC, OBJECT_TYPE_CREATURE, MOUSECURSOR_MAGIC, MOUSECURSOR_NOMAGIC);
|
|
return;
|
|
}
|
|
}
|
|
else // Feats, Spells, Special Abilities.
|
|
{
|
|
int nFeat = JsonGetInt(JsonArrayGet(jSpell, 5));
|
|
if(nFeat)
|
|
{
|
|
if(!nSpell || Get2DAString("spells", "Range", nSpell) == "P" || // Self
|
|
nFeat == FEAT_SUMMON_FAMILIAR || nFeat == FEAT_ANIMAL_COMPANION ||
|
|
nFeat == FEAT_TURN_UNDEAD)
|
|
{
|
|
if(ai_GetIsInCombat(oAssociate)) AssignCommand(oAssociate, ai_ClearCreatureActions(TRUE));
|
|
// Adjust the spell used for wild shape and other shape feats.
|
|
if(nFeat == FEAT_WILD_SHAPE) nSpell += 607;
|
|
if(nFeat == FEAT_ELEMENTAL_SHAPE)
|
|
{
|
|
if(nSpell == 397) nSpell == SUBFEAT_ELEMENTAL_SHAPE_FIRE;
|
|
else if(nSpell == 398) nSpell == SUBFEAT_ELEMENTAL_SHAPE_WATER;
|
|
else if(nSpell == 399) nSpell == SUBFEAT_ELEMENTAL_SHAPE_EARTH;
|
|
else if(nSpell == 400) nSpell == SUBFEAT_ELEMENTAL_SHAPE_AIR;
|
|
}
|
|
// Do special targeting for attack feats.
|
|
if(nFeat == FEAT_STUNNING_FIST || nFeat == FEAT_DIRTY_FIGHTING ||
|
|
nFeat == FEAT_WHIRLWIND_ATTACK || nFeat == FEAT_QUIVERING_PALM ||
|
|
nFeat == FEAT_KNOCKDOWN || nFeat == FEAT_IMPROVED_KNOCKDOWN ||
|
|
nFeat == FEAT_SAP || nFeat == FEAT_KI_DAMAGE ||
|
|
nFeat == FEAT_DISARM || nFeat == FEAT_IMPROVED_DISARM ||
|
|
nFeat == FEAT_SMITE_EVIL || nFeat == FEAT_SMITE_GOOD)
|
|
{
|
|
SetLocalString(oPC, AI_TARGET_MODE, "ASSOCIATE_USE_FEAT");
|
|
SetLocalObject(oPC, AI_TARGET_ASSOCIATE, oAssociate);
|
|
EnterTargetingMode(oPC, OBJECT_TYPE_CREATURE, MOUSECURSOR_ATTACK, MOUSECURSOR_NOATTACK);
|
|
}
|
|
// Check feat and adjust if it is an action mode feat.
|
|
if(ai_SetActionMode(oAssociate, nFeat)) return;
|
|
AssignCommand(oAssociate, ActionUseFeat(nFeat, oAssociate, nSpell));
|
|
DelayCommand(6.0, ai_UpdateAssociateWidget(oPC, oAssociate));
|
|
return;
|
|
}
|
|
SetLocalString(oPC, AI_TARGET_MODE, "ASSOCIATE_USE_FEAT");
|
|
}
|
|
else SetLocalString(oPC, AI_TARGET_MODE, "ASSOCIATE_CAST_SPELL");
|
|
}
|
|
SetLocalObject(oPC, AI_TARGET_ASSOCIATE, oAssociate);
|
|
int nObjectType;
|
|
string sTarget = Get2DAString("spells", "TargetType", nSpell);
|
|
int nTarget = ai_HexStringToInt(sTarget);
|
|
//SendMessageToPC(GetFirstPC(), "nTarget: " + IntToString(nTarget));
|
|
if((nTarget & 1) && !(nTarget & 2) &&!(nTarget & 4))
|
|
{
|
|
if(ai_GetIsInCombat(oAssociate)) AssignCommand(oAssociate, ai_ClearCreatureActions(TRUE));
|
|
ai_CastWidgetSpell(oPC, oAssociate, oAssociate, GetLocation(oAssociate));
|
|
DelayCommand(6.0, ai_UpdateAssociateWidget(oPC, oAssociate));
|
|
return;
|
|
}
|
|
if((nTarget & 1) || (nTarget & 2)) nObjectType += OBJECT_TYPE_CREATURE;
|
|
if(nTarget & 4) nObjectType += OBJECT_TYPE_TILE;
|
|
if(nTarget & 8) nObjectType += OBJECT_TYPE_ITEM;
|
|
if(nTarget & 16) nObjectType += OBJECT_TYPE_DOOR;
|
|
if(nTarget & 32) nObjectType += OBJECT_TYPE_PLACEABLE;
|
|
if(nTarget & 64) nObjectType += OBJECT_TYPE_TRIGGER;
|
|
string sShape = Get2DAString("spells", "TargetShape", nSpell);
|
|
int nShape, nSetData;
|
|
float fRange;
|
|
if(oPC == oAssociate)
|
|
{
|
|
nSetData = TRUE;
|
|
fRange = ai_GetSpellRange(nSpell);
|
|
if(fRange == 0.1) fRange = 0.0;
|
|
}
|
|
if(sShape == "sphere")
|
|
{
|
|
nShape = SPELL_TARGETING_SHAPE_SPHERE;
|
|
nSetData = TRUE;
|
|
}
|
|
else if(sShape == "rectangle")
|
|
{
|
|
nShape = SPELL_TARGETING_SHAPE_RECT;
|
|
nSetData = TRUE;
|
|
}
|
|
else if(sShape == "hsphere")
|
|
{
|
|
nShape = SPELL_TARGETING_SHAPE_HSPHERE;
|
|
nSetData = TRUE;
|
|
}
|
|
else if(sShape == "cone") nShape = SPELL_TARGETING_SHAPE_CONE;
|
|
else nShape = SPELL_TARGETING_SHAPE_NONE;
|
|
if(nSetData)
|
|
{
|
|
float fSizeX = StringToFloat(Get2DAString("spells", "TargetSizeX", nSpell));
|
|
float fSizeY = StringToFloat(Get2DAString("spells", "TargetSizeY", nSpell));
|
|
int nFlags = StringToInt(Get2DAString("spells", "TargetFlags", nSpell));
|
|
SetEnterTargetingModeData(oPC, nShape, fSizeX, fSizeY, nFlags, fRange);
|
|
}
|
|
EnterTargetingMode(oPC, nObjectType, MOUSECURSOR_MAGIC, MOUSECURSOR_NOMAGIC);
|
|
}
|
|
void ai_UpdateAssociateWidget(object oPC, object oAssociate)
|
|
{
|
|
int nUIToken = NuiFindWindow(oPC, ai_GetAssociateType(oPC, oAssociate) + AI_WIDGET_NUI);
|
|
if(nUIToken)
|
|
{
|
|
DelayCommand(0.0, NuiDestroy(oPC, nUIToken));
|
|
DelayCommand(0.1, ai_CreateWidgetNUI(oPC, oAssociate));
|
|
/* Not sure why I did this?
|
|
if(oPC != oAssociate)
|
|
{
|
|
nUIToken = NuiFindWindow(oPC, "pc" + AI_WIDGET_NUI);
|
|
if(nUIToken)
|
|
{
|
|
DelayCommand(0.0, NuiDestroy(oPC, nUIToken));
|
|
DelayCommand(0.1, ai_CreateWidgetNUI(oPC, oPC));
|
|
}
|
|
} */
|
|
}
|
|
}
|
|
int ai_SetActionMode(object oAssociate, int nFeat)
|
|
{
|
|
int nMode;
|
|
if(nFeat == FEAT_POWER_ATTACK) nMode = ACTION_MODE_POWER_ATTACK;
|
|
else if(nFeat == FEAT_RAPID_SHOT) nMode = ACTION_MODE_RAPID_SHOT;
|
|
else if(nFeat == FEAT_FLURRY_OF_BLOWS) nMode = ACTION_MODE_FLURRY_OF_BLOWS;
|
|
else if(nFeat == FEAT_IMPROVED_POWER_ATTACK) nMode = ACTION_MODE_IMPROVED_POWER_ATTACK;
|
|
else if(nFeat == FEAT_EXPERTISE) nMode = ACTION_MODE_EXPERTISE;
|
|
else if(nFeat == FEAT_IMPROVED_EXPERTISE) nMode = ACTION_MODE_IMPROVED_EXPERTISE;
|
|
else if(nFeat == FEAT_DIRTY_FIGHTING) nMode = ACTION_MODE_DIRTY_FIGHTING;
|
|
if(nMode)
|
|
{
|
|
SetActionMode(oAssociate, nMode, !GetActionMode(oAssociate, nMode));
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|