Grab Pivot Lever

From Second Life Wiki
Revision as of 15:04, 29 September 2010 by Haravikk Mistral (talk | contribs) (→‎Script)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to 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

<lsl>// ===================================================================== // 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 lever will go. float BACKWARD_LIMIT = 240.0;

// The axis on which to rotate the lever, 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 lever. 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);
   }

}</lsl>