Jump to content

Susie Chaffe

Resident
  • Posts

    30
  • Joined

  • Last visited

Posts posted by Susie Chaffe

  1. This has not shown up in the forums for a while so I thought it maybe time for a reminder for anyone brave enough to put up with the current state of region crossings.

    Because llMoveToTarget only accepts region coordinates, it is frequently assumed that it can only be used within a single region. Not true.
    Given a target outside of it's current region an object will travel far enough into an adjacent region to trigger a CHANGED_REGION event.
    At this point a new target can be calculated and the object can proceed on its merry way.

    Any distance can be covered as long as it is broken down into steps .The maximum distance that can be moved in one hop is 64 metres - but based on experience I would recommend a slightly lower value.

    Below is a sample tour script that demonstrates one method of inter sim travel.
    The script also attempts to maintain an almost constant velocity throughout each move.

    I have included a short sample tour from Blake Sea Spyglass to Barbarossa infohub.
    There is rez zone at the end of the pier in Spyglass where you can create or rez a tour disc.

    slurl:  http://maps.secondlife.com/secondlife/Blake%20Sea%20-%20Spyglass/145/213/22

    Note. This is a rework of a script that appeared in the legacy scripting archives somewhere around 2005 - thanks to the original creator whoever you are.

    //Tour Gadget using llMoveToTarget - Susie Chaffe 2008-2013
     
    integer DEBUG = FALSE;
    //--------------------- Target Variables ----------------------
    vector gTargetLocal;  // Target in Region coordinates
    vector gTargetRC;     // Target Region Corner
    vector gCurrentPos;   // Current position in Region coordinates
    vector gCurrentRC;    // Current Region Corner
     
    //--------------------- llMoveTo Control Variables -------------
    integer targetID;
    vector  gMoveTo;             // Next target vector
    float   maxStepSize = 60.0;  // Avoid using the 64metre limit of llMoveToTarget
    float   range = 0.5;         // at_target condition
    float   speed = 5.0;         //
     
    // -------------------- Notecard Reader Variables --------
    string   gNotecard;    // name of route notecard in the object's inventory
    integer  gLine = 0;    // current line number
    key      gQueryID;     // id used to identify dataserver queries
    list     gLstData;     // List to hold notecard line for parsing to commands
    string   gCommand;
     
    //--------------------- User Functions ---------------------------
    gNextLine()
    {
        ++gLine;
        gQueryID = llGetNotecardLine(gNotecard, gLine);
    }
     
    waypoint()
    {
        llSetStatus(STATUS_PHYSICS | STATUS_PHANTOM, TRUE);
        gTargetLocal = (vector)llList2String(gLstData, 2);  //Local xyz
        gTargetRC =    (vector)llList2String(gLstData, 3);  //Region Corner
        gCurrentPos = llGetPos();
        gCurrentRC  = llGetRegionCorner();
     
        if (gTargetRC == gCurrentRC)  gMoveTo = gTargetLocal;
        else gMoveTo = gTargetLocal + (gTargetRC - gCurrentRC);
     
        vector vectorAngle = gMoveTo - gCurrentPos;
        float distance = llVecMag(vectorAngle);    //Calculate Distance
     
        if(distance <= range)
        {
            llTargetRemove(targetID);
            llStopMoveToTarget();
            gNextLine();
            return;
        }
        else
        {
            if (distance > maxStepSize) distance = maxStepSize;
            gMoveTo = gCurrentPos + llVecNorm(vectorAngle)*distance;
            targetID = llTarget(gMoveTo, range);
            llLookAt(gMoveTo,1,1);
            llMoveToTarget(gMoveTo,distance/speed);
        }
    }
    //----------------------Create Personal Transport Device-------------------------
    makePTD()
    {
        llSetLinkPrimitiveParamsFast(LINK_THIS,[
            PRIM_TYPE, PRIM_TYPE_SPHERE, 0, <0.0, 1.0, 0.0>, 0.0, ZERO_VECTOR, <0.0, 1.0, 0.0>
            ,   PRIM_SIZE, <0.1, 0.5, 0.5>
            ,   PRIM_ROTATION, <0.0, 0.70711, 0.0, 0.70711>
            ,   PRIM_TEXTURE, 0, "5748decc-f629-461c-9a36-a35a221fe21f", <1.0, 1.0, 0.0>,ZERO_VECTOR, 0.0
            ,   PRIM_COLOR, 0, <0.5, 0.0, 0.0>, 1.0
            ,   PRIM_BUMP_SHINY, 0, PRIM_SHINY_MEDIUM, PRIM_BUMP_NONE]);
        llSetClickAction(CLICK_ACTION_SIT);
        llSitTarget(<-0.76916, 0.004, -0.09061>, <0.0, -0.70711, 0.0, 0.70711>);
        llSetPos(llGetPos() + <0.0,0.0,0.75>);
        llSetText("Sit to Start \n Tour \n  \n ", <1.0, 1.0, 1.0>, 1.0);
    }
    //-------------------------------------------------------------------------------
    default
    {
        on_rez(integer start_param)
        {
            llResetScript();
        }
     
        state_entry()
        {
            makePTD();
        }
     
        changed(integer change)
        {
            if (change & CHANGED_LINK)
            {
                llSleep(0.5);
                if (llAvatarOnSitTarget() != NULL_KEY)
                {
                    llSetText("", ZERO_VECTOR, 0.0);
                    state Tour;
                }
            }
        }
    }
     
    state Tour
    {
        on_rez(integer start_param)
        {
            llResetScript();
        }
     
        state_entry()
        {
            llSetStatus(STATUS_PHYSICS | STATUS_ROTATE_Y | STATUS_ROTATE_Z, FALSE);
            llSetStatus(STATUS_ROTATE_X, TRUE);
            gNotecard = llGetInventoryName(INVENTORY_NOTECARD, 0);
            gQueryID = llGetNotecardLine(gNotecard, gLine);
        }
     
        dataserver(key query_id, string data)
        {
            if (query_id == gQueryID)
            {
                if(data != EOF)
                {
                    // Delete Comments Routine by Void Singer
                    integer comment = llSubStringIndex (data, "//");
                    if (~(comment = llSubStringIndex( data, "//" ))) data = llDeleteSubString( data, comment, -1);
                    data = llStringTrim( data, STRING_TRIM );
                    if (data)
                    {
                        if(DEBUG) llOwnerSay((string) gLine + " : " + data);
                        llToUpper(data);
                        gLstData = llParseString2List(data, ["|"], []);
                        gCommand = llList2String(gLstData, 0);
                        if("W" == gCommand) waypoint();
                        else gNextLine();
                    }
                    else gNextLine();
                }
                else
                {
                    llSetStatus(STATUS_PHYSICS | STATUS_PHANTOM, FALSE);
                    llUnSit(llAvatarOnSitTarget());
                    llSleep(1.0);
                    llDie();
                }
            }
        }
     
        not_at_target()
        {
            float thisStep = llVecDist(llGetPos(),gMoveTo);
            llMoveToTarget(gMoveTo,thisStep/speed);
        }
        at_target( integer number, vector targetpos, vector ourpos )
        {
            llTargetRemove(targetID);
            waypoint();
        }
     
        changed(integer change)
        {
            if (change & CHANGED_REGION)
            {
                gCurrentRC  = llGetRegionCorner();
                waypoint();
            }
            else if (change & CHANGED_LINK)
            {
                if (llAvatarOnSitTarget() == NULL_KEY)
                {
                    llSleep(0.5);
                    llDie();
                }
            }
        }
    }
    //============================================================================

    Sample Tour - copy and paste into a new notecard:

    W|Blake Sea - Spyglass|<145,212,40.0>|<290048,269568,0>
    W|Blake Sea - Spyglass|<152,110,40.0>|<290048,269568,0>
    W|Blake Sea - Half Hitch|<116,152,35.0>|<290048,268800,0>
    W|Blake Sea - Half Hitch|<72,122,35.0>|<290048,268800,0>
    W|Barbarossa|<175,19,24.0>|<289792,268800,0>
    W|Barbarossa|<175,19,21.0>|<289792,268800,0>

    • Like 1
  2.  A little late coming to the party - the example you used is restricted to 2 pages
    This script should handle any number of items and is customisable to a certain extent.  It is a cut down and adapted version of one I use for populating a dance hud.


    Replace
    // Dialog Based Object Rezzer Script// Susie Chaffe 2013// Free to use modify and mangle as you see fit // No Limit on Number of Object Names// Handles 24 character limit by creating alised object names// Has the option to create custom buttons / submenus if required// Is a little long winded for the sake of clarity //======================= SYSTEM VARIABLES ==========================// vector  vRezPos = <0.00, 0.00, 1.00>;integer dialogChannel;integer dialogHandle;integer iInvType = INVENTORY_OBJECT;integer iPageIdx = 0;list    lstShortName; //======================= CUSTOM FUNCTIONS ==========================// // Create Short name for Long Object Names and add to a look up list// Note that only the first 12 characters will normally show on the menu button// You can use a shorter substring if you preferstring AliasName(string sLongName){    string sShortName = (llGetSubString(sLongName,0,23));    lstShortName += [sShortName,sLongName];    return sShortName;} // Reorder Buttons as shown in listlist order(list buttons){    return llList2List(buttons, -3, -1) + llList2List(buttons, -6, -4) + llList2List(buttons, -9, -7) + llList2List(buttons, -12, -10);} // Dynamic menu dialog functiondoDialog(key av){    // Standard Buttons that will appear on every page - normally navigation buttons    list lstStatic = ["<< PREV","< MAIN >","NEXT >>"];    integer iBtnSlots = 12-llGetListLength(lstStatic);     // Optional extra buttons that will only appear on the first page    // Can be used to create sub-menus if required    // list lstExtraBtn = []; // **Use this if you no extra buttons    list lstExtraBtn = ["-CONTENTS-"];  // ** This is just an example    integer iAdjSlots = llGetListLength(lstExtraBtn);     // Dynamic buttons - read from inventory object names    integer iTotalNames = llGetInventoryNumber(iInvType);     // Calculate menu last page index    integer iMaxPageIdx = (llFloor((float) (iTotalNames + iAdjSlots) / (float) iBtnSlots));     // First & Last Page Navigation Toggle    if (iPageIdx < 0 ) iPageIdx = iMaxPageIdx;    else if (iPageIdx > iMaxPageIdx) iPageIdx = 0;     // Build the button list    list lstDialog = [];    lstShortName = [];    integer idxSlot = iPageIdx*iBtnSlots;     integer i;    // First menu page that has extra buttons    if((0 == iPageIdx) && ([] != lstExtraBtn))    {        for(i = idxSlot; (i < idxSlot+iBtnSlots-iAdjSlots) && (i <= iTotalNames-1 ); i++)        {            string sItemName = llGetInventoryName(iInvType,i);            if(24 < llStringLength(sItemName)) sItemName = AliasName(sItemName);            lstDialog += [sItemName];        }        //Add the Extra button(s)        lstDialog += lstExtraBtn;    }     // Other Pages or First Page if no extra buttons    else    {        for(i = idxSlot; (i < idxSlot+iBtnSlots) && (i <= iTotalNames-1 + iAdjSlots); i++)        {            string sItemName = llGetInventoryName(iInvType,i-iAdjSlots);            if(24 < llStringLength(sItemName)) sItemName = AliasName(sItemName);            lstDialog += [sItemName];        }    }     // Add Static Btns to the end of the Dyanamic List    lstDialog += lstStatic;     // Menu message    string msg = " \n Choose an Option: "  + "Page " + (string) (iPageIdx+1) + " of " + (string) (iMaxPageIdx + 1);     //Open a Listen    dialogChannel = (integer)llFrand(DEBUG_CHANNEL)*-1;    dialogHandle = llListen(dialogChannel, "", "", "");     // Call Function and set a time out for the listen    llDialog(av, msg , order(lstDialog), dialogChannel);    llSetTimerEvent(30.0);} close_menu(){    llSetTimerEvent(0);    llListenRemove(dialogHandle);} //=============================== RUN TIME =========================================// default{    touch_start(integer total_number)    {        key id = llDetectedKey(0);        close_menu();        doDialog(id);    }     listen( integer channel, string name, key id, string msg )    {        close_menu();         if(msg == "< MAIN >")        {            iPageIdx=0;            doDialog(id);            return;        }         else if(msg == "<< PREV")        {            iPageIdx--;            doDialog(id);        }         else if(msg == "NEXT >>")        {            iPageIdx++;            doDialog(id);        }         else if(llGetInventoryType(msg) == 6)        {            llRezObject(msg, llGetPos() + vRezPos, ZERO_VECTOR, llGetRot(), 0);        }         else if(~llListFindList(lstShortName, [msg]))        {            integer idx = llListFindList(lstShortName, [msg]);            llRezObject(llList2String(lstShortName,idx+1), llGetPos() + vRezPos, ZERO_VECTOR, llGetRot(), 0);        }        // Example of Extra Button        else if(msg == "-CONTENTS-") llSay(0, "There are " + (string) llGetInventoryNumber(iInvType) + " items in this container");    }     timer()    {        close_menu();    }} //=============================================================================================//
  3. I needed a dialog menu that has extra command buttons on the first page but not on the rest.
    I went searching in the library and the forums, but everthing I found was horribly complex.

    I came up with the following function which seems to work ok. It handles static buttons on every page, special buttons on first page and cycles through pages.
    I am open to comments and constructive critism to make it better. I don't script all that often these days
    Before anyone points out I havn't removed the listen, it was deliberate to aid clarity.

    The code below is a complete script, just copy and paste.

    // Dialog function to handle extra command buttons on first page of menu
     
    // GLOBALS
    integer chDialog;
    integer iPageIdx;
    list lstAlpha = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R"];
     
    // USER FUNCTIONS
    list order_buttons(list buttons) {
    	return llList2List(buttons, -3, -1) + llList2List(buttons, -6, -4) + llList2List(buttons, -9, -7) + llList2List(buttons, -12, -10);
    }
     
    dialogUser(key av)
    {
    	// Buttons that will appear on every page - normally navigation buttons
    	list lstStatic = ["<< PREV","MAIN","NEXT >>"];
    	integer iSpaces = 12-llGetListLength(lstStatic);
     
    	// Extra buttons that will appear only on the first page
    	list lstExtraBtn = ["Special 1","Special 2","Special 3"];
    	integer iAdjSpaces = llGetListLength(lstExtraBtn);
     
    	// Dynamic buttons - read from list modify script to read from inventory
    	integer iDynLen = llGetListLength(lstAlpha);
     
    	// Calculate menu last page index
    	integer iMaxPageIdx = (llFloor((float) (iDynLen + iAdjSpaces) / (float) iSpaces));
     
    	// Keep pages within limits
    	if (iPageIdx < 0 ) iPageIdx = iMaxPageIdx;
    	else if (iPageIdx > iMaxPageIdx) iPageIdx = 0;
     
    	// Build the button list
    	list lstDialog = [];
    	integer idxSlot = iPageIdx*iSpaces;
     
    	integer i;
    	if((0 == iPageIdx) && ([] != lstExtraBtn)) {
    		for(i = idxSlot; (i < idxSlot+iSpaces-iAdjSpaces) && (i <= iDynLen-1 ); i++)
    			lstDialog += [llList2String(lstAlpha,i) ];
    		lstDialog += lstExtraBtn;
    	}
     
    	else
    		for(i = idxSlot; (i < idxSlot+iSpaces) && (i <= iDynLen-1 + iAdjSpaces); i++)
    			lstDialog += [llList2String(lstAlpha,i-iAdjSpaces) ];
     
    	lstDialog += lstStatic;
     
    	// Finally give the user a dialog using order_buttons function to make it sort properly
    	llDialog(av, " \n Choose an Option", order_buttons(lstDialog), chDialog);
    }
     
    // STATES
     
    default
    {
    	state_entry(){
    		chDialog = ( -1 * (integer)("0x" + llGetSubString((string)llGetKey(),-7,-1)) );
    		llListen( chDialog, "", NULL_KEY, "" );
    	}
     
    	touch_start(integer num_detected) {
    		dialogUser(llDetectedKey(0));
    	}
     
    	listen( integer channel, string name, key id, string msg ) {
    		key k = id;
    		if(msg == "<< PREV") {
    			iPageIdx--;
    			dialogUser(k);
    		}
    		else if(msg == "NEXT >>") {
    			iPageIdx++;
    			dialogUser(k);
    		}
    		else if(msg == "MAIN") {
    			iPageIdx=0;
    			dialogUser(k);
    		}
    		else llOwnerSay("You chose : " + msg);
    	}
    }
    <\lsl>
  4. Yes its a polygon - that is currently the only way to approximate a curve with the current toolset.

    failed ............yes and no

    The primary objective was to produce a visually smooth turn rather than a sharp right angle.....it does.

    The assumed result that it would finish where it was intended to... is a fail.

    The magnitude of error is not massive and can be corrected by reference to a fixed point following the turn.


    I had the opportunity to bring this question up at Simon Linden's office hour yesterday.
    It was mentioned that "drift" had been noticed even in simple lateral motion...though this is difficult to repro.
    There was some suggestion that region time dilation might be affecting the movement during a given frame.

    Like many things in SL...half the fun is working out how to get around its shortcomings


    As for the bad computation....thats why I asked for help :matte-motes-smile:

  5. All my previous vehicles have used physical motion, but with the arrival of llSetKeyframedMotion I thought I would experiment a little.
    Linear motion is good, and region crossings present no unusual problems.

    Turns are ugly...the strength/damping effect of llRotLookAt on non-physical objects is relatively ineffective.

    I have tried to smooth the turns by creating a keyframe list from interpolated points on an arc.

    This is in the form of a function that will eventually be called from a command in a route notecard.
    The gTurnLR is simply a hack to get it working for testing

    The script works...but there is a certain amount of drift by the time the object reaches its final point.
    I had ascribed this to rounding errors, but now I am not so sure. There is quite a variance in results.

    I plan most turns to be 90 degrees..but this script will make an object complete a circle.

    If you want to test, you will need a mesh enabled viewer so you can set a normal prim to a convex hull, and access to a region running Magnum.

    I struggle with rotations...so I am open to any suggestions on refining this script, or taking a completely different approach to the problem.

     

    integer gTurnLR;      //-1 for clockwise (right turn) 1 for anticlockwise (left turn)
    list glstFrames;       //list of keyframes
     
    makeKeyFrames(vector vStepPos, rotation qStepRot)
    {
        glstFrames = [];
        float fRadius  = 10.0;  //radius of turn
        integer iSteps = 24;    //variable depending on radius of turn - experiment!
        float fTime = 1.0;      //KeyFrame step time interval
        rotation vRotArc       = llEuler2Rot( <0.0, 0.0, (TWO_PI/iSteps)*gTurnLR>); // step angle -radians
        vector   vPosOffset     = <0.0, fRadius*gTurnLR, 0.0>;                      // radius of turn
     
        integer i;
        for(i=0;i< iSteps ;i++)  // iSteps/4 for a 90 degree turn
        {
            vector vKeyFrame = vStepPos;
            // http://wiki.secondlife.com/wiki/Rotation
            vStepPos = vStepPos + (vPosOffset - vPosOffset * vRotArc) * qStepRot;
            qStepRot = vRotArc * qStepRot;
            vKeyFrame = vStepPos - vKeyFrame;       //relative to last frame
            glstFrames += [vKeyFrame, vRotArc, fTime];
        }
    }
     
     
    default
    {
        touch_start(integer total_number)
        {
            gTurnLR = -1;       //clockwise turn - actual value from route notecard
            makeKeyFrames(llGetPos(),llGetRot());
            llSetKeyframedMotion(glstFrames, [KFM_MODE, KFM_FORWARD]);
        }
    }

     

    Grr...trying to edit a post is "challenging"

×
×
  • Create New...