How to write scripts

Table of Contents



1. Writing scripts

You've never scripted before?
Click on scripting course to learn the basics in programming.

1. Compilation

In each 3D object you can add scripts that run and make the object alive.

To create a script :
- edit an object (Object > Edit), go in tab "Scripts" and click "Add".
- select the line "1 SCR Script" and click "Edit"
- write your script in the editor and click button "Apply".

In the script list, * denotes a compilation error, and ! denotes a runtime error.

Click the button show_error in the editor to display any runtime error details.

2. Language Description

The Planet script language is a simple derivative of the C-family.

Data types include : bool, int, float, array, struct, and string(N) with N in 0 .. 1024.

A script is composed of : const, global variables, typedef, functions, and event handlers.

Functions and event handlers may contain const, typedef and local variables.
Function parameters can have mode 'in' (per default), 'ref', or 'out'.
In case a function parameter or function return type is string, it must appear without maximum length specification.

Control statements include : if, switch, for, while, break, continue, assert, abort, clear (zeroes variables).

Constants/values of array/struct can appear as aggregates, ex: {1, 2, i}

Operators include unary: + - ! ~ -- ++ and binary: * / % + - << >> == != < > <= >= && || & | ^

3. Script execution speed

A typical script is supposed to handle short events from the Planet system, like opening a door when the user clicks on it.

If your script executes a very large number of instructions per day, this is not normal. Planet will penalize it by slowing it down.

A script that executes a lot of instructions per day is probably badly written:
- either it goes round in infinite loops,
- or it executes timer events with too short durations during the whole day,
- or it receives a large amount of messages from other scripts.

We encourage you to check the instruction consumption of your scripts via the Tools > Scripts menu. Check the column 'CPU Usage' : if it shows more than 1,000 units, there may be a problem. If it is over 10.000 units, there are certainly improvements to be made or bugs to be fixed. You can place say() statements for example in the event timer to check when it is triggered.

While the Tools > Scripts window is open, red bubbles rise above an object when a script changes its position or appearance. Too many red bubbles indicate a poorly written script that sends its commands one after the other instead of using Move Batches as explained in chapter 4.21. Move Batches are highly recommended because they do not consume server CPU nor network traffic while executing. In addition, they allow for smooth, shake-free movements.

2. Events

1. Start

When the script is compiled, or the object is taken from inventory or worn, the script is completely reinitialized. The START event is then executed first.

  event start ()
  {
    say ("hi, i'm starting !");
  }

2. Touch

When the user clicks on the object with the left mouse button, the TOUCH event is executed.

  event touch ()
  {
    key            k       = touched_avatar();
    int            mesh_nr = touched_mesh_nr();
    world_position wp      = touched_world_position();
    vector         v       = touched_mesh_position();

    say ("touched avatar = " + itos(k[0]) + "," + itos(k[1]) + "," + itos(k[2]) + "," + itos(k[3]));
    say ("touched mesh_nr = " + itos(mesh_nr));
    say ("touched world position = " + itos(wp.x) + "," + itos(wp.y) + "," + itos(wp.z));
    say ("touched mesh position = " + ftos(v.x) + "," + ftos(v.y) + "," + ftos(v.z));
  }
When treating a touch event, the following functions can be called :
  // returns a unique permanent key denoting the avatar that touched the object
  key touched_avatar ();

  // returns the touched mesh number (1 to 32)
  int touched_mesh_nr ();

  // returns the touch position in absolute world coordinates (see below)
  world_position touched_world_position ();

  // returns the touch position relative to the mesh center vector (see below)
  vector touched_mesh_position ();

The following data types are predefined :

  typedef int[4] key;     // key is a unique key identifying an avatar in the Planet system

  struct world_position   // a position on the planet
  {
    int x, y, z;            // coordinates in 1/256 mm
  }

  struct vector      // a small position or rotation
  {
    float x, y, z;     // coordinates in m
  }

The function same_key() can be used to check if two keys are identical :

  bool same_key (key k1, key k2);

The function is_null_key() can be used to check if a key is null :

  bool is_null_key (key k);

3. Collision

Event COLLISION is executed when an avatar collides with the object.

  // avatar collided with scripted object.

  event collision()
  {
    key    k  = collision_avatar();
    int    nr = collision_mesh_nr();
    vector n  = collision_normal();

    say (avatar_name(k) +
         " collides with mesh nr " + itos(nr)
              + " normal " + ftos(n.x)
                     + " " + ftos(n.y)
                     + " " + ftos(n.z));
  }

The following functions can be called within an event COLLISION :

  key collision_avatar();      // avatar who collided
  int collision_mesh_nr();     // mesh number that collided
  vector collision_normal();   // avatar to object normal vector

Objects having matter COLLISION have no physics (like matter PHANTOM) but they generate an event collision. They can be used as sensor to detect an arriving avatar.

Placing several objects in COLLISION material that overlap is not a good idea, because only one of them (at random) will trigger a collision event. You will then have the impression that this event is unreliable because it will not be triggered every time on the same object.

4. Listen

When an avatar writes a chat line, a nearby object receives the line it its event LISTEN.

  // object heard an avatar talk

  event listen (key avatar, string message)
  {
    say (avatar_name(avatar) + " said " + message);
  }

Note that listeners in moving objects should be avoided, they do not work reliably.

Default range is 50m but that can be changed with set_listen_range() :

  void set_listen_range (float range);    // set range 0 to 50m, default is 50m.

When not in use, it is good practice to set the range to 0.0 to reduce server cpu.

To save also server cpu, you can set a filter to capture only some chat messages :

  void set_listen_filter (string filter);
Default filter is "*" to capture all messages.
"*" replaces many characters, "?" replaces one character.

Example: filter "!*" will capture only chat lines starting with "!", like "!start" and "!stop".

5. Timer

Each script can start maximum 16 timers (nr from 0 to 15) with different durations.
When a timer expires, the event TIMER is executed with the timer number that expired.

  event start ()
  {
    start_timer (nr => 0, seconds => 3.0);
  }

  // this event is executed when a timer expires
  event timer (int nr)     // nr is timer number
  {
    say ("timer number " + itos(nr) + " expired");
  }

Periodical timers can be created by restarting the timer each time after it expired, however this produces lag so it should be used as rarely as possible.

Example:

  event start ()
  {
    start_timer (nr => 0, seconds => 0.0);
  }

  // this event is executed when a timer expires
  event timer (int nr)     // nr is timer number
  {
    say ("hi !");
    start_timer (nr => 0, seconds => 3.0);   // repeat after 3 seconds
  }

6. Message_Received

Scripts can send messages to one another with send_message or broadcast_message.

  void send_message (int object_id, string message);
  void broadcast_message (string script_name, string message);

Sending example:

  event touch()
  {
    send_message (object_id => 235,
                  message   => "hello to all scripts of object 235");

    broadcast_message (script_name => "script1",
                       message     => "hello to all scripts named script1 on the planet");
  }

The event message_received is executed when our script receives a message from another script.

  event message_received (int sender_object_id, string message)
  {
    say ("received : " + message + " from " + itos(sender_object_id));
  }

The sender_object_owner() function returns the owner of the object that sent us the message.

  key sender_object_owner();    // query the owner of the object who sent us a message
7. Server Restart

The event server_restart is called when the planet server was just started.

  event server_restart()
  {
    say ("hi, the planet server just started");
  }

A script can use this event to reprocess any clock-dependant computations.

2. Conversation

1. Say

  void say (string value);

Display text on chat within a range of 20m

  // Example:
  event start ()
  {
    say ("hi, i'm starting !");
  }

2. say_to

  void say_to (key avatar, string value);

Same as say but for a single avatar.

3. Displaying Icons

The chr(0,i) codes are used to display icons, replace i by a number.

  event touch ()
  {
    string(32) s;
    int        i;

    s = "";
    for (i=0; i<16; i++)
      s = s + chr(0,i);
    say ("icons : " + s);
  }

4. Displaying Images

You can display an image on the chat by placing it in the script folder.

  event touch ()
  {
    // needs a texture "sun" in the object's script folder
    say ("this is my sun : " + image ("sun") + " :)");
  }

5. Writing in different font and color

The codes chr(1,i) change the font, chr(2,i) change the writing style, and chr(4,i,j) change the color, replace i and j with numbers.

  // greeter

  string font(int f)                                     { return chr(1, f); }
  string style(bool underlined, bool italic, bool bold)  { int s=0; if (underlined) s++; if (italic) s+=2; if (bold) s+=4;  return chr(2, s); }
  string color(int col)                                  { return chr(4, col & 0xFFFF, col>>16); }

  event touch ()
  {
    say ("big "
       + color(0x8080FF)
       + "kiss"
       + font(9)
       + color(0x00FF00)
       + style(underlined => true, italic => true, bold => true)
       + " from Didi");
  }

3. Mesh

1. Set Mesh Active

  void set_mesh_active (int mesh_nr, bool enable);

Makes mesh active or inactive.
Inactive meshes are invisible and cause no avatar collisions.

Example:

  event start ()
  {
    set_mesh_active (mesh_nr => 1, enable => false);  // make mesh 1 inactive
    set_mesh_active (mesh_nr => 2, enable => true);   // make mesh 2 active
  }

2. Set Mesh Position

  void set_mesh_position (int mesh_nr, vector position);

Moves mesh within an object (range -32.768 to +32.767 m)
The position is given as a vector in x,y,z coordinates.

Example:

  event start ()
  {
    set_mesh_position (mesh_nr => 1,  position => {1.299, -0.893, 0.5});
  }

3. Set Mesh Rotation

  void set_mesh_rotation (int mesh_nr, vector rotation);

Turns a mesh within an object.
The rotation is given as x,y,z angles in degrees (-360.0 to +360.0)
The rotation axis is the center 0,0,0 of the mesh.

Example:

  event start ()
  {
    set_mesh_rotation (mesh_nr => 1,  rotation => {0.0, 0.0, 45.0});
  }

4. Set Mesh Color

  void set_mesh_color (int mesh_nr, int color);

Changes mesh color
Color is defined as hex RGB value (0xBBGGRR)

Example:

  event start ()
  {
    set_mesh_color (mesh_nr => 1,   color => 0xFFC0D0);
  }

5. Set Mesh Transparency

  void set_mesh_transparency (int mesh_nr, float transparency);

Changes mesh transparency.
The transparency value must be between 0.0 and 1.0

Example:

  event start ()
  {
    set_mesh_transparency (mesh_nr => 1,   transparency => 0.3);
  }

6. Set Mesh Light

  void set_mesh_light (int   mesh_nr,
                       int   color,
                       float attenuation,  // between 0.0 and 1.0
                       float range,        // between 0.0 and 65.0
                       int   cone);        // 0=point light, or spot size between 1 and 255.

Defines a point or spot light at coordinate 0,0,0 of the mesh.
To disable the light, call this function with range 0.0

Example:

  event start ()
  {
    set_mesh_light (mesh_nr => 1, color => 0xFFFFFF, attenuation => 0.6, range => 12.0, cone => 0);
  }

7. Set Mesh Lightning

  void set_mesh_lightning (int   mesh_nr,
                           bool  ambiant,   // true = daylight enlightens the mesh
                           bool  sunlight); // true = sun enlightens the mesh

Allows to remove the daylight or the sun that enlightens a mesh, for example if it's in a basement.

Example:

  event start ()
  {
    set_mesh_lightning (mesh_nr => 1, ambiant => false, sunlight => false);
  }

8. Set Mesh Glow

  void set_mesh_glow (int mesh_nr,
                      int color);

Allows an object to shine in the dark.

Example:

  event start ()
  {
    set_mesh_glow (mesh_nr => 1, color => 0x0000FF);
  }

9. Set Mesh Uv

  void set_mesh_uv (int    mesh_nr,
                    int    mode,
                    float  u         = 0.0,
                    float  v         = 0.0,
                    bool   ping_pong = false,
                    bool   one_shot  = false,
                    bool   perlin    = false);

Make all textures of this mesh move by changing the UV dynamically.
There are 6 modes :

  // mode 0 : turn uv off
  set_mesh_uv (mesh_nr => 1, mode => 0);

  // mode 1 : set static U,V offset
  set_mesh_uv (mesh_nr => 1, mode => 1, u => 0.5, v => 0.3);

  // mode 2 : set sliding U,V offset
  // u, v denote the speed of sliding
  set_mesh_uv (mesh_nr => 1, mode => 2, u => 0.1, v => 0.3);

  // mode 3 : set frames
  // the texture must consist of N images of same size, disposed vertically.
  // The successive images are shown like in a diaporama.
  // u denotes the number of images, and v denotes the speed of change
  // optional parameter ping_pong indicates if we want to show the images backwards again when arriving at the end.
  // optional parameter one_shot indicates if we want to show all images only once, or repeat forever.
  set_mesh_uv (mesh_nr => 1, mode => 3, u => 3.0, v => 100.0);
  set_mesh_uv (mesh_nr => 1, mode => 3, u => 3.0, v => 100.0, ping_pong => true, one_shot => true);

  // mode 4 : rotating texture
  // u denotes the maximum angle of rotation (360.0 for a full circle).
  // v denotes the rotation speed.
  // optional parameter ping_pong indicates if we want to rotate back when arriving at the end.
  // rotation occurs at u,v = 0,0 so it a centered rotation is wished
  //   you should set the texture u,v in range -0.5 to +0.5 when editing the mesh.
  set_mesh_uv (mesh_nr => 1, mode => 4, u => 360.0, v => 10.0);
  set_mesh_uv (mesh_nr => 1, mode => 4, u => 45.0, v => 80.0, ping_pong => true);

  // mode 5 : pulse texture
  // u denotes the pulse amplitude
  // v denotes the speed.
  // optional parameter ping_pong indicates if we want a back-and-forth movement
  set_mesh_uv (mesh_nr => 1, mode => 5, u => 0.1, v => 100.0);
  set_mesh_uv (mesh_nr => 1, mode => 5, u => 0.1, v => 100.0, ping_pong => true);

  Finally, option 'perlin' can be enabled for horizontal water textures.

10. Set Mesh Texture

  void set_mesh_texture (int mesh_nr, string texture);

Remplaces the texture of a whole mesh by a new temporary texture.
The new texture must be copied in the object's scripts folder.
An empty texture name "" will restore the original texture.
Likewise, if vous rezz the object on the land, it will receive again its original texture.

Example:

  event start ()
  {
    set_mesh_texture (mesh_nr => 1, texture => "checkboard");
  }

4. Object

1. object_id

int object_id();

returns the object's ID. This is a number that identifies the object on this Planet server until the object is deleted.

2. is_worn

bool is_worn ();
returns true if the current object is worn, false if it's created on land.
(in fact a worn object has a negative object_id, one on land has a positive one)

3. object_name

string object_name ([int object_id]);

returns object's name (max 32 characters)

4. set_object_name

void set_object_name (string name);
for a worn object, temporarily changes the object's name, so that say() calls use a different name.
for a world-rezzed object, the name change is permanent.

5. object_owner

key object_owner ([int object_id]);

returns object's owner.

6. object_owner_date

string object_owner_date ([int object_id]);

returns a date in format "YYYYMMDD"

7. object_nb_meshes

int object_nb_meshes();

return object's number of meshes.

8. domain_id

int domain_id();

return domain's id where the object is located.

9. domain_name

string domain_name();

return domain's name where the object is located.

10. domain_adult

bool domain_adult();

test if the object is located on an adult domain.

11. object_access

int object_access();

return object's access rights, a sum of the following values :

  RIGHT_IS_LOCKED             = 1
  RIGHT_ANYONE_CAN_MOVE       = 2
  RIGHT_ANYONE_CAN_BUY        = 4
  RIGHT_DISTRIBUTE_MONEY      = 8
  RIGHT_OWNER_CAN_SELL        = 16
  RIGHT_OWNER_CAN_MODIFY      = 32
  RIGHT_NEXT_OWNER_CAN_SELL   = 64
  RIGHT_NEXT_OWNER_CAN_MODIFY = 128
  RIGHT_BUILDER_CAN_TAKE_COPY = 256
  RIGHT_BUILDER_CAN_MODIFY    = 512
  RIGHT_BLOCK_SCRIPTS         = 1024

You can test individual accesses with the operator &.

12. object_world_position

world_position object_world_position (int object_id);

returns object's world position.

13. object_rotation

vector object_rotation (int object_id);

returns object's rotation.

14. set_object_world_position

int set_object_world_position (int object_id, world_position position);
moves object
  returns 0 if OK, or one of the following negative values :
         -1 : bad object_id
         -2 : no move access rights on this object
         -3 : no rezz access rights on new area
         -4 : new area is full

You can move an object over sea, but it will be deleted if it stays 1 hour on sea without any avatar watching it.
You cannot move an object different from the current object itself if the script object is worn or was rezzed by script.

15. set_object_rotation

void set_object_rotation (int object_id, vector rotation);

rotates object

16. rezz_object_absolute / rezz_object_absolute

int rezz_object_relative (string item_name [, vector         position [, vector rotation]]);
int rezz_object_absolute (string item_name  , world_position position [, vector rotation]);

deposits an object in the world, either at a relative position from the script object, or at an absolute world position.
returns a positive value (id of the created object), or a negative error (-1 = no rezz rights, -2 = area full)
This function is only allowed in the events touch, collision, menu_selected or money_received.

17. parent_object_id

int parent_object_id ();
returns a positive id of the parent object who rezzed or wore this object,
or -1 if it was not rezzed or worn by a script,
or -2 if the id is now invalid because the user relogged.
this function should be used only in the first start() because the parent can quickly become invalid.

18. delete_object

void delete_object();

deletes the object running this script.

19. event child_died

If you rezz an object on land, and this object is deleted, then the parent object that rezzed it receives an event child_died with the object_id of the object that was deleted :

  event child_died (int child_id)
  {
  }

Examples:

  event touch ()
  {
    say ("object id = " + itos(object_id()));
    say ("object name = " + object_name());
    say ("domain name = " + domain_name());
  }

  event touch ()
  {
    world_position wp = object_world_position (object_id());
    say ("world position = " + itos(wp.x) + ","
           + itos(wp.y) + "," + itos(wp.z));
  }

  event touch()
  {
    vector v = object_rotation (object_id());
    say ("rx = " + ftos(v.x));
    say ("ry = " + ftos(v.y));
    say ("rz = " + ftos(v.z));
  }

  event touch()
  {
    say ("bye");
    delete_object ();
  }

20. next_object_of_area

int next_object_of_area (int area_x, int area_y, ref int snr);
list all objects of an area.
snr must be initialized to 0 and is changed at each call.
returns next object_id of this area, or 0 if there are no more objects.
if the script object is moving this function is not reliable.

Example:

  event touch()
  {
    int snr = 0;
    for (;;)
    {
      int object_id = next_object_of_area (area_x => 2, area_y => 3, ref snr);
      if (object_id == 0)
        break;

     say (itos(object_id));
    }
  }

21. Move Batches

To move an object or a mesh, the naive way is to repeat commands several times per second. This however produces a shacking movement, uses a lot of CPU and generates a lot of traffic on the internet. In short, it creates lag.

The serious way is to use a move batch. To do this, you need to prepare a sequence of movements to be executed in advance. The sequence will be compressed and sent to the PC only once where it will be executed without disturbing the server. For example to open a door, you will make a batch that gives just the time of movement and the final rotation, and the PC will take care of generating all intermediate positions. The PC can even repeat your sequence a lot of times, imagine for example a fan.

Move Batches are highly recommended because they do not consume server CPU nor network traffic while executing. In addition, they allow for smooth, shake-free movements.

1. begin_move, end_move

All commands of a batch must be enclosed between :

  void begin_move ();
  void end_move ();

2. move_job

Inside a batch, you can specify one or more jobs, each one must start with :

  void move_job (bool repeating = false, bool sync = false, int sync_delay = 0);

    repeating   : true to repeat the job commands.
    sync        : true to synchronize with other jobs of same total duration (for repeating=true only)
    sync_delay  : a synchronization offset value in milliseconds (for sync=true only).

You can specify one or more of the following commands within a job :

  void set_mesh_active (int mesh_nr, bool enable);
  void set_mesh_position (int mesh_nr, vector position);
  void set_mesh_rotation (int mesh_nr, vector rotation);
  void set_mesh_color (int mesh_nr, int color);
  void set_mesh_transparency (int mesh_nr, float transparency);
  void set_mesh_light (int mesh_nr, int color, float attenuation, float range);
  void set_mesh_lightning (int mesh_nr, bool ambiant, bool sunlight);
  void set_mesh_glow (int mesh_nr, int color);
  void set_mesh_uv (int mesh_nr, int mode, float u = 0.0, float v = 0.0, bool ping_pong = false, bool one_shot = false);
  void set_mesh_texture (int mesh_nr, string texture);
  int set_object_world_position (int object_id, world_position position);
  void set_object_rotation (int object_id, vector rotation);

When one of these commands is specified between begin_move() and end_move(), instead of executing the command immediately, it is recorded in the move batch. Once arrived at end_move(), the batch is closed, checked, compressed and sent from server to PC for execution.

3. job_duration

Between commands, you can specify a duration to cause a fading between the former and later states with :

  void job_duration (int duration);    // duration must be >= 16 milliseconds

4. stop_move

To stop a running batch, call :

  void stop_move ();

Some rules : . if you specify a direct command outside the batch, the server will first stop the batch.
. command set_object_world_position() can only be specified in the first job of a batch.
. a repeating job must have at least one command job_duration.
. a non-repetitive job cannot have a job_duration command at the end.
. batches are executed in order.
. a repeating batch will no longer repeat when another following batch is sent.

Further commands :

5. end_move2

  int end_move2 ();

Instead of end_move() that stops on an area error, you can also use end_move2() that returns 0 if OK or a negative error if the area cannot be crossed.

6. nb_queued_moves

  int nb_queued_moves ();
You can query the number of queued batches in case several non-looping moves are pending.
Note that a maximum of 128 moves are allowed, after which the script halts.

7. rest_move_duration

 float rest_move_duration ();

Returns the time it will take for a non-repetitive batch to stop, in seconds.

Here are some examples:

A turning fan
-------------

  // fan

  bool g_enabled;

  event touch ()
  {
    g_enabled = !g_enabled;

    if (g_enabled)
    {
      begin_move ();

      move_job (repeating => true);
        set_mesh_rotation (mesh_nr => 1, rotation => {0.0, 0.0, 0.0});
        job_duration (1000);
        set_mesh_rotation (mesh_nr => 1, rotation => {0.0, 0.0, 120.0});
        job_duration (1000);
        set_mesh_rotation (mesh_nr => 1, rotation => {0.0, 0.0, 240.0});
        job_duration (1000);

      end_move ();
    }
    else
    {
      stop_move();
    }
  }

Rotation angles should be less than 180° apart to guarantee a deterministic path between two angles.

A smooth moving door
--------------------

  // door

  bool g_is_open;

  event touch()
  {
    float angle;

    g_is_open = !g_is_open;

    begin_move ();

    move_job (repeating => false);

      job_duration (1000);

      if (g_is_open)
        angle = 90.0;
      else
        angle = 0.0;

      set_mesh_rotation (mesh_nr => 1, rotation => {0.0, 0.0, angle});

    end_move ();
  }


Flying carpet
-------------

  // flying carpet

  bool g_on;

  event touch()
  {
    g_on = !g_on;

    if (g_on)
    {
      int id = object_id();
      world_position wp = object_world_position (id);
      world_position wp2 = wp;
      int rc;

      wp2.z += 256*1000;
      wp2.x -= 256*65536;

      begin_move ();

      // first job : change object position between two points
      move_job (repeating => true);
        rc = set_object_world_position (id, wp);
        job_duration (100000);
        rc = set_object_world_position (id, wp2);
        job_duration (100000);

      // second job : some weird rotations
      move_job (repeating => true);
       set_mesh_rotation (mesh_nr => 1, rotation => {0.0, 0.0, 0.0});
       job_duration (10000);
       set_mesh_rotation (mesh_nr => 1, rotation => {0.0, 20.0, 120.0});
       job_duration (10000);
       set_mesh_rotation (mesh_nr => 1, rotation => {20.0, 0.0, 240.0});
       job_duration (10000);

      end_move ();
    }
    else
    {
      stop_move ();
    }
  }


 Fading color object
 -------------------

  // fading color object

  event start()
  {
    begin_move ();
    move_job (repeating => true);

      set_mesh_color (mesh_nr => 1,color => 0xFF);
      job_duration (6000);
      set_mesh_color (mesh_nr => 1,color => 0xFF00);
      job_duration (6000);
      set_mesh_color (mesh_nr => 1,color => 0xFF0000);
      job_duration (6000);

    end_move ();
  }


Bouncing Cube
-------------

  // bouncing cube that changes color

  event start()
  {
    int i;

    begin_move ();

      // first job : bouncing position up and down
      move_job (repeating => true);

        for (i=0; i<10; i++)
        {
          float f = itof(i);
          f = 1.0 - f * f * 0.01;
          set_mesh_position (mesh_nr => 1, position => {0.0, 0.0, f});
          job_duration (100);
        }

        for (i=8; i>0; i--)
        {
          float f = itof(i);
          f = 1.0 - f * f * 0.01;
          set_mesh_position (mesh_nr => 1, position => {0.0, 0.0, f});
          job_duration (100);
        }

      // second job : changing rotation angles
      move_job (repeating => true);

        set_mesh_rotation (mesh_nr => 1, rotation => {0.0, 0.0, 0.0});
        job_duration (1000);
        set_mesh_rotation (mesh_nr => 1, rotation => {30.0, 0.0, 90.0});
        job_duration (1000);
        set_mesh_rotation (mesh_nr => 1, rotation => {0.0, 0.0, 180.0});
        job_duration (1000);
        set_mesh_rotation (mesh_nr => 1, rotation => {0.0, 30.0, 270.0});
        job_duration (1000);

      // third job : fading and changing color
      move_job (repeating => true);

        set_mesh_color (mesh_nr => 1,color => 0xFF);
        job_duration (2000);
        set_mesh_color (mesh_nr => 1,color => 0xFF00);
        job_duration (2000);
        set_mesh_color (mesh_nr => 1,color => 0xFF0000);
        job_duration (2000);

    end_move ();
  }

5. Avatar

1. avatar_online

  bool avatar_online (key k);

Returns true if an avatar is online, false if not.

2. avatar_name

  string avatar_name (key k);

Returns an avatar current full name (maximum 71 characters).

3. avatar_rank

  int avatar_rank (key k);

Returns an avatar rank : -1=banned, 0=visitor, 1=resident, 2=member manager, 3=security officer, 4=senior builder, 5=domain manager, 6=land owner.

4. has_rank

  bool has_rank (key k);
Returns true if the avatar is present in the members of the domain where the object is located, false otherwise.

5. set_avatar_rank

  void set_avatar_rank (key k, int rank);

Changes an avatar's rank, if you have rights for that.

6. avatar_gender

  int avatar_gender (key k);

Returns an avatar gender : 0=male, 1=female.

7. avatar_adult

  int avatar_adult (key k);

Returns an avatar's adult state : 0=non-adult, 1=adult, -1=invalid key.

8. avatar_language

  int avatar_language (key k);

Returns avatar langage (0 = FRENCH, 1 = ENGLISH, 2 = GERMAN, -1 = unknown key)

9. avatar_experience

  int avatar_experience (key k);

Returns avatar experience, or -1 if avatar key is unknown.

10. avatar_timezone

  int avatar_timezone (key k);

Returns avatar timezone, in hours, from -12 to +12.

11. avatar_title

  string avatar_title (key k);

Returns avatar title, max 24 characters.

12. avatar_world_position

  world_position avatar_world_position (key k);

Returns an avatar world position (z is 0 if standing on floor at altitude 0).

13. avatar_z_rotation

  int avatar_z_rotation (key k);

Returns an avatar orientation, as follows :

        0          = north, facing positive Y
       90          = east, facing positive X
      -90          = west, facing negative X
      -180 or +180 = south, towards negative Y

Example:

  event touch ()
  {
    key            k;
    bool           online;
    string(71)     name;
    int            rank;
    int            gender;
    world_position wp;
    int            angle;

    k = touched_avatar();

    online = avatar_online (k);
    name   = avatar_name (k);
    rank   = avatar_rank (k);
    gender = avatar_gender (k);
    wp     = avatar_world_position (k);
    angle  = avatar_z_rotation (k);

    say ("online = " + btos(online));
    say ("name = " + name);
    say ("rank = " + itos(rank));
    say ("gender = " + itos(gender));
    say ("world pos = " + itos(wp.x) + "," + itos(wp.y) + "," + itos(wp.z));
    say ("angle = " + itos(angle));
  }

14. set_avatar_world_position_and_angle

  void set_avatar_world_position_and_angle (key            k,
                                            world_position position,
                                            int            angle);

Teleports an avatar.

Example:

  event touch ()
  {
    key            k  = touched_avatar();
    world_position wp = touched_world_position();
    set_avatar_world_position_and_angle (k, position => wp, angle => 0);
  }

15. teleport_to_planet

  void teleport_to_planet (key k, string url);

teleports an avatar to another planet server.

url must have the form "planet://37.59.48.75/domain4"
If specified, the domain must be visible in public search.

The script object must have at least rank security officer on the origin domain.

See also the teleport_to_planet2 command in chapter 21. Inter-planet communication.

16. next_avatar

key next_avatar (ref int snr);
lists one by one all avatars in a radius of 256 meters.
snr must be initialized to 0, it is changed at each call.
If snr is 0 after the call, there are no avatars left.
This function is only allowed in the event touch, collision, listen, click_avatar ou menu_selected.

Example:

  event touch()
  {
    int snr = 0;
    for (;;)
    {
      key avatar = next_avatar (ref snr);
      if (snr == 0)
        break;

      say (avatar_name (avatar));
    }
  }

17. event owner_login

  event owner_login (bool in)
  {
  }

Event owner_login is called when the owner of the object logs in or out.

. it is used typically for a presence cube.
. it does not work in worn objects.

18. avatars_online_count

  int avatars_online_count();

returns the number of online avatars on this planet (0 to 1024).

19. avatar_key

  key avatar_key (string name);

returns the avatar key corresponding to the avatar name supplied. The name may be a little inaccurate, so the function will choose the closest name. A null key is returned if the avatar has not been found, which can be tested with is_null_key().

6. Sitting and Animations

1. is_sitting

bool is_sitting (key avatar);

Tests if an avatar is sitting on this object.

2. sit

bool sit (int mesh_nr, vector position, vector rotation, key avatar);
Sits an avatar with position/rotation on this object's mesh_nr.
This overrides any previous sitting and cancels all previous animations.
The script must run on same domain as the avatar is now, or else it returns false.

3. unsit

void unsit (key avatar);

Make an avatar stand up if it was sitting on this object and stops all animations that were started during the sitting.

4. start/stop_animation

void start_animation (string animation, key avatar);
void stop_animation (string animation, key avatar);
Starts or stops an animation.
A BVH animation must be present in the Script tab of the object.

5. is_animation_active

bool is_animation_active (string animation, key avatar);

Tests if this animation currently runs on the given avatar.

6. override_animation

void override_animation (int typ, string animation);
override standard animations (stand, walk, run, fly, jump)
for a worn object only.
typ : 0=stand, 1=walk, 2=run, 3=fly, 4=jump
use an empty string for animation to cancel the override.

// Example 1 : dancing ball

  const string ANIMATION_NAME = "dancing";

  event touch()
  {
    key k = touched_avatar();

    if (is_animation_active (ANIMATION_NAME, k))
      stop_animation (ANIMATION_NAME, k);
    else
      start_animation (ANIMATION_NAME, k);
  }


// Example 2 : sit on a chair

  event touch()
  {
    key k = touched_avatar();

    if (is_sitting (k))
    {
      unsit (k);
    }
    else if (sit (mesh_nr  => 1,
                  position => {0.0, 0.0, 1.0},
                  rotation => {0.0, 0.0, 0.0},
                  avatar   => k))
    {
      start_animation ("sit", k);
    }
  }

7. event click_avatar

  event click_avatar()
  {
    key k1 = clicking_avatar();   // get key of avatar who clicks
    key k2 = clicked_avatar();    // get key of avatar who is clicked.

    say ("click " + avatar_name(k1)
                  + " clicked on "
                  + avatar_name(k2));
  }

Event click_avatar() is generated when the user clicks on an avatar that is sitting on an object. All scripts of this object receives then this event. This can be used to change the avatar's animation.

The following functions are allowed inside an event click_avatar :

  key clicking_avatar();   // get key of avatar who clicks
  key clicked_avatar();    // get key of avatar who is clicked.

It is possible that the avatar is not sitting on the object anymore if this event arrives late. You can check this with is_sitting().

8. get_sitter

  bool get_sitter (out sitter);

Returns, in several calls, all the avatars sitting on the object.
Returns false if there are no more avatars left.
At each event, we start again at the first avatar.

All avatars sitting on an object can be listed like this :

  sitter s;

  while (get_sitter (out s))
  {
    say ("sitter : " + avatar_name(s.avatar));
    say ("mesh_nr : " + itos(s.mesh_nr));
    say ("pos : " + ftos(s.position.x)
            + " " + ftos(s.position.y)
            + " " + ftos(s.position.z));
  }

The structure sitter is defined like this :

  struct sitter
  {
    key    avatar;
    int    mesh_nr;
    vector position;
    vector rotation;
  }

This can be used for example for couple animations :

  event touch()
  {
    key k = touched_avatar();

    if (is_sitting (k))   // already sitting
    {
      unsit (k);    // stand up
    }
    else
    {
      sitter s;
      if (get_sitter(out s) && s.mesh_nr == 1)
      {
        // someone is already sitting on mesh 1
        if (sit (mesh_nr  => 2,   // sit on mesh 2
                      position => {0.0, 0.0, 1.0},
                      rotation => {0.0, 0.0, 0.0},
                      avatar   => k))
        {
          start_animation ("sit", k);
        }
      }
      else
      {
        if (sit (mesh_nr  => 1,   // sit on mesh 1
                      position => {1.0, 0.0, 1.0},
                      rotation => {0.0, 0.0, 0.0},
                      avatar   => k))
        {
          start_animation ("sit", k);
        }
      }
    }
  }

9. event avatar_unsits

  event avatar_unsits (key avatar)
  {
  }

Event avatar_unsits signals that an avatar unsits from an object.

It is called when a) another object calls sit() on the avatar, or b) the avatar teleports away, or c) the avatar disconnects.
It is not called when a) the object calls unsit() on the avatar, or b) the sitting object is deleted, or c) the planet server stops.

10. set_camera_stability

After sitting an avatar, you can set its camera stability.

void set_camera_stability (int stability, key avatar);
The following three stability values are possible:
  
  0 = STABLE (camera always horizontal) (this is the default after sitting)
  1 = DYNAMIC (horizontal and vertical camera rotations allowed)
  2 = UNRESTRICTED (camera follows the avatar, all camera rotations allowed, be careful makes sea sick)

7. Vehicles

To drive a vehicle, you must :
1) sit the avatar on the vehicle like on a couch,
2) optionally set camera stability (see above),
3) call control_vehicle() with a lot of parameters.

Additionnal passengers can also sit on the vehicle and set their camera stability, but only one driver can call control_vehicle(); any previous drivers become passengers.

1. control_vehicle

void control_vehicle (vehicle_parameters p, key avatar);


  struct vehicle_parameters
  {
    bool           active;     // true = behaves as vehicle, false = vehicle mode off

    //---------------------------------

    // physics

    float          mass;                  // 1000 kg
    float          max_speed;             // in km/h   maximal speed


    vector         dimensions;            // vehicle dimensions (large, length, height) (0.6 to 32.767 meter)

        The vehicule's physik is modelled as a stretched bubble with these dimensions.


    int            nb_wheels;             // 1 (monocycle), 2 (bike), 3 (tricycle), or 4 (car) (1 to 4)

        Under the vehicle are wheels, modelled as compressible spheres, that push it upwards.
        The number of wheels has an influence on the balance of the vehicle.
        if the number of wheels is 1, it is a vehicle that always remains horizontal (monocycle)
        if the number of wheels is 2,
          it can be a bike (wheels one behind the other)
             if left_to_right_wheel_distance = 0 and axis_to_axis_distance > 0,
          or a segway (one wheel on the left and one on the right)
             if left_to_right_wheel_distance > 0 and axis_to_axis_distance = 0.
        if the number of wheels is 3, there is one wheel in front in the middle, and two wheels behind.
        if the number of wheels is 4, it is a classic car.
        The wheels are centered and disposed according to left_to_right_wheel_distance and axis_to_axis_distance.


    float          tyre_radius;                  // 0.25  (0.001 to 32.767 meter)

        Radius of a wheel sphere, in meters.


    float          left_to_right_wheel_distance; // (0.0 to 32.767 meter)

        Distance between the center of the left wheel sphere and the center of the right wheel sphere, in meters (0.0 to 32.767)
        you have to specify zero for a bike.


    float          axis_to_axis_distance;        // (0.0 to 32.767 meter) (use 0.0 for segway)

        Distance between the center of the front wheel sphere and the center of the rear wheel sphere, in meters (0.0 to 32.767)
        you have to specify zero for a segway.
        this influences the radius of the circle in which the vehicle turns (a long truck turns on a longer circle than a car)


    bool           set_z_range;           // true = vehicle only allowed in altitude range min_z to max_z, false = no restriction.

        if you want to limit allowed altitudes of the vehicule, you set set_z_range to true.

    float          min_z;                 // (-8000.0 to 8000.0 meters) (set 0.0 for a ship)
    float          max_z;                 // (-8000.0 to 8000.0 meters) (set 0.0 for a ship)

        Once set_z_range is true, you can set a range here, for example:
        set both min_z and max_z to 0.0 for a ship, so it's always on sea level.
        set min_z to 0.0 and max_z to 8000.0 for a hovercraft, so it cannot sink below sea level.
        set min_z to -8000.0 and max_z to 0.0 for a submarine so it cannot go above sea level.

    //---------------------------------

    // keyboard configuration

    int key_direction;  // turns wheels left/right               default: 1
    int key_engine;     // moves vehicle forwards/backwards      default: 2
    int key_climb;      // moves vehicle upwards/downwards (helicopter)
    int key_yaw;        // turns vehicle (nose left/right)
    int key_pitch;      // turns vehicle (nose up/down)
    int key_roll;       // rolls vehicle (left ear up/down)

    The following key values are allowed :
        0 = key not assigned
        1 = cursor left/right
        2 = cursor up/down
        3 = page up/down
        4 = ctrl+cursor left/right
        5 = ctrl+cursor up/down
        6 = ctrl+page up/down
    (you can use negative value to inverse the sense of the controls)

    //---------------------------------

    // vehicle control

    float          engine_force_on_road;    // engine forward force when wheels touch ground (0.0 to 1.0e8)

      This is used for a car to give a forward force.
      set value 1.0


    float          engine_force_in_air;     // engine forward force when wheels don't touch ground (0.0 to 1.0e8)

      This is used for a ship or a plane to give a forward force.
      set value 1.0


    float          climbing_force;          // upwards force with key_climb (0.0 to 1.0e8)

      This is used for a helicopter/drone to give an upward force.
      set value 1.0


    float          yaw_force;               // turn force with key_yaw      (0.0 to 1.0e8)
    float          pitch_force;             // turn force with key_pitch    (0.0 to 1.0e8)
    float          roll_force;              // turn force with key_roll     (0.0 to 1.0e8)

      These 3 are used for ships or planes for turning in all directions.
      set value 1.0


    bool           yaw_dependant_on_forward;    // true = yaw force depends on forward movement, false = not
    bool           pitch_dependant_on_forward;  // true = pitch force depends on forward movement, false = not
    bool           roll_dependant_on_forward;   // true = roll force depends on forward movement, false = not

      Set to true for harder control : you can only turn when you also move forward.


    float          direction_factor;      // how fast the wheels turn when pressing key (1.0) (0.0 to 1.0e8)
    float          direction_max_angle;   // max wheels angle (30.0 degrees) (0.0 to 90.0)

      Set how fast the user makes the wheel turn when pressing direction key,
      and what max angle the wheels can have.


    float          yaw_factor;            // how fast yaw rudder moves (0.0 to 1.0e8)
    float          yaw_max_angle;         // max yaw angle (30.0 degrees) (0.0 to 90.0)
    float          pitch_factor;          // how fast pitch rudder moves (0.0 to 1.0e8)
    float          pitch_max_angle;       // max pitch angle (30.0 degrees) (0.0 to 90.0)
    float          roll_factor;           // how fast roll rudder moves (0.0 to 1.0e8)
    float          roll_max_angle;        // max roll angle (30.0 degrees) (0.0 to 90.0)

      Set how fast the user makes the rudder or flaps turn when pressing keys,
      and what max angle the rudder/flaps can have.


    float          sliding_coef;          // (0.0 to 1.0)
    float          sliding_z;             // -0.05 (-32.767 to 32.767)  negative z where wheels force applies, in meters

      sliding_coef indicates how slippy the tyres are (0.0 = asphalt to 1.0 = ice snow)
      sliding_z is a value in meter that indicates at which height the tyres force is applied to the ground,
        it can have 0.0 for total stability. A negative value like -0.05 can make the car turn over in curves.


    float          air_friction_coef;           // (0.0 to 1.0)
    float          air_friction_vertical_coef;  // (0.0 to 1.0)

      air_friction_coef indicates how dense the air/water is, for a ship/plane.
        a value of 0.0 will feel like in space (total inertia).
        For better control when turning you will want some like 0.5

      air_friction_vertical_coef is usually 0.0
        a value like 1.0 blocks vertical movement when not in vehicle forward direction.
        this can be used for a vehicule that flies strictly forward, like a plane, glider, deltaplane.


    float          spring_force;          // 1.3    shock absorbers force
    float          spring_damping;        // 0.005  shock absorbers counter-force (damping)

      These parameter control the wheel shock absorbers : the force and counter-force.


    float          keel_on_road;        // (0.0 to 1.0e8)
    float          keel_in_air;         // (0.0 to 1.0e8)

      These parameter work like a ship keel, adding weight under the car to incrase stability.
      The first parameter is used when the vehicle wheels touch ground, the second when not.


    float          stability_in_air;      // 0.01   (0.0 to 1.0)

      Slows down rotationnal speed, for a plane or a ship.
      Value 0.0 does nothing, so the vehicle will balance back and forth like a pendulum.
      A small value like 0.01 will slow down this.


    float          yaw_z;                 // (0.0 to 1.0e8) z offset above center of mass where to apply directional force

      When a plane is turning using yaw, this will also roll the plane/ship a little in the direction of the turn.


    float          forward_slowdown;      // (-1.0 to 1.0)

      slows forward move of vehicle (-1.0 = no slowdown, 0.0 = slowdown of 0.5%, 1.0 = slowdown of 1%)


    float          turning_slowdown;      // (-1.0 to 1.0)

      slows turning of vehicle (-1.0 = no slowdown, 0.0 = slowdown of 0.5%, 1.0 = slowdown of 1%)


    //---------------------------------

    // options

    bool           can_fly;                    // true = cancels gravity when in the air or on sea, for flying, sailing vehicles

      true for vehicles that can stay in the air without falling, like planes, ships, submarines.
      false for cars, hovercrafts or anything that falls on the ground.


    bool           apply_cubic_shape_in_air;   // true = smooth turning when wheels not touching ground

      true is recommended for flying/sailing objects for easier keyboard rotation control.
        when in the air, the rectangular vehicule mass is ignored and replaced by a cube.
      false for cars


    bool           block_fly_backwards;

      when true, you can't move backwards when wheels don't touch the ground.


    bool           auto_unblock;               // true

      when true, when vehicle is not moving and wheels don't touch the ground, it is supposed stuck.
      Then an automatic unblock resets rotations to zero.


    bool           block_wheels_in_the_air;    // stop the wheels of a plane when it's in the air

      when true, the mesh wheels stop turning when they don't touch the ground anymore (for a plane after take off)


    //---------------------------------

    // mesh wheels display

    ROTATING_MESH  rotating_mesh[2 .. 32];

      mesh 1 is the vehicle and cannot be configured
      mesh 2 to 32 can be configured as wheels, rudder or other moving parts.
        for each mesh, you specify a type and a radius.
        the mesh numbers must be contiguous, i.e. don't leave holes.

            struct ROTATING_MESH
            {
              int   typ;        // type of wheel
              float radius;     // in meters
            }

          the following types of wheels are possible:

            0 = off

            1 = front left wheel
            2 = front center wheel
            3 = front right wheel

            4 = back left wheel
            5 = back center wheel
            6 = back right wheel

            7 = segway left (or tank)
            8 = segway right (or tank)

            9 = car steering wheel, bike fourche (controlled by direction) (set radius to 1.0 !)

            10 = ship rudder   (controlled by yaw)   (set radius to 1.0 !)
            11 = height rudder (controlled by pitch) (set radius to 1.0 !)
            12 = roll rudder   (controlled by roll)  (set radius to 1.0 !)


      The specified radius must match exactly the radius of the mesh wheels, in meters,
      so that the wheels turn with correct speed.


    vector   mesh_rotation[2 .. 32];

      for each mesh 2 to 32, you can specify a constant post-rotation.
      the vector indicates the (x, y, z) rotation in degrees.
      this is used for example to inclinate a bike front part by 20°


    float    inclination_angle_direction;

      for a monocycle or a bike, max angle to inclinate the vehicule when turning in curves.


    float    inclination_angle_height;

      for a monocycle or a segway, max angle to inclinate the vehicule when moving forward or back.


    //---------------------------------
  }

For script examples, see the demo vehicles in the shopping center.

2. event vehicle_changed

You can handle the event "vehicle_changed" for reacting to some actions :

  event vehicle_changed (vehicle_change e)
  {
    if (e.forward)
    {
      say ("go !")
    }

    if (e.backward)
    {
      say ("STOP")
    }
  }

The structure "vehicle_change" is defined as :

  struct vehicle_change
  {
    bool forward;      // true if user pressed forward key to increase engine speed
    bool backward;     // true if user pressed backward key to reduce engine speed
    bool left;         // true if user turned left 25°
    bool right;        // true if user turned right 25°
  }

3. vehicle collisions

1. events collision_vehicle_avatar, collision_vehicle_vehicle, collision_vehicle_object

Events collision_vehicle_avatar, collision_vehicle_vehicle and collision_vehicle_object are triggered when your vehicle collides with an avatar, another vehicle, or an object in matter COLLISION.

Example:


// in a vehicle
event collision_vehicle_avatar ()
{
  key k = collision_avatar();
  say ("collision_vehicle_avatar " + avatar_name(k) + " force = " + ftos(collision_force()));
}

// in a vehicle
event collision_vehicle_vehicle ()
{
  key k  = collision_avatar();
  say ("collision_vehicle_vehicle " + avatar_name(k) + " force = " + ftos(collision_force()));
}

// in a vehicle or an object of matter COLLISION
event collision_vehicle_object ()
{
  key    k  = collision_avatar();
  float  force = collision_force();
  int    id  = collision_object_id();
  say (avatar_name(k) + " a collisioné avec " + itos(id) + " force " + ftos(force));
}

2. collision_avatar

  key collision_avatar();

This function can then be used to determine the colliding avatar.

For event COLLISION, COLLISION_VEHICLE_AVATAR, COLLISION_VEHICLE_VEHICLE, COLLISION_VEHICLE_OBJECT.

3. collision_force

  float collision_force ();

This function can then be used to determine the collision force.

For event COLLISION_VEHICLE_AVATAR, COLLISION_VEHICLE_VEHICLE, COLLISION_VEHICLE_OBJECT.

4. collision_object_id

  int collision_object_id ();

This function can be used to determine the ID of the other object (vehicle or object).

For event COLLISION_VEHICLE_VEHICLE, COLLISION_VEHICLE_OBJECT.

8. Animated Mesh

Animated meshes allow the execution of animations on the rigged meshes of an object, without any avatar.

A rigged mesh can be based on the standard skeleton of the male or female avatar, or have a custom skeleton. In Edit Mesh, mode JOINTS, check the appropriate option.

An object worn on the avatar must always have its own custom skeleton if it wants to execute its own animations by script, independently of the avatar's animations.

1. object_start/stop_animation

  void object_start_animation (string animation);
  void object_stop_animation (string animation);
Starts or stops an animation on an object.
A BVH animation must be present in the scripts folder of the object.
A maximum of 4 animations can run at the same time.

2. object_is_animation_active

  bool object_is_animation_active (string animation);

Tests if an animation is currently running on the given object.

9. Menus

1. display_menu

void display_menu (key avatar, string menu [,string menu2[,string menu3[,string menu4, ..]]]);
display a user menu.
Menus can have max 3 levels, max 20 items in each level.
If the menu is longer than max 1024 characters, split it in several parts.
When a user selects a menu item, the script receives an event menu_selected.

2. event menu_selected

Example:
  event touch ()
  {
    key k = touched_avatar();
    display_menu (k, "On:1,Off:0,Color:[red:0xFF,green:0xFF00,blue:0xFF0000]");
  }

  event menu_selected (key avatar, int menu_id)
  {
    say ("avatar " + avatar_name(avatar) + " has selected menu " + itos(menu_id));
  }

10. Inventory

1. event give_inventory

void give_inventory (string item_name);
gives the item of given name to the user's inventory.
this function is only allowed inside events TOUCH, MENU_SELECTED and COLLISION.

2. item_name

string item_name (string previous_name [, int step]);
lists all items present in the script folder of an object.
returns name of next item, "" if no more.
step can be +1 to browser forwards, or -1 to browse backwards.

3. item_type

string item_type (string item_name);
used to get the type of an item
returns one of "TEX", "OBJ", "SCR", "BVH", "SHP", "WAV"
or "" if item_name does not exist.

4. event items_changed

event items_changed is triggered when the user adds, modifies or deletes an item in the object's script folder.
  event items_changed()
  {
    // user did add/modify/remove an item from scripts folder
  }

Example:

  // give all items except those of type script :
  event touch()
  {
    string(32) name;
    clear name;
    for (;;)
    {
      name = item_name (previous_name => name);
      if (name == "")
        break;
      if (item_type (name) != "SCR")
      {
        give_inventory (item_name => name);
        say ("giving " + name);
      }
    }
  }

11. Dressing

The following script commands allow to automatically dress and undress an avatar. They drag objects, textures, shapes and animations into the avatar's inventory and make it wear them, or take them off.

If an avatar has just arrived on the planet and his clothes are not yet loaded, then a script running clothing commands will be paused until all his clothes are loaded. A clothing script can therefore sometimes pause for almost a minute in case of a slow connection before continuing its execution. This mechanism prevents the wrong clothes from being put on the avatar.

1. dressing of objects

1. wear_object

void wear_object (key avatar, string item_name, bool permanent = false);

Automatically make an avatar wear an object.

The object to be worn must be in the "Scripts" folder of the object that runs this script.

The object is added to the avatar's inventory, in the "Object" folder if permanent is true, otherwise in the "Temporary" folder.

An object in "Temporary" is limited: when it is removed, it is moved to the "Trash" folder and the avatar cannot change or reuse the object after that.

2. unwear_object

void unwear_object ();
Remove the worn object that runs this script.
Allowed only if this script is running in a worn object.

3. unwear_object()

void unwear_object (key avatar, string item_name);
Removes the worn object from an avatar.
The characters '? (replaces 1 char) and '*' (replaces multiple chars) are allowed in item_name to remove multiple objects.

4. next_worn_object

string next_worn_object (key avatar, ref int snr);
Lists all worn objects
Returns the name of the next worn object (max 32 characters).
snr must be initialized to 0 and will be changed at each call, it is reset to 0 if there are no more objects.

5. event detached

In case an automatically worn object is removed, the parent script that issued wear_object() receives an event with the avatar key and the name of the removed object :
  event detached (key avatar, string name)
  {
  }

2. dressing of textures

1. wear_texture

void wear_texture (key avatar, string item_name, bool permanent = false);

Automatically make an avatar wear a texture.

The texture to be worn must be in the "Scripts" folder of the object that runs this script.

The texture is added to the avatar's inventory, in the "Textures" folder if permanent is true, otherwise in the "Temporary" folder.

An texture in "Temporary" is limited: when it is removed, it is moved to the "Trash" folder and the avatar cannot reuse the texture after that.

2. unwear_texture

void unwear_texture (key avatar, string item_name, int body_part = -1, int layer = -1);

Removes the worn texture from an avatar.

The characters '? (replaces 1 char) and '*' (replaces multiple chars) are allowed in item_name to remove multiple textures.

body_part : 0=head, 1=chest, 2=legs, 3=eyes, 4=finger nails, 5=toe nails, -1=all

layer : 0=skin, 1=tatoo, 2=underwear, 3=clothing, 4=overwear, -1=all

3. dressing of shapes

1. wear_shape

void wear_shape (key avatar, string item_name, bool permanent = false);

Automatically make an avatar wear a shape.

The shape to be worn must be in the "Scripts" folder of the object that runs this script.

The shape is added to the avatar's inventory, in the "Shapes" folder if permanent is true, otherwise in the "Temporary" folder.

An shape in "Temporary" is limited: when it is removed, it is moved to the "Trash" folder and the avatar cannot change or reuse the shape after that.

2. unwear_shape

void unwear_shape (key avatar, string item_name);

Removes the worn shape from an avatar.

The characters '? (replaces 1 char) and '*' (replaces multiple chars) are allowed in item_name to remove multiple shapes.

4. dressing of animations

1. wear_override_animation

void wear_override_animation (key avatar, int typ, string item_name, bool permanent = false);

Automatically make an avatar wear an animation.

The animation to be worn must be in the "Scripts" folder of the object that runs this script.

The animation is added to the avatar's inventory, in the "Animations" folder if permanent is true, otherwise in the "Temporary" folder.

An animation in "Temporary" is limited: when it is removed, it is moved to the "Trash" folder and the avatar cannot change or reuse the animation after that.

The animation is also added to My Animations.

  
  typ: 0=standing, 1=walking, 2=running, 3=flying, 4=jumping,
       100-103 = permanent, 1 to 4
       200-207 = individual, 1 to 8
       300-307 = couple A, 1 to 8
       400-407 = couple B, 1 to 8
An empty item_name deletes the animation.

12. Blockings

a) Blockings from objects in world

Blockings can be applied to avatars that are within a box-shape space around a rezzed object.

1. block_avatar_options

  struct space
  {
    vector min, max;
  }

  // Apply blockings to an avatar.
  // The avatar must be in the same domain as the object.
  // If the avatar parameter is empty, the commmand applies to all avatars
  //   that are inside the space.
  // options:
  //   CAPTIVE   :  0x0001 means avatar can't leave box space.
  //   FLY       :  0x0002 means avatar can't fly.
  //   INVENTORY :  0x0008 means avatar can't open inventory.
  //   NAMES     :  0x0010 hides avatar names in People and Conversation, and blocks other people's profiles.
  //   TELEPORT  :  0x0020 means avatar can't teleport.
  //   SENDCHAT  :  0x0040 block sending on chat (except on worn objects with event listen)
  //   READCHAT  :  0x0080 block receiving on chat (except from commands say/say_to from worn objects)
  //   CAMERA    :  0x0200 means avatar can't move camera outside box space.
  //   TOUCH     :  0x0400 means avatar can't touch objects outside box space.
  //   RADAR     :  0x0800 means avatar can't see anyone in People dialog.
  //   JUMP      :  0x1000 means avatar can't jump.
  //   several options can be added.
  //   0 means no blockings.
  //   any script recompilation or script error in the object removes also all blockings.
  // space can be as large as 512m x 512m (-256.0 to +256.0)
  void block_avatar_options (key avatar, int options, space space);

2. avatar_block_options

  // retrieve avatar block options
  int avatar_block_options (key avatar);

3. next_avatar_blocked

  // retrieve next avatar having blockings.
  // clear avatar on first call, empty returned key means no further avatar is blocked.
  key next_avatar_blocked (key avatar);

b) Blockings from worn objects

1. block_worn_object

void block_worn_object (blocking_parameters parameters);
Block avatar features.
Allowed only for a worn object.
struct blocking_parameters
{
  int   options;
  float walking_speed;    // in meters. if less than 3.2 m/s then running is blocked too.
  float camera_distance;  // in meters.
  float touch_distance;   // in meters.
  float radar_distance;   // in meters. 0.0 to 256.0
  float camera_angle;     // angle in degrees. 0.0 to 180.0
  uint  fog_setting;      // 0xKS_BBGGRR, example: 0xfe_000000
}                         // with :
                          //   K = frontier between non-fog and fog, from 0=progressive to F=hard,
                          //   S = distance of fog, from 0=far away to F=nearby,
                          //   BBGGRR = rgb fog color.

// options:
// 0x0001 : block menus 'unwear' and 'properties' for this object
// 0x0002 : block flying
// 0x0004 : block menus 'wear'/'unwear' of skins and clothes textures
// 0x0008 : block inventory
// 0x0010 : hides avatar names in People and Conversation, and blocks other people's profiles
// 0x0020 : block teleport
// 0x0040 : block sending on chat (except on worn objects with event listen)
// 0x0080 : block receiving on chat (except from commands say/say_to from worn objects)
// 0x0100 : limit walking speed, see .walking_speed
// 0x0200 : limit camera distance, see .camera_distance
// 0x0400 : limit touch distance by left click, see .touch_distance
// 0x0800 : limit distance avatar are seen in People, see .radar_distance
// 0x1000 : block jump
// 0x2000 : fix camera on avatar's mFace bone
// 0x4000 : limit horizontal and vertical camera view angle, for example .camera_angle = 45;
// 0x8000 : set fog, see .fog_setting
// 0x10000 : make avatar invisible for myself (my shadow remains visible).

An avatar that is blocked can click button CheatOut in profile to erase all blockings, but it is then added in the Cheats list in domain info.

13. Text Files

Text files can be added in the scripts and read line by line.

1. count_lines

  // counts number of lines of a text file
  int count_lines (string item);

2. read_line

  // returns line n of a text file
  string read_line (string item, int line);

Example:

  // read all lines of a text file
  event start()
  {
    int count = count_lines ("mytext");
    int i;
    for (i=1; i<=count; i++)
    {
      string(128) s;
      s = read_line ("mytext", i);
      say (s);
    }
  }

14. Permanent Storage

a) storage for world objects

1. store

void store (string index, string value);    // insert or update value associated with index

store() is used to save a string value on permanent server storage linked to the object's id.

You can store a maximum of 10,000 strings, after that you get a script error.

The strings are not deleted when the script is restarted, the only way to delete all storage is to delete the object and rezz a new one.

You can delete one individual string value by storing an empty string.

Index must not be longer than 50 characters and contain only non-nul characters.

2. fetch

string fetch (string index);                // retrieve
fetch() is used to retrieve a value previously saved with store(). If there was none, an empty string is returned.

3. navigate

string navigate (string index, int direction);
returns index immediately smaller or larger than the provided index. direction can be -1 (smaller) or +1 (larger).
returns an empty string if no further index.

Example:

  event start()
  {
    store (index => "hi-score", value => " 10 ");
    say (fetch(index => "hi-score"));   // will say 10
  }

b) storage for worn objects

Data can be permamently stored in the user's inventory, even if the user relogs or if the object is unworn and worn again.

1. save_data_to_inventory

void save_data_to_inventory (string index, string value);

store the string 'value' in the inventory on the client's pc

. for worn object only

. index must have maximum 50 characters and contain only non-nul characters.

. an empty string as value deletes the record.

. you can store max 10_000 records, further records are silently ignored.

2. load_data_from_inventory

void load_data_from_inventory (string index, int direction = 0);
reloads a string that was previously stored in the client inventory
. for worn object only
. 'direction' can have the following values :
    -1 = obtain index smaller than provided index,
     0 = obtain provided index,
    +1 = obtain index larger than provided index.
. returns an empty string index and value if no further index.
. value is returned in the following event :

3. event inventory_data_arrived

    event inventory_data_arrived (string index, string value)
    {
      say ("index = '" + index + "'");
      say ("value = '" + value + "'");
    }

15. Particles

The command generate_particles() creates a particle emitter at the center of a mesh.

1. generate_particles

  void generate_particles (particule_parameters p);

Example:

  event start ()
  {
    particule_parameters p;

    clear p;

    p.mesh_nr      = 1;

    p.nb_particles = 10;
    p.pause        = 1000;

    // sprayer
    p.direction_angle = {-180.0, +180.0};
    p.height_angle    = {0.0, 0.0};
    p.radius          = 0.5;

    p.life[0].durations   = {15_000, 15_000};    // 15 seconds
    p.life[0].begin_speed = {1.0, 1.0};          // 1 m/s
    p.life[0].end_speed   = {1.0, 1.0};

    p.life[0].begin_color = {0xFFFFFFFF, 0xFFFFFFFF};  // white, 0% transparent
    p.life[0].end_color   = {0xFFFFFFFF, 0xFFFFFFFF};

    p.life[0].begin_size  = {{0.2, 0.2}, {0.2, 0.2}};   // size 0.2 x 0.2 m
    p.life[0].end_size    = {{0.2, 0.2}, {0.2, 0.2}};

    p.life[0].orientation = 0;     // always oriented vertically

    p.ambiant = true;
    p.sunlight = true;

//    p.texture = "brilliant";   // requires a texture called "brilliant" in folder scripts

    generate_particles (p);
  }

The structure particule_parameters contains the following fields :

struct particule_parameters
{
  int         mesh_nr;              // mesh nr where the emitter is created

  // bursts
  int         nb_particles;         // number of particles emitted per burst
  int         pause;                // pause time between bursts, in milliseconds
  int         nb_bursts;            // number of bursts, 0 means infinite

  // particle birth in box
  vector[2]   box;                  // particles are generated at random position within box

  // particle birth in sprayer
  float[2]    direction_angle;      // spray outward in horizontal angle range
  float[2]    height_angle;         // spray outward in vertical angle range
  float       radius;               // spray distance from center where particles are created

  // particle 3 lifetimes
  lifetime    life[3];              // 3 successive lifes, see below.

  bool        dont_follow_emitter;  // true = if emitter mesh moves or rotates, particles don't follow.

  bool        ambiant;              // true = particles receive ambiant light
  bool        sunlight;             // true = particles receive sunlight

  float       smooth;               // 0.0 .. 0.99  0.0 = rough, 0.99 = smooth
  float       metal;                // 0.0 .. 1.0   0.0 = other than metal, 1.0 = metal
  float       translucid;           // 0.0 .. 1.0   0.0 = non-translucid, 1.0 = translucid
  bool        front;                // false = transparency priority -3, true = transparency priority +3.

  int         emissive_color;       // glow color

  string(32)  texture;              // particle texture name, must be added in scripts folder

  int         mode_uv;              // same as mesh u, v
  float       u;
  float       v;

  bool        sort;                 // sort particles (nicer result for transparent textures but uses CPU !)

  // leash mode
  int       leash_id;       // id of object to link per leash (non-zero enables leash mode)
  int       leash_mesh_nr;  // mesh nr of distant object where to attach the leash (1 .. 32)
  float     leash_length;   // leash length in meters (maximum 256)
  float     leash_stretch;  // stretch factor (0..1) so that the particles overlap a bit at maximum length (0.0 = none, 0.2=chain rings)
  bool      leash_physics;  // true = pulls avatar back if further away than leash_length
}

Particles can have a maximum of 3 lifes during which they have a different behaviour.

The structure lifetime contains the fields hereafter.

When there are two fields, you can specify a lower and a higher limit, and a random value between both limits is selected for each particle.

struct lifetime
{
  int[2]        durations;           // particle life duration range, in msecs (max 1 day)
  float[2]      begin_speed;         // sprayer begin speed range
  float[2]      end_speed;           // sprayer end speed range
  vector[2]     begin_velocity;      // additional begin velocity in m/s
  vector[2]     end_velocity;        // additional end velocity in m/s
  vector[2]     begin_acceleration;  // additional begin acceleration in m/s2
  vector[2]     end_acceleration;    // additional end acceleration in m/s2
  vector[2]     begin_size;          // begin particle size (width, height) in meters
  vector[2]     end_size;            // end particle size (width, height) in meters
  int[2]        begin_color;         // begin color and transparency
  int[2]        end_color;           // end color and transparency
  int           orientation;         // 0=vertical on screen, 1=towards velocity, 2=horizontal in world, 3=chained.
}

To stop particles, set p.mesh_nr or p.nb_particles to zero.

Example:

  event start ()
  {
    particule_parameters p;
    clear p;
    generate_particles (p);
  }

16. Sound

1. play_sound

  void play_sound (string item_name,
                   float  volume = 1.0,     // 0.0 to 1.0
                   float  radius = 20.0);   // 0 to 1024 m
Play a sound.
Maximum 16 sounds can be started each minute, further sounds are ignored.
Sound files must be .wav 44.1KHz PCM 16-bit maximum 90 seconds or they are cut. You can convert sound files into this format using the free software https://audacity.fr/

Examples:

  play_sound ("ding");
  play_sound ("ding", volume => 1.0);
  play_sound ("ding", volume => 1.0, radius => 20.0);

2. start_ambiant_sound

  void start_ambiant_sound (string item_name,
                            float  volume = 1.0,     // 0.0 to 1.0
                            float  radius = 20.0);   // 0 to 1024 m
Plays a sound in a loop.
Sound files must be .wav 44.1KHz PCM 16-bit maximum 90 seconds or they are cut.
Only one ambiant sound per object is allowed, the previous ambiant sound is cancelled.

Examples:

  start_ambiant_sound ("sea");
  start_ambiant_sound ("sea", volume => 1.0);
  start_ambiant_sound ("sea", volume => 1.0, radius => 20.0);

3. stop_ambiant_sound

  void stop_ambiant_sound ();

17. Media

1. media_play

  void media_play (key avatar, string url);

Plays a multimedia file or stream (mp3, mp4, wav, ..)

Empty or illegal url will stop the media.

Providing an url that is currently playing has no effect.

For a video media you need to build a rectangular mesh screen with ratio width=4 height=3, disable Ambiant and Sunlight, set Color and Glow to 255,255,255, and then go in menu "Mesh / Various" and enable option 'Video'.

Example:

  event touch()
  {
    key k = touched_avatar();
    media_play (k, "http://dino.com/tina.mp4");
  }

2. media_pause

  void media_pause (key avatar, bool on);
Pause or restart media

Examples:

  media_pause (k, on => true);   // pauses
  media_pause (k, on => false);  // restarts

3. media_seek

  void media_seek (key avatar, int mode, float seconds);
Changes media play position
  . mode 0 : move to absolute position in seconds
  . mode 1 : move relative position in seconds (seconds can be negative)
  . mode 2 : relative from end (seconds must be negative)

Example:

  media_seek (k, mode => 1, seconds => -5.0);  // move 5 secs backwards

4. media_volume

  void media_volume (key avatar, float volume);   // 0.0 to 1.0
Changes media volume.
This also changes the setting in tools/sounds

Example:

  media_volume (k, volume => 0.5);  // half volume

18. Tcpip calls

1. tcp_open/close/send

You can exchange UTF-16 strings of maximum 1024 characters with an external tcp/ip server using the following commands :

  void tcp_open (string ip, int port);
  void tcp_close ();
  void tcp_send (string message);

2. event tcp_received/disconnected

and these script events :

  event tcp_received (string message)
  event tcp_disconnected ()

Strings are sent with a 2-byte prefixed length field.

You cannot send more than one string per second.

In case of event server_restart(), the connection is lost and must be reopened.

3. server_name/udp_port/tcp_port

You can obtain the planet server hostname and port using :

  string server_name ();   // max 256 characters

  int server_udp_port();   // used for planet, usually 13000
  int server_tcp_port();   // used for web, usually 80

19. Drag & Drop

1. event image_dropped

With your mouse, you can drag & drop images from your hard disk to display them on an in-world object.

Example:

  event image_dropped ()
  {
    set_mesh_texture (1, "");  // apply image to mesh 1
  }

The mesh display should have a 4/3 format (width = 4, height = 3).

Images will be automatically resized and centered with a transparent border.

The command set_mesh_texture(mesh_nr, ""); will have an effect only :

. for world objects (not attachments),
. for commands, (not in a job),
. in event image_dropped only.

In all other cases, it restores the default mesh texture.

Inside an event image_dropped, the following calls are allowed :

  key            touched_avatar ();          // who did the drag & drop
  int            touched_mesh_nr ();         // on what mesh nr was it dropped
  world_position touched_world_position ();  // on what world position
  vector         touched_mesh_position ();   // on what mesh position

20. Money

1. show_money_dialog

void show_money_dialog (key avatar);

Displays a money dialog window on the avatar's viewer.

2. money_balance

string money_balance (key avatar);

Returns user's money balance, example "+124.50"

3. give_activity_money

int give_activity_money (key beneficiary);
Transfers daily planet activity bonus money on user's account.
Returns the amount transferred, which can be 0.

4. ask_money_payment

void ask_money_payment (key customer, int amount, string comment);

Asks the customer to pay the amount to the script owner.

The amount must be between 1 and 999_999

A 5% tax is deduced from the amount when the money is coming from someone else.

The function generates the event money_received when the customer accepts.

5. event money_received

event money_received (key customer, int amount, string comment);

This event is generated when the customer just paid the amount to script owner.

Example:

  // payment script

  const int PRICE = 1;   // prix, price, preis

  void act (int action)
  {
    string(32) name;
    clear name;
    for (;;)
    {
      name = item_name (previous_name => name);
      if (name == "")
        break;
      if (item_type (name) != "SCR")
      {
        if (action == 1)
        {
          ask_money_payment (touched_avatar(), PRICE, name);
          break;
        }
        else
        {
          give_inventory (item_name => name);
          say ("gives " + name);
        }
      }
    }
  }

  event touch()
  {
    act (1);
  }

  event money_received (key customer, int amount, string comment)
  {
    act (2);
  }

6. give_money

int give_money (key beneficiary, int amount, string comment);

Transfers money from script owner to beneficiary.

In the object's Access tab you must set option "distribute money".

An empty beneficiary gives money to planet central bank.

The amount must be between 1 and 999_999.

The function returns an error code :

  
     0 transfer was successful
    -3 illegal amount, must be between 1 and 999_999
    -4 insufficient funds
    -5 not allowed in worn object
    -6 object not allowed to distribute money, see Access tab

7. area_bonus

int area_bonus (int amount);

This function increases an area maximum cost.

To use it, rezz the object on the area you want to increase.

You get 1 extra land cost per 10ρ of money, til a hard limit of 9000 area cost.

The function works only in an event touch or menu, and only if the script owner is the person who touches or clicks the menu.

The touching avatar's account gets debited.

The function returns an error code :

     0 ok
    -3 illegal amount, must be between 20 and 999_999.
    -4 insufficient funds.
    -5 not allowed in worn object.
    -6 illegal area (not owned).
    -7 cost (area is already at maximum cost 9000).
    -8 only in event touch or menu_selected.
    -9 touching avatar must be object owner.

21. Inter-Planet Communication

1. get_planet_id

pid get_planet_id ();

Obtain the unique planet id (pid) of this planet.
A pid is defined as:

  typedef int[2] pid;

Example:

  event start()
  {
    pid p;
    p = get_planet_id ();
    say ("planet = {" + itos(p[0]) + ", " + itos(p[1]) + "}");
  }

2. same_pid

bool same_pid (pid a, pid b);

Tests if two planet id's are identical.

3. send_message_to_planet

void send_message_to_planet (pid planet, int object_id, string message);

Send a message to an object on a specified planet, where it will trigger an event planet_message_received.

4. broadcast_message_to_planet

void broadcast_message_to_planet (pid planet, string script_name, string message);

Send a message to all scripts of the given name on a specified planet, where it will trigger an event planet_message_received.

5. event planet_message_received

This event is triggered when a message was received, from an object on another (or same) planet.
Inside the event, the function sender_object_owner() can be used to obtain the owner of the scripted object that sent the message.

Example:

event planet_message_received (pid planet, int object_id, string message)
{
  say ("on planet {" + itos(planet[0]) + ", " + itos(planet[1]) + "}"
    + " user " + avatar_name (sender_object_owner())
    + " object " + itos(object_id) 
    + " sent us message '" + message + "'");
}

6. sender_object_owner

key sender_object_owner();

obtain the owner of the scripted object that sent the message.
Can be used only in event planet_message_received.

7. teleport_to_planet2

void teleport_to_planet2 (key k, pid planet);
void teleport_to_planet2 (key k, pid planet, int domain);

Teleports avatar k to a planet and optionally to a domain.
The domain must be visible in public search.
The script object must have at least rank security officer on the origin domain.

22. Combat Mode

To configure the combat mode, choose the menu Tools / Settings: activate the combat mode via the C or F6 key, select if you want to run all the time, and choose the mouse speed.

To enter combat mode, press F6 or C.

1. set_arm_range

void set_arm_range (float distance);

set the range the selected arm will cover, in meters.

2. set_arm_power

void set_arm_power (int power);

set the power (the amount of damage) the selected arm will inflict.

3. shot

event shot (key victim, int power)

this event is called when we shot a victim.

4. damage

event damage (key shooter, int power)

this event is called when we have been shot.

Example:

event start()
{
  set_arm_power (3);
  set_arm_range (1024.0);
}

event shot (key victim, int power)
{
  say ("shot " + avatar_name (victim) + " " + itos(power));
}

event damage (key shooter, int power)
{
  say ("ouch! damage from " + avatar_name (shooter) + " " + itos(power));
}


Appendix A : Libraries

1. Conversions

  string itos (int    value, int width=0, string filler=" ");      // example: itos(23, 5, "0") == "00023"
  string ftos (float  value, int width=0, int decimals = 3);       // example: ftos(23.2, 5) == " 23.2"
  string btos (bool   value, int width=0);                         // example: btos(b) == "true"
  bool   stob (string str);                                        // example: stob(" true ") == true
  int    stoi (string str);                                        // example: stoi("-23") == -23
  float  stof (sring  str);                                        // example: stof(" 13.7 ") == 13.699
  float  itof (int    value);
  int    ftoi (float  value);                                      // truncates

2. String Manipulation

  int    len   (string value);                                     // example: len("abc") == 3
  string chr   (int c1, int c2, int c3, ..., int c16);             // example: chr(65,66,67) == "ABC"
  int    asc   (string value, int index=1);                        // example: asc("AB") == 65    asc("ABC",2) == 66
  string left  (string value, int count);                          // example: left("ABCD",2) == "AB"
  string right (string value, int count);                          // example: right("ABCD",2) == "CD"
  string mid   (string value, int index, int count=);         // example: mid("ABCDEF",2,3) == "BCD"   mid("ABCDEF",5) == "EF"
  int    pos   (string phrase, string word);                       // example: pos("ABCDEF","CD") == 3    pos("ABCDEF","X") == 0
  string dup   (string value, int count);                          // example: dup("-*",3) == "-*-*-*"
  string upper (string value);                                     // remove accents and convert to upper case
  string lower (string value);                                     // remove accents and convert to lower case
  string trim  (string value);                                     // remove leading and trailing spaces
  string ltrim (string value);                                     // remove leading spaces
  string rtrim (string value);                                     // remove trailing spaces
  string resolve_icons (string value);                             // converts sequences like :) in icon codes

3. Calendar

  struct date_time
  {
    int  year;     /* 1901 to 9999 */
    int  month;    /*    1 to   12 */
    int  day;      /*    1 to   31 */
    int  hour;     /*    0 to   23 */
    int  min;      /*    0 to   59 */
    int  sec;      /*    0 to   59 */
    int  msec;     /*    0 to  999 */
  }

  date_time now ();                                                // gmt time
  int       weekday            (date_time date);                   // (1=monday, 7=sunday)
  date_time add_seconds        (date_time date, int seconds);
  int       area_time_offset   ();                                 // seconds to add to gmt time to obtain local area time
  int       nb_days_since_1901 (date_time date);                   // nb days between 1/1/1901 and date

4. Math

  int       abs   (int value);
  int       min   (int a, int b, int c, .., int h);
  int       max   (int a, int b, int c, .., int h);
  int       rnd   (int a, int b);

  float     frnd  ();                                              // value between 0 included and 1 excluded
  float     fabs  (float value);

  float     sin   (float angle);                                   // angle in degrees
  float     cos   (float angle);                                   // angle in degrees
  float     atan2 (float y, float x);                              // result angle in degrees

  float     sqrt  (float angle);
  float     trunc (float angle);
  float     round (float angle);
  float     fmin  (float a, float b, float c, .., float h);
  float     fmax  (float a, float b, float c, .., float h);

Appendix B : Some Examples

A simple door script

  // door script

  bool g_is_open;

  event touch ()
  {
    float angle;

    g_is_open = !g_is_open;

    if (g_is_open)
      angle = 120.0;
    else
      angle = 0.0;

    set_mesh_rotation (mesh_nr => 1,  rotation => {0.0, 0.0, angle});
  }

A door script with closing door after 10 seconds

  // door script with closing after 10 seconds

  bool g_is_open;

  event touch ()
  {
    float angle;

    g_is_open = !g_is_open;

    if (g_is_open)
      angle = 120.0;
    else
      angle = 0.0;

    set_mesh_rotation (mesh_nr => 1,  rotation => {0.0, 0.0, angle});

    start_timer (nr => 0, seconds => 10.0);
  }

  event timer (int nr)
  {
    set_mesh_rotation (mesh_nr => 1,  rotation => {0.0, 0.0, 0.0});
    g_is_open = false;
  }

A light switch

  // lamp script

  bool g_is_enabled;

  event touch ()
  {
    float range;

    g_is_enabled = !g_is_enabled;

    if (g_is_enabled)
      range = 2.0;
    else
      range = 0.0;

    set_mesh_light (mesh_nr => 1, color => 0xFFFFFF, attenuation => 0.6, range => range);
  }

Appendix C : Script Cost

Script cost depends on the number of instructions, on the size of global variables and the storage for string literals.

Local variables are not included.

Appendix D : In case of bug

In case of bug, send a description to : marcsamu@hotmail.com