Generic NPC script

What it does
This script will add a little life to your NPCs. They can now move about doing odd jobs, making small talk with other NPCs, sit down & rest, etc... It can add a lot of atmosphere if you add this script to a few NPCs in some busy place like an inn or shop. For example, my Innkeeper, moves about, fiddles with a keg, talks to a patron, switches on a candelabra, uses a crate, takes the odd swig from a bottle, sits down on his chair, looks about the inn, etc... You get the idea. The tasks are just set using a couple of simple function calls in the NPC's OnSpawn file. The tasks may be performed in a circular or random order, during the day or night only, or at a specified time.

The Script
General NPC script version 1.0 readme file Author : Eugene Scully (eugene_scully@hotmail.com) for www.ardan-nights.org Date : 5/7/2002 Purpose of this script (GeneralNPC) : This script will add a little life to your NPCs. They can now move about doing odd jobs, making small talk with other NPCs, sit down & rest, etc... It can add a lot of atmosphere if you add this script to a few NPCs in some busy place like an inn or shop. For example, my Innkeeper, moves about, fiddles with a keg, talks to a patron, switches on a candelabra, uses a crate, takes the odd swig from a bottle, sits down on his chair, looks about the inn, etc... You get the idea. The tasks are just set using a couple of simple function calls in the NPC's OnSpawn file. The tasks may be performed in a circular or random order, during the day or night only, or at a specified time.

The possible tasks you can set each NPC are: 1) Use a usable item (e.g. turn a lightsource on/off, look like they are opening a crate, fiddle with an oven/keg, etc...). The use animation can be for something on the ground or at normal height. 2) Start a conversation with another NPC. This is limited at the moment, but the basics are there (They can speak greeting & farewell strings, and speak random speech strings). They can talk in a whisper, normal voice or shouting. 3) Sit down on a specified chair. 4) Sit cross-legged on the ground. 5) Read, drink & worship animations. 6) Observe animation, look about.

How to use the script: 1) Add the include, heartbeat, on user defined, and onspawn files into your module (Use copy/paste into new files). Name the include file "TH_NPC_INCLUDE". 2) Set the NPC's OnSpawn, OnUserDefined, OnHearbeat scripts to these files. 3) Modify the OnSpawn file so that your NPC performs the actions you want. Basically the OnSpawn file should be the only file you need to change on each NPC. It specifies what tasks your NPC does during the day.

How to modify the OnSpawnFile: You should change the OnSpawn file for each NPC. The included OnSpawn file is for an innkeeper I use in my module. I will go through function call in the script to describe what it does... RemoveAllTasks : Initialises the task variables, ready for the NPC to accept tasks. This function should be called before any other GeneralNPC function calls are made. SetTaskOrder(int iOrder) : Call with NPC_WPTYPE_RANDOM to perform tasks in a random order, or with NPC_WPTYPE_CIRCULAR to cycle through each task in order. ClearPhrases : If your NPC is going to talk to other NPCs, this function should be called before SetGreetingPhrase, SetFarewellPhrase, or AddRandomPhrase. AddTask(struct sNpcTask inputWaypoint, int iStartHour = 25, int iStartMinute = 0) : This function adds a task specified by the structure sNpcTask to the NPC's routine. If a iStartHour/iStartMinute is specified, then this task is performed at that specific hour/minute each day. struct sNpcTask : This structure specifies the NPC's task. Each element in the structure should be specified depending on the task to be performed. The structure's elements are described below: int associatedCommand : set this element to one of the defines in the include file (NPC_COMMAND_SLEEP, NPC_COMMAND_SIT, etc...). This determines the task to be performed. object oWaypoint : this element determines where the task is to be performed, or what object the task is performed on. It may be set to a waypoint, an inanimate object, or another NPC. The NPC will move to this object, and then perform the action specified in the associatedCommand element. int dayNight : Set this element to either NPC_TIME_ALWAYS, NPC_TIME_DAY, NPC_TIME_NIGHT, or NPC_TIME_EXACTTIME. NPC_TIME_ALWAYS - means the task is to be performed at any time of the day/night. NPC_TIME_DAY - means only perform the task during the day. NPC_TIME_NIGHT - means only perform the task during the night. NPC_TIME_EXACTTIME - means this task is only to be performed at the time specified by the iStartHour/iStartMinute parameter in the AddTask(..) function call. float fDuration : The duration in seconds that the task is to be performed. A few actions (NPC_COMMAND_OBSERVE, NPC_COMMAND_DRINK, & NPC_COMMAND_READ only) unfortunately have a maximum duration of about 50 seconds. int bRunToPoint : Set to TRUE if the NPC is to run to the location to perform their task, or FALSE if they should only walk. All the possible actions at the moment are listed in that include file. Just have a fiddle with some of the parameters to get the script doing what you require for your NPC.

Comments about the other files: OnHeartbeat : This file has only been changed from the default very slightly. If you want to use your own OnHeartbeat file, just put the ActionDoTasks function call in at a sensible place. OnUserDefined : This files is not really required, only when the NPC is talking to another NPC. If your innkeeper's OnSpawn script tells him to walk up to	a chicken and start talking to it, then if you add the OnUserDefined script to the chicken's properties (as well as the SetGreetingPhrase(...) & SetFarewellPhrase	function calls to the chicken's OnSpawn script), then the chicken will talk back (saying its set greeting and farewell phrases).

Things to be careful of when using the script: - A few actions (NPC_COMMAND_OBSERVE, NPC_COMMAND_DRINK, & NPC_COMMAND_READ only) unfortunately have a maximum duration of about 50 seconds currently. - Only up to 10 tasks (chores) may be performed at an exact time (NPC_TIME_EXACTTIME) currently. - Be careful not to use the event numbers 201 & 202 for something else. - Note that there is a minutes per hour variable within modules, which is set to 2 minutes/hour by default. If the time for a task is set to hour = 6, minute = 30, then you must ensure that there are at least 31 minutes in an hour in your module.

Bugs/Issues to be looked into: - Not sure if the fDuration time is very accurate. - There is a sleep command, but the animation for the NPCs looks like they are falling down dead. Not sure if I can do much about this. Will look into it. - Chores (NPC_TIME_EXACTTIME tasks) are missed if the PC is talking to the NPC at the time the chore is to be completed.

I hope this script is useful. Please report any problems to eugene_scully@hotmail.com

Below is the include file. It should be named "TH_NPC_INCLUDE" in your module.

//:://///////////////////////////////////////////// //:: Default: Include file //:: TH_NPC_INCLUDE //::////////////////////////////////////////////// /*   //  This contains all the functions/defines/structs required // for my generic NPC scripts... //::////////////////////////////////////////////// //:: Created By: Eugene Scully (for www.ardan-nights.org) //:: Created On: July 1, 2002 //::////////////////////////////////////////////// // Uncomment this to compile as a normal script ... //int StartingConditional { //   return 0; //} // Local used variable name list // "bStartTimer"       - Duration timer has started // "iTimerVal"         - Timer start time seconds // "iTimerValMin"      - Timer start time minutes // "fTimerDuration"    - Timer duration // "iContinuedAction"  - Action is continued action struct sNpcTask { object oWaypoint; int    associatedCommand; int    dayNight;           //  NPC_TIME_... //int    percentChance;      //  Cumulative for each entry float  fDuration;          //  Max 3000 secs for now int    bRunToPoint;        //  Run to the location (Later) }; int NPC_STATE_BUSY = 1; int NPC_STATE_WAITCOMMAND = 2; int NPC_COMMAND_INVALID = 0; int NPC_COMMAND_SLEEP = 1;         //  Requires waypoint int NPC_COMMAND_TALKNORMAL = 2;    //  Requires NPC int NPC_COMMAND_SIT = 3;           //  Requires chair/stool int NPC_COMMAND_OBSERVE = 4;       //  Requires waypoint int NPC_COMMAND_USEOBJECTMED = 5;  //  Requires object/waypoint int NPC_COMMAND_WAIT = 6;          //  Requires waypoint int NPC_COMMAND_DRINK = 7;         //  Requires waypoint int NPC_COMMAND_READ = 8;          //  Requires waypoint //int NPC_COMMAND_STEAL = 9;         //  Requires waypoint int NPC_COMMAND_WORSHIP = 10;      //  Requires waypoint int NPC_COMMAND_USEOBJECTLOW = 11; //  Requires object/waypoint int NPC_COMMAND_TALKSALUTE = 12; int NPC_COMMAND_SITCROSS = 13;     //  Requires waypoint int NPC_COMMAND_TALKWHISPER = 14;  //  Requires NPC int NPC_COMMAND_TALKSHOUT = 15;    //  Requires NPC // Might implement these later ... //int NPC_COMMAND_ANNOUNCE = 16; //int  NPC_COMMAND_GUARDOBJECT = 17; int NPC_TIME_ALWAYS = 0; int NPC_TIME_DAY = 1; int NPC_TIME_NIGHT = 2; int NPC_TIME_EXACTTIME = 3; int NPC_WPTYPE_CIRCULAR = 0; int NPC_WPTYPE_RANDOM = 1; void RemoveAllTasks { //SpeakString("RemoveAllWaypoints", TALKVOLUME_TALK); // Init all variables SetLocalInt(OBJECT_SELF, "NumWaypoints", 0); SetLocalInt(OBJECT_SELF, "CurrentWaypoint", 0); SetLocalInt(OBJECT_SELF, "iContinuedAction", 0); SetLocalInt(OBJECT_SELF, "iNumChores", 0); SetLocalInt(OBJECT_SELF, "bIsBusy", FALSE); } void SetTaskOrder(int iOrder) { SetLocalInt(OBJECT_SELF, "WpType", iOrder); } void AddTask(struct sNpcTask inputWaypoint, int iStartHour = 25, int iStartMinute = 0) { //SpeakString("AddWaypoint", TALKVOLUME_TALK); int NumWaypoints = GetLocalInt(OBJECT_SELF, "NumWaypoints"); string sVarName; string sWaypointNum; sWaypointNum = IntToString(NumWaypoints); SpeakString(sWaypointNum, TALKVOLUME_TALK); sVarName = "NPC_WP_" + sWaypointNum; SetLocalObject(OBJECT_SELF, sVarName, inputWaypoint.oWaypoint); sVarName = "NPC_COMM_" + sWaypointNum; SetLocalInt(OBJECT_SELF, sVarName, inputWaypoint.associatedCommand); sVarName = "NPC_DAYN_" + sWaypointNum; SetLocalInt(OBJECT_SELF, sVarName, inputWaypoint.dayNight); //sVarName = "NPC_PERC_" + sWaypointNum; //SetLocalInt(OBJECT_SELF, sVarName, inputWaypoint.percentChance); sVarName = "NPC_DURA_" + sWaypointNum; SetLocalFloat(OBJECT_SELF, sVarName, inputWaypoint.fDuration); sVarName = "NPC_BRUN_" + sWaypointNum; SetLocalInt(OBJECT_SELF, sVarName, inputWaypoint.bRunToPoint); if (iStartHour < 24) { // This is a daily chore int NumChores = GetLocalInt(OBJECT_SELF, "iNumChores"); string sNumChores = IntToString(NumChores); sVarName = "NPC_CHORETIME_" + sNumChores; SetLocalInt(OBJECT_SELF, sVarName, iStartHour); sVarName = "NPC_CHOREMIN_" + sNumChores; SetLocalInt(OBJECT_SELF, sVarName, iStartMinute); sVarName = "NPC_CHORENUM_" + sNumChores; SetLocalInt(OBJECT_SELF, sVarName, NumWaypoints); NumChores++; SetLocalInt(OBJECT_SELF, "iNumChores", NumChores); }   NumWaypoints++; SetLocalInt(OBJECT_SELF, "NumWaypoints", NumWaypoints); } int GetTaskCommand { //SpeakString("GetNextWaypoint", TALKVOLUME_TALK); int NumWaypoints = GetLocalInt(OBJECT_SELF, "NumWaypoints"); int CurrentWaypoint = GetLocalInt(OBJECT_SELF, "CurrentWaypoint"); if (NumWaypoints == 0) { return (NPC_COMMAND_INVALID); }   string sVarName; sVarName = "NPC_COMM_" + IntToString(CurrentWaypoint); return (GetLocalInt(OBJECT_SELF, sVarName)); }

struct sNpcTask GetNextTask(int iNum = 255) { //SpeakString("GetNextWaypoint", TALKVOLUME_TALK); struct sNpcTask returnWaypoint; int NumWaypoints = GetLocalInt(OBJECT_SELF, "NumWaypoints"); int CurrentWaypoint; if (iNum == 255) { CurrentWaypoint = GetLocalInt(OBJECT_SELF, "CurrentWaypoint"); //SpeakString("Getting Normal Task: " + IntToString(CurrentWaypoint)); }   else { //SpeakString("Getting Chore: " + IntToString(iNum)); CurrentWaypoint = iNum; }   if (NumWaypoints == 0) { //SpeakString("No waypoints set " + IntToString(CurrentWaypoint)); returnWaypoint.associatedCommand = NPC_COMMAND_INVALID; return (returnWaypoint); }   string sVarName; string sWaypointNum; sWaypointNum = IntToString(CurrentWaypoint); sVarName = "NPC_WP_" + sWaypointNum; returnWaypoint.oWaypoint = GetLocalObject(OBJECT_SELF, sVarName); sVarName = "NPC_COMM_" + sWaypointNum; returnWaypoint.associatedCommand = GetLocalInt(OBJECT_SELF, sVarName); sVarName = "NPC_DAYN_" + sWaypointNum; returnWaypoint.dayNight = GetLocalInt(OBJECT_SELF, sVarName); //sVarName = "NPC_PERC_" + sWaypointNum; //returnWaypoint.percentChance = GetLocalInt(OBJECT_SELF, sVarName); sVarName = "NPC_DURA_" + sWaypointNum; returnWaypoint.fDuration = GetLocalFloat(OBJECT_SELF, sVarName); sVarName = "NPC_BRUN_" + sWaypointNum; returnWaypoint.bRunToPoint = GetLocalInt(OBJECT_SELF, sVarName); //SpeakString("Task: " + IntToString(returnWaypoint.associatedCommand) + " Dur: " + FloatToString(returnWaypoint.fDuration)); if (GetLocalInt(OBJECT_SELF, "WpType") == NPC_WPTYPE_RANDOM) { float fRand = d100/100.0 - 0.01; //  Random number 0.00 to 0.99 int iNumTasks = GetLocalInt(OBJECT_SELF, "NumWaypoints"); CurrentWaypoint = FloatToInt(IntToFloat(iNumTasks) * fRand); //SpeakString("Next: " + IntToString(CurrentWaypoint)); }   else if (iNum == 255) { CurrentWaypoint++; if (CurrentWaypoint == NumWaypoints) { CurrentWaypoint = 0; }   }    SetLocalInt(OBJECT_SELF, "CurrentWaypoint", CurrentWaypoint); //SpeakString("Exit get tasks:" + IntToString(CurrentWaypoint)); return (returnWaypoint); } void SetGreetingPhrase(string sGreeting) { SetLocalString(OBJECT_SELF, "sNpcGreeting", sGreeting); } void SetFarewellPhrase(string sFarewell) { SetLocalString(OBJECT_SELF, "sNpcFarewell", sFarewell); } void AddRandomPhrase(string sPhrase) { // Get this working later ... int iNumPhrase = GetLocalInt(OBJECT_SELF, "iNpcNumPhrases"); string sVarName = "NPC_RNDTALK_" + IntToString(iNumPhrase); SetLocalString(OBJECT_SELF, sVarName, sPhrase); iNumPhrase++; SetLocalInt(OBJECT_SELF, "iNpcNumPhrases", iNumPhrase); } void ClearPhrases { SetLocalInt(OBJECT_SELF, "iNpcNumPhrases", 0); } void ActionSpeakRandomPhrase(int iTalkVolume) { float fRand = d100/100.0 - 0.01; //  Random number 0.00 to 0.99 int iNumPhrase = GetLocalInt(OBJECT_SELF, "iNpcNumPhrases"); int iPhrase = FloatToInt(IntToFloat(iNumPhrase) * fRand); string sVarName = "NPC_RNDTALK_" + IntToString(iPhrase); ActionSpeakString(GetLocalString(OBJECT_SELF, sVarName), iTalkVolume); } void ActionDoTasks { //SpeakString("ActionDoTasks", TALKVOLUME_TALK); int iChoreNum = -1; int iIsTalking; if (GetTimeSecond < 7) { // New minute ticked over. string sVarName; string sChoreNum; int    i, iStartHour, iCurrHour, iCurrMin, iStartMin; // Check if chores need doing ... iCurrHour = GetTimeHour; iCurrMin = GetTimeMinute; int NumChores = GetLocalInt(OBJECT_SELF, "iNumChores"); if ((NumChores > 0) && (NumChores < 11)) { for (i = 0; i < NumChores; i++) { sChoreNum = IntToString(i); sVarName = "NPC_CHORETIME_" + sChoreNum; iStartHour = GetLocalInt(OBJECT_SELF, sVarName); sVarName = "NPC_CHOREMIN_" + sChoreNum; iStartMin = GetLocalInt(OBJECT_SELF, sVarName); //SpeakString("Checking Chore: " + IntToString(iStartHour) + ":" + IntToString(iStartMin)); //SpeakString("Current time: " + IntToString(iCurrHour) + ":" + IntToString(iCurrMin)); if ((iStartHour == iCurrHour) && (iStartMin == iCurrMin)) { sVarName = "NPC_CHORENUM_" + sChoreNum; iChoreNum = GetLocalInt(OBJECT_SELF, sVarName); break; }           }            if (iChoreNum != -1) { //SpeakString("Found Chore: " + IntToString(iCurrHour) + ":" + IntToString(iCurrMin)); ClearAllActions; SetLocalInt(OBJECT_SELF, "iContinuedAction", 0); }       }    }    iIsTalking = GetLocalInt(OBJECT_SELF, "iIsTalking"); if (iIsTalking != 0) { if (iChoreNum == -1) { switch(iIsTalking) { case 1:    //  Talking (first call) ... // I am having a problem telling how long the current task has been // Running for, so for now just restart the task after the // conversation is over. {               //SpeakString("Blah, blah..."); ClearAllActions; //ActionDoCommand(SetLocalInt(OBJECT_SELF, "bIsBusy", FALSE)); ActionWait(0.5); int iTalkVolume = GetLocalInt(OBJECT_SELF, "TalkVolume"); ActionSpeakString(GetLocalString(OBJECT_SELF, "sNpcGreeting"), iTalkVolume); object oNPC = GetNearestObjectByTag(GetLocalString(OBJECT_SELF, "TalkToNPC")); if (oNPC != OBJECT_INVALID) ActionDoCommand(SetFacing(GetFacingFromLocation(GetLocation(oNPC)) - 180.0)); ActionPlayAnimation(ANIMATION_LOOPING_TALK_NORMAL, 1.0, 5.0); // Restart current task upon finish talking. int iCurrWaypoint = GetLocalInt(OBJECT_SELF, "CurrentWaypoint"); iCurrWaypoint--; if (iCurrWaypoint < 0) iCurrWaypoint = (GetLocalInt(OBJECT_SELF, "NumWaypoints") - 1); SetLocalInt(OBJECT_SELF, "CurrentWaypoint", iCurrWaypoint); SetLocalInt(OBJECT_SELF, "iIsTalking", 2); }               return; case 2:    //  Still Talking (subsequent calls) ... ActionPlayAnimation(ANIMATION_LOOPING_TALK_NORMAL, 1.0, 5.0); //SpeakString("Rhubarb, Rhubarb..."); return; }       }        else { // Chore to be done ... stop talking please ... SpeakString("Please excuse me..."); SpeakString(GetLocalString(OBJECT_SELF, "sNpcFarewell")); SetLocalInt(OBJECT_SELF, "iIsTalking", 0); }   }    int aCurrAction = GetCurrentAction; if (aCurrAction == ACTION_SIT) { if (GetLocalInt(OBJECT_SELF, "bStartTimer") == FALSE) { SetLocalInt(OBJECT_SELF, "bStartTimer", TRUE); SetLocalInt(OBJECT_SELF, "iTimerVal", GetTimeSecond); SetLocalInt(OBJECT_SELF, "iTimerValMin", GetTimeMinute); return; }       int iTimeDiff = GetTimeSecond - GetLocalInt(OBJECT_SELF, "iTimerVal"); if (iTimeDiff < 0) iTimeDiff += 60; if (iTimeDiff < FloatToInt(GetLocalFloat(OBJECT_SELF, "fTimerDuration"))) return; // Make NPC get up! ClearAllActions; }   else if (GetLocalInt(OBJECT_SELF, "bIsBusy") == TRUE) { //SpeakString("Busy ...", TALKVOLUME_TALK); ActionDoCommand(SetLocalInt(OBJECT_SELF, "bIsBusy", FALSE)); return; }   else if (aCurrAction != ACTION_INVALID) { return; }   int iAction = GetLocalInt(OBJECT_SELF, "iContinuedAction"); struct sNpcTask npcWaypoint; if (iAction != 0) { // A continued action ... //SpeakString("Continue action", TALKVOLUME_TALK); if (GetLocalInt(OBJECT_SELF, "bStartTimer") == FALSE) { // Start the timer ... SetLocalInt(OBJECT_SELF, "bStartTimer", TRUE); SetLocalInt(OBJECT_SELF, "iTimerVal", GetTimeSecond); //SpeakString("Start Timer: " + IntToString(GetLocalInt(OBJECT_SELF, "iTimerValMin")) + ":" + IntToString(GetLocalInt(OBJECT_SELF, "iTimerVal")), TALKVOLUME_TALK); return; }       // Check if action done ... //SpeakString("Time: " + IntToString(GetTimeHour) + ":" + IntToString(GetTimeMinute) + ":" + IntToString(GetTimeSecond), TALKVOLUME_TALK); int iTimeDiff = GetTimeSecond - GetLocalInt(OBJECT_SELF, "iTimerVal"); if (iTimeDiff < 0) iTimeDiff += 60; //SpeakString("Time Left: " + IntToString(iTimeDiff) + "/" + FloatToString(GetLocalFloat(OBJECT_SELF, "fTimerDuration"), 5, 2), TALKVOLUME_TALK); if (iTimeDiff > FloatToInt(GetLocalFloat(OBJECT_SELF, "fTimerDuration"))) { // Finished action ... SetLocalInt(OBJECT_SELF, "iContinuedAction", 0); iAction = 0; }   }    if (iAction == 0) { // Not a continued action ... //SpeakString("Get next action", TALKVOLUME_TALK); if(!GetIsInCombat) { // Unequip all weapons if(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND) != OBJECT_INVALID) ActionUnequipItem(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND)); if(GetItemInSlot(INVENTORY_SLOT_LEFTHAND) != OBJECT_INVALID) ActionUnequipItem(GetItemInSlot(INVENTORY_SLOT_LEFTHAND)); }       int iCount = 0; int iNumTasks = GetLocalInt(OBJECT_SELF, "NumWaypoints"); if (iChoreNum == -1) { npcWaypoint = GetNextTask; // Check if task is valid for this time of day while (((GetIsNight == FALSE) && (npcWaypoint.dayNight == NPC_TIME_NIGHT)) ||                   ((GetIsNight == TRUE) && (npcWaypoint.dayNight == NPC_TIME_DAY))  ||                    (npcWaypoint.dayNight == NPC_TIME_EXACTTIME)) { // If not valid get another task npcWaypoint = GetNextTask; if (iCount++ > iNumTasks) { SpeakString("What should I do?"); break; //    This is bad - no tasks for this time of day }           }        }        else { //SpeakString("Get chore", TALKVOLUME_TALK); npcWaypoint = GetNextTask(iChoreNum); }       //  We should store a location in the spawn script to speed this up ... if (!GetIsObjectValid(npcWaypoint.oWaypoint)) { // Object has been destroyed/killed ... //SpeakString("Bad object !!", TALKVOLUME_TALK); return; }       float     fDistance;   //   Distance to object switch (iAction) { case 1:    //  Sleeping need to be closer ? case 13:   //  Sitting fDistance = 0.4; break; default: fDistance = 1.5; break; }       ActionMoveToObject(npcWaypoint.oWaypoint, npcWaypoint.bRunToPoint, fDistance); SetLocalInt(OBJECT_SELF, "bIsBusy", TRUE); iAction = npcWaypoint.associatedCommand; //string sCommandString; switch (iAction) { case 0: //sCommandString = "Invalid Command!"; ActionWait(npcWaypoint.fDuration); break; case 1:    //  (incomplete. This is a little dramatic. Looks like they are dying) //sCommandString = "Going to Sleep ..."; ActionDoCommand(SetFacing(GetFacing(npcWaypoint.oWaypoint))); ActionDoCommand(ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectSleep, OBJECT_SELF, npcWaypoint.fDuration)); ActionWait(npcWaypoint.fDuration - 1.0); break; case 14: case 15: case 2: {           int iTalkVolume; if (iAction == 2) iTalkVolume = TALKVOLUME_TALK; else if (iAction == 14) iTalkVolume = TALKVOLUME_WHISPER; else iTalkVolume = TALKVOLUME_SHOUT; //sCommandString = "Initiate Conversation ..."; //ActionPlayAnimation(ANIMATION_FIREFORGET_GREETING); SetLocalInt(npcWaypoint.oWaypoint, "TalkVolume", iTalkVolume); SetLocalString(npcWaypoint.oWaypoint, "TalkToNPC", GetTag(OBJECT_SELF)); ActionSpeakString(GetLocalString(OBJECT_SELF, "sNpcGreeting"), iTalkVolume); ActionDoCommand(SignalEvent(npcWaypoint.oWaypoint, EventUserDefined(201))); ActionPlayAnimation(ANIMATION_LOOPING_TALK_NORMAL, 1.0, npcWaypoint.fDuration/2 - 1.0); // Should speak a random string somewhere in here ... ActionSpeakRandomPhrase(iTalkVolume); ActionPlayAnimation(ANIMATION_LOOPING_TALK_NORMAL, 1.0, npcWaypoint.fDuration/2 - 1.0); ActionSpeakString(GetLocalString(OBJECT_SELF, "sNpcFarewell"), iTalkVolume); ActionWait(1.0); ActionDoCommand(SignalEvent(npcWaypoint.oWaypoint, EventUserDefined(202))); }           break; case 3: //sCommandString = "Find chair & sit ..."; ActionSit(npcWaypoint.oWaypoint); break; case 4: //sCommandString = "Observing ..."; ActionDoCommand(SetFacing(GetFacing(npcWaypoint.oWaypoint))); break; case 5: //sCommandString = "Working (med)..."; ActionInteractObject(npcWaypoint.oWaypoint); ActionPlayAnimation(ANIMATION_LOOPING_GET_MID, 1.0, npcWaypoint.fDuration - 1.0); ActionWait(1.0); // We should turn to face the object ... ActionDoCommand(SetFacing(GetFacingFromLocation(GetLocation(npcWaypoint.oWaypoint)))); break; case 6: //sCommandString = "Waiting ..."; ActionDoCommand(SetFacing(GetFacing(npcWaypoint.oWaypoint))); ActionPlayAnimation(ANIMATION_FIREFORGET_PAUSE_BORED); ActionWait(npcWaypoint.fDuration - 1.0); break; case 7: //sCommandString = "Drinking ..."; ActionDoCommand(SetFacing(GetFacing(npcWaypoint.oWaypoint))); break; case 8: //sCommandString = "Reading ..."; ActionDoCommand(SetFacing(GetFacing(npcWaypoint.oWaypoint))); break; case 9:    //  Stealing (incomplete) //sCommandString = "Stealing ..."; ActionDoCommand(SetFacing(GetFacing(npcWaypoint.oWaypoint))); ActionWait(npcWaypoint.fDuration - 1.0); break; case 10: //sCommandString = "Worshipping ..."; ActionDoCommand(SetFacing(GetFacingFromLocation(GetLocation(npcWaypoint.oWaypoint)))); ActionPlayAnimation(ANIMATION_LOOPING_WORSHIP, 1.0, npcWaypoint.fDuration - 1.0); ActionWait(1.0); break; case 11: //sCommandString = "Working (low) ..."; // We should turn to face the object ... ActionInteractObject(npcWaypoint.oWaypoint); ActionPlayAnimation(ANIMATION_LOOPING_GET_LOW, 1.0, npcWaypoint.fDuration - 1.0); ActionWait(1.0); ActionDoCommand(SetFacing(GetFacingFromLocation(GetLocation(npcWaypoint.oWaypoint)))); break; case 12: //sCommandString = "Talk salute ..."; ActionPlayAnimation(ANIMATION_FIREFORGET_SALUTE); ActionDoCommand(SignalEvent(npcWaypoint.oWaypoint, EventUserDefined(201))); ActionPlayAnimation(ANIMATION_LOOPING_TALK_NORMAL, 1.0, npcWaypoint.fDuration - 1.0); ActionDoCommand(SignalEvent(npcWaypoint.oWaypoint, EventUserDefined(202))); break; case 13: //sCommandString = "Sit cross-legged ..."; ActionDoCommand(SetFacing(GetFacing(npcWaypoint.oWaypoint))); ActionDoCommand(SetFacing(GetFacing(npcWaypoint.oWaypoint))); ActionPlayAnimation(ANIMATION_LOOPING_SIT_CROSS, 1.0, npcWaypoint.fDuration - 1.0); ActionWait(1.0); break; default: //sCommandString = "Command is not recognised!"; break; }       //ActionDoCommand(SetLocalInt(OBJECT_SELF, "bIsBusy", FALSE)); //ActionSpeakString(sCommandString, TALKVOLUME_TALK); SetLocalInt(OBJECT_SELF, "bStartTimer", FALSE); SetLocalFloat(OBJECT_SELF, "fTimerDuration", npcWaypoint.fDuration); }   //SpeakString("Action #" + IntToString(iAction), TALKVOLUME_TALK); switch (iAction) { case 4:    //Observing ... ActionPlayAnimation(ANIMATION_FIREFORGET_HEAD_TURN_LEFT); ActionPlayAnimation(ANIMATION_FIREFORGET_HEAD_TURN_RIGHT); //ActionWait(npcWaypoint.fDuration); SetLocalInt(OBJECT_SELF, "iContinuedAction", NPC_COMMAND_OBSERVE); break; case 7:    //Drinking ... ActionPlayAnimation(ANIMATION_FIREFORGET_DRINK); ActionWait(1.0); ActionPlayAnimation(ANIMATION_FIREFORGET_DRINK); ActionWait(1.0); SetLocalInt(OBJECT_SELF, "iContinuedAction", NPC_COMMAND_DRINK); break; case 8:    //Reading ... ActionPlayAnimation(ANIMATION_FIREFORGET_READ); ActionWait(1.5); ActionPlayAnimation(ANIMATION_FIREFORGET_PAUSE_SCRATCH_HEAD); ActionWait(1.0); SetLocalInt(OBJECT_SELF, "iContinuedAction", NPC_COMMAND_READ); break; default:   //Command is not recognised! SetLocalInt(OBJECT_SELF, "iContinuedAction", 0); break; } }

Below is the Onheartbeat file:

//:://///////////////////////////////////////////// //:: Default: On Heartbeat //:: TH_NPC_HEARTBEAT //::////////////////////////////////////////////// /*   This is the main NPC heartbeat script ... //::////////////////////////////////////////////// //:: Edited By: Eugene Scully (for www.ardan-nights.org) //:: Based on the generic bioware OnHeartbeat script //:: Created On: July 1, 2002 //::////////////////////////////////////////////// void main { if(GetSpawnInCondition(NW_FLAG_FAST_BUFF_ENEMY)) {       if(TalentAdvancedBuff(40.0)) {           SetSpawnInCondition(NW_FLAG_FAST_BUFF_ENEMY, FALSE); return; }   }    if(!GetHasEffect(EFFECT_TYPE_SLEEP)) {       if(!GetIsPostOrWalking) {           if(!GetIsObjectValid(GetAttemptedAttackTarget) && !GetIsObjectValid(GetAttemptedSpellTarget)) {               if(!GetIsObjectValid(GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN))) {                   if(!GetBehaviorState(NW_FLAG_BEHAVIOR_SPECIAL) && !IsInConversation(OBJECT_SELF)) {                       ActionDoTasks; if(GetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS) || GetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS_AVIAN)) {                           PlayMobileAmbientAnimations; }                       else if(GetIsEncounterCreature &&                        !GetIsObjectValid(GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN))) {                           PlayMobileAmbientAnimations; }                       else if(GetSpawnInCondition(NW_FLAG_IMMOBILE_AMBIENT_ANIMATIONS) &&                           !GetIsObjectValid(GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN))) {                           PlayImmobileAmbientAnimations; }                   }                    else {                       DetermineSpecialBehavior; }               }                else {                   //DetermineCombatRound; }           }        }    }    else {       if(GetSpawnInCondition(NW_FLAG_SLEEPING_AT_NIGHT)) {           effect eVis = EffectVisualEffect(VFX_IMP_SLEEP); if(d10 > 6) {               ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, OBJECT_SELF); }       }    }    if(GetSpawnInCondition(NW_FLAG_HEARTBEAT_EVENT)) {       SignalEvent(OBJECT_SELF, EventUserDefined(1001)); } }
 * 1) include "NW_O2_CONINCLUDE"
 * 2) include "NW_I0_GENERIC"
 * 3) include "TH_NPC_INCLUDE"

Below is the OnUserDefine file:

//:://///////////////////////////////////////////// //:: Default: On User Defined //:: TH_NPC_USERDEF //::////////////////////////////////////////////// /*   Determines the course of action to be taken on a user defined event. //::////////////////////////////////////////////// //:: Created By: Eugene Scully (for www.ardan-nights.org) //:: Created On: July 1, 2002 //::////////////////////////////////////////////// void main {   switch(GetUserDefinedEventNumber) { case 201:      //  Another NPC is talking to me ! // We should pause current action here for the moment & resume later ... // Get nearest NPC (hope for the moment that it is the correct one!) {       //object oNPC = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN); //SpeakString("Talking to : " + GetLocalString(OBJECT_SELF, "TalkToNPC")); object oNPC = GetNearestObjectByTag(GetLocalString(OBJECT_SELF, "TalkToNPC")); if (oNPC != OBJECT_INVALID) SetFacing(GetFacingFromLocation(GetLocation(oNPC)) - 180.0); SetLocalInt(OBJECT_SELF, "iIsTalking", 1); }       break; case 202:      //  Another NPC has finished talking to me ! {       int iTalkVolume = GetLocalInt(OBJECT_SELF, "TalkVolume"); SpeakString(GetLocalString(OBJECT_SELF, "sNpcFarewell"), iTalkVolume); SetLocalInt(OBJECT_SELF, "iIsTalking", 0); }       break; }   return; }

Below is an example OnSpawn script for my innkeeper (You should alter this script):

// This is an initial script to test my general NPC script void main {   // OPTIONAL BEHAVIORS (Comment In or Out to Activate ) **************************************************************************** //SetSpawnInCondition(NW_FLAG_SPECIAL_CONVERSATION); //SetSpawnInCondition(NW_FLAG_SPECIAL_COMBAT_CONVERSATION); // This causes the creature to say a special greeting in their conversation file // upon Perceiving the player. Attach the [NW_D2_GenCheck.nss] script to the desired // greeting in order to designate it. As the creature is actually saying this to               // himself, don't attach any player responses to the greeting. //SetSpawnInCondition(NW_FLAG_SHOUT_ATTACK_MY_TARGET); // This will set the listening pattern on the NPC to attack when allies call //SetSpawnInCondition(NW_FLAG_STEALTH); // If the NPC has stealth and they are a rogue go into stealth mode //SetSpawnInCondition(NW_FLAG_SEARCH); // If the NPC has Search go into Search Mode //SetSpawnInCondition(NW_FLAG_SET_WARNINGS); // This will set the NPC to give a warning to non-enemies before attacking //SetSpawnInCondition(NW_FLAG_SLEEP); //Creatures that spawn in during the night will be asleep. SetSpawnInCondition(NW_FLAG_DAY_NIGHT_POSTING); //SetSpawnInCondition(NW_FLAG_APPEAR_SPAWN_IN_ANIMATION); //SetSpawnInCondition(NW_FLAG_IMMOBILE_AMBIENT_ANIMATIONS); //SetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS); //This will play Ambient Animations until the NPC sees an enemy or is cleared. //NOTE that these animations will play automatically for Encounter Creatures. // NOTE: ONLY ONE OF THE FOLOOWING ESCAPE COMMANDS SHOULD EVER BE ACTIVATED AT ANY ONE TIME. //SetSpawnInCondition(NW_FLAG_ESCAPE_RETURN);   // OPTIONAL BEHAVIOR (Flee to a way point and return a short time later.) //SetSpawnInCondition(NW_FLAG_ESCAPE_LEAVE);    // OPTIONAL BEHAVIOR (Flee to a way point and do not return.) //SetSpawnInCondition(NW_FLAG_TELEPORT_LEAVE);  // OPTIONAL BEHAVIOR (Teleport to safety and do not return.) //SetSpawnInCondition(NW_FLAG_TELEPORT_RETURN); // OPTIONAL BEHAVIOR (Teleport to safety and return a short time later.) // CUSTOM USER DEFINED EVENTS /*   The following settings will allow the user to fire one of the blank user defined events in the NW_D2_DefaultD. Like the On Spawn In script this script is meant to be customized by the end user to allow for unique behaviors. The user defined events user 1000 - 1010 //SetSpawnInCondition(NW_FLAG_HEARTBEAT_EVENT);       //OPTIONAL BEHAVIOR - Fire User Defined Event 1001 //SetSpawnInCondition(NW_FLAG_PERCIEVE_EVENT);        //OPTIONAL BEHAVIOR - Fire User Defined Event 1002 //SetSpawnInCondition(NW_FLAG_ATTACK_EVENT);          //OPTIONAL BEHAVIOR - Fire User Defined Event 1005 //SetSpawnInCondition(NW_FLAG_DAMAGED_EVENT);         //OPTIONAL BEHAVIOR - Fire User Defined Event 1006 //SetSpawnInCondition(NW_FLAG_DISTURBED_EVENT);       //OPTIONAL BEHAVIOR - Fire User Defined Event 1008 //SetSpawnInCondition(NW_FLAG_END_COMBAT_ROUND_EVENT); //OPTIONAL BEHAVIOR - Fire User Defined Event 1003 //SetSpawnInCondition(NW_FLAG_ON_DIALOGUE_EVENT);     //OPTIONAL BEHAVIOR - Fire User Defined Event 1004 //SetSpawnInCondition(NW_FLAG_DEATH_EVENT);           //OPTIONAL BEHAVIOR - Fire User Defined Event 1007 // DEFAULT GENERIC BEHAVIOR (DO NOT TOUCH) ***************************************************************************************** SetListeningPatterns;   // Goes through and sets up which shouts the NPC will listen to. GenerateNPCTreasure;    //* Use this to create a small amount of treasure on the creature // Reset all waypoints to start. RemoveAllTasks; // Random or in a set order. SetTaskOrder(NPC_WPTYPE_RANDOM);   //  This parameter does nothing atm // Only call these functions if I have conversations with other NPCs. ClearPhrases; SetGreetingPhrase("Why hello."); SetFarewellPhrase("Bye friend."); AddRandomPhrase("Do you need anything?");    //  Only 1 phrase works atm struct sNpcTask       yarriTask; object                oWayPoint; // Set waypoint/task 1 (Uses a keg in the kitchen) oWayPoint = GetNearestObjectByTag("th_KegKitchen1"); yarriTask.oWaypoint = oWayPoint; yarriTask.associatedCommand = NPC_COMMAND_USEOBJECTLOW; yarriTask.dayNight = NPC_TIME_ALWAYS; //yarriTask.percentChance = 100; //  This parameter does nothing atm yarriTask.fDuration = 10.0; yarriTask.bRunToPoint = FALSE; AddTask(yarriTask); // Set waypoint/task 2 (Has a drink at the bar) oWayPoint = GetNearestObjectByTag("TH_BARSERVEWP"); yarriTask.oWaypoint = oWayPoint; yarriTask.associatedCommand = NPC_COMMAND_DRINK; yarriTask.dayNight = NPC_TIME_ALWAYS; //yarriTask.percentChance = 20;  //  This parameter does nothing atm yarriTask.fDuration = 10.0; yarriTask.bRunToPoint = FALSE; AddTask(yarriTask); // Set waypoint/task 3 (Reads at a waypoint set in the kitchen) oWayPoint = GetNearestObjectByTag("TH_KITCHENWP"); yarriTask.oWaypoint = oWayPoint; yarriTask.associatedCommand = NPC_COMMAND_READ; yarriTask.dayNight = NPC_TIME_ALWAYS; //yarriTask.percentChance = 20;  //  This parameter does nothing atm yarriTask.fDuration = 10.0; yarriTask.bRunToPoint = FALSE; AddTask(yarriTask); // Set waypoint/task 4 (Starts a conversation with Ries) oWayPoint = GetNearestObjectByTag("Ries"); yarriTask.oWaypoint = oWayPoint; yarriTask.associatedCommand = NPC_COMMAND_TALKNORMAL; yarriTask.dayNight = NPC_TIME_ALWAYS; //yarriTask.percentChance = 100; //  This parameter does nothing atm yarriTask.fDuration = 30.0; yarriTask.bRunToPoint = FALSE; AddTask(yarriTask); // Set waypoint/task 5 (Looks in the large crate in the Kitchen) oWayPoint = GetNearestObjectByTag("th_BoxCrateKitchen"); yarriTask.oWaypoint = oWayPoint; yarriTask.associatedCommand = NPC_COMMAND_USEOBJECTMED; yarriTask.dayNight = NPC_TIME_ALWAYS; //yarriTask.percentChance = 80;  //  This parameter does nothing atm yarriTask.fDuration = 20.0; yarriTask.bRunToPoint = FALSE; AddTask(yarriTask); // Set waypoint/task 6 (Stands at the bar watching everyone) oWayPoint = GetNearestObjectByTag("TH_BARSERVEWP"); yarriTask.oWaypoint = oWayPoint; yarriTask.associatedCommand = NPC_COMMAND_OBSERVE; yarriTask.dayNight = NPC_TIME_ALWAYS; //yarriTask.percentChance = 20;  //  This parameter does nothing atm yarriTask.fDuration = 40.0; yarriTask.bRunToPoint = FALSE; AddTask(yarriTask); // Set waypoint/task 7 (Sits down on the chair behind the bar) oWayPoint = GetNearestObjectByTag("th_StoolBehindBar"); yarriTask.oWaypoint = oWayPoint; yarriTask.associatedCommand = NPC_COMMAND_SIT; yarriTask.dayNight = NPC_TIME_ALWAYS; //yarriTask.percentChance = 60;  //  This parameter does nothing atm yarriTask.fDuration = 100.0; yarriTask.bRunToPoint = FALSE; AddTask(yarriTask); // Set waypoint/task 8 (Attends the oven in the kitchen) oWayPoint = GetNearestObjectByTag("th_WoodBurningOvenKitchen"); yarriTask.oWaypoint = oWayPoint; yarriTask.associatedCommand = NPC_COMMAND_USEOBJECTLOW; yarriTask.dayNight = NPC_TIME_ALWAYS; //yarriTask.percentChance = 100; //  This parameter does nothing atm yarriTask.fDuration = 15.0; yarriTask.bRunToPoint = FALSE; AddTask(yarriTask); // Set waypoint/task 9 (Sits cross legged on a bedroll in the corner) oWayPoint = GetNearestObjectByTag("TH_BED1WP"); yarriTask.oWaypoint = oWayPoint; yarriTask.associatedCommand = NPC_COMMAND_SITCROSS; yarriTask.dayNight = NPC_TIME_ALWAYS; //yarriTask.percentChance = 100; //  This parameter does nothing atm yarriTask.fDuration = 60.0; yarriTask.bRunToPoint = FALSE; AddTask(yarriTask); // Set waypoint/task 10 (Turns the candelabra on or off at time 16:01) oWayPoint = GetNearestObjectByTag("th_CandelabraBar"); yarriTask.oWaypoint = oWayPoint; yarriTask.associatedCommand = NPC_COMMAND_USEOBJECTMED; yarriTask.dayNight = NPC_TIME_EXACTTIME; //yarriTask.percentChance = 60; yarriTask.fDuration = 10.0; yarriTask.bRunToPoint = FALSE; AddTask(yarriTask, 16, 1); }
 * 1) include "NW_O2_CONINCLUDE"
 * 2) include "NW_I0_GENERIC"
 * 3) include "TH_NPC_INCLUDE"