Announcement

Collapse
No announcement yet.

Script Docu v1

Collapse
This is a sticky topic.
X
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Script Docu v1

    Avalonia Online & Co is finally gaining scripting power. Right now it's very limited, but with the time it should be very powerful :-)

    Client-side scripting: not yet supported

    Server-side scripting of NPCs:

    The server engine is mostly written in Javascript. However these server engine scripts have unlimited access to everything, including files and database. To make it possible to embed scripts in maps and guarantee a stable server, the scripts of NPCs must be sandboxed and limited in what they can do and how much resources they take.

    The goal is to enable those NPC scripts to do everything that the engine scripts can do, but in a secure way and with an easy-to-use API. This is work in progress and the script engine might look very different in the future, but we have to start somewhere and progress. Ideas and feature requests are very welcome!

    Script events:

    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. The current event name is specified as a global boolean variable, which you can check like "if (playersays)".
    Alternatively you can react to the event by writing a function "function onEventname" such as
    PHP Code:
    function onPlayerSays(pl) {

    Following events exist right now:
    playertouchsme - the player collides with the NPC
    playersays - a player is in a distance of around 300 pixels or less, and is chatting
    mousedown - a player taps or clicks on the NPC
    timeout - when calling settimeout this event is automatically invoked after the specified time
    updated - the script has been updated with the in-game editor

    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.

    this.map: map object {name, template, width, height}
    this.hp: integer, hitpoints, read-only
    this.maxhp: integer, max-hitpoints, read-only
    this.name: string
    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.storyid: integer, current story played back by the NPC

    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.
    this.play(sound:string) - plays a sound effect to near players
    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

    Player

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

    player.id: integer, the unique player id, read-only
    player.map: map object {name, template, width, height}
    player.hp: integer, hitpoints, read-only
    player.maxhp: integer, max-hitpoints, read-only
    player.name: string, 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.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

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

    player.hasitem(itemid:string): boolean - tells you if the player has a certain item
    player.additem(itemid:string): boolean - the item must be configured with "addbyscript":true and "addinmaps":["mapofnpc"]
    player.setani(ani:string) - shows an animation until the player moves again, filename without file extension
    player.showurl(url:string) - opens the URL in a new window, currently only works on mobile because of popup blocker
    player.showmessage(message:string) - shows a message at the top of the screen
    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.unstick() - unsticks the player

    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}
    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:
    PHP Code:
    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', ...} 
    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)

    Example:

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

    PHP Code:
    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.saycounterplayer.saycounter+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!");
            }
        }


    Screenshot of an NPC in tiled (map editor):

    Click image for larger version  Name:	scripting.png Views:	1 Size:	112.5 KB ID:	3372
    Last edited by Stefan; 10-15-2018, 10:58 PM.
    Also known as Daddy and bomber

  • #2
    Cool Script

    Comment


    • #3
      Very sweet
      Looking forward to watch the baby grow up

      Comment


      • #4
        Very very nice. I can't wait to see the progress on this over the years! Thank you. =]
        Corleone Online Manager/Owner

        Comment


        • #5
          Good to see.

          Comment


          • #6
            Added a screenshot of an NPC in tiled If you have any ideas, functions or functionality you need for your projects then let me know. Will probably soon add a way to call external URLs (Webhooks).
            Also known as Daddy and bomber

            Comment


            • #7
              Is this compiled or ran as raw Javascript?

              Comment


              • #8
                Running with the same v8 engine, basically just the "global" (environment) object is different. Script execution is limited to 10 milliseconds at this time.
                Also known as Daddy and bomber

                Comment


                • #9
                  you can script in tiled? what?

                  Comment


                  • #10
                    Originally posted by Ximithie View Post
                    you can script in tiled? what?
                    You can make NPC-objects in Tiled and add custom parameters to it. That's all it is.

                    Comment


                    • #11
                      Added some more functions :-)
                      Also known as Daddy and bomber

                      Comment


                      • #12
                        Awesome . 😮

                        Comment


                        • #13
                          Huge script update (live on Testbed):
                          - this. object (the NPC)
                          - echo("blabla", object) to admin console
                          - settimeout(2), scheduleevent("1", "myevent", 123) - using timeouts
                          - more object attributes such as this.image, player.ani, player.x/y, clanname, clanid, map (map has attributes name, template, width, height)
                          - more functions: this.setbody(item), this.sethead(item), this.sethat(item), this.setweapon(item), this.setbow(item), this.setmount(item), the same for player.setbody etc.
                          - move: this.move(seconds, x, y)
                          - you can use "function onMouseDown" instead of "if (mousedown)", it's possible that calling other functions in this mode doesn't work perfectly yet
                          - new event "updated" (use function onUpdated(pl) or if (updated) to react to the event). This event is fired when the NPC script has been updated with the in-game editor
                          - Server.searchPlayers(options) returns an array. Options can be: {id:123}, {clanname:"Corleone"}, {clanid:123}, {map:"main"}, {map:"main", clanname:"Corleone"}

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

                              
                          this.settimeout(2);
                              
                          this.scheduleevent(1"myevent"123);
                          }

                          function 
                          onTimeout() {
                              echo(
                          "Timeout!");
                              
                          this.say("Timeout!");
                          }
                          function 
                          onMyEvent(testvar) {
                              echo(
                          "MyEvent: " testvar "!");
                              
                          this.say("MyEvent: " testvar "!");

                          PHP Code:
                          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);
                                      
                          this.setani("player_walk");
                                      
                          this.move(timeplayer.xplayer.y);
                                      break;
                                  }
                              }
                          }
                          function 
                          onMoveFinished(newani) {
                              
                          this.setani(newani);

                          PHP Code:
                          function onPlayerTouchsMe(pl) {
                              
                          let arr Server.searchPlayers({map:pl.map.nameclanname:pl.clanname});
                              
                          this.say("Found " arr.length " members of " pl.clanname +
                                
                          " (" pl.clanid ")! List on console.");
                              for (
                          let i=0i<arr.lengthi++)
                                  echo(
                          "Search: " " - " arr[i].name);
                          }
                          function 
                          onUpdated(pl) {
                              
                          this.say("Updated by: " pl.name);

                          Last edited by Stefan; 10-07-2018, 12:27 AM.
                          Also known as Daddy and bomber

                          Comment


                          • #14
                            Great. I've made a script for race events

                            PHP Code:
                              //Reset the function
                            function onUpdated(pl) {
                                
                            this.race_winner null;
                                
                            this.race_timer_message 0;
                                
                            this.scheduleevent(1"updatemessage"pl);    
                            }

                            function 
                            onPlayerTouchsMe(pl) {
                                if (
                            this.race_timer_message 0) return;

                                    
                            //Set the winner
                                
                            this.race_winner pl.name;
                                
                            this.say("Winner: " this.race_winner "!");

                                    
                            //Copy the players look to give it that special look :)
                                
                            this.setani("player_idle");
                                
                            this.sethead(pl.head);
                                
                            this.setbody(pl.body);
                                
                            this.sethat(pl.hat);

                                    
                            //How long to keep the message on display so it cannot be touched
                                
                            this.race_timer_message 60;

                                    
                            //Potentially warp them some place within the map so the events team can easily find the winner 
                                
                            pl.setmap("flameboy","template_flameboy",this.xthis.5);

                                
                            this.scheduleevent(1"updatemessage"pl);
                            }

                              
                            //Every 3 seconds update the chat to say the winners name for the remainder of the 'this.race_timer_message'
                            function onUpdateMessage(pl) {
                                if (
                            this.race_timer_message 0) {
                                    
                            this.say("Winner: " this.race_winner "!");

                                    
                            this.race_timer_message -= 3;
                                    
                            this.scheduleevent(3"updatemessage""");
                                } else {
                                          
                            //Once the race_timer_message is less than or equal to 0, stop the timeout from running
                                    
                            this.race_winner null;
                                    
                            this.race_timer_message 0;
                                }

                            Last edited by Flameboy; 10-10-2018, 06:30 PM.

                            Comment


                            • #15
                              Updated the first post to add the new stuff
                              Also known as Daddy and bomber

                              Comment

                              Working...
                              X