Staredit Network > Forums > SC1 Mapping Tools > Topic: Ruby Wrappers for Trigedit generation
Ruby Wrappers for Trigedit generation
Jun 6 2009, 7:54 pm
By: flagitious  

Jun 6 2009, 7:54 pm flagitious Post #1



download here

I wrote some wrappers to make generating trigedit (for smcdraft2) compatible triggers from ruby easy.

Trigedit is very handy because you can copy and paste into it, unfortunately it is very tedious to to actually write triggers in it.

These wrappers make it easy for three reasons:
1) There is a consistent syntax using duck typing and smart default arguments for unit, location, and player.
2) You can name any abstraction and reuse it
3) You can use loops to generate triggers that are very repetitive (binary cutoffs, shared vision ai scripts, etc).

I realize Wormer has already created a macro system for generating triggers. It might be really good, but I didn't look into it very much because I was not keen on needing to learn a whole new language just to generate triggers (well if you don't know ruby this will still be a problem for you, but at least it will be a useful language that you learn). It also seemed to still suffer from problem of having a bunch of functions and needing to remember their argument orders.

With these wrappers the arguments for nearly all conditions/actions is straightforward.

To specify the player for a cond/action use array indexing []
To specify a location, use .at
To specify a unit, use a symbol :

The default location if omitted is Anywhere
The default player if omitted is current player
The default unit if omitted is any unit

For example the condition to check if player 3 has at least 5 marines is
:marine[3]>=5

The action to create 1 zergling at "start" for allies is
:zergling["allies"].at("start")+1

The action to remove all units for the current player is
clear

This last example might seem like a special case but it is not. Clear is simply an alias to (set 0) or (self|0). any unit, anywhere, and current player are all just default. Note that when you set unit to 0 it uses remove all, if a location is present it automatically uses remove units at location. You could remove just x units by using self-x. If you wanted to kill the units instead of remove them, just specify kill (for example kill.clear or :zergling.kill-1)

To actually build triggers use trig, or ptrig as a shortcut for including the action preserve trigger. trig accepts all arguments as conditions or actions, as well as a block which is ran. Any argument that is not a condition or action is assumed to be the player to run the trigger for. You can also use "section" to specify default players trigger is for if non are given in trig. "Always" is assumed if no conditions present.

Some examples:
section All
trig(Ore.set 50)

Code
#this will create hyper triggers for player 1
#or you just write hypertriggers which is defined as this already
3.times{
    ptrig(1) {
        63.times { wait 0 }
    }
}


The best thing about this is being able to create your own abstractions and reuse them. For example in my phantom map here is the trigger that handles victory for slayers. (The abstractions are reused for victory for phantoms).

Code
trig(Phantom[All].off?, PhantomChosen.on?) {
 playwav 'death3'
 fi {
   BeenPhantom.on?
   display'<04>Have a fun time in <17>hell'
   kill.clear
 }
 sync {
   BeenPhantom.off?
   display '<04>Good has prevailed, the <07>phantom <04>is no more!'
   swait(20)
   playwav 'yippie2'
   victory
 }
}


This might not seem that short to handle victory. But alot is actually going on here. Some explaitions:
Phantom and BeenPhantom are death counter variables used to represent if a player is/was the phantom. They were created by simply Phantom = var. var is a function which creates a death counter variable automatically using unused units.

PhantomChosen is a switch which was created using PhantomChosen = switch.

swait is a simulated wait, which must be used to avoid wait blocks if hyper triggers are present (which they are, since in a phantom game any player could be absent so the hyper triggers just run for all players).

fi means if, but that is a reserved word in ruby.

sync, means to start running this trigger next trigger cycle starting with the first player. This is used because say there are 3 players. Player 1 is a slayer, player 2 is the phantom, player 3 is also a slayer. Player 2 is eliminated, he will have a trigger running for him that clears his Phantom switch. This means player 3 will detect the victory first. But victory actually ends the scenario in defeat for all players that do not also run the victory trigger this very trigger cycle, thus if sync was not used, player 1 would see defeat even though its victory would run just 1/12th of a second later (this was actually a bug in my first phantom map).

The triggers that actually get generated look like this:
Code
Trigger("all players"){
Conditions:
   Deaths("all players", "Khaydarin Crystal Formation\r", at most, 0);
Actions:
   Set Deaths("current player", "Khaydarin Crystal Formation\r", set to, 1);
}

Trigger("all players"){
Conditions:
   Always();
Actions:
   Preserve Trigger();
   Set Deaths("current player", "Cantina\r", subtract, 1);
}

Trigger("all players"){
Conditions:
   Deaths("all players", "Unused Terran Bldg type 2\r", at most, 0);
   Switch("switch7", set);
Actions:
   Play WAV("staredit\\wav\\death3.wav", 0);
   Set Deaths("current player", "Mineral Field (Type 2)\r", set to, 1);
   Set Switch("switch15", set);
}

Trigger("all players"){
Conditions:
   Deaths("current player", "Mineral Field (Type 2)\r", at least, 1);
   Deaths("current player", "Unused type 2\r", at least, 1);
Actions:
   Display Text Message(always display, "<04>Have a fun time in <17>hell");
   Kill Unit("current player", "any unit");
}

Trigger("all players"){
Conditions:
   Deaths("current player", "Khaydarin Crystal Formation\r", at least, 1);
   Switch("switch15", set);
Actions:
   Set Switch("switch16", set);
}

Trigger("all players"){
Conditions:
   Switch("switch16", set);
   Deaths("current player", "Unused type 2\r", at most, 0);
   Deaths("current player", "Cantina\r", at most, 0);
Actions:
   Display Text Message(always display, "<04>Good has prevailed, the <07>phantom <04>is no more!");
   Set Deaths("current player", "Cantina\r", set to, 21);
}

Trigger("all players"){
Conditions:
   Deaths("current player", "Cantina\r", exactly, 1);
Actions:
   Play WAV("staredit\\wav\\yippie2.wav", 0);
   Victory();
}


Note if you reuse this abstraction the first two triggers would not also be duplicated, they would be reused automatically. This may now seem like alot of bloat in the actual triggers. But it is pretty much the bare minimum if you actually want to do it right. (When I did it by hand, I had less because I used toggleable hyper triggers instead of swait, but I switched to swaits because I didn't want replays to desynch).

This trigger generation system is something that I just made, there may still be bugs and quirks, but the could thing is it is fairly simple, if you want to change the way something behaves you can just do it your self, since it is just a couple of ruby files. I realize this quick tutorial I have given is not really enough to learn how to use them to their fullest, I may try to explain it in more detail later. If you use them, it probably will be confusing at first. But if you map alot, this really does make it easy once you understand it. I would prefer to use them to using menu based triggers for anything, even non repetitive stuff. There are tons of things that could be added or improved, but I make no promises to update. (I wrote these primarily to help with my own mapping)

If you want to try to modify what I've done or add your own abstractions, I'll give a brief description of the files:

units.rb: loads the list of units
bindings.rb: creates a bunch of functions that do the trig edit equivalent of all actions and conditions, for example
displaytextmessage(AlwaysDisplay, "asdf") generates:
Display Text Message(Always Display, "asdf")
wrapper.rb: creates the class like consistent behavior wrappers
abstractions.rb: a bunch of abstractions like swait
phantom.rb: the code to generate my phantom map triggers
autohotkey.ini: you can use with a program called autohotkey to automatically copy out.txt to clipboard, switch to trigedit, paste, compile, check if error and save map.



None.

Jun 6 2009, 8:13 pm Falkoner Post #2



This looks pretty nice, however, to tell you the truth, it seems like more of an entire language than Wormer's MacroTriggers, the syntax looks like it would take a bit of memorization, but I think once you get it down, it'll be totally worth it, nice work.



None.

Jun 7 2009, 9:02 am Wormer Post #3



Nice thing :) Though I don't know ruby at all =) I am gonna to enumerate some number of random opinions, thoughts and questions that accidentally came to my mind which even does not necessary strictly correspond to the wrapper itself.

What is confusing at first glance is that sometimes you have conditions as parameters to a "function":
Quote from flagitious
trig(Phantom[All].off?, PhantomChosen.on?) {
playwav 'death3'
...
and sometimes you have conditions at start of a "block":
Quote from flagitious
 fi {
BeenPhantom.on?
display'<04>Have a fun time in <17>hell'
...

Just a random offtopic thought now. Triggers are event-driven machinery by their nature. People learned how to use them to do a bit more than was intended, but this is achieved by complexity of the triggers. We unite such triggers dealing with the same task in trigger systems. I thought that probably some higher-level system of notions and abstractions could be invented to represent trigger systems in more natural way.

Anyways, what I liked about the wrapper was that it enables to express such constructs as "if C is true then do A, further if additionally D is true then do B in addition to A". IMO, this is more natural way of human thinking than reducing all conditions to the disjunctive form.

Quote from flagitious
Trigger("all players"){
Conditions:
Deaths("all players", "Khaydarin Crystal Formation\r", at most, 0);
Actions:
Set Deaths("current player", "Khaydarin Crystal Formation\r", set to, 1);
}
+1 :idea: A very nice way of "electing" a leader of the game! I call a leader of the game the player who accepts the responsibility to execute triggers which are intended to execute only once per cycle. Also I usually divide players on 3 groups: an opening player, intermediate players, and a closing player. The opening player is the player who's triggers will execute first, the closing player is in contrast the player who's triggers will execute last and intermediate players are all other players. So the leader there is also double jobbing as an opening player (or is it vice versa? :rolleyes: ).

Quote from flagitious
But victory actually ends the scenario in defeat for all players that do not also run the victory trigger this very trigger cycle
I thought that in addition to what have been said, the scenario also ends in the victory to all allied victory players to the players for whom the victory action executes. Not perfectly sure though.

Quote from flagitious
I realize Wormer has already created a macro system for generating triggers. It might be really good, but I didn't look into it very much because I was not keen on needing to learn a whole new language just to generate triggers (well if you don't know ruby this will still be a problem for you, but at least it will be a useful language that you learn). It also seemed to still suffer from problem of having a bunch of functions and needing to remember their argument orders.
The Macro Triggers Language sounds loudly but actually there is little new to learn. And now I have autocompletion and hints ;)

Quote from flagitious
For example the condition to check if player 3 has at least 5 marines is
:marine[3]>=5
Will this generate a command condition or a bring condition?

Quote from flagitious
The best thing about this is being able to create your own abstractions and reuse them. For example in my phantom map here is the trigger that handles victory for slayers. (The abstractions are reused for victory for phantoms).
What do you mean under "abstraction" there. When read I thought of a "sych" abstraction and from personal experience with Macro Triggers the ability to introduce "variables" (actually constants :rolleyes: ) to name units. Is there anything else you understand under the "abstraction"?

Quote from flagitious
var is a function which creates a death counter variable automatically using unused units.
Yay! this is really fun! I always thought of adding something like this to Macro Triggers. The same for switches.
Also,
Quote from ideas.txt
ideal world:
...
autodetection of unused units from chk
you have tremendous plans!

Quote from ideas.txt
add a "not" / inverse abstraction for conditions
:cool2:

Quote from flagitious
fi means if, but that is a reserved word in ruby.
Probably "iff"?

Quote from flagitious
(When I did it by hand, I had less because I used toggleable hyper triggers instead of swait, but I switched to swaits because I didn't want replays to desynch).
Wow, haven't known! Could you please explain, how do toggleable hyper triggers desynch replays?

Quote from flagitious
autohotkey.ini: you can use with a program called autohotkey to automatically copy out.txt to clipboard, switch to trigedit, paste, compile, check if error and save map.
A usefull thing! Might also be used with Macro Triggers...

I came here from the Phantom map showcase thread. I like how you tweaked the phantom rules to make it more fun. Don't know how this will work on practice but anyways it is a good attempt to make the map more interesting!

Good job :cool:



Some.

Jun 7 2009, 3:45 pm flagitious Post #4



Hey Wormer, glad you got a chance to look at what I've done.

Quote from Wormer
What is confusing at first glance is that sometimes you have conditions as parameters to a "function":
Quote from flagitious
trig(Phantom[All].off?, PhantomChosen.on?) {
playwav 'death3'
...
and sometimes you have conditions at start of a "block":
Quote from flagitious
 fi {
BeenPhantom.on?
display'<04>Have a fun time in <17>hell'
...
Yes, this is true, possibly a design flaw on my part. How it is now is conditions or actions can be in either arguments or blocks. It can tell what is a condition and what is an action, so it is just a matter of style. As a convention I put conditions in arguments. Blocks however don't get run at function call time, but when yield is used, so for some macros, everything needs to be in the block. This is less than ideal, and if I had to do it all over again I would probably rethink this to avoid using side effects and make it more functional. (I may try and tackle this, which would result in major changes)

Quote from Wormer
A very nice way of "electing" a leader of the game! I call a leader of the game the player who accepts the responsibility to execute triggers which are intended to execute only once per cycle. Also I usually divide players on 3 groups: an opening player, intermediate players, and a closing player. The opening player is the player who's triggers will execute first, the closing player is in contrast the player who's triggers will execute last and intermediate players are all other players. So the leader there is also double jobbing as an opening player (or is it vice versa? :rolleyes: ).
I thought about including a closing/intermediate player type thing, but for my uses I didn't have any need for it (people can still just definite their own as they go though). Not that there is no need, I have really only used these to develop 1 map so far. At one point I almost made closing player but instead just made the action for first player, done before it reset the things in question (vote detection).

Quote from Wormer
Quote from flagitious
But victory actually ends the scenario in defeat for all players that do not also run the victory trigger this very trigger cycle
I thought that in addition to what have been said, the scenario also ends in the victory to all allied victory players to the players for whom the victory action executes. Not perfectly sure though.

I saw that written in thread somewhere, and so I did a quick test a while back, the result was it only ended it in victory for the current player. (This was done by ending in victory for a computer player with mutual allied victory to me). As a side note there is actually a bug in default melee triggers for ums map. buildings of non allied victory players at most 0 is not the correct way to do it because if there are 3 players, A,B,C. Everyone is allied to A, but B and C not allied. A has no non allied victory players, so it will end in victory for him, defeating others.

Quote from Wormer
The Macro Triggers Language sounds loudly but actually there is little new to learn. And now I have autocompletion and hints ;)

Ah sorry, this is really a flaw of my own, I tend to prefer to develop my own solutions rather than learn other peoples. Also it wasn't a totally conscious choice to not use your trigger macros. What happened was originally I liked writing in classic trigedit, I only needed macros for the binary cutoffs for resource bonus, and later the giving vision stuff when I added it, so at the time I thought it would be easier to write a ruby script than learn macro triggers. However the original code for my binary cutoffs was ugly and hard to understand, I had problems changing it when I needed to make it work for 2 phantoms mode. So I thought I would make bindings for the trigedit rather than use strings. Then I thought it would be cool if syntax was the same for all types of values, regardless if it was a mineral count, score, death counter, etc.

Quote from Wormer
Quote from flagitious
For example the condition to check if player 3 has at least 5 marines is
:marine[3]>=5
Will this generate a command condition or a bring condition?
If no location is specified it will use a command trigger, otherwise a bring. There are a couple more things that use this optimization too (I think it doesn't matter much though, may run slightly faster in starcraft).

Quote from Wormer
What do you mean under "abstraction" there. When read I thought of a "sych" abstraction and from personal experience with Macro Triggers the ability to introduce "variables" (actually constants :rolleyes: ) to name units. Is there anything else you understand under the "abstraction"?
I think of abstraction as any concept for getting something done. By writing your own abstraction, you use this concept directly rather than reusing how the abstraction works everytime and needing to consider how it actually is implemented. One problem I had when making abstractions was I had written in trigedit classic for so long, that I would automatically write out all parts of an abstraction. I would have to go back and say hey, this is what I was really thinking, this is what should actually be shown.

Quote from Wormer
Quote from ideas.txt
ideal world:
autodetection of unused units from chk
you have tremendous plans!
Haha, its not a plan it's an idea... Probably will never get done (or even attempted). But it could be done.

Quote from Wormer
Quote from flagitious
fi means if, but that is a reserved word in ruby.
Probably "iff"?
I think that means if and only if in math, also a flaw with fi is in some langs in means end if. This is another flaw in my system, my function names (for some abstractions) are confusing to other people.

Quote from Wormer
Quote from flagitious
(When I did it by hand, I had less because I used toggleable hyper triggers instead of swait, but I switched to swaits because I didn't want replays to desynch).
Wow, haven't known! Could you please explain, how do toggleable hyper triggers desynch replays?
Well originally I was toggling the hyper triggers off during the part where it does the intro (several messages separated by real waits). However wait, waits real seconds and a real second is a different duration depending on game speed (replay watchers will fast forward). So I shouldn't have said toggleable hyper triggers don't cause desynch, but real waits do, but only reason to toggle hyper triggers is to use a real wait (not an issue for end of game message though).

Thanks for your feedbacks. Your system is more mature and more suitable for people who do not know ruby, but I hope that some of my ideas can benefit it. Specifically there are three areas which I think my system does something better. (I say this to be constructive and give ideas, there are also many many ways yours does something better).

1) is the same type of operation (addto, set, leaderboard, etc) having the same way of being called regardless if it is a switch, units at location, deaths, opponents, etc
2) is the default values (anywhere, anyunit, current player), and lack of arguments and thus argument order
3) is the power of abstract macroes, via having the power of ruby. Like is it possible to define something like swait as a macro (without having to modify your java source?).

You might be 1 step ahead of me for 3) here (again I need to look more into what you've got already). But it seems to have that kind of power, you would really need to have to write your own total language (where as I have everything ruby can do for free). However you can make any syntax mean anything whereas I am stuck to ruby syntax. This is the tradeoff of new language versus building on an existing one.

I will read more about your system, instead of just making guesses about how much it can do (hopefully I am not complete off base here).



None.

Jun 8 2009, 10:46 am Wormer Post #5



I see that you've developed the Wrapper for personal use to simplify own life and released it just in hope that it could be useful for someone else. You did it exactly as I did with MacroTriggers, that is why I understand you very well. You shouldn't be sorry for something not done or done in not obvious way because you've done it for yourself. Habits of other people obviously must not match yours.

I simply told you what first came to a mind of a man who is not familiar with Ruby when he looked at it. This might or might not be helpful if you decide to improve the wrapper. I am also glad there is an alternative to MacroTriggers, because alternatives always make you see your own flaws or ways of further improvement.

I'll tell you how I came to MacroTriggers, if you're interested. I was working on a map (that intended to be a new version of a Macro Micro map) and realized that copying text triggers by hand is not the best way, because if you want to change something later you'll have known problems. I decided to use text macro generation tool called M4. However on half way of using it I realized that triggers written in M4 look ugly and were inconvenient. So I decided to make my own "macro" "language", hence the name MacroTriggers. What is funny that I've never finished that new version of the Macro Micro map :) because I became too lazy :drunk:

Quote from flagitious
I thought about including a closing/intermediate player type thing, but for my uses I didn't have any need for it (people can still just definite their own as they go though). Not that there is no need, I have really only used these to develop 1 map so far. At one point I almost made closing player but instead just made the action for first player, done before it reset the things in question (vote detection).
I just told what notions I mark out. Probably the closing player is somewhat odd, because executing triggers for closing player is almost equivalent to executing triggers for the opening player. The closing player is harder to determine, and what is more essential I believe that every system which determines the closing player should have a latency of at least 1 trigger cycle, because to understand that the particular player is the last one should necessary try to execute triggers for next players.

Quote from flagitious
As a side note there is actually a bug in default melee triggers for ums map. buildings of non allied victory players at most 0 is not the correct way to do it because if there are 3 players, A,B,C. Everyone is allied to A, but B and C not allied. A has no non allied victory players, so it will end in victory for him, defeating others.
It is nice how you've noticed this! Indeed. One should use opponents condition instead (Current player has got exactly 0 opponents remaining in the game). This condition is equivalent to the statement "all remaining players are allied victoried to each other". I don't know how exactly SC calculates the number of opponents, but I think I have a right feeling of understanding the number of opponents in each particular case. In the described example player A will still have 2 opponents (I've just tested this to be 100% sure). I also believe players B and C will have 1 opponent (not tested but I'm 90% sure).

There are some wired and not obvious things about alliance statuses. You may read this post, in case you're interested.

Just an idea for your phantom map. Probably it might be interesting not to reveal phantom elimination. What I mean is to announce victory only when 1) all players are allied victoried and 2) there is no phantom among them.

Quote from flagitious
Well originally I was toggling the hyper triggers off during the part where it does the intro (several messages separated by real waits). However wait, waits real seconds and a real second is a different duration depending on game speed (replay watchers will fast forward). So I shouldn't have said toggleable hyper triggers don't cause desynch, but real waits do, but only reason to toggle hyper triggers is to use a real wait (not an issue for end of game message though).
Do you imply the replay of a UMS which has not any other waits except hyper triggers will work fine on any speed?

Quote from flagitious
If no location is specified it will use a command trigger, otherwise a bring. There are a couple more things that use this optimization too (I think it doesn't matter much though, may run slightly faster in starcraft).
Yup, the command condition seem to work little faster. But sometimes it does matter what is specified: bring to anywhere or command. The reason is wired and lies in so called location invalidation thing, discovered a year go. The discovery started from here (actually it started on now lost Warbox forums :( ). Here is the second part of the discussion. The final solution is stated here (Firinite was a Disiple of Adun, so I refer to him as to DoA in the thread). Somewhere in the last thread you can find explanations why command works a little bit faster than bring.

The invalidation thing, and some other subtle things made me very attentive and cautious towards any kind of far going abstraction and powerfull automation. Of course abstraction makes life easier but sometimes triggerer requires subtle control over situation. This becomes even more essential when there are very limited resources one can use (quite limited number of DCs, strings, CPU power and such). That why MacroTriggers does not have complex means of defining abstractions. I believe that in complex cases a man could do the job better than the machine. That why MacroTriggers gives only an opportunity to specify sequence of triggers and nothing more. The so called macroses in MacroTriggers is simply a way to remember and parametrize a continual "block" of triggers in case to easily substitute it in various places. When making MacroTriggers my primary goal was not to provide a powerful abstraction of triggers, but to eliminate manual triggers copying.

So it is impossible in MacroTriggers to specify something in actions or conditions which will lead to generation of additional triggers (like swait). That why probably it is you who is ahead of me.

Quote from flagitious
I hope that some of my ideas can benefit it. Specifically there are three areas which I think my system does something better. (I say this to be constructive and give ideas, there are also many many ways yours does something better).

1) is the same type of operation (addto, set, leaderboard, etc) having the same way of being called regardless if it is a switch, units at location, deaths, opponents, etc
2) is the default values (anywhere, anyunit, current player), and lack of arguments and thus argument order
3) is the power of abstract macroes, via having the power of ruby. Like is it possible to define something like swait as a macro (without having to modify your java source?).
The first and the second seem to be correlated and are an advantage, but I can't understand how I can know (and thus have control over) what condition or action will be generated in the particular case. The last seems like a disadvantage, if one can't do it. Actually this gave me an idea of simpler syntax... I need more investigation on this though.

I don't know how to consider the third. Generally this seems to be an advantage, but again if one constructs a complex system usage of macroses may limit him. But you'll probably say that more specific macroses could be written to satisfy his needs... That is why I don't quite sure how to consider this. Also you can say that in some simple cases it is easier to use ready abstractions, but on the other hand the system is valuable when it can help writing complex triggers because in simple cases triggers could be easily written with one's own hand.

Once more, what you've made is something new and definitely deserves greater attention.



Some.

Jun 8 2009, 8:48 pm flagitious Post #6



Quote from Wormer
Indeed. One should use opponents condition instead (Current player has got exactly 0 opponents remaining in the game). This condition is equivalent to the statement "all remaining players are allied victoried to each other".

Good to know, opponents should definitely be used instead of non allied victory players.

Quote from Wormer
Just an idea for your phantom map. Probably it might be interesting not to reveal phantom elimination. What I mean is to announce victory only when 1) all players are allied victoried and 2) there is no phantom among them.

I'll think about this, so you mean replace Slayer|Phantom eliminated message with just Player eliminated. And then change victory conditions to be all remaining players are in same "force", and are allied victory?

Quote from Wormer
Do you imply the replay of a UMS which has not any other waits except hyper triggers will work fine on any speed?

Using non zero waits definitely causes it, but there might be other things/bugs in sc that I am unaware of (besides EUDs) that can also cause desynch.

Quote from Wormer
Yup, the command condition seem to work little faster. But sometimes it does matter what is specified: bring to anywhere or command. ...
The invalidation thing, and some other subtle things made me very attentive and cautious towards any kind of far going abstraction and powerfull automation. Of course abstraction makes life easier but sometimes triggerer requires subtle control over situation. This becomes even more essential when there are very limited resources one can use (quite limited number of DCs, strings, CPU power and such). That why MacroTriggers does not have complex means of defining abstractions. I believe that in complex cases a man could do the job better than the machine. That why MacroTriggers gives only an opportunity to specify sequence of triggers and nothing more. The so called macroses in MacroTriggers is simply a way to remember and parametrize a continual "block" of triggers in case to easily substitute it in various places. When making MacroTriggers my primary goal was not to provide a powerful abstraction of triggers, but to eliminate manual triggers copying.

I was unaware of that difference between command and bring. However you can still easily generate the desired trigger using unit.at("anywhere") instead of just unit. I understand your point about over abstracting triggers. It is like comparing assembly language to a high level language. The high level language is much easier to program in but it can be extremely wasteful with resources. However the case is even more extreme with triggers, because abstractions can be messier and even less efficient due to the lack of power and side effect nature of triggers themselves. However just because an abstraction exist doesn't mean you have to use it. I think it is best to provide both ways of doing things, a direct way where you know and understand exactly what will be generated, but also providing ways for higher level thinking. Then again by having only one way, you make it easier for people to learn and help them make decisions since they do not have to consider which way to do it.

Looking at your work I see that you have auto completion for triggers, this seems like it would solve the problem of knowing what the names of commands are. If it also tells you about argument order then I think it is very good, and just an alternative style to the points I made under 1) and 2). Maybe it would be possible to automatically reorder arguements based on their type, so that in most cases the order would not matter. For 3) I would say that it is also a style thing. MacroTriggers is already so much more powerful than writing in triggers, that there is not need to complicated it any further for further power.

I have started going over my code and cleaning it up. First I will simplify the wrapper and make it more consistant (for example sethealth will be gone and health will be like a value, etc). After that I will try to make it easier to write abstractions.

Have you looked into generating output other than trigedit? There are a few limitations with it, like you cannot seem to use 10 digit numbers (useful for some EUDs), and also you cannot do some invalid things which are useful, like playing a wav that will make SC crash. Going straight to the CHK seems daunting though.



None.

Jun 14 2009, 2:22 pm Wormer Post #7



I'm sorry for my silence, I had not time to answer earlier.

Quote from flagitious
I understand your point about over abstracting triggers. It is like comparing assembly language to a high level language. The high level language is much easier to program in but it can be extremely wasteful with resources.
Right, that was exactly what I wanted to say.

Also, having defaults sounds nice, but defaults can be source of mistakes. I like when everything is specified explicitly and is there right here before your eyes when you look at it.

Quote from flagitious
Maybe it would be possible to automatically reorder arguements based on their type, so that in most cases the order would not matter.
Yes, a good idea, it is possible in most cases. Exceptions are when there are two arguments of the same type: GiveUnits, MoveUnits, Order, Transmission. Not qute sure what to do with this.

The other solution might be assigning labels to arguments, like:
GiveUnits(Unit: @All, Location: @Anywhere, From: @CurrentPlayer, To: @P(2))

(But then you have to remember labels names. :) )
Quote from flagitious
MacroTriggers is already so much more powerful than writing in triggers, that there is not need to complicated it any further for further power.
There are few things that MacroTriggers requires. The main thing is (like Falkoner fairly noticed) having arrays to place units, locations and such things in arrays and later pick out items using math operations. There were few other things, but I don't remember right now. As far as I don't need them right now and not much people use MacroTriggers I'm not motivated to add this yet.

Quote from flagitious
Have you looked into generating output other than trigedit? There are a few limitations with it, like you cannot seem to use 10 digit numbers (useful for some EUDs), and also you cannot do some invalid things which are useful, like playing a wav that will make SC crash. Going straight to the CHK seems daunting though.

Yeah, right. Making a tool which would compile triggers right into the map is too complicated for me, because 1) you should examine ways of working with mpq archives and 2) work directly with raw data stored in the map. Also SCMD handles strings well. If only there was an easier front-end for operations with maps...

Yes, I was so happy when SI added support for the memory condition, but unfortunately there is a bug with trigedit about memory condition :( I already reported it there. By the way, you can remind SI of this bug...

There is one easier way to begin with instead of compiling directly to CHK. You can generate .trg file to import into the map. Find the description of it's format in the Wiki. It is quite simple. Unfortunately there are also limitations, for example I was very surprised to know you can't specify WAVs in .trg files. But using this method seems like a good start if you wish to compile into CHK later.

Also, this thread inspired me of writing a wrapper for triggers on Scheme. I've written an experimental 5-minute prototype and it seems that a functional language fits very well for the task of writing triggers. I generate .trg file right now. I realized how using an existing language have an advantage that you can use the full power of the target language instead of writing your own compiler to implement everything manually.



Some.

Jun 23 2009, 3:34 pm flagitious Post #8



I have kind of stopped working on this. I did go though and rewrite the wrapper to use a config file and be much more consistent. But then when thinking about abstractions I thought of a very uniform and simple method for abstractions which would then need me to write an optimizer to keep the trigger size down (it could keep size to as low as human written). However the maps I am making aren't that complicated and it is frankly not worth the effort. (Might post the re written wrapper though, reason I haven't is because I haven't actually used it to make a map so it might have bugs)



None.

Options
  Back to forum
Please log in to reply to this topic or to report it.
Members in this topic: None.
[11:50 pm]
O)FaRTy1billion[MM] -- nice, now i have more than enough
[11:49 pm]
O)FaRTy1billion[MM] -- if i don't gamble them away first
[11:49 pm]
O)FaRTy1billion[MM] -- o, due to a donation i now have enough minerals to send you minerals
[2024-4-17. : 3:26 am]
O)FaRTy1billion[MM] -- i have to ask for minerals first tho cuz i don't have enough to send
[2024-4-17. : 1:53 am]
Vrael -- bet u'll ask for my minerals first and then just send me some lousy vespene gas instead
[2024-4-17. : 1:52 am]
Vrael -- hah do you think I was born yesterday?
[2024-4-17. : 1:08 am]
O)FaRTy1billion[MM] -- i'll trade you mineral counts
[2024-4-16. : 5:05 pm]
Vrael -- Its simple, just send all minerals to Vrael until you have 0 minerals then your account is gone
[2024-4-16. : 4:31 pm]
Zoan -- where's the option to delete my account
[2024-4-16. : 4:30 pm]
Zoan -- goodbye forever
Please log in to shout.


Members Online: Roy, l)ark_ssj9kevin, Oh_Man