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" (note: not required anymore but still good to do)!
- scriptclasses - the preferred way to use scripts: assign one (or several comma-separated) of the existing classes from scriptclasses/* to use it for the NPC
- script - custom server script for the NPC
- clientscript - custom client-side script for the 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:
- Players - in main.json add a class in "playerscriptclasses": ["myclass"]
- Weapons - in the item.json file specify "scriptclasses": ["myclass"]
- Items - other items can also be scripted, in the item.json specify "scriptclasses": ["myclass"] and then add the onEquip or onUnequip event code. This also works for furniture items, when placed down in your house.
- Projectiles - in projectiles.json define a "impact": { "scriptclasses": ["myclass"] }
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:
- function onEVENT() - custom events scheduled by calling scheduleevent
- function onTimeout() - when calling settimeout this event is automatically invoked after the specified time
- function onStoryEnd(storyid:number) - when a non-looping story finishes (setstory)
For NPCs:
- function onCreated() - the NPC has been loaded (server or map loaded), important for script classes
- function onUpdated() - the script has been updated with the in-game editor
- function onPlayerTouchsMe(pl) - the player collides with the NPC (the player moved)
- function onPlayerSays(pl) - a player is in a distance of around 300 pixels or less, and is chatting
See more at NPC Reference
For players:
- function onLogin() - called on player login (in class player_events.js)
- function onDeath(killer) - called when a player dies, check
killer.type == "player"
to see if the killer was a player - function onKill(victim, rejectReason) - called when the player kills another player, rejectReason can be "sameip", "playerland", "clanland" (it's not adding a playerkill then)
- function onEnterMap(map, template, oldpos:{x,y,map,template}) - the player enters a new map or warps
See more at Player Reference
For weapons / tools:
- function onClientEVENT(player, action [, ...parameters]) - a client script called triggerserver, then this function is called on the server. Parameter player is omitted for player scripts.
- function onServerEVENT(player, action [, ...parameters]) - a server script called triggerclient, then this function is called on the client. Parameter player is omitted for player scripts.
- function onKeyDown(player, ascii:number, char:string, code:string, action:string) - client-side event when the weapon is equipped and a key is pressed, example: 65, "a", "KeyA", "MoveLeft2". No matter if its qwerty or qwertz layout, "KeyY" always refers to the 6. key in that row, so 'code' can be used to be compatible to all keyboard layouts. Action describes the configured default action on that key. Return true if the key should be ignored (e.g. player should not move).
For items:
- function onEquip(player, autoequip:boolean) - called on item scripts on the server when equipped or auto-equipped (on pickup) by the player
- function onEquip(player) - called on weapon scripts on client when equipped by the player
- function onUnequip(player) - called on item scripts (and on weapon scripts on client) when the player unequips a weapon or undresses clothes
For projectiles:
- function onCreated() - on projectile impact
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 this.save()
. 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.
- this.id: string, read-only, identifies the NPC, you can use Server.searchnpcs({id:ID}) to find it
- this.map: map object, see more at the Maps section
- this.hp: integer, hitpoints, read-only
- this.maxhp: integer, max-hitpoints, read-only
- this.name: string, you can use Server.searchnpcs({name:NAME}) to find the NPC
- this.tags: array of strings, you can use Server.searchnpcs({tag:tag}) to find the NPC
- this.chat: string
- this.scriptclasses: array of strings, read-only, lists the join()ed classes
See more at NPC Reference
Player
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.
- player.id: integer, the unique player id, read-only
- player.map: map object, see more at the Maps section
- player.hp: integer, hitpoints, kills if set to zero
- player.maxhp: integer, max-hitpoints, sets temporary max hp
- player.name: string, read-only
- player.coins: integer, read only
- player.chat: string
- player.weapon: string
- player.weapondata: object
- player.body: string
- player.head: string
- player.hat: string
See more at Player Reference
Maps
You can access the current map or the map of the player by this.map and player.map. The map itself has following attributes and functions. Also check out the Tiled-guide
- map.type: string, always "map"
- map.name: string, map name (first parameter in setmap)
- map.template: string, template name (second parameter in setmap)
- map.addnpc(obj) - NPC, only on server, adds a NPC with the provided info. You have to provide the initial NPC attributes, including x, y, image or ani, dontsave(boolean), possibly also npcclass, mobtype, hat, head etc. Do add a scriptnpc, provided npcclass:"scriptnpc" and specify scriptclasses (array)
See more at Map Reference
Server
- Server.playercount - current number of players connected to the server
- 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)
See more at Server Reference
GUI
GUI Controls are used to pop up a window on the players screen, on client-side. The functions that are used are:
- GUI.showpopup({title:string, width:integer, height:integer}) - shows a popup
- GUI.setpopuptitle(title:string) - changes the title of the currently shown popup
- GUI.hidepopup() - hides a popup
- GUI.get("id") - finds the GUI/dom object
- GUI.show("id" or object) - shows an object
- GUI.hide("id" or object) - hides an object
- GUI.onclick("id" or object, (event, x, y) => { ... }) - catches the click event
See a full list at GUI Reference
Database
Database tables must be created manually using the file browser. Following function exist to access the database:
- DB.load(tablename, options, entries => { ... }) - loads an array of DB entries, options specifies the attributes (exact match) and the third parameter is a callback to receive the entries. Options can also include limit, offset (numbers) and orderby of format "column ASC/DESC".
- DB.save(tablename, options, () => { ... }) - inserts or updates an object into the database. Unsupported attributes will make the call fail. Third parameter is an optional callback which is called when the data has been saved
See a full list at DB Reference and an Example: Database
Image functions
Load, change and save image data on server-side.
- Image.create(options: {width:number, height:number}) - creates an (empty) image object
- Image.load(filename: string) - returns an image object, filename is of format "/Files/gui/...png" (only pngs are supported at the moment)
- Image.save(image: object, filename: string[, options: object]) - saves an image under a new filename, options is optional and compatible to pngjs write options
- Image.bitblt(src: object, dest: object, srcX: number, srcY: number, width: number, height: number, destX: number, destY: number, overwrite: boolean) - copies an image into another image object, set overwrite to false to only copy non-transparent pixels
- Image.applyhsv(image: object, hue:number, saturation:number, value:number) - changes the hue (color 0..359), saturation (0..1) and value (brightness, 0..1) of an image object
Misc
- Array, Date, JSON, Math, Number, Object, String: built-in Javascript functionality
- echo(text:string [, ...objects to print]) - outputs text on the admin console
- uuid(): string - server-only, generates a unique id (uuidv4)
- isNaN(number): boolean - server-only, returns wether the number is NaN (not a number), it can happen on division by zero or trying to convert a string to number
- alert(text:string) - client-only, shows a modal popup with text
- translate(text [, params:array]) - client-only, translates a text into the player's language, the transaction must be provided in client_LOCALE.js in the Translations project; use player.translate() on server-side
Simple Scripting Examples
You can find some more complex examples in Examples/ folder. Here some basic examples such as adding items and starting a timeout (there is also a this.sleep(seconds) function!).
Adding items
This adds a firework once you say three times "yes".
function onPlayerTouchsMe(pl) {
this.say("Say 'yes' three times if you want to get a firework!");
}
function onPlayerSays(pl) {
if (pl.chat == "yes") {
pl.saycounter = (pl.saycounter? pl.saycounter+1 : 1);
if (pl.saycounter < 3)
this.say("Say 'yes' " + (3-pl.saycounter) + " more time(s)!");
else {
pl.saycounter = 0;
if (pl.hasitem("firework1"))
this.say("You already have that!");
else if (pl.additem("firework1"))
this.say("There it is!");
else
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.touched++;
this.say("check: " + pl.name + " - " + 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() {
this.say("Timeout!");
}
//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 (pl.chat) {
case "copy": {
this.setani("player_idle"); // pl.ani);
this.sethead(pl.head);
this.setbody(pl.body);
this.sethat(pl.hat);
this.setweapon(null); // pl.weapon);
this.setmount(null); // pl.mount);
this.setbow(pl.bow);
this.save();
this.say("Copied you!");
break;
}
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
break;
}
}
}
//Created by the scheduleevent above and is called once the object has finished moving
function onMoveFinished(newani) {
this.setani(newani);
}
Music
Client-side juke-box, use commands play1, play2, play3, play radio and stop music
function onPlayerSays(pl) {
switch (pl.chat) {
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("http://streams.antennemv.de/antennemv-live/mp3-128/listenlive/play.m3u"); break; }
case "stop music": { this.stopmusic(); break; }
}
}