How to write scripts

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

Writing scripts
---------------
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.


Script Library Reference
========================

    Chapters
    ========
    Events
    Conversation
    Mesh
    Object
    Move Batches
    Avatar
    Sitting and Animations
    Menus
    Inventory
    Blockings
    Text Files
    Permanent Storage
    Particles
    Sound
    Media
    Tcpip calls
    Drag & Drop
    Money
    Conversions
    String Manipulation
    Calendar
    Math
    Some Examples


Events
======

1. Start
--------

  // start is the initial event after the script is compiled, the object is rezzed from inventory, or worn.
  // the script is reinitialized entirely when recompiled, rezzed or worn.

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


2. Touch
--------

  // user clicked an object with left mouse button
  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 :

  key touched_avatar ();
  Returns a unique permanent key denoting the avatar that touched the object

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

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

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


The following data types are predefined :

  typedef int[4] key;    // key denotes a unique avatar identifier

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

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


Event collision is generated when an avatar collides with a scripted object.

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.


4. Listen
---------

  // object heard an avatar talk

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

When an avatar writes a chat line, a nearby object receives the line it its event listen.
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
--------

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

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

  Each script can start maximum 16 timers (nr from 0 to 15) with different durations.
  When a timer expires, the event timer() is called with the timer number that 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 called 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
-------------------

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

  The event message_received is called when another script sends a message
  to our script using send_message or broadcast_message.

  void send_message (int object_id, string message);
  void broadcast_message (string script_name, string message);
  key sender_object_owner();    // query the owner of the object who sent us a message


  // 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 objects having a script called script1");
  }



7. Server Restart
-----------------

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

  The event server_restart is called when the planet server was just started after some time off.
  A script can use this event to reprocess any clock-dependant computations.



Conversation
============

Say
---

  void say (string value);

  Display text on chat within a range of 20m

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


say_to
------

  void say_to (key avatar, string value);

  Same as say but for a single avatar.



Displaying Icons
----------------

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

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


Displaying Images
-----------------

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


Writing in different font and color
-----------------------------------

  // 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");
  }


Mesh
====

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
  }


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


Set Mesh Rotation
-----------------

  void set_mesh_rotation (int mesh_nr, vector rotation);

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


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


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


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


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


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



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.


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



Object
======
int object_id();
  returns object ID.

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)

string object_name ([int object_id]);
  returns object's name (max 32 characters)

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.

key object_owner ([int object_id]);
  returns object's owner.

string object_owner_date ([int object_id]);
  returns a date in format "YYYYMMDD"

int object_nb_meshes();
  return object's number of meshes.

int domain_id();
  return domain's id.

string domain_name();
  return domain's name.

int object_access();
  return object's access rights, a sum of the following values :
  // const RIGHTS RIGHT_IS_LOCKED             = 1;
  // const RIGHTS RIGHT_ANYONE_CAN_MOVE       = 2;
  // const RIGHTS RIGHT_ANYONE_CAN_BUY        = 4;
  // const RIGHTS RIGHT_OWNER_CAN_SELL        = 16;
  // const RIGHTS RIGHT_OWNER_CAN_MODIFY      = 32;
  // const RIGHTS RIGHT_NEXT_OWNER_CAN_SELL   = 64;
  // const RIGHTS RIGHT_NEXT_OWNER_CAN_MODIFY = 128;
  // const RIGHTS RIGHT_BUILDER_CAN_TAKE_COPY = 256;
  // const RIGHTS RIGHT_BUILDER_CAN_MODIFY    = 512;

world_position object_world_position (int object_id);
  returns object's world position.

vector object_rotation (int object_id);
  returns object's rotation.

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.

void set_object_rotation (int object_id, vector rotation);
  rotates object

int rezz_object_relative (string item_name [, vector         position [, vector rotation]]);
int rezz_object_absolute (string item_name  , world_position position [, vector rotation]);
  rezzes 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)

void wear_object (key avatar, string item_name);
  Automatically make an avatar wear an object.
  The object is added in the avatar's inventory, in the folder "Temporary".
  When unworn, the object is moved into the folder Trash.
  The avatar cannot edit or reuse the object once it's in trash.

void unwear_object ();
  Unwear the worn object.
  Allowed only if the script runs in a worn object.

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.

void delete_object();
  deletes the object running this script.


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


int next_object_of_area (int area_x, int area_y, ref int snr);
  list all objects of an area.
  returns next object_id of this area, or 0 if there are no more objects.
  snr must be initialized to 0 and is changed at each call.
  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));
    }
  }



Move Batches
============
Instead of sending individual commands to objects and meshes,
you can also send a sequence of commands in batches.

All commands of a batch must be enclosed between :

  void begin_move ();
  void end_move ();

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 objects (for repeating=true only)
    sync_delay  : a synchronization value >= 0 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);

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

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 move must have at least one command job_duration.
. batches are executed in order.
. a repeating batch will no longer repeat when another following batch is sent.

Further commands :


  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.


 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.


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


Avatar
======

  bool avatar_online (key k);
  Returns true if an avatar is online, false if not.

  string avatar_name (key k);
  Returns an avatar current full name (maximum 71 characters).

  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.

  void set_avatar_rank (key k, int rank);
  Changes an avatar's rank, if you have rights for that.

  int avatar_gender (key k);
  Returns an avatar gender : 0=male, 1=female.

  int avatar_adult (key k);
  Returns an avatar's adult state : 0=non-adult, 1=adult, -1=invalid key.

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

  int avatar_experience (key k);
  Returns avatar experience, or -1 if avatar key is unknown.

  int avatar_timezone (key k);
  Returns avatar timezone.

  string avatar_title (key k);
  Returns avatar title, max 24 characters.

  world_position avatar_world_position (key k);
  Returns an avatar world position (z is 0 if standing on floor at altitude 0).

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


Avatar Teleportation
--------------------
  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);
  }



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



Sitting and Animations
======================
bool is_sitting (key avatar);
  Tests if an avatar is sitting on this object.

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.

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.

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.

bool is_animation_active (string animation, key avatar);
  Tests if this animation currently runs on the given avatar.

void override_animation (int typ, string animation);
  override standard animations (stand, walk, run, fly)
  for a worn object only.
  typ : 0=stand, 1=walk, 2=run, 3=fly
  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);
    }
  }


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.


Sitters
-------

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


Animated Joints and 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.

  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.

  bool object_is_animation_active (string animation);
    Tests if an animation is currently running on the given object.



Menus
=====

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.

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


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.

string item_name (string previous_name);
  lists all items present in the script folder of an object.
  returns name of next item, "" if no more.

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.


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


Blockings
=========

a) Blockings from objects in world
----------------------------------
Blockings can be applied to avatars that are within a box-shape space around a rezzed object.

  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
  //   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
  //   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);

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

  // 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);

Example:

  event touch()
  {
    key k;
    clear k;
    block_avatar_options (avatar  => k,
                          options => 0x0001 + 0x0400,
                          space   => {min => {-5.0, -4.0, 0.0},
                                      max => {+5.0,  4.0, 4.0}});
  }


b) Blockings from worn objects
------------------------------

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 (when 0.0 the avatar is invisible for myself)
  float touch_distance;   // in meters.
  float radar_distance;   // in meters. 0.0 to 256.0
  uint  fog_setting;      // lower 3 bytes = fog rgb color, high byte = fog level 0..15.
}

// option values:
// 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
// 0x8000 : set fog, see .fog_setting

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.


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

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

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

Permanent Storage
=================

a) storage for world objects
----------------------------

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.


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.


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.

void save_data_to_inventory (string index, string value);

  store data in inventory on client 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.

void load_data_from_inventory (string index, int direction = 0);

  load data from inventory on client pc
  . for worn object only
  . value is returned in the following event :

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

  . direction can be :
    -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.


Particles
=========

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

  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

  int         shininess;            // 0 .. 4  0=off, 1 to 4 shininess intensity
  int         shinysize;            // 0 .. 7  0=no spot, 1 to 7 spot size

  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, 1=towards velocity, 2=horizontal, 3=leash.
}


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

Example:

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


Sound
=====

Play a sound
------------
  void play_sound (string item_name,
                   float  volume = 1.0,     // 0.0 to 1.0
                   float  radius = 20.0);   // 0 to 1024 m

Sound files must be .wav 44.1KHz PCM 16-bit maximum 90 seconds or they are cut.
Maximum 8 sounds can be started each minute, further sounds are ignored.

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


Play a sound in a loop
----------------------
  void start_ambiant_sound (string item_name,
                            float  volume = 1.0,     // 0.0 to 1.0
                            float  radius = 20.0);   // 0 to 1024 m

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


Stop ambiant sound
------------------

  void stop_ambiant_sound ();


Media
=====

Play a media
------------
  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");
  }


Pause a media
-------------
  void media_pause (key avatar, bool on);

Pause or restart media

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


Change media play position
--------------------------
  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


Change 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


Tcpip calls
===========
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);

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.


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


Drag & Drop
===========
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


Money
=====

void show_money_dialog (key avatar);
  Displays a money dialog window on the avatar's viewer.

string money_balance (key avatar);
  Returns user's money balance, example "+124.50"

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

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.

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


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

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 20ρ 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.


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

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

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 */
  }

  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

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


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


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.

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