Jump to content

Sylvia Wasp

Resident
  • Posts

    269
  • Joined

  • Last visited

Posts posted by Sylvia Wasp

  1. Thanks again, 🙂 

    For a visually oriented person the way in which it's displayed makes all the difference.  

    For instance I looked at the key section for a long time and always saw it as:

    3 * (Text_var + Alpha_var);

    not 

    ( Text_var * 3) + Alpha_var;

    which would give: 

    3 * 0 + 0 = 0
    3 * 0 + 1 = 3
    3 * 0 + 2 = 6
    3 * 1 + 0 = 3
    3 * 1 + 1 = 6
    3 * 1 + 2 = 9
    3 * 2 + 0 = 6
    3 * 2 + 1 = 9
    3 * 2 + 2 = 12
    3 * 3 + 0 = 9
    3 * 3 + 1 = 12
    3 * 3 + 2 = 15
    3 * 4 + 0 = 12
    3 * 4 + 1 = 15
    3 * 4 + 2 = 18
    3 * 5 + 0 = 15
    3 * 5 + 1 = 18
    3 * 5 + 2 = 7

    Anyway, problem solved.  :) 

    The product will be finished in a day or two but anyone who contributed to this thread is welcome to a gift of anything in my store, although they're not likely to be of interest to all you math geeks, lol.  Check it out if you need female avatar clothing for Tonic (objectively, the BEST mesh body) or Maitreya (the most POPULAR mesh body)

    https://marketplace.secondlife.com/stores/184651 

    Sylvia

  2. 9 hours ago, Wulfie Reanimator said:

    Yes. You can imagine a list as an "array of boxes" or slots or whatever. You would have all 18 textures in a single list, all in the same order. Here's a visual (pardon my poor digital handwriting):

    529f3dba76.png

    Index 0-2 are for the first texture, each with the different kinds of sheer. Index 3-5 are for the second texture, and so on.

    Cool pic, excellent illustration of what's happening.  :)

    OK, so I took Molly's advice and put the calculations for the index in the HUD itself, which makes the code in the mesh even simpler.  Here is my finished version of both the HUD code and the Mesh code.  

     

    The only thing I can think of that may be wrong here is that I'm not "handling" the listen I guess.  

    //===== Texture Changer (HUD code) - by Sylvia Wasp ====
    
    string  HUD_Name = "some HUD";
    
    integer cmdChannel;
    integer Text_var;
    integer Alph_var;
    
    integer Key2Chan(key ID) 
    {
        return 0x80000000 | (integer)("0x"+(string)ID);
    }
    
    default
    {
        state_entry ()
        {
            llSetLinkPrimitiveParamsFast(LINK_THIS,[PRIM_NAME,HUD_Name]);
            cmdChannel = Key2Chan(llGetOwner());
        }
    
        touch_start(integer n)
        {
            integer button = (integer)llGetLinkName(llDetectedLinkNumber(0));
    
            if (button >= 6) {
                Alph_var = button - 6;
                }   
            else {
                Text_var = button;
                } 
    
            integer index = 3 * Text_var + Alph_var;
            llRegionSayTo(llGetOwner(),cmdChannel, (string)index);
        }                
    }
    
    //===== Texture Changer (Mesh code) - by Sylvia Wasp ======
    
    string  HUD_Name = "some HUD";
    integer cmdChannel;
    
    integer Key2Chan(key ID) 
    {
        return 0x80000000 | (integer)("0x"+(string)ID);
    }
    
    key     label_tex = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
    key     trans_tex = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
    
    list    textures =
        [
            "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",    //texture_01_90
            "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",    //texture_01_80
            "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",    //texture_01_60
            "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",    //texture_02_90
            "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",    //texture_02_80
            "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",    //texture_02_60
            "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",    //texture_03_90
            "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",    //texture_03_80
            "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",    //texture_03_60
            "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",    //texture_04_90
            "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",    //texture_04_80
            "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",    //texture_04_60
            "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",    //texture_05_90
            "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",    //texture_05_80
            "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",    //texture_05_60
            "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",    //texture_06_90
            "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",    //texture_06_80
            "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"     //texture_06_60 
        ];
    
    default
    {
        state_entry ()
        {      
            cmdChannel = Key2Chan(llGetOwner());
            llListen(cmdChannel, HUD_Name, NULL_KEY, "");
            llSetLinkPrimitiveParamsFast(LINK_THIS,[ PRIM_TEXTURE, ALL_SIDES, trans_tex, <1.0, 1.0, 1.0>,ZERO_VECTOR, 0.0 ]);
            llSetLinkPrimitiveParamsFast(LINK_THIS,[ PRIM_TEXTURE, 0, label_tex, <-1.0, -1.0, 1.0>,ZERO_VECTOR, 0.0 ]);
            llSetLinkPrimitiveParamsFast(LINK_THIS,[ PRIM_TEXTURE, 5, label_tex, <1.0, 1.0, 1.0>,ZERO_VECTOR, 0.0 ]);
        }
    
        listen(integer channel, string name, key id, string message) 
        {
            integer index = (integer) message;
            string  texture_key = llList2String(textures, index);
            llSetLinkPrimitiveParamsFast(2,[ PRIM_TEXTURE, ALL_SIDES, texture_key, <1.0, 1.0, 1.0>,ZERO_VECTOR, 0.0 ]);
        }
    }

    This seems to work although it's late and I haven't tested it much.  

    Sokay? :) 

    Sylvia

    • Like 2
  3. 9 hours ago, Mollymews said:

    go with Wulfie's method

    making a HUD where each button is its own linked prim can be a lot simpler to visualise and get up and working

     

    edit add

    linked buttons HUD

    10 prims. 1 x backboard. 6 x texture buttons. 3 x sheer buttons

    name the texture buttons 0, 1, 2, 3, 4, 5

    name the sheer buttons 6, 7, 8

     

    Okay, thanks for this.  Here is my HUD code altered to match the idea, (I changed the variable names a bit and I had to cast the index as a string to send it)

    //===== Texture Changer (HUD) - by Sylvia Wasp ====
    
    string  HUD_Name = "some HUD";
    
    integer cmdChannel;
    integer Text_var;
    integer Alph_var;
    
    integer Key2Chan(key ID) 
    {
        return 0x80000000 | (integer)("0x"+(string)ID);
    }
    
    default
    {
        state_entry ()
        {
            llSetLinkPrimitiveParamsFast(LINK_THIS,[PRIM_NAME,HUD_Name]);
            cmdChannel = Key2Chan(llGetOwner());
        }
    
        touch_start(integer n)
        {
            integer button = (integer)llGetLinkName(llDetectedLinkNumber(0));
    
            if (button >= 6) {
                Alph_var = button - 6;
                }   
            else {
                Text_var = button;
                } 
    
            integer index = 3 * Text_var + Alph_var;
            llRegionSayTo(llGetOwner(),cmdChannel, (string)index);
        }                
    }

    But this is what I mean about math, lol. 

    I know this bit:  integer index = 3 * Text_var + Alph_var; ... is *why* it works.  But I don't exactly understand why it works.

     I will have to think about it for a (long) while. 

    Sylvia

  4. 9 hours ago, Mollymews said:

    using the same calculation method Wulfie's shows.  Calculating a list index from some input ordered differently to the list

    when our HUD is a single surface then we can use llDetectedTouchST

    http://wiki.secondlife.com/wiki/LlDetectedTouchST


    for a HUD using this function, then in the OP minimalist case there is a 3 x 3 grid and we need a way to convert the grid reference to a list index


    in this example the texture uuids are in the HUD and we are sending the uuid to the garment/object. In another setup the UUIDs list can be in the garment/object and we send the index in the message

    Hey Molly, 🙂 

    This is pretty brilliant, but I honestly, think it's beyond me.  

    It involves math, which for me is a no-no (I'm not dumb but I'm a very visually oriented person).  It also requires me to figure out each individual HUD differently as I create them (although I guess I could create a series of HUDs with different button arrangements as templates?)  As I said to Wulfie, re-usability and solving the problem 'once and for good' is key for me.  

    TBH, I'm still trying to figure out that great solution you gave me for the listens in my avatar detector thingie, lol.  

    I keep putting the code in the wrong place which stops the whole function of the scanner.  

    Sylvia

  5. 13 hours ago, Wulfie Reanimator said:

    OH. It's super clear now, thanks for that.

    If you rename your buttons a little, you could get rid of all the if-checks and use my list method. For example:

    • Let's assign your sheer options some simple values: 0, 1, and 2.
    • Let's do the same thing for the textures: 0, 1, 2, 3, 4, and 5.
    • So the first button would be called "texture_0_" and the last one is "texture_5_"

    Based on that, you can write your HUD like this:

    The reason why we're counting from zero is because that's how list indexes work...

    (0 * 3) + 0 = index 0; First texture with 90 sheer.

    (5 * 3) + 2 = index 17; Last texture with 60 sheer. That covers all 18 textures.

    Thanks Wulfie, 

    I have to admit I don't quite get this, (because math), but if I understand generally ... you're saying that I should keep the texture keys in a list and use the information sent by the HUD to generate indexes and thus pick the correct texture from the list?  

    I usually just list the texture keys at the top of the script like this: 

    key         texture_02_80 = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";    
    key         texture_02_60 = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
    key         texture_03_90 = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";    
    key         texture_03_80 = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
    key         texture_03_60 = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";    

    but putting them in an actual list makes a lot of sense.  

    Re: the HUD,

    This is the entirety of my HUD code.  It's very short and simple and allows me to re-use it over and over again.  I just change the HUD_name variable and make sure the buttons are labelled correctly.  It relies on a scheme wherein the button simply says it's name when clicked, like so: 

    //===== Texture Changer (HUD) - by Sylvia Wasp ====
    //      installed in the root prim of a linkset
    //      where the other prims are buttons labelled
    //      "button_01", "button_02", etc.
    //=================================================
    
    integer cmdChannel;
    string  HUD_Name = "some HUD";
    
    integer Key2Chan(key ID) 
    {
        return 0x80000000 | (integer)("0x"+(string)ID);
    }
    
    default
    {
        state_entry ()
        {
            llSetLinkPrimitiveParamsFast(LINK_THIS,[PRIM_NAME,HUD_Name]);
            cmdChannel = Key2Chan(llGetOwner());
        }
    
        touch_start(integer n)
        {
            string button = llGetLinkName(llDetectedLinkNumber(0));
            llRegionSayTo(llGetOwner(),cmdChannel, button);
        }                
    }

    For me, this reusability is crucial as I don't have the skill to work out a new HUD for every piece of clothing.  

    So you're suggesting (for the HUD) that instead of this bit: 

        touch_start(integer n)
        {
            string button = llGetLinkName(llDetectedLinkNumber(0));
            llRegionSayTo(llGetOwner(),cmdChannel, button);
        }                

    That I detect the link number instead of the link name and send that to the clothing item instead? 

    And then use that number to find things in the list of keys based on indexes?  

    This makes a lot of sense also, but I'm not sure why I couldn't continue to use the button names, since they are logical and mostly numbers themselves (despite being technically strings of course).  

    Also, is there not a way to pick a texture (a key) based on the naming convention I've used in my list of keys?  

    I mean I can easily shorten the if-else trees that I normally use, if, instead of picking the texture key by it's full name, I can pick it though a concatenated string.  Like instead of saying (essentially) "texture_01_90" if I could say "(Text_var + Alph_variable)" instead?  

    Can I call a key just by using it's name (the name that I give it at the top of the file)?  It seems to fail when I try.   

    Sylvia

     

  6. 2 hours ago, Kardargo Adamczyk said:

    here's a snippet that should work, only two if statements

    
    list button_list = ["sheer-1","sheer-2","sheer-3","texture-4","texture-5","texture-6","texture-1","texture-2","texture-3"];
    string sheer = "sheer-1";
    string texturep = "texture-1"; //part of the texture name
    string texture;
    integer lchan = -12345; //listenchannel
    
    default
    {
        state_entry()
        {
        }
    
        touch_start(integer total_number)
        {
            llListen(lchan,"","","");
            llDialog(llDetectedKey(0),"select texture or sheer \ncurrent texture: "+texturep+"\ncurrent sheer: "+sheer,button_list,lchan);
        }
        listen(integer chan, string name, key id, string text)
        {
            if (llGetSubString(text,0,4) == "sheer")
            {
                sheer = text;
                texture = texturep+"-"+sheer;
                llOwnerSay("set texture to : "+texture);
            }
            else if (llGetSubString(text,0,6) == "texture")
            {
                texturep = text;
                texture = texturep+"-"+sheer;
                llOwnerSay("set texture to : "+texture);
            }
        }         
    }

    regs,

    Dargo

    Thanks Drago, but it's a picture HUD for changing dress textures.  llDialog is not a good fit for that situation.  

    Sylvia

  7. 3 hours ago, Wulfie Reanimator said:

    Just to recap, to make sure I've understood it correctly:

    6 textures. 3 variations of each texture.

    You want the "variant" to stay when the texture is changed. (Or just know which one to apply?)

    Either way, you want to get rid of the big if-else tree.

     

    I guess my first question is why would you need 36 if-else checks if you only have 18 textures / buttons with unique names? You should only need 18 at most.

    
    if (message == "texture_01_sheer_90")
    {
        // Do stuff;
    }
    else if (message == "texture_01_sheer_80")
    {
        // Do stuff;
    }
    else if (message == "texture_01_sheer_60")
    {
        // Do stuff;
    }
    else if (message == "texture_02_sheer_90")
    {
        // Do stuff;
    }

    Right?

    Alternatively, you could name your buttons something like "texture1", "texture2", "texture3" and so on. Then you have an ordered list in the clothing item, which uses the number as an index of that list. Then you won't need any if-checks (besides making sure you're getting correct input).

    
    list textures = ["UUID", "UUID","UUID", "UUID"];
    
    listen(integer channel, string name, key id, string message)
    {
        // 7 is the length of "texture", -1 means "end of string"
        integer index = (integer)llGetSubString(message, 7, -1);
        
        string uuid = llList2String(textures, index);
        llSetLinkTexture(LINK_SET, uuid, ALL_SIDES);
    }

     

    Well, because there are two sets of buttons and I want the choice in each set to "stick" as I said, one possible solution is to have six if-else checks for the six texture buttons with three if-else checks for the sheer buttons nested inside each one, so ... 18.  Then the three if-else checks for the sheer buttons would each have six if-else checks nested inside them for the texture buttons, which is another 18. i.e - 36.  Doing it that way, I'd have to probably add some other checks to account for conditions that are outside of what's expected too, so probably *more* than 36.  

    The thing is, if you select a texture button, the HUD has to save the level of transparency/sheer selected (one of three options) to know which level of sheer texture to apply.  The only way I can think of doing that is to have a variable that saves the level of transparency and another variable to save which of the six textures is currently selected.  Then checking for those in the giant if else structure described.  

    I think that would actually work, but it's long, kind of crazy, and I just think there has to be a better way.  

    It's a pictorial HUD, so the user selects a fabric texture based on the six pictures and then they can fool around with the level or transparency/sheer.  Because it's clothing however, I can't just adjust the alpha of the texture, I have to pre-prepare 18 detailed dress textures (3 levels of sheer for each pattern) and then pick one based on the user input from the buttons.  

    Like, if the user selects "texture_02" and then selects a different sheer factor, the HUD needs to know that "we are on texture 2 right now" and select the texture_02 version of that sheer factor.  If they then select "texture_03" instead, I need the HUD to remember that the sheer factor was 90 or 60 or whatever it happened to be and select the texture_03 version of that sheer level.  

    I'm attaching a crude mockup of the HUD (below) to illustrate.  

    Sylvia

     

    Demo_HUD.jpg

    • Thanks 1
  8. OK, I'm not a great scripter, so I'm probably going to phrase this incorrectly ... please have patience! 

    I'm a clothing designer and I make & use HUDs to change the textures on the clothes.  Up until now this has been just a simple array of 4, 6, 9 or 12 buttons, (one for each texture).  Clicking the button on the HUD makes it shout it's name ("button_01", "button_02" etc.).  The script in the clothing listens for the HUD.  It has a list of keys (UUIDs) at the top and an if, else if structure that uses llSetLinkPrimitiveParamsFast to set the textures on the correct faces and so on depending on which button it heard.

    This works fine when there's one simple array of 12 or less buttons, but I'm now trying to make a HUD with two sets of buttons, one for choosing the pattern and one for setting the level of sheerness of the pattern.  To be clear, I'm not talking about setting the Alpha of a texture, (I know how to do that) these are all still just textures but some of the textures have sheer areas.  

    There are 6 fabric patterns and 3 levels of sheerness, so ... 18 textures in all.

    For clarity, I've labelled the keys to the textures things like,  "texture_01_sheer_90", "texture_01_sheer_80", "texture_01_sheer_60", "texture_02_sheer_90" ... and so on

    But if I want the level of sheerness and the selected texture to "stick," then every time I press one of the six texture buttons, I have to test for three possible levels of sheerness' and every time I press a sheerness button I have to test for the six different patterns.  This gives me (at minimum) a super unwieldy if-else tree that has 36 levels!  It just doesn't seem like the right way to go.  

    Even if I save the pattern choice (1 of 6) and the sheerness level (1 of 3) in variables, I still have to test for that variable and I'm still stuck with the super long unwieldy if-else structure. 

    I'm sure this problem must have been solved before, can anyone point me in the right direction?  

    Assuming I've made any goddamned sense at all, lol. :) 

    Sylvia  

  9. On 1/21/2020 at 2:05 AM, hectic1 said:

    Wouldn't be much simpler to just use llKey2Name() instead of llGetDsiplayName()? It returns the legacy name of the avatar which I think it's guaranteed to consist of ascii letters only (double check on that please).

     

    Yes, except for the fact that most people don't actually use their legacy names (sadly).  

    The idea behind this scanner is that if you're in a room full of avatars and looking at the labels above their heads, what the scanner should return is what those labels say, so that the user can correlate the label on the button with the persons they see in front of them.   

  10. On 1/14/2020 at 9:44 AM, Wulfie Reanimator said:

    This has a minor problem at the cutoff point, where it will turn any multi-byte character into the either the "non-displaying" or "unrecognized" symbol.

    
    string display_name = "I, the Alpha and Omega Ω"; // 23 + 3 bytes
    llOwnerSay(display_name);
    
    string truncated = llBase64ToString(llGetSubString(llStringToBase64(display_name), 0, 31));
    llOwnerSay(truncated);
    
    Object: I, the Alpha and Omega Ω
    Object: I, the Alpha and Omega ?

    The least significant byte of Ω (which is left after truncating) is 166, which is the extended-ASCII character ¦, which isn't in my viewer font's character map so I get an error symbol (could be other symbols like ☐, ☒, �). Other characters might get cut off in a way that displays normal-looking characters that weren't actually there.

    This really only matters if you're comparing the button name directly with something, or trying to use it to find information about the avatar, because the two strings no longer match.

    That's a neat way of getting around the problem though.

    thanks for this detail Wulfie. 🙂 

    Overall, though I don't see this as a problem, especially when the alternative is severely truncated names.  The scanner is mostly for use at dances, shopping malls, etc. and let's face it, most avatars won't be using characters that cause any problems. 

    The majority of avatars just use regular names in the language symbols of their home country in standard fonts available on most modern computers.  Only a small sub-set of avatars will use "weird/interesting" characters from other languages and font sets (for the "cool" factor 🙂 ), and an even smaller subset of those will use characters that might cause a problem with the scanner.  

    In testing, it seems that llBase64ToString deals with those rare cases very well.  

    All the names I've seen on the buttons are clear and easily recognisable as the folks standing around me when I'm scanning. 

  11. 12 minutes ago, Chic Aeon said:

    No answer. They appear to get corrupted in the database. They can sometimes be FINE on an object still but the texture itself is no longer good. Always good to keep copies of ALL your textures on your computer  --- and of course a backup of the files also.  

     

    Also, like other assets, textures can mysteriously disappear from the complete database (not just your inventory). So sending important ones to an alt doesn't help :D.   

    thanks Chic, I was worried that was the answer, but thanks anyway :) 

    Seems to happen way more often with TGA's too for what it's worth.

  12. I've seen this happen on and off over the years and never heard of a good explanation, so here goes ...  

    Basically, pictures (textures) that I've uploaded into inventory, (especially those I use in my profile), just randomly sometimes show up as blurred.  They never resolve into an image.  I've tried rebooting, changing graphics settings and all the typical things, but it isn't that.  I have a good computer, I run on mostly Max settings etc.  It's as if the server is just storing a low-res version and has dispensed with the actual file.  

    This is NOT a transient problem.  Once a texture gets blurred out, there seems no fixing for it.  It never "comes back."  I can download this textures out of SL and they show up as blurred on my desktop.

    It seems SL is just randomly scr*wing up my textures and no one seems to know why or how or what to do about it.  

  13. On 1/12/2020 at 7:33 PM, KT Kingsley said:

    There's a nice little code snippet in the llDialog caveats that shows how to truncate a string so that it'll work in a button, even when it contains UTF-8 characters that'd take it over the byte limit although it's 24 or fewer characters in length:

    llBase64ToString(llGetSubString(llStringToBase64(theString), 0, 31))

    So, @Sylvia Wasp, all you need to do is replace llGetSubString(llGetDisplayName(thisKey), 0, 11) in your button list building loop with llBase64ToString(llGetSubString(llStringToBase64(llGetDisplayName(thisKey)), 0, 31)).

    Thanks KT.  :) 

    This is what I suspected (if I understand what you're saying here).  That it might be possible to screen out the errant characters before they cause problems and hopefully, (again if I understand what you're saying), without having to cut the names down to un-recogniseable stubs of only 6 characters in length.  

    The whole deal in what I'm trying to achieve is a bank of buttons with avatars names on them.  If they aren't identifiable as the avatars standing around the user, then there isn't much point. 

  14. On 1/12/2020 at 5:28 PM, Mollymews said:

    to not create and destroy listeners unnecessarily  then we can use llListenControl() to listen only for the time needed

    http://wiki.secondlife.com/wiki/LlListenControl

    pcode dialog menu example with timeout

    
    //global listener handle
    integer handle;
    
    state_entry()
    {
        handle = llListen( ... );
        llListenControl(handle, FALSE);  // stop listening until touch
    }
    
    touch_start(...)
    {
        llListenControl(handle, TRUE); // begin listening
        llSetTimerEvent(30.0);  // timeout the listener after 30 seconds 
        llDialog(...);          // present the dialog menu
    }
    
    listen( ..., string text, ...)
    {
        llListenControl(handle, FALSE); // stop listening until next touch
        if (text == ...)   // process dialog menu button texts
        {
            ...
        }
    }
    
    timer()
    {
        llSetTimerEvent(0.0);   // stop the timer
        llListenControl(handle, FALSE); // timeout, so stop listening until next touch
    }

     

    Cool. :) Thanks a lot Molly.  This is exactly what I need.  

    • Like 1
  15. Okay, so I wrote this script (with help from this very forum!) that lists out all the avatars in a sim and puts their names on a button dialogue.  

    That may not seem useful but it's just a part of a bigger project.  :) 

    There are two problems with the script (if anyone wants to help out).  

    1) I clearly don't know what I'm doing with the listens, lol.  every time I try to fix them so they are both created and deleted properly, I get "doubles" on the buttons, (every avatar name appears twice) so I've had to comment out most of it which is kind of bad practice (listens must be dealt with).


    2) If there is some douchebag avatar in the sim with some fancy ass name with symbols and stuff in it, I get a script error something like, "buttons should only have so many characters dumbass," even though I've got code in here to stop that exact thing (see below).  

                for (i = 0; i < number_of_keys; ++i) {
                    thisKey = llList2Key(keys,i);
                    if (thisKey != llGetOwner()) {
                        detected_list += [
                            llRound(llVecDist(currentPos, llList2Vector(llGetObjectDetails(thisKey,[OBJECT_POS]),0))), 
                            llGetSubString(llGetDisplayName(thisKey), 0, 11),
                            thisKey 
                            ];
                        }

     

    Only the names with the weird symbols in them sets off the error, so I'm thinking that somehow the Trademark symbol or the Copyright symbol or whatever stupid emoji it is, is functioning like an escape character or something?  

    It rarely happens, and it only happens when some 8 foot tall toxic male with a ridiculous long name is in the sim, so it's probably just a single individual character that's doing it.  

    Maybe it's being done on purpose?  

    In any case, if anyone know if there is a way to limit the Unicode set, or identify the errant character and then check for it, or take it out before this happens I'd appreciate it.    

    the entire script (so far) follows:

    integer cmdChannel;
    integer gMenuPos;
    integer gListen;
    
    list    detected_list;
    list    avatar_list;
    
    integer Key2Chan(key ID) 
    {
        return 0x80000000 | (integer)("0x"+(string)ID);
    }
    
    multipage_dialogue()    // replaces regular dialogue, deals with more pages
    {
        integer lastPage;
        list    buttons;
        integer listLength = llGetListLength(avatar_list);
        
        if(gMenuPos >= 9) {
            buttons += "    <----";
                if((listLength - gMenuPos) > 11) {
                    buttons += "    ---->";
                    }
                else {
                    lastPage = TRUE;
                    }            
            }    
        else if (listLength > gMenuPos+9) {
                if((listLength - gMenuPos) > 11) {
                    buttons += "    ---->";
                    }
                else {
                    lastPage = TRUE;
                    }            
            }
        else {
            lastPage = TRUE;
            }
    
        if (listLength > 0) {
            integer b_count;
            integer num_of_buttons = llGetListLength(buttons);
    
            for(b_count = gMenuPos + num_of_buttons + lastPage - 1 ; (num_of_buttons < 12) && (b_count < listLength); ++b_count) {
                buttons = buttons + [llList2String(avatar_list,b_count)];
                num_of_buttons = llGetListLength(buttons);
                }
            }
    //    gListen = llListen(cmdChannel,"","","");    
        llSetTimerEvent(10.0);
        llDialog(llGetOwner()," \n",buttons,cmdChannel);
    }
    
     
    default
    {
        state_entry()
        {
            cmdChannel = Key2Chan(llGetOwner());
            llListen(cmdChannel, "", llGetOwner(), "");
        }
                    
        touch_start(integer total_number)
        {
            llListenRemove(gListen);
            llSetTimerEvent(0.0);
            gMenuPos = 0;
            avatar_list = ["SCAN", "Reset", "Done"];
            detected_list = [];
            multipage_dialogue();
        }
        
        listen( integer channel, string name, key id, string message )
        {                
            if( message == "SCAN" ){
                avatar_list = [];
                llOwnerSay( "scanning for avatars within 96m ..." );
                
                key     thisKey;
                vector  currentPos = llGetPos();
                list    keys = llGetAgentList(AGENT_LIST_REGION, []); 
                integer number_of_keys = llGetListLength(keys);
         
                integer i;
                for (i = 0; i < number_of_keys; ++i) {
                    thisKey = llList2Key(keys,i);
                    if (thisKey != llGetOwner()) {
                        detected_list += [
                            llRound(llVecDist(currentPos, llList2Vector(llGetObjectDetails(thisKey,[OBJECT_POS]),0))), 
                            llGetSubString(llGetDisplayName(thisKey), 0, 11),
                            thisKey 
                            ];
                        }
                    }
                detected_list = llListSort(detected_list, 3, FALSE);
                avatar_list = llList2ListStrided(llDeleteSubList(detected_list, 0, 0), 0, -1, 3);
                    if (llGetListLength(avatar_list) < 1) {
                        llSleep(1.0);
                        llOwnerSay( "Sorry ... no other avatars found in this simulator");
                        return;
                        }
                    else
                        multipage_dialogue();
                }
            else if ( message == "Reset") {
                llResetScript();
                }
            else if ( message == "Done") {
    //            llListenRemove(gListen);
                }
            else if (~llSubStringIndex(message,"---->")) {
                gMenuPos += 10;
                multipage_dialogue();
                }
            else if (~llSubStringIndex(message,"<----")) {
                gMenuPos -= 10;
                multipage_dialogue ();
                }
            else {
                llOwnerSay(message);    // the (named) button you pressed
                llOwnerSay((string) llList2String(detected_list, (llListFindList(detected_list,[message]) + 1)));   // that avatar's key
                multipage_dialogue ();
                }
        }        
    
        timer()
        {
            llSetTimerEvent(0.0);
    //        llListenRemove(gListen);
        }
    }

    thanks, 

    Sylvia Wasp

  16. 4 hours ago, Rolig Loon said:

    I'm not sure exactly sure what you mean by "add extra buttons."  Each time you click to trigger the touch_start event, you are clearing avatar_list.  Anyone detected in the next scan is being "added" to an empty list. 

    Well, the "SCAN" button is always presented in the first dialogue box from the touch event, but previously the structure has been that the avatar list is appended to that, so that even when the buttons are populated with names, one of them is always "SCAN."  This gives the user the option of (re)SCAN-ing to refresh the list.  

    While this was easy at first, now that I'm using strided lists and a bunch of (to me) super-complicated and counter-intuitive functions to grab/slice/sort various parts of said strided list, the addition of non-strided data just made things super confusing so I left it out of this version.  In this version, after the initial SCAN button is presented and clicked, it's replaced by the avatar name buttons.  

    I would like to include it again, and possibly a "Done" and a "Reset" mostly for the same reasons you mention yourself about user interactions.  I'll probably figure out a way to do it eventually.  I think I need to read up on ordering/formatting the button layout but that's also super-confusing too, so I guess I've been avoiding dealing with it.  

    4 hours ago, Rolig Loon said:

    Now, I do notice in passing that you have not been emptying the detected_list as well, so you are adding information there.  That's almost certainly a mistake, but I don't have time at the moment to think through your logic and see just where it will cause problems first.

    This is funny because the very last thing I did before I posted this code was to remove a detected_list = [ ]; from the code at the top of the SCAN section of the listen because it was stopping the whole script from working.  It made detected_list unavailable to the last part of the listen where I use it to get the avatar key, which is the whole point of the script.    

    The avatar_list = ["SCAN"]; in the touch event functions (sort of) as a clearing of that list in that it replaces whatever might have been in there before.  Should I also have detected_list = [ ]; there for the same reason?  As I said, if I put it in the listen, where avatar_list = [ ]; is, it breaks the whole script.  

    And since both lists are declared at the top ... why should I need to put it in the touch event anyway?  Is putting avatar_list = ["SCAN"]; and detected_list = [ ]; at the top with the other variables the same thing as putting it in the touch event?  Do I have to do both?  Is one better than the other? 

    In short, I'm still confused as to where exactly to put things, or whether I've put them in the right place, since LSL doesn't seem to check for duplicate variable names or anything to do with context.  I call detected_list at the top, but if I call it again within a loop of a listen, then it's treated like a completely different, second list but with the same name?? WTF? And the compiler produces no errors to inform you of your mistake??  Insanity! (lol) :)  

    Where to call things, and which parts of the script those things will ultimately be available too, or "seen by," has to be the most confusing and screwed up part of the whole LSL scripting language.  I'm not super experienced as I noted, but it seems highly irregular to me relative to other scripting languages I've used.

    I don't really expect you to answer these questions.  I'm just trying to convey how confusing and nonsensical all this is to the novice scripter. :( 

    ok </rant> over, lol

    Sylvia 

    • Like 1
  17. Okay, I honestly thought this thread was over, lol ... but I can't seem to put this thing down.

    So, I quickly rewrote the script using llGetAgentList and it works but I'd like some feedback because my solution feels a bit "messy" to me.  I have some stuff in the touch event that may not really belong there?  May not be necessary even?  

    I didn't end up restricting it by distance as during my testing it was fast and smooth and the number of avatars doesn't seem to affect things at all (I imagine this is because it's a single call to the server for a list instead of a cumbersome scan/calculation?) not sure. 

    What it's missing is:

    - a "re-scan" button (every time I add extra buttons beyond the avatar list into the mix, things seem to screw up)

    - a timer to disappear the dialogue box if nothing it touched for a while (or a reset button?)

    Anyway, here it is.  Detects all avatars in a sim, creates a strided list (distance, display name, key) and sorts by distance.  Presents the user with a multi-page dialogue filled with buttons with the avatars names on them.  Pressing one of the named buttons gives you the name and key of that avatar (so you can do stuff).  I also (I think) added in the handler for "no avatars found" (re: Qie's comment on the earlier script).

    It all seems to work, but please ... tell me what's wrong with it.  :) 

    // ====== Sylvia's Avatar Detect v3 ======
    
    integer cmdChannel;
    integer gMenuPos;
    integer gListen;
    
    list    detected_list;
    list    avatar_list;
    
    integer Key2Chan(key ID) 
    {
        return 0x80000000 | (integer)("0x"+(string)ID);
    }
    
    multipage_dialogue()
    {
        integer lastPage;
        list    buttons;
        integer listLength = llGetListLength(avatar_list);
        
        if(gMenuPos >= 9) {
            buttons += "    <----";
                if((listLength - gMenuPos) > 11) {
                    buttons += "    ---->";
                    }
                else {
                    lastPage = TRUE;
                    }            
            }    
        else if (listLength > gMenuPos+9) {
                if((listLength - gMenuPos) > 11) {
                    buttons += "    ---->";
                    }
                else {
                    lastPage = TRUE;
                    }            
            }
        else {
            lastPage = TRUE;
            }
    
        if (listLength > 0) {
            integer b_count;
            integer num_of_buttons = llGetListLength(buttons);
    
            for(b_count = gMenuPos + num_of_buttons + lastPage - 1 ; (num_of_buttons < 12) && (b_count < listLength); ++b_count) {
                buttons = buttons + [llList2String(avatar_list,b_count)];
                num_of_buttons = llGetListLength(buttons);
                }
            }
        gListen = llListen(cmdChannel,"","","");    
        llSetTimerEvent(10.0);
        llDialog(llGetOwner()," \n",buttons,cmdChannel);
    }
    
     
    default
    {
        state_entry()
        {
            cmdChannel = Key2Chan(llGetOwner());
            llListen(cmdChannel, "", llGetOwner(), "");
        }
                    
        touch_start(integer total_number)
        {
            llListenRemove(gListen);
            llSetTimerEvent(0.0);
            gMenuPos = 0;
            avatar_list = ["SCAN"];
            multipage_dialogue();
        }
        
        listen( integer channel, string name, key id, string message )
        {                
            if( message == "SCAN" ){
                avatar_list = [];
                llOwnerSay( "scanning for avatars within 96m ..." );
                
                key     thisKey;
                vector  currentPos = llGetPos();
                list    keys = llGetAgentList(AGENT_LIST_REGION, []);
                integer number_of_keys = llGetListLength(keys);
         
                integer i;
                for (i = 0; i < number_of_keys; ++i) {
                    thisKey = llList2Key(keys,i);
                    if (thisKey != llGetOwner()) {
                        detected_list += [
                            llRound(llVecDist(currentPos, llList2Vector(llGetObjectDetails(thisKey,[OBJECT_POS]),0))), 
                            llGetSubString(llGetDisplayName(thisKey), 0, 11),
                            thisKey 
                            ];
                        }
                    }
                detected_list = llListSort(detected_list, 3, FALSE);
                avatar_list = llList2ListStrided(llDeleteSubList(detected_list, 0, 0), 0, -1, 3);
                    if (llGetListLength(avatar_list) < 1) {
                        return;
                        // probably needs to give a message about "no avatars found" here
                        }
                    else
                        multipage_dialogue();
                }
            else if (~llSubStringIndex(message,"---->")) {
                gMenuPos += 10;
                multipage_dialogue();
                }
            else if (~llSubStringIndex(message,"<----")) {
                gMenuPos -= 10;
                multipage_dialogue ();
                }
            else {
                llOwnerSay(message);    // the (named) button you pressed
                llOwnerSay((string) llList2String(detected_list, (llListFindList(detected_list,[message]) + 1)));   // the key!
                multipage_dialogue ();
                }
        }        
    
        timer()
        {
            llSetTimerEvent(0.0);
            llListenRemove(gListen);
        }
    }

     

  18. 2 hours ago, Rolig Loon said:

    That's exactly the way to do it.  As you look in the LSL wiki, you'll see that llGetAgentList allows you to collect keys across the entire region or across the parcel that you happen to be in at the moment.  In either case, you can always filter the results further by ruling out avatars that are too far away, not in your group, too tall, or whatever.

    In the years before that function was introduced, scripters invented several other clever solutions, most of which are clumsy by comparison.  Some, however, offer suggestions for ways to scan for things other than avatars.  My favorites are devices that behave like drones, flitting around the region and making sensor scans in overlapping areas.  If you search through earlier incarnations of these forums, you'll find some of Void Singer's scanners that work that way.

    A lot of the earlier work (even though I don't understand it) seemed much more creative to me.  I was once given a machine by Xylor Baysklef, made out of prims itself, that used scripts to rez rods and balls and panels that then self assembled into giant geodesic spheres in the air.  It was a beautiful, elegant and intuitive design and a wonder just to watch it do it's work.  It was made long before mesh and most of the scripting possibilities that exist today. 

  19. 1 hour ago, Rolig Loon said:

    Congratulations.  She was right.  Strided lists are a good solution.  Personally, I prefer to work with parallel, unstrided lists that accomplish the same thing, but scripters all do whatever they feel most comfortable with.  In the end, the goal is to create scripts that do what they are supposed to.

    Yes, there is.  In fact, there are several ways around it.  The easiest is to abandon llSensor altogether and use llGetAgentList, which will give you a list of keys for every blessed avatar in the region (or parcel).  You use it, typically, by calling the function in a touch_start event or (if you really want something like a repeating sensor) in a timer event.  You have to go through a bit of juggling to create a parallel list of avatar names from those keys, but from there on, you use the results the same way that you are using sensor results now.

    I think it's unlikely to be due to lag or shenanigans. Chances are good that someone in that unruly crowd has a display name with unprintable characters, adding to the visible ones and pushing the length over the limit.  You might try using llStringTrim in addition to the truncation scheme you have now, to see if it makes a difference. 

    In the end, of course, it doesn't truly matter which name shows up on your dialog buttons, because you are the only one who will see them anyway.  As long as they are recognizable, your system will do what you intended.  I know that you have expressed a dislike for numbered buttons, but that's exactly what was going through my mind as I wrote the script that I pointed out for you.  The names that your script collects all show up in the text portion of the dialog box, where they do not have to be truncated at all.  Each name has a number next to it, and it's the number that appears on the dialog button.

    Cool. 

    I wish there were another way around the 16 avatar limit than getting a list of every avatar in a sim though which may be very long and unwieldy for my purposes, but it’s something I’ll have to try.  

    The original idea was for a HUD that would be used in a busy store or a dance floor situation. Basically scanning the avatars around you, that you can see.  Collecting the whole sim means collecting avatars in other contexts doing other things that the user can neither see nor in many cases is even aware of, which may be confusing for them besides being a possible invasion of privacy.  

    Only collecting 16 avatars seems far too few and trying to collect the whole sim seems like overkill.  30 or so would be perfect, but if those are the only choices then I will have to pick one or the other.  Perhaps I can collect the whole sim, then sort by distance and mask out those avatars clearly out of range of the present location/context of the user.  It will be interesting to try.  

    might also try the numbered buttons thing after all as it (foolishly) didn’t occur to to me that there would be a text based ‘key’ to the numbered buttons as you describe above.  My frustration with numbered buttons was more because blank numbered buttons don’t convey anything to the user, as in my example of a piece of furniture that just presents the user with buttons labelled 1-12 for poses.  

    Thanks again for all the help. :) 

    Sylvia

  20. On 8/4/2019 at 10:28 AM, Rolig Loon said:

    ...

    Stepping back from the immediate question to address a philosophical point ...  When I write a script (except for a dirt simple one that I can write with my eyes closed), I have a running dialog in my head, explaining what I am doing in words that my rather intelligent by non-scripting grandmother could have understood.  I figure that if Granny can understand what I am saying, then I must have a pretty clear idea of how the script is going to work in world.  My point is the not-too-subtle one that computers will only do what you tell them, whether you meant it or not.  Scripting is 90% logic.  It does not allow for ambiguities.  Your task is to write directions that are clear as a bell, do not leave ambiguous or unfulfilled pathways, and try to anticipate the ways that an end user might try to make the script do something that you didn't intend.  I have always figured that I have lived up to the task if I can explain my logic clearly enough to satisfy my grandmother.  That's why she is in my head.

    Very interesting.  A part of the reason I hate code is that I find it difficult to get that clear conception in my head.  When I'm painting a picture, or making something out of prims, or using a design/paint program I *begin* with a definite, clear, "vision" if you will of exactly what it looks like when it's finished then I just keep working until the reality matches the vision.  With code on the other hand I usually feel like I'm crawling across a bumpy floor in complete darkness looking for "stuff" and only about half-way through (if I'm lucky) do I get a clear vision of what it is I should be doing or working towards.  

    In any case, due to the help that I've received here, I've managed to correct all the stupid errors (I think) and had a long talk with Grandmother about what it is I'm doing.  

    She said ... that what I want in the end is avatar keys, and what I want for the dialogue box is avatar names and I want them associated together.  So what I really want is a database.  The closest thing to a database in SL is the strided list, so I spent a day reading up on strided lists and re-wrote the thing from that perspective and with (most) stupid errors removed. 

    It's not that different but at least it finally works OK and could be said to be "finished" in terms of my original question at the top of the thread.  It scans the area for avatars, produces a nice button list of their names, and clicking on a button gives you their name and their key so you can "do stuff" with it.  

    There are some things I'd like to make it work better though.  

    1) I had no idea that sensor was limited to 16 avatars!  Is there any way around that at all?

    2) It sometimes errors out saying that I have a button with more than 24 characters (when obviously I don't).  So far this has only happened in a Linden Hub where the crowd is mostly griefers, racists, paedophiles, & Trump supporters, so I'm thinking it's either LAG or shenanigans of some kind.  

    2b) if it's LAG, is there any way to assure that bits of the script don't execute in the wrong order or that every part of it waits properly before continuing in a high lag environment?  

    2c) if it's shenanigans, that suggests that a player can "do something" to their display name to screw up my code. Is that really possible? 

    Edit: Just realised that the avatar_list = ["SCAN"];  at the beginning of the sensor is probably unnecessary or should be blank.

    // ====== Sylvia's Avatar Detect v2 ======
    
    integer cmdChannel;
    integer gMenuPos;
    integer gListen;
    
    float   sensor_range = 96.0;
    list    detected_list;
    list    avatar_list;
    
    integer Key2Chan(key ID) 
    {
        return 0x80000000 | (integer)("0x"+(string)ID);
    }
    
    multipage_dialogue()
    {
        integer lastPage;
        list    buttons;
        integer listLength = llGetListLength(avatar_list);
        
        if(gMenuPos >= 9) {
            buttons += "    <----";
                if((listLength - gMenuPos) > 11) {
                    buttons += "    ---->";
                    }
                else {
                    lastPage = TRUE;
                    }            
            }    
        else if (listLength > gMenuPos+9) {
                if((listLength - gMenuPos) > 11) {
                    buttons += "    ---->";
                    }
                else {
                    lastPage = TRUE;
                    }            
            }
        else {
            lastPage = TRUE;
            }
    
        if (listLength > 0) {
            integer b_count;
            integer num_of_buttons = llGetListLength(buttons);
    
            for(b_count = gMenuPos + num_of_buttons + lastPage - 1 ; (num_of_buttons < 12) && (b_count < listLength); ++b_count) {
                buttons = buttons + [llList2String(avatar_list,b_count)];
                num_of_buttons = llGetListLength(buttons);
                }
            }
        gListen = llListen(cmdChannel,"","","");    
        llSetTimerEvent(10.0);
        llDialog(llGetOwner()," \n",buttons,cmdChannel);
    }
    
     
    default
    {
        state_entry()
        {
            cmdChannel = Key2Chan(llGetOwner());
            llListen(cmdChannel, "", llGetOwner(), "");
        }
                    
        touch_start(integer total_number)
        {
            avatar_list = ["SCAN"];
            multipage_dialogue();
            llListenRemove(gListen);
            gMenuPos = 0;
        }
    
        sensor( integer number_detected )
        {
            avatar_list = ["SCAN"];
            detected_list = [];
            integer i;
            for( i = 0; i < number_detected; i++ ){
                if( llDetectedKey( i ) != llGetOwner() ){
                    string detected_name = llGetSubString(llGetDisplayName(llDetectedKey( i )), 0, 11);
                    key detected_key = llDetectedKey( i);
                    detected_list += detected_name;
                    detected_list += detected_key;
                    }
                }
            avatar_list = llList2ListStrided(detected_list, 0, -1, 2);
            multipage_dialogue();
        }
        
        listen( integer channel, string name, key id, string message )
        {
            llListenRemove(gListen);
            llSetTimerEvent(0.0);
            
            if( message == "SCAN" ){
                llOwnerSay( "scanning for avatars within 96m ..." );
                llSensor("", NULL_KEY, AGENT, sensor_range, PI);
                }
            else if (~llSubStringIndex(message,"---->")) {
                gMenuPos += 10;
                multipage_dialogue();
                }
            else if (~llSubStringIndex(message,"<----")) {
                gMenuPos -= 10;
                multipage_dialogue ();
                }
            else {
                llOwnerSay(message);
                llOwnerSay((string) llList2String(detected_list, (llListFindList(detected_list,[message]) + 1))); 
                multipage_dialogue ();
                }
        }        
    
        timer()
        {
            llSetTimerEvent(0.0);
            llListenRemove(gListen);
        }
    }

     

  21. 15 hours ago, Rolig Loon said:

    I've been out doing errands all day and have only had a few minutes to look at your current script, so I will probably miss something. KT and Qie are right, though.  Until the sensor has done its job, you don't have a current version of avatar_list to drop into a llDialog.  The place to call llDialog, therefore, is at the end of the sensor event. In fact, that's where you have put it in your latest post.  However, you are calling llDialog there instead of calling the dialogue_box routine that does all the multipage stuff.  The multipage dialog always starts with the the first (oldest) elements of avatar_list and then displays younger ones ten at a time on successive pages. That's what you should be calling. You can get rid of the llDialog statement at the start of the touch_start event as well, since you already call dialogue_box three lines later.

    Thanks for chiming in Rolig, I realise that these questions must be both a bit on the easy/boring side, as well as one's you've been asked many times before.  In my searching for answers I found evidence of you helping out in the past on scripts very similar to mine (several times!). :) 

    I realised my mistake about calling llDialog instead of the multipage routine right after I posted last time, so that's fixed already. 

    The reason I am using llDIalog on the first touch, and then the multi-page routine later, is that I think what I want to do works best as a Menu/subMenu combination.  The first menu (llDialog) presents the user with an announcement of what's going to happen and presents the "SCAN" button ... then the results of the scan are (necessarily) dropped into the multipage menu system, which handles the data.  I'm not sure if it's correct, but that was the logic behind the choice.  

    The trouble is that these two calls to Dialogue boxes are now fighting with each other.  

    I press the "SCAN" button on the first dialogue box and it sometimes replaces with a blank box with an "OK" button for a moment, before then being replaced by the multipage dialogue box with the correct data.  Because the whole scanning and invoking of the multipage dialogue box takes a while, I made it work by putting in the llSLeep command, but then what is displayed is the last (old) data from the previous scan (in multipage format), before a moment later being replaced by the new, correct, scan data.  Either one creates a horrible user interaction and llSleep is definitely a kludge I don't want to use anyway.  

    Sadly, I've also lost confidence that I even have the right approach for the task at hand. I realised that I probably need the avatars keys to do anything worthwhile with the named buttons at the end and I don't think you can find an avatars key with just the first 12 characters of their display names.  I can collect the keys instead, but then I need a whole other routine (in the listen?) to parse that data into display names before dumping them in the dialogue?  Or I need to collect two lists in the sensor (keys and names) and then associate them in a data structure with strided lists or something? Ack!   

    The whole thing is a mess really, lol.

    the relevant code section:

        touch_start(integer total_number)
        {
            llDialog(llDetectedKey(0),"\n~Press SCAN for a list of avatars~\n", ["SCAN"],cmdChannel);
            llListenRemove(gListen);
            gMenuPos = 0;
        }
    
        sensor( integer number_detected )
        {
            avatar_list = ["SCAN"];
            integer i;
            for( i = 0; i < number_detected; i++ ){
                if( llDetectedKey( i ) != llGetOwner() ){
                    string detected_name = llGetDisplayName(llDetectedKey( i ));
                    avatar_list += llGetSubString(detected_name, 0, 11); 
                    }
                }
            llSleep(2.0);
            multipage_dialogue();
        }
    

     

  22. Okay, so ... here is the code where I combined the multi-page button dialogue into what I already had as well as taking KT's simplification notes into account.  I renamed a lot of stuff and formatted it so it's clearer because I'm a super visual person (thus why I suck at code), and I simply can't even read it unless I do that.  

    It (sorta) works?  

    Obviously there is a problem with the touch_start event in that I'm calling my original dialogue box and then calling the multi-page dialogue box function right after.  If I comment out one or the other then it doesn't work at all, but the way it is currently, if I'm quick with hitting the SCAN button, then eventually it works fine and I get a multi-page dialogue with the correct forward and backward arrows and all the names of the avatars correctly formatted etc.  

    So I'm 90% of the way there?  I mean it's working ... but with problems, lol. 

    I'm thinking that what I want is a first page menu where I invoke the scan and then the multipage dialogue thingie is a *sub menu of that?  But I'm not sure how to do that either, lol. 

    Anyway, here is what I have now for anyone still interested and thanks for all the help so far: 

     Sylvia

    // Sylvia's avatar scan code (combined)
    // 
    
    integer cmdChannel;
    integer gMenuPos;
    integer gListen;
    
    float   sensor_range = 96.0;
    list    avatar_list;
    
    integer Key2Chan(key ID) 
    {
        return 0x80000000 | (integer)("0x"+(string)ID);
    }
    
    dialogue_box()
    {
        integer lastPage;
        list    buttons;
        integer listLength = llGetListLength(avatar_list);
        
        if(gMenuPos >= 9) {
            buttons += "    <----";
                if((listLength - gMenuPos) > 11) {
                    buttons += "    ---->";
                    }
                else {
                    lastPage = TRUE;
                    }            
            }    
        else if (listLength > gMenuPos+9) {
                if((listLength - gMenuPos) > 11) {
                    buttons += "    ---->";
                    }
                else {
                    lastPage = TRUE;
                    }            
            }
        else {
            lastPage = TRUE;
            }
    
        if (listLength > 0) {
            integer b_count;
            integer num_of_buttons = llGetListLength(buttons);
    
            for(b_count = gMenuPos + num_of_buttons + lastPage - 1 ; (num_of_buttons < 12) && (b_count < listLength); ++b_count) {
                buttons = buttons + [llList2String(avatar_list,b_count)];
                num_of_buttons = llGetListLength(buttons);
                }
            }
        gListen = llListen(cmdChannel,"","","");    
        llSetTimerEvent(10.0);
        llDialog(llGetOwner()," \n",buttons,cmdChannel);
    }
    
     
    default
    {
        state_entry()
        {
            cmdChannel = Key2Chan(llGetOwner());
            llListen(cmdChannel, "", llGetOwner(), "");
        }
                    
        touch_start(integer total_number)
        {
            llDialog(llDetectedKey(0),"\n~AVATAR LIST~\n", ["SCAN"],cmdChannel);
            llListenRemove(gListen);
            gMenuPos = 0;
            dialogue_box();
        }
    
        sensor( integer number_detected )
        {
            avatar_list = ["SCAN"];
            integer i;
            for( i = 0; i < number_detected; i++ ){
                if( llDetectedKey( i ) != llGetOwner() ){
                    string detected_name = llGetDisplayName(llDetectedKey( i ));
                    avatar_list += llGetSubString(detected_name, 0, 11);
                    }
                }
            llDialog(llGetOwner(), "\n avatars found ...",  avatar_list, cmdChannel);
        }
        
        listen( integer channel, string name, key id, string message )
        {
            llListenRemove(gListen);
            llSetTimerEvent(0.0);
            
            if( message == "SCAN" ){
                llOwnerSay( "scanning for avatars within 96m ..." );
                llSensor("", NULL_KEY, AGENT, sensor_range, PI);
                }
            else if (~llSubStringIndex(message,"---->")) {
                gMenuPos += 10;
                dialogue_box();
                }
            else if (~llSubStringIndex(message,"<----")) {
                gMenuPos -= 10;
                dialogue_box ();
                }
            else {
                llOwnerSay(message);
                }
        }        
    
        timer()
        {
            llSetTimerEvent(0.0);
            llListenRemove(gListen);
        }
    }
    
    

     

  23. 6 hours ago, KT Kingsley said:

    You can reset the avatar list more easily: avatar_list = [];.

    You need to wait for the sensor to do its stuff and for your code to build the list of buttons before you display the dialog. It can go in at the end of the loop that builds the avatar list in the sensor event.

    You could put the "SCAN" button text at the start of the avatar list so you can initiate a re-scan from the avatar list dialog. So when you reset the avatar list start it off like this: avatar_list = ["SCAN"];.

    (First thing in the morning, and I've only just made my tea. I hope the effects aren't infecting my post.)

    thanks KT,  all great suggestions :) 

    I feel like a dummy for not seeing that easier list reset.  So obvious once you see it, lol.  

    I base most of my decisions on finding some example elsewhere, so lots of times it works, but is inappropriate to the setting or overly complicated.  

    Sylvia

  24. On 8/1/2019 at 9:18 PM, Rolig Loon said:

    That sounds good to me.  As a scripter, I tend to get fiddly about cleaning up loose ends and making my work as efficient as I can,  In the end, though, what always has to count first is that the script does what you want it to.  Just be sure that those names are all truncated so that they don't kick up a script error.  The odd high-bit ASCII characters won't look like anything readable, but if you can identify which avatar is which, that's enough.

    Well, I would also like it to be as efficient and "clean" as possible, but yes it's most important that it fit the situation it was made for.  

    OK, so if anyone's still interested, here is a "version 2" of the thing.

    - I've successfully dumped the list to "llDialog".  

    - I've changed the sensor to read Display Names instead of the actual avatar names (no one seems to use those anymore) and truncated them at twelve characters. 

    - I've made the sensor scan reset the list each time so it doesn't just get bigger and bigger with each scan, (which allowed me to remove the "reset" button).

    - I've simplified the nomenclature a bit and chopped out as much unnecessary stuff as possible.

    - I've added a bit to the end of the listen which would serve as an "action" for the avatar name buttons (currently it just says the name). 

    I haven't dealt with the 12 button limit yet, because my intention is to (later on) dump the avatar_list to a multi-page dialog that expands to fit however long the list gets. I'm still working on understanding that part.  

    I also need to add the "SCAN" button to the llDialog that lists the avatars names for better UI flow. 

    What I don't understand is why I can't blend the "list" part with the "scan" part.  Every time I put the llDialog command after the scan, (within the same else-if as the scan), it reads as either empty or as just the first name in the list.  I seem to be forced to scan and list separately.  Other than that, and the fact that I'm still not checking for the button limit (and the fact that I'm probably not dealing with the listen handle as I should), it seems to work.  

    // Sylvia's avatar scan code (take 2)
    
    integer cmdChannel;
    list    avatar_list;
    float   sensor_range = 96.0;
    
    
    integer Key2Chan(key ID) 
    {
        return 0x80000000 | (integer)("0x"+(string)ID);
    }
     
    default
    {
        state_entry()
        {
            cmdChannel = Key2Chan(llGetOwner());
            llListen(cmdChannel, "", llGetOwner(), "");
        }
                    
        touch_start(integer total_number)
        {
            llDialog(llDetectedKey(0),"\n~AVATAR LIST~\n",
            ["SCAN","list"],cmdChannel);
        }
    
        sensor( integer number_detected )
        {
            avatar_list = llDeleteSubList(avatar_list, 0, llGetListLength(avatar_list));
    
            integer i;
            for( i = 0; i < number_detected; i++ ){
                if( llDetectedKey( i ) != llGetOwner() ){
                    string detected_name = llGetDisplayName(llDetectedKey( i ));
                    avatar_list += llGetSubString(detected_name, 0, 11);
                    }
                }    
        }
        
        listen( integer channel, string name, key id, string message )
        {
            if( id != llGetOwner() ){
                return;
                }
            else if( message == "SCAN" ){
                llOwnerSay( "scanning for avatars within 96m ..." );
                llSensor("", NULL_KEY, AGENT, sensor_range, PI);
                
                // why can't I put "llDialog" here?
                
                }
            else if( message == "list" ){
                llDialog(llGetOwner(), "\n avatars found ...",  avatar_list, cmdChannel);
                }
            else {
                llOwnerSay(message);
                }
        }        
    }
    
    

     

  25. 8 hours ago, Rolig Loon said:

    You're almost there.  You have packed the names into visitor_list, which is almost the list that you need to hand to llDialog as

    llDialog(kAv, "\n Pick a name ... any name... ",  visitor_list, my_dialog_channel);

    You need to do only a tiny bit more cleanup.  First, you need to truncate (or somehow reshape) the names to fit into 12-character dialog button labels.  Or you can put the names into a numbered list in the dialog message and then use numbered buttons >>> 

    ).. Second, you need to provide a dynamic way to remove names from visitor_list if the target avatars are no longer in sensor range.  Wiping the entire list clean with "Reset" is overkill.

    Having done those things, you really can just drop your polished list directly into the llDialog statement.  Then you just have to figure out what to do when the script hears the response from a dialog button, but I'm sure you have plans for that.

     

    Edit: Thank you, Fenix, for pointing to that script.  There are quite a few dialog/menu scripts in the Library that you can build on, or at least use for inspiration as you write your own.  I think most scripters have found that it's easiest to customize someone else's rough model to fit their own needs. 

    Thanks Rolig, 

    It will take me a while to ingest this stuff of course but the multi-page dialogue code will be especially useful indeed.  

    I probably won't use the numbered buttons thing.  Scriptors won't understand this of course, but regular folks don't actually like numbers or numbered buttons. :)  Even the worst formatted, garbled name is better than numbers.  There is nothing worse than a dialogue on a piece of furniture that just has numbers for the poses for example.  

    Currently the avatar list is a "scan once and done" kind of deal, I was intending to replace it with something that appends new names and removes names of avatars no longer detected.  I was going to stick with a manual "rescan" button however, rather than rely on a timer as it seemed more efficient. Most of the time you're only going to scan once anyway and constantly scanning around for avatars seems resource wasteful no matter how you do it. 

    As for what to do with the names once compiled, each name will be a button so I was thinking that any response from the dialogue that *wasn't* an expected command would obviously be someone pressing a button with an avatars name on it.  So I would have an else-if at the end of the listen that executed the code on the avatars names.  

    I will deal with all the avatar names as they are character-wise.  Those who have chosen ridiculous (and ridiculously long) avatar names will just be out of luck (for being ridiculous), and it will be their loss.  I'm a bit worried about escape characters, but I'm sure there is some way to deal with that. 

    If I can identify a particularly useless sub-set of characters to screen out of the name strings I might do that, but with all the silly font nonsense that people do nowadays, it's unlikely that any particular character can be safely eliminated in that fashion.  Probably I will just take the first twelve characters and truncate the name.  

    thanks again, :) 

    Sylvia

     

    • Like 1
×
×
  • Create New...