The new versioning scheme uses a letter instead of the dash. The letter is a p for a work-in-progress/pre-release version, or an r for a release version. For example: 2.2p04 - work in progress for version 2.2r04 2.2r04 - release version (2.2-04 with the previous scheme) The two digit number will be increasing even for minor changes. The last minor changes indicator is dropped.
640 lines
20 KiB
Text
640 lines
20 KiB
Text
/*
|
||
* [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.2p04";
|
||
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 = [custom_text]; //OSS::list MENU_LIST; // Force error in LSO
|
||
list DATA_LIST;
|
||
integer MTYPE;
|
||
integer LMSOURCE = 0; //lmsource self = 0, lmsource linkset = 1
|
||
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 = "\x7F";
|
||
|
||
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:") == 0)
|
||
{
|
||
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!");
|
||
}
|
||
MENU_LIST = [];
|
||
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() || LMSOURCE == 1)
|
||
{
|
||
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;
|
||
}
|
||
else if (command == "LMSOURCE")
|
||
{
|
||
LMSOURCE = (integer)part0;
|
||
}
|
||
notecard_query = llGetNotecardLine(notecard_name, ++notecard_line);
|
||
}
|
||
}
|
||
}
|
||
}
|