Comment écrire des scripts

Table des matières


1. Ecrire des scripts

Vous n'avez jamais scripté ?
Cliquez sur cours de script pour apprendre les bases en programmation.

1. Compilation

Dans chaque objet 3D, vous pouvez ajouter des scripts qui s'exécutent et rendent l'objet vivant.

Pour créer un script :
- éditer un objet (Objet > Editer), aller dans le sous-menu "Scripts" et cliquer "Ajouter".
- sélectionner la ligne du script "1 SCR Script" et cliquer "Editer".
- écrire votre script dans l'éditeur et cliquer le bouton "Appliquer".

Dans la liste des script, * indique une erreur de compilation, et ! indique une erreur d'exécution.

Cliquez le bouton "Voir Erreur" dans l'éditeur pour voir les détails de l'erreur.

2. Description du langage script

Le langage de script Planet est un dérivé simple de la famille C.

Les types de données incluent : bool, int, float, array, struct, et string(N) avec N dans 0 ... 1024.

Un script est composé de : const, variables globales, typedef, fonctions, et gestionnaires d'évènements.

Les fonctions et les gestionnaires d'événements peuvent contenir des const, typedef et variables locales.
Les paramètres des fonctions peuvent avoir le mode 'in' (par défaut), 'ref', ou 'out'.
Si un paramètre de fonction ou une valeur de retour de fonction est de type string, alors le type doit apparaître sans spécification de longueur maximale.

Les instructions de contrôle comprennent : if, switch, for, while, break, continue, assert, abort, clear (remise à zéro des variables).

Les constantes/valeurs de tableaux/structures peuvent apparaître sous forme d'agrégats, par exemple : {1, 2, i}.

Les opérateurs incluent les unaires : + - ! ~ -- ++ et binaires : * / % + - << >> == != < > <= >= && || & | ^

3. Vitesse d'exécution des scripts

Un script typique est censé gérer de courts événements du système Planet, comme l'ouverture d'une porte lorsque l'utilisateur clique dessus.

Si votre script exécute un très grand nombre d'instructions par jour, ce n'est pas normal. Planet va alors le pénaliser en le ralentissant.

Un script qui exécute beaucoup d'instructions par jour est probablement mal écrit :
- soit il tourne en rond dans des boucles infinies,
- soit il exécute des évènements timer avec des durées trop courtes pendant toute la journée,
- soit il reçoit une grande quantité de messages en provenance d'autres scripts.

Nous vous encourageons à vérifier la consommation d'instructions de vos scripts via le menu Outils > Scripts. Vérifiez la colonne 'Usage CPU' : si elle indique plus de 1.000 unités, il y a peut-être un problème. Au delà de 10.000 il y a certainement des améliorations à faire ou des bugs à corriger. Vous pouvez placer des instructions say() par exemple dans l'event timer pour vérifier quand il se déclenche.

Pendant que la fenêtre du menu Outils > Script est ouverte, des petites bulles rouges montent au dessus d'un objet lorsqu'un script change sa position ou son aspect. Trop de bulles rouges à répétition indiquent un script mal écrit qui envoie ses commandes l'une après l'autre au lieu d'utiliser un batch de mouvement comme expliqué au chapitre 4.21. Les batchs de mouvement sont très conseillés car ils ne consomment pas de CPU serveur ni de traffic réseau pendant qu'ils tournent. Ils permettent en outre des mouvements réguliers et sans secousses.

2. Evènements

1. Start

Quand le script est compilé, ou que l'objet est extrait de l'inventaire ou porté sur soi, le script est entièrement réinitialisé. L'évenement START est alors exécuté en premier.

  event start ()
  {
    say ("bonjour, je démarre !");
  }

2. Touch

Quand l'utilisateur clique sur l'objet avec le bouton gauche de la souris, l'évenement TOUCH est exécuté.

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

Lors du traitement d'un événement TOUCH, les fonctions suivantes peuvent être appelées :

  // renvoie une clé permanente unique indiquant l'avatar qui a touché l'objet
  key touched_avatar ();

  // renvoie le numéro de mesh touché (1 à 32)
  int touched_mesh_nr ();

  // renvoie la position touchée en coordonnées absolues du monde (voir ci-dessous)
  world_position touched_world_position ();

  // renvoie la position touchée par rapport au vecteur de centre du mesh (voir ci-dessous)
  vector touched_mesh_position ();

Les types de données suivants sont prédéfinis :

  typedef int[4] key;     // key est une clé unique identifiant un avatar dans le système Planet

  struct world_position   // une coordonnée absolue sur la planète
  {
    int x, y, z;            // coordonnée en 1/256 mm
  }

  struct vector           // une petite position ou rotation
  {
    float x, y, z;          // coordonnée en m
  }

La fonction same_key() permet de vérifier si deux clés sont identiques :

  bool same_key (key k1, key k2);

La fonction is_null_key() permet de vérifier si une clé est nulle :

  bool is_null_key (key k);

3. Collision

L'événement COLLISION est exécuté lorsqu'un avatar entre en collision avec l'objet.

  // avatar est entré en collision avec un objet scripté.

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

    say (avatar_name(k) +
         " a collisioné avec mesh nr " + itos(nr)
              + " normal " + ftos(n.x)
                     + " " + ftos(n.y)
                     + " " + ftos(n.z));
  }

Les fonctions suivantes peuvent être appelées lors d'un événement COLLISION :

  key collision_avatar();      // avatar qui est entré en collision
  int collision_mesh_nr();     // numéro de mesh qui est entré en collision
  vector collision_normal();   // vecteur normalisé avatar vers objet

Les objets ayant la matière COLLISION n'ont pas de physique (comme la matière PHANTOM) mais ils génèrent un événement collision. Ils peuvent être utilisés comme capteur pour détecter l'arrivée d'un avatar.

Placer plusieurs objets en matière COLLISION qui se recouvrent n'est pas une bonne idée, parce que seul l'un d'eux (au hasard) va déclencher un évènement collision. Vous aurez alors l'impression que cet évènement n'est pas fiable car il ne se déclenchera pas à chaque fois sur le même objet.

4. Listen

Lorsqu'un avatar écrit une ligne de chat, un objet proche reçoit la ligne dans son évènement LISTEN.

  // l'objet écoute parler un avatar

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

Notez que les events listen dans les objets en mouvement doivent être évités, ils ne fonctionnent pas de manière fiable.

La portée par défaut est de 50m mais elle peut être modifiée avec set_listen_range() :

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

Lorsqu'il n'est pas utilisé, il est conseillé de régler la portée à 0.0 pour réduire la consommation cpu du serveur.

Pour réduire également la consommation cpu du serveur, vous pouvez définir un filtre pour capturer uniquement certains messages de chat :

  void set_listen_filter (string filter);
Le filtre par défaut est "*" pour capturer tous les messages.
"*" remplace plusieurs caractères, " ?" remplace un caractère.

Exemple: "!*" ne capturera que les lignes de chat commençant par "!", comme "!start" et "!stop".

5. Minuterie

Chaque script peut démarrer au maximum 16 minuteries (nr de 0 à 15) avec des durées différentes.
Quand une minuterie expire, l'évènement TIMER est exécuté avec le numéro de la minuterie qui a expiré.

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

  // cet événement est appelé à l'expiration d'une minuterie
  event timer (int nr)     // nr est le numéro de la minuterie
  {
    say ("timer number " + itos(nr) + " a expiré");
  }

Des minuteries périodiques peuvent être créées en redémarrant la minuterie chaque fois qu'elle a expiré, cependant, comme cela consomme du cpu, il faut l'utiliser aussi rarement que possible.

Exemple:

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

  // cet événement est appelé à l'expiration d'une minuterie
  event timer (int nr)     // nr est le numéro de la minuterie
  {
    say ("bonjour !");
    start_timer (nr => 0, seconds => 3.0);   // répéter après 3 secondes
  }

6. Message_Received

Les scripts peuvent s'envoyer des messages entre eux avec send_message() ou broadcast_message().

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

Exemple d'envoi:

  event touch()
  {
    send_message (object_id => 235,
                  message   => "bonjour à tous les scripts de l'objet 235");

    broadcast_message (script_name => "script1",
                       message     => "bonjour à tous les scripts de la planète appelés script1");
  }

L'événement message_received est executé lorsque notre script reçoit le message d'un autre script.

  event message_received (int sender_object_id, string message)
  {
    say ("reçu : " + message + " de " + itos(sender_object_id));
  }

La fonction sender_object_owner() renvoie le propriétaire de l'objet qui nous a envoyé le message.

  key sender_object_owner();

Consultez le chapitre "Communication inter-planètes" pour envoyer des messages entre scripts se trouvant sur des serveurs différents.

7. Server Restart

L'évènement server_restart est appelé lorsque le serveur planet vient d'être redémarré.

  event server_restart()
  {
    say ("bonjour, le serveur planet vient de redémarrer");
  }

Un script peut utiliser cet événement pour retraiter tout calcul dépendant de l'horloge.

2. Conversation

1. Say

  void say (string value);
Affichage de texte sur le chat dans un rayon de 20m
  // Exemple:
  event start ()
  {
    say ("Bonjour, je démarre !");
  }

2. say_to

  void say_to (key avatar, string value);

Idem à say mais destiné à un seul avatar.

3. Affichage d'icônes

Les codes chr(0,i) permettent d'afficher des icones, remplacer i par un numéro.

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

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

4. Affichage d'images

Vous pouvez afficher une image sur le chat en la plaçant dans le dossier script.

  event touch ()
  {
    // nécessite une texture "sun" dans le dossier script de l'objet
    say ("voici mon soleil : " + image ("sun") + " :)");
  }

5. Ecriture en différentes polices et couleurs

Les codes chr(1,i) permettent de changer de font, chr(2,i) permet de changer de style d'écriture, et chr(4,i,j) permet de changer de couleur, remplacer i et j par des numéros.

  // message d'accueil

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

Rend le mesh actif ou inactif.
Les meshs inactifs sont invisibles et ne causent aucune collision avec l'avatar.

Exemple:

  event start ()
  {
    set_mesh_active (mesh_nr => 1, enable => false);  // désactiver le mesh 1
    set_mesh_active (mesh_nr => 2, enable => true);   // activer le mesh 2
  }

2. Set Mesh Position

  void set_mesh_position (int mesh_nr, vector position);

Déplace un mesh à l'intérieur d'un objet (de -32.768 à +32.767 m)
La position est donnée sous forme de vecteur en coordonnées x,y,z.

Exemple:

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

Tourne un mesh à l'intérieur d'un objet.
La rotation est donnée sous forme d'angles x,y,z en degrés (-360.0 à +360.0)
L'axe de rotation est le centre 0,0,0 du mesh.

Exemple:

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

Permet de changer la couleur du mesh.
La couleur est définie comme valeur RVB hexadécimale (0xBBVVRR)

Exemple:

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

5. Set Mesh Transparency

  void set_mesh_transparency (int mesh_nr, float transparency);

Modifie la transparence du mesh.
La valeur de transparence doit être comprise entre 0.0 and 1.0

Exemple:

  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,  // entre 0.0 et 1.0
                       float range,        // entre 0.0 et 65.0
                       int   cone);        // 0=ponctuelle, ou taille du spot entre 1 et 255.

Définit une lumière ponctuelle ou spot à la coordonnée 0,0,0 du mesh.
Pour désactiver la lumière, appeler cette fonction avec distance (range) 0.0

Exemple:

  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 = lumière du jour éclaire le mesh
                           bool  sunlight); // true = soleil éclaire le mesh

Permet de supprimer la lumière du jour ou le soleil qui éclaire le mesh, par exemple s'il se trouve dans une cave.

Exemple:

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

Permet de faire briller un objet dans l'obscurité.

Exemple:

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

Faites bouger toutes les textures de ce mesh en changeant l'UV dynamiquement.
Il y a 6 modes :

  // mode 0 : éteindre UV
  set_mesh_uv (mesh_nr => 1, mode => 0);

  // mode 1 : définir un déplacement U,V fixe
  set_mesh_uv (mesh_nr => 1, mode => 1, u => 0.5, v => 0.3);

  // mode 2 : définir un glissement U,V
  // u, v denote la vitesse du glissement
  set_mesh_uv (mesh_nr => 1, mode => 2, u => 0.1, v => 0.3);

  // mode 3 : images successives
  // la texture doit être constituée de N images de même taille, disposées verticalement.
  // Les images successives sont montrées comme dans un diaporama.
  // u indique le nombre d'images, et v indique la vitesse du changement
  // le paramètre optionnel ping_pong indique si l'on veut afficher les images à l'envers en arrivant à la fin.
  // le paramètre optionnel one_shot indique si nous voulons afficher toutes les images une seule fois, ou les répéter indéfiniment.
  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 : texture en rotation
  // u représente l'angle de rotation maximal (360.0 pour un cercle complet).
  // v indique la vitesse de rotation.
  // le paramètre optionnel ping_pong indique si l'on veut revenir en arrière en arrivant à la fin.
  // la rotation se produit à u,v = 0,0 donc si une rotation centrée est souhaitée
  // vous devez régler l'uv de la texture dans la plage -0.5 à +0.5.
  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 : texture pulsée
  // u représente l'amplitude de la pulsation.
  // v indique la vitesse.
  // paramètre optionnel ping_pong indique si nous voulons un mouvement de va-et-vient.
  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);

Enfin, l'option 'perlin' peut être activée pour les textures d'eau horizontales.

10. Set Mesh Texture

  void set_mesh_texture (int mesh_nr, string texture);

Remplace la texture de tout un mesh par une nouvelle texture provisoire.
La nouvelle texture doit être copiée dans le répertoire des scripts de l'objet.
Un nom de texture vide "" restaure à nouveau la texture originale.
De même, si vous déposez l'objet sur le terrain il reçoit à nouveau sa texture originale.

Exemple:

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

4. Objet

1. object_id

int object_id();
retourne l'ID de l'objet. C'est un numéro qui identifie l'objet sur ce serveur Planet jusqu'à ce que l'objet soit effacé.

2. is_worn

bool is_worn ();
retourne vrai si l'objet courant est porté, faux s'il est créé sur terre.
(en fait un objet porté a un object_id négatif, un objet sur la terre a un objet id positif)

3. object_name

string object_name ([int object_id]);
retourne le nom de l'objet (max 32 caractères)

4. set_object_name

void set_object_name (string name);
pour un objet porté, change temporairement le nom de l'objet, de sorte que les appels say() utilisent un nom différent.
pour un objet sur terre, le changement de nom est permanent.

5. object_owner

key object_owner ([int object_id]);
retourne le propriétaire de l'objet.

6. object_owner_date

string object_owner_date ([int object_id]);
retourne une date en format "YYYYMMDD"

7. object_nb_meshes

int object_nb_meshes();
retourne le nombre de meshes de l'objet.

8. domain_id

int domain_id();
return l'id du domaine où se trouve l'objet.

9. domain_name

string domain_name();
retourne le nom de domaine où se trouve l'objet.

10. domain_adult

bool domain_adult();

teste si l'objet est situé sur un domaine adulte.

11. object_access

int object_access();
retourne les droits d'accès de l'objet, une somme des valeurs suivantes :
  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

Vous pouvez tester des accès individuals avec l'opérateur &

12. object_world_position

world_position object_world_position (int object_id);
retourne la position de l'objet sur la planète.

13. object_rotation

vector object_rotation (int object_id);
retourne la rotation de l'objet.

14. set_object_world_position

int set_object_world_position (int object_id, world_position position);
déplace l'objet
renvoie 0 si OK, ou l'une des valeurs négatives suivantes :
         -1 : mauvais object_id
         -2 : pas de droits d'accès de déplacement sur cet objet
         -3 : pas de droits d'accès sur la nouvelle area
         -4 : la nouvelle area est pleine

Vous pouvez déplacer un objet au-dessus de la mer, mais il sera supprimé s'il reste 1 heure en mer sans qu'aucun avatar ne le regarde.
Vous ne pouvez pas déplacer un objet différent de l'objet courant lui-même si l'objet script est porté ou a été rezzed par script.

15. set_object_rotation

void set_object_rotation (int object_id, vector rotation);
fait tourner l'objet

16. rezz_object_relative/absolute

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

dépose un objet dans le monde, soit à une position relative de l'objet parent, ou à une position absolue dans le monde.
renvoie une valeur positive (id de l'objet créé), ou une erreur négative (-1 = pas le droit de rezzer, -2 = area pleine)
Cette fonction n'est autorisée que dans les évènements touch, collision, menu_selected ou money_received.

17. parent_object_id

int parent_object_id ();
retourne un identifiant positif de l'objet parent qui a rezzé ou porté cet objet,
ou -1 s'il n'a pas été rezzé ou porté par un script,
ou -2 si l'id est maintenant invalide parce que l'utilisateur s'est reloggé.
Cette fonction ne doit être utilisée que lors du premier start car le parent peut rapidement devenir invalide.

18. delete_object

void delete_object();
supprime l'objet exécutant ce script.

19. event child_died

Si vous rezzez un objet dans le monde, puis que cet objet est supprimé, alors l'objet parent qui l'a rezzé reçoit un événement child_died avec l'object_id de l'objet qui a été supprimé :

  event child_died (int child_id)
  {
  }

Exemples:

  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 ("au revoir");
    delete_object ();
  }

20. next_object_of_area

int next_object_of_area (int area_x, int area_y, ref int snr);
liste tous les objets de l'area.
snr doit être initialisé à 0 et est changé à chaque appel.
renvoie l'id de l'objet suivant de cette area, ou 0 s'il n'y a plus d'objets.
si l'objet script bouge, la fonction n'est pas fiable.

Exemple:

  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. Batches de mouvement

Pour bouger un objet ou un mesh, la manière naive consiste à répéter des commandes plusieurs fois par seconde. Cela produit cependant un mouvement saccadé, utilise beaucoup de CPU et génère pas mal de traffic sur internet. Bref cela vous fait lagger.

La manière sérieuse consiste à utiliser un batch de mouvement. Pour cela, il faut préparer à l'avance une séquence de mouvements à exécuter. La séquence sera compressée et envoyée sur le PC une seule fois où elle sera executée sans déranger le serveur. Par exemple pour ouvrir une porte, vous ferez un batch qui donne juste le temps de mouvement et la rotation finale, et le PC va s'occuper de générer toutes les positions intermédiaires. Le PC peut même répéter votre séquence un grand nombre de fois, imaginez par exemple un ventilateur.

Les batchs de mouvement sont très conseillés car ils ne consomment pas de CPU serveur ni de traffic réseau pendant qu'ils tournent. Ils permettent en outre des mouvements réguliers et sans secousses.

1. begin_move, end_move

Toutes les commandes d'un batch doivent être entourées par :

  void begin_move ();
  void end_move ();

2. move_job

A l'intérieur d'un batch, vous pouvez spécifier un ou plusieurs jobs, chacun doit commencer par :

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

    repeating   : true pour répéter les commandes.
    sync        : true pour synchroniser avec d'autres jobs de même durée totale (pour repeating=true uniquement)
    sync_delay  : une valeur de décalage de synchronisation en millisecondes (pour sync=true uniquement).

Vous pouvez spécifier une ou plusieurs des commandes suivantes dans un 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);

Quand l'une de ces commandes est spécifiée entre begin_move() et end_move(), au lieu d'exécuter la commande immédiatement celle-ci est enregistrée dans le batch de mouvement. Une fois arrivé au end_move(), le batch est clôturé, vérifié, compressé, et envoyé du serveur au PC pour exécution.

3. job_duration

Entre les commandes, vous pouvez spécifier une durée pour provoquer un fondu enchaîné entre l'état précédent et l'état suivant avec :

  void job_duration (int duration);    // la durée doit être >= 16 millisecondes

4. stop_move

Pour arrêter un batch en cours d'exécution, appelez :

  void stop_move ();
Quelques règles :
. si vous spécifiez une commande directe en dehors du batch, le serveur arrêtera d'abord le batch.
. la commande set_object_world_position() ne peut être spécifié que dans le premier job d'un batch.
. un job répétitif doit avoir au moins une commande job_duration.
. un job non-répétitif ne peut pas avoir une commande job_duration à la fin.
. les batchs sont exécutés dans l'ordre.
. un batch répétitif ne se répète plus lorsqu'un autre batch est défini.

Autres commandes :

5. end_move2

  int end_move2 ();

Au lieu de end_move() qui s'arrête sur une erreur d'area, vous pouvez aussi utiliser end_move2() qui renvoie 0 si OK ou une erreur négative si l'area ne peut pas être traversée.

6. nb_queued_moves

 int nb_queued_moves ();
Vous pouvez interroger le nombre de mouvements mis en file d'attente au cas où plusieurs mouvements sans répétion sont en attente.
Notez qu'un maximum de 128 mouvements sont autorisés, après quoi le script s'arrête.

7. rest_move_duration

 float rest_move_duration ();
Renvoie le temps qu'il faudra à un batch non-répétitif pour s'arrêter, en secondes.

En voici quelques exemples :


Ventilateur tournant
--------------------

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

Les angles de rotation doivent être espacés de moins de 180° pour garantir une trajectoire déterministe entre deux angles.

Une porte qui bouge doucement
-----------------------------

  // porte

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


Tapis volant
------------

  // Tapis volant

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


Objet de couleur pâlissante
 --------------------------

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


Cube rebondissant
-----------------

  // cube rebondissant qui change de couleur

  event start()
  {
    int i;

    begin_move ();

      // job 1 : position de rebond vers le haut et vers le bas
      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);
        }

      // job 2 : modification des angles de rotation
      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);

      // job 3 : changement de couleur
      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);
Renvoie vrai si un avatar est en ligne, faux sinon.

2. avatar_name

  string avatar_name (key k);
Retourne le nom complet d'un avatar (71 caractères maximum).

3. avatar_rank

  int avatar_rank (key k);
Retourne un rang d'avatar : -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);
Retourne true si l'avatar est présent dans les membres du domaine sur lequel se trouve l'objet, false sinon.

5. set_avatar_rank

  void set_avatar_rank (key k, int rank);
Change le rang d'un avatar, si vous en avez le droit.

6. avatar_gender

  int avatar_gender (key k);
Retourne un genre d'avatar : 0=homme, 1=femme.

7. avatar_adult

  int avatar_adult (key k);
Retourne l'état adulte de l'avatar : 0=mineur, 1=adulte, -1=key invalide.

8. avatar_language

  int avatar_language (key k);
Retourne la langue de l'avatar (0 = français, 1 = anglais, 2 = allemand, -1 = key incorrecte)

9. avatar_experience

  int avatar_experience (key k);
Retourne l'experience de l'avatar, ou -1 si key est inconnue.

10. avatar_timezone

  int avatar_timezone (key k);
Retourne la zone de temps de l'avatar, en heures, de -12 à +12.

11. avatar_title

  string avatar_title (key k);
Retourne le titre de l'avatar, maximum 24 caractères.

12. avatar_world_position

  world_position avatar_world_position (key k);
Renvoie une position de l'avatar sur la planète (Z est 0 si debout sur le sol à l'altitude 0).

13. avatar_z_rotation

  int avatar_z_rotation (key k);
Renvoie une orientation de l'avatar, comme suit :
        0          = nord,  orienté Y positif
       90          = est,   orienté X positive
      -90          = ouest, orienté X negative
      -180 ou +180 = sud,   orienté Y négatif

Exemple:

  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);
Téléporte un avatar.

Exemple:

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

téléporte un avatar sur une autre planète.

L'url doit avoir la forme "planet://37.59.48.75/domain4".
Si spécifié, le domaine doit être visible dans la recherche publique.

L'objet script doit avoir au moins le rang security officer sur le domaine de départ.

Voir aussi la commande teleport_to_planet2 dans le chapitre 21. Communication inter-planètes.

16. next_avatar

key next_avatar (ref int snr);
liste un à un tous les avatars dans un rayon de 256 mètres.
snr doit être initialisé à 0, il est changé à chaque appel.
Si snr vaut 0 après l'appel il ne reste plus d'avatars.
Cette fonction n'est autorisée que dans les évènements touch, collision, listen, click_avatar ou menu_selected.

Exemple:

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

L'événement owner_login est appelé lorsque le propriétaire de l'objet se connecte ou se déconnecte.

. il est utilisé typiquement pour un cube de présence.
. il ne fonctionne pas dans les objets portés.

18. avatars_online_count

  int avatars_online_count();

retourne le nombre d'avatars présents sur cette planète (0 à 1024).

19. avatar_key

  key avatar_key (string name);

renvoie la clé d'avatar correspondante au nom d'avatar fourni. Le nom peut être un peu inexact, la fonction va alors choisir le nom le plus proche. Une clé nulle est retournée si l'avatar n'a pas été trouvé, ce qui peut être testé avec is_null_key().

6. S'asseoir, animations

1. is_sitting

bool is_sitting (key avatar);
Teste si un avatar est assis sur cet objet.

2. sit

bool sit (int mesh_nr, vector position, vector rotation, key avatar);
Assied un avatar avec position/rotation sur le nr de mesh de cet objet.
Ceci annule tout sit précédent et annule toutes les animations précédentes.
Le script doit s'exécuter sur le même domaine que l'avatar, sinon il renvoie faux.

3. unsit

void unsit (key avatar);
Fait qu'un avatar se lève s'il était assis sur cet objet et arrête toutes les animations qui ont été lancées par cet objet.

4. start/stop_animation

void start_animation (string animation, key avatar);
void stop_animation (string animation, key avatar);
Démarre ou arrête une animation.
Une animation BVH doit être présente dans le dossier Scripts de l'objet.

5. is_animation_active

bool is_animation_active (string animation, key avatar);
Teste si cette animation est actuellement active sur l'avatar donné.

6. override_animation

void override_animation (int typ, string animation);
remplace les animations standard (se tenir debout, marcher, courir, voler, sauter)
uniquement pour un objet porté.
typ : 0=debout, 1=marcher, 2=courir, 3=voler, 4=sauter
utilisez un string animation vide pour annuler le remplacement.

// Exemple 1 : sphère de danse

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


// Exemple 2 : s'asseoir sur une chaise

  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();   // obtenir la clé de l'avatar qui clique
    key k2 = clicked_avatar();    // obtenir la clé de l'avatar qui est cliqué.

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

Event click_avatar() est généré lorsque l'utilisateur clique sur un avatar qui est assis sur un objet. Tous les scripts de cet objet reçoivent alors cet événement. Ceci peut être utilisé pour changer l'animation de l'avatar.

Les fonctions suivantes sont autorisées dans un événement click_avatar :

  key clicking_avatar();   // obtenir la clé de l'avatar qui clique
  key clicked_avatar();    // obtenir la clé de l'avatar qui est cliqué.

Il se peut que l'avatar ne soit déjà plus assis sur l'objet si cet évènement arrive avec du retard. Vous pouvez vérifier cela avec is_sitting().

8. get_sitter

  bool get_sitter (out sitter);

Renvoie, en plusieurs appels, tous les avatars assis sur l'objet.
Renvoie false s'il ne reste plus d'avatars.
A chaque évènement, on recommence au premier avatar.

Tous les avatars assis sur un objet peuvent être listés comme ceci :

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

La structure sitter est définie comme suit :

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

Ceci peut être utilisé par exemple pour des animations de couple :

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

    if (is_sitting (k))   // déjà assis
    {
      unsit (k);    // se lever
    }
    else
    {
      sitter s;
      if (get_sitter(out s) && s.mesh_nr == 1)
      {
        // quelqu'un est déjà assis sur le mesh 1
        if (sit (mesh_nr  => 2,   // s'asseoir sur 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,   // s'asseoir sur 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)
  {
  }

L'événement avatar_unsits signale qu'un avatar se lève.

Il est appelé lorsque a) un autre objet appelle sit() sur l'avatar, ou b) l'avatar se téléporte, ou c) l'avatar se déconnecte.
Il n'est pas appelé lorsque a) l'objet appelle unsit() sur l'avatar, ou b) l'objet assis est supprimé, ou c) le serveur de la planète s'arrête.

10. set_camera_stability

Après avoir assis un avatar, vous pouvez définir la stabilité de sa caméra.

void set_camera_stability (int stability, key avatar);
Les trois valeurs de stabilité suivantes sont possibles :
  0 = STABLE (caméra toujours horizontale) (c'est la valeur par défaut après avoir été assis)
  1 = DYNAMIQUE (rotations horizontales et verticales de la caméra autorisées)
  2 = UNRESTRICTED (la caméra suit l'avatar, toutes les rotations de la caméra sont autorisées, attention au mal de mer)

7. Véhicules

Pour conduire un véhicule, vous devez :
1) asseoir l'avatar sur le véhicule comme sur un canapé,
2) optionnellement régler la stabilité de la caméra (voir ci-dessus),
3) appeler control_vehicle() avec de nombreux paramètres.

Des passagers supplémentaires peuvent également s'asseoir sur le véhicule et régler la stabilité de leur caméra, mais un seul conducteur peut appeler control_vehicle() ; tous les conducteurs précédents deviennent des passagers.

1. control_vehicle

void control_vehicle (vehicle_parameters p, key avatar);


  struct vehicle_parameters
  {
    bool           active;     // true = se comporte comme un véhicule, false = mode véhicule désactivé

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

    // physique

    float          mass;                  // 1000 kg
    float          max_speed;             // en km/h  vitesse maximale


    vector         dimensions;            // les dimensions du véhicule (taille, longueur, hauteur) (0.6 à 32.767 mètres)

        La physique du véhicule est modélisée comme une bulle étirée avec ces dimensions.


    int            nb_wheels;             // 1 (monocycle), 2 (vélo), 3 (tricycle), ou 4 (voiture) (1 à 4)

        Sous le véhicule se trouvent les roues, modélisées comme des sphères compressibles, qui le poussent vers le haut.
        Le nombre de roues a une influence sur l'équilibre du véhicule.
        si le nombre de roues est de 1, c'est un véhicule qui reste toujours horizontal (monocycle)
        si le nombre de roues est de 2,
          il peut s'agir d'un vélo (roues l'une derrière l'autre).
             si left_to_right_wheel_distance = 0 et axis_to_axis_distance > 0,
          ou un segway (une roue à gauche et une à droite).
             si left_to_right_wheel_distance > 0 et axis_to_axis_distance = 0.
        si le nombre de roues est de 3, il y a une roue devant au milieu, et deux roues derrière.
        si le nombre de roues est de 4, il s'agit d'une voiture classique.
        Les roues sont centrées et disposées en fonction de left_to_right_wheel_distance et axis_to_axis_distance.


    float          tyre_radius;                  // 0.25  (0.001 à 32.767 mètres)

        Rayon d'une sphère de roue, en mètres.


    float          left_to_right_wheel_distance; // (0.0 à 32.767 mètres)

        Distance entre le centre de la sphère de la roue gauche et le centre de la sphère de la roue droite, en mètres (0.0 à 32.767)
        il faut indiquer 0.0 pour un vélo.


    float          axis_to_axis_distance;        // (0.0 à 32.767 mètres) (0.0 pour un segway)

        Distance entre le centre de la sphère de la roue avant et le centre de la sphère de la roue arrière, en mètres (0.0 à 32.767)
        vous devez spécifier 0.0 pour un segway.
        cela influence le rayon du cercle dans lequel le véhicule tourne (un camion long tourne sur un cercle plus grand qu'une voiture)


    bool           set_z_range;           // true = véhicule autorisé uniquement dans la plage d'altitude min_z bis max_z, false = aucune restriction.

        si vous voulez limiter les altitudes autorisées du véhicule, vous mettez set_z_range à true.

    float          min_z;                 // (-8000.0 à 8000.0 mètres) (0.0 pour un bateau)
    float          max_z;                 // (-8000.0 à 8000.0 mètres) (0.0 pour un bateau)

        Une fois que set_z_range est true, vous pouvez définir une plage, par exemple :
        fixer min_z et max_z à 0.0 pour un navire, afin qu'il soit toujours au niveau de la mer.
        fixer min_z à 0.0 et max_z à 8000.0 pour un aéroglisseur, afin qu'il ne puisse pas descendre sous le niveau de la mer.
        fixer min_z à -8000.0 et max_z à 0.0 pour un sous-marin afin qu'il ne puisse pas dépasser le niveau de la mer.

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

    // configuration du clavier

    int key_direction;  // tourne les roues à gauche/droite         par défaut: 1
    int key_engine;     // déplace le véhicule en avant/en arrière  par défaut: 2
    int key_climb;      // déplace le véhicule vers le haut/bas (hélicoptère)
    int key_yaw;        // tourne le véhicule (nez à gauche/droite)
    int key_pitch;      // tourne le véhicule (nez vers le haut/bas)
    int key_roll;       // tourne le véhicule (oreille gauche vers le haut/en bas)

    Les valeurs de touches suivantes sont autorisées :
        0 = fonction non attribuée
        1 = flèche gauche/droite
        2 = flèche haut/bas
        3 = page précédente/suivante
        4 = ctrl+flèche gauche/droite
        5 = ctrl+flèche haut/bas
        6 = ctrl+page précédente/suivante
    (vous pouvez indiquer une valeur négative pour inverser le sens des commandes)

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

    // contrôle du véhicule

    float          engine_force_on_road;    // force motrice vers l'avant lorsque les roues touchent le sol (0.0 à 1.0e8)

      Utilisé pour une voiture pour donner une force vers l'avant.
      régler sur la valeur 1.0


    float          engine_force_in_air;     // force motrice vers l'avant lorsque les roues ne touchent pas le sol (0.0 à 1.0e8)

      Utilisé pour un navire ou un avion pour donner une force vers l'avant.
      régler sur la valeur 1.0


    float          climbing_force;          // force vers le haut avec key_climb (0.0 à 1.0e8)

      Utilisé pour un hélicoptère/drone pour donner une force ascendante.
      régler sur la valeur 1.0


    float          yaw_force;               // force de rotation avec key_yaw      (0.0 à 1.0e8)
    float          pitch_force;             // force de rotation avec key_pitch    (0.0 à 1.0e8)
    float          roll_force;              // force de rotation avec key_roll     (0.0 à 1.0e8)

      Ces 3 sont utilisés pour les navires ou les avions pour tourner dans toutes les directions.
      régler sur la valeur 1.0


    bool           yaw_dependant_on_forward;    // true = la force yaw dépend du mouvement vers l'avant, false = ne dépend pas
    bool           pitch_dependant_on_forward;  // true = la force pitch dépend du mouvement vers l'avant, false = ne dépend pas
    bool           roll_dependant_on_forward;   // true = la force roll dépend du mouvement vers l'avant, false = ne dépend pas

      Défini à true pour un contrôle plus strict : vous ne pouvez tourner que si vous avancez également.


    float          direction_factor;      // la vitesse à laquelle les roues tournent en appuyant sur la touche (1.0) (0.0 à 1.0e8)
    float          direction_max_angle;   // angle maximal des roues (30.0 degrés) (0.0 à 90.0)

      Définit la vitesse à laquelle l'utilisateur fait tourner la roue en appuyant sur la touche directionnelle,
      et l'angle maximum que les roues peuvent avoir.


    float          yaw_factor;            // la vitesse de déplacement de la gouverne yaw (0.0 à 1.0e8)
    float          yaw_max_angle;         // angle yaw maximal (30.0 degrés) (0.0 à 90.0)
    float          pitch_factor;          // la vitesse de déplacement de la gouverne pitch (0.0 à 1.0e8)
    float          pitch_max_angle;       // angle pitch maximal (30.0 degrés) (0.0 à 90.0)
    float          roll_factor;           // la vitesse de déplacement de la gouverne roll (0.0 à 1.0e8)
    float          roll_max_angle;        // angle roll maximal (30.0 degrés) (0.0 à 90.0)

      Définit la vitesse à laquelle l'utilisateur fait tourner le gouvernail ou les volets en appuyant sur les touches,
      et l'angle maximum que le gouvernail/les volets peuvent avoir.


    float          sliding_coef;          // 0.0 à 1.0
    float          sliding_z;             // -0.05 (-32.767 bis 32.767)  z négatif où s'applique la force des roues, en mètres

      sliding_coef indique à quel point les pneus sont glissants (0.0 = asphalte à 1.0 = glace neige)
      sliding_z est une valeur en mètre qui indique à quelle hauteur la force des pneus est appliquée au sol,
        il peut avoir 0.0 pour une stabilité totale.
        Une valeur négative comme -0.05 peut faire que la voiture se retourne dans les virages.


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

      air_friction_coef indique la densité de l'air/eau pour un navire/avion.
        Une valeur de 0.0 donnera l'impression d'être dans l'espace (inertie totale).
        Pour une meilleure maitrise dans les tournants, vous voudrez une valeur de 0.5.

      air_friction_vertical_coef est généralement 0.0
        une valeur comme 1.0 bloque le mouvement vertical quand il n'est pas dans le sens de la marche du véhicule.
        Ceci peut être utilisé pour un véhicule qui vole strictement vers l'avant, comme un avion, un planeur, un deltaplane.


    float          spring_force;          // 1.3    force des amortisseurs
    float          spring_damping;        // 0.005  contre-force des amortisseurs

      Ces paramètres contrôlent les amortisseurs de roue : la force et la contre-force.


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

      Ces paramètres fonctionnent comme la quille d'un bateau, en ajoutant du poids sous la voiture pour en augmenter la stabilité.
      Le premier paramètre est utilisé lorsque les roues du véhicule touchent le sol, le second dans le cas contraire.


    float          stability_in_air;      // 0.01   (0.0 à 1.0)

      Ralentit la vitesse de rotation, pour un avion ou un bateau.
      La valeur 0.0 ne fait rien, donc le véhicule se balance d'avant en arrière comme un pendule.
      Une petite valeur comme 0.01 ralentira ce phénomène.


    float          yaw_z;                 // (0.0 à 1.0e8) décalage z au-dessus du centre de la masse où appliquer la force directionnelle yaw

      Lorsqu'un avion effectue un virage avec yaw, cela fait également basculer l'avion/navire dans la direction roll.


    float          forward_slowdown;      // (-1.0 à 1.0)

      ralentit l'avancement du véhicle (-1.0 = aucun ralentissement, 0.0 = ralentissement de 0.5%, 1.0 = ralentissement de 1%)


    float          turning_slowdown;      // (-1.0 à 1.0)

      ralentit la rotation du véhicle (-1.0 = aucun ralentissement, 0.0 = ralentissement de 0.5%, 1.0 = ralentissement de 1%)


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

    // options

    bool           can_fly;                    // true = annule la gravité en l'air ou sur la mer, pour les véhicules volants ou à voile

      true pour les véhicules qui peuvent rester en l'air sans tomber, comme les avions, les bateaux, les sous-marins.
      faux pour les voitures, les aéroglisseurs ou tout ce qui tombe sur le sol.


    bool           apply_cubic_shape_in_air;   // true = virage en douceur lorsque les roues ne touchent pas le sol

      true est recommandé pour les objets qui naviguent ou volent afin de faciliter le contrôle de la rotation au clavier.
        En l'air, la masse rectangulaire du véhicule est ignorée et remplacée par un cube.
      false pour les voitures

    bool           block_fly_backwards;

      quand true, on ne peut pas reculer quand les roues ne touchent pas le sol.


    bool           auto_unblock;               // true

      si true, lorsque le véhicule ne bouge pas et que les roues ne touchent pas le sol, il est supposé bloqué.
      Dans ce cas, un déblocage automatique remet les rotations à zéro.


    bool           block_wheels_in_the_air;    // arrêter les roues d'un avion lorsqu'il est en vol

      true = les roues s'arrêtent de tourner lorsqu'elles ne touchent plus le sol (pour un avion après le décollage)


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

    // Roues en mesh

    ROTATING_MESH  rotating_mesh[2 .. 32];

      le mesh 1 est le véhicule et ne peut être configuré
      les meshs 2 à 32 peuvent être configurés comme des roues, un gouvernail ou d'autres parties mobiles.
        pour chaque mesh, vous spécifiez un type et un rayon.
        les numéros de mesh doivent être contigus (ne pas laisser de trous).

            struct ROTATING_MESH
            {
              int   typ;        // type de roue
              float radius;     // en mètres
            }

          les types de roues suivants sont possibles :

            0 = pas utilisé

            1 = roue avant gauche
            2 = roue avant centrale
            3 = roue avant droite

            4 = roue arrière gauche
            5 = roue arrière centrale
            6 = roue arrière droite

            7 = segway gauche (ou tank)
            8 = segway droite (ou tank)

            9 = volant de voiture, fourche de vélo (contrôlé par la direction) (régler le rayon sur 1.0 !)

            10 = gouvernail de bateau (contrôlé par yaw) (régler le rayon sur 1.0 !)
            11 = gouvernail de hauteur (contrôlé par pitch) (régler le rayon sur 1.0 !)
            12 = gouvernail de roulis (contrôlé par roll) (régler le rayon sur 1.0 !)

      La valeur radius doit correspondre exactement au rayon des roues en mesh, en mètres,
      afin que les roues tournent à la bonne vitesse.

    vector   mesh_rotation[2 .. 32];

      pour chaque mesh 2 à 32, vous pouvez spécifier une post-rotation constante.
      le vecteur indique la rotation (x, y, z) en degrés.
      Ceci est utilisé par exemple pour incliner de 20° la partie avant d'un vélo.


    float    inclination_angle_direction;

      pour un monocycle ou un vélo, angle maximal d'inclinaison du véhicule dans les virages.


    float    inclination_angle_height;

      pour un monocycle ou un segway, angle maximal d'inclinaison du véhicule en marche avant ou arrière.


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

Pour des exemples de script, voir les véhicules de démonstration dans le shopping center.

2. event vehicle_changed

Vous pouvez gérer l'événement "vehicle_changed" pour réagir à certaines actions :

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

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

La structure "vehicle_change" est définie comme suit :

  struct vehicle_change
  {
    bool forward;      // true si l'utilisateur a appuyé sur la touche avant pour augmenter la vitesse du moteur
    bool backward;     // true si l'utilisateur a appuyé sur la touche arrière pour diminuer la vitesse du moteur
    bool left;         // true si l'utilisateur a tourné de 25° vers la gauche
    bool right;        // true si l'utilisateur a tourné de 25° vers la droite
  }

3. collision de vehicles

1. évènements collision_vehicle_avatar, collision_vehicle_vehicle, collision_vehicle_object

Les événements collision_vehicle_avatar, collision_vehicle_vehicle et collision_vehicle_object sont déclenchés lorsque votre véhicule entre en collision avec un avatar, un autre véhicule ou un objet en matière COLLISION.

Exemple:

// dans un véhicule
event collision_vehicle_avatar ()
{
  key k = collision_avatar();
  say ("collision_vehicle_avatar " + avatar_name(k) + " force = " + ftos(collision_force()));
}

// dans un véhicule
event collision_vehicle_vehicle ()
{
  key k  = collision_avatar();
  say ("collision_vehicle_vehicle " + avatar_name(k) + " force = " + ftos(collision_force()));
}

// dans un véhicule ou un object en matière 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();

Cette fonction peut être utilisée pour déterminer l'avatar en collision.

Pour event COLLISION, COLLISION_VEHICLE_AVATAR, COLLISION_VEHICLE_VEHICLE, COLLISION_VEHICLE_OBJECT.

3. collision_force

  float collision_force ();

Cette fonction peut être utilisée pour déterminer la force de collision.

Pour event COLLISION_VEHICLE_AVATAR, COLLISION_VEHICLE_VEHICLE, COLLISION_VEHICLE_OBJECT.

4. collision_object_id

  int collision_object_id ();

Cette fonction peut être utilisée pour déterminer l'ID de l'autre objet (véhicule ou objet).

Pour event COLLISION_VEHICLE_VEHICLE, COLLISION_VEHICLE_OBJECT.

8. Mesh Animés

Les meshs animés permettent d'exécuter une animation sur le mesh riggé d'un objet, et cela sans avatar.

Un mesh riggé peut être basé sur le squelette standard de l'avatar homme, femme ou bien avoir un squelette sur-mesure. Dans Editer Mesh, mode JOINTS, cochez à cet effet l'option appropriée.

Un objet porté sur l'avatar doit toujours avoir son propre squelette sur-mesure s'il veut exécuter par script ses propres animations indépendantes de celles de l'avatar.

1. object_start/stop_animation

  void object_start_animation (string animation);
  void object_stop_animation (string animation);
Démarre ou arrête une animation sur un objet.
Une animation BVH doit être présente dans le dossier scripts de l'objet.
Un maximum de 4 animations peuvent s'exécuter en même temps.

2. object_is_animation_active

  bool object_is_animation_active (string animation);
Teste si une animation s'exécute actuellement sur l'objet donné.

9. Menus

1. display_menu

void display_menu (key avatar, string menu [,string menu2[,string menu3[,string menu4, ..]]]);
Affiche un menu utilisateur.
Le menu peut avoir max 3 niveaux, max 20 lignes dans chaque niveau.
Si le menu est plus long que 1024 caractères, coupez le en plusieurs parties.
Quand un utilisateur sélectionne un menu, le script reçoit un évènement menu_selected.

2. event menu_selected

Exemple:

  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) + " a choisit le menu " + itos(menu_id));
  }

10. Inventaire

1. give_inventory

void give_inventory (string item_name);
donne l'item de nom donné à l'inventaire de l'utilisateur.
cette fonction n'est autorisée que dans les évènements TOUCH, MENU_SELECTED and COLLISION.

2. item_name

string item_name (string previous_name [, int step]);
liste tous les items du dossier script de l'objet.
renvoie le nom de l'item suivant, ou "" s'il n'y en a pas d'autre.
step peut être +1 pour naviguer vers l'avant, ou -1 pour naviguer vers l'arrière.

3. item_type

string item_type (string item_name);
renvoie le type d'un item
renvoie l'une des valeurs "TEX", "OBJ", "SCR", "BVH", "SHP", "WAV"
ou "" si item_name n'existe pas.

4. event items_changed

L'événement items_changed est déclenché lorsque l'utilisateur ajoute, modifie ou supprime un élément dans le dossier de script de l'objet.

  event items_changed()
  {
    // l'utilisateur a ajouté/modifié/supprimé un élément du dossier scripts
  }

Exemple:

  // donne tous les items de l'objet sauf ceux de 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 ("donne " + name);
      }
    }
  }

11. Habillage

Les commandes script suivantes permettent d'habiller et de déshabiller automatiquement un avatar. Elles glissent des objets, textures, shapes et animations dans l'inventaire de l'avatar et lui font porter, ou lui enlèvent.

Si un avatar vient d'arriver sur la planète et que ses vêtements ne sont pas encore chargés, alors un script lançant des commandes d'habillement sera mis en pause jusqu'à ce que tout ses vêtements soient chargés. Un script d'habillement peut donc s'arrêter parfois près d'une minute en cas de connexion lente avant de continuer son exécution. Ce mécanisme évite que des vêtements erronés soient revêtus sur l'avatar.

1. habillage d'objets

1. wear_object

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

Faire automatiquement porter un objet à un avatar.

L'objet à porter doit se trouver dans le dossier "Scripts" de l'objet qui fait tourner ce script.

L'objet est ajouté dans l'inventaire de l'avatar, dans le dossier "Objet" si permanent est vrai, sinon dans le dossier "Temporaire".

Un objet dans "Temporaire" est limité : lorsqu'il est enlevé, il est déplacé dans le dossier "Poubelle" et l'avatar ne peut pas modifier ou réutiliser l'objet après cela.

2. unwear_object

void unwear_object ();
Enlève l'objet porté qui fait tourner ce script.
Autorisé seulement si ce script tourne dans un objet porté.

3. unwear_object()

void unwear_object (key avatar, string item_name);
Enlève l'objet porté d'un avatar.
Les caractères '?' (remplace 1 car) et '*' (remplace plusieurs cars) sont autorisés dans item_name pour enlever plusieurs objets.

4. next_worn_object

string next_worn_object (key avatar, ref int snr);
Liste tous les objets portés
Renvoie le nom de l'objet porté suivant (max 32 caractères).
snr doit être initialisé à 0 et sera changé à chaque appel, il est remis à 0 s'il n'y a plus d'objets.

5. event detached

Si un objet porté automatiquement est enlevé, le script parent qui a émis wear_object() reçoit un événement avec la clé de l'avatar et le nom de l'objet enlevé :

  event detached (key avatar, string name)
  {
  }

2. habillage de textures

1. wear_texture

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

Faire automatiquement porter une texture à un avatar.

La texture doit se trouver dans le dossier "Scripts" de l'objet qui fait tourner ce script.

La texture est ajoutée dans l'inventaire de l'avatar, dans le dossier "Textures" si permanent est vrai, sinon dans le dossier "Temporaire".

Une texture dans "Temporaire" est limitée : lorsqu'elle est enlevée, elle est déplacée dans le dossier "Poubelle" et l'avatar ne peut pas réutiliser la texture après cela.

2. unwear_texture

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

Enlève la texture portée d'un avatar.

Les caractères '?' (remplace 1 car) et '*' (remplace plusieurs cars) sont autorisés dans item_name pour enlever plusieurs textures.

body_part : 0=tête, 1=poitrine, 2=jambes, 3=yeux, 4=ongles doigts, 5=ongles orteils, -1=tout

layer : 0=peau, 1=tatoo, 2=sous-vêtements, 3=vêtements, 4=sur-vêtements, -1=tout

3. habillage de shapes

1. wear_shape

void wear_shape (key avatar, string item_name, bool permanent = false);
Faire automatiquement porter une shape à un avatar.

La shape doit se trouver dans le dossier "Scripts" de l'objet qui fait tourner ce script.

La shape est ajoutée dans l'inventaire de l'avatar, dans le dossier "Formes" si permanent est vrai, sinon dans le dossier "Temporaire".

Une shape dans "Temporaire" est limitée : lorsqu'elle est enlevée, elle est déplacée dans le dossier "Poubelle" et l'avatar ne peut pas réutiliser la shape après cela.

2. unwear_shape

void unwear_shape (key avatar, string item_name);

Enlève la shape portée d'un avatar.

Les caractères '?' (remplace 1 car) et '*' (remplace plusieurs cars) sont autorisés dans item_name pour enlever plusieurs shapes.

4. habillage d'animations

1. wear_override_animation

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

Faire automatiquement porter une animation à un avatar.

L'animation doit se trouver dans le dossier "Scripts" de l'objet qui fait tourner ce script.

L'animation est ajoutée dans l'inventaire de l'avatar, dans le dossier "Animations" si permanent est vrai, sinon dans le dossier "Temporaire".

Une animation dans "Temporaire" est limitée : lorsqu'elle est enlevée, elle est déplacée dans le dossier "Poubelle" et l'avatar ne peut pas réutiliser l'animation après cela.

L'animation est également ajoutée dans Mes Animations.

  typ : 0=debout, 1=marcher, 2=courir, 3=voler, 4=sauter,
        100-103 = permanent, 1 à 4
        200-207 = individuel, 1 à 8
        300-307 = couple A, 1 à 8
        400-407 = couple B, 1 à 8
Un item_name vide efface l'animation.

12. Blocages

a) Blocages à partir d'objets dans le monde

Les blocages peuvent être appliqués aux avatars qui se trouvent dans un espace en forme de boîte autour d'un objet rezzé.

1. block_avatar_options

  struct space
  {
    vector min, max;
  }

  // Appliquer des blocages à un avatar.
  // L'avatar doit être dans le même domaine que l'objet.
  // Si le paramètre avatar est vide, la commande s'applique à tous les avatars
  //   qui sont à l'intérieur de l'espace.
  // options:
  //   CAPTIVE   :  0x0001 signifie que l'avatar ne peut pas quitter l'espace de la boîte.
  //   FLY       :  0x0002 signifie que l'avatar ne peut pas voler.
  //   INVENTORY :  0x0008 bloque l'inventaire.
  //   NAMES     :  0x0010 cache les noms d'avatars dans People et Conversation, et bloque les profils sauf le sien.
  //   TELEPORT  :  0x0020 signifie que l'avatar ne peut pas se téléporter.
  //   SENDCHAT  :  0x0040 bloque l'écriture sur le chat (sauf pour les event listen d'objets portés)
  //   READCHAT  :  0x0080 bloque la réception sur le chat (sauf depuis des commandes say/say_to d'objets portés)
  //   CAMERA    :  0x0200 signifie que l'avatar ne peut pas déplacer la caméra à l'extérieur de la boîte.
  //   TOUCH     :  0x0400 signifie que l'avatar ne peut pas toucher les objets en dehors de l'espace de la boîte.
  //   RADAR     :  0x0800 signifie que l'avatar ne peut pas voir les gens dans Personnes.
  //   JUMP      :  0x1000 signifie que l'avatar ne peut pas sauter.
  //   plusieurs options peuvent être additionnées.
  //   0 signifie aucun blocage.
  //   toute recompilation de script ou erreur de script dans l'objet supprime également tous les blocages.
  // l'espace peut être aussi grand que 512m x 512m (-256.0 to +256.0)
  void block_avatar_options (key avatar, int options, space space);

2. avatar_block_options

  // Récupérer les options de blocage d'avatar
  int avatar_block_options (key avatar);

3. next_avatar_blocked

  // Récupérer le prochain avatar ayant des blocages.
  // Effacer l'avatar au premier appel, la clé vide retournée signifie qu'aucun autre avatar n'est bloqué.
  key next_avatar_blocked (key avatar);

b) Blocages à partir d'objets portés

1. block_worn_object

void block_worn_object (blocking_parameters parameters);
Bloquer certaines fonctions de l'avatar.
Autorisé uniquement pour un objet porté.
struct blocking_parameters
{
  int   options;
  float walking_speed;    // en mètres. si moins de 3.2 m/s alors courir est bloqué aussi.
  float camera_distance;  // en mètres.
  float touch_distance;   // en mètres.
  float radar_distance;   // en mètres. 0.0 à 256.0
  float camera_angle;     // angle en degrés. 0.0 à 180.0
  uint  fog_setting;      // 0xKS_BBGGRR, exemple: 0xfe_000000
}                         // avec :
                          //   K = frontière entre non-brouillard et brouillard, de 0=progressive à F=dure,
                          //   S = distance du brouillard, de 0=lointain à F=proche,
                          //   BBGGRR = couleur rgb du brouillard.

// options:
// 0x0001 : bloque les menus 'unwear' et 'properties' de l'objet porté.
// 0x0002 : bloque voler
// 0x0004 : bloque les menus 'wear'/'unwear' de skins et autres textures de vêtements
// 0x0008 : bloque l'inventaire
// 0x0010 : cache les noms d'avatars dans People et Conversation, et bloque les profils sauf le sien
// 0x0020 : bloque la téléportation
// 0x0040 : bloque l'écriture sur le chat (sauf pour les event listen d'objets portés)
// 0x0080 : bloque la réception sur le chat (sauf depuis des commandes say/say_to d'objets portés)
// 0x0100 : limite la vitesse de la marche, voir .walking_speed
// 0x0200 : limite la distance de la caméra, voir .camera_distance
// 0x0400 : limite la distance de toucher par clic gauche, voir .touch_distance
// 0x0800 : limite la distance à laquelle les avatars sont vus dans Personnes, voir .radar_distance
// 0x1000 : bloque sauter
// 0x2000 : fixe la caméra sur l'os mVisage de l'avatar
// 0x4000 : limite l'angle de vue horizontal et vertical de la caméra, par exemple .camera_angle = 45;
// 0x8000 : active le brouillard, voir .fog_setting
// 0x10000 : rend l'avatar invisible pour moi-même (mon ombre reste visible)

Un avatar qui est bloqué peut cliquer sur le bouton CheatOut dans le profil pour effacer tous les blocages, mais il est ensuite ajouté dans la liste des Tricheurs dans les informations du domaine.

13. Fichiers Texte

Des fichiers texte peuvent être ajoutés dans les scripts et lus ligne par ligne.

1. count_lines

  // compte le nombre de lignes d'un fichier texte
  int count_lines (string item);

2. read_line

  // retourne la ligne n d'un fichier texte
  string read_line (string item, int line);

Exemple:

  // lire toutes les lignes d'un fichier texte
  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. Stockage Permanent

a) stockage pour des objets dans le monde

1. store

void store (string index, string value);    //  insérer ou mettre à jour la valeur associée à index

store() est utilisé pour sauvegarder une chaîne de caractères sur un serveur de stockage permanent lié à l'identifiant de l'objet.

Vous pouvez stocker un maximum de 10.000 chaînes de caractères, après quoi vous obtenez une erreur de script.

Les chaînes ne sont pas effacées au redémarrage du script, la seule façon de le faire est de supprimer l'objet et d'en créer un nouveau.

Vous pouvez supprimer une valeur de chaîne individuelle en stockant une chaîne vide.

La chaine index ne doit pas dépasser 50 caractères et ne doit pas contenir des caractères nuls.

2. fetch

string fetch (string index);                // récupérer
fetch() est utilisé pour récupérer une valeur précédemment sauvegardée avec store().
S'il n'y en avait pas, une chaîne vide est retournée.

3. navigate

string navigate (string index, int direction);
renvoie un index immédiatement plus petit ou plus grand que l'index fourni.
direction peut être -1 (plus petit) ou +1 (plus grand).
Renvoie une chaîne vide s'il n'y a aucun autre index.

Exemple:

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

b) stockage pour des objets portés

Des données permanentes peuvent être stockées dans l'inventaire de l'utilisateur, même si l'utilisateur se reconnecte ou si l'objet est enlevé et porté à nouveau.

1. save_data_to_inventory

void save_data_to_inventory (string index, string value);

stocke le string 'value' dans l'inventaire sur le PC client

. pour objet porté uniquement

. index doit avoir un maximum de 50 caractères et ne doit pas contenir des caractères nuls.

. une chaîne vide comme valeur supprime l'enregistrement.

. vous pouvez stocker un maximum de 10_000 enregistrements, les autres enregistrements sont ignorés silencieusement.

2. load_data_from_inventory

void load_data_from_inventory (string index, int direction = 0);
recharge un string précédemment stocké dans l'inventaire client
. pour des objets portés seulement
. 'direction' peut avoir les valeurs suivantes :
    -1 = obtenir un index plus petit que l'index fourni,
     0 = obtenir l'index fourni,
    +1 = obtenir un index plus grand que l'index fourni.
. renvoie une chaîne vide index et valeur si aucun autre index n'existe.
. 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. Particules

La commande generate_particles() crée un pulvérisateur de particules au centre d'un mesh.

1. generate_particles

  void generate_particles (particule_parameters p);

Exemple:

  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 secondes
    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};  // blanc, 0% transparent
    p.life[0].end_color   = {0xFFFFFFFF, 0xFFFFFFFF};

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

    p.life[0].orientation = 0;     // toujours orienté verticalement

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

//    p.texture = "brillant";   // nécessite une texture appelée "brillant" dans le dossier "scripts"

    generate_particles (p);
  }

La structure particule_parameters contient les zones suivantes :

struct particule_parameters
{
  int         mesh_nr;              // nr mesh où l'émetteur est créé

  // rafales
  int         nb_particles;         // nombre de particules émises par rafale
  int         pause;                // temps de pause entre rafales, en millisecondes
  int         nb_bursts;            // nombre de rafales, 0 signifie infini

  // naissance de particules dans une boîte
  vector[2]   box;                  // les particules sont générées à une position aléatoire à l'intérieur de la boîte

  // naissance de particules dans le pulvérisateur
  float[2]    direction_angle;      // pulvériser vers l'extérieur dans la plage d'angle horizontal
  float[2]    height_angle;         // pulvériser vers l'extérieur dans la plage d'angle vertical
  float       radius;               // distance de pulvérisation à partir du centre où les particules sont créées

  // Particule 3 durées de vie
  lifetime    life[3];              // 3 vies successives, voir ci-dessous.

  bool        dont_follow_emitter;  // true = si le mesh émetteur bouge ou tourne, les particules ne suivent pas.

  bool        ambiant;              // true = les particules reçoivent la lumière ambiante
  bool        sunlight;             // true = les particules reçoivent la lumière du soleil

  float       smooth;               // 0.0 .. 0.99  0.0 = rugueux, 0.99 = lisse
  float       metal;                // 0.0 .. 1.0   0.0 = autre que métal, 1.0 = métal
  float       translucid;           // 0.0 .. 1.0   0.0 = non-translucide, 1.0 = translucide
  bool        front;                // false = priorité de transparence -3, true = priorité de transparence +3.

  int         emissive_color;       // couleur luminescente

  string(32)  texture;              // nom de la texture de la particule, doit être ajouté dans le dossier scripts

  int         mode_uv;              // idem que mesh u, v
  float       u;
  float       v;

  bool        sort;                 // trier les particules (meilleur résultat pour textures transparentes mais utilise du CPU !)

  // mode chaine
  int       leash_id;       // id de l'objet à relier par la chaîne (non-zero active le mode chaine)
  int       leash_mesh_nr;  // numéro de mesh de l'objet distant où attacher la chaîne (1..32)
  float     leash_length;   // longueur de chaine en mètres (maximum 256)
  float     leash_stretch;  // facteur d'allongement (0..1) de sorte que les particules se chevauchent un peu à la longueur maximale (0.0 = aucun, 0.2 = anneaux de chaîne)
  bool      leash_physics;  // true = tire l'avatar vers l'arrière s'il est plus éloigné que la longueur de chaine
}

Les particules peuvent avoir un maximum de 3 vies au cours desquelles elles ont un comportement différent.

La structure lifetime comprend les zones ci-après.

Lorsqu'il y a deux zones, vous pouvez spécifier une limite inférieure et une limite supérieure, et une valeur aléatoire entre les deux limites est sélectionnée pour chaque particule.

struct lifetime
{
  int[2]        durations;           // durée de vie des particules, en msecs (max 1 jour)
  float[2]      begin_speed;         // Début de la plage de vitesse du pulvérisateur
  float[2]      end_speed;           // Fin de la plage de vitesse du pulvérisateur
  vector[2]     begin_velocity;      // Vitesse de début supplémentaire en m/s
  vector[2]     end_velocity;        // Vitesse de fin supplémentaire en m/s
  vector[2]     begin_acceleration;  // accélération de début supplémentaire en m/s2
  vector[2]     end_acceleration;    // accélération de fin supplémentaire en m/s2
  vector[2]     begin_size;          // début taille des particules (largeur, hauteur) en mètres
  vector[2]     end_size;            // fin taille des particules (largeur, hauteur) en mètres
  int[2]        begin_color;         // début couleur et transparence
  int[2]        end_color;           // fin couleur et transparence
  int           orientation;         // 0=vertical à l'écran, 1=vers la vitesse, 2=horizontal dans le monde, 3=chaîné.
}

Pour arrêter les particules, mettez p.mesh_nr ou p.nb_particles à zéro.

Exemple:

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

16. Sons

1. play_sound

  void play_sound (string item_name,
                   float  volume = 1.0,     // 0.0 à 1.0
                   float  radius = 20.0);   // 0 à 1024 m
Jouer un son.
Un maximum de 16 sons par minute peuvent être démarrés, au-delà ils sont ignorés.
Les fichiers son doivent être des .wav 44.1KHz PCM 16-bit maximum 90 secondes ou bien ils sont coupés. Vous pouvez convertir des fichiers son dans ce format à l'aide du logiciel gratuit https://audacity.fr/

Exemples:

  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 à 1.0
                            float  radius = 20.0);   // 0 à 1024 m
Joue un son en boucle.
Les fichiers son doivent être des .wav 44.1KHz PCM 16-bit maximum 90 secondes ou bien ils sont coupés.
Seul un son d'ambiance par objet est autorisé, les sons d'ambiance précédents sont annulés.

Exemples:

  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 ();
Arrête le son d'ambiance.

17. Media

1. media_play

  void media_play (key avatar, string url);

Joue un fichier ou stream multimédia (mp3, mp4, wav, ..)

Un url vide ou illegal va stopper le média.

Un url identique à celui joué actuellement n'a pas d'effet.

Pour un média vidéo, vous devez construire un écran mesh rectangulaire de ratio longueur=4 hauteur=3, désactiver Ambiant et Soleil, régler Couleur et Lueur à 255,255,255, et ensuite aller dans le menu "Mesh / Divers" et cocher l'option 'Video'.

Exemple:

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

2. media_pause

  void media_pause (key avatar, bool on);
Mettre un média en pause ou le redémarrer.

Exemples:

  media_pause (k, on => true);   // pause
  media_pause (k, on => false);  // redémarre

3. media_seek

  void media_seek (key avatar, int mode, float seconds);
Changer la position d'un média
  . mode 0 : position absolue en secondes
  . mode 1 : position relative en secondes (secondes peuvent être négatives)
  . mode 2 : position relative depuis la fin (secondes doivent être negatives)

Exemple:

  media_seek (k, mode => 1, seconds => -5.0);  // revenir 5 secs en arrière

4. media_volume

  void media_volume (key avatar, float volume);   // 0.0 to 1.0
Changer le volume du média
Ceci change également la valeur dans Outils/Sons

Exemple:

   media_volume (k, volume => 0.5);  // volume 50%

18. Appels Tcpip

1. tcp_open/close/send

Vous pouvez échanger des chaînes UTF-16 de 1024 caractères maximum avec un serveur tcp/ip externe en utilisant les commandes suivantes :

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

2. tcp_received/disconnected

et ces évènements :

  event tcp_received (string message)
  event tcp_disconnected ()

Les chaines sont envoyées avec une entête longueur de 2 bytes.

Vous ne pouvez pas envoyer plus d'une chaine par seconde.

En cas d'event server_restart(), la connexion est perdue et doit être réouverte.

3. server_name/udp_port/tcp_port

Vous pouvez obtenir le nom d'hôte et port du serveur planète en utilisant :

  string server_name ();  // max 256 caractères

  int server_udp_port();  // pour planet, habituellement 13000
  int server_tcp_port();  // pour le web, habituellement 80

19. Glisser-déposer

1. event image_dropped

Avec la souris, vous pouvez glisser et déposer des images de votre disque dur pour les afficher sur un objet dans le monde.

Exemple:

  event image_dropped ()
  {
    set_mesh_texture (1, "");  // appliquer l'image sur le mesh 1
  }

L'affichage doit avoir un format 4/3 (largeur = 4, hauteur = 3).

Les images seront automatiquement redimensionnées et centrées avec un bord transparent.

La commande set_mesh_texture(mesh_nr, "") ; aura un effet uniquement :

. pour les objets dans le monde (pas les attachments),
. pour les commandes, (pas dans un job),
. à l'intérieur d'un évènement image_dropped seulement.

Dans tous les autres cas, il restaure la texture originale.

A l'intérieur d'un événement image_dropped, les appels suivants sont autorisés :

  key            touched_avatar ();          // qui a glissé l'image
  int            touched_mesh_nr ();         // sur quel numéro de mesh
  world_position touched_world_position ();  // à quelle position dans le monde
  vector         touched_mesh_position ();   // à quelle position sur le mesh

20. Argent

1. show_money_dialog

void show_money_dialog (key avatar);
Affiche une fenêtre de dialogue "Mon Argent" pour l'avatar.

2. money_balance

string money_balance (key avatar);
Retourne le solde de l'argent de l'utilisateur, exemple "+124.50"

3. give_activity_money

int give_activity_money (key beneficiary);
Transfère le bonus quotidien d'activité sur le compte de l'utilisateur.
Retourne le montant transféré, qui peut être égal à 0.

4. ask_money_payment

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

Demande au client de payer le montant au propriétaire du script.

Le montant doit être compris entre 1 et 999_999.

Une taxe de 5 % est déduite du montant lorsque l'argent provient de quelqu'un d'autre.

La fonction génère l'événement money_received lorsque le client accepte.

5. event money_received

event money_received (key customer, int amount, string comment);
Cet événement est généré lorsque le client vient de payer le montant au propriétaire du script.

Exemple:

  // script de paiement

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

Transfère l'argent du propriétaire du script au bénéficiaire.

Dans l'onglet Accès de l'objet, vous devez cocher l'option "distribuer de l'argent".

Un bénéficiaire vide donne de l'argent à la banque centrale de la planète.

Le montant doit être compris entre 1 et 999_999.

La fonction renvoie un code d'erreur :

     0 le transfert a réussi
    -3 montant illégal, doit être compris entre 1 et 999_999
    -4 fonds insuffisants
    -5 non autorisé dans un objet porté.
    -6 l'objet n'est pas autorisé à distribuer de l'argent, voir l'onglet Accès

7. area_bonus

int area_bonus (int amount);

Cette fonction augmente le coût maximal d'une aréa.

Pour l'utiliser, rezzez l'objet sur l'aréa que vous voulez augmenter.

Vous obtenez 1 coût de terrain supplémentaire par 10ρ d'argent, jusqu'à une limite dure de 9000.

La fonction ne fonctionne que dans le cadre d'un événement touch ou menu_selected, et seulement si le propriétaire du script est la personne qui clique.

Le compte de l'avatar qui clique est débité.

La fonction renvoie un code d'erreur :

     0 ok
    -3 montant illégal, doit être compris entre 20 et 999_999
    -4 fonds insuffisants
    -5 non autorisé dans un objet porté.
    -6 aréa illégale (non-possédée)
    -7 coût (l'area est déjà au coût maximum de 9000).
    -8 uniquement en cas d'évènement touch ou menu_selected.
    -9 L'avatar qui clique doit être le propriétaire de l'objet.

21. Communication inter-planètes

1. get_planet_id

pid get_planet_id ();

Obtient l'identifiant unique de cette planète (pid).
Un pid est défini comme suit :

  typedef int[2] pid;

Exemple :

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

Teste si deux identifiants de planète sont identiques.

3. send_message_to_planet

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

Envoie un message à un objet sur une planète spécifiée, où il déclenchera un événement planet_message_received.

4. broadcast_message_to_planet

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

Envoie un message à tous les scripts du nom donné sur une planète spécifiée, où il déclenchera un événement planet_message_received.

5. event planet_message_received

Cet événement est déclenché lorsqu'un message a été reçu, en provenance d'un objet situé sur une autre (ou même) planète.
A l'intérieur de l'événement, la fonction sender_object_owner() peut être utilisée pour obtenir le propriétaire de l'objet scripté qui a envoyé le message.

Exemple:

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

permet d'obtenir le propriétaire de l'objet scripté qui a envoyé le message.
Ne peut être utilisé que dans l'événement 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);

Téléporte l'avatar k sur une planète et éventuellement sur un domaine.
Le domaine doit être visible dans la recherche publique.
L'objet script doit avoir au moins le rang security officer sur le domaine de départ.

22. Mode Combat

Pour configurer le mode combat, choisissez le menu Outils / Réglages : activez le mode combat via la touche C ou F6, sélectez si on court tout le temps, et choisissez la vitesse de la souris.

Pour entrer en mode combat, appuyez sur F6 ou C.

1. set_arm_range

void set_arm_range (float distance);

Définit la portée de l'arme sélectionnée, en mètres.

2. set_arm_power

void set_arm_power (int power);

définit la puissance (la quantité de dégâts) que produira l'arme sélectionnée.

3. shot

event shot (key victim, int power)

cet événement est appelé lorsque nous avons atteint une victime.

4. damage

event damage (key shooter, int power)

cet événement est appelé lorsque nous avons été blessé.

Exemple:

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




Appendice A : Bibliothèques

1. Conversions

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

2. Manipulation de chaines de caractère

  int    len   (string value);                                     // exemple: len("abc") == 3
  string chr   (int c1, int c2, int c3, ..., int c16);             // exemple: chr(65,66,67) == "ABC"
  int    asc   (string value, int index=1);                        // exemple: asc("AB") == 65    asc("ABC",2) == 66
  string left  (string value, int count);                          // exemple: left("ABCD",2) == "AB"
  string right (string value, int count);                          // exemple: right("ABCD",2) == "CD"
  string mid   (string value, int index, int count=);         // exemple: mid("ABCDEF",2,3) == "BCD"   mid("ABCDEF",5) == "EF"
  int    pos   (string phrase, string word);                       // exemple: pos("ABCDEF","CD") == 3    pos("ABCDEF","X") == 0
  string dup   (string value, int count);                          // exemple: dup("-*",3) == "-*-*-*"
  string upper (string value);                                     // enlève les accents et converti en majuscules
  string lower (string value);                                     // enlève les accents et converti en minuscules
  string trim  (string value);                                     // enlève les espaces avant et après
  string ltrim (string value);                                     // enlève les espaces avant
  string rtrim (string value);                                     // enlève les espaces après
  string resolve_icons (string value);                             // converti les séquences comme :) en codes icônes

3. Calendrier

  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 ();                                                // heure gmt
  int       weekday            (date_time date);                   // (1=lundi, 7=dimanche)
  date_time add_seconds        (date_time date, int seconds);
  int       area_time_offset   ();                                 // secondes à ajouter au temps pour obtenir l'heure locale de l'area
  int       nb_days_since_1901 (date_time date);                   // nb jours entre le 1/1/1901 et la 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  ();                                              // valeur comprise entre 0 inclus et 1 exclu
  float     fabs  (float value);

  float     sin   (float angle);                                   // angle en degrés
  float     cos   (float angle);                                   // angle en degrés
  float     atan2 (float y, float x);                              // angle en degrés

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

Appendice B : Quelques exemples

Un script de porte simple

  // script de porte

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

Un script de porte avec fermeture après 10 secondes

  // Un script de porte avec fermeture après 10 secondes

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

Interrupteur de lampe


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

Appendice C : Coût des Scripts

Le coût du script dépend du nombre d'instructions, de la taille des variables globales et du stockage pour les chaînes de caractères.

Les variables locales ne sont pas incluses.

Appendice D : En cas de bug

En cas de bug, envoyez un descriptif à : marcsamu@hotmail.com