Jump to content


Photo

[WIP] Expanded Triggers


  • Please log in to reply
88 replies to this topic

#41 Miloch

Miloch

    Barbarian

  • Modder
  • 6579 posts

Posted 22 March 2011 - 08:46 AM

Is this the component you are using such a thing for?

Optional: Joinable NPCs more closely match the PCs experience
NPCs, upon joining, will have their XP total adjusted to something closer to the PC's. This component is new to the last beta, and I don't much like the implementation at this point. It'll be revised, eventually.

Yes.

Infinity Engine Contributions
Aurora * BG1 NPC * BG1 Fixpack * Haiass * Infinity Animations * Level 1 NPCs * P5Tweaks
PnP Free Action * Thrown Hammers * Unique Containers * BG:EE * BGII:EE * IWD:EE
================================================================
Player & Modder Resources
BAM Batcher * Creature Lister * Creature Checker * Creature Fixer * Tutu/BGT Area Map & List * Tutu Mod List
================================================================
"Infinity turns out to be the opposite of what people say it is. It is not 'that which has nothing beyond itself' that is infinite, but 'that which always has something beyond itself'." -Aristotle


#42 Ascension64

Ascension64
  • Modder
  • 5983 posts

Posted 24 March 2011 - 01:01 PM

OK, I think the best long term solution is to allow variable substitution to some extent, because it will enable much more than a L1NPCs-specific feature. That will obviously take a while to implement, so so long as you are willing to wait a bit then all is good?

--------------
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)


#43 Miloch

Miloch

    Barbarian

  • Modder
  • 6579 posts

Posted 24 March 2011 - 05:45 PM

OK, I think the best long term solution is to allow variable substitution to some extent, because it will enable much more than a L1NPCs-specific feature. That will obviously take a while to implement, so so long as you are willing to wait a bit then all is good?

I personally didn't need it right away, and I've only heard one person asking about it (recently anyway). I liked the idea of a sort of ActualXP trigger, because it would allow a simple script substitution if TobEx were detected (vs. the old way if not). But I suppose variable substitution would be more flexible and could make the script a lot shorter (offhand I can't envision how such a script would look but I guess that's for the future to sort out). Hey, if it takes long enough, maybe even Nythrun will rear her head and code it in, saving me from doing it :).

Infinity Engine Contributions
Aurora * BG1 NPC * BG1 Fixpack * Haiass * Infinity Animations * Level 1 NPCs * P5Tweaks
PnP Free Action * Thrown Hammers * Unique Containers * BG:EE * BGII:EE * IWD:EE
================================================================
Player & Modder Resources
BAM Batcher * Creature Lister * Creature Checker * Creature Fixer * Tutu/BGT Area Map & List * Tutu Mod List
================================================================
"Infinity turns out to be the opposite of what people say it is. It is not 'that which has nothing beyond itself' that is infinite, but 'that which always has something beyond itself'." -Aristotle


#44 Ascension64

Ascension64
  • Modder
  • 5983 posts

Posted 24 April 2012 - 11:19 PM

I've started work on some variable substitutions in script. Here are some starts with a few triggers:

0x411A Eval1Str(S:Name*,S:Scope*,I:Mode*TrigEval)
0x411A Eval1Int(I:Mode*TrigEval,I:StatNum*Stats)
0x411B Eval2Str(S:Name*,S:Scope*,I:Mode*TrigEval)
0x411B Eval2Int(I:Mode*TrigEval,I:StatNum*Stats)
Modifies a value in the next trigger depending on the Mode specified based on TRIGEVAL.IDS
This trigger does not evaluate and does not count as a trigger in an OR() block
This trigger does not count as a trigger modified by Eval*()
Use NextTriggerObject() to modify the target to derive the value from

TRIGEVAL.IDS
0 VAR
Modify target value with the value of a variable named "Name" of scope "Scope"

1 STAT
Modify target value with the value of the the stat at index "StatNum"

2 2DA
Modify target value with a value inside a table contained in a 2DA file
Usage:
-"Name" is treated as "<2DA filename>.<column name>.<row name>"
-"Scope" is unused. Note that you can theoretically specify "<2DA filename>." here with "Name" as "<column name>.<row name>". This is discouraged, as certain compilers such as NearInfinity do not compile this correctly.
Example:
-Eval1Str("HPPRS.ROLLS.1","",2DA) will modify the first integer argument of the next valid trigger with the value contained at column "ROLLS", row "1" in "HPPRS.2DA".


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, Eval*() triggers should be used prior to this trigger

Here is a test script I have been mucking around with:
IF
	HotKey©
THEN
	RESPONSE #100
		SetGlobal("test","GLOBAL",100)
		SetGlobal("test2","GLOBAL",100)
		SetGlobal("test3","GLOBAL",3)
END

IF
	HotKey(D)
THEN
	RESPONSE #100
		SetGlobal("test","GLOBAL",200)
		SetGlobal("test2","GLOBAL",100)
		SetGlobal("test3","GLOBAL",0)
END

IF
	HotKey(E)
THEN
	RESPONSE #100
		SetGlobal("test","GLOBAL",100)
		SetGlobal("test2","GLOBAL",200)
		SetGlobal("test3","GLOBAL",1)
END

IF
	Eval1Str("test","GLOBAL",VAR)
	Eval2Str("test2","GLOBAL",VAR)
	E(0,1) //it doesn't matter what these values are, they will always be overridden
THEN
	RESPONSE #100 //should only work if I press C or as soon as I give a cre the script for the first time in a game
		DisplayStringHead(Myself,1) // No, I'm sorry, none of them sound familiar.
		SetGlobal("test","GLOBAL",1000)
		SetGlobal("test2","GLOBAL",1001) //stops it from firing forever
		Continue()
END

IF
	Eval1Int(STAT,XP)
	GlobalLT("test","GLOBAL",0) //does test < XP
THEN
	RESPONSE #100 //should be true in all cases
		DisplayStringHead(Myself,2) // You played Elminster?
		SetGlobal("test","GLOBAL",10000000) //stops it from firing forever
		Continue()
END

IF
	Eval1Str("test3","GLOBAL",VAR)
	Eval2Str("HPPRS.ROLLS.1","",2DA) //in ToB, the value of this is 1
	E(0,0)
THEN
	RESPONSE #100 //should only be true if I press E
		DisplayStringHead(Myself,3) // Uh, the yugoloth, was it? Yeah, you stole the show with that one, if I recall.
		SetGlobal("test3","GLOBAL",0) //stops it from firing forever
END
There is potential to add a lot more Modes to evaluate, especially things that cannot usually be accessed with existing triggers. This also opens up a lot more opportunity to be able to compare numbers beyond what is already available in existing triggers. For example, you can theoretically use these new triggers to compare two variables, but that already exists. However, you might want to compare one stat with another stat (e.g. am I strong enough to beat this AC value character?).

I'm thinking about how exactly to add arithmetic, but no bright ideas yet. Not sure how I can modify actions yet either, but we will see.

Edited by Ascension64, 24 April 2012 - 11:29 PM.

--------------
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)


#45 i30817

i30817
  • Member
  • 611 posts

Posted 25 April 2012 - 08:57 PM

What's the difficulty with actions? Just that you don't know the right offsets yet? (i assume you could do it for triggers due to your work for TriggerOverride)

Or is it the "cycle" delay (not knowing which actions will get triggered on each frame, potentially misordering the assigments)? Can't you just add a action with the same name that does the same thing as the trigger (but to the action function offsets of course, so you don't misorder?

Edited by i30817, 25 April 2012 - 09:12 PM.


#46 Ascension64

Ascension64
  • Modder
  • 5983 posts

Posted 25 April 2012 - 11:47 PM

I just need to check how I can directly advance the action to the next action in the list without waiting an AI update, etc. Shouldn't be too hard, just need to look at the code.

--------------
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)


#47 i30817

i30817
  • Member
  • 611 posts

Posted 26 April 2012 - 05:33 PM

It's impossible to do OR without a additional block?
As in:


IF
		OR(2)
		Eval1Str("test","GLOBAL",VAR)
		Eval2Str("test2","GLOBAL",VAR)
		EQ(_,_)
		Eval1Str("test","GLOBAL",VAR)
		Eval2Str("test3","GLOBAL",VAR)
		EQ(_,_)

if not, if we do instead:
IF
		Eval1Str("test","GLOBAL",VAR)
		Eval2Str("test2","GLOBAL",VAR)
		EQ(_,_)
....

IF
	    !EQ(_,_)
		Eval1Str("test","GLOBAL",VAR)
		Eval2Str("test3","GLOBAL",VAR)
		EQ(_,_)
would this work? Or would it need to replicate the EvalStr before using !EQ in the second block?

Lol, the horrible contortions that BGScript original design is going through.

Edited by i30817, 26 April 2012 - 05:40 PM.


#48 Ascension64

Ascension64
  • Modder
  • 5983 posts

Posted 28 April 2012 - 04:44 AM

The top one would work fine.
I've done some more work on this. I have figured out to add the actions, but I should settle on some things in concrete with the triggers first.

How obfuscated is this code?
IF
	Eval1("HPCLASS","",0,1,2DA_STR_SET) //HPCLASS is a 2DA table with strings, let's try and substitute that string is the next 2DA table to look up and get a value from it
	Eval2("","",-10,0,INT_ADD)
	Eval2("LEVEL","",0,0,STAT_SET)
	Eval1("<Eval>","",1,0,2DA_INT_SET) //var1 = (HPCLASS[0.1])[1.(STAT.LEVEL - 10)]; note that this processes all the evaluators above
	Eval2("test","GLOBAL",0,0,VAR_SET) //var2 = "GLOBALtest"
	E(-33,-33) //ends up evaluating to E(1,0) on vanilla+TobEx installation
THEN
	RESPONSE #100
		DisplayStringHead(Myself,1) // No, I'm sorry, none of them sound familiar.
		SetGlobal("test","GLOBAL",0)
		Continue()
END

Edited by Ascension64, 28 April 2012 - 04:46 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)


#49 Ascension64

Ascension64
  • Modder
  • 5983 posts

Posted 28 April 2012 - 05:31 AM

Current implementation. I hacked the internal script compiler (used for dialogues) so that it recognises the (S,S,I,I,I) trigger construction, making it easier to implement the Eval*() triggers without overloading as much.

0x411A Eval1(S:Name*,S:Scope*,I:Num1*,I:Num2*,I:Mode*Evaluate)
0x411B Eval2(S:Name*,S:Scope*,I:Num1*,I:Num2*,I:Mode*Evaluate)
Modifies a value in the next trigger depending on the Mode specified based on EVALUATE.IDS
This trigger does not evaluate and does not count as a trigger in an OR() block
This trigger does not count as a trigger modified by Eval*() unless one of the 2DA_* modes is used
Use NextTriggerObject() to modify the target to derive the value from

EVALUATE.IDS
Specifies the possible types and modes for the Eval*() triggers
Opcodes are separated into the upper word (Type) and lower word (Mode)

Types
0x0000 *_SET
0x0001 *_ADD
0x0002 *_MUL
0x0004 *_DIV
0x0008 *_MOD
0x0010 *_PC

Modes
1 INT_*
Modify target value with a constant integer "Num1"

2 VAR_*
Modify target value with the value of a variable named "Name" of scope "Scope"

3 STAT_*
Modify target value with the value of the stat specified in "Name"

4 2DA_INT_*
Modify target value with the integer value located at column index "Num1", row index "Num2" of the 2DA file specified in "Name"
"Name", "Num1" and "Num2" can be modified by previous Eval*() triggers

5 STR_SET
Modify target value with a constant string "Name"

6 2DA_STR_SET
Modify target value with the string value located at column index "Num1", row index "Num2" of the 2DA file specified in "Name"
"Name", "Num1" and "Num2" can be modified by previous Eval*() triggers

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, Eval*() triggers should be used prior to this trigger


Edited by Ascension64, 28 April 2012 - 05:32 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)


#50 i30817

i30817
  • Member
  • 611 posts

Posted 28 April 2012 - 09:54 PM

I don't really like the modes idea, and i'd prefer overloaded versions for each... I'm used to that kind of programming, not argument modifiers.

Unless there is a reason you can't (or would prefer to keep) have many more triggers or actions.

A person that should give his input is certainly the_bigg. He's already helped a lot with the TriggerOverride syntax translation (and if that idea is applicable here, one less argument would help a lot).

As for arithmetic, can't you do a "simple" infix to postfix string evaluator?
http://scriptasylum....stfix/index.htm

I guess the problem is variables right? Unless you find a way to bind them to a number or representing the number assignment itself on the expression, it wouldn't be very useful.

Ordering doesn't seem to be enough for more complex expressions.

Can't you hack the second string argument to bind to a variable name instead of a scope? Let the scope be expressed in the function name or as a suffix to the first string?

ReadGlobal("blah", "X", VAR)
ReadLocal("bleh", "Y", VAR)
Eval("X*2 + 3")
Eval("X^3 + Y")
E(_,_)

?

I confess i'm confused about your last example though, and might have lost the plot.
(in the example above, it would be nice to reset the assignments at the end of the block - or if not possible, after the E(), GT() or LT() ).

Keeping the arithmetic functions hidden behind the string form should give you more latitude to add more simple functions like max(), log, % etc, and should allow you to use one of the hundreds of math expression evaluator libraries - so you don't even have to do that.

What about the write operation back to the globals or locals - or stats -? The equality operators won't allow that (but that would be actions i forgot)
Comparing stats/globals/locals is already very good though.

Edited by i30817, 28 April 2012 - 10:48 PM.


#51 Avenger_teambg

Avenger_teambg
  • Member
  • 604 posts

Posted 28 April 2012 - 11:11 PM

I haven't seen the current implementation yet. I just know, GemRB stores the script lines in static objects (so caching them is easy and efficient).
If its possible, modify some local variables in the script context, do not hack the script itself. You can store the variable inside the script or its runner, but do not modify loaded script lines.
Avenger

#52 Ascension64

Ascension64
  • Modder
  • 5983 posts

Posted 28 April 2012 - 11:21 PM

Your idea is way better. Ideally, I should be able to find a platform-independent math expression parser that is relatively easy to use and has the standard operators + variable support. Any ideas? I am having a look at MathPresso and muParserSSE at the moment - both use jit compiler, but I'm not all that familiar with whether it would work on Wine Mac and Linux.

I haven't seen the current implementation yet. I just know, GemRB stores the script lines in static objects (so caching them is easy and efficient).
If its possible, modify some local variables in the script context, do not hack the script itself. You can store the variable inside the script or its runner, but do not modify loaded script lines.

I don't intend to modify the script directly. I have changed the way that the triggers are evaluated such that a copy of the trigger is evaluated rather than the triggers themselves. This allows me to modify the copy in any way using local vars. By the way, does GemRB recognise invalid trigger constructions which would spout Unknown trigger errors in the internal dialogue script parser? The constructions are:

(S,I,I)
(S,I,I,I)
(S,O,I,I)
(S,O,I,I,I)
(S,S,O)


Edited by Ascension64, 28 April 2012 - 11:25 PM.

--------------
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)


#53 i30817

i30817
  • Member
  • 611 posts

Posted 28 April 2012 - 11:45 PM

Just to reiterate if my last post was confusing, i'd much prefer new ReadXXXX/WriteXXXX functions than arguments.

If i've read the arguments right:

ReadGlobal("blah", "X", VAR)
ReadLocal("bleh", "Y")
ReadStat("STR", "Z")
Read2DA("HPCLASS", 0, 1, "N") etc.

And write forms.

Calculation engines... there is this .NET thing: http://ncalc.codeplex.com/
but i'm not sure if it's pure c++ (or if it's too much... dates?).
edit: nope, it's C#

As for wine, there is no reason not to try (unless it's just a wrapper over a javascript script engine - that would be bad).

Edited by i30817, 28 April 2012 - 11:54 PM.


#54 i30817

i30817
  • Member
  • 611 posts

Posted 29 April 2012 - 12:04 AM

Oh, btw, weren't there some strange datatypes stored in LOCALS or GLOBALS in BGScript? Like radians or floats? It's not all ints right?

Edit: P:Point

[x.y].

edit: I'm still not clear if it's going to be possible to use arguments from LOCALS or GLOBALS in functions that don't expect it.
You're only doing it for E() LT() and GT() for now (or ever)?

For instance,
CheckStat(O:Object*,I:Value*,I:StatNum*Stats)
will be able to check a stat against a value in that map (as it can't now, without WEIDU unrolling all possible values?)

If it could be made general - though i suppose this would interfere with GemRB caching Avenger warned about above?

Edited by i30817, 29 April 2012 - 12:26 AM.


#55 Ascension64

Ascension64
  • Modder
  • 5983 posts

Posted 29 April 2012 - 01:30 AM

This is really tricky. If I were to create a separate trigger to read anything, I would keep creating triggers until my eyes popped out. With a single type-based system, I only need to implement the code for the type of data we want to fetch.

Say for example, if I wanted to read all these things...
  • The global variable DOG
  • The value at 2DA table CAT.2DA at point (NearestEnemyOf(Myself).LEVEL + 1).(3)
  • The STAT on Myself NUMBEROFATTACKS
  • The current game time
  • The number of charges on the item Myself has currently equipped on ability number 2
  • How many copies of MAGIC_MISSILE I have got left
...I would rather use only a fixed number of trigger opcodes to achieve all of these. This is perfectly fine if I don't have to do any arithmetic. For arithmetic, I would have to store all of this stuff in some local variables, parse some math expression (substituting those variables), and spitting out a result.

Strings are a separate story and are fairly easy because it is easy enough to evaluate a string.

Variables are int and int only. P:Point* is expanded into two ints x and y. Note, triggers don't even have a point struct member, only actions do.

The bottom line is sticking to only what is practicable. I am not willing or interest in putting heaps of work into making an 'optimal' solution where you can evaluate some crazy expression like sin(sqrt(7) * log(e) 56). TobEx itself isn't optimal. Optimal solutions should be implemented by BG:EE and co. Hence, I would actually like to hear from people about how exactly they would use variable substitution if it was implemented in any way they please. I need to get a good idea of how complex people will actually use the variable substitution system.

I'll start with a really simple AI example that ignores any scripting conventions for the time being:
//if MyLevel + 6 <= EnemyLevel, runaway 75% of the time
IF
  OR(2)
	Eval1(Myself, STATS.LEVEL + 6)
	Eval2(NearestEnemyOf(), STATS.LEVEL)
	LT(%1, %2)

	Eval1(Myself, STATS.LEVEL + 6)
	Eval2(NearestEnemyOf(), STATS.LEVEL)
	E(%1, %2)
THEN
  RESPONSE #75
	RunAwayFrom(NearestEnemyOf(),90)
  RESPONSE #25
	AttackReevaluate(NearestEnemyOf(),90)
END

Edited by Ascension64, 29 April 2012 - 01:33 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)


#56 i30817

i30817
  • Member
  • 611 posts

Posted 29 April 2012 - 02:24 AM

IF
		ReadStat(Myself, STATS.LEVEL, "MyLevel")
		ReadStat(NearestEnemyOf(), STATS.LEVEL, "EnemyLevel")
		Eval("MyLevel + 6")
		Eval("EnemyLevel")
  OR(2)
		LT(%1, %2)
		E(%1, %2)
THEN
  RESPONSE #75
		RunAwayFrom(NearestEnemyOf(),90)
  RESPONSE #25
		AttackReevaluate(NearestEnemyOf(),90)
END

The crazy expressions should be handled for you by the mathematical evaluator library you choose (if you can bind variables). I suggested you separate the assignment and evaluation and use (unlike your example above), because
1: you might want to use the same variable more than once in 2 expressions (or even the calculated expression)
2: it's easier to read if you do want a expression (granted, it's harder if you don't)

Do you think you can do something that "intercepts any" next trigger (except or or trigger override of course), and replace the first int, the second int etc? It would probably be ideal (not sure if strings arguments would be useful in this context), so people could use E() or CheckStat or whatever.
It also would remove (most) need of write functions, if you can just SetGlobal and derived value (but not sure about stats).

It would probably be useful to make warnings for more Evals than there are arguments in the next one (and to clear the variables binded at the end of the block, so no funny business happens in the next one).

If you can hack the compiler, i suggest making it accept either a placeholder (such as _) or a positional argument like in your example; so that you can match the Eval(s) completely to a trigger and warn if there are less or more than the placeholders.
I never took a compiler class, so i better shut up now.

edit: i mentioned the point because they'd be another kind of "next argument" that would have to be handled specifically if you wanted to allow the triggers and actions that use that argument with a calculated value.

edit 2: you might want to add a trigger that evaluates a boolean expression so you can write this instead of OR, E, LT and GT
Bool would be something that just returns bgscript true for 1 and false for 0 (or whatever the evaluator returns for booleans).
IF
		ReadStat(Myself, STATS.LEVEL, "MyLevel")
		ReadStat(NearestEnemyOf(), STATS.LEVEL, "EnemyLevel")
		Eval("MyLevel + 6 <= EnemyLevel")
		Bool(%1)
THEN
  RESPONSE #75
		RunAwayFrom(NearestEnemyOf(),90)
  RESPONSE #25
		AttackReevaluate(NearestEnemyOf(),90)
END

Edited by i30817, 29 April 2012 - 02:58 AM.


#57 Ascension64

Ascension64
  • Modder
  • 5983 posts

Posted 29 April 2012 - 02:35 AM

Do you think you can do something that "intercepts any" next trigger (except or or trigger override of course), and replace the first int, the second int etc? It would probably be ideal (not sure if strings would be useful in this context), so people could use E() or CheckStat or whatever.

That would destroy the next trigger. It would take extensive hackery to make a copy of the entire trigger block. So, I cannot do something like this:
IF
  Assign("STATS.LEVEL", Myself, 1, ) //I can't use more than one string in any trigger
  Assign("STATS.LEVEL", NearestEnemyOf(), 2)	
  Eval("%1 + 6 <= %2")
THEN...
What I could possibly do with this is to parse the expression first before passing it to a math expression library, so all the variable substitutions are made before the jit compiler does its work. The assigns will have to map variables to a numerical index (which was something suggested by Galactygon).

It would probably be useful to make warnings for more Evals than there are arguments in the next one (and to clear the variables binded at the end of the block, so no funny business happens in the next one).

No, you will get a warning every single AI update for a trigger!

If you can hack the compiler, i suggest making it accept either a placeholder (such as _) or a positional argument like in your example; so that you can match the Eval(s) completely to a trigger and warn if there are less or more than the placeholders.

I don't want to hack the compiler. What I do should be completely compatible with the existing GemRB, NearInfinity, and WeiDU implementations of the compiler.

I never took a compiler class, so i better shut up now.

I never took a class. :)

Edited by Ascension64, 29 April 2012 - 02:43 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)


#58 i30817

i30817
  • Member
  • 611 posts

Posted 29 April 2012 - 03:10 AM

Could explain with small words why would it destroy the next trigger? Is it because you're piggybacking on that trigger hidden arguments or something? I know from checking the source of TobEx that there's only one large trigger function.

Edit: oh only one string argument per trigger. So they have to be positional, not nominal to allow reading from maps. Bummer.

But why doesn't this work?
IF
  Assign("STATS.LEVEL", Myself) //deleted the number... unless it's critical to reassign?
  Assign("STATS.LEVEL", NearestEnemyOf())  
  Eval("%1 + 6 <= %2")
THEN...
If you indeed replace the variables (as you'd have to if you used nominal ones anyway).
Please don't use the % symbol (it's a math operator).

#1, #2 etc should work ok ( i don't think the cardinal set symbol will be used).
or even 'at'
@1, @2
either is a good metaphor

Edited by i30817, 29 April 2012 - 04:01 AM.


#59 i30817

i30817
  • Member
  • 611 posts

Posted 29 April 2012 - 03:35 AM

BTW,

It seems that you want to use Eval as returning conditional expressions specially?
Because then you'd can't distinguish between failure due to syntax/runtime/divby0 error in the expression and due to the expression returning false (since it's going to be jit-ed or interpreted at runtime).

I'm also wary of how you'd use a int expression later on, unless you want to treat eval like another Assignment? Eg:
IF
  Assign("STATS.LEVEL", Myself) //saves #1
  Eval("1 < 2")//returns true saves #2 (1)
  Eval("6+ #1")//not boolean only returns false if the expression failed (it won't), saves #3
  E(#3, 4)
THEN...

My examples above were using nominal variables and positional Eval expression results. And i was not counting on Eval returning anything except if the expression was parsed and computed successfully or not (and the have the possibility of using the result in a later trigger).

I don't think it's a good idea to make Eval return anything except expression parsing/compilation success or failure (don't overload the return). And that it's needed to save the result of eval expressions, not just variables, if they are going to be used in triggers.
I'm not sure what is more confusing, to use the same indexed positions ordering for expressions or to use another indexation
(like #1,#2 for variables and €1, €2 for expressions or something).

Edited by i30817, 29 April 2012 - 03:55 AM.


#60 Ascension64

Ascension64
  • Modder
  • 5983 posts

Posted 29 April 2012 - 04:31 AM

Look, the code examples I give are not to be taken as exactly how I plan to implement things. I use them only to illustrate a principle. Once we get the principles right, we can then think about the actual implementation. So, symbols this, symbols that, whatever. Let's just sort out the general idea.

Could explain with small words why would it destroy the next trigger?

I have to pass the entire Trigger to the EvaluateTrigger() function. In order to do this, I have to replace the values in the Trigger struct with those I want to use to determine if the Trigger returns TRUE or FALSE. At the moment, I get away with permanently defacing the Trigger struct 't' by making a 'tCopy' and directly modifying the elements of 'tCopy'.
If I am in a trigger block of 3 triggers, of which trigger 1 and 2 substitute some of the variables in trigger 3, I cannot program so that when the engine is looking at trigger 1, it searches ahead for trigger 3 and replaces some values in it. That would destroy trigger 3 and breaks the GemRB static caching system.

Another problem scenario is the one I showed before using the current local copy implementation. The problem is that I want to evaluate a specific 2DA table of 2DA tables, and then use the sub-table to extract the value of a specific evaluated coordinate.
It might look something like this... (let's just say I'm using # to specify something to be evaluated)
IF
  AssignString("HPCLASS",0,1,/*#1*/1) //#1 = HPCLASS x0 y1
  AssignNum("STAT.LEVEL", Myself, /*#2*/2) //#2 = Myself.STAT.LEVEL
  AssignEval("#2 - 10", /*#3*/3) //#3 = #2 - 10 = Myself.STAT.LEVEL - 10
  Eval1("#1[1.#3]") //replace int1 with #1 x1 y#3 = replace int1 with <some table> x1 y(Myself.STAT.LEVEl-10)
  AssignNum("GLOBALtest", /*#4*/4) //#4 = the value of the GLOBAL variable 'test'
  Eval2("#4") // replace int2 with #4 = replace int2 with the value of the GLOBAL variable 'test'
  E(-33,-33) //int1 and int2 get replaced from the above; note: I'm just using a -33 placeholder here, since I have to put some numbers in those places that get substituted
THEN
		RESPONSE #100
				DisplayStringHead(Myself,1) // No, I'm sorry, none of them sound familiar.
				SetGlobal("test","GLOBAL",0)
				Continue()
END
Looks just as messy as the original code above.

Some rules about the compiler and IDS file:
-I am not allowed to use the same trigger name, so I can't have Assign(I:Num1*) and Assign(I:Num1, I:Num2*). This completely confuses the compiler and the engine
-I am not allowed to use the use more than one string (I said this earlier)
-I am not allowed to use one trigger construction to do two different things if I decide to use the same opcode for it, e.g. I have 0x411A AssignString(S:Name*,I:Num1*,I:Num2*) and 0x411A AssignNum(S:Name*,I:Num1*,I:Num2*). The compiler and the engine cannot tell the difference between these two because it looks up by opcode only. The only way around this for the same opcode, is to specify a MODE in one of the arguments. The alternative is to use different opcodes, which I believe makes things way too complex

Edited by Ascension64, 29 April 2012 - 04:41 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)