Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

Animation stand..

Teddy Wishbringer
Snuggly Bear Cub
Join date: 28 Nov 2004
Posts: 208
03-06-2006 07:21
Has anyone spotted a freebie script for making a animation stand (not a fixed posing stand for adjusting avie attachments). I'm finding it a royal PITA going through my inventory and having to click a bunch of times to open on animation, play locally, close, open next one, play locally.. etc.

I'm not looking for a vendor, just something I can throw all my animations in so I can cycle through them until I find the one I'm looking for. :) Prefereably one that works with no transfer anims as well, if possible.

Thankies!
Teddy Wishbringer
Snuggly Bear Cub
Join date: 28 Nov 2004
Posts: 208
03-07-2006 07:35
No replies so I sat down at wrote the code last night. It has one annoying bug which I hope someone can help me with. Everything works, but if it runs into a animation it played once already, it stays on the last animation for some reason.

For instance, if I go two animations forward (1 & 2), then one back, it stays on the previous one (2). It's not an issue of going back that seems to be the problem.. it just doesn't play the animation.

If I keep going back, it will play the first one that it hadn't played already.

Very odd behaviour. *sighs*

Anyways, here's the code. I'm making it free and open source. The assembly instructions are located at the bottom of this post. I've tried to document the code clearly, so hopefully some can learn a bit about scripting by following along.

(Stand code)
CODE

// Animation Stand (Stand Script)
// by Teddy Wishbringer
//
// Allows owner to cycle through all the animations stored within the stand by
// clicking on BACK and FORWARD buttons. This script goes in the stand, which
// should be the root prim in the linkset. Linkset requires two additional
// prim buttons (forward and backward) with the forward script and backward
// script in their respective prims.
//
// NOTE: This script is made free, and may be destributed as long as you
// do not charge for it, the comments remain untouched and this script
// remains end-user modifiable.


// User adjustable settings

vector animation_offset = <0,0,1.5>; // Animation offset from stand
string base_float_text = "Now playing animation: "; // Float text prefix
vector color_float_text = <1,1,1>; // Floating text colour

// System settings - Do Not Change Anything Below!

integer animation_qty; // Number of animations available
integer animation_current; // Inventory number of current animation
string animation_name; // Name of current animation
integer animation_on = FALSE; // Current animation status
string float_text; // Variable we'll use when we evaluate the entire floating text message
key avatar; // Who's sitting on me?

update_inventory()
{
llSetText("Updating animation list...", color_float_text,1);
animation_qty = llGetInventoryNumber(INVENTORY_ANIMATION); // Check how many anims we have?
if (animation_qty > 0)
{
llWhisper(0, "Total animations loaded in this stand: " + (string)animation_qty);
}
else
{
llSetText("No Animations Found.. Offline", color_float_text,1);
}
}

// Animate the next animation available (or first one if we're on the last one)
set_anim(integer inventory_number)
{
animation_name = llGetInventoryName(INVENTORY_ANIMATION,inventory_number);
float_text = base_float_text + animation_name + "\n(" + (string)(animation_current + 1) + " of " + (string)animation_qty + ")";
llSetText(float_text, color_float_text,1);

avatar = llAvatarOnSitTarget();

if (avatar != NULL_KEY)
{
llStopAnimation("sit");
llStartAnimation(animation_name);
}
}

default
{

on_rez(integer num)
{
llResetScript(); // Reset the script
}

state_entry()
{
update_inventory(); // Perform an inventory update and determine how many animations we have

if (animation_qty > 0)
{
llSetSitText("Pose");
llSitTarget(animation_offset,ZERO_ROTATION);
animation_name = llGetInventoryName(INVENTORY_ANIMATION, 0);
set_anim(animation_current);
}

}

// If a link message has been received, perform the neccessary function.
link_message(integer sender, integer num, string msg, key id)
{
if (msg == "next") // The next button was pressed
{
animation_current++;
if ((animation_current + 1) > animation_qty) // Test if are are at the last animation or not
{
animation_current = 0;
}
set_anim(animation_current);
}
else if (msg == "back") // The back button was pressed
{
animation_current--;
if (animation_current < 0)
{
animation_current = animation_qty - 1;
}
set_anim(animation_current);
}
}

changed(integer change)
{
// The object's inventory has changed, we need to see if there has been any changes in available animations
if (change == CHANGED_INVENTORY)
{
update_inventory();
}

// The object's sit target has been triggered
if (change == CHANGED_LINK)
{
avatar = llAvatarOnSitTarget();
if(avatar != NULL_KEY)
{
llRequestPermissions(avatar,PERMISSION_TRIGGER_ANIMATION);
}
else
{
if (llGetPermissionsKey() != NULL_KEY)
{
llStopAnimation(animation_name);
}
}
}
}

run_time_permissions(integer perm)
{
if (perm == PERMISSION_TRIGGER_ANIMATION)
{
llStopAnimation("sit");
llStartAnimation(animation_name);
}
}
}


(next button code)
CODE

// Animation Stand (Next Script)
// by Teddy Wishbringer
//
// Allows owner to cycle through all the animations stored within the stand by
// clicking on BACK and FORWARD buttons.
//
// This script goes in the next prim button.
//
// NOTE: This script is made free, and may be destributed as long as you
// do not charge for it, the comments remain untouched and this script
// remains end-user modifiable.

// System settings - Do Not Change Anything Below!

string message = "next"; // Message to send to linkset's root prim

default
{

touch_start(integer num_detected)
{
llMessageLinked(LINK_ROOT,0,message,NULL_KEY);
}

}


(back button code)
CODE
// Animation Stand (Back Script)
// by Teddy Wishbringer
//
// Allows owner to cycle through all the animations stored within the stand by
// clicking on BACK and FORWARD buttons.
//
// This script goes in the back prim button.
//
// NOTE: This script is made free, and may be destributed as long as you
// do not charge for it, the comments remain untouched and this script
// remains end-user modifiable.

// System settings - Do Not Change Anything Below!

string message = "back"; // Message to send to linkset's root prim

state_default()
{

touch_start(integer num_detected)
{
llMessageLinked(LINK_ROOT,0,message,NULL_KEY);
}

}


Instructions:

Make three prims, the base stand, a next button and a back button. Insert the code into the respective prims, and link all three prims with the base stand bring the root prim.

Throw your poses into the contents of the object, and 'sit' on it. Use the buttons to change the animations.

Any assistance in fixing the one bug would be greatly appreciated!
Sky Honey
Coder
Join date: 16 May 2005
Posts: 105
03-07-2006 07:57
I think you need to do an llStopAnimation of the current animation before you start a new one. Avatars don't have a single animation that plays, they can stack up a whole list of them.
_____________________
Argent Stonecutter
Emergency Mustelid
Join date: 20 Sep 2005
Posts: 20,263
03-07-2006 08:38
Here's a simplification, you can do it in one script...
CODE

// Animation Stand (Stand Script)
// by Teddy Wishbringer
//
// Allows owner to cycle through all the animations stored within the stand by
// clicking on BACK and FORWARD buttons. This script goes in the stand, which
// should be the root prim in the linkset. Linkset requires two additional
// prim buttons (forward and backward) with the forward script and backward
// script in their respective prims.
//
// NOTE: This script is made free, and may be destributed as long as you
// do not charge for it, the comments remain untouched and this script
// remains end-user modifiable.


// User adjustable settings

vector animation_offset = <0,0,1.5>; // Animation offset from stand
string base_float_text = "Now playing animation: "; // Float text prefix
vector color_float_text = <1,1,1>; // Floating text colour

// System settings - Do Not Change Anything Below!

integer animation_qty; // Number of animations available
integer animation_current; // Inventory number of current animation
string animation_name; // Name of current animation
string new_animation_name; // Name of next animation.
integer animation_on = FALSE; // Current animation status
string float_text; // Variable we'll use when we evaluate the entire floating text message
key avatar; // Who's sitting on me?

update_inventory()
{
llSetText("Updating animation list...", color_float_text,1);
animation_qty = llGetInventoryNumber(INVENTORY_ANIMATION); // Check how many anims we have?
if (animation_qty > 0)
{
llWhisper(0, "Total animations loaded in this stand: " + (string)animation_qty);
}
else
{
llSetText("No Animations Found.. Offline", color_float_text,1);
}
}

// Animate the next animation available (or first one if we're on the last one)
set_anim(integer inventory_number)
{
new_animation_name = llGetInventoryName(INVENTORY_ANIMATION,inventory_number);
float_text = base_float_text + new_animation_name + "\n(" + (string)(animation_current + 1) + " of " + (string)animation_qty + ")";
llSetText(float_text, color_float_text,1);

avatar = llAvatarOnSitTarget();

if (avatar != NULL_KEY)
{

llStopAnimation(animation_name);
llStartAnimation(new_animation_name);
animation_name = new_animation_name;
}
}

default
{

on_rez(integer num)
{
llResetScript(); // Reset the script
}

state_entry()
{
update_inventory(); // Perform an inventory update and determine how many animations we have

if (animation_qty > 0)
{
llSetSitText("Pose");
llSitTarget(animation_offset,ZERO_ROTATION);
animation_name = llGetInventoryName(INVENTORY_ANIMATION, 0);
set_anim(animation_current);
}

}

// If touched, detect touch location and do the right thing
touch_start(integer num)
{
string where = llGetLinkName(llDetectedLink(0));

if (where == "next") // The next button was pressed
{
animation_current++;
if ((animation_current + 1) > animation_qty) // Test if are are at the last animation or not
{
animation_current = 0;
}
set_anim(animation_current);
}
else if (where == "back") // The back button was pressed
{
animation_current--;
if (animation_current < 0)
{
animation_current = animation_qty - 1;
}
set_anim(animation_current);
}
}

changed(integer change)
{
// The object's inventory has changed, we need to see if there has been any changes in available animations
if (change == CHANGED_INVENTORY)
{
update_inventory();
}

// The object's sit target has been triggered
if (change == CHANGED_LINK)
{
avatar = llAvatarOnSitTarget();
if(avatar != NULL_KEY)
{
llRequestPermissions(avatar,PERMISSION_TRIGGER_ANIMATION);
}
else
{
if (llGetPermissionsKey() != NULL_KEY)
{
llStopAnimation(animation_name);
}
}
}
}

run_time_permissions(integer perm)
{
if (perm == PERMISSION_TRIGGER_ANIMATION)
{
llStopAnimation("sit");
llStartAnimation(animation_name);
}
}
}
For this, you just name the prim holding the "next" button "next", and the "back" button "back". The root prim handles all the touches.
Teddy Wishbringer
Snuggly Bear Cub
Join date: 28 Nov 2004
Posts: 208
03-07-2006 11:15
From: sky Honey
I think you need to do an llStopAnimation of the current animation before you start a new one. Avatars don't have a single animation that plays, they can stack up a whole list of them.


Yeah, I put one in the set_anim() function. Unfortunately I could only go by examples in the Wiki that only showed llStopAnimation("sit";), which 'sit' seemed to be a pre-defined animation. As helpful as the Wiki is, it could use more case examples. :)
Teddy Wishbringer
Snuggly Bear Cub
Join date: 28 Nov 2004
Posts: 208
03-07-2006 11:20
From: Argent Stonecutter
Here's a simplification, you can do it in one script...

For this, you just name the prim holding the "next" button "next", and the "back" button "back". The root prim handles all the touches.

Oooo.. thank you very much! I have to admit, this is only the second script I've written from scratch, so I'm very much still a newbie to LSL. I greatly appreciate the simplifications made. Once I get a chance to test this out tonight, I'll be sure post it in the script library and credit you for the streamlining.

I also noticed you corrected the llStopAnimation() in set_anim(). Presumably this is what I should have done in the first place. *laughs*

Again, thank you very muchly!

Teddy.
Ziggy Puff
Registered User
Join date: 15 Jul 2005
Posts: 1,143
03-07-2006 12:04
A really really tiny nitpick:

CODE
if (change == CHANGED_INVENTORY)


I'd make that (and the one following it):

CODE
if (change & CHANGED_INVENTORY)


Since change is a bitfield, and there is the (infinitesimally small) chance that two change events will show up at the same time.
Teddy Wishbringer
Snuggly Bear Cub
Join date: 28 Nov 2004
Posts: 208
03-07-2006 12:19
From: Ziggy Puff
A really really tiny nitpick


Yes, you are correct.

Two reasons why I didn't, one was because I didn't know about it *laughs*, and now thinking about it further, if someone was to come along and I hope use it as an aid to learn scripting, it might be somewhat confusing to a newbie as what's actually being tested.

The readability of 'if this equals this' is something everyone can wrap their head around, verses using a bitwise operator. But yes, you are correct.. that's what should be used.

I appreciate the input!
Ziggy Puff
Registered User
Join date: 15 Jul 2005
Posts: 1,143
03-07-2006 12:36
From: someone
The readability of 'if this equals this' is something everyone can wrap their head around, verses using a bitwise operator.


That's a good point. The most explicit way of writing that would be:

CODE
if ((change & CHANGED_INVENTORY) == CHANGED_INVENTORY)


That's usually what I write, even though it's kinda redundant. But it makes it clear what you're doing - you're checking a specific bit to see if it is set or not. But yeah, bit fields are a little confusing in the beginning.
Argent Stonecutter
Emergency Mustelid
Join date: 20 Sep 2005
Posts: 20,263
03-07-2006 13:34
From: Ziggy Puff
A really really tiny nitpick:

CODE
if (change == CHANGED_INVENTORY)


I'd make that (and the one following it):

CODE
if (change & CHANGED_INVENTORY)
Oooh, I missed that.

Since this is testing a single bit you don't need to elaborate it to ((change & CHANGED_INVENTORY) == CHANGED_INVENTORY), but it definitely needs & and not ==.
Teddy Wishbringer
Snuggly Bear Cub
Join date: 28 Nov 2004
Posts: 208
03-07-2006 14:51
Hehe - ok, I'll change it then.. I'll just put comments in saying what it's doing instead.
Teddy Wishbringer
Snuggly Bear Cub
Join date: 28 Nov 2004
Posts: 208
03-07-2006 15:04
lslint reports an error with:

CODE
string where = llGetLinkName(llDetectedLink(0));

Which I assume should be instead llDetectedLinkNumber(0), but the wiki doesn't really offer a good explanation of the arguments passed to it. Is there somewhere I can find out more about it? I'm confused by what the zero represents.

The wiki only seems to say:

From: someone
Returns the link position (0 for a non-linked object, 1 for the root (parent) of a linked object, 2 for the first child, etc.) of the object the touch or collision event is triggered for.
Ziggy Puff
Registered User
Join date: 15 Jul 2005
Posts: 1,143
03-07-2006 15:15
The 0 is basically a number between 0 to n - 1, where n is the input parameter to the touch/sensor event. So let's say a sensor scan returns 5 matches, then (for example) llDetectedPos(0) through llDetectedPos(4) will give you the locations of the 5 people scanned.

The same idea works in a touch_start event, but basically, n is always equal to 1, because what are the chances that two people touched your object at the exact same time? So llDetectedPos(0) would be the position of the person who touched your object. llDetectedLinkNumber(0), therefore, is the link number of the child prim in your object that was touched.

The Wiki section you quoted tells you how child prim numbers are assigned. But, you don't care, because you didnt build your object making sure you select the prims in a precise sequence so you know what the link number of a given prim is. And if you do it that way, unlinking/relinking is likely to break your script. However, you did name that prim. And llGetLinkName can be used to get the name of a prim, if you know its number. So basically you get the link number from llDetectedLinkNumber(0) (whatever that number is, you don't care) and then send that to llGetLinkName, and now you have the name of the prim that was touched. Which is much more useful, and your script will still work if the object gets relinked and that prim ends up with a different link number.
Teddy Wishbringer
Snuggly Bear Cub
Join date: 28 Nov 2004
Posts: 208
03-07-2006 15:24
Wow - thank you.. that was a very helpful explanation. :) The Wiki could definately use an explanation like that for all the people like me that have to rely upon it for reference to lsl functions.

Muchly appreciated.
Argent Stonecutter
Emergency Mustelid
Join date: 20 Sep 2005
Posts: 20,263
03-08-2006 07:37
Sorry about the typo... and thanks for explaining it, Ziggy.