// Shack's Ferry Script
// Version 0.1
//
// Copyright 2004 Shack Dougall
//
// Licensed under the Apache License, Version 2.0 (the "License"

;
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//
http://www.apache.org/licenses/LICENSE-2.0//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// DATE: 12-28-2004
// DESCRIPTION: The prim containing this script will move to position1 the
// first time that it is touched. Thereafter, touching it will cause it to
// ferry between position1 and position2.
// Position1 and Position2 can be anywhere in the same sim, but there must
// be an unobstructed, straight-line path between them. However, there is
// no limit to how high or how far apart the points are. This script is not
// limited to up/down or left/right motion. Any two points will work.
// NOTE: Unattached objects disappear above z=4096. Also, the script uses
// physics, so mass/energy limitations apply. That is, the prim containing
// this script will not move if it does not have sufficient energy to move
// both itself and anything standing on it.
// It uses llMoveToTarget, so the prim should be at least as massive as
// everything else it is moving. But if it is too massive, then it won't
// have enough energy to move the mass.
// If you have problems, experiment with small unattached prims first.
// You could modify this to use llSetPos instead of llMoveToTarget if you
// would prefer a different set of limitations

// *********************************************
// YOU MUST SET THE TWO POSITION VARIABLES!
vector position1 = ZERO_VECTOR;
vector position2 = ZERO_VECTOR;
// for example, < X, Y, Z >
// vector position1 = <10, 10, 50>;
// vector position2 = <20, 20, 60>;
// *********************************************
// YOU MAY PLAY WITH THE VARIABLES BELOW
//Small TAU means faster movement. min: 0.032 good: > 0.2
//STEP_SIZE is the amount of distance that we will try to travel with one
//call to llMoveToTarget. Necessary because llMoveToTarget
//damps to the current position if this distance is > 64m.
float TAU = 2.0;
float STEP_SIZE = 63.0;
//STEP_TARGET_RANGE is how close to a move target we go before
//we issue a new move target. The idea is that we don't want to slow down
//too much, so we set a new target before we get to the first one.
float STEP_TARGET_RANGE = 10.0;
// &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
// DON'T TOUCH ANYTHING BELOW THIS UNLESS YOU KNOW WHAT YOU'RE DOING

// danger Will Robinson
// where we are going
vector destCoords;
integer NULL_TARGET = -1;
//targetID is the ID returned by the last call to llTarget if one is active.
integer targetID = NULL_TARGET;
//Vector of length STEP_SIZE that points in the direction
//we want to go. Add this to the current position to get the coords of
//where we want to go next to get us closer to our goal.
//Calculated at the start and cached for use during the trip.
vector deltaVector;
init()
{
if (position1 == ZERO_VECTOR || position2 == ZERO_VECTOR)
{
llSay(0, "You must set the values of both: position1, position2."

;
return;
}
llStopMoveToTarget();
llSetStatus(STATUS_ROTATE_X|STATUS_ROTATE_Y|STATUS _ROTATE_Z,FALSE);
llSetStatus(STATUS_PHYSICS, TRUE);
llMoveToTarget(llGetPos(), TAU);
llSay( 0, "Touch me to make me move."

;
}
//Removes the llTarget. Does nothing with movement.
clearTarget()
{
if (targetID != NULL_TARGET)
{
llTargetRemove( targetID );
targetID = NULL_TARGET;
}
}
//Returns the displacement vector between two coordinates.
//That is, returns the vector you need to add to the start location to get the
//destination location.
vector calcDeltaVector( vector destPos, vector startPos )
{
vector coordsDelta = destPos - startPos;
return coordsDelta;
}
//Stores our destination and calculates the initial deltaVector that will
//get us there.
setDestination( vector newCoords )
{
clearTarget();
// get current position info and store dest info
vector currCoords = llGetPos();
destCoords = newCoords;
// Calculate the total displacement vector
// then, normalize it to get a unit vector (vector of length 1)
// then, multiply it by the STEP_SIZE to get a displacement vector of
// length STEP_SIZE
deltaVector = calcDeltaVector(destCoords, currCoords);
deltaVector = STEP_SIZE * llVecNorm(deltaVector);
}
//Returns the distance to between two coordinates.
float distance( vector startCoords, vector destCoords )
{
vector curDelta = calcDeltaVector(destCoords, startCoords);
return llVecMag(curDelta);
}
// low-level workhorse of movement.
// It calls llMoveToTarget to move us and llTarget to detect when we are there.
// Each call to this function moves us a distance of STEP_SIZE toward our
// eventual destination. Don't call this directly.
moveToNextTarget()
{
vector nextTargetCoords;
vector currCoords = llGetPos();
float distToDestination = distance( currCoords, destCoords );
if (distToDestination > STEP_SIZE) {
nextTargetCoords = currCoords + deltaVector;
} else {
nextTargetCoords = destCoords;
}
targetID = llTarget( nextTargetCoords, STEP_TARGET_RANGE );
llMoveToTarget( nextTargetCoords, TAU );
}
toggleDestination()
{
vector newDest;
if( destCoords == position1 )
newDest = position2;
else
newDest = position1;
setDestination( newDest );
}
default
{
state_entry()
{
init();
}
touch_start(integer total_number)
{
toggleDestination();
moveToNextTarget();
}
at_target( integer targetID, vector targetPos, vector ourPos )
{
float distanceToDestination = distance( ourPos, destCoords );
integer nearDestination = distanceToDestination <= STEP_TARGET_RANGE;
if (! nearDestination)
{
clearTarget();
moveToNextTarget();
}
}
}