Scripting guide

Scripts - where do I put them?

Each server/game has its own script project with the sub-folders scriptclasses (for server scripts) and clientclasses (to run on the phone/browser). Look there to also see some cool existing scripts.

But how do you use the scripts? In Tiled (the map editor we use) you can add following custom properties for each object. Don't forget to set type to "NPC"!

Another way is to type /addscriptnpc as admin, which is adding a "rock", which is actually a scriptable NPC. Click on the rock to change its look and add a script.

For other objects you need to define the scriptclasses in a configuration file:

Script events (functions):

When certain things happen, such as the player colliding (touching) with the NPC, then the script of the NPC is started and can react to the collision event. You can react to the event by writing a function "function onEventname" such as

function onPlayerSays(pl) {

Following events exist right now:

For all scripted objects:

For NPCs:

For players:

For weapons:

For items:

For projectiles:

Objects and global variables

Following objects and variables exist. Some of these are read-only. You can additionally set attributes of the objects and call functions.

Non-Player Character

The NPC object can be accessed by the identifier this. If you change a property (such as image or ani) and want it to be permanent, then call Most attributes can also be changed by calling setATTRIBUTE(name) such as this.setchat(text). NPC functions are currently also available as global functions, so that you can call either "this.say(text)" or "say(text)", although the use of this.say is recommended. Object attributes defined in Tiled can also be accessed by script, such as this.npcclass.


The player who was causing the event can be accessed with the identifier player.

You can add custom variables to the player object (player.myvar = 123). These are kept until the player logouts, and are saved to the player object permanently when being configured in syncscriptvars.json.

setting description
soundsoff Turn off sound effects
musicoff Turn off background music
hidehome Hide the home button
hideminimap Hide the minimap (on right side)
noministories No guide videos on mini map
nomobilezoom Zoom out the game (mobile)
zoom2x Zoom in the game (desktop)
hidenames Hide player names (show with N key)
hidesidekicks Hide pets which follow other players
nohouseinvites No house invitations
noclaninvites No clan invitations
nosparinvites No spar invitations
notradeinvites No trade invitations
nolikemsg No like notifications
noweather Hide weather effects


Server.searchplayers(options:{}) - returns an array of players which match the options. Possible combinations for options are: {id:number}, {map:string}, {map, area:{x,y,w,h}}, {clanname:string}, {clanid:integer}, {map:string, clanname:string}, {map:string, clanid:integer}, {} (full server)

function onPlayerTouchsMe(pl) {
      //Find all the players on the map with the same clan name as the player that has touched the object
    let arr = Server.searchPlayers({, clanname:pl.clanname});

      //A message to show the players it has found with the above criteria
    this.say("Found " + arr.length + " members of " + pl.clanname +
      " (" + pl.clanid + ")! List on console.");

      //Loop through all the players it has found and set their chat
    for (let i = 0; i < arr.length; i ++) {
        arr[i].chat = "Found"; //Make the player say 'Found' if they meet the criteria above

Server.searchnpcs({map, area:{x,y,w,h}}) or alternatively {id:ID} or {name:NAME} (name search right now only returns a single NPC)

function onUpdated(pl) {
    let npcs = Server.searchnpcs({
        area:{x:this.x-20, y:this.y-20, w:40, h:40}
    for (let i in npcs)
        npcs[i].scheduleevent(0, "otherevent", this);

//Add the onOtherEvent function to the other NPCs to see them triggered.
function onOtherEvent(caller) {
    echo("called NPC: " + + " from " +;

Server.getconfig(filewithoutextension: string [, indexAttribute] [, categoryAttribute]) - loads a configuration file (fast), the configuration should have the structure {filename:[objects]}, the returned object also contains ways for quick lookup: objects: array of all objects, index[name] one object, category[name] array of objects

Server.getitemconfig(itemid) - shortcut to load an item

Server.getconfig/getitemconfig examples:

echo("default weapon: " + Server.getconfig("main").defaultweapon);
  -> sword1
echo("config skeleton: ", Server.getconfig("monsters", "type").index["skeleton"]);
  -> { type: 'skeleton', name: 'Pirate Skeleton', ...}
echo("config item nr: " + Server.getconfig("items", "itemid", "itemtype").objects.length);
  -> 591
echo("config weapon nr: " + Server.getconfig("items", "itemid", "itemtype").categories["weapon"].length);
  -> 97
echo("config sword1: ", Server.getconfig("items", "itemid", "itemtype").index["sword1"]);
  -> { itemid: 'sword1', name: 'Standard Sword', itemtype: 'weapon', ...}  
echo("config sword1 simple: ", Server.getitemconfig("sword1"));

Server.message(message:string) - displays a message for all players, similar to /servermessage

Server.setambient(red:number, green:number, blue:number) - sets the current ambient color for all players (day-night-effect)


Scripting examples:

This adds a firework once you say three times "yes". The firework1 item must have the attributes "addbyscript":true and "addinmaps":["currentmapname"].

function onPlayerTouchsMe(pl) {
    this.say("Say 'yes' three times if you want to get a firework!");

function onPlayerSays(pl) {
    if ( == "yes") {
        player.saycounter = (player.saycounter? player.saycounter+1 : 1);
        if (player.saycounter < 3)
            this.say("Say 'yes' " + (3-player.saycounter) + " more time(s)!");
        else {
            player.saycounter = 0;
            if (player.hasitem("firework1"))
                this.say("You already have that!");
            else if (player.additem("firework1"))
                this.say("There it is!");
                this.say("There was a problem adding the item!");

Scheduling a timeout or a scheduled event

function onPlayerTouchsMe(pl) {
    if (!this.touched)
      this.touched = 0;
    this.say("check: " + + " - " + this.touched + " - " + pl.x);

    this.settimeout(2); //Start the timeout
    this.scheduleevent(1, "myevent", 123); //Or start a scheduled event

//After 2 seconds the timeout function is called
function onTimeout() {

//After 1 second the scheduled event is called
function onMyEvent(testvar) {
    this.say("MyEvent: " + testvar + "!");

Copy and NPC Movement

//If the player says copy, the NPC will look exactly like the player
function onPlayerSays(pl) {
    switch ( {
        case "copy": {
            this.setani("player_idle"); // pl.ani);
            this.setweapon(null); // pl.weapon);
            this.setmount(null); // pl.mount);
            this.say("Copied you!");
        case "come": {  
            let time = 1;
            this.scheduleevent(time, "movefinished", this.ani); //Invoke the MoveFinished event once it arrives at the destination
            this.setani("player_walk"); //Act as though the object is moving
            this.move(time, player.x, player.y); //Move the object to the location of the player

//Created by the scheduleevent above and is called once the object has finished moving
function onMoveFinished(newani) {

GUI Controls

Gui Controls are used to pop up a window on the players screen, on client-side. The functions that are used are:

Example: GUI-Controls

Database Control

Database tables must be created manually be the server admin. Following function exist to access the database:

Example: Database

Private Messages

Send a simple PM:


Send a PM with name and head icon:

player.showpm("Hello!", {name:"Mario", isadmin:true, isonline:true, head:"head2", hat:"hat5"});

Send a PM and react to the reply of the player:

let sender = {name:"Puzzle", isonline:true};
player.showpm("What is 2+2? Tell me the answer!", sender, function(player, msg) {
  if (msg == "4")
    player.showpm("That's correct!", sender);
    player.showpm("Sorry that's wrong!", sender);

Send a PM with story as invitation (using the optional third data parameter):

player.showpm(message, null, {type:"invitation", storyid:1807});

Send a PM with map markers:

player.showpm("Hello!", {head:"1", name:"Go here to find food!"}, {mapmarkers:[
  { "x":375, "y":193, "text":"Trash", "icon":"job_trash-bottle.png", "blink":true}

Send a PM on touch which automatically opens, by setting show:true in the data parameter. Additional advantage: the player can easily translate the text:

function onPlayerTouchsme(pl) {
    pl.showpm("Did you know you can change your name with /setname?",
        {head:"1", name:"Guide"},


Client-side juke-box, use commands play1, play2, play3, play radio and stop music

function onPlayerSays(pl) {
    switch ( {
        case "play1": { this.setmusic("egyptmusic.mp3"); break; }
        case "play2": { this.setmusic("dungeon_music1.mp3"); break; }
        case "play3": { this.setmusic("diningmusic.mp3"); break; }
        case "play radio": { this.setmusic(""); break; }
        case "stop music": { this.stopmusic(); break; }