Part 1:
http://forums.achaea.com/discussion/4584/javascript-for-nexus-newbies-part-1The thing we're going to build today is the first part of a small package that I actually use. The package is for talking to people OOCly. It's polite to always preface an OOC tell with some indication that it's OOC, but it's also a pain - who wants to type // or [OOC] or OOC: in every single tell or reply? I'm way too lazy for that and given how inconsistent a lot of the people I'm talking to are about marking OOC tells, I think I'm probably not alone in that.
This pair of posts is long, but don't be intimidated. It should be pretty easy to follow and it shouldn't be too overwhelming if you take it step by step. We're not actually doing that much, I'm just trying to make sure it's all fully explained so you can understand what you're actually doing.
Today we'll get the basics of the package done. Next week we'll reorganise it and in the process we'll learn the last of our JavaScript fundamentals (and one pretty advanced concept!). Once we've got the fundamentals nailed down, we can move on to some more exciting stuff like GMCP!
I realise this week's project isn't terribly exciting, but it's still useful and it'll teach a lot. Bear with me!
So first things first, we're going to make a package, so go to the package manager and make a new package. Don't be shy about making packages! Divide your code up into logical packages as often as possible so you can easily share it with people! I called mine "oocly" because I think that sounds more clever than it probably does. Give it a description while you're at it.
The Basic OOC Alias
Let's get started with the basic alias for our tell.
I've decided that I want my command to look like this:
ooc Sarapis hi!
And I want it to send something like this:
tell Sarapis *achaean OOC: Hi!
Since I'm usually speaking my city language and my OOC tells might be to friends in other cities, I decided to make sure my OOC tells are always in Achaean too. Convenient!
So go to the reflexes tab, use the drop down to make sure we're in our new package, and make an alias. I'm going to call mine OOC Tell. Make sure to set the matching type to regular expression and the Action to Execute Script, just like last time.
The Regex Pattern
First we'll need our regex pattern. This one is going to have some new stuff in it. In fact, it's going to have the majority of the regex you'll probably ever use.
^ooc +([\w']+) +(.*)$
Let's break that down just like last time.
First we know that ^ is the beginning of the line and ooc is just the letters ooc. Then we've just got a space - don't get tricked here, that space just means "match a space", exactly like any other letter. We know what the $ at the end is too - that just matches the end of the line.
Now we get to the first new thing. That + is called a
quantifier. The plus quantifier means "one or more of the previous thing". So what we're saying here is "one or more spaces". We want the alias to go off whether we type "ooc Sarapis hi!" or "ooc Sarapis hi!".
Next it starts to get really crazy. Let's look at what's inside that first set of parentheses: [\w']
Brackets like that define a
character class, so if we do something like [aq2], we're saying "match an a, a q, or a 2". Pretty simple! We can also do ranges, like [a-cq2], which matches a, b, c, q, and 2.
Then we have this weird \w thing. This is one of the built-in character classes in regex. \w just means [a-zA-Z_0-9], so it matches any upper or lowercase letter, any number, or an underscore. This is supposed to be a shortcut for "word" characters (hence the "w" in \w).
So what [\w'] means is: any word character (any uppercase or lowercase letter, any number, or an underscore)
or the character ' (there are some denizens with a ' in their name - I'm not sure why you'd want to send them an OOC tell, but whatever!).
Then we see that + again. It means one or more of the previous thing, so we're matching one or more [\w'].
What's that get us? A name! Names are made up of one or more word characters (and sometimes a ' or two).
We put it in parentheses because our script is going to actually need this name. We'll need to know who to send the tell to. Parentheses define a
capture group. It's just a way of saying "okay, whenever this regex matches and the alias fires, please save whatever this part of the match was, because I'm going to need to use it". We'll see how to use it in a second.
So after that capture group, then we've just got one or more spaces again. We don't care how many spaces are between the name and the message of the tell - we want it to go off if you type "tell Sarapis hi!" too.
Then we've got a second capture group. In this one we've got a . and a *
The . is a lot like the \w, but it's even more broad. The . means
any character. It'll match an a or a b or a # or a space or anything at all.
The * is a quantifier just like the +, but instead of meaning one or more, it means zero or more. Actually, maybe this should be a + too, shouldn't it? We don't want to do "tell Sarapis " and have it send Sarapis a tell that says "OOC: ", so let's change that to a +! A bad habit a lot of coders develop is using * when they should be using +, so let's try to avoid that here. Another bad habit is using . when you could use something more specific like \w or another character class, but we really do want to be able to send anything in the body of the tell, so we'll have to keep it here.
So now we've got:
^ooc +([\w']+) +(.+)$
So to sum up, that regex says: "watch for any string that contains, in this order: the beginning of the line, the letter o, the letter o, the letter c, one or more spaces, one or more word character or ' (and save that part of the match for later use), one or more spaces, one or more of any character (and save that part of the match for later use), the end of the line.
Well, that's about enough regex for a lifetime. You know just about all the regex you'll probably ever need now. The only really common thing you might need that wasn't covered here was \d, which is just a shortcut for [0-9], so it matches any number. From now on I'm not going to explain the regex in as much detail. If you need a refresher, google up a regex cheatsheet (there are thousands of them floating around). If you want to learn more in-depth, check out www.regular-expressions.info (you can easily learn
all of regex, including the more-complicated stuff I haven't gone over and won't go over, in one afternoon).
The Script
This should be pretty easy. All we're going to do is call send_command, just like before. But this time we're not going to hard-code-in what to send beforehand, we're going to use those capture groups!
Our script will look like this:
send_command("tell " + args[1] + " *achaean OOC: " + args[2], true);
Remember that send_command takes two arguments separated by a comma. We always want to use true as the second argument to send_command, that way what we're sending is never going to end up triggering some random alias instead of just sending our text to the game.
Let's break down that first argument: "tell " + args[1] + " *achaean OOC: " + args[2]
We know what "tell " means and what " *achaean OOC: " mean. They're just strings, exactly like we sent last time. (Notice those spaces! They're not mistakes! We don't want to send "tellSarapis*achaean OOC:Hi!").
Now we need to get at that stuff in the capture groups from our regex. We do that using the built-in args array. This is an array that capture groups get saved to. Every trigger and alias has its own separate args array. What is an array? Think of it like a bunch of numbered slots. So args[0] is the first slot - JavaScript always starts counting at 0 (Nexus automatically puts the
whole match into args[0], which isn't often very useful, but is occasionally handy). Then args[1] is the second slot in the args array, where whatever matches the first capture group gets saved and args[2] is the third slot where the second capture group gets saved, etc.
(args is an array that Nexus makes and fills for us, but we can actually make our own arrays too and that can be really useful. There's nothing magic about arrays, they're just numbered lists of things. We'll see how to do that some other time.)
Remember that our first capture group was supposed to capture the name of the person we want to send our OOC tell to. So we'll use args[1] for the recipient of our tell.
Our second capture group was supposed to capture the message we actually wanted to send, so we'll use args[2] for the content of the tell we want to send.
The thing our capture groups capture is actual text, which we remember comes in the form of
strings.
So if we do "ooc Sarapis hi!", then args[0] is "ooc Sarapis hi!" (the whole match), args[1] is "Sarapis" (the string that matches our first capture group), and args[2] is "hi!" (the string that matches our second capture group).
So now we've got four strings:
"tell "
args[1]
" *achaean OOC: "
args[2]
But send_command doesn't take that many arguments, it only takes one string. We need to glue all of these together. We do that in JavaScript with +. So "blah" + "bloob" is the string "blahbloob". We just glue all of our four strings together with + and we've got our one string to pass to send_command.
Gluing strings together like this is called
string concatenation. We're
concatenating four strings into one string.
You might be starting to get confused about + here and that's a pretty reasonable confusion. + pulls triple-duty for us now. We can use it to add numbers, we can use it to glue strings together, and it means "one or more of the last thing" in regex. You're not crazy, this really is pretty confusing. Other languages use a different symbol for string concatenation (Lua, like in Mudlet, concatenates with ..) to make this less confusing and avoid some potential errors we'll probably have to talk about later. But for now it should be pretty easy: if + is between strings, it's concatenating them, if it's between numbers it's adding them, and if it's in a regex it means "one or more of the last thing".
So now we should be able to understand our script: we're just gluing together some strings and some stuff from our capture groups and sending it.
Did Something Go Wrong?
I haven't set you up for failure like last time, but maybe something isn't working the way you expect it to. First we want to check our regex with a display_notice just like last time. Let's quickly remind ourselves how to do that and have it display our capture groups too. Our script will look like this:
display_notice("Our alias fired!");
display_notice("Capture group 1: " + args[1]);
display_notice("Capture group 2: " + args[2]);
You should be well-equipped to understand how and why this works by now. If you're not sure, now's a good time to review before continuing.
Just like I mentioned in the last tutorial, you're going to want to get used to writing scripts like this - they'll let you see if your regex is working or if it's just some part of the script that isn't working right.
In fact, here's a useful trick. If your script isn't working, do something like this:
//send_command("tell " + args[1] + " *achaean OOC: " + args[2], true);
display_notice("Our alias fired!");
display_notice("Capture group 1: " + args[1]);
display_notice("Capture group 2: " + args[2]);
Remember the // thing? That's a comment, it doesn't get run. What we just did is commonly called
commenting out code. This way we can test our alias with display notices, but we don't have to worry about typing in our script again. There's a way to comment out multiple lines at once so you don't have to type // at the beginning of every line in a long block of stuff you want to comment out, but I'l leave it up to you to google JavaScript comments if you want to learn that.
Comments
But I'm Even Lazier
Here's the thing: I'm really lazy. I don't even want to have to press shift if I don't have to. I'm used to typing "tell sarapis hi" and having it come out as "Hi.", and I want to type "ooc sarapis hi" and have it come out as "OOC: Hi.". But we have a problem. The problem is that the game's automatic capitalisation of the first letter of our tell won't work here since the first letter of our tell is the O from OOC, not our actual message.To do this, we're going to have to use two new string functions, learn something about arrays, and learn a funky new (very useful) way to call a special kind of function.
Here's what it's going to look like:
Now things are getting weird. Let's break it down.
First, we've got this bizarre args[2][0] thing. Two sets of brackets?! What is the world coming to?!
This is pretty simple actually and reveals one of the great secrets of strings. Strings are secretly just arrays of characters. This is true in almost every programming language (ironically it's a bit of a lie in JavaScript, but we can act like it's true and our lives will be simpler).
So what we're saying with args[2][0] is "get me the third thing in args (our second capture group, the message for our tell), then get me the first thing in that". args[2] is our string and a string is just an array of characters, so the first index in the string is the first letter.
So args[2][0] is just "the first letter (the [0]) of the string in args[2]". That first letter is, of course, a string (a string that's only one character long).
Now we've got this weird funky thing that looks like a function call, but it's strapped onto the back of our first-character string with a period. This is called a method call. Methods are a special kind of function that live on objects. Strings are a kind of object. In fact, basically everything in JavaScript is an object. It's turtles all the way down.
toUpperCase is a method that all strings have. In fact, there are a bunch of methods that all strings have.
If we do "test".toUpperCase(); we'll get back a string "TEST".
The idea here is that this saves us some typing. Instead of toUpperCase being a generic function that we pass in a string like toUpperCase("test"); we can just call it as a method on the string and we don't have to pass it anything. This is nice because if we start chaining things, it's a lot easier to read "test".charAt(2).toUpperCase(); than it would be to read toUpperCase(charAt("test", 2)); so it's pretty nifty. There are some other nice features of methods we'll probably get into some other time and, of course, we'll be able to make our own methods too. Note that the charAt method does the same thing as when we were using the index with the string above, so we could also rewrite that as "test"[2].toUpperCase();
So now we have our uppercase first letter. We'll need to concatenate that with the rest of our message body. But how are we going to get "the message body, but without the first letter"? We're going to use another string method called slice. We call slice on a string and give it the stard and (optionally) the end of the slice of that string we want. We want the slice of the string that goes from the second character to the end, so we do slice(1) remembering that JavaScript starts numbering from 0, so 1 is the second character, and we don't need to tell slice where to end our slice because we just want the whole rest of that string.
So now we have these strings:
"tell "
args[1] // This is our tell recipient
" *achaean OOC: "
args[2][0].toUpperCase() // This is the first letter of our message, but uppercase
args[2].slice(1) // This is the rest of our message
And we'll just glue those together and send them just like before.
A little hairy, but not too bad once we break it down!
Let's Reply
We'll want an alias to reply with too. This should be easy. Let's make another alias (don't forget to set the matching type to Regular expression!).The Regex Pattern
I decided I want my alias to work like this: I'm going to do "oocr hi to you too!" and I want it to do "reply *achaean OOC: Hi to you too!".So this is what my pattern will look like:
I don't need the recipient anymore, so that's all I'll need. Easy, no?
This says: "watch for an input with, in this order: the beginning of the line, the letter o, the letter o, the letter c, the letter r, one or more spaces, one or more of any character (and save that because I'll need it later), the end of the line.
This should make sense now. If it doesn't, time to review.
The Script
The script will look pretty similar too:The main thing to pay attention to here is that our message is now in args[1], not args[2]. We only have one capture in our oocr group now (take another look at the regex to confirm this for yourself).
So now we've just got three strings:
"reply *achaean OOC: "
args[1][0].toUpperCase()
args[1].slice(1)
And we'll just concatenate them into one string and send them. Simple!
Final Test
Just like reply, you'll probably want an OOC retell alias too. I used "oocrt" for it. You should make your own OOC retell alias and make sure it works. If you've been following along, this should be very easy for you to do now.Next Week
That's it for this week. Next time we'll clean this up a bit. We won't change what any of it actually does, but we'll change how we organise it so it's easier to work with. This will let us talk about a few important things and let us develop a framework that will make it way easier when we started developing more complicated packages.