So any help is greatly appreciated!
//
CODE
// Door Control
//
// *******************************************************************
// This program is free software; you can redistribute it and/or
// modify as you wish. Just don't try and sell it to someone ...
// that would not be nice or fair.
// If you have paid someone for this script .. contact me and we'll
// track them down inworld and harass them ;-)
//
// While I do not offer support for this script if you have questions
// feel free to contact me inworld.
//
// Writen by Shifting Dreamscape
// *******************************************************************
//'Bootstrap' Values ...
//Channel to call the param Reader ... Same for all scripts
integer PARAMETER_APP = -741037;
//My Id for them to return my request on ... unique for all scripts
integer MY_PARAM = -110110;
//The keys I am interested in
list PARAM_KEYS = ["control_channel","response_channel", "dialog_channel", "doorcontrol_chat_channel", "control_key", "door_individ", "user_control_channel", "user_resp_channel", "repeater_channel"];
//The position constants for the list
integer DOOR_CHAN = 0;//Channel the doors talk on
integer DOOR_RESP_CHAN = 1;//For responses fromn the door during setting
integer DIALOG_CHAN = 2;//Dialog Channel to listen on
integer DATA_CHAN = 3;//DataLink 'channel'
integer CONTROL_KEY = 4;//ID of the controller
integer INDIVID_CHAN = 5;//Link channels 'listened' to
integer USER_CHAN = 6;//For calls to the user control
integer USER_RESP_CHAN = 7;//Responses from the user control
integer REPEATER_CHAN = 8;//Channel (link) for talking to the repeater
//Values from Param app
list gParams;
//End Bootstrap Variables
//Parameter Notecard
string CONFIG_NOTECARD = "door_config";
//Notecard read variables
integer gConfigLine;
key gReadConfig;
//Options for doors
list DOOR_OPTIONS = ["Lock", "Unlock"];
//Door data
list gDoors = [];
list gDoorStatus = [];
list gDoorAccess = [];
list gTempDoors = [];
//The index in the list of the door selected
integer gIndex;
//Holds the name of the door while user is being checked
string gHoldDoor;
//Holds the human arranged list of doors ,,
list gDoorOptions;
//Flag if we have called the user control
integer gRequested = C_FALSE;
//For use with repeaters
string gHoldMessage;
string gLastDay;
list gMsgIds = [];
//Hold the key of the user for dialogs
key gCurId;
//Memory Saving Constants?
string EMPTY_STR = "";
string @_STR = "@";
integer C_TRUE = TRUE;
integer C_FALSE = FALSE;
key C_NULL_KEY = NULL_KEY;
//Repeater Functions
//These functions are used to be able to transmit messages further than the current limits of a say
//Apart from using llSay it will as well send a link message that will be picked up by any linked
//repeaters that will then say as well. To avoid duplication messages transmitted this way will have
//an id added to the beginning based on the seconds after midnight (LST). The receiving script will
//check to see if it already received that message and ignore if so, otherwise it will store the id
//in an internal list. As well the object will store the day it last received a message to be able
//to reset the list
//Function to add the id to the message and say/link message it
adv_say(integer channel, string message)
{
integer seconds;
string msgId;
integer length;
integer i;
//The id will ALWAYS be in the format MID##### so we will need to add lead 0's in many cases to the seconds
//Get the time in seconds
seconds = (integer)llGetWallclock();
//now convert to string
msgId = (string)seconds;
//Based on the length add leading 0's
length = llStringLength(msgId);
for (i = length; i < 5; i++)
{
msgId = "0" + msgId;
}
msgId = "MID" + msgId;
//append to message
message = msgId + @_STR + message;
//send out
llSay(channel, message);
llMessageLinked(LINK_ALL_OTHERS, llList2Integer(gParams, REPEATER_CHAN), message, (string)channel);
//Clean Up varianles
msgId = EMPTY_STR;
message = EMPTY_STR;
}//End adv_say
//function that checks the message id .. and manages the lists of ids
integer check_message(string message)
{
list dataList;
string firstElement;
integer result = C_FALSE;
integer msgId;
string today;
//First see if we have a message id
//Split
dataList = llParseString2List(message, [@_STR], [EMPTY_STR]);
//If the first element does not start with MID .. and is not 8 chars long .. it is not a message id and we ignore
firstElement = llList2String(dataList, 0);
if (llStringLength(firstElement) != 8 || llGetSubString(firstElement, 0, 2) != "MID")
{
//No message id so ignore ad we are done
result = C_TRUE;
}
else
{
//reset the message removing the first element
dataList = llList2List(dataList, 1, llGetListLength(dataList) - 1);
message = llDumpList2String(dataList, @_STR);
//Now strip the MID off the id
msgId = (integer)llGetSubString(firstElement, 3, 8);
//First check the last day we were called. If it is not today then we empty are list, and this one is good
today = llGetDate();
if (gLastDay != today)
{
gMsgIds = [msgId];
result = C_TRUE;
gLastDay = today;
}
else
{
//check for id in list
if (llListFindList(gMsgIds, [msgId]) == -1)
{
//not there so its good ...
result = C_TRUE;
//Prune old values to keep list size manageable
prune_data_list();
//Add this value to the list
gMsgIds = (gMsgIds=[]) + gMsgIds + [msgId];
gLastDay = today;
}
else
{
//Already recieved ...
result = C_FALSE;
//Prune old values to keep list size manageable
prune_data_list();
}
}
}
//We set the message to a global since LSL only passes byVal and we cannot manipulate the original here
gHoldMessage = message;
//Clean Up variables
dataList = [];
firstElement = EMPTY_STR;
today = EMPTY_STR;
message = EMPTY_STR;
return result;
}//End check_message
//Function to manage the list of received message ids
//Cleans up the mesage id list, removing any more that 5 min old to keep size manageable
prune_data_list()
{
list tempList = [];
integer quit = C_FALSE;
integer seconds;
integer posit;
integer cur = 0;
integer checkValue;
//Get Current seconds and then take back 5 minutes
seconds = (integer)llGetWallclock() - 600;
//If seconds are 0 or less we will have no ids older than 5 minutes so we are done
if (seconds > 0)
{
//now we run through the list .. going backwards till we are at 5 min ...
posit = llGetListLength(gMsgIds);
while (quit == C_FALSE && cur < posit)
{
checkValue = llList2Integer(gMsgIds, cur);
if (checkValue < seconds)
{
//Keep going
cur++;
}
else
{
//we found where the ones to keep begin
tempList = llList2List(gMsgIds, cur, posit - 1);
quit = C_TRUE;
}
}
gMsgIds = tempList;
//Clean Up variables
tempList = [];
}
}//End prune_data_list
//End Repeater Functions
//#######
//Principal work functions for actions of the Door controller
//Function to check if the door can be opened
respond_door(string message)
{
integer continue;
string doorId;
integer index;
string openDoor;
list dataList;
key user;
//First we check the message to see if already received (repeater function)
continue = check_message(message);
//Since LSL always passes ByVal we need to retrieve the message from a global
//as it has had the id portion stripped off
message = gHoldMessage;
gHoldMessage = EMPTY_STR;
if (continue == C_TRUE)
{
dataList = llParseString2List(message, [@_STR], [EMPTY_STR]);
user = llList2String(dataList, 0);
doorId = llList2String(dataList, 1);
index = llListFindList(gDoors, [doorId]);
if (index != -1)
{
if (llList2String(gDoorStatus, index) == "Unlock")
{
openDoor = "true";
}
else if (llList2String(gDoorAccess, index) == "Owner Only" && user == llGetOwner())
{
openDoor = "true";
}
else
{
//We need to call the user control to check
openDoor = "check";
gRequested = C_TRUE;
llMessageLinked(LINK_THIS, USER_CHAN, "check", user);
gHoldDoor = doorId;
}
//Create return .. if we haven't called for a user check
if (openDoor != "check")
{
adv_say(llList2Integer(gParams, DOOR_RESP_CHAN), doorId + @_STR + llList2String(gParams, CONTROL_KEY) + @_STR + openDoor);
}
}
}
//Clean up variables
doorId = EMPTY_STR;
openDoor = EMPTY_STR;
dataList = [];
user = C_NULL_KEY;
message = EMPTY_STR;
}//End respond_door
//During setup ... when we are setting a number of doors
//This function basically just holds the door id until we are done
set_door_data(string message)
{
integer continue;
string doorId;
//First we check the message to see if already received (repeater function)
continue = check_message(message);
//Since LSL always passes ByVal we need to retrieve the message from a global
//as it has had the id portion stripped off
message = gHoldMessage;
gHoldMessage = EMPTY_STR;
if (continue == C_TRUE)
{
llSay(0,message);
//This is the door calling to check on what has been set or passing its data
//the message has passed the name of who touched the door and door ID
doorId = llList2String(llParseString2List(message, [@_STR], [EMPTY_STR]), 1);
gTempDoors = (gTempDoors=[]) + gTempDoors + [doorId];
//and respond so the door can turn off its listener
adv_say(llList2Integer(gParams, DOOR_RESP_CHAN), doorId + @_STR + "0" + @_STR + "0");
llOwnerSay("Door Set: " + doorId);
}
//Clean Up variables
message = EMPTY_STR;
doorId = EMPTY_STR;
}//End set_door_data
//If we are just adding a single door to the currnt list, this is the function executed
add_door_data(string message)
{
integer continue;
string doorId;
//First we check the message to see if already received (repeater function)
continue = check_message(message);
//Since LSL always passes ByVal we need to retrieve the message from a global
//as it has had the id portion stripped off
message = gHoldMessage;
gHoldMessage = EMPTY_STR;
if (continue == C_TRUE)
{
//Call the door back to let it know to turn off the listener
doorId = llList2String(llParseString2List(message, [@_STR], [EMPTY_STR]), 1);
adv_say(llList2Integer(gParams, DOOR_RESP_CHAN), doorId + @_STR + "0" + @_STR + "0");
gDoors = (gDoors=[]) + gDoors + [doorId];
gDoorStatus = (gDoorStatus=[]) + gDoorStatus + ["Unlock"];
gDoorAccess = (gDoorAccess=[]) + gDoorAccess + ["Owner Only"];
gDoorOptions = gDoors + ["All"];
llOwnerSay("Done. By default the door is unlocked, and set to Owner only");
}
//Clean Up variables
message = EMPTY_STR;
doorId = EMPTY_STR;
}//End add_door_data
//This function dumps the door configurations to chat
//to allow them to be stored in a notecard
dump_data()
{
integer i;
integer length;
//Dumps the configuration data to chat
//so that it can be copied into the config notecard
llOwnerSay("Data Dump to follow");
length = llGetListLength(gDoors);
for (i = 0; i < length; ++i)
{
llSay(0, "=" + llList2String(gDoors, i) + @_STR + llList2String(gDoorStatus, i) + @_STR + llList2String(gDoorAccess, i));
}
}//End dump_data
//End Door Functions
//#####
//Common functions
//Any 'standard' functions used in this or other scripts
//!!!! Need to check function cost compared to two time inline
//Reorders the button text into a human order for the dialog
list set_buttons(list baseList)
{
integer i;
for (i=0; i<llGetListLength(baseList); i+=3)
{
baseList = llListInsertList(llDeleteSubList(baseList, -3, -1), llList2List(baseList, -3, -1), i);
}
return baseList;
}//End set_buttons
//End Common functions
//End Functions
//#####
//Body
//Default State
// Used to read in our parameter data ... only executed on start
default
{
state_entry()
{
llOwnerSay("Door Control: Requesting Parameters");
//Get my params from the notecard reader
llMessageLinked(LINK_THIS, PARAMETER_APP, llDumpList2String(PARAM_KEYS, @_STR), (string)MY_PARAM);
} // end state_entry
dataserver(key requested, string data)
{
list dataPair;
if (requested == gReadConfig)
{
if (data != EOF)
{
if (gConfigLine == 0)
{
if (data == "set")
{
//We then read in the rest until EOF
gConfigLine++;
gReadConfig = llGetNotecardLine(CONFIG_NOTECARD, gConfigLine);
}
else
{
//No data in the notecard so off to run
state running;
}
}
else
{
//only here if we have preset data
//set the data to the lists
dataPair = llParseString2List(data, ["="], [EMPTY_STR]);
//First is the text from the say .. we then need to parse the second part
dataPair = llParseString2List(llList2String(dataPair,1), [@_STR], [EMPTY_STR]);
gDoors = (gDoors=[]) + gDoors + llList2String(dataPair, 0);
gDoorStatus = (gDoorStatus=[]) + gDoorStatus + llList2String(dataPair, 1);
gDoorAccess = (gDoorAccess=[]) + gDoorAccess + llList2String(dataPair, 2);
//Read the next line
gConfigLine++;
gReadConfig = llGetNotecardLine(CONFIG_NOTECARD, gConfigLine);
}
}
else
{
state running;
}
}
}//End dataserevr
link_message(integer sender, integer channel, string message, key id)
{
//Only listen from this prim ... and my param chan
if (sender == LINK_ROOT && channel == MY_PARAM)
{
gParams = llParseString2List(message, [@_STR], [EMPTY_STR]);
llOwnerSay("Door Control: Parameters Loaded");
//Clear lists
gDoors = [];
gDoorStatus = [];
gDoorAccess = [];
//Read in my configuration data
gConfigLine = 0;
gReadConfig = llGetNotecardLine(CONFIG_NOTECARD, gConfigLine);
// state config_load;
}
}//End link_message
state_exit()
{
llOwnerSay("Door Control: Parameters Loaded");
}//End state_exit
}//End default
//Running State
// The script is normally in this state listening for calls ...
// be the call from a door checking of it can open, or from
// the building controller requesting the dialog
state running
{
state_entry()
{
//set up listen for doors to call
llListen(llList2Integer(gParams, DOOR_CHAN), EMPTY_STR, C_NULL_KEY, EMPTY_STR);
}//End state_entry
link_message(integer sender, integer channel, string message, key id)
{
//Building control calling for the dialog
if (sender == LINK_ROOT && channel == llList2Integer(gParams, INDIVID_CHAN))
{
//Hold the id of the user for the dialog
gCurId = id;
//Show the dialog to select the door
state dialog_select;
}
//Call from a door passed on by a repeater
else if (channel == llList2Integer(gParams, REPEATER_CHAN))
{
//we use the (key)Id to pass the actual channel that comms were on
//seems I need to cast to a string before I can cast to an integer
string sId = (string)id;
if ((integer)sId == llList2Integer(gParams, DOOR_CHAN))
{
respond_door(message);
}
}
//Return from checking the user id to open the door
else if (sender == LINK_ROOT && channel == llList2Integer(gParams, USER_RESP_CHAN) && gRequested ==C_TRUE)
{
//Just pass to the door
gRequested = C_FALSE;
adv_say(llList2Integer(gParams, DOOR_RESP_CHAN), gHoldDoor + @_STR + llList2String(gParams, CONTROL_KEY) + @_STR + message);
}
}//End link_message
//Just hearing a door asking for a check
listen(integer channel, string name, key id, string message)
{
respond_door(message);
}//End listen
}//End running
