Difference between revisions of "Chatbot"
(add llSetText "" example (and tweak cbList2String to strip quotes even when quotes enclose an empty string)) |
(add an example to show an expression with results, results as parameters, and comments (and tweak valueEach to discard comments)) |
||
Line 15: | Line 15: | ||
/7 llSetText("look at me black", <0.0, 0.0, 0.0>, 1.0); | /7 llSetText("look at me black", <0.0, 0.0, 0.0>, 1.0); | ||
/7 llSetText("", <0.0, 0.0, 0.0>, 1.0); | /7 llSetText("", <0.0, 0.0, 0.0>, 1.0); | ||
</pre> | |||
<pre> | |||
/7 ZERO_VECTOR | |||
/7 llGetAgentSize(llGetLinkKey(llGetNumberOfPrims())) // often not ZERO_VECTOR while avatar sits | |||
</pre> | </pre> | ||
Line 316: | Line 321: | ||
{ | { | ||
string word = llList2String(words, index++); | string word = llList2String(words, index++); | ||
// Join together the words of a quotation | // Join together the words of a quotation | ||
Line 336: | Line 341: | ||
} | } | ||
// | // Discard slash slash commentary | ||
else if ( | else if ((word == "/") && (llList2String(words, index) == "/")) | ||
{ | { | ||
values | return values; | ||
} | } | ||
Line 348: | Line 353: | ||
{ | { | ||
; | ; | ||
} | |||
// Keep spacers | |||
else if (0 <= llListFindList(spacers, [word])) | |||
{ | |||
values += word; | |||
} | } | ||
Revision as of 05:47, 5 September 2007
Compile and run the LSL you chat on a channel, faster than you can compile and run it thru the 2007-08 SL GUI.
Created by Ppaatt Lynagh. "Allow anyone to copy", "next owner can copy & modify", and "mark item for sale price L$0", are the rights on my copies of this script in SL, as of 2007-09-04.
The LSL commands you can chat include:
/7 llSetColor(<0.3, 0.3, 0.3>, ALL_SIDES); /7 llSetColor(<1, 1, 1>, ALL_SIDES);
/7 llSetText("look at me white", <1.0, 1.0, 1.0>, 1.0); /7 llSetText("look at me green", <0.0, 1.0, 0.0>, 1.0); /7 llSetText("look at me black", <0.0, 0.0, 0.0>, 1.0); /7 llSetText("", <0.0, 0.0, 0.0>, 1.0);
/7 ZERO_VECTOR /7 llGetAgentSize(llGetLinkKey(llGetNumberOfPrims())) // often not ZERO_VECTOR while avatar sits
The sample code follows.
To keep this sample code conveniently short, this example understands only a few library function names, a few code constant names, a few punctuation marks, and a few data types. I hope you find it easy to add the library function names and code constant names that interest you. Adding the list data type as a possible parameter type might require real work: LSL doesn't understand lists of lists yet. Adding arithmetic operators might require real work, but may be needed for experimenting with conversions between local and global coordinates.
I do think the SL GUI should let me toggle a switch to do this, i.e., to teach any prim of mine to listen to my LSL chat, for the sake of script folk like me who think by chatting. Meanwhile, I write code like this to let me chat commands to my prims anyhow. I wrote this sample just after walking thru the delightfully concise Getting Started with LSL tutorial, which only taught me to call llSetColor and llSetText. Likely I'll soon return here to add a few more example commands, after I learn some other fun commands.
Enjoy,
// http://wiki.secondlife.com/wiki/Chatbot // // Compile and run the Lsl you type on a channel, // faster than you can thru the 2007-08 SL GUI. // integer theChannel = 7; // Evaluate any one parameter list theCodes = [ TRUE, FALSE, PI, TWO_PI, PI_BY_TWO, DEG_TO_RAD, RAD_TO_DEG, SQRT2, NULL_KEY, ALL_SIDES, EOF, ZERO_VECTOR, ZERO_ROTATION, 0 ]; list theCodenames = [ "TRUE", "FALSE", "PI", "TWO_PI", "PI_BY_TWO", "DEG_TO_RAD", "RAD_TO_DEG", "SQRT2", "NULL_KEY", "ALL_SIDES", "EOF", "ZERO_VECTOR", "ZERO_ROTATION", "0" ]; list valueOf(string word) { if (0 <= llSubStringIndex(word, ".")) { return [(float) word]; } else if (0 <= llSubStringIndex("0123456789", llGetSubString(word, 0, 0))) { return [(integer) word]; } else { integer index = llListFindList(theCodenames, [word]); if (0 <= index) { return llList2List(theCodes, index, index); } else { return [word]; // unevaluated } } } // Strip the leading and trailing quotes from a quoted evaluated string string cbList2String(list parameters, integer index) { string chars = llList2String(parameters, index); if (llStringLength(chars) <= 2) { return ""; } return llGetSubString(chars, 1, -2); } // Pass the parameters to the named routine. // Return no result, one result, else the callable and parameters. list resultOf(string callable, list parameters) { list ps = parameters; // abbreviate "parameters" llOwnerSay(callable + "(" + llList2CSV(ps) + ")"); // Return one result to say callable found and one result collected // Or fall thru to say callable found and no result collected if ("llGetAgentSize" == callable) // id { return [llGetAgentSize(llList2Key(ps, 0))]; } else if ("llGetLinkKey" == callable) // linknum { return [llGetLinkKey(llList2Integer(ps, 0))]; } else if ("llGetNumberOfPrims" == callable) // linknum { return [llGetNumberOfPrims()]; } else if ("llSetColor" == callable) // color, face { llSetColor(llList2Vector(ps, 0), llList2Integer(ps, 1)); } else if ("llSetText" == callable) // text, color, alpha { llSetText(cbList2String(ps, 0), llList2Vector(ps, 1), llList2Float(ps, 2)); } // Return more than one result to say callable not found else { return [callable, "not found"]; } // Return no result to say callable found but no result return []; } // Obey one command. list callEach(list words) { // Begin with almost nothing list values = [""]; list depths = [0]; // Take each action in order integer lenWords = llGetListLength(words); integer index; for (index = 0; index < lenWords; ++index) { string word = llList2String(words, index); integer beyond = llGetListLength(values); // Count values of "( ... )" or of "< ... >" if ((word == "(") || (word == "<")) { depths += [beyond]; // push the depth } else if ((word == ")") || (word == ">")) { // Pop the depth integer first = llList2Integer(depths, -1); depths = llList2List(depths, 0, -2); // pop the tail // Pop the parameters integer last = beyond - 1; list parameters = []; if (first <= last) { parameters = llList2List(values, first, last); values = llList2List(values, 0, first - 1); } // Join together the components of a vector if (word == ">") { integer depth = llGetListLength(parameters); if (depth == 3) { vector vec; vec.x = llList2Float(parameters, 0); vec.y = llList2Float(parameters, 1); vec.z = llList2Float(parameters, 2); values += [vec]; } // Join together the components of a rotation if (depth == 4) { rotation rot; rot.x = llList2Float(parameters, 0); rot.y = llList2Float(parameters, 1); rot.z = llList2Float(parameters, 2); rot.s = llList2Float(parameters, 2); values += [rot]; } } // Call with a list of parameters (of non-list types) else if (word == ")") { string callable = llList2String(values, -1); values = llList2List(values, 0, -2); // pop the tail list results = resultOf(callable, parameters); if (llGetListLength(results) == 1) { values += results; } } } // Push any other word as a parameter else { values += word; } } // Succeed return llList2List(values, 1, -1); // pop the head (only if head is not tail) } // Divide a string into words, // by returning each of the spacers as a word // and then all the chars between as words. list split(string source, list spacers) { list outputs = [source]; // Pass never more than 8 spacers to llParseString2List integer lenSpacers = llGetListLength(spacers); integer ss; for (ss = 0; ss < lenSpacers; ss += 8) { list someSpacers = llList2List(spacers, ss, ss + 7); // Call llParseString2List again for each result so far list inputs = outputs; outputs = []; integer lenInputs = llGetListLength(inputs); integer ii; for (ii = 0; ii < lenInputs; ++ii) { string input = llList2String(inputs, ii); outputs += llParseString2List(input, [], someSpacers); } } // Succeed return outputs; } // Divide the command into words. // Take care to join together the words of each quotation. // Return a list of the words of the command. list valueEach(string chars) { // Begin with nothing list values = []; // Describe the language of an LSL expression without operators string escape = "\\"; string quote = "\""; list separators = ["\t", " ", ",", ";"]; list spacers = [quote, escape, "(", ")", "/", "<", ">", "[", "]"]; // Divide the command into words, but also shatter quotations list words = split(chars, separators + spacers); integer lenWords = llGetListLength(words); // Consider each word or word of a quotation integer index = 0; while (index < lenWords) { string word = llList2String(words, index++); // Join together the words of a quotation if (word == quote) { string value = ""; do { word = llList2String(words, index++); if (word != quote) { value += word; } } while ((word != quote) && (index < lenWords)); // Quote the quotation simply, so the quotation is never found in spacers values += quote + value + quote; } // Discard slash slash commentary else if ((word == "/") && (llList2String(words, index) == "/")) { return values; } // Discard separators else if ((0 <= llListFindList(separators, [word])) || (word == "")) { ; } // Keep spacers else if (0 <= llListFindList(spacers, [word])) { values += word; } // Evaluate other words else { values += valueOf(word); } } // Succeed return values; } // Hear and obey the chat of the owner at the channel. default { state_entry() { llListen(theChannel, "", llGetOwner(), ""); } listen(integer channel, string name, key id, string message) { llOwnerSay(message); list values = valueEach(message); // llOwnerSay(llList2CSV(values)); list results = callEach(values); if (results != []) { llOwnerSay(llList2CSV(results)); } } }