///////////////////////////////////////////////////////////////////////////
//  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 UUID / Key of the scripted agent.
string CORRADE = "18a56ee7-4330-48f9-835a-f7839a32cdbf";
 
// The name of a configured group.
string GROUP = "My Group";
 
// The password for the configured group.
string PASSWORD = "mypassword";
 
// How many seconds per letter are allowed before dropping a hint.
integer TIME_PER_LETTER = 5;
 
// The prefix used to trigger trivia commands.
string COMMAND_PREFIX = "@";
 
// A string to be used as a notification tag.
string NOTIFY_TAG = "TjP9Nd1boQ";
 
///////////////////////////////////////////////////////////////////////////
//                              INTERNALS                                //
///////////////////////////////////////////////////////////////////////////
 
///////////////////////////////////////////////////////////////////////////
//    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) 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) 2015 Wizardry and Steamworks - License: CC BY 2.0    //
///////////////////////////////////////////////////////////////////////////
string wasKeyValueEncode(list data) {
    integer i = llGetListLength(data);
    if (i % 2 != 0 || i == 0) return "";
    --i;
    do {
        data = llListInsertList(
            llDeleteSubList(
                data, 
                i-1, 
                i
            ),
            [ llList2String(data, i-1) + "=" + llList2String(data, i) ], 
            i-1
        );
        i -= 2;
    } while(i > 0);
    return llDumpList2String(data, "&");
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
// unescapes a string in conformance with RFC1738
string wasFormDecode(string i) {
    return llUnescapeURL(
        llDumpList2String(
            llParseString2List(
                llDumpList2String(
                    llParseString2List(
                        i, 
                        ["+"], 
                        []
                    ), 
                    " "
                ), 
                ["%0D%0A"], 
                []
            ), 
            "\n"
        )
    );
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
// escapes a string in conformance with RFC1738
string wasFormEncode(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;
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0    //
///////////////////////////////////////////////////////////////////////////
string wasListToCSV(list l) {
    list v = [];
    do {
        string a = llDumpList2String(
            llParseStringKeepNulls(
                llList2String(
                    l, 
                    0
                ), 
                ["\""], 
                []
            ),
            "\"\""
        );
        if(llParseStringKeepNulls(
            a, 
            [" ", ",", "\n", "\""], []
            ) != 
            (list) a
        ) a = "\"" + a + "\"";
        v += a;
        l = llDeleteSubList(l, 0, 0);
    } while(l != []);
    return llDumpList2String(v, ",");
}
 
list cards = [];
string card = "";
integer cardIterator = 0;
integer lineIterator = 0;
list categories = [];
string category = "";
string select = "";
string question = "";
string clue = "";
list store = [];
list scores = [];
list names = [];
string name = "";
integer seconds = 0;
string URL = "";
integer running = FALSE;
key dataRequest = NULL_KEY;
 
default {
    state_entry() {
        // DEBUG
        llOwnerSay("Please wait, reading brains...");
 
        integer i = llGetInventoryNumber(INVENTORY_NOTECARD)-1;
        do {
            list brainCard = llParseString2List(
                llGetInventoryName(
                    INVENTORY_NOTECARD, 
                    i
                ), 
                ["_"], 
                [""]
            );
            if(llList2String(brainCard, 0) == "Trivia" && 
                llList2String(brainCard, 1) == "Brain") {
                cards += llGetInventoryName(INVENTORY_NOTECARD, i);
            }
        } while(--i > -1);
 
        if(llGetListLength(cards) == 0) {
            // DEBUG
            llOwnerSay("Failed to find any trivia brains!");
            return;
        }
 
        // DEBUG
        llOwnerSay("Categorizing brains...");
        cardIterator = llGetListLength(cards)-1;
        card = llList2String(cards, cardIterator);
 
        // DEBUG
        llOwnerSay("Reading brain: " + card);
 
        lineIterator = 0;
        llGetNotecardLine(card, lineIterator);
    }
    dataserver(key id, string data) {
        if(data == EOF) {
            // If all cards have been read, jump to the next state.
            if(cardIterator-- == 0) {
                // DEBUG
                llOwnerSay("All brain cards have been read.");
                state url;
            }
 
            // Select the next card.
            card = llList2String(cards, cardIterator);
 
            // DEBUG
            llOwnerSay("Reading brain: " + card);
 
            lineIterator = 0;
            llGetNotecardLine(card, lineIterator);
            return;
        }
 
        category = llList2String(
                llParseString2List(
                    llList2String(
                        llParseString2List(
                            data, 
                            ["//"], 
                            [""]), 
                        0
                    ), 
                    ["/"], 
                    [""]
                ), 
            0
        );
 
        // Broken line in notecard brain.
        if(category == "") {
            // DEBUG
            llOwnerSay("Skipping brain line without category in " + 
                card + 
                " on line " + 
                (string)lineIterator
            );
            llGetNotecardLine(card, ++lineIterator);
            return;
        }
 
        // If category has not been added yet then add it.          
        if(llListFindList(categories, (list)category) == -1) {
            categories += category;
        }
 
        llGetNotecardLine(card, ++lineIterator);
    }
    changed(integer change) {
        if(!(change & (CHANGED_INVENTORY | CHANGED_OWNER))) {
            return;
        }
        llResetScript();
    }
    on_rez(integer param) {
        llResetScript();
    }
}
 
state url {
    state_entry() {
        // DEBUG
        llOwnerSay("Requesting LSL URL...");
        llReleaseURL(URL);
        llRequestURL();
    }
    http_request(key id, string method, string body) {
        if(method != URL_REQUEST_GRANTED) {
            // DEBUG
            llOwnerSay("LSL URL reqest failed...");
            llResetScript();
        }
 
        URL = body;
        state rebind;
    }
}
 
state rebind {
    state_entry() {
        // DEBUG
        llOwnerSay("Removing group notification...");
        llMessageLinked(LINK_THIS, 0,
            wasKeyValueEncode(
                [
                    "command", "notify",
                    "group", wasFormEncode(GROUP),
                    "password", wasFormEncode(PASSWORD),
                    "action", "remove",
                    "tag", wasFormEncode(NOTIFY_TAG),
                    // afterburn
                    "aftAction", "remove",
                    "callback", wasFormEncode(URL)
                ]
            ),
            CORRADE
        );
        llSetTimerEvent(60);
    }
    http_request(key id, string method, string body) {
        llHTTPResponse(id, 200, "OK");
 
        if(wasKeyValueGet("command", body) != "notify") {
            return;
        }
 
        if(wasKeyValueGet("success", body) != "True") {
            // DEBUG
            llOwnerSay("Cannot rebind group notifications: " + 
                wasKeyValueGet(
                    "error", 
                    body
                )
            );
 
            state url;
        }
 
        if(wasKeyValueGet("success", body) == "True" && 
            wasKeyValueGet("aftAction", body) == "remove") {
            llMessageLinked(LINK_THIS, 0,
                wasKeyValueEncode(
                    [
                        "command", "notify",
                        "group", wasFormEncode(GROUP),
                        "password", wasFormEncode(PASSWORD),
                        "action", "set",
                        "tag", wasFormEncode(NOTIFY_TAG),
                        "type", "group",
                        // afterburn
                        "aftAction", "set",
                        "URL", wasFormEncode(URL),
                        "callback", wasFormEncode(URL)
                    ]
                ),
                CORRADE
            );
 
            return;
        }
 
        // DEBUG
        llOwnerSay("Notification bound successfully!");
        state group_messages;
    }
    timer() {
        // DEBUG
        llOwnerSay("Timeout binding to group notification.");
        state url;
    }
    changed(integer change) {
        if(!(change & (CHANGED_INVENTORY | CHANGED_OWNER))) {
            return;
        }
 
        llResetScript();
    }
    on_rez(integer param) {
        llResetScript();
    }
    state_exit() {
        llSetTimerEvent(0);
    }
}
 
state group_messages {
    state_entry() {
        // If no category has been selected, then stop and wait for commands.
        if(running == FALSE) {
            // DEBUG
            llOwnerSay("Waiting for command...");
            return;
        }
 
        // A category and question has been selected so send the message to the group.
        llMessageLinked(LINK_THIS, 0,
            wasKeyValueEncode(
                [
                    "command", "tell",
                    "group", wasFormEncode(GROUP),
                    "password", wasFormEncode(PASSWORD),
                    "entity", "group",
                    "message", wasFormEncode("[" + category + "]: " + question)
                ]
            ),
            CORRADE
        );
 
        seconds = llStringLength(clue);
        llSetTimerEvent(TIME_PER_LETTER);
    }
    http_request(key id, string method, string body) {
        llHTTPResponse(id, 200, "OK");
 
        // Skip messages that are not for the configured group.
        if(wasFormDecode(wasKeyValueGet("group", body)) != GROUP) {
            return;
        }
 
        // Ignore self messages.
        if(wasKeyValueGet("agent", body) == CORRADE) {
            return;
        }
 
        // Skip messages that are not trivia commands.
        string message = wasFormDecode(wasKeyValueGet("message", body));
        if(llGetSubString(message, 0, 0) != COMMAND_PREFIX) {
            jump interpret_answer;
        }
 
        list data = llParseString2List(
            message,
            [" "], 
            []
        );
 
        // Ignore non-trivia commands.
        if(llList2String(data, 0) != "@trivia") {
            return;
        }
 
        if(llList2String(data, 1) == "start") {
            // If no category has been selected then set the category to any.
            if(llListFindList(categories, (list)llList2String(data, 2)) == -1) {
                select = "any";
            }
 
            running = TRUE;
            state select_question;
        }
 
        if(llList2String(data, 1) == "help") {
            llMessageLinked(LINK_THIS, 0,
                wasKeyValueEncode(
                    [
                        "command", "tell",
                        "group", wasFormEncode(GROUP),
                        "password", wasFormEncode(PASSWORD),
                        "entity", "group",
                        "message", wasFormEncode("syntax: @trivia [start <category>|stop|categories|help]")
                    ]
                ),
                CORRADE
            );
 
            return;
        }
 
        if(llList2String(data, 1) == "categories") {
            llMessageLinked(LINK_THIS, 0,
                wasKeyValueEncode(
                    [
                        "command", "tell",
                        "group", wasFormEncode(GROUP),
                        "password", wasFormEncode(PASSWORD),
                        "entity", "group",
                        "message", wasFormEncode(llDumpList2String(categories, ", "))
                    ]
                ),
                CORRADE
            );
 
            return;
        }
 
        if(llList2String(data, 1) == "stop") {
            // DEBUG
            llOwnerSay("Stopping trivia...");
 
            llSetTimerEvent(0);
            running = FALSE;
 
            llMessageLinked(LINK_THIS, 0,
                wasKeyValueEncode(
                    [
                        "command", "tell",
                        "group", wasFormEncode(GROUP),
                        "password", wasFormEncode(PASSWORD),
                        "entity", "group",
                        "message", wasFormEncode("Trivia stopped.")
                    ]
                ),
                CORRADE
            );
 
            return;
        }
 
        return;
 
@interpret_answer;
 
        if(running == FALSE) {
            return;
        }
 
        integer i = llGetListLength(store) - 1;
        string answer = "";
        do {
            answer = llList2String(store, i);
 
            if(llStringLength(answer) == llStringLength(message) && 
                !(llToUpper(answer) != llToUpper(message))) {
                    jump correct_answer;
            }
        } while(--i > -1);
 
        return;
 
@correct_answer;
 
        llSetTimerEvent(0);
 
        // Get the name of the avatar sending the message.
        name = wasFormDecode(
            wasKeyValueGet(
                "firstname", 
                body
            )
        ) + 
        " " + 
        wasFormDecode(
            wasKeyValueGet(
                "lastname", 
                body
            )
        );
 
        llMessageLinked(LINK_THIS, 0,
            wasKeyValueEncode(
                [
                    "command", "tell",
                    "group", wasFormEncode(GROUP),
                    "password", wasFormEncode(PASSWORD),
                    "entity", "group",
                    "message", wasFormEncode(name + " answered the question correctly: " + answer + " is a correct answer!")
                ]
            ),
            CORRADE
        );
 
        i = llListFindList(names, (list)name);
        if(i == -1) {
            names = llListInsertList(names, (list)name, 0);
            scores = llListInsertList(scores, (list)1, 0);
            jump compute_score;
        }
 
        integer score = llList2Integer(scores, i) + 1;
        scores = llListReplaceList(scores, (list)score, i, i);
 
@compute_score;
 
        llMessageLinked(LINK_THIS, 0,
            wasKeyValueEncode(
                [
                    "command", "tell",
                    "group", wasFormEncode(GROUP),
                    "password", wasFormEncode(PASSWORD),
                    "entity", "group",
                    "message", wasFormEncode("Score: " + 
                        llDumpList2String(
                            wasDualQuicksort(
                                scores,
                                names
                            ),
                            " -> "
                        )
                    )
                ]
            ),
            CORRADE
        );
 
        state select_question;
    }
    timer() {
        if(running == FALSE) {
            return;
        }
 
        integer i = (integer)llFrand(llStringLength(clue));
        do {
            if(--i < 0) {
                i = llStringLength(clue);
            }
        } while(llGetSubString(clue, i, i) != "-");
 
        //
        // f = a + i + b
        //       
        //      / 0..i - 1 iff. i - 1 >= 0
        // a = {
        //      \ emp iff. i - 1 < 0
        //
        //
        //      / 0..i + 1 iff. i + 1 < len(b)
        // b = {
        //      \ emp iff. i + 1 >= len(b)
        //
        //
        string answer = llList2String(store, 0);
        clue = llList2String(
                [ 
                    llGetSubString(clue, 0, i - 1), 
                    ""
                ], 
                i - 1 < 0
            ) +
            llGetSubString(answer, i, i) +
            llList2String(
                [
                    llGetSubString(clue, i + 1, -1), 
                    "" 
                ], 
                i + 1 >= llStringLength(clue)
        );
 
        llMessageLinked(LINK_THIS, 0,
            wasKeyValueEncode(
                [
                    "command", "tell",
                    "group", wasFormEncode(GROUP),
                    "password", wasFormEncode(PASSWORD),
                    "entity", "group",
                    "message", wasFormEncode("Clue: " + "[" + clue +  "]")
                ]
            ),
            CORRADE
        );
 
        // The question has not been answered yet so return.
        if(--seconds != 0) {
            return;
        }
 
        llMessageLinked(LINK_THIS, 0,
            wasKeyValueEncode(
                [
                    "command", "tell",
                    "group", wasFormEncode(GROUP),
                    "password", wasFormEncode(PASSWORD),
                    "entity", "group",
                    "message", wasFormEncode("A correct answer would have been: " + answer + ".")
                ]
            ),
            CORRADE
        );
 
        state select_question;
    }
    changed(integer change) {
        if(!(change & (CHANGED_INVENTORY | CHANGED_OWNER))) {
            return; 
        }
 
        llResetScript();
    }
    on_rez(integer param) {
        llResetScript();
    }
    state_exit() {
        llSetTimerEvent(0);
    }
}
 
state select_question {
    state_entry() {
        // DEBUG
        llOwnerSay("Selecting a line...");
 
        if(cardIterator < 0) {
            cardIterator = (integer)llFrand(llGetListLength(cards));
        }
 
        card = llList2String(cards, cardIterator);
        dataRequest = llGetNumberOfNotecardLines(card);
    }
    dataserver(key id, string data) {
        if(dataRequest == id) {
            lineIterator = (integer)llFrand((integer)data);
 
            // DEBUG
            llOwnerSay("Selecting a question...");
 
            card = llList2String(cards, cardIterator);
            llGetNotecardLine(card, lineIterator);
 
            return;
        }
 
        if(data == EOF) {
            --cardIterator;
 
            if(cardIterator < 0) {
                cardIterator = (integer)llFrand(llGetListLength(cards));
            }
 
            card = llList2String(cards, cardIterator);
            dataRequest = llGetNumberOfNotecardLines(card);
 
            return;
        }
 
        // Skip empty notecard lines.
        if(data == "") {
            llGetNotecardLine(card, ++lineIterator);
 
            return;
        }
 
        list q = llParseString2List(
            data, 
            ["//"], 
            [""]
        );
 
        list c = llParseString2List(
            llList2String(q, 0), 
            ["/"], 
            [""]
        );
 
        if(select != "any" && 
            select != llList2String(c, 0)) {
            // DEBUG
            llOwnerSay("Question for category not found, continuing...");
 
            llGetNotecardLine(card, ++lineIterator);
 
            return;
        }
 
        category = llList2String(c, 0);
        question = llList2String(q, 1);
 
        // Build a clue string.
        c = llDeleteSubList(c, 0, 0);
        string head = llList2String(c, 0);
        integer i = llStringLength(head) - 1;
        clue = "";
        do {
            clue += "-";
        } while(--i > -1);
 
        // DEBUG
        llOwnerSay("Generated clue: " + clue);
 
        store = (list)head;
 
        do {
            store += llList2String(c, 0);
            c = llDeleteSubList(c, 0, 0);
        } while(llGetListLength(c) != 0);
 
        ++lineIterator;
 
        // DEBUG
        llOwnerSay("Question selected...");
 
        state group_messages;
    }
    changed(integer change) {
        if(!(change & (CHANGED_INVENTORY | CHANGED_OWNER))) {
            return;
        }
 
        llResetScript();
    }
    on_rez(integer param) {
        llResetScript();
    }
}

secondlife/scripted_agents/corrade/projects/in_world/trivia/main.txt ยท Last modified: 2022/11/24 07:45 by 127.0.0.1

Access website using Tor Access website using i2p Wizardry and Steamworks PGP Key


For the contact, copyright, license, warranty and privacy terms for the usage of this website please see the contact, license, privacy, copyright.