Jump Teleport

From Second Life Wiki
Jump to navigation Jump to search

Created by Kira Komarov.

Introduction

Teleporters in Second Life have been plagued with the use of warpPos function which, although it is supposed to accomplish a simple task, the implementation is obfuscated with magic numbers, the design is very poor and funny effects are acheived. Here is a rundown of the problems with warpPos:

  • The code mentions:
    // Try and avoid stack/heap collisions
     if (jumps > 411)
         jumps = 411

with no explanation whatsoever about what that means and why an overflow occurs.

  • warpPos calculates the number of jumps necessary and then proceeds to jump 10 meters in the calculated direction without even accounting for affinity.
  • warpPos is based on a function that executes llSetPos calls which has the effect of timing out on OpenSim due to the timeout imposed on event handlers: an event handler just goes away after some time and warpPos will fail for long distances.

The following code should solve those problems by using vector calculus and calculating 10 meters in the direction towards the final destination (the math is explained in the Second Life category). The function doing that is jumpGates which recursively computes hops towards the final destination and returns them in a list which we process later using a timer. By using a timer we make sure that the handler does not time out and we call that timer using the minimal float value (1.175494351E-38) since there are no limitations on the minimal timer interval.

When jumping to the next gate, we additionally use the list as a stack in order to pop off the first coordinates and discard them from the list entirely. This makes the script start with an initial memory pool and, as it runs, it frees up memory instead of consuming more and more memory.

Setup

The example teleporter below is meant to be placed in a primitive with the object description set to a region position which represent the destination. For example, create a primitive on the ground and set the object description to:

<14.233517, 86.036453, 1004.25>

which, when an avatar sits on the object, will move the primitive along with the agent to the coordinates <14.233517, 86.036453, 1004.25>. The script will then unsit the avatar and return to its original position.

Code

//////////////////////////////////////////////////////////
// [K] Komarov - 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 = [];
vector sPos = ZERO_VECTOR;
// 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);
}

default
{
    state_entry() {
        // Grab local position.
        sPos = llGetPos();
        llSitTarget(<0,0.0,1>, ZERO_ROTATION);
    }
    changed(integer change) {
        if(change & CHANGED_LINK) {
            if(llAvatarOnSitTarget()) {
              state move;
            }
        }
    }
}

state move
{
    state_entry() {
        // Grab local position again.
        sPos = llGetPos();
        // Grab distance from description
        vector oPos = ZERO_VECTOR;
        list oDesc = llParseString2List(llGetObjectDesc(), ["<", ">", ","], []);
        oPos.x = llList2Float(oDesc, 0);
        oPos.y = llList2Float(oDesc, 1);
        oPos.z = llList2Float(oDesc, 2);
        
        // Calculate list of intermediary jump gates.
        jumps = jumpGates(llGetPos(), oPos, 10);
        // 1.175494351E-38 is the smallest float.
        llSetTimerEvent(1.175494351E-38);

    }
    timer() {
        if(llGetListLength(jumps) == 0) {
            llSetTimerEvent(0);
            key a = llAvatarOnSitTarget();
            if(a) llUnSit(llAvatarOnSitTarget());
            state recall;
        }
        vector nPos = llList2Vector(jumps, 0);
        jumps = llDeleteSubList(jumps, 0, 0);
        llSetLinkPrimitiveParamsFast(LINK_THIS, [PRIM_POSITION, nPos]);
    }
}

state recall
{
    state_entry() {
        jumps = jumpGates(llGetPos(), sPos, 10);
        // 1.175494351E-38 is the smallest float.
        llSetTimerEvent(1.175494351E-38);
    }
    timer() {
        if(llGetListLength(jumps) == 0) {
            llSetTimerEvent(0);
            state default;
        }
        vector nPos = llList2Vector(jumps, 0);
        jumps = llDeleteSubList(jumps, 0, 0);
        llSetLinkPrimitiveParamsFast(LINK_THIS, [PRIM_POSITION, nPos]);
    }
}