Welcome to the Achaea Forums! Please be sure to read the Forum Rules.

JavaScript for Nexus Newbies Part 1

TaelTael Member Posts: 1,197 @ - Epic Achaean
edited March 2016 in Client Help
I've been helping people with Nexus in a piecemeal way in posts and messages for a while. At some point I said I would write a tutorial series. This is the first part in a series of scripting tutorials for Nexus.

This post is going to explain some basic concepts that will be useful, then walk you through scripting your first alias. Along the way we'll learn some basics of JavaScript and regular expressions.

I'm only going to use examples of scripts that I actually use. This isn't just going to be a bunch of useless "Hello World" scripts or toy examples or demonstrations of how to compute a factorial. The alias in this first tutorial is an alias I currently use every single time I play Achaea.

In the long run, we're going to learn the basic features of JavaScript, basic regular expressions, some more-advanced JavaScript concepts, and even how to modify the client. I'll probably end it with a detailed look at the design process for a largish module that shows off most of what I talk about.


Preamble

Nexus is a client, but it's not a client like Mudlet.

Mudlet is a separate program that connects to the game and displays it. It's scripted in a language called Lua, which is a common embedded language for applications (e.g., you use Lua to write addons for WoW).

Nexus is basically just a webpage, albeit a very complicated one.

That means that everything you see in Nexus is just normal HTML. Those lines of text you're seeing from the game? Those are just normal <p></p> paragraphs. If you want to see some HTML, you can see it on the forums - just start typing in a reply and click the little </> button in the editor controls. In fact, any modern browser will let you see the HTML source of any page: in Chrome, press ctrl+shift+j and go to the Elements tab. You won't need to understand much, if any, HTML to do most scripting, but the point is that the Nexus client isn't some mysterious, inscrutable thing, it's just a normal webpage, so don't be intimidated!

So HTML defines what you see in the game, but what you see changes as you interact with the game: you send messages, messages arrive and appear, the map changes, you click on tabs, and all sorts of things. The thing that allows the HTML to change is JavaScript.

The whole client is run on JavaScript. JavaScript is what adds a new paragraph to the main output when the game sends you a line. Your scripts are JavaScript too. This is very powerful: it means your scripts can interact with any part of the client. Nothing is denied you.

In fact, the whole internet runs on JavaScript. Any page you go to where things change? That's JavaScript. Google Docs? JavaScript. Amazon? JavaScript. Most pages you go to where things don't change are built using JavaScript too. JavaScript isn't a dinky little special purpose language for scripting in Achaea, it's a fully expressive programming language, and probably the most popular one in the world at that. Learning any programming language is useful - learning Lua will get you a long way toward learning any other language that's more general-purpose. And regular expressions are used in every programming language (and can even be used in most word processing programs!).

But JavaScript is the language of the web. Learning to write JavaScript for Achaea is taking the first steps in learning an actual language that's actually used all over the place. Learning JavaScript could literally get you a job. This is not a waste of time.

Also, it's fun.

Maybe.

Disclaimer

JavaScript is a gigantic language. There are a lot of really questionable things in it. I am going to go as far out of the way as possible to avoid all of them. Some people get macho and defensive and insist on using bad things. These are the same people who refuse to use safeties on firearms because they insist that they have perfect trigger discipline (and they can't be bothered to flick a switch just in case).

Responsible gun owners know not to associate with these people.

Get On With It: My First Real Alias

You've probably made a basic alias already. You type in a pattern, set it to "Begins with" or "Exact match", and tell it to send the game some text.

Today, we're going to make a simple alias that does exactly that. But we're going to do it with a regular expression pattern and we're going to send the commands using a script. This is a real alias that's really useful.

The alias we're going to create today is for doing some stuff before quitting. Every time I quit, I want to make sure everything's in my rift and my timeout is back to normal (so I can never forget to change it back).

I'm going to set the Alias name to Quit.

Now I'm going to change the Matching type to Regular expression.

Finally, I'm going to change the drop-down menu from Send a command to Execute script.

My First Regex

Regular expressions seem confusing. They've got all sorts of weird symbols in them. But really, they're pretty simple. All the extra symbols do is let us match things we don't have keys on our keyboard for. We don't have a "beginning of the line" key, so regex gives us a symbol in case we want to match that. We don't have an "s or w" key, so regex gives us a symbol in case we want to match that.

Our first regex pattern is going to be:
^quit$|^qq$
The idea here is that we want to make sure this happens whether we type quit or we type qq.

Let's break it down:

First, let's look at that vertical bar | (commonly called a "pipe" character) in the middle. Pipes in regex mean "or". So we're saying that this alias should fire if it matches the stuff before the pipe or the stuff after the pipe. It should match whether we type quit or we type qq.

Next, let's look at have the caret or "hat" ^. This just means "the beginning of the line". We don't want this alias to go off just because we did say I hope you're not serious about quitting Mhaldor!. We only want our alias to go off if quit or qq comes right after the start of the line.

Next, let's look at the $ character. This is just like the ^, but it means "the end of the line". We don't want this alias to go off just because we did quit Mhaldor. We only want our alias to go off if quit or qq come right before the end of the line.

Finally, we have the actual letters quit and qq. These just match one-for-one in the obvious way.

So what this regex says is:

Fire my alias when:
You see the beginning of the line (^) followed by the characters qui, and t (quit) followed by the end of the line ($)
OR (|)
You see the beginning of the line (^) followed by the characters q and q (qq) followed by the end of the line ($)

So that's our pattern. Notice that without regular expressions we would have needed two aliases to do this: one for quit and one for qq. Having two aliases that do the same thing is lame! Regex forever!

My First Script

First, let's make sure our regex is right. We'll do this by printing some stuff to the screen whenever it fires.

Before you do that, notice how the script box has a little line in it already? You can delete that, but see how it starts with //?

Anything that comes after // on a line isn't part of your script. Nexus will just ignore it. This lets you write yourself notes in your code. These notes are called "comments". Our script is simple enough that it doesn't really need comments, but they're invaluable once you start writing more complicated things. Your really complicated script might make perfect sense to you now without comments, but if you decide you want to change some part of how it works six months from now, you're really going to wish you had left yourself some comments explaining how the more complicated parts of it work.

So now, displaying something to the screen. Here's our code, explained below.
// This is a comment
display_notice("My alias fired!", "red"); // This is a comment too
// Guess what this is
Before you read the explanation, save your alias, go back to the main game tab, and type quit or qq (try both). It should add the line My alias fired! to your display in red. Make sure that works before continuing.

...

First, look at those comments. Notice how you can put them on their own lines or after the code on your line, like in the second line.

Okay, so we wanted to display some text. The way we do this is using the display_notice function. This is a function built into Nexus (it's part of the Nexus "API" - a list of functions that are there for us to use in scripts).

IMPORTANT DISTINCTION: The Nexus client has a totally different kind of thing called a "function" too as a type of reflex. This is confusing. Whenever I'm talking about the weird Nexus client "function" reflex, my distaste for the decision to name it "function" will be clear. If I don't seem suitably irate, I'm not talking about the weird Nexus reflex "function" things, I'm talking about regular JavaScript functions.

Now when we want to use a function we call it. This is a weird terminology choice that basically just means "run". The original metaphor was that it was like giving a librarian (your web browser in this case) a call number for a library book so they could go get it for you and you could read it (well, really so that the librarian could read it - it was never a great metaphor).

We call functions by sticking some parentheses after their names. So to call display_notice, we use display_notice(). But just calling it won't do much good - we've got to tell it what text to display!

We do this by passing arguments to the function. Imagine that your function is a mixing bowl you can call to make dough. If you want to make dough, you can't just use the mixing bowl - you've got to pass it some ingredients when you call it. So when you want to make some dough, you pass in flour and water as the arguments to your mixing bowl function.

So we need to pass in the text we want to display. In virtually all programming languages, when we're working with text it needs to be in quotes. This kind of text is called a string (it's a "string" of characters). The reason for this is that the browser needs to know the difference between referencing a function called login and using the actual text "login". We don't want to display something called My alias fired!, we want to actually send the text (the string) "My alias fired!".

While we're at it, we pass in another string that says "red" as the second argumentdisplay_notice takes three arguments separated by commas: the text we want to display, the color of the text, and the background color for the text. You can leave off either of the colors and it'll just use the defaults, so we left off the background color and let it use the default background color.

Finally, we stick a semicolon after the function call. This is just the programming equivalent of a period. They're technically optional in JavaScript (if it sees a line end in a place where it makes sense, it invisibly sticks in a semicolon for you). They are not optional for you. Always use semicolons. Don't leave the safety off because you have good trigger discipline. I promise you that getting to avoid moving your pinky finger a few millimeters isn't worth the time you spend fixing bugs from your mistakes.

You'll find yourself writing these display_notice calls all the time. When your alias or trigger isn't working and you can't figure out why, you'll replace its script with a display_notice call to see whether the problem is the script or the regex (is it messing up because there's an error in what you're telling it to do, or because it's not firing in the first place?).

Now that we're sure our regex works, let's make the actual script we're going to run:
send_command("inr all");
send_command("config timeout 15"); // I set my timeout to 15, you should change this to whatever you use
This should look pretty familiar. We're just calling send_command (another part of the Nexus API) and passing it some text (some strings) for it to send to the game.

Save this and try it!

...

Uh oh, wait - it inrifted everything and set the timeout, but it didn't actually quit!

When an alias fires, it consumes whatever line caused it to go off. So when we typed quit into the game, it fired our alias instead of sending the text quit to the game.

That should be easy to fix: we just need to add another send_command:
send_command("inr all");
send_command("config timeout 15"); send_command("quit");
Save this and try it!

...

Nexus is probably mad at you now.

What happened is that you created an infinite loop: your alias fires whenever it sees quit, and your alias sends quit, which causes your alias to fire, and your alias sends quit, which causes your alias to fire, etc.

What we need is a way to tell Nexus to just send the damn text - forget about checking whether it matches any aliases.

This is the purpose of the second argument to send_command. Solving our problem looks like this:
send_command("inr all", true);
send_command("config timeout 15", true); send_command("quit", true);
Here we passed send_command a second argument, truetrue and false are special things in JavaScript (sometimes called "Booleans" for esoteric reasons you shouldn't worry about). They mean exactly what you think they mean.

Passing true to send_command as its second argument tells it to ignore aliases. When you don't pass send_command a second argument, it defaults to false. This is a sort of unfortunate default: you should always pass true as the second argument any time you call send_command. Even if you don't have an alias that interferes with whatever you're sending right now, in six months when you've forgotten about this script, you might make one and suddenly you have to go figure out what's going wrong with your Quit alias and you've got to go fix this script. Use the safety.

And there you go! You're a programmer now!

If people have particular things they'd like to see or questions, feel free to add them below. I'll keep an eye on this thread and try to answer anything I can.
JhuiAtalkezKlendathuFrederichRakkaroKietTectonSarapisRomKyriellaValkynSirixiMathildaAntidas

Comments

  • RakkaroRakkaro Member Posts: 10
    Hello. First of all, thanks for doing this. Now, I'm trying to make an alias for archery, which will aim in all directions looking for my target and snipe them if it finds them. Having no idea about IFs and labels etc, I'm kinda lost. Could you show a rough sketch of such an alias, one that I can start building from whilst having 0 knowledge of the subject? Thanks.
  • TaelTael Member Posts: 1,197 @ - Epic Achaean
    edited March 2016
    Rakkaro said:
    Hello. First of all, thanks for doing this. Now, I'm trying to make an alias for archery, which will aim in all directions looking for my target and snipe them if it finds them. Having no idea about IFs and labels etc, I'm kinda lost. Could you show a rough sketch of such an alias, one that I can start building from whilst having 0 knowledge of the subject? Thanks.
    That is not at all as trivial to set up as it seems like it might be.

    The problem you're going to run into is that you can't just send all the aim commands at once. The naive solution is to have an alias that aims in every direction and a trigger that fires on a successful aim and sends the snipe command. If you do that, you'll end up aiming in the right direction at some point, but by the time your snipe gets sent, you've already aimed in another (wrong) direction.

    I'll try to keep it in mind as a potential future tutorial. I'm trying to avoid class-specific scripts for now, but I might make an exception since automatic aiming for archery/sniping is a pretty common Mudlet script. If I do it, it'll be a little bit out though since it's going to involve variables, arrays, GMCP (potentially - if you wanted to do it the best way), triggers, and enabling/disabling reflexes. It's a pretty good candidate for a later tutorial though, especially since there are a few non-obvious optimisations you can make to a snipe script that would probably make for a good lesson.

    It's not something you can build with 0 knowledge. Though hopefully if you follow along with these tutorials, you won't have 0 knowledge!
  • AhmetAhmet Wherever I wanna beMember Posts: 3,370 @@ - Legendary Achaean
    Aim all directions. Trigger for successful aim. Re-aim that direction. Send snipe target direction based on where you aimed.
    Huh. Neat.
  • TaelTael Member Posts: 1,197 @ - Epic Achaean
    It's been a while since I had aim or wrote one of these aim/snipe scripts for Mudlet - does aim include both the target name and the direction when it succeeds?

    If so, then yeah, you could do it quick and dirty like this with a trigger, though you'll need to know a little more regex to extract the direction from the successful aim message (I'll probably cover this aspect of regex in the next tutorial!).
  • AhmetAhmet Wherever I wanna beMember Posts: 3,370 @@ - Legendary Achaean
    Tael said:
    It's been a while since I had aim or wrote one of these aim/snipe scripts for Mudlet - does aim include both the target name and the direction when it succeeds?

    If so, then yeah, you could do it quick and dirty like this with a trigger, though you'll need to know a little more regex to extract the direction from the successful aim message (I'll probably cover this aspect of regex in the next tutorial!).
    No, but given that you'll be aiming in the same set of directions you can easily implement a counter. No regex required.
    Huh. Neat.
  • RakkaroRakkaro Member Posts: 10
    Thanks for your answers. I'm trying to scrap around info to at least try to make it, look forward to more tutorials as well.
  • SarapisSarapis Member, Administrator Posts: 3,409 Achaean staff
    This is a great intro, thanks @Tael!
    Utianima
  • KronimyrKronimyr Member Posts: 14
    This is great, thanks @Tael
Sign In to Comment.