Jump to content

Unlimited Texture Changing Single Prim Script?


Recommended Posts

Does anyone know if there is a script out there in lala land (aka second life) that is either unlimited, or with a high texture count that I could use for ONE prim, touch to change?

I am trying to limit the amount of prims I have out but, the current script I use, however useful, only allows 22 textures at a time (per box), and I would like to increase it.

This is the current script that I am using. Again, it's a perfect script, it is just to little amount. I don't know how to read this but, is there maybe a way to increase the number from 22 to something higher? Or unlimited?

Also, I prefer to use the physical texture, not so much the uuid (is that the right word?)... that last one confuses me. Numbers. Yuck.

Thanks for any help!

PS: I should also mention, yes, I tried changing the "22" to something else, and it didn't work.

 

 

list MENU1 = [];
list MENU2 = [];
integer listener;
integer MENU_CHANNEL = 1000;

// opens menu channel and displays dialog
Dialog(key id, list menu)
{
llListenRemove(listener);
listener = llListen(MENU_CHANNEL, "", NULL_KEY, "");
llDialog(id, "Select one object below: ", menu, MENU_CHANNEL);
}

default
{
on_rez(integer num)
{
// reset scripts on rez
llResetScript();
}

touch_start(integer total_number)
{if(llDetectedKey(0)!=llGetOwner())return; 
integer i = 0;
MENU1 = [];
MENU2 = [];
// count the textures in the prim to see if we need pages
integer c = llGetInventoryNumber(INVENTORY_TEXTURE);
if (c <= 12)
{
for (; i < c; ++i)
MENU1 += llGetInventoryName(INVENTORY_TEXTURE, i);
}
else

for (; i < 11; ++i)
MENU1 += llGetInventoryName(INVENTORY_TEXTURE, i);
if(c > 22)
c = 22;
for (; i < c; ++i)
MENU2 += llGetInventoryName(INVENTORY_TEXTURE, i); 
MENU1 += ">>";
MENU2 += "<<"; 
}
// display the dialog 
Dialog(llDetectedKey(0), MENU1);
}

listen(integer channel, string name, key id, string message) 
{
if (channel == MENU_CHANNEL)
{
llListenRemove(listener); 
if (message == ">>")
{
Dialog(id, MENU2);
}
else if (message == "<<")
{
Dialog(id, MENU1);

else 
{
// display the texture from menu selection 
llSetTexture(message, ALL_SIDES);


}

}

 

 

Edited by Morgana Hilra
Link to comment
Share on other sites

I too would start from scratch rather than try to fix this script.

One of the basic problems in this script is that it uses the menu key labels to present the texture names, which as written will fail if any name is longer than 24 characters, and those labels will hide differences beyond about the twelfth character. The way around this is to use, instead of button labels, the dialog "message" area to display the list of options. (That's not infinite either, but holds up to 512 characters total, so only list as many options as can fit.)

The good news is that the script doesn't need to have any limit in the number of textures it offers. It's still limited, but by the number of items an object's inventory can contain. It's pretty big, though; the Limits page says it's 10,000. But there's no reason the script should populate menus in advance of showing them; just keep track of one integer: the index of the last INVENTORY_TEXTURE offered.

It's not a difficult script. A little fussy because LSL menus are clunky.

  • Like 1
Link to comment
Share on other sites

Another option which gets around the dialog menu button limitation is to list via owner say the texture names plus number, and then use llTextBox to request a number from the toucher. No need to work out next and previous page buttos.

It can swamp local chat a bit of you have upwards of 30 textures but as you can resize the chat window it's not as hard as it sounds.

As a refinement, the answer from lltextBox could be tested firstly to see if it an integer, and if so, load the texture number, or if not an integer, assume it's a texture name and try to load that.

 

 

Link to comment
Share on other sites

Thank you to, Nofu Nagy for your help inworld.

They gave me a script that does exactly what I needed.

This one is no mod, so, if someone has answers to help others that may come to my issues, that would be great, and thank you to the ppl that have commented here! ❤️

  • Like 1
Link to comment
Share on other sites

My three cents:

 

string  PREV        = "<<";
string  NEXT        = ">>";

key     gOwner;
integer gChannel;
integer gListener;
list    gTextures;
integer gNrofTextures;
integer gNrofPages;
integer gPage;
list    gMenu;
integer gNrofButtonsLastPage;

load_textures(){
    gNrofTextures = llGetInventoryNumber(INVENTORY_TEXTURE);
    gTextures = [];
    integer i;
    for (i=0; i < gNrofTextures;i++){
        string name = llGetInventoryName(INVENTORY_TEXTURE,i);
        if(llStringLength(name)<=12)
            gTextures += [name];
        else
            llOwnerSay("WARNING: texture names must shorter than 13!");
    }

    if(gNrofTextures>12){
        gNrofPages = 1+gNrofTextures/10;
        gNrofButtonsLastPage = gNrofTextures%10;
    }else{
        gNrofPages = 1;
        gNrofButtonsLastPage = gNrofTextures;        
    }
}

menu(){
    gMenu = [];
    integer i;
    if(gNrofPages==1)
        gMenu = llList2List(gTextures,0,gNrofButtonsLastPage);
    else if(gPage==gNrofPages-1){
        integer index = 10*gPage;
        gMenu = [PREV,NEXT];
        gMenu += llList2List(gTextures,index,index+gNrofButtonsLastPage);
    }else{
        gMenu = [PREV,NEXT];
        gMenu += llList2List(gTextures,0,9);
    }
    llDialog(gOwner, "Select texture: ",gMenu,gChannel);
}

default{

    on_rez(integer num){
        // reset scripts on rez
        llResetScript();
    }

    state_entry(){
        gOwner = llGetOwner();
        gChannel = (integer) ("0x" + llGetSubString ((string)gOwner, -8, -1)) | 0x80000000;
        load_textures();
        if(gTextures==[])
            llOwnerSay("WARNING: No textures in inventory!");
    }

    touch_start(integer total_number){
        if(llDetectedKey(0)!=gOwner)return;
        gPage = 0;
        llListenRemove(gListener);
        gListener = llListen(gChannel, "",gOwner, "");
        // display the menu
        menu();
    }

    listen(integer channel, string name, key id, string message){
        if (channel == gChannel){
            if(message == PREV){
                if(gPage>0)
                    gPage--;
                menu();
            }else if (message == NEXT){
                if(gPage<gNrofPages-1)
                    gPage++;                    
                menu();
            }else {
                // apply the texture from the selected button 
                llSetTexture(message, ALL_SIDES);    
            } 
        }
    }
    
    changed(integer change){
        if(change & CHANGED_INVENTORY)
            llResetScript();
    } 
}
 

Link to comment
Share on other sites

On 4/28/2024 at 2:50 PM, Marvin Benelli said:

My three cents:

 

string  PREV        = "<<";
string  NEXT        = ">>";

key     gOwner;
integer gChannel;
integer gListener;
list    gTextures;
integer gNrofTextures;
integer gNrofPages;
integer gPage;
list    gMenu;
integer gNrofButtonsLastPage;

load_textures(){
    gNrofTextures = llGetInventoryNumber(INVENTORY_TEXTURE);
    gTextures = [];
    integer i;
    for (i=0; i < gNrofTextures;i++){
        string name = llGetInventoryName(INVENTORY_TEXTURE,i);
        if(llStringLength(name)<=12)
            gTextures += [name];
        else
            llOwnerSay("WARNING: texture names must shorter than 13!");
    }

    if(gNrofTextures>12){
        gNrofPages = 1+gNrofTextures/10;
        gNrofButtonsLastPage = gNrofTextures%10;
    }else{
        gNrofPages = 1;
        gNrofButtonsLastPage = gNrofTextures;        
    }
}

menu(){
    gMenu = [];
    integer i;
    if(gNrofPages==1)
        gMenu = llList2List(gTextures,0,gNrofButtonsLastPage);
    else if(gPage==gNrofPages-1){
        integer index = 10*gPage;
        gMenu = [PREV,NEXT];
        gMenu += llList2List(gTextures,index,index+gNrofButtonsLastPage);
    }else{
        gMenu = [PREV,NEXT];
        gMenu += llList2List(gTextures,0,9);
    }
    llDialog(gOwner, "Select texture: ",gMenu,gChannel);
}

default{

    on_rez(integer num){
        // reset scripts on rez
        llResetScript();
    }

    state_entry(){
        gOwner = llGetOwner();
        gChannel = (integer) ("0x" + llGetSubString ((string)gOwner, -8, -1)) | 0x80000000;
        load_textures();
        if(gTextures==[])
            llOwnerSay("WARNING: No textures in inventory!");
    }

    touch_start(integer total_number){
        if(llDetectedKey(0)!=gOwner)return;
        gPage = 0;
        llListenRemove(gListener);
        gListener = llListen(gChannel, "",gOwner, "");
        // display the menu
        menu();
    }

    listen(integer channel, string name, key id, string message){
        if (channel == gChannel){
            if(message == PREV){
                if(gPage>0)
                    gPage--;
                menu();
            }else if (message == NEXT){
                if(gPage<gNrofPages-1)
                    gPage++;                    
                menu();
            }else {
                // apply the texture from the selected button 
                llSetTexture(message, ALL_SIDES);    
            } 
        }
    }
    
    changed(integer change){
        if(change & CHANGED_INVENTORY)
            llResetScript();
    } 
}
 

Is it possible to make the menu tags longer? We tried it and it said it needs to be less than 13 characters.

Thank you for trying ❤️

Link to comment
Share on other sites

3 minutes ago, Morgana Hilra said:

Is it possible to make the menu tags longer? We tried it and it said it needs to be less than 13 characters.

most viewers only display 12 characters on a button, but the button names are allowed to go up to 24 characters before script errors happen.

Simply change the line if(llStringLength(name)<=12)

Link to comment
Share on other sites

Below is an alternative posted more for feedback than as a practical solution to the OP's problem. (

There's a different way to use llDialog, putting clickable responses inside the message text area. It's not ideal for menus with many items having longer names (as here, potentially) because the overhead in each app/chat URI is huge, but it does free-up the dialog buttons for other interactions (here just using them for next and previous page).

It seems that CoolVL Viewer has a problem with this but maybe that's intentional. @Henri Beauchamp might say this is all misguided anyway. I do fret a bit that we've been trying to teach stranger danger to residents about clicking links they shouldn't trust, and if they get used to trusting URIs in dialog boxes they may hurt themselves.

integer DIALOG_CHANNEL = 9; // non-zero single-digit saves string length, more items in menu list
float DIALOG_TIMEOUT = 90.0;
string PREVIOUS = "<<";
string NEXT = ">>";

integer invCount;
integer menuStart;  // inventory index of first element in menu
integer menuEnd = -1;    // inventory index of last element in menu
integer listenHandle;
integer forward?; // is menulist index counting forward from top (1) or backward from bottom (0)?
key toucher;    // does NOT handle interaction with multiple agents simultaneously

string menuList(integer incr)
{
    forward = TRUE;
    string menuString;
    integer itemIdx;
    if (0 < incr)   // fill forwards from previous menuEnd
    {
        menuStart = (menuEnd + 1) % invCount;
        itemIdx = menuStart;
        integer full;
        do
        {
            string itemString = "[secondlife:///app/chat/"+(string)DIALOG_CHANNEL+"/"+(string)(itemIdx-menuStart)+" "
                + llGetInventoryName(INVENTORY_TEXTURE, itemIdx)
                +"]\n"; 
            if (512 > (llStringLength(menuString)+llStringLength(itemString)))
            {
                menuString += itemString;
                menuEnd = itemIdx;
                ++itemIdx;
            }
            else
                full = TRUE;
        } 
        while (!full && (itemIdx < invCount) && (20 > (menuEnd - menuStart)));
    }
    else
    if (0 > incr)   // fill backwards from previous menuStart
    {
        forward = FALSE;
        menuEnd = menuStart - 1;
        if (0 > menuEnd)
            menuEnd = invCount-1;
        itemIdx = menuEnd;
        integer full;
        do
        {
            string itemString = "[secondlife:///app/chat/"+(string)DIALOG_CHANNEL+"/"+(string)(menuEnd-itemIdx)+" "
                + llGetInventoryName(INVENTORY_TEXTURE, itemIdx)
                +"]\n"; 
            if (512 > (llStringLength(menuString)+llStringLength(itemString)))
            {
                menuString = itemString + menuString;
                menuStart = itemIdx;
                --itemIdx;
            }
            else
                full = TRUE;
        } 
        while (!full && (itemIdx >= 0) && (20 > (menuEnd - menuStart)));
    }
    else    // incr == 0, just refill same menu as befor
    {
        itemIdx = menuStart;
        do
            menuString += "[secondlife:///app/chat/"+(string)DIALOG_CHANNEL+"/"+(string)(itemIdx-menuStart)+" "
                + llGetInventoryName(INVENTORY_TEXTURE, itemIdx)
                +"]\n"; 
        while (++itemIdx <= menuEnd);
    }
    return menuString;
}

default
{
    state_entry()
    {
        invCount = llGetInventoryNumber(INVENTORY_TEXTURE);
        if (0 == invCount) state empty;
    }
    changed(integer change)
    {
        if (CHANGED_INVENTORY == change)
            if (invCount != llGetInventoryNumber(INVENTORY_TEXTURE))
                llResetScript();
    }
    touch_start(integer total_number)
    {
        toucher = llDetectedKey(0);
        llListenRemove(listenHandle);   // won't reuse. maybe new toucher
        listenHandle = llListen(DIALOG_CHANNEL, "", toucher, "");
        llSetTimerEvent(DIALOG_TIMEOUT);
        llDialog(toucher, menuList(TRUE), [PREVIOUS, NEXT], DIALOG_CHANNEL);
    }
    timer()
    {
        llListenRemove(listenHandle);
    }
    listen(integer channel, string name, key id, string text)
    {
        integer incr;
        if (NEXT == text)
            incr = 1;
        else
        if (PREVIOUS == text)
            incr = -1;
        else
        {
            integer textureIdx;;
            if (forward?)
                textureIdx = menuStart + (integer)text;
            else
                textureIdx = menuEnd - (integer)text;
            llSetTexture(llGetInventoryName(INVENTORY_TEXTURE, textureIdx), ALL_SIDES);
        }
        llSetTimerEvent(DIALOG_TIMEOUT);
        llDialog(toucher, menuList(incr), [PREVIOUS, NEXT], DIALOG_CHANNEL);
    }
}

state empty
{
    state_entry()
    {
        llWhisper(DEBUG_CHANNEL, "No textures in inventory. Add some.");
    }
    changed(integer change)
    {
        if (CHANGED_INVENTORY == change)
            if (0 != llGetInventoryNumber(INVENTORY_TEXTURE))
                llResetScript();
    }
}

/*
    Wiki says only 20 lines shown in dialog message before it needs a scrollbar.
    Thatn's not my experience, but whatever, 20 lines seem enough, plus
    BAREST minimum of 32 chars in each menu item so will max 512 char limit first anyway.
*/

This script may be buggy as hell; I barely tested it. Also there must be a more elegant way to fill these menus forward and backward but "quick and dirty" exhausted my patience with this for now.

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

Posted (edited)
49 minutes ago, Qie Niangao said:
[secondlife:///app/chat/

"chat links" (IDK if there's a standard name, but that's what I've always called SLURLs of that form) can be a very powerful tool, but current SL limitations make them very clunky in most practical applications. As I think you pointed out in the comments of your script, llDialog's 512 char limit limits the number you can stuff into a dialog box. I have seen/used at least one product that llRegionSay's the links to the user as an alternative to a llDialog style menu, which works fairly well (as long as you constantly remind that most viewers seem afraid of chat links for some imagined security reason, and require the user to right-click then 'run this command' in order to activate a chat link) except when you want/need to hold a conversation in local chat while using the product.

IMO a notecard filled with chat links would be a very useful menuing alternative, but alas chat links do not display as clickable in notecards.

IIRC you can get chat links to work from within media displayed by the in-world web-browser, but at that point, there are better solutions that also work with an external web-browser. Not to mention some people's fear of releasing their IP address via media.

Chat links can work in your profile to hand out a notecard to people within chat range of your avatar (or some other object you've scripted to listen to the command). I have one in one of my picks.

Edited by Quistess Alpha
I'm a bit compulsive about spelling/grammar mistakes.
  • Thanks 2
Link to comment
Share on other sites

40 minutes ago, Qie Niangao said:

clickable responses inside the message text area

Devilishly clever. I always forget which all functions exist in the viewer URI scheme, the chat function supports only channels above 0 (not spamming local chat is understandable, not sending on negatives is a bit weird) which is why I may have glossed it over previously.

I like this approach, missing only some kind of an indicator if there's more stuff to go back/forward to.

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

2 minutes ago, Frionil Fang said:

Devilishly clever.

Oh, I should disclose that it's not my original idea. I encountered it at SL20B in an exhibit tour shuttle using @Jessicatz Fairymeadow's Solares Colibri product (I'd already bought and hacked the non-tour version to shuttle over a fixed mixed KFM / llSetRegionPos route). Once I saw the dialog message I realized how it had to be working, but until now never thought to ask if it was her innovation.

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

1 hour ago, Qie Niangao said:

an exhibit tour shuttle

I didn't happen to see that particular shuttle, but the idea of teleports and chat links in dialog boxes reminded me of this old thing I figured out:

 

  • Like 2
Link to comment
Share on other sites

4 minutes ago, Quistess Alpha said:

I didn't happen to see that particular shuttle, but the idea of teleports and chat links in dialog boxes reminded me of this old thing I figured out:

 

I took a peek, and was surprised that llRequestInventoryData() (https://wiki.secondlife.com/wiki/LlRequestInventoryData) is still only for Landmarks!

I had assumed llRequestInventoryData() was for other things too.

 

  • Like 1
  • Sad 1
Link to comment
Share on other sites

8 minutes ago, Quistess Alpha said:

the idea of teleports and chat links in dialog boxes

It's very possible your example was what prompted me to also use dialog message text for the /worldmap/ link (which gets around some llMapDestination restrictions), and just straight external URLs instead of llLoadURL. The "plumbing" to use /chat/ links as regular dialog input never crossed my mind before SL20B, but it may be old hat for all I know.

  • Like 1
Link to comment
Share on other sites

Posted (edited)
1 hour ago, Nofunawo said:

a scanner

What's the 'Zoom' SLURL? I never could figure out a convenient way to tell the viewer to camera-follow a specific object (I.E. automatically ctrl+alt+click an object that may be quickly moving and hard to click manually) , and briefly skimming the specifications again I'm only seeing things that would at best bring up a beacon for a specific position.

Edited by Quistess Alpha
Link to comment
Share on other sites

Posted (edited)
if (action == "zoom")
{
vector agentSize = llGetAgentSize(keyid);
if (agentSize == ZERO_VECTOR)
{
llOwnerSay("Zoom in " + cbcAgentProfile(keyid) + " not possible. Most likely the Avatar left the Region");
return;
}
list details = llGetObjectDetails(keyid, [OBJECT_POS, OBJECT_ROT]);
vector tPos = llList2Vector (details, 0);
rotation tRot = llList2Rot (details, 1);
llClearCameraParams();
    llSetCameraParams 
    ([
        CAMERA_ACTIVE, TRUE,
        CAMERA_FOCUS, tPos, 
        CAMERA_FOCUS_LOCKED, TRUE,
        CAMERA_POSITION, tPos + (camera_offset * tRot),
        CAMERA_POSITION_LOCKED, TRUE 
    ]);
llSetTimerEvent(1);
}

The timer aks again for pos & rot and sets camera focus & position. It is not smooth but it fulfills its purpose 🙂

camera_offset is a defined vector to show the ava in full size and not the belly only...

Edited by Nofunawo
  • Thanks 1
Link to comment
Share on other sites

And that is my solution for the original question. There is neither a limit of 12 nor 24 as it uses numbers for the buttons.

The "*List*" command prints all options to nearby chat - which is a quicker way to choose if there are many entries in the menu.

integer chan;
integer cHandle;

integer chat_handler;
integer chatchan;

key id;
integer idxMenu;

list lTextures;
string CurTex;
integer NumTex;
integer sum;


texture_me()
{
CurTex = llList2String(lTextures,NumTex);   
llSetLinkTexture(LINK_SET,CurTex,ALL_SIDES);
}

buildMenu(key id) 
{
llListenRemove(cHandle);   
chan = (integer)(llFrand(9999999) * -1);
cHandle = llListen(chan, "", "", "");
list menu;
integer i;
string out = "\nTextures\n";
out+="\nCurrent Texture: "+CurTex+"\n";
string num; 
if(idxMenu > 0) menu += ["<<PREV"]; 
else menu += ["*List*"]; 
if(idxMenu*10+9 < sum - 1) menu += ["NEXT>>"]; 

    for(i=idxMenu*10; (i <= idxMenu*10+9) && (i <= sum - 1);i++)
    {
    num = (string)(i+1);
    out += "\n"+num+": "+llList2String(lTextures,i);
    menu += [num];
    }
llSetTimerEvent(30);       
llDialog(id, out, menu, chan);
}

listing()
{
llOwnerSay("Chat command will work for 60 seconds:");
llSetTimerEvent(60);
llListenRemove(chat_handler); 
chatchan = (integer)llFrand(60000) + 12345;
chat_handler = llListen(chatchan,"","","");

string titel;
integer c;
string output;
string object=llGetObjectName();

    while (c < sum)
    {
    titel = llList2String(lTextures,c);
    output =  "[secondlife:///app/chat/"+(string)chatchan+"/"+(string)c+"/ "+titel+"]";
    integer d = c+1;
    llSetObjectName("# "+(string)d);
    llOwnerSay(output);
    llSetObjectName(object);
    c++;
    }
}

init()
{
sum = llGetInventoryNumber(0);
integer i;
string name;
lTextures = [];

    for(i=0; i<sum; i++)
    {
    name = llGetInventoryName(0, i);
    lTextures += [name];
    } 
}

default
{
    
state_entry()
{
init();
}

timer()
{
llSetTimerEvent(0);
llOwnerSay("Menu/Chat timed out - Touch again");
llListenRemove(cHandle);
llListenRemove(chat_handler); 
}

changed(integer chg)
{
    if(chg & CHANGED_INVENTORY)
    {
    llOwnerSay("Hello "+llKey2Name(llGetOwner())+", change detected - will reset");
    init(); 
    }                   
}

touch_start(integer touchme)
{
idxMenu = 0;
buildMenu(llGetOwner());
}

    
listen(integer channel, string name, key id, string message)
{
llListenRemove(cHandle);
    if (message == "*List*")
    {
    listing();
    }        
    else if (message == "NEXT>>")
    {
    idxMenu++; 
    buildMenu(id);
    }
    else if (message == "<<PREV")
    {
    idxMenu--; 
    buildMenu(id);
    }
    else if (channel == chan)
    {
    NumTex = (integer)message - 1;
    if (NumTex >= 0)
        {
        texture_me();
        }
    }
    else if (channel == chatchan)
    {
    NumTex = (integer)message;
    texture_me();
    }
}

}

 

Link to comment
Share on other sites

5 hours ago, Qie Niangao said:

It seems that CoolVL Viewer has a problem with this but maybe that's intentional. @Henri Beauchamp might say this is all misguided anyway.

The Cool VL Viewer has no problem at all: it simply displays what the script is printing, without hiding the URL...

And you are totally right on the second point: this ”feature” (Wiki-like URL links) was introduced in v2+ viewers, and I indeed chose not to backport it to my viewer, thinking it would be a bad service to the end user, who would be faced with a link without any way to see what the URL it points to actually is... This is unlike in-viewer URLs (such as for resident names which link to their in-viewer profile) or SLURLs (which are strictly verified and also have security features, such as for ”trusted”, ”non-trusted”, ”click-only” sources, etc).

Call me a paranoid old fart 👴, but before I click on a link, I want first to know where it would lead me...

  • Thanks 1
Link to comment
Share on other sites

1 hour ago, Henri Beauchamp said:

Call me a paranoid old fart 👴, but before I click on a link, I want first to know where it would lead me...

Usually one can find where a link is pointing to by hovering over it to get a hover tip, or via some option in its right-click menu. But that's neither here nor there.

  • Like 1
Link to comment
Share on other sites

On 5/2/2024 at 8:42 AM, Qie Niangao said:

Below is an alternative posted more for feedback than as a practical solution to the OP's problem. (

There's a different way to use llDialog, putting clickable responses inside the message text area. It's not ideal for menus with many items having longer names (as here, potentially) because the overhead in each app/chat URI is huge, but it does free-up the dialog buttons for other interactions (here just using them for next and previous page).

It seems that CoolVL Viewer has a problem with this but maybe that's intentional. @Henri Beauchamp might say this is all misguided anyway. I do fret a bit that we've been trying to teach stranger danger to residents about clicking links they shouldn't trust, and if they get used to trusting URIs in dialog boxes they may hurt themselves.

integer DIALOG_CHANNEL = 9; // non-zero single-digit saves string length, more items in menu list
float DIALOG_TIMEOUT = 90.0;
string PREVIOUS = "<<";
string NEXT = ">>";

integer invCount;
integer menuStart;  // inventory index of first element in menu
integer menuEnd = -1;    // inventory index of last element in menu
integer listenHandle;
integer forward?; // is menulist index counting forward from top (1) or backward from bottom (0)?
key toucher;    // does NOT handle interaction with multiple agents simultaneously

string menuList(integer incr)
{
    forward = TRUE;
    string menuString;
    integer itemIdx;
    if (0 < incr)   // fill forwards from previous menuEnd
    {
        menuStart = (menuEnd + 1) % invCount;
        itemIdx = menuStart;
        integer full;
        do
        {
            string itemString = "[secondlife:///app/chat/"+(string)DIALOG_CHANNEL+"/"+(string)(itemIdx-menuStart)+" "
                + llGetInventoryName(INVENTORY_TEXTURE, itemIdx)
                +"]\n"; 
            if (512 > (llStringLength(menuString)+llStringLength(itemString)))
            {
                menuString += itemString;
                menuEnd = itemIdx;
                ++itemIdx;
            }
            else
                full = TRUE;
        } 
        while (!full && (itemIdx < invCount) && (20 > (menuEnd - menuStart)));
    }
    else
    if (0 > incr)   // fill backwards from previous menuStart
    {
        forward = FALSE;
        menuEnd = menuStart - 1;
        if (0 > menuEnd)
            menuEnd = invCount-1;
        itemIdx = menuEnd;
        integer full;
        do
        {
            string itemString = "[secondlife:///app/chat/"+(string)DIALOG_CHANNEL+"/"+(string)(menuEnd-itemIdx)+" "
                + llGetInventoryName(INVENTORY_TEXTURE, itemIdx)
                +"]\n"; 
            if (512 > (llStringLength(menuString)+llStringLength(itemString)))
            {
                menuString = itemString + menuString;
                menuStart = itemIdx;
                --itemIdx;
            }
            else
                full = TRUE;
        } 
        while (!full && (itemIdx >= 0) && (20 > (menuEnd - menuStart)));
    }
    else    // incr == 0, just refill same menu as befor
    {
        itemIdx = menuStart;
        do
            menuString += "[secondlife:///app/chat/"+(string)DIALOG_CHANNEL+"/"+(string)(itemIdx-menuStart)+" "
                + llGetInventoryName(INVENTORY_TEXTURE, itemIdx)
                +"]\n"; 
        while (++itemIdx <= menuEnd);
    }
    return menuString;
}

default
{
    state_entry()
    {
        invCount = llGetInventoryNumber(INVENTORY_TEXTURE);
        if (0 == invCount) state empty;
    }
    changed(integer change)
    {
        if (CHANGED_INVENTORY == change)
            if (invCount != llGetInventoryNumber(INVENTORY_TEXTURE))
                llResetScript();
    }
    touch_start(integer total_number)
    {
        toucher = llDetectedKey(0);
        llListenRemove(listenHandle);   // won't reuse. maybe new toucher
        listenHandle = llListen(DIALOG_CHANNEL, "", toucher, "");
        llSetTimerEvent(DIALOG_TIMEOUT);
        llDialog(toucher, menuList(TRUE), [PREVIOUS, NEXT], DIALOG_CHANNEL);
    }
    timer()
    {
        llListenRemove(listenHandle);
    }
    listen(integer channel, string name, key id, string text)
    {
        integer incr;
        if (NEXT == text)
            incr = 1;
        else
        if (PREVIOUS == text)
            incr = -1;
        else
        {
            integer textureIdx;;
            if (forward?)
                textureIdx = menuStart + (integer)text;
            else
                textureIdx = menuEnd - (integer)text;
            llSetTexture(llGetInventoryName(INVENTORY_TEXTURE, textureIdx), ALL_SIDES);
        }
        llSetTimerEvent(DIALOG_TIMEOUT);
        llDialog(toucher, menuList(incr), [PREVIOUS, NEXT], DIALOG_CHANNEL);
    }
}

state empty
{
    state_entry()
    {
        llWhisper(DEBUG_CHANNEL, "No textures in inventory. Add some.");
    }
    changed(integer change)
    {
        if (CHANGED_INVENTORY == change)
            if (0 != llGetInventoryNumber(INVENTORY_TEXTURE))
                llResetScript();
    }
}

/*
    Wiki says only 20 lines shown in dialog message before it needs a scrollbar.
    Thatn's not my experience, but whatever, 20 lines seem enough, plus
    BAREST minimum of 32 chars in each menu item so will max 512 char limit first anyway.
*/

This script may be buggy as hell; I barely tested it. Also there must be a more elegant way to fill these menus forward and backward but "quick and dirty" exhausted my patience with this for now.

Thank you! this is perfect!

  • Like 1
Link to comment
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
 Share

×
×
  • Create New...