Difference between revisions of "Random AV Profile Projector"

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


'''~ RANDOM AV PROFILE PICTURE PROJECTOR v5.4 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
'''Set-up, configuration and operation Help:''' http://wiki.secondlife.com/wiki/Talk:Random_AV_Profile_Projector
Line 7: Line 7:
'''Profile:''' http://wiki.secondlife.com/wiki/User:Debbie_Trilling
'''Profile:''' http://wiki.secondlife.com/wiki/User:Debbie_Trilling


{{World.secondlife.com changes}}


<lsl>
<source lang="lsl2">
// ~ RANDOM PROFILE PROJECTOR v5.4 by Debbie Trilling ~
// ~ 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
// 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
// listen channel for Owner
integer OwnerChannel = 54321;
integer OwnerChannel = 54321;
 
// set the maximum number of entries in the 'Exclude List' at any one time
// set the maximum number of entries in the 'Exclude List' at any one time
integer ExcludeListSize = 30;
integer ExcludeListSize = 30;
 
// the number of seconds that a listen remains open before timing-out & automatically closing
// the number of seconds that a listen remains open before timing-out & automatically closing
float DialogTimeout = 20.00;
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;
 
// 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 DefaultTexturePalette = ["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
// 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;
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 ("EmployDefaultTexture = FALSE") OR ("EmployDefaultTexture = TRUE" and "TexturePrim=FALSE")
// 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
// 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 PrimUUIDTexture = "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
// set the degree of 'shininess' to apply to the prim "0" = None, "1" = Low, "2" = medium, "3" = high
integer PrimShine = 3;
integer PrimShine = 3;
 
 
//************************
//************************
// FLOATING TEXT PARAMETERS
// FLOATING TEXT PARAMETERS
Line 101: Line 110:
// set to TRUE for floating text above the prim; FALSE to disable the floating text
// set to TRUE for floating text above the prim; FALSE to disable the floating text
integer ApplyFloatingText = TRUE;
integer ApplyFloatingText = TRUE;
 
// set the text to be displayed
// set the text to be displayed
string FloatingText = "'Touch' for more information";
string FloatingText = "'Touch' for more information";
 
// set the text colour
// set the text colour
vector FloatingTextColour = <1.0,1.0,0.0>;
vector FloatingTextColour = <1.0,1.0,0.0>;
 
// set the text alpha
// set the text alpha
float FloatingTextAlpha = 0.8;
float FloatingTextAlpha = 0.8;
 
 
//************************
//************************
// ROTATION PARAMETERS
// ROTATION PARAMETERS
//************************
//************************
 
// if TRUE, applies a slow rotation to the prim when the toy is swtiched ON and TexturePrim = TRUE; FALSE to disable rotation
// if TRUE, applies a slow rotation to the prim when the toy is swtiched ON and TexturePrim = TRUE; FALSE to disable rotation
integer ApplyRotation = TRUE;
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 **
//************************
//************************
 
 
 
 
integer Power = FALSE;
integer Power = FALSE;
integer ListenChannel;
integer ListenChannel;
Line 152: Line 161:
string Author = "Debbie Trilling";
string Author = "Debbie Trilling";
string Supplier = "The Particle Crucible";
string Supplier = "The Particle Crucible";
string Version = " v5.4";
string Version = " v5.4.5.20091126";
string OwnerListenText = "OpenListen";
string OwnerListenText = "OpenListen";
string SelfExcludedSuffix = "**";
string SelfExcludedSuffix = "**";
string PowerText = "On";
string PowerText = "On";
list ExcludeListing = [];
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()
AnnounceWelcome()
{
{
llOwnerSay(
    llOwnerSay(
"\nThank you for your interest in this " + ObjectName + " "
        "\nThank you for your interest in this " + ObjectName + " "
+ Version + " created by " + Author + " at " + Supplier + ". \nHelp setting up, configuring and operating the "
            + 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.");
        + ObjectName + " can be gotton at http://wiki.secondlife.com/wiki/Talk:Random_AV_Profile_Projector \nTOUCH the " + ObjectName + " to operate it.");
}
}
 
 
InitialiseObject()
InitialiseObject()
{
{
llParticleSystem([]);
    llParticleSystem([]);
StopRotation();
    StopRotation();
ObjectOwner = llGetOwner();
    ObjectOwner = llGetOwner();
OwnerName = llKey2Name(ObjectOwner);
    OwnerName = llKey2Name(ObjectOwner);
llSetObjectName(ObjectName + Version);
    llSetObjectName(ObjectName + Version);
llSetObjectDesc("Supplied free by " + Author + "'s " + Supplier);
    llSetObjectDesc("Supplied free by " + Author + "'s " + Supplier);
CloseAllListens();
    CloseAllListens();
    profile_key_prefix_length = llStringLength(profile_key_prefix);
    profile_img_prefix_length = llStringLength(profile_img_prefix);
}
}
 
 
SetFloatingText()
SetFloatingText()
{
{
if (ApplyFloatingText)
    if (ApplyFloatingText)
{
    {
llSetText(FloatingText, FloatingTextColour, FloatingTextAlpha);
        llSetText(FloatingText, FloatingTextColour, FloatingTextAlpha);
}
    }
else
    else
{
    {
llSetText("",<1.0,1.0,1.0>,0.0);
        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 (EmployDefaultTexture)
    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, PRIM_BUMP_SHINY, ALL_SIDES, 0, 0 ]);
            ApplyPrimSurface();
}
        }
else
        else
{
        {
// not texturing the prim, so apply the prim preferences
            // not texturing the prim, so apply the prim preferences
ApplyPrimPrefs();
            ApplyPrimPrefs();
}
        }
ApplyDefaultTexture();
        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, 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 ]);
        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()
StartUp()
{
{
Power = TRUE;
    Power = TRUE;
PowerText = "Off";
    PowerText = "Off";
llSensorRepeat("",NULL_KEY,AGENT,Range,PI,RepeatTime);
    llSensorRepeat("",NULL_KEY,AGENT,Range,PI,RepeatTime);
NoSensorCounter = 0;
    NoSensorCounter = 0;
ApplySlowRotation();
    ApplyPrimUpdate();
llOwnerSay("\nThe " + ObjectName + " is now switched ON. Please wait...");
    llOwnerSay("\nThe " + ObjectName + " is now switched ON. Please wait...");
}
}
 
 
ApplySlowRotation()
ApplyPrimUpdate()
{
{
if ((ApplyRotation) && (TexturePrim))
    if (TexturePrim)
{
    {
llTargetOmega(<0.0,0.0,1.0>, 0.2, PI);
        ApplyPrimSurface();
}
        if (ApplyRotation)
        {
            llTargetOmega(<0.0,0.0,1.0>, 0.2, PI);
        }
    }
}
}
 
ApplyDefaultTexture()
ApplyDefaultTexture()
{
{
ApplySelectedTexture((key)llList2String(DefaultTexturePalette, (integer)llFrand((float)llGetListLength(DefaultTexturePalette))));
    ApplySelectedTexture((key)llList2String(DefaultTexturePalette, (integer)llFrand((float)llGetListLength(DefaultTexturePalette))));
}
}
 
 
string RemainingExcludeSlots()
string RemainingExcludeSlots()
{
{
string RemainingSlots = (string)(ExcludeListSize - llGetListLength(ExcludeListing)) + " slots are now available.";
    string RemainingSlots = (string)(ExcludeListSize - llGetListLength(ExcludeListing)) + " slots are now available.";
return RemainingSlots;
    return RemainingSlots;
}
}
 
 
string DeriveName(string messagecapture)
string DeriveName(string messagecapture)
{
{
string DerivedName = llGetSubString(llStringTrim(messagecapture,STRING_TRIM),8,llStringLength(llStringTrim(messagecapture,STRING_TRIM)));
    string DerivedName = llGetSubString(llStringTrim(messagecapture,STRING_TRIM),8,llStringLength(llStringTrim(messagecapture,STRING_TRIM)));
return DerivedName;
    return DerivedName;
}
}
 
 
integer DeriveNamePosition(string messagecapture)
integer DeriveNamePosition(string messagecapture)
{
{
integer DerivedNamePosition = llListFindList(ExcludeListing, (list)llGetSubString(llStringTrim(messagecapture,STRING_TRIM),8,llStringLength(llStringTrim(messagecapture,STRING_TRIM))));
    integer DerivedNamePosition = llListFindList(ExcludeListing, (list)llGetSubString(llStringTrim(messagecapture,STRING_TRIM),8,llStringLength(llStringTrim(messagecapture,STRING_TRIM))));
return DerivedNamePosition;
    return DerivedNamePosition;
}
}
 
 
ApplySelectedTexture(key texture)
ApplySelectedTexture(key texture)
{
{
if (DisplayBanner)
    if (DisplayBanner)
{
    {
// make the particle banner if required
        // make the particle banner if required
//core code by Moriash Moreau. Adapted to suit by Debbie Trilling
        //core code by Moriash Moreau. Adapted to suit by Debbie Trilling
llParticleSystem([
        llParticleSystem([
PSYS_PART_FLAGS, 0,
            PSYS_PART_FLAGS, 0,
PSYS_SRC_PATTERN, 4,
            PSYS_SRC_PATTERN, 4,
PSYS_PART_START_ALPHA, 0.50,
            PSYS_PART_START_ALPHA, 0.50,
PSYS_PART_END_ALPHA, 0.50,
            PSYS_PART_END_ALPHA, 0.50,
PSYS_PART_START_COLOR, <1.0,1.0,1.0>,
            PSYS_PART_START_COLOR, <1.0,1.0,1.0>,
PSYS_PART_END_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_START_SCALE, <Size * 1.6 ,Size,0.00>,
PSYS_PART_END_SCALE, <Size * 1.6,Size,0.00>,
            PSYS_PART_END_SCALE, <Size * 1.6,Size,0.00>,
PSYS_PART_MAX_AGE, 1.20,
            PSYS_PART_MAX_AGE, 1.20,
PSYS_SRC_MAX_AGE, 0.00,
            PSYS_SRC_MAX_AGE, 0.00,
PSYS_SRC_ACCEL, <0.0,0.0,0.0>,
            PSYS_SRC_ACCEL, <0.0,0.0,0.0>,
PSYS_SRC_ANGLE_BEGIN, 0.00,
            PSYS_SRC_ANGLE_BEGIN, 0.00,
PSYS_SRC_ANGLE_END, 0.00,
            PSYS_SRC_ANGLE_END, 0.00,
PSYS_SRC_BURST_PART_COUNT, 8,
            PSYS_SRC_BURST_PART_COUNT, 8,
PSYS_SRC_BURST_RADIUS, Height,
            PSYS_SRC_BURST_RADIUS, Height,
PSYS_SRC_BURST_RATE, 0.10,
            PSYS_SRC_BURST_RATE, 0.10,
PSYS_SRC_BURST_SPEED_MIN, 0.00,
            PSYS_SRC_BURST_SPEED_MIN, 0.00,
PSYS_SRC_BURST_SPEED_MAX, 0.00,
            PSYS_SRC_BURST_SPEED_MAX, 0.00,
PSYS_SRC_OMEGA, <0.00,0.00,0.00>,
            PSYS_SRC_OMEGA, <0.00,0.00,0.00>,
PSYS_SRC_TEXTURE, texture]);
            PSYS_SRC_TEXTURE, texture]);
}
    }
 
if (TexturePrim)
    if (TexturePrim)
{
    {
llSetTexture(texture, ALL_SIDES);
        llSetTexture(texture, ALL_SIDES);
}
    }
}
}
 
 
ShutDown()
ShutDown()
{
{
Power = FALSE;
    Power = FALSE;
PowerText = "On";
    PowerText = "On";
CloseAllListens();
    CloseAllListens();
llSensorRemove();
    llSensorRemove();
StopRotation();
    StopRotation();
ProjectTexture();
    ProjectTexture();
llOwnerSay("\nThe " + ObjectName + " is now switched OFF.");
    llOwnerSay("\nThe " + ObjectName + " is now switched OFF.");
}
}
 
 
StopRotation()
StopRotation()
{
{
llTargetOmega(<0.0,0.0,0.0>, 0.0, PI);
    llTargetOmega(<0.0,0.0,0.0>, 0.0, PI);
}
}
 
 
CloseAllListens()
CloseAllListens()
{
{
CloseUserListen();
    CloseUserListen();
llListenRemove(OwnerListenChannel);
    llListenRemove(OwnerListenChannel);
OwnerListenText = "OpenListen";
    OwnerListenText = "OpenListen";
}
}
 
 
CloseUserListen()
CloseUserListen()
{
{
llSetTimerEvent(0.0);
    llSetTimerEvent(0.0);
llListenRemove(ListenChannel);
    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
llResetScript();
            ApplyDefaultTexture();
}
        }
 
        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
// reset script on change of owner
            if (NewTexture == LastTexture || NewTexture == NULL_KEY)
llResetScript();
            {
}
                // same profile pic as last time. so display a random default instead
}
                ApplyDefaultTexture();
 
                // clear the last texture out
state_entry()
                LastTexture = "";
{
            }
//initialise system
            else
InitialiseObject();
            {
SetFloatingText();
                // are they on the ExcludeListing, with or without a suffix? if so, display a random default texture instead
ProjectTexture();
                if ((llListFindList(ExcludeListing, (list)llKey2Name(AVKey)) != -1 ) || (llListFindList(ExcludeListing, (list)(llKey2Name(AVKey) + SelfExcludedSuffix)) != -1))
AnnounceWelcome();
                {
}
                    // they are on the 'Exclude List'
 
                    ApplyDefaultTexture();
touch_start(integer total_number)
                }
{
                else
DetectedUser = llDetectedKey(0);
                {
list MenuItems = ["LearnMore", "GetScript", "Help"];
                    // different profile from last time & not on ExcludeListing, so display it
string MenuText = "MAIN MENU: Please make a selection within " + (string)llFloor(DialogTimeout)
                    ApplySelectedTexture(NewTexture);
+ " 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 "
                    // save the key for comparison purposes next time tho'
+ ObjectName + " Help page";
                    LastTexture = NewTexture;
 
if (DetectedUser == ObjectOwner)
                    // give a 'ShoutOut', if set to do so
{
                    if (ShoutOut)
// touched by Owner
                    {
 
                        GiveShoutOut();
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;
    listen(integer channel, string name, key id, string message)
MenuText = MenuText + "\n- On: Switch on";
    {
}
}
        if (message == "LearnMore")
else
        {
{
            string URL_FORUMTHREAD = "http://forums-archive.secondlife.com/54/1b/225692/1.html";
// touched by someone other than Owner. Send them a message & a dialog box of options
            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();
// 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 )
        else if (message == "GetScript")
{
        {
// they placed themself on the 'Exclude List' so are therefore allowed to clear their name
            string URL_WIKIPAGE = "http://wiki.secondlife.com/wiki/User:Debbie_Trilling";
MenuItems = (list)"IncludeMe" + MenuItems;
            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);
MenuText =  MenuText + "\n- IncludeMe: Have the " + ObjectName + " remove your name from the 'Exclude List'";
            CloseUserListen();
}
        }
else
        else if (message == "Help")
{
        {
// they have not placed themself on the 'Exclude List', so check whether the Owner has already done it
            string URL_HELPPAGE = "http://wiki.secondlife.com/wiki/Talk:Random_AV_Profile_Projector";
if (llListFindList(ExcludeListing, (list)llKey2Name(AVKey)) == -1 )
            llLoadURL(DetectedUser, "This link will take you to the " + ObjectName + "'s Help page.", URL_HELPPAGE);
{
            CloseUserListen();
// neither they themself nor the Owner has previously excluded them, so give them the opportunity to exclude themselves now
        }
MenuItems = (list)"ExcludeMe" + MenuItems;
        else if (message == "ExcludeMe")
MenuText = MenuText + "\n- ExcludeMe: Have your name added to the " + ObjectName + "'s 'Exclude List'";
        {
}
            // 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, "\nThank you for your interest in the " + ObjectName + " created by " + Author + "\nThe dialog menu offers a number of options.");
            llInstantMessage(DetectedUser, "You have been added to the " + ObjectName + "'s 'Exclude List'");
}
            CloseUserListen();
// generate the dialog menu
        }
integer CommChannel = (-200000 - ((integer)llFrand(12345) * -1));
        else if (message == "IncludeMe")
ListenChannel = llListen(CommChannel, "", DetectedUser, "");
        {
llDialog(DetectedUser, MenuText, MenuItems, CommChannel);
            integer ExcludeListPosition = llListFindList(ExcludeListing, (list)(llKey2Name(DetectedUser) + SelfExcludedSuffix));
llSetTimerEvent(DialogTimeout);
            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'
            StartUp();
AVKey = llDetectedKey((integer)llFrand(total_number));
        }
// core code by Jana Kamachi and Solar Alter. Adapted to suit by Debbie Trilling
        else if ((message == "Off") && (id == ObjectOwner))
string URL_RESIDENT = "http://world.secondlife.com/resident/";
        {
llHTTPRequest( URL_RESIDENT + (string)AVKey,[HTTP_METHOD,"GET"],"");
            ShutDown();
}
        }
 
        else if ((message == "OpenListen") && (id == ObjectOwner))
 
        {
no_sensor()
            // open the Owner Only channel
{
            OwnerListenChannel = llListen(OwnerChannel, "", 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
            OwnerListenText = "CloseListen";
// when the number of no_sensors exceeds TotalNoScansAllowed. However, this functionality is disabled if TotalNoScansAllowed is set to zero.
            llOwnerSay("Owner Only channel " + (string)OwnerChannel + " is now open for you.\n Options are: 'Exclude <AV_NAME>', 'Include <AV_NAME>', 'ClearAll' and 'List'");
NoSensorCounter++;
        }
if ((NoSensorCounter > TotalNoScansAllowed) && (TotalNoScansAllowed > 0))
        else if ((message == "CloseListen") && (id == ObjectOwner))
{
        {
ShutDown();
            // close the Owner Only channel
llInstantMessage(ObjectOwner, "\nThe " + ObjectName + " has been automatically switched OFF as no Agents have been detected within the set timeframe.");
            llListenRemove(OwnerListenChannel);
}
            OwnerListenText = "OpenListen";
else
            llOwnerSay("Owner Only channel " + (string)OwnerChannel + " is now closed.");
{
        }
ApplyDefaultTexture();
        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.
 
http_response(key req,integer stat, list met, string body)
            if ((DeriveNamePosition(message) != -1 ) || (DeriveNamePosition(message + SelfExcludedSuffix) != -1))
{
            {
// core code by Jana Kamachi and Solar Alter. Adapted to suit by Debbie Trilling
                llOwnerSay(DeriveName(message) + " already exists on the " + ObjectName + "'s 'Exclude List'.");
integer s1 = 0;
            }
integer s2 = 0;
            else
integer s1l= 0;
            {
integer s2l= -3;
                // not on the list, so add them (without a suffix, as it is the Owner doing the adding)
s1 = llSubStringIndex(body,"<img alt=\"profile image\" src=\"http://secondlife.com/app/image/");
                string NewExcludeName = DeriveName(message);
s1l = llStringLength("<img alt=\"profile image\" src=\"http://secondlife.com/app/image/");
                ExcludeListing = (list)NewExcludeName + llList2List( ExcludeListing, 0, (ExcludeListSize - 2));
s2 = llSubStringIndex(body,"\" class=\"parcelimg\" />");
                llOwnerSay(NewExcludeName + " has been added to the "
 
                    + ObjectName + "'s 'Exclude List'. \nThere are now " + RemainingExcludeSlots());
if(s1 == -1)
            }
{
        }
// selected AV doesn't have a profile picture, so use the default instead
        else if (((llGetSubString(message,0,6) == "Include") || (llGetSubString(message,0,6) == "include")) && (id == ObjectOwner))
ApplyDefaultTexture();
        {
}
            // 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
else
            // locate their position within in 'Exclude List', if they do exist
{
            integer NamePositionTest = DeriveNamePosition(message);
//check whether this was the texture used last time
            integer NamePositionSuffixTest = DeriveNamePosition(message + SelfExcludedSuffix);
if ((key)llGetSubString(body,s1+s1l,s2+s2l) == LastTexture)
{
            // test to see if either are on the 'Exclude List'.
// same profile pic as last time. so display a random default instead
            if ((NamePositionTest != -1) || (NamePositionSuffixTest != -1))
ApplyDefaultTexture();
            {
// clear the last texture out
                // well, their name is definately on the 'Exclude list', but is it with or without a suffix? Is it both?
LastTexture = "";
                // 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)
else
                {
{
                    // it's on without a suffix
// are they on the ExcludeListing, with or without a suffix? if so, display a random default texture instead
                    ExcludeListing = llDeleteSubList(ExcludeListing, NamePositionTest, NamePositionTest);
if ((llListFindList(ExcludeListing, (list)llKey2Name(AVKey)) != -1 ) || (llListFindList(ExcludeListing, (list)(llKey2Name(AVKey) + SelfExcludedSuffix)) != -1))
                }
{
// they are on the 'Exclude List'
                if (NamePositionSuffixTest != -1)
ApplyDefaultTexture();
                {
}
                    //it's on with a suffix
else
                    ExcludeListing = llDeleteSubList(ExcludeListing, NamePositionSuffixTest, NamePositionSuffixTest);
{
                }
// different profile from last time & not on ExcludeListing, so display it
                llOwnerSay(DeriveName(message) + " has been removed from the "
ApplySelectedTexture((key)llGetSubString(body,s1+s1l,s2+s2l));
                    + ObjectName + "'s 'Exclude List'. \nThere are now " + RemainingExcludeSlots());
// save the key for comparison purposes next time tho'
            }
LastTexture = (key)llGetSubString(body,s1+s1l,s2+s2l);
            else
 
            {
// give a 'ShoutOut', if set to do so
                llOwnerSay(DeriveName(message) + " could not located on the " + ObjectName + "'s 'Exclude List'.");
if (ShoutOut)
            }
{
        }
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());
}
}
        }
 
        else if (((llGetSubString(message,0,3) == "List") || (llGetSubString(message,0,3) == "list")) && (id == ObjectOwner))
 
        {
listen(integer channel, string name, key id, string message)
            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"
if (message == "LearnMore")
                    + llDumpList2String(ExcludeListing, " | "));
{
            }
string URL_FORUMTHREAD = "http://forums.secondlife.com/showthread.php?t=225692";
            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();
                llOwnerSay("No AV's are listed on the 'Exclude List'.");
}
            }
else if (message == "GetScript")
        }
{
        else
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);
            llInstantMessage(DetectedUser, "Unrecognised option or selection made from a timed-out menu.");
CloseUserListen();
        }
}
else if (message == "Help")
    }
{
string URL_HELPPAGE = "http://wiki.secondlife.com/wiki/Talk:Random_AV_Profile_Projector";
    timer()
llLoadURL(DetectedUser, "This link will take you to the " + ObjectName + "'s Help page.", URL_HELPPAGE);
    {
CloseUserListen();
        CloseUserListen();
}
    }
else if (message == "ExcludeMe")
{
//default end
// 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
}
}
 
</lsl>
</source>

Latest revision as of 08: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
}