Looking for more active discussion? Join our Discord at https://discord.gg/x2s7fY6

A "How to" on Scripting

edited February 2015 in Tech Talk
This is for those people who want to learn how to code but can't seem to find help or just plain can't understand or stay awake to read how those manuals are coded. I'll break down what I've learned So that even the most novice of people at scripting can understand.

First the basics. There are a couple of scripting terms you need to know before you start any scripting. These are Aliases, Variables, Triggers, Functions, and Keybindings.

In laymen's terms:
Alias: Just another way of doing something that would take a lot of typing in a shorter or different way.

Example: You could set an alias to do sip health just by typing 'sh'.

(note) You can also perform complex scripts with aliases. More on that later.

Keybindings: In essence they are the same thing as an alias only instead of typing it out and hitting enter. You press a key or combination of keys to run your script.

Example: You can make it to where if you press "F1" to sip health.

Variables: A way to store and call upon information. I think of it like a bank or safe. Makes it easy to do multiple different things that involve the same information.

Example: You can set a variable called 'amount'. And use an alias to do.
outc 'amount' herb
put 'amount' herb in pot
boil pot for 'concoction'
fill emptyvial from pot (this 'amount' of times)

Triggers: This is a powerful tool that actually reads what you are being shown on your screen and when you've triggered something that matches what you see, the script automatically happens. No touching of the keyboard is necessary other than actually scripting it.

Example:
What you want to the trigger to 'fire' at of: You can eat some more irid moss.
What you want the script to do: Eat moss.

This will send 'eat moss' just as if you had typed out 'eat moss' every time the game sends you the line 'You can eat some more irid moss'.

(note) Actually making this trigger will make you eat a ton of moss. So I don't recommend making it do that.

Function: Now a function is a little more complex. Functions are specific words or phrases used by your client that do specific things. I know this is probably a bad explanation of a function, but I'm not entirely sure how else to explain it.

Cmud example: #show hi there!
If you are using Cmud the function #show will show whatever is to the right of #show, but won't actually send it to the game, on your screen, just as if you had received the line from the game. In this case whenever the trigger, alias or keybinding attached to this script happens you will see "hi there!" pop up on your screen.

Mudlet example: echo("hi there!")
Same thing as the Cmud example, but mudlet uses parenthesis and quotation marks instead of just having it next to it. Also not you need a space inbetween #show for cmud and in mudlet you need to not have a space there.

(note) no one but you will actually be able to see this, unless you have someone reading over your shoulder.

Notice that each client has its own functions you've got to learn. And I'm not going to go through every single function. But I will teach you enough to do almost anything in Aetolia.

Aside from actually knowing what functions do what. That is everything you need to know about how to script. Will be posting those below.
AlderaAreka

Comments

  • edited February 2015
    Please note that the examples I give are extremely basic. When making your own system and scripts, you'll want to do more than just the examples I give.

    First up is the 'if' functions, also known as 'if' statements. This is the bread and butter of coding. With it you can do almost anything. And here's an example of how they work.

    (note) When talking about triggers, when I say the word 'pattern' it refers to when you want your script to fire. Also 'script' is what you want it to do. For aliases, 'pattern' is what you want to type in instead of what you want your 'script' to do.

    The following is an example of when you see the words 'You are afflicted with paralysis' and you want to eat a bloodroot to cure it.

    In Cmud:
    Trigger pattern:
    You are afflicted with paralysis.
    Trigger script:
    #if (@herb_balance = yes) {outc bloodroot;eat bloodroot}

    Notice I put @herb_balance, this is a way to tell Cmud that you want to only eat a bloodroot if you have herb balance. Also it is in parenthesis. You'll need to put what you want Cmud to compare in order to fire what you want it to do in parenthesis for it to work.

    In Mudlet:
    Trigger pattern:
    You are afflicted with paralysis.
    Trigger script:
    if herb_balance == true then send("outc bloodroot")
    send("eat bloodroot")
    end

    Also notice for mudlet I put two == to tell it to compare. This is not a typo. For mudlet, if you want to ask it to compare something you put two, if you are trying to set a variable you put only one.

    Now how about we tell it when we have herb balance so it knows when its okay and when its not.

    To set a variable:
    Example trigger in Cmud:
    Trigger pattern:
    You can eat another plant.
    Trigger script:
    herb_balance = yes
    Trigger pattern:
    You eat a bloodroot leaf.
    Trigger script:
    herb_balance = no

    Now our trigger for eating bloodroot will work and eat only when you your herb balance variable is set to yes. There are more efficient ways to track herb balance, such as gmcp which I will go over later.

    In mudlet:
    Trigger pattern:
    You can eat another plant.
    Trigger script:
    herb_balance = "yes"
    Trigger pattern:
    You eat a bloodroot leaf.
    Trigger script
    herb_balance = "no"

    Notice that I put the yes and no in quotation marks. This is needed in mudlet when asking it to compare words.

    Now I shall discuss wildcards.
    Just as in poker, a wildcard can be anything.

    The wildcards for Cmud are:
    (%w) - to match any word in a line
    (%d) - to match any number in a line
    (*) - to match anything in a line
    (%s) - to match any number of blank spaces
    %1, %2, %3 - to match a script
    ^ - to where the beginning of a line should start.
    $ - to tell cmud where the end of the line should be.

    Examples:
    Trigger pattern:
    ^You are afflicted with (*).$
    Trigger script:
    #if %1=paralysis and @herb_balance = yes {outc bloodroot;eat bloodroot}

    Trigger pattern:
    ^You collect (%d) gold from the (*).$
    Trigger script:
    #show I just got %1 gold!
    #if %2 = corpse {#show I kicked that creatures behind!}

    The wildcards for mudlet are:
    (\w+) - to match any word in a line
    (\d+) - to match any number in a line
    (\s+) - to match any number of blank spaces
    (.*) - to match anything in a line
    (.+) - to match any word phase and space after the last word entered until the next word entered.
    matches[2], matches[3], matches[4] - to match a script
    ^ - to where the beginning of a line should start.
    $ - to tell mudlet where the end of the line should be.

    Examples:
    Trigger pattern:
    ^You are afflicted with (.+).$
    Trigger script:
    if matches[2] == "paralysis" and herb_balance == "yes" then
    send("outc bloodroot")
    send("eat bloodroot")
    end

    Trigger pattern:
    ^You collect (\d+) gold from the (.*).$
    Trigger script:
    echo("I just got ".. matches[2].." gold!"))
    if matches[3] == "corpse" then
    echo(" I kicked that creatures behind!")
    end

    Also note that in mudlet in order for wildcards to be fired off of, you must set the type of trigger to perl regex.

    In both of these when you see, You collect 'this amount of gold' gold from the corpse. From the game your client whether it be cmud or mudlet will also add the text "I just got 'the amount of gold you got' gold!" as well as "I kicked that creatures behind!"
  • edited February 2015
    Sometimes you don't want to see a particular line over and over again. If that happens you can actually make it to where you won't ever see those lines again.

    In Cmud the command for this is: #gag
    Example Trigger
    Pattern:
    ^You see no @plant in your inventory.$
    Script:
    #gag

    Notice I put @plant in the pattern line. Cmud will match that to any word you have set to the variable plant. And you'll never see that line ever again no matter what plant you have, as long as that word is part of that variable. Also note that should you see anything other than what is in your plant variable the line will still appear on your screen.

    In mudlet its a little different. Also note in mudlet you cannot match variables unless you use a wildcard and if statement/function.
    Pattern:
    ^You see no (\w+) in your inventory.$
    script:
    selectString(matches[1], 1)
    deleteLine()

    That will make it to where you never see that line again no matter what word is used. To change this to only when its showing you a plant name you'd modify the script to be.

    selectString(matches[1], 1)
    if matches[2] == plant then
    deleteLine()
    end

    You can also make it to where you see your own special lines instead of the ones the game gives you. For the same trigger you could modify the scripts to:

    In cmud:
    #sub {You ain't got no %1 homie!}

    (note) yes I know the new line would be retarded... but its just an example. Also the grammar is just plain awful.

    in mudlet:
    selectString(matches[1], 1)
    if matches[2] == plant then
    deleteLine()
    echo("You ain't got no "..matches[2].." homie!")
    end


    You can also add color to your new lines with these functions.
    In Cmud: #cw or %ansi
    Example
    Pattern:
    ^~(guild~)
    Script:
    #cw blue

    The cw stands for color word. Also note the tildes is to keep cmud from thinking its part of a script and you want it to fire only when you see (guild) exactly.

    For %ansi you could use;

    Pattern:
    ^You have regained balance on all limbs.$
    Script:
    #sub {%ansi(white)Balance gained: %ansi(yellow) ALL LIMBS}

    This would make it to where you'd no longer see You have regained balance on all limbs. And instead you'd see.
    (in white) Balance gained:(in yellow) ALL LIMBS.

    For mudlet the function is the same as deleting it above but putting cecho("") with it as well
    Pattern:
    ^You have regained balance on all limbs.$
    Script:
    selectString(matches[1], 1)
    deleteLine()
    cecho("/nBalance gained:ALL LIMBS")

    This is the mudlet equivalent of the cmud example above. Also note the \n makes it to where it appears on its own line.
  • edited February 2015
    Both clients also have a sense of time! Meaning you can have scripts go off after a certain amount of time has passed.

    For Cmud the functions are: #wait and #alarm
    When doing #wait you must specify the number of "milliseconds" after it and then tell it what to do. For those of you who don't know a millisecond is 1/1000th of a second. So to wait 1 second you'd put #wait 1000.

    Example Alias
    Alias pattern: confuse (%w)
    Alias script:
    slap %1
    #wait 5000
    kiss %1

    In this example, if you did confuse omei. Aside from getting pimp-slapped with a moonlight spear by Her, you'd also slap her and if you lived that long, you'd also kiss her.

    (note) I seriously recommend not doing this to @Omei.

    You can also accomplish the same thing by doing it this way. Yes Cmud is full of redundancies, but its more powerful in what it can do.

    Script:
    slap %1
    #alarm {+5} {kiss %1}

    It is also important to know that if you put the time for alarm in a specific format then your scripts will go off every time they are fired at certain periods of time, in the format {hh:mm:ss} h is hours, m is minutes and s is seconds.

    Examples:
    #alarm {+00:01:00} {bellypoke} - makes you bellypoke yourself after 1 minute and won't fire again.
    #alarm {-00:30:00} {sip health} - makes you sip health every 30 minutes
    #alarm {12:00:00} {whistle} - makes you whistle at 12pm local time.

    In mudlet to accomplish this you'd make an alias.
    Pattern: ^confuse (\w+)$
    Script:
    send("slap "..matches[2])
    tempTimer(5, [[send("kiss "..matches[2])]]

  • edited February 2015
    Alright remember that concoction alias I was talking about variables before? Well now we are going to write a legitimate script for it. For Cmud I can actually have it auto fill any amount of vials as well. However, while its possible to have it auto fill in mudlet, I don't know how to do that just yet. I only know its possible because @Xemnas wrote that part in for me on my mudlet system. Thanks again bud!

    Alright Cmud alias:
    Pattern: amount
    Script: amount = %1

    Pattern: chealth
    Script:
    outc @amount valerian
    outc @amount valerian
    outc @amount goldenseal
    outc @amount ginseng
    outc @amount myrrh
    inpot @amount valerian to pot
    inpot @amount valerian to pot
    inpot @amount goldenseal to pot
    inpot @amount ginseng to pot
    inpot @amount myrrh to pot
    boil pot for health

    Trigger Pattern: With a sudden slow pulsing of white light, the liquid and floating bits of plant matter in the pot combine into {an|a} {elixir|salve} of (%w)
    Script: #loo @amount {fill emptyvial from pot}

    With these 2 aliases and this 1 trigger working together. By typing amount 10 and then chealth, you will automatically make and fill 10 empty vials should you have the ability in concoctions and at least 10 empty vials.

    To accomplish the same thing in mudlet without the auto fill you'd do:
    Pattern: ^amount (\d+)$
    Script: amount = matches[2]

    Pattern: ^chealth$
    Script:
    send("outc "..amount.." valerian")
    send("outc "..amount.." valerian")
    send("outc "..amount.." goldenseal")
    send("outc "..amount.." ginseng")
    send("outc "..amount.." myrrh")
    send("inpot "..amount.." valerian to pot")
    send("inpot "..amount.." valerian to pot")
    send("inpot "..amount.." goldenseal to pot")
    send("inpot "..amount.." ginseng to pot")
    send("inpot "..amount.." myrrh to pot")
    send("boil pot for health")

    Now technically you can make a trigger off the line that you get when your health elixir is finished boiling, and script;
    if amount > 0 then send("fill emptyvial from pot")
    amount = amount - 1
    end

    And then make a trigger off the line you get when you fill your first vial and say
    if amount > 0 then send("fill emptyvial from pot") end
    amount = amount - 1
    end

    And it would actually fill your vials. But just not all at once, you'd have to wait for each line to fire. It is still kinda fast. But you could accomplish the same thing just by typing it yourself and holding down the enter key till you run out faster.

    That was also an example of math in mudlet. For Cmud to accomplish the same things you'd do
    if @amount > 0 {fill emptyvial from pot}
    amount = @amount-1
  • edited February 2015
    Also note that you can also do multiplication, division and addition as well in the same formats. By using the / symbol for division, * for multiplication, and + for addition
  • edited February 2015
    Something else that I forgot to mention a little earlier, that goes hand in hand with if statements are the elseif and switch statements. Essentially they work the same way, but writing if or #if this and if or #if that, a million times can get rather tedious. So these are made to make long scripts a little less time consuming and a little easier.

    Examples for cmud:
    Trigger: ^You are afflicted with (*).$
    script:
    #sw (%1= paralysis and @herb_balance = yes) {outc bloodroot;eat bloodroot}
    _tab_(%1 = asthma and @herb_balance = yes) {outc kelp;eat kelp}
    _tab_(%1 = berserking and @herb_balance = yes) {outc lobelia;eat lobelia}

    What this accomplishes is, every time you see yourself afflicted with something it will first check if its paralysis then it will see if you have herb balance and if both of those conditions are met then it will eat bloodroot. And stop there. Otherwise it'll move on to the next one and if both of those conditions are it'll eat kelp, so on and so forth until you run out of things to check. Writing it this way also is a lot faster for your system than scripting it this way.

    #if (%1 = paralysis and @herb_balance = yes) {outc bloodroot;eat bloodroot}
    #if (%1 = asthma and @herb_balance = yes) {outc kelp;eat kelp}
    #if (%1 = berserking and @herb_balance = yes) {outc lobelia;eat lobelia}

    The reason being is that if you were to write it this way. Not only would you be trying to eat three herbs at a time if you were afflicted with all three of these, but it would have to check every if statement individually without stopping every time you saw it.

    To accomplish the same feat in Mudlet, you'd write it this way.
    if matches[2] == "paralysis" and herb_balance == "yes" then
    send("outc bloodroot")
    send("eat bloodroot")
    elseif matches[2] == "asthma" and herb_balance == "yes" then
    send("outc kelp")
    send("eat kelp")
    elseif matches[2] == "berserking" and herb_balance == yes then
    send("outc lobelia")
    send("eat lobelia")
    end
  • Sometimes you may find yourself in a bit of a pickle and need to turn off a specific aspect of your system but you don't want to go in and manually do it. In cmud you can use the #T+ and #T- functions straight from the command line to enable and disable groups of your triggers all at the same time.

    So for example if you needed to turn off your curing system and all of your curing triggers were in a folder (or class as they are known in cmud). And it was called Curing. Then to disable it you'd type in #T- Curing and when you are ready to re-enable it you would use #T+ Curing.

    To do the same thing in Mudlet you'd do enableGroup("Curing") and disableGroup("Curing") respectively. Also not that if you do enableAlias() disableAlias(), enableTrigger() and disableTrigger() you can enable and disable specific triggers and aliases without having to disable the entire group. The catch though is you'd have to make a script or alias to do it from the command line.
  • Sometimes your variables can be a long list of things and you need your client to search for them. The %ismember() and table.contains() functions can be used for this purpose. Here's an example of how they are useful.

    For Cmud:

    Trigger: ^You are afflicted with (*).$
    Script: #addi afflictions %1
    #sw (%ismember(paraysis, @afflictions)) {outc bloodroot;eat bloodroot}
    _tab_(%ismember(stupidity, @afflictions)) {outc goldenseal;eat goldenseal}

    (note) The #addi function adds what you tell it to, to a variable in list form.

    For Mudlet:
    Trigger: ^You are afflicted with (.+).$
    Script: table.Insert(afflictions, matches[2])
    if table.contains(afflictions, "paralysis") then send("outc bloodroot")
    send("eat bloodroot")
    elseif table.contains(afflictions, "stupidity") then send("outc goldenseal")
    send("eat goldenseal")
    end

    (note) The table.insert() function performs the same as the #addi function in Cmud.
  • The next topic is the opposite of the last topic I covered. Removing items from a list.
    For Cmud: #deli and %pop are God sends.
    Example Trigger: ^You have cured (*).$
    Script; #deli afflictions %1

    %pop does something a little different. While it does take the first item from the variable specified. It will also send it to the game as a command.
    Example Variable: command_list = {say hi|say how are you?|say bye}
    Example Alias: sayhi
    Script: %pop(command_list)

    The first time you'd put in sayhi it would send to the game say hi. And your character would say hi.
    The second time you'd put in sayhi it would send to the game say how are you and your character would say how are you?
    The third time you'd put in sayhi it would send to the game say bye and your character would say bye.

    To accomplish the same thing in mudlet you'd first set your variable.
    Command_list = command_list or {"say hi", "say how are you", "say bye", }
    Alias: ^sayhi$
    Script: send(command_list[1])
    table.remove(command_list, 1)

    (note) for the send command, the [1] in the bracket sends the first item, for the table.remove function the 1 removes the first item in the list. Sometimes you won't know where in the list a specific item is. In that case you can use a second function in conjunction with table.remove like so:

    table.remove(command_list, table.index_of(command_list, "say hi"))
  • And last, but certainly not least. The mother of all information gathering. I am of course talking about gmcp.

    Now gmcp isn't actually a function. Gmcp, is how the aetolian servers see your status. It is a plethora of knowledge, that can be read and used by both Cmud and Mudlet. So you can gather information about your character at server side speeds. It really doesn't get any faster than that. Now I 'could' go into every single little detail about gmcp. But, not only would I probably confuse you, but this thing is long enough as is. So instead i'll give you a few useful items that will work for both cmud and mudlet in the same format! As you can see from above... that doesn't happen ... like at all... other than with gmcp. That just goes to show you how special it is. Thanks IRE for providing this service.

    health = gmcp.Char.Vitals.hp
    mana = gmcp.Char.Vitals.mp
    max_health = gmcp.Char.Vitals.maxhp
    max_mana = gmcp.Char.Vitals.maxmp
    percent_health = (health * 100) / max_health
    percent_mana = (mana * 100) / max_mana
    balance = gmcp.Char.Vitals.balance
    equilibrium = gmcp.Char.Vitals.equilibrium
    sipBalance = gmcp.Char.Vitals.elixir
    mossBalance = gmcp.Char.Vitals.moss
    salveBalance = gmcp.Char.Vitals.salve
    treeBalance = gmcp.Char.Vitals.tree
    focusBalance = gmcp.Char.Vitals.focus
    renewBalance = gmcp.Char.Vitals.renew
    pipeBalance = gmcp.Char.Vitals.pipe
    herbBalance = gmcp.Char.Vitals.herb
    affelixirBalance = gmcp.Char.Vitals.affelixir
    bleeding = gmcp.Char.Vitals.bleeding
  • Well that's pretty much everything. If anyone ever needs any help with scripting Cmud or Mudlet let me know and i'll do what I can
  • Thanks a bunch @Plato this is a great help for someone trying to figure things out without pestering someone to death over every little detail.
  • Welcome. I hope its detailed enough to where you can understand all of it. You should be able to do just about anything with all of this. Heh I spent 8 hours writing it all. One final piece of advice though. Try to keep your script clean and organized. I can't tell you how many times I've seen people write code that looks like.

    if k in ipairs (k,v) == banana do squash bug
    elseif spacebar is not alien then punch myself in the face cause this script is so bad.
  • @plato One of the things I don't see or perhaps I'm just not putting it together a macro that holds a variable. Something basic even like a target for hunting.

    Now I get that it would probably start as ^t (.*)$ this way it would hold either a name or number for this purpose. How would I get it to hold the wildcard or set it as a variable for other purposes?
  • edited February 2015
    ah well to be honest I don't use macros all that often. But yea you aren't putting it together. I said at the beginning that Aliases and keybinidngs aka. macros work the same way. So you'd write/code it the same way you would an alias. That alias is in mudlet format so I assume you are working with mudlet.

    So in mudlet you'd assign the keybinding to whatever you want and then for the example you gave you'd script it like this

    target = matches[2]

    You could also put in there a variable for auto attacking like

    if auto_attack then send("insert hunting attack here" ..target)

    but you'd also have to set up a way make your auto_attack variable true or false. Which I actually have set with aliases.

    In the examples above I used yes and no instead of true and false, but if you used yes and no then it'd be more like

    if auto_attack == "yes" then send("insert hunting attack here"..target)
  • One thing I didn't see (though I will admit I didn't go over it thoroughly) was mention of GMCP. So in addition, I'll add a little blurb about it.

    GMCP
    ------

    GMCP is something that will allow your client to learn a bit about your character, items, location etc from the Aetolia servers. Since Mudlet is what I use, I can only help with that, if anyone uses anything else and can give some input, go for it! First things first, we need to be able to see what information we can grab from GMCP for use in our systems. This can be accomplished two ways:

    1) Head to the Iron Realms Page on the matter, where you will find an easier to read list of some of the things you can access via GMCP or
    2) Create an alias in Mudlet; call it whatever you would like, but have it execute the following command: display(gmcp) Note how gmcp is in lower case, as with all scripting, case does count!

    When using the second method, the output is a little more garbled, but you will get a more practical idea of what exactly is stored in the GMCP database. When you use the display function to look at GMCP you will see the actual stats of your character and get a better idea of what you are working with. I suggest reading through the web page first, then taking a look at the real deal.

    Now for a few examples of what you can do with gmcp. Let's say we want a script that will execute "commune lightning " .. azzkick.target but only execute if we have both balance and equilibrium. Well we can go through and set up a trigger for when we commune lightning to set our EQ variable to 0, then when we gain EQ again set it to 1, then have a trigger checking that every balance to see if we can commune or not, sounds like a lot of work right? I think so too, so instead of doing all that we can make a script:

    function bashCommand()

    if gmcp.Char.Vitals.balance and gmcp.Char.Vitals.equilibrium then
    send("commune boost")
    send("commune lightning " .. azzkick.target)
    end

    As you can see, when this script runs it will first check the gmcp (notice the upper case C on Char and V on Vitals, case is important!) for our balance, then our equilibrium, if they both check out OK, then it will send our attacks out. Now we can set our aliases, triggers, prompt triggers etc to execute bashCommand() and it will check our GMCP state and see if we can actually cast or not, then if we can, go ahead and do it. GMCP can be used to make our lives much easier in many different ways, it can tell us the vNum of the room we are in, items in our inventory, whether we are standing or sitting, and a plethora of other useful information. A little creative thinking, and a lot of tinkering and I think you will find GMCP to be one of the greatest things that ever hit the MUD world.
  • @azzello look at the next to last comment before Aldera's comment
  • Ah well, I tried to be helpful at least. :p
  • and Where plato has been using == "yes" and == "no" in the mudlet stuff you can just have it be true / false much faster respondes that way from mudlet itself.
    Mudlet Bashing System for sale. Message if interested
  • thats correct I actually use true and false myself, makes scripting a little bit faster, but I find people learn easier when you can actually read like you would the game itself instead of using terms like true and false.
  • edited February 2015
    Simplifying it even more than that, you don't have to put anything in there for most circumstances a person is going to run across at this level.

    if azzkick.systems.autobash and not azzkick.systems.combat then
    send("commune lightning " .. azzkick.tar)

    This will just check for the standard 1/0 instead of true/false, it works especially well for dealing with gmcp.
Sign In or Register to comment.