Script people in a town area going to sleep at night?

Thanks for trying to help @raymondsebas but I don’t think that’s it, actually. I only use the sh_hbeat_generic_standardhb so I have no NPCs with different professions doing different things. None of that, and thus I don’t use any waypoints from this system.

Edit: From what I can see, there are a few NPCs with waypoint sets (not from Kamal’s system) (they wander around in a specific way) and those seem to not be able to go to a door and disappear (Scripthidden). Do I need some extra code for that, @kamal?

Edit 2: Looking at my area again, and looking at those NPCs that didn’t go to bed, it’s really strange. They have the exact same scripts as everyone else.

1 Like

Oh, my bad. In my humble opinion, putting a lot of attention and effort into animating NPCs is useless, since most players don’t really care. I tended to be like you on this subject, but it’s sometimes a real headache and it required a lot of time that I prefer to devote elsewhere. Building a module already takes a lot of time. But this is only my opinion and it only concerns me of course. I encourage you to do what you think is right and which you obviously like. :slightly_smiling_face:

1 Like

I wholeheartedly agree with you on this. However, in this case, why I’m using it, is because of a certain side quest. One NPC tells the party that everyone goes to sleep at night and thus haven’t seen a certain thing. If people stand around all the time it takes away that illusion, I think…but maybe…maybe it’s overkill to try and get this to work properly, I don’t know. I don’t use it in any other area.

1 Like

Here is Kamal’s script, the sh_hbeat_generic_standardhb. What I’m thinking is, I can only find one ClearAllActions and that’s set to FALSE. Isn’t that needed to stop an NPC from moving around along a “waypoint course”? Oh, wait, looking at the function FALSE just checks if the NPC is in combat so it should work.

#include "ginc_misc"
#include "ginc_math"
#include "ginc_wp"
#include "hench_i0_ai"

#include "sh_npc_activinc_simple"
#include "sh_npc_include"

void PlayCustomLoopingAnimation(object oObject, string sAnimationName)
	{
	PlayCustomAnimation(oObject, sAnimationName, 1);
	}

void PlayCustomOneShotAnimation(object oObject, string sAnimationName)
	{
	PlayCustomAnimation(oObject, sAnimationName, 0);
	}



void main()
{


	
	if (GetAILevel(OBJECT_SELF) == AI_LEVEL_VERY_LOW)
		{
		return;
		}
   

   int nTimeGoHomeHour;
   int nTimeGoHomeMinute;
   int nTimeWakeHour;
   int nTimeWakeMinute;
   
      	//record position and facing, times to sleep/wake, put into any desired animation. Only needs to happen once.
	int DoThisOnce = GetLocalInt(OBJECT_SELF, GetTag(OBJECT_SELF));
	if (DoThisOnce!=TRUE)
	{
		SetLocalInt(OBJECT_SELF, GetTag(OBJECT_SELF), TRUE);
		location lSelf = GetLocation(OBJECT_SELF);
		vector vSelf = GetPositionFromLocation(lSelf);
		float fFacing = GetFacing(OBJECT_SELF);
		SetLocalFloat(OBJECT_SELF, "fLocationX", vSelf.x);
		SetLocalFloat(OBJECT_SELF, "fLocationY", vSelf.y);
		SetLocalFloat(OBJECT_SELF, "fLocationZ", vSelf.z);
		SetLocalFloat(OBJECT_SELF, "fFacingStart", fFacing);
		SetLocalInt(OBJECT_SELF, "nIsInCombat", 0);
		SetLocalInt(OBJECT_SELF, "nUnequipDoOnce", 0);
		
		SetLocalInt(OBJECT_SELF, "nIsInCombat", 0);
		SetLocalInt(OBJECT_SELF, "nUnequipDoOnce", 0);
		//determine and store a time to go home, and a time to wake
		nTimeGoHomeHour = Random(2) + 19; //so 7 or 8pm
		nTimeGoHomeMinute = Random(2); //because the game doesn't do minutes properly.... minute 1 is effectively 30 minutes.
		SetLocalInt(OBJECT_SELF, "nTimeGoHomeHour", nTimeGoHomeHour);
		SetLocalInt(OBJECT_SELF, "nTimeGoHomeMinute", nTimeGoHomeMinute);
		
		nTimeWakeHour = Random(2) + 7; //so 7 or 8am
		nTimeWakeMinute = Random(2); //because the game doesn't do minutes properly.... minute 1 is effectively 30 minutes.
		SetLocalInt(OBJECT_SELF, "nTimeWakeHour", nTimeWakeHour);
		SetLocalInt(OBJECT_SELF, "nTimeWakeMinute", nTimeWakeMinute);
	}

	effect eEffect;
	int nCurrentTimeHour = GetTimeHour();
	int nCurrentTimeMinute = GetTimeMinute();

	//if we're going home, continue to do so.
	if (GetLocalInt(OBJECT_SELF, "nNpcMovingHome")== 1)
		{
			//define a "home" door
			//original code.
			if (GetLocalInt(OBJECT_SELF, "nStartedMovingHome")!=1)
			{
			SetLocalInt (OBJECT_SELF, "nStartedMovingHome", 1);
			object oHomeDoor = GetRandomHomeDoor(100.0);
			SetLocalObject(OBJECT_SELF, "oHomeDoor", oHomeDoor);
			SimpleNpcGoHomeAndSleepUntilWakeTime (OBJECT_SELF, oHomeDoor, 0);
			}
			SimpleNpcGoHomeAndSleepUntilWakeTime (OBJECT_SELF, GetLocalObject(OBJECT_SELF,"oHomeDoor"), 0);
		}
	
	else //not currently moving home.
		{
		//determine if time to go home
	   	if ((((GetLocalInt(OBJECT_SELF, "nTimeGoHomeHour") < nCurrentTimeHour) && (GetLocalInt(OBJECT_SELF, "nNpcIsAsleep") !=1 )) 
			|| 
	   		((GetLocalInt(OBJECT_SELF, "nTimeGoHomeHour") == nCurrentTimeHour) && (GetLocalInt(OBJECT_SELF, "nTimeGoHomeMinute") == nTimeGoHomeMinute) && (GetLocalInt(OBJECT_SELF, "nNpcIsAsleep") !=1 )))
			//make sure they don't all gome at same time, it causes lag if too many npcs do this at once.
			&&
			(Random(3)==1))
	   		{
			ClearAllActions(FALSE);
	   		SetLocalInt(OBJECT_SELF, "nNpcMovingHome", 1);
			object oHomeLeaving = GetLocalObject(OBJECT_SELF, "oMyHome");
			}

		//not time to go home,  	   
		if (GetLocalInt(OBJECT_SELF, "nNpcMovingHome")!=1)
			{
			if // determine if time to be active	  
				(((((GetLocalInt(OBJECT_SELF, "nNpcIsAsleep") ==1 ) && (nCurrentTimeHour > GetLocalInt(OBJECT_SELF, "nTimeWakeHour")) )
				|| ((GetLocalInt(OBJECT_SELF, "nNpcIsAsleep") ==1 ) && (nCurrentTimeHour == GetLocalInt(OBJECT_SELF, "nTimeWakeHour")) && (nCurrentTimeMinute == GetLocalInt(OBJECT_SELF, "nTimeWakeMinute"))) )
				//and before sleep time
				&&
				((GetLocalInt(OBJECT_SELF, "nTimeGoHomeHour") > nCurrentTimeHour) || (GetLocalInt(OBJECT_SELF, "nTimeGoHomeHour") == nCurrentTimeHour) && (GetLocalInt(OBJECT_SELF, "nTimeGoHomeMinute") < nTimeGoHomeMinute)))
				//make sure they don't all wake at same time, it causes lag if too many npcs do this at once.
				&&
				(Random(3)==1))
				{
				//jumping back to where we went hidden shouldn't be necessary, but testing has shown it is.
				//for an unknown reason when the npc ends the hidden status, it appears immediately at it's original location
				float fSelfHomeX = GetLocalFloat(OBJECT_SELF, "fLocationHomeX");
				float fSelfHomeY = GetLocalFloat(OBJECT_SELF, "fLocationHomeY");
				float fSelfHomeZ = GetLocalFloat(OBJECT_SELF, "fLocationHomeZ");
				float fFacingAtSleep = GetLocalFloat(OBJECT_SELF, "fFacingAtSleep");
				float fFacingAtWake = fabs(fFacingAtSleep-180.0);
				vector vHome = Vector(fSelfHomeX, fSelfHomeY, fSelfHomeZ);
				location lHome = Location(GetArea(OBJECT_SELF), vHome, 0.0);
				JumpToLocation(lHome);
				SetFacing(fFacingAtWake);
				SetLocalInt(OBJECT_SELF, "nActivity", 0); //no longer doing activity.
				SetLocalInt(OBJECT_SELF, "nNpcIsAsleep", 0);
				SetScriptHidden(OBJECT_SELF, 0);
				SetLocalInt(OBJECT_SELF, "nNpcMovingHome", 0);
				}
	   
 
			// if awake
			int nAsleep = GetLocalInt(OBJECT_SELF, "nNpcIsAsleep");
			int nMovingHome = GetLocalInt(OBJECT_SELF, "nNpcMovingHome");
			if ((nAsleep !=0 ) && (nMovingHome!= 1))
				{
			   	location lCurrent = GetLocation(OBJECT_SELF);
				vector vCurrentLocation = GetPositionFromLocation(lCurrent);
				float fOriginX = 	GetLocalFloat(OBJECT_SELF, "fLocationX");
				float fOriginY = 	GetLocalFloat(OBJECT_SELF, "fLocationY");
				float fOriginZ = 	GetLocalFloat(OBJECT_SELF, "fLocationZ");
				vector vOrigin = Vector(fOriginX, fOriginY, fOriginZ);
				location lOrigin = Location(GetArea(OBJECT_SELF), vOrigin, 0.0);
	
				//if awake and at original position, not in conversation or combat
				if ((vCurrentLocation == vOrigin) && (IsInConversation(OBJECT_SELF) == FALSE) && (GetIsInCombat(OBJECT_SELF) == FALSE))
					{

					SetLocalInt(OBJECT_SELF, "nIsInCombat", 0);
					//we only want to unequip once per time period at origin
					if (GetLocalInt(OBJECT_SELF, "nUnequipDoOnce")== 0)
						{
						SetLocalInt(OBJECT_SELF, "nUnequipDoOnce", 1);
						//remove the vfx for what we were holding while Activity.
						}
					SetLocalInt(OBJECT_SELF, "nActivity", 1); //at origin, mark as acting
					
					//run standard heartbeat
					if (GetAILevel() == AI_LEVEL_VERY_LOW) return;
					
					//insert code block for commoner ai activity. 
					//mark self as in combat.
					SetLocalInt(OBJECT_SELF, "nIsInCombat", 1);
					//mark so we will unequip when combat over and we've returned to origin.
					SetLocalInt(OBJECT_SELF, "nUnequipDoOnce", 0); 
					SetLocalInt(OBJECT_SELF, "nActivity", 0);
					//end inserted code. begin the stock hb script for the rest of the incombat if statement

					if(GetSpawnInCondition(NW_FLAG_FAST_BUFF_ENEMY))
						{
						if(HenchTalentAdvancedBuff(40.0))
							{
							SetSpawnInCondition(NW_FLAG_FAST_BUFF_ENEMY, FALSE);
							// TODO evaluate continue with combat
							return;
							}
						}

					if (HenchCheckHeartbeatCombat())
						{
						HenchResetCombatRound();
						}
					if(GetHasEffect(EFFECT_TYPE_SLEEP))
						{
						if(GetSpawnInCondition(NW_FLAG_SLEEPING_AT_NIGHT))
							{
							effect eVis = EffectVisualEffect(VFX_IMP_SLEEP);
							if(d10() > 6)
								{
								ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, OBJECT_SELF);
								}
							}
						}

					// If we have the 'constant' waypoints flag set, walk to the next
					// waypoint.
					else if (!GetIsObjectValid(GetNearestSeenOrHeardEnemyNotDead(HENCH_MONSTER_DONT_CHECK_HEARD_MONSTER)))
						{
						CleanCombatVars();
						if (GetBehaviorState(NW_FLAG_BEHAVIOR_SPECIAL))
							{
							HenchDetermineSpecialBehavior();
							}
						else if (GetLocalInt(OBJECT_SELF, sHenchLastHeardOrSeen))
							{
							// continue to move to target
							MoveToLastSeenOrHeard();
							}
						else
							{
							SetLocalInt(OBJECT_SELF, HENCH_AI_SCRIPT_POLL, FALSE);
							if (DoStealthAndWander())
								{
								// nothing to do here
								}
							// sometimes waypoints are not initialized
							else if (GetWalkCondition(NW_WALK_FLAG_CONSTANT))
								{
								WalkWayPoints();
								}
							else
								{
								if(!IsInConversation(OBJECT_SELF))
									{
									if(GetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS) ||
										GetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS_AVIAN))
										{
										PlayMobileAmbientAnimations();
										}
									else if(GetSpawnInCondition(NW_FLAG_IMMOBILE_AMBIENT_ANIMATIONS))
										{
										PlayImmobileAmbientAnimations();
										}
									}
								}
							}
						}
					else if (GetUseHeartbeatDetect())
						{
						//	Jug_Debug(GetName(OBJECT_SELF) + " starting combat round in heartbeat");
						//	Jug_Debug("*****" + GetName(OBJECT_SELF) + " heartbeat action " + IntToString(GetCurrentAction()));
						HenchDetermineCombatRound();
						}
					if(GetSpawnInCondition(NW_FLAG_HEARTBEAT_EVENT))
						{
						SignalEvent(OBJECT_SELF, EventUserDefined(EVENT_HEARTBEAT));
						}



					} //end of normal hb for being in distance.

				//if in combat, run a normal hb.
				if (GetIsInCombat(OBJECT_SELF)==TRUE)//run a normal npc hb
					{
					if (GetAILevel() == AI_LEVEL_VERY_LOW) return;
						
					//insert first code block for commoner ai activity. 
					//another one at end of regular hb code. kamal
					 //mark self as in combat.
					SetLocalInt(OBJECT_SELF, "nIsInCombat", 1);
					//mark so we will unequip when combat over and we've returned to origin.
					SetLocalInt(OBJECT_SELF, "nUnequipDoOnce", 0); 
					SetLocalInt(OBJECT_SELF, "nActivity", 0);
					//end inserted code. begin the stock hb script for the rest of the incombat if statement

					if(GetSpawnInCondition(NW_FLAG_FAST_BUFF_ENEMY))
						{
						if(HenchTalentAdvancedBuff(40.0))
							{
							SetSpawnInCondition(NW_FLAG_FAST_BUFF_ENEMY, FALSE);
							// TODO evaluate continue with combat
							return;
							}
						}

					if (HenchCheckHeartbeatCombat())
						{
						HenchResetCombatRound();
						}
					if(GetHasEffect(EFFECT_TYPE_SLEEP))
						{
						if(GetSpawnInCondition(NW_FLAG_SLEEPING_AT_NIGHT))
							{
							effect eVis = EffectVisualEffect(VFX_IMP_SLEEP);
							if(d10() > 6)
								{
								ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, OBJECT_SELF);
								}
							}
						}

					// If we have the 'constant' waypoints flag set, walk to the next
					// waypoint.
					else if (!GetIsObjectValid(GetNearestSeenOrHeardEnemyNotDead(HENCH_MONSTER_DONT_CHECK_HEARD_MONSTER)))
						{
						CleanCombatVars();
						if (GetBehaviorState(NW_FLAG_BEHAVIOR_SPECIAL))
							{
							HenchDetermineSpecialBehavior();
							}
						else if (GetLocalInt(OBJECT_SELF, sHenchLastHeardOrSeen))
							{
							// continue to move to target
							MoveToLastSeenOrHeard();
							}
						else
							{
							SetLocalInt(OBJECT_SELF, HENCH_AI_SCRIPT_POLL, FALSE);
							if (DoStealthAndWander())
								{
								// nothing to do here
								}
							// sometimes waypoints are not initialized
							else if (GetWalkCondition(NW_WALK_FLAG_CONSTANT))
								{
								WalkWayPoints();
								}
							else
								{
								if(!IsInConversation(OBJECT_SELF))
									{
									if(GetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS) ||
										GetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS_AVIAN))
										{
										PlayMobileAmbientAnimations();
										}
									else if(GetSpawnInCondition(NW_FLAG_IMMOBILE_AMBIENT_ANIMATIONS))
										{
										PlayImmobileAmbientAnimations();
										}
									}
								}
							}
						}
					else if (GetUseHeartbeatDetect())
						{
						//	Jug_Debug(GetName(OBJECT_SELF) + " starting combat round in heartbeat");
						//	Jug_Debug("*****" + GetName(OBJECT_SELF) + " heartbeat action " + IntToString(GetCurrentAction()));
						HenchDetermineCombatRound();
						}
					if(GetSpawnInCondition(NW_FLAG_HEARTBEAT_EVENT))
						{
						SignalEvent(OBJECT_SELF, EventUserDefined(EVENT_HEARTBEAT));
						}
					}
				
			//not at origin, move to origin if not in combat.
			if (vCurrentLocation != vOrigin) 
				{
				float fOriginX = 	GetLocalFloat(OBJECT_SELF, "fLocationX");
				float fOriginY = 	GetLocalFloat(OBJECT_SELF, "fLocationY");
				float fOriginZ = 	GetLocalFloat(OBJECT_SELF, "fLocationZ");
				vector vOrigin = Vector(fOriginX, fOriginY, fOriginZ);
				location lOrigin = Location(GetArea(OBJECT_SELF), vOrigin, 0.0);
				//just woke up
				if ((GetIsInCombat(OBJECT_SELF)==FALSE) && 	(GetLocalInt(OBJECT_SELF, "nActivity")== 0)) 
					{
					location lOriginLocation = Location(GetArea(OBJECT_SELF), vOrigin, 	GetLocalFloat(OBJECT_SELF, "fFacingStart"));
					ActionMoveToLocation(lOriginLocation, FALSE);
					}
					//if wandered too far away, or was drawn away via combat.
				else if ((GetIsInCombat(OBJECT_SELF)==FALSE) &&(GetDistanceBetweenLocations(GetLocation(OBJECT_SELF),lOrigin)>=15.0 ))
					{
					location lOriginLocation = Location(GetArea(OBJECT_SELF), vOrigin, 	GetLocalFloat(OBJECT_SELF, "fFacingStart"));
					ActionMoveToLocation(lOriginLocation, FALSE);
					}
			
				}
				//end of combat scripts
					
				}
			}	
		}
} 

Edir: Since there’s a bit of randomness to them going home, maybe the ones I saw not going home just rolled the dice to not go home every time? Though I find it unlikely since I walked around for quite a while observing the NPCs. I think, even if that’s not necessary, I will take away the FALSE from ClearAllActions, since there’s no combat in this area anyway. I shouldn’t do anything but, well…I’ll test this again. Actually, I believe Lance told me ClearAllActions can mess with things sometimes. I’ll try and remove that temporarily instead and see what happens.

1 Like

I suggest that you ask to @kamal @4760 @kevL_s @Lance_Botelle @travus @Tarot_Redhand

they are all excellent scriptwriters overhere (and decent fellows). My talents are in building areas, my knowledge of scripts is normal, but not as advanced as them.

Yep. I know. Those guys are amazing at scripting, no doubt about that! They’ve helped me a lot over the years. Especially kevL_s and travus are real masters, but Lance is not far behind. Tarot, I believe, works mostly with NWN1 though (if I remember correctly).

1 Like

Tried taking away ClearAllActions but that did almost nothing. It seems to me that only half of the characters in the area go to sleep and that’s not that good. I’ll try one last time with ClearAllActions back on.

It seems that’s as good as it’s gonna get. Half the characters manages to go to sleep. The others seem stuck on their way to a door. I wonder if you could do something about that script-wise…

Edit: I’ll try and and add 4760’s function ClearPendingActions to the code and see if that fixes things.

yep setting a var in the Collection is the same as scripted SetLocal*() functs.

not sure about this but maybe NOT static …

i think of a Static object as barely a step above an Environmental object … that might be why stuff works for some but not others

Edit: 4760’s function ClearPendingActions did quite little to help those NPCs stuck who seems to be stuck about to go home. At least I think so, since I found them at a different place than before, and they just stand there looking.

Well, Kamal states in the description of the function this:

“Note: the GoHomeAndSleep function looks for static doors to go to, so you’ll need some in the area.”

“hbeat_generic_ : The hbeat_generic_ set of scripts adds the ability for the npc to “go home” at night, and sends the
npc to a random static door in range. The hbeat_generic carry out their activity where placed or spawned.”

@kevL_s - Do you really think I should change the doors to non-static?

i think it deserves to be (re) tested … at least investigated by looking in the toolset at a few doors that work and comparing them with doors that don’t work

Ok. I’m not actually sure what doors work or not. The area is quite big (not huge but still) and it’s difficult to follow each character. Maybe I’ll just do every door non-static and see what happens (a lot of work, but maybe it’s worth it).

something i’ve learned: don’t waste yer time going over everything when debugging. Work with a specific NPC + a specific door only.

find out what works,

Well, since each NPC picks a random door in this system every time, it’s difficult to test. I have already changed each door now. I’ll see what that does. If no NPC manages to go to sleep then at least that tells us something…

(id temporarily subvert the system, and make a chosen NPC go to a chosen door)

This was interesting. Seems like the static thing wasn’t important. Most of the NPCs did go to bed now, however in one area within the area there were four people standing around. Not sure if they had tried to go to bed yet, but after running around for quite a while with my PC, they were still there. So, it seems that the important thing is the Local Int on the door. I have many doors in this area. I’m thinking that maybe I should limit the number of doors with the local int. Don’t know if that would do anything but…

Well, I’m probably putting way too much time on this particular detail, just because I don’t want NPCs around at night because of a side quest where an NPC hints that everyone is asleep at night and therefore hasn’t noticed a certain thing.

1 Like

good to know  :)

If I was better at scripting I would try and change the code so that if they can’t reach the door they will teleport to it. In fact I tried that, kind of, with 4760’s ClearPendingActions and in one case I think it helped but not in many others so…

It seems to come down to this function, which is in an include script, and it looks like this:

void SimpleNpcGoHomeAndSleepUntilWakeTime (object oNpc, object oHome, int nSpeak) //simplified version for use with npcs set to binary activity, sleep and a single activity.
{
	ClearFlags (oNpc);
	//object oHome = GetRandomHomeDoor(fMaxDistance) ;
	float fDwp1 = GetDistanceToObject (oHome);
	//move to door if not already there.
	if (fDwp1 >= 3.0)
		{
		//SetLocalInt (oNpc, "atloc", 0);
		ActionMoveToObject(oHome, FALSE, fMaxDistance);
		//SetLocalInt(OBJECT_SELF, "nNpcMovingHome", 1);
		}

	//at door.
	if (fDwp1 <= 5.0)
		{
	//	PlayCustomAnimation(OBJECT_SELF, "gettable ", 1); //"open" the door
		PlayAnimation(ANIMATION_LOOPING_GET_MID, 1.f, 1.f); //"open" the door
		SetLocalInt(OBJECT_SELF, "nNpcIsAsleep",1);
		SetLocalInt(OBJECT_SELF, "nNpcMovingHome", 0);
		
		SetLocalInt (OBJECT_SELF, "nStartedMovingHome", 0); //arrived at home
		
		location lSelfHome = GetLocation(OBJECT_SELF);
		vector vSelfHome = GetPositionFromLocation(lSelfHome);
		float fFacing = GetFacing(OBJECT_SELF);
		SetLocalFloat(OBJECT_SELF, "fLocationHomeX", vSelfHome.x);
		SetLocalFloat(OBJECT_SELF, "fLocationHomeY", vSelfHome.y);
		SetLocalFloat(OBJECT_SELF, "fLocationHomeZ", vSelfHome.z);
		SetLocalFloat(OBJECT_SELF, "fFacingAtSleep", fFacing);
	
	//	SetScriptHidden (OBJECT_SELF,1,0);
		DelayCommand(2.0,SetScriptHidden (OBJECT_SELF,1,0));
		DelayCommand(3.0,SetAILevel(OBJECT_SELF, AI_LEVEL_DEFAULT));// have to restore default ai so we can continue to process while hidden in prep for waking.
		}
}

4760’s ClearPendingActions look like this (which I tried using but it didn’t quite work any better):

void ClearPendingActions(object oPerson, object oWP)
{
	// just to make sure we're right on the spot
	if (GetCurrentAction(oPerson) == ACTION_MOVETOPOINT)
	{
		//SendMessageToPC(GetFirstPC(), "Timing issue!");
		AssignCommand(oPerson, ClearAllActions());
	}
	AssignCommand(oPerson, ActionJumpToLocation(GetLocation(oWP)));
}

I’m tempted at using ActionJumpToLocation instead of ActionMoveToObject but that will probably not look that good. It’s weird if every character teleports to their homes.

am reworking SimpleNpcGoHomeAndSleepUntilWakeTime()

gimme a minute or 10

Ok. I just changed the

object oHomeDoor = GetRandomHomeDoor(100.0);

to

GetRandomHomeDoor(30.0); 

and that went well. Now only 1 or 2 NPCs are left out. Since most of my NPCs are pretty near a door this was a safe way to go.

This is how the GetRandomHomeDoor function looks like (apparently based on one of your scripts):

object GetRandomHomeDoor(float fMaxDistance)  //based on kevL's random object code.
{
	int iObject; object oObject;
	int i = 1;	
	object oNear = GetNearestObject(OBJECT_TYPE_DOOR, OBJECT_SELF, i);
  	// GetNearest seems to work only in the area of OBJECT_SELF, good.
  	while ((GetIsObjectValid(oNear)) && (GetDistanceToObject(oObject) <= fMaxDistance))
	  	{
    	i ++;
    	oNear = GetNearestObject(OBJECT_TYPE_DOOR, OBJECT_SELF, i);
  		}
  	// count is one too many ..
  	i -= 1;
   	// Random starts w/ 0 (subtract 1), so add 1
   	iObject = Random(i) + 1; 		// iObject -> that's a pun, haha :|
  	// max the count at the number of objects, i
  	if (iObject > i) iObject = i;
	oObject = GetNearestObject(OBJECT_TYPE_DOOR, OBJECT_SELF, iObject);
		int nCAIUseOK = GetLocalInt(oObject, "nCAIUseOK");
		while ((GetIsObjectValid(oObject)) && (GetDistanceToObject(oObject) <= fMaxDistance)
			&& (nCAIUseOK!=1))
	  		{
			//SendMessageToPC(GetFirstPC(), "Debug: usable door, recalculating");
			iObject = Random(i) + 1; 
			if (iObject > i) iObject = i;
			oObject = GetNearestObject(OBJECT_TYPE_DOOR, OBJECT_SELF, iObject);
    		nCAIUseOK = GetLocalInt(oObject, "nCAIUseOK");
			}
	
			//sanity check of found door.
		if ((GetIsObjectValid(oObject)) && (GetDistanceToObject(oObject) <= fMaxDistance))
			{	
    		return oObject;
			}
		return OBJECT_INVALID;

}

Edit: One of the NPCs that refused to go and sleep had the wrong heartbeat script. Now, then it seems to working almost fully, and I’m a happy camper. I will test it one final time and see how it goes.

1 Like