AVsitter/AVsitter2/Plugins/AVprop/[AV]menu.lsl
Sei Lisa bf8f10bfe6 Fix several calls that had wraparound hazards.
llGetSubString("abc", 3, -1) gives "abc"; llList2List(L, 1, -1) gives L when L has length 1. In general, llGetSubString or llList2List return the whole thing when the starting index is grater than the last element in the list.

There were a number of spots with that hazard, so fix all we found.
2017-11-19 18:38:33 +01:00

634 lines
19 KiB
Text
Raw Blame History

/*
* [AV]menu - Allow using props without using sitting scripts.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright © the AVsitter Contributors (http://avsitter.github.io)
* AVsitter™ is a trademark. For trademark use policy see:
* https://avsitter.github.io/TRADEMARK.mediawiki
*
* Please consider supporting continued development of AVsitter and
* receive automatic updates and other benefits! All details and user
* instructions can be found at http://avsitter.github.io
*/
string product = "AVmenu™";
string version = "2.2";
integer verbose = 0;
string prop_script = "[AV]prop";
string notecard_name = "AVpos";
string main_script = "[AV]sitA";
string custom_text;
list MENUCONTROL_TYPES = ["ALL", "OWNER ONLY", "GROUP ONLY"];
integer MENUCONTROL_INDEX;
integer owner_only;
integer last_menu_unixtime;
string last_menu_avatar;
integer menu_channel;
key notecard_key;
key notecard_query;
list MENU_LIST;
list DATA_LIST;
integer MTYPE;
integer notecard_line;
integer current_menu = -1;
integer menu_page;
integer choosing;
string choice;
integer listen_handle;
integer number_per_page = 9;
integer menu_pages;
string last_text;
string SEP = "<22>"; // OSS::string SEP = "\u007F";
integer pass_security(key id)
{
integer access_allowed = FALSE;
string SECURITY_TYPE = llList2String(MENUCONTROL_TYPES, MENUCONTROL_INDEX);
if (SECURITY_TYPE == "ALL")
{
access_allowed = TRUE;
}
else if (SECURITY_TYPE == "GROUP ONLY" && llSameGroup(id) == TRUE)
{
access_allowed = TRUE;
}
else if (id == llGetOwner())
{
access_allowed = TRUE;
}
return access_allowed;
}
check_avsit()
{
if (llGetInventoryType(main_script) == INVENTORY_SCRIPT)
{
remove_script("This script can not be used with the sit script in the same prim. Removing script!");
}
}
list order_buttons(list buttons)
{
return llList2List(buttons, -3, -1) + llList2List(buttons, -6, -4) + llList2List(buttons, -9, -7) + llList2List(buttons, -12, -10);
}
Out(integer level, string out)
{
if (verbose >= level)
{
llOwnerSay(llGetScriptName() + "[" + version + "] " + out);
}
}
Readout_Say(string say)
{
llSleep(0.2);
string objectname = llGetObjectName();
llSetObjectName("");
llRegionSayTo(llGetOwner(), 0, "◆" + say);
llSetObjectName(objectname);
}
dialog(key av, string menu_text, list menu_items)
{
llDialog(av, product + " " + version + "\n\n" + menu_text, order_buttons(menu_items), menu_channel);
last_menu_unixtime = llGetUnixTime();
llSetTimerEvent(120);
}
integer avprop_is_copy_transfer(integer owner_mask)
{
integer perms = llGetInventoryPermMask(prop_script, owner_mask);
if (perms & PERM_COPY && perms & PERM_TRANSFER)
{
return 1;
}
return 0;
}
integer prim_is_mod()
{
integer perms = llGetObjectPermMask(MASK_OWNER);
if (perms & PERM_MODIFY)
{
return 1;
}
return 0;
}
menu_check(string name, key id)
{
if (pass_security(id) == TRUE)
{
if (name == last_menu_avatar || llGetUnixTime() - last_menu_unixtime > 5)
{
last_menu_unixtime = llGetUnixTime();
last_menu_avatar = name;
menu_page = 0;
current_menu = -1;
prop_menu(FALSE, id);
}
else
{
llDialog(id, product + " " + version + "\n\n" + llList2String(llParseString2List(last_menu_avatar, [" "], []), 0) + " is already using the menu.\nPlease wait a moment.", ["OK"], -585868);
}
}
else
{
llDialog(id, product + " " + version + "\n\n" + "Sorry, the owner has set this menu to: " + llList2String(MENUCONTROL_TYPES, MENUCONTROL_INDEX), ["OK"], -585868);
}
}
options_menu()
{
string text;
list menu_items = ["[BACK]"];
text = "Prop options:\n";
if (avprop_is_copy_transfer(MASK_OWNER) && prim_is_mod())
{
menu_items += ["[NEW]", "[DUMP]"];
text += "\n[NEW] = Add a new prop.";
text += "\n[DUMP] = Read settings to chat.";
}
menu_items += ["[SAVE]", "[CLEAR]", "[SECURITY]", "[RESET]"];
text += "\n[SAVE] = Save prop positions.";
text += "\n[CLEAR] = Clear props.";
text += "\n[SECURITY] = Menu security.";
text += "\n[RESET] = Reload notecard.";
dialog(llGetOwner(), text, menu_items);
}
choice_menu(list options, string menu_text)
{
last_text = menu_text;
choosing = TRUE;
menu_text = "\n(Page " + (string)(menu_page + 1) + "/" + (string)menu_pages + ")\n" + menu_text + "\n\n";
list menu_items;
integer i;
if (llGetListLength(options) == 0)
{
menu_text = "\nNo items of required type in the prim inventory.";
menu_items = ["[BACK]"];
}
else
{
integer cutoff = 65;
integer all_options_length = llStringLength(llDumpList2String(options, ""));
integer total_need_to_cut = 412 - all_options_length;
if (total_need_to_cut < 0)
{
cutoff = 43;
}
for (i = 0; i < llGetListLength(options); i++)
{
menu_items += (string)(i + 1);
string item = llList2String(options, i);
if (llStringLength(item) > cutoff)
{
item = llGetSubString(item, 0, cutoff) + "..";
}
menu_text += (string)(i + 1) + "." + item + "\n";
}
while (llGetListLength(menu_items) < number_per_page)
{
menu_items += " ";
}
menu_items += ["[BACK]", "[<<]", "[>>]"];
}
dialog(llGetOwner(), menu_text, menu_items);
}
list get_choices(integer page)
{
menu_page = page;
list options;
integer i;
integer start = number_per_page * menu_page;
integer end = start + number_per_page;
integer type = INVENTORY_OBJECT;
i = start;
while (llGetListLength(options) + start < end && i < llGetInventoryNumber(type))
{
options += llGetInventoryName(type, i);
i++;
}
i = llGetInventoryNumber(type);
menu_pages = llCeil((float)i / number_per_page);
return options;
}
remove_script(string reason)
{
string message = "\n" + llGetScriptName() + " ==Script Removed==\n\n" + reason;
llDialog(llGetOwner(), message, ["OK"], -3675);
llInstantMessage(llGetOwner(), message);
llRemoveInventory(llGetScriptName());
}
integer prop_menu(integer return_pages, key av)
{
choosing = FALSE;
choice = "";
integer total_items;
integer i = current_menu + 1;
while (i < llGetListLength(MENU_LIST) && llSubStringIndex(llList2String(MENU_LIST, i), "M:") != 0)
{
total_items++;
i++;
}
list menu_items2;
if (current_menu != -1)
{
menu_items2 = ["[BACK]"] + menu_items2;
}
list menu_items1;
if (llGetInventoryType(prop_script) == INVENTORY_SCRIPT)
{
menu_items2 += ["[OWNER]"];
}
if (total_items + llGetListLength(menu_items2) > 12)
{
menu_items2 += ["[<<]", "[>>]"];
}
integer items_per_page = 12 - llGetListLength(menu_items2);
integer page_start = current_menu + 1 + menu_page * items_per_page;
for (i = page_start; i < page_start + items_per_page; i++)
{
if (i < llGetListLength(MENU_LIST))
{
if (llSubStringIndex(llList2String(MENU_LIST, i), "M:") != -1)
{
jump end;
}
if (llListFindList(["T:", "S:", "B:"], [llGetSubString(llList2String(MENU_LIST, i), 0, 1)]) == -1)
{
menu_items1 += llList2String(MENU_LIST, i);
}
else
{
menu_items1 += llGetSubString(llList2String(llParseString2List(llList2String(MENU_LIST, i), ["|"], []), 0), 2, 99999);
}
}
}
@end;
if (return_pages)
{
integer pages = llCeil(total_items) / (12 - llGetListLength(menu_items2));
if (total_items % (12 - llGetListLength(menu_items2)) == 0)
{
pages--;
}
return pages;
}
if (llList2String(menu_items2, 0) == "[BACK]")
{
menu_items1 = ["[BACK]"] + menu_items1;
menu_items2 = llDeleteSubList(menu_items2, 0, 0);
}
menu_channel = ((integer)llFrand(0x7FFFFF80) + 1) * -1; // 7FFFFF80 = max float < 2^31
llListenRemove(listen_handle);
listen_handle = llListen(menu_channel, "", av, "");
dialog(av, custom_text, menu_items1 + menu_items2);
return 0;
}
string strReplace(string str, string search, string replace)
{
return llDumpList2String(llParseStringKeepNulls(str, [search], []), replace);
}
naming()
{
llTextBox(llGetOwner(), "\nPlease type a button name for your prop\nProp: " + choice, menu_channel);
}
default
{
state_entry()
{
if (llSubStringIndex(llGetScriptName(), " ") != -1)
{
remove_script("Use only one copy of this script!");
}
check_avsit();
notecard_key = llGetInventoryKey(notecard_name);
Out(0, "Loading...");
notecard_query = llGetNotecardLine(notecard_name, notecard_line);
}
timer()
{
llListenRemove(listen_handle);
}
listen(integer listen_channel, string name, key id, string msg)
{
if (choice != "")
{
if (msg == "")
{
naming();
}
else
{
integer perms = llGetInventoryPermMask(choice, MASK_NEXT);
if (!(perms & PERM_COPY))
{
llSay(0, "Could not add prop '" + choice + "'. Props and their content must be COPY-OK for NEXT owner.");
}
else
{
llMessageLinked(LINK_THIS, 90173, msg, choice); // add PROP line to [AV]prop
MENU_LIST = ["B:" + msg] + MENU_LIST;
DATA_LIST = [90200] + DATA_LIST; // Rez prop (with menu)
}
choice = "";
options_menu();
}
return;
}
if (choosing && llListFindList(["1", "2", "3", "4", "5", "6", "7", "8", "9"], [msg]) != -1)
{
choosing = FALSE;
choice = llList2String(get_choices(menu_page), (integer)msg - 1);
naming();
return;
}
if (msg == "[SECURITY]")
{
if (id == llGetOwner())
{
dialog(llGetOwner(), "Who is allowed to control this menu?", MENUCONTROL_TYPES);
}
else
{
llRegionSayTo(id, 0, "Sorry, only the owner can use this.");
}
return;
}
integer mindex_test = llListFindList(MENU_LIST, ["M:" + msg]);
if (mindex_test != -1)
{
menu_page = 0;
current_menu = mindex_test;
}
mindex_test = llListFindList(MENU_LIST, ["B:" + msg]);
if (mindex_test != -1)
{
list button_data = llParseStringKeepNulls(llList2String(DATA_LIST, mindex_test), [SEP], []);
if (llList2String(button_data, 1) != "")
{
msg = llList2String(button_data, 1);
}
if (llList2String(button_data, 2) != "")
{
id = llList2String(button_data, 2);
}
llMessageLinked(LINK_SET, (integer)llList2String(button_data, 0), msg, id);
return;
}
else if (msg == "[>>]" || msg == "[<<]")
{
if (choosing)
{
if (msg == "[>>]")
{
menu_page++;
if (menu_page >= menu_pages)
{
menu_page = 0;
}
}
else if (msg == "[<<]")
{
menu_page--;
if (menu_page < 0)
{
menu_page = menu_pages - 1;
}
}
choice_menu(get_choices(menu_page), last_text);
}
else
{
if (msg == "[<<]")
{
menu_page--;
if (menu_page < 0)
{
menu_page = prop_menu(TRUE, NULL_KEY);
}
}
else
{
menu_page++;
if (menu_page > prop_menu(TRUE, NULL_KEY))
{
menu_page = 0;
}
}
prop_menu(FALSE, id);
}
return;
}
else if (msg == "[BACK]")
{
menu_page = 0;
current_menu = -1;
}
else if (msg == "[NEW]")
{
llMessageLinked(LINK_THIS, 90200, "", ""); // Clear props
choice_menu(get_choices(0), "Please choose your prop:\n\n(Props must include the [AV]object script!)");
return;
}
else if (msg == "[DUMP]")
{
Readout_Say("");
Readout_Say("--✄--COPY BELOW INTO \"AVpos\" NOTECARD--✄--");
Readout_Say("");
if (custom_text != "")
{
Readout_Say("TEXT " + strReplace(custom_text, "\n", "\\n"));
}
integer i;
for (i = 0; i < llGetListLength(MENU_LIST); i++)
{
list change_me = llParseString2List(llList2String(MENU_LIST, i), [":"], []);
if (llGetListLength(change_me) == 2)
{
if (llList2String(change_me, 0) == "M")
{
Readout_Say("MENU " + llGetSubString(llList2String(change_me, 1), 0, -2));
}
else if (llList2String(change_me, 0) == "T")
{
Readout_Say("TOMENU " + llGetSubString(llList2String(change_me, 1), 0, -2));
}
else if (llList2String(change_me, 0) == "B")
{
list l = [llList2String(change_me, 1), strReplace(strReplace(llList2String(DATA_LIST, i), "90200", ""), SEP, "|")];
if (llList2String(l, 1) == "")
{
l = llList2List(l, 0, 0);
}
string end = llDumpList2String(l, "|");
Readout_Say("BUTTON " + end);
}
}
}
llMessageLinked(LINK_THIS, 90020, "0", prop_script); // Dump prop settings
return;
}
else if (msg == "[SAVE]" && id == llGetOwner())
{
llMessageLinked(LINK_SET, 90101, "0|" + msg, ""); // Menu choice notification
options_menu();
return;
}
else if (msg == "[CLEAR]")
{
Out(0, "Props have been cleared!");
llMessageLinked(LINK_THIS, 90200, "", ""); // Clear props
}
else if (msg == "[RESET]")
{
llMessageLinked(LINK_THIS, 90200, "", ""); // Clear props
llSleep(1);
llResetOtherScript(prop_script);
llResetScript();
return;
}
else if (msg == "[BACK]")
{
}
else if (msg == "[OWNER]")
{
if (id == llGetOwner())
{
options_menu();
return;
}
else
{
llRegionSayTo(id, 0, "Sorry, only the owner can use this.");
}
}
else if (id == llGetOwner() && llListFindList(MENUCONTROL_TYPES, [msg]) != -1)
{
MENUCONTROL_INDEX = llListFindList(MENUCONTROL_TYPES, [msg]);
Out(0, "Menu access set to: " + llList2String(MENUCONTROL_TYPES, MENUCONTROL_INDEX));
if (llGetInventoryType(prop_script) == INVENTORY_SCRIPT)
{
options_menu();
return;
}
else
{
}
}
prop_menu(FALSE, id);
}
touch_start(integer touched)
{
if (MTYPE < 3)
{
menu_check(llDetectedName(0), llDetectedKey(0));
}
}
changed(integer change)
{
if (change & CHANGED_INVENTORY)
{
if (llGetInventoryKey(notecard_name) != notecard_key)
{
llResetScript();
}
check_avsit();
}
}
link_message(integer sender, integer num, string msg, key id)
{
if (sender == llGetLinkNumber())
{
if (num == 90005) // send menu to id
{
menu_check(llKey2Name(id), id);
}
else if (num == 90022) // send dump to [AV]adjuster
{
Readout_Say(msg);
}
else if (num == 90021) // end of dump
{
Readout_Say("");
Readout_Say("--✄--COPY ABOVE INTO \"AVpos\" NOTECARD--✄--");
Readout_Say("");
}
}
}
dataserver(key query_id, string data)
{
if (query_id == notecard_query)
{
if (data == EOF)
{
Out(0, (string)llGetListLength(MENU_LIST) + " menu items Ready, Memory: " + (string)llGetFreeMemory());
llPassTouches(FALSE);
if (MTYPE == 3)
{
llPassTouches(TRUE);
}
}
else
{
data = llGetSubString(data, llSubStringIndex(data, "◆") + 1, 99999);
data = llStringTrim(data, STRING_TRIM);
string command = llGetSubString(data, 0, llSubStringIndex(data, " ") - 1);
list parts = llParseStringKeepNulls(llGetSubString(data, llSubStringIndex(data, " ") + 1, 99999), [" | ", " |", "| ", "|"], []);
string part0 = llStringTrim(llList2String(parts, 0), STRING_TRIM);
string part1 = llList2String(parts, 1);
if (llGetListLength(parts) > 1)
{
part1 = llStringTrim(llDumpList2String(llList2List(parts, 1, 99999), SEP), STRING_TRIM);
}
if (command == "TEXT")
{
custom_text = strReplace(part0, "\\n", "\n");
}
part0 = llGetSubString(part0, 0, 22);
if (command == "MENU")
{
MENU_LIST += ["M:" + part0 + "*"];
DATA_LIST += "";
}
else if (command == "TOMENU")
{
MENU_LIST += ["T:" + part0 + "*"];
DATA_LIST += "";
}
else if (command == "BUTTON")
{
MENU_LIST += ["B:" + part0];
if (part1 == "")
{
part1 = "90200";
}
DATA_LIST += part1;
}
else if (command == "MTYPE")
{
MTYPE = (integer)part0;
}
notecard_query = llGetNotecardLine(notecard_name, ++notecard_line);
}
}
}
}