Amon_PRC8/_module/nss/hench_o0_act.nss

851 lines
30 KiB
Plaintext
Raw Permalink Normal View History

2025-04-03 19:00:46 -04:00
/*
Henchman Inventory And Battle AI
This file contains routines to handle locks, traps, auto pickup of items
*/
#include "hench_i0_ident"
#include "hench_i0_act"
#include "hench_i0_melee"
#include "hench_i0_ai"
#include "hench_i0_itemsp"
const string sDisarmTried = "tk_disarm_tried";
const string sLockTried = "tk_lock_tried";
const string sMasterShownFlag = "tk_master_shown_flag";
const string sGiveToMaster = "tk_give_to_master";
const string sLastMoveDistance = "tk_last_move_distance";
const string sSomethingFishy = "tk_something_fishy";
const int DOING_MOVINGTO_ACTION = 2;
int g_spellsInit;
void InitItemSpellCasting()
{
if (!g_spellsInit)
{
InitializeItemSpells(HenchDetermineClassToUse(), GetHasEffect(EFFECT_TYPE_POLYMORPH), HENCH_INIT_LOCK_SPELLS);
g_spellsInit = TRUE;
}
}
int GetMaxThievesToolBonus()
{
object oThievesTool;
oThievesTool = GetItemPossessedBy(OBJECT_SELF, "X2_IT_PICKS002");
if (GetIsObjectValid(oThievesTool))
{
return 12;
}
oThievesTool = GetItemPossessedBy(OBJECT_SELF, "NW_IT_PICKS004");
if (GetIsObjectValid(oThievesTool))
{
return 10;
}
oThievesTool = GetItemPossessedBy(OBJECT_SELF, "X2_IT_PICKS001");
if (GetIsObjectValid(oThievesTool))
{
return 8;
}
oThievesTool = GetItemPossessedBy(OBJECT_SELF, "NW_IT_PICKS003");
if (GetIsObjectValid(oThievesTool))
{
return 6;
}
oThievesTool = GetItemPossessedBy(OBJECT_SELF, "NW_IT_PICKS002");
if (GetIsObjectValid(oThievesTool))
{
return 3;
}
oThievesTool = GetItemPossessedBy(OBJECT_SELF, "NW_IT_PICKS001");
if (GetIsObjectValid(oThievesTool))
{
return 1;
}
return 0;
}
int IsDoorInLineOfSight(object oTarget)
{
float fMeDoorDist;
object oDoor = GetFirstObjectInShape(SHAPE_SPHERE, 40.0, GetLocation(OBJECT_SELF),
TRUE, OBJECT_TYPE_DOOR);
float fMeTrapDist = GetDistanceBetween(oTarget, OBJECT_SELF);
while (GetIsObjectValid(oDoor))
{
fMeDoorDist = GetDistanceBetween(oDoor, OBJECT_SELF);
if (fMeDoorDist < fMeTrapDist && !GetIsOpen(oDoor))
{
if (IsOnOppositeSideOfDoor(oDoor, oTarget, OBJECT_SELF))
{
return TRUE;
}
}
oDoor = GetNextObjectInShape(SHAPE_SPHERE, 40.0, GetLocation(OBJECT_SELF),
TRUE, OBJECT_TYPE_DOOR);
}
return FALSE;
}
int DoOpenWithAttackSpells(object oTarget, int bDoMoveAway = FALSE)
{
int nSpell;
if (GetHasSpell(SPELL_MAGIC_MISSILE))
{
nSpell = SPELL_MAGIC_MISSILE;
}
else if (GetHasSpell(SPELL_ICE_DAGGER))
{
nSpell = SPELL_ICE_DAGGER;
}
else if (GetHasSpell(SPELL_NEGATIVE_ENERGY_RAY) && (GetObjectType(oTarget) != OBJECT_TYPE_DOOR))
{
nSpell = SPELL_NEGATIVE_ENERGY_RAY;
}
else if (GetHasSpell(SPELL_ACID_SPLASH))
{
nSpell = SPELL_ACID_SPLASH;
}
else if (GetHasSpell(SPELL_ELECTRIC_JOLT))
{
nSpell = SPELL_ELECTRIC_JOLT;
}
else if (GetHasSpell(SPELL_RAY_OF_FROST))
{
nSpell = SPELL_RAY_OF_FROST;
}
else
{
return FALSE;
}
if (bDoMoveAway && GetDistanceToObject(oTarget) < 5.0)
{
ActionMoveAwayFromObject(oTarget, FALSE, 5.0);
}
ActionCastSpellAtObject(nSpell, oTarget);
return TRUE;
}
void DoOpenWithWeapons(object oTarget)
{
VoiceCanDo();
ActionEquipMostDamagingMelee(oTarget);
ActionWait(1.0);
HenchAttackObject(oTarget);
SetLocalObject(OBJECT_SELF, "NW_GENERIC_DOOR_TO_BASH", oTarget);
ActionDoCommand(VoiceTaskComplete());
}
void DoDisarmTrap(object oTrap, int bForce = FALSE)
{
ClearAllActions();
if (GetTrapDisarmable(oTrap) && GetLocalInt(oTrap, "NW_L_IATTEMPTEDTODISARMNOWORK") == 0)
{
InitItemSpellCasting();
if (GetHasFixedSpell(SPELL_FIND_TRAPS))
{
CastFixedSpellOnObject(SPELL_FIND_TRAPS, OBJECT_SELF, 3);
SetLocalInt(oTrap, "NW_L_IATTEMPTEDTODISARMNOWORK", 10);
return;
}
}
int bIsATrigger = GetObjectType(oTrap) == OBJECT_TYPE_TRIGGER;
int nClass = HenchDetermineClassToUse();
int iAmMage = (nClass == CLASS_TYPE_SORCERER || nClass == CLASS_TYPE_WIZARD);
if (iAmMage && !bIsATrigger && DoOpenWithAttackSpells(oTrap))
{
return;
}
if (!bIsATrigger)
{
HenchStartRangedBashDoor(oTrap);
return;
}
if (!bForce)
{
return;
}
// Throw ourselves on it nobly! :-)
ActionMoveToLocation(GetLocation(oTrap));
SetFacingPoint(GetPositionFromLocation(GetLocation(oTrap)));
ActionRandomWalk();
}
void DoOpenLock(object oLocked)
{
ClearAllActions();
int bIsTrapped = GetTrapDetectedBy(oLocked, OBJECT_SELF);
if (bIsTrapped)
{
DoDisarmTrap(oLocked);
return;
}
if (GetPlotFlag(oLocked))
{
VoiceCannotDo();
return;
}
if ((GetObjectType(oLocked) == OBJECT_TYPE_DOOR) ||
(GetObjectType(oLocked) == OBJECT_TYPE_PLACEABLE))
{
InitItemSpellCasting();
if (GetHasFixedSpell(SPELL_KNOCK))
{
VoiceCanDo();
CastFixedSpellOnObject(SPELL_KNOCK, OBJECT_SELF, 2);
ActionWait(1.0);
return;
}
}
int nClass = HenchDetermineClassToUse();
int iAmMage = (nClass == CLASS_TYPE_SORCERER || nClass == CLASS_TYPE_WIZARD);
if (iAmMage && DoOpenWithAttackSpells(oLocked))
{
return;
}
// TK reduced strength requirement for Xanos bashing
int bBashPossible = GetIsDoorActionPossible(oLocked, DOOR_ACTION_BASH)
|| GetIsPlaceableObjectActionPossible(oLocked, PLACEABLE_ACTION_BASH);
if (GetAbilityScore(OBJECT_SELF, ABILITY_STRENGTH) >= 12 && bBashPossible)
{
DoOpenWithWeapons(oLocked);
return;
}
// try again with spells (i.e. bard with no strength using items)
if (DoOpenWithAttackSpells(oLocked))
{
return;
}
if (bBashPossible)
{
DoOpenWithWeapons(oLocked);
return;
}
VoiceCannotDo();
}
int DoSearchLoop()
{
if (!GetIAmNotDoingAnything())
{
return FALSE;
}
int nDisarmTrapSkill = GetSkillRank(SKILL_DISABLE_TRAP);
int nCanDisarm = GetHasSkill(SKILL_DISABLE_TRAP);
int nAutoDisarm = nCanDisarm && !GetLocalInt(OBJECT_SELF, sHenchNoDisarmTraps);
int nCanRecoverTraps = GetLocalInt(OBJECT_SELF, sHenchAutoRecoverTraps);
int nOpenLockSkill = GetSkillRank(SKILL_OPEN_LOCK);
int nCanOpenLock = GetHasSkill(SKILL_OPEN_LOCK) && GetLocalInt(OBJECT_SELF, sHenchAutoOpenLocks);
int nMaxThievesToolBonus = GetMaxThievesToolBonus();
int nCanPickUp = GetLocalInt(OBJECT_SELF, sHenchAutoPickup);
int nCanOpenChests = GetLocalInt(OBJECT_SELF, sHenchAutoOpenChest);
object oMasterFailLock = GetLocalObject(OBJECT_SELF, sLockMasterFailed);
if (GetLocalInt(oMasterFailLock, "X2_L_BASH_FALSE"))
{
oMasterFailLock = OBJECT_INVALID;
}
int nMasterFailedLock = GetIsObjectValid(oMasterFailLock);
int bForceTrap = GetLocalInt(OBJECT_SELF, sForceTrap);
// first check if we have completed something
object oTrap = GetLocalObject(OBJECT_SELF, "NW_ASSOCIATES_LAST_TRAP");
if (GetIsObjectValid(oTrap))
{
DeleteLocalObject(OBJECT_SELF, "NW_ASSOCIATES_LAST_TRAP");
if (GetIsTrapped(oTrap))
{
int nTrapDC = GetTrapDisarmDC(oTrap);
int nCanDisarmThisTrap = (nDisarmTrapSkill + 20) >= nTrapDC;
if (nCanDisarmThisTrap)
{
DeleteLocalInt(oTrap, sDisarmTried);
}
else
{
VoiceCannotDo();
}
}
else
{
VoiceTaskComplete();
DeleteLocalInt(oTrap, sDisarmTried);
}
}
object oLock = GetLocalObject(OBJECT_SELF, "NW_ASSOCIATES_LAST_LOCK");
if (GetIsObjectValid(oLock))
{
DeleteLocalObject(OBJECT_SELF, "NW_ASSOCIATES_LAST_LOCK");
if (GetLocked(oLock))
{
int nLockDC = GetLockUnlockDC(oLock);
int nCanOpenThisLock = nOpenLockSkill + nMaxThievesToolBonus + 20 >= nLockDC;
if (nCanOpenThisLock)
{
DeleteLocalInt(oLock, sLockTried);
}
VoiceCuss();
}
else
{
VoiceTaskComplete();
DeleteLocalInt(oLock, sLockTried);
}
}
// add check for follow mode
if (!nMasterFailedLock && GetLocalInt(OBJECT_SELF, sHenchDontAttackFlag))
{
return FALSE;
}
object oRealMaster = GetRealMaster();
if (!GetIsObjectValid(oRealMaster))
{
return FALSE;
}
// if too far away, first priority is to get back to master
if (GetDistanceToObject(oRealMaster) > 15.5)
{
ClearForceOptions();
ClearAllActions();
ActionForceMoveToObject(oRealMaster, TRUE, 5.0, 10.0);
return FALSE;
}
int iActionToDo = 0;
object oProblemTrapOnGroundFound;
object oNearestItemTrap = OBJECT_INVALID;
float fDistanceToItemTrap = 1000.0;
object oNearestDoorOrPlaceable = oMasterFailLock;
float fDistanceToDoorOrPlaceable;
if (nMasterFailedLock)
{
fDistanceToDoorOrPlaceable = GetDistanceToObject(oMasterFailLock);
if (bForceTrap)
{
iActionToDo = ACTION_DISABLETRAP;
}
else
{
iActionToDo = ACTION_OPENLOCK;
}
}
else
{
fDistanceToDoorOrPlaceable = 1000.0;
}
location oMasterLoc = GetLocation(oRealMaster);
object oCurrent = GetFirstObjectInShape(SHAPE_SPHERE, 15.0, oMasterLoc, FALSE, OBJECT_TYPE_DOOR | OBJECT_TYPE_ITEM | OBJECT_TYPE_PLACEABLE | OBJECT_TYPE_TRIGGER);
//Cycle through the targets within the spell shape until an invalid object is captured.
while (!nMasterFailedLock && GetIsObjectValid(oCurrent))
{
int nObjectType = GetObjectType(oCurrent);
float fDistance = GetDistanceToObject(oCurrent);
// Jug_Debug(GetName(OBJECT_SELF) + " found " + GetName(oCurrent) + " type " + IntToString(nObjectType) + " distance " + FloatToString(fDistance));
if (GetTrapDetectedBy(oCurrent, oRealMaster) || GetTrapDetectedBy(oCurrent, OBJECT_SELF) || GetTrapFlagged(oCurrent))
{
SetTrapDetectedBy(oCurrent, oRealMaster);
SetTrapDetectedBy(oCurrent, OBJECT_SELF);
int nTrapDC = GetTrapDisarmDC(oCurrent);
int nTrapTried = GetLocalInt(oCurrent, sDisarmTried);
int nCanDisarmThisTrap = nAutoDisarm && (nDisarmTrapSkill + 20 >= nTrapDC);
// we can't disarm this : play message once to indicate failure
if (!nAutoDisarm)
{
if (nObjectType == OBJECT_TYPE_TRIGGER)
{
oProblemTrapOnGroundFound = oCurrent;
}
}
if (nAutoDisarm)
{
if (nObjectType == OBJECT_TYPE_TRIGGER)
{
// Jug_Debug(GetName(OBJECT_SELF) + " found " + GetName(oCurrent) + " type " + IntToString(nObjectType) + " distance " + FloatToString(fDistance) + " trap base type " + IntToString(GetTrapBaseType(oCurrent)));
if (fDistance < fDistanceToItemTrap)
{
oNearestItemTrap = oCurrent;
fDistanceToItemTrap = fDistance;
}
}
else if (nCanDisarmThisTrap || nTrapTried == 0)
{
if (fDistance < fDistanceToDoorOrPlaceable)
{
oNearestDoorOrPlaceable = oCurrent;
fDistanceToDoorOrPlaceable = fDistance;
iActionToDo = ACTION_DISABLETRAP;
}
}
}
}
else if (nCanOpenLock && nObjectType == OBJECT_TYPE_PLACEABLE && GetLocked(oCurrent) && !GetLockKeyRequired(oCurrent) && !GetLocalInt(oCurrent, "X2_L_BASH_FALSE"))
{
int nLockDC = GetLockUnlockDC(oCurrent);
int nLockTried = GetLocalInt(oCurrent, sLockTried);
int nCanOpenThisLock = nOpenLockSkill + nMaxThievesToolBonus + 20 >= nLockDC;
if (nCanOpenThisLock || nLockTried == 0)
{
if (fDistance < fDistanceToDoorOrPlaceable)
{
oNearestDoorOrPlaceable = oCurrent;
fDistanceToDoorOrPlaceable = fDistance;
iActionToDo = ACTION_OPENLOCK;
}
}
}
else if (nCanPickUp && nObjectType == OBJECT_TYPE_ITEM && !GetLocalInt(oCurrent, "X2_ITEM_UNACQUIRED"))
{
if (fDistance < fDistanceToDoorOrPlaceable)
{
oNearestDoorOrPlaceable = oCurrent;
fDistanceToDoorOrPlaceable = fDistance;
iActionToDo = ACTION_PICKUPITEM;
}
}
else if (nObjectType == OBJECT_TYPE_PLACEABLE && GetHasInventory(oCurrent))
{
// Jug_Debug(GetName(OBJECT_SELF) + " testing placeable" + GetName(oCurrent) + " tag " + GetTag(oCurrent) + " already searched " + IntToString(GetLocalInt(oCurrent, sMasterShownFlag)));
// treat body bags as items on the ground
if (((GetTag(oCurrent) == "Body Bag") || (GetTag(oCurrent) == "BodyBag") ||
(GetStringLength(GetTag(oCurrent)) == 0)) &&
!GetLocalInt(oCurrent, sMasterShownFlag))
{
if (nCanPickUp && !GetLocked(oCurrent))
{
// Jug_Debug(GetName(OBJECT_SELF) + " checking placeable" + GetName(oCurrent));
if (fDistance < fDistanceToDoorOrPlaceable)
{
// Jug_Debug(GetName(OBJECT_SELF) + " using placeable" + GetName(oCurrent));
oNearestDoorOrPlaceable = oCurrent;
fDistanceToDoorOrPlaceable = fDistance;
iActionToDo = ACTION_USEOBJECT;
}
}
}
else if (nCanOpenChests && !GetLocked(oCurrent))
{
if (GetLocalInt(oCurrent, "NW_DO_ONCE"))
{
// delete master shown flag if it is there
DeleteLocalInt(oCurrent, sMasterShownFlag);
// TK should this be done or just leave opened things alone?
// maybe just use speak string (i.e. "something has been left behind" ?
/*
if (GetIsValidObject(GetFirstItemInIventory())
{
if (fDistance < fDistanceToDoorOrPlaceable)
{
oNearestDoorOrPlaceable = oCurrent;
fDistanceToDoorOrPlaceable = fDistance;
iActionToDo = ACTION_USEOBJECT;
}
}
*/
}
else if (!GetLocalInt(oCurrent, sMasterShownFlag))
{
if (fDistance < fDistanceToDoorOrPlaceable)
{
oNearestDoorOrPlaceable = oCurrent;
fDistanceToDoorOrPlaceable = fDistance;
iActionToDo = ACTION_USEOBJECT;
}
}
}
}
//Select the next object
oCurrent = GetNextObjectInShape(SHAPE_SPHERE, 15.0, oMasterLoc, FALSE, OBJECT_TYPE_DOOR | OBJECT_TYPE_ITEM | OBJECT_TYPE_PLACEABLE | OBJECT_TYPE_TRIGGER);
}
float lastDistance = GetLocalFloat(OBJECT_SELF, sLastMoveDistance);
if (GetIsObjectValid(oNearestItemTrap))
{
if (!IsDoorInLineOfSight(oNearestItemTrap))
{
int nTrapDC = GetTrapDisarmDC(oNearestItemTrap);
int nCanDisarmThisTrap = (nDisarmTrapSkill + 20 >= nTrapDC);
int nTrapTried = GetLocalInt(oNearestItemTrap, sDisarmTried);
if (!nCanDisarmThisTrap && nTrapTried)
{
// don't do anything, can't disarm nearest trap
return FALSE;
}
if (fDistanceToItemTrap > 2.0 && fDistanceToItemTrap != lastDistance)
{
SetLocalFloat(OBJECT_SELF, sLastMoveDistance, fDistanceToItemTrap);
ClearAllActions();
ActionMoveToObject(oNearestItemTrap, FALSE);
return DOING_MOVINGTO_ACTION;
}
DeleteLocalFloat(OBJECT_SELF, sLastMoveDistance);
ClearAllActions();
ActionUseSkill(SKILL_DISABLE_TRAP, oNearestItemTrap, GetTrapRecoverable(oNearestItemTrap) &&
nCanRecoverTraps && (nDisarmTrapSkill + 10 >= nTrapDC) ? SUBSKILL_RECOVERTRAP : 0);
ActionDoCommand(SetLocalInt(oNearestItemTrap, sDisarmTried, TRUE));
ActionDoCommand(SetLocalObject(OBJECT_SELF, "NW_ASSOCIATES_LAST_TRAP", oNearestItemTrap));
return TRUE;
}
else if (!GetIsObjectValid(oNearestDoorOrPlaceable))
{
if (!GetLocalInt(oNearestItemTrap, sSomethingFishy))
{
SpeakString(sHenchSomethingFishy);
SetLocalInt(oNearestItemTrap, sSomethingFishy, TRUE);
}
}
}
if (GetIsObjectValid(oNearestDoorOrPlaceable))
{
// Jug_Debug(GetName(OBJECT_SELF) + " doing something oNearestDoorOrPlaceable " + GetName(oNearestDoorOrPlaceable) + " action " + IntToString(iActionToDo));
if (nMasterFailedLock || !IsDoorInLineOfSight(oNearestDoorOrPlaceable))
{
if (!nMasterFailedLock)
{
if (GetIsObjectValid(oProblemTrapOnGroundFound))
{
int nTrapTried = GetLocalInt(oProblemTrapOnGroundFound, sDisarmTried);
if (!nTrapTried)
{
SetLocalInt(oProblemTrapOnGroundFound, sDisarmTried, TRUE);
SpeakString(sHenchWaitTrapsCleared);
}
// exit - stay put - trap on ground that can't be removed
return FALSE;
}
}
if (iActionToDo == ACTION_DISABLETRAP)
{
int nTrapDC = GetTrapDisarmDC(oNearestDoorOrPlaceable);
int nTrapTried = GetLocalInt(oNearestDoorOrPlaceable, sDisarmTried);
int nCanDisarmThisTrap = nCanDisarm && (nDisarmTrapSkill + 20 >= nTrapDC);
// only attempt to move toward trap if disarm is to be tried
if (nCanDisarm && (nCanDisarmThisTrap || !nTrapTried))
{
if (fDistanceToDoorOrPlaceable > 2.0 && fDistanceToDoorOrPlaceable != lastDistance)
{
SetLocalFloat(OBJECT_SELF, sLastMoveDistance, fDistanceToDoorOrPlaceable);
ClearAllActions();
ActionMoveToObject(oNearestDoorOrPlaceable, FALSE);
return DOING_MOVINGTO_ACTION;
}
}
}
else
{
if (fDistanceToDoorOrPlaceable > 2.0 && fDistanceToDoorOrPlaceable != lastDistance)
{
SetLocalFloat(OBJECT_SELF, sLastMoveDistance, fDistanceToDoorOrPlaceable);
ClearAllActions();
ActionMoveToObject(oNearestDoorOrPlaceable, FALSE);
return DOING_MOVINGTO_ACTION;
}
}
DeleteLocalFloat(OBJECT_SELF, sLastMoveDistance);
// give a last chance to check out the placeable for a trap (do a full search)
if (GetIsTrapped(oNearestDoorOrPlaceable) && GetTrapDetectable(oNearestDoorOrPlaceable) &&
!GetTrapDetectedBy(oNearestDoorOrPlaceable, oRealMaster))
{
int nDC = GetTrapDetectDC(oNearestDoorOrPlaceable);
if (d20() + GetSkillRank(SKILL_SEARCH) >= nDC )
{
SetTrapDetectedBy(oNearestDoorOrPlaceable, oRealMaster);
if (!nCanDisarm)
{
// start over, if asked to do lock stop doing anything
if (nMasterFailedLock)
{
ClearForceOptions();
return FALSE;
}
return TRUE;
}
iActionToDo = ACTION_DISABLETRAP;
}
}
if (iActionToDo == ACTION_DISABLETRAP)
{
ClearForceOptions();
int nTrapDC = GetTrapDisarmDC(oNearestDoorOrPlaceable);
int nTrapTried = GetLocalInt(oNearestDoorOrPlaceable, sDisarmTried);
int nCanDisarmThisTrap = nCanDisarm && (nDisarmTrapSkill + 20 >= nTrapDC);
// only attempt to disable trap if disarm is to be tried
if (nCanDisarm && (nCanDisarmThisTrap || !nTrapTried))
{
ClearAllActions();
int nTrapDC = GetTrapDisarmDC(oNearestDoorOrPlaceable);
ActionUseSkill(SKILL_DISABLE_TRAP, oNearestDoorOrPlaceable, GetTrapRecoverable(oNearestDoorOrPlaceable) &&
nCanRecoverTraps && (nDisarmTrapSkill + 10 >= nTrapDC) ? SUBSKILL_RECOVERTRAP : 0);
ActionDoCommand(SetLocalInt(oNearestDoorOrPlaceable, sDisarmTried, TRUE));
ActionDoCommand(SetLocalObject(OBJECT_SELF, "NW_ASSOCIATES_LAST_TRAP", oNearestDoorOrPlaceable));
return TRUE;
}
// attempt various means to disarm trap
else if (nMasterFailedLock)
{
DoDisarmTrap(oNearestDoorOrPlaceable, TRUE);
return FALSE;
}
}
if (iActionToDo == ACTION_OPENLOCK)
{
ClearAllActions();
ClearForceOptions();
int nLockDC = GetLockUnlockDC(oNearestDoorOrPlaceable);
int nLockTried = GetLocalInt(oNearestDoorOrPlaceable, sLockTried);
int nCanOpenThisLock = nOpenLockSkill + nMaxThievesToolBonus + 20 >= nLockDC;
if (GetHasSkill(SKILL_OPEN_LOCK) && !GetLockKeyRequired(oNearestDoorOrPlaceable) &&
(nCanOpenThisLock || !nLockTried))
{
// SetLocalInt(oNearestDoorOrPlaceable, sLockTried, TRUE);
object oSelThievesTool;
int bonusNeeded = GetLockUnlockDC(oNearestDoorOrPlaceable) - GetSkillRank(SKILL_OPEN_LOCK) - 20;
object oThievesTool1 = GetItemPossessedBy(OBJECT_SELF, "NW_IT_PICKS001");
object oThievesTool3 = GetItemPossessedBy(OBJECT_SELF, "NW_IT_PICKS002");
object oThievesTool6 = GetItemPossessedBy(OBJECT_SELF, "NW_IT_PICKS003");
object oThievesTool8 = GetItemPossessedBy(OBJECT_SELF, "X2_IT_PICKS001");
object oThievesTool10 = GetItemPossessedBy(OBJECT_SELF, "NW_IT_PICKS004");
object oThievesTool12 = GetItemPossessedBy(OBJECT_SELF, "X2_IT_PICKS002");
if (bonusNeeded < 1)
{
oSelThievesTool = OBJECT_INVALID;
}
else if (bonusNeeded < 2 && GetIsObjectValid(oThievesTool1))
{
oSelThievesTool = oThievesTool1;
}
else if (bonusNeeded < 4 && GetIsObjectValid(oThievesTool3))
{
oSelThievesTool = oThievesTool3;
}
else if (bonusNeeded < 7 && GetIsObjectValid(oThievesTool6))
{
oSelThievesTool = oThievesTool6;
}
else if (bonusNeeded < 9 && GetIsObjectValid(oThievesTool8))
{
oSelThievesTool = oThievesTool8;
}
else if (bonusNeeded < 11 && GetIsObjectValid(oThievesTool10))
{
oSelThievesTool = oThievesTool10;
}
else
{
oSelThievesTool = oThievesTool12;
}
if (oMasterFailLock == oNearestDoorOrPlaceable)
{
VoiceCanDo();
}
else
{
VoicePicklock();
}
ActionUseSkill(SKILL_OPEN_LOCK, oNearestDoorOrPlaceable, 0, oSelThievesTool);
ActionDoCommand(SetLocalInt(oNearestDoorOrPlaceable, sLockTried, TRUE));
ActionDoCommand(SetLocalObject(OBJECT_SELF, "NW_ASSOCIATES_LAST_LOCK", oNearestDoorOrPlaceable));
return TRUE;
}
else if (nMasterFailedLock)
{
DoOpenLock(oNearestDoorOrPlaceable);
return FALSE;
}
}
if (iActionToDo == ACTION_PICKUPITEM)
{
ClearAllActions();
ActionPickUpItem(oNearestDoorOrPlaceable);
SetLocalInt(oNearestDoorOrPlaceable, sGiveToMaster, 1);
SetLocalInt(OBJECT_SELF, sGiveToMaster, 1);
return TRUE;
}
if (iActionToDo == ACTION_USEOBJECT)
{
ClearAllActions();
if ((GetTag(oNearestDoorOrPlaceable) == "Body Bag") || (GetTag(oNearestDoorOrPlaceable) == "BodyBag")
|| (GetStringLength(GetTag(oNearestDoorOrPlaceable)) == 0))
{
int nPlotItemFound = FALSE;
int nFoundAnItem = FALSE;
object oItem = GetFirstItemInInventory(oNearestDoorOrPlaceable);
while (GetIsObjectValid(oItem))
{
if (GetPlotFlag(oItem))
{
if (!nPlotItemFound)
{
SpeakString(sHenchSomethingImportant);
}
nPlotItemFound = TRUE;
}
else
{
ActionTakeItem(oItem, oNearestDoorOrPlaceable);
SetLocalInt(oItem, sGiveToMaster, 1);
nFoundAnItem = TRUE;
}
oItem = GetNextItemInInventory(oNearestDoorOrPlaceable);
}
ActionDoCommand(SetLocalInt(oNearestDoorOrPlaceable, sMasterShownFlag, 1));
if (nFoundAnItem)
{
SetLocalInt(OBJECT_SELF, sGiveToMaster, 1);
}
return TRUE;
}
// give indication of opening (doesn't actually open, just shows animation)
AssignCommand(oNearestDoorOrPlaceable, PlayAnimation(ANIMATION_PLACEABLE_OPEN));
if (GetPlotFlag(oNearestDoorOrPlaceable))
{
SpeakString(sHenchSomethingImportant);
SetLocalInt(oNearestDoorOrPlaceable, sMasterShownFlag, 1);
return TRUE;
}
else
{
SpeakString(sHenchFoundSomething);
AssignCommand(oRealMaster, ActionDoCommand(DoPlaceableObjectAction(oNearestDoorOrPlaceable, PLACEABLE_ACTION_USE)));
SetLocalInt(oNearestDoorOrPlaceable, sMasterShownFlag, 1);
return TRUE;
}
}
}
}
ClearForceOptions();
// give things to master if not doing anything
if (GetLocalInt(OBJECT_SELF, sGiveToMaster))
{
if (GetDistanceToObject(oRealMaster) > 2.0)
{
ClearAllActions();
// always run back with items
ActionForceMoveToObject(oRealMaster, TRUE, 1.0, 10.0);
return TRUE;
}
int nAnyItemsFound = FALSE;
int iMaxGPIdentify = HenchGetMaxGPToIdentify();
object oItem = GetFirstItemInInventory();
while (GetIsObjectValid(oItem))
{
if (GetLocalInt(oItem, sGiveToMaster))
{
if (!nAnyItemsFound)
{
ClearAllActions();
SpeakString(sHenchGiveThings);
nAnyItemsFound = TRUE;
}
HenchIdentifyItem(oItem, iMaxGPIdentify);
ActionGiveItem(oItem, oRealMaster);
DeleteLocalInt(oItem, sGiveToMaster);
}
oItem = GetNextItemInInventory();
}
if (GetGold())
{
if (!nAnyItemsFound)
{
ClearAllActions();
SpeakString(sHenchGiveGold);
nAnyItemsFound = TRUE;
}
GiveGoldToCreature(oRealMaster, GetGold());
TakeGoldFromCreature(GetGold(), OBJECT_SELF, TRUE);
}
if (nAnyItemsFound)
{
SetCommandable(FALSE, OBJECT_SELF);
DelayCommand(1., SetCommandable(TRUE, OBJECT_SELF));
return TRUE;
}
else
{
DeleteLocalInt(OBJECT_SELF, sGiveToMaster);
}
}
return FALSE;
}
void main()
{
int nCurAction = GetLocalInt(OBJECT_SELF, "tk_doing_action");
if (nCurAction)
{
ActionDoCommand(ActionWait(0.5));
ActionDoCommand(ExecuteScript("hench_o0_act", OBJECT_SELF));
SetLocalInt(OBJECT_SELF, "tk_doing_action", FALSE);
return;
}
HenchGetDefSettings();
int result = DoSearchLoop();
// only if we did something
if (!result)
{
SetLocalInt(OBJECT_SELF, "tk_doing_action", FALSE);
}
else
{
if (result != DOING_MOVINGTO_ACTION)
{
SetLocalInt(OBJECT_SELF, "tk_doing_action", TRUE);
}
else
{
SetLocalInt(OBJECT_SELF, "tk_doing_action", FALSE);
}
ActionDoCommand(ActionWait(0.5));
ActionDoCommand(ExecuteScript("hench_o0_act", OBJECT_SELF));
}
SetLocalInt(OBJECT_SELF, "tk_action_result", result);
}