I'm sure this guy is annoying to some of you, but the pickpocket near the docks is awesome. Too bad I can't look under the skirt of this mod because I'm really curious with how the script was written.
Anyone know?
Friar
I'm sure this guy is annoying to some of you, but the pickpocket near the docks is awesome. Too bad I can't look under the skirt of this mod because I'm really curious with how the script was written.
Anyone know?
Tchos
Well, as I've found, the version of Mysteries of Westgate that comes with the GOG version is unencrypted. The encryption was part of that heinous DRM that delayed MoW's release for a year and a half, but GOG insists that all of its products be DRM-free and removed it, so I can see that the pickpocket script is the following heartbeat:
// heartbeat script for ambient thieves
// .. they steal money from the locals and walk their waypoints
#include "hf_in_ambient"
#include "nw_i0_generic"
void main()
{
// don't do anything if we have no AI
if (GetAILevel() == AI_LEVEL_VERY_LOW)
{
return;
}
// steal or walk our waypoints
// .. note that the waypoint function checks if it is "safe" to walk waypoints
if (AmbientIsReady())
{
AmbientActionSteal();
}
else
{
if (GetWalkCondition(NW_WALK_FLAG_CONSTANT))
{
WalkWayPoints(TRUE, "heartbeat");
}
}
// Send the user-defined event signal if specified
if(GetSpawnInCondition(NW_FLAG_HEARTBEAT_EVENT))
{
SignalEvent(OBJECT_SELF, EventUserDefined(EVENT_HEARTBEAT));
}
}
It requires the inclusion of hf_in_ambient:
#include "nw_i0_generic"
const int HF_SHOUT_OPEN = 101;
const int HF_SHOUT_LOOT = 102;
// ====================================================================
// ================== STATES: BUSY, WALK, USE =========================
// ====================================================================
int HasRoute(object oNPC=OBJECT_SELF);
int AmbientActionDestinationAction(object oNPC=OBJECT_SELF);
// mark yourself as busy for a while
void SetIsBusy(float fDur, object oNPC=OBJECT_SELF)
{
SetLocalInt(oNPC, "HF_AM_IS_BUSY", 1);
DelayCommand(fDur, SetLocalInt(oNPC, "HF_AM_IS_BUSY", 0));
}
// is this creature busy doing something?
int GetIsBusy(object oNPC=OBJECT_SELF)
{
return(GetLocalInt(oNPC, "HF_AM_IS_BUSY"));
}
// pause along a route
void SetIsPaused(object oNPC=OBJECT_SELF)
{
int nDur = GetLocalInt(oNPC, "HF_AM_ROUTE_PAUSE");
if (nDur > 0)
{
SetLocalInt(oNPC, "HF_AM_IS_PAUSED", 1);
DelayCommand(IntToFloat(nDur), SetLocalInt(oNPC, "HF_AM_IS_PAUSED", 0));
}
}
// are we paused along our route somewhere?
int GetIsPaused(object oNPC=OBJECT_SELF)
{
return(GetLocalInt(oNPC, "HF_AM_IS_PAUSED"));
}
// walk to a destination location
void SetIsWalking(location lLoc, int nRun, object oNPC=OBJECT_SELF)
{
SetLocalInt(oNPC, "HF_AM_IS_WALKING", 30);
SetLocalLocation(oNPC, "HF_AM_WALK_DEST", lLoc);
ActionMoveToLocation(lLoc, nRun);
}
// stop walking
void ClearIsWalking(object oNPC=OBJECT_SELF)
{
DeleteLocalInt(oNPC, "HF_AM_IS_WALKING");
DeleteLocalLocation(oNPC, "HF_AM_WALK_DEST");
}
// check if we are still walking to our destination
int GetIsWalking(object oNPC=OBJECT_SELF)
{
location lLoc = GetLocalLocation(oNPC, "HF_AM_WALK_DEST");
int nWalking = GetLocalInt(oNPC, "HF_AM_IS_WALKING");
if (nWalking > 0)
{
if (GetDistanceBetweenLocations(GetLocation(oNPC), lLoc) <= 5.0)
{
// we have arrived at our destination, so stop walking
ClearIsWalking(oNPC);
SetIsPaused(oNPC);
AmbientActionDestinationAction(oNPC);
nWalking = FALSE;
} else {
// still going, nudge us in case someone blocked us along the way
if (nWalking == 1) nWalking = 0;
SetLocalInt(oNPC, "HF_AM_IS_WALKING", --nWalking);
ClearAllActions();
ActionMoveToLocation(lLoc);
}
} else if (nWalking < 0) {
// timer expired, might be stuck somewhere, try teleport
JumpToLocation(lLoc);
ClearIsWalking(oNPC);
}
return(nWalking);
}
// mark this object as used so that we don't keep trying to use it
void SetIsUsed(object oThing, float fDur=-1.0, object oNPC=OBJECT_SELF)
{
if (fDur < 0.0)
{
fDur = 100.0 + Random(25);
}
string sVar = "HF_AM_USED_" + ObjectToString(oNPC);
SetLocalInt(oThing, sVar, 1);
DelayCommand(fDur, DeleteLocalInt(oThing, sVar));
SetLocalInt(oThing, "HF_AM_BUSY", 1);
DelayCommand(20.0, DeleteLocalInt(oThing, "HF_AM_BUSY"));
}
// is this object safe to use?
int GetIsUsed(object oThing, object oNPC=OBJECT_SELF)
{
string sVar = "HF_AM_USED_" + ObjectToString(oNPC);
if (GetLocalInt(oThing, sVar) || GetLocalInt(oThing, "HF_AM_BUSY"))
return(TRUE);
return(FALSE);
}
// go to sleep
void SetIsAsleep(object oPlace, object oNPC=OBJECT_SELF)
{
float fDur = 30.0;
if (GetIsNight())
{
fDur = 360.0;
}
ActionDoCommand(ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectSleep(), oNPC, fDur));
}
// is this creature asleep?
int GetIsAsleep(object oNPC=OBJECT_SELF)
{
if (GetHasEffect(EFFECT_TYPE_SLEEP))
{
effect eVis = EffectVisualEffect(VFX_IMP_SLEEP);
if(d10() > 6)
{
ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, OBJECT_SELF);
}
return(TRUE);
}
return(FALSE);
}
// ====================================================================
// =============================== ROUTES =============================
// ====================================================================
// construct a route tag from a name and a current number
string MakeRouteTag(string sName, int nCur)
{
if (nCur <= 9)
return("WP_" + sName + "_0" + IntToString(nCur));
else
return("WP_" + sName + "_" + IntToString(nCur));
}
// cache waypoints on object
void AmbientRouteInitialize()
{
string sRoute = GetLocalString(OBJECT_SELF, "HF_AM_ROUTE_NAME");
int i = 1;
if (sRoute != "")
{
while (1)
{
string sTag = MakeRouteTag(sRoute, i);
object oWP = GetWaypointByTag(sTag);
if (GetIsObjectValid(oWP))
{
SetLocalLocation(OBJECT_SELF, "HF_" + sTag, GetLocation(oWP));
string sAction = GetLocalString(oWP, "HF_AM_SCRIPT");
if (sAction != "")
SetLocalString(OBJECT_SELF, "HF_" + sTag + "_ACTION", sAction);
i++;
} else {
break;
}
}
}
SetLocalInt(OBJECT_SELF, "HF_WP_COUNT", i-1);
}
// do we have a route?
int AmbientHasRoute(object oNPC=OBJECT_SELF)
{
return(GetLocalString(oNPC, "HF_AM_ROUTE_NAME") != "");
}
// remove the NPC's route
void AmbientRemoveRoute(object oNPC=OBJECT_SELF)
{
DeleteLocalString(oNPC, "HF_AM_ROUTE_NAME");
DeleteLocalInt(oNPC, "HF_WP_COUNT");
DeleteLocalInt(oNPC, "HF_WP_CUR");
DeleteLocalInt(oNPC, "HF_WP_DIR");
}
// walk to the next waypoint in the chain
// nWrap = 0 -- follow waypoints like this: 1, 2, 3, 2, 1.
// nWrap = 1 -- follow waypoints like this: 1, 2, 3, 1, 2, 3.
// nWrap = -1 -- follow waypoints like this: 1, 2, 3.
void AmbientWalkRoute()
{
int nCount = GetLocalInt(OBJECT_SELF, "HF_WP_COUNT");
if (nCount == 0)
return;
int nWrap = GetLocalInt(OBJECT_SELF, "HF_AM_ROUTE_WRAP");
int nCur = GetLocalInt(OBJECT_SELF, "HF_WP_CUR");
int nDir = GetLocalInt(OBJECT_SELF, "HF_WP_DIR");
if (nDir >= 0)
{
// walking forwards, check if we've reached the end
nCur++;
if (nCur > nCount)
{
if (nWrap == 0)
{
// walked forwards to the end, so turn around and go backwards
nCur = nCount - 1;
nDir = -1;
} else if (nWrap == -1) {
// walked forwards to the end, and stop
AmbientRemoveRoute();
return;
} else {
// walked forwards to the end, wrap to beginning
nCur = 2;
nDir = 0;
}
}
} else {
// walking backwards, check if we've reached the end
nCur--;
if (nCur <= 0)
{
if (nWrap == 0)
{
// walked backwards to the start, so turn around and go forwards
nCur = 2;
nDir = 0;
} else if (nWrap == -1) {
// walked backwards to the end, and stop
AmbientRemoveRoute();
return;
} else {
// walked backwards to the start, wrap to end
nCur = nCount - 1;
nDir = -1;
}
}
}
SetLocalInt(OBJECT_SELF, "HF_WP_DIR", nDir);
SetLocalInt(OBJECT_SELF, "HF_WP_CUR", nCur);
int nRun = GetLocalInt(OBJECT_SELF, "HF_AM_ROUTE_RUN");
string sTag = "HF_" + MakeRouteTag(GetLocalString(OBJECT_SELF, "HF_AM_ROUTE_NAME"), nCur);
SetIsWalking(GetLocalLocation(OBJECT_SELF, sTag), nRun);
string sAction = GetLocalString(OBJECT_SELF, sTag + "_ACTION");
if (sAction != "")
SetLocalString(OBJECT_SELF, "HF_AM_DEST_ACTION", sAction);
}
// ====================================================================
// ============================= ACTIONS ==============================
// ====================================================================
// action performed when a destination is reached
int AmbientActionDestinationAction(object oNPC=OBJECT_SELF)
{
string sName = GetLocalString(oNPC, "HF_AM_DEST_ACTION");
if ( sName != "")
{
DeleteLocalString(oNPC, "HF_AM_DEST_ACTION");
ExecuteScript(sName, oNPC);
return(TRUE);
}
return(FALSE);
}
// interact with a placeable
void AmbientUsePlaceable(object oPlace)
{
string sType = GetLocalString(oPlace, "HF_AM_ACTION");
if (sType == "")
sType = GetLocalString(oPlace, "HF_ACTION_TYPE");
ActionDoCommand(SetFacingPoint(GetPosition(oPlace)));
if (sType == "READ")
{
if (GetAbilityScore(OBJECT_SELF, ABILITY_INTELLIGENCE) >= 9)
{
ActionPlayAnimation(ANIMATION_FIREFORGET_READ);
} else {
ActionPlayAnimation(ANIMATION_FIREFORGET_PAUSE_SCRATCH_HEAD);
}
}
else if (sType == "BASH")
{
if (GetIsPlaceableObjectActionPossible(oPlace, PLACEABLE_ACTION_BASH))
{
DoPlaceableObjectAction(oPlace, PLACEABLE_ACTION_BASH);
ActionPlayAnimation(ANIMATION_LOOPING_PAUSE_TIRED, 1.0, 2.0);
DoPlaceableObjectAction(oPlace, PLACEABLE_ACTION_BASH);
ActionPlayAnimation(ANIMATION_LOOPING_PAUSE_TIRED, 1.0, 2.0);
} else {
ActionAttack(oPlace);
DelayCommand(6.0, ClearAllActions());
DelayCommand(6.1, SurrenderToEnemies());
}
}
else if (sType == "PRAY")
{
if (d2() == 1)
{
ActionPlayAnimation(ANIMATION_LOOPING_MEDITATE, 1.0, 10.0);
} else {
ActionPlayAnimation(ANIMATION_LOOPING_WORSHIP, 1.0, 10.0);
}
}
else if (sType == "SLEEP")
{
SetIsAsleep(oPlace);
}
else if (sType == "TALK")
{
SetLocalObject(OBJECT_SELF, "HF_AM_DIALOG_TARGET", oPlace);
SpeakOneLinerConversation();
}
else if (sType == "DRINK")
{
ActionPlayAnimation(ANIMATION_FIREFORGET_DRINK);
ActionPlayAnimation(ANIMATION_LOOPING_PAUSE_DRUNK, 1.0, 3.0);
ActionPlayAnimation(ANIMATION_FIREFORGET_DRINK);
ActionPlayAnimation(ANIMATION_LOOPING_PAUSE_DRUNK, 1.0, 3.0);
}
else if (sType == "SIT")
{
if (!GetIsObjectValid (GetSittingCreature(oPlace)))
{
ActionSit(oPlace);
} else {
ActionPlayAnimation(ANIMATION_FIREFORGET_PAUSE_SCRATCH_HEAD);
}
}
else if (sType == "USE")
{
ActionInteractObject(oPlace);
if (GetHasInventory(oPlace))
{
if (GetIsOpen(oPlace))
{
AssignCommand(oPlace, ActionPlayAnimation(ANIMATION_PLACEABLE_CLOSE));
} else {
AssignCommand(oPlace, ActionPlayAnimation(ANIMATION_PLACEABLE_OPEN));
}
}
}
else if (sType == "LOOK")
{
ActionPlayAnimation(ANIMATION_LOOPING_LOOK_FAR, 1.0, 5.0);
}
else
{
if (GetHasInventory(oPlace))
{
if (GetIsOpen(oPlace))
{
AssignCommand(oPlace, ActionPlayAnimation(ANIMATION_PLACEABLE_CLOSE));
} else {
AssignCommand(oPlace, ActionPlayAnimation(ANIMATION_PLACEABLE_OPEN));
}
} else {
vector vPos = GetPosition(oPlace);
vector vMyPos = GetPosition(OBJECT_SELF);
if (vMyPos.z - vPos.z >= 0.0)
{
ActionPlayAnimation(ANIMATION_LOOPING_GET_MID, 1.0, 10.0);
} else {
ActionPlayAnimation(ANIMATION_LOOPING_GET_LOW, 1.0, 10.0);
}
}
}
string sName = GetLocalString(oPlace, "HF_AM_SCRIPT");
if (sName != "")
ExecuteScript(sName, OBJECT_SELF);
}
// wave hello to nearby friends
int AmbientActionWaveHello()
{
object oFriend = GetNearestCreature(CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, OBJECT_SELF, 1);
if (GetIsObjectValid(oFriend))
{
if (GetIsPlayableRacialType(oFriend) && !GetIsUsed(oFriend) &&
!GetIsReactionTypeFriendly(oFriend) && GetDistanceToObject(oFriend) < 10.0)
{
float fDur = 3.0 + Random(3);
SetIsUsed(oFriend);
SetIsBusy(fDur);
SetFacingPoint(GetPosition(oFriend));
ActionPlayAnimation(ANIMATION_FIREFORGET_GREETING);
if (d4() == 1)
PlayVoiceChat(VOICE_CHAT_HELLO);
SetLocalObject(OBJECT_SELF, "HF_AM_DIALOG_TARGET", oFriend);
SpeakOneLinerConversation("", oFriend);
return(TRUE);
}
}
return(FALSE);
}
// fiddle with nearby objects; random chance of not using the nearest placeable
// to avoid making them follow the same route among placeables all of the time
// workers don't use a random chance since that would make them look lazy
int AmbientActionUseObject()
{
int i = 1;
string sTag = GetLocalString(OBJECT_SELF, "HF_AM_USE_OBJECT");
object oPlace = GetNearestObjectByTag(sTag, OBJECT_SELF, i);
while (GetIsObjectValid(oPlace))
{
if (d2() == 1)
{
if (!GetIsUsed(oPlace) && GetDistanceToObject(oPlace) < 30.0)
{
float fDur = 7.0;
SetIsBusy(fDur);
SetIsUsed(oPlace);
ActionMoveToObject(oPlace, FALSE, 0.25);
ActionDoCommand(AmbientUsePlaceable(oPlace));
return(TRUE);
}
}
oPlace = GetNearestObjectByTag(sTag, OBJECT_SELF, ++i);
}
return(FALSE);
}
// wander around aimlessly for a while
int AmbientActionWander()
{
float fDur = 5.0 + Random(5);
SetIsBusy(fDur);
ActionRandomWalk();
return(TRUE);
}
// return to our original spawn point
int AmbientActionGoHome()
{
if (GetDistanceBetweenLocations(GetLocation(OBJECT_SELF), GetLocalLocation(OBJECT_SELF, "HF_AM_SPAWN_LOC")) > 5.0)
{
SetIsWalking(GetLocalLocation(OBJECT_SELF, "HF_AM_SPAWN_LOC"), FALSE);
return (TRUE);
}
return(FALSE);
}
// play random ambient animations
int AmbientActionAmbient()
{
float fDur = 5.0 + Random(5);
SetIsBusy(fDur);
if (GetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS))
PlayMobileAmbientAnimations();
else
PlayImmobileAmbientAnimations();
return(TRUE);
}
// walk to night post if one is defined
int AmbientActionBedTime()
{
string sPost = GetLocalString(OBJECT_SELF, "HF_AM_NIGHT_WP");
if (sPost != "")
{
if (GetIsNight())
{
object oWP = GetWaypointByTag(sPost);
if (GetIsObjectValid(oWP))
{
SetIsWalking(GetLocation(oWP), FALSE);
return(TRUE);
}
}
}
return(FALSE);
}
// close an open door
int AmbientActionCloseDoors()
{
int n = 1;
object oDoor = GetNearestObject(OBJECT_TYPE_DOOR);
location lLoc = GetLocation(OBJECT_SELF);
while (GetIsObjectValid(oDoor))
{
if (GetIsOpen(oDoor) && !GetIsUsed(oDoor) &&
GetDistanceToObject(oDoor) < 30.0)
{
float fDur = 5.0 + Random(5);
SetIsUsed(oDoor);
SetIsBusy(fDur);
ActionCloseDoor(oDoor);
ActionMoveToLocation(lLoc);
return TRUE;
}
oDoor = GetNearestObject(OBJECT_TYPE_DOOR, OBJECT_SELF, ++n);
}
return(FALSE);
}
// follow a creature
int AmbientActionFollow()
{
object oFollow = GetLocalObject(OBJECT_SELF, "HF_AM_FOLLOW");
if (!GetIsObjectValid(oFollow))
{
return(FALSE);
}
if (GetIsEnemy(oFollow))
{
DeleteLocalObject(OBJECT_SELF, "HF_AM_FOLLOW");
return(FALSE);
}
if (GetDistanceToObject(oFollow) > 25.0 || GetArea(OBJECT_SELF) != GetArea(oFollow))
{
DeleteLocalObject(OBJECT_SELF, "HF_AM_FOLLOW");
return(FALSE);
}
if (GetStealthMode(oFollow) == STEALTH_MODE_ACTIVATED)
{
ActionPlayAnimation(ANIMATION_FIREFORGET_PAUSE_SCRATCH_HEAD);
string sSex = "he";
if (GetGender(oFollow) == GENDER_FEMALE)
sSex = "she";
ActionSpeakString("Huh? Where did " + sSex + " go?"); //TLK
} else {
ActionForceFollowObject(oFollow, 3.0);
}
return(TRUE);
}
// perform a "source -> destination" carry task
int AmbientActionWork()
{
string sTag = GetLocalString(OBJECT_SELF, "HF_AM_TASK");
int nState = GetLocalInt(OBJECT_SELF, "HF_AM_TASK_STATE");
if (nState == 0)
{
sTag += "_src";
} else {
sTag += "_dst";
}
object oDest = GetNearestObjectByTag(sTag);
if (GetIsObjectValid(oDest))
{
if (GetDistanceToObject(oDest) <= 5.0)
{
ActionMoveToObject(oDest, FALSE, 0.25);
ActionDoCommand(AmbientUsePlaceable(oDest));
if (nState == 0)
{
SetLocalInt(OBJECT_SELF, "HF_AM_TASK_STATE", 1);
} else {
SetLocalInt(OBJECT_SELF, "HF_AM_TASK_STATE", 0);
}
} else {
int bRun = GetLocalInt(OBJECT_SELF, "HF_AM_ROUTE_RUN");
SetIsWalking(GetLocation(oDest), bRun, OBJECT_SELF);
}
return(TRUE);
}
return(FALSE);
}
// rogues might steal something from someone and flee
int AmbientActionSteal()
{
// are we currently stalking a victim?
int nState = GetLocalInt(OBJECT_SELF, "HF_AMBIENT_STATE");
if (nState <= 0)
{
// not stalking anyone, so search for a mark
object oMark = GetNearestCreature(CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, OBJECT_SELF, 1);
if (GetIsObjectValid(oMark))
{
SetLocalInt(OBJECT_SELF, "HF_AMBIENT_STATE", 1);
SetLocalObject(OBJECT_SELF, "HF_AM_FOLLOW", oMark);
return(TRUE);
}
return(FALSE);
}
// is our victim still valid?
object oMark = GetLocalObject(OBJECT_SELF, "HF_AM_FOLLOW");
if (!GetIsObjectValid(oMark) ||
(GetArea(oMark) != GetArea(OBJECT_SELF)) ||
(GetIsInCombat(oMark)) ||
(IsInConversation(oMark)))
{
DeleteLocalInt(OBJECT_SELF, "HF_AMBIENT_STATE");
DeleteLocalObject(OBJECT_SELF, "HF_AM_FOLLOW");
return(FALSE);
}
if (nState < 4)
{
// stalking someone, but keep at it for a while
SetLocalInt(OBJECT_SELF, "HF_AMBIENT_STATE", ++nState);
return(TRUE);
}
else if (nState == 4)
{
// warn the player that someone is trying to pickpocket
if (GetIsPC(oMark))
{
int nRoll = d20() + GetSkillRank(SKILL_SPOT, oMark) - GetSkillRank(SKILL_SLEIGHT_OF_HAND);
if (nRoll >= 10)
{
FloatingTextStringOnCreature("... Someone is Trying to Pickpocket you ...", oMark, FALSE); //TLK
}
}
SetLocalInt(OBJECT_SELF, "HF_AMBIENT_STATE", ++nState);
return(TRUE);
}
else
{
// try to steal something from our mark
ActionMoveToObject(oMark, TRUE);
ActionPlayAnimation(ANIMATION_FIREFORGET_STEAL);
int nRoll = d20() + GetSkillRank(SKILL_SLEIGHT_OF_HAND) - GetSkillRank(SKILL_SPOT, oMark);
if (nRoll >= 10)
{
int nGold = d20()+10;
if (nGold > GetGold(oMark))
nGold = GetGold(oMark);
if (nGold > 0)
{
SetLocalInt(oMark, "HF_AMBIENT_THIEF_STOLE", nGold);
ActionDoCommand(FloatingTextStringOnCreature("... Pickpocket: Lost " + IntToString(nGold) + " GP ...", oMark, FALSE)); //TLK
ActionSpeakString("Heh Heh Heh!", TALKVOLUME_WHISPER); //TLK
ActionDoCommand(TakeGoldFromCreature(nGold, oMark, FALSE));
}
}
else
{
ActionDoCommand(FloatingTextStringOnCreature("... Pickpocket: Failure ...", oMark, FALSE)); //TLK
}
ActionMoveAwayFromObject(oMark, TRUE, 30.0);
SetIsBusy(15.0);
DeleteLocalInt(OBJECT_SELF, "HF_AMBIENT_STATE");
DeleteLocalObject(OBJECT_SELF, "HF_AM_FOLLOW");
return(TRUE);
}
return(FALSE);
}
// rough up the townsfolk a bit
int AmbientActionIntimidate()
{
// find a nearby victim
object oMark = GetNearestCreature(CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, OBJECT_SELF, 1);
if (GetIsObjectValid(oMark))
{
if (!GetIsUsed(oMark))
{
SetIsUsed(oMark);
if (!GetIsPC(oMark) && !GetPlotFlag(oMark))
{
// push the townsfolk around
ActionMoveToObject(oMark, TRUE, 0.5);
ActionSpeakString(GetStringByStrRef(151151)); // "Ah ha ha HA HA HA!"
ActionDoCommand(AssignCommand(oMark, ClearAllActions()));
ActionDoCommand(AssignCommand(oMark, ActionSpeakString("Hey!"))); //TLK
ActionDoCommand(ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectKnockdown(), oMark, 6.0));
ActionMoveAwayFromObject(oMark, FALSE, 20.0);
}
return(TRUE);
}
}
return(FALSE);
}
// sing a song for a while (don't put too many bards in one area!)
int AmbientActionPerform()
{
int nState = GetLocalInt(OBJECT_SELF, "HF_AMBIENT_STATE");
if (nState <= 0)
{
// strike up a song!
SetLocalInt(OBJECT_SELF, "HF_AMBIENT_STATE", 1);
ActionSpeakString("Greetings, Ladies and Gentlemen!"); //TLK
ActionPlayAnimation(ANIMATION_FIREFORGET_BOW);
return(TRUE);
}
else if (nState <= 3)
{
// keep playing for a while
if (GetLocalString(OBJECT_SELF, "HF_AM_ITEM") == "FLUTE")
{
ActionDoCommand(PlaySound("as_cv_flute1"));
ActionWait(2.0);
ActionDoCommand(PlaySound("as_cv_flute2"));
} else {
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectVisualEffect(VFX_DUR_BARD_SONG), OBJECT_SELF, 6.0);
ActionWait(2.0);
if (d2() == 1)
ActionDoCommand(PlaySound("as_cv_lute1"));
else
ActionDoCommand(PlaySound("as_cv_lute1b"));
}
SetLocalInt(OBJECT_SELF, "HF_AMBIENT_STATE", ++nState);
return(TRUE);
}
else
{
// performance is over
ActionSpeakString("Thank-you!"); //TLK
ActionPlayAnimation(ANIMATION_FIREFORGET_BOW);
DeleteLocalInt(OBJECT_SELF, "HF_AMBIENT_STATE");
SetIsBusy(15.0);
return(TRUE);
}
return(FALSE);
}
// ====================================================================
// ============================= SHOUTS ===============================
// ====================================================================
// one of my chests was opened
int AmbientActionShoutOpened()
{
object oShouter = GetLastSpeaker();
if (GetFactionEqual(oShouter))
{
object oThief = GetLocalObject(oShouter, "HF_THIEF");
ClearAllActions();
ActionMoveToObject(oShouter, TRUE, 2.0);
if (GetObjectSeen(oThief))
SpeakString("Get away from there!"); //TLK
else
SpeakString("Huh? What was that noise?"); //TLK
return(TRUE);
}
return(FALSE);
}
// one of my chests was looted
int AmbientActionShoutLooted()
{
object oShouter = GetLastSpeaker();
if (GetFactionEqual(oShouter) || GetIsFriend(oShouter))
{
object oThief = GetLocalObject(oShouter, "HF_THIEF");
if (GetObjectSeen(oThief) || GetObjectHeard(oThief))
{
SpeakString(GetStringByStrRef(156189)); // "Thief"
SpeakString("HF_SHOUT_LOOT", TALKVOLUME_SILENT_SHOUT);
SetIsTemporaryEnemy(oThief, OBJECT_SELF, TRUE, 60.0);
DetermineCombatRound(oThief);
ClearAllActions(TRUE);
return(TRUE);
}
}
return(FALSE);
}
// ====================================================================
// =============================== SPAWN ==============================
// ====================================================================
string AmbientGetRandomClothing()
{
string sResRef;
switch(Random(30) + 1)
{
case 1: sResRef = "nw_cloth002"; break;
case 2: sResRef = "nw_cloth003"; break;
case 3: sResRef = "nw_cloth005"; break;
case 4: sResRef = "nw_cloth006"; break;
case 5: sResRef = "nw_cloth007"; break;
case 6: sResRef = "nw_cloth008"; break;
case 7: sResRef = "nw_cloth009"; break;
case 8: sResRef = "nw_cloth010"; break;
case 9: sResRef = "nw_cloth011"; break;
case 10: sResRef = "nw_cloth013"; break;
case 11: sResRef = "nw_cloth017"; break;
case 12: sResRef = "nw_cloth018"; break;
case 13: sResRef = "nw_cloth019"; break;
case 14: sResRef = "nw_cloth021"; break;
case 15: sResRef = "nw_cloth022"; break;
case 16: sResRef = "nw_cloth023"; break;
case 17: sResRef = "nw_cloth024"; break;
case 18: sResRef = "nw_cloth025"; break;
case 19: sResRef = "nw_cloth027"; break;
case 20: sResRef = "shirt_wizard"; break;
case 21: sResRef = "shirt_sorcerer"; break;
case 22: sResRef = "shirt_rogue"; break;
case 23: sResRef = "shirt_ranger"; break;
case 24: sResRef = "shirt_paladin"; break;
case 25: sResRef = "shirt_monk"; break;
case 26: sResRef = "shirt_fighter"; break;
case 27: sResRef = "shirt_druid"; break;
case 28: sResRef = "shirt_wizard"; break;
case 29: sResRef = "shirt_cleric"; break;
case 30: sResRef = "shirt_barb"; break;
}
return sResRef;
}
// ====================================================================
// ============================== CONTROL =============================
// ====================================================================
// returns true if the NPC should execute an ambient action now
int AmbientIsReady()
{
// don't use so much CPU if our AI level is low
if (GetLocalInt(OBJECT_SELF, "HF_AM_AI_LEVEL") < 0)
{
if (d2() == 1) return(FALSE);
}
// is ambient system disabled?
if (GetLocalInt(OBJECT_SELF, "HF_AM_DISABLE") ||
GetLocalInt(GetArea(OBJECT_SELF), "HF_AM_DISABLE"))
{
return(FALSE);
}
if (!GetIsObjectValid(GetAttemptedAttackTarget()) &&
!GetIsObjectValid(GetAttemptedSpellTarget()) &&
!GetIsObjectValid(GetNearestSeenEnemy()) &&
!IsInConversation(OBJECT_SELF) &&
!GetIsInCombat())
{
// if we are busy doing something, don't interrupt us
if (!GetIsAsleep() && !GetIsBusy() && !GetIsWalking())
{
return(TRUE);
}
}
return(FALSE);
}
// call this function in the onDialog event
void AmbientOnDialogue()
{
if (GetHasEffect(EFFECT_TYPE_SLEEP) || GetIsDead(OBJECT_SELF))
{
return;
}
int nMatch = GetListenPatternNumber();
if (nMatch == HF_SHOUT_OPEN)
{
AmbientActionShoutOpened();
}
else if (nMatch == HF_SHOUT_LOOT)
{
AmbientActionShoutLooted();
}
}
// call this once, typically in the spawn script
void AmbientInit()
{
if (GetLocalInt(OBJECT_SELF, "HF_AM_INITIALIZED") == 0)
{
SetLocalInt(OBJECT_SELF, "HF_AM_INITIALIZED", 1);
SetLocalLocation(OBJECT_SELF, "HF_AM_SPAWN_LOC", GetLocation(OBJECT_SELF));
SetSpawnInCondition(NW_FLAG_HEARTBEAT_EVENT);
if (GetLocalString(OBJECT_SELF, "HF_AM_USE_OBJECT") == "")
{
// by default look for any interactive object
SetLocalString(OBJECT_SELF, "HF_AM_USE_OBJECT", "hf_ambient_object");
}
if (AmbientHasRoute())
{
// cache the waypoints for less lag when the game runs
AmbientRouteInitialize();
}
}
}
// sets the NPC's ambient animation flag on the fly
// X2_L_SPAWN_USE_AMBIENT
void SetAmbientAnimations(object oNPC)
{
AssignCommand(oNPC, SetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS));
}
// sets the NPC's immobile ambient animation flag on the fly
// X2_L_SPAWN_USE_AMBIENT_IMMOBILE
void SetImmobileAmbientAnimations(object oNPC)
{
AssignCommand(oNPC, SetSpawnInCondition(NW_FLAG_IMMOBILE_AMBIENT_ANIMATIONS));
}
// ====================================================================
// ========================== DOCUMENTATION ===========================
// ====================================================================
/*
You can specify NPC behaviours using this system. After installing the
correct scripts (typically spawn/heartbeat), set a few variables on the
NPC to define the behaviour. Create objects in the area with appropriate
tags for the NPC to interact with. Set a variable on the object to define
how the NPCs should interact with the object.
NPCS:
int HF_AM_AI_LEVEL
Set this to -1 if you want to ease the CPU load a bit
str HF_AM_ROUTE_NAME
Routes are waypoints with tags WP_ROUTE_01, WP_ROUTE_02, etc.
where ROUTE = the value of this HF_ROUTE variable. If you do not
specify a route, the NPC will wander freely in the area but will
often return to the original spawn location.
int HF_AM_ROUTE_WRAP
When an NPC reaches the end of the waypoints, normally he/she will
walk the waypoints in reverse order. Set this variable to -1 to
stop walking once the route is done. Set this variable to 1 to
return to the first waypoint (like a circle).
int HF_AM_ROUTE_RUN
Set this variable to 1 if you want the NPC to run instead of walk
the route.
int HF_AM_ROUTE_PAUSE
NPC will pause for this many seconds at each waypoint along the route
there is a default value if you don't set this variable. When the
NPC doesn't have a route, this variable doesn't do anything.
str HF_AM_USE_OBJECT
NPCs interact with any nearby objects tagged "hf_ambient_object"
Set this variable to a different tag and they will only use objects
with this tag. If you don't want an NPC to use any objects, set this
variable to a tag that is not used in your module.
str HF_AM_DEST_ACTION
Name of a script to execute when a walk destination is reached.
str HF_AM_TASK
NPC will move between work sources and work destinations. The source
object has this tag plus suffix _src; the destination object has
this tag plus suffix _dst.
str HF_AM_NIGHT_WP
NPC will walk to a waypoint with this tag each night (or if it is
raining. If you do not define this variable, the NPC will do the same
thing at night as during the day.
obj HF_AM_FOLLOW
The NPC will follow this creature until the creature uses stealth to
escape, gets too far away, or leaves the area.
int HF_AM_DISABLE
The entire ambient system will be disabled if you set this variable.
str HF_AM_ITEM
Some NPCs possess "items" that affect how they do their jobs.
FLUTE plays the flute instead of the lute
OBJECTS:
hf_ambient_object - NPCs will manipulate this object when near it
str HF_AM_ACTION - defines how NPCs interact with the object
TOUCH (default) NPC will perform the GET_MID/GET_LOW animation
USE NPC will try to use the object; give it a OnUse script
READ NPC will read a book if smart enough to do so
BASH NPC will attack the object for a few seconds
PRAY NPC will worship/meditate for a few seconds
SLEEP NPC will fall asleep (nap in the day, longer at night)
TALK NPC will speak a one liner conversation
DRINK NPC will take a few sips from a drink (use at bar/keg/fountain/etc)
SIT NPC will sit down on the object (if no one else is sitting on it)
LOOK NPC will look at the object for a few seconds
str HF_AM_SCRIPT - name of the script to execute when an NPC interacts with this object
WAYPOINTS:
WP_ROUTE_01 - waypoints used in routes
str HF_AM_SCRIPT - name of the script to execute when the NPC reaches this point
DIALOGS:
obj HF_AM_DIALOG_TARGET
This temporary variable is set on the NPC to indicate who/what they
are talking to (for speaking one liners).
CONTAINERS:
set faction to same as NPC and use scripts hf_am_con_open or hf_am_con_dist
to trigger hostile behaviours when the container is looted.
*/
Friar
Ah, when I saw the word "unencrypted" I mistakeningly assumed it had something to do with being able to play the module itself. I see now that it refers to accessing the module in the toolset.
Thank you for sharing that script Tchos. It rules, and it's also surprisingly shorter than I envisioned.









