/////////////////////////////////////////////////////////////////////////// // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 // /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// // 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: GNU GPLv3 // /////////////////////////////////////////////////////////////////////////// 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) 2011 Wizardry and Steamworks - License: GNU GPLv3 // /////////////////////////////////////////////////////////////////////////// vector wasCirclePoint(float radius) { float x = llPow(-1, 1 + (integer) llFrand(2)) * llFrand(radius*2); float y = llPow(-1, 1 + (integer) llFrand(2)) * llFrand(radius*2); if(llPow(x,2) + llPow(y,2) <= llPow(radius,2)) return ; return wasCirclePoint(radius); } /////////////////////////////////////////////////////////////////////////// // Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // /////////////////////////////////////////////////////////////////////////// integer wasIsAvatarInSensorRange(key avatar) { return llListFindList( llGetAgentList( AGENT_LIST_REGION, [] ), (list)((key)avatar) ) != -1 && llVecDist( llGetPos(), llList2Vector( llGetObjectDetails( avatar, [OBJECT_POS] ), 0 ) ) <= 96; } /////////////////////////////////////////////////////////////////////////// // Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 // /////////////////////////////////////////////////////////////////////////// // 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; } // corrade data string CORRADE = ""; string GROUP = ""; string PASSWORD = ""; integer RANGE = 5; // for holding the callback URL string callback = ""; // for notecard reading integer line = 0; // key-value data will be read into this list list tuples = []; default { state_entry() { // set color for button llSetColor(<1,1,0>, ALL_SIDES); if(llGetInventoryType("configuration") != INVENTORY_NOTECARD) { llOwnerSay("Sorry, could not find an inventory notecard."); return; } // DEBUG llOwnerSay("Reading configuration file..."); llGetNotecardLine("configuration", line); } dataserver(key id, string data) { if(data == EOF) { // invariant, length(tuples) % 2 == 0 if(llGetListLength(tuples) % 2 != 0) { llOwnerSay("Error in configuration notecard."); return; } CORRADE = llList2String( tuples, llListFindList( tuples, [ "corrade" ] ) +1); if(CORRADE == "") { llOwnerSay("Error in configuration notecard: corrade"); return; } GROUP = llList2String( tuples, llListFindList( tuples, [ "group" ] ) +1); if(GROUP == "") { llOwnerSay("Error in configuration notecard: password"); return; } PASSWORD = llList2String( tuples, llListFindList( tuples, [ "password" ] ) +1); if(GROUP == "") { llOwnerSay("Error in configuration notecard: group"); return; } RANGE = llList2Integer( tuples, llListFindList( tuples, [ "range" ] ) +1); if(RANGE == 0) { llOwnerSay("Error in configuration notecard: range"); return; } // DEBUG llOwnerSay("Read configuration file..."); state url; } if(data == "") jump continue; integer i = llSubStringIndex(data, "#"); if(i != -1) data = llDeleteSubString(data, i, -1); list o = llParseString2List(data, ["="], []); // get rid of starting and ending quotes string k = llDumpList2String( llParseString2List( llStringTrim( llList2String( o, 0 ), STRING_TRIM), ["\""], [] ), "\""); string v = llDumpList2String( llParseString2List( llStringTrim( llList2String( o, 1 ), STRING_TRIM), ["\""], [] ), "\""); if(k == "" || v == "") jump continue; tuples += k; tuples += v; @continue; llGetNotecardLine("configuration", ++line); } on_rez(integer num) { llResetScript(); } changed(integer change) { if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) { llResetScript(); } } } state url { state_entry() { // DEBUG llOwnerSay("Requesting URL..."); llRequestURL(); } http_request(key id, string method, string body) { if(method != URL_REQUEST_GRANTED) return; callback = body; // DEBUG llOwnerSay("Got URL..."); state off; } on_rez(integer num) { llResetScript(); } changed(integer change) { if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) { llResetScript(); } } } state off { state_entry() { // set color for button llSetColor(<1,0,0>, ALL_SIDES); } touch_start(integer num) { state on; } on_rez(integer num) { llResetScript(); } changed(integer change) { if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) { llResetScript(); } } } state on { state_entry() { // set color for button llSetColor(<0,1,0>, ALL_SIDES); // if Corrade is in-range then just follow if(wasIsAvatarInSensorRange(CORRADE)) { state follow; } // DEBUG llOwnerSay("Detecting if Corrade is online..."); llSetTimerEvent(5); } timer() { llRequestAgentData(CORRADE, DATA_ONLINE); } dataserver(key id, string data) { if(data != "1") { // DEBUG llOwnerSay("Corrade is not online, sleeping..."); llSetTimerEvent(30); return; } llSensorRepeat("", CORRADE, AGENT, RANGE, TWO_PI, 5); } no_sensor() { // DEBUG llOwnerSay("Teleporting Corrade..."); llInstantMessage(CORRADE, wasKeyValueEncode( [ "command", "teleport", "group", wasURLEscape(GROUP), "password", wasURLEscape(PASSWORD), "entity", "region", "region", wasURLEscape(llGetRegionName()), "position", llGetPos() + wasCirclePoint((integer)RANGE), "callback", callback ] ) ); } sensor(integer num) { state follow; } http_request(key id, string method, string body) { llHTTPResponse(id, 200, "OK"); if(wasKeyValueGet("command", body) == "teleport") { integer success = wasKeyValueGet("success", body) == "True"; if(success) { // DEBUG llOwnerSay("Teleport succeeded..."); state follow; } // DEBUG llOwnerSay("Teleport failed..."); return; } } on_rez(integer num) { llResetScript(); } changed(integer change) { if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) { llResetScript(); } } state_exit() { llSetTimerEvent(0); } } state follow { state_entry() { // DEBUG llOwnerSay("In follow state..."); // check every second whether Corrade is online llRequestAgentData(CORRADE, DATA_ONLINE); } touch_start(integer num) { state off; } dataserver(key id, string data) { // if Corrade is not online if(data != "1") state on; // Corrade is online, so attempt to dectect llSensorRepeat("", CORRADE, AGENT, RANGE, TWO_PI, 1); } no_sensor() { // check if Corrade is in range, and if not, start detecting if(!wasIsAvatarInSensorRange(CORRADE)) { state on; } // Corrade is in sensor range, so execute move. llInstantMessage(CORRADE, wasKeyValueEncode( [ "command", "walkto", "group", wasURLEscape(GROUP), "password", wasURLEscape(PASSWORD), // move in a radius around the current primitive. "position", llGetPos() + wasCirclePoint((integer)RANGE), "vicinity", 1, "timeout", 2500 ] ) ); llSensorRepeat("", CORRADE, AGENT, (integer)RANGE, TWO_PI, 1); llRequestAgentData(CORRADE, DATA_ONLINE); } on_rez(integer num) { llResetScript(); } changed(integer change) { if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) { llResetScript(); } } }