Ce site est consacré principalement au langage LSL de Second Life...
Dans SL on dispose d'une map qui nous permet de voir les constructions d'une sim ainsi que la localisation des avatars présents et leur nombre. Il est parfois intéressant de savoir nommément qui se trouve à tel emplacement. Je vous propose de réaliser un HUD...
Pour visualiser la carte de la sim avec les avatars présents mettez ce script dans une simple primitive :
//----------------------------------------------------------------------------------
// Avatars Show V 2.02
//----------------------------------------------------------------------------------
// Copyright (c) 2013 by Bestmomo Lagan
//----------------------------------------------------------------------------------
//----------------------------------------------
// PARAMETRES
//----------------------------------------------
integer FACE = 4;
string VERSION = "2.02";
list LANG_SHORT = ["fr","es","ja","de","en","pt","ko","zh","it","da","hu","nl","pl","ru","tr","uk","el","fi"];
list LANG = ["french","spanish","japanese","german","english","portuguese","korean","chinese","italiano",
"danish","hungarian","dutch","polish","russian","turkish","ukrainian","greek","finnish"];
//----------------------------------------------
// VARIABLES
//----------------------------------------------
string myURL;
list l_keys;
key k_script;
key k_ava_select;
string s_ava_select;
integer i_listen;
//----------------------------------------------
// PARAMETRES MODULE MENU
//----------------------------------------------
key KEY_MENU = "168d4f3c-e095-a9c8-058d-d6075e6e51b8";
integer MENU_INIT = 10; // Initialisation d'un menu
integer MENU_ANSWER = 11; // Réponse d'un menu
integer MENU_BUSY = 12; // Réponse busy
//----------------------------------------------
// FONCTIONS MODULE MENU
//----------------------------------------------
send_menu (key id_script, key id, string texte, integer type_menu, list boutons) {
llMessageLinked(LINK_THIS, MENU_INIT, (string)id_script + "@"
+ (string)id + "@"
+ texte + "@"
+ (string)type_menu + "@"
+ llDumpList2String(boutons, "|"), KEY_MENU);
}
//----------------------------------------------
// FIN
//----------------------------------------------
browse(integer face, string url) {
llClearLinkMedia(LINK_THIS, face);
llSetLinkMedia(LINK_THIS, face, [
PRIM_MEDIA_CONTROLS, PRIM_MEDIA_CONTROLS_MINI,
PRIM_MEDIA_AUTO_PLAY, TRUE,
PRIM_MEDIA_AUTO_LOOP, TRUE,
PRIM_MEDIA_AUTO_SCALE, TRUE,
PRIM_MEDIA_CURRENT_URL, url,
PRIM_MEDIA_HOME_URL, url,
PRIM_MEDIA_HEIGHT_PIXELS, 512,
PRIM_MEDIA_WIDTH_PIXELS, 512]);
}
string getPage(string noms, string pos, string url_map) {
return "
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
background: url(" + url_map + ") no-repeat;
background-size: 512px;
height: 512px;
width: 512px;
}
#container {
height: 502px;
width: 502px;
border: 5px ridge orange;
}
</style>
</head>
<body>
<div id='container'></div>
<script src='http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.4.3.min.js'></script>
<script>
var noms = [" + noms + "];
var pos = [" + pos + "];
var stage = new Kinetic.Stage({
container: 'container',
width: 502,
height: 502
});
var mapLayer = new Kinetic.Layer();
var topLayer = new Kinetic.Layer();
var tooltipLayer = new Kinetic.Layer();
function ajustePos(mp, height) {
if(mp > stage.getWidth() / 2) {
return mp - height - 10;
} else {
return mp + 10;
}
}
for(var i = 0; i < noms.length; ++i) {
var circle = new Kinetic.Circle({
name: noms[i],
id: i,
x: pos[i].x,
y: stage.getHeight() - pos[i].y,
radius: 5,
fill: 'blue'
});
circle.on('mouseover', function() {
document.body.style.cursor = 'pointer';
this.setFill('red');
var mousePos = stage.getMousePosition();
var x = ajustePos(mousePos.x, tooltip.getWidth());
var y = ajustePos(mousePos.y, tooltip.getHeight());
tooltip.setPosition(x , y);
rect.setPosition(x, y);
tooltip.setText(this.getName());
rect.setWidth(tooltip.getWidth());
tooltip.show();
rect.show();
mapLayer.draw();
tooltipLayer.draw();
});
circle.on('mouseout', function() {
document.body.style.cursor = 'default';
this.setFill('blue');
mapLayer.draw();
tooltip.hide();
rect.hide();
tooltipLayer.draw();
});
circle.on('dblclick', function() {
var xmlhttp = new XMLHttpRequest();
xmlhttp.open('GET', '" + myURL + "/' + " + "this.getId(), true);
xmlhttp.send();
});
mapLayer.add(circle);
}
var tooltip = new Kinetic.Text({
text: 'Test',
fontFamily: 'Calibri',
fontSize: 18,
padding: 5,
textFill: 'white',
fill: 'black',
visible: false,
padding: 4,
align: 'center'
});
var rect = new Kinetic.Rect({
stroke: '#555',
strokeWidth: 1,
fill: '#ddd',
shadowColor: 'black',
shadowBlur: 5,
shadowOffset: [5, 5],
shadowOpacity: 0.3,
cornerRadius: 6,
visible: false,
height: tooltip.getHeight()
});
tooltipLayer.add(rect);
tooltipLayer.add(tooltip);
stage.add(mapLayer);
stage.add(topLayer);
stage.add(tooltipLayer);
</script>
</body>
</html>";
}
default
{
changed(integer change) {
if (change & (CHANGED_REGION_START | CHANGED_REGION | CHANGED_TELEPORT))
llResetScript();
}
state_entry() {
llOwnerSay("Avatars Show V " + VERSION);
k_script = llGetInventoryKey(llGetScriptName());
llRequestURL();
}
on_rez(integer start_param) {
llResetScript();
}
http_request(key id, string method, string body) {
if (method == URL_REQUEST_GRANTED) {
myURL = body;
browse(FACE, body);
}
else if (method == "GET") {
string path_info = llGetHTTPHeader(id, "x-path-info");
// Appel url de base
if (path_info == "") {
// Liste des clés des avatars dans la région
l_keys = llGetAgentList(AGENT_LIST_REGION, []);
// Noms
integer i;
integer n = llGetListLength(l_keys);
string s_noms;
for(; i < n; ++i) s_noms += "'" + llKey2Name(llList2Key(l_keys, i)) + "',";
s_noms = llGetSubString(s_noms, 0, -2);
// Adresse de la map
vector sim_coord = llGetRegionCorner();
string x = (string)((integer)(sim_coord.x / 256.0));
string y = (string)((integer)(sim_coord.y / 256.0));
string url_map = "http://map.secondlife.com/map-1-" + x + "-" + y + "-objects.jpg";
// Positions des avatars
string s_pos;
for(i = 0; i < n; ++i) {
vector v = llList2Vector(llGetObjectDetails(llList2Key(l_keys, i), [OBJECT_POS]), 0);
s_pos += "{x : " + (string)(v.x * 2) + ",y : " + (string)(v.y * 2) + "},";
}
// Réponse
llSetContentType(id, CONTENT_TYPE_HTML);
llHTTPResponse(id, 200, getPage(s_noms, s_pos, url_map));
}
// Appel avec index avatar
else {
// Index
integer i = (integer)llGetSubString(path_info, 1, -1);
// Clé de l'avatar
k_ava_select = llList2Key(l_keys, i);
// Avatar toujours là
if(llGetAgentSize(k_ava_select) != ZERO_VECTOR) {
s_ava_select = llKey2Name(k_ava_select);
// Menu d'options
send_menu (k_script, llGetOwner(), "Select an option for avatar " + s_ava_select + " :", 0, ["Exit","Send IM","Get Infos","TP to"]);
}
// Avatar parti
else llOwnerSay("Sorry but this avatar is not in this region now, you should refresh the map");
}
}
}
//----------------------------------------------
// RETOUR DU MODULE MENU
//----------------------------------------------
link_message(integer sender_number, integer number, string message, key id) {
if(id == KEY_MENU) {
if(number == MENU_ANSWER) {
list l_sections = llParseString2List(message, ["@"], []);
// Clé du script
key id_script = (key)llList2String(l_sections, 0);
// Test bon script
if(id_script == k_script) {
if(llGetAgentSize(k_ava_select) != ZERO_VECTOR) {
// Nom du bouton cliqué
string s_message = llList2String(l_sections, 3);
if(s_message == "Send IM") {
llSetTimerEvent(60.0);
i_listen = llListen(11, "", NULL_KEY, "");
string s = "You have 60 seconds to type your message for " + s_ava_select;
llTextBox(llGetOwner(), s, 11);
}
else if(s_message == "Get Infos") {
integer i_infos = llGetAgentInfo(k_ava_select);
string s_infos = "Infos for " + s_ava_select + " :\n";
if(i_infos & AGENT_FLYING) s_infos += "is flying.\n";
if(i_infos & AGENT_IN_AIR) s_infos += "is in the air.\n";
if(i_infos & AGENT_AWAY) s_infos += "is away.\n";
if(i_infos & AGENT_BUSY) s_infos += "is busy.\n";
if(i_infos & AGENT_SITTING) s_infos += "is sitting.\n";
if(i_infos & AGENT_TYPING) s_infos += "is typing.\n";
if(i_infos & AGENT_WALKING) s_infos += "is walking.\n";
if(i_infos & AGENT_BUSY) s_infos += "is busy.\n";
if(i_infos & AGENT_MOUSELOOK) s_infos += "is in mouselook.\n";
string s = llGetAgentLanguage(k_ava_select);
integer i = llListFindList(LANG_SHORT, [s]);
if(~i) s_infos += "has language " + llList2String(LANG, i) + "\n";
list l = llGetObjectDetails(k_ava_select, [OBJECT_POS, OBJECT_RUNNING_SCRIPT_COUNT]);
s_infos += "has position " + llList2String(l, 0) + "\n";
s_infos += "has " + llList2String(l, 1) + " running scripts\n";
s_infos += "has displayname " + llGetDisplayName(k_ava_select) + "\n";
llOwnerSay(s_infos);
}
else if(s_message == "TP to") {
list l = llGetObjectDetails(k_ava_select, [OBJECT_POS]);
llMapDestination(llGetRegionName(), llList2Vector(l, 0), ZERO_VECTOR);
}
}
// Avatar parti
else llOwnerSay("Sorry but this avatar is not in this region now, you should refresh the map");
}
}
else if(number == MENU_BUSY) {
if(message == (string)k_script)
// Réponse en cas de mémoire limite atteinte
llOwnerSay("Sorry but module menu is busy now, please try later.");
}
}
}
listen(integer channel, string name, key id, string message) {
llListenRemove(i_listen);
llSetTimerEvent(.0);
if(message != "") {
llInstantMessage(k_ava_select, message);
llOwnerSay("Message sent to " + llKey2Name(k_ava_select));
}
}
timer() {
llSetTimerEvent(.0);
llOwnerSay("Time out !");
}
}
Mettez aussi ce script pour la gestion des menus :
//----------------------------------------------------------------------------------
// Module Menu V2.05
//----------------------------------------------------------------------------------
// Copyright (c) 2013 by Bestmomo Lagan
//----------------------------------------------------------------------------------
//
// Format du message reçu : id script @ id avatar @ texte @ type de menu @ Bouton1 | bouton2 | ...
// Format du message retourné : id script @ id avatar @ type de menu @ bouton cliqué
//
//
//----------------------------------------------------------------------------------
// LIBRARY FUNCTIONS
//----------------------------------------------------------------------------------
//---------------------------------------------------------------
// MENU FUNCTIONS
//---------------------------------------------------------------
// -- Initialisation menu --
// @ param [key] clé de l'avatar
// @ param [string] texte pour le menu
// @ param [list] noms des boutons
InitMenu(key id_script, key id, string texte, integer menu_type, list boutons) {
// Test avatar déjà présent
if(~llListFindList(lAvaMenu, [id])) CancelMenu(id);
// Détermination de l'index du menu
integer i = llGetListLength(lAvaMenu);
// Choix d'un canal
integer c = GetRandomChannel();
// Enregistrement des paramètres
lIndexScript += id_script;
lAvaMenu += id;
lBoutonsMenu += llDumpList2String(GetNomsCourts(boutons), "|");
lBoutonsLongs += llDumpList2String(GetNomsLongs(boutons), "|");
lCanalMenu += c;
lTexteMenu += texte;
lIndexMenu += 0;
lTypeMenu += menu_type;
// Mise en place et enregistrement de l'écoute
lEcouteMenu += llListen(llList2Integer(lCanalMenu, i), "", id, "");
// Envoi du menu
GestMenu(id, i);
}
// -- Gestion de l'index des boutons du menu --
// @ param [key] clé de l'avatar
// @ param [string] bouton de déplacement
GestIndexBoutons(key id, string browse) {
// Index du menu
integer i = llListFindList(lAvaMenu, [id]);
// Index dans les boutons du menu
integer IndexBoutons = llList2Integer(lIndexMenu, i);
// Nombre de boutons
integer n = llGetListLength(llParseString2List(llList2String(lBoutonsMenu, i), ["|"], []));
// Navigation simple pour deux pages
if(n < 23) {
if(IndexBoutons) IndexBoutons = 0;
else IndexBoutons = 11;}
// Navigation riche pour plus de deux pages
else {
if(browse == NEXT) {
IndexBoutons += 10;
if(IndexBoutons >= n) IndexBoutons = 0;}
else {
IndexBoutons -= 10;
if(IndexBoutons < 0) {
integer x = n % 10;
if(!x) x = 10;
IndexBoutons = n - x;}
}
}
// Mise à jour de l'index des boutons du menu
lIndexMenu = llListReplaceList(lIndexMenu, [IndexBoutons], i, i);
// Envoi du menu
GestMenu(id, i);
}
// -- Gestion menu --
// @ param [key] clé de l'avatar
// @ param [index] index du menu
GestMenu(key id, integer i) {
// Index pour les boutons
integer IndexBoutons = llList2Integer(lIndexMenu, i);
// Liste globale des boutons
list lBoutons = llParseString2List(llList2String(lBoutonsMenu, i), ["|"], []);
// Nombre total de boutons
integer n = llGetListLength(lBoutons);
// Si plusieurs pages
if(n > 12) {
// Que 2 pages -> navigation simple
if(n < 23) {
// Deuxième page
if(IndexBoutons)
lBoutons = [PREVIOUS] + llList2List(lBoutons, IndexBoutons, -1);
// Première page
else
lBoutons = llList2List(lBoutons, 0, 1) + [NEXT] + llList2List(lBoutons, 2, 10);}
// Plus de 2 pages -> navigation riche
else {
list l = [PREVIOUS, llList2String(lBoutons, IndexBoutons), NEXT];
// Première page ou page intermédiaire
if(n - IndexBoutons > 10)
lBoutons = l + llList2List(lBoutons, IndexBoutons + 1, IndexBoutons + 9);
// Dernière page
else {
if(IndexBoutons + 1 < n)
lBoutons = l + llList2List(lBoutons, IndexBoutons + 1, -1);
else lBoutons = l;}
}
}
// Mise en route ou réinitialisation du timer
llSetTimerEvent(TIMEOUT);
// Envoi du menu
llDialog(id, llList2String(lTexteMenu, i), lBoutons, llList2Integer(lCanalMenu, i));
}
// -- Suppression d'un menu --
// @ param [key] clé de l'avatar
CancelMenu(key id) {
// Index du menu
integer i = llListFindList(lAvaMenu, [id]);
// On teste s'il y a quelque chose à arrêter
if(~i) {
// Arrêt de l'écoute du canal
llListenRemove(llList2Integer(lEcouteMenu, i));
// Suppression des paramètres pour ce menu
lIndexScript = llDeleteSubList(lIndexScript, i, i);
lAvaMenu = llDeleteSubList(lAvaMenu, i, i);
lBoutonsMenu = llDeleteSubList(lBoutonsMenu, i, i);
lBoutonsLongs = llDeleteSubList(lBoutonsLongs, i, i);
lCanalMenu = llDeleteSubList(lCanalMenu, i, i);
lEcouteMenu = llDeleteSubList(lEcouteMenu, i, i);
lIndexMenu = llDeleteSubList(lIndexMenu, i, i);
lTexteMenu = llDeleteSubList(lTexteMenu, i, i);
lTypeMenu = llDeleteSubList(lTypeMenu, i, i);
// Si c'est le dernier on arrête le timer
if(!llGetListLength(lAvaMenu)) llSetTimerEvent(.0);}
}
// -- Fin du délai d'écoute des menu --
FinMenu() {
// Balayage de tous les menus
while(llGetListLength(lAvaMenu)) {
key k = llList2Key(lAvaMenu, 0);
// On prévient l'avatar
llInstantMessage(llList2Key(lAvaMenu, 0), "Time out for dialog.");
// On supprime le menu
CancelMenu(k);}
}
// -- Récupération du type de menu --
// @ param [key] clé de l'avatar
// @ return [string] retourne le type du menu
string get_type_menu(key id) {
// Index du menu
integer i = llListFindList(lAvaMenu, [id]);
// Type du menu
return llList2String(lTypeMenu, i);
}
// -- Génération d'un canal négatif --
// @ return [integer] retourne le canal
integer GetRandomChannel() {
return ~(integer)llFrand((float)DEBUG_CHANNEL);
}
// -- Génération d'une liste avec noms courts --
// @ param [list] liste de noms longs
// @ return [list] retourne la liste de noms courts
list GetNomsCourts(list l_noms) {
integer c;
string s;
integer n = llGetListLength(l_noms);
list l;
for(;c < n; c++) {
s = llList2String(l_noms, c);
if(llStringLength(s) > 24) l += llGetSubString(s, 0, 23);
else l += s;
}
return l;
}
// -- Génération d'une liste avec noms longs --
// @ param [list] liste de noms
// @ return [list] retourne la liste de noms longs
list GetNomsLongs(list l_noms) {
integer c;
integer n = llGetListLength(l_noms);
list l;
for(;c < n; c++) l += llList2String(l_noms, c);
return l;
}
// -- Récupération index du script appelant --
// @ param [key] clé de l'avatar
// @ return [key] clé du script
string GetIndexScript(key id) {
// Index du menu
integer i = llListFindList(lAvaMenu, [id]);
// Réponse
return llList2String(lIndexScript, i);
}
// -- Récupération nom long --
// @ param [key] clé de l'avatar
// @ param [string] nom court
// @ return [string] nom long
string GetNomLong(key id, string message) {
// Index du menu
integer i = llListFindList(lAvaMenu, [id]);
// Boutons de ce menu
list l_boutons = llParseString2List(llList2String(lBoutonsMenu, i), ["|"], []);
// Noms longs
list l_boutons_longs = llParseString2List(llList2String(lBoutonsLongs, i), ["|"], []);
// Index du nom
i = llListFindList(l_boutons, [message]);
// Nom long
return llList2String(l_boutons_longs, i);
}
//----------------------------------------------------------------------------------
// LIBRARY VARIABLES
//----------------------------------------------------------------------------------
//----------------------------------------------
// MENUS
//----------------------------------------------
string PREVIOUS = "<<<"; // Bouton pour page arrière
string NEXT = ">>>"; // Bouton pour page avant
float TIMEOUT = 60.0; // Délai pour réponse au menu
list lCanalMenu; // Canaux du Chat
list lEcouteMenu; // Handle de l'écoute
list lIndexMenu; // Index de position dans les boutons
list lTexteMenu; // Texte pour le menu
list lAvaMenu; // Clé de l'avatar
list lBoutonsMenu; // Boutons des menus
list lBoutonsLongs; // Noms longs
list lTypeMenu; // Type du menu
list lIndexScript; // Index script appelant
//----------------------------------------------
// PARAMETRES
//----------------------------------------------
key KEY_MENU = "168d4f3c-e095-a9c8-058d-d6075e6e51b8";
string VERSION = "2.05";
integer MENU_INIT = 10; // Initialisation d'un menu
integer MENU_ANSWER = 11; // Réponse d'un menu
integer MENU_BUSY = 12; // Réponse busy
//--------------------------------------------------------------------------------------
// STATE par defaut
//--------------------------------------------------------------------------------------
default
{
state_entry() {
// Accueil
llWhisper(0, "Module Menu V " + VERSION);}
link_message(integer sender_number, integer number, string message, key id) {
if(id == KEY_MENU) {
// Initialisation d'un menu
if(number == MENU_INIT) {
list l_sections = llParseString2List(message, ["@"], []);
// Test mémoire libre et traitement
if(llGetUsedMemory() < 56000) {
InitMenu(
(key)llList2String(l_sections, 0),
(key)llList2String(l_sections, 1),
llList2String(l_sections, 2),
(integer)llList2String(l_sections, 3),
llParseString2List(llList2String(l_sections, 4), ["|"], [])
);
}
else llMessageLinked(LINK_THIS, MENU_BUSY, llList2String(l_sections, 0), KEY_MENU);
}
}
}
listen(integer channel, string name, key id, string message) {
// Test de changement de page
if(~llListFindList([PREVIOUS,NEXT], [message]))
GestIndexBoutons(id, message);
else {
// Envoi de la réponse
llMessageLinked(LINK_THIS, MENU_ANSWER, GetIndexScript(id) + "@"
+ (string)id + "@"
+ get_type_menu(id) + "@"
+ GetNomLong(id, message), KEY_MENU);
CancelMenu(id);
}
}
timer() {
FinMenu();
}
}
La carte apparaît, tous les avatars dans la région sont sous la forme de points bleus. Pour éviter de saturer l'affichage le nom d'un avatar apparaît lorsqu'on dispose le curseur de la souris sur le point bleu qui le représente. Pour actualiser il suffit de rafraîchir la page. Je n'ai pas prévu d'actualisation automatique.
Il doit s'utiliser obligatoirement en HUD, l'affichage se rafraîchira automatiquement à chaque changement de région. Pour obtenir des informations sur un avatar particulier il suffit d'un double-clic sur le petit cercle bleu qui le représente, un menu classique de SL apparaît, il suffit alors de cliquer sur le bouton qui correspond à ce que l'on désire connaître : position, displayname, envoi d'IM...