Script to spawn placeables at MULItiple waypoints

Is it even possible to have a script spawn the same placeable at multiple waypoints named the same, in the same area via a onenter trigger script?

Everything I have attempted so far only gets them to spawn at a single waypoint usually until the script errors

void main()
{
object oTarget;
object oSpawn;
object oArea;
object oWP;

//check that the entering creature is a PC or DM
object oEnterer = GetEnteringObject();
if (GetIsPC(oEnterer) && !GetIsDM(oEnterer))
SpeakString (“checked for entering object”);

{
object oTarget = GetFirstObjectInArea(oArea);
//while(oTarget != OBJECT_INVALID)

// Only look for objects that are Waypoints, if they are not, ignore and go on to the next object in the area
if(GetObjectType(oWP) == OBJECT_TYPE_WAYPOINT);
//SpeakString (“Looking for WPs”);
// {
// If we do find an object that is a Waypoint check the found wp tag
if (GetTag(oWP)== “wp_qst_arsa_bwmush”)
SpeakString(“You found the right WPs”);
// {
//spawns 12 times only
object oWP;
int nWP;
oWP = GetNextObjectInArea(oArea);
for (nWP=0; nWP<12; nWP++)

{

oSpawn= CreateObject(OBJECT_TYPE_PLACEABLE, “qst_arsa_bw_mshrm”, GetLocation(oTarget));
SpeakString (“spawning placeable at WPs”);

}

/> Blockquote

the SpeakString lines are only there to help me see what is happening. This seems to get me the closest as it spawns 12 , which is the number of waypoints in the area but they all spawn on the same waypoint

Sneaking in from the NWN1 section, so no guarantees for NWScript differences between NWN1 and NWN2, here, but - when things go wrong, try reading your code one line at a time and vocalize what’s happening in it. At the moment, not counting the confusion with the commented-out lines, it looks to me like you’re checking whether the first object in the area is a waypoint with a specific tag and, if it is, targeting the second object in the area and repeatedly spawning a placeable at it’s location.

Try it like this:

    object oTarget = GetFirstObjectInArea(oArea);
    while (oTarget != OBJECT_INVALID)
        {
        if (GetTag(oTarget) == "wp_qst_arsa_bwmush")
            {
            CreateObject(OBJECT_TYPE_PLACEABLE, "qst_arsa_bw_mshrm", GetLocation(oTarget));
            }

        oTarget = GetNextObjectInArea(oArea);
        }

As The Barbarian pointed out, your script creates 12 placeables at the same location: the first waypoint found with the correct tag.

Try this instead (assuming all twelve waypoints already exist in the area):
void main()
{
object oTarget;
object oSpawn;
object oWP;
//check that the entering creature is a PC or DM
object oEnterer = GetEnteringObject();
object oArea = GetArea(oEnterer); // only look for WP in the area oEnterer is in
if (GetIsPC(oEnterer) && !GetIsDM(oEnterer))
{
oTarget = GetFirstObjectInArea(oArea);
while(oTarget != OBJECT_INVALID)
// Only look for objects that are Waypoints, if they are not, ignore and go on to the next object in the area
{
if (GetObjectType(oWP) == OBJECT_TYPE_WAYPOINT)
{ // If we do find an object that is a Waypoint check the found wp tag
if (GetTag(oWP)== “wp_qst_arsa_bwmush”)
{ // then create the placeable
oSpawn= CreateObject(OBJECT_TYPE_PLACEABLE, “qst_arsa_bw_mshrm”, GetLocation(oTarget));
}
oTarget = GetNextObjectInArea(oArea); // whether it was the right object or not, we’re done with it. Let’s get to the next one
}
}
}
}

Sorry for the formatting, it looked OK before I hit the Reply button.

Your way is very cpu-intensive, and I am a little confused. It seems to me that there are some parentheses missing as well? But maybe you just didn’t write those in the code.

You have:

  object oTarget = GetFirstObjectInArea(oArea);
  while(oTarget != OBJECT_INVALID)
  {
    ...other stuff...
    oWP = GetNextObjectInArea(oArea);
   }

So, as long as there is at least one object in the area, oTarget is never invalid and the while loop keep running, and I don’t think that is what you want.

Anyway, you don’t have to cycle through all objects in the area, you can only look for waypoints with a specific tag and up to a certain number.

Here is the code:

   object oSpawn;
   int n=1;
   int max_iterations = 12;
   object oWP = GetNearestObjectByTag("your_tag",oEnterer,n);
	
	while(GetIsObjectValid(oWP) && n <= max_iterations)
	{
		n++;
        oSpawn= CreateObject(OBJECT_TYPE_PLACEABLE, “qst_arsa_bw_mshrm”, GetLocation(oWP));
		oWP = GetNearestObjectByTag("your_tag",oEnterer,n);
	}

Basically, GetNearestObjectByTag gets the n-th object with the given tag nearest to the target, only searching within the same area as the target. As you change n, it returns different objects.

Please let me know if it works for you!

The Barbarian . . . Tried that - they all spawn at the first Waypoint found

You sure? Did you try the exact code? Because I have something like that in my module (it spawns placeables at wapoints) and it works perfectly.

4760 … Tried that script … can’t test as DM , testing as player gives …OID:80000021,Tag:GenericTrigger, ERROR; TOO MANY INSTRUCTIONS

If you mean by that that you’d originally tried to do it like this, and it’d failed - Artemis caught another good one, there, which might explain what could’ve happened. Look at the code closely; within the while loop, you aren’t altering oTarget, you’re altering oWP.

If you started cycling through the objects in the area using oTarget

    oTarget = GetFirstObjectInArea(oArea);

… but continued cycling using oWP …

    oWP = GetNextObjectInArea(oArea);

… within a while loop whose condition is that oTarget must not be OBJECT_INVALID …

    while (oTarget != OBJECT_INVALID)
        {
        }

… then oTarget is never being altered, and the events within the loop aimed at oTarget always target the first object in the area, over and over again. F̵̮̥̘͉̻͡Ơ͕̯͉̮̺̜͡͞R̶̨̤̰̼̲̼E̜̻͡V̷̙͉͓̠͞Ę̲͈͎̯̺̙̳͎̥͟R̪͔̻̖̳̪̦͔



I’d suggest erasing your current script and starting anew, get rid of all the tangles in one go:

Declare the variables you will be using.

    object oArea = OBJECT_SELF;
    object oPC   = GetEnteringObject();
    object oTarget;

Place an abort check:

    // Abort at this point if the entering object is neither a PC nor a DM.
    if (!GetIsPC(oPC) && !GetIsDM(oPC))
        return;

Cycle through the objects in the area:

    oTarget = GetFirstObjectInArea(oArea);
    while (oTarget != OBJECT_INVALID)
        {
        // If the tag is a match...
        if (GetTag(oTarget) == "wp_qst_arsa_bwmush")
            {
            // ... spawn the placeable:
            CreateObject(OBJECT_TYPE_PLACEABLE, "qst_arsa_bw_mshrm", GetLocation(oTarget));
            }

        oTarget = GetNextObjectInArea(oArea);
        }

Also, sidenote: You’re gonna want to be aware that, at the moment, this will happen every time a player enters the area, so if you only want these placeables to be spawned once, you’ll need to put in a “Have we done this already?”-abort check of some sort.

Artemis - - - THANK YOU . . this works for PCs at least, though not for DMs to trigger

TheBarbarian - - - at this point yes I know it will happen every time they enter … plan is to link it to a journal quest check once I figure out how to spawn the placeables so then it would only spawn if the PC has the correct variable of the quest. I was told that in trying to learn scripting one gets one part to work before moving to the next part …

TheBarbarian - Tried the script as you have it written ----- still spawns the placeables at the first WP found until it spawns so many it errors.

1 Like

Weird. Weird, and interesting. I’ll chalk it down to NWN1/NWN2 scripting differences, for lack of an explanation. Fingers crossed somebody else knows what’s going on there.

I think that GetIsPC returns TRUE if the PC is a DM, so if you want both DMs and PCs to trigger the script you only want GetIsPC, while your code only returns TRUE for actual players.

1 Like

I figured it out: when a new object is created, it’s added at the top of the list. Which means that the GetNextObjectInArea call will come back to just the waypoint we were dealing with!
The solution: call GetNextObjectInArea twice, to skip the current object.

1 Like

Working on the principle that you want a working script, if you can’t get this one to work you obviously have a real problem somewhere. This solution does require you to do some work though. The first thing you will need to do is to go through all your waypoints and change their tags (not their names) as follows. You will need to give each one a name and a number. So for example you could call all your waypoint’s tags “WP_Tag” but number them like “WP_Tag01”, “WP_Tag02”…“WP_Tag12”. Yes this is a little bit of extra work but when coupled with this tiny script

const sBaseWPTag = "WP_Tag"

string PadNumber(string sNumToPad, int iSize = 2)
{
	while(GetStringLength(sNumToPad) < iSize)
		sNumToPad = "0" + sNumToPad;
	
	return sNumToPad;
}

void main()
{
	int iIndex;
	string sThisWayPoint;
	object oDummy;
	
	for(iIndex = 1 ; iIndex <= 12 ; iIndex++)
	{
		sThisWayPoint = sBaseWPTag + PadNumber(iIndex);
		oDummy = CreateObject(OBJECT_TYPE_PLACEABLE, “qst_arsa_bw_mshrm”, GetLocation(GetObjectByTag(sThisWayPoint)));
	}
}

should be a slam-dunk to actually work. Note - the numbers must be 2 digits in length. So for numbers less than 10 you must pad them with a leading 0 like in the examples above.

To get that script into your module, delete what you currently have then copy and paste the script in its entirety into your module.

TR

1 Like

Apart from the oversight oWP vs oTarget which TheBarbarian already noticed, there was the fact that creating an object inside a loop checking for all objects added a new object to the list. The problem was it was added at the top of the list, not at the bottom which cause the current object oTarget to fall down one rank in the list, and GetNextObjectInArea retrieved… the same object!

Try with this script (and this time I ran it to make sure it works!)

void main()
{
	object oEnterer = GetEnteringObject();
	object oArea = GetArea(oEnterer); // only look for WP in the area oEnterer is in
	object oTarget = GetFirstObjectInArea(oArea);
	object oSpawn;
	int n = GetLocalInt(OBJECT_SELF, "Done");
	if (n != 0) return;
	//check that the entering creature is a PC or DM
	while (oTarget != OBJECT_INVALID)
	{	// Only look for objects that are Waypoints, if they are not, ignore and go on to the next object in the area
		if (GetObjectType(oTarget) == OBJECT_TYPE_WAYPOINT)
		{ // If we do find an object that is a Waypoint check the found wp tag
			if (GetTag(oTarget)== "wp_qst_arsa_bwmush")
			{ // then create the placeable
				n++;
				oSpawn= CreateObject(OBJECT_TYPE_PLACEABLE, "qst_arsa_bw_mshrm", GetLocation(oTarget),FALSE, "placeable_" + IntToString(n));
				oTarget = GetNextObjectInArea(oArea); // we just added a new object at the top of the list, so let's move one rank down or we'll come back to the current oTarget!
			}
		}
		oTarget = GetNextObjectInArea(oArea); // whether it was the right object or not, we’re done with it. Let’s get to the next one
	}
	SetLocalInt(OBJECT_SELF, "Done", n);
}

Note the comment " //check that the entering creature is a PC or DM"
I removed this part of the script, you’ll have to add it again. And I also added a check to have the trigger add the placeables once only, you’ll need to remove the SetLocalInt(OBJECT_SELF, “Done”, n); line to prevent this.

1 Like

4760 . . . THANKS . . this works well too

Here is another way that would work:

void main()
{
	object oPC = GetEnteringObject();
	object oTarget;
	int n;
		
	if ((GetIsPC(oPC) || GetIsDM(oPC) || GetIsDMPossessed(oPC)) && !GetLocalInt(OBJECT_SELF, "DoOnce")) 
	{
		SetLocalInt(OBJECT_SELF, "DoOnce", TRUE);
		for (n = 0; n < 13; n++)
		{
			oTarget = GetNearestObjectByTag("wp_qst_arsa_bwmush", OBJECT_SELF, n);
			CreateObject(OBJECT_TYPE_PLACEABLE, "qst_arsa_bw_mshrm", GetLocation(oTarget));
		} 
	}
}

Put your 12 waypoints using the same tag then use the following code snippet to spawn the placeables :

int n = 0;
object oTarget = GetObjectByTag("wp_qst_arsa_bwmush");
while (GetIsObjectValid(oTarget))
{
    CreateObject(OBJECT_TYPE_PLACEABLE, "qst_arsa_bw_mshrm", GetLocation(oTarget));
    n++;
    oTarget = GetObjectByTag("wp_qst_arsa_bwmush", n);
}

The script loops through the 12 waypoints. Notice the ,n that changes everything! n is incremented at each iteration of course.
The beauty is that the script is totally dynamic, you can add/remove waypoints at will … Besides it does not scan all objects of the area.

If you pass the tag of the waypoints and the SResRef of the placeable as paramateres to the script you can make it generic.

2 Likes