Work in progress.
/////////////////////////////////////////////////////////////////////////// // Copyright (C) Wizardry and Steamworks 2016 - License: CC BY 2.0 // /////////////////////////////////////////////////////////////////////////// // // This script automates the TWI Wolf Avatar with Corrade. // // For more information on Corrade, please see: // http://grimore.org/secondlife/scripted_agents/corrade // /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// // LIBRARY INCLUDES // /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// // Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 // /////////////////////////////////////////////////////////////////////////// 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) 2013 Wizardry and Steamworks - License: CC BY 2.0 // /////////////////////////////////////////////////////////////////////////// string wasKeyValueEncode(list data) { list k = llList2ListStrided(data, 0, -1, 2); list v = llList2ListStrided(llDeleteSubList(data, 0, 0), 0, -1, 2); data = []; do { data += llList2String(k, 0) + "=" + llList2String(v, 0); k = llDeleteSubList(k, 0, 0); v = llDeleteSubList(v, 0, 0); } while(llGetListLength(k) != 0); return llDumpList2String(data, "&"); } /////////////////////////////////////////////////////////////////////////// // Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 // /////////////////////////////////////////////////////////////////////////// // escapes a string in conformance with RFC1738 string wasURLEscape(string i) { string o = ""; do { string c = llGetSubString(i, 0, 0); i = llDeleteSubString(i, 0, 0); if(c == "") jump continue; if(c == " ") { o += "+"; jump continue; } if(c == "\n") { o += "%0D" + llEscapeURL(c); jump continue; } o += llEscapeURL(c); @continue; } while(i != ""); return o; } /////////////////////////////////////////////////////////////////////////// // Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 // /////////////////////////////////////////////////////////////////////////// list wasCSVToList(string csv) { list l = []; list s = []; string m = ""; do { string a = llGetSubString(csv, 0, 0); csv = llDeleteSubString(csv, 0, 0); if(a == ",") { if(llList2String(s, -1) != "\"") { l += m; m = ""; jump continue; } m += a; jump continue; } if(a == "\"" && llGetSubString(csv, 0, 0) == a) { m += a; csv = llDeleteSubString(csv, 0, 0); jump continue; } if(a == "\"") { if(llList2String(s, -1) != a) { s += a; jump continue; } s = llDeleteSubList(s, -1, -1); jump continue; } m += a; @continue; } while(csv != ""); // postcondition: length(s) = 0 return l + m; } /////////////////////////////////////////////////////////////////////////// // Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 // /////////////////////////////////////////////////////////////////////////// // unescapes a string in conformance with RFC1738 string wasURLUnescape(string i) { return llUnescapeURL( llDumpList2String( llParseString2List( llDumpList2String( llParseString2List( i, ["+"], [] ), " " ), ["%0D%0A"], [] ), "\n" ) ); } /////////////////////////////////////////////////////////////////////////// // 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); } /////////////////////////////////////////////////////////////////////////// // CONSTANTS // /////////////////////////////////////////////////////////////////////////// list WOLF_ACTIONS = [ "bark", "bark2", "bark3", "bark4", "bark5", "bite", "biteR", "bouncin", "dance", "dig", "drink", "facepaw", "fidget", "flop", "growl", "growlp", "headtilt", "howl", "parthowl", "laypaws", "layside", "laysideR", "licknose", "pant", "pawat", "pawatR", "pawbeat", "play", "prance", "roll", "scratch", "scratchR", "shake", "sit", "sleepbelly", "sleepcurl", "sleepcurlR", "sleepside", "sleepsideR", "sniff", "stretch", "submit", "survey", "surveyR", "tailchase", "tailchaseR", "whine" ]; list MAIN_MENU = [ "Actions", "Idle" ]; string MENU_TEXT = "Wizardry and Steamworks TMI Wolf Avatar Animator!\n\nˁ˚ᴥ˚ˀ\n\nCurrent Settings:\n\nIdle Animations: %idle%\n\nPlease select an option."; list MENU_TOGGLES = [ "idle" ]; list MENU_TOGGLES_STATE = [ FALSE ]; /////////////////////////////////////////////////////////////////////////// // LOCAL FUNCTIONS // /////////////////////////////////////////////////////////////////////////// string menuText() { list menuText = llParseString2List(MENU_TEXT, ["%"], []); string replace = ""; do { string first = llList2String(menuText, 0); menuText = llDeleteSubList(menuText, 0, 0); if(llSubStringIndex(first, " ") != -1) { replace += first; jump continue; } integer i = llListFindList(MENU_TOGGLES, [ first ]); integer toggleState = llList2Integer(MENU_TOGGLES_STATE, i); if(toggleState) { replace += "ON"; jump continue; } replace += "OFF"; @continue; } while(llGetListLength(menuText) != 0); return replace; } /////////////////////////////////////////////////////////////////////////// // INTERNALS // /////////////////////////////////////////////////////////////////////////// // callback URL string callback = ""; // configuration data string configuration = ""; // menu channel integer menuChannel = -1; integer channelHandle = -1; integer menuTop = 0; default { state_entry() { // Generate channel. menuChannel = ((integer)("0x"+llGetSubString((string)llGetKey(),-8,-1)) & 0x3FFFFFFF) ^ 0xBFFFFFFF; // Start polling for configuration. llSetTimerEvent(1); } link_message(integer sender, integer num, string message, key id) { if(id != "configuration") return; configuration = message; state main_menu; } timer() { llMessageLinked(LINK_THIS, 0, "configuration", NULL_KEY); } attach(key id) { llResetScript(); } on_rez(integer num) { llResetScript(); } changed(integer change) { if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START) || (change & CHANGED_OWNER) || (change & CHANGED_REGION)) { llResetScript(); } } state_exit() { llSetTimerEvent(0); } } state main_menu { state_entry() { // If this is not a top button press, stop right here. if(menuTop != TRUE) return; menuTop = FALSE; // Coming from a top menu action so send the menu. llListenRemove(channelHandle); channelHandle = llListen(menuChannel, "", llGetOwner(), ""); llDialog( llGetOwner(), menuText(), wasDialogMenu( MAIN_MENU, [], "" ), menuChannel ); } touch_start(integer num) { llListenRemove(channelHandle); channelHandle = llListen(menuChannel, "", llGetOwner(), ""); llDialog( llGetOwner(), menuText(), wasDialogMenu( MAIN_MENU, [], "" ), menuChannel ); } listen(integer channel, string name, key id, string message) { // Process menu operations. if(message == "Actions") { state wolf_actions; } // Process buttons. if(message == "Idle") { // Toggle the state. integer i = llListFindList(MENU_TOGGLES,[ "idle" ]); integer s = !llList2Integer(MENU_TOGGLES_STATE, i); MENU_TOGGLES_STATE = llListReplaceList( MENU_TOGGLES_STATE, [ s ], i, i ); message = "idle off"; if(s) message = "idle off"; // Send the message. llInstantMessage((key)wasKeyValueGet( "corrade", configuration ), wasKeyValueEncode( [ "command", "tell", "group", wasURLEscape( wasKeyValueGet( "group", configuration ) ), "password", wasURLEscape( wasKeyValueGet( "password", configuration ) ), "message", wasURLEscape(message), "entity", "local", "type", "Normal", "channel", wasKeyValueGet( "wolf channel", configuration ) ] ) ); jump remenuMain; } @remenuMain; // Re-menu. llDialog( llGetOwner(), menuText(), wasDialogMenu( MAIN_MENU, [], "" ), menuChannel ); } attach(key id) { llResetScript(); } on_rez(integer num) { llResetScript(); } changed(integer change) { if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START) || (change & CHANGED_OWNER) || (change & CHANGED_REGION)) { llResetScript(); } } state_exit() { llListenRemove(channelHandle); } } state wolf_actions { state_entry() { llListenRemove(channelHandle); channelHandle = llListen(menuChannel, "", llGetOwner(), ""); llDialog( llGetOwner(), menuText(), wasDialogMenu( WOLF_ACTIONS, [ "⟵ Back", "↑ Top", "Next ⟶" ], "" ), menuChannel ); } touch_start(integer num) { llListenRemove(channelHandle); channelHandle = llListen(menuChannel, "", llGetOwner(), ""); llDialog( llGetOwner(), menuText(), wasDialogMenu( WOLF_ACTIONS, [ "⟵ Back", "↑ Top", "Next ⟶" ], "" ), menuChannel ); } listen(integer channel, string name, key id, string message) { // Process menu operations. if(message == "⟵ Back") { llDialog( llGetOwner(), menuText(), wasDialogMenu( WOLF_ACTIONS, [ "⟵ Back", "↑ Top", "Next ⟶" ], "<" ), menuChannel ); return; } if(message == "Next ⟶") { llDialog( llGetOwner(), menuText(), wasDialogMenu( WOLF_ACTIONS, [ "⟵ Back", "↑ Top", "Next ⟶" ], ">" ), channel ); return; } if(message == "↑ Top") { menuTop = TRUE; state main_menu; return; } // Send the message. llInstantMessage((key)wasKeyValueGet( "corrade", configuration ), wasKeyValueEncode( [ "command", "tell", "group", wasURLEscape( wasKeyValueGet( "group", configuration ) ), "password", wasURLEscape( wasKeyValueGet( "password", configuration ) ), "message", wasURLEscape(message), "entity", "local", "type", "Normal", "channel", wasKeyValueGet( "wolf channel", configuration ) ] ) ); // Re-menu. llDialog( llGetOwner(), menuText(), wasDialogMenu( WOLF_ACTIONS, [ "⟵ Back", "↑ Top", "Next ⟶" ], "" ), menuChannel ); } attach(key id) { llResetScript(); } on_rez(integer num) { llResetScript(); } changed(integer change) { if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START) || (change & CHANGED_OWNER) || (change & CHANGED_REGION)) { llResetScript(); } } state_exit() { llListenRemove(channelHandle); } }