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.
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 : * / % + - << >> == != < > <= >= && || & | ^
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.22.
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.
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 !"); }
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);
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.
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.
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 }
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.
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.
void say (string value);Affichage de texte sur le chat dans un rayon de 20m
// Exemple: event start () { say ("Bonjour, je démarre !"); }
void say_to (key avatar, string value);
Idem à say mais destiné à un seul avatar.
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); }
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") + " :)"); }
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"); }
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 }
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}); }
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}); }
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); }
void set_mesh_transparency (int mesh_nr, float transparency);
Modifie la transparence du mesh.
La valeur de transparence doit être comprise entre 0.0 et 1.0
Exemple:
event start () { set_mesh_transparency (mesh_nr => 1, transparency => 0.3); }
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); }
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); }
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); }
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.
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"); }
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é.
bool is_worn ();retourne vrai si l'objet courant est porté, faux s'il est créé sur terre.
string object_name ([int object_id]);retourne le nom de l'objet (max 32 caractères)
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.
key object_owner ([int object_id]);retourne le propriétaire de l'objet.
string object_owner_date ([int object_id]);retourne une date en format "YYYYMMDD"
int object_nb_meshes();retourne le nombre de meshes de l'objet.
int domain_id();return l'id du domaine où se trouve l'objet.
string domain_name();retourne le nom de domaine où se trouve l'objet.
bool domain_adult();
teste si l'objet est situé sur un domaine adulte.
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 &
world_position object_world_position (int object_id);retourne la position de l'objet sur la planète.
vector object_rotation (int object_id);retourne la rotation de l'objet.
int set_object_world_position (int object_id, world_position position);déplace l'objet
-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.
void set_object_rotation (int object_id, vector rotation);fait tourner l'objet
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)
int clone_object (bool active);
dépose une copie de l'objet qui exécute le script, exactement au même endroit que l'objet initial.
avec active = true pour une copie exacte, ou false pour une copie avec tous les meshs initialement désactivés pour qu'il reste invisible.
renvoie une valeur positive (id de l'objet créé), ou une erreur négative (-1 = pas le droit de rezzer, -2 = area pleine, -3 = pas autorisé pour un objet porté)
int parent_object_id ();retourne un identifiant positif de l'objet parent qui a rezzé, cloné ou porté cet objet,
void delete_object();supprime l'objet exécutant ce script.
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 (); }
int next_object_of_area (int area_x, int area_y, ref int snr);liste tous les objets de l'area.
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)); } }
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.
Toutes les commandes d'un batch doivent être entourées par :
void begin_move (); void end_move ();
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.
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
Pour arrêter un batch en cours d'exécution, appelez :
void stop_move ();Quelques règles :
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.
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.
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 (); }
bool avatar_online (key k);Renvoie vrai si un avatar est en ligne, faux sinon.
string avatar_name (key k);Retourne le nom complet d'un avatar (71 caractères maximum).
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.
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.
void set_avatar_rank (key k, int rank);Change le rang d'un avatar, si vous en avez le droit.
int avatar_gender (key k);Retourne un genre d'avatar : 0=homme, 1=femme.
int avatar_adult (key k);Retourne l'état adulte de l'avatar : 0=mineur, 1=adulte, -1=key invalide.
int avatar_language (key k);Retourne la langue de l'avatar (0 = français, 1 = anglais, 2 = allemand, -1 = key incorrecte)
int avatar_experience (key k);Retourne l'experience de l'avatar, ou -1 si key est inconnue.
int avatar_timezone (key k);Retourne la zone de temps de l'avatar, en heures, de -12 à +12.
string avatar_title (key k);Retourne le titre de l'avatar, maximum 24 caractères.
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).
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)); }
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); }
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.
key next_avatar (ref int snr);liste un à un tous les avatars dans un rayon de 256 mètres.
Exemple:
event touch() { int snr = 0; for (;;) { key avatar = next_avatar (ref snr); if (snr == 0) break; say (avatar_name (avatar)); } }
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.
int avatars_online_count();
retourne le nombre d'avatars présents sur cette planète (0 à 1024).
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().
bool is_sitting (key avatar);Teste si un avatar est assis sur cet objet.
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.
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.
void start_animation (string animation, key avatar); void stop_animation (string animation, key avatar);Démarre ou arrête une animation.
bool is_animation_active (string animation, key avatar);Teste si cette animation est actuellement active sur l'avatar donné.
void override_animation (int typ, string animation);remplace les animations standard (se tenir debout, marcher, courir, voler, sauter)
// 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); } }
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().
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); } } } }
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.
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)
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.
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.
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 }
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)); }
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.
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.
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.
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.
void object_start_animation (string animation); void object_stop_animation (string animation);Démarre ou arrête une animation sur un objet.
bool object_is_animation_active (string animation);Teste si une animation s'exécute actuellement sur l'objet donné.
void display_menu (key avatar, string menu [,string menu2[,string menu3[,string menu4, ..]]]);Affiche un menu utilisateur.
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)); }
void give_inventory (string item_name);donne l'item de nom donné à l'inventaire de l'utilisateur.
string item_name (string previous_name [, int step]);liste tous les items du dossier script de l'objet.
string item_type (string item_name);renvoie le type d'un item
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); } } }
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.
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.
void unwear_object ();Enlève l'objet porté qui fait tourner ce script.
void unwear_object (key avatar, string item_name);Enlève l'objet porté d'un avatar.
string next_worn_object (key avatar, ref int snr);Liste tous les objets portés
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) { }
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.
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
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.
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.
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 à 8Un item_name vide efface l'animation.
Les blocages peuvent être appliqués aux avatars qui se trouvent dans un espace en forme de boîte autour d'un objet rezzé.
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);
// Récupérer les options de blocage d'avatar int avatar_block_options (key avatar);
// 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);
void block_worn_object (blocking_parameters parameters);Bloquer certaines fonctions de l'avatar.
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.
Des fichiers texte peuvent être ajoutés dans les scripts et lus ligne par ligne.
// compte le nombre de lignes d'un fichier texte int count_lines (string item);
// 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); } }
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.
string fetch (string index); // récupérerfetch() est utilisé pour récupérer une valeur précédemment sauvegardée avec store().
string navigate (string index, int direction);renvoie un index immédiatement plus petit ou plus grand que l'index fourni.
Exemple:
event start() { store (index => "hi-score", value => " 10 "); say (fetch(index => "hi-score")); // va dire 10 }
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.
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.
void load_data_from_inventory (string index, int direction = 0);recharge un string précédemment stocké dans l'inventaire client
-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.
event inventory_data_arrived (string index, string value) { say ("index = '" + index + "'"); say ("value = '" + value + "'"); }
La commande generate_particles() crée un pulvérisateur de particules au centre d'un mesh.
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; // reflet 0.0 .. 0.99 0.0 = rugueux, 0.99 = lisse float metal; // metal 0.0 .. 1.0 0.0 = autre que métal, 1.0 = métal float translucid; // translucide 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); }
Voici un script neige avec 2 vies : d'abord le flocon descend, puis il reste un moment sur le sol.
event start () { particule_parameters p; clear p; p.mesh_nr = 1; p.nb_particles = 1; p.pause = 200; p.emissive_color = 0xFFFFFF; // toujours lumineux p.smooth = 0.93; // reflet 0.0 .. 0.99 0.0 = rugueux, 0.99 = lisse p.box = {{-32.767, -32.767, 0.0}, {32.767, 32.767, 0.0}}; // boite qui couvre toute l'area p.direction_angle = {-180.0, +180.0}; p.height_angle = {0.0, 0.0}; p.life[0].durations = {60000, 60000}; p.life[0].begin_speed = {0.0, 0.0}; p.life[0].end_speed = {0.0, 0.1}; // petite vitesse de direction p.life[0].end_velocity = {{0.0, 0.0, -0.2}, {0.0, 0.0, -0.2}}; // vitesse de chute p.life[0].begin_size = {{0.10, 0.10}, {0.10, 0.10}}; p.life[0].end_size = {{0.10, 0.10}, {0.10, 0.10}}; p.life[0].begin_color = {0xFFFFFFFF, 0xFFFFFFFF}; p.life[0].end_color = {0xFFFFFFFF, 0xFFFFFFFF}; p.life[0].orientation = 0; // orientation verticale p.life[1].durations = {60000, 60000}; p.life[1].begin_size = {{0.10, 0.10}, {0.10, 0.10}}; p.life[1].end_size = {{0.10, 0.10}, {0.10, 0.10}}; p.life[1].begin_color = {0xFFFFFFFF, 0xFFFFFFFF}; p.life[1].end_color = {0xFFFFFFFF, 0xFFFFFFFF}; p.life[1].orientation = 2; // orientation horizontale // p.texture = "flocon"; // ajouter une texture "flocon" dans le dossier script generate_particles (p); }
void play_sound (string item_name, float volume = 1.0, // 0.0 à 1.0 float radius = 20.0); // 0 à 1024 mJouer un son.
Exemples:
play_sound ("ding"); play_sound ("ding", volume => 1.0); play_sound ("ding", volume => 1.0, radius => 20.0);
void start_ambiant_sound (string item_name, float volume = 1.0, // 0.0 à 1.0 float radius = 20.0); // 0 à 1024 mJoue un son en boucle.
Exemples:
start_ambiant_sound ("sea"); start_ambiant_sound ("sea", volume => 1.0); start_ambiant_sound ("sea", volume => 1.0, radius => 20.0);
void stop_ambiant_sound ();Arrête le son d'ambiance.
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"); }
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
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
void media_volume (key avatar, float volume); // 0.0 to 1.0Changer le volume du média
Exemple:
media_volume (k, volume => 0.5); // volume 50%
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);
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.
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
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
void show_money_dialog (key avatar);Affiche une fenêtre de dialogue "Mon Argent" pour l'avatar.
string money_balance (key avatar);Retourne le solde de l'argent de l'utilisateur, exemple "+124.50"
int give_activity_money (key beneficiary);Transfère le bonus quotidien d'activité sur le compte de l'utilisateur.
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.
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); }
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
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.
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]) + "}"); }
bool same_pid (pid a, pid b);
Teste si deux identifiants de planète sont identiques.
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.
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.
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 + "'"); }
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.
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.
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.
void set_arm_range (float distance);
Définit la portée de l'arme sélectionnée, en mètres.
void set_arm_power (int power);
définit la puissance (la quantité de dégâts) que produira l'arme sélectionnée.
event shot (key victim, int power)
cet événement est appelé lorsque nous avons atteint une victime.
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)); }
struct ray_casting { // input int mask; // 1 = détecter les objets, 2 = détecter les avatars, 3 = les deux. world_position origin; vector direction; // vecteur unitaire de direction, par exemple {0.0, 0.0, -1.0} pointant vers le bas float max_distance; // en mètres (0.001 à 100.0) float radius; // rayon du rayon, en mètres, (0.0 à 10.0) // output int typ; // 0 = pas de collision, 1 = collision avec l'objet, 2 = collision avec l'avatar. world_position position; // position de collision float distance; // en mètres (plus petit que max_distance en cas de collision) vector normal; // vecteur normal de collision. ex : {0.0, 0.0, 1.0} pour une collision avec le sol. // output for typ 1 (object) : int object_id; // id de l'objet collisionné int mesh_nr; // nb de mesh collisionné (1 .. 32) vector mesh_position; // position locale du mesh heurté par le rayon // output for typ 2 (avatar) : key avatar; // clé de l'avatar entré en collision (si typ == 2) }
bool cast_ray (ref ray_casting r);
Lance un rayon invisible et détecte le premier object non-fantome ou avatar qu'il touche.
Il faut remplir les champs 'input' avant l'appel de la fonction, et lire les résultats dans les champs 'output'.
La fonction renvoie true si un objet ou avatar est touché, false sinon.
Exemple:
event touch() { ray_casting r; say ("============="); clear r; r.mask = 3; r.origin = object_world_position (object_id()); r.direction = {0.0, 0.0, -1.0}; // vers le bas r.max_distance = 100.0; // 100 mètres r.radius = 0.001; // 1 mm say ("origin = " + itos(r.origin.x>>8) + " " + itos(r.origin.y>>8) + " " + itos(r.origin.z>>8) + " mm"); if (cast_ray (ref r)) { say ("ray touched"); say ("typ = " + itos(r.typ)); say ("id = " + itos(r.object_id)); say ("pos = " + itos(r.position.x>>8) + " " + itos(r.position.y>>8) + " " + itos(r.position.z>>8) + " mm"); say ("mesh_nr = " + itos(r.mesh_nr)); say ("distance = " + ftos(r.distance) + " m"); say ("z = " + itos(r.position.z / 256) + " mm"); say ("mesh pt = " + ftos(r.mesh_position.x) + ", " + ftos(r.mesh_position.y) + ", " + ftos(r.mesh_position.z)); say ("normal = " + ftos(r.normal.x) + ", " + ftos(r.normal.y) + ", " + ftos(r.normal.z)); say ("avatar = " + avatar_name (r.avatar)); } else say ("no collision"); }
void earthquake (int intensity, // 0 à 100 float duration); // 0.0 à 30.0 sec
Déclenche un tremblement de terre d'une intensité et d'une durée données. Vous devez être land owner pour l'utiliser.
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
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
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
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);
Certaines commandes de script qui agissent sur les avatars nécessitent des autorisations, sinon elles n'ont aucun effet.
En particulier, les commandes suivantes requièrent une autorisation :
sit, start/stop_animation, wear/unwear_xxx, block_avatar_options, display_menu, media_xxx, show_money_dialog, set_avatar_world_position, teleport_to_planet.
L'autorisation est accordée dans 3 cas :
a) si l'avatar A déclenche un événement (par exemple A clique, event touch), alors le script peut agir sur A.
b) si l'objet appartient à l'avatar A, ou est porté par A, alors le script peut agir sur A.
c) si l'objet appartient à l'avatar A, ou est porté par A, et que A est au moins security officer sur le domaine, alors le script peut agir sur tous les avatars.
Si l'événement n'a pas été déclenché par A (par exemple un event start ou timer), ni que A possède l'objet, ni que le propriétaire de l'objet est au moins security officer, alors la commande n'a pas d'effet sur A.
// 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 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; }
// 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); }
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.
En cas de bug, envoyez un descriptif à : [email protected]