Action Scripting

Was hoping someone could help me out with this script I’m trying to write, I don’t know that much about scripting but i’m having more luck than I thought I would so far.
I’m also using Neshke’s Extendable Spawning System and this is part of a partrol where the waypoint fires this script, script 2

The script compiles successfully but I’m putting the NPC on a timer to sit on a bench before he goes to work.
The NPC will sit but will never get up.

Is there a way to make the npc stand after sitting? The looping animation works that way but I don’t know how to use that to sit on a placeable

if (nPatrolScript == 2)
{
object oChair = GetObjectByTag(“x3_plc_bench002”);
ActionMoveToObject(oChair);
ActionSit(oChair);
ActionWait(82.0);
ActionMoveAwayFromObject(oChair,FALSE,1.0f);
}

Replace ActionWait with

DelayCommand(82.0, ClearAllActions());

and it should work.

You’re adding actions to a queue, and ActionSit is one that’ll keep going until canceled. At this point, your NPC has ActionSit and ActionWait in the queue, but until ActionSit is canceled, they won’t even start waiting for 82 seconds.

Goes for combat, too - ActionAttack will just continue until the attack target is dead, or until ActionAttack is canceled manually. If you want to get someone out of combat state, use ClearAllActions(TRUE).

3 Likes

Oh thanks I didn’t even think of that for some reason, seems kind of obvious now.

1 Like

Just thought I’d come back here and show the script I ended up using, had to make a few more changes. After clearing all the actions the NPC would no longer continue his route, this script will make him switch to a new waypoint route that doesn’t have the sit script, and he walks to his job. Not changing to a new route the NPC became stuck in a bench sitting loop.

if (nPatrolScript == 2)
{
object oChair = GetObjectByTag(“BenchPew”);
ActionMoveToObject(oChair);
ActionSit(oChair);
DelayCommand(81.0, ClearAllActions());
DelayCommand(82.0,AddPatrolStop(02,00));
DelayCommand(82.5,SetPatrolRoute(02));
DelayCommand(83.0,DoPatrolRoute(02,3));
}

1 Like

Came across a weird issue that I haven’t been able to figure out but I think it’s something NESS is doing on its own.

I’ve placed this script on a way point as I did in the above problem:

if (nPatrolScript == 5)
{
object oNearestDoor = GetNearestObjectByTag(“x3_door_wood003”);
ActionMoveToObject(oNearestDoor);
if (GetIsOpen(oNearestDoor) == TRUE)
if (GetIsOpen(oNearestDoor))ActionOpenDoor(oNearestDoor);
}

And this is the name I’ve put on the spawn in way point for the bartender SP_SD2_SF_PR00T3_HR18

I want him to spawn in at hour 18 walk home and despawn, which he does perfectly, until I add
the above script to open his door.

With the script activated on the way point he opens his door and despawns as he should, yet, one or two seconds later he appears again at the last scripted way point and runs the script one more time, but only once, then the NPC disappears for good.

I’ve tried destroying the object, despawning it, nothing seems to work except not using a script, but then I don’t get the effect of him opening his door to go home, which I thought was kind of cool to have.

Any help would be appreciated, I’ll make sure I post if I figure it out myself.

That’s odd. All I’m seeing here is that the duplicate condition is strange (you’re checking whether the door is open twice), and the door being open is the condition for the door being opened. Shouldn’t that be

if (!GetIsOpen(oNearestDoor)) ActionOpenDoor(oNearestDoor);

(If the door is not open, open the door)

instead of

if (GetIsOpen(oNearestDoor) == TRUE)
if (GetIsOpen(oNearestDoor))ActionOpenDoor(oNearestDoor);

(if the door is open, if the door is open, open the door)

?

Weird, though. Not sure why they’d jump to any waypoints, or rerun everything once before despawning. If it’s not the door lines here, maybe it’s happening elsewhere in the script. Why is the NPC even opening the door if he’s only supposed to open the door if the door is already open? :thinking:

Try putting in another ClearAllActions(), to clear the action queue before you make them move to the door and open it. Maybe it’s a queued-actions-thing again. :confused:

Sidenote: I think you can use GetNearestObject(OBJECT_TYPE_DOOR) to target the nearest door without it needing to have a particular tag, in case that’s desirable.

This has no change in behavior, still does the script twice for whatever reason:

if (nPatrolScript == 5)
{
object oNearestDoor = GetNearestObjectByTag(“x3_door_wood003”);
ActionMoveToObject(oNearestDoor);
ActionDoCommand(ClearAllActions());
if (GetIsOpen(oNearestDoor))ActionOpenDoor(oNearestDoor);
}

Not sure whats going on exactly, very frustrating though

my intention of :

if (GetIsOpen(oNearestDoor) == TRUE)
if (GetIsOpen(oNearestDoor))ActionOpenDoor(oNearestDoor);

Was so the npc would open the door if it was closed but not if it was open. I guess I wrote this wrong?

if (nPatrolScript == 5)
{
    	object oNearestDoor = GetNearestObjectByTag(“x3_door_wood003”);
       	ActionMoveToObject(oNearestDoor);
       	ActionDoCommand(ClearAllActions());

	if (GetIsOpen(oNearestDoor))
		ActionOpenDoor(oNearestDoor);
}

Why are you telling the NPC to try to open an already open door? Read your if statement. Currently it reads if this door is open, open it. What you should be doing is -

if (!(GetIsOpen(oNearestDoor)))
	ActionOpenDoor(oNearestDoor);

Notice the not symbol “!”. That changes it to read “if the door is not open, open it.”

The code you keep showing will not cause it to do the actions twice. Therefore your problem lies elsewhere. Try looking at where the fragment above is called from or any preceding code to the above.

Is there any way you can post the whole script as a plain text file somewhere it can be downloaded from other than here?

Also there is another thing that is bothering me. In your earlier post that @TheBarbarian solved for you there is the line

and in this script I just commented on there is the line

You’re not doing a whole list of if(nPatrolScript == value x) are you? If you are then you need to look at this to see why you shouldn’t be doing that.

TR

The door wasn’t already open when the npc opens it, but the reason I wrote it that way is because I don’t really know what i’m doing, only experience with this is from NWN lexicon and NWN itself. Didn’t know ! could change the meaning. The door was actually opening the way I intended with my script somehow?

My only real problem is the script occuring twice.
This whole thing is from the NESS(Neshke’s Extendable Spawning System) spawn_sc_patrol script.

https://neverwintervault.org/project/nwn1/script/neshkes-extendable-spawning-system-ness-v813

Here’s the entire script file I’m writing in, its suppose to pick from a list of scripts and then you tag your number to the way point the npc goes to, when he reaches that way point it fires the script.

//
// Patrol Scripts
//
#include “spawn_functions”
//
object GetChildByTag(object oSpawn, string sChildTag);
object GetChildByNumber(object oSpawn, int nChildNum);
object GetSpawnByID(int nSpawnID);
void DeactivateSpawn(object oSpawn);
void DeactivateSpawnsByTag(string sSpawnTag);
void DeactivateAllSpawns();
void DespawnChildren(object oSpawn);
void DespawnChildrenByTag(object oSpawn, string sSpawnTag);
//
//
void main()
{
// Retrieve Script Number
int nPatrolScript = GetLocalInt(OBJECT_SELF, “PatrolScript”);

// Retrieve Stop Information
int nStopNumber = GetLocalInt(OBJECT_SELF, "PR_NEXTSTOP");
object oStop = GetLocalObject(OBJECT_SELF, "PR_SN" + PadIntToString(nStopNumber, 2));

// Invalid Script
if (nPatrolScript == -1)
{
    return;
}

//
// Only Make Modifications Between These Lines
// -------------------------------------------

// Script 00
if (nPatrolScript == 0)
{
    ActionDoCommand(SpeakString("Example!"));
}
//

// Turn Off Lights
if (nPatrolScript == 7)
{
    object oLight = GetNearestObjectByTag("Light", oStop);
    if ((GetIsDay() == TRUE && GetPlaceableIllumination(oLight) == TRUE)
     || (GetIsNight() == TRUE && GetPlaceableIllumination(oLight) == FALSE))
    {
        ActionDoCommand(DoPlaceableObjectAction(oLight, PLACEABLE_ACTION_USE));
    }
}
//

// Trap-Setting Rogue
if (nPatrolScript == 1)
{
    object oTrap = GetNearestTrapToObject();
    if (oTrap == OBJECT_INVALID || GetDistanceToObject(oTrap) > 5.0)
    {
        // Create a Trap Kit
        object oTrapKit = CreateItemOnObject("NW_IT_TRAP001", OBJECT_SELF, 1);
        // Set Trap
        SignalEvent(GetModule(), EventActivateItem(oTrapKit, GetLocation(OBJECT_SELF)));
    }
}

//Sit 1
if (nPatrolScript == 3)
{
object oChair = GetObjectByTag(“ZEP_CHBOG_002”);
ActionMoveToObject(oChair);
ActionSit(oChair);
DelayCommand(81.0, ClearAllActions());
DelayCommand(82.0,AddPatrolStop(00,00));
DelayCommand(82.5,SetPatrolRoute(00));
DelayCommand(83.0,DoPatrolRoute(00,2));
}

//Sit 2
if (nPatrolScript == 2)
{
object oChair = GetObjectByTag(“BenchPew”);
ActionMoveToObject(oChair);
ActionSit(oChair);
DelayCommand(81.0, ClearAllActions());
DelayCommand(82.0,AddPatrolStop(02,00));
DelayCommand(82.5,SetPatrolRoute(02));
DelayCommand(83.0,DoPatrolRoute(02,3));
}
//Toilet
if (nPatrolScript == 6)
{
object oChair = GetObjectByTag(“ZEP_TOILET001”);
ActionMoveToObject(oChair);
ActionSit(oChair);
DelayCommand(81.0, ClearAllActions());
DelayCommand(82.0,AddPatrolStop(01,00));
DelayCommand(82.5,SetPatrolRoute(01));
DelayCommand(83.0,DoPatrolRoute(01,2));
}
//Fiddle with something
if (nPatrolScript == 4)
{
ActionPlayAnimation(ANIMATION_LOOPING_GET_MID,1.0,5.0);
}
//Open Door
if (nPatrolScript == 5)
{
object oNearestDoor = GetNearestObjectByTag(“x3_door_wood003”);
ActionMoveToObject(oNearestDoor);
ActionDoCommand(ClearAllActions());
if (GetIsOpen(oNearestDoor))ActionOpenDoor(oNearestDoor);
}
// -------------------------------------------
// Only Make Modifications Between These Lines
//

}

There’s really no reason not to use the “if” format. In nwscript it’s really a matter of scripter preference. Switches compile to pretty much the same code as if it was “if x else if y else …”. Don’t need to tell someone what they should or should not do here.

That said, the OP may want to invest in some else statements.

In that case you may well find the three scripting beginner’s ebooks (in pdf) that I have written to be of use. They go into a bit more depth than the lexicon but only cover a fraction of what the lexicon does. This one covers if statements amongst others. This one covers the operators and what they do. This last one covers such things as && (and), || (or) and ! (not).

Couple of other things you might find useful are the scripting FAQs in this submission (especially the newbie FAQ) and finally the lstk script generator.

@meaglyn I was thinking more along the lines of the switch() statement as it is the same variable being tested each time and it is tested more than 3 times.

TR

Back to your script. Which event are they used in?

TR

I know you meant the switch statement. It doesn’t matter. Switch compiles to basically the same thing as if else if else (in nwnscript at least). There’s nothing to be gained. And actually if you are doing more than a line or two it’s sometimes cleaner to use if because it’s more natural to use {} for scope there. I’d not try to add more new things to the OP’s scripting. Except adding the else statements which would actually have an effect on the execution…

yeah, i had actually planned on trying more with the if and else statements but was kind of letting is go cause it was working for me right now, but maybe this is my problem?

The script isn’t really being used in an event itself but gets called from heartbeat with the spawn_sample_hb script that comes with NESS

Hmm.

At a glance, the “if without elses” setup looks like it could let two events qualify at once, but the numbers’re all different, so that seems unlikely to be what’s going wrong here. This is a point in favor of using switches for stuff like this, too - switches’ll complain if you’re using the same number twice. A string of ifs won’t, it’ll just run both, and if-elses will just run the first and never do the second.

Try putting in debug messages. You’ve got the other events with the long delays, right? Maybe it’s a previous event interfering with the current one. Make that NPC tell you what it’s doing when it’s doing it.


And, since we’re pouring somewhat unrelated tips and tricks onto you while we’re at it - you could set up some int constants and use names instead of numbers to identify your patrol events. That lets you keep all the numbers in a row, so you can check easily whether there are any duplicates, and it allows you to change the numbers any way you want as you add more events to the list.

const int PATROL_EVENT_SET_TRAP = 1;
const int PATROL_EVENT_SIT_BENCH = 2;
const int PATROL_EVENT_SIT_CHAIR = 3;
const int PATROL_EVENT_FIDDLE_MISC = 4;
const int PATROL_EVENT_OPEN_DOOR = 5;
const int PATROL_EVENT_USE_TOILET = 6;
const int PATROL_EVENT_TURN_OFF_LIGHTS = 7;

Constants need to be declared outside of any voids, somewhere up near where you’ve got the #include line. You can use constants in any function in the script, but you can’t change them from within the script.


Fun with constants and debug messages:

const int PATROL_DEBUG_MSGS = TRUE;

// Sends a debug message if PATROL_DEBUG_MSGS is turned on in the script file.
void PatrolDebug(string sMsg);
void PatrolDebug(string sMsg)
{
object oPC = GetFirstPC();

if (PATROL_DEBUG_MSGS) SendMessageToPC(oPC, sMsg);
}

Lets you avoid having to edit your debug messages back out again, and gives you a single place to change how all your debug messages work, in case you want to make checking debug-messages multiplayer-compatible someday or make them able to be turned on from within a running module.


You could also add an abort check to your heartbeat script. As in, if your NPC is not supposed to do anything else anymore, set a “youaresupposedtodespawnnow”-variable on them, and check for that variable in your heartbeat script and don’t run the patrol events thing if it’s set, in case your heartbeat script is causing the second run-through.

1 Like