One Script, many doors

From Second Life Wiki
Jump to navigation Jump to search

This script is my attempt at solving the massive amount of simple door scripts one can have in a sim (I removed about 110 door scripts in my sim using this system)

It handles clockwise and counter clockwise door direction, a name-based access control list, play sounds, and the root prim reports memory free (useful for REALLY big door sets).

HOW TO USE IT


All your doors have to be one prim, typical cube with a path cut of 0.125 0.625 .

  • All your doors must be linked to a root prim which won't be a door.
  • All the doors must be uniquely named.
  • All the doors must have "left" or "right" in their description depending on the direction you want them to swing to.
  • In the root prim you will need 3 rezzable objects (simple cubes will do) named "open" , "close" and "lock" with, in them, the corresponding scripts (see at the bottom of the page) in order to play sounds at the right place.
  • You need a configuration notecard called "config"

main door script: must be in the root prim of the door link set.

/*******************************************
Single script, linked, lockable, door system
by Kyrah Abattoir

allow you to control many doors from a single
core script, complete with sound projectors
and name based access control!
*******************************************/

float autoclose_time = 30.0;//define how long the script will wait before closing a door.
float activation_distance = 2.0;//define how long the script will wait before closing a door.

//This functions is from Nomad Padar, pretty awesome!
//encode from vector to integer
integer vec2int(vector v)
{
    integer x = (integer)(v.x);
    integer y = (integer)(v.y);
    integer z = (integer)(v.z);
    integer out;

    //Rounds to 0, .5, or 1.0
    float delta = v.x-x;

    if((delta>.25) && (delta<.75))
        out += 0x10000000;
    else if(delta>.75)
        out += 0x1;

    delta = v.y-y;

    if((delta>.25) && (delta<.75))
        out += 0x20000000;
    else if(delta>.75)
        out += 0x100;

    delta = v.z-z;

    if((delta>.25) && (delta<.75))
        out += 0x40000000;
    else if(delta>.75)
        out += 0x10000;

    out += x+(y<<8)+(z<<16);
    return out;
}

list door_names         = [];//door list
list door_num           = [];//corresponding link numbers
list door_closed_rot    = [];//corresponding closed state rotation
list door_direction     = [];//which direction the door spin open
list door_open          = [];//is the door open?
list door_open_time     = [];//when was it open?
list door_acl           = [];//who can open the door (by user id)
list door_pos           = [];//the position of the door

list user_indexes       = [];//list of users (user ids)

integer is_active = FALSE;//if this is false the script ignore touches

recount_doors()
{
    //this function recount the doors and record all the different door characteristics
    //only ran after script reset/when an object is linked/unlinked
    is_active = FALSE;
    llSetText("recounting doors...",<1.0,1.0,1.0>,1.0);
    integer prims = llGetNumberOfPrims();

    integer i;
    for(i=2;i<=prims;i++)
    {
        key prim = llGetLinkKey(i);
        list tmp = llGetObjectDetails(prim,[OBJECT_NAME,OBJECT_DESC,OBJECT_ROT,OBJECT_POS]);
        string prim_name = llList2String(tmp,0);
        string prim_descr = llList2String(tmp,1);
        rotation prim_rot = (rotation)llList2String(tmp,2);
        vector prim_pos = (vector)llList2String(tmp,3);

        door_num += [i];
        door_names += [prim_name];
        door_closed_rot += [prim_rot];
        door_open += [FALSE];
        door_open_time += [0];
        if(prim_descr == "right")
            door_direction += [TRUE];
        else
            door_direction += [FALSE];
        door_acl += [""];
        door_pos += [prim_pos];
    }
    llSetText("...done!",<1,1,1>,1.0);
    reload_acl();
}

string config = "config";//name of the configuration file
integer nLine = 0;//current line we are reading

//this function re read the config notecard and populate the
//user list and acl table
reload_acl()
{

    llSetText("reloading access controls...",<1,1,1>,1.0);
    is_active = FALSE;
    nLine = 0;
    //remove all acl
    integer i;
    for(i=0;i<llGetListLength(door_num);i++)
        door_acl = llListReplaceList(door_acl,[""],i,i);

    user_indexes = [];
    llGetNotecardLine(config,nLine);
}

//close a door (by index) using the stored rotation
close(integer index)
{
    integer linknum = llList2Integer(door_num,index);

    rotation closed = llList2Rot(door_closed_rot,index);
    llSetLinkPrimitiveParams(linknum,[PRIM_ROTATION,closed]);

    door_open = llListReplaceList(door_open,[FALSE],index,index);
}

//close a door (by index) using the stored rotation and adding or substracting 90 degrees
//on the Z world axis depending of the direction recorded for the door.
//also record it's opening time (unix timestamp)
open(integer index)
{
    integer linknum = llList2Integer(door_num,index);
    rotation closed = llList2Rot(door_closed_rot,index);
    rotation open;
    if( llList2Integer(door_direction,index) )
        open = llEuler2Rot(llRot2Euler(closed)+ (<0,0,90> *DEG_TO_RAD));
    else
        open = llEuler2Rot(llRot2Euler(closed)+ (<0,0,-90> *DEG_TO_RAD));
    llSetLinkPrimitiveParams(linknum,[PRIM_ROTATION,open]);

    door_open_time = llListReplaceList(door_open_time,[llGetUnixTime()],index,index);
    door_open = llListReplaceList(door_open,[TRUE],index,index);

    llSetTimerEvent(1.0);
}

//those 3 functions rez a sound emitter with it's destination position encoded in integer
play_open(vector pos)
{
    llRezObject("open",llGetPos(),ZERO_VECTOR,ZERO_ROTATION,vec2int(pos));
}
play_close(vector pos)
{
    llRezObject("close",llGetPos(),ZERO_VECTOR,ZERO_ROTATION,vec2int(pos));
}
play_lock(vector pos)
{
    llRezObject("lock",llGetPos(),ZERO_VECTOR,ZERO_ROTATION,vec2int(pos));
}


default
{
    touch_start(integer total_number)
    {
        integer userid;

        if(!is_active)
            return;

        //considering we MAY receive more than one touch at the same time since
        //we can manage many doors.
        for(userid=0;userid<total_number;userid++)
        {
            integer door_id = llDetectedLinkNumber(userid);
            string clicker_name = llDetectedName(userid);

            integer door_index = llListFindList(door_num,[door_id]);
            if(door_index == -1)
                return;//unknown door? do nothing

            integer is_open = llList2Integer(door_open,door_index);
            vector door_vec = llList2Vector(door_pos,door_index);

            //we do not accept touches that are further away from activation_distance
            if( llVecDist(door_vec,llDetectedPos(userid) )> activation_distance )
                llInstantMessage(llDetectedKey(userid),"You can't use this from this distance.");
            else
            {
                if(is_open)
                {   //if the door is open, simply play sound and close it.
                    play_close(door_vec);
                    close(door_index);
                }
                else
                {
                    string acl_string = llList2String(door_acl,door_index);

                    //if the acl string is empty for this door we simply open it
                    if(acl_string == "") //no specific locking
                    {
                        play_open(door_vec);
                        open(door_index);
                    }
                    else
                    {
                        //otherwise we lookup if the userid that correspond to the person who touched
                        //the door match any of the ones in the ACL string
                        integer user_index = llListFindList(user_indexes,[clicker_name]);

                        if(user_index == -1)  //we don't even know this user? sure as hell there is no acl about him
                            play_lock(door_vec);
                        else
                        {
                            list acl_list =  llParseString2List(acl_string,[","],[]);

                            if(llListFindList(acl_list,[(string)user_index]) != -1)
                            {
                                play_open(door_vec);
                                open(door_index);
                            }
                            else
                                play_lock(door_vec);
                        }
                    }
                }
            }
        }
    }
    dataserver(key id,string data)
    {
        //notecard reading stuff
        if(data != EOF)
        {
            if(data != "" && data != " " && llGetSubString(data,0,1) != "//")
            {
                list command_and_data = llParseString2List(data,["="],[]);
                string door_name = llList2String(command_and_data,0);
                string user_names_string = llList2String(command_and_data,1);

                integer index = llListFindList(door_names,[door_name]);
                if(index != -1)
                {
                    list user_names_list = llParseString2List(user_names_string,[","],[]);
                    integer j;

                    list pre_acl_list;
                    for(j=0;j<llGetListLength(user_names_list);j++)
                    {
                        string user_name = llList2String(user_names_list,j);
                        integer index2 = llListFindList(user_indexes,[user_name]);
                        if( index2 == -1)
                        {
                            pre_acl_list += llGetListLength(user_indexes);
                            user_indexes += [user_name];
                        }
                        else
                            pre_acl_list += [index2];

                    }
                    string pre_acl_string = llDumpList2String(pre_acl_list,",");
                    door_acl = llListReplaceList(door_acl,[pre_acl_string],index,index);
                }
            }
            nLine++;
            llGetNotecardLine(config,nLine);
        }
        else
        {
            is_active = TRUE;
            llOwnerSay("config reloaded");
            llSetText((string)llGetFreeMemory()+"bits free",<.5,.5,.5>,.5);
        }
    }
    state_entry()
    {
        recount_doors();
    }
    changed(integer change)
    {
        //if we detect an inventory change we reread the notecard
        //if we detect a linkchange we reset the script and recount doors first
        if( (change & CHANGED_INVENTORY) == CHANGED_INVENTORY)
            reload_acl();
        if( (change & CHANGED_LINK) == CHANGED_LINK)
            llResetScript();
    }
    timer()
    {
        //timer will run once a second as long as at least one door is open.
        if(llListFindList(door_open,[TRUE]) == -1)
        {
            llSetTimerEvent(0.0);
            return;
        }
        integer i;

        //each run of the timer we loop through the timestamps to check if one of them is too old
        //(which means we can force close the associated door
        for(i=0;i<llGetListLength(door_open_time);i++)
        {
            integer is_open = llList2Integer(door_open,i);
            integer time = llList2Integer(door_open_time,i);
            if( is_open && ((time + autoclose_time) <  llGetUnixTime()))
            {
                vector door_vec = llList2Vector(door_pos,i);
                llRezObject("close",llGetPos(),ZERO_VECTOR,ZERO_ROTATION,vec2int(door_vec));
                close(i);
            }
        }
    }
}

Sound emitter script


You need this script in the 3 rezzable objects "open" "close" and "lock" in order to play your door sounds at the right place (At the time i wrote this , lsl doesn't allow yet to play sounds from an unscripted prim in a link set)

string sound_to_play = "my door sound";// put your sound name/key here

//This functions is from Nomad Padar, pretty awesome!
//decode from integer to vector
vector int2vec(integer n)
{
    //0zyx ZZZZ ZZZZ ZZZZ YYYY YYYY XXXX XXXX
    return <n&0xFF, (n>>8)&0xFF, ((n>>16)&0xFFF)> + <((n&0x10000000)!=0)*.5,((n&0x20000000)!=0)*.5,((n&0x40000000)!=0)*.5>;
}

default
{
    state_entry()
    {
        llSetStatus(STATUS_PHANTOM, TRUE);
        llSetPrimitiveParams([PRIM_TEMP_ON_REZ, TRUE]);
        llSetTexture(TEXTURE_TRANSPARENT,ALL_SIDES);
    }
    on_rez(integer a)
    {
        if(a == 0)
            return;

        vector destination = int2vec(a);

        integer i;
        for(i=0 ; i<50 && (llGetPos() != destination) ; i++)
            llSetLinkPrimitiveParamsFast(0, [PRIM_POSITION, destination]);

        if(llGetPos() == destination)
            llTriggerSound(sound_to_play,1.0);
        llDie();
    }
}

Example "config" notecard:

//lines starting with "//" are ignored
//Syntax: one line per door in the format door_name=user name,user name
My fancy front door=Kyrah Abattoir
My secret playroom door=Kyrah Abattoir,Timeless Prototype
//of course if you want to lock a door from anybody you can set it to something like this:
Mine entrance=condemned until further notice

--Kyrah Abattoir 18:46, 7 March 2010 (UTC)