Difference between revisions of "Random AV Profile Projector"

From Second Life Wiki
Jump to navigation Jump to search
m (<lsl> tag to <source>)
 
(19 intermediate revisions by 4 users not shown)
Line 1: Line 1:
{{LSL Header}}
{{LSL Header}}


'''~ RANDOM AV PROFILE PICTURE PROJECTOR v5.1 by Debbie Trilling ~'''
'''~ RANDOM AV PROFILE PICTURE PROJECTOR v5.4.5 by Debbie Trilling ~'''
 
'''Set-up, configuration and operation Help:''' http://wiki.secondlife.com/wiki/Talk:Random_AV_Profile_Projector


'''Profile:''' http://wiki.secondlife.com/wiki/User:Debbie_Trilling
'''Profile:''' http://wiki.secondlife.com/wiki/User:Debbie_Trilling


<lsl>
{{World.secondlife.com changes}}
// ~ RANDOM AV PROFILE PICTURE PROJECTOR v5.1 by Debbie Trilling ~


<source lang="lsl2">
// ~ RANDOM PROFILE PROJECTOR v5.4.5.20091126 by Debbie Trilling ~
// *** This script randomly selects an AV from a crowd & then displays their
// *** This script randomly selects an AV from a crowd & then displays their
// profile picture as texture on a prim and/or a 'holographic' image projected above the prim***
// profile picture as texture on a prim and/or a 'holographic' image projected above the prim***
 
// Free to use as you wish by under condition that the title and this introduction remain in place,
// Free to use as you wish by under condition that the title and this introduction remain in place,
// and that due credit continues to be given to Moriash Moreau, Jana Kamachi and Solar Alter, and
// and that due credit continues to be given to Moriash Moreau, Coder Kas and
// Debbie Trilling.
// Debbie Trilling.
 
// 2009-11-24 VD - mod to use <meta> tag, to work around WEB-1383
// 2009-11-26 VD - add fallback to <img> tag in case the profile changes are reverted
// TOUCH to switch ON and OFF
// TOUCH to switch ON and OFF
 
// ONLINE HELP AT:  http://wiki.secondlife.com/wiki/Talk:Random_AV_Profile_Projector
// ** PARAMETERS THAT YOU CAN CHANGE **
// ** PARAMETERS THAT YOU CAN CHANGE **
 
 
//************************
//************************
// GENERAL PARAMETERS
// GENERAL PARAMETERS
//************************
//************************
// listen channel for Owner
integer OwnerChannel = 54321;
// set the maximum number of entries in the 'Exclude List' at any one time
integer ExcludeListSize = 30;
// the number of seconds that a listen remains open before timing-out & automatically closing
float DialogTimeout = 20.00;
// how often in seconds the sensor fires
// how often in seconds the sensor fires
float RepeatTime = 35.00;
float RepeatTime = 35.00;
 
// sensor range in meters. Maximum 96m but in practice 10 to 30m because of particle draw distance
// sensor range in meters. Maximum 96m but in practice 10 to 30m because of particle draw distance
float Range = 25.00;
float Range = 25.00;
 
// sets the number of consecutive times that the scanner is allowed to operate without having located an AV within range
// sets the number of consecutive times that the scanner is allowed to operate without having located an AV within range
// eg: if RepeatTime = 60.0 seconds and TotalNoScansAllowed = 30, then the toy will operate for 1800 seconds (60x30, or 30 minutes) without locating
// eg: if RepeatTime = 60.0 seconds and TotalNoScansAllowed = 30, then the toy will operate for 1800 seconds (60x30, or 30 minutes) without locating
// anyone before it automatically powers down. Set to '0' to disable the auto-off function
// anyone before it automatically powers down. Set to '0' to disable the auto-off function
integer TotalNoScansAllowed = 20;
integer TotalNoScansAllowed = 20;
 
// sets whether the DefaultTexture will be textured/projected when the toy is switched OFF. 'TRUE' to texture/project; 'FALSE' to have no texturing/projection when off
integer ProjectDefaultTexture = TRUE;
 
// texture palette of UUID's. One will be randomly selected for display when an AV without a profile pic is selected
// texture palette of UUID's. One will be randomly selected for display when an AV without a profile pic is selected
list DefaultTexture = ["7cfd684e-2141-941c-eac8-bd439f0d5a9f", "d02531dd-491c-45b5-2cab-2e47ec81ec0d", "c3eebd9e-ee92-a16f-f906-bc275928df86"];
list DefaultTexturePalette = ["8fb9ad84-4183-51df-f566-9b21c3a610fe", "1201c3de-d022-c0a5-56bd-bc49ab971726", "c3eebd9e-ee92-a16f-f906-bc275928df86"];
 
// sets whether the DefaultTexturePalette will be used to texture/project when the toy is switched OFF. 'TRUE' to texture/project; 'FALSE' to have no texturing/projection when off
integer EmployDefaultTexture = TRUE;


string profile_key_prefix = "<meta name=\"imageid\" content=\"";
string profile_img_prefix = "<img alt=\"profile image\" src=\"http://secondlife.com/app/image/";
integer profile_key_prefix_length; // calculated from profile_key_prefix in InitialiseObject()
integer profile_img_prefix_length; // calculated from profile_img_prefix in InitialiseObject()
//************************
//************************
// PARTICLE EMISSION PARAMETERS
// PARTICLE EMISSION PARAMETERS
//************************
//************************
// set to TRUE to display the profile picture as a particle 'holographic' image above the prim
// set to TRUE to display the profile picture as a particle 'holographic' image above the prim
integer DisplayBanner = TRUE;
integer DisplayBanner = TRUE;
 
// width size in meters of the projected image (max 4.00)
// width size in meters of the projected image (max 4.00)
float Size = 2.50;
float Size = 2.50;
 
// height above object the centre of projected image will be (theoretical max. 50.0, in practice 2.0 to 10.0))
// height above object the centre of projected image will be (theoretical max. 50.0, in practice 2.0 to 10.0))
float Height = 2.50;
float Height = 2.50;
 
 
//************************
//************************
// PRIM TEXTURE PARAMETERS
// PRIM TEXTURE PARAMETERS
//************************
//************************
// set to TRUE to texture the prim with the profile picture on ALL_SIDES
// set to TRUE to texture the prim with the profile picture on ALL_SIDES
integer TexturePrim = TRUE;
integer TexturePrim = TRUE;
 
// the following 'Prim*' parameters effect the prim only if ProjectDefaultTexture and/or TexturePrim = FALSE
// the following 'Prim*' parameters effect the prim only if ("EmployDefaultTexture = FALSE") OR ("EmployDefaultTexture = TRUE" and "TexturePrim=FALSE")
// if either = TRUE then the prim is automatically set to solid blank white with full bright
// if TexturePrim = TRUE then the prim is automatically set to solid blank white no shiny with full bright as this is usually the best surface to display the profile picture
// texture to use for the prim when toy is OFF
// texture to use for the prim when toy is OFF
key PrimTexture = "5748decc-f629-461c-9a36-a35a221fe21f";
key PrimUUIDTexture = "5748decc-f629-461c-9a36-a35a221fe21f";
 
// set to TRUE to turn on Full Bright on ALL_SIDES when the toy is OFF
// set to TRUE to turn on Full Bright on ALL_SIDES when the toy is OFF
integer PrimFullBright = FALSE;
integer PrimFullBright = FALSE;
 
// vector for the prim colour when toy is OFF
// vector for the prim colour when toy is OFF
vector PrimColour = <0.0, 0.0, 0.0>;
vector PrimColour = <0.0, 0.0, 0.0>;
 
// set the alpha of the prim from 0.0 (clear) to 1.0 (solid) for when toy is OFF
// set the alpha of the prim from 0.0 (clear) to 1.0 (solid) for when toy is OFF
float PrimAlpha = 1.00;
float PrimAlpha = 1.00;
 
 
// set the degree of 'shininess' to apply to the prim "0" = None, "1" = Low, "2" = medium, "3" = high
integer PrimShine = 3;
//************************
// FLOATING TEXT PARAMETERS
//************************
// set to TRUE for floating text above the prim; FALSE to disable the floating text
integer ApplyFloatingText = TRUE;
// set the text to be displayed
string FloatingText = "'Touch' for more information";
// set the text colour
vector FloatingTextColour = <1.0,1.0,0.0>;
// set the text alpha
float FloatingTextAlpha = 0.8;
//************************
// ROTATION PARAMETERS
//************************
// if TRUE, applies a slow rotation to the prim when the toy is swtiched ON and TexturePrim = TRUE; FALSE to disable rotation
integer ApplyRotation = FALSE;
//************************
//************************
// SHOUTOUT PARAMETERS
// SHOUTOUT PARAMETERS
//************************
//************************
// set to 'TRUE' to give a 'ShoutOut' to the AV once they have been selected; 'FALSE' for no 'ShoutOut'
// set to 'TRUE' to give a 'ShoutOut' to the AV once they have been selected; 'FALSE' for no 'ShoutOut'
integer ShoutOut = TRUE;
integer ShoutOut = TRUE;
 
// text to 'ShoutOut' when an AV's profile is projected. Text will be preceeded by their name, eg: "<AV Name>'s face is up in lights!"
// text to 'ShoutOut' when an AV's profile is projected. Text will be preceeded by their name, eg: "<AV Name>'s face is up in lights!"
string ShoutOutText = "'s face is up in lights!";
string ShoutOutText = "'s face is up in lights!";
 
 
 
 
//************************
//************************
// ** DO NOT CHANGE BELOW THIS LINE **
// ** DO NOT CHANGE BELOW THIS LINE **
//************************
//************************
 
string URL_FORUMTHREAD = "http://forums.secondlife.com/showthread.php?t=225692";
string URL_WIKIPAGE = "http://wiki.secondlife.com/wiki/User:Debbie_Trilling";
string URL_RESIDENT = "http://world.secondlife.com/resident/";
key LastTexture = "";
integer Power = FALSE;
integer Power = FALSE;
integer ListenChannel;
integer OwnerListenChannel;
integer NoSensorCounter = 0;
integer NoSensorCounter = 0;
key AVKey = "";
key AVKey = "";
key DetectedUser = "";
key DetectedUser = "";
key ObjectOwner = "";
key ObjectOwner = "";
key LastTexture = "";
string OwnerName = "";
string OwnerName = "";
string ObjectName = "Profile Projector";
string ObjectName = "Profile Projector";
string Author = "Debbie Trilling";
string Author = "Debbie Trilling";
string Supplier = "The Particle Crucible";
string Supplier = "The Particle Crucible";
string Version = " v5.1";
string Version = " v5.4.5.20091126";
integer ListenChannel;
string OwnerListenText = "OpenListen";
float DialogTimeout = 20.0;
string SelfExcludedSuffix = "**";
list KillJoyListing = [];
string PowerText = "On";
 
list ExcludeListing = [];
 
 
 
GiveShoutOut()
GiveShoutOut()
{
{
// any interaction with selected AV (give Inventory items etc) can safely be done from this function
    // any interaction with selected AV (give Inventory items etc) can safely be done from this function
// this function will only execute if ShoutOut == TRUE
    // this function will only execute if ShoutOut == TRUE
 
//although fondly calling it a 'ShoutOut', it actually makes more sense to keep within the 20m range of llSay
    //although fondly calling it a 'ShoutOut', it actually makes more sense to keep within the 20m range of llSay
llSay(0, llKey2Name(AVKey) + ShoutOutText);
    llSay(0, llKey2Name(AVKey) + ShoutOutText);
 
}
AnnounceWelcome()
{
    llOwnerSay(
        "\nThank you for your interest in this " + ObjectName + " "
            + Version + " created by " + Author + " at " + Supplier + ". \nHelp setting up, configuring and operating the "
        + ObjectName + " can be gotton at http://wiki.secondlife.com/wiki/Talk:Random_AV_Profile_Projector \nTOUCH the " + ObjectName + " to operate it.");
}
InitialiseObject()
{
    llParticleSystem([]);
    StopRotation();
    ObjectOwner = llGetOwner();
    OwnerName = llKey2Name(ObjectOwner);
    llSetObjectName(ObjectName + Version);
    llSetObjectDesc("Supplied free by " + Author + "'s " + Supplier);
    CloseAllListens();
    profile_key_prefix_length = llStringLength(profile_key_prefix);
    profile_img_prefix_length = llStringLength(profile_img_prefix);
}
SetFloatingText()
{
    if (ApplyFloatingText)
    {
        llSetText(FloatingText, FloatingTextColour, FloatingTextAlpha);
    }
    else
    {
        llSetText("",<1.0,1.0,1.0>,0.0);
    }
}
}
 
 
ProjectTexture()
ProjectTexture()
{
{
// are we going to use a default texture when toy is OFF?
    // are we going to use a default texture when toy is OFF?
if (ProjectDefaultTexture)
    if (EmployDefaultTexture)
{
    {
if (TexturePrim)
        if (TexturePrim)
{
        {
// using a default texture; if putting texture on the prim, let's make sure it is solid white, blank. full bright
            // using a default texture
llSetPrimitiveParams([PRIM_FULLBRIGHT, ALL_SIDES, TRUE, PRIM_COLOR, ALL_SIDES, <1.0, 1.0, 1.0>, 1.0 ]);
            ApplyPrimSurface();
}
        }
else
        else
{
        {
// not texturing the prim, so apply the prom preferences
            // not texturing the prim, so apply the prim preferences
ApplyPrimPrefs();
            ApplyPrimPrefs();
}
        }
ParticleStart((key)llList2String(DefaultTexture, (integer)llFrand((float)llGetListLength(DefaultTexture))));
        ApplyDefaultTexture();
}
    }
else
    else
{
    {
// we're not doing anything when the toy is OFF; change the prim to user preferences
        // we're not doing anything when the toy is OFF; change the prim to user preferences
llParticleSystem([]);
        llParticleSystem([]);
ApplyPrimPrefs();
        ApplyPrimPrefs();
}
    }
}
ApplyPrimSurface()
{
    // putting texture on the prim, let's make sure it is solid white, blank. full bright
    llSetPrimitiveParams([PRIM_FULLBRIGHT, ALL_SIDES, TRUE, PRIM_COLOR, ALL_SIDES, <1.0, 1.0, 1.0>, 1.0, PRIM_BUMP_SHINY, ALL_SIDES, 0, 0 ]);
}
}
 
ApplyPrimPrefs()
ApplyPrimPrefs()
{
{
llSetPrimitiveParams([PRIM_FULLBRIGHT, ALL_SIDES, PrimFullBright,
    llSetPrimitiveParams([PRIM_FULLBRIGHT, ALL_SIDES, PrimFullBright,
PRIM_TEXTURE, ALL_SIDES, PrimTexture, <1.000000, 1.000000, 0.000000>, <0.000000, 0.000000, 0.000000>, 0.000000, PRIM_COLOR, ALL_SIDES, PrimColour, PrimAlpha ]);
        PRIM_TEXTURE, ALL_SIDES, PrimUUIDTexture, <1.000000, 1.000000, 0.000000>, <0.000000, 0.000000, 0.000000>, 0.000000, PRIM_COLOR, ALL_SIDES, PrimColour, PrimAlpha, PRIM_BUMP_SHINY, ALL_SIDES, PrimShine, 0 ]);
}
StartUp()
{
    Power = TRUE;
    PowerText = "Off";
    llSensorRepeat("",NULL_KEY,AGENT,Range,PI,RepeatTime);
    NoSensorCounter = 0;
    ApplyPrimUpdate();
    llOwnerSay("\nThe " + ObjectName + " is now switched ON. Please wait...");
}
ApplyPrimUpdate()
{
    if (TexturePrim)
    {
        ApplyPrimSurface();
        if (ApplyRotation)
        {
            llTargetOmega(<0.0,0.0,1.0>, 0.2, PI);
        }
    }
}
ApplyDefaultTexture()
{
    ApplySelectedTexture((key)llList2String(DefaultTexturePalette, (integer)llFrand((float)llGetListLength(DefaultTexturePalette))));
}
string RemainingExcludeSlots()
{
    string RemainingSlots = (string)(ExcludeListSize - llGetListLength(ExcludeListing)) + " slots are now available.";
    return RemainingSlots;
}
string DeriveName(string messagecapture)
{
    string DerivedName = llGetSubString(llStringTrim(messagecapture,STRING_TRIM),8,llStringLength(llStringTrim(messagecapture,STRING_TRIM)));
    return DerivedName;
}
}
 
 
ParticleStart(key texture)
integer DeriveNamePosition(string messagecapture)
{
{
if (DisplayBanner)
    integer DerivedNamePosition = llListFindList(ExcludeListing, (list)llGetSubString(llStringTrim(messagecapture,STRING_TRIM),8,llStringLength(llStringTrim(messagecapture,STRING_TRIM))));
{
    return DerivedNamePosition;
//core code by Moriash Moreau. Adapted to suit by Debbie Trilling
llParticleSystem([
PSYS_PART_FLAGS, 0,
PSYS_SRC_PATTERN, 4,
PSYS_PART_START_ALPHA, 0.50,
PSYS_PART_END_ALPHA, 0.50,
PSYS_PART_START_COLOR, <1.0,1.0,1.0>,
PSYS_PART_END_COLOR, <1.0,1.0,1.0>,
PSYS_PART_START_SCALE, <Size * 1.6 ,Size,0.00>,
PSYS_PART_END_SCALE, <Size * 1.6,Size,0.00>,
PSYS_PART_MAX_AGE, 1.20,
PSYS_SRC_MAX_AGE, 0.00,
PSYS_SRC_ACCEL, <0.0,0.0,0.0>,
PSYS_SRC_ANGLE_BEGIN, 0.00,
PSYS_SRC_ANGLE_END, 0.00,
PSYS_SRC_BURST_PART_COUNT, 8,
PSYS_SRC_BURST_RADIUS, Height,
PSYS_SRC_BURST_RATE, 0.10,
PSYS_SRC_BURST_SPEED_MIN, 0.00,
PSYS_SRC_BURST_SPEED_MAX, 0.00,
PSYS_SRC_OMEGA, <0.00,0.00,0.00>,
PSYS_SRC_TEXTURE, texture]);
}
 
if (TexturePrim)
{
llSetTexture(texture, ALL_SIDES);
}
}
}
 
AnnounceWelcome()
ApplySelectedTexture(key texture)
{
{
llOwnerSay(
    if (DisplayBanner)
"\nThank you for your interest in this product created by Debbie Trilling at The Particle Crucible.");
    {
        // make the particle banner if required
        //core code by Moriash Moreau. Adapted to suit by Debbie Trilling
        llParticleSystem([
            PSYS_PART_FLAGS, 0,
            PSYS_SRC_PATTERN, 4,
            PSYS_PART_START_ALPHA, 0.50,
            PSYS_PART_END_ALPHA, 0.50,
            PSYS_PART_START_COLOR, <1.0,1.0,1.0>,
            PSYS_PART_END_COLOR, <1.0,1.0,1.0>,
            PSYS_PART_START_SCALE, <Size * 1.6 ,Size,0.00>,
            PSYS_PART_END_SCALE, <Size * 1.6,Size,0.00>,
            PSYS_PART_MAX_AGE, 1.20,
            PSYS_SRC_MAX_AGE, 0.00,
            PSYS_SRC_ACCEL, <0.0,0.0,0.0>,
            PSYS_SRC_ANGLE_BEGIN, 0.00,
            PSYS_SRC_ANGLE_END, 0.00,
            PSYS_SRC_BURST_PART_COUNT, 8,
            PSYS_SRC_BURST_RADIUS, Height,
            PSYS_SRC_BURST_RATE, 0.10,
            PSYS_SRC_BURST_SPEED_MIN, 0.00,
            PSYS_SRC_BURST_SPEED_MAX, 0.00,
            PSYS_SRC_OMEGA, <0.00,0.00,0.00>,
            PSYS_SRC_TEXTURE, texture]);
    }
    if (TexturePrim)
    {
        llSetTexture(texture, ALL_SIDES);
    }
}
}
 
 
ShutDown()
ShutDown()
{
{
llSensorRemove();
    Power = FALSE;
llListenRemove(ListenChannel);
    PowerText = "On";
Power = FALSE;
    CloseAllListens();
ProjectTexture();
    llSensorRemove();
    StopRotation();
    ProjectTexture();
    llOwnerSay("\nThe " + ObjectName + " is now switched OFF.");
}
StopRotation()
{
    llTargetOmega(<0.0,0.0,0.0>, 0.0, PI);
}
CloseAllListens()
{
    CloseUserListen();
    llListenRemove(OwnerListenChannel);
    OwnerListenText = "OpenListen";
}
CloseUserListen()
{
    llSetTimerEvent(0.0);
    llListenRemove(ListenChannel);
}
}
 
 
default
default
{
{
    on_rez(integer start_param)
    {
        // reset script on rez
        llResetScript();
    }
    changed( integer change )
    {
        if(change & CHANGED_OWNER )
        {
            // reset script on change of owner
            llResetScript();
        }
    }
    state_entry()
    {
        //initialise system
        InitialiseObject();
        SetFloatingText();
        ProjectTexture();
        AnnounceWelcome();
    }
    touch_start(integer total_number)
    {
        DetectedUser = llDetectedKey(0);
        list MenuItems = ["LearnMore", "GetScript", "Help"];
        string MenuText = "MAIN MENU: Please make a selection within " + (string)llFloor(DialogTimeout)
            + " seconds.\n- LearnMore: Read the forum thread on this product\n- GetScript: Get yourself the latest version of this free script\n- Help: Link to the "
            + ObjectName + " Help page";
        if (DetectedUser == ObjectOwner)
        {
            // touched by Owner
            if (Power)
            {
                MenuItems = [PowerText, OwnerListenText] + MenuItems;
                MenuText = MenuText + "\n- Off: Switch off \n- Open/CloseListen: Opens/Closes Owner listen channel " + (string)OwnerChannel;
            }
            else
            {
                MenuItems = (list)PowerText + MenuItems;
                MenuText = MenuText + "\n- On: Switch on";
            }
        }
        else
        {
            // touched by someone other than Owner. Send them a message & a dialog box of options
            // if they placed their own name on 'Exclude List', so give opportunity to clear their name, else give opportunity to exclude their name
            if (llListFindList(ExcludeListing, (list)(llKey2Name(AVKey) + SelfExcludedSuffix)) != -1 )
            {
                // they placed themself on the 'Exclude List' so are therefore allowed to clear their name
                MenuItems = (list)"IncludeMe" + MenuItems;
                MenuText =  MenuText + "\n- IncludeMe: Have the " + ObjectName + " remove your name from the 'Exclude List'";
            }
            else
            {
                // they have not placed themself on the 'Exclude List', so check whether the Owner has already done it
                if (llListFindList(ExcludeListing, (list)llKey2Name(AVKey)) == -1 )
                {
                    // neither they themself nor the Owner has previously excluded them, so give them the opportunity to exclude themselves now
                    MenuItems = (list)"ExcludeMe" + MenuItems;
                    MenuText =  MenuText + "\n- ExcludeMe: Have your name added to the " + ObjectName + "'s 'Exclude List'";
                }
            }
            llInstantMessage(DetectedUser, "\nThank you for your interest in the " + ObjectName + " created by " + Author + "\nThe dialog menu offers a number of options.");
        }
        // generate the dialog menu
        integer CommChannel = (-200000 - ((integer)llFrand(12345) * -1));
        ListenChannel = llListen(CommChannel, "", DetectedUser, "");
        llDialog(DetectedUser, MenuText, MenuItems, CommChannel);
        llSetTimerEvent(DialogTimeout);
    }
    sensor(integer total_number)
    {
        // save the AV key in case it is needed for a 'ShoutOut'
        AVKey = llDetectedKey((integer)llFrand(total_number));
        // core code by Coder Kas. Adapted to suit by Debbie Trilling
        string URL_RESIDENT = "http://world.secondlife.com/resident/";
        llHTTPRequest( URL_RESIDENT + (string)AVKey,[HTTP_METHOD,"GET"],"");
    }
    no_sensor()
    {
        // counts the number of times that the scanner doesn't find anyone in range. If TotalNoScansAllowed is set to greater than zero, automatically powers down the toy
        // when the number of no_sensors exceeds TotalNoScansAllowed. However, this functionality is disabled if TotalNoScansAllowed is set to zero.
        NoSensorCounter++;
        if ((NoSensorCounter > TotalNoScansAllowed) && (TotalNoScansAllowed > 0))
        {
            ShutDown();
            llInstantMessage(ObjectOwner, "\nThe " + ObjectName + " has been automatically switched OFF as no Agents have been detected within the set timeframe.");
        }
        else
        {
            ApplyDefaultTexture();
        }
    }
    http_response(key req,integer stat, list met, string body)
    {
        // core code by Coder Kas. Adapted to suit by Debbie Trilling
        integer s1 = llSubStringIndex(body, profile_key_prefix);
        integer s1l = profile_key_prefix_length;
        if(s1 == -1)
        { // second try with img tag
            s1 = llSubStringIndex(body, profile_img_prefix);
            s1l = profile_img_prefix_length;
        }


on_rez(integer start_param)
        if(s1 == -1)
{
        {
// reset script on rez
            // selected AV doesn't have a profile picture, so use the default instead
AnnounceWelcome();
            ApplyDefaultTexture();
llResetScript();
        }
}
        else
 
        {
changed( integer change )
            key NewTexture = (key)llGetSubString(body,s1+s1l,s1+s1l+35);
{
if(change & CHANGED_OWNER )
            //check whether this was the texture used last time
{
            if (NewTexture == LastTexture || NewTexture == NULL_KEY)
// reset script on change of owner
            {
AnnounceWelcome();
                // same profile pic as last time. so display a random default instead
llResetScript();
                ApplyDefaultTexture();
}
                // clear the last texture out
}
                LastTexture = "";
 
            }
state_entry()
            else
{
            {
//initialise system
                // are they on the ExcludeListing, with or without a suffix? if so, display a random default texture instead
llParticleSystem([]);
                if ((llListFindList(ExcludeListing, (list)llKey2Name(AVKey)) != -1 ) || (llListFindList(ExcludeListing, (list)(llKey2Name(AVKey) + SelfExcludedSuffix)) != -1))
ObjectOwner = llGetOwner();
                {
OwnerName = llKey2Name(ObjectOwner);
                    // they are on the 'Exclude List'
llSetObjectName(ObjectName + Version);
                    ApplyDefaultTexture();
llSetObjectDesc("Supplied free by " + Author + "'s " + Supplier);
                }
ProjectTexture();
                else
llOwnerSay("\nTOUCH the " + ObjectName + " to switch it ON and OFF.");
                {
}
                    // different profile from last time & not on ExcludeListing, so display it
 
                    ApplySelectedTexture(NewTexture);
touch_start(integer total_number)
                    // save the key for comparison purposes next time tho'
{
                    LastTexture = NewTexture;
if (llDetectedKey(0) == ObjectOwner)
{
                    // give a 'ShoutOut', if set to do so
// operation by Owner Only
                    if (ShoutOut)
if (Power)
                    {
{
                        GiveShoutOut();
// touch to OFF
                    }
ShutDown();
                }
llOwnerSay("\nThe " + ObjectName + " is now switched OFF.");
            }
}
        }
else
    }
{
// touch to ON
Power = TRUE;
    listen(integer channel, string name, key id, string message)
NoSensorCounter = 0;
    {
llSensorRepeat("",NULL_KEY,AGENT,Range,PI,RepeatTime);
llOwnerSay("\nThe " + ObjectName + " is now switched ON. Please wait...");
        if (message == "LearnMore")
}
        {
}
            string URL_FORUMTHREAD = "http://forums-archive.secondlife.com/54/1b/225692/1.html";
else
            llLoadURL(DetectedUser, "Thank you for choosing to learn more about the " + ObjectName + ".This link will take you to the relevant SL forum thread.", URL_FORUMTHREAD);
{
            CloseUserListen();
// touched by someone other than Owner. Send them a message & a dialog box of options
        }
DetectedUser = llDetectedKey(0);
        else if (message == "GetScript")
llInstantMessage(DetectedUser, "\nThank you for your interest in the " + ObjectName + " created by " + Author + "\nThe dialog menu offers a number of options.");
        {
integer CommChannel = (-200000 - ((integer)llFrand(12345) * -1));
            string URL_WIKIPAGE = "http://wiki.secondlife.com/wiki/User:Debbie_Trilling";
list MenuItems = ["LearnMore","GetScript"];
            llLoadURL(DetectedUser, "Thank you for choosing to look at the script for the " + ObjectName + ".This link will allow you to get your own free copy.", URL_WIKIPAGE);
string MenuText = "MAIN MENU: Please make a selection within " + (string)llFloor(DialogTimeout)  + " seconds.\n- LearnMore: Read the forum thread on this product\n- GetScript: Get yourself a free copy of this script";
            CloseUserListen();
// if already on 'Exclude List', give opportunity to clear their name, else give opportunity to exclude their name
        }
if (llListFindList(KillJoyListing, (list)llKey2Name(AVKey)) != -1 )
        else if (message == "Help")
{
        {
// they are on the 'Exclude List' so give opportunity to clear their name
            string URL_HELPPAGE = "http://wiki.secondlife.com/wiki/Talk:Random_AV_Profile_Projector";
MenuItems = MenuItems + (list)"IncludeMe";
            llLoadURL(DetectedUser, "This link will take you to the " + ObjectName + "'s Help page.", URL_HELPPAGE);
MenuText =  MenuText + "\n- IncludeMe: Have the " + ObjectName + " include you in on the fun!";
            CloseUserListen();
}
        }
else
        else if (message == "ExcludeMe")
{
        {
// they are not on the 'Exclude List' so give opportunity to add their name
            // as a suffix to the name, as it is the user adding their own name
MenuItems = MenuItems + (list)"ExcludeMe";
            ExcludeListing = (list)(llKey2Name(DetectedUser) + SelfExcludedSuffix) + llList2List( ExcludeListing, 0, (ExcludeListSize - 2));
MenuText =  MenuText + "\n- ExcludeMe: Have the " + ObjectName + " ignore your presence";
            llInstantMessage(DetectedUser, "You have been added to the " + ObjectName + "'s 'Exclude List'");
}
            CloseUserListen();
ListenChannel = llListen(CommChannel, "", DetectedUser, "");
        }
llDialog(DetectedUser, MenuText, MenuItems, CommChannel);
        else if (message == "IncludeMe")
llSetTimerEvent(DialogTimeout);
        {
}
            integer ExcludeListPosition = llListFindList(ExcludeListing, (list)(llKey2Name(DetectedUser) + SelfExcludedSuffix));
}
            ExcludeListing = llDeleteSubList(ExcludeListing, ExcludeListPosition, ExcludeListPosition);
 
            llInstantMessage(DetectedUser, "You have been removed from the " + ObjectName + "'s 'Exclude List'");
 
            CloseUserListen();
sensor(integer total_number)
        }
{
        else if ((message == "On") && (id == ObjectOwner))
// save the AV key in case it is needed for a 'ShoutOut'
        {
AVKey = llDetectedKey((integer)llFrand(total_number));
            StartUp();
// core code by Jana Kamachi and Solar Alter. Adapted to suit by Debbie Trilling
        }
llHTTPRequest( URL_RESIDENT + (string)AVKey,[HTTP_METHOD,"GET"],"");
        else if ((message == "Off") && (id == ObjectOwner))
}
        {
 
            ShutDown();
 
        }
no_sensor()
        else if ((message == "OpenListen") && (id == ObjectOwner))
{
        {
// counts the number of times that the scanner doesn't find anyone in range. If TotalNoScansAllowed is set to greater than zero, automatically powers down the toy
            // open the Owner Only channel
// when the number of no_sensors exceeds TotalNoScansAllowed. However, this functionality is disabled if TotalNoScansAllowed is set to zero.
            OwnerListenChannel = llListen(OwnerChannel, "", ObjectOwner, "");
NoSensorCounter++;
            OwnerListenText = "CloseListen";
if ((NoSensorCounter > TotalNoScansAllowed) && (TotalNoScansAllowed > 0))
            llOwnerSay("Owner Only channel " + (string)OwnerChannel + " is now open for you.\n Options are: 'Exclude <AV_NAME>', 'Include <AV_NAME>', 'ClearAll' and 'List'");
{
        }
ShutDown();
        else if ((message == "CloseListen") && (id == ObjectOwner))
llInstantMessage(ObjectOwner, "\nThe " + ObjectName + " has been automatically switched OFF as no Agents have been detected within the set timeframe.");
        {
}
            // close the Owner Only channel
else
            llListenRemove(OwnerListenChannel);
{
            OwnerListenText = "OpenListen";
ParticleStart((key)llList2String(DefaultTexture, (integer)llFrand((float)llGetListLength(DefaultTexture))));
            llOwnerSay("Owner Only channel " + (string)OwnerChannel + " is now closed.");
}
        }
}
        else if (((llGetSubString(message,0,6) == "Exclude") || (llGetSubString(message,0,6) == "exclude")) && (id == ObjectOwner))
 
        {
 
            // before adding them to the 'Exclude List', check if already on it
http_response(key req,integer stat, list met, string body)
            // they could be on the list simply as their own name, their name + a suffix, perhaps even both.
{
// core code by Jana Kamachi and Solar Alter. Adapted to suit by Debbie Trilling
            if ((DeriveNamePosition(message) != -1 ) || (DeriveNamePosition(message + SelfExcludedSuffix) != -1))
integer s1 = 0;
            {
integer s2 = 0;
                llOwnerSay(DeriveName(message) + " already exists on the " + ObjectName + "'s 'Exclude List'.");
integer s1l= 0;
            }
integer s2l= -3;
            else
s1 = llSubStringIndex(body,"<img alt=\"profile image\" src=\"http://secondlife.com/app/image/");
            {
s1l = llStringLength("<img alt=\"profile image\" src=\"http://secondlife.com/app/image/");
                // not on the list, so add them (without a suffix, as it is the Owner doing the adding)
s2 = llSubStringIndex(body,"\" class=\"parcelimg\" />");
                string NewExcludeName = DeriveName(message);
 
                ExcludeListing = (list)NewExcludeName + llList2List( ExcludeListing, 0, (ExcludeListSize - 2));
if(s1 == -1)
                llOwnerSay(NewExcludeName + " has been added to the "
{
                    + ObjectName + "'s 'Exclude List'. \nThere are now " + RemainingExcludeSlots());
// selected AV doesn't have a profile picture, so use the default instead
            }
ParticleStart((key)llList2String(DefaultTexture, (integer)llFrand((float)llGetListLength(DefaultTexture))));
        }
}
        else if (((llGetSubString(message,0,6) == "Include") || (llGetSubString(message,0,6) == "include")) && (id == ObjectOwner))
else
        {
{
            // they could be on the list simply as their own name, their name + a suffix, perhaps even both. We need to test for all scenerios
//check whether this was the texture used last time
            // locate their position within in 'Exclude List', if they do exist
if ((key)llGetSubString(body,s1+s1l,s2+s2l) == LastTexture)
            integer NamePositionTest = DeriveNamePosition(message);
{
            integer NamePositionSuffixTest = DeriveNamePosition(message + SelfExcludedSuffix);
// same profile pic as last time. so display a random default instead
ParticleStart((key)llList2String(DefaultTexture, (integer)llFrand((float)llGetListLength(DefaultTexture))));
            // test to see if either are on the 'Exclude List'.
// clear the last texture out
            if ((NamePositionTest != -1) || (NamePositionSuffixTest != -1))
LastTexture = "";
            {
}
                // well, their name is definately on the 'Exclude list', but is it with or without a suffix? Is it both?
else
                // would look neater to do the next tests in an IF-IF/ELSE-ELSE, but we can save a lil memory using two IF statement (albeit with a tiny speed overhead)
{
                if (NamePositionTest != -1)
// are they on the KillJoyListing? if so, we had better pander to their delicate sensibilities & display a random default texture instead
                {
if (llListFindList(KillJoyListing, (list)llKey2Name(AVKey)) != -1 )
                    // it's on without a suffix
{
                    ExcludeListing = llDeleteSubList(ExcludeListing, NamePositionTest, NamePositionTest);
// they are on the 'Exclude List'
                }
ParticleStart((key)llList2String(DefaultTexture, (integer)llFrand((float)llGetListLength(DefaultTexture))));
}
                if (NamePositionSuffixTest != -1)
else
                {
{
                    //it's on with a suffix
// different profile from last time & not on KillJoyListing, so display it
                    ExcludeListing = llDeleteSubList(ExcludeListing, NamePositionSuffixTest, NamePositionSuffixTest);
ParticleStart((key)llGetSubString(body,s1+s1l,s2+s2l));
                }
// save the key for comparison purposes next time tho'
                llOwnerSay(DeriveName(message) + " has been removed from the "
LastTexture = (key)llGetSubString(body,s1+s1l,s2+s2l);
                    + ObjectName + "'s 'Exclude List'. \nThere are now " + RemainingExcludeSlots());
 
            }
// give a 'ShoutOut', if set to do so
            else
if (ShoutOut)
            {
{
                llOwnerSay(DeriveName(message) + " could not located on the " + ObjectName + "'s 'Exclude List'.");
GiveShoutOut();
            }
}
        }
}
        else if (((llGetSubString(message,0,7) == "ClearAll") || (llGetSubString(message,0,7) == "clearall") || (llGetSubString(message,0,8) == "clear all") || (llGetSubString(message,0,8) == "Clear all")) && (id == ObjectOwner))
}
        {
}
            ExcludeListing = [];
}
            llOwnerSay("The 'Exclude List' has been cleared. All " + RemainingExcludeSlots());
 
 
        }
listen(integer channel, string name, key id, string message)
        else if (((llGetSubString(message,0,3) == "List") || (llGetSubString(message,0,3) == "list")) && (id == ObjectOwner))
{
        {
llSetTimerEvent(0.0);
            if (llGetListLength(ExcludeListing) > 0)
llListenRemove(ListenChannel);
            {
message = llToLower(message);
                llOwnerSay("The following " + (string)llGetListLength(ExcludeListing) + " AV's are listed on the 'Exclude List'. Names ending with '" + SelfExcludedSuffix + "' chose to exclude themselves.\n"
 
                    + llDumpList2String(ExcludeListing, " | "));
if (message == "learnmore")
            }
{
            else
llLoadURL(DetectedUser, "Thank you for choosing to learn more about the " + ObjectName + ".\nThis link will take you to the relevant SL forum thread.", URL_FORUMTHREAD);
            {
}
                llOwnerSay("No AV's are listed on the 'Exclude List'.");
else if (message == "getscript")
            }
{
        }
llLoadURL(DetectedUser, "Thank you for choosing to look at the script for the " + ObjectName + ".\nThis link will allow you to get your own copy.", URL_WIKIPAGE);
        else
}
        {
else if (message == "excludeme")
            llInstantMessage(DetectedUser, "Unrecognised option or selection made from a timed-out menu.");
{
        }
KillJoyListing = KillJoyListing + llKey2Name(DetectedUser);
llInstantMessage(DetectedUser, "You have been added to the 'Exclude List' and the " + ObjectName + " will now ignore your presence");
    }
}
else if (message == "includeme")
    timer()
{
    {
KillJoyListing = llDeleteSubList(KillJoyListing, llListFindList(KillJoyListing, (list)llKey2Name(DetectedUser)), llListFindList(KillJoyListing, (list)llKey2Name(DetectedUser)));
        CloseUserListen();
llInstantMessage(DetectedUser, "You has been removed from the 'Exclude List' and the " + ObjectName + " looks forward to selecting you!");
    }
}
else
//default end
{
llInstantMessage(DetectedUser, "Unrecognised option or selection made from a timed-out menu.");
}
}
 
timer()
{
llSetTimerEvent(0.0);
llListenRemove(ListenChannel);
}
 
 
//default end
}
}
 
</lsl>
</source>

Latest revision as of 07:43, 25 January 2015

~ RANDOM AV PROFILE PICTURE PROJECTOR v5.4.5 by Debbie Trilling ~

Set-up, configuration and operation Help: http://wiki.secondlife.com/wiki/Talk:Random_AV_Profile_Projector

Profile: http://wiki.secondlife.com/wiki/User:Debbie_Trilling

KBcaution.png Important: This script relies on data scraped from world.secondlife.com. That site, and the page format it currently serves, will probably be replaced by my.secondlife.com pages in the coming months. The new pages use a different format and have different access controls, so expect this script to stop working if and when the change is made. See WEB-3510 for details.
// ~ RANDOM PROFILE PROJECTOR v5.4.5.20091126 by Debbie Trilling ~
 
// *** This script randomly selects an AV from a crowd & then displays their
// profile picture as texture on a prim and/or a 'holographic' image projected above the prim***
 
// Free to use as you wish by under condition that the title and this introduction remain in place,
// and that due credit continues to be given to Moriash Moreau, Coder Kas and
// Debbie Trilling.
 
// 2009-11-24 VD - mod to use <meta> tag, to work around WEB-1383
// 2009-11-26 VD - add fallback to <img> tag in case the profile changes are reverted
 
// TOUCH to switch ON and OFF
 
// ONLINE HELP AT:  http://wiki.secondlife.com/wiki/Talk:Random_AV_Profile_Projector
 
// ** PARAMETERS THAT YOU CAN CHANGE **
 
 
//************************
// GENERAL PARAMETERS
//************************
 
// listen channel for Owner
integer OwnerChannel = 54321;
 
// set the maximum number of entries in the 'Exclude List' at any one time
integer ExcludeListSize = 30;
 
// the number of seconds that a listen remains open before timing-out & automatically closing
float DialogTimeout = 20.00;
 
// how often in seconds the sensor fires
float RepeatTime = 35.00;
 
// sensor range in meters. Maximum 96m but in practice 10 to 30m because of particle draw distance
float Range = 25.00;
 
// sets the number of consecutive times that the scanner is allowed to operate without having located an AV within range
// eg: if RepeatTime = 60.0 seconds and TotalNoScansAllowed = 30, then the toy will operate for 1800 seconds (60x30, or 30 minutes) without locating
// anyone before it automatically powers down. Set to '0' to disable the auto-off function
integer TotalNoScansAllowed = 20;
 
// texture palette of UUID's. One will be randomly selected for display when an AV without a profile pic is selected
list DefaultTexturePalette = ["8fb9ad84-4183-51df-f566-9b21c3a610fe", "1201c3de-d022-c0a5-56bd-bc49ab971726", "c3eebd9e-ee92-a16f-f906-bc275928df86"];
 
// sets whether the DefaultTexturePalette will be used to texture/project when the toy is switched OFF. 'TRUE' to texture/project; 'FALSE' to have no texturing/projection when off
integer EmployDefaultTexture = TRUE;
 

string profile_key_prefix = "<meta name=\"imageid\" content=\"";
string profile_img_prefix = "<img alt=\"profile image\" src=\"http://secondlife.com/app/image/";
integer profile_key_prefix_length; // calculated from profile_key_prefix in InitialiseObject()
integer profile_img_prefix_length; // calculated from profile_img_prefix in InitialiseObject()
 
//************************
// PARTICLE EMISSION PARAMETERS
//************************
 
// set to TRUE to display the profile picture as a particle 'holographic' image above the prim
integer DisplayBanner = TRUE;
 
// width size in meters of the projected image (max 4.00)
float Size = 2.50;
 
// height above object the centre of projected image will be (theoretical max. 50.0, in practice 2.0 to 10.0))
float Height = 2.50;
 
 
//************************
// PRIM TEXTURE PARAMETERS
//************************
 
// set to TRUE to texture the prim with the profile picture on ALL_SIDES
integer TexturePrim = TRUE;
 
// the following 'Prim*' parameters effect the prim only if ("EmployDefaultTexture = FALSE") OR ("EmployDefaultTexture = TRUE" and "TexturePrim=FALSE")
// if TexturePrim = TRUE then the prim is automatically set to solid blank white no shiny with full bright as this is usually the best surface to display the profile picture
 
// texture to use for the prim when toy is OFF
key PrimUUIDTexture = "5748decc-f629-461c-9a36-a35a221fe21f";
 
// set to TRUE to turn on Full Bright on ALL_SIDES when the toy is OFF
integer PrimFullBright = FALSE;
 
// vector for the prim colour when toy is OFF
vector PrimColour = <0.0, 0.0, 0.0>;
 
// set the alpha of the prim from 0.0 (clear) to 1.0 (solid) for when toy is OFF
float PrimAlpha = 1.00;
 
// set the degree of 'shininess' to apply to the prim "0" = None, "1" = Low, "2" = medium, "3" = high
integer PrimShine = 3;
 
 
//************************
// FLOATING TEXT PARAMETERS
//************************
// set to TRUE for floating text above the prim; FALSE to disable the floating text
integer ApplyFloatingText = TRUE;
 
// set the text to be displayed
string FloatingText = "'Touch' for more information";
 
// set the text colour
vector FloatingTextColour = <1.0,1.0,0.0>;
 
// set the text alpha
float FloatingTextAlpha = 0.8;
 
 
//************************
// ROTATION PARAMETERS
//************************
 
// if TRUE, applies a slow rotation to the prim when the toy is swtiched ON and TexturePrim = TRUE; FALSE to disable rotation
integer ApplyRotation = FALSE;
 
 
//************************
// SHOUTOUT PARAMETERS
//************************
 
// set to 'TRUE' to give a 'ShoutOut' to the AV once they have been selected; 'FALSE' for no 'ShoutOut'
integer ShoutOut = TRUE;
 
// text to 'ShoutOut' when an AV's profile is projected. Text will be preceeded by their name, eg: "<AV Name>'s face is up in lights!"
string ShoutOutText = "'s face is up in lights!";
 
 
 
 
//************************
// ** DO NOT CHANGE BELOW THIS LINE **
//************************
 
 
 
 
integer Power = FALSE;
integer ListenChannel;
integer OwnerListenChannel;
integer NoSensorCounter = 0;
key AVKey = "";
key DetectedUser = "";
key ObjectOwner = "";
key LastTexture = "";
string OwnerName = "";
string ObjectName = "Profile Projector";
string Author = "Debbie Trilling";
string Supplier = "The Particle Crucible";
string Version = " v5.4.5.20091126";
string OwnerListenText = "OpenListen";
string SelfExcludedSuffix = "**";
string PowerText = "On";
list ExcludeListing = [];
 
 
 
GiveShoutOut()
{
    // any interaction with selected AV (give Inventory items etc) can safely be done from this function
    // this function will only execute if ShoutOut == TRUE
 
    //although fondly calling it a 'ShoutOut', it actually makes more sense to keep within the 20m range of llSay
    llSay(0, llKey2Name(AVKey) + ShoutOutText);
 
}
 
 
AnnounceWelcome()
{
    llOwnerSay(
        "\nThank you for your interest in this " + ObjectName + " "
            + Version + " created by " + Author + " at " + Supplier + ". \nHelp setting up, configuring and operating the "
        + ObjectName + " can be gotton at http://wiki.secondlife.com/wiki/Talk:Random_AV_Profile_Projector \nTOUCH the " + ObjectName + " to operate it.");
}
 
 
InitialiseObject()
{
    llParticleSystem([]);
    StopRotation();
    ObjectOwner = llGetOwner();
    OwnerName = llKey2Name(ObjectOwner);
    llSetObjectName(ObjectName + Version);
    llSetObjectDesc("Supplied free by " + Author + "'s " + Supplier);
    CloseAllListens();
    profile_key_prefix_length = llStringLength(profile_key_prefix);
    profile_img_prefix_length = llStringLength(profile_img_prefix);
}
 
 
SetFloatingText()
{
    if (ApplyFloatingText)
    {
        llSetText(FloatingText, FloatingTextColour, FloatingTextAlpha);
    }
    else
    {
        llSetText("",<1.0,1.0,1.0>,0.0);
    }
}
 
 
ProjectTexture()
{
    // are we going to use a default texture when toy is OFF?
    if (EmployDefaultTexture)
    {
        if (TexturePrim)
        {
            // using a default texture
            ApplyPrimSurface();
        }
        else
        {
            // not texturing the prim, so apply the prim preferences
            ApplyPrimPrefs();
        }
        ApplyDefaultTexture();
    }
    else
    {
        // we're not doing anything when the toy is OFF; change the prim to user preferences
        llParticleSystem([]);
        ApplyPrimPrefs();
    }
}
 
 
ApplyPrimSurface()
{
    // putting texture on the prim, let's make sure it is solid white, blank. full bright
    llSetPrimitiveParams([PRIM_FULLBRIGHT, ALL_SIDES, TRUE, PRIM_COLOR, ALL_SIDES, <1.0, 1.0, 1.0>, 1.0, PRIM_BUMP_SHINY, ALL_SIDES, 0, 0 ]);
}
 
 
ApplyPrimPrefs()
{
    llSetPrimitiveParams([PRIM_FULLBRIGHT, ALL_SIDES, PrimFullBright,
        PRIM_TEXTURE, ALL_SIDES, PrimUUIDTexture, <1.000000, 1.000000, 0.000000>, <0.000000, 0.000000, 0.000000>, 0.000000, PRIM_COLOR, ALL_SIDES, PrimColour, PrimAlpha, PRIM_BUMP_SHINY, ALL_SIDES, PrimShine, 0 ]);
}
 
 
StartUp()
{
    Power = TRUE;
    PowerText = "Off";
    llSensorRepeat("",NULL_KEY,AGENT,Range,PI,RepeatTime);
    NoSensorCounter = 0;
    ApplyPrimUpdate();
    llOwnerSay("\nThe " + ObjectName + " is now switched ON. Please wait...");
}
 
 
ApplyPrimUpdate()
{
    if (TexturePrim)
    {
        ApplyPrimSurface();
 
        if (ApplyRotation)
        {
            llTargetOmega(<0.0,0.0,1.0>, 0.2, PI);
        }
    }
}
 
ApplyDefaultTexture()
{
    ApplySelectedTexture((key)llList2String(DefaultTexturePalette, (integer)llFrand((float)llGetListLength(DefaultTexturePalette))));
}
 
 
string RemainingExcludeSlots()
{
    string RemainingSlots = (string)(ExcludeListSize - llGetListLength(ExcludeListing)) + " slots are now available.";
    return RemainingSlots;
}
 
 
string DeriveName(string messagecapture)
{
    string DerivedName = llGetSubString(llStringTrim(messagecapture,STRING_TRIM),8,llStringLength(llStringTrim(messagecapture,STRING_TRIM)));
    return DerivedName;
}
 
 
integer DeriveNamePosition(string messagecapture)
{
    integer DerivedNamePosition = llListFindList(ExcludeListing, (list)llGetSubString(llStringTrim(messagecapture,STRING_TRIM),8,llStringLength(llStringTrim(messagecapture,STRING_TRIM))));
    return DerivedNamePosition;
}
 
 
ApplySelectedTexture(key texture)
{
    if (DisplayBanner)
    {
        // make the particle banner if required
        //core code by Moriash Moreau. Adapted to suit by Debbie Trilling
        llParticleSystem([
            PSYS_PART_FLAGS, 0,
            PSYS_SRC_PATTERN, 4,
            PSYS_PART_START_ALPHA, 0.50,
            PSYS_PART_END_ALPHA, 0.50,
            PSYS_PART_START_COLOR, <1.0,1.0,1.0>,
            PSYS_PART_END_COLOR, <1.0,1.0,1.0>,
            PSYS_PART_START_SCALE, <Size * 1.6 ,Size,0.00>,
            PSYS_PART_END_SCALE, <Size * 1.6,Size,0.00>,
            PSYS_PART_MAX_AGE, 1.20,
            PSYS_SRC_MAX_AGE, 0.00,
            PSYS_SRC_ACCEL, <0.0,0.0,0.0>,
            PSYS_SRC_ANGLE_BEGIN, 0.00,
            PSYS_SRC_ANGLE_END, 0.00,
            PSYS_SRC_BURST_PART_COUNT, 8,
            PSYS_SRC_BURST_RADIUS, Height,
            PSYS_SRC_BURST_RATE, 0.10,
            PSYS_SRC_BURST_SPEED_MIN, 0.00,
            PSYS_SRC_BURST_SPEED_MAX, 0.00,
            PSYS_SRC_OMEGA, <0.00,0.00,0.00>,
            PSYS_SRC_TEXTURE, texture]);
    }
 
    if (TexturePrim)
    {
        llSetTexture(texture, ALL_SIDES);
    }
}
 
 
ShutDown()
{
    Power = FALSE;
    PowerText = "On";
    CloseAllListens();
    llSensorRemove();
    StopRotation();
    ProjectTexture();
    llOwnerSay("\nThe " + ObjectName + " is now switched OFF.");
}
 
 
StopRotation()
{
    llTargetOmega(<0.0,0.0,0.0>, 0.0, PI);
}
 
 
CloseAllListens()
{
    CloseUserListen();
    llListenRemove(OwnerListenChannel);
    OwnerListenText = "OpenListen";
}
 
 
CloseUserListen()
{
    llSetTimerEvent(0.0);
    llListenRemove(ListenChannel);
}
 
 
default
{
 
    on_rez(integer start_param)
    {
        // reset script on rez
        llResetScript();
    }
 
    changed( integer change )
    {
        if(change & CHANGED_OWNER )
        {
            // reset script on change of owner
            llResetScript();
        }
    }
 
    state_entry()
    {
        //initialise system
        InitialiseObject();
        SetFloatingText();
        ProjectTexture();
        AnnounceWelcome();
    }
 
    touch_start(integer total_number)
    {
        DetectedUser = llDetectedKey(0);
        list MenuItems = ["LearnMore", "GetScript", "Help"];
        string MenuText = "MAIN MENU: Please make a selection within " + (string)llFloor(DialogTimeout)
            + " seconds.\n- LearnMore: Read the forum thread on this product\n- GetScript: Get yourself the latest version of this free script\n- Help: Link to the "
            + ObjectName + " Help page";
 
        if (DetectedUser == ObjectOwner)
        {
            // touched by Owner
 
            if (Power)
            {
                MenuItems = [PowerText, OwnerListenText] + MenuItems;
                MenuText = MenuText + "\n- Off: Switch off \n- Open/CloseListen: Opens/Closes Owner listen channel " + (string)OwnerChannel;
            }
            else
            {
                MenuItems = (list)PowerText + MenuItems;
                MenuText = MenuText + "\n- On: Switch on";
            }
        }
        else
        {
            // touched by someone other than Owner. Send them a message & a dialog box of options
 
            // if they placed their own name on 'Exclude List', so give opportunity to clear their name, else give opportunity to exclude their name
            if (llListFindList(ExcludeListing, (list)(llKey2Name(AVKey) + SelfExcludedSuffix)) != -1 )
            {
                // they placed themself on the 'Exclude List' so are therefore allowed to clear their name
                MenuItems = (list)"IncludeMe" + MenuItems;
                MenuText =  MenuText + "\n- IncludeMe: Have the " + ObjectName + " remove your name from the 'Exclude List'";
            }
            else
            {
                // they have not placed themself on the 'Exclude List', so check whether the Owner has already done it
                if (llListFindList(ExcludeListing, (list)llKey2Name(AVKey)) == -1 )
                {
                    // neither they themself nor the Owner has previously excluded them, so give them the opportunity to exclude themselves now
                    MenuItems = (list)"ExcludeMe" + MenuItems;
                    MenuText =  MenuText + "\n- ExcludeMe: Have your name added to the " + ObjectName + "'s 'Exclude List'";
                }
            }
            llInstantMessage(DetectedUser, "\nThank you for your interest in the " + ObjectName + " created by " + Author + "\nThe dialog menu offers a number of options.");
        }
        // generate the dialog menu
        integer CommChannel = (-200000 - ((integer)llFrand(12345) * -1));
        ListenChannel = llListen(CommChannel, "", DetectedUser, "");
        llDialog(DetectedUser, MenuText, MenuItems, CommChannel);
        llSetTimerEvent(DialogTimeout);
    }
 
 
    sensor(integer total_number)
    {
        // save the AV key in case it is needed for a 'ShoutOut'
        AVKey = llDetectedKey((integer)llFrand(total_number));
        // core code by Coder Kas. Adapted to suit by Debbie Trilling
        string URL_RESIDENT = "http://world.secondlife.com/resident/";
        llHTTPRequest( URL_RESIDENT + (string)AVKey,[HTTP_METHOD,"GET"],"");
    }
 
 
    no_sensor()
    {
        // counts the number of times that the scanner doesn't find anyone in range. If TotalNoScansAllowed is set to greater than zero, automatically powers down the toy
        // when the number of no_sensors exceeds TotalNoScansAllowed. However, this functionality is disabled if TotalNoScansAllowed is set to zero.
        NoSensorCounter++;
        if ((NoSensorCounter > TotalNoScansAllowed) && (TotalNoScansAllowed > 0))
        {
            ShutDown();
            llInstantMessage(ObjectOwner, "\nThe " + ObjectName + " has been automatically switched OFF as no Agents have been detected within the set timeframe.");
        }
        else
        {
            ApplyDefaultTexture();
        }
    }
 
 
    http_response(key req,integer stat, list met, string body)
    {
        // core code by Coder Kas. Adapted to suit by Debbie Trilling
        integer s1 = llSubStringIndex(body, profile_key_prefix);
        integer s1l = profile_key_prefix_length;
        if(s1 == -1)
        { // second try with img tag
            s1 = llSubStringIndex(body, profile_img_prefix);
            s1l = profile_img_prefix_length;
        }

        if(s1 == -1)
        {
            // selected AV doesn't have a profile picture, so use the default instead
            ApplyDefaultTexture();
        }
        else
        {
            key NewTexture = (key)llGetSubString(body,s1+s1l,s1+s1l+35);
 
            //check whether this was the texture used last time
            if (NewTexture == LastTexture || NewTexture == NULL_KEY)
            {
                // same profile pic as last time. so display a random default instead
                ApplyDefaultTexture();
                // clear the last texture out
                LastTexture = "";
            }
            else
            {
                // are they on the ExcludeListing, with or without a suffix? if so, display a random default texture instead
                if ((llListFindList(ExcludeListing, (list)llKey2Name(AVKey)) != -1 ) || (llListFindList(ExcludeListing, (list)(llKey2Name(AVKey) + SelfExcludedSuffix)) != -1))
                {
                    // they are on the 'Exclude List'
                    ApplyDefaultTexture();
                }
                else
                {
                    // different profile from last time & not on ExcludeListing, so display it
                    ApplySelectedTexture(NewTexture);
                    // save the key for comparison purposes next time tho'
                    LastTexture = NewTexture;
 
                    // give a 'ShoutOut', if set to do so
                    if (ShoutOut)
                    {
                        GiveShoutOut();
                    }
                }
            }
        }
    }
 
 
    listen(integer channel, string name, key id, string message)
    {
 
        if (message == "LearnMore")
        {
            string URL_FORUMTHREAD = "http://forums-archive.secondlife.com/54/1b/225692/1.html";
            llLoadURL(DetectedUser, "Thank you for choosing to learn more about the " + ObjectName + ".This link will take you to the relevant SL forum thread.", URL_FORUMTHREAD);
            CloseUserListen();
        }
        else if (message == "GetScript")
        {
            string URL_WIKIPAGE = "http://wiki.secondlife.com/wiki/User:Debbie_Trilling";
            llLoadURL(DetectedUser, "Thank you for choosing to look at the script for the " + ObjectName + ".This link will allow you to get your own free copy.", URL_WIKIPAGE);
            CloseUserListen();
        }
        else if (message == "Help")
        {
            string URL_HELPPAGE = "http://wiki.secondlife.com/wiki/Talk:Random_AV_Profile_Projector";
            llLoadURL(DetectedUser, "This link will take you to the " + ObjectName + "'s Help page.", URL_HELPPAGE);
            CloseUserListen();
        }
        else if (message == "ExcludeMe")
        {
            // as a suffix to the name, as it is the user adding their own name
            ExcludeListing = (list)(llKey2Name(DetectedUser) + SelfExcludedSuffix) + llList2List( ExcludeListing, 0, (ExcludeListSize - 2));
            llInstantMessage(DetectedUser, "You have been added to the " + ObjectName + "'s 'Exclude List'");
            CloseUserListen();
        }
        else if (message == "IncludeMe")
        {
            integer ExcludeListPosition = llListFindList(ExcludeListing, (list)(llKey2Name(DetectedUser) + SelfExcludedSuffix));
            ExcludeListing = llDeleteSubList(ExcludeListing, ExcludeListPosition, ExcludeListPosition);
            llInstantMessage(DetectedUser, "You have been removed from the " + ObjectName + "'s 'Exclude List'");
            CloseUserListen();
        }
        else if ((message == "On") && (id == ObjectOwner))
        {
            StartUp();
        }
        else if ((message == "Off") && (id == ObjectOwner))
        {
            ShutDown();
        }
        else if ((message == "OpenListen") && (id == ObjectOwner))
        {
            // open the Owner Only channel
            OwnerListenChannel = llListen(OwnerChannel, "", ObjectOwner, "");
            OwnerListenText = "CloseListen";
            llOwnerSay("Owner Only channel " + (string)OwnerChannel + " is now open for you.\n Options are: 'Exclude <AV_NAME>', 'Include <AV_NAME>', 'ClearAll' and 'List'");
        }
        else if ((message == "CloseListen") && (id == ObjectOwner))
        {
            // close the Owner Only channel
            llListenRemove(OwnerListenChannel);
            OwnerListenText = "OpenListen";
            llOwnerSay("Owner Only channel " + (string)OwnerChannel + " is now closed.");
        }
        else if (((llGetSubString(message,0,6) == "Exclude") || (llGetSubString(message,0,6) == "exclude")) && (id == ObjectOwner))
        {
            // before adding them to the 'Exclude List', check if already on it
            // they could be on the list simply as their own name, their name + a suffix, perhaps even both.
 
            if ((DeriveNamePosition(message) != -1 ) || (DeriveNamePosition(message + SelfExcludedSuffix) != -1))
            {
                llOwnerSay(DeriveName(message) + " already exists on the " + ObjectName + "'s 'Exclude List'.");
            }
            else
            {
                // not on the list, so add them (without a suffix, as it is the Owner doing the adding)
                string NewExcludeName = DeriveName(message);
                ExcludeListing = (list)NewExcludeName + llList2List( ExcludeListing, 0, (ExcludeListSize - 2));
                llOwnerSay(NewExcludeName + " has been added to the "
                    + ObjectName + "'s 'Exclude List'. \nThere are now " + RemainingExcludeSlots());
            }
        }
        else if (((llGetSubString(message,0,6) == "Include") || (llGetSubString(message,0,6) == "include")) && (id == ObjectOwner))
        {
            // they could be on the list simply as their own name, their name + a suffix, perhaps even both. We need to test for all scenerios
            // locate their position within in 'Exclude List', if they do exist
            integer NamePositionTest = DeriveNamePosition(message);
            integer NamePositionSuffixTest = DeriveNamePosition(message + SelfExcludedSuffix);
 
            // test to see if either are on the 'Exclude List'.
            if ((NamePositionTest != -1) || (NamePositionSuffixTest != -1))
            {
                // well, their name is definately on the 'Exclude list', but is it with or without a suffix? Is it both?
                // would look neater to do the next tests in an IF-IF/ELSE-ELSE, but we can save a lil memory using two IF statement (albeit with a tiny speed overhead)
                if (NamePositionTest != -1)
                {
                    // it's on without a suffix
                    ExcludeListing = llDeleteSubList(ExcludeListing, NamePositionTest, NamePositionTest);
                }
 
                if (NamePositionSuffixTest != -1)
                {
                    //it's on with a suffix
                    ExcludeListing = llDeleteSubList(ExcludeListing, NamePositionSuffixTest, NamePositionSuffixTest);
                }
                llOwnerSay(DeriveName(message) + " has been removed from the "
                    + ObjectName + "'s 'Exclude List'. \nThere are now " + RemainingExcludeSlots());
            }
            else
            {
                llOwnerSay(DeriveName(message) + " could not located on the " + ObjectName + "'s 'Exclude List'.");
            }
        }
        else if (((llGetSubString(message,0,7) == "ClearAll") || (llGetSubString(message,0,7) == "clearall") || (llGetSubString(message,0,8) == "clear all") || (llGetSubString(message,0,8) == "Clear all")) && (id == ObjectOwner))
        {
            ExcludeListing = [];
            llOwnerSay("The 'Exclude List' has been cleared. All " + RemainingExcludeSlots());
 
        }
        else if (((llGetSubString(message,0,3) == "List") || (llGetSubString(message,0,3) == "list")) && (id == ObjectOwner))
        {
            if (llGetListLength(ExcludeListing) > 0)
            {
                llOwnerSay("The following " + (string)llGetListLength(ExcludeListing) + " AV's are listed on the 'Exclude List'. Names ending with '" + SelfExcludedSuffix + "' chose to exclude themselves.\n"
                    + llDumpList2String(ExcludeListing, " | "));
            }
            else
            {
                llOwnerSay("No AV's are listed on the 'Exclude List'.");
            }
        }
        else
        {
            llInstantMessage(DetectedUser, "Unrecognised option or selection made from a timed-out menu.");
        }
 
    }
 
    timer()
    {
        CloseUserListen();
    }
 
//default end
}