////////////////////////////////////////////////////////////////
// 1000m Menu-driven Intra-Sim Teleporter
// By Nepenthes Ixchel
// July 2006

/////////////////////////////////////////////////////////////////
// License:
// This code is released into the public domain, but if you sell
// it without making any changes to add value to it then hordes of
// purple undead monkeys will torment you in the afterlife.
//
// This code is released without any warranty of any sort.
//
// Code in this script includes:
//
// WarpPos by Keknehv Psaltery, April 2006, Public Domain
// This provides the instant movement code.
//
// Single Neat Elevator by Seagul Neville, Dec 2005, Public Domain
// This script started as SN_Elevator, but I don't think much
// of the original code survived. :-)

/////////////////////////////////////////////////////////////////
// Design:
//
// Menu system sets target location from pre-defined list
// When a user sits on the object it moves to the target location,
// unists the avatar, and returns. Use of WarpPos by Keknehv
// Psaltery makes this possible beyond the 10 metre limit normally
// associated with position changes.

/////////////////////////////////////////////////////////////////
// Quirks:
//
// If you move the teleporter after placing it you need to
// reset this script so it learns it's new home position
//
// All target locations must be under 768m in height
//
// All target locations must be in teh same sim as teh teleport
//
// Max distance is 1000m... but it's impossible to move more than
// 850m in a single sim without going higher than 768m.
//
// If use of llSetPrimitiveParams to bypass the 10m movement
// restriction is ever nerfed then this script will stop working.


/////////////////////////////////////////////////////////////////
// Usage
//
// Edit the list of locations.
// Place script in a prim
// Touch to get a menu to set destination
// Right click -> sit to teleport

/////////////////////////////////////////////////////////////////
//user variables, you should set these.

// A list of locations (names and position)
// This is for one sim only; teh sim the teleporter is in.
// No more than 12 locations or you'll get an error from llDialog
// Buttons are drawn left to right, bottom to top, in row of three.
list gLocations=[
"New Releases",<155,155,102>,
"Playful Kitten",<155,155,115>,
"Boneflower",<155,155,127>,
"IMD",<155,168,138>,
"Cyber Apoc",<164,52,27>,
"Pirate Kitty",<36,32,21>,
"Telehub",<155,108,106>,
"Shrine",<29,178,27>,
"Moon",<184,143,147>
];

// Text for the "pie menu"
string gSitText="Teleport";
// If you don't enable this teh teleport object will be left at the destination.
integer gReturnToStartPos=TRUE;
// Alpha for hovertext
float gTextAlpha=0.5;
// colour for hovertext
vector gTextColour=<1.0,1.0,1.0>;


/////////////////////////////////////////////////////////////////
//Runtime variables. You should leave these alone.

vector gStartPos=<0,0,0>;
key gAvatarID=NULL_KEY;
integer gChannel=574368374;
vector gTargetPos=<0,0,0>;


//////////////////////////////////////////////////////////////////
// Function for instant intra-sim movement

warpPos( vector d ) //R&D by Keknehv Psaltery, ~05/25/2006
{
if ( d.z > 768 ) //Otherwise we'll get stuck hitting the ceiling
d.z = 768;
//The number of jumps necessary
integer s = (integer)(llVecMag(d-llGetPos())/10)+1;
//Try and avoid stack/heap collisions
if ( s > 100 )
s = 100; // 1km should be plenty
//Solve '2^n=s'
integer e = (integer)( llLog( s ) / llLog( 2 ) );
list rules = [ PRIM_POSITION, d ]; //The start for the rules list
integer i;
for ( i = 0 ; i < e ; ++i ) //Start expanding the list
rules += rules;
integer r = s - (integer)llPow( 2, e );
if ( r > 0 ) //Finish it up
rules += llList2List( rules, 0, r * 2 + 1 );
llSetPrimitiveParams( rules );
}

//////////////////////////////////////////////////////////////////
// Main codeblock.

default
{
state_entry() {
llSetSitText(gSitText);
gStartPos = llGetPos();
llSitTarget(<0,0,1>,ZERO_ROTATION);
gChannel=(integer)llFrand(1000000000)+1000000000;
llSetText(llList2String(gLocations,0),gTextColour, gTextAlpha);
gTargetPos=(llList2Vector(gLocations,1));
llListen(gChannel,"",NULL_KEY,"");
}
on_rez(integer start_param){
llResetScript();
}
changed(integer change){
if(change & CHANGED_LINK)
{
gAvatarID = llAvatarOnSitTarget();
if(gAvatarID != NULL_KEY)
{
warpPos(gTargetPos);
llSleep(1);
llUnSit(gAvatarID);
llSleep(1);

if (gReturnToStartPos){
warpPos(gStartPos);
}
}
}
}

touch_start(integer number){
list options=[];
integer i =0;
for(i=0;i