Texture Changer
Put several textures in a prim and click through them, or pick one from a menu. Adding another is dropping another in.
What it does
It reads the textures out of the prim's own Contents and puts one on the face you choose. One click moves to the next, or a menu lets people pick by name, and above nine textures the menu pages itself. When you drop a new texture in, it keeps showing the one that was already up rather than jumping, because it remembers the current texture by its name and not by its position in a list that has just been reordered.
What you can build with it
A picture frame with a dozen paintings in it, the wall colour of a house you are selling, a poster board that changes with the season, a shop sign, a demo of a texture set you are selling, a menu board outside a cafe. With WHOLE_LINKSET a wall built from six prims redecorates in one click.
How to use it
Settings you can change
6Edit these at the top of the script. Everything works on the defaults · change them only when you need to.
// =====================================================================
// Texture Changer
// skimi3d script library
// https://grid.skimi3d.com/scripts/texture-changer
//
// Put several textures in a prim and click through them. The wall
// colour of a house you are selling, the poster in a frame, the
// painting over the fireplace, the sign outside a shop that changes
// with the season.
//
// It reads the textures out of the prim's own Contents, so adding one
// more is dropping one more in. Nothing to edit.
//
// Plain LSL.
// =====================================================================
// ---------------------------------------------------------------------
// Settings
// ---------------------------------------------------------------------
// Which face changes. -1 means every face of the prim. Give a single
// face number to change only the front of a picture frame and leave the
// wood alone.
integer FACE = -1;
// How it is chosen:
// 0 = one click, next texture
// 1 = a menu, so people can pick by name
integer MODE = 0;
// Change every prim in the linkset rather than just this one. Useful
// for a wall built from six prims that should redecorate together.
integer WHOLE_LINKSET = FALSE;
// Which texture it starts on, counting from 0. Contents is kept in
// alphabetical order, so naming them 1 Oak, 2 Pine, 3 Ash puts them in
// the order you meant.
integer START_AT = 0;
// Say the name of each texture as it comes up. Handy while you are
// setting up, noisy afterwards.
integer ANNOUNCE = FALSE;
// Who may change it. 0 = everyone, 1 = only the owner, 2 = same group.
integer WHO_MAY_CHANGE = 0;
// ---------------------------------------------------------------------
// Everything below here is the machinery
// ---------------------------------------------------------------------
float PATIENCE = 60.0; // how long a menu waits for an answer
integer PER_PAGE = 9; // a dialog holds nine buttons and no more
list gTextures = [];
integer gAt = 0;
integer gChannel = 0;
integer gListen = 0;
integer gPage = 0;
list findTextures()
{
list out = [];
integer n = llGetInventoryNumber(INVENTORY_TEXTURE);
integer i;
for (i = 0; i < n; ++i)
{
out += [llGetInventoryName(INVENTORY_TEXTURE, i)];
}
return out;
}
show(integer at)
{
integer n = llGetListLength(gTextures);
if (n == 0) return;
// Wrap both ways, so a click past the end comes back to the start.
while (at < 0) at += n;
while (at >= n) at -= n;
gAt = at;
string tex = llList2String(gTextures, at);
integer link = LINK_THIS;
if (WHOLE_LINKSET) link = LINK_SET;
llSetLinkPrimitiveParamsFast(link, [PRIM_TEXTURE, FACE, tex,
<1.0, 1.0, 0.0>, ZERO_VECTOR, 0.0]);
if (ANNOUNCE) llOwnerSay("Now showing: " + tex);
}
integer privateChannel(key who)
{
return -1 - (integer)("0x" + llGetSubString((string)who, 0, 6));
}
list page(integer n)
{
integer total = llGetListLength(gTextures);
if (total <= PER_PAGE) return gTextures;
integer room = PER_PAGE - 2;
integer pages = (total + room - 1) / room;
if (n < 0) n = pages - 1;
if (n >= pages) n = 0;
gPage = n;
integer from = n * room;
integer to = from + room - 1;
if (to > total - 1) to = total - 1;
return llList2List(gTextures, from, to) + ["< Back", "Next >"];
}
menu(key who, integer n)
{
if (gListen) llListenRemove(gListen);
gChannel = privateChannel(who);
gListen = llListen(gChannel, "", who, "");
llDialog(who, "Which one?", page(n), gChannel);
llSetTimerEvent(PATIENCE);
}
shut()
{
if (gListen) llListenRemove(gListen);
gListen = 0;
gPage = 0;
llSetTimerEvent(0.0);
}
integer mayChange(key who)
{
if (WHO_MAY_CHANGE == 1) return who == llGetOwner();
if (WHO_MAY_CHANGE == 2) return llSameGroup(who);
return TRUE;
}
default
{
state_entry()
{
shut();
gTextures = findTextures();
if (llGetListLength(gTextures) == 0)
{
llOwnerSay("I have no textures in my Contents. Drop some in and I will start working.");
return;
}
show(START_AT);
}
touch_start(integer total)
{
key who = llDetectedKey(0);
if (!mayChange(who))
{
llRegionSayTo(who, 0, "This one is not yours to change.");
return;
}
if (llGetListLength(gTextures) == 0)
{
llRegionSayTo(who, 0, "There is nothing here to show yet.");
return;
}
if (MODE == 1)
{
menu(who, 0);
return;
}
show(gAt + 1);
}
listen(integer channel, string name, key who, string message)
{
if (message == "Next >")
{
menu(who, gPage + 1);
return;
}
if (message == "< Back")
{
menu(who, gPage - 1);
return;
}
shut();
integer at = llListFindList(gTextures, [message]);
if (at != -1) show(at);
}
timer()
{
shut();
}
changed(integer what)
{
// Dropping another texture in should be all it takes. Holding
// the current one by NAME rather than by number means the
// picture on the wall does not jump when a new texture sorts
// itself in ahead of it.
if (what & CHANGED_INVENTORY)
{
string was = llList2String(gTextures, gAt);
gTextures = findTextures();
integer at = llListFindList(gTextures, [was]);
if (at == -1) at = 0;
gAt = at;
}
}
on_rez(integer start)
{
llResetScript();
}
}
Copy it, make a new script inworld, paste over what is there and save. Nothing here phones home.