Previous Index Next


12 - Interfaces

Table of Contents


12.1 Introduction to interfaces

Axmud interacts with the world using triggers, aliases, macros, timers and hooks. These are collectively called interfaces.

Interfaces have a stimulus and a response. In other words, when something happens (the stimulus), Axmud takes some form of action (the response).

The Axmud Guide discusses interfaces in detail. In this Section we'll discuss how scripts can interact with their own interfaces.

12.2 WAITTRIG

In Section 4 we introduced the WAITTRIG statement, which creates a temporary trigger. The trigger waits for the world to send a line matching a certain pattern, in this case a line containing the word opens.

    WAITTRIG "opens"

When the trigger notices a matching line, it takes some kind of action. We say that the trigger fires. Until then, the Axbasic script is paused. (In the previous Section we discussed pauses and mentioned that Axbasic scripts with pauses must be run as a task.)

When the temporary trigger created by WAITTRIG fires, it deletes itself and the script resumes execution. (When the script stops running, Axmud will automatically delete any triggers it created.)

In Section 4 we also discussed using a timeout. In this example, if the trigger doesn't fire within 60 seconds, the Axbasic script resumes running (and the trigger is deleted, never having fired).

    WAITTRIG "opens", 60

By the way, there is no WAITALIAS statement, and there are no WAITMACRO, WAITTIMER or WAITHOOK statements. There are better ways of handling those types of interface.

12.3 ADDTRIG

ADDTRIG also creates a trigger, but the script doesn't wait around for a response; it just creates the trigger and then executes the next statement. As a result, the trigger doesn't interact with the script directly.

ADDTRIG expects three arguments - a stimulus, a response and (optionally) a trigger name. If you don't specify a name, Axmud will choose a name for you.

This example creates a trigger that waits for a line containing the word treasure, and then sends the world command get treasure in response.

    ADDTRIG "treasure", "get treasure"

An ADDTRIG statement behaves just like the client command ;addtrigger, except for one important detail - when the Axbasic script stops running, the trigger is deleted automatically.

Of course, if you don't want the trigger to be deleted automatically, you can create a permanent trigger. If you were typing a client command, you would type:

    ;addtrigger -s treasure -p <get treasure>

But in an Axbasic script, you would use a CLIENT statement:

    CLIENT "addtrigger -s treasure -p <get treasure>"

If you're not sure how to compose a client command in the correct format, you can consult the Axmud Guide or just type

    ;help addtrigger

(Note that ;addtrigger is expecting a response that contains no spaces. Because our get treasure response contains spaces, we need to enclose it within diamond brackets.)

12.4 ADDALIAS, ADDMACRO, ADDTIMER and ADDHOOK

ADDALIAS, ADDMACRO and so on behave in the same was as ADDTRIG, and use the same three arguments - a stimulus, a response and an optional name.

    ADDALIAS "^gt$", "get treasure"
    ADDMACRO "f1", "kill orc"
    ADDTIMER 60, "score"
    ADDHOOK "lose_focus", "sleep"

In each case, the interface is deleted automatically when the script stops running.

12.5 DELTRIG, DELALIAS, DELMACRO, DELTIMER and DELHOOK

The opposite of ADDTRIG is DELTRIG.

To delete a trigger, you need to know its name. If you don't know the name, then you need to modify your script to specify one.

    ADDTRIG "treasure", "get treasure", "mytrigger"
    SLEEP 60
    DELTRIG "mytrigger"

DELALIAS, DELMACRO, DELTIMER and DELHOOK behave in exactly the same way. In fact, these five statements are the equivalent of the client commands ;deletetrigger, ;deletealias and so on.

All five statements can be used to delete an interface that was created by a different script, or which you have created yourself. This is behaviour is, in general, not a good idea.

By the way, if you try to delete a trigger that doesn't exist, you'll see a system error message (but the Axbasic script will keep running).

12.6 Interfaces for profiles

Axmud interfaces are available, by default, whenever you connect to a particular world. That is to say, if you're connected to Discworld and you run the following script, the trigger it creates will not be available when you connect to Cryosphere.

    ADDTRIG "treasure", "get treasure"
    END

However, it's possible to tie interfaces to a particular character, so that they're only available when you're playing that character. You could also tie your interfaces to a particular guild or a particular race, so that they're only available when you're playing a character of that guild or race. (This is explained in detail in the Axmud Guide.)

By default, a trigger created with ADDTRIG is tied to the current world profile. It's available whenever you connect to that world, regardless of which character you're playing.

If you want to create triggers tied to a particular character profile, you can use the PROFILE statement.

    PROFILE "gandalf"

After a PROFILE statement, ADDTRIG statements will create triggers tied to that profile. This behaviour continues until you use a different PROFILE statement.

If you want to go back to creating triggers with the current world, just use PROFILE on its own.

    ! This trigger is available to characters who
    ! are members of the thief guild
    PROFILE "thief"
    ADDTRIG "orc", "backstab orc"

    ! This trigger is available to all characters
    PROFILE
    ADDTRIG "troll", "escape"

The PROFILE statement also affects DELTRIG. Ordinarily, DELTRIG deletes the trigger that's tied to the current world profile. If you have specified a different profile using a PROFILE statement, the trigger tied to that profile is deleted instead.

ADDALIAS, ADDMACRO and so on, as well as DELALIAS, DELMACRO and so on, are all affected by a PROFILE statement in exactly the same way. (WAITTRIG isn't affected at all.)

By the way, the Showprofile$ () function returns the name of the profile currently used by the statements ADDTRIG, DELTRIG (and so on).

    PRINT Showprofile$ ()

12.7 Interfaces and main loops

So far, the interfaces we've created don't really interact with script in any meaningful way.

In some respects, BASIC is a poor choice of language for interacting with interfaces. In many scripting languages, a script does nothing until one of its subroutines or functions is actually called.

That's not possible in Axbasic, at least not directly. If you need your scripts to interact with interfaces, you should design them with a main loop, as discussed in the previous Section.

This is roughly how it works:

Of course, dedicated scripting languages like Lua (or Perl, for that matter) handle this kind of thing much more efficiently. For most purposes, though, Axbasic is good enough.

12.8 Interface notifications

Let's create a complete script that creates a trigger and uses a main loop to check it continuously.

The trigger is created with a SETTRIG statement.

    SETTRIG "treasure"

We use a SETTRIG statement because, when the trigger fires, Axmud sends an interface notification to the script. The notification contains information about the fired trigger.

When the script receives an interface notification, nothing happens immediately; the notification is just stored. If the same trigger fires ten times, then ten separate notifications are stored in the order they're received.

When your script is ready, it can retrieve the first notification and take action. That notification can then be discarded. The script can continue retrieving and discarding notifications until there are none left.

12.9 Retrieving notifications

The Ifacecount () function returns the number of interface notifications the script has received (and not yet discarded).

    LET count = Ifacecount ()

If more than one notification has been received, we deal with them one at a time. The Ifacename$ () function gets the name of the trigger that generated the first notification.

    LET name$ = Ifacename$ ()

When you use SETTRIG, Axbasic chooses a suitable trigger name. You can't specify one for yourself.

If your script uses only one SETTRIG statement, then you don't need to know the trigger name that was chosen. Any notification that's received must originate from that single trigger.

However, if your script contains multiple SETTRIG statements, you'll need to keep track of them. The Iface$ () function returns the name of the trigger created by the most recent SETTRIG statement.

Ordinarily, every SETTRIG statement should be followed by an Iface$ () function.

    ! Create a trigger and store its name
    SETTRIG "treasure"
    LET treasure_trigger$ = Iface$ ()

If you know which trigger fired, you can take whatever action is required, for example:

    IF Ifacename$ () = treasure_trigger$ THEN CALL GetStuff

Once that's done, we can discard the notification using a NEXTIFACE statement.

Here is the complete script. Once per main loop, the earliest remaining notification (if any) is retrieved, a subroutine is called, and the notification is then discarded.

    OPTION NEEDTASK

    ! Create a trigger and store its name
    SETTRIG "treasure"
    LET treasure_trigger$ = Iface$ ()

    ! Main loop
    WHILE 1

        ! Retrieve the first notification...
        IF Ifacecount () > 0 THEN
            IF Ifacename$ () = treasure_trigger$ THEN
                CALL GetStuff
            END IF
        END IF

        ! ...and discard it, ready for the next main loop
        NEXTIFACE

    LOOP

    END

    ! This subroutine is called if the treasure trigger fires
    SUB GetStuff
        SEND "get treasure"
        SEND "put treasure in sack"
    END SUB

Actually, if no notifications have been received, Ifacename$ () returns an empty string. Therefore we could simplify the main loop by removing the Ifacecount () line entirely.

    WHILE 1
        IF Ifacename$ () = treasure_trigger$ THEN
            CALL GetStuff
        END IF
        NEXTIFACE
    LOOP

12.10 Advanced notifications

An interface notification tells us the name of the trigger that fired, but also a lot more besides. There are several functions for retrieving the additional information, if you need it.

The Ifacetext$ () function returns the line that caused the trigger to fire.

    LET line$ = Ifacetext$ ()

The Ifacetime () function returns the time at which the trigger fired. The time is expressed as the number of seconds since the current session started.

The Timestamp () function also returns a time expressed in this way, so we can work out how long ago the trigger fired by subtracting one from the other.

    LET interval = Timestamp () - Ifacetime ()

Axmud gives each active trigger a unique number. This probably isn't very useful, but nevertheless you can retrieve the number using the Ifacenum () function, if you want to.

    LET number = Ifacenum ()

The Ifacetype () function returns the type of interface that fired, in other words the string "trigger".

12.11 Retrieving substrings

Triggers test a pattern against a line of text. The Ifacestrings () function returns the number of matching substrings. (See the regular expression tutorial in the Axmud Guide if you don't know what that means.)

The contents of those substrings can be retrieved using the Ifaceshift$ () and Ifacepop$ () functions. Both of those functions remove the matching substring from the notification, so you can use them several times until there are no more substrings left. Ifaceshift$ () removes the first matching substring, and Ifacepop$ () removes the last matching substring.

Another way to retrieve the substrings is with the Ifaceselect$ (). Get the first substring with Ifaceselect$ (1), the second with Ifaceselect$ (2) and so on. Unlike Ifaceshift$ () and Ifacepop$ (), nothing is removed. If the specified substring doesn't exist, the function returns an empty string.

A special case is when you use alternatives with substrings. For example, you might create a trigger than matches any of the following lines:

    The warrior hits you with axe
    The warrior hits you with handaxe
    The warrior hits you with poleaxe

...but not this line:

    The warrior hits you with surtaxes

In that case, you might use the following regular expression:

    The warrior hits you with (hand|pole)?axe

Additionally, you might want to capture the type of weapon used, and for that, you would add substrings:

    The warrior hits you with ((hand)|(pole))?axe

When the regular expression matches a line containing "handaxe" or "poleaxe", Perl produces a list of substrings containing two items, one of them an undefined value:

    ( "hand", undefined )
    ( undefined, "pole" )

The Ifaceselect () function returns an empty string in place of the undefined value, but it might also return an empty string if the line contained just "axe" and not "handaxe" or "poleaxe".

You can use the Ifacedefined () function if you specifically want to test for undefined values. Test the first substring with Ifacedefined (1), the second with Ifacedefined (2) and so on.

The function returns 1 if the specified substring exists, and is defined. If the specified substring exists but is not defined, it returns -1. If the specified substring doesn't exist, it returns 0.

12.12 Skipping notifications

By default, these functions work on the earliest notification received. Usually, you'll retrieve the information you want, and then discard the notification, ready for the next one.

However, if you want to retrieve information from a different notification, you can use a SKIPIFACE statement.

Functions like Ifacename$ () work on the current notification, which is usually the earliest remaining one. A SKIPIFACE statement changes the current notification to the next one in the list.

    ! Get the name of the trigger that generated
    ! the earliest notification
    LET first$ = Ifacename$ ()

    ! Get the name of the trigger that generated
    ! the notification after that
    SKIPIFACE
    LET second$ = Ifacename$ ()

If only one notification remains, then SKIPIFACE will have no effect. If there are ten remaining notifications and you've already used SKIPIFACE nine times, then the next SKIPIFACE statements moves back to the beginning of the list.

The Ifacepos () function returns the number of the current notification. By default, it returns 1, representing the earliest remaining notification. If you've used SKIPIFACE once, it would return 2, representing the second notification on the list. (If no notifications have been received, or if they have all been discarded, it returns 0.)

12.13 DELIFACE

An active interface is one that's currently available. We've discussed how triggers, aliases and so on can be tied to a particular character profile. Those interfaces are only active when you're playing the right character.

A DELIFACE statement can be used to delete an active interface. As mentioned above, you shouldn't normally use Axbasic scripts to delete interfaces created by something else:

      DELIFACE "status_task_trigger_11"

You shouldn't use DELIFACE to delete triggers created with an ADDTRIG statement, either; use DELTRIG to do that. (The reason for this is a bit complicated, but we can summarise by saying that an active interface might not have the name you were expecting it to have.)

However, DELIFACE is the recommended way to delete a trigger created with a SETTRIG or a WAITTRIG statement.

If you want to be clever, you can delete the trigger that generated the current interface notification using a line like this:

      DELIFACE Ifacename$ ()

12.14 Starting a new script with SETTRIG

SETTRIG creates a trigger. When the trigger fires, the Axbasic script receives a notification, which can be retrieved when the script is ready.

However, SETTRIG has a second purpose: it can be used to run a different Axbasic script. If this is what you want, specify both the trigger stimulus and the name of the other Axbasic script:

    SETTRIG "You are dead", "prayer_script"

The new script is not run as a task. The original Axbasic script does not receive an interface notification.

12.15 Future plans

At the time of writing, there are no SETALIAS, SETMACRO, SETTIMER or SETHOOK statements.


Previous Index Next