Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

Changing child prim params using name.

Megano Hashimoto
Registered User
Join date: 22 Aug 2006
Posts: 7
03-12-2009 07:10
Hello. I'm trying to create a single script that would change things like, alpha, in specific prims on a linked set, without having to toss a script on each prim for it. For low prim sets, just checking link numbers and adding these to a set of commands would be enough. But, what about things with many more prims?

A good application would be for when two attachments need to go in a specific part of the body, but you need to change properties on them (like alpha) individually. (A good example, a furry paw with a gun).

I have seen this applied somewhere by using the names of the prims. Thing is, from my root prim script, I can't find a way to send commands to unscripted child prims using anything but link number. And to make matters worse, There's no way for this root prim script to collect the link number on an unscripted child prim, without said childprim being put in a detection way (touch or collision).

So having seen it done, I know there IS a way which so far has escaped me. Anybody here has any ideas?

My idea would be to name some prims like "set_1" and the other prims "set2", and make the script send commands to either set_1 or set_2 individually.

Thanks for any help.
Argent Stonecutter
Emergency Mustelid
Join date: 20 Sep 2005
Posts: 20,263
03-12-2009 07:52
list names;

// call this from state_entry() and changed(c) when c&CHANGED_LINK
init_names()
{
integer n = llGetNumberOfPrims();
integer i;
for(i = 1; i <= n; i++) names += llGetLinkName(i);
}

// call this to get link number.
integer link_by_name(string name)
{
return llListFindList(names, [name]) + 1;
}
_____________________
Argent Stonecutter - http://globalcausalityviolation.blogspot.com/

"And now I'm going to show you something really cool."

Skyhook Station - http://xrl.us/skyhook23
Coonspiracy Store - http://xrl.us/coonstore
Megano Hashimoto
Registered User
Join date: 22 Aug 2006
Posts: 7
03-12-2009 07:57
Thanks. I'll study this. I'll get back to this thread when I apply it fully to what I need.
Megano Hashimoto
Registered User
Join date: 22 Aug 2006
Posts: 7
03-12-2009 08:33
Excuse me for the next question. I have never before worked with this kind of user functions. But, how do i actually call the link_by_name part? Here's what I have done so far of the script. As an example, i'd like it first to give me the link numbers in an llOwnerSay message when I touch the object. A "for" loop I bet is the way to go. But. not sure:

list names;

init_names()
{
integer n = llGetNumberOfPrims();
integer i;
for(i = 1; i = n; i++) names += llGetLinkName(i);
}

integer link_by_name(string name)
{
return llListFindList(names, [name]) + 1;
}

default
{
state_entry()
{
init_names();
}
changed(integer change)
{
if(change & CHANGED_LINK)
{
init_names();
}
}

touch_start(integer total_number)
{

}
}
Argent Stonecutter
Emergency Mustelid
Join date: 20 Sep 2005
Posts: 20,263
03-12-2009 08:51
From: Megano Hashimoto
Excuse me for the next question. I have never before worked with this kind of user functions. But, how do i actually call the link_by_name part?
link_by_name will return the first object that matches the name.

To get all of them you have to do a loop and return a list:

list links_by_name(string name)
{
integer i = llListFindList(names,[name]) + 1;
list l;
list tmp = names;
integer n;
while(i > 0) {
n += i;
l += n;
tmp = llList2List(tmp, i, -1);
i = llListFindList(tmp,[name]) + 1;
}
return l;
}

You probably only want to generate this list when you get a CHANGED_LINK, so you can add a call to this to the end of the init_names() call. Let's say you're looking for prims named "red" or "blue":

Also, note the typo I corrected in init_names. I had "i = n", that should have been "i <= n". You wouldn't be happy with the original version. :)

CODE

list names;
list reds;
list blues;

init_names()
{
integer n = llGetNumberOfPrims();
integer i;
for(i = 1; i <= n; i++) names += llGetLinkName(i);
reds = links_by_name("red");
blues = link_by_name("blue");
}

integer link_by_name(string name)
{
return llListFindList(names, [name]) + 1;
}

default
{
state_entry()
{
init_names();
}
changed(integer change)
{
if(change & CHANGED_LINK)
{
init_names();
}
}

touch_start(integer total_number)
{
integer n;
integer i;
// turn all the reds blue:
n = llGetListLength(reds);
for(i = 0; i < n; i++) {
llSetLinkColor(llList2Integer(reds, i),<0,0,1>,ALL_SIDES);
}
}
}


Optimizing the init function to avoid having the extra list and the search function is left to the reader. :)
_____________________
Argent Stonecutter - http://globalcausalityviolation.blogspot.com/

"And now I'm going to show you something really cool."

Skyhook Station - http://xrl.us/skyhook23
Coonspiracy Store - http://xrl.us/coonstore
Hewee Zetkin
Registered User
Join date: 20 Jul 2006
Posts: 2,702
03-12-2009 09:45
For a more complex example, here's one that puts together all the sets ahead of time, based on the number at the end of each prim name. This has not been compiled or tested, so it may need a few syntax fixes. Hopefully it is at least a good bit of pseudo-code to work from though.

CODE

string DIGITS = "0123456789";


list setNumbers = [];
integer nSets = 0;
list setStartEndIndices = [];
list setLinkNumbers = [];


integer initialized = FALSE;
init()
{
if (initialized)
{
return;
}
initialized = TRUE;

setupLinkSets();
}

integer extractLinkSetSuffix(string name)
{
integer nChars = llStringLength(name);
integer firstDigit = nChars;
while (firstDigit > 0 &&
llSubStringIndex(DIGITS, llGetSubString(firstDigit-1, firstDigit-1))
>= 0)
{
--firstDigit;
}

if (firstDigit < nChars)
{
return (integer)llGetSubString(name, firstDigit, -1);
} else
{
return 0;
}
}

// This function is probably quite slow. However, it is pretty
// memory-efficient and should only need to run once plus when the link set
// changes.
setupLinkSets()
{
setNumbers = [];
nSets = 0;
setStartEndIndices = [];
setLinkNumbers = [];

integer nPrims = llGetNumberOfPrims();
if (nPrims == 1)
{
integer setNum = extractLinkSetSuffix(llGetObjectName());
setNumbers += setNum;
setStartEndIndices += [ 0, 0 ];
setLinkNumbers += [ 0 ];
return;
}

while (llGetAgentSize(llGetLinkKey(nPrims)) != ZERO_VECTOR)
{
--nPrims;
}

integer i;
for (i = 1; i <= nPrims; ++i)
{
integer setNum = extractLinkSetSuffix(llGetLinkName(i));
if (llListFindList(setNumbers, [ setNum ]) < 0)
{
integer setStart = llGetListLength(setLinkNumbers);
integer setEnd = setStart;
setLinkNumbers += i;

integer j;
for (j = i+1; j <= nPrims; ++j)
{
if (extractLinkSetSuffix(llGetLinkName(j)) == setNum)
{
setLinkNumbers += j;
++setEnd;
}
}

setStartEndIndices += [ setStart, setEnd ];
setNumbers += setNum;
++nSets;
}
}
}

// If you don't care about the actual set numbers (the set's "name"), just
// pass this a number from 0 to nSets-1, inclusive.
list getIndexedSetLinkNumbers(integer setIndex)
{
if (setIndex < 0 || setIndex >= nSets)
{
return [];
}

integer setStart = llList2Integer(setStartEndIndices, 2*setIndex);
integer setEnd = llList2Integer(setStartEndIndices, 2*setIndex+1);

return llList2List(setLinkNumbers, setStart, setEnd);
}

// Given a set number, returns a list of all the link numbers that logically
// belong to that set. Pass it the "name" of the set. For example, a prim
// named "door - set 5" would be in the link set "named" 5.
list getLinkSetLinkNumbers(integer setNum)
{
integer index = llListFindList(setNumbers, [ setNum ]);
if (index < 0)
{
return [];
}

return getIndexedSetLinkNumbers(index);
}


state default
{
state_entry()
{
init();

// ...
}

changed(integer changes)
{
if (changes & CHANGED_LINK)
{
setupLinkSets();

// ...
}

// ...
}
}


Implementation note: The setupLinkSets() function is O(n^2). It could probably be made O(n log n), but it's really probably not worth it in LSL as this would require putting together some kind of tree structure. Also, the amount of string processing could be reduced at the cost of O(n) memory by procesing each prim name only once. Maybe this would be worth it, but it depends on how close to the memory limit the script is, how bad the performance actually is, etc.
Megano Hashimoto
Registered User
Join date: 22 Aug 2006
Posts: 7
03-12-2009 12:06
Great. I managed to get on the right track now. I have been able to add prims to my build, name them in a certain way, and make the script affect only those prims I named. I can see quite nice applications to this already. Of course. While I got it to work, I still need to learn the magic behind it so I can do the proper optimizations where and when neccessary(I'm a slow learner but I tend to keep what I get). Thanks for your patience. :)
XSummerX Moonites
Registered User
Join date: 18 Oct 2008
Posts: 4
Had a question bout this, thx
03-30-2009 11:20
From: Argent Stonecutter
link_by_name will return the first object that matches the name.

To get all of them you have to do a loop and return a list:

list links_by_name(string name)
{
integer i = llListFindList(names,[name]) + 1;
list l;
list tmp = names;
integer n;
while(i > 0) {
n += i;
l += n;
tmp = llList2List(tmp, i, -1);
i = llListFindList(tmp,[name]) + 1;
}
return l;
}


CODE

list names;
list reds;
list blues;

init_names()
{
integer n = llGetNumberOfPrims();
integer i;
for(i = 1; i <= n; i++) names += llGetLinkName(i);
reds = links_by_name("red");
blues = link_by_name("blue");
}

integer link_by_name(string name)
{
return llListFindList(names, [name]) + 1;
}

default
{
state_entry()
{
init_names();
}
changed(integer change)
{
if(change & CHANGED_LINK)
{
init_names();
}
}

touch_start(integer total_number)
{
integer n;
integer i;
// turn all the reds blue:
n = llGetListLength(reds);
for(i = 0; i < n; i++) {
llSetLinkColor(llList2Integer(reds, i),<0,0,1>,ALL_SIDES);
}
}
}


Optimizing the init function to avoid having the extra list and the search function is left to the reader. :)

edited with removal of text only for space.ty.

Hi, i tried to PM ya Argent but i think i deleted the PM. :( I'm still learning this forums way of thinking.. lol.. I had a question bout your post above..
I made a test and in the test script..
(1) removed the " link by name" as that was not needed for test.
(2) kept all else you wrote also excluding the lists for red an blue since i made a temp list to hold name of prim i called from my link-set.. all works great but.
it won't find the last prim in the linkset.. i eliminated everything that would have interupted the process but the best i could do is exchange a # to get it to actually find an interpret the last prim ..i was like really happy till i was baffled as to why NOW it : Script run-time error & : Stack-Heap Collision on the Second prim of link set ,wile accepting all the others..
Soo, in conclusion to my Q'n.. why would it ether
(A) memory crash on the last prim an not the rest or
(B) memory crash on the second prim an not the rest.. ether or
i can't seem to get it to work right.. IMHO..the script does exactly what i was hoping for but i would hate to have to add a function to ignore or hide the last prim in linkset just to avoid a memory error if thats what run-time error is..
I was able to conclude that this error ONLY originated when the {{list links_by_name(string name)}} function was called.. as on this test an all others.. everything upto that said function worked perfect.. the {{ init_names() }} worked perfect aswell.. i have no clue though as to why the one function does what it does or how i could fix it without breaking its intended purpose..
any help on this would be GREATLY appreciated as i am soo close to getting this test to work flawlessly with your help but with a lotsa bumps an bruising from racking head on desk. ;-D thx. heres the test i was using. .

[/php]
list names;
list TMPO;
integer x = TRUE;
string name = "Spike";

list links_by_name(string name)
{
integer i = llListFindList(names,[name]) + 1;
list l;
list tmp = names;
integer n;
while(i > 0) {
n += i;
l += n;
tmp = llList2List(tmp, i, -1);
i = llListFindList(tmp,[name]) + 1;
}
return l;
}

init_names()
{
integer n = llGetNumberOfPrims();
integer i;
for(i = 1; i <= n; i++) names += llGetLinkName(i);
TMPO = links_by_name("name";);
}

default
{
state_entry()
{
init_names();
}

touch_start( integer n )
{
x=!x;
if(x)
{
llOwnerSay(" Button selected: " + name
+ "\n # Of prims: " + ((string)llGetNumberOfPrims())
+ "\n # Revised Main list: " + ((string) links_by_name( "path";)));

}
else
{
llOwnerSay(" Button selected: " + name
+ "\n # Of prims: " + ((string)llGetNumberOfPrims())
+ "\n # In Main list: " + ((string) names));
}
}}[/php]

{ note } that I used 6 prims linked with the last linked prim named "path" an the prim named "Spike" could be Any other prim in the same link set, thx.