You must be logged in to post messages.
Please login or register

AI, RMS Scripting, and Modding
Moderated by Yeebaagooon, TAG

Hop to:    
Welcome! You are not logged in. Please Login or Register.18 replies
Age of Mythology Heaven » Forums » AI, RMS Scripting, and Modding » Trigger Breakthrough - Global Variables
Bottom
Topic Subject:Trigger Breakthrough - Global Variables
TwentyOneScore
Mortal
posted 18 March 2003 11:19 PM EDT (US)         
Have you ever wished you could have a global variable that you can reference between triggers? I've figured out a way to trick the trigger code generator into defining global variables. If you've been following the other recent discoveries of mine, you'll know that it's possible to have user defined conditions & effects. Using this same methodology, I created two new effects that declare a start and end block for global declarations. These effects can only be used ONCE per scenario and should be used in the very first trigger - so they are at the top of the trigger code. Here is the code; 1 condition and 3 effects. Put them in the typetest.xml file in the proper places.


<Condition name="xsUserCondition">
<Param name="MyString" dispName="$$99420$$Code" varType="string">true</Param>
<Expression>(%MyString%)</Expression>
</Condition>

<Effect name="$$99420$$xsGlobalsStart">
<Command>}</Command>
<Command>}</Command>
</Effect>

<Effect name="$$99420$$xsGlobalsEnd">
<Command>rule _DeclareGlobalsEnd</Command>
<Command>inactive</Command>
<Command>{</Command>
<Command>if (1==1) {</Command>
</Effect>

<Effect name="$$99420$$xsUserCode">
<Param name="MyString" dispName="Code" VarType="stringid"></Param>
<Command>/**/ %MyString%</Command>
</Effect>

How to use it:
1) Create a new scenario and place some units for players 1 & 2 so the game don't end quickly.
2) Add a trigger, make it inactive and leave the default settings for Conditions.
3) Go to the trigger effects and change the first effect to xsGlobalsStart.
4) Insert a second effect and set it to xsUserCode and in the code field put int MyGlobal=1;
5) Insert a third effect and set it to xsGlobalsEnd.
6) Create a second trigger, loop it, and put it on a 2 second timer.
7) Add a Message effect for the new trigger with the text equal MyGlobal is now = "+MyGlobal+"!! (exactly as shown with the quotes!).
8) Add a second effect and set it to xsUserCode and in the code field put MyGlobal++;

Playtest the scenario and you'll see a message box that displays your global variable and increments it each time it is displayed. Sweet. Now let's add a user condition using our new global:

9) Add a third trigger to your scenario, loop it and put it on a 2 second timer.
10) Add a condition and set it to xsUserCondition and in the code field put (MyGlobal%%2)==0 ** % = Mod function
11) Add an effect and set it to GrantResources, food, player 1, and in the Amount put MyGlobal ** You cannot type alpha characters into the Amount box, but you can paste them, so cut & paste the value in.

Playtest the scenario and you'll see a message box that displays your global variable and increments it each time it is displayed. But now, every other time the message box displays, you will get MyGlobal food added to player 1.

Now to get to the nitty gritty and how it works behind the scenes. If you set up the triggers as I have indicated you will generate the following code in the trigtemp.xs file:

01 void main(void)
02 {
03 }
04
05 rule _Trigger_0
06 minInterval 4
07 inactive
08 {
09 bool bVar0 = (true);
10 if (bVar0)
11 {
12 }
13 }
14 /**/ int MyGlobal=1;
15 rule _DeclareGlobalsEnd
16 inactive
17 {
18 if (1==1) {
19 xsDisableRule("_Trigger_0");
20 trEcho("Trigger disabling rule Trigger_0");
21 }
22 }
23
24 rule _Trigger_1
25 minInterval 4
26 active
27 {
28 bool bVar0 = ((trTime()-cActivationTime) >= 2);
29 if (bVar0)
30 {
31 trMessageSetText("MyGlobal is now = "+MyGlobal+"!!", 3000);
32 /**/ MyGlobal++;
33 xsDisableRule("_Trigger_1");
34 trEcho("Trigger disabling rule Trigger_1");
35 trDelayedRuleActivation("_Trigger_1");
36 }
37 }
38
39 rule _Trigger_2
40 minInterval 4
41 active
42 {
43 bool bVar0 = (((MyGlobal%2)==0));
44 bool bVar1 = ((trTime()-cActivationTime) >= 2);
45 if (bVar0 && bVar1)
46 {
47 trPlayerGrantResources(1, "Food", MyGlobal);
48 xsDisableRule("_Trigger_2");
49 trEcho("Trigger disabling rule Trigger_2");
50 trDelayedRuleActivation("_Trigger_2");
51 }
52 }

The xsGlobalsStart effect simply inserts lines 12 & 13, which tricks the generator into ending it's definition of _Trigger_0. Then we can insert our globals (and global functions!!) using the xsUserCode effect. And finally the xsGlobalsEnd effect adds lines 15-18, which create a new rule definition and if statement to catch the closing brackets from _Trigger_0 that the generator is still waiting to close. It basically creates two dummy rules that do nothing with the global code in between. I haven't tried it but you should be able to put entire blocks of code, including global functions, in a line at a time using the xsUserCode effect.

Neat Stuff! I hope I explained it well, if not ask questions. It's definately advanced stuff and you can easily break your triggers with no easy way to debug them, but for the skilled user this is a major advancement and worth looking into.


"Just invite him over for dinner. Turn him from an enemy into a friend. Then when he's least expecting, BAM! The old fork in the eye!" - M.S.
--
Member °ƒ Tsunami Studios <<< Play TOS Maps - 9 Maps - Avg Rating: 4.67! >>>

[This message has been edited by TwentyOneScore (edited 03-18-2003 @ 11:31 PM).]

AuthorReplies:
Elpea
Hal
(id: lp_usa)
posted 19 March 2003 00:52 AM EDT (US)     1 / 18       
i love you

great! , tks


Matei
Mortal
posted 19 March 2003 08:04 AM EDT (US)     2 / 18       
Very clever , too bad it can't be used in RMS though. This makes triggers pretty much as powerful as VC files. The great part is it transfers over the internet.

Programmer on 0 A.D., author of Norse Wars, co-author of Fort Wars.
TwentyOneScore
Mortal
posted 19 March 2003 09:10 AM EDT (US)     3 / 18       
>too bad it can't be used in RMS though.

Yeah, especially since I'm an RMS developer! I might have to make my latest map a scenario since it is heavily hand coded. But now that I have a way to do this outside the VC file it's worth doing. It took me three days to find someone to help test that RMS because of the VC file d/l so I don't think that it's a viable way of doing triggers for multiplay... I need to join up with a good design group so I have access to good testers & experienced developers, but who would have me?


"Just invite him over for dinner. Turn him from an enemy into a friend. Then when he's least expecting, BAM! The old fork in the eye!" - M.S.
--
Member °ƒ Tsunami Studios <<< Play TOS Maps - 9 Maps - Avg Rating: 4.67! >>>
The King of Cheese
Mortal
posted 19 March 2003 10:26 AM EDT (US)     4 / 18       
These discoveries will take scen design to a whole new level have you posted about them in Scen Design?

  • >> LKS_Cheeselord >>
  • Proud member of LKS. Click here to join.
  • Member of Arcane Studios, DGDN, and LKSware
  • PROJECTS: Magic Rugs - Cheese Giant - Anim Editor
  • Fear the power of CHEESE!
  • TwentyOneScore
    Mortal
    posted 19 March 2003 11:00 AM EDT (US)     5 / 18       
    No, seeing as the techniques are very code intensive, I figured this forum is the right place as it is be more "programmer" based and I did'nt want to double post. I did post it in the Scency Design forum at AOMC since they don't have a similar scripting forum.

    I have a few more trigger conditions that I haven't tested out yet, but will likely release tonight. One is "Player Is Human" & the other is "Player On Team" - used for determining if the player is a computer or human and the other to check which team they are on.


    "Just invite him over for dinner. Turn him from an enemy into a friend. Then when he's least expecting, BAM! The old fork in the eye!" - M.S.
    --
    Member °ƒ Tsunami Studios <<< Play TOS Maps - 9 Maps - Avg Rating: 4.67! >>>
    TwentyOneScore
    Mortal
    posted 19 March 2003 01:22 PM EDT (US)     6 / 18       
    Ok, as an add on to this thread, here are 3 more useful conditions. I think it is apparent what they do from the name. I'm pretty sure that the Player Exists In Game condition is different from the existing Player Active condition.


    <Condition name="Player Human">
    <Param name="Player" dispName="$$22301$$Player" VarType="player">1</Param>
    <Param name="MyBool" dispName="$$99420$$YesNo" VarType="bool">True</Param>
    <Expression>kbIsPlayerHuman(%Player%)==%MyBool%</Ex pression>
    </Condition>

    <Condition name="Player Exists In Game">
    <Param name="Player" dispName="$$22301$$Player" VarType="player">1</Param>
    <Param name="MyBool" dispName="$$99420$$YesNo" VarType="bool">True</Param>
    <Expression>kbIsPlayerValid(%Player%)==%MyBool%</Ex pression>
    </Condition>

    <Condition name="Player Team">
    <Param name="Player" dispName="$$22301$$Player" VarType="player">1</Param>
    <Param name="Op" dispName="$$22297$$Operator" VarType="operator">==</Param>
    <Param name="MyInt" dispName="$$99420$$Team" VarType="long">1</Param>
    <Expression>kbGetPlayerTeam(%Player%) %Op% %MyInt%</Expression>
    </Condition

    I'm running out now. I was looking for a solution to one problem and went off on this trigger tangent. Now I need to get back to my previous problem which is still unsolved! Too bad the RMS & Triggers is a redheaded step child to the scenario editor...


    "Just invite him over for dinner. Turn him from an enemy into a friend. Then when he's least expecting, BAM! The old fork in the eye!" - M.S.
    --
    Member °ƒ Tsunami Studios <<< Play TOS Maps - 9 Maps - Avg Rating: 4.67! >>>
    mistacheese
    Mortal
    posted 19 March 2003 04:15 PM EDT (US)     7 / 18       
    Could this be used to

    Condition: Find unit with 1 HP
    Effect: Teleport unit

    This is what I have been trying to do, but I'm pretty bad at scripting triggers.

    PS: Your RMS is very creative, I enjoyed it even though I didn't like every part of it.


    Exiled
    Battle Dwarves
    Battlefields: Kemsyt's Uprise

    Mistacheese wins the "Person-who-secretly-emmits-CheeZy's-thoughts" award - Cheezy
    Matei
    Mortal
    posted 19 March 2003 05:17 PM EDT (US)     8 / 18       
    You should probably at least post a pre-made trigtemp.xml with all the new triggers in the Scen Design forum, those people would like it.

    Programmer on 0 A.D., author of Norse Wars, co-author of Fort Wars.
    TwentyOneScore
    Mortal
    posted 19 March 2003 05:51 PM EDT (US)     9 / 18       
    I have been sending these to reyk, who has been maintaining a copy of the new triggers. I'm sure he will post them around again soon as he gets them together.

    I have run into a stumbling block and hopefully someone can answer it, but it might just be one more screw to the RMS. When I am attempting to pass in code through the RMS function rmSetTriggerConditionParam, I find that I need to pass in Quoted strings, such as a condition myVar<>"Zeus", but there is not way to pass a quote into the function!

    rmSetTriggerConditionParam("MyString", "myVar<>"Zeus"", false)

    has obvious problems with the quotes. Any suggestions or solutions? I'm looking to write the triggers by hand in the RMS and then create a scenario from that RMS. The method of injecting the code, while nifty, is not exactly user friendly. Doing it in a text editor first should really help that. But, if this is a screw for the RMS, then cut & paste it is...
    --
    I guess I could make all the quotes a special char like ~, and then use AOMED to extract, search & replace the char, & put back in.


    "Just invite him over for dinner. Turn him from an enemy into a friend. Then when he's least expecting, BAM! The old fork in the eye!" - M.S.
    --
    Member °ƒ Tsunami Studios <<< Play TOS Maps - 9 Maps - Avg Rating: 4.67! >>>

    [This message has been edited by TwentyOneScore (edited 03-19-2003 @ 05:58 PM).]

    reyk
    Mortal
    posted 19 March 2003 06:23 PM EDT (US)     10 / 18       
    @TwentyOneScore:
    I would open a new thread with all new conditions and effects, but there are too many data, so I´ll make a download file with all new triggers on weekend.
    TwentyOneScore
    Mortal
    posted 19 March 2003 07:45 PM EDT (US)     11 / 18       
    Thanks reyk - that's what I was hoping you would do.

    FYI - I just transferred 170 lines of xs code, including global variables, user defined functions, and user defined rules from my VC file into a scenario generated version of my RMS using the xsUserCode effect. It works like a charm! Other than having to change all the double quotes after generation, and having to track a bug down a line at a time, it was easy. But once my RMS was in a scenario, there was no way to pick teams or civs. I haven't made scenarios so far- is that standard? or just for single play mode? (heading to FAQ...)


    "Just invite him over for dinner. Turn him from an enemy into a friend. Then when he's least expecting, BAM! The old fork in the eye!" - M.S.
    --
    Member °ƒ Tsunami Studios <<< Play TOS Maps - 9 Maps - Avg Rating: 4.67! >>>
    Matei
    Mortal
    posted 19 March 2003 08:29 PM EDT (US)     12 / 18       

    Quote:

    I have run into a stumbling block and hopefully someone can answer it, but it might just be one more screw to the RMS. When I am attempting to pass in code through the RMS function rmSetTriggerConditionParam, I find that I need to pass in Quoted strings, such as a condition myVar<>"Zeus", but there is not way to pass a quote into the function!


    The way you use double quotes in strings is by escaping them with a backslash (so write \" instead of "). If you want to print a backslash character, you have to write \\ (double backslash). So your code should be:

    rmSetTriggerConditionParam("MyString", "myVar<>\"Zeus\"", false)


    Quote:

    once my RMS was in a scenario, there was no way to pick teams or civs. I haven't made scenarios so far- is that standard? or just for single play mode? (heading to FAQ...)


    In multiplayer, players can still choose civs and teams. It's just in single-player that the civs selected in the editor (Player Data dialog) are used.

    Programmer on 0 A.D., author of Norse Wars, co-author of Fort Wars.
    TwentyOneScore
    Mortal
    posted 19 March 2003 09:32 PM EDT (US)     13 / 18       
    Thanks! I can now take my RMS, simply create a scenario from it and all the code from my VC file is generated with triggers and it runs flawlessly. So, I'll just develop with a VC file, then import the VC code into the RMS triggers, create a scenario and it's ready to ship. Now, if I can just find a way to do random numbers in a trigger we can have some real fun - or maybe that's a recipe for an OOS...

    "Just invite him over for dinner. Turn him from an enemy into a friend. Then when he's least expecting, BAM! The old fork in the eye!" - M.S.
    --
    Member °ƒ Tsunami Studios <<< Play TOS Maps - 9 Maps - Avg Rating: 4.67! >>>
    Matei
    Mortal
    posted 20 March 2003 08:21 AM EDT (US)     14 / 18       
    For the random numbers, first check to see if rmRandInt() works in trigger code. If it does, that's great .

    If not, you can generate random numbers using a reasonably simple algorithm, as long as you have a random "seed" number to start with (something like the current time in milliseconds at 3-4 seconds in the game could do, if the trigger that checks it has low priority and fires sort of randomly). Store this variable in an int seed.

    You need the following global variables at the top:


    int seed = 0; // assign whatever to it later

    int M = 233280; // constants chosen from a book
    int A = 9301;
    int C = 4927;

    float rand; // the current random number

    And use the following code when you want to get a random number (this will return a random float between 0 and 1 actually):

    seed = (seed*A + C) % M;
    rand = (seed*1.0)/(M*1.0); // now rand stores the random number

    Instead of writing this every time you want a random number you might be able to write the code in a function using the xsUserCode command between xsGlobalsStart and xsGlobalsEnd.

    Programmer on 0 A.D., author of Norse Wars, co-author of Fort Wars.
    Larmir
    Mortal
    posted 20 March 2003 04:11 PM EDT (US)     15 / 18       
    I was messing with a detect HP thing, but I can't seem to find the problem and I don't have it here.

    Using a kbUnitGetCurrentHitpoints command, but it seems to want to work only for < "value". I also got some more tr commands that I'll check later for any use.


    Oh, a way to detect HP could be done with kbUnitGetMaxHitpoints(or something similar, can't remember) then doing the percent damage thing X the number of hitpoints it has max.


    Area 42
    EE Studio | AoM ZooM Editor
    11 Relics Modpack(4.8) | Soccer Scenario(4.2) | SWGB Casino(4.7)
    Quand un athlète fait cent fois l'salaire d'un médecin Moi j'dis qu'c'est ça l'déclin d'l'empire américain - Cowboys Fringants
    The artist formerly known as Prin...errm...EO

    [This message has been edited by Larmir (edited 03-20-2003 @ 04:12 PM).]

    Matei
    Mortal
    posted 20 March 2003 05:13 PM EDT (US)     16 / 18       
    How do you pass unit ID's to KB commands BTW? I had for example a unit whose ID was 61, and using trDistanceToPoint to get its distance to a point worked, but using kbUnitGetPosition with the same ID didn't. Do you have to do something special to the ID it displays in the editor?

    Programmer on 0 A.D., author of Norse Wars, co-author of Fort Wars.
    TwentyOneScore
    Mortal
    posted 20 March 2003 05:24 PM EDT (US)     17 / 18       
    That's where the xsSetContextPlayer(i) comes in. The knowledge base queries for a particular player must have the context set for that player (thanks to AK Vie for his VC work), and then set back when you are done.

    There are some kb functions that accept the player as input and these do not require the context be set - such as the ones I've used in the new conditions Player Civ, Player Culture, Player Human, etc...


    "Just invite him over for dinner. Turn him from an enemy into a friend. Then when he's least expecting, BAM! The old fork in the eye!" - M.S.
    --
    Member °ƒ Tsunami Studios <<< Play TOS Maps - 9 Maps - Avg Rating: 4.67! >>>
    Matei
    Mortal
    posted 20 March 2003 07:41 PM EDT (US)     18 / 18       
    OK I see, thanks.

    Programmer on 0 A.D., author of Norse Wars, co-author of Fort Wars.
    You must be logged in to post messages.
    Please login or register

    Hop to:    

    Age of Mythology Heaven | HeavenGames