Custom GetFirst/GetNext functions/pass-by-reference?

TL;DR: Is there any way in the Aurora scripting system to pass function arguments – other than objects – by reference rather than by value? Or write a custom GetFirst/GetNext function pair?

The built-in GetFirstThing/GetNextThing functions use internal per-user context storage to keep track of where they are in the list, with the context initially set up by the GetFirst call. In normal coding, this is re-entrant and can cause problems. (Think about a GetNext loop that calls a function that does a separate GetFirst/GetNext loop on the same object. I haven’t tried it, but I wouldn’t be surprised if it messed up.)

One avoidance approach is to make the caller responsible for keeping the context, rather than having it stored internally by the functions. So GetFirst sets it up and returns it, and that context variable gets passed to each related GetNext call, which updates it. This requires pass-by-reference semantics, but other than objects I haven’t found any way to use PBR. Is there one?

No. There are no pointers in nwscript. There is an example of using a struct for the state in the tokenizer code. See x0_i0_stringlib.nss. The structure is passed around by value, of course. With the string tokenizer though you essentially have a list of the values and can index it. For other things you’d probably need to make such a list first probably using the built in routines :slight_smile:

It’s generally not that difficult not to nest the GetFirst/Next* routines.

Yes if you nest GetFirst/Next functions you can mess them up and trigger endless loop, however only very few of them share the same internal “pointer”. If any at all, I don’t think I ran into this problem before other than using same GetFirst/Next function inside loop of that function. And even if you need to do this, it can be workarounded by using a delay.

Actually I ran into this problem with custom trap scripts. Applying effect EffectDamage without delay will mess up GetFirst/Next functions. But again, just add delay to effect application and this won’t happen. It seems Bioware knew about this since all the spellscripts uses delaycommand when applying damage.

Yeah, the common one is creating an object in the area, or item in inventory, while looping same.

DelayCommand is your friend.

Interesting. Which First/Nexts? OTOH are you sure the delay isn’t there to sync the moment of damage with spell impact VFX? Instant hit spells, like ray of frost don’t have any delays.

1 Like

I suppose I could do something like this (substitute whatever for Effect):

object GetEffectStart(object oidTarget=OBJECT_SELF);
effect GetEffectNext(object oidContext);

object xContext = GetEffectStart(OBJECT_SELF);
effect eVFX     = GetEffectNext(xContext);
while (GetIsEffectValid(eVFX)) {
    /*
     * Do something with the effect
     */
    effect = GetEffectNext(xContext);
}

and store index information in variables on the xContext object returned by GetEffectStart().

I’m thinking of this for regular placement of actions (visual effects) or placeables, such as:

// Self-explanatory, except fFacing. -1.0f means face toward the
// centre, -2.0f means face away from the centre, and any positive
// value has the usual meaning.
object StartRingOfThings(location lCentre, float fRadius, int iThings=3, float fFacing=-1.0f);
// Get the next location based on the context information.  Once
// the last valid location has been returned, subsequent calls have
// the area of the returned location set to OBJECT_INVALID -- because
// there is no GetIsLocationValid() function.
location GetNextThingLocation(object xContext);

object xContext = StartRingOfThings(GetLocation(OBJECT_SELF), 5.0f);
location lThing = GetNextThingLocation(xContext);
while (GetIsObjectValid(GetAreaFromLocation(lThing))) {
    /*
     * Do something at the location.
     */
    lThing = GetNextThingLocation(xContext);
}

Or you could use a struct but what index information would xContext hold? If it’s just the nNth argument or something (like the effect you returned last time) you will be doing n^2 operations when you could do n. Otherwise, you need to build a pseudo-array analogue into which you can index.

Also, if you are just doing them in single scripts like that, just use the existing ones, delay any creation of new effects or objects. Done. :slight_smile:

Notice I gave no actual implementation details. :slightly_smiling_face: It’s entirely need-dependent. For the RingOfThings example, the actual locations could be precalculated by the StartRingOfThings() and stored in the context object with SetLocalLocation(), and GetNext just indexes through them, or is implemented with the index as a parameter like GetObjectByTag(). However, if the things being iterated cannot be pre-calculated, and/or have no builtin SetLocal* type, the context variable may be more complex.