-----------------------------------------------------------------------
Librairie pour la gestion de menus multiples
-----------------------------------------------------------------------
La gestion des menus avec LSL n'est pas toujours évidente et plusieurs solutions ont été proposées mais aucune ne me satisfait vraiment. J'ai un peu reconsidéré le problème en essayant de créer une librairie souple et solide. Fonctionalités :
* Gestion simultanée de multiples avatars avec menus distincts,
* Gestion d'un nombre quelconque de boutons,
* Automatisation de l'écoute du Chat avec canal unique par avatar,
* Gestion du timeout pour libérer les ressources,
* Simplicité d'utilisation de la librairie
J'ai adopté la structure de commentaires de BlackShade parce que je la trouve très claire (merci à toi).
J'ai aussi abondamment commenté le code pour ceux qui désirent comprendre le fonctionnement.
-----------------------------------------------------------------------
Utilisation de la librairie
-----------------------------------------------------------------------
Vous pouvez regarder l'exemple dans l'état par défaut...
* Voici la syntaxe de l'initialisation d'un menu avec la fonction "InitMenu" :
InitMenu(clé avatar, texte du bouton, liste des boutons);
* Il faut aussi prévoir la gestion du changement de page dans l'événement "listen" dans le cas où vos menus comportent plus de 12 boutons avec la fonction "GestIndexBoutons" :
// Test de changement de page
if(llListFindList([PREVIOUS,NEXT], [message]) != -1)
GestIndexBoutons(id, message);
* La libération d'un menu se fait avec la fonction "CancelMenu" dont le seul paramètre est la clé de l'avatar :
CancelMenu(clé avatar);
* Il faut enfin renseigner l'événement "timer" si vous voulez bénéficier du timeout avec la fonction "FinMenu" :
timer() {FinMenu();}
* Si vous devez filtrer dans l'événement "listen" les canaux affectés aux menus :
listen(integer channel, string name, key id, string message) {
...
// Test des canaux du Chat
if(llListFindList(lCanalMenu, [channel]) != -1) {
-----------------------------------------------------------------------
Gestion des boutons
-----------------------------------------------------------------------
Dans le cas où il y a plus de 12 boutons j'ai considéré deux cas :
* Moins de 23 boutons : deux pages de menus avec un bouton NEXT sur la première et un bouton PREVIOUS sur la seconde
* A partir de 23 boutons : plusieurs pages de menus avec sur chacune un boutons NEXT et un bouton PREVIOUS avec un fonctionnement "circulaire".
Le nom des boutons NEXT et PREVIOUS est défini dans les paramètres (par défaut "<<<" et ">>>")
-----------------------------------------------------------------------
TimeOut
-----------------------------------------------------------------------
J'ai prévu un timeout global, il n'y a pas trop d'intérêt de différencier par avatar. A chaque initialisation d'un menu le compteur est remis à zéro pour tous les avatars. La valeur est défini dans les paramètres par la variable TIMEOUT (par défaut 30 secondes).
-----------------------------------------------------------------------
J'attends vos remarques et commentaires et si vous trouvez des bugs...
-----------------------------------------------------------------------
Script
-----------------------------------------------------------------------
A tester à plusieurs avatars
//----------------------------------------------------------------------------------
// Test Menu V1.0
//----------------------------------------------------------------------------------
//----------------------------------------------------------------------------------
// 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, string texte, list boutons) {
// Test avatar déjà présent
if(llListFindList(lAvaMenu, [id]) != -1) CancelMenu(id);
// Mise en route ou réinitialisation du timer
llSetTimerEvent(TIMEOUT);
// Détermination de l'index du menu
integer i = llGetListLength(lAvaMenu);
// Choix d'un canal
integer c = GetRandomChannel();
// Vérification unicité de ce canal
while(llListFindList(lCanalMenu, [c]) != -1)
c = GetRandomChannel();
// Enregistrement des paramètres
lAvaMenu += id;
lBoutonsMenu += llDumpList2String(boutons, "|");
lCanalMenu += c;
lTexteMenu += texte;
lIndexMenu += 0;
// 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) IndexBoutons = n - n % 10;}
}
// 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;}
}
}
// 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 != -1) {
// Arrêt de l'écoute du canal
llListenRemove(llList2Integer(lEcouteMenu, i));
// Suppression des paramètres pour ce menu
lAvaMenu = llDeleteSubList(lAvaMenu, i, i);
lBoutonsMenu = llDeleteSubList(lBoutonsMenu, i, i);
lCanalMenu = llDeleteSubList(lCanalMenu, i, i);
lEcouteMenu = llDeleteSubList(lEcouteMenu, i, i);
lIndexMenu = llDeleteSubList(lIndexMenu, i, i);
lTexteMenu = llDeleteSubList(lTexteMenu, 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);}
}
// -- Fonction qui génère un canal au hasard (négatif) --
// @ return [integer] retourne le canal
integer GetRandomChannel() {
return ~(integer)llFrand((float)DEBUG_CHANNEL);
} // fonction de BlackShade NightFire
//----------------------------------------------------------------------------------
// LIBRARY VARIABLES
//----------------------------------------------------------------------------------
//----------------------------------------------
// MENUS
//----------------------------------------------
string PREVIOUS = "<<<"; // Bouton pour page arrière
string NEXT = ">>>"; // Bouton pour page avant
float TIMEOUT = 30.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 du menu
//--------------------------------------------------------------------------------------
// SCRIPT GLOBAL VARIABLES
//--------------------------------------------------------------------------------------
// Liste des boutons
list lBoutons;
//--------------------------------------------------------------------------------------
// STATE default de test
//--------------------------------------------------------------------------------------
default
{
state_entry() {
// Création d'une liste de boutons pour les tests
integer c;
for(c = 0; c < 54; ++c)
lBoutons += (string)c;
}
listen(integer channel, string name, key id, string message) {
// Test de changement de page pour navigation
if(llListFindList([PREVIOUS,NEXT], [message]) != -1)
GestIndexBoutons(id, message);
// Traitement du bouton choisi
else {
llInstantMessage(id, "Vous avez choisi le bouton " + message);
CancelMenu(id);
}
}
touch_start(integer total_number) {
// Traitement pour tous les avatars qui ont cliqué
integer c;
for(c = 0; c < total_number; ++c)
InitMenu(llDetectedKey(c), "\nChoisissez une option :", lBoutons);
}
timer() {FinMenu();}
}