-->

FoxSan's 3D Tools and LSL Script Repository

Tons of LSL scripts, examples and 3D tools, free for all. There are currently 207 scripts and articles in this database.

Avatar Magnet

// Avatar Magnet Script 0.1
// a simple avatar magnet with some "nice" features built in
// if you turn these features off *please*
// don't use it outside of a combat area
// even if you leave them on this script uses push to simulate a
// magnet effect it *will* register as PvP outside combat areas
//
// if you turn OBJECTS on it will attract physical objects too
// but it doesn't work very well
// especially with small objects as they usually end up flying off world
//
// this script is mostly untested
// if you have problems let me know and i'll try to fix them
 
// whether the object asks permission to attract or not
// be nice and ask
integer REQUIRE_PERMISSION = TRUE;
 
// if we are requiring permissions set this based on whether
// you want it to prompt nearby users for permissions
// or require them to touch this object to be asked permission
// TRUE means it will ask without touching
// FALSE means they must touch it to be asked
integer UNSOLICITED = TRUE;
 
// how far to look around for targets
// this is highly dependant on the mass of this object
// and the mass of the target
// small objects might not have enough energy to attract far away targets
// maximum of 96.0 meters
float RADIUS = 30.0;
 
// how forceful you want to the magnet to be
// if you set this too high it's hard for the magnet to keep up
// with the velocity of the target so you get a lot of "ping-pong" effect
// if you set it too low then the target can break away easily
// if it has the ability to apply its own impulse
// (an avatar can fly away from the magnet if it's set too low ...
// but this may be a good thing)
// the maximum amount is effectively 2147483647
// but really > 100 is too much
float FORCE = 20.0;
 
// attract avatars
integer AVATARS = TRUE;
 
// attract physical objects
// this really doesn't work very well, especially for small objects
integer OBJECTS = FALSE;
 
// if we're asking permission we need to keep a list of people
// who have accepted and people who have declined
// unfortunately due to memory constraints these lists will be capped
// i haven't tested this!
// if you get stack-heap errors you will need to lower the cap
integer CAP = 50;
list declined;
list accepted;
 
// listener handle
integer listener;
 
askPermission(key avatar) {
// build the message based on the settings
string message = "May this magnet attract ";
if (AVATARS) message += "you";
if (OBJECTS && AVATARS) message += " and ";
if (OBJECTS) message += "your objects";
message += "?";
 
// build dialog button list
// based on whether or not this is the owner
list buttons = ["Accept", "Decline"];
if (avatar == llGetOwner()) buttons += ["Turn Off"];
 
llDialog(avatar, message, buttons, -38739454);
}
 
default {
state_entry() {
// turn of collision effects
llCollisionSprite("");
llCollisionSound("", 0.0);
 
llSetText("Magnet Off", <1.0, 1.0, 1.0>, 1.0);
 
llOwnerSay("Magnet Off");
}
 
touch_start(integer touches) {
// turn the magnet on
if (llDetectedKey(0) == llGetOwner()) state magnetized;
}
}
 
state magnetized {
on_rez(integer start) {
llResetScript();
}
 
state_entry() {
// clear out the accepted list
accepted = [];
 
llSetText("Magnet On", <1.0, 1.0, 1.0>, 1.0);
 
llOwnerSay("Magnet On");
 
// the magnet shouldn't be phantom or you get weird results
llSetStatus(STATUS_PHANTOM, FALSE);
 
// figure out which types of targets we're attracting
integer types;
if (AVATARS) types = types | AGENT;
if (OBJECTS) types = types | ACTIVE;
 
// set our sensor
llSensorRepeat("", "", types, RADIUS, TWO_PI, 0.1);
 
// listen for dialog responses if we're asking permission
if (REQUIRE_PERMISSION)
listener = llListen(-38739454, "", "", "");
}
 
listen(integer channel, string name, key id, string message) {
// only listen to avatars
if (llGetOwnerKey(id) == id) {
if (message == "Accept") {
// if they were already on the accepted list
// remove their earlier entry
integer index = llListFindList(accepted, [id]);
 
if (index > -1)
accepted = llDeleteSubList(accepted, index, index);
 
// add to the end of the accepted list
accepted += [id];
 
// make sure our list isn't bigger than the cap
if (llGetListLength(accepted) > CAP)
accepted = llDeleteSubList(accepted, 0, 0);
 
// remove from the declined list if they're on it
index = llListFindList(declined, [id]);
 
if (index > -1)
declined = llDeleteSubList(declined, index, index);
} else if (message == "Decline") {
// remove from the accepted list if they're on it
integer index = llListFindList(accepted, [id]);
 
if (index > -1)
accepted = llDeleteSubList(accepted, index, index);
 
// if they were already on the declined list
// remove their earlier entry
index = llListFindList(declined, [id]);
 
if (index > -1)
declined = llDeleteSubList(declined, index, index);
 
// add to the end of the declined list
declined += [id];
 
// make sure our list isn't bigger than the cap
if (llGetListLength(declined) > CAP)
declined = llDeleteSubList(declined, 0, 0);
} else if (message == "Turn Off" && id == llGetOwner()) {
state default;
}
}
}
 
touch_start(integer touches) {
key toucher = llDetectedKey(0);
 
if (REQUIRE_PERMISSION) {
askPermission(toucher);
 
return;
}
 
if (toucher == llGetOwner()) state default;
}
 
sensor(integer sensed) {
integer i;
 
for (i = 0; i < sensed; i ++) {
key target_key = llDetectedKey(i);
 
// if we're getting permission
// we need to see if we've alreay bothered this person
if (REQUIRE_PERMISSION) {
// since we may be attracting objects too
// we need to make sure we're asking the owner
key owner_key = llDetectedOwner(i);
 
// if this person has not already accepted
// don't attract them
if (llListFindList(accepted, [owner_key]) == -1) {
// but if we haven't already bothered them
// we need to ask them
if (llListFindList(declined, [owner_key]) == -1) {
// put their name on the denied list
declined += [owner_key];
 
// make sure our list isn't bigger than the cap
if (llGetListLength(declined) > CAP)
declined = llDeleteSubList(declined, 0, 0);
 
if (UNSOLICITED) askPermission(owner_key);
}
 
return;
}
}
 
// the position of our target
vector target_pos = llDetectedPos(i);
 
vector my_pos = llGetPos();
 
// normalized vector describing the direction
// from our target to us
// this is a negative vector
// which will draw the object towards us
vector direction = llVecNorm(my_pos - target_pos);
 
// apply set amount of force
// in the direction from the target to this object
vector impulse = FORCE * direction;
 
// equalize for the targets mass so pull is consistent
impulse *= llGetObjectMass(target_key);
 
// equalize for the distance of the target
// so pull is consistent
impulse *= llPow(llVecDist(my_pos, target_pos), 3.0);
 
// negate the targets current velocity
impulse -= llDetectedVel(i);
 
llPushObject(target_key, impulse, ZERO_VECTOR, FALSE);
}
}
 
state_exit() {
// we don't actually need to do this any more because
// listeners are cleaned up on state change but we might as well
if (REQUIRE_PERMISSION) llListenRemove(listener);
}
}

Basic Radar

//  Basic Radar
//  Created by Water Rogers for IBM/Opensource
 
//  Purpose
//  --------------------------------------------------------------
//  This is a basic example of how you can create a simple radar
//  to detect Avatars within a given distance around the object
//  that this script resides in.  Names will be viewed above the
//  object along with distance (in meters).
 
//  Requirements
//  --------------------------------------------------------------
//  A single prim is all that is necessary for this example.
 
//  GLOBAL VARIABLES
//  --------------------------------------------------------------
 
float   g_Range     = 96.0;     //  The range of the sensor in meters
float   g_Arc       = PI;       //  The arc of the sensor
float   g_Rate      = 1.0;      //  The repeat rate of the sensor in seconds
vector  g_TextColor = <1,1,1>;  //  Text color <1,1,1> = White  
 
//  EVENTS
//  --------------------------------------------------------------
 
default
{
    state_entry()
    {
        //  ------------------------------------------------------
        //  This is the entry-point of the script.  After a script
        //  has been saved or reset, this event will fire off first
        //  ------------------------------------------------------
 
        //  We call llSetText() first to "clear" the text above the object
        //  passing an empty string into the text parameter.
        llSetText("", g_TextColor, TRUE);
 
        //  We then call the llSensorRepeat() function to start detecting
        //  avatars.  This function will raise the sensor() event handle
        //  at a rate of g_Rate seconds.
        llSensorRepeat("", NULL_KEY, AGENT, g_Range, g_Arc, g_Rate);
 
        //  Since we passed an empty string and key value to the sensor,
        //  this tells the sensor that we want to look for any name with
        //  any key as long as it's an AGENT, which is a fancy name for
        //  Avatar.  If we wanted to look for a specific person only, we
        //  could simply pass their name and/or key into these arguments.
    }
 
    sensor(integer num_detected)
    {
        //  ------------------------------------------------------
        //  This event will fire off at the rate of g_Rate in seconds
        //  after being called by either llSensor() or llSensorRepeat()
        //  ------------------------------------------------------
 
        //  First we start out by declaring a local variable "output". This
        //  variable will store information we want in a string until we
        //  are ready to display it at the end.
        string output;
 
        //  The next variable sets up the for loop
        integer x;
 
        //  This next section will loop through each one of the num_detected
        //  items properly sensed (in this case, it will be AGENTS or Avatars)
        //  and store the results into the output string.
        for(x = 0 ; x < num_detected ; x++)
        {
            //  Using llVecDist(), we can quickly figure out the distance
            //  between 2 objects in 3D space.  To make it more readable, we
            //  store it as an integer so it truncates any decimal placements
            integer distance = (integer)llVecDist(llGetPos(), llDetectedPos(x));
            output += llDetectedName(x) + " (" + (string)distance + "m)\n";
 
            //  output += llDetectedName(x)...
            //      is the same as using
            //  output = output + llDetectedName(x)...
            //  The "\n" at the end is an escape sequence used for llSetText()
            //  to declare a new line.
        }
 
        //  When we have all the information collected, we can display it
        //  above the object using llSetText()
        llSetText(output, g_TextColor, TRUE);
    }
}

Camera Follower

//  Camera Follower
//  Created by Water Rogers for IBM/Opensource
 
//  Purpose
//  --------------------------------------------------------------
//  This script shows you how you can make an object physical, and
//  have it follow your camera around.
 
//  Requirements
//  --------------------------------------------------------------
//  A single prim is all that is necessary for this example.
 
//  Usage
//  --------------------------------------------------------------
//  Permissions to track your camera are required for it to work
//  correctly.  Type "follow" to track the camera.  Type "stop" to
//  stop tracking the camera.  Touching the object will also start
//  tracking the camera.
 
//  GLOBALS
//  --------------------------------------------------------------
integer     g_ListenChannel = 0;        //  This is the listen channel
float       g_Timer         = 0.10;     //  This is the timer call rate
 
//  FUNCTIONS
//  --------------------------------------------------------------
permissions()
{
    //  Since this block of code is used more then once in the main
    //  loop, it's a good idea to make it a function.
 
    //  Get the permissions
    if(llGetPermissions() & PERMISSION_TRACK_CAMERA)
    {
        //  Permissions were passed, so we make the object physical
        //  and start the timer
        llSetStatus(STATUS_PHYSICS, TRUE);
        llSetTimerEvent(g_Timer);
    }
    else
    {
        //  Permissions were not passed, so we make the object static,
        //  set the timer to 0, and Request permissions.
        llSetStatus(STATUS_PHYSICS, FALSE);
        llSetTimerEvent(FALSE);
        llRequestPermissions(llGetOwner(), PERMISSION_TRACK_CAMERA);
    }
}
 
//  EVENTS
//  --------------------------------------------------------------
default
{
    state_entry()
    {
        //  Collision Sounds and Sprites can be replaced.  We make them
        //  empty strings so there are no sounds or sprites if the camera
        //  happens to knock up against a wall.
        llCollisionSound("", 0);
        llCollisionSprite("");
 
        //  Make a listener to listen for commands the owner may use.
        llListen(g_ListenChannel, "", llGetOwner(), "");
 
        //  Call the permissions() function block
        permissions();
    }
 
    on_rez(integer start_param)
    {
        //  The object was just rezzed out of inventory, so call up the
        //  permissions() function block
        permissions();
    }
 
    touch_start(integer num_detected)
    {
        //  The object was touched, so first we make sure that the owner
        //  touched the object, and if so... call the permissions()
        //  function block
        if(llDetectedKey(0) == llGetOwner()) permissions();
    }
 
    listen(integer channel, string name, key id, string message)
    {
        //  Turn all commands into lowercase using llToLower()
        message = llToLower(message);
        if(message == "stop")
        {
            //  The owner said "stop", so we'll turn off the object by
            //  making the object static, and setting the timer to 0
            llSetStatus(STATUS_PHYSICS, FALSE);
            llSetTimerEvent(FALSE);
        }
        else if(message == "follow")
        {
            //  The owner said "follow", so we call up the permissions()
            //  function block
            permissions();
        }
    }
 
    run_time_permissions(integer perm)
    {
        //  Permissions were requested through the permissions() function
        //  block.  Remember to use bitwise opperands when dealing with
        //  conditional statements for permissions.
        if(perm & PERMISSION_TRACK_CAMERA)
        {
            //  The owner granted permissions, so we set the object to
            //  physical, and fire up the timer.
            llSetStatus(STATUS_PHYSICS,TRUE);
            llSetTimerEvent(g_Timer);
        }
    }
 
    timer()
    {
        //  Here the timer uses llMoveToTarget() which simply moves the
        //  object to wherever the owner's camera is, and about 1 meter
        //  above (as to not obtruct the owner's view.  The owner will
        //  not be able to see the object under most circumstances, so if
        //  the owner wants to get rid of the object, they must first say
        //  "stop" in chat to make the object stop moving.  Then they can
        //  click on the object a lot easier to delete or take.
        llMoveToTarget(llGetCameraPos() + <0.0, 0.0, 1.0>, 0.20);
 
        //  Since the object looks a bit awkward only moving to position and
        //  not rotating properly, we call a simple sensor looking for the
        //  owner's current position.
        llSensor("", llGetOwner(), AGENT, 96, PI);
    }
 
    sensor(integer num_detected)
    {
        //  After we get the owner's position, we'll have the object just
        //  look at the owner using it's forward axis.  This gives a nice
        //  effect.  If you wanted to track the owners actual camera rotations
        //  then you would have to use llGetCameraRot() and make the correct
        //  calculations.  We do it this way for simplicity.
        llLookAt(llDetectedPos(0), 0.32, 0.32);
    }
}

Springboard

default
{
    collision_start(integer total_number)
    {
        llPushObject(llDetectedKey(0), <0,100,0>, <0,0,0>, FALSE);
    }
}