Myriad Lite Module RLV

// Myriad_Lite_Module_RLV-v0.0.0-20131024.lsl
// Copyright (c) 2013 by Allen Kerensky (OSG/SL) All Rights Reserved.
// This work is dual-licensed under
// Creative Commons Attribution (CC BY) 3.0 Unported
// http://creativecommons.org/licenses/by/3.0/
// - or -
// Modified BSD License (3-clause)
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright notice, 
//   this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
// * Neither the name of Myriad Lite nor the names of its contributors may be
//   used to endorse or promote products derived from this software without
//   specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
// NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The Myriad RPG System was designed, written, and illustrated by Ashok Desai
// Myriad RPG System licensed under:
// Creative Commons Attribution (CC BY) 2.0 UK: England and Wales
// http://creativecommons.org/licenses/by/2.0/uk/
 
// SEE: http://wiki.secondlife.com/wiki/LSL_Protocol/RestrainedLoveAPI
    // RLV_VERSIONNUM();
    // ... more setup functions here
    // RLV Functions http://wiki.secondlife.com/wiki/LSL_Protocol/RestrainedLoveAPI
    // LOAD current from PPMA
    // -- VERSIONS
    // Automated version @version=<channel>
    // RPG: Automated version @versionnew=<channel>
    // RPG: Automated version @versionnum=<channel>
    // Manual version IM @version
    // --- BLACKLIST
    // Automated version followed by blacklist: @versionnumbl=<channel>
    // Get contents of the blacklist with filter: @getblacklist[:filter]=<channel>
    // Manual blacklist checking: IM @getblacklist
    // --- MISCELLANEOUS
    // Start/stop notifications on a private channel: @notify:<channel>[;word]=<rem/add>
    //      /accepted_in_rlv inv_offer <folder> accepted and available under #RLV/
    //      /accepted_in_inv inv_offer <folder> accepted but not shared
    //      /declined inv_offer <folder> 
    //      /worn legally <layer>
    //      /unworn legally <layer>
    //      /attached legally <attach_point_name>
    //      /attached illegally <attach_point_name>
    //      /detached legally <attach_point_name>
    //      /detached illegally <attach_point_name>
    // Allow/deny permissive exceptions: @permissive=y/n RLV,PERMISSIVE,y/n
    // Clear all rules tied to an object: @clear
    // Clear a subset of the rules tied to an object: @clear=<string>
    // RPG: Get the list of restrictions the avatar is currently submitted to: @getstatus[:<part_of_rule>[;<custom_separator>]]=<channel>
    // Get the list of ALL restrictions the avatar is currently submitted to: @getstatusall[:<part_of_rule>[;<custom_separator>]]=<channel>
    // --- MOVEMENT
    // RPG: Allow/prevent flying @fly=<y/n>
    // Allow/prevent run by double-tap an arrow: @temprun=<y/n>
    // RPG: Allow/prevent always running: @alwaysrun=<y/n>
    // Force rotate the avatar to a set direction: @setrot=<angle_in_radians>=force
    // BROKEN w/SSA Change the height of the avatar: @adjustheight:<distance_pelvis_to_foot_in_meters>;<factor>[;delta_in_meters]=force
    // --- CHAT
    // Allow/prevent sending chat: @sendchat=<y/n> - blocks channel 0, / works, /me 30 chars, /message 15 chars, punctuation blocked, . ends the string
    // Allow/prevent shouting: @chatshout=<y/n> - forces llShout to llSay range
    // Allow/prevent chatting at normal volume: @chatnormal=<y/n> - forces llSay to llWhisper range
    // Allow/prevent whispering: @chatwhisper=<y/n> - force whisper to llSay range
    // RPG: Redirect public chat to private channels: @redirchat:<channel>=<rem/add> -- turn ON OFF with Emoter control
    // Allow/prevent receiving chat messages : "@recvchat=<y/n>" 
    // Allow/prevent receiving chat messages, secure way : "@recvchat_sec=<y/n>" 
    // Remove/add exceptions to the chat message receiving prevention : "@recvchat:<UUID>=<rem/add>"
    // Allow/prevent receiving chat messages from someone in particular : "@recvchatfrom:<UUID>=<y/n>"
    // --- EMOTES
    // Remove/add an exception to the emote truncation above : "@emote=<rem/add>" 
    // Redirect public emotes to private channels : "@rediremote:<channel_number>=<rem/add>" 
    // Allow/prevent seeing emotes : "@recvemote=<y/n>" 
    // Allow/prevent receiving emotes seen in public chat from someone in particular : "@recvemotefrom:<UUID>=<y/n>" 
    // Allow/prevent seeing emotes, secure way : "@recvemote_sec=<y/n>" 
    // Remove/add exceptions to the emote seeing prevention : "@recvemote:<UUID>=<rem/add>"
    // --- PRIVATE CHANNELS
    // RPG: Allow/prevent using any chat channel but certain channels : @sendchannel[:<channel>]=<y/n> - only allow Myriad channels
    // RPG: Allow/prevent using any chat channel but certain channels, secure way : @sendchannel_sec[:<channel>]=<y/n>
    // --- INSTANT MESSAGES 
    // RPG: Allow/prevent sending instant messages : "@sendim=<y/n>" 
    // RPG: Allow/prevent sending instant messages, secure way : "@sendim_sec=<y/n>" 
    // Remove/add exceptions to the instant message sending prevention : "@sendim:<UUID>=<rem/add>" 
    // RPG: Allow/prevent sending instant messages to someone in particular : "@sendimto:<UUID>=<y/n>" - allow to region staff
    // Allow/prevent starting an IM session with anyone : "@startim=<y/n>" 
    // Remove/add exceptions to the IM session start prevention : "@startim:<UUID>=<rem/add>" 
    // RPG: Allow/prevent starting an IM session with someone in particular : "@startimto:<UUID>=<y/n>" -- allow to region staff
    // RPG: Allow/prevent receiving instant messages : "@recvim=<y/n>" 
    // RPG: Allow/prevent receiving instant messages, secure way : "@recvim_sec=<y/n>"
    // Remove/add exceptions to the instant message receiving prevention : "@recvim:<UUID>=<rem/add>" 
    // RPG: Allow/prevent receiving instant messages from someone in particular : "@recvimfrom:<UUID>=<y/n>"  - allow from Region staff
    // --- TELEPORT
    // RPG: Allow/prevent teleporting to a landmark : "@tplm=<y/n>"  - enable TP in OOC, disable in IC
    // RPG: Allow/prevent teleporting to a location : "@tploc=<y/n>" - enable TP in OOC, disable in IC
    // RPG: Allow/prevent teleporting by a friend : "@tplure=<y/n>" 
    // RPG: Allow/prevent teleporting by a friend, secure way : "@tplure_sec=<y/n>"
    // Remove/add exceptions to the friend teleport prevention : "@tplure:<UUID>=<rem/add>"
    // RPG: Unlimit/limit sit-tp : "@sittp=<y/n>"
    // RPG: Allow/prevent standing up at a different location than where we sat down : @standtp=<y/n> 
    // Force-Teleport the user : @tpto:<X>/<Y>/<Z>=force (*)
    // RPG: Remove/add auto-accept teleport offers from a particular avatar : "@accepttp[:<UUID>]=<rem/add>" -- region owner and RPG staff?
    // --- INVENTORY
    // RPG: Allow/prevent using inventory : @showinv=<y/n>
    // RPG: Allow/prevent reading notecards : @viewnote=<y/n>
    // RPG: Allow/prevent opening scripts : @viewscript=<y/n> 
    // RPG: Allow/prevent opening textures : @viewtexture=<y/n> 
    // RPG: Allow/prevent editing objects : "@edit=<y/n>" 
    // Remove/add exceptions to the edit prevention : "@edit:<UUID>=<rem/add>" 
    // RPG: Allow/prevent rezzing inventory : "@rez=<y/n>" 
    // Allow/prevent editing particular objects : "@editobj:<UUID>=<y/n>" 
    // --- SITTING
    // Allow/prevent standing up : @unsit=<y/n> 
    // Force sit on an object : @sit:<UUID>=force (*) 
    // Get the UUID of the object the avatar is sitting on : @getsitid=<channel_number> 
    // Force unsit : @unsit=force (*)
    // Allow/prevent sitting down : @sit=<y/n> 
    // --- CLOTHING
    // Render an object detachable/nondetachable : "@detach=<y/n>" 
    // Unlock/Lock an attachment point : "@detach:<attach_point_name>=<y/n>" 
    // Unlock/Lock an attachment point empty : "@addattach[:<attach_point_name>]=<y/n>" 
    // Unlock/Lock an attachment point full : "@remattach[:<attach_point_name>]=<y/n>" 
    // Allow/deny the "Wear" contextual menu : "@defaultwear=<y/n> 
    // Force removing attachments : @detach[:attachpt]=force (*) 
    //      chest|skull|left shoulder|right shoulder|left hand|right hand|left foot|right foot|spine|
    //      pelvis|mouth|chin|left ear|right ear|left eyeball|right eyeball|nose|r upper arm|r forearm|
    //      l upper arm|l forearm|right hip|r upper leg|r lower leg|left hip|l upper leg|l lower leg|stomach|left pec|
    //      right pec|center 2|top right|top|top left|center|bottom left|bottom|bottom right|neck|root
    //      If part is not specified, removes everything. 
    // Force removing attachments (alias) : @remattach[:attachpt]=force (*) 
    // Allow/prevent wearing clothes : @addoutfit[:<part>]=<y/n> 
    //       gloves|jacket|pants|shirt|shoes|skirt|socks|underpants|undershirt|skin|eyes|hair|shape|alpha|tattoo|physics
    //       If part is not specified, prevents from wearing anything beyond what the avatar is already wearing. 
    // Allow/prevent removing clothes : @remoutfit[:<part>]=<y/n> (underpants and undershirt are kept for teens) 
    //      gloves|jacket|pants|shirt|shoes|skirt|socks|underpants|undershirt|skin|eyes|hair|shape|alpha|tattoo|physics
    //      If part is not specified, prevents from removing anything in what the avatar is wearing
    // Force removing clothes : @remoutfit[:<part>]=force (*) (teens can't be forced to remove underpants and undershirt)
    //      gloves|jacket|pants|shirt|shoes|skirt|socks|underpants|undershirt|alpha|tattoo|physics
    //      If part is not specified, removes everything. 
    // Get the list of worn clothes : @getoutfit[:part]=<channel_number> 
    //      gloves,jacket,pants,shirt,shoes,skirt,socks,underpants,undershirt,skin,eyes,hair,shape
    //      gloves,jacket,pants,shirt,shoes,skirt,socks,underpants,undershirt,skin,eyes,hair,shape,alpha,tattoo 
    // Get the list of worn attachments : @getattach[:attachpt]=<channel_number> 
    //     none,chest,skull,left shoulder,right shoulder,left hand,right hand,left foot,right foot,spine,
    //     pelvis,mouth,chin,left ear,right ear,left eyeball,right eyeball,nose,r upper arm,r forearm,
    //     l upper arm,l forearm,right hip,r upper leg,r lower leg,left hip,l upper leg,l lower leg,stomach,left pec,
    //     right pec,center 2,top right,top,top left,center,bottom left,bottom,bottom right,neck,root
    // Force the viewer to automatically accept attach and take control permission requests : @acceptpermission=<rem/add> 
    // Allow/prevent accepting attach and take control permissions : @denypermission=<rem/add> 
    // Force detach an item : @detachme=force (*) 
    // --- SHARED FOLDERS
    // Allow/prevent wearing clothes and attachments that are not part of the #RLV folder : @unsharedwear=<y/n> 
    // Allow/prevent removing clothes and attachments that are not part of the #RLV folder : @unsharedunwear=<y/n> 
    // Get the list of shared folders in the avatar's inventory : @getinv[:folder1/.../folderN]=<channel_number> 
    // Get the list of shared folders in the avatar's inventory, with information about worn items : @getinvworn[:folder1/.../folderN]=<channel_number> 
    // Get the path to a shared folder by giving a search criterion : @findfolder:part1[&&...&&partN]=<channel_number> 
    // Force attach items contained inside a shared folder : @attach:<folder1/.../folderN>=force (*) 
    // Force attach items contained inside a shared folder, without replacing what is already being worn : @attachover:<folder1/.../folderN>=force (*) 
    // Force attach items contained inside a shared folder : @attachoverorreplace:<folder1/.../folderN>=force (*) 
    // Force attach items contained inside a shared folder, and its children recursively : @attachall:<folder1/.../folderN>=force (*) 
    // Force attach items contained inside a shared folder, and its children recursively, without replacing what is already being worn : @attachallover:<folder1/.../folderN>=force (*) 
    // Force attach items contained inside a shared folder, and its children recursively : @attachalloverorreplace:<folder1/.../folderN>=force (*) 
    // Force detach items contained inside a shared folder : @detach:<folder_name>=force (*) 
    // Force detach items contained inside a shared folder, and its children recursively : @detachall:<folder1/.../folderN>=force (*) 
    // Get the path to the shared folder containing a particular object/clothing worn on a point : @getpath[:<attachpt> or <clothing_layer>]=<channel_number> 
    // Get the all paths to the shared folders containing the objects/clothing worn on a point : @getpathnew[:<attachpt> or <clothing_layer>]=<channel_number> 
    // Force attach items contained into a shared folder that contains a particular object/clothing : @attachthis[:<attachpt> or <clothing_layer>]=force (*) 
    // Force attach items contained inside a shared folder, without replacing what is already being worn : @attachthisover[:<attachpt> or <clothing_layer>]=force (*) 
    // Force attach items contained inside a shared folder : @attachthisoverorreplace[:<attachpt> or <clothing_layer>]=force (*) 
    // Force attach items contained into a shared folder that contains a particular object/clothing, and its children folders : @attachallthis[:<attachpt> or <clothing_layer>]=force (*) 
    // Force attach items contained inside a shared folder, without replacing what is already being worn : @attachallthisover[:<attachpt> or <clothing_layer>]=force (*) 
    // Force attach items contained inside a shared folder : @attachallthisoverorreplace[:<attachpt> or <clothing_layer>]=force (*) 
    // Force detach items contained into a shared folder that contains a particular object/clothing : @detachthis[:<attachpt> or <clothing_layer>]=force (*) 
    // Force detach items contained into a shared folder that contains a particular object/clothing, and its children folders : @detachallthis[:<attachpt> or <clothing_layer>]=force (*) 
    // Allow/prevent removing some folders : @detachthis[:<layer>|<attachpt>|<path_to_folder>]=<y/n> 
    // Allow/prevent removing some folders and their children : @detachallthis[:<layer>|<attachpt>|<path_to_folder>]=<y/n> 
    // Allow/prevent wearing some folders : @attachthis:<layer>|<attachpt>|<path_to_folder>=<y/n> 
    // Allow/prevent wearing some folders and their children : @attachallthis[:<layer>|<attachpt>|<path_to_folder>]=<y/n> 
    // Remove/add exceptions to the detachallthis restriction, for one folder only : "@detachthis_except:<folder>=<rem/add>" 
    // Remove/add exceptions to the detachallthis restriction, for one folder and its children : "@detachallthis_except:<folder>=<rem/add>" 
    // Remove/add exceptions to the attachallthis restriction, for one folder only : "@attachthis_except:<folder>=<rem/add>" 
    // Remove/add exceptions to the attachallthis restriction, for one folder and its children : "@attachallthis_except:<folder>=<rem/add>" 
    // --- TOUCH
    // RPG: Allow/prevent touching objects located further than 1.5 meters away from the avatar : @fartouch=<y/n> 
    // Allow/prevent touching objects located further than 1.5 meters away from the avatar : @touchfar=<y/n> 
    // Allow/prevent touching any objects : @touchall=<y/n> 
    // Allow/prevent touching objects in-world : @touchworld=<y/n> 
    // Remove/add exceptions to the touchworld prevention : "@touchworld:<UUID>=<rem/add>" 
    // Allow/prevent touching one object in particular : @touchthis:<UUID>=<rem/add> 
    // Remove/add an exception to the touch* preventions, for one object only : "@touchme=<rem/add>" 
    // Allow/prevent touching attachments : @touchattach=<y/n> 
    // Allow/prevent touching one's attachments : @touchattachself=<y/n> 
    // ???: Allow/prevent touching other people's attachments : @touchattachother=<y/n>  
    // --- LOCATION
    // RPG: Allow/prevent viewing the world map : @showworldmap=<y/n>  -- only allow when holding MAP item
    // RPG: Allow/prevent viewing the mini map : @showminimap=<y/n> -- only allow when holding SCANNER item
    // RPG: Allow/prevent knowing the current location : @showloc=<y/n> 
    // --- NAME TAGS
    // RPG: Allow/prevent seeing the names of the people around : @shownames=<y/n> 
    // RPG: Allow/prevent seeing all the hovertexts : @showhovertextall=<y/n> 
    // RPG: Allow/prevent seeing one hovertext in particular : @showhovertext:<UUID>=<y/n>  -- allow see region owner and staff?
    // RPG: Allow/prevent seeing the hovertexts on the HUD of the user : @showhovertexthud=<y/n> 
    // RPG: Allow/prevent seeing the hovertexts in-world : @showhovertextworld=<y/n> 
    // --- GROUP
    // RPG: Force the agent to change the active group : @setgroup:<group_name>=force 
    // RPG: Allow/prevent activating a group : @setgroup=<y/n> 
    // Get the name of the active group : @getgroup=<channel_number> 
    // --- VIEWER
    // Allow/prevent changing some debug settings : @setdebug=<y/n> 
    // Force change a debug setting : @setdebug_<setting>:<value>=force (*) 
    // Get the value of a debug setting : @getdebug_<setting>=<channel_number>
    // RPG: Allow/prevent changing the environment settings : @setenv=<y/n>      
    // Allow/prevent changing the environment settings : @setenv=<y/n> 
    // RPG: Force change an environment setting : @setenv_<setting>:<value>=force (*) -- set world, drug effects, incapacitated and dead effects and more
    //     _daytime     0.0-1.0 and <0     Time of day (sunrise:0.25, midday:0.567, sunset:0.75, midnight:0.0, set back to region default:<0). Attention, resets all other Windlight parameters
    //     _preset     String     A Preset environment, e.g. Gelatto, Foggy. Attention, loading a Preset is heavy on the viewer and can slow it down for a short while, don't do it every second
    //     _ambientr     0.0-1.0     Ambient light, Red channel
    //     _ambientg     0.0-1.0     Ambient light, Green channel
    //     _ambientb     0.0-1.0     Ambient light, Blue channel
    //     _ambienti     0.0-1.0     Ambient light, Intensity
    //     _bluedensityr     0.0-1.0     Blue Density, Red channel
    //     _bluedensityg     0.0-1.0     Blue Density, Green channel
    //     _bluedensityb     0.0-1.0     Blue Density, Blue channel
    //     _bluedensityi     0.0-1.0     Blue Density, Intensity
    //     _bluehorizonr     0.0-1.0     Blue Horizon, Red channel
    //     _bluehorizong     0.0-1.0     Blue Horizon, Green channel
    //     _bluehorizonb     0.0-1.0     Blue Horizon, Blue channel
    //     _bluehorizoni     0.0-1.0     Blue Horizon, Intensity
    //     _cloudcolorr     0.0-1.0     Cloud color, Red channel
    //     _cloudcolorg     0.0-1.0     Cloud color, Green channel
    //     _cloudcolorb     0.0-1.0     Cloud color, Blue channel
    //     _cloudcolori     0.0-1.0     Cloud color, Intensity
    //     _cloudcoverage     0.0-1.0     Cloud coverage
    //     _cloudx     0.0-1.0     Cloud offset X
    //     _cloudy     0.0-1.0     Cloud offset Y
    //     _cloudd     0.0-1.0     Cloud density
    //     _clouddetailx     0.0-1.0     Cloud detail X
    //     _clouddetaily     0.0-1.0     Cloud detail Y
    //     _clouddetaild     0.0-1.0     Cloud detail density
    //     _cloudscale     0.0-1.0     Cloud scale
    //     _cloudscrollx     0.0-1.0     Cloud scroll X
    //     _cloudscrolly     0.0-1.0     Cloud scroll Y
    //     _densitymultiplier     0.0-0.9     Density multiplier of the fog
    //     _distancemultiplier     0.0-100.0     Distance multiplier of the fog
    //     _eastangle     0.0-1.0     Position of the east, 0.0 is normal
    //     _hazedensity     0.0-1.0     Density of the haze
    //     _hazehorizon     0.0-1.0     Haze at the horizon
    //     _maxaltitude     0.0-4000.0     Maximum altitude of the fog
    //     _scenegamma     0.0-10.0     Overall gamma, 1.0 is normal
    //     _starbrightness    0.0-2.0     Brightness of the stars
    //     _sunglowfocus     0.0-0.5     Focus of the glow of the sun
    //     _sunglowsize     1.0-2.0     Size of the glow of the sun
    //     _sunmooncolorr     0.0-1.0     Sun and moon, Red channel
    //     _sunmooncolorg     0.0-1.0     Sun and moon, Green channel
    //     _sunmooncolorb     0.0-1.0     Sun and moon, Blue channel
    //     _sunmooncolori     0.0-1.0     Sun and moon, Intensity
    //     _sunmoonposition     0.0-1.0     Position of the sun/moon, different from "daytime", use this to set the apparent sunlight after loading a Preset
    // Get the value of an environment setting : @getenv_<setting>=<channel_number> 
 
// CONSTANTS - DO NOT CHANGE DURING RUN
string BASENAME = "Myriad Lite Module RLV";
string VERSION = "0.0.0"; // Allen Kerensky's script version
string VERSIONDATE = "20131024"; // Allen Kerensky's script yyyymmdd
 
// Module to Module Messaging Constants
integer MODULE_RLV = -15;
integer LM_SENDTOATTACHMENT = 0x80000000;
 
// Configurable
integer SPACE_OFFSET = 300; // How high over ground are we to be "in space" FIXME PPMA CONFIG,SPACEALTITIDE,<meters>
 
// Runtime globals - may change anytime during run
integer RLV_CHAN;
integer RLV_HAND;
string RLV_CMD;
integer RLV_AVAIL; // is RLV available?
vector CURPOS; // current X,Y,Z location of avatar
integer GROUND_LEVEL; // the current Z altitude of ground level under the av.
 
// FIXME move to PPMA RLV,<regionname>,<settingname>
//list SETTINGS = [
//    "Myriad_Central", "Fine Day",
//    "Myriad_Combat", "Rot",
//    "Myriad_Medieval", "Alchemy Immortalis - Foggy Morning",
//    "Myriad_Modern", "[EUPHORIA] air pollution 2",
//    "Myriad_Future", "Blizzard",
//    "Lani", "Arrakis",
//    "ixi", "Arrakis",
//    //"Bade Plaza", "",
//    "Lbsa Plaza", "Annyka's Soft Lavender Day",
//    //"Recreation Plaza", "",
//    //"Sandbox Plaza", "",
//    //"Sandbox Plaza II", "",
//    "Seaprior Plaza", "Sailor's Delight",
//    //"Teravus Plaza", "",
//    //"Wright Plaza", "",
//    //"Zaius Plaza", "",
//    //"Jump4000", "",
//    //"Jump8000", "",
//    //"Cuteulala Park", "",
//    "OSgrid Welcome Station", "Default"
//];
 
//
// PPMA GET_VAL
//
string KEY_NOT_FOUND = "[KEY_NOT_FOUND]";
string GET_VAL(string dbkey,string field) {
    //OWNERSAY("GET_VAL KEY=["+dbkey+"] FIELD=["+field+"]");
    string out = KEY_NOT_FOUND;
    integer i;
    string name;
    string desc;
    for ( i = 2; i <= llGetNumberOfPrims(); i++ ) {
        name = llList2String(llGetLinkPrimitiveParams(i,[PRIM_NAME]),0);
        desc = llList2String(llGetLinkPrimitiveParams(i,[PRIM_DESC]),0);
        if ( llToLower(name) == llToLower(dbkey) && llToLower(desc) == llToLower(field) ) {
            out = llList2String(llGetLinkPrimitiveParams(i,[PRIM_TEXT]),0);
            //DEBUG("GET_VAL RETURN=["+out+"]");
            return out; // bail on first match?
        }
    }
    //OWNERSAY("GET_VAL RETURN=["+out+"]");
    return out;
}
 
SET_VAL(string dbkey, string field, string val) {
    //OWNERSAY("SET_VAL KEY=["+dbkey+"] FIELD=["+field+"] VAL=["+val+"]");
    integer i;
    string name;
    string desc;
    integer written = FALSE;
    for ( i = 2; i <= llGetNumberOfPrims(); i++ ) {
        name = llStringTrim(llList2String(llGetLinkPrimitiveParams(i,[PRIM_NAME]),0),STRING_TRIM);
        desc = llStringTrim(llList2String(llGetLinkPrimitiveParams(i,[PRIM_DESC]),0),STRING_TRIM);
        if ( ( llToLower(name) == llToLower(dbkey) && llToLower(desc) == llToLower(field) ) && written == FALSE ) {
            //OWNERSAY("SET_VAL UPDATE RECORD=["+(string)i+"] DBKEY=["+dbkey+"] DESC=["+field+"] VAL=["+val+"]");
            llSetLinkPrimitiveParamsFast(i,[PRIM_NAME,dbkey,PRIM_DESC,field,PRIM_TEXT,val,ZERO_VECTOR,0.0]);
            written = TRUE; // we did an update, remember it
        }    
    }
    if ( written == TRUE ) {
        //OWNERSAY("SET_VAL UPDATE COMPLETE.");
        return;
    }
    // data hasn't been written, scan for free prim
    for ( i = 2; i <= llGetNumberOfPrims(); i++ ) {
        name = llStringTrim(llList2String(llGetLinkPrimitiveParams(i,[PRIM_NAME]),0),STRING_TRIM);
        desc = llStringTrim(llList2String(llGetLinkPrimitiveParams(i,[PRIM_DESC]),0),STRING_TRIM);
        if ( ( name == "" && desc == "" ) && written == FALSE ) {
            //OWNERSAY("SET_VAL INSERT RECORD=["+(string)i+"] DBKEY=["+dbkey+"] DESC=["+field+"] VAL=["+val+"]");
            llSetLinkPrimitiveParamsFast(i,[PRIM_NAME,dbkey,PRIM_DESC,field,PRIM_TEXT,val,ZERO_VECTOR,0.0]);
            written = TRUE; // we did an update, remember it
        }
    }
    if ( written == TRUE ) {
        //OWNERSAY("SET_VAL INSERT COMPLETED.");
        return;
    }
    //OWNERSAY("SET_VAL NO FREE RECORD FOUND TO INSERT INTO! DATA LOST");
}
 
//////////////////////////////////////////////////////////////////////////////
// FLAG FUNCTIONS
// INCAPACITATED - lost wounds
// DEAD - lost critical wounds
// DEBUG - show debug messages or not
// IDEA: INDECISION - lost resolve?
 
// LMIM: SET_FLAG|<name>=<TRUE|FALSE>
// PPMA: FLAG,<flagname>,<TRUE|FALSE>
// LMOUT: FLAG|<flagname>=<TRUE|FALSE>
// LMERR: FIXME
integer GET_FLAG(string flag) {
    string val = GET_VAL("FLAG",llToUpper(flag));
    if ( val == KEY_NOT_FOUND ) {
        ERROR("Flag ["+flag+"] does not exist.");
    }
    if ( val != "0" && val != "1") {
        ERROR("Flag: "+flag+" invalid value ["+val+"]");
    }
    integer bool = (integer)val;
    return bool;
}
 
// SET_FLAG
// LMIM: SET_FLAG|<flagname>=<TRUE|FALSE>
// PPMA: FLAG,<flagname>,<TRUE|FALSE>
// LMOUT: SET_FLAG|<flagname>=<TRUE|FALSE>
// LMERR: FIXME
SET_FLAG(string flag,integer value) {
    if ( flag == "" ) return; // FIXME add error
    if ( value != TRUE && value != FALSE ) return; // FIXME add err
    SET_VAL("FLAG",llToUpper(flag),(string)value);
}
 
//
// GET_RLVREGION - check PPMA for RLV,<regionname>,<setting>
//
string GET_RLV(string setting) {
    if ( setting == "" ) {
        ERROR("GET_RLV passed empty setting");
        return "";
    }
    string value = GET_VAL("RLV",setting);
    if ( value == KEY_NOT_FOUND ) value = "";
    return value;
}
 
//
// SET_RLV
//
SET_RLV(string setting,string value) {
    if ( setting == "" || value == "" ) {
        ERROR("SET_RLV passed empty setting or value.");
        return;
    }
    SET_RLV(setting,value);
}
 
// DEBUG - show debug chat with wearer name for sorting
DEBUG(string dmessage) {
    if ( GET_FLAG("DEBUG") == TRUE ) llMessageLinked(LINK_THIS,MODULE_RLV,"DEBUG|"+dmessage,llGetOwner());
}
 
// ERROR - show errors on debug channel with wearer name for sorting
ERROR(string emessage) {
    llMessageLinked(LINK_THIS,MODULE_RLV,"ERROR|"+emessage,llGetOwner());
}
 
// MEMORY
GET_MEMORY() {
    OWNERSAY(BASENAME+" free memory: "+(string)llGetFreeMemory()); // show this module's free memory info
}
 
// GETVERSION
GET_VERSION() {
    OWNERSAY(BASENAME+" v"+VERSION+"-"+VERSIONDATE); // show this module's version info
}
 
// HELP
HELP(string help) {
    llMessageLinked(LINK_THIS,MODULE_RLV,"HELP|"+help,llGetOwner());
}
 
OWNERSAY(string str) {
    llMessageLinked(LINK_THIS,MODULE_RLV,"OWNERSAY|"+str,llGetOwner());
}
 
PARSE(string message,key id) {
    DEBUG("PARSE message=["+message+"] id=["+(string)id+"]");
    // Debug handled in say and link events
    // First - handle type 1 messages that do not require breaking down
    list tokens = llParseString2List(message,["|"],[]);
    string cmd = llToLower(llStringTrim(llList2String(tokens,0),STRING_TRIM));
    //string data = llList2String(tokens,1);
    //list subtokens = llParseString2List(data,["="],[]);
    //string attrib = llList2String(subtokens,0);
    //integer idata = llList2Integer(subtokens,1);
    //string sdata = llList2String(subtokens,1);
 
    if ( cmd == "memory" ) { GET_MEMORY(); return; }
    if ( cmd == "reset" ) { RESET(); return;}
    if ( cmd == "version" ) { GET_VERSION(); return;}
 
    // REMOVEME all GET_* command blocks once PPMA is integrated in all HUD scripts    
    if ( cmd == "rlv_ic" ) { RLV_IC(); return;} // switch to IC mode
    if ( cmd == "rlv_ooc" ) { RLV_OOC(); return;} // switch to OOC mode
    if ( cmd == "rlv_version" ) { RLV_VERSION(); return;}
    if ( cmd == "rlv_versionnew" ) { RLV_VERSIONNEW(); return;}
    if ( cmd == "rlv_versionnum" ) { RLV_VERSIONNUM(); return;}
} 
 
//
// RESET
//
RESET() {
    // do anything to clean up before reset
    llResetScript();
}
 
RLV_CLOSE() {
    if (RLV_HAND != 0) llListenRemove(RLV_HAND);
    RLV_CHAN = -1;
    llSetTimerEvent(0.0);
    RLV_CMD = "";
}
 
RLV_IC() {
    // FIXME region tool to enable OOC and in IC - location narrator?
    //if ( RLV_AVAIL == TRUE ) {
        list details = llGetParcelDetails(<0,0,0>,[PARCEL_DETAILS_OWNER]);
        key parcelowner = llList2String(details,0);        
        llOwnerSay("@fly=n"); // RPG: Allow/prevent flying @fly=<y/n>
        llOwnerSay("@alwaysrun=n"); // RPG: Allow/prevent always running: @alwaysrun=<y/n>
        llOwnerSay("@redirchat:44=add"); // RPG: Redirect public chat to private channels: @redirchat:<channel>=<rem/add>
        // llOwnerSay("@sendchannel:<channel>=y"); // RPG: Allow/prevent using any chat channel but certain channels : @sendchannel[:<channel>]=<y/n> -- FIXME only allow Myriad channels
        // llOwnerSay("@sendchannel:<channe>=y"); // RPG: Allow/prevent using any chat channel but certain channels, secure way : @sendchannel_sec[:<channel>]=<y/n> -- FIXME only allow Myriad channels
        llOwnerSay("@sendim=n"); // RPG: Allow/prevent sending instant messages : "@sendim=<y/n>" 
        llOwnerSay("@sendim_sec=n"); // RPG: Allow/prevent sending instant messages, secure way : "@sendim_sec=<y/n>" 
        llOwnerSay("@sendimto:"+(string)parcelowner+"=y"); // RPG: Allow/prevent sending instant messages to someone in particular : "@sendimto:<UUID>=<y/n>" -- FIXME allow to region owner/staff
        llOwnerSay("@startimto:"+(string)parcelowner+"=y"); // RPG: Allow/prevent starting an IM session with someone in particular : "@startimto:<UUID>=<y/n>" -- FIXME allow to region owner/staff
        llOwnerSay("@recvim=n"); // RPG: Allow/prevent receiving instant messages : "@recvim=<y/n>" 
        llOwnerSay("@recvim_sec=n"); // RPG: Allow/prevent receiving instant messages, secure way : "@recvim_sec=<y/n>"
        llOwnerSay("@recvimfrom:"+(string)parcelowner+"=y"); // RPG: Allow/prevent receiving instant messages from someone in particular : "@recvimfrom:<UUID>=<y/n>"  - FIXME allow from region owner/staff
        llOwnerSay("@tplm=n"); // RPG: Allow/prevent teleporting to a landmark : "@tplm=<y/n>" 
        llOwnerSay("@tploc=n"); // RPG: Allow/prevent teleporting to a location : "@tploc=<y/n>"
        llOwnerSay("@tplure=n"); // RPG: Allow/prevent teleporting by a friend : "@tplure=<y/n>"
        llOwnerSay("@tplure_sec=n"); // RPG: Allow/prevent teleporting by a friend, secure way : "@tplure_sec=<y/n>" 
        llOwnerSay("@sittp=n"); // RPG: Unlimit/limit sit-tp : "@sittp=<y/n>" 
        llOwnerSay("@standtp=n"); // RPG: Allow/prevent standing up at a different location than where we sat down : @standtp=<y/n> 
        llOwnerSay("@accepttp:"+(string)parcelowner+"=add"); // RPG: Remove/add auto-accept teleport offers from a particular avatar : "@accepttp[:<UUID>]=<rem/add>" -- FIXME allow from region owner/staff
        llOwnerSay("@showinv=n"); // RPG: Allow/prevent using inventory : @showinv=<y/n>
        llOwnerSay("@viewnote=n"); // RPG: Allow/prevent reading notecards : @viewnote=<y/n>
        llOwnerSay("@viewscript=n"); // RPG: Allow/prevent opening scripts : @viewscript=<y/n> 
        llOwnerSay("@viewtexture=n"); // RPG: Allow/prevent opening textures : @viewtexture=<y/n> 
        llOwnerSay("@edit=n"); // RPG: Allow/prevent editing objects : "@edit=<y/n>" 
        llOwnerSay("@rez=n"); // RPG: Allow/prevent rezzing inventory : "@rez=<y/n>" -- FIXME TEST IF THIS AFFECTS BULLETS
        llOwnerSay("@fartouch=n"); // RPG: Allow/prevent touching objects located further than 1.5 meters away from the avatar : @fartouch=<y/n> 
        llOwnerSay("@showworldmap=n"); // RPG: Allow/prevent viewing the world map : @showworldmap=<y/n>  -- FIXME only allow when holding MAP item
        llOwnerSay("@showminimap=n"); // RPG: Allow/prevent viewing the mini map : @showminimap=<y/n> -- FIXME only allow when holding SCANNER item
        llOwnerSay("@showloc=n"); // RPG: Allow/prevent knowing the current location : @showloc=<y/n>
        llOwnerSay("@shownames=n"); // RPG: Allow/prevent seeing the names of the people around : @shownames=<y/n> 
        llOwnerSay("@showhovertextall=n"); // RPG: Allow/prevent seeing all the hovertexts : @showhovertextall=<y/n> 
        llOwnerSay("@showhovertext:"+(string)parcelowner+"=y"); // RPG: Allow/prevent seeing one hovertext in particular : @showhovertext:<UUID>=<y/n>  -- FIXME allow see region owner/staff
        llOwnerSay("@showhovertexthud=n"); // RPG: Allow/prevent seeing the hovertexts on the HUD of the user : @showhovertexthud=<y/n> -- FIXME work around for BAM status?
        llOwnerSay("@showhovertextworld=n"); // RPG: Allow/prevent seeing the hovertexts in-world : @showhovertextworld=<y/n> 
        llOwnerSay("@setgroup:Myriad RPG=force"); // RPG: Force the agent to change the active group : @setgroup:<group_name>=force // FIXME get current group - switch to whatever parcel group is
        llOwnerSay("@setgroup=n"); // RPG: Allow/prevent activating a group : @setgroup=<y/n>
        llOwnerSay("@setenv=n"); // RPG: Allow/prevent changing the environment settings : @setenv=<y/n> 
        // llOwnerSay("@setenv_preset:<presetname> =force"); // RPG: Force change an environment setting : @setenv_<setting>:<value>=force (*) -- FIXME set world, drug, incapacitated, dead, magic  effects
 
        OWNERSAY("Myriad Lite RLV IC Mode enabled");
    //}
}
 
RLV_OOC() {
    // FIXME region tool to enable OOC and in IC - location narrator?
    //if ( RLV_AVAIL == TRUE ) {
        list details = llGetParcelDetails(<0,0,0>,[PARCEL_DETAILS_OWNER]);
        key parcelowner = llList2String(details,0);        
        llOwnerSay("@fly=y"); // RPG: Allow/prevent flying @fly=<y/n>
        llOwnerSay("@alwaysrun=y"); // RPG: Allow/prevent always running: @alwaysrun=<y/n>
        llOwnerSay("@redirchat:44=rem"); // RPG: Redirect public chat to private channels: @redirchat:<channel>=<rem/add>
        // llOwnerSay("@sendchannel:<channel>=y"); // RPG: Allow/prevent using any chat channel but certain channels : @sendchannel[:<channel>]=<y/n> -- FIXME only allow Myriad channels
        // llOwnerSay("@sendchannel:<channe>=y"); // RPG: Allow/prevent using any chat channel but certain channels, secure way : @sendchannel_sec[:<channel>]=<y/n> -- FIXME only allow Myriad channels
        llOwnerSay("@sendim=y"); // RPG: Allow/prevent sending instant messages : "@sendim=<y/n>" 
        llOwnerSay("@sendim_sec=y"); // RPG: Allow/prevent sending instant messages, secure way : "@sendim_sec=<y/n>" 
        //llOwnerSay("@sendimto:"+(string)parcelowner+"=y"); // RPG: Allow/prevent sending instant messages to someone in particular : "@sendimto:<UUID>=<y/n>" -- FIXME allow to region owner/staff
        //llOwnerSay("@startimto:"+(string)parcelowner+"=y"); // RPG: Allow/prevent starting an IM session with someone in particular : "@startimto:<UUID>=<y/n>" -- FIXME allow to region owner/staff
        llOwnerSay("@recvim=y"); // RPG: Allow/prevent receiving instant messages : "@recvim=<y/n>" 
        llOwnerSay("@recvim_sec=y"); // RPG: Allow/prevent receiving instant messages, secure way : "@recvim_sec=<y/n>"
        //llOwnerSay("@recvimfrom:"+(string)parcelowner+"=y"); // RPG: Allow/prevent receiving instant messages from someone in particular : "@recvimfrom:<UUID>=<y/n>"  - FIXME allow from region owner/staff
        llOwnerSay("@tplm=y"); // RPG: Allow/prevent teleporting to a landmark : "@tplm=<y/n>" 
        llOwnerSay("@tploc=y"); // RPG: Allow/prevent teleporting to a location : "@tploc=<y/n>"
        llOwnerSay("@tplure=y"); // RPG: Allow/prevent teleporting by a friend : "@tplure=<y/n>"
        llOwnerSay("@tplure_sec=y"); // RPG: Allow/prevent teleporting by a friend, secure way : "@tplure_sec=<y/n>" 
        llOwnerSay("@sittp=y"); // RPG: Unlimit/limit sit-tp : "@sittp=<y/n>" 
        llOwnerSay("@standtp=y"); // RPG: Allow/prevent standing up at a different location than where we sat down : @standtp=<y/n> 
        llOwnerSay("@accepttp:"+(string)parcelowner+"=rem"); // RPG: Remove/add auto-accept teleport offers from a particular avatar : "@accepttp[:<UUID>]=<rem/add>" -- FIXME allow from region owner/staff
        llOwnerSay("@showinv=y"); // RPG: Allow/prevent using inventory : @showinv=<y/n>
        llOwnerSay("@viewnote=y"); // RPG: Allow/prevent reading notecards : @viewnote=<y/n>
        llOwnerSay("@viewscript=y"); // RPG: Allow/prevent opening scripts : @viewscript=<y/n> 
        llOwnerSay("@viewtexture=y"); // RPG: Allow/prevent opening textures : @viewtexture=<y/n> 
        llOwnerSay("@edit=y"); // RPG: Allow/prevent editing objects : "@edit=<y/n>" 
        llOwnerSay("@rez=y"); // RPG: Allow/prevent rezzing inventory : "@rez=<y/n>" -- FIXME TEST IF THIS AFFECTS BULLETS
        llOwnerSay("@fartouch=y"); // RPG: Allow/prevent touching objects located further than 1.5 meters away from the avatar : @fartouch=<y/n> 
        llOwnerSay("@showworldmap=y"); // RPG: Allow/prevent viewing the world map : @showworldmap=<y/n>  -- FIXME only allow when holding MAP item
        llOwnerSay("@showminimap=y"); // RPG: Allow/prevent viewing the mini map : @showminimap=<y/n> -- FIXME only allow when holding SCANNER item
        llOwnerSay("@showloc=y"); // RPG: Allow/prevent knowing the current location : @showloc=<y/n>
        llOwnerSay("@shownames=y"); // RPG: Allow/prevent seeing the names of the people around : @shownames=<y/n> 
        llOwnerSay("@showhovertextall=y"); // RPG: Allow/prevent seeing all the hovertexts : @showhovertextall=<y/n> 
        // llOwnerSay("@showhovertext:"+(string)parcelowner+"=y"); // RPG: Allow/prevent seeing one hovertext in particular : @showhovertext:<UUID>=<y/n>  -- FIXME allow see region owner/staff
        llOwnerSay("@showhovertexthud=y"); // RPG: Allow/prevent seeing the hovertexts on the HUD of the user : @showhovertexthud=<y/n> -- FIXME work around for BAM status?
        llOwnerSay("@showhovertextworld=y"); // RPG: Allow/prevent seeing the hovertexts in-world : @showhovertextworld=<y/n> 
        //llOwnerSay("@setgroup:Myriad RPG=force"); // RPG: Force the agent to change the active group : @setgroup:<group_name>=force 
        llOwnerSay("@setgroup=y"); // RPG: Allow/prevent activating a group : @setgroup=<y/n>
        llOwnerSay("@setenv=y"); // RPG: Allow/prevent changing the environment settings : @setenv=<y/n> 
        // llOwnerSay("@setenv_preset:<presetname> =force"); // RPG: Force change an environment setting : @setenv_<setting>:<value>=force (*) -- FIXME set world, drug, incapacitated, dead, magic  effects
 
        OWNERSAY("Myriad Lite RLV OOC Mode enabled");
    //}
}
 
RLV_OPEN() {
    RLV_CLOSE();
    RLV_HAND = llListen(RLV_CHAN,"",NULL_KEY,"");
    llSetTimerEvent(60.0);
}
 
//////////////////////////////////////////////////////////////////////////////
// RLV TPTO FORCE TELEPORT EXAMPLE
// Listens on channel 4 for local coordinates and a sim name
// and tells your viewer to teleport you there.
//
// By Marine Kelley 2008-08-26
// RLV version required : 1.12 and above
//
// HOW TO USE :
//   * Create a script inside a box
//   * Overwrite the contents of the script with this one
//   * Wear the box
//   * Say the destination coords Region/X/Y/Z on channel 4 :
//     Example : /4 Help Island Public/128/128/50
 
//key kRequestHandle; // UUID of the dataserver request
//vector vLocalPos;   // local position extracted from the
 
//Init () {
//  kRequestHandle = NULL_KEY;
//  llListen (4, "", llGetOwner (), "");
//}
 
//default {
//  state_entry () {
//    Init ();
//  }
 
//  on_rez(integer start_param) {
//    Init ();
//  }
 
//  listen(integer channel, string name, key id, string message) {
//    list tokens = llParseString2List (message, ["/"], []);
//    integer L = llGetListLength (tokens);
 
//    if (L==4) {
//      // Extract local X, Y and Z
//      vLocalPos.x = llList2Float (tokens, 1);
//      vLocalPos.y = llList2Float (tokens, 2);
//      vLocalPos.z = llList2Float (tokens, 3);
 
      // Request info about the sim
//      kRequestHandle=llRequestSimulatorData (llList2String (tokens, 0), DATA_SIM_POS);
//    }
//  }
 
//  dataserver(key queryid, string data) {
//    if (queryid == kRequestHandle) {
      // Parse the dataserver response (it is a vector cast to a string)
//      list tokens = llParseString2List (data, ["<", ",", ">"], []);
//      string pos_str = "";
//      vector global_pos;
 
      // The coordinates given by the dataserver are the ones of the
      // South-West corner of this sim
      // => offset with the specified local coordinates
//      global_pos.x = llList2Float (tokens, 0);
//      global_pos.y = llList2Float (tokens, 1);
//      global_pos.z = llList2Float (tokens, 2);
//      global_pos += vLocalPos;
 
      // Build the command
//      pos_str =      (string)((integer)global_pos.x)
//                +"/"+(string)((integer)global_pos.y)
//                +"/"+(string)((integer)global_pos.z);
//      llOwnerSay ("Global position : "+(string)pos_str); // Debug purposes
 
      // Fire !
//      llOwnerSay ("@tpto:"+pos_str+"=force");
//    }
//  }
//}
//////////////////////////////////////////////////////////////////////////////
 
RLV_VERSION() {
    RLV_OPEN();
    llOwnerSay("@version="+(string)RLV_CHAN);
}
 
RLV_VERSIONNEW() {
    RLV_OPEN();
    llOwnerSay("@versionnew="+(string)RLV_CHAN);
}
 
RLV_VERSIONNUM() {
    RLV_CMD="versionnum";
    RLV_OPEN();
    llOwnerSay("@versionnum="+(string)RLV_CHAN);
}
 
// RPEVENT
RPEVENT(string rpevent) {
    llMessageLinked(LINK_THIS,MODULE_RLV,"RPEVENT|"+rpevent,llGetOwner());
}
 
SETUP() {
    RLV_VERSIONNEW();    
    RLV_OOC();
    OWNERSAY("RLV module active.");
}
 
SETUP_WINDLIGHT() {
    if ( RLV_AVAIL == TRUE ) {
        // check altitude
        CURPOS = llGetPos();
        GROUND_LEVEL = llFloor(llGround(<0,0,0>));
        if ( CURPOS.z >= ( GROUND_LEVEL + SPACE_OFFSET ) ) {
            OWNERSAY("RestrainedLight configuring Midnight for Space.");
            llOwnerSay("@setenv_preset:midnight=force");
        } else {
            string setting = GET_RLV("RL_"+llGetRegionName());
            if ( setting != "" ) {
                llOwnerSay("@setenv_preset:"+setting+"=force");
                OWNERSAY("RestrainedLight set to "+setting+" for region "+llGetRegionName());
            } else {
                llOwnerSay("@setenv_preset:Default=force");
                OWNERSAY("RestrainedLight set to Default for region "+llGetRegionName());
            }
        }
    } else {
        OWNERSAY("Please enabled RestrainedLove Viewer features to activate RestrainedLight.");
    }
}
 
 
default {
 
    changed(integer changes) {
        if ( ( RLV_AVAIL == TRUE ) && ( ( changes & CHANGED_REGION ) || (changes & CHANGED_TELEPORT ) ) ) {
            SETUP_WINDLIGHT();
        }
    }  
 
    link_message(integer sender_num,integer num,string str,key id) {
        if ( num == MODULE_RLV || num == LM_SENDTOATTACHMENT ) return; // ignore our own messages
        list tokens = llParseString2List(str,["|"],[]);
        string cmd = llToLower(llStringTrim(llList2String(tokens,0),STRING_TRIM));
 
        if ( cmd == "debug" || cmd == "error" || cmd == "help" || cmd == "ownersay" || cmd == "rpevent" ) return; // ignore WELL commands
        // show debug only when not ignoring other commands
        DEBUG("EVENT: link_message("+(string)sender_num+","+(string)num+","+str+","+(string)id+")");             
        PARSE(str,id);
    }
 
    listen(integer channel,string name,key id,string message) {
        DEBUG("EVENT listen channel=["+(string)channel+"] name=["+name+"] id=["+(string)id+"] message=["+message+"]");
        if ( channel != RLV_CHAN ) return; // woken up for message that is not for this module FIXME show error?
        if ( RLV_CMD == "versionnum" ) {
            if ( (integer)message > 2080000 ) {
                RLV_AVAIL = TRUE;
            }
            RLV_CLOSE();
            return;
        }
    }
 
    state_entry() {
        SETUP();
    }
 
    timer() {
        RLV_CLOSE();
    }
}