Jump to content

Quistess Alpha

Resident
  • Posts

    3,764
  • Joined

  • Last visited

Posts posted by Quistess Alpha

  1. 2 hours ago, MadamePuqe said:

    dialog(integer channel, string name, key id, integer buttons)
    {
        // Check if the received dialog message is on the poseMenuChannel
        if (channel == poseMenuChannel)
        {
            // Process the dialog message (e.g., start animation)
            llStartAnimation(name);
        }
    }

    compiles fine for me as long as the global variable poseMenuChannel is declared. Check that your global variable declaration exists, and is exactly the same spelling as in the function.

  2. To rotate the child prim 20 degrees on it's own z-axis:

    rotation Z20 = llAxisAngle2Rot(<0,0,1>,20*DEG_TO_RAD);
    // for example 
    integer link = 2;
    rotation rChild = llList2Rot(llGetLinkPrimitiveParams(link,[PRIM_ROTATION]),0);
    llSetLinkPrimitiveParamsFast(link,
    [   PRIM_ROT_LOCAL,  (Z20*rChild)/llGetRootRotation()
    ]);

    for root's axes, swap the order of the change and orientation rotations:

    [   PRIM_ROT_LOCAL,  (rChild*Z20)/llGetRootRotation() 

     

  3. For SL at least "standard orientation" for objects is for them to be "facing" the +x direction, with +z being up and +y being left.

    When faced with a situation where the object you're working with isn't "right side up" and "facing" due east when its orientation is set to ZERO_ROTATION, I usually suggest just making it so: link your object to an invisible root prim, and rotate it so that the child prim is in standard orientation. It saves a lot of headaches.

    That said, if you ~absolutely must work with an object in non-standard orientation, you will probably need to determine its "intrinsic rotation" which I'll call rINTRINSIC. Rotate your object so that it's in standard orientation, and confirm that when the object is selected in edit mode, with the 'move' radio button on and "snap: World" checked,  the red arrow and the object point in the same direction and the blue arrow points "up". Then, you can use the three rotation numbers in the object tab of the build window to set rINTRINSIC, and use that to find the axes of the object that point forward, up and left:

    rotation rINTRINSIC;
    
    vector UP;
    vector FWD;
    vector LEFT;
    
    default
    {   state_entry()
        {   rINTRINSIC = llEuler2Rot(DEG_TO_RAD*<0,270,0>); // X,Y,Z numbers from the build menu.
            UP = <0,0,1>/rINTRINSIC;
            FWD = <1,0,0>/rINTRINSIC;
            LEFT = <0,1,0>/rINTRINSIC;
        }
    }

    Preferably though, you should set rINTRINSIC based on the axes of the object. When the object is in standard orientation as above and if your object has a 'sane' intrinsic orientation, changing between snap:world and snap:local will only change the colors of the arrows, the local axes are still aligned with the world axes.

    rotation rINTRINSIC;
    
    // which local arrow points up?
    vector UP = <1,0,0>; // for example, the red arrow (representing <1,0,0> or +x) in snap:local points in the same direction as the blue arrow in snap:world, up.
    
    // which local arrow points forward? for an animal, which arrow points with its eyes, for a vehicle, where would it move?
    vector FWD = <0,0,-1>; // for example, the blue arrow (+z) in snap:local is aligned with the red arrow in snap:world, but points opposite direction (so add a minus sign). 
    
    // which local arrow points left?
    vector LEFT = <0,1,0>; // for example, the green arrow (+y) is exactly the same between snap:world and snap:local.
    
    default
    {   state_entry()
        {   rINTRINSIC = llAxes2Rot(FWD,LEFT,UP);
        }
    }

    With those values in-hand, here are a few functions that might be helpful. (I'm writing them as functions for clarity, but you can of course in-line the simpler ones)

    vector dirFacing() // in global coordinates, what direction is this object 'facing'?
    {   return FWD*llGetRot();
    }
    integer WhichSideIsUp() // 0 for 'right-side-up, 1 for upside-down, based on the value of global variable UP
    {   return ( (UP*llGetRot())*<0,0,1> ) < 0;
        // more reasonable than testing the object's roll.
    }
    integer AmLookingDown() // 1 for looking below horizon, 0 for looking up
    {   return ( (FWD*llGetRot())*<0,0,1> ) < 0;
    }
    
    uSetRotYPR(vector v) // 'Yaw', 'Pitch' and 'Roll', with respect to rINTRINSIC
    {   
        rotation ypr = 
            llAxisAngle2Rot(<1,0,0>,v.z) *
            llAxisAngle2Rot(<0,1,0>,v.y) *
            llAxisAngle2Rot(<0,0,1>,v.x);
        llSetLinkPrimitiveParamsFast(0,
        [   PRIM_ROTATION, (ZERO_ROTATION/rINTRINSIC)*ypr
        ]);
    }
    
    vector uGetRotYPR()
    {   rotation q = rINTRINSIC*llGetRot();
    // https://community.secondlife.com/forums/topic/471561-urot2euler/?do=findComment&comment=2293046
        vector v;
        // x-axis
        float s = 2 * (q.s * q.x + q.y * q.z);
        float c = 1 - 2 * (q.x * q.x + q.y * q.y);
        v.z = llAtan2(s, c);
        // y-axis
        s = 2 * (q.s * q.y - q.z * q.x);
        if (llFabs(s) >= 1)
            v.y = (-1 | (s < 0)) * PI_BY_TWO;
        else
            v.y = llAsin(s);
        // z-axis
        s = 2 * (q.s * q.z + q.x * q.y);
        c = 1 - 2 * (q.y * q.y + q.z * q.z);
        v.x = llAtan2(s, c);
        return v;
    }
    
    uLookAt(vector direction, float roll) // to look at a specific position in-world, set direction to (position-llGetPos())
    {   rotation look = // inline YPR2Rot() 
            llAxisAngle2Rot(<1,0,0>, roll) *
            llAxisAngle2Rot(<0,1,0>, 
                -llAtan2(direction.z,llVecMag(<direction.x,direction.y,0>)) )*
            llAxisAngle2Rot(<0,0,1>, llAtan2(direction.y,direction.x) ) ;
            
        llSetLinkPrimitiveParamsFast(0,
        [   PRIM_ROTATION, (ZERO_ROTATION/rINTRINSIC)*look
        ]);
    }

     

    • Thanks 2
  4. 15 minutes ago, Love Zhaoying said:

    Just so they could test the process?

    Yeah exactly, That and not really relevant for my specific use-case, but if it was a one-use item like a firework or something, that goes off when rezzed, it would be neat if the owner/maker could easily switch it from safe for-sale mode, to detonation mode by buying the object. Solution is of course to have a separate vendor and sale object, but being able to detect if an object was just bought could simplify things.

  5. 16 minutes ago, Wulfie Reanimator said:

    Does the script need to "do its thing" in the bought copy, or the original?

    In the bought copy. I briefly tested the money event, but that seems to only work for paying the object, not buying it via the simple self-vendor system.

    16 minutes ago, Wulfie Reanimator said:

    If the copy, you can store the current owner in state_entry and check for a new value on CHANGED_OWNER.

    That's exactly what I did, (and even had the original and bought copies do different things) but just as a mostly academic exercise, it would be kind of nice if the system worked when the owner buys their own object. Using the money event and selling a copy of the object from the vendor's inventory like a normal vendor would do that, but that would require more tedious setup from the seller.

  6. Ciao Katie, e bienvenudos a SL!

    In order to sit someone on something, you need to use one of two systems, either RLV or an experience.

    RLV isn't enabled on all viewers, and it's probably a whole topic on its own, but, to ask someone who's wearing a RLV relay to sit on an object, you might use something like:

    sit_on_prim(key avatar, key object)
    {
       llRegionSayTo(avatar,-1812221819, "sit,"+(string)avatar+",@sit:"+(string)object+"=force");
    }

    An experience is a bit different. In order to have an experience you can add scripts to, you need to make one as a premium member, or be a part of a group which gives you access to one.

    assuming you can compile a script to an experience, you also need to have the script ask permission from the people it wants to sit, and the script can only sit people on the object the script is in. the wiki has a decent example for one person: https://wiki.secondlife.com/wiki/LlSitOnLink

    either way, to sit more than one person, you just need to iterate through the list of all people and sit them.

  7. If I'm reading it right, it just expects an object named smf.gallows anchor to say something on Key2Chan(llGetKey()) on a timer. particles and some other things seem to be handled by another script in your set that you didn't post, but my best guess from this info alone is just:

    // anchor script?
    integer Key2Chan(key ID) 
    {
        return 0x80000000 | (integer)("0x"+(string)ID);
    }
    float spam_rate = 0.2;
    
    integer isOn = TRUE;
    integer chan;
    default
    {  state_entry()
       {
          chan = Key2Chan(llGetKey());
          llSetTimerEvent(spam_rate);
       }
       touch_start(integer n)
       {  llSetTimerEvent( (isOn=!isOn)*spam_rate );
          if(isOn) llSay(0,"On");
          else llSay(0,"Off");
       }
       timer()
       {   llSay(chan,"something, doesn't seem to matter what?");
       }
    }

    Honestly, since your noose already has a sensor_repeat in it, it would be better to rescript it to handle the timing on its own, rather than relying on messages from the anchor to clock its pull. you could have it read the description of the anchor to tell if it's supposed to be on or not.

  8. 7 minutes ago, Bavid Dailey said:

        Enrico Pieranunzi/Thomas Fonnesb%3*%1-k/Andr%3* Ceccarelli - The Heart of a Child

    That looks somewhat like URL encoding, which llUnescapeURL would fix, but doesn't look like that fixes that specific example.

    • Like 1
  9. 18 hours ago, LissomePrey said:

    pitfalls to avoid

    Try not to mix movement methods

    • 'Omega': definitely isn't what you want, because it can only do fixed speed rotation well, and ideally a continuous circle that rarely if ever changes. can lead to different perception for different observers.
    • Physics based movement: could be interesting to try, but you don't get a lot of specific control over how it works.
    • KFM: probably your best option for a final product, but it can be quite finicky to get the data it needs in the format it wants, and to control its numerical drift. For anything that's set to run for a long time, turn off KFM and set it to a fixed position and rotation on occasion, maybe once an hour.
    • repeated llSetLinkPrimitiveParamsFast: can work, and could be useful for prototyping different path equations  before dealing with reformatting the data for KFM.

    Be aware of your object's center, a sanely built object will help immensely with how you script it. For a pendulum, you probably either want the rod to be the root prim and built as a cylinder with 'slice' set to <0,  0.5, 0>, and only control the pendulum through its rotation, OR cast a particle stream with PSYS_PART_LINEAR_MASK between the anchor and weight (would have to be separate objects if using KFM) as a stand-in for the rod.

  10. So someone asked me to make a few tweaks to a really old script for an item that is set to sale. Ideally, the script should do a thing and delete itself the first time it's rezzed after having been bought when the original in-world copy by the seller has 'for sale' (copy) enabled in its build parameters. Is there a way to do that in a way that would also work if the seller buys their own object?

    Interesting aside: when a copy of an object is bought by not-the-owner, a changed (CHANGED_OWNER) event is triggered in both the inventory copy and the one in-world, even though in the in-world copy, llGetOwner() hasn't changed since state_entry().

    • Like 1
  11. 1 hour ago, musichero said:

    When using web content on PM is it possible to detect touches of hyperlinks that open new windows or tabs?  These seem to do nothing and it would be nice to process them in some way. Thanks!

    There is no way to change that behavior of the on-prim browser from within the media-content (webpage) or media params. The best you can do is try and direct the link to your script, from which you can bounce the link back at the user to open in a tab or wherever. That's a bit tricky because media doesn't know who's interacting with it, but as a proof of concept:

    (not a complete script, boilerplate media stuff removed)

    // in the 'webpage' :
    <a href="./redir/www.duckduckgo.com"> redirect </a> // the '.' at the front is verfy important.
    
      // ....
        http_request(key ID, string Method, string Body)
        {   if(ID==ghRequestURL)
            {   gsURL=Body;
                llSetLinkMedia(LINK_THIS,2,
                    [   PRIM_MEDIA_HOME_URL, gsURL+"/home", // 'home' is arbitrary, but you must at a minimum add '/' to the end of the suppliued URL in order for local links (href ="./something") to correctly parse back to your prim's namespace.
                        PRIM_MEDIA_CURRENT_URL, gsURL+"/home",
                        PRIM_MEDIA_AUTO_PLAY, TRUE,
                        PRIM_MEDIA_PERMS_CONTROL, PRIM_MEDIA_PERM_NONE, // don't show nav-bar.
                        PRIM_MEDIA_PERMS_INTERACT, PRIM_MEDIA_PERM_NONE // does not actually prevent loading different links, just causes media to reload the previous page after any link is touched. PRIM_MEDIA_PERM_ANYONE would be more sane for this use-case.
                    ]);
            }else
            {   string PATH = llGetHTTPHeader(ID,"x-path-info");
                if("/home"==PATH)
                {
                    llSetContentType(ID,CONTENT_TYPE_XHTML);
                    llHTTPResponse(ID,200,gNCText); // where gNCText is the content of the webpage containing the link
                }else
                {   llHTTPResponse(ID,204,"no content"); // don't confuse the browser.
                    if("/redir/"==llGetSubString(PATH,0,6))
                    {   llSensor("","",AGENT,7.5,PI); // if only owner can use the media, like a HUD, would not need to sensor.
                        gsURLRedir = "http://"+llDeleteSubString(PATH,0,6);
                    }
                }
            }
        }
        sensor(integer n)
        {   while(~--n)
            {   llLoadURL(llDetectedKey(n),"open a new tab?",gsURLRedir);
            }
        }

     

  12. I like vim, but I'm a linux nerd. Surprisingly my distribution came with LSL syntax highlighting. it's out of date, but the syntax color file is very human-readable, I've not bothered updating it though. . .

    • Like 2
    • Thanks 1
  13. 2 hours ago, Jenna Huntsman said:

    I'm not a great mathematician

    For this sort of thing, it's more about information flow than the math. Figure out what information you have, what information you need, and then find a way to convert one to the other.

    Thinking about it backwards starting with the 'want': a spherical arc between 2 points needs a center, then once you have that, there are probably several ways to turn that into an arc, but fixing the thing I wrote years ago to include a center rather than just assuming <0,0,0> is the center:

    // 2 points, a center and a list of percents along the circular arc to return. percent=0->A, percent=1.0->B
    // assumes A,center,B is an isosceles triangle, which in general it won't be.
    list sphere_interpolate(vector A,vector B, vector center, list percents)
    { // return A when percent ==0, B when percent==1
      
      A = A-center;
      B = B-center;
      
      rotation rot = llRotBetween(A,B);
      vector axis = llRot2Axis(rot);
      vector angle = llRot2Angle(rot);
      
      list rets;
      integer i = llGetListLength(percents);
      while(~--percents)
      {   //vector ret = center + A * llAxisAngle2Rot(axis,percent*angle);
          rets+= ( center + A*llAxisAngle2Rot(axis,angle*llList2Float(percents,i)) );
      }
      
      return rets;
    }

    but if I'm understanding you right, we don't know what point is the center of our hypothetical sphere, we only have a normal to the plane, and a radius (or some other measure of the curvature. After looking at some quick sketch diagrams, the most intuitive and easy to work with piece of information is the inner angle of the curve). Would need some debugging, but my first try would probably go something like:

    // UNTESTED!
    // angle in radians, TWO_PI -> return midpoint of A&B; PI -> return center that makes a quarter arc.; 0-> math error (point at infinity).
    vector find_center(vector A, vector B, vector normal, float angle)
    {
       vector average = 0.5*(B+A); // point between B&A.
       vector delta = average-A; // could use (B-A)/2, but this makes a bit more sense since we'll use average for the last step.
       
       float halfChord = llVecMag(delta); // half the distance from A to B.
        
       float dist = halfChord*llTan(0.5*angle); // distance from average to center
      
       vector cross = llVecNorm(normal%delta); // should point from midpoint of B&A to center.
       return average + dist*cross;
       
    }

    Big picture idea is that whenever you have a normal vector, you almost always want to take a cross product with some vector that lies in the plane of interest, giving you 2 vectors in that plane, from which you should be able to make any other point in that plane as ( (some point in the plane) + (a weighted sum of those 2 vectors) ).

    • Thanks 2
  14. I thought I mentioned this in my rambles above, but I'm not finding it in a brief skim, and it's conceptually important:

    Vectors can represent both positions in-world, and global changes to position. To 'apply' a change to a position in-world, you simply add a change vector to a position vector, and set the position of something to that new position. For example:

    llSetPos(llGetPos()+<1,0,0>); // move 1 unit in the x-direction.

    Rotations can be used in a similar way, but with a few important differences:

    • rotations are less trivial to express on their own. One of the best ways of creating a rotation 'from scratch' is to use llAxisAngle2Rot()
    • rotations are multiplied instead of added together.
    • the order of multiplication matters.

    that last point can be confusing at first, but once you understand what each order does, it can be very useful: In LSL, when you multiply in the order orientation*change, you rotate around the world x,y,z axes (like you would see when editing an object with 'snap world' checked); when you multiply in the order change*orientation, you rotate around the object's intrinsic axes (like you would see when editing an object with 'snap local' checked)

    As a practical example, say you're scripting something like a turret that can aim up and down and move left and right. To aim up you might use something like

    llSetRot(llAxisAngle2Rot(<0,1,0>,-15*DEG_TO_RAD) * llGetRot() ); // apply local rotation on y axis

    to move the gun up around its y axis, independent of whatever direction it's facing, but to aim left/right you would use:

    llSetRot(llGetRot() * llAxisAngle2Rot(<0,0,1>, 15* DEG_TO_RAD) ); // apply global rotation on z axis

    Of course, for an actual turret, you might want to apply the rotations to different links, but the same principals apply.

    • Thanks 1
  15. It shouldn't be impossible to do what you're looking for, or even particularly difficult, but yeah, there's really no telling what no-mod scripts might be doing. you'd probably need/want something written from scratch.

    25 minutes ago, xXSuicidalIdolXx Candycane said:

    so if I have 1 prim I need to make another which would make 2, expect you like those together then you add the sit targets and adjust from there? sorry just a bit confused but up for the challenge.

    I would actually recommend 3 prims, the first or "root" prim defines the center of rotation for llTargetOmega(). if an avatar sits on the root prim while it's spinning and the sit-target doesn't have a large offset, they will spin in place like a balerina, which doesn't sound like what you want. You can of course set a different offset in the script, but for things like this, I usually find it's easier to change the position and rotation of a prim than to get your head around the numbers in the script.

    • Thanks 1
  16. 25 minutes ago, xXSuicidalIdolXx Candycane said:

    it says there is no place to sit

    The 'correct' solution is to add extra prims to your object, one for each person who is going to sit on it, and then use llLinkSitTarget() to set a sit target for each of the prims, for example:

    default
    {   state_entry()
       {  // optionally prevent avatars from sitting on the root prim rather than designated seats:
          llSetLinkPrimitiveParamsFast(1,[SCRIPTED_SIT_ONLY,TRUE]);
      
          llLinkSitTarget(2, <0,0,0.1>, ZERO_ROTATION);
          llLinkSitTarget(3, <0,0,0.1>, ZERO_ROTATION);
          // if more sit target prims, repeat similarly. . . 
       }
    }

    then, when sitting on the link, avatars will sit with an orientation and position determined by the rotation and position of the new prims. editing the prims while an avatar is seated on it will NOT adjust the seated avatar, to make adjustments with this method, the avatar has to stand up and sit back down after any change.

    --

    A less correct solution would be to make sure you are 'outside' (not within the convex hull of some mesh or prim) and make your single prim something relatively large that 'looks like' it has room for more than one person to sit on. Sitting without designated sit-targets is possible, but IMO rather fiddly.

    • Thanks 1
  17. 12 hours ago, Phate Shepherd said:

    I don't want the script to attach the linkset to the user during the configure process.

    As far as I know, the only way to set the default attach point is to actually attach to an avatar. Closest thing would be to llAttachToAvatar (NOT the temp version, which would destroy the object) then llDetachFromAvatar immediately after attached in the attach() event. Unfortunately it is also impossible to 'drop' (detach, then rez self on ground) an object via script.

    <technical meandering> It's probable that 'default attach point' is a property of inventory assets, and not the actual objects themselves, which would mean setting it without it being attached would be basically impossible to implement. </technical meandering>

    • Like 3
  18. This was kinda fun to think through.

    integer within_hull(vector test, list points)
    {   integer ind_a= 0;
        integer ind_b= 1;
        integer ind_c= 2; // indexes for 3 points from points.
        vector a = llList2Vector(points,ind_a);
        vector b = llList2Vector(points,ind_b);
        vector c = llList2Vector(points,ind_c);
        integer len = llGetListLength(points);
        // for all pairs of 3 points a,b,c:
        while(ind_c<len)
        {
            vector normal = (b-a)%(c-a);
            integer side = (test-a)*normal >0; // on which side of the plane defined by a,b,c is test?
            integer outside = TRUE; //early return variable.
            integer ind_p = len;
            // for each other point p:
            while(~--ind_p)
            {   if(ind_p!=ind_a && ind_p!=ind_b && ind_p!=ind_c)
                {   vector p = llList2Vector(points,ind_p);
                    if( (((p-a)*normal)>0) != side)
                    {   //llOwnerSay("point del");
                        // p is on opposite side from test:
                        // remove it from list.
                        points = llDeleteSubList(points,ind_p,ind_p);
                        // fix indexes!
                        --len;
                        if(ind_c>ind_p) --ind_c;
                        if(ind_b>ind_p) --ind_b;
                        if(ind_a>ind_p) --ind_a; 
                    }else
                    {   // it is possible test is within points:
                        outside = FALSE;
                    }
                }
            }
            if(outside) // there were no points on the same side of a,b,c as test, therefore it is impossible it is within the cloud:
            {   return FALSE;
            }
            
            // set indexes for next loop:
            if((ind_a+1)<ind_b)
            {   a = llList2Vector(points,++ind_a);
            }else
            {   a = llList2Vector(points,ind_a=0);
                if((ind_b+1)<ind_c)
                {   b = llList2Vector(points,++ind_b);
                }else
                {   b = llList2Vector(points,ind_b=1);
                    c = llList2Vector(points,++ind_c);
                }
            }
            //llOwnerSay("Debug: "+llList2CSV([ind_a,ind_b,ind_c]));
        }
        // either the initial point list had fewer than 4 points, or test is within the convex hull:
        return TRUE;
    }
    
    
    default
    {
    
        touch_start(integer total_number)
        {
            // for debug, determine if this object is within the convex hull of all nearby objects named "Test Point"
            llSensor("Test Point","",PASSIVE,7.0,PI);
        }
        sensor(integer n)
        {   llOwnerSay("Found "+(string)n+" test points.");
            list points;
            while(~--n)
            {   points+=llDetectedPos(n);
            }
            integer inside = within_hull(llGetPos(),points);
            if(inside)
            {   llOwnerSay("Within hull!");
            }else
            {   llOwnerSay("Out of bounds!");
            }
        }
    }

    I'm pretty sure it works, but I didn't test it super rigorously.

    • Like 1
  19. As long as we're bringing up the dead, you can also do it with a media face. Media 'knows' where you're touching if you've touched it once to let it grab your mouse. I know I posted a demo long time passing. . . but easier to find it in my inventory than on the forums so. . .

    Quote

    script in a 0.5x0.5x0.01 sized cube as the root prim, with a 0.0625 sized cube as a child prim:

    key ghRequestURL;
    string gsURL;
    
    string gsMain_header =
    "<!DOCTYPE xhtml>
    <html xmlns=\"http://www.w3.org/1999/xhtml\">
    <head><style>
    .grid-container {
      display: flex;
      flex-wrap: wrap;
      background-color: teal;
      width: 1000px;
      height: 1000px;
      margin: 12px auto;
    }
    .grid-container div {
      background-color: rgba(255,255,255, 0.8);
      border: 1px solid rgba(0,0,0, 0.6);
      width:  123px;
      height: 123px;
    }
    .grid-container div:hover {
      background-color: rgba(255, 128, 128, 0.4);
    }
    </style></head>
    <body>";
    string gsMain_body;
    string gsMain_footer = 
    "
    <script type=\"text/javascript\">
    //<![CDATA[
    
    function postB(val) {
      var xhttp = new XMLHttpRequest();
      xhttp.open(\"POST\",val+\"button\");
      xhttp.send();
    } 
    
    //]]>
    </script>
    </body>
    </html>";
    
    generate_page(string page, key ID)
    {   
        //llOwnerSay("Debug: "+":"+page+":"+llGetHTTPHeader(ID,"x-remote-ip"));
        if(llGetSubString(page,0,0)=="/")
        {   page = llDeleteSubString(page,0,0);
        }
        if(page=="home")
        {   llSetContentType(ID,CONTENT_TYPE_XHTML);
            llHTTPResponse(ID,200,
                    gsMain_header+
                    gsMain_body+
                    gsMain_footer);
        }else if(llGetSubString(page,-6,-1)=="button")
        {   
            integer button = (integer)page;
            integer row = button/8;
            integer col = button%8;
            vector scale = llGetScale();
            float inc_X = scale.x/8;
            float inc_Y = -scale.y/8;
            float off_X = -3.5*inc_X;
            float off_Y = -3.5*inc_Y;
            vector setPos = <off_X+(col*inc_X),off_Y+(row*inc_Y),inc_X/2+0.005>;
            
            llSetLinkPrimitiveParamsFast(2,[PRIM_POS_LOCAL,setPos]);
            
            llHTTPResponse(ID,204,"No Content.");
        }
        else
        {   llSay(0,"Unknown page: "+page);
            llHTTPResponse(ID,404,"Not found.");
        }
    }
    
    
    default
    {   state_entry()
        {   ghRequestURL = llRequestURL();
            
            gsMain_body = "<div class =\"grid-container\">";
            integer i;
            for(i=0;i<64;++i)
            {   gsMain_body+="<div onmouseover=\"postB("+(string)i+")\">"+
                    /*"button_text"+*/"\</div>\n";
            }
            gsMain_body+="</div>";
            llOwnerSay((string)llGetFreeMemory());
    
        }
        on_rez(integer i)
        {   llReleaseURL(gsURL);
            ghRequestURL = llRequestURL();
        }
        changed(integer c)
        {   if(c&(CHANGED_REGION_START|CHANGED_REGION))
            {   llReleaseURL(gsURL);
                ghRequestURL = llRequestURL();
            }
        }
        http_request(key ID, string Method, string Body)
        {   if(ID==ghRequestURL)
            {   llOwnerSay("Got URL: "+Body); // debug
                if (Method == URL_REQUEST_DENIED)
                {   llOwnerSay(Method);
                }else //if (Method == URL_REQUEST_GRANTED)
                {   gsURL=Body;
                    llSetLinkMedia(LINK_THIS,0,
                    [   PRIM_MEDIA_HOME_URL, gsURL+"/home",
                        PRIM_MEDIA_CURRENT_URL, gsURL+"/home",
                        PRIM_MEDIA_AUTO_PLAY, TRUE,
                        PRIM_MEDIA_PERMS_CONTROL, PRIM_MEDIA_PERM_NONE, // don't show nav-bar.
                        PRIM_MEDIA_PERMS_INTERACT, PRIM_MEDIA_PERM_NONE
                        // * when an in-world MoaP does not have 'interact' permissions, their
                        // browser will immediately request the 'current URL' after navigation
                        // to a ' new page' after it recieves a response, if that response
                        // does not have status 204 (no content).
                        // * if the in-world MoaP ~Does have interact perms, navigation to a 
                        // valid page (not status 204) will cause the CURRENT_URL to change
                        // to the new page and other MoaP instances in range should
                        // avigate to that URL as well.
                    ]);
                }
            }else
            {   
                string path = llGetHTTPHeader(ID,"x-path-info");
                generate_page(path,ID);
            }
        }
    }

     

    Too complicated for a simple use-case like in the OP, but it's a fun proof-of-concept.

  20. 10 hours ago, Vzlenscu said:

    Wondering if there is a "resync" option, where the host can toggle everyone back to the same point in the movie?

    Not to toot my own horn, but the tv script I wrote (see above) is a minimally viable example of doing exactly that (pause/restart, or skip back/forward all resync everyone.). As for how to do that with any other specific product. . . do they have a support group or anything?

    • Like 1
×
×
  • Create New...