Don't click here unless you want to be banned.

LSL Wiki : LibraryPortRingCreator

HomePage :: PageIndex :: RecentChanges :: RecentlyCommented :: UserSettings :: You are crawl814.us.archive.org
The PortRing Creator automatically sets up a system of teleporters between a "base" location and several "stations", as well as between the "stations" themselves.

Each station is created with a teleporter link to the "base", as well as to the previous station (if not at the first station) and the next station (if not at the last station). The teleporters are given a sit-type teleporter that will place the avatar at the destination in the correct orientation.

The system consists of the following pieces:

- Teleporter objects: One for the previous entry, one for the next entry, and one for going back to base. These can all be the same if you wish. Teleporter objects are placed at the "base" and at each "station" to provide the teleport links. Each teleporter object may be any arbitrary linked object that has the PortRing Porter script in its root.
- Creator object: This is the object that does most of the work. It won't be visible to the users of the teleporter system, so it can be a simple prim. It must contain the PortRing Creator script as well as the Teleporter objects listed above. When the Control notecard (see below) is dropped onto this object, it will go to the base location and each station and create the Teleporter objects as appropriate.
- Retriever object: Used to clean up when you don't need the teleporters anymore. It must contain the PortRing Retriever script. When the Control notecard is dropped on this object, it will go to the base location and each station and retrieve the Teleporter objects, linking them to itself, then will return to its starting location when finished to be deleted. Note that, if there are a lot of Teleporter objects and they have a lot of prims, this may be unable to link. If this is the case, it should keep going through the cycle and make it back with some portion of the teleporter objects, and you should be able to create a new one to collect the rest.
- Control notecard: This controls the Creator and Retriever scripts. Its control lines tell the system the name of the teleporter objects, and the location and names of the "base" and "stations".

Limitations and known bugs in version 1.3.0:
- The "base" and all "stations" must be in the same sim.
- If a location is outside the X,Y range 0..255, the creator will never stop moving when it tries to go to to that location.
- No error checking is done to ensure that teleport locations are within the 300m "Sit" range.

An example Control notecard:
//  PortRing controller card
//  for PortRing Creater version 1.3.0
//
//  This notecard contains the information that will be used to create
//  a PortRing. A PortRing consists of a "base", which has a single
//  "next" Porter, and a set of "stations", each of which has a Porter
//  back to the "base", a "back" Porter (except for the first station),
//  and a "next" Porter (except for the last station).
//
//  Lines that are blank or begin with // are ignored. All other lines
//  must be a comma-separated list, beginning with a keyword:
//
//  PORTERS,<name-of-"back"-porter>,<"forward"-porter>,<"base"-porter>,
//    <spacing>
//  BASE,<location-of-base>,<heading>,<name-of-base>
//  STATION,<location-of-station>,<heading>,<name-of-station>
//  OPTIONS,<option>[,<option>...]
//
//  There must be exactly one PORTERS line, followed by exactly one BASE
//  line, followed by one or more STATION lines.
//
//  Any number of OPTIONS lines may appear anywhere on the card.
//
//  The PORTERS line tells the PortRingCreator and PortLinkRetriever
//  objects what objects to create or retrieve for the various Porters.
//  The <spacing> parameter tells the Creator how far apart to place the
//  different Porters at a location.
//  The BASE and STATION lines tell the Creator and Retriever where to
//  create (or from where to retrieve) the porters.  <location> is a
//  vector containing the <x,y> location of the station, and the height-
//  above-ground (z) at which to create the 'porters.  <heading> is a
//  compass heading, from the list below.  <name> is the name of the
//  base or station - the text that will appear in the teleporters that
//  link to that location.
//
//  Compass headings:
//      N,NNE,NE,ENE, E, ESE, SE, SSE, S, SSW, SW, WSW, W, WNW, NW, NNW
//
//  The OPTIONS lines alter the behaviour of the creator.  Options and
//  option values are not case sensitive.  The following options are known:
//
//      BasePorters=FirstOnly
//      BasePorters=FirstAndLast
//      BasePorters=All
//          Sets the 'porters that will be created at the base.  The default
//          is FirstOnly, meaning that one 'porter will be created, going to
//          the first station.  FirstAndLast adds an additional 'porter to the
//          last station.  All makes a 'porter for every station.
//
//      RingStyle=Linear
//      RingStyle=Loop
//          Determines if the first and last entries will have previous/next
//          'porters that "wrap around" to each other ("Loop" style), or if
//          the first and last entries will not have previous/next 'porters,
//          respectively ("Linear" style).
//
//  The Porter objects themselves must be placed in the PortRingCreator
//  before this notecard is dropped in.  The Porter objects must each
//  have a copy of the PortRing Porter script.

PORTERS,Teleporter to Previous Entry,Teleporter to Next Entry,Teleporter to Base,1.0

OPTIONS,BasePorters=All,RingStyle=Loop

BASE,<160,40,0.5>,N,Base

STATION,<176,22,0.5>,S,Station 1
STATION,<100,26,0.5>,S,Station 2
STATION,<137,58,0.5>,NW,Station 3
STATION,<156,58,0.5>,NE,Station 4


The PortRing&nbsp;Creator script:
//  PortRingCreator - Create a PortRing from a set of Porter objects and
//                    a dropped-in notecard.
//  version 1.3.0, 1 August 2004, by Neo Rebus
//
//  COPYRIGHT 2004 BY NEO REBUS - SEE LICENSE AT END
//
//  This script will create a PortRing structure.  Currently, it must
//  all be within the same sim!
//
//  The object containing this script must also contain the three Porter
//  objects - one for Back, one for Next, and one for Base (obviously,
//  each must have a different name).
//
//  The owner can drop a notecard onto the object.  This script will
//  detect that, read the notecard, process it, delete it, then create
//  the porters.
//
//  General algorithm:
//
//      When a notecard is dropped in, parse the notecard into the
//      gxPortRingInfo and gxPortRingStations global lists.  When
//      finished, the first three entries should be strings containing
//      the names of the three Porter objects - back, forward, base,
//      respectively, followed by the spacing between 'porters in a
//      set, and then by vector, rotation pairs, the first for the base
//      location, the rest
//      for each station.
//
//      Delete the notecard.
//
//      Using the "CreatePorterSet" function, create the base 'porter
//      and the 'porters for each station.
//
//  The CreatePorterSet(list axPorters) algorithm:
//      axPorters is a strided list consisting of sets of:
//          [ "porterName", <destVector>, <destRotation>, "descriptiveName" ]
//
//      Teleport to the 'porter location plus 2m in Z direction, and
//      orient to the correct rotation.
//
//      Calculate the relative offset of the first 'porter: <spacing>
//      meters in the positive Y direction.
//
//      Loop through axPorters:
//          Rez the requested 'porter in its location and correct
//          orientation.  Send a message with the destVector and
//          destRotation in a CSV to the private channel given in
//          the start code.
//
////////////////////////////////////////////////////////////////////////
//
//  REVISION HISTORY
//
//  version 1.3.0, 1 August 2004, by Neo Rebus
//      Added in RINGSTYLE option, equal to LINEAR or LOOP; this
//      determines if the first and last stations should "wrap around"
//      to each other.
//  version 1.2.1, 1 August 2004, by Neo Rebus
//      Added a check for a vector of <0,0,0> to make sure that invalid
//      vectors don't pass the error checking.
//  version 1.2.0, 25 July 2004, by Neo Rebus
//      Added OPTIONS line to notecard format.
//      Added BASEPORTERS option to control what base 'porters get
//      created:  FIRSTONLY, FIRSTANDLAST, or ALL
//  version 1.1.0, 25 July 2004, by Neo Rebus
//      Allow station/base names in the control card; pass those on to
//      the PortRing Porter script to set the sit text.
//  version 1.0.1, 25 July 2004, by Neo Rebus
//      New version of f_TeleportTo, that only lifts up when necessary.
//  version 1.0.0, 24 July 2004, by Neo Rebus
//      First version.  Limited to a single sim.
//
////////////////////////////////////////////////////////////////////////
//
//  A NOTE ABOUT VARIABLE NAMES
//
//  I use a form of "polish notation", where variable names are prefixed
//  by their scope and type.  The first letter of a variable name is its
//  scope:
//
//    p - parameter (a global that is set by the program maintainer and
//                   referenced but not changed by the program itself)
//    g - global
//    f - function
//    s - state     (a global that is only used in a particular state)
//    l - local
//    a - argument  (in the formal argument list for a function)
//
//  The second letter is the variable's type:
//
//    b - boolean   (an integer that holds TRUE or FALSE)
//    f - float
//    i - integer
//    r - rotation
//    s - string
//    v - vector
//    x - list      ('x' is used instead of 'l' to avoid llXYZ names)
//    _ - void      (only for functions)
//
//  The rest of the name is in MultipleCaps notation.
//
////////////////////////////////////////////////////////////////////////
//
//  CONSTANTS
//
//  cxHeadingToRotation
//      A list used to translate a heading to a rotation

list cxHeadingToRotation = [
    "E", <0.00000, 0.00000, 0.00000, 1.00000>,
    "ENE", <0.00000, 0.00000, 0.19509, 0.98079>,
    "NE", <0.00000, 0.00000, 0.38268, 0.92388>,
    "NNE", <0.00000, 0.00000, 0.55557, 0.83147>,
    "N", <0.00000, 0.00000, 0.70711, 0.70711>,
    "NNW", <0.00000, 0.00000, 0.83147, 0.55557>,
    "NW", <0.00000, 0.00000, 0.92388, 0.38268>,
    "WNW", <0.00000, 0.00000, 0.98079, 0.19509>,
    "W", <0.00000, 0.00000, 1.00000, -0.00000>,
    "WSW", <0.00000, 0.00000, 0.98078, -0.19509>,
    "SW", <0.00000, 0.00000, 0.92388, -0.38268>,
    "SSW", <0.00000, 0.00000, 0.83147, -0.55557>,
    "S", <0.00000, 0.00000, 0.70711, -0.70711>,
    "SSE", <0.00000, 0.00000, 0.55557, -0.83147>,
    "SE", <0.00000, 0.00000, 0.38268, -0.92388>,
    "ESE", <0.00000, 0.00000, 0.19509, -0.98078>
] ;

////////////////////////////////////////////////////////////////////////
//
//  PARAMETERS
//
//  piPrivateChannel
//      The first channel number to use for passing information to the
//  'porters.

integer piPrivateChannel = 1850896466 ;

////////////////////////////////////////////////////////////////////////
//
//  GLOBAL VARIABLES
//
//  gsNotecardName
//      The name of the notecard we're parsing.
//
//  giNotecardLine
//      The line number in the notecard we're parsing.
//
//  gxPortRingInfo
//      A list of:
//          string   porterBackName
//          string   porterForwardName
//          string   porterBaseName
//          float    porterSpacing
//
//  gxPortRingStations
//      A list of:
//          vector   baseLocation
//          rotation baseRotation
//          vector   stationLocation
//          rotation stationRotation
//          ...  add'l stations  ...
//
//  gxPortRingStationNames
//      A list of:
//          string   stationName
//          ...  add'l stations  ...
//
//  gxOptions
//      A list of:
//          string   optionName
//          string   optionValue
//          string   possibleValues
//      Initialized to the option names and default option values.
//      option names must be in all caps and start with a ",".
//      possibleValues is a CSV of the possible values.  If empty,
//      any value is allowed.

string gsNotecardName ;

integer giNotecardLine ;

list gxPortRingInfo ;
list gxPortRingStations ;
list gxPortRingStationNames ;

list gxOptions = [
    ",BASEPORTERS", "FIRSTONLY", "FIRSTONLY,FIRSTANDLAST,ALL" ,
    ",RINGSTYLE", "LINEAR", "LINEAR,LOOP"
] ;

////////////////////////////////////////////////////////////////////////
//
//  FUNCTIONS
//

////////////////////////////////////////////////////////////////////////
//
//  f_TeleportTo(vector avDestination)
//      teleport to avDestination, in the same sim as the object.
//
//      record PHANTOM status, and set PHANTOM to true.
//      Move UP to 250m, move to avDestination (at 250m), then move down
//      to a height above gronud equal to avDestination's Z value.

f_TeleportTo(vector avDestination)
{
    integer liOldPhantom = llGetStatus(STATUS_PHANTOM);
    llSetStatus(STATUS_PHANTOM, TRUE);

    vector lvTemp;
    vector lvDest;
    float  lfHeight;

    lvDest = avDestination;
    lvDest.z = 0;
    lvTemp = llGetPos();
    lfHeight = lvTemp.z;
    lvTemp.z = 0;

    while (llVecDist(lvTemp, lvDest) > 0.1)
    {
        vector lvDelta = lvDest - lvTemp;
        if (llVecMag(lvDelta) > 8)
        {
            lvDelta = llVecNorm(lvDelta) * 8;
        }
        if (llGround(lvDelta) > (lfHeight - 1.0))
        {
            lfHeight = llGround(lvDelta) + 1.0;
        } else {
            lvTemp += lvDelta ;
        }
        llSetPos( <lvTemp.x, lvTemp.y, lfHeight> );
    }

    lvDest = avDestination;
    lvDest.z += llGround(ZERO_VECTOR);

    while (llVecDist(llGetPos(), lvDest) > 0.1)
    {
        llSetPos(lvDest);
    }

    llSetStatus(STATUS_PHANTOM, liOldPhantom);
}

////////////////////////////////////////////////////////////////////////
//
//  f_CreatePorterSet(vector avDest, rotation arRot, list axPorters)
//      axPorters is a strided list consisting of sets of:
//          [ "porterName", <destVector>, <destRotation>, "descriptiveName" ]
//
//      Teleport to the 'porter location plus 2m in Z direction, and
//      orient to the correct rotation.
//
//      Calculate the relative offset of the first 'porter: <spacing>
//      meters in the Y direction.
//
//      Loop through axPorters:
//          Rez the requested 'porter in its location.
//          Send a message with the destVector and destRotation
//          in a CSV to the private channel given in the start code.
//

f_CreatePorterSet(vector avDest, rotation arRot, list axPorters)
{
    f_TeleportTo(avDest + <0,0,2>);
    llSetRot(arRot);

    integer liNumPorters = llGetListLength(axPorters) / 4;
    float lfSpacing = llList2Float(gxPortRingInfo, 3);
    float lfOffset = (lfSpacing * 0.5) * (float)(liNumPorters - 1);

    integer i;
    for (i = 0; i < liNumPorters; i++)
    {
        vector lvPorterLocation = llGetPos() + <0, lfOffset, -2> * llGetRot() ;
        llRezObject(
            llList2String(axPorters, i*4),  // the name of object to rez
            lvPorterLocation,               // the location at which to rez
            ZERO_VECTOR,                    // the velocity
            llGetRot(),                     // the rotation
            piPrivateChannel + i            // the start code
        );
        llSleep(1.0);
        vector lvDest = llList2Vector(axPorters, i*4+1);
        rotation lrRot = llList2Rot(axPorters, i*4+2);
        string lsName = llList2String(axPorters, i*4+3);
        lvDest += <-2,0,0> * lrRot;
        llWhisper(piPrivateChannel + i, "Porter:Destination:" + llList2CSV( [ lvDest, lrRot, lsName ] ));
        lfOffset -= lfSpacing;
    }
}

////////////////////////////////////////////////////////////////////////
//
//  Default state:
//
//  Immediately switch to state WaitingForNotecard.

default
{

    on_rez(integer liStartCode)
    {
        llResetScript();
    }

    state_entry()
    {
        state WaitingForNotecard ;
    }

}

////////////////////////////////////////////////////////////////////////
//
//  WaitingForNotecard state:
//
//  on changed(), if CHANGED_INVENTORY, check if we have a notecard.
//  if so, get the notecard name and switch to ParsingNotecard state.

state WaitingForNotecard
{
    on_rez(integer liStartCode)
    {
        llResetScript();
    }

    changed(integer aiWhat)
    {
        if (aiWhat == CHANGED_INVENTORY)
        {
            integer liNotecardCount = llGetInventoryNumber(INVENTORY_NOTECARD);
            if (liNotecardCount > 0)
            {
                gsNotecardName = llGetInventoryName(INVENTORY_NOTECARD, liNotecardCount - 1);
                state ParsingNotecard ;
            }
        }
    }
}

////////////////////////////////////////////////////////////////////////
//
//  ParsingNotecard state:
//
//  On entry, read the next notecard line.
//  On exit, delete the notecard.
//
//  On dataserver, fetch the line.  If EOF, switch to state
//  CreatingPortRing.  If not blank or comment, parse the information
//  out and add it to the gxPortRingInfo list.

state ParsingNotecard
{
    on_rez(integer liStartCode)
    {
        llResetScript();
    }

    state_entry()
    {
        llWhisper(0, "Parsing notecard " + gsNotecardName);
        gxPortRingInfo = [ ] ;
        gxPortRingStations = [ ] ;
        gxPortRingStationNames = [ ] ;
        llGetNotecardLine(gsNotecardName, 0);
        giNotecardLine = 1;
        llSetTimerEvent(10.0);
    }

    state_exit()
    {
        llSetTimerEvent(0.0);
        llRemoveInventory(gsNotecardName);
    }

    timer()
    {
        llWhisper(0, "Still parsing...");
    }

    dataserver(key akRequestID, string asLine)
    {
        string lsError = "";

        if (asLine == EOF)
        {
            state CreatingPortRing;
        }

        if ((asLine != "") && (llGetSubString(asLine, 0, 1) != "//"))
        {
            list lxLineInfo = llCSV2List(asLine);
            string lsLineType = llList2String(lxLineInfo, 0);
            if ("PORTERS" == lsLineType)
            {
                if (llGetListLength(gxPortRingInfo) != 0)
                {
                    lsError = "Invalid notecard format - only one PORTERS line allowed";
                } else {
//llWhisper(0, "Parsed line " + (string)giNotecardLine + ": " + asLine);
                    gxPortRingInfo += llList2List(lxLineInfo, 1, 3);
                    gxPortRingInfo += [ (float)llList2String(lxLineInfo, 4) ];
                }
            } else if ("OPTIONS" == lsLineType)
            {
                integer liNumOptions = llGetListLength(lxLineInfo) - 1;
                integer i;
                for (i = 1; i <= liNumOptions; i++)
                {
                    string lsOptionName = llList2String(lxLineInfo, i);
                    string lsOptionValue;
                    integer liIndex = llSubStringIndex(lsOptionName, "=");
                    if (liIndex > -1)
                    {
                        lsOptionValue = llGetSubString(lsOptionName, liIndex + 1, -1);
                        lsOptionName = llGetSubString(lsOptionName, 0, liIndex - 1);
                    } else {
                        lsOptionValue = "";
                    }
                    lsOptionName = llToUpper(lsOptionName);
                    lsOptionValue = llToUpper(lsOptionValue);
                    liIndex = llListFindList( gxOptions, [ "," + lsOptionName ] );
                    if (liIndex >= 0)
                    {
                        list lxAllowedValues = llCSV2List( llList2String(gxOptions, liIndex + 2) );
                        if ("" == lsOptionValue)
                        {
                            lsOptionValue = llList2String(lxAllowedValues, 0);
                        }
                        if ((llGetListLength(lxAllowedValues) == 0) || (llListFindList(lxAllowedValues, [lsOptionValue]) > -1))
                        {
                            gxOptions = llListInsertList( llDeleteSubList(gxOptions, liIndex + 1, liIndex + 1), [ lsOptionValue ], liIndex + 1);
                        } else {
                            lsError = "Invalid notecard format - invalid value " + lsOptionValue + " for option " + lsOptionName;
                        }
                    } else {
                        lsError = "Invalid notecard format - unknown option " + lsOptionName;
                    }
                }
            } else if ("BASE" == lsLineType)
            {
                if (llGetListLength(gxPortRingInfo) == 0)
                {
                    lsError = "Invalid notecard format - first entry must be PORTERS";
                } else if (llGetListLength(gxPortRingStations) != 0)
                {
                    lsError = "Invalid notecard format - only one BASE line allowed";
                } else {
                    vector lvPosition = (vector)(llList2String(lxLineInfo, 1));
                    string lsHeading = llList2String(lxLineInfo, 2);
                    string lsName = llList2String(lxLineInfo, 3);
                    integer liIdx = llListFindList(cxHeadingToRotation, [ lsHeading ]);
                    if (llVecMag(lvPosition) < 1.0)
                    {
                        lsError = "Invalid notecard format - illegal vector " + llList2String(lxLineInfo, 1);
                    } else if (liIdx < 0)
                    {
                        lsError = "Invalid notecard format - unknown heading " + lsHeading;
                    } else {
//llWhisper(0, "Parsed line " + (string)giNotecardLine + ": " + asLine);
                        gxPortRingStations += [
                            lvPosition,
                            llList2Rot(cxHeadingToRotation, liIdx + 1)
                        ] ;
                        if (lsName == "")
                        {
                            lsName = "Base";
                        }
                        gxPortRingStationNames += [ lsName ] ;
                    }
                }
            } else if ("STATION" == lsLineType)
            {
                if (llGetListLength(gxPortRingInfo) == 0)
                {
                    lsError = "Invalid notecard format - first entry must be PORTERS";
                } else if (llGetListLength(gxPortRingStations) == 0)
                {
                    lsError = "Invalid notecard format - BASE must occur before STATION lines";
                } else {
                    vector lvPosition = (vector)(llList2String(lxLineInfo, 1));
                    string lsHeading = llList2String(lxLineInfo, 2);
                    integer liIdx = llListFindList(cxHeadingToRotation, [ lsHeading ]);
                    string lsName = llList2String(lxLineInfo, 3);
                    if (liIdx < 0)
                    {
                        lsError = "Invalid notecard format - unknown heading " + lsHeading;
                    } else {
//llWhisper(0, "Parsed line " + (string)giNotecardLine + ": " + asLine);
                        gxPortRingStations += [
                            lvPosition,
                            llList2Rot(cxHeadingToRotation, liIdx + 1)
                        ] ;
                        if (lsName == "")
                        {
                            integer num = llGetListLength(gxPortRingStationNames);
                            lsName = "Station " + (string)num;
                        }
                        gxPortRingStationNames += [ lsName ] ;
                    }
                }
            } else {
                lsError = "Invalid notecard format - found unknown line type " + lsLineType;
            }
        }

        if (lsError != "")
        {
            llWhisper(0, "ERROR: " + lsError);
            llWhisper(0, (string)giNotecardLine + ": " + asLine);
            state WaitingForNotecard ;
        } else {
            llGetNotecardLine(gsNotecardName, giNotecardLine);
            giNotecardLine += 1;
        }
    }

}

////////////////////////////////////////////////////////////////////////
//
//  CreatingPortRing state:
//
//  Call f_CreatePorterSet for the base and each station.
//
//  For the base, the porter list will be a single 'porter set for
//  "forward" pointing to station #1.
//
//  For each station, the porter list will contain back (if not the
//  first station), base, and forward (if not the last station).

state CreatingPortRing
{
    on_rez(integer liStartCode)
    {
        llResetScript();
    }

    state_entry()
    {
        llWhisper(0, "Creating PortRing...");

        integer i;
        integer liNumStations = (llGetListLength(gxPortRingStations) / 2) - 1;
        vector lvStartingLocation;
        vector lvBaseLocation;
        rotation lrBaseRotation;
        vector lvStationLocation;
        rotation lrStationRotation;
        vector lvNextStationLocation;
        rotation lrNextStationRotation;
        vector lvPreviousStationLocation;
        rotation lrPreviousStationRotation;
        string lsBackPorter;
        string lsForwardPorter;
        string lsBasePorter;
        list lxPorters;
        string lsBaseName;
        string lsPreviousStationName;
        string lsStationName;
        string lsNextStationName;

        lvStartingLocation = llGetPos();
        lvStartingLocation.z -= llGround(ZERO_VECTOR);

        // initialize BASE and first STATION values [as NEXT station]
        lvBaseLocation = llList2Vector(gxPortRingStations, 0);
        lrBaseRotation = llList2Rot(gxPortRingStations, 1);
        lsBaseName = llList2String(gxPortRingStationNames, 0);
        lvNextStationLocation = llList2Vector(gxPortRingStations, 2);
        lrNextStationRotation = llList2Rot(gxPortRingStations, 3);
        lsNextStationName = llList2String(gxPortRingStationNames, 1);
        lsBackPorter = llList2String(gxPortRingInfo, 0);
        lsForwardPorter = llList2String(gxPortRingInfo, 1);
        lsBasePorter = llList2String(gxPortRingInfo, 2);

        i = llListFindList(gxOptions, [",RINGSTYLE"]);
        string lsRingStyleOption = llList2String(gxOptions, i+1);

        // construct BASE 'porters, based on BASEPORTERS option.
        i = llListFindList(gxOptions, [",BASEPORTERS"]);
        string lsBasePortersOption = llList2String(gxOptions, i+1);

        lxPorters = [
            lsForwardPorter,
            lvNextStationLocation,
            lrNextStationRotation,
            lsNextStationName
        ];

        if ("ALL" == lsBasePortersOption)
        {
            for (i = 2; i <= liNumStations; i++)
            {
                lvStationLocation = llList2Vector(gxPortRingStations, i * 2);
                lrStationRotation = llList2Rot(gxPortRingStations, i * 2 + 1);
                lsStationName = llList2String(gxPortRingStationNames, i);
                lxPorters += [
                    lsForwardPorter,
                    lvStationLocation,
                    lrStationRotation,
                    lsStationName
                ];
            }
        } else if ("FIRSTANDLAST" == lsBasePortersOption)
        {
            lvStationLocation = llList2Vector(gxPortRingStations, liNumStations * 2);
            lrStationRotation = llList2Rot(gxPortRingStations, liNumStations * 2 + 1);
            lsStationName = llList2String(gxPortRingStationNames, liNumStations);
            lxPorters += [
                lsForwardPorter,
                lvStationLocation,
                lrStationRotation,
                lsStationName
            ];
        }

//llWhisper(0, "BASE : " + (string)lvBaseLocation + " @ " + (string)lrBaseRotation);
        f_CreatePorterSet(lvBaseLocation, lrBaseRotation, lxPorters);

        // then create stations
        for (i = 1; i <= liNumStations; i++)
        {

            lvPreviousStationLocation = lvStationLocation;
            lrPreviousStationRotation = lrStationRotation;
            lsPreviousStationName = lsStationName;
            lvStationLocation = lvNextStationLocation;
            lrStationRotation = lrNextStationRotation;
            lsStationName = lsNextStationName;

            // construct lxPorters for BACK (if appropriate), BASE, FORWARD (if appropriate)
            lxPorters = [ ] ;
            if (i > 1)
            {
                lxPorters += [
                    lsBackPorter,
                    lvPreviousStationLocation,
                    lrPreviousStationRotation,
                    lsPreviousStationName
                ];
            } else if ("LOOP" == lsRingStyleOption)
            {
                lvPreviousStationLocation = llList2Vector(gxPortRingStations, liNumStations * 2);
                lrPreviousStationRotation = llList2Rot(gxPortRingStations, liNumStations * 2 + 1);
                lsPreviousStationName = llList2String(gxPortRingStationNames, liNumStations);
                lxPorters += [
                    lsBackPorter,
                    lvPreviousStationLocation,
                    lrPreviousStationRotation,
                    lsPreviousStationName
                ];
            }
            lxPorters += [
                lsBasePorter,
                lvBaseLocation,
                lrBaseRotation,
                lsBaseName
            ];
            if (i < liNumStations)
            {
                lvNextStationLocation = llList2Vector(gxPortRingStations, i * 2 + 2);
                lrNextStationRotation = llList2Rot(gxPortRingStations, i * 2 + 3);
                lsNextStationName = llList2String(gxPortRingStationNames, i + 1);
                lxPorters += [
                    lsForwardPorter,
                    lvNextStationLocation,
                    lrNextStationRotation,
                    lsNextStationName
                ];
            } else if ("LOOP" == lsRingStyleOption)
            {
                lvNextStationLocation = llList2Vector(gxPortRingStations, 2);
                lrNextStationRotation = llList2Rot(gxPortRingStations, 3);
                lsNextStationName = llList2String(gxPortRingStationNames, 1);
                lxPorters += [
                    lsForwardPorter,
                    lvNextStationLocation,
                    lrNextStationRotation,
                    lsNextStationName
                ];
            }

//llWhisper(0, "STATION " + (string)i + " : " + (string)lvStationLocation + " @ " + (string)lrStationRotation);
            f_CreatePorterSet(lvStationLocation, lrStationRotation, lxPorters);
        }

        f_TeleportTo(lvStartingLocation);

        state WaitingForNotecard;
    }
}

////////////////////////////////////////////////////////////////////////
//
//  LICENSE FOR USE
//
//  This code is copyright (c) 2004 by Johnson Earls.
//  All rights reserved.
//
//  Permission to use and redistribute this source code, with or without
//  modifications, is granted under the following conditions:
//
//  1)  This code may be used, in whole or in part, in other code, so
//      long as the author is credited by SL name (Neo Rebus) or FL
//      name (Johnson Earls).
//  2)  This code, including portions of this code that are
//      incorporated into other works, may be redistributed as long as
//      it is accompanied by the author's original copyright notice,
//      and a pointer ot the full original souce code.  For example:
//          Portions of this code are copyright (c) 2004 by Johnson
//          Earls, used under license.  Original code available at
//          http://www.badgeometry.com/wiki/LibraryPortRingCreator
//
////////////////////////////////////////////////////////////////////////


The PortRing&nbsp;Retriever script:
//  PortRingRetriever - Retrieve a set of PortRing objects from
//                      a dropped-in notecard.
//  version 1.3.0, 1 August 2004, by Neo Rebus
//
//  This script will retrieve the objects that make up a PortRing
//  structure.
//
//  See PortRingCreator for the details of the notecard.
//
//  General algorithm:
//
//      When a notecard is dropped in, parse the notecard into the
//      gxPortRingInfo and gxPortRingStations global lists.  When
//      finished, the first three entries should be strings containing
//      the names of the three Porter objects - back, forward, base,
//      respectively, followed by the spacing between 'porters in a
//      set, and then by vector, rotation pairs, the first for the base
//      location, the rest
//      for each station.
//
//      Delete the notecard.
//
//      Loop through the base and station locations, use a sensor to
//      find the different objects, and attach them to this object.
//
////////////////////////////////////////////////////////////////////////
//
//  REVISION HISTORY
//
//  version 1.3.0, 1 August 2004, by Neo Rebus
//      Added RINGSTYLE option for compatibility with PortRingCreator.
//  version 1.2.0, 25 July 2004, by Neo Rebus
//      Added OPTIONS line to notecard format.
//      Added BASEPORTERS option for compatibility with PortRingCreator.
//  version 1.0.1, 25 July 2004, by Neo Rebus
//      New version of f_TeleportTo, that only lifts up when necessary.
//  version 1.0.0, 24 July 2004, by Neo Rebus
//      First version.  Only works inside a single sim.
//
////////////////////////////////////////////////////////////////////////
//
//  A NOTE ABOUT VARIABLE NAMES
//
//  I use a form of "polish notation", where variable names are prefixed
//  by their scope and type.  The first letter of a variable name is its
//  scope:
//
//    p - parameter (a global that is set by the program maintainer and
//                   referenced but not changed by the program itself)
//    g - global
//    f - function
//    s - state     (a global that is only used in a particular state)
//    l - local
//    a - argument  (in the formal argument list for a function)
//
//  The second letter is the variable's type:
//
//    b - boolean   (an integer that holds TRUE or FALSE)
//    f - float
//    i - integer
//    r - rotation
//    s - string
//    v - vector
//    x - list      ('x' is used instead of 'l' to avoid llXYZ names)
//    _ - void      (only for functions)
//
//  The rest of the name is in MultipleCaps notation.
//
////////////////////////////////////////////////////////////////////////
//
//  CONSTANTS
//
//  cxHeadingToRotation
//      A list used to translate a heading to a rotation

list cxHeadingToRotation = [
    "E", <0.00000, 0.00000, 0.00000, 1.00000>,
    "ENE", <0.00000, 0.00000, 0.19509, 0.98079>,
    "NE", <0.00000, 0.00000, 0.38268, 0.92388>,
    "NNE", <0.00000, 0.00000, 0.55557, 0.83147>,
    "N", <0.00000, 0.00000, 0.70711, 0.70711>,
    "NNW", <0.00000, 0.00000, 0.83147, 0.55557>,
    "NW", <0.00000, 0.00000, 0.92388, 0.38268>,
    "WNW", <0.00000, 0.00000, 0.98079, 0.19509>,
    "W", <0.00000, 0.00000, 1.00000, -0.00000>,
    "WSW", <0.00000, 0.00000, 0.98078, -0.19509>,
    "SW", <0.00000, 0.00000, 0.92388, -0.38268>,
    "SSW", <0.00000, 0.00000, 0.83147, -0.55557>,
    "S", <0.00000, 0.00000, 0.70711, -0.70711>,
    "SSE", <0.00000, 0.00000, 0.55557, -0.83147>,
    "SE", <0.00000, 0.00000, 0.38268, -0.92388>,
    "ESE", <0.00000, 0.00000, 0.19509, -0.98078>
] ;

////////////////////////////////////////////////////////////////////////
//
//  PARAMETERS
//
//  (none)

////////////////////////////////////////////////////////////////////////
//
//  GLOBAL VARIABLES
//
//  gsNotecardName
//      The name of the notecard we're parsing.
//
//  giNotecardLine
//      The line number in the notecard we're parsing.
//
//  gxPortRingInfo
//      A list of:
//          string   porterBackName
//          string   porterForwardName
//          string   porterBaseName
//          float    porterSpacing
//
//  gxPortRingStations
//      A list of:
//          vector   baseLocation
//          rotation baseRotation
//          vector   stationLocation
//          rotation stationRotation
//          ...  add'l stations  ...
//
//  gxOptions
//      A list of:
//          string   optionName
//          string   optionValue
//          string   possibleValues
//      Initialized to the option names and default option values.
//      option names must be in all caps and start with a ",".
//      possibleValues is a CSV of the possible values.  If empty,
//      any value is allowed.
//
//  gxLocations
//      A refactored form of gxPortRingInfo and gxPortRingStations; this
//      is a list of:
//          string   porterName
//          vector   stationLocation
//          ...
//
//  giLocationIndex
//      Holds the current index into gxLocations as we're travelling around.
//
//  gvStartLocation
//      Where we want to return to when we're done.

string gsNotecardName ;

integer giNotecardLine ;

list gxPortRingInfo ;
list gxPortRingStations ;

list gxOptions = [
    ",BASEPORTERS", "FIRSTONLY", "FIRSTONLY,FIRSTANDLAST,ALL" ,
    ",RINGSTYLE", "LINEAR", "LINEAR,LOOP"
] ;

list gxLocations ;
integer giLocationIndex ;
vector gvStartLocation ;

////////////////////////////////////////////////////////////////////////
//
//  FUNCTIONS
//

////////////////////////////////////////////////////////////////////////
//
//  f_TeleportTo(vector avDestination)
//      teleport to avDestination, in the same sim as the object.
//
//      record PHANTOM status, and set PHANTOM to true.
//      Move UP to 250m, move to avDestination (at 250m), then move down
//      to a height above gronud equal to avDestination's Z value.

f_TeleportTo(vector avDestination)
{
    integer liOldPhantom = llGetStatus(STATUS_PHANTOM);
    llSetStatus(STATUS_PHANTOM, TRUE);

    vector lvTemp;
    vector lvDest;
    float  lfHeight;

    lvDest = avDestination;
    lvDest.z = 0;
    lvTemp = llGetPos();
    lfHeight = lvTemp.z;
    lvTemp.z = 0;

    while (llVecDist(lvTemp, lvDest) > 0.1)
    {
        vector lvDelta = lvDest - lvTemp;
        if (llVecMag(lvDelta) > 8)
        {
            lvDelta = llVecNorm(lvDelta) * 8;
        }
        if (llGround(lvDelta) > (lfHeight - 1.0))
        {
            lfHeight = llGround(lvDelta) + 1.0;
        } else {
            lvTemp += lvDelta ;
        }
        llSetPos( <lvTemp.x, lvTemp.y, lfHeight> );
    }

    lvDest = avDestination;
    lvDest.z += llGround(ZERO_VECTOR);

    while (llVecDist(llGetPos(), lvDest) > 0.1)
    {
        llSetPos(lvDest);
    }

    llSetStatus(STATUS_PHANTOM, liOldPhantom);
}

////////////////////////////////////////////////////////////////////////
//
//  Default state:
//
//  Immediately switch to state WaitingForNotecard.

default
{

    on_rez(integer liStartCode)
    {
        llResetScript();
    }

    state_entry()
    {
        llRequestPermissions(llGetOwner(), PERMISSION_CHANGE_LINKS);
    }

    run_time_permissions(integer aiPermissions)
    {
        if (aiPermissions & PERMISSION_CHANGE_LINKS)
        {
            state WaitingForNotecard ;
        } else {
            llSay(0, "Cannot run without PERMISSION_CHANGE_LINKS");
            llRequestPermissions(llGetOwner(), PERMISSION_CHANGE_LINKS);
        }
    }

}

////////////////////////////////////////////////////////////////////////
//
//  WaitingForNotecard state:
//
//  on changed(), if CHANGED_INVENTORY, check if we have a notecard.
//  if so, get the notecard name and switch to ParsingNotecard state.

state WaitingForNotecard
{
    on_rez(integer liStartCode)
    {
        llResetScript();
    }

    changed(integer aiWhat)
    {
        if (aiWhat == CHANGED_INVENTORY)
        {
            integer liNotecardCount = llGetInventoryNumber(INVENTORY_NOTECARD);
            if (liNotecardCount > 0)
            {
                gsNotecardName = llGetInventoryName(INVENTORY_NOTECARD, liNotecardCount - 1);
                state ParsingNotecard ;
            }
        }
    }
}

////////////////////////////////////////////////////////////////////////
//
//  ParsingNotecard state:
//
//  On entry, read the next notecard line.
//  On exit, delete the notecard.
//
//  On dataserver, fetch the line.  If EOF, switch to state
//  CreatingPortRing.  If not blank or comment, parse the information
//  out and add it to the gxPortRingInfo list.

state ParsingNotecard
{
    on_rez(integer liStartCode)
    {
        llResetScript();
    }

    state_entry()
    {
        llWhisper(0, "Parsing notecard " + gsNotecardName);
        gxPortRingInfo = [ ] ;
        gxPortRingStations = [ ] ;
        llGetNotecardLine(gsNotecardName, 0);
        giNotecardLine = 1;
        llSetTimerEvent(10.0);
    }

    state_exit()
    {
        llSetTimerEvent(0.0);
        llRemoveInventory(gsNotecardName);
    }

    timer()
    {
        llWhisper(0, "Still parsing...");
    }

    dataserver(key akRequestID, string asLine)
    {
        string lsError = "";

        if (asLine == EOF)
        {
            state RetrievingPortRing;
        }

        if ((asLine != "") && (llGetSubString(asLine, 0, 1) != "//"))
        {
            list lxLineInfo = llCSV2List(asLine);
            string lsLineType = llList2String(lxLineInfo, 0);
            if ("PORTERS" == lsLineType)
            {
                if (llGetListLength(gxPortRingInfo) != 0)
                {
                    lsError = "Invalid notecard format - only one PORTERS line allowed";
                } else {
                    gxPortRingInfo += llList2List(lxLineInfo, 1, 3);
                    gxPortRingInfo += [ (float)llList2String(lxLineInfo, 4) ];
                }
            } else if ("OPTIONS" == lsLineType)
            {
                integer liNumOptions = llGetListLength(lxLineInfo) - 1;
                integer i;
                for (i = 1; i <= liNumOptions; i++)
                {
                    string lsOptionName = llList2String(lxLineInfo, i);
                    string lsOptionValue;
                    integer liIndex = llSubStringIndex(lsOptionName, "=");
                    if (liIndex > -1)
                    {
                        lsOptionValue = llGetSubString(lsOptionName, liIndex + 1, -1);
                        lsOptionName = llGetSubString(lsOptionName, 0, liIndex - 1);
                    } else {
                        lsOptionValue = "";
                    }
                    lsOptionName = llToUpper(lsOptionName);
                    lsOptionValue = llToUpper(lsOptionValue);
                    liIndex = llListFindList( gxOptions, [ "," + lsOptionName ] );
                    if (liIndex >= 0)
                    {
                        list lxAllowedValues = llCSV2List( llList2String(gxOptions, liIndex + 2) );
                        if ("" == lsOptionValue)
                        {
                            lsOptionValue = llList2String(lxAllowedValues, 0);
                        }
                        if ((llGetListLength(lxAllowedValues) == 0) || (llListFindList(lxAllowedValues, [lsOptionValue]) > -1))
                        {
                            gxOptions = llListInsertList( llDeleteSubList(gxOptions, liIndex + 1, liIndex + 1), [ lsOptionValue ], liIndex + 1);
                        } else {
                            lsError = "Invalid notecard format - invalid value " + lsOptionValue + " for option " + lsOptionName;
                        }
                    } else {
                        lsError = "Invalid notecard format - unknown option " + lsOptionName;
                    }
                }
            } else if ("BASE" == lsLineType)
            {
                if (llGetListLength(gxPortRingInfo) == 0)
                {
                    lsError = "Invalid notecard format - first entry must be PORTERS";
                } else if (llGetListLength(gxPortRingStations) != 0)
                {
                    lsError = "Invalid notecard format - only one BASE line allowed";
                } else {
                    vector lvPosition = (vector)(llList2String(lxLineInfo, 1));
                    string lsHeading = llList2String(lxLineInfo, 2);
                    integer liIdx = llListFindList(cxHeadingToRotation, [ lsHeading ]);
                    if (liIdx < 0)
                    {
                        lsError = "Invalid notecard format - unknown heading " + lsHeading;
                    } else {
                        gxPortRingStations += [
                            lvPosition,
                            llList2Rot(cxHeadingToRotation, liIdx + 1)
                        ] ;
                    }
                }
            } else if ("STATION" == lsLineType)
            {
                if (llGetListLength(gxPortRingInfo) == 0)
                {
                    lsError = "Invalid notecard format - first entry must be PORTERS";
                } else if (llGetListLength(gxPortRingStations) == 0)
                {
                    lsError = "Invalid notecard format - BASE must occur before STATION lines";
                } else {
                    vector lvPosition = (vector)(llList2String(lxLineInfo, 1));
                    string lsHeading = llList2String(lxLineInfo, 2);
                    integer liIdx = llListFindList(cxHeadingToRotation, [ lsHeading ]);
                    if (liIdx < 0)
                    {
                        lsError = "Invalid notecard format - unknown heading " + lsHeading;
                    } else {
                        gxPortRingStations += [
                            lvPosition,
                            llList2Rot(cxHeadingToRotation, liIdx + 1)
                        ] ;
                    }
                }
            } else {
                lsError = "Invalid notecard format - found unknown line type " + lsLineType;
            }
        }

        if (lsError != "")
        {
            llSay(0, "ERROR: " + lsError);
            llSay(0, (string)giNotecardLine + ": " + asLine);
            state WaitingForNotecard ;
        } else {
            llGetNotecardLine(gsNotecardName, giNotecardLine);
            giNotecardLine += 1;
        }
    }

}

////////////////////////////////////////////////////////////////////////
//
//  RetrievingPortRing state:
//

state RetrievingPortRing
{
    on_rez(integer liStartCode)
    {
        llResetScript();
    }

    state_entry()
    {
        llWhisper(0, "Retrieving PortRing...");

        llSetRot( <0,0.707,0,0.707> );

        integer i;
        integer liNumStations = (llGetListLength(gxPortRingStations) / 2) - 1;
        vector lvLocation;
        rotation lrRotation;
        string lsBackPorter;
        string lsForwardPorter;
        string lsBasePorter;

        lsBackPorter = llList2String(gxPortRingInfo, 0);
        lsForwardPorter = llList2String(gxPortRingInfo, 1);
        lsBasePorter = llList2String(gxPortRingInfo, 2);

        // then create stations
        for (i = 0; i <= liNumStations; i++)
        {
            lvLocation = llList2Vector(gxPortRingStations, i * 2);
            lrRotation = llList2Rot(gxPortRingStations, i * 2 + 1);

            gxLocations += [ lsBackPorter, lvLocation, lsForwardPorter, lvLocation, lsBasePorter, lvLocation ] ;
        }

        gvStartLocation = llGetPos() ;
        gvStartLocation.z = liNumStations + 2;
        giLocationIndex = 0;
        llSetTimerEvent(1.0);
    }

    timer()
    {
        llSetTimerEvent(0.0);

        if (giLocationIndex < llGetListLength(gxLocations))
        {
            float lfDist = (giLocationIndex/6) + 1;
            float lfSpacing = llList2Float(gxPortRingInfo, 3);
            f_TeleportTo(llList2Vector(gxLocations, giLocationIndex + 1) + <0,0,lfDist>);

            llSensor(
                llList2String(gxLocations, giLocationIndex),
                NULL_KEY,
                SCRIPTED|PASSIVE|ACTIVE|AGENT,
                llVecMag( <0, lfDist+lfSpacing, 2*lfSpacing> ),
                PI
            );
            giLocationIndex += 2;
        } else {
            f_TeleportTo(gvStartLocation);
        }
    }

    no_sensor()
    {
        llSetTimerEvent(1.0);
    }

    sensor(integer num)
    {
        integer i;
        for (i=0; i<num; i++)
        {
            llCreateLink(llDetectedKey(i), TRUE);
        }
        llSetTimerEvent(1.0);
    }

}

////////////////////////////////////////////////////////////////////////
//
//  LICENSE FOR USE
//
//  This code is copyright (c) 2004 by Johnson Earls.
//  All rights reserved.
//
//  Permission to use and redistribute this source code, with or without
//  modifications, is granted under the following conditions:
//
//  1)  This code may be used, in whole or in part, in other code, so
//      long as the author is credited by SL name (Neo Rebus) or FL
//      name (Johnson Earls).
//  2)  This code, including portions of this code that are
//      incorporated into other works, may be redistributed as long as
//      it is accompanied by the author's original copyright notice,
//      and a pointer ot the full original souce code.  For example:
//          Portions of this code are copyright (c) 2004 by Johnson
//          Earls, used under license.  Original code available at
//          http://www.badgeometry.com/wiki/LibraryPortRingCreator
//
////////////////////////////////////////////////////////////////////////


The PortRing&nbsp;Porter script:
//  PortRingPorter - The script in the PortRing 'Porters that actually
//                   teleports you to a location.
//  version 1.1.0, 24 July 2004, by Neo Rebus
//
//  When rezzed, it listens for on the channel indicated by the start
//  code for a message that begins "Porter:Destination:".  The rest of
//  that message is a CSV with the 'porter's destination position and
//  rotation.  Compute the Sit Target from those values, and set the
//  Sit Text, then remove the listener.  When an avatar sits, unsit
//  him/her after a 0.1 second delay.
//
////////////////////////////////////////////////////////////////////////
//
//  REVISION HISTORY
//
//  version 1.1.0, 24 July 2004, by Neo Rebus
//      3rd argument in CSV is the sit text.  If none given,
//      use "Teleport!"
//  version 1.0.0, 24 July 2004, by Neo Rebus
//      First version
//
////////////////////////////////////////////////////////////////////////
//
//  A NOTE ABOUT VARIABLE NAMES
//
//  I use a form of "polish notation", where variable names are prefixed
//  by their scope and type.  The first letter of a variable name is its
//  scope:
//
//    p - parameter (a global that is set by the program maintainer and
//                   referenced but not changed by the program itself)
//    g - global
//    f - function
//    s - state     (a global that is only used in a particular state)
//    l - local
//    a - argument  (in the formal argument list for a function)
//
//  The second letter is the variable's type:
//
//    b - boolean   (an integer that holds TRUE or FALSE)
//    f - float
//    i - integer
//    r - rotation
//    s - list      ('s' is used instead of 'l' to avoid llXYZ names)
//    v - vector
//    _ - void      (only for functions)
//
//  The rest of the name is in MultipleCaps notation.
//
////////////////////////////////////////////////////////////////////////
//
//  PAREMETERS
//
//  (None)

////////////////////////////////////////////////////////////////////////
//
//  GLOBAL VARIABLES
//
//  giListenerID
//      The ID of the listener, to remove it as soon as we've received
//      our information [listeners cause lag!]

integer giListenerID ;

////////////////////////////////////////////////////////////////////////
//
//  FUNCTIONS
//
//  (none)

////////////////////////////////////////////////////////////////////////
//
//  Default state:
//
//  Description of what happens in the state.
//
//  on_rez:
//      Reset the script.
//
//  state_entry:
//      Zero out the gvTeleportDestination and grTeleportRotation.
//
//  changed:
//      if grTeleportDestination is not ZERO_VECTOR:
//          If there's an avatar on the Sit Target, unsit the avatar.
//
//  listen:
//      if the string begins with "Porter:Destination", set the
//          sit target to the destination and rotation values in
//          the CSV in the message string, and the sit text to
//          "Teleport!"

default
{

    on_rez(integer liStartCode)
    {
        giListenerID = llListen(liStartCode, "", NULL_KEY, "");
    }

    listen(integer liChannel, string lsName, key lkSender, string lsMsg)
    {
        if ("Porter:Destination:" == llGetSubString(lsMsg, 0, 18))
        {
            rotation lrMyInverseRot = (ZERO_ROTATION / llGetRot());
            list lsParams = llCSV2List(llGetSubString(lsMsg, 19, -1));
            vector lvTeleportDestination = (vector)(llList2String(lsParams, 0));
            rotation lrTeleportRotation = (rotation)(llList2String(lsParams, 1));
            string lsSitText = llList2String(lsParams, 2);
            if ("" == lsSitText)
            {
                lsSitText = "Teleport!";
            }
            llListenRemove(giListenerID);
            lvTeleportDestination = lvTeleportDestination - llGetPos();
            lvTeleportDestination.z += 100.0;
            lvTeleportDestination = lvTeleportDestination + <0,0,llGround(lvTeleportDestination)>;
            lvTeleportDestination.z -= 100.0;
            lvTeleportDestination = lvTeleportDestination * lrMyInverseRot;
            lrTeleportRotation = lrMyInverseRot * lrTeleportRotation;
//llWhisper(0, "Setting sit target to " + (string)lvTeleportDestination + " @ " + (string)lrTeleportRotation);
            llSitTarget(lvTeleportDestination, lrTeleportRotation);
            llSetSitText(lsSitText);
        }
    }

    changed(integer aiWhatChanged)
    {
//llWhisper(0, "Got CHANGED event");
        if (aiWhatChanged == CHANGED_LINK)
        {
//llWhisper(0, "Got CHANGED_LINK event");
            key lkAvatar = llAvatarOnSitTarget();
//llWhisper(0, "Sitter = " + (string)lkAvatar);
            if (lkAvatar != NULL_KEY)
            {
//llWhisper(0, "Delaying 1/10 second...");
                llSleep(0.1);
//llWhisper(0, "Unsitting sitter...");
                llUnSit(lkAvatar);
            }
        }
//llWhisper(0, "Done with CHANGED");
    }
}

////////////////////////////////////////////////////////////////////////
//
//  LICENSE FOR USE
//
//  This code is copyright (c) 2004 by Johnson Earls.
//  All rights reserved.
//
//  Permission to use and redistribute this source code, with or without
//  modifications, is granted under the following conditions:
//
//  1)  This code may be used, in whole or in part, in other code, so
//      long as the author is credited by SL name (Neo Rebus) or FL
//      name (Johnson Earls).
//  2)  This code, including portions of this code that are
//      incorporated into other works, may be redistributed as long as
//      it is accompanied by the author's original copyright notice,
//      and a pointer ot the full original souce code.  For example:
//          Portions of this code are copyright (c) 2004 by Johnson
//          Earls, used under license.  Original code available at
//          http://www.badgeometry.com/wiki/LibraryPortRingCreator
//
////////////////////////////////////////////////////////////////////////


ScriptLibrary
Comments [Hide comments/form]
Attach a comment to this page: