Difference between revisions of "Random AV Profile Projector"

From Second Life Wiki
Jump to navigation Jump to search
(WEB-1383 workaround)
Line 10: Line 10:
<lsl>
<lsl>
// ~ RANDOM PROFILE PROJECTOR v5.4.5 by Debbie Trilling ~
// ~ RANDOM PROFILE PROJECTOR v5.4.5 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, Coder Kas and
// and that due credit continues to be given to Moriash Moreau, Coder Kas and
// Debbie Trilling.
// Debbie Trilling.


// 2009-11-24 Viktoria Dovgal -  mod to use <meta> tag, to work around WEB-1383
 
// 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 = ["8fb9ad84-4183-51df-f566-9b21c3a610fe", "1201c3de-d022-c0a5-56bd-bc49ab971726", "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;
// What we look for in world.secondlife.com to find the image UUID
string profile_key_prefix = "<meta name=\"imageid\" content=\"";


// length of profile_key_prefix, filled in at InitialiseObject()
integer s1l;


//************************
//************************
// 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 108:
// 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 = FALSE;
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 159:
string Author = "Debbie Trilling";
string Author = "Debbie Trilling";
string Supplier = "The Particle Crucible";
string Supplier = "The Particle Crucible";
string Version = " v5.4.5";
string Version = " v5.4.5.20091124";
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();
    s1l = llStringLength(profile_key_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
            // using a default texture
ApplyPrimSurface();
            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()
ApplyPrimSurface()
{
{
// putting texture on the prim, let's make sure it is solid white, blank. full bright
    // 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 ]);
    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;
ApplyPrimUpdate();
    ApplyPrimUpdate();
llOwnerSay("\nThe " + ObjectName + " is now switched ON. Please wait...");
    llOwnerSay("\nThe " + ObjectName + " is now switched ON. Please wait...");
}
}
 
 
ApplyPrimUpdate()
ApplyPrimUpdate()
{
{
if (TexturePrim)
    if (TexturePrim)
{
    {
ApplyPrimSurface();
        ApplyPrimSurface();
if (ApplyRotation)
        if (ApplyRotation)
{
        {
llTargetOmega(<0.0,0.0,1.0>, 0.2, PI);
            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);
        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);


on_rez(integer start_param)
            //check whether this was the texture used last time
{
            if (NewTexture == LastTexture)
// reset script on rez
            {
llResetScript();
                // same profile pic as last time. so display a random default instead
}
                ApplyDefaultTexture();
 
                // clear the last texture out
changed( integer change )
                LastTexture = "";
{
            }
if(change & CHANGED_OWNER )
            else
{
            {
// reset script on change of owner
                // are they on the ExcludeListing, with or without a suffix? if so, display a random default texture instead
llResetScript();
                if ((llListFindList(ExcludeListing, (list)llKey2Name(AVKey)) != -1 ) || (llListFindList(ExcludeListing, (list)(llKey2Name(AVKey) + SelfExcludedSuffix)) != -1))
}
                {
}
                    // they are on the 'Exclude List'
 
                    ApplyDefaultTexture();
state_entry()
                }
{
                else
//initialise system
                {
InitialiseObject();
                    // different profile from last time & not on ExcludeListing, so display it
SetFloatingText();
                    ApplySelectedTexture(NewTexture);
ProjectTexture();
                    // save the key for comparison purposes next time tho'
AnnounceWelcome();
                    LastTexture = NewTexture;
}
 
                    // give a 'ShoutOut', if set to do so
touch_start(integer total_number)
                    if (ShoutOut)
{
                    {
DetectedUser = llDetectedKey(0);
                        GiveShoutOut();
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
    listen(integer channel, string name, key id, string message)
 
    {
if (Power)
{
        if (message == "LearnMore")
MenuItems = [PowerText, OwnerListenText] + MenuItems;
        {
MenuText = MenuText + "\n- Off: Switch off \n- Open/CloseListen: Opens/Closes Owner listen channel " + (string)OwnerChannel;
            string URL_FORUMTHREAD = "http://forums.secondlife.com/showthread.php?t=225692";
}
            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);
else
            CloseUserListen();
{
        }
MenuItems = (list)PowerText + MenuItems;
        else if (message == "GetScript")
MenuText = MenuText + "\n- On: Switch on";
        {
}
            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);
else
            CloseUserListen();
{
        }
// touched by someone other than Owner. Send them a message & a dialog box of options
        else if (message == "Help")
 
        {
// if they placed their own name on 'Exclude List', so give opportunity to clear their name, else give opportunity to exclude their name
            string URL_HELPPAGE = "http://wiki.secondlife.com/wiki/Talk:Random_AV_Profile_Projector";
if (llListFindList(ExcludeListing, (list)(llKey2Name(AVKey) + SelfExcludedSuffix)) != -1 )
            llLoadURL(DetectedUser, "This link will take you to the " + ObjectName + "'s Help page.", URL_HELPPAGE);
{
            CloseUserListen();
// they placed themself on the 'Exclude List' so are therefore allowed to clear their name
        }
MenuItems = (list)"IncludeMe" + MenuItems;
        else if (message == "ExcludeMe")
MenuText =  MenuText + "\n- IncludeMe: Have the " + ObjectName + " remove your name from the 'Exclude List'";
        {
}
            // as a suffix to the name, as it is the user adding their own name
else
            ExcludeListing = (list)(llKey2Name(DetectedUser) + SelfExcludedSuffix) + llList2List( ExcludeListing, 0, (ExcludeListSize - 2));
{
            llInstantMessage(DetectedUser, "You have been added to the " + ObjectName + "'s 'Exclude List'");
// they have not placed themself on the 'Exclude List', so check whether the Owner has already done it
            CloseUserListen();
if (llListFindList(ExcludeListing, (list)llKey2Name(AVKey)) == -1 )
        }
{
        else if (message == "IncludeMe")
// neither they themself nor the Owner has previously excluded them, so give them the opportunity to exclude themselves now
        {
MenuItems = (list)"ExcludeMe" + MenuItems;
            integer ExcludeListPosition = llListFindList(ExcludeListing, (list)(llKey2Name(DetectedUser) + SelfExcludedSuffix));
MenuText =  MenuText + "\n- ExcludeMe: Have your name added to the " + ObjectName + "'s 'Exclude List'";
            ExcludeListing = llDeleteSubList(ExcludeListing, ExcludeListPosition, ExcludeListPosition);
}
            llInstantMessage(DetectedUser, "You have been removed from the " + ObjectName + "'s 'Exclude List'");
}
            CloseUserListen();
llInstantMessage(DetectedUser, "\nThank you for your interest in the " + ObjectName + " created by " + Author + "\nThe dialog menu offers a number of options.");
        }
}
        else if ((message == "On") && (id == ObjectOwner))
// generate the dialog menu
        {
integer CommChannel = (-200000 - ((integer)llFrand(12345) * -1));
            StartUp();
ListenChannel = llListen(CommChannel, "", DetectedUser, "");
        }
llDialog(DetectedUser, MenuText, MenuItems, CommChannel);
        else if ((message == "Off") && (id == ObjectOwner))
llSetTimerEvent(DialogTimeout);
        {
}
            ShutDown();
 
        }
 
        else if ((message == "OpenListen") && (id == ObjectOwner))
sensor(integer total_number)
        {
{
            // open the Owner Only channel
// save the AV key in case it is needed for a 'ShoutOut'
            OwnerListenChannel = llListen(OwnerChannel, "", ObjectOwner, "");
AVKey = llDetectedKey((integer)llFrand(total_number));
            OwnerListenText = "CloseListen";
// core code by Coder Kas. Adapted to suit by Debbie Trilling
            llOwnerSay("Owner Only channel " + (string)OwnerChannel + " is now open for you.\n Options are: 'Exclude <AV_NAME>', 'Include <AV_NAME>', 'ClearAll' and 'List'");
string URL_RESIDENT = "http://world.secondlife.com/resident/";
        }
llHTTPRequest( URL_RESIDENT + (string)AVKey,[HTTP_METHOD,"GET"],"");
        else if ((message == "CloseListen") && (id == ObjectOwner))
}
        {
 
            // close the Owner Only channel
 
            llListenRemove(OwnerListenChannel);
no_sensor()
            OwnerListenText = "OpenListen";
{
            llOwnerSay("Owner Only channel " + (string)OwnerChannel + " is now closed.");
// 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.
        else if (((llGetSubString(message,0,6) == "Exclude") || (llGetSubString(message,0,6) == "exclude")) && (id == ObjectOwner))
NoSensorCounter++;
        {
if ((NoSensorCounter > TotalNoScansAllowed) && (TotalNoScansAllowed > 0))
            // 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.
ShutDown();
llInstantMessage(ObjectOwner, "\nThe " + ObjectName + " has been automatically switched OFF as no Agents have been detected within the set timeframe.");
            if ((DeriveNamePosition(message) != -1 ) || (DeriveNamePosition(message + SelfExcludedSuffix) != -1))
}
            {
else
                llOwnerSay(DeriveName(message) + " already exists on the " + ObjectName + "'s 'Exclude List'.");
{
            }
ApplyDefaultTexture();
            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));
http_response(key req,integer stat, list met, string body)
                llOwnerSay(NewExcludeName + " has been added to the "
{
                    + ObjectName + "'s 'Exclude List'. \nThere are now " + RemainingExcludeSlots());
// core code by Coder Kas. Adapted to suit by Debbie Trilling
            }
integer s1 = 0;
        }
integer s2 = 0;
        else if (((llGetSubString(message,0,6) == "Include") || (llGetSubString(message,0,6) == "include")) && (id == ObjectOwner))
integer s1l= 0;
        {
integer s2l= -3;
            // 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
s1 = llSubStringIndex(body,"<img alt=\"profile image\" src=\"http://secondlife.com/app/image/");
            // locate their position within in 'Exclude List', if they do exist
s1l = llStringLength("<img alt=\"profile image\" src=\"http://secondlife.com/app/image/");
            integer NamePositionTest = DeriveNamePosition(message);
s2 = llSubStringIndex(body,"\" class=\"parcelimg\" />");
            integer NamePositionSuffixTest = DeriveNamePosition(message + SelfExcludedSuffix);
 
if(s1 == -1)
            // test to see if either are on the 'Exclude List'.
{
            if ((NamePositionTest != -1) || (NamePositionSuffixTest != -1))
// selected AV doesn't have a profile picture, so use the default instead
            {
ApplyDefaultTexture();
                // 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)
else
                if (NamePositionTest != -1)
{
                {
//check whether this was the texture used last time
                    // it's on without a suffix
if ((key)llGetSubString(body,s1+s1l,s2+s2l) == LastTexture)
                    ExcludeListing = llDeleteSubList(ExcludeListing, NamePositionTest, NamePositionTest);
{
                }
// same profile pic as last time. so display a random default instead
ApplyDefaultTexture();
                if (NamePositionSuffixTest != -1)
// clear the last texture out
                {
LastTexture = "";
                    //it's on with a suffix
}
                    ExcludeListing = llDeleteSubList(ExcludeListing, NamePositionSuffixTest, NamePositionSuffixTest);
else
                }
{
                llOwnerSay(DeriveName(message) + " has been removed from the "
// are they on the ExcludeListing, with or without a suffix? if so, display a random default texture instead
                    + ObjectName + "'s 'Exclude List'. \nThere are now " + RemainingExcludeSlots());
if ((llListFindList(ExcludeListing, (list)llKey2Name(AVKey)) != -1 ) || (llListFindList(ExcludeListing, (list)(llKey2Name(AVKey) + SelfExcludedSuffix)) != -1))
            }
{
            else
// they are on the 'Exclude List'
            {
ApplyDefaultTexture();
                llOwnerSay(DeriveName(message) + " could not located on the " + ObjectName + "'s 'Exclude List'.");
}
            }
else
        }
{
        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))
// different profile from last time & not on ExcludeListing, so display it
        {
ApplySelectedTexture((key)llGetSubString(body,s1+s1l,s2+s2l));
            ExcludeListing = [];
// save the key for comparison purposes next time tho'
            llOwnerSay("The 'Exclude List' has been cleared. All " + RemainingExcludeSlots());
LastTexture = (key)llGetSubString(body,s1+s1l,s2+s2l);
 
        }
// give a 'ShoutOut', if set to do so
        else if (((llGetSubString(message,0,3) == "List") || (llGetSubString(message,0,3) == "list")) && (id == ObjectOwner))
if (ShoutOut)
        {
{
            if (llGetListLength(ExcludeListing) > 0)
GiveShoutOut();
            {
}
                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'.");
 
            }
listen(integer channel, string name, key id, string message)
        }
{
        else
 
        {
if (message == "LearnMore")
            llInstantMessage(DetectedUser, "Unrecognised option or selection made from a timed-out menu.");
{
        }
string URL_FORUMTHREAD = "http://forums.secondlife.com/showthread.php?t=225692";
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();
}
    timer()
else if (message == "GetScript")
    {
{
        CloseUserListen();
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
//default end
}
}
</lsl>
</lsl>

Revision as of 04:41, 24 November 2009

~ 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


<lsl> // ~ RANDOM PROFILE PROJECTOR v5.4.5 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 Viktoria Dovgal - mod to use <meta> tag, to work around WEB-1383

// 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;

// What we look for in world.secondlife.com to find the image UUID string profile_key_prefix = "<meta name=\"imageid\" content=\"";

// length of profile_key_prefix, filled in at InitialiseObject() integer s1l;

//************************ // 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.20091124"; 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();
   s1l = llStringLength(profile_key_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);

       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)
           {
               // 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.secondlife.com/showthread.php?t=225692";
           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 } </lsl>