Difference between revisions of "Chatbot"
(Point to the Talk:Chatbot discussion of arithmetic operators, etc.) |
(add llSetLocalRot llApplyImpulse llApplyRotationalImpulse to demo kicking a translucent, bouncing, spinning box) |
||
Line 3: | Line 3: | ||
Created by [[User:Ppaatt Lynagh|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. | Created by [[User:Ppaatt Lynagh|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: | The LSL commands you can chat to this script include the following demo of kicking a translucent, bouncing, spinning box: | ||
<pre> | <pre> | ||
/7 llSetColor(<0.3, 0.3, 0.3>, ALL_SIDES); | /7 llSetColor(<0.3, 0.3, 0.3>, ALL_SIDES); | ||
/7 llSetColor(<1, 1, 1>, ALL_SIDES); | /7 llSetColor(<1, 1, 1>, ALL_SIDES); | ||
/7 llSetAlpha(0.6, ALL_SIDES); | |||
</pre> | </pre> | ||
<pre> | <pre> | ||
/7 llSetText("look at me green", <0.0, 1.0, 0.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("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 llSetStatus(STATUS_PHYSICS, FALSE); | |||
/7 llSetLocalRot(llEuler2Rot(<0.0, 0.0, PI_BY_TWO>)); | |||
/7 llSetLocalRot(llEuler2Rot(ZERO_ROTATION)); | |||
/7 llSetStatus(STATUS_PHYSICS, TRUE); | |||
/7 llSetBuoyancy(0.9); | |||
/7 llApplyImpulse(<0.0, 0.0, 1.0>, TRUE); // advance along the Z axis | |||
/7 llApplyRotationalImpulse(<0.0, 0.0, 3.0>, TRUE); // yaw about the Z axis | |||
/7 llSetStatus(STATUS_PHYSICS, FALSE); llSetStatus(STATUS_PHYSICS, TRUE); // zero rot inertia | |||
</pre> | </pre> | ||
Line 22: | Line 33: | ||
</pre> | </pre> | ||
The | The commands shown above work well in the order shown with a 0.5 x 0.5 x 0.5 wood box such as the default object of the SL GUI. Sending these commands to other objects will show you how the effects of some commands that require STATUS_PHYICS TRUE do vary greatly with the mass of the object. | ||
To keep this sample script conveniently short, this script 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, even prefix (not infix) function names like the "*" multiply operator. Adding the list data type as a possible parameter type might require real work: LSL doesn't understand lists of lists yet. | To keep this sample script conveniently short, this script 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, even prefix (not infix) function names like the "*" multiply operator. Adding the list data type as a possible parameter type might require real work: LSL doesn't understand lists of lists yet. | ||
Line 29: | Line 40: | ||
Enjoy, | Enjoy, | ||
P.S. The code of the sample script follows: | |||
<pre> | <pre> | ||
Line 36: | Line 49: | ||
// faster than you can thru the 2007-08 SL GUI. | // faster than you can thru the 2007-08 SL GUI. | ||
// | // | ||
// Choose the channel | |||
integer theChannel = 7; | integer theChannel = 7; | ||
// Describe the language of an LSL expression without infix operators | |||
string escape = "\\"; | |||
string quote = "\""; | |||
list separators = ["\t", " ", ",", ";"]; | |||
list spacers = [quote, escape, "(", ")", "/", "<", ">", "[", "]"]; | |||
// Evaluate any one parameter | // Evaluate any one parameter | ||
Line 57: | Line 79: | ||
ZERO_VECTOR, | ZERO_VECTOR, | ||
ZERO_ROTATION, | ZERO_ROTATION, | ||
STATUS_PHYSICS, | |||
0 | 0 | ||
Line 77: | Line 101: | ||
"ZERO_VECTOR", | "ZERO_VECTOR", | ||
"ZERO_ROTATION", | "ZERO_ROTATION", | ||
"STATUS_PHYSICS", | |||
"0" | "0" | ||
Line 101: | Line 127: | ||
} | } | ||
} | } | ||
} | } | ||
Line 126: | Line 140: | ||
// Or fall thru to say callable found and no result collected | // Or fall thru to say callable found and no result collected | ||
if ("llGetAgentSize" == callable) // id | if ("llApplyImpulse" == callable) // force, local | ||
{ | |||
llApplyImpulse(llList2Vector(ps, 0), llList2Integer(ps, 1)); | |||
} | |||
else if ("llApplyRotationalImpulse" == callable) // force, local | |||
{ | |||
llApplyRotationalImpulse(llList2Vector(ps, 0), llList2Integer(ps, 1)); | |||
} | |||
else if ("llEuler2Rot" == callable) // v | |||
{ | |||
return [llEuler2Rot(llList2Vector(ps, 0))]; | |||
} | |||
else if ("llGetAgentSize" == callable) // id | |||
{ | { | ||
return [llGetAgentSize(llList2Key(ps, 0))]; | return [llGetAgentSize(llList2Key(ps, 0))]; | ||
Line 134: | Line 160: | ||
return [llGetLinkKey(llList2Integer(ps, 0))]; | return [llGetLinkKey(llList2Integer(ps, 0))]; | ||
} | } | ||
else if ("llGetNumberOfPrims" == callable) // | else if ("llGetNumberOfPrims" == callable) // | ||
{ | { | ||
return [llGetNumberOfPrims()]; | return [llGetNumberOfPrims()]; | ||
} | |||
else if ("llRot2Euler" == callable) // q | |||
{ | |||
return [llRot2Euler(llList2Rot(ps, 0))]; | |||
} | |||
else if ("llSetAlpha" == callable) // alpha, face | |||
{ | |||
llSetAlpha(llList2Float(ps, 0), llList2Integer(ps, 1)); | |||
} | |||
else if ("llSetBuoyancy" == callable) // buoyancy | |||
{ | |||
llSetBuoyancy(llList2Float(ps, 0)); | |||
} | } | ||
else if ("llSetColor" == callable) // color, face | else if ("llSetColor" == callable) // color, face | ||
{ | { | ||
llSetColor(llList2Vector(ps, 0), llList2Integer(ps, 1)); | llSetColor(llList2Vector(ps, 0), llList2Integer(ps, 1)); | ||
} | |||
else if ("llSetLocalRot" == callable) // rot | |||
{ | |||
llSetLocalRot(llList2Rot(ps, 0)); | |||
} | |||
else if ("llSetPos" == callable) // pos | |||
{ | |||
llSetPos(llList2Vector(ps, 0)); | |||
} | |||
else if ("llSetRot" == callable) // rot | |||
{ | |||
llSetRot(llList2Rot(ps, 0)); | |||
} | |||
else if ("llSetStatus" == callable) // status, value | |||
{ | |||
llSetStatus(llList2Integer(ps, 0), llList2Integer(ps, 1)); | |||
} | } | ||
else if ("llSetText" == callable) // text, color, alpha | else if ("llSetText" == callable) // text, color, alpha | ||
{ | { | ||
llSetText( | llSetText(llList2String(ps, 0), llList2Vector(ps, 1), llList2Float(ps, 2)); | ||
} | } | ||
Line 166: | Line 220: | ||
// Begin with almost nothing | // Begin with almost nothing | ||
list | list results = [""]; | ||
list depths = [0]; | list depths = [0]; | ||
Line 176: | Line 230: | ||
{ | { | ||
string word = llList2String(words, index); | string word = llList2String(words, index); | ||
integer beyond = llGetListLength( | integer beyond = llGetListLength(results); | ||
// Count | // Count results of "( ... )" or of "< ... >" | ||
if ((word == "(") || (word == "<")) | if ((word == "(") || (word == "<")) | ||
Line 199: | Line 253: | ||
if (first <= last) | if (first <= last) | ||
{ | { | ||
parameters = llList2List( | parameters = llList2List(results, first, last); | ||
results = llList2List(results, 0, first - 1); | |||
} | } | ||
Line 214: | Line 268: | ||
vec.y = llList2Float(parameters, 1); | vec.y = llList2Float(parameters, 1); | ||
vec.z = llList2Float(parameters, 2); | vec.z = llList2Float(parameters, 2); | ||
results += [vec]; | |||
} | } | ||
Line 226: | Line 280: | ||
rot.z = llList2Float(parameters, 2); | rot.z = llList2Float(parameters, 2); | ||
rot.s = llList2Float(parameters, 2); | rot.s = llList2Float(parameters, 2); | ||
results += [rot]; | |||
} | } | ||
} | } | ||
Line 234: | Line 288: | ||
else if (word == ")") | else if (word == ")") | ||
{ | { | ||
string callable = llList2String( | string callable = llList2String(results, -1); | ||
results = llList2List(results, 0, -2); // pop the tail | |||
results += resultOf(callable, parameters); | |||
} | } | ||
} | } | ||
Line 248: | Line 298: | ||
else | else | ||
{ | { | ||
if (llGetSubString(word, 0, 0) == quote) | |||
{ | |||
if (word == (quote + quote)) | |||
{ | |||
results += ""; | |||
} | |||
else | |||
{ | |||
results += llGetSubString(word, 1, -2); | |||
} | |||
} | |||
else | |||
{ | |||
results += word; | |||
} | |||
} | } | ||
} | } | ||
Line 254: | Line 318: | ||
// Succeed | // Succeed | ||
return llList2List( | return llList2List(results, 1, -1); // pop the head (only if head is not tail) | ||
} | } | ||
Line 296: | Line 325: | ||
// Return a list of the words of the command. | // Return a list of the words of the command. | ||
list valueEach( | list valueEach(list words) | ||
{ | { | ||
Line 302: | Line 331: | ||
list values = []; | list values = []; | ||
// Divide the command into words, but also shatter quotations | // Divide the command into words, but also shatter quotations | ||
integer lenWords = llGetListLength(words); | integer lenWords = llGetListLength(words); | ||
Line 373: | Line 394: | ||
return values; | return values; | ||
} | |||
// Divide a string into words, | |||
// by returning each of the spacers as a word | |||
// and then all the chars between as words. | |||
list separateEach(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; | |||
} | } | ||
Line 386: | Line 442: | ||
{ | { | ||
llOwnerSay(message); | llOwnerSay(message); | ||
list values = valueEach( | |||
list words = separateEach(message, separators + spacers); | |||
// llOwnerSay(llList2CSV(words)); | |||
list values = valueEach(words); | |||
// llOwnerSay(llList2CSV(values)); | // llOwnerSay(llList2CSV(values)); | ||
list results = callEach(values); | list results = callEach(values); | ||
if (results != []) | if (results != []) |
Revision as of 21:02, 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 to this script include the following demo of kicking a translucent, bouncing, spinning box:
/7 llSetColor(<0.3, 0.3, 0.3>, ALL_SIDES); /7 llSetColor(<1, 1, 1>, ALL_SIDES); /7 llSetAlpha(0.6, ALL_SIDES);
/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 llSetStatus(STATUS_PHYSICS, FALSE); /7 llSetLocalRot(llEuler2Rot(<0.0, 0.0, PI_BY_TWO>)); /7 llSetLocalRot(llEuler2Rot(ZERO_ROTATION)); /7 llSetStatus(STATUS_PHYSICS, TRUE); /7 llSetBuoyancy(0.9); /7 llApplyImpulse(<0.0, 0.0, 1.0>, TRUE); // advance along the Z axis /7 llApplyRotationalImpulse(<0.0, 0.0, 3.0>, TRUE); // yaw about the Z axis /7 llSetStatus(STATUS_PHYSICS, FALSE); llSetStatus(STATUS_PHYSICS, TRUE); // zero rot inertia
/7 ZERO_VECTOR /7 llGetAgentSize(llGetLinkKey(llGetNumberOfPrims())) // often not ZERO_VECTOR while avatar sits
The commands shown above work well in the order shown with a 0.5 x 0.5 x 0.5 wood box such as the default object of the SL GUI. Sending these commands to other objects will show you how the effects of some commands that require STATUS_PHYICS TRUE do vary greatly with the mass of the object.
To keep this sample script conveniently short, this script 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, even prefix (not infix) function names like the "*" multiply operator. Adding the list data type as a possible parameter type might require real work: LSL doesn't understand lists of lists yet.
See the discussion tab for talk of more example command lines that we could/ should soon add.
Enjoy,
P.S. The code of the sample script follows:
// 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. // // Choose the channel integer theChannel = 7; // Describe the language of an LSL expression without infix operators string escape = "\\"; string quote = "\""; list separators = ["\t", " ", ",", ";"]; list spacers = [quote, escape, "(", ")", "/", "<", ">", "[", "]"]; // 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, STATUS_PHYSICS, 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", "STATUS_PHYSICS", "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 } } } // 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 ("llApplyImpulse" == callable) // force, local { llApplyImpulse(llList2Vector(ps, 0), llList2Integer(ps, 1)); } else if ("llApplyRotationalImpulse" == callable) // force, local { llApplyRotationalImpulse(llList2Vector(ps, 0), llList2Integer(ps, 1)); } else if ("llEuler2Rot" == callable) // v { return [llEuler2Rot(llList2Vector(ps, 0))]; } else if ("llGetAgentSize" == callable) // id { return [llGetAgentSize(llList2Key(ps, 0))]; } else if ("llGetLinkKey" == callable) // linknum { return [llGetLinkKey(llList2Integer(ps, 0))]; } else if ("llGetNumberOfPrims" == callable) // { return [llGetNumberOfPrims()]; } else if ("llRot2Euler" == callable) // q { return [llRot2Euler(llList2Rot(ps, 0))]; } else if ("llSetAlpha" == callable) // alpha, face { llSetAlpha(llList2Float(ps, 0), llList2Integer(ps, 1)); } else if ("llSetBuoyancy" == callable) // buoyancy { llSetBuoyancy(llList2Float(ps, 0)); } else if ("llSetColor" == callable) // color, face { llSetColor(llList2Vector(ps, 0), llList2Integer(ps, 1)); } else if ("llSetLocalRot" == callable) // rot { llSetLocalRot(llList2Rot(ps, 0)); } else if ("llSetPos" == callable) // pos { llSetPos(llList2Vector(ps, 0)); } else if ("llSetRot" == callable) // rot { llSetRot(llList2Rot(ps, 0)); } else if ("llSetStatus" == callable) // status, value { llSetStatus(llList2Integer(ps, 0), llList2Integer(ps, 1)); } else if ("llSetText" == callable) // text, color, alpha { llSetText(llList2String(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 results = [""]; 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(results); // Count results 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(results, first, last); results = llList2List(results, 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); results += [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); results += [rot]; } } // Call with a list of parameters (of non-list types) else if (word == ")") { string callable = llList2String(results, -1); results = llList2List(results, 0, -2); // pop the tail results += resultOf(callable, parameters); } } // Push any other word as a parameter else { if (llGetSubString(word, 0, 0) == quote) { if (word == (quote + quote)) { results += ""; } else { results += llGetSubString(word, 1, -2); } } else { results += word; } } } // Succeed return llList2List(results, 1, -1); // pop the head (only if head is not tail) } // 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(list words) { // Begin with nothing list values = []; // Divide the command into words, but also shatter quotations 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; } // Divide a string into words, // by returning each of the spacers as a word // and then all the chars between as words. list separateEach(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; } // 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 words = separateEach(message, separators + spacers); // llOwnerSay(llList2CSV(words)); list values = valueEach(words); // llOwnerSay(llList2CSV(values)); list results = callEach(values); if (results != []) { llOwnerSay(llList2CSV(results)); } } }