Creative Commons License Machine/Kiosk Script

The kiosk is actually just a general purpose state machine engine. The particular state machine that is used for the Creative Commons LIcense Machine is in the Creative Commons License Machine/Kiosk Program.
<lsl>
key gBlankTexture = "5748decc-f629-461c-9a36-a35a221fe21f"; // well known
float gTimeOut = 60.0;
integer gUserListener = 0;
integer gLicenseChannel = 0;
integer gLicenseListener = 0;
string gNode;
list gPath;
 // path of nodes, including current node at the end
key gUser = NULL_KEY;
 // user we are interacting with
 
list gParams;
 // user parameters, stride two: [ <key>, <value> ... ]
string gAltTrigger;
key gArgID;
string gArgMessage;
clearUse()
{
 gNode = "";
 gPath = [ ];
 gUser = NULL_KEY;
 gParams = [ ];
 
 clearNode();
}
clearNode()
{
 gTimeOut = 30.0;
}
cmdGo(string node)
{
 gNode = node;
 gPath += [ node ];
 llMessageLinked(LINK_THIS, 200, node, NULL_KEY);
 
 clearNode();
 runAnyAction("init", "", NULL_KEY, "");
 
 llSetTimerEvent(gTimeOut);
 if (gUserListener) { llListenRemove(gUserListener); }
 gUserListener = llListen(0, "", gUser, "");
}
cmdReset()
{
 clearUse();
 cmdGo("start");
}
cmdBack()
{
 integer n = llGetListLength(gPath);
 if (n >= 2) {
 string node = llList2String(gPath, -2);
 gPath = llDeleteSubList(gPath, -2, -1);
 cmdGo(node);
 }
 else {
 cmdReset();
 }
}
cmdUser(key id)
{
 gUser = id;
}
cmdAnyone()
{
 gUser = NULL_KEY;
}
cmdTimeOut(float t)
{
 gTimeOut = t;
}
cmdImage(string inv)
{
 llSetTexture(inv, 0);
}
cmdSay(string text)
{
 llSay(0, text);
}
cmdMovie() { }
cmdGive(string inv)
{
 llGiveInventory(gUser, inv);
}
cmdWebsite()
{
 llLoadURL(gUser, "View the Creative Commons web site.",
 "http://creativecommons.org/");
}
cmdSet(string param, string value)
{
 integer i = llListFindList(gParams, [ param ]);
 if (i < 0) {
 gParams += [ param, value ];
 }
 else {
 gParams = llListReplaceList(gParams, [ value ], i + 1, i + 1);
 }
}
cmdSetUser(string param)
{
 cmdSet(param, llKey2Name(gUser));
}
cmdSetNow(string param)
{
 cmdSet(param, llGetSubString(llGetDate(), 0, 3));
}
string
getParam(string param)
{
 integer i = llListFindList(gParams, [ param ]);
 if (i < 0) return "";
 
 return llList2String(gParams, i + 1);
}
cmdReport(string param, string prompt)
{
 string value = getParam(param);
 if (value == "") return;
 
 llSay(0, prompt + value);
}
cmdLicenseImage()
{
 string commercialUse = getParam("commercial-use");
 string modifications = getParam("modifications");
 
 string license = "by";
 if (commercialUse == "no") { license += "-nc"; }
 if (modifications == "no") { license += "-nd"; }
 if (modifications == "share") { license += "-sa"; }
 
 string imageName = "lic-" + license;
 cmdImage(imageName);
 
 cmdSet("image-key", llGetInventoryKey(imageName));
}
cmdLicenseRez()
{
 gLicenseChannel = 10000000 + (integer)llFrand(10000000.0);
 gLicenseListener = llListen(gLicenseChannel, "", NULL_KEY, "");
 
 rotation r = llEuler2Rot(<0, -45, 0> * DEG_TO_RAD) * llGetRot();
 
 llRezObject("badge",
 llGetPos() + <-0.358, 0.0, 0.523> * r,
 ZERO_VECTOR,
 r,
 gLicenseChannel);
}
finishLicense()
{
 llListenRemove(gLicenseListener);
 
 integer n = llGetListLength(gParams);
 integer i;
 for (i = 0; i < n; i += 2) {
 string param = llList2String(gParams, i);
 string value = llList2String(gParams, i + 1);
 llSay(gLicenseChannel, param + ":" + value);
 }
 
 rotation r = llEuler2Rot(<0, -45, 0> * DEG_TO_RAD) * llGetRot();
 vector p = llGetPos() + <-0.358, 0.0, 0.761> * r;
 llSay(gLicenseChannel, "done " + (string)p);
}
cmdLicenseDie()
{
 if (gLicenseChannel != 0) {
 llSay(gLicenseChannel, "die");
 gLicenseChannel = 0;
 }
}
executeStatement(string statement)
{
 integer sep = llSubStringIndex(statement, "|");
 string tail = "";
 if (sep >= 1) {
 tail = llGetSubString(statement, sep + 1, -1);
 statement = llGetSubString(statement, 0, sep - 1);
 }
 list parts = llParseString2List(statement, [ " " ], [ ]);
 if (tail != "") {
 parts += [ tail ];
 }
 
 string cmd = llList2String(parts, 0);
 string arg1 = llList2String(parts, 1);
 string arg2 = llList2String(parts, 2);
 if (arg2 == "*") { arg2 = gArgMessage; }
 
 if (cmd == "back") { cmdBack(); }
 else if (cmd == "go") { cmdGo(arg1); }
 else if (cmd == "reset") { cmdReset(); }
 else if (cmd == "user") { cmdUser(gArgID); }
 else if (cmd == "anyone") { cmdAnyone(); }
 else if (cmd == "timeout") { cmdTimeOut((float)arg1); }
 else if (cmd == "image") { cmdImage(arg1); }
 else if (cmd == "say") { cmdSay(arg1); }
 else if (cmd == "movie") { cmdMovie(); }
 else if (cmd == "give") { cmdGive(arg1); }
 else if (cmd == "website") { cmdWebsite(); }
 else if (cmd == "set") { cmdSet(arg1, arg2); }
 else if (cmd == "setUser") { cmdSetUser(arg1); }
 else if (cmd == "setNow") { cmdSetNow(arg1); }
 else if (cmd == "report") { cmdReport(arg1, arg2); }
 else if (cmd == "licenseImage") { cmdLicenseImage(); }
 else if (cmd == "licenseRez") { cmdLicenseRez(); }
 else if (cmd == "licenseDie") { cmdLicenseDie(); }
 else {
 llOwnerSay("Unknown command: " + cmd);
 }
}
executeBlock(string block)
{
 list statements = llParseString2List(block,
 [ " ; ", " ;", "; ", ";" ], [ ]);
 integer n = llGetListLength(statements);
 integer i;
 for (i = 0; i < n; ++i) {
 executeStatement(llList2String(statements, i));
 }
}
runAnyAction(string trigger, string altTrigger, key id, string message)
{
 gAltTrigger = altTrigger;
 gArgID = id;
 gArgMessage = message;
 
 llMessageLinked(LINK_THIS, 300, trigger, NULL_KEY);
}
default
{
 state_entry() {
 llSetTexture(gBlankTexture, 0);
 llOwnerSay("Touch to start up.");
 }
 
 touch_start(integer n)
 {
 if (llDetectedKey(0) == llGetOwner()) {
 state readProgram;
 }
 else {
 llSay(0, "This kiosk is under construction.");
 }
 }
}
 
state readProgram
{
 state_entry() {
 llSetTexture(gBlankTexture, 0);
 llMessageLinked(LINK_THIS, 100, "", NULL_KEY);
 }
 
 state_exit() {
 llOwnerSay("main memory: " + (string)llGetFreeMemory());
 }
 link_message(integer sender, integer num, string str, key id) {
 if (num == 101) {
 state run;
 }
 }
 touch_start(integer n) { }
 on_rez(integer n) { llResetScript(); }
}
state run
{
 state_entry() {
 cmdReset();
 }
 
 state_exit() {
 llSetTimerEvent(0.0);
 }
 
 touch_start(integer n) {
 key id = llDetectedKey(0);
 if (gUser != NULL_KEY && gUser != id) { return; }
 runAnyAction("touch", "", id, "");
 llSetTimerEvent(gTimeOut);
 }
 
 listen(integer ch, string who, key id, string message) {
 if (ch != 0 && ch == gLicenseChannel) {
 finishLicense();
 return;
 }
 
 string trigger = "\"" + llToLower(message) + "\"";
 runAnyAction(trigger, "listen", id, message);
 llSetTimerEvent(gTimeOut);
 }
 changed(integer what) {
 if (what & CHANGED_INVENTORY) {
 state readProgram;
 }
 }
 
 link_message(integer sender, integer num, string str, key id) {
 if (num == 301) {
 executeBlock(str);
 }
 else if (num == 399) {
 if (gAltTrigger != "") {
 llMessageLinked(LINK_THIS, 300, gAltTrigger, NULL_KEY);
 gAltTrigger = "";
 }
 }
 }
 timer() {
 cmdReset();
 }
 
 on_rez(integer n) { llResetScript(); }
}
</lsl>