I've got a somewhat buggy script

I’m currently betatesting my module and this script I’ve used in two areas. In one area it works like a charm, and in another area it’s been a bit buggy and I don’t know why. Maybe there is some way to write the code to get less bugs? This script fires in a conversation when the PC has left the companions and is about to rejoin them. There are 11 possible companions, so I made a script to check for each and every one of them in the area. The bug that appears is that on a few occasions only 2 of 3 companions that are actually in the area rejoins the companion instead of all 3. I don’t know why that is. Here is the code:

#include "andg_party"

void main()
{
    object oCompanion = GetFirstObjectInArea();
    
    object oGardok  = GetObjectByTag("gardok");
    object oValerie  = GetObjectByTag("valerie");
    object oElyia  = GetObjectByTag("elyia");
    object oTyn  = GetObjectByTag("tyn");
    object oSnofwin = GetObjectByTag("snofwin");
    object oMeah = GetObjectByTag("meah");
    object oAnelia = GetObjectByTag("anelia");
    object oSymeon  = GetObjectByTag("symeon");
    object oSandera  = GetObjectByTag("sandera");
    object oOra  = GetObjectByTag("ora");
    object oCelina = GetObjectByTag("celina2");
    
    while (GetIsObjectValid(oCompanion))
    {    
        if (oCompanion == oAnelia)
        {
        AddCompanionToParty("anelia");
        }
        else if (oCompanion == oGardok)
        {
            AddCompanionToParty("gardok");
        }    
        else if (oCompanion == oValerie)
        {
            AddCompanionToParty("valerie");
        }    
        else if (oCompanion == oElyia)
        {
            AddCompanionToParty("elyia");
        }
        
        else if (oCompanion == oTyn)
        {
            AddCompanionToParty("tyn");
        }    
        else if (oCompanion == oSnofwin)
        {
            AddCompanionToParty("snofwin");
        }    
        else if (oCompanion == oMeah)
        {
            AddCompanionToParty("meah");
        }
        else if (oCompanion == oCelina)
        {
            AddCompanionToParty("celina2");
        }    
        else if (oCompanion == oSymeon)
        {
            AddCompanionToParty("symeon");
        }    
        else if (oCompanion == oSandera)
        {
            AddCompanionToParty("sandera");
        }    
        else if (oCompanion == oOra)
        {
            AddCompanionToParty("ora");
        }
        oCompanion = GetNextObjectInArea();
    }
}

The include script is based on andysks and Colorsfade’s Comanion System.

Is it consistently the same companion that doesn’t get selected?

An alternative approach would be to use the GetArea call to check if a companion object is in the current area.

I’m not sure if it’s the same…I haven’t tested it enough to know that I’m afraid.

How do you propose I use the GetArea call in this case?

Do you mean like gc_area or something similar?

What is in this function? Show its code here.

Try this (it’s untested, but should compile). GetNearestObjectByTag should only get objects in the same area, so no need to cycle through every object in the area, which is probably why your script fails at times.

#include "andg_party"

void main()
{
	object oCompanion;
	
	oCompanion = GetNearestObjectByTag("gardok");
	if(GetIsObjectValid(oCompanion))
	{
		AddCompanionToParty("gardok");
	}
	
	oCompanion = GetNearestObjectByTag("valerie");
	if(GetIsObjectValid(oCompanion))
	{
		AddCompanionToParty("valerie");
	}
	
	//Repeat for all companions
	
}
3 Likes

Thanks @cuiilv ! That actually sounds like a good idea to try your take on it. Will try it out.

@Aqvilinus
Here is the function that is part of the include script:

void AddCompanionToParty(string sCompanionTag)
{
	/////////////////////////////////////////////////////
	// Make the companion visible on the Roster GUI
	/////////////////////////////////////////////////////
	SetIsRosterMemberCampaignNPC(sCompanionTag, 0);
	
	/////////////////////////////////////////////////////
	// Make the companion selectable on the Roster GUI
	// FROM: ga_roster_selectable
	/////////////////////////////////////////////////////
	SetIsRosterMemberSelectable(sCompanionTag, 1);
	
	/////////////////////////////////////////////////////
	// Add the companion to the party
	//FROM: ga_roster_party_add
	/////////////////////////////////////////////////////
	object oPC = GetFirstPC();
	AddRosterMemberToParty(sCompanionTag, oPC);

	/////////////////////////////////////////////////////
	// Just in case we forgot to set this, when we add a
	// companion to our party, we've met them. 
	/////////////////////////////////////////////////////
	SetLocalInt(oPC, "met_" + sCompanionTag, TRUE);

	/////////////////////////////////////////////////////
	// Set the companion's XP equal to that of the PC
	/////////////////////////////////////////////////////
	object oCompanion = GetObjectByTag(sCompanionTag);
	int nXP = GetPCAverageXP();
	SetXP(oCompanion, nXP);
	ForceRest(oCompanion);
}
1 Like

GetNearestObjectByTag vs GetObjectByTag should definitely help. Even though only those companions in the actual area return valid in this script … I believe GetObjectByTag is still searching the entire module for them and just flagging those in the same area as the PC. So GetNearestObjectByTag should make the whole process a lot less laggy. If your using NWNX4 maybe you can raise the TMI level as a test first (You can with NWNX2 in NWN, not sure about NWN2 though).

I’m not using NWNX4. To my understanding it doesn’t work with single player modules.

Here’s another sweet and simple way of doing it:

#include "andg_party"

void main()
{
	int n = 1;
    object oCompanion = GetNearestObject(OBJECT_TYPE_CREATURE, OBJECT_SELF, n);
    
    while (GetIsObjectValid(oCompanion))
	{
		if (GetIsRosterMember(oCompanion) && GetArea(OBJECT_SELF) == GetArea(oCompanion))
        {
			AddCompanionToParty(GetTag(oCompanion));
        }
		
		n++;
        oCompanion = GetNearestObject(OBJECT_TYPE_CREATURE, OBJECT_SELF, n);
    }
}

This, of course, is assuming you are Initializing companions or they are otherwise in the roster.

1 Like

@travus
GetNearestObject can only return objects in oTarget’s area (in your example oTarget is OBJECT_SELF). Only for these objects the concept of proximity is defined. The check GetArea(OBJECT_SELF) == GetArea(oCompanion) is redundant.

1 Like

Yeah, I wasn’t 100% sure on that so I left that there as a fail-safe.

Should be int n = 0; or it will miss the first creature.

@travus That was indeed a neat script. Yes, the companions are initialized with the Companion System, but I’ve experienced some few weird bugs with this (even if all the code looks rock solid) so…

I think I’ll go with the slightly more convoluted script from cuiilv just to be on the safe side.

Thanks everyone for all the help @travus @Aqvilinus @cuiilv @kalbaern @rjshae !

1 Like

ps. @travus
i’ve seen GetIsRosterMember() fail … unfortunately I forget how, but i don’t trust it anymore.

something about removing/re-adding a label from/to the roster

Another question that is somewhat related to this: I’ve often used GetObjectByTag but now I see that GetNearestObjectByTag is much better to use in some instances when you don’t want to check the whole module and just the area. In many of the stock scripts they use GetTarget instead. Maybe one should use that one? Looking at the code in ginc_param_const it seems that it is a bit safer to use than maybe the other two (or I could be wrong here)? It seems that that function covers more and even has GetNearestObjectByTag in its code…Any thoughts on this from you script wizards out there?

there is (relatively) a lot of overhead with GetTarget() but nobody notices

use what you’re comfortable with …