secretary.lsl
///////////////////////////////////////////////////////////////////////////
//  Copyright (C) Wizardry and Steamworks 2013 - License: GNU GPLv3      //
//  Please see: http://www.gnu.org/licenses/gpl.html for legal details,  //
//  rights of fair usage, the disclaimer and warranty conditions.        //
///////////////////////////////////////////////////////////////////////////
 
///////////////////////////////////////////////////////////////////////////
//    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    //
///////////////////////////////////////////////////////////////////////////
vector wasStringToVector(string in) {
    list v = llParseString2List(in, ["<", ",", ">"], []);
    return <llList2Float(v, 0), llList2Float(v, 1), llList2Float(v, 2)>;
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
integer wasMenuIndex = 0;
list wasDialogMenu(list input, list actions, string direction) {
    integer cut = 11-wasListCountExclude(actions, [""]);
    if(direction == ">" &&  (wasMenuIndex+1)*cut+wasMenuIndex+1 < llGetListLength(input)) {
        ++wasMenuIndex;
        jump slice;
    }
    if(direction == "<" && wasMenuIndex-1 >= 0) {
        --wasMenuIndex;
        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) 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<length);
    return p + llList2String(symbols,3);
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
vector wasPercentToGradient(float percent, string rgb) {
    if(llStringLength(rgb) != 2) {
        llSay(DEBUG_CHANNEL, "Assert failed, rgb parameter must consist of a pair of either r, g, or b.");
        return ZERO_VECTOR;
    }
    string a = llGetSubString(rgb, 0, 0);
    string b = llGetSubString(rgb, 1, 1);
    list col = [ "r", "g", "b" ];
    integer ax = llListFindList(col, (list)a);
    integer bx = llListFindList(col, (list)b);
    if(ax == -1 || bx == -1) {
        llSay(DEBUG_CHANNEL, "Asset failed, rgb parameters must contain either r, g, or b letters.");
        return ZERO_VECTOR;
    }
    col = llListReplaceList(col, (list)((100-percent)/100), ax, ax);
    col = llListReplaceList(col, (list)(percent/100), bx, bx);
    return 2*<llList2Float(col, 0), llList2Float(col, 1), llList2Float(col, 2)>;
}
 
integer i = 0;
integer channel = 0;
key toucher = NULL_KEY;
list monogram = [];
 
string DIALOG_PREAMBLE = "\n            Welcome to the Secretary Glasses.\nCreated in 2013 by Wizardry and Steamworks\n                 28 September 2013: Version: 1.0";
 
default {
    state_entry() {
        if(wasKeyValueGet("auto", llList2String(llGetLinkPrimitiveParams(13, [PRIM_DESC]), 0)) == "on") llSensorRepeat("", "", AGENT, 3, TWO_PI, 3);
        if(toucher == NULL_KEY) return;
        llListen(channel, "", toucher, "");
        llDialog(toucher, DIALOG_PREAMBLE + "\n\nAuto Tilt: " + wasKeyValueGet("auto", llList2String(llGetLinkPrimitiveParams(13, [PRIM_DESC]), 0)), ["⬆ Up", "⬇ Down", "⎙ Options"], channel);
    }
    touch_start(integer total_number) {
        toucher = llDetectedKey(0);
        channel = (integer)("0x8" + llGetSubString(toucher, 0, 6));
        llListen(channel, "", toucher, "");
        llDialog(toucher, DIALOG_PREAMBLE + "\n\nAuto Tilt: " + wasKeyValueGet("auto", llList2String(llGetLinkPrimitiveParams(13, [PRIM_DESC]), 0)), ["⬆ Up", "⬇ Down", "⎙ Options"], channel);
    }
    listen(integer channel, string name, key toucher, string message) {
        if(message == "⬆ Up") {
            if(wasKeyValueGet("status", llList2String(llGetLinkPrimitiveParams(13, [PRIM_DESC]), 0)) == "up") return;
            llRequestPermissions(llGetOwner(), PERMISSION_TRIGGER_ANIMATION);
            llSetTimerEvent(.5);
            llListenRemove(i);
            return;
        }
        if(message == "⬇ Down") {
            if(wasKeyValueGet("status", llList2String(llGetLinkPrimitiveParams(13, [PRIM_DESC]), 0)) == "down") return;
            llRequestPermissions(llGetOwner(), PERMISSION_TRIGGER_ANIMATION);
            llSetTimerEvent(.5);
            llListenRemove(i);
            return;
        }
// REGION: OWNER
        if(toucher != llGetOwner()) {
            llInstantMessage(toucher, "Sorry, this option is restricted to the owner.");
            llListenRemove(i);
            return;
        }
        if(message == "⎙ Options") state options;
// REGION: OWNER
    }
    sensor(integer num) {
        if(wasKeyValueGet("status", llList2String(llGetLinkPrimitiveParams(13, [PRIM_DESC]), 0)) == "up") return;
        llRequestPermissions(llGetOwner(), PERMISSION_TRIGGER_ANIMATION);
        llSetTimerEvent(.5);
    }
    no_sensor() {
        if(wasKeyValueGet("status", llList2String(llGetLinkPrimitiveParams(13, [PRIM_DESC]), 0)) == "down") return;
        llRequestPermissions(llGetOwner(), PERMISSION_TRIGGER_ANIMATION);
        llSetTimerEvent(.5);
    }
    run_time_permissions(integer perm) {
        if(!(perm & PERMISSION_TRIGGER_ANIMATION)) return;
        if(llGetInventoryType("Glasses") != INVENTORY_ANIMATION) return;
        llStartAnimation("Glasses");
    }
    timer() {
        if(wasKeyValueGet("status", llList2String(llGetLinkPrimitiveParams(13, [PRIM_DESC]), 0)) == "up") {
            llSetLinkPrimitiveParamsFast(LINK_THIS, [PRIM_ROT_LOCAL, ZERO_ROTATION, PRIM_POS_LOCAL, llGetLocalPos() - wasStringToVector(wasKeyValueGet("tilt", llList2String(llGetLinkPrimitiveParams(13, [PRIM_DESC]), 0)))]);
            llSetLinkPrimitiveParamsFast(13, [PRIM_DESC, wasKeyValueSet("status", "down", llList2String(llGetLinkPrimitiveParams(13, [PRIM_DESC]), 0))]);
            jump reset;
        }
        llSetLinkPrimitiveParamsFast(LINK_THIS, [PRIM_ROT_LOCAL, llEuler2Rot(<0,-50,0>*DEG_TO_RAD), PRIM_POS_LOCAL, llGetLocalPos() + wasStringToVector(wasKeyValueGet("tilt", llList2String(llGetLinkPrimitiveParams(13, [PRIM_DESC]), 0)))]);
        llSetLinkPrimitiveParamsFast(13, [PRIM_DESC, wasKeyValueSet("status", "up", llList2String(llGetLinkPrimitiveParams(13, [PRIM_DESC]), 0))]);
@reset;
        llResetScript();
    }
    on_rez(integer num) {
        llResetScript();
    }
}
 
state options {
    state_entry() {
        llListen(channel, "", toucher, "");
        llDialog(toucher, DIALOG_PREAMBLE, ["⏏ Exit", "⎙ Color", "⎙ Opacity", "⎙ Bright", "⎙ Glow", "⎙ Size", "⎙ Adjust Tilt", "⎙ Auto Tilt", "⎙ Stamp"], channel);
    }
    touch_start(integer num) {
        if(llDetectedKey(0) != llGetOwner()) return;
        llListen(channel, "", toucher, "");
        llDialog(toucher, DIALOG_PREAMBLE, ["⏏ Exit", "⎙ Color", "⎙ Opacity", "⎙ Bright", "⎙ Glow", "⎙ Size", "⎙ Adjust Tilt", "⎙ Auto Tilt", "⎙ Stamp"], channel);
    }
    listen(integer channel, string name, key toucher, string message) {
        if(message == "⏏ Exit") state default;
        if(message == "⎙ Color") state color;
        if(message == "⎙ Opacity") state opacity;
        if(message == "⎙ Bright") state bright;
        if(message == "⎙ Glow") state glow;
        if(message == "⎙ Size") state size;
        if(message == "⎙ Adjust Tilt") state tilt;
        if(message == "⎙ Auto Tilt") state auto;
        if(message == "⎙ Stamp") state read_monogram;
    }
    on_rez(integer num) {
        llResetScript();
    }
}
 
state color {
    state_entry() {
        llListen(channel, "", toucher, "");
        llDialog(toucher, DIALOG_PREAMBLE, ["Rims ⌥", "Lenses ⎃", "⏏ Exit"], channel);
    }
    touch_start(integer num) {
        if(llDetectedKey(0) != llGetOwner()) return;
        llListen(channel, "", toucher, "");
        llDialog(toucher, DIALOG_PREAMBLE, ["Rims ⌥", "Lenses ⎃", "⏏ Exit"], channel);
    }
    listen(integer channel, string name, key toucher, string message) {
        if(message == "⏏ Exit") state options;
        if(message == "Rims ⌥") {
            monogram = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16, 17, 18, 19 ];
            state color_parts;
        }
        if(message == "Lenses ⎃") {
            monogram = [ 10, 14 ];
            state color_parts;
        }
@menu;
        llDialog(toucher, DIALOG_PREAMBLE, ["Rims ⌥", "Lenses ⎃", "⏏ Exit"], channel);
    }
    on_rez(integer num) {
        llResetScript();
    }
}
 
state color_parts {
    state_entry() {
        llListen(channel, "", toucher, "");
        llDialog(toucher, DIALOG_PREAMBLE, ["R ▼", "G ▼", "B ▼", "R ▲", "G ▲", "B ▲", "⏏ Exit"], channel);
    }
    touch_start(integer num) {
        if(llDetectedKey(0) != llGetOwner()) return;
        llListen(channel, "", toucher, "");
        llDialog(toucher, DIALOG_PREAMBLE, ["R ▼", "G ▼", "B ▼", "R ▲", "G ▲", "B ▲", "⏏ Exit"], channel);
    }
    listen(integer channel, string name, key toucher, string message) {
        if(message == "⏏ Exit") state options;
        list params = llGetLinkPrimitiveParams(llList2Integer(monogram, -1), [PRIM_COLOR, ALL_SIDES]);
        vector color = llList2Vector(params, 0);
        float alpha = llList2Float(params, 1);
        i = llGetListLength(monogram)-1;
        do {
            if(message == "R ▼") llSetLinkPrimitiveParamsFast(llList2Integer(monogram, i), [PRIM_COLOR, ALL_SIDES, color-<.1,0,0>, alpha]);
            if(message == "R ▲") llSetLinkPrimitiveParamsFast(llList2Integer(monogram, i), [PRIM_COLOR, ALL_SIDES, color+<.1,0,0>, alpha]);
            if(message == "G ▼") llSetLinkPrimitiveParamsFast(llList2Integer(monogram, i), [PRIM_COLOR, ALL_SIDES, color-<0,.1,0>, alpha]);
            if(message == "G ▲") llSetLinkPrimitiveParamsFast(llList2Integer(monogram, i), [PRIM_COLOR, ALL_SIDES, color+<0,.1,0>, alpha]);
            if(message == "B ▼") llSetLinkPrimitiveParamsFast(llList2Integer(monogram, i), [PRIM_COLOR, ALL_SIDES, color-<0,0,.1>, alpha]);
            if(message == "B ▲") llSetLinkPrimitiveParamsFast(llList2Integer(monogram, i), [PRIM_COLOR, ALL_SIDES, color+<0,0,.1>, alpha]);
@skip;
        } while(--i>-1);
        llDialog(toucher, DIALOG_PREAMBLE, ["R ▼", "G ▼", "B ▼", "R ▲", "G ▲", "B ▲", "⏏ Exit"], channel);
    }
    on_rez(integer num) {
        llResetScript();
    }
}
 
state opacity {
    state_entry() {
        llListen(channel, "", toucher, "");
        llDialog(toucher, DIALOG_PREAMBLE, ["⏏ Exit", "Opacity ▼", "Opacity ▲"], channel);
    }
    touch_start(integer num) {
        if(llDetectedKey(0) != llGetOwner()) return;
        llListen(channel, "", toucher, "");
        llDialog(toucher, DIALOG_PREAMBLE, ["⏏ Exit", "Opacity ▼", "Opacity ▲"], channel);
    }
    listen(integer channel, string name, key toucher, string message) {
        if(message == "⏏ Exit") state options;
        list params = llGetLinkPrimitiveParams(10, [PRIM_COLOR, ALL_SIDES]);
        vector color = llList2Vector(params, 0);
        float alpha = llList2Float(params, 1);
        if(message == "Opacity ▼") {
            llSetLinkPrimitiveParamsFast(10, [PRIM_COLOR, ALL_SIDES, color, alpha-.1]);
            llSetLinkPrimitiveParamsFast(14, [PRIM_COLOR, ALL_SIDES, color, alpha-.1]);
            jump menu;
        }
        if(message == "Opacity ▲") {
            llSetLinkPrimitiveParamsFast(10, [PRIM_COLOR, ALL_SIDES, color, alpha+.1]);
            llSetLinkPrimitiveParamsFast(14, [PRIM_COLOR, ALL_SIDES, color, alpha+.1]);
            jump menu;
        }
@menu;
        llDialog(toucher, DIALOG_PREAMBLE, ["⏏ Exit", "Opacity ▼", "Opacity ▲"], channel);
    }
    on_rez(integer num) {
        llResetScript();
    }
}
 
state bright {
    state_entry() {
        llListen(channel, "", toucher, "");
        llDialog(toucher, DIALOG_PREAMBLE, ["⏏ Exit", "◉ ON", "◯ OFF"], channel);
    }
    touch_start(integer num) {
        if(llDetectedKey(0) != llGetOwner()) return;
        llListen(channel, "", toucher, "");
        llDialog(toucher, DIALOG_PREAMBLE, ["⏏ Exit", "◉ ON", "◯ OFF"], channel);
    }
    listen(integer channel, string name, key toucher, string message) {
        if(message == "⏏ Exit") state options;
        if(message == "◉ ON") {
            llSetLinkPrimitiveParamsFast(10, [PRIM_FULLBRIGHT, ALL_SIDES, TRUE]);
            llSetLinkPrimitiveParamsFast(14, [PRIM_FULLBRIGHT, ALL_SIDES, TRUE]);
            jump menu;
        }
        if(message == "◯ OFF") {
            llSetLinkPrimitiveParamsFast(10, [PRIM_FULLBRIGHT, ALL_SIDES, FALSE]);
            llSetLinkPrimitiveParamsFast(14, [PRIM_FULLBRIGHT, ALL_SIDES, FALSE]);
        }
@menu;
        llDialog(toucher, DIALOG_PREAMBLE, ["⏏ Exit", "◉ ON", "◯ OFF"], channel);
    }
    on_rez(integer num) {
        llResetScript();
    }
}
 
state glow {
    state_entry() {
        llListen(channel, "", toucher, "");
        llDialog(toucher, DIALOG_PREAMBLE, ["⏏ Exit", "Glow ▼", "Glow ▲"], channel);
    }
    touch_start(integer num) {
        if(llDetectedKey(0) != llGetOwner()) return;
        llListen(channel, "", toucher, "");
        llDialog(toucher, DIALOG_PREAMBLE, ["⏏ Exit", "Glow ▼", "Glow ▲"], channel);
    }
    listen(integer channel, string name, key toucher, string message) {
        if(message == "⏏ Exit") state options;
        list params = llGetLinkPrimitiveParams(10, [PRIM_GLOW, ALL_SIDES]);
        float glow = llList2Float(params, 0);
        if(message == "Glow ▼") {
            if(glow - .01 < 0) jump menu;
            llSetLinkPrimitiveParamsFast(10, [PRIM_GLOW, ALL_SIDES, glow-.01]);
            llSetLinkPrimitiveParamsFast(14, [PRIM_GLOW, ALL_SIDES, glow-.01]);
            jump menu;
        }
        if(message == "Glow ▲") {
            if(glow + .01 > 1) jump menu;
            llSetLinkPrimitiveParamsFast(10, [PRIM_GLOW, ALL_SIDES, glow+.01]);
            llSetLinkPrimitiveParamsFast(14, [PRIM_GLOW, ALL_SIDES, glow+.01]);
            jump menu;
        }
@menu;
        llDialog(toucher, DIALOG_PREAMBLE, ["⏏ Exit", "Glow ▼", "Glow ▲"], channel);
    }
    on_rez(integer num) {
        llResetScript();
    }
}
 
state size {
    state_entry() {
        llListen(channel, "", toucher, "");
        llDialog(toucher, DIALOG_PREAMBLE, ["-.01", "-.02", "-.05", "+.01", "+.02", "+.05", "⏏ Exit"], channel);
    }
    touch_start(integer num) {
        if(llDetectedKey(0) != llGetOwner()) return;
        llListen(channel, "", toucher, "");
        llDialog(toucher, DIALOG_PREAMBLE, ["-.01", "-.02", "-.05", "+.01", "+.02", "+.05", "⏏ Exit"], channel);
    }
    listen(integer channel, string name, key toucher, string message) {
        if(message == "⏏ Exit") state options;
        if((float)message == 0) return;
        float scale = 1 + (float)message;
        integer i = llGetNumberOfPrims();
        do {
            vector linkScale = llList2Vector(llGetLinkPrimitiveParams(i, [PRIM_SIZE]), 0);
            vector linkPos = llList2Vector(llGetLinkPrimitiveParams(i, [PRIM_POS_LOCAL]), 0);
            llSetLinkPrimitiveParamsFast(i, [PRIM_SIZE, scale * linkScale, PRIM_POSITION, scale * linkPos]);
        } while(--i>1);
        llDialog(toucher, DIALOG_PREAMBLE, ["-.01", "-.02", "-.05", "+.01", "+.02", "+.05", "⏏ Exit"], channel);
    }
    on_rez(integer num) {
        llResetScript();
    }
}
 
state tilt {
    state_entry() {
        llListen(channel, "", toucher, "");
        llTextBox(toucher, DIALOG_PREAMBLE + "n\n This menu lets you adjust the displacement vector when the glasses are put up. Current tilt adjustment vector: " + (string)wasStringToVector(wasKeyValueGet("tilt", llList2String(llGetLinkPrimitiveParams(13, [PRIM_DESC]), 0))), channel);
    }
    touch_start(integer num) {
        if(llDetectedKey(0) != llGetOwner()) return;
        state options;
    }
    listen(integer channel, string name, key toucher, string message) {
        vector tilt = wasStringToVector(message);
        if(tilt == ZERO_VECTOR) {
            llSetLinkPrimitiveParamsFast(13, [PRIM_DESC, wasKeyValueSet("tilt", "<.02,0,.04>", llList2String(llGetLinkPrimitiveParams(13, [PRIM_DESC]), 0))]);
            jump main;
        }
       llSetLinkPrimitiveParamsFast(13, [PRIM_DESC, wasKeyValueSet("tilt", (string)tilt, llList2String(llGetLinkPrimitiveParams(13, [PRIM_DESC]), 0))]);
@main;
        state default;
    }
    on_rez(integer num) {
        llResetScript();
    }
}
 
state auto {
    state_entry() {
        llListen(channel, "", toucher, "");
        llDialog(toucher, DIALOG_PREAMBLE + "\n\nTurning this opon on will automatically tilt the glasses up once you are standing close to an avatar and put them back down again when you move away from avatars.", ["⏏ Exit", "◉ ON", "◯ OFF" ], channel);
    }
    touch_start(integer num) {
        if(llDetectedKey(0) != llGetOwner()) return;
        llListen(channel, "", toucher, "");
        llDialog(toucher, DIALOG_PREAMBLE + "\n\nTurning this option on will automatically tilt the glasses up once you are standing close to an avatar and put them back down again when you move away from avatars.", ["⏏ Exit", "◉ ON", "◯ OFF" ], channel);
    }
    listen(integer channel, string name, key toucher, string message) {
        if(message == "◉ ON") {
            llSetLinkPrimitiveParamsFast(13, [PRIM_DESC, wasKeyValueSet("auto", "on", llList2String(llGetLinkPrimitiveParams(13, [PRIM_DESC]), 0))]);
            jump options;
        }
        if(message == "◯ OFF") llSetLinkPrimitiveParamsFast(13, [PRIM_DESC, wasKeyValueSet("auto", "off", llList2String(llGetLinkPrimitiveParams(13, [PRIM_DESC]), 0))]);
@options;
        state options;
    }
    on_rez(integer num) {
        llResetScript();
    }
}
 
state read_monogram {
    state_entry() {
        if(llGetInventoryType("Monogram") == INVENTORY_NOTECARD) jump found_notecard;
        llSay(DEBUG_CHANNEL, "Failed to find Monogram notecard.");
        return;
@found_notecard;
        monogram = [];
        i = 0;
        llGetNotecardLine("Monogram", i);
    }
    dataserver(key id, string data) {
        if(data == EOF) {
            llSetText("", <0,0,0>, .0);
            state monogram_menu;
        }
        if(data != "") monogram += data;
        data = (string)(100 * i/26);
        llSetText("Please wait, loading menu...\n" + wasProgress((integer)data, 10, ["[", "█", "░", "]"]) + "\n Memory: " + (string)llGetFreeMemory() + "\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n", wasPercentToGradient((integer)data, "rg"), 1);
        llGetNotecardLine("Monogram", ++i);
    }
    on_rez(integer num) {
        llResetScript();
    }
}
 
state monogram_menu {
    state_entry() {
        wasMenuIndex = 0;
        llListen(channel, "", toucher, "");
        llDialog(toucher, DIALOG_PREAMBLE, wasDialogMenu(["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"], ["⏏ Exit", "⟻ Back", "Next ⟼"], ""), channel);
    }
    touch_start(integer num) {
        if(llDetectedKey(0) != llGetOwner()) return;
        llListen(channel, "", toucher, "");
        llDialog(toucher, DIALOG_PREAMBLE, wasDialogMenu(["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"], ["⏏ Exit", "⟻ Back", "Next ⟼"], ""), channel);
    }
    listen(integer channel, string name, key toucher, string message) {
        if(message == "⏏ Exit") {
            monogram = [];
            state options;
        }
        if(message == "⟻ Back") {
            llDialog(toucher, DIALOG_PREAMBLE, wasDialogMenu(["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"], ["⏏ Exit", "⟻ Back", "Next ⟼"], "<"), channel);
            return;
        }
        if(message == "Next ⟼") {
            llDialog(toucher, DIALOG_PREAMBLE, wasDialogMenu(["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"], ["⏏ Exit", "⟻ Back", "Next ⟼"], ">"), channel);
            return;
        }
        i = llListFindList(["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"], (list)message);
        llSetLinkPrimitiveParamsFast(19, [PRIM_TYPE, PRIM_TYPE_SCULPT, llList2String(monogram, i), PRIM_SCULPT_TYPE_SPHERE]);
    }
    on_rez(integer num) {
        llResetScript();
    }
}