Scripting Documentation


  • Corleone Staff Forum Admin

    Hello,

    Stefan has made a scripting guide which I've pasted below πŸ™‚

    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:

    • 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

    • function onNPCTouch(pl) - the NPC collides with the player (the NPC moved)

    • function onMouseDown(pl) - a player taps or clicks on the NPC

    • function onPlayerAttacks(player) - Player uses a melee attack on the NPC

    • function onActivated(player, dir) - client-side event when an item is used by the player

    • function onTimeout() - when calling settimeout this event is automatically invoked after the specified time

    • function onEVENT() - custom events scheduled by calling scheduleevent

    • function onClientEVENT() - a client script called triggerserver, then this function is called on the server

    • function onServerEVENT() - a server script called triggerclient, then this function is called on the client

    • function onPlayerLogin(player) - called on scriptclasses/player.js on player login

    • function onPlayerDeath(player, killer) - called on scriptclasses/player.js when a player dies, check killer.type == "player" to see if the killer was a player

    • function onPlayerKill(player, victim, rejectReason) - called on scriptclasses/player.js when the player kills another player, rejectReason can be "sameip", "playerland", "clanland" (it's not adding a playerkill then)

    • function onConsoleOpen(admin) - called on scriptclasses/player.js when an admin opens the admin console

    • function onConsoleClose(admin) - called on scriptclasses/player.js when an admin closes the admin console

    • function onConsoleChat(admin, message) - called on scriptclasses/player.js when an admin types something, return true if you have processed the message (and don't want to show it on the console)

    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.

    NPC

    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, ready-only, identifies the NPC, you can use Server.searchnpcs({id:ID}) to find it

    • this.map: map object {name, template, width, height, iswater(x,y), iswall(x,y [, onlytiles])}

    • this.hp: integer, hitpoints, read-only

    • this.maxhp: integer, max-hitpoints, read-only

    • this.isdead: boolean

    • 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.weapon: string

    • this.weapondata: object

    • this.body: string

    • this.head: string

    • this.hat: string

    • this.bow: string

    • this.mount: string

    • this.image: string, image of the NPC

    • this.ani: string

    • this.aniarg1..9: string, the ARG1..9 values in the .bani files

    • this.x, y, z, dir: number, current NPC position and direction

    • this.zoom: number, default 1

    • this.storyid: integer, current story played back by the NPC

    • this.waypoints: array of objects, play back the specified waypoints, get waypoints by clicking on the waypoints button in story windows

    • this.scriptclasses: array of strings, read-only, lists the join()ed classes

    • this.say(text:string [,delay:number]) - shows text, by default for 5 seconds but you can optionally specify the number of seconds

    • this.setimg(image:string) - changes the image of the NPC, must include the file extensions and exist in the npcs/ folder of the Files project

    • this.setani(ani:string) - sets the animation (.bani file) of the NPC, without file extension

    • this.move(timeinseconds:number, x:number, y:number) - moves to the specified position in the specified time, updates the direction of the NPC. On server this is currently updating the position of the NPC immediately, but players see it moving smoothly. Also works by client-side scripts (only moves locally then).

    • this.play(sound:string) - plays a sound effect to near players, without file extension

    • this.setmusic(fileorurl:string) - plays a music, only works on client-side, switches off when leaving the map

    • this.stopmusic() - stops a music previously played with setmusic

    • this.shoot([itemid:string]) - shoots a projectile in the current direction of the NPC with the specified weapon, or use the current weapon if not specified

    • this.settimeout(timeinseconds:number) - schedules a "timeout" event which is invoked after the specified time on the same script

    • this.scheduleevent(delayinseconds:number, eventname:string [, ...additional parameters]) - schedules a custom event which is invoked after the specified time with optional additional parameters which are passed to the onEventname function

    • this.cancelevents(event:string)

    • this.save() - saves the NPC to database

    • this.join(classname) - joins a script class, call save() afterwards to make it permanent

    • this.leave(classname) - leaves a script class

    • this.map.iswater(x, y), this.map.iswall(x, y), this.map.iswall(x, y, true) -> only checks tiles

    Player

    The player who was causing the event can be access with the identifier player

    • player.type: string, always "player"
    • player.id: integer, the unique player id, read-only
    • player.map: map object {name, template, width, height, iswater(x,y), iswall(x,y [, onlytiles])}
    • player.hp: integer, hitpoints, read-only
    • player.maxhp: integer, max-hitpoints, read-only
    • player.name: string, read-only
    • player.coins: integer, read only
    • player.bombs: integer, read only
    • player.arrows: integer, read only
    • player.chat: string
    • player.weapon: string
    • player.weapondata: object
    • player.body: string
    • player.head: string
    • player.hat: string
    • player.bow: string
    • player.mount: string
    • player.ani: string
    • player.aniarg1..9: string, the ARG1..9 values in the .bani files
    • player.x, y, z, dir: number, current player position and direction
    • player.zoom: number, default 1
    • player.onlinetime: integer, seconds of playing the game
    • player.kills, player.streak, player.mobkills, player.sparwins: integer
    • player.adminlevel: integer
    • player.clanname: string, the current clan name / tag of the player
    • player.clanid: integer, the id of the current clan
    • player.isdead: boolean

    You can add custom variables to the player object (player.myvar = 123). These are kept until the player logouts.

    • player.heal() - refills hitpoints
    • player.hasitem(itemid:string): boolean - tells you if the player has a certain item
    • player.additem(itemid:string): boolean - only adds single items, logs to scriptitems.log
    • player.setani(ani:string) - shows an animation until the player moves again, filename without file extension
    • player.showhp(nr:number, type:string, hitpoints:number, maxhitpoints:number) - shows a temporary number floating over the head of the player. Type can be "healed" for green and "inflicted" otherwise. If you specify hitpoints and maxhitpoints, then the health bar is shown over the head of the player.
    • player.setmap(map:string, template:string, x:number, y:number) - moves the player, do ("outside", "unknown", 0, 0) to move out of a house
    • player.showmessage(message:string) - shows a message at the top of the screen
    • player.showpm(message, senderlook [, data], onreply) - sends a PM to the player, see examples below
    • player.showurl(url:string) - opens the URL in a new window, currently only works on mobile because of popup blocker
    • player.unstick() - unsticks the player
    • player.echo(text:string [, ...objects to print]) - outputs text on the admin console for this admin only
    • player.addcoins(amount:number, reason:string) - adds coins, must be greater than 0 and less or equal to option "addcoinslimit" in the main.json configuration, reason is mandatory, it's to logged to scriptcoins.log
    • player.translate(message [, paramarray]) - translates a text to the user language, the text must be added to the Translation git project, if you use placeholders like %s and %d then they will be filled with the values from paramarray

    Server

    Server.searchplayers(options:{}) - returns an array of players which match the options. Possible combinations for options are: {id:number}, {map:string}, {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({map:pl.map.name, 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({
            map: this.map,
            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: " + this.id + " from " + caller.id);
    }  
    

    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.getconfig 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', ...}  
    

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

    Misc

    Math, JSON, Dates, Number, String: built-in Javascript functionality
    echo(text:string [, ...objects to print]) - outputs text on the admin console
    uuid() - generates a unique id (uuidv4)

    Scripting example:

    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 (player.chat == "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!");
                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);
    }  
    

    Clientside to Serverside Interaction

    this.triggerclient(player or id, action [, ...params]) - sends a trigger to the client script of the NPC

    //Clientside script
    function onPlayerTouchsMe(pl) {
        var popup = GUI.showpopup({
            title: "Welcome to " + Server.getconfig().gamename + "!",
            width: 400,
            height: 300
        });
        popup.innerHTML = '<br></br>Hmmm cool!' +
            '<img src="../files/icons/corleone_icon_spar.png"></img>' +
            '' +
            '';
    
        var self = this;
        GUI.onclick("actionbutton1", function(event) {
            GUI.hidepopup();
            self.triggerserver("clicked", "button1");
        });
        GUI.onclick("actionbutton2", function(event) {
            GUI.hidepopup();
            self.triggerserver("clicked", "button2");
        });
    }  
    
    //Serverside Code
    function onClientClicked(pl, buttonname) {
        this.say(pl.name + " clicked on " + buttonname);
    }  
    

    Hurting an NPC
    Once you set this.hp the object can be attacked by player It's automatically calculating the damage based on the players weapon, the NPC hp and the NPC armor/body level as defined in items.json. An example of a barrel which can be shot:

    function onUpdated() {
        this.onRespawn();
    }
    function onMouseDown() {
        this.say("HP: " + this.hp + "/" + this.maxhp, 3);
    }
    function onDeath(pl) {
        this.chat = "killed by " + pl;
        this.ani = "explode";
        this.scheduleevent(1, "respawn");
    }
    function onRespawn() {
        this.image = "barrel.png";
        this.chat = "";
        this.hp = this.maxhp = 50; //setting the HP to 50
    }  
    

    GUI Controls

    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.hidepopup() - hides a popup
    • GUI.get("id") - finds the GUI/dom object
    • GUI.exists("id") - checks if a GUI object exists
    • GUI.show("id" or object) - shows an object
    • GUI.hide("id" or object) - hides an object
    • GUI.onclick("id", function(event) { }) - catches the click event
    function onPlayerGrabs(pl) {
        var popup = GUI.showpopup({
            title: "Welcome to " + Server.getconfig().gamename + "!",
            width: 400,
            height: 300
        });
        popup.innerHTML = '<br></br>Enter text:' +
            '' +
            '' +
            '  SWORD' +
            '  HEAD' +
            '  BODY' +
            '' +
            '';
    
        var self = this;
        GUI.onclick("actionbutton1", function(event) {
            pl.chat = "text: " + GUI.get("textfield").value + " - " + GUI.get("selectfield").value;
            GUI.hidepopup();
        });
    }  
    
    // Hide button by id
    GUI.hide("actionbutton1");
    
    // Hide button by object
    let button = GUI.get("actionbutton1");
    GUI.hide(button);
    
    // Get current value of text inputs or select fields
    let value = GUI.get("textfield").value;
    echo("value: " + value); // prints it on browser developer console
    
    // Change the text of a text input
    GUI.get("textfield").value = "hmmm";  
    

    Particle Engine
    You can define and fire particle emitters inside .bani animation files:

    • Create emitters, with one or more particle templates
    • Fire the emitter with command "emit" inside single animation frames, or as a cascade action in particles. Or just define a position and an interval, then the emitter will fire automatically each few moments.
    • list itemUse the effects-array to define how the particles change over time, such as fading out or zooming out.
      "emitters": {
        "1": {
          "relative": true,
          "quantity": 30,
          "limit": 300,
          "draworder": "down",
          "sound": ["crack"],
          "soundchance": 100,
    
          "particles": [
            {
              "lifetime": [0.9,1.3],
              "image": "bbuilder_streak.png",
              "mode": "add",
              "alpha": 1,
              "zoom": 0.9,
              "red": [0.6, 1],
              "green": [0.6, 1],
              "blue": 1,
              "angle": [0, 360],
              "speed": 192,
              "emitatend": 2,
    
              "effects": [
                {"type":"range","time":[0.5,1],"attr":"alpha","mode":"replace","value":[1,0.5]},
                {"type":"range","time":[0.8,1.3],"attr":"zoom","mode":"replace","value":[0.9,0.2]}
              ]
            }
          ]
        },
        "2": {
          "relative": true,
          "quantity": 5,
          "limit": 300,
          "draworder": "down",
          "sound": ["crack"],
          "soundchance": 10,
    
          "particles": [
            {
              "lifetime": 1,
              "image": "bbuilder_flare.png",
              "mode": "add",
              "alpha": 1,
              "zoom": [0.4, 0.5],
              "red": [0.2, 0.5],
              "green": 0.6,
              "blue": 1,
              "angle": [0, 360],
              "speed": 64,
    
              "effects": [
                {"type":"range","time":[0.2,1],"attr":"alpha","mode":"replace","value":[1,0]}
              ]
            }
          ]
        }
      },
    

    Firing the emitter: you can specify which emitter to fire, and at which position it should spawn the particles. As alternative you can also just type "emit":1, then it will fire emitter 1 at the position of the emitter.

      "frames":[
        {
          "directions": [
            [[2,0,2], [5,-48,-207]]
          ],
          "duration": 500,
          "emit": [{"emitter":"1", "position":[16,0,144]}]
        }
      ];
    

    Weather Particle Effect stored inside a BANI

    {        
      "filetype": "bbuilderani",
      "aniversion": 1,
      "copyright": "2017 iAppsBeats LTD",
    
      "options": {
      },
    
      "defaults": {
      },      
    
      "emitters": {
        "1": {
          "position": ["RANDOMSCREENX", "RANDOMSCREENY", 100],
          "relative": false,
          "interval": 0.05,
          "reduceonmobile": true,
          "quantity": 2,
          "limit": 300,
          "draworder": "up",
          "screenwrap": true,
    
          "particles": [
            {
              "lifetime": [0.9,1.3],
              "image": "bbuilder_snowflake.png",
              "mode": "add",
              "alpha": 1,
              "zoom": [0.25, 0.35],
              "red": [0.6, 1],
              "green": [0.6, 1],
              "blue": 1,
              "rotation": [0, 360],
              "angle": 3.14,
              "speed": [15,20],
              "zangle": -100,
              "gravity": 0,
              "spin": [-0.15, -0.05],
              "removeatground": false,
              "emitatend": 2,
    
              "effects": [
                {"type":"range","time":[0.5,1],"attr":"alpha","mode":"replace","value":[1,0.5]}
              ]
            }
          ]
        },
        "2": {
          "relative": false,
          "position": [0, 0, 0],
          "quantity": 1,
          "limit": 300,
          "screenwrap": true,
    
          "particles": [
            {
              "lifetime": 0.5,
              "image": "bbuilder_snowflake.png",
              "mode": "add",
              "alpha": 1,
              "zoom": [0.25, 0.3],
              "red": 0.3,
              "green": [0.3, 0.6],
              "blue": 1,
              "angle": 0,
              "rotation": [0, 360],
              "speed": 0,
              "spin": 0,
              "gravity": 0,
              "removeatground": false,
    
              "effects": [
                {"type":"range","time":[0.1,0.5],"attr":"alpha","mode":"replace","value":[1,0]}
              ]
            }
          ]  
        }      
      },      
    
      "sprites": {
         "1": {"gfx":"bbuilder_smoke1.png", "bounds": [0, 0, 128, 128]}
      },
    
      "frames": [
      ]  
    }
    

    DB

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

    • DB.load(tablename, options, function(entries[]) { }) - loads an array of DB entries, options specifies the attributes (exact match) and the third parameter is a callback to receive the entries
    • DB.save(tablename, options) - inserts or updates an object into the database. Unsupported attributes will make the call fail.
    • DB.delete(tablename, options) - deletes DB entries, options specified the attributes (exact match)

    Example insert:

    function onUpdated(pl) {
        DB.save("supportticket", {
            userid: pl.id,
            date_created: new Date(),
            nickname: pl.name,
            category: "general",
            subject: "Test2",
            description: "Test " + uuid()
        });
    }  
    

    Example load and update:

    function onUpdated(pl) {
        var self = this;
        DB.load("supportticket", {}, function(tickets) {
            self.say("Load tickets from DB: " + tickets.length + " - " + tickets[0].description);
    
            tickets[0].description = "Update Test " + uuid();
            DB.save("supportticket", tickets[0]);
        });
    }  
    

    Example delete:

    function onUpdated(pl) {
        DB.delete("supportticket", {ticket_id: 2});
    }
    

    PMs

    Send a simple PM:

    player.showpm("Hello!");
    

    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);
      else
        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"},
            {show:true});
    }
    

    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; }
        }
    }
    


  • Nice! Asking if it would be possible you could do a Player integer, like player.burger to show the amount of burgers someone has made. Not too sure if there is already being used in the Burger Room Show Case top 5 Burger Player Room. πŸ‘ πŸ”₯ πŸ”


  • Corleone Staff

    Nice! Wallet gona do something now πŸ˜„


  • Corleone Staff

    //Author: Valithor
    //This prototype script allows a player to "call" another player
    //The script is activated when someone says "call"
    //The object then states "player.name" "player.id" is now calling someone with "this.name"
    //Caller says "find player " followed by the ID of the other player
    //The player can cancel finding a player by saying "end find"
    //If player is found, they will be notified by saying "I've been bamboozled!"
    //Their chat will be displayed above the phone until the call is ended when the caller says "end call"
    
    function onCreated() {
        //Default values
        this.lastplayer = null; //The player who says call
        this.targetplayer = null; //The player who is being called
        this.active = false; //True if the script is running
        this.linked = false; //Don't let other people call if someone is currently on a call or still linked
        this.find = false; //When true, the script will try to locate the id lastplayer says with "find player id"
        this.targetlinked = false; //When true, "find player id" was able to locate a player successfully
        this.timer = 1; //Counter to stop this script from run indefinitely trying to process something that will never happen
    }
    
    function onUpdated() {
        this.onCreated();
    }
    
    function onPlayerSays(pl)
    {
        //debug verbs
        if (player.chat == "lastplayer"){
            this.say(this.lastplayer);
        } else if (player.chat == "find"){
            this.say(this.find);
        } else if (player.chat == "timer"){
            this.say(this.timer);
        } else if (player.chat == "linked"){
            this.say(this.linked);
        } else if (player.chat == "targetlinked"){
            this.say(this.targetlinked);
        } else if (player.chat == "targetplayer"){
            this.say(this.targetplayer);
        } else if (player.chat == "targetreplace"){
            this.say(this.targetreplace);
        } else if (player.chat == "active"){
            this.say(this.active);
        } /*else if (player.chat == "break"){
            this.say("ice cream machine broke");
            //Reset to default state
            this.lastplayer = this.targetplayer = null;
            this.active = this.linked = this.find = this.targetlinked = false;
            this.timer = 1;
        }*/ else if (player.chat == "call"){
            if (!this.linked){
                this.lastplayer = pl; //Needed declaration
                this.lastplayer.linked = true; //Not yet implemented
                this.triggerclient(pl); //Not yet implemented
                this.active = true; //Needed declaration
                this.linked = true; //Needed declaration
                this.find = false; //Needed declaration
                this.targetplayer = null; //Needed declaration
                this.targetlinked = false; //Needed declaration
                this.timer = 1; //Needed declaration
                this.say(this.lastplayer.name + " " + this.lastplayer.id + " is now calling someone with " + this.name);
                this.settimeout(2);
            }
        }
        if (this.lastplayer !== null){
            this.testchat = this.lastplayer.chat;
            if (this.testchat.includes("find player")){ //Find the player in onTimeout
                this.find = true;
                this.timer = 1;
                this.settimeout(2);
            }
        }
        else{
            this.testchat = null;
        }
    }
    
    function onPlayerTouchsMe(pl) {
        if (this.lastplayer !== null){
            this.say(this.lastplayer.name + " " + this.lastplayer.id + " was last linked to this " + this.name);
        }
    }
    
    function onTimeout(){
        this.timer++;
        //Find player
        if (this.find && this.lastplayer !== null){
            //Early break conditions
            if (this.timer > 15){ //If no input after 15 seconds kill the script
                this.say("Call failed. Please try again.");
                //Reset to default state
                this.lastplayer = this.targetplayer = null;
                this.active = this.linked = this.find = this.targetlinked = false;
                this.timer = 1;
                return;
            } else if (this.lastplayer !== null && this.lastplayer.chat == "end find"){
                this.say("Cancelled search.");
                //Reset to default state
                this.lastplayer = this.targetplayer = null;
                this.active = this.linked = this.find = this.targetlinked = false;
                this.timer = 1;
                return;
            }
            if (timer == 1){ //Trigger once
                this.say("Please say 'find player' followed by the ID of the person you are trying to call");
            }
            this.target = this.lastplayer.chat;
            this.targetreplace = this.target.replace("find player ",""); //If done properly, targetreplace is now an ID
            //If the ID is not null and is a number, query the server
            if (this.targetreplace.length > 1 || Number.isNaN(Number(this.targetreplace))) {
                //this.say(this.targetreplace); //debug, the player ID being searched for
                this.playerQuery = Server.searchplayers({id:this.targetreplace});
                if (this.playerQuery.length < 1) {
                    this.say("Player given not found!");
                }
                else{
                    this.targetplayer = this.playerQuery[0];
                    this.targetlinked = true;
                    this.find = false;
                    this.say("Linked with player: " + this.targetplayer.name + " who has chat: " + this.targetplayer.chat);
                    this.targetplayer.chat = "I've been bamboozled";
                }
                this.settimeout(2);
            } else{
                this.say("Invalid player ID:" + this.targetreplace);
                this.settimeout(2);
            }
            
        }
        if (!this.find && this.lastplayer !== null){ //Don't bother doing any of this if someone is trying to place a call
            //End call
            if (this.lastplayer.chat == "end call"){
                this.say("The call has been ended.");
                //Reset to default state
                //this.lastplayer.phone = this.targetplayer.phone = false; //Not yet implemented
                this.lastplayer = this.targetplayer = null;
                this.active = this.linked = this.find = this.targetlinked = false;
                this.timer = 1;
                return; 
            } else if(this.targetlinked){
                //this.say(lastplayer.name + ": " + lastplayer.chat); //debug, repeats chat of player who started the call
                this.say(this.targetplayer.name + ": " + this.targetplayer.chat);
                this.settimeout(2);
            }
        }
        //Call
        if (this.active && this.linked && !this.find && this.targetlinked && this.lastplayer !== null){
            if (this.timer > 60) {  //Kill the script if there is nobody on the phone after 60 seconds
                this.say("The call has timed out.");
                //Reset to default state
                //this.lastplayer.phone = this.targetplayer.phone = false; //Not yet implemented
                this.lastplayer = this.targetplayer = null;
                this.active = this.linked = this.find = this.targetlinked = false;
                this.timer = 1;
            } else{
                this.settimeout(2);
            }
        }
    }
    

    My life right now



  • Haha nice!
    It reminds me of gs2 ☺


  • Forum Admin

    Added some info for sending PMs and answering to replies πŸ˜„



  • imao just like gscript lol but really simillar



  • the player chat in here is different in graal if (player.chat = "this is me"}{
    and npc can chat
    this.chat ="why"{



  • mmm true, maybe even smoother



  • @flameboy @Stefan Maybe you could make an automated trivia event using the new pm functions



  • Automated trivia ?? 😱 please no. It’s much more fun when a human being hosts it



  • @Z-Tetris said in Scripting Documentation:

    Automated trivia ?? 😱 please no. It’s much more fun when a human being hosts it

    Idk. I like automated events they will be hosted more often



  • @iSneak I agree. Automated events give good practice and variation while not relying on a staff member to host. As well as lower prize rewards. They have always been pretty fun in my opinion.



  • Yeah I do like some automated events eg: LMS, Chance ones and race but not so keen on trivia. Could be good though.



  • Yeah automated events just have a feel to them where they feel special. Idk why they just do I’ve always liked going into a automated event, and rocking to the finish to win the prize to flex on em.



  • Guess so and it would be even more special when there are more maps to these events rather than the standard water race and the two LMS Maps. πŸ˜„


  • Forum Admin

    Added player.coins, bombs and arrows, not sure why I was forgetting those πŸ˜„ right now only read-only though


  • Forum Admin

    Added setmusic and example πŸ™‚



  • @flameboy Good script πŸ‘Œ πŸ‘


Log in to reply