Table of Contents

The jumpdrive/crossdrive seems to behave well with the Imprudence viewer but newer viewers and a bad connection to SecondLife may get the viewer stuck between regions. The reason most likely is that the viewer is not fast enough to process all the region borders. Since this is none of our concern, how fast or how slow the viewer works, there will be no hacks applied to compensate for Linden's poor design.

Video

Overview

Test drive showing the drives in action: JumpDrive for intra-region teleports and the CrossDrive for crossing the region borders.

Complementary to teleport, which is just a Wizardry and Steamworks remake of a standard intra-region teleporter, the purpose of the JumpDrive/CrossDrive is to provide a way to safely teleport avatars across regions using just one script and without using RLV features and with only one script.

Setup

The set-up is very simple, the script requires a notecard called Waypoints, consisting of coordinates given on every line and with a blank line at the end, to be dropped along with the script in a primitive. The only catch is that a coordinate line has to be given for every region border. For example, right at the beginning of the movie (right from the START marker, a region crossing takes place. This is done by the following sequence of coordinates:

<31,59,1004>
<0,59,1006>

The first set of coordinates moves the shuttle to <31,59,1004> and the next set of coordinates <0,59,1006> moves the shuttle right at the border. You can observe this by the 0-value of the X-coordinate.

Suppose the following schematic represents a region:

In that case, the second coordinate from before <0,59,1006> represents a sim-crossing on the left border of the region (looking at the map, it is a crossing from SUNO Regents into Tulane). This happens again in the sample notecard below: <0,182,24> which, again, is a left-crossing over the region border between Tulane Regents and Loyola Regents.

For the grand-prix you have watched in the video, a notecard with the following contents was used:

<31,59,1004>
<0,59,1006>
<129,189,25>
<0,182,24>
<41,42,28>
<66,220,27>
<64,255,35>
<111,206,26>
<255,54,48>
<255,76,37>
<146,132,23>
<180,255,44>
<128,128,29>
<128,255,28>
<202,170,43>

Explanations

The script uses the two distinct drives:

Code

jumpdrive_crossdrive.lsl
//////////////////////////////////////////////////////////
// (c) Wizardry and Steamworks - 2012, License GPLv3    //
// Please see: http://www.gnu.org/licenses/gpl.html     //
// for legal details, rights of fair usage and          //
// the disclaimer and warranty conditions.              //
//////////////////////////////////////////////////////////
 
list jumps = [];
 
// Calculate jump gates
list jumpGates(vector iPos, vector dPos, integer jumpDistance) {
    list gates = [];
    if(jumpDistance == 0) return gates;
    float dist = llVecDist(iPos, dPos);
    if(dist > jumpDistance) {
        // We move 1/jumpDistance from the initial position
        // towards the final destination in the description.
        iPos = iPos + jumpDistance * (dPos-iPos) / dist;
        gates += iPos;
        return gates + jumpGates(iPos, dPos, jumpDistance);
    }
    return gates + jumpGates(iPos, dPos, --jumpDistance);
}
 
vector oPos = ZERO_VECTOR;
vector cross = ZERO_VECTOR;
 
key nQuery = NULL_KEY;
//pragma inline
string nName = "UNO Grand Prix";
 
float regionTimeDilation = 2;
 
default
{
    state_entry() {
        llSetObjectDesc("0");
        llSitTarget(<0,0,1>, ZERO_ROTATION);
    }
    on_rez(integer num) {
        llSetObjectDesc("0");
    }
    changed(integer change) {
        if(change & CHANGED_LINK) {
            key a = llAvatarOnSitTarget();
            if(a) state read;
        }
    }
}
 
state read
{
    state_entry() {
        integer itra = llGetInventoryNumber(INVENTORY_NOTECARD)-1;
        do {
            if(llGetInventoryName(INVENTORY_NOTECARD, itra) == nName)
                jump found_notecard;
        } while(--itra>=0);
        llOwnerSay("Failed to find notecard.");
        return;
@found_notecard;
        integer nLinePos = (integer)llGetObjectDesc();
        llOwnerSay("Reading at: " + (string)nLinePos);
        nQuery = llGetNotecardLine(nName, nLinePos);
    }
    dataserver(key id, string data) {
        if(id != nQuery) return;
        if(data == EOF || data == "") {
            // DEBUG
            // llOwnerSay("FINISH!!");
            key a = llAvatarOnSitTarget();
            if(a) llUnSit(llAvatarOnSitTarget());
            state default;
            return;
        }
        // Now extract the coordinates.
        list nextCoordinates = llParseString2List(data, ["<", ">", ","], []);
        oPos.x = llList2Integer(nextCoordinates, 0);
        oPos.y = llList2Integer(nextCoordinates, 1);
        oPos.z = llList2Integer(nextCoordinates, 2);
        state process;
    }
}
 
state process
{
    state_entry() {
        // If we are at the margin, we switch to cross- 
        // drive in order to cross the sim border.
        vector sPos = llGetPos();
        if(llFloor(sPos.x) == 0) {
            cross = <-1,0,0>;
            state crossdrive;
        }
        if(llFloor(sPos.y) == 0) {
            cross = <0,-1,0>;
            state crossdrive;
        }
        if(llCeil(sPos.x) == 255) {
            cross = <1,0,0>;
            state crossdrive;
        }
        if(llCeil(sPos.y) == 255) {
            cross = <0,1,0>;
            state crossdrive;
        }
        // 1.175494351E-38 is the smallest float.
        // pad that with the lag of the simulator.
        regionTimeDilation = 1 - llGetRegionTimeDilation() + 1.175494351E-38;
        state jumpdrive;
    }
 
}
 
state crossdrive
{
    state_entry() {
        llOwnerSay("Crossdriving...");
        llSetStatus(STATUS_PHYSICS|STATUS_BLOCK_GRAB, TRUE);
        llSetStatus(STATUS_ROTATE_X|STATUS_ROTATE_Y|STATUS_ROTATE_Z, FALSE);
        llSetBuoyancy(1.0);
        llSetForce(cross, FALSE);
    }
    changed(integer change) {
        if(change & CHANGED_REGION) {
            // 1.175494351E-38 is the smallest float.
            // pad that with the lag of the simulator.
            regionTimeDilation = 1 - llGetRegionTimeDilation() + 1.175494351E-38;
            llSetTimerEvent(regionTimeDilation);
        }
    }
    timer() {
        vector sPos = llGetPos();
        if(llFloor(sPos.x) == 0 || llFloor(sPos.y) == 0 || llCeil(sPos.x) == 255 || llCeil(sPos.y) == 255) return;
        llSetStatus(STATUS_PHYSICS, FALSE);
        llSetTimerEvent(0);
        state read;
    }
}
 
state jumpdrive
{
    state_entry() {
        llOwnerSay("Jumpdrive...");
        // Grab local position again.
        vector sPos = llGetPos();
        // Calculate list of intermediary jump gates.
        jumps = jumpGates(llGetPos(), oPos, 10);
        llSetTimerEvent(regionTimeDilation);
 
    }
    timer() {
        if(llGetListLength(jumps) == 0) {
            llSetTimerEvent(0);
            integer nLinePos = (integer)llGetObjectDesc() + 1;
            llSetObjectDesc((string)nLinePos);
            state read;
        }
        vector nPos = llList2Vector(jumps, 0);
        jumps = llDeleteSubList(jumps, 0, 0);
        llSetLinkPrimitiveParamsFast(LINK_THIS, [PRIM_POSITION, nPos]);
    }
}