/////////////////////////////////////////////////////////////////////////// // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 // // Please see: http://www.gnu.org/licenses/gpl.html for legal details, // // rights of fair usage, the disclaimer and warranty conditions. // /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // /////////////////////////////////////////////////////////////////////////// integer wasMenuIndex = 0; list wasDialogMenu(list input, list actions, string direction) { integer cut = 11-wasListCountExclude(actions, [""]); if(direction == ">" && (wasMenuIndex+1)*cut+wasMenuIndex+1 < llGetListLength(input)) { ++wasMenuIndex; jump slice; } if(direction == "<" && wasMenuIndex-1 >= 0) { --wasMenuIndex; jump slice; } @slice; integer multiple = wasMenuIndex*cut; input = llList2List(input, multiple+wasMenuIndex, multiple+cut+wasMenuIndex); input = wasListMerge(input, actions, ""); return input; } /////////////////////////////////////////////////////////////////////////// // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // /////////////////////////////////////////////////////////////////////////// integer wasListCountExclude(list input, list exclude) { if(llGetListLength(input) == 0) return 0; if(llListFindList(exclude, (list)llList2String(input, 0)) == -1) return 1 + wasListCountExclude(llDeleteSubList(input, 0, 0), exclude); return wasListCountExclude(llDeleteSubList(input, 0, 0), exclude); } /////////////////////////////////////////////////////////////////////////// // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // /////////////////////////////////////////////////////////////////////////// list wasListMerge(list l, list m, string merge) { if(llGetListLength(l) == 0 && llGetListLength(m) == 0) return []; string a = llList2String(m, 0); if(a != merge) return [ a ] + wasListMerge(l, llDeleteSubList(m, 0, 0), merge); return [ llList2String(l, 0) ] + wasListMerge(llDeleteSubList(l, 0, 0), llDeleteSubList(m, 0, 0), merge); } /////////////////////////////////////////////////////////////////////////// // Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 // /////////////////////////////////////////////////////////////////////////// string wasKeyValueGet(string k, string data) { if(llStringLength(data) == 0) return ""; if(llStringLength(k) == 0) return ""; list a = llParseStringKeepNulls(data, ["&", "="], []); integer i = llListFindList(llList2ListStrided(a, 0, -1, 2), [ k ]); if(i != -1) return llList2String(a, 2*i+1); return ""; } /////////////////////////////////////////////////////////////////////////// // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // /////////////////////////////////////////////////////////////////////////// string wasKeyValueSet(string k, string v, string data) { if(llStringLength(k) == 0) return ""; if(llStringLength(v) == 0) return ""; if(llStringLength(data) == 0) return k + "=" + v; integer i = llListFindList( llList2ListStrided( llParseString2List(data, ["&", "="], []), 0, -1, 2 ), [ k ]); if(i != -1) return llDumpList2String( llListReplaceList( llParseString2List(data, ["&"], []), [ k + "=" + v ], i, i), "&"); return data + "&" + k + "=" + v; } /////////////////////////////////////////////////////////////////////////// // Copyright (C) 2011 Wizardry and Steamworks - License: GNU GPLv3 // /////////////////////////////////////////////////////////////////////////// integer wasAddNonEmptyStrings(list input) { if(input == []) return 0; integer a = llList2Integer(input , 0); if(a != 0) return 1 + wasAddNonEmptyStrings(llDeleteSubList(input, 0, 0)); return wasAddNonEmptyStrings(llDeleteSubList(input, 0, 0)); } // Ord() function, written by Pedro Oval, 2010-05-28 // Inlined by Wizardry and Steamworks integer Ord(string chr) { if (chr == "") return 0; string hex = llEscapeURL(chr); if (llGetSubString(hex, 0, 0) != "%") return llBase64ToInteger("AAAA" + llStringToBase64(llGetSubString(chr, 0, 0))); integer b = (integer)("0x" + llGetSubString(hex, 1, 2)); if (b < 194 || b > 244) return b; if (b < 224) return ((b & 0x1F) << 6) | (integer)("0x" + llGetSubString(hex, 4, 5)) & 0x3F; if (b < 240) return (b & 0x0F) << 12 + ((integer)("0x" + llGetSubString(hex, 4, 5)) & 0x3F) << 6 + (integer)("0x" + llGetSubString(hex, 7, 8)) & 0x3F; return (b & 0x07) << 18 + ((integer)("0x" + llGetSubString(hex, 4, 5)) & 0x3F) << 12 + ((integer)("0x" + llGetSubString(hex, 7, 8)) & 0x3F) << 6 + (integer)("0x" + llGetSubString(hex, 10, 11)) & 0x3F; } integer comHandle = 0; list menu = []; list view = []; string deleteCardText = "◯ DELETE"; integer deleteCard = 0; default { state_entry() { // allow users to drop items in the object llAllowInventoryDrop(TRUE); // get the delete setting. deleteCard = (integer)wasKeyValueGet("delete", llGetObjectDesc()); if(deleteCard) deleteCardText = "◉ DELETE"; // generate the menu list that will be displayed when // the user drops a notecard into the inventory. // // only the first 23 characters are considered due to // limitations imposed on the size of the dialog buttons integer i = llGetInventoryNumber(INVENTORY_NOTECARD)-1; integer count = 0; do { ++count; menu += llGetSubString(llGetInventoryName(INVENTORY_NOTECARD, i), 0, 23); } while(--i>-1); // get the read list from the object description string desc = wasKeyValueGet("read", llGetObjectDesc()); if(desc != "") view = llCSV2List(desc); i = count-llGetListLength(view); if(i <= 0) jump equal; do { view += 0; } while(--i>0); @equal; // sum up the read list and... integer read = wasAddNonEmptyStrings(view); // set the new read list if(view != []) { llSetObjectDesc( wasKeyValueSet( "read", llList2CSV(view), llGetObjectDesc() ) ); } // set the text llSetText( "Notes (Read / Total): " + (string)read + "/" + (string)llGetInventoryNumber(INVENTORY_NOTECARD), <1,1,1>, 1.0); } changed(integer change) { // if it is not an inventory change, ignore if(change & CHANGED_INVENTORY == 0) return; // delete any inventory item that is not // the current script and is not a notecard integer i = llGetInventoryNumber(INVENTORY_ALL)-1; integer cards = 0; do { string item = llGetInventoryName(INVENTORY_ALL, i); if(item == llGetScriptName()) jump continue; if(llGetInventoryType(item) == INVENTORY_NOTECARD) { ++cards; jump continue; } llRemoveInventory(item); @continue; } while(--i>-1); // get the read list from the object description view = llCSV2List( wasKeyValueGet("read", llGetObjectDesc()) ); // if a card was added, just reset the script if(cards > llGetListLength(view)) llResetScript(); // if a card was removed step through the list // of notecards and check the sequence of view i = 0; do { string name = llGetInventoryName(INVENTORY_NOTECARD, i); // compute ord-hash and index of the notecard name integer j = llStringLength(name)-1; integer o = 0; do { o += Ord(llGetSubString(name, j, j)) ^ j; } while(--j>-1); // check if the hash matches the one stored in view if(o != llList2Integer(view, i)) view = llDeleteSubList(view, i, i); } while(++i"), channel); return; } if(message == "⌠ Settings ⌡") { llDialog(id, "Please chose from the options below:\n\n-The delete option, when toggled, will delete the notecards after reading.\n", ["⏏ Exit", deleteCardText ], channel); return; } if(message == "◯ DELETE") { deleteCardText = "◉ DELETE"; deleteCard = 1; llSetObjectDesc( wasKeyValueSet( "delete", "1", llGetObjectDesc() ) ); llDialog(id, "Please browse the available notecards:\n", wasDialogMenu(menu, ["⟵ Back", "⌠ Settings ⌡", "Next ⟶"], ""), channel); return; } if(message == "◉ DELETE") { deleteCardText = "◯ DELETE"; deleteCard = 0; llSetObjectDesc( wasKeyValueSet( "delete", "0", llGetObjectDesc() ) ); llDialog(id, "Please browse the available notecards:\n", wasDialogMenu(menu, ["⟵ Back", "⌠ Settings ⌡", "Next ⟶"], ""), channel); return; } if(message == "⏏ Exit") { llDialog(id, "Please browse the available notecards:\n", wasDialogMenu(menu, ["⟵ Back", "⌠ Settings ⌡", "Next ⟶"], ""), channel); return; } // go through the inventory and match the first 23 characters of the // menu selection to the name of the notecard and send the notecard integer i = llGetInventoryNumber(INVENTORY_NOTECARD)-1; do { string card = llGetInventoryName(INVENTORY_NOTECARD, i); if(llGetSubString(message, 0, 23) != llGetSubString(card, 0, 23)) jump continue; // hand out the notecard llGiveInventory(id, card); // delete the card in case it was requested if(deleteCard) { integer x = llListFindList(menu, (list)llGetSubString(card, 0, 23)); menu = llDeleteSubList(menu, x, x); llRemoveInventory(card); } // get the read list from store view = llCSV2List( wasKeyValueGet("read", llGetObjectDesc()) ); // replace an ord-hash and index of the notecard name integer j = llStringLength(card)-1; integer o = 0; do { o += Ord(llGetSubString(card, j, j)) ^ j; } while(--j>-1); view = llListReplaceList(view, (list)o, i, i); // set the new read list llSetObjectDesc( wasKeyValueSet( "read", llList2CSV(view), llGetObjectDesc() ) ); // add-up the read notecards... integer read = wasAddNonEmptyStrings(view); // and set the text llSetText( "Notes (Read / Total): " + (string)read + "/" + (string)llGetInventoryNumber(INVENTORY_NOTECARD), <1,1,1>, 1.0); // finally, close the channel and abort llListenRemove(comHandle); return; @continue; } while(--i>-1); } }