Jump to content


Photo

[COMPLETED] Script variable substitution: triggers and actions

For beta 0024

  • Please log in to reply
10 replies to this topic

#1 Ascension64

Ascension64
  • Modder
  • 5983 posts

Posted 17 May 2012 - 03:20 AM

Actions

356 Assign(S:Statement*,I:Type*ArgType,I:Local*)
356 AssignFromObject(S:Statement*,O:Object*,I:Type*ArgType,I:Local*)
Assigns a value determined by Statement of the type Type from ARGTYPE.IDS (INT integer, or STR string) to a local action block variable.
The general form of Statement is "prefix[params]".
If Object is specified, the value of Statement is determined relative to Object instead of the object that owns the script.
This action is ignored by prior Eval() actions.
The general form of Statement is "prefix[params]"
-"prefix" can be:
* 'c' - assigns a constant value; params is a number, e.g. c[1], or string, e.g. c[FOO]
* 'e' - assigns the value of an expression; params is an expression, e.g. e[6 + 7]; see 0x411B Eval() for the format of Expression
* 'id' - assigns the index of a IDS file value; params is file.value, e.g. id[EA.CHARMED]
* 's' - assigns the value of the stat specified of the current object; params is the stat name, e.g. s[LEVEL]
* 'tn' - assigns the value of a 2DA file value by coordinates; params is file.x.y, e.g. tn[IMPORT01.0.0]
* 'ts' - assigns the value of a 2DA file value by column and row name; params is file.column.row, e.g. tn[IMPORT01.ITEMS.1]
* 'v' - assigns the value of a variable; params is name.scope, e.g. [foo.GLOBAL]
-"params" values containing #<num> and @<num> are replaced by the integer and string values, respectively, stored in local action block variables of index "num".
Avoid using integer variables in expressions of string type. Avoid using string variables in expressions of integer type. The range of "num" is 0 to 24.

357 Eval(S:Expression*,I:Type*ArgType,I:Loc*)
Overwrites the (Loc)th argument of type Type from ARGTYPE.IDS (INT integer, or STR string) in the next action with the value returned by Expression.
To replace point X and point Y, use a Loc value of 4 and 5, respectively.
This action skips subsequent Assign() and Eval() actions when determining in which action to overwrite a value.
This action does not overwrite values of actions beyond ClearBlockVariables() actions.
-Expression is a math expression that can use the following symbols:
=+-*/%^()
min(x, y), max(x, y), avg(x, y)
ceil(x), floor(x), round(x)
abs(x)
reciprocal(x)
sqrt(x), pow(x, y)
log(x), log10(x)
sin(x), cos(x), tan(x), sinh(x), cosh(x), tanh(x), asin(x), acos(x), atan(x), atan2(x)
-Any text in Expression of form #<num> and @<num> is replaced by the integer and string values, respectively, stored in local action block variables of index "num".
Avoid using integer variables in expressions of string type. Avoid using string variables in expressions of integer type. The range of "num" is 0 to 24.

358 ClearBlockVariables()
Sets all action block integer variables to 0 and empties all actions block string variables.


Triggers

0x411A Assign(S:Statement*,I:Type*ArgType,I:Local*)
Assigns a value determined by Statement of the type Type from ARGTYPE.IDS (INT integer, or STR string) to a local trigger block variable.
The general form of Statement is "prefix[params]".
This trigger does not evaluate and does not count as a trigger in an OR() block.
-"prefix" can be:
* 'c' - assigns a constant value; params is a number, e.g. c[1], or string, e.g. c[FOO]
* 'e' - assigns the value of an expression; params is an expression, e.g. e[6 + 7]; see 0x411B Eval() for the format of Expression
* 'id' - assigns the index of a IDS file value; params is file.value, e.g. id[EA.CHARMED]
* 's' - assigns the value of the stat specified of the current object; params is the stat name, e.g. s[LEVEL]
* 'tn' - assigns the value of a 2DA file value by coordinates; params is file.x.y, e.g. tn[IMPORT01.0.0]
* 'ts' - assigns the value of a 2DA file value by column and row name; params is file.column.row, e.g. tn[IMPORT01.ITEMS.1]
* 'v' - assigns the value of a variable; params is name.scope, e.g. [foo.GLOBAL]
-"params" values containing #<num> and @<num> are replaced by the integer and string values, respectively, stored in local trigger block variables of index "num".
Avoid using integer variables in expressions of string type. Avoid using string variables in expressions of integer type. The range of "num" is 0 to 24.

0x411B Eval(S:Expression*,I:Type*ArgType,I:Loc*)
Overwrites the (Loc)th argument of type Type from ARGTYPE.IDS (INT integer, or STR string) in the next trigger with the value returned by Expression.
This trigger does not evaluate and does not count as a trigger in an OR() block.
This trigger does not overwrite values of the Assign(), NextTriggerObject() and OR() triggers.
The NextTriggerObject() trigger ignores this trigger.
-Expression is a math expression that can use the following symbols:
=+-*/%^()
min(x, y), max(x, y), avg(x, y)
ceil(x), floor(x), round(x)
abs(x)
reciprocal(x)
sqrt(x), pow(x, y)
log(x), log10(x)
sin(x), cos(x), tan(x), sinh(x), cosh(x), tanh(x), asin(x), acos(x), atan(x), atan2(x)
-Any text in Expression of form #<num> and @<num> is replaced by the integer and string values, respectively, stored in local trigger block variables of index "num".
Avoid using integer variables in expressions of string type. Avoid using string variables in expressions of integer type. The range of "num" is 0 to 24.

0x411C E(I:Num1*,I:Num2*)
0x411D GT(I:Num1*,I:Num2*)
0x411E LT(I:Num1*,I:Num2*)
Compares "Num1" to "Num2", where E is equals, GT is greater than, and LT is less than.
To make use of these triggers, the 0x411B Eval() trigger should be used prior to this trigger.


--------------
Retired Modder
Note: I do not respond to profile comments/personal messages in regards to troubleshooting my modifications. Please post on the public forums instead.

Baldur's Gate Trilogy-WeiDU and Mods
Throne of Bhaal Extender (TobEx)

Contributions: (NWN2) A Deathstalker (voice acting) - (IWD2) IWD2 NPC Project (soundset editing) - (Misc) SHS PC Soundsets (voice acting)
Legacy: (BG/Tutu/BGT) Beregost Crash Fixer 1.9 (18 Jul 10) - (BG2) Enable conversations with charmed/dominated creatures (18 Jul 10) - (BG2) Experience Corrections (18 Jul 10) - (Misc) Platform Conversion Utility RC2 (13 Feb 10)


#2 i30817

i30817
  • Member
  • 611 posts

Posted 17 May 2012 - 03:37 AM

Why the AssignFromObject; ActionOverride wouldn't work or just convenience?

type conversions from integer to string: could they be possible in the assigment since type mixing iin eval is not on the cards - it appears you don't want to support them in either of the cases?
tn[] and ts[] (which i assume is pure string) seem to lose a bit of usefulness if they can't be converted to ints too - but in their case it's dangerous since even numeric tables sometimes have "marker" values like *****; and there is no consistency. Could the assign trigger just ... fail and return false if a type conversion fails?

finally as a documentation aid, could the variables be have a *prefix* or maybe *suffix* so that they can be self documenting?

@1_PC_LEVEL or @PC_LEVEL_1 - ugly but better than comments everywhere.

edit: the above wouldn't be name checked though. Only the @1 part would be functional so if the writer typos the documenting suffix afterwards it would be ignored (but cause slight confusion).

This might be a bad idea if later on you resolve to add "real" named variables (because of all the typos in existing code then).

Edited by i30817, 17 May 2012 - 04:20 AM.


#3 Ascension64

Ascension64
  • Modder
  • 5983 posts

Posted 17 May 2012 - 05:09 AM

Why the AssignFromObject; ActionOverride wouldn't work or just convenience?

ActionOverride() forcefully clears all actions on the target object and adds the action to the tail of the action queue of the target object. The behaviour Assign() is OK but only if you want to store a block variable in the scope of the target object. AssignFromObject() will store a block variable in the scope of this object but using the target object as reference point. Slightly confusing, I know.

Example: bar.bcs
...
ActionOverride("foo", Assign("s[LEVEL]", INT, 1)) //puts foo's LEVEL into foo's local action block int var 1
AssignFromObject("s[LEVEL]", "foo", INT, 1) //puts foo's LEVEL into bar's local action block int var 1

On a different note, Eval() is useless with ActionOverride() because clear all actions will purge the block variables and then the Eval() doesn't know what to evaluate because it will be the only action in the action queue.
There is not much I can do about these behaviours because of the way ActionOverride() is implemented in vanilla.

type conversions from integer to string: could they be possible in the assigment since type mixing iin eval is not on the cards - it appears you don't want to support them in either of the cases?

They are already possible and TobEx will try to make a type conversion. Still, it's just bad practice to do this, so the preferred method is to avoid type conversion altogether.

tn[] and ts[] (which i assume is pure string) seem to lose a bit of usefulness if they can't be converted to ints too - but in their case it's dangerous since even numeric tables sometimes have "marker" values like *****; and there is no consistency. Could the assign trigger just ... fail and return false if a type conversion fails?

Well, a type conversion is made anyway if you use an argtype of INT when reading from 2DA tables (which are stored as CStrings), so ***** would be converted to a value of 0.

finally as a documentation aid, could the variables be have a *prefix* or maybe *suffix* so that they can be self documenting?

@1_PC_LEVEL or @PC_LEVEL_1 - ugly but better than comments everywhere.

Well, I don't expect modders to over-use assignments, so the Assign() statements should be fairly easily readable without necessarily needing comments at all.

This might be a bad idea if later on you resolve to add "real" named variables (because of all the typos in existing code then).

Nah, never.

Edited by Ascension64, 17 May 2012 - 05:15 AM.

--------------
Retired Modder
Note: I do not respond to profile comments/personal messages in regards to troubleshooting my modifications. Please post on the public forums instead.

Baldur's Gate Trilogy-WeiDU and Mods
Throne of Bhaal Extender (TobEx)

Contributions: (NWN2) A Deathstalker (voice acting) - (IWD2) IWD2 NPC Project (soundset editing) - (Misc) SHS PC Soundsets (voice acting)
Legacy: (BG/Tutu/BGT) Beregost Crash Fixer 1.9 (18 Jul 10) - (BG2) Enable conversations with charmed/dominated creatures (18 Jul 10) - (BG2) Experience Corrections (18 Jul 10) - (Misc) Platform Conversion Utility RC2 (13 Feb 10)


#4 i30817

i30817
  • Member
  • 611 posts

Posted 17 May 2012 - 05:19 AM

Well, a type conversion is made anyway if you use an argtype of INT when reading from 2DA tables (which are stored as CStrings), so ***** would be converted to a value of 0.


Is x<=0 going to be the "universal" way to test invalid values?

I know some use -1 too instead of *****.

Could the returned value be -1 then? I'm unsure if any table uses 0 as placeholder currently.
Failing the trigger is unambiguous, but semantically **** is the same as -1 on some tables and yet if **** failed and -1 passed it would be inconsistent.

So i'd rather ***** (or any other string value converted to int) return -1 all things considered

I know it won't cancel *, but it seems a way to test also the tables that use -1 as placeholder with the same pattern. I've never seen a table with negative numbers otherwise (maybe something about AC?).



edit: i suppose each table is a case. If it uses ***** safest would not to perform the conversion until you know that it's a valid value by comparing it to the "table null". If it uses 0 or -1 or ****, you read it and compare to the "null" and only go on then.
Annoying data inconsistency. I suppose it's unstandardizable since there are oddles of code using it.

Edited by i30817, 17 May 2012 - 02:43 PM.


#5 Ascension64

Ascension64
  • Modder
  • 5983 posts

Posted 18 May 2012 - 01:41 PM

I'm willing to wait and see how people use tn[] and ts[] before I modify anything as such. I suspect modders will primarily create their own new 2DA tables for Assign() use other than use existing ones (though L1NPCs would probably use XPLEVEL.2DA a lot).

--------------
Retired Modder
Note: I do not respond to profile comments/personal messages in regards to troubleshooting my modifications. Please post on the public forums instead.

Baldur's Gate Trilogy-WeiDU and Mods
Throne of Bhaal Extender (TobEx)

Contributions: (NWN2) A Deathstalker (voice acting) - (IWD2) IWD2 NPC Project (soundset editing) - (Misc) SHS PC Soundsets (voice acting)
Legacy: (BG/Tutu/BGT) Beregost Crash Fixer 1.9 (18 Jul 10) - (BG2) Enable conversations with charmed/dominated creatures (18 Jul 10) - (BG2) Experience Corrections (18 Jul 10) - (Misc) Platform Conversion Utility RC2 (13 Feb 10)


#6 i30817

i30817
  • Member
  • 611 posts

Posted 19 May 2012 - 03:58 AM

if you did the "suffix" thing couldn't you have your cake and eat it with mathpresso?

From it's wiki page variable code:
// Create variables that can be used in expression.
MathPresso::mreal_t variables[] = { 1, 2, 3 };
const char* names[] = { "x", "y", "z" };


So using @1_PC_Level, @1 gets parsed and decides where to put it on the variable array:
variables[0] = int_value;
variable 0 (1) changes name:
names[0] = "@1_PC_Level";

And mathpresso would freak out without you checking for it if the scripter later on used @1_PCLevel or another similar typo?
edit: you'd need to check it for strings though since they are not going to the evaluator.

That actionoverride thing is ... unfortunate. I had the idea that the variable arrays were static and shared to all objects (but i suppose it's a good idea to have them private to not always be reading things from files like 2da if you can avoid it). But even if it did not dup them, i guess the "ActionOverride needs to clear all array variables" (why?) would put a end to that.
But then again, if the variables of your current object are going to be regularly and inevitably cleared, why not make it static?

About Eval being useless with AO - that's bad news isn't it? Since AO is the only way to affect a creature from another script.

Edited by i30817, 19 May 2012 - 04:41 AM.


#7 Ascension64

Ascension64
  • Modder
  • 5983 posts

Posted 24 May 2012 - 12:07 AM

I don't use any variable features in MathPresso. All the @ and # substitutions I have parsed with my own code pre-Presso.

--------------
Retired Modder
Note: I do not respond to profile comments/personal messages in regards to troubleshooting my modifications. Please post on the public forums instead.

Baldur's Gate Trilogy-WeiDU and Mods
Throne of Bhaal Extender (TobEx)

Contributions: (NWN2) A Deathstalker (voice acting) - (IWD2) IWD2 NPC Project (soundset editing) - (Misc) SHS PC Soundsets (voice acting)
Legacy: (BG/Tutu/BGT) Beregost Crash Fixer 1.9 (18 Jul 10) - (BG2) Enable conversations with charmed/dominated creatures (18 Jul 10) - (BG2) Experience Corrections (18 Jul 10) - (Misc) Platform Conversion Utility RC2 (13 Feb 10)


#8 i30817

i30817
  • Member
  • 611 posts

Posted 25 May 2012 - 08:29 PM

I'm willing to wait and see how people use tn[] and ts[] before I modify anything as such. I suspect modders will primarily create their own new 2DA tables for Assign() use other than use existing ones (though L1NPCs would probably use XPLEVEL.2DA a lot).

Even in java, i tend to use the 'Maybe' functional pattern for initialization. You know, read this, but if you can't return this value i give as argument (though in the real Haskell Maybe, it's more like 'this computation').
This could work for ints, but probably not for strings (because of the "only one string" limitation).

Advantage being, reading and the 'null value' substitution could be generalized to 1 function for the user. Although that has the disadvantage of triggering for all failures, not only "can't parse this" (file deleted, file truncated etc) that are likely indication of other bugs.
edit: i forgot about no overloading... though it would be nice

Edited by i30817, 25 May 2012 - 08:37 PM.


#9 i30817

i30817
  • Member
  • 611 posts

Posted 03 June 2012 - 07:53 PM

Been looking at this: do stats give a way to get the object coordinates?

At first look it looks like
storepartylocations()

could do it, but that seems like a transfer to the "current" GAM file, not really the origin of the info (i assume the action exists because it gets out of date?)

Anyway, something to read the GAM file variables in triggers/actions would be nice. However, if that file/memory structure is only "actually" updated on savegame, pulling the values from the sources of that update would be best (for instance, not "StorePartyLocations()" and read from the GAM struture, but read directly from the where "StorePartyLocations" is reading.)

Also, for applications it would be nice to get not only partymembers locations but also other objects like clouds, traps, gold piles, etc.

Edited by i30817, 03 June 2012 - 08:14 PM.


#10 Ascension64

Ascension64
  • Modder
  • 5983 posts

Posted 05 June 2012 - 12:30 AM

No way to get coordinates yet. I plan to add some sort of special type based on an IDS file that will get some values you cannot normally get.

--------------
Retired Modder
Note: I do not respond to profile comments/personal messages in regards to troubleshooting my modifications. Please post on the public forums instead.

Baldur's Gate Trilogy-WeiDU and Mods
Throne of Bhaal Extender (TobEx)

Contributions: (NWN2) A Deathstalker (voice acting) - (IWD2) IWD2 NPC Project (soundset editing) - (Misc) SHS PC Soundsets (voice acting)
Legacy: (BG/Tutu/BGT) Beregost Crash Fixer 1.9 (18 Jul 10) - (BG2) Enable conversations with charmed/dominated creatures (18 Jul 10) - (BG2) Experience Corrections (18 Jul 10) - (Misc) Platform Conversion Utility RC2 (13 Feb 10)


#11 -qwerty123qwerty-

-qwerty123qwerty-
  • Guest

Posted 05 February 2014 - 02:49 PM

I'm trying wrap my head around these additions. What I want to do is

 

1) check if a creature has any pickpocketable items

2) if it does, pickpocket it

3) mark this creature as "pickpocketed" and don't try to repeat

 

I don't know in advance which creature is it going to be. So, I assume that I should save the creature's death variable in a local var and check it later. Is it possible with these actions/triggers? If so, could you point me in the right direction?

 

Thanks in advance.

 







Also tagged with one or more of these keywords: For beta 0024