/////////////////////////////////////////////////////////////////////////// // Copyright (C) Wizardry and Steamworks 2012 - License: GNU GPLv3 // // Please see: http://www.gnu.org/licenses/gpl.html for legal details, // // rights of fair usage, the disclaimer and warranty conditions. // /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// // CONFIGURATION // /////////////////////////////////////////////////////////////////////////// // The trivia machine pauses this number of seconds before revealing a // letter from the correct answer. float TIME_PER_LETTER = 2; /////////////////////////////////////////////////////////////////////////// // INTERNALS // /////////////////////////////////////////////////////////////////////////// list cards = []; string card = ""; integer cardIterator = 0; integer lineIterator = 0; list categories = []; string selectedCategory = ""; string category = ""; string question = ""; string clue = ""; list store = []; string topDisplay = ""; vector qC = ZERO_VECTOR; list scores = []; list names = []; integer seconds = 0; integer comHandle = 0; integer shutdown = 10; list mem = []; integer memCacheHit = 0; /////////////////////////////////////////////////////////////////////////// // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // /////////////////////////////////////////////////////////////////////////// list wasDualQuicksort(list a, list b) { if(llGetListLength(a) <= 1) return a+b; integer pivot_a = llList2Integer(a, 0); a = llDeleteSubList(a, 0, 0); string pivot_b = llList2String(b, 0); b = llDeleteSubList(b, 0, 0); list less = []; list less_b = []; list more = []; list more_b = []; do { if(llList2Integer(a, 0) > pivot_a) { less += llList2List(a, 0, 0); less_b += llList2List(b, 0, 0); jump continue; } more += llList2List(a, 0, 0); more_b += llList2List(b, 0, 0); @continue; a = llDeleteSubList(a, 0, 0); b = llDeleteSubList(b, 0, 0); } while(llGetListLength(a)); return wasDualQuicksort(less, less_b) + [ pivot_a ] + [ pivot_b ] + wasDualQuicksort(more, more_b); } /////////////////////////////////////////////////////////////////////////// // Copyright (C) 2012 Wizardry and Steamworks - License: GNU GPLv3 // /////////////////////////////////////////////////////////////////////////// integer wasLevenshteinDistance(string a, string b) { integer cost = 0; if(llGetSubString(a, 0, 0) != llGetSubString(b, 0, 0)) cost = 1; integer len_a = llStringLength(a); integer len_b = llStringLength(b); a = llDeleteSubString(a, 0, 0); b = llDeleteSubString(b, 0, 0); if(len_a == 0) return cost; if(len_b == 0) return cost; return cost + wasLevenshteinDistance(a, b); } /////////////////////////////////////////////////////////////////////////// // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // /////////////////////////////////////////////////////////////////////////// list wasDialogMenu(list input, list actions, string direction) { integer cut = 11-wasListCountExclude(actions, [""]); integer wasMenuIndex = (integer)wasKeyValueGet("index", llGetObjectDesc()); if(direction == ">" && (wasMenuIndex+1)*cut+wasMenuIndex+1 < llGetListLength(input)) { ++wasMenuIndex; llSetObjectDesc(wasKeyValueSet("index", (string)wasMenuIndex, llGetObjectDesc())); jump slice; } if(direction == "<" && wasMenuIndex-1 >= 0) { --wasMenuIndex; llSetObjectDesc(wasKeyValueSet("index", (string)wasMenuIndex, llGetObjectDesc())); 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); } /////////////////////////////////////////////////////////////////////////// // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // /////////////////////////////////////////////////////////////////////////// string wasKeyValueGet(string var, string kvp) { list dVars = llParseString2List(kvp, ["%"], []); do { list data = llParseString2List(llList2String(dVars, 0), ["="], []); string k = llList2String(data, 0); if(k != var) jump continue; return llList2String(data, 1); @continue; dVars = llDeleteSubList(dVars, 0, 0); } while(llGetListLength(dVars)); return ""; } /////////////////////////////////////////////////////////////////////////// // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // /////////////////////////////////////////////////////////////////////////// string wasKeyValueSet(string var, string val, string kvp) { list dVars = llParseString2List(kvp, ["%"], []); if(llGetListLength(dVars) == 0) return var + "=" + val; list result = []; do { list data = llParseString2List(llList2String(dVars, 0), ["="], []); string k = llList2String(data, 0); if(k == "") jump continue; if(k == var && val == "") jump continue; if(k == var) { result += k + "=" + val; val = ""; jump continue; } string v = llList2String(data, 1); if(v == "") jump continue; result += k + "=" + v; @continue; dVars = llDeleteSubList(dVars, 0, 0); } while(llGetListLength(dVars)); if(val != "") result += var + "=" + val; return llDumpList2String(result, "%"); } /////////////////////////////////////////////////////////////////////////// // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // /////////////////////////////////////////////////////////////////////////// string wasKeyValueDelete(string var, string kvp) { list dVars = llParseString2List(kvp, ["%"], []); list result = []; list added = []; do { list data = llParseString2List(llList2String(dVars, 0), ["="], []); string k = llList2String(data, 0); if(k == var) jump continue; string v = llList2String(data, 1); if(v == "") jump continue; if(llListFindList(added, (list)k) != -1) jump continue; result += k + "=" + v; added += k; @continue; dVars = llDeleteSubList(dVars, 0 ,0); } while(llGetListLength(dVars)); return llDumpList2String(result, "%"); } /////////////////////////////////////////////////////////////////////////// // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 // /////////////////////////////////////////////////////////////////////////// list spinChars = [ "↑", "↗", "→", "↘", "↓", "↙", "←" ]; string spin() { string text = llList2String(llGetLinkPrimitiveParams(LINK_THIS, [PRIM_TEXT]), 0); do { string tok = llGetSubString(text, llStringLength(text)-1, llStringLength(text)-1); if(!~llListFindList(spinChars, (list)tok) && tok != "\n" && tok != "[" && tok != "]") jump show; text = llDeleteSubString(text, llStringLength(text)-1, llStringLength(text)-1); } while(llStringLength(text)); @show; string next = llList2String(spinChars, 0); spinChars = llDeleteSubList(spinChars, 0, 0); spinChars += next; return "[" + next + "]"; } /////////////////////////////////////////////////////////////////////////// // Copyright (C) 2011 Wizardry and Steamworks - License: GNU GPLv3 // /////////////////////////////////////////////////////////////////////////// string wasProgress(integer percent, integer length, list symbols) { percent /= (integer)((float)100.0/(length)); string p = llList2String(symbols,0); integer itra = 0; do { if(itra>percent-1) p += llList2String(symbols,2); else p += llList2String(symbols,1); } while(++itra; } default { state_entry() { llSetObjectDesc(wasKeyValueSet("index", "0", llGetObjectDesc())); llSetTextureAnim(ANIM_ON | SMOOTH | LOOP, ALL_SIDES,1,1,1,1,0.1); llSetText("-=:[ Please wait, reading brains... ]:=-\n", <1,1,0>, 1); integer i = llGetInventoryNumber(INVENTORY_NOTECARD)-1; do { list bCard = llParseString2List(llGetInventoryName(INVENTORY_NOTECARD, i), ["_"], [""]); if(llList2String(bCard, 0) != "Trivia" || llList2String(bCard, 1) != "Brain") jump continue; cards += llGetInventoryName(INVENTORY_NOTECARD, i); @continue; } while(--i>-1); if(llGetListLength(cards) != 0) state categorize; llSay(DEBUG_CHANNEL, "Failed to find any Trivia brains."); } on_rez(integer param) { llResetScript(); } changed(integer change) { if(change & CHANGED_INVENTORY) llResetScript(); } } state categorize { state_entry() { cardIterator = llGetListLength(cards)-1; card = llList2String(cards, cardIterator); llSetText("-=:[ Please wait, categorizing brains... ]:=-\nBrain: " + card + " " + spin(), <1,1,0>, 1); lineIterator = 0; llGetNotecardLine(card, lineIterator); } dataserver(key id, string data) { if(data == EOF) { if(cardIterator-- == 0) state start; card = llList2String(cards, cardIterator); lineIterator = 0; llGetNotecardLine(card, lineIterator); return; } if(data == "") jump continue; string category = llList2String( llParseString2List( llList2String( llParseString2List(data, ["//"], [""]), 0), ["/"], [""]), 0); if(llListFindList(categories, (list)category) != -1) jump continue; categories += category; @continue; llSetText("-=:[ Please wait, categorizing brains... ]:=-\nBrain: " + card + " " + spin(), <1,1,0>, 1); llGetNotecardLine(card, ++lineIterator); } } state start { state_entry() { qC = ; llParticleSystem([PSYS_SRC_TEXTURE,llGetInventoryName(0,0),PSYS_PART_START_SCALE,<.2,.3,0>,6,<.2,.3,0>,PSYS_PART_START_COLOR, qC,PSYS_PART_END_COLOR,qC,PSYS_PART_START_ALPHA,.05,PSYS_PART_END_ALPHA,.05,PSYS_SRC_BURST_PART_COUNT,0x7FFFFFFF,PSYS_PART_MAX_AGE,1,PSYS_SRC_MAX_AGE,0,PSYS_SRC_PATTERN,(integer)4,PSYS_SRC_BURST_SPEED_MIN,0,PSYS_SRC_BURST_SPEED_MAX,0,PSYS_SRC_ANGLE_BEGIN, (float)0.03*3.141593,PSYS_SRC_ANGLE_END,(float)0*3.141593,8, ZERO_VECTOR,PSYS_PART_FLAGS,(integer)(0|1|PSYS_PART_EMISSIVE_MASK)]); llSetText("-=:[ Touch to Start Trivia ]:=-", <1,1,0>, 1); } touch_start(integer num) { llSetText("", <0,0,0>, 1); // only for owner if(llDetectedKey(0) != llGetOwner()) state proximity; // fallthrough user state configure; } on_rez(integer param) { llResetScript(); } changed(integer change) { if(change & CHANGED_INVENTORY) llResetScript(); } } state proximity { state_entry() { qC = ; llParticleSystem([PSYS_SRC_TEXTURE,llGetInventoryName(0,0),PSYS_PART_START_SCALE,<.2,.3,0>,6,<.2,.3,0>,PSYS_PART_START_COLOR, qC,PSYS_PART_END_COLOR,qC,PSYS_PART_START_ALPHA,.05,PSYS_PART_END_ALPHA,.05,PSYS_SRC_BURST_PART_COUNT,0x7FFFFFFF,PSYS_PART_MAX_AGE,1,PSYS_SRC_MAX_AGE,0,PSYS_SRC_PATTERN,(integer)4,PSYS_SRC_BURST_SPEED_MIN,0,PSYS_SRC_BURST_SPEED_MAX,0,PSYS_SRC_ANGLE_BEGIN, (float)0.03*3.141593,PSYS_SRC_ANGLE_END,(float)0*3.141593,8, ZERO_VECTOR,PSYS_PART_FLAGS,(integer)(0|1|PSYS_PART_EMISSIVE_MASK)]); shutdown = 10; llSensorRepeat("", "", AGENT, 64, TWO_PI, 1); } touch_start(integer num) { // abort, only for owner if(llDetectedKey(0) != llGetOwner()) return; state configure; } sensor(integer num) { shutdown = 10; state select; } no_sensor() { if(--shutdown > 0) return; state start; } } state select { state_entry() { if(cardIterator < 0) cardIterator = llGetListLength(cards)-1; card = llList2String(cards, cardIterator); llGetNumberOfNotecardLines(card); } touch_start(integer num) { // abort, only for owner if(llDetectedKey(0) != llGetOwner()) return; state configure; } dataserver(key id, string data) { lineIterator = (integer)data-1; state search; } on_rez(integer param) { llResetScript(); } changed(integer change) { if(change & CHANGED_INVENTORY) llResetScript(); } } state search { state_entry() { card = llList2String(cards, cardIterator); llGetNotecardLine(card, lineIterator); } touch_start(integer num) { // abort, only for owner if(llDetectedKey(0) != llGetOwner()) return; state configure; } dataserver(key id, string data) { if(data == EOF) { --cardIterator; state select; } if(data == "") jump continue; list qaSplit = llParseString2List(data, ["//"], [""]); list caSplit = llParseString2List(llList2String(qaSplit, 0), ["/"], [""]); string selectedCategory = wasKeyValueGet("category", llGetObjectDesc()); if(selectedCategory != "" && selectedCategory != llList2String(caSplit, 0)) jump continue; category = llList2String(caSplit, 0); question = llList2String(qaSplit, 1); caSplit = llDeleteSubList(caSplit, 0, 0); string head = llList2String(caSplit, 0); if(llGetFreeMemory() < 2048) { memCacheHit = 0; mem = []; } if(llListFindList(mem, (list)head) != -1) { if(++memCacheHit > 4) state start; jump continue; } memCacheHit = 0; integer s = llStringLength(head)-1; clue = ""; do { clue += "-"; } while(--s>-1); mem += [ head ]; store = [ head ]; do { store += llList2String(caSplit, 0); caSplit = llDeleteSubList(caSplit, 0, 0); } while(llGetListLength(caSplit) != 0); state ask; @continue; string text = "-=:[ Searching question... ]:=-\n" + "Brain: " + card + " " + spin(); if(llStringLength(topDisplay) != 0) text += "\n\n\n-=:[ Top ]:=-\n" + topDisplay; llSetText(text, <1,1,0>, 1); llGetNotecardLine(card, --lineIterator); } on_rez(integer param) { llResetScript(); } changed(integer change) { if(change & CHANGED_INVENTORY) llResetScript(); } } state ask { state_entry() { comHandle = llListen(0, "", "", ""); llSay(0, "[" + category + "]: " + question); seconds = llStringLength(clue); llSetTimerEvent(TIME_PER_LETTER); } touch_start(integer num) { // abort, only for owner if(llDetectedKey(0) != llGetOwner()) return; state configure; } listen(integer chan,string name,key id,string mes) { // if it's a number, we want a perfect match if((integer)mes > 0 && llListFindList(store, (list)llToLower(mes)) == -1) return; integer i = llGetListLength(store)-1; do { string answer = llList2String(store, i); if(llStringLength(answer) != llStringLength(mes)) jump continue; if(wasLevenshteinDistance(llToLower(answer), llToLower(mes)) >= 2) jump continue; // lock llListenRemove(comHandle); llSetTimerEvent(0); jump correct; @continue; } while(--i>-1); return; @correct; llSay(0, name + "'s answer is correct! " + llList2String(store, 0) + ", is the correct answer!"); integer idx = llListFindList(names, (list)name); if(idx == -1) { names = llListInsertList(names, (list)name, 0); scores = llListInsertList(scores, (list)1, 0); jump compute; } integer score = llList2Integer(scores, idx)+1; scores=llListReplaceList(scores, (list)score, idx, idx); @compute; list sort = wasDualQuicksort(scores, names); topDisplay = ""; integer count = 0; do { topDisplay += llList2String(sort, 1); topDisplay += " -> "; topDisplay += llList2String(sort, 0); topDisplay += "\n"; sort = llDeleteSubList(sort, 0, 0); sort = llDeleteSubList(sort, 0, 0); if(++count==5) jump show; } while(llGetListLength(sort) != 0); @show; llSetText("-=:[ Top ]:=-\n" + topDisplay, <1,1,0>, 1); state proximity; } timer() { llParticleSystem([ PSYS_SRC_TEXTURE, llGetInventoryName(INVENTORY_TEXTURE, 0), PSYS_PART_START_SCALE, <.01, .014, 0>, PSYS_PART_END_SCALE, <.1, .14, 0>, PSYS_PART_START_COLOR, qC, PSYS_PART_END_COLOR, <.5 + qC.x,.5 + qC.y, .5 + qC.z>, PSYS_SRC_BURST_PART_COUNT, 0x7FFFFFFF, PSYS_PART_MAX_AGE, 1.25, PSYS_SRC_MAX_AGE, 1.25, PSYS_SRC_PATTERN, 8, PSYS_SRC_BURST_SPEED_MIN, (float)1, PSYS_SRC_BURST_SPEED_MAX, (float)1.2, PSYS_SRC_ANGLE_BEGIN, 0, PSYS_SRC_ANGLE_END, TWO_PI, PSYS_PART_FLAGS, ( 0 | PSYS_PART_INTERP_SCALE_MASK | PSYS_PART_INTERP_COLOR_MASK | PSYS_PART_EMISSIVE_MASK ) ]); string answer = llList2String(store, 0); integer i = (integer)llFrand(llStringLength(answer)); do { --i; if(i < 0) i = llStringLength(answer); } while(llGetSubString(clue, i, i) != "-"); clue = llDeleteSubString(clue, i, i); clue = llInsertString(clue, i, llGetSubString(answer, i, i)); string text = "Clue: " + "[" + clue + "]"; integer progress; i = llStringLength(clue)-1; do { if(llGetSubString(answer, i, i) != "-") ++progress; } while(--i>-1); if(llStringLength(topDisplay) != 0) text += "\n\n\n-=:[ Top ]:=-\n" + topDisplay; llSetText(text, wasPercentToGradient(100*progress/llStringLength(clue), "rg"), 1.0); if(--seconds == 0) { llSay(0, "A correct answer would have been: " + answer + "."); state proximity; } } on_rez(integer param) { llResetScript(); } changed(integer change) { if(change & CHANGED_INVENTORY) llResetScript(); } } state configure { state_entry() { qC = ; llParticleSystem([PSYS_SRC_TEXTURE,llGetInventoryName(0,0),PSYS_PART_START_SCALE,<.2,.3,0>,6,<.2,.3,0>,PSYS_PART_START_COLOR, qC,PSYS_PART_END_COLOR,qC,PSYS_PART_START_ALPHA,.05,PSYS_PART_END_ALPHA,.05,PSYS_SRC_BURST_PART_COUNT,0x7FFFFFFF,PSYS_PART_MAX_AGE,1,PSYS_SRC_MAX_AGE,0,PSYS_SRC_PATTERN,(integer)4,PSYS_SRC_BURST_SPEED_MIN,0,PSYS_SRC_BURST_SPEED_MAX,0,PSYS_SRC_ANGLE_BEGIN, (float)0.03*3.141593,PSYS_SRC_ANGLE_END,(float)0*3.141593,8, ZERO_VECTOR,PSYS_PART_FLAGS,(integer)(0|1|PSYS_PART_EMISSIVE_MASK)]); llSetText("-=:[ Configuring... ]:=-", <1,1,0>, 1); integer comChannel = (integer)("0x8" + llGetSubString(llGetKey(), 0, 6)); llListen(comChannel, "", llGetOwner(), ""); llDialog(llGetOwner(), "Select a category for the Trivia machine from the following options:\n", wasDialogMenu(categories, ["⟵ Back", "⦡ Any", "Next ⟶", "◯ STOP", "", "◉ START"], ""), comChannel); seconds = 30; llSetTimerEvent(1); } touch_start(integer num) { if(llDetectedKey(0) != llGetOwner()) return; llSetText("-=:[ Configuring... ]:=-", <1,1,0>, 1); integer comChannel = (integer)("0x8" + llGetSubString(llGetKey(), 0, 6)); llListen(comChannel, "", llGetOwner(), ""); llDialog(llGetOwner(), "Select a category for the Trivia machine from the following options:\n", wasDialogMenu(categories, ["⟵ Back", "⦡ Any", "Next ⟶", "◯ STOP", "", "◉ START"], ""), comChannel); seconds = 30; llSetTimerEvent(1); } listen(integer channel, string name, key id, string message) { if(message == "◉ START") state proximity; if(message == "◯ STOP") state start; seconds = 30; if(message == "⟵ Back") { llDialog(id, "Select a category for the Trivia machine from the following options:\n", wasDialogMenu(categories, ["⟵ Back", "⦡ Any", "Next ⟶", "◯ STOP", "", "◉ START"], "<"), channel); return; } if(message == "Next ⟶") { llDialog(id, "Select a category for the Trivia machine from the following options:\n", wasDialogMenu(categories, ["⟵ Back", "⦡ Any", "Next ⟶", "◯ STOP", "", "◉ START"], ">"), channel); return; } if(message == "⦡ Any") { llSetObjectDesc(wasKeyValueDelete("category", llGetObjectDesc())); llDialog(id, "Select a category for the Trivia machine from the following options:\n", wasDialogMenu(categories, ["⟵ Back", "⦡ Any", "Next ⟶", "◯ STOP", "", "◉ START"], ""), channel); return; } llSetObjectDesc(wasKeyValueSet("category", message, llGetObjectDesc())); llDialog(id, "Select a category for the Trivia machine from the following options:\n", wasDialogMenu(categories, ["⟵ Back", "⦡ Any", "Next ⟶", "◯ STOP", "", "◉ START"], ""), channel); } timer() { if(seconds <= 0) state start; integer progress = 100*(seconds--)/30; string text = "-=:[ Configuring... ]:=-\n" + wasProgress(progress, 10, ["[", "#", "o", "]"]); if(llStringLength(topDisplay) != 0) text += "\n\n-=:[ Top ]:=-\n" + topDisplay; llSetText(text, wasPercentToGradient(progress, "rg"), 1); } on_rez(integer param) { llResetScript(); } changed(integer change) { if(change & CHANGED_INVENTORY) llResetScript(); } }