// Script Name : kjs_worker_npc // Author : Kurt Jaegers (NWN Account : Krebbs) // Version : 1.20 // Date : July 1, 2002 // Description : A working routine script for shopkeepers or any other // type of NPC that can perform a fairly random set of // tasks. // // Usage : Put this script on an NPC in the OnHeartbeat code. // For each workstation the NPC will work at, you need // *BOTH* a Placeable Object and a Waypoint. // The waypoints should face the placable objects (ie, they // should be where the NPC is going to be standing while // interacting with the placeable object. // Both placables and waypoints should be named by these // standards: // // Placeable objects (workstations) : ws_NPCTAG_# // Waypoints : wsp_NPCTAG_# // // Where NPCTAG is the tag of the NPC that will follow // this routine, and # is a number starting with 1 and // incrementing upwards with no gaps (ie, 1,2,3,4,5 if you // have 5 workstations). // // Other Info : If you wish to deactivate the routine for any period of // time, simply set a local integer on the NPC called // KJ_WKS_DEACTIVATE to 1. That will disable the // process. To reactivate the process, set the local // variable back to 0. The script now detects if the NPC // is in conversation and doesn't do anything if so. // // If you wish to alter how often the NPC switches workstations // or how often the NPC performs interactions/animations, // set integers on the NPC named KJ_WKS_CHANGE_CHANCE and // KJ_WKS_ANIM_CHANCE. Both are percentage values. They // Can be set both in OnSpawn or at any time as needed. // // To make custom animations, set the following variables // in your OnSpawn script for the NPC: // Local Int : KJ_WKS_CUSTOM_ANIM set to 1 // Local Int : KJ_WKS_ANIM_COUNT set to # of animations // Local Ints: KJ_WKS_ANIM_# set to animation numbers. // Note that you can use constants to set these. // For example, : // SetLocalInt(OBJECT_SELF, "KJ_WKS_ANIM_1", ANIMATION_FIREFORGET_TAUNT); // // In addition, you can have your NPC speak lines of floating // text. These have the same chance to play as animations. // To use them, set KJ_WKS_STRING_COUNT to the number of // strings the NPC can speak, and create strings on the object // as local string variables called KJ_WKS_STRING_# // For example: // SetLocalInt(OBJECT_SELF, "KJ_WKS_STRING_COUNT", 3); // SetLocalString(OBJECT_SELF, "KJ_WKS_STRING_1", "Need a drink?"); // SetLocalString(OBJECT_SELF, "KJ_WKS_STRING_2", "Settle down there!"); // SetLocalString(OBJECT_SELF, "KJ_WKS_STRING_3", "I need a vacation!"); // // To make an NPC silent at a certain workstation (ie, they // will still play animations, but won't say any of their // defined strings) set the variable KJ_WKS_SILENT_# to 1 // where # is the workstation number. // // If a local variable on the NPC called KJ_WKS_MIN_WAIT is set // to a non-zero value, the NPC will wait at least that many // heartbeats at each workstation. This is useful if your // workstations are spread out a bit and it may take more // than one heartbeat to reach them. Otherwise the NPC may // bounce around non-stop. // // If you wish to make sure that an NPC doesn't choose the // workstation they are currently at to "switch" to, set // the local int KJ_WKS_NO_REPEAT to 1. // // Versions : 1.20 - Added Minimum Wait Time, No Repeat, and the ability to speak strings. // 1.10 - Updated to automatically not activate during conversation // 1.00 - Initial Script void main() { // Check to see if setup has already been done for this NPC. if (!(GetLocalInt(OBJECT_SELF, "KJ_WKS_SETUP")==1)) { // Determine the number of workstations int iWksCounter=0; // Counts the number of workstations object oWorkstation; // Temporary object to hold workstation info string sBuildName; // Temporary string to build workstatio names in // Assemble the first workstation name from it's parts sBuildName = "ws_" + GetTag(OBJECT_SELF) + "_" + IntToString(iWksCounter+1); // Get the first workstation oWorkstation = GetObjectByTag(sBuildName); while (GetIsObjectValid(oWorkstation)) { // Loop until we find a non-existant workstation iWksCounter++; sBuildName = "ws_" + GetTag(OBJECT_SELF) + "_" + IntToString(iWksCounter+1); oWorkstation = GetObjectByTag(sBuildName); } if (iWksCounter==0) { // No workstations were found, so shut off the routine SetLocalInt(OBJECT_SELF, "KJ_WKS_DEACTIVATE", 1); } else { // Put the number of Workstations into a local variable on the NPC SetLocalInt(OBJECT_SELF, "KJ_WKS_COUNT", iWksCounter); // Check to see if a custom workstation change chance is set. // if not, set the default. if (GetLocalInt(OBJECT_SELF, "KJ_WKS_CHANGE_CHANCE")==0) { SetLocalInt(OBJECT_SELF, "KJ_WKS_CHANGE_CHANCE", 30); } // Check to see if a custom animation chance is set. // if not, set the default. if (GetLocalInt(OBJECT_SELF, "KJ_WKS_ANIM_CHANCE")==0) { SetLocalInt(OBJECT_SELF, "KJ_WKS_ANIM_CHANCE", 55); } // Check to see if custom animations are set up. if (!(GetLocalInt(OBJECT_SELF, "KJ_WKS_CUSTOM_ANIM")==1)) { // if not, set up the defaults. They are: // 3 Animations : Steal, Get Middle, and Pause Tired SetLocalInt(OBJECT_SELF, "KJ_WKS_ANIM_COUNT",3); SetLocalInt(OBJECT_SELF, "KJ_WKS_ANIM_1", ANIMATION_FIREFORGET_STEAL); SetLocalInt(OBJECT_SELF, "KJ_WKS_ANIM_2", ANIMATION_LOOPING_GET_MID); SetLocalInt(OBJECT_SELF, "KJ_WKS_ANIM_3", ANIMATION_LOOPING_PAUSE_TIRED); } } // Indicate that setup has been completed. SetLocalInt(OBJECT_SELF, "KJ_WKS_SETUP", 1); } // If we are deactivated, don't do anything at all. if ((!(GetLocalInt(OBJECT_SELF, "KJ_WKS_DEACTIVATE")==1)) && !(IsInConversation(OBJECT_SELF))) { // Get the variables that represnt the current wait time and the // Minimum wait time. int iWaitCount = GetLocalInt(OBJECT_SELF, "KJ_WKS_WAIT_COUNT"); int iMinWait = GetLocalInt(OBJECT_SELF, "KJ_WKS_MIN_WAIT"); // Bump up the counter if necessary. if (iWaitCount < iMinWait) { SetLocalInt(OBJECT_SELF, "KJ_WKS_WAIT_COUNT", iWaitCount+1); } // Otherwise, it is ok to go ahead and see if we should change areas. // Roll the dice to see if we should change workstations if ((iWaitCount >= iMinWait) && (d100()<=GetLocalInt(OBJECT_SELF, "KJ_WKS_CHANGE_CHANCE"))) { object oWayPoint; // Working variable to store the new waypoint if we move. string sWPtag; // String to build the tag for the new waypoint. int iCurObj; // Integer representing the Workspace # we want to use. string sInterObj; // The Workspace object we are interacting with. // Pick a random workstation to move to. iCurObj = Random(GetLocalInt(OBJECT_SELF, "KJ_WKS_COUNT"))+1; // Check to see if we need to pick a new workstation because of then // KJ_WKS_NO_REPEAT variable. if (GetLocalInt(OBJECT_SELF, "KJ_WKS_NO_REPEAT")==1) { // Build the workstation tag string sTestObj = "ws_" + GetTag(OBJECT_SELF) + "_" + IntToString(iCurObj); // Check to see if it matches the current workstation if (sTestObj==GetLocalString(OBJECT_SELF, "KJ_WKS_CURRENT")) { // If so, add 1. If we go past the WKS count, set the workstation // to 1. iCurObj++; if (iCurObj > GetLocalInt(OBJECT_SELF, "KJ_WKS_COUNT")) { iCurObj=1; } // Debugging info. Uncomment to see NPCs report repeated workstations. //ActionSpeakString("Found Repeat... Bouncing Number to " + IntToString(iCurObj)); } } // Assemble tags for the workstation and the waypoint. sWPtag = "wsp_" + GetTag(OBJECT_SELF) + "_" + IntToString(iCurObj); sInterObj = "ws_" + GetTag(OBJECT_SELF) + "_" + IntToString(iCurObj); // Debugging info. Uncomment to see NPC talk about what they are doing. //ActionSpeakString("Changing Jobs to : " + sInterObj); // Get the waypoint and move the NPC to it. oWayPoint = GetObjectByTag(sWPtag); if ( GetIsObjectValid(oWayPoint)) { AssignCommand(OBJECT_SELF, ActionMoveToLocation(GetLocation(oWayPoint))); AssignCommand(OBJECT_SELF, ActionInteractObject(GetObjectByTag(sInterObj))); // Store the name of the workstation object as a local string on the NPC // This is used in later heartbeats to play animations. SetLocalString(OBJECT_SELF, "KJ_WKS_CURRENT", sInterObj); // Also store the workstation number to make life easier later on. SetLocalInt(OBJECT_SELF, "KJ_WKS_CURRENT_INT", iCurObj); // Reset the wait counter SetLocalInt(OBJECT_SELF, "KJ_WKS_WAIT_COUNT", 0); } } else { if (d100() <= GetLocalInt(OBJECT_SELF, "KJ_WKS_ANIM_CHANCE")) { int iAction; // The action/animation to play. int iAnimCount = GetLocalInt(OBJECT_SELF, "KJ_WKS_ANIM_COUNT"); int iRValue = iAnimCount; // What to actually roll agains. int iPlaceableAnimation = 0; // Is it possible to interact with object. // If the workstation is a usable placeable, allow one of the // animations to be to use it via ActionInteractObject. if (GetIsPlaceableObjectActionPossible(GetObjectByTag(GetLocalString(OBJECT_SELF, "KJ_WKS_CURRENT")), PLACEABLE_ACTION_USE)) { iPlaceableAnimation = 1; iRValue++; } // Check to see if we can speak strings at this workstation if (GetLocalInt(OBJECT_SELF, "KJ_WKS_STRING_COUNT")>0) { // Get the current workstation number int iTestWorkstation = GetLocalInt(OBJECT_SELF, "KJ_WKS_CURRENT_INT"); // Check to see if we should be silent here. if (!(GetLocalInt(OBJECT_SELF, "KJ_WKS_SILENT_" + IntToString(iTestWorkstation))==1)) { // If not, go ahead and add the number of strings to the random limit. iRValue = iRValue + GetLocalInt(OBJECT_SELF, "KJ_WKS_STRING_COUNT"); } } // Roll the dice. Equal chance of each animation. iAction=Random(iRValue) + 1; // Debugging info. Uncomment to see the NPC talk about animations. //ActionSpeakString("Playing Animation " + IntToString(iAction) + " of " + IntToString(iAnimCount)); // The random result is interacting with the object, so do it. if ((iAction==iAnimCount+1) && (iPlaceableAnimation)) { // Did we roll the action to interact with the workstation? // If so, execute ActionInteractObject AssignCommand(OBJECT_SELF, ActionInteractObject(GetObjectByTag(GetLocalString(OBJECT_SELF, "KJ_WKS_CURRENT")))); } // The random result is to speak a string, so do that. if ((iAction>iAnimCount) || ((iAction>iAnimCount+1) && (!iPlaceableAnimation))) { // Figure out what string we are going to speak. int iStringToSay = iAction - iAnimCount; // Need to subtract one if the placable is usable. if (iPlaceableAnimation) { iStringToSay--; } // Assign the NPC to speak the dialogue. AssignCommand(OBJECT_SELF, ActionSpeakString(GetLocalString(OBJECT_SELF, "KJ_WKS_STRING_" + IntToString(iStringToSay)))); } // The random result is an animation, so play it. if (iAction <= iAnimCount) { // If not, play the appropriate animation. AssignCommand(OBJECT_SELF, ActionPlayAnimation(GetLocalInt(OBJECT_SELF, "KJ_WKS_ANIM_" + IntToString(iAction)))); } } } } }