Ga_local_int (bug!?)

I had the rare need where I tried to pass =-1 (to set the value to -1) as a value using ga_local_int.

As an example, in the comments, it says, “=-2” (Set to -2).

In my own tests, however, this does not appear to be the case. All it does, is simply do the same thing as passing -1 to the original var. So, for example, when the var is currently 2, and I want to set it to -1, I should be able to use the =-1 to set it. However, it sets it to 1.

Can anyone else confirm or deny this activity, in case I have done something wrong?

that function is just a very pompus/snoobish wrapper for :
SetLocalInt(object,string,int) wicht works perfectly I use it all the time. So why you don’ t have your value ?

Always the same cause, always the the same trouble, all the problems are in the object where your store it.

Be sure the object where your store your value is the same object where you want to fetch it. And check also that the wraper around SetLocalInt(object,string,int) does correctly its job.

1 Like

@Shallina

I am familiar with the default SetLocalInt function, but this wrapper conversation option does normally allow for some useful uses for this variable handling. :slight_smile: (Although, in this case, I am currently using my own function for the purpose here, as this does not appear to do what it says.) I.e. what function do you use when comparing ints in a conversation?

Having tested some more, I am 100% sure the object is correct, as debug confirms. (I will keep testing some more in case I missed something though.)

The point is, this is otherwise a very handy conversation function, which I am sure many would use without possibly noticing this error, especially as it is a side of the function that may be rarely required.

P.S. I suspect the error is somewhere in the CalcNewIntValue function handling, where it fails to have the “=” recognise the negative value as requiring replacement.

1 Like

SetLocalInt(object,string,int) works perfectly
GetLocalInt(object,string) works perfectly everything turns around those 2 function

The ga_ calls the SetLocalInt the gc calls the GetLocalInt.

It can be super powerfull when used beceause you can dissociate several object/actions while using the same script for them.

But you need to be sure that the object where you set or get the value are the “good one”.

ga_setlocal_value_int

void main(string sTag,string sVariable,int iValue){

object oObjectStoredIn = GetObjectByTag(sTag);
SetLocalInt(oObjectStoredIn,sVariable,iValue);

}

int gc_getlocal_value_int

int StartingConditional(string sTag,string sVariable){

object oObjectStoredIn = GetObjectByTag(sTag);
int iValue = GetLocalInt(oObjectStoredIn,sVariable);
return iValue;

}

Those are the same functions without the wrappers. If it doesn’t work the answer is always the same :

you don’t fetch the same object with

object oObjectStoredIn = GetObjectByTag(sTag);

or sVariable hasn’t the same value

The object where you store the value in and the variable you use to name your data have to be the same with the Get function and Set function.

You give me an “Int” but ask someone else the “int” you gave me. That other person won’t give you your “int” back. You need to ask me !

I didn’t use NWN2 default wrapers for storing value, i wrote mine which are usaully super “short and dumb”

SetLocalInt(OBJECT_SELF,sVar,iValue);
int myValue = GetLocalInt(OBJECT_SELF,sVar);

I store the value in OBJECT_SELF, the Area GetArea(oObject) or the module GetModule()

If you use GlobalInt to access it everywhere you need to be sure the different modules are set correctly in the same campaign.

SetCampaignInt are absolute value that be fetch from one campaign to an other on 2 different gamplay session save and on. They are permanent and tied to the computer where they are saved.

SetGlobalInt are tied to the campaign and the save . the are “normal global variable”

SetLocalInt are tied to the object where you save them.

if the object where you store them is deletted or “killed” then destroyed, you won’t be able to access that object and will only get a 0.

Completly unrelated :

I used the trick you found for launching a movie with an XML file so it doesn 't have to be at the begining or the end of a mod, it works perfectly.

1 Like

@Shallina

Thanks! :+1:

Thank you for your detailed response, however, you may wish to consider …

I am aware of how to use the Set/Get Local Int/String/Float/Object variables. I also use Set/Get Global and Set/Get Campaign equivalents throughout my coding. I must have used them many times in my modules. :slight_smile: I even wrote a tutorial trying to explain how to use them for others.

But, I do like to store local variables locally, wherever possible, and not just by using OBJECT_SELF, module or area. Sometimes, I like to use the function to alter a variable on another target, or check on another target … sometimes because I have to.

Now, most of the times, the wrapper function ga_local_int works flawlessly, except (as I post here) in this rather unusual usage of it, when I am trying to set a particular negative value. In all my years of coding, this has never been required (as far as I recall - and in a conversation), but I have just encountered this for the first time, and pass it along as information to others who may also sometimes like to check for negative variables on a local object. E.g. I like to set an int to -1 rather than zero sometimes.

In this particular situation, I had a variable that could be in either “1”, “2” or “0” and when totally finished, “-1”. Therefore, when jumping from a local variable setting that may be “2” using the function to set to “-1”, it failed … it did not do what it said it would do. (It set 2 to 1 instead.)

For this kind of requirement, moving forward, I will use this edited version of the function …

See TRAVUS script below for fixed version.

The reason why I like to use the ga_local_int in other conversation situations, is because it handles additions and subtractions on a value very well (rather than set absolutes), which is very handy when handling conversation nodes. E.g. Passing ++ or - - is a very easy way to add or subtract from the current integer value.

The problem must be with the wraper to be more specific it lies with CalcNewIntValue

that function doesn 't take the “-” sign as negative value but as substract value.

check ginc_var_ops the default wrappers don’t set the value it computes it.

if you want it to work, you need to complete CalcNewIntValue in ginc_var_ops

1 Like

@Shallina

Yes, you may have missed my earler edited post … :slight_smile:

In this case, I think that would be more trouble than its worth. :wink:

But, at least I have drawn it to attention now. :slight_smile:

Try this new ga_local_int script:

#include "ginc_param_const"
#include "ginc_var_ops"

void main(string sVariable, string sChange, string sTarget)
{
    object oTarg = GetTarget(sTarget, TARGET_OBJECT_SELF);
	int nOldValue = GetLocalInt(oTarg, sVariable);
	int nNewValue;
	
	if (GetStringLeft(sChange, 1) == "=")
	{
		string s = GetStringRight(sChange, GetStringLength(sChange) - 1);
		nNewValue = StringToInt(s);
	}		
	
	else nNewValue = CalcNewIntValue(nOldValue, sChange);

	SetLocalInt(oTarg, sVariable, nNewValue);
}
2 Likes

@travus

That looks better. :slight_smile: (Will test, but looks good to me. UPDATE: As suspected, works fine!)

I guess this will end up a new campaign script then. :slight_smile:

In this case, your fix to the official script is better than having to use a workaround script.

Thanks!

1 Like

For the record, the ga_global_int script requires the same fix.

@Akhacha @travus @Lance_Botelle

the bug propagates from ginc_var_ops

struct ChangeComponents GetChangeComponents(string sChange, int bDefaultIsDelta);
	string sOperator = GetStringLeft(sChange, 1);

	if (sOperator == "=" || sOperator == "+" || sOperator == "-")
	{
		sChange = GetStringRight(sChange, GetStringLength(sChange) - 1);

		string sChar2 = GetStringLeft(sChange, 1);
		if (sChar2 == "+" || sChar2 == "-" || sChar2 == "") // handle ++, --, +, -
			sChange = "1";
	}
	else // doesn't start with operator
	{
		if (bDefaultIsDelta)
			sOperator = "+";
		else
			sOperator = "=";
	}

ie. If the first char is “=” then look at the second char, and if that is “-”
then the code treats the operator as “–” (decrement by 1)

So here’s what i came up with over the last few days →

// private.
// Returns the operator and value from 'sChange'.
// - bDefaultIsDelta : TRUE to treat 'sChange' as a stringified integer
//   (addition or subtraction shall be applied as appropriate); FALSE to let the
//   function parse 'sChange' in accord with the
// rules
// to set a value
//   do not use "+" to set positive values (unless preceeded by "=")
//   do not use "-" to set negative values (unless preceeded by "=")
//   use "=" or "=+" for positive values and "=-" for negative values
// to set a positive value
//   input an integer as a string (do not use a prefix other than "=" or "=+")
//   Eg "5" "=5" "=+5"
// to set a negative value
//   input an integer as a string prefixed by "="
//   Eg "=-5"
// to add a value
//   use prefix "+"
//   Eg "+5"
// to subtract a value
//   use prefix "-"
//   Eg "-5"
// to increment a value
//   use "++" or "+" (without a string integer)
//   Eg "++" "+"
// to decrement a value
//   use "--" or "-" (without a string integer)
//   Eg "--" "-"
// In short positive values can be set with (eg) "5" but setting negative values
// REQUIRES a preceeding "=" because "-" specifies a subtraction.
struct ChangeComponents GetChangeComponents(string sChange, int bDefaultIsDelta)
{
	string sOperator = "";	// note that only "+" and "-" get handled by CalcNewIntValue()
							// any other value of 'sOperator' results in a SET operation

	if (bDefaultIsDelta)
	{
		sOperator = "+";
	}
	else if (sChange == "") // reset the original variable
	{
		sChange = "0";
	}
	else if (sChange == "+" || sChange == "-" || sChange == "++" || sChange == "--")
	{
		sOperator = GetStringLeft(sChange, 1);
		sChange = "1";
	}
	else
	{
		string sFirst = GetStringLeft(sChange, 1);
		if (sFirst == "=" || sFirst == "+" || sFirst == "-")
		{
			sOperator = sFirst;
			sChange = GetStringRight(sChange, GetStringLength(sChange) - 1);
		}
		// else do nothing - eg "5" - results in assignment
	}

	struct ChangeComponents xChange;
							xChange.sOperator = sOperator;
							xChange.sValue    = sChange;
	return xChange;
}

 
and here is the stock version of

int CalcNewIntValue(int nOldValue, string sChange, int bDefaultIsDelta = FALSE)
{
	struct ChangeComponents xChange = GetChangeComponents(sChange, bDefaultIsDelta);

	int nValue = StringToInt(xChange.sValue);

	int nNewValue;
	if (xChange.sOperator == "+")
	{
		nNewValue = nOldValue + nValue;
	}
	else if (xChange.sOperator == "-")
	{
		nNewValue = nOldValue - nValue;
	}
	else // if not + or - then just set the value
	{
		nNewValue = nValue;
	}

	return nNewValue;
}

 
CalcNewIntValue() appears to be called by
ga_gint_max.NSS
ga_gint_min.NSS
ga_global_int.nss
ga_henchman_setmax.NSS
ga_local_int.nss

My intent is to fix GetChangeComponents() and add it to Nwn2Fixes (with recompiled scripts).

? I believe the only functional change has been to fix the “=-5” SET operation … other than that, it’s just a refactor – and perhaps an improvement on the bDefaultIsDelta parameter (which is unused in the stock scripts)

3 Likes

@kevL_s

I remain somewhat clueless with respect to structs. :dizzy_face:

Yes, to confirm, it is (for example) if we passed =-5 (with the equals) that is is to set to -5 rather than subtract 5 from the existing variable.

EG:

VAR VAL 7: If we pass -5, VAR VAL should now be 2.
VAR VAL 7: If we pass =-5, VAR VAL should now be -5.

VAR VAL 7: If we pass +5, VAR VAL should now be 12.
VAR VAL 7: If we pass =9, VAR VAL should now be 9.

structs are not as hard as you think, Lance.

first define the struct with its internal variables

struct ChangeComponents
{
	string sOperator;
	string sValue;
};

then to use it in a function, first declare the struct as a variable

	struct ChangeComponents xChange;

and assign values to its subvariables

	xChange.sOperator = sOperator;
	xChange.sValue    = sChange;

 
structs can be passed into functions as parameters, and returned from functions

A struct is used as a variable that contains subvariables.

A good conceptual example is

struct CoinPurse
{
	int iGold;
	int iSilver;
	int iCopper;
};

(a struct’s variables are accessed with the dot operator)

Notice how GetChangeComponents() actually returns 2 separate variables by encapsulating them in a struct.

there are other ways to do it, and personally i find that structs are seldom if ever the best way … unless one wants to group together and pass around complex but related info. For example in the core AI →

// this is the structure that is used to store information about a potential spell to cast
struct SpellInfo
{
	talent   tTalent;

	int      spellLevel;
	int      spellInfo;
	int      spellID;
	int      otherID;
	int      castingInfo;

	int      shape;
	int      range;
	float    radius;

	object   oTarget;
	location lTargetLoc;
};

That way you can declare a (struct)variable, initialize all those subvariables, and pass all that data around as a single variable. I’m sure you could glean specifics about its syntax from stock scripts here & there …

(sry I’m not up to writing a comprehensive struct tutorial atm, but im sure you get the gist)

 
 

yep, that’s how it should work,

2 Likes

It’s me who should apologise, as you have tried to explain this to me before, and each time I think I understand, only to forget the next time I see one. :flushed: I still even have an example from before … :sweat_smile:

I am trying to formulate the language I need to use in my head that helps me to remember it.

I think this is what I need to work on in my head… like before, the more I see it, the more I guess I 'll be one step closer. So, again, thanks for helping each time I ask. :+1:

2 Likes

well, the best way to learn something is to have a use for it, and like i intimated, i think that that’s unlikely :)

2 Likes

@kevL_s

Yes, I believe you are absolutely correct. :grin:

If I have not ever used it in all these years, then I guess I shall just have to remain content with the bits I do know and can work with already. :thinking: :+1:

P.S. I think I do kind of “get it”… and reckon it is something I’d eventually grasp. The key phrase is “subvariable”, which I believe suggests exactly that … variables within variables determined by additional variable names within a main variable name … and determined by the “.” bit.

1 Like