Grab Pivot Lever

From Second Life Wiki

Second Life Wiki > Grab Pivot Lever
Jump to: navigation, search

Description

This sample script creates a pivot lever which will rotate around its centre-point through a series of positions, as a user drags it. This script is a part of Haravikk Mistral's Lever Library.

When a user clicks and drags this level it will rotate in the direction they drag until it reaches FORWARD_LIMIT or BACKWARD_LIMIT. This means you will have a lever that can be dragged to any position at will by the user.

There is however a special case. Should you set BACKWARD_LIMIT equal to FORWARD_LIMIT then the lever will enter a "continuous" mode whereby it will rotate in a single direction in a full-circle, as it essentially has no limit. In this case it can be continuously dragged to rotate it repeatedly.

This script also contains other features, including the ability to have it reset after a period of time, and also have it contact other objects regarding changes, either using link-messages or by sending messages using whisper/say/shout/region-say. See the comments in the script below for COMMS_TYPE on how to enable these.

Script

// =====================================================================
// Simple Grab Lever by Haravikk Mistral
// =====================================================================
// This script allows you to create a simple lever that can be clicked 
// and dragged in order to rotate it through a series of positions.
//
// This script can be used and modified freely, though I ask that you 
// give credit to myself where possible, and provide a link to my 
// wiki page at https://wiki.secondlife.com/wiki/User:Haravikk_Mistral
// =====================================================================
 
// =====================================================================
// CONSTANTS
// =====================================================================
// The following values can be used to quickly set-up a lever without 
// having to modify the script. Read the comments above each item to 
// get an idea of what to do.
 
// The number of lever positions from FORWARD_LIMIT and BACKWARD_LIMIT 
// (inclusive). Minimum POSITIONS is 2.
integer POSITIONS       = 3;
// The starting position, positions are numbered from 1 to POSITIONS
integer START_POSITION  = 1;
 
// The angle (in degrees) that is as far forward as the lever will go.
float   FORWARD_LIMIT   = 300.0;
// The angle (in degrees) that is as far back as the level will go.
float   BACKWARD_LIMIT  = 240.0;
 
// The axis on which to rotate the level, only one should be set to 1.0, 
// the rest to 0.0, unless you know what you're doing.
vector  ROTATE_AXIS     = <0.0, 1.0, 0.0>;
 
// This is the amount the lever must be dragged by before it will move 
// to the next position.
float   DRAG_SENSITIVITY = 0.5;
// The axis on which to test drag-amount, only one should be set to 1.0, 
// the rest to 0.0, unless you know what you're doing.
vector  DRAG_AXIS       = <1.0, 0.0, 0.0>;
 
// A sound to play when moving the lever
key     SOUND           = NULL_KEY;
// The volume of the sound
float   SOUND_VOLUME    = 1.0;
 
// This value determines how long the lever will wait before resetting 
// it's position back to START_POSITION and START_DIRECTION. Set this 
// to 0.0 if you never want to reset the level.
float   RESET_TIME      = 30.0;
 
// Determines what type of message this lever sends when its position 
// changes. The available types are:
// -1   lever sends no communications (lever is a prop only)
//  0   link-message (see LINK_* constants below to set-up)
//  1   whisper      (see CHANNEL constant)
//  2   say          (see CHANNEL constant)
//  3   shout        (see CHANNEL constant)
//  4   region-say   (see CHANNEL constant)
//
// Link messages will have the position number in their string parameter 
// and the key of the user who touched the lever in the key parameter.
// All other message types will contain a message in the form:
//  <position>,<user>
// That is; the position, and user-key are comma-separated.
// NOTE: Any time the lever positions itself (reset) the key will be 
// NULL_KEY.
integer COMMS_TYPE      = 1;
 
// Determines what channel the lever whispers/says/shouts/region-says on
integer CHANNEL         = -1234567890;
 
// The link in a linked-set to send messages to upon changing position
integer LINK_TARGET     = LINK_ROOT;
// The value to place in the integer parameter of a link-message for 
// easy filtering.
integer LINK_FILTER     = 8192;
 
// =====================================================================
// VARIABLES
// =====================================================================
// Below here are variables, these are used by the script.
integer position        = 0;
vector  lastGrab        = ZERO_VECTOR;
 
float   positionsF      = 0.0;
integer continuous      = FALSE;
 
rotateToPosition(integer newPosition, key id) {
    if (newPosition > POSITIONS) newPosition = POSITIONS;
    else if (newPosition < 1) newPosition = 1;
 
    integer steps = newPosition - position;
    if (!steps) return; // Already there
 
    integer change = 1;
    if (steps < 0) {
        change = -1;
        steps = -steps;
    }
 
    integer i = 0; float angle = 0.0;
    rotation rotOffset = ZERO_ROTATION;
    integer doOffset = (llGetLinkNumber() > 1);
    rotation rot;
 
    float strength = llGetMass();
 
    do {
        position += change;
        angle = BACKWARD_LIMIT + ((FORWARD_LIMIT - BACKWARD_LIMIT) * 
            ((float)(position - 1) / positionsF));
 
        rot = llEuler2Rot(ROTATE_AXIS * angle);
        if (doOffset) rot /= rotOffset;
 
        llRotLookAt(
            rot,
            strength,
            0.1
        );
        if (SOUND) llPlaySound(SOUND, SOUND_VOLUME);
    } while ((++i) < steps);
 
    if (COMMS_TYPE >= 0) {
        if (COMMS_TYPE == 0)
            llMessageLinked(LINK_TARGET, LINK_FILTER, (string)position, id);
        else {
            string str = (string)position + "," + (string)id;
 
            if (COMMS_TYPE == 1) llWhisper(CHANNEL, str);
            else if (COMMS_TYPE == 2) llSay(CHANNEL, str);
            else if (COMMS_TYPE == 3) llShout(CHANNEL, str);
            else if (COMMS_TYPE == 4) llRegionSay(CHANNEL, str);
        }
    }
 
    llStopLookAt();
 
    if (RESET_TIME > 0.0) llSetTimerEvent(RESET_TIME);
}
 
default {
    state_entry() {
        // Determine if the lever is continuous (can turn full-circle) or 
        // fixed (turns on part of a circle)
        continuous = (FORWARD_LIMIT == BACKWARD_LIMIT);
 
        // Convert angles to radians
        FORWARD_LIMIT   *= DEG_TO_RAD;
        if (continuous) BACKWARD_LIMIT = FORWARD_LIMIT + TWO_PI;
        else BACKWARD_LIMIT  *= DEG_TO_RAD;
 
        // Convert POSITIONS to a float for rotations
        positionsF = (float)(POSITIONS - 1);
 
        rotateToPosition(START_POSITION, NULL_KEY);
    }
 
    touch(integer x) {
        vector grab = llDetectedGrab(0);
        vector diff = grab - lastGrab;
               diff.x *= DRAG_AXIS.x;
               diff.y *= DRAG_AXIS.y;
               diff.z *= DRAG_AXIS.z;
        float  drag = llVecMag(diff);
 
        if (drag > DRAG_SENSITIVITY) {
            integer sign = TRUE;
            if (diff.x < 0.0) sign = !sign;
            if (diff.y < 0.0) sign = !sign;
            if (diff.z < 0.0) sign = !sign;
 
            integer steps = llFloor(drag / DRAG_SENSITIVITY);
            if (steps <= 0) return;
 
            integer newPosition = position;
            if (sign) newPosition += steps;
            else newPosition -= steps;
 
            if (newPosition > POSITIONS) {
                if (continuous) 
                    newPosition = (newPosition % POSITIONS) + 1;
                else newPosition = POSITIONS;
            } else if (newPosition < 1) {
                if (continuous) 
                    newPosition = POSITIONS - (newPosition % POSITIONS);
                else newPosition = 1;
            }
 
            rotateToPosition(newPosition, llDetectedKey(0));
            lastGrab = grab;
        }
    }
 
    touch_start(integer x) {
        lastGrab = ZERO_VECTOR;
    }
 
    touch_end(integer x) {
        lastGrab = ZERO_VECTOR;
    }
 
    timer() {
        llSetTimerEvent(0.0);
        lastGrab = ZERO_VECTOR;
        rotateToPosition(START_POSITION, NULL_KEY);
    }
}
Personal tools