Page 1 of 2

Args Explained

Posted: 03 May 2015 10:53
by Vlek
You might have seen several really high functioning javascript code examples using args or an index of args to get extra information that was passed along with an alias. You might not have seen however anywhere that states what exactly it's doing or why they're doing it. In this post, I'll explain what args are, how they are used, and why they're awesome. Lets take a look:

Explanation:
Args are the arguments that are passed when one gives a command (so basically everything you ever enter into the console, that's fair game for args). This is done by taking a command, like 'kill stinky pete', cutting it up into pieces in the background, and having it stored for the user to use in their javascript code in their alias as usable strings.

The important part about args is that you can select all or some of the text that was passed along with the alias depending on the index of args that you are looking it. For our example of 'kill stinky pete', args would have five indexes: 0, 1, 2, $*, and *. If you were to programmatically retrieve them, it would look like this: kill stinky pete, stinky, pete, stinky pete, stinky pete. The ones we care about are going to be the non-zero numbers and *. The non-zero numbers inform us that at least one other word was passed along with our alias. The asterisk will allow us to retrieve all information that was passed other than the alias.

In a nice graph format:
Index | Output
0 | kill stinky pete
1 | stinky
2 | pete
$* | stinky pete
* | stinky pete

In use:
So, lets say that you want to automate killing crap. The command for doing so is "kill <identifier for what to kill>". So it would be a good idea to create an alias "kill", and then use some fancy javascript to remember what you last attacked. That way, when your "You killed" trigger pops, you can have it try to kill the same thing again in the hopes that there's another enemy standing around. To do that, we're going to need to use what was supplied with the "kill" command and store it to a variable. That'd look something like this:

alias: kill

Code: Select all

if (args[1] !== undefined){ 
  gwc.userdata.last_target = args['*'];
  gwc.connection.send( 'kill ' + args['*'] );
}
else{
  gwc.output.append('Kill what?');
  gwc.output.color('red');
}
The reason that we're using '*' instead of the second index like we checked is because the '*' index is always going to be present. If one was to only type "kill", then the '*' index will be an empty string, or ''. It would always cause our if statement to pop, and that's not what we want. Instead, we are checking whether there was at least one other word after "kill" that was separated by a space. If that's the case, we need to grab ALL of the words that were after the "kill" command. That's why we can't use the second index, or [1], for when we go to use the data. If we did, then when stinky pete is dead and we want to kill his clone that happens to be near by, we would then be trying to only "kill stinky" which would inevitably be met with the most irritating of all console errors of "You find no such living creature". If you have a trigger that responds to that by moving on, then you may completely overlook all the other Pete's on that tile.

Our trigger would then look something like this:
trigger: You killed

Code: Select all

gwc.connection.send( 'kill ' + gwc.userdata.last_target );

Advanced args use: (Using non-capturing groups in regular expressions)
Once you get the hang of taking chunks of text out of a sentence using regular expressions, you may feel like it is time to simplify your trigger list by putting multiple triggers together that all perform the same types of actions. This can be done normally using (?:<statement one>|<statement two>). When one wishes to receive a portion of the statements within the non-capturing group, then the indexing for args becomes different. Instead of having each numerical index in args being a portion of the received text, it instead returns what each portion of the non-capturing group collected when the trigger fired. This means that, if you have four different statements, you will have three that are undefined and a forth one that is defined (and contains the text that you want).

To give an example, if you were trying to put all of your statements together that were for a quest in which there were several statements that all used different language to describe the same thing, you may be tempted to put them together into a non-capturing group that had capturing groups inside of it.

Example of regular expression:
(?:The (.*) throws a rock!|The (.*) bites you|The (.*) goes berserk)
We know that, because there are three different statements within our non-capturing group, there will be three different non-zero integer indexes for args: 1, 2, and 3. Each of these will correlate with the individual statements. So, if you wanted the first statement, you would use args[1] and so forth. What if you wanted to get whichever one is available though?

Then you'd use something like this in order to get the one defined value:

Code: Select all

for (var i = 1; i < 5; i++){
  if ( args[ i ] !== undefined ){
    var value = args[ i ];
    break;
  }
}
//Here is where you'd use the value variable

Why that's awesome:
Aliases are amazing. They have a great amount of power, but aliases that can have extra information passed with them are even more awesome. Don't take my word for it, look at script examples like the one that allows you to save statistical information about forgings and retrieve specific stats by an alias call. Literally, the sky is the limit.

Re: Args Explained

Posted: 10 May 2015 05:25
by Zhar
vlek wrote: Example of regular expression:
(?:The (.*) throws a rock!|The (.*) bites you|The (.*) goes berserk)
We know that, because there are three different statements within our non-capturing group, there will be three different non-zero integer indexes for args: 1, 2, and 3. Each of these will correlate with the individual statements. So, if you wanted the first statement, you would use args[1] and so forth. What if you wanted to get whichever one is available though?

Then you'd use something like this in order to get the one defined value:

Code: Select all

for (var i = 1; i < 5; i++){
  if ( args[ i ] !== undefined ){
    var value = args[ i ];
    break;
  }
}
//Here is where you'd use the value variable
Sorry to nitpick, but that's very inefficient...

First of all, your regex doesn't conform with DRY (the most important principle). Second of all, you declare your value variable inside a loop and then immediately break out of it and try to do something with the variable outside of the loop (not good since you're trying to access the variable out of scope now).

Code: Select all

^=The (.*) (thorws a rock|bites you|goes berserk)

var value = null;
var i = 1;

while (i < 5) {
    if (args[i]) {
        value = args[i];
        break;
    }

    i += 1;
}

// do stuff with variable 'value'

Re: Args Explained

Posted: 11 May 2015 00:26
by Vlek
Hello Zhar, thanks for the input.

You're right, I didn't give a very good example because it does seem like what I was doing was inefficient, but I'm talking about cases in which the syntax of the line can be vastly different, causing one to use an or case statement like the one that I was using, and how that effects args. If one were to use the regex statement that you provided, they wouldn't get the weird args indexing I wanted to highlight.

Since the trigger would only fire in the case of at least one of those statements being in the output, it is impossible that one would be trying to interact with the value variable and it being out of scope.

Lastly, as far as the choice of loop goes, you're right. I'd change it in the main post if I still could edit it.

Re: Args Explained

Posted: 11 May 2015 01:44
by Zhar
vlek wrote:Hello Zhar, thanks for the input.

You're right, I didn't give a very good example because it does seem like what I was doing was inefficient, but I'm talking about cases in which the syntax of the line can be vastly different, causing one to use an or case statement like the one that I was using, and how that effects args. If one were to use the regex statement that you provided, they wouldn't get the weird args indexing I wanted to highlight.

Since the trigger would only fire in the case of at least one of those statements being in the output, it is impossible that one would be trying to interact with the value variable and it being out of scope.

Lastly, as far as the choice of loop goes, you're right. I'd change it in the main post if I still could edit it.
Well, it doesn't really matter if you use for or while loop in this case (I think that while loop is easier to grasp for people unfamiliar with coding, that's why I used it). What was mostly wrong in your original code was that you declared 'var value =' inside of the loop. If you tried to access it later then it might not have been set at all. Maybe not in this particular case, but it's usually better to always try and guard against such things in your code. Good habits go a long way :)

Re: Args Explained

Posted: 11 May 2015 10:07
by cotillion
Zhar wrote: Well, it doesn't really matter if you use for or while loop in this case (I think that while loop is easier to grasp for people unfamiliar with coding, that's why I used it). What was mostly wrong in your original code was that you declared 'var value =' inside of the loop. If you tried to access it later then it might not have been set at all. Maybe not in this particular case, but it's usually better to always try and guard against such things in your code. Good habits go a long way :)
Due to one of the oddities of javascript that's actually not true.
'var' declarations are always hoisted to the top of the current function scope.

https://developer.mozilla.org/en-US/doc ... r_hoisting

Re: Args Explained

Posted: 11 May 2015 10:48
by Zhar
cotillion wrote:
Zhar wrote: Well, it doesn't really matter if you use for or while loop in this case (I think that while loop is easier to grasp for people unfamiliar with coding, that's why I used it). What was mostly wrong in your original code was that you declared 'var value =' inside of the loop. If you tried to access it later then it might not have been set at all. Maybe not in this particular case, but it's usually better to always try and guard against such things in your code. Good habits go a long way :)
Due to one of the oddities of javascript that's actually not true.
'var' declarations are always hoisted to the top of the current function scope.

https://developer.mozilla.org/en-US/doc ... r_hoisting
One of the reasons I dislike JS. My IDE is always warning about possible variables without set values so I kind of got into the habit of declaring everything explicitly within the scope I want to use them.

Re: Args Explained

Posted: 28 May 2017 17:07
by Tarax the Terrible
Been searching and searching cus I read this in the past but then got confused and though last target was something that was in gmcp data. Doh!

Glad I found it again, great example thank you!

Re: Args Explained

Posted: 29 May 2017 04:53
by Kwevin
What the heck is all in that "gwc" thingy?

Re: Args Explained

Posted: 29 May 2017 16:19
by Drazson
Kwevin wrote:What the heck is all in that "gwc" thingy?
In my experience, coding consists of copying stuff you have no idea how/why work (but they do) and then adding/replacing stuff to make your case work. So I wouldn't worry about that!

Re: Args Explained

Posted: 29 May 2017 18:58
by Melarec
Kwevin wrote:What the heck is all in that "gwc" thingy?
I don't know what "gwc" stands for specifically, but it's likely a main global variable that relates to the document.
For instance, when coding a canvas element, these are the first two lines in your script:

Code: Select all

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
"ctx" is then used whenever you want to modify the canvas.
Such as:

Code: Select all

  ctx.font = height*0.75 + "px Helvetica Neue";
  ctx.textBaseline="middle";
  ctx.fillStyle="green";
  ctx.fillText("Hello World");
This would print "Hello World" in green in the middle of the canvas.
But it wouldn't work without the "ctx"

"gwc" likely serves a similar purpose.