Jump to content

Quistess Alpha

Resident
  • Posts

    3,764
  • Joined

  • Last visited

Posts posted by Quistess Alpha

  1. -- irrelevant tangent --

    Certain high-frequency events like touch() and control() jump-queue and replace their last instance such that there is only ever one event of the type in the queue (preventing it from overflowing too easily). consider

    default
    {   touch_start(integer n)
        {   llSleep(1.0); }
        touch(integer n)
        {   llOwnerSay((string)llGetTime()); }
    } // touching this object for less than 1 second produces only 1 line of output.

     

    21 minutes ago, Love Zhaoying said:

    When you remove a listener via llListenRemove(), or inactivate it via llListenControl(), would we say it is "blocking" events? Or not, since the event will never occur?

    That's a bit of a semantic question, but I'd reserve the term 'blocking' only for scenarios where an event sleeps or loops for an unordinarily long time. It's the difference between closing your eyes and turning off your monitor. Maybe if you want to be pedantically more correct, you could say llListenControl "masks" the event?

    • Thanks 1
  2. 2 hours ago, elleevelyn said:

    I should spend more time learning about how LSL actual works rather than just assume it works like other interrupt event driven languages that I am used too

    single threaded, with a fifo queue that holds 64? events. Ergo, nothing can happen 'at the same time' in a single script.

    • Thanks 2
  3. 18 hours ago, Wulfie Reanimator said:

    There's no limit, aside from the total memory limit.

    which last time I checked was something like 100 megabytes. but there are LSL functions to check how much you have left in your own experience.

  4. 1 hour ago, Wulfie Reanimator said:

    The bad news is that I can't easily test something funny like caching thousands of notecards (to see if I can force a notecard to get uncached) since the beta grid is pretty inactive. Any clever ideas for something like that?

    I'm too lazy to do tests, but editing a notecard should effectively simulate uncaching it if you read it by name? Put an unneccessary sleep in the read loop to give yourself time to do the edit while the loop is still running.

    • Like 1
  5. 8 hours ago, Bleuhazenfurfle said:

    A practical example I did quite some years ago, was a "dots hud", that puts a little dot over everyone's head.

    The HUD for Nanite system's main product does this; they call it 'laserline'. Anecdotally, I read they're not putting that feature into their total-overhaul of the system because it takes up too much script resources for a relatively unnecessary feature. Might be interesting to see if they reconsider given this new function.

  6. 6 hours ago, RobotGirlOwnedAndControlled said:

    know a way for a script or maybe RLV to change the user's view so it switches to wireframe view?

    The specifications of everything RLV can do (RLVa, which catznip and firestorm use can do some other things, but nothing super major, mostly minor blindfold differences, and payment restriction) is here: https://wiki.secondlife.com/wiki/LSL_Protocol/RestrainedLoveAPI  Wireframe mode is not a supported action.

    Closest thing would be denying textures, which by default turns everything grey or replaces all (albedo/non-material, unclear how it will work with PBR) textures with one specified:

    Quote
    • Turn all the textures blank, except for the avatars : "@camtextures[:texture_uuid]=<y/n>"

    Implemented in v2.9, uuid parameter added in v2.9.20

    When active, this restriction makes the viewer ignore every texture in world, except those on the attachments worn by the avatars, as well as their skins and clothes. The materials are untouched as well so this restriction is good for simulating the fact that the avatar cannot "see", let alone "read" textures, but can still "feel" the world around. Since v2.9.20, the uuid specified as a parameter is used to display the corresponding texture everywhere in-world, rather than a grey texture.

     

    • Thanks 2
  7. Multiply by a power of ten and then cast to an integer? ( llFloor(x) is basically the same as (integer)x )

    float f = 123.456789;
    string round = llInsertString((string)llFloor(f*100),-3,"."); // off the top of my head. // for only 10ths: *10),-2
    // will fail for numbers < .1
    llOwnerSay("Debug, "+(string)f+" -> "+(string)round);

     

    • Thanks 1
  8. I'd argue that it is, but possibly in a way you hadn't considered. Just as you have trouble listening to voice, there are some people who have a hard time with reading (not speaking to any specific condition, but some people are just slow at it, others, could get headaches from reading). While text-to speech solutions exist, It's not hard to imagine such people would much rather just hang out in places where voice is the norm.

     

  9. 40 minutes ago, Qie Niangao said:

    Just in case, you know you can read notecards "remotely" by their UUID, right? That only works if you can get their UUID, though. And only relevant if notecards are the actual intended source of the data to be sent.

    I'm sure you know, but might be important to add that a notecard's UUID changes every time it is modified; so 'remote reading' either requires some way to track the UUID for anyone who might want to read it (KVP would be a good intermediary) or accept that the data in the notecard can never be changed if it is 'hard-coded' in the script.

    • Like 1
    • Thanks 1
  10. 22 minutes ago, PheebyKatz said:

    [Is] using llVolumeDetect() is lighter on resources than using a constantly-running llSensorRepeat()

    Yes. Polling for a result is (marginally) more resource intensive, than an event trigger. llSensorRepeat is annoying kinds in the back of the car asking if we're there yet, llVolumeDetect is the parents just knowing when you get there and saying so. The physics simulator is always running in any case and probably already knows if something collided with your object whether the script asks to be informed about it or not.

    Now, in the very improbable edge-case in which lots and lots of non-avatars are colliding with your object all the time, and you only care about avatars, maybe llSensorRepeat() could hypothetically be better than constantly asking if the thing is an avatar and finding out that it isn't.

    • Like 1
    • Thanks 2
  11. 1 minute ago, beli Lorefield said:

    How can I do that when there are multiple textures?

    Check the box next to 'edit linked' and the radio button next to 'select face. Then, click the left or right arrows that are directly underneath the checkbox for 'edit linked' until only the part you want to modify has the highlight effect around it.

    • Like 2
    • Thanks 1
  12. If I had to guess, perhaps there's some sort of naive show/hide system in the collar that (on some condition) turns everything in the linkset visible. The solution in that case would be to set the texture of the ball to the 100% transparent texture.

  13. If you could make it physical, you could use llPushObject() on it on land that doesn't have push disabled (I do not recommend doing so, but AFAIK it's the only method of affecting an object via script without the script being in that object) . I don't think you can make non-physical no-mod objects physical, but it's not impossible that llRezObjectWithParams allows setting that flag on no-mod objects (if it does, I'd consider that a bug to be honest.)

    • Like 1
  14. 42 minutes ago, Qie Niangao said:

    "Slide" you say? Maybe llDetectedGrab() would work here?

    my assumption would be dragging from inventory onto a face to change the texture (which is very error-prone, I highly recommend doing it via the build window instead).

    And in that case, the problem is that changed(integer c){  if(c&CHANGED_TEXTURE){ /*...*/}  } provides no information to the script about ~how the textures have changed. AFAIK, the only solution would have to be a rather involved script-copy of the information about all texture-parameters of the object in global variables or (much more conveniently) Linkset Data.

  15. This (the script in the OP, which as love suggests, is the same script twice, once unformatted and again with formatting) is verbatim a script I wrote near the end of 2020. It was meant as an example of how to utilize another script which presents a menu to the user to select a target from a list. The (following) script heavily utilizes code originally written by void singer.

    I don't recall if I fixed all the bugs, as doing this in a way that doesn't overflow llDialog's 512 byte limit for the message is somewhat non-trivial, but here is the matching script along with 2 other minor variations that were part of a set at the time: (FWIW, Qss stands for 'Q Sub Script')

    Qss2 Set Target (Agent)

    list gAgentList;
    list gLstMnu; // global list for the menu.
    
    integer gChannel;
    integer gListenHandle; // TODO: make this a strided handler,user list for multiple users.
    
    list uDlgBtnLst( integer vIdxPag ){
        list vLstRtn;
        if ((gLstMnu != []) > 12){ //-- we have more than one possible page
            integer vIntTtl = -~((~([] != gLstMnu)) / 10);                                 //-- Total possible pages
            integer vIdxBgn = (vIdxPag = (vIntTtl + vIdxPag) % vIntTtl) * 10;              //-- first menu index
            string  vStrPag = llGetSubString( "                     ", 21 - vIdxPag, 21 ); //-- encode page number as spaces
             //-- get ten (or less for the last page) entries from the list and insert back/fwd buttons
            vLstRtn = llListInsertList( llList2List( gLstMnu, vIdxBgn, vIdxBgn + 9 ), (list)(" «" + vStrPag), 0xFFFFFFFF ) +
                      (list)(" »" + vStrPag);
        }else{ //-- we only have 1 page
            vLstRtn = gLstMnu; //-- just use the list as is
        }
        return //-- fix the order for [L2R,T2B] and send it out
          llList2List( vLstRtn, -3, -1 ) + llList2List( vLstRtn, -6, -4 ) +
          llList2List( vLstRtn, -9, -7 ) + llList2List( vLstRtn, -12, -10 );
    }/*//  Void Singer [ https://wiki.secondlife.com/wiki/User:Void_Singer ]  //*/
    string uList2nString(list l,integer offset)
    {   integer index = llGetListLength(l);
        string sReturn;
        while(index>0)
        {   index--;
            //string item = "secondlife:///app/agent/" + llList2String(l,index) + "/inspect";
            string item = llGetDisplayName(llList2String(l,index));
            sReturn = (string)(index+offset) + ") " + item + "\n" + sReturn;
        }
        return sReturn;
    }
    default
    {
        link_message(integer sender,integer n,string str,key who)
        {
            if(n==2)
            {
                gAgentList=llGetAgentList(AGENT_LIST_PARCEL,[]);
                /*fill gLstMnu with numbers from 0 to llGetListLength(AgentList)*/
                integer index=0;
                integer max = llGetListLength(gAgentList);
                gLstMnu=[];
                while(index<max)
                {
                    gLstMnu+=(string)(index++);
                }
                gChannel = (integer)(llFrand(-1000000000.0) - 1000000000.0);
                gListenHandle = llListen(gChannel,"",who,"");
                list agents=gAgentList;
                if(llGetListLength(agents)>10)
                    agents=llDeleteSubList(gAgentList,11,-1);
                llDialog( who, uList2nString(agents,0), uDlgBtnLst( 0 ), gChannel );
                llSetTimerEvent(60); // 60 second timeout
            }
        }
        listen( integer vIntChn, string vStrNom, key vKeySpk, string vStrMsg ){
            if (!llSubStringIndex( vStrMsg, " " ))
            { //-- detects (hidden) leading non-breaking space of page change buttons
                integer vIdxPage = ( llStringLength( vStrMsg ) + llSubStringIndex( vStrMsg, "»" ) - 2 );
                integer vIdxBgn = 0;
                if(vIdxPage !=0)
                { /* calculate the index of the first item in list for this page*/
                    integer vIntTtl = -~((~([] != gLstMnu)) / 10);
                    vIdxBgn = (vIdxPage = (vIntTtl + vIdxPage) % vIntTtl) * 10;              //-- first menu index
                }
                llDialog( vKeySpk,
                          uList2nString( llList2List(gAgentList,vIdxBgn, vIdxBgn+9),vIdxBgn ),
                          uDlgBtnLst(vIdxPage),
                          vIntChn );
            }else
            {   //-- button was not a page button, your code goes here,
                //-- use (llListFindList( gLstMnu, (list)vStrMsg ) / 10) for remenu command if present
                //integer vIdxPage=(llListFindList( gLstMnu, (list)vStrMsg ) / 10);
                llListenRemove(gListenHandle);
                ///*DEBUG*/llSay(0,"Test! "+(string)llList2Key(gAgentList,(integer)vStrMsg));
                llMessageLinked(LINK_THIS,-2,"",llList2Key(gAgentList,(integer)vStrMsg));
            }
        }
        timer()
        {
            llListenRemove(gListenHandle);
            llSetTimerEvent(0);
        }
    }

    Qss01 Set Target (sensor)

    list gAgentList;
    list gLstMnu; // global list for the menu.
    key gWho; //who is using menu.
    
    integer gChannel;
    integer gListenHandle; // TODO: make this a strided handler,user list for multiple users.
    
    list uDlgBtnLst( integer vIdxPag ){
        list vLstRtn;
        if ((gLstMnu != []) > 12){ //-- we have more than one possible page
            integer vIntTtl = -~((~([] != gLstMnu)) / 10);                                 //-- Total possible pages
            integer vIdxBgn = (vIdxPag = (vIntTtl + vIdxPag) % vIntTtl) * 10;              //-- first menu index
            string  vStrPag = llGetSubString( "                     ", 21 - vIdxPag, 21 ); //-- encode page number as spaces
             //-- get ten (or less for the last page) entries from the list and insert back/fwd buttons
            vLstRtn = llListInsertList( llList2List( gLstMnu, vIdxBgn, vIdxBgn + 9 ), (list)(" «" + vStrPag), 0xFFFFFFFF ) +
                      (list)(" »" + vStrPag);
        }else{ //-- we only have 1 page
            vLstRtn = gLstMnu; //-- just use the list as is
        }
        return //-- fix the order for [L2R,T2B] and send it out
          llList2List( vLstRtn, -3, -1 ) + llList2List( vLstRtn, -6, -4 ) +
          llList2List( vLstRtn, -9, -7 ) + llList2List( vLstRtn, -12, -10 );
    }/*//  Void Singer [ https://wiki.secondlife.com/wiki/User:Void_Singer ]  //*/
    string uList2nString(list l,integer offset)
    {   integer index = llGetListLength(l);
        string sReturn;
        while(index>0)
        {   index--;
            //string item = "secondlife:///app/agent/" + llList2String(l,index) + "/inspect";
            string item = llKey2Name(llList2String(l,index));
            sReturn = (string)(index+offset) + ") " + item + "\n" + sReturn;
        }
        return sReturn;
    }
    default
    {
        link_message(integer sender,integer n,string str,key who)
        {
            if(n==1)
            {
                gWho=who;
                llSensor("",NULL_KEY,(AGENT|PASSIVE|ACTIVE),32.0,PI);
            }
        }
        sensor(integer detectedN)
        {       llSay(0,"DEBUG!");
                gAgentList=[];
                while(detectedN-- >0)
                    gAgentList+=llDetectedKey(detectedN);
                /*fill gLstMnu with numbers from 0 to llGetListLength(AgentList)*/
                integer index=0;
                integer max = llGetListLength(gAgentList);
                gLstMnu=[];
                while(index<max)
                {
                    gLstMnu+=(string)(index++);
                }
                gChannel = (integer)(llFrand(-1000000000.0) - 1000000000.0);
                gListenHandle = llListen(gChannel,"",gWho,"");
                list agents=gAgentList;
                if(llGetListLength(agents)>10)
                    agents=llDeleteSubList(gAgentList,11,-1);
                llDialog( gWho, uList2nString(agents,0), uDlgBtnLst( 0 ), gChannel );
                llSetTimerEvent(60); // 60 second timeout
        }
        listen( integer vIntChn, string vStrNom, key vKeySpk, string vStrMsg ){
            if (!llSubStringIndex( vStrMsg, " " ))
            { //-- detects (hidden) leading non-breaking space of page change buttons
                integer vIdxPage = ( llStringLength( vStrMsg ) + llSubStringIndex( vStrMsg, "»" ) - 2 );
                
                llDialog( vKeySpk,
                          uList2nString( llDeleteSubList(gAgentList,(10*vIdxPage) + 11, (10*vIdxPage) -1),10*vIdxPage ),
                          uDlgBtnLst(vIdxPage),
                          vIntChn );
            }else
            {   //-- button was not a page button, your code goes here,
                //-- use (llListFindList( gLstMnu, (list)vStrMsg ) / 10) for remenu command if present
                //integer vIdxPage=(llListFindList( gLstMnu, (list)vStrMsg ) / 10);
                llListenRemove(gListenHandle);
                ///*DEBUG*/llSay(0,"Test! "+(string)llList2Key(gAgentList,(integer)vStrMsg));
                llMessageLinked(LINK_THIS,-1,"",llList2Key(gAgentList,(integer)vStrMsg));
            }
        }
        timer()
        {
            llListenRemove(gListenHandle);
            llSetTimerEvent(0);
        }
    }

    Qss3 Select From List

    list gItemList;
    list gLstMnu; // global list for the menu.
    
    integer gChannel;
    integer gListenHandle; // TODO: make this a strided handler,user list for multiple users.
    
    list uDlgBtnLst( integer vIdxPag ){
        list vLstRtn;
        if ((gLstMnu != []) > 12){ //-- we have more than one possible page
            integer vIntTtl = -~((~([] != gLstMnu)) / 10);                                 //-- Total possible pages
            integer vIdxBgn = (vIdxPag = (vIntTtl + vIdxPag) % vIntTtl) * 10;              //-- first menu index
            string  vStrPag = llGetSubString( "                     ", 21 - vIdxPag, 21 ); //-- encode page number as spaces
             //-- get ten (or less for the last page) entries from the list and insert back/fwd buttons
            vLstRtn = llListInsertList( llList2List( gLstMnu, vIdxBgn, vIdxBgn + 9 ), (list)(" «" + vStrPag), 0xFFFFFFFF ) +
                      (list)(" »" + vStrPag);
        }else{ //-- we only have 1 page
            vLstRtn = gLstMnu; //-- just use the list as is
        }
        return //-- fix the order for [L2R,T2B] and send it out
          llList2List( vLstRtn, -3, -1 ) + llList2List( vLstRtn, -6, -4 ) +
          llList2List( vLstRtn, -9, -7 ) + llList2List( vLstRtn, -12, -10 );
    }/*//  Void Singer [ https://wiki.secondlife.com/wiki/User:Void_Singer ]  //*/
    string uList2nString(list l,integer offset)
    {   integer index = llGetListLength(l);
        string sReturn;
        while(index>0)
        {   index--;
            //string item = "secondlife:///app/agent/" + llList2String(l,index) + "/inspect";
            string item = llList2String(l,index);
            sReturn = (string)(index+offset) + ") " + item + "\n" + sReturn;
        }
        return sReturn;
    }
    default
    {
        link_message(integer sender,integer n,string str,key who)
        {
            if(n==3)
            {
                gItemList=llParseString2List(str,[";"],[]);
                /*fill gLstMnu with numbers from 0 to llGetListLength(AgentList)*/
                integer index=0;
                integer max = llGetListLength(gItemList);
                gLstMnu=[];
                while(index<max)
                {
                    gLstMnu+=(string)(index++);
                }
                gChannel = (integer)(llFrand(-1000000000.0) - 1000000000.0);
                gListenHandle = llListen(gChannel,"",who,"");
                list items=gItemList;
                if(llGetListLength(items)>10)
                    items=llList2List(gItemList,0,10);
                llDialog( who, uList2nString(items,0), uDlgBtnLst( 0 ), gChannel );
                llSetTimerEvent(60); // 60 second timeout
            }
        }
        listen( integer vIntChn, string vStrNom, key vKeySpk, string vStrMsg ){
            if (!llSubStringIndex( vStrMsg, " " ))
            { //-- detects (hidden) leading non-breaking space of page change buttons
                integer vIdxPage = ( llStringLength( vStrMsg ) + llSubStringIndex( vStrMsg, "»" ) - 2 );
                integer vIdxBgn = 0;
                if(vIdxPage !=0)
                { /* calculate the index of the first item in list for this page*/
                    integer vIntTtl = -~((~([] != gLstMnu)) / 10);
                    vIdxBgn = (vIdxPage = (vIntTtl + vIdxPage) % vIntTtl) * 10;              //-- first menu index
                }
                llDialog( vKeySpk,
                          uList2nString( llList2List(gItemList,vIdxBgn, vIdxBgn+9),vIdxBgn ),
                          uDlgBtnLst(vIdxPage),
                          vIntChn );
            }else
            {   //-- button was not a page button, your code goes here,
                //-- use (llListFindList( gLstMnu, (list)vStrMsg ) / 10) for remenu command if present
                //integer vIdxPage=(llListFindList( gLstMnu, (list)vStrMsg ) / 10);
                llListenRemove(gListenHandle);
                ///*DEBUG*/llSay(0,"Test! "+(string)llList2Key(gAgentList,(integer)vStrMsg));
                llMessageLinked(LINK_THIS,-3,llList2String(gItemList,(integer)vStrMsg),"");
            }
        }
        timer()
        {
            llListenRemove(gListenHandle);
            llSetTimerEvent(0);
        }
    }

    If I were to re-do this in 2024, I would use linksetData to pass the request and result to/from the 'sub-script' synchronously, and also leverage linkset data for string bytelength checking (why we can't easily get a string's bytelengh directly is beyond me).

    • Thanks 1
  16. Just now, Love Zhaoying said:

    Possibly you were using a different viewer?

    I'm too lazy to test, but Cool VL Viewer sometimes handles this kind of thing differently. Last time I checked, with that viewer it's possible to directly edit scripts in objects owned by other people (assuming you have permission to do so) whereas other viewers I tested at the time, you have to drag the script to your inventory and then back into the object after editing.

    • Like 1
  17. For ease of use, I generally prefer llListenControl with a static listener. There are pros and cons to this approach though.

    integer gHandleListen;
    integer gChannelListen = -17;
    key gToucher;
    
    default
    {   state_entry()
        {   gHandleListen = llListen(gChannelListen,"","","");
            llListenControl(gHandleListen,FALSE);
        }
        touch_end(integer n)
        {   if(gToucher)
            {   llRegionSayTo(llDetectedKey(0),0,"Menu is in-use.");
            }else
            {   llListenControl(gHandleListen,TRUE);
                llSetTimerEvent(30.0);
                llTextBox(gToucher=llDetectedKey(0),"Please type new description:",gChannelListen);
            }
        }
        timer()
        {   llSetTimerEvent(0);
            gToucher=NULL_KEY;
            llListenControl(gHandleListen,FALSE);
        }
        listen(integer chan, string name, key ID, string text)
        {   llRegionSayTo(gToucher,0,"Setting description to '"+text+"'.");
            llSetObjectDesc(text);
            llSetTimerEvent(0); // alternatively, set to 0.02 to execute listen close procedure.
            gToucher = NULL_KEY;
            llListenControl(gHandleListen,FALSE);
        }
    }

     

    • Like 1
  18. A subtle point, but after making the changes suggested by Love and Elleevelyn, you should also call

    llListenRemove(dialogHandle);

    at the beginning of your open_menu() function. Otherwise, if the prim is touched 64 times while menu is open (perhaps split across multiple times the menu is opened), the script will accumulate an unclosed listener for each one and crash.

    12 hours ago, Shatter Roundel said:
            // Register to listen on channel 0624
            llListen(624, "", llGetOwner(), "");

    unclear why this is in the script. channel 624 is never listened to, and this listener is never closed, which again, will crash the script eventually.

     

    • Thanks 1
  19. 9 hours ago, Marvin Benelli said:

    In that case I fully agree with Quintess because you will need 130 (bones)*36(angles) = 4680 animations just for setting the angles assuming you would accept 5 degrees as minimal rotation change. 

    a year or two ago, I played with the idea of making a system where you could move/rotate prims and eventually it would spit out an animation you could upload to apply to your avatar, but that's still at least 65 Land impact, and would be expensive to use on the main grid rather than the beta grid for getting things just so.

    • Thanks 1
×
×
  • Create New...