3 Menüs für alle Fälle

Oftmals benötigt man für ein Skript einen Dialog, bei dem man etwas auswählen kann, wie z.B. eine Textur oder Farbe. Dazu hab ich mal ein paar Menüs geschrieben, die ich immer wieder verwende. Drei davon will ich hier mal vorstellen.

Menü 1

Mein Standardmenü. Es können beliebig viele Menüeinträge vorhanden sein (in der Liste DlgItems). Als Demonstration stehen hier nur Zahlen drauf. Schaltflächen für Vor und Zurück werden bei Bedarf automatisch eingefügt. Die Auswertung der einzelnen Tasten erfolgt im Event listen und muss von euch selbst eingebaut werden.
Die Hauptprozedur ist DlgShow. Hier werden Menüeinträge erzeugt, je nachdem auf welcher Seite man gerade ist, und dann der eigentliche Dialog aufgerufen. Mir persönlich gefällt er aber nicht, daher gibt es hier die die Prozedur DlgNice, die dem Dialog ein schöneres Aussehen gibt. D.h. die Schaltflächen gehen von links oben nach rechts unten, fehlende werden mit leeren Flächen aufgefüllt.
Die Funktion DlgMain behandelt die Standardtasten Vor, Zurück und Leer, so dass ihr euch nur um eure eigenen Schaltflächen kümmern müsst.
Das vorgestellte Menü wirkt nur für den Eigentümer und ist zeitbegrenzt.




Code Menü 1:


// Menu 1
// Ein Menü mit beliebig vielen Schaltflächen
// diese stehen in der Liste DlgItems

string NEXT = ">>>";						// Schaltflächen für vor und zurück
string PREV = "<<<";
string DLGTITLE = "Drücke eine Taste..";	// Dialog-Text
integer DLGCHANNEL;						 	// Chatkanal
integer TIME = 30;							// Zeit nach der abgebrochen wird, wenn TIMEOUT = TRUE ist
integer TIMEOUT = TRUE;				 		// soll ein Abbruch nach der Zeit TIME erfolgen?
integer MenuOn = FALSE;				 		// kein neues Menu wenn schon eins da ist
list DlgItems = [];						 	// die Schaltflächen des Dialogs
integer DlgPage = 0;						// aktuelle Seite - wird vom Skript verwendet
integer Listener;

list DlgNice(list Dlg)				 // "schöneres" Aussehen
{	list l = [];
		integer len = llGetListLength(Dlg);
		integer i;
		while(llGetListLength(Dlg) < 12) Dlg += [" "];
		integer ip = llListFindList(Dlg, [PREV]);
		integer in = llListFindList(Dlg, [NEXT]);
		if(in != -1)
		{
			Dlg = llDeleteSubList(Dlg, in, in);
			Dlg += [NEXT];
		}
		if(ip != -1)
		{
			Dlg = llDeleteSubList(Dlg, ip, ip);
			Dlg = llListInsertList(Dlg, [PREV], 9);
		}
		for(i = 0; i< 3; i++) l += llList2List(Dlg, i + 9, i + 9);
		for(i = 3; i< 6; i++) l += llList2List(Dlg, i + 3, i + 3);
		for(i = 6; i< 9; i++) l += llList2List(Dlg, i - 3, i - 3);
		for(i = 9; i< 12; i++) l += llList2List(Dlg, i - 9, i - 9);
		return l;
	}

DlgShow(string msg, integer channel)		// die eigentliche Dialogprozedur
{
	if(MenuOn) return;
	MenuOn = TRUE;
	list Dlg = [];
	integer Count = llGetListLength(DlgItems);
	integer Start = (DlgPage - 1) * 10 + 11;
	if(DlgPage == 0)
	{
		Start = 0;
		if(Count < 13) Dlg = DlgItems;
		else Dlg = llList2List(DlgItems, 0, 1) + [NEXT] + llList2List(DlgItems, 2, 10);
	}
	else
	{
		if(Count < Start + 12) Dlg = [PREV] + llList2List(DlgItems, Start, Start + 11);
		else Dlg = [PREV] + llList2List(DlgItems, Start, Start) + [NEXT] + llList2List(DlgItems, Start + 1, Start + 9);
	}
	integer i;
	for(i = llGetListLength(Dlg) - 1; i >= 0; i--) Dlg = llListReplaceList(Dlg, [llGetSubString(llList2String(Dlg, i), 0, 23)], i, i);
	if(TIMEOUT) llSetTimerEvent(TIME);
	llListenRemove(Listener);
	Listener = llListen(channel, "", llGetOwner(), "");
	// der Standard-Dialog, den ich nicht mag
	// llDialog(llGetOwner(), msg, Dlg, channel);
	// wenns schöner aussehen soll:
	llDialog(llGetOwner(), msg, DlgNice(Dlg), channel);}

integer DlgMain(string msg)
{
	MenuOn = FALSE;
	llSetTimerEvent(0);
	llListenRemove(Listener);
	if(msg == PREV)
	{
		DlgPage--;
		DlgShow(DLGTITLE, DLGCHANNEL);
		return TRUE;
	} else if(msg == NEXT)
{
	DlgPage++;
	DlgShow(DLGTITLE, DLGCHANNEL);
	return TRUE;
} else if(msg == " ")
{
	DlgShow(DLGTITLE, DLGCHANNEL);
	return TRUE;
}	else return FALSE;}

	default
	{
		state_entry()
		{
			integer i;
			DLGCHANNEL = (integer)llFrand(10000) + 999;
			DlgItems = [];
			// nur zum Test
			for(i = 0; i < 35; i++) DlgItems += (string)i;
		}
		touch_start(integer total_number)
		{
			if(!MenuOn && llDetectedKey(0) == llGetOwner())
			{
				DlgPage = 0;
				DlgShow(DLGTITLE, DLGCHANNEL);
			}
		}
		listen(integer channel, string name, key id, string message)
		{
			if(DlgMain(message)) return;
			// hier werden die Schaltflächen ausgewertet

				// Wenn das Menü erneut aufgerufen werden soll, dann die nächste Zeile verwenden
				// DlgShow(DLGTITLE, DLGCHANNEL);
			}
		timer()
		{
			llSetTimerEvent(0);
			llListenRemove(Listener);
			MenuOn = FALSE;
			llOwnerSay("Die Zeit für das Menü ist abgelaufen. Bitte nochmals aufrufen.");
		}
	}

Menü 2

Fast das gleiche wie das erste Menü, nur werden hier die Namen aus der Liste der Dialogeinträge im Textfeld dargestellt und die Schaltflächen durchnummeriert. Das ist sehr praktisch, wenn die Texte nicht mehr auf die Schaltflächen passen, oder wenns einfach nur ordentlich aussehen soll.
Im Event listen steht dann in der Variable msg der Text der gedrückten Schaltfläche aus der Liste DlgItems, den ihr dann auswerten könnt.



Code Menü 2:


// Menu 2
// Die Menü-Einträge werden im Textfeld des Dialogs angezeigt
// und die Schaltflächen durchnumeriert
string NEXT = ">>>";						// Tasten zum Weiterschalten
string PREV = "<<<";
string DLGTITLE = "Drücke eine Taste..";	// Dialog-Text
integer DLGCHANNEL;							// Chatkanal
integer TIME = 30;							// Zeit nach der abgebrochen wird, wenn TIMEOUT = TRUE ist
integer TIMEOUT = TRUE;						// soll ein Abbruch nach der Zeit TIME erfolgen?
integer MenuOn = FALSE;						// kein neues Menu wenn schon eins da ist
list DlgItems;								// die Menü-Einträge
integer DlgPage = 0;						// aktuelle Seite - wird vom Skript verwendet
integer Listener;

list DlgNice(list Dlg)		// "schöneres" Aussehen
{
	list l = [];
	integer len = llGetListLength(Dlg);
	integer i;	while(llGetListLength(Dlg) < 12) Dlg += [" "];
	integer ip = llListFindList(Dlg, [PREV]);
	integer in = llListFindList(Dlg, [NEXT]);
	if(in != -1)
	{
		Dlg = llDeleteSubList(Dlg, in, in);
		Dlg += [NEXT];
	}
	if(ip != -1)
	{
		Dlg = llDeleteSubList(Dlg, ip, ip);
		Dlg = llListInsertList(Dlg, [PREV], 9);
	}
	for(i = 0; i< 3; i++) l += llList2List(Dlg, i + 9, i + 9);
	for(i = 3; i< 6; i++) l += llList2List(Dlg, i + 3, i + 3);
	for(i = 6; i< 9; i++) l += llList2List(Dlg, i - 3, i - 3);
	for(i = 9; i< 12; i++) l += llList2List(Dlg, i - 9, i - 9);
	return l;
}

DlgShow(integer channel)
{
	if(MenuOn) return;
	list Dlg = [];
	integer Count = llGetListLength(DlgItems);
	integer Start = 0;
	if(DlgPage > 0) Start = (DlgPage - 1) * 10 + 11;
	integer End = Count - 1;
	if((End - Start) > 11) End = Start + 11;
	integer i;
	for(i = Start; i <= End; i++) Dlg += [(string)(i + 1)];
	string msg = "";
	if(DlgPage == 0)
	{
		if(Count > 12) Dlg = llList2List(Dlg, 0, 1) + [NEXT] + llList2List(Dlg, 2, 10);
	} else
{
	if(Count < Start + 12) Dlg = [PREV] + llList2List(Dlg, 0, 10);
	else Dlg = [PREV] + llList2List(Dlg, 0, 0) + [NEXT] + llList2List(Dlg, 1, 9);
}	integer x = llGetListLength(Dlg);
	if(llListFindList(Dlg, [PREV]) != -1) x--;
	if(llListFindList(Dlg, [NEXT]) != -1) x--;
	for(Count = 0; Count < x; Count++)
	{
		msg += (string)(Start + Count + 1) + " - " + llList2String(DlgItems, Start + Count) + "\n";
	}
	llListenRemove(Listener);	Listener = llListen(channel, "", llGetOwner(), "");
	if(TIMEOUT) llSetTimerEvent(TIME);
	// der Standard-Dialog, den ich nicht mag
	// llDialog(llGetOwner(), msg, Dlg, channel);
	// wenns schöner aussehen soll:
	llDialog(llGetOwner(), msg, DlgNice(Dlg), channel);
}

integer DlgMain(string msg)
{
	MenuOn = FALSE;
	llSetTimerEvent(0);
	llListenRemove(Listener);
	if(msg == PREV)
	{
		DlgPage--;
		DlgShow(DLGCHANNEL);
		return TRUE;
	} else if(msg == NEXT)
{
	DlgPage++;
	DlgShow(DLGCHANNEL);
	return TRUE;
} else if(msg == " ")
{
	DlgShow(DLGCHANNEL);
	return TRUE;
}	else return FALSE;
}

default
{
	state_entry()
	{
		DLGCHANNEL = -((integer)llFrand(50000) + 100);
		integer i;		// nur zum Test
		for(i = 0; i < 35; i++) DlgItems += ["Menüeintrag "+(string)i];
	}
	touch_start(integer total_number)
	{
		if(!MenuOn && llDetectedKey(0) == llGetOwner())
		{
			DlgPage = 0;
			DlgShow(DLGCHANNEL);
		}
	}
	listen(integer channel, string name, key id, string message)
	{
		if(DlgMain(message)) return;
		integer x = (integer)message;
		string msg = llList2String(DlgItems, x - 1);
		// hier werden die Tasten ausgewertet

		// Wenn das Menü erneut aufgerufen werden soll, dann die nächste Zeile verwenden
		//DlgShow(DlgItems);
	}
	timer()
	{
		llSetTimerEvent(0);
		llListenRemove(Listener);
		MenuOn = FALSE;
		llOwnerSay("Die Zeit für das Menü ist abgelaufen. Bitte nochmals aufrufen.");
	}
}

Menü 3

Dieses Menü ist an das MLPV-Skript angelehnt, aber nur sehr einfach gehalten. Hier könnt ihr mittels Notecard ein Hauptmenü und Untermenüs erzeugen. Schaltflächen für Vor und Zurück werden wieder automatisch eingefügt wenn nötig, genauso wie eine Schaltfläche Zurück, wenn man sich in einem Untermenü befindet.
In der Notecard gibt es nur drei verschiedene Einträge:
- Menu Name - Name eines Menüs, das erste ist das Hauptmenü
- ToMenu Name - Name einew Untermenüs auf einer Schaltfläche, bei Klick wird dahin verzweigt
- Item Name - Name einer Schaltfläche, die im Event listen ausgewertet werden muss
Zur Demonstration hab ich mal eine Beispiel-NC angehängt - die macht zwar keinen Sinn, zeigt aber die Wirkungsweise.



Code Menü 3:


// Menü 3
// ein Menü mit Notecard
string DLGTXT = "Wähle eine Option";
integer TIMEOUT = TRUE;
integer TIME = 30;
list KEYWORDS = ["menu", "tomenu", "item"];
integer BACKCMD = 0;
integer TOMENU = 1;
integer ITEM = 2;
string NEXT = ">>>";
string PREV = "<<<";
string BACK = "^^^";
list Cmds;
list Dlgs;
integer CurrDlg;
list ButtonIndex;
list Buttons;
integer MenuOn = FALSE;
integer DlgPage;
integer DlgChannel;
list DlgStack;
list DlgItems;
integer Listener;
key NCid;
integer NCLine;
string NCNAME = "config";

list DlgNice(list Dlg)						// "schöneres" Aussehen
{
	list l = [];
	integer i;
	integer in = llListFindList(Dlg, [NEXT]);
	integer ip = llListFindList(Dlg, [PREV]);
	integer ib = llListFindList(Dlg, [BACK]);
	while(llGetListLength(Dlg) < 12) Dlg += [" "];
	if(in != -1)
	{
		Dlg = llDeleteSubList(Dlg, in, in);
		Dlg += [NEXT];
	}
	if(ib != -1)
	{
		Dlg = llDeleteSubList(Dlg, ib, ib);
		Dlg = llListInsertList(Dlg, [BACK], 10);
	}
	if(ip != -1)
	{
		Dlg = llDeleteSubList(Dlg, ip, ip);
		Dlg = llListInsertList(Dlg, [PREV], 9);
	}
	for(i = 0; i < 3; i++) l += llList2List(Dlg, i + 9, i + 9);
	for(i = 3; i < 6; i++) l += llList2List(Dlg, i + 3, i + 3);
	for(i = 6; i < 9; i++) l += llList2List(Dlg, i - 3, i - 3);
	for(i = 9; i < 12; i++) l += llList2List(Dlg, i - 9, i - 9);
	return l;
}

DlgShow(string msg, integer channel, integer dlg)		// die eigentliche Dialogprozedur
{
	list Dlg = [];
	integer next = FALSE;
	integer d = 0;
	MenuOn = TRUE;
	if(dlg != 0) d = 1;
	integer Start = (DlgPage - 1) * (10 - d) + 11 - d;
	CurrDlg = dlg;
	DlgItems = llList2List(Buttons, llList2Integer(ButtonIndex, dlg), llList2Integer(ButtonIndex, dlg + 1) - 1);
	integer Count = llGetListLength(DlgItems);
	if(DlgPage == 0)
	{
		if(Count < (13 - d)) Dlg = DlgItems;
		else
		{
			Dlg = llList2List(DlgItems, 0, 1) + [NEXT] + llList2List(DlgItems, 2, (10 - d));
			next = TRUE;
		}
	} else
{
	if(Count < Start + 12 - d) Dlg = [PREV] + llList2List(DlgItems, Start, Start + 10 - d);
	else
	{
		Dlg = [PREV] + llList2List(DlgItems, Start, Start) + [NEXT] + llList2List(DlgItems, Start + 1, Start + 9 - d);
		next = TRUE;
	}
}
	if(dlg != 0)
	{
		if(next)
		{
			Dlg = [llList2String(Dlg, 0)] + [llList2String(Dlg, 2)] + [llList2String(Dlg, 1)] + llList2List(Dlg, 3, 11);
		}
		Dlg = llListInsertList(Dlg, [BACK], 1);
	}
	if(TIMEOUT) llSetTimerEvent(TIME);
	llListenRemove(Listener);
	Listener = llListen(DlgChannel, "", llGetOwner(), "");
	// llDialog(llGetOwner(), msg, Dlg, channel);
	// wenns schöner aussehen soll:
	llDialog(llGetOwner(), msg, DlgNice(Dlg), channel);
}

integer DlgMain(string msg, integer cmd)
{
	integer i;
	integer dlg;
	integer result = TRUE;
	MenuOn = FALSE;
	llSetTimerEvent(0);	llListenRemove(Listener);
	if(msg == NEXT)
	{
		DlgPage++;
		DlgShow(DLGTXT, DlgChannel, CurrDlg);
	} else if(msg == PREV)
{
	DlgPage--;
	DlgShow(DLGTXT, DlgChannel, CurrDlg);
} else if(msg == BACK)
{
	i = llGetListLength(DlgStack) - 2;
	dlg = llList2Integer(DlgStack, i);
	DlgPage = llList2Integer(DlgStack, i + 1);
	DlgStack = llDeleteSubList(DlgStack, i, i + 1);
	DlgShow(DLGTXT, DlgChannel, dlg);
} else if(msg == " ")
{
	DlgShow(DLGTXT, DlgChannel, CurrDlg);
} else if(cmd == TOMENU)
{
	dlg = llListFindList(Dlgs, [msg]);
	DlgStack += [CurrDlg, DlgPage];
	DlgPage = 0;
	DlgShow(DLGTXT, DlgChannel, dlg);
} else result = FALSE;
	return result;
}

Init()
{
	Cmds = [];
	Dlgs = [];
	Buttons = [];
	ButtonIndex = [];
	DlgStack = [];
	DlgPage = 0;
	NCLine = 0;
	MenuOn = TRUE;
	DlgChannel = (integer)llFrand(100000) + 999;
	if(llGetInventoryType(NCNAME) == INVENTORY_NONE) llOwnerSay("Keine config Notecard gefunden.");
	else
	{
		llOwnerSay("Lese config Notecard...");
		NCid = llGetNotecardLine(NCNAME, NCLine++);
	}
	llSetTimerEvent(0);
	llListenRemove(Listener);
}

default
{
	state_entry()
	{
		Init();
	}
	on_rez(integer start_param)
	{
		Init();
	}
	changed(integer change)
	{
		if(change & CHANGED_INVENTORY) Init();
	}
	touch_start(integer total_number)
	{
		if(!MenuOn && llDetectedKey(0) == llGetOwner())
		{
			DlgPage = 0;
			DlgShow(DLGTXT, DlgChannel, 0);
		}
	}	dataserver(key id, string data)
{
	if(id == NCid)
	{
		if(data != EOF)
		{
			// Kommentare entfernen
			integer i = llSubStringIndex(data, "//");
			if(i != -1)
			{
				if(i == 0) data = "";
				else data = llGetSubString(data, 0, i - 1);
			}
			// Menueintrag und Kommando
			i = llSubStringIndex(data, " ");
			string item = llToLower(llGetSubString(data, 0, i - 1));
			data = llStringTrim(llDeleteSubString(data, 0, i), STRING_TRIM);
			if(item == "menu")
			{
				Dlgs += [data];
				ButtonIndex += [llGetListLength(Buttons)];
			} else if(item == "tomenu")
		{
			Buttons += [data];
			Cmds += [TOMENU];
		} else if(item == "item")
		{
			Buttons += [data];
			Cmds += [ITEM];
		}
			NCid = llGetNotecardLine(NCNAME, NCLine++);
		} else
	{
		ButtonIndex += [llGetListLength(Buttons)];
		llOwnerSay((string)llGetListLength(Buttons) + " Menüeinträge geladen.");
		MenuOn = FALSE;
	}
	}
}
	listen(integer channel, string name, key id, string msg)
	{
		integer i = llListFindList(Buttons, [msg]);
		integer cmd = llList2Integer(Cmds, i);
		if(DlgMain(msg, cmd)) return;
		if(cmd == ITEM)
		{
			// Hier werden die Tasten ausgewertet
		}
	}
	timer()
	{
		llSetTimerEvent(0);
		llListenRemove(Listener);
		MenuOn = FALSE;
		llOwnerSay("Die Zeit für das Menü ist vorbei. Bitte erneut aufrufen.");
	}
}

NC config:


// Testmenumenu test1 
// Das erste Menü
tomenu test2
tomenu test3
tomenu test4
tomenu test5
tomenu test6
tomenu test7
tomenu test8
tomenu test9
tomenu test10
tomenu test11
tomenu test12

menu test2
item 21
item 22
item 23
item 24
item 25
item 26
item 27
item 28
item 29
item 30
item 31
item 32
item 33
item 34
item 35
item 36
item 37
item 38
item 39
item 40
item 41
item 42

menu test3
item 31
item 32

menu test4
item 41
item 42

menu test5
item 51
item 52

menu test6
item 61
item 62

menu test7
item 71
item 72

menu test8
item 81
item 82

menu test9
item 91
item 92

menu test10
item 101
item 102

menu test11
item 111
item 112

menu test12
item 121
item 122