Tuesday, August 7, 2007

Ring scripts, part 3 - the left hand

// Hand mesh guesser, left hand v1.0 by Allegory Malaprop of Schadenfreude
// This version sized for women's hand 45.

// Instructions:
// The easy way to make this work is to skip down to the hand states (fist_state is the first one), and input where you want your ring to be in each state. Even easier way is to just drop this in your ring and let it run. It assumes the link parent is dead center of a ring on the ring finger, a small invisible sphere if there isn't a nice handy one piece band, and goes from there. You can't move it or rotate it or anything, the script will override any of that sort of thing- so if you want it moved or rotated, you have to set that in the hand states. It also assumes a female hand size of 45, because I found 50 was veering a little towards manhands, at least on my shape. There will be attachment wander with other sizes if you don't code other options, but then, that's typical, isn't it?

// Documentation:
// I have a tendency to not document, a lot, and then I wander off and am distracted by shiny things and forget how to use the language and have no idea what I did. So instead, I'm over documenting so when my brain has leaked out of my ear I can still, maybe, figure out just what it is I was doing. Also, I can be really really wordy in my epistles to the invisible people, even though when it comes to actual conversation I usually don't know what to say. Go figure.

// Distribution:
// This script had a bad experience, locked up in a closet as a child. As such, it wants to be free as the pretty birdies in the sky. So give it to anyone who might be interested in "finger attachments," bundle it up in freebie script give aways, share it with a loved one over a romantic candlelight dinner. Feel free to sell objects that contain this script for whatever your little heart (or wallet) desires, but please do not charge for the script itself, not even a single Linden. This is for many reasons, but the biggest is that this script works best if people believe in it. It's like Tinkerbell in that way. Animation Overrides are very prone to breaking it, and maybe if people use it and like it, they will make AOs that leave the default hand states alone whenever they can. It's not a perfect solution by any means, but unfortunately this script can't actually do anything about knowing the actual hand position beyond its best guess due to limitations in the language currently. See section: Where this breaks. Feel free to modify this script in any way and distribute your modifications, because you may have made it better, which I'm sure can be done in a great many ways.

// What this does:
// This script makes educated guesses about what position an avatar's hand will be in, and allows you to set things for each position. So rings can move with your hand, and it can be adapted for claws and fingernails that will actually move with your fingers. Not perfectly, no, and you may be able to improve upon the code to make it work better, but this should do the heavy lifting of figuring out what the hand position is most likely to be out of the box, and provide ways to do things with it, as well as providing a jumping off point for improvement.

// How it works:
// Well, my little darlings, I figured out the necessary functions by disecting the Franimation Overrider (available for your animation overriding needs at YadNi's Junkyard), in that the important bits are detecting in what animations the avatar is currently running. Now, it is designed to assume that you are using the default hand states, which isn't always true, as they can be overridden, but it is the most likely, and as there is no way to actually detect actual hand positions of which I am aware, this will have to do. Once it makes the guess about what the hand should be, it directs it to a function that tells the object where it should be located, and how it should be rotated (or whatever else you want to add in the functions).

// Where this breaks:
// This makes a lot of assumptions about hand positions, because it cannot actually detect them. Any Animation Overrides that have different hand states included will break this script- your ring will go one way, your hand will go the other. Poseballs are also likely to break it, as they also often override hands, in fact, any animation that overrides default hand state, broken. Drinks and other such hand attachments that have poses attached won't really break it, since they are generally hand attachments- therefore they will simply take the place of the object that has this script in it. Also, cases are added for most built in animations, it's just that custom ones are something we can't know, and the possibility also exists that I missed some of the built in ones. The one advantage to the bug wherein hand states are not being recognized properly, is that default hand states are more likely to be used so we avoid the hand issue a bit more. The disadvantage is that some built in states aren't being handled properly either, so if we actually do what we ought to, it can break. I think it's a bad priority thing, but watch the peace sign or holding a handgun- the hand style breaks when you change to any stands but the base one, I think. It's all sort of a mess, and I mostly just ignored it because it might be temporary. Whatever it is, I treat it as not intentional and did not code ways around it. Also, since these things are in motion, most people probably won't notice the majority of it anyway.

////////////////////////////////// HAND STATES ////////////////////////////////////
// Yes, this is where you have to work if you want to have it be anywhere by the default position. It's actually not that bad. Attach your object on your hand, get it in the place you want for each state, and make a note of the rotation and the position for each. The position is easy, just plug it right in. The rotation is a bit more difficult, as it involves translating the rotation into quaternions, but if you use the Rotation Getter, which may be included in this package, that's pretty easy to just get the numbers and copy paste too. Drop a notecard to Allegory Malaprop if you can't find the Rotation Getter, and I'll drop you a copy when I'm around (sometimes I'm AFK, so don't panic if I don't respond right away. Also, sometimes I crash, so IMs may not be reliable, but you can try that as well).
// If you want to move objects in more than way (claws, fingernails, multiple rings, etc.), you're on your own. This is the framework you need, but you'll have to get the rest of the way yourself because I haven't sat down to figure it out yet.
// Good gestures to check states: Relaxed I just did standing around; Impatient for fists; Wink (Hollywood) is good for pointing; Surprise or waiting around for the stand with the right hand in a fist and the left spread, for spread. You may have to define custom gestures for all of them, I'm not sure any are in the common gestures library.

// What do we do if we think the hand is relaxed?
relaxedState() {
llSetPrimitiveParams([PRIM_POSITION, <-0.014, 0.044, 0.026>, PRIM_ROTATION, <0.52439, -0.49190, -0.44772, 0.53160>]);
// Set position relative to avatar attachment point, in this case -0.014x, -0.044y, 0.026z, and set rotation the same way. This is 106.15 degrees x, 277 degrees y, 19.1 degrees z, translated into quaternion.

// What do we do if we think they have a fist?
fistState() {
llSetPrimitiveParams([PRIM_POSITION, <-0.013, 0.038, 0.007>, PRIM_ROTATION, <0.21548, -0.64547, -0.16706, 0.71346>]);
// Rotation: 51.2x, 276.75y, 19.75z

// What do we do if we think the hand is spread out?
spreadState() {
llSetPrimitiveParams([PRIM_POSITION, <-0.020, 0.045, 0.016>, PRIM_ROTATION, <0.54979, -0.44383, -0.38158, 0.59595>]);
// Rotation: 89.75x, 288.45y, 6z

// What do we do if the hand is pointing?
pointingState() {
llSetPrimitiveParams([PRIM_POSITION, <-0.013, 0.038, 0.007>, PRIM_ROTATION, <0.21548, -0.64547, -0.16706, 0.71346>]);
// Rotation: 51.2x, 276.75y, 19.75z

// What do we do if the hand is typing? Really, this ought to be animated, but you wouldn't have any way to sync up the animation. I'm just referring it to relaxed, because that's close enough for me. You can decide if it's close enough for you.
typeState() {
relaxedState(); // Treat as relaxed


// More functions for each hand state. This is entirely to keep the above code less confusing for the end user, so the only line of code they mess with is the thing they might want to change. Only relaxed and fist are included, because they are the only ones called multiple times, so the others are unnecessary. I'm warning you here about the inconsistency so you know what's going on.
relaxed() { // Relaxed case, calls the state above.
if (lastAnim == "relaxed") { // if that's what we just were doing, do nothing.
} else { // Otherwise, we want the relaxed state.
lastAnim = "relaxed"; // reset lastAnim state so we know what to look for to skip it next time.

fist() { // Fist case, calls the state above.
if (lastAnim == "fist") { // if that's what we just were doing, do nothing.
} else { // Otherwise, we want the fist state.
lastAnim = "fist"; // reset lastAnim state so we know what to look for to skip it next time.

string lastAnim = ""; // The last state in which we decided we were.
// How often are we going to check on the animation state? More often = more lag. So while you can check 0.001, that's hammering the server pretty hard. 0.25 only checks 4 times a second, which, really, is still often enough for what people are going to see, especially with lag and all.
float timerChecking = 0.25;

// Meaty mcMeatFingers, the script that goes through all our possibles and tells us which if the above states to use.
detectHand() {
list curAnims = llGetAnimationList(llGetOwner()); // A list of all the animations currently playing. They're going to be in UUID format, so, well, it's going to be great fun to deal with it all.

// Gestures get checked out first, because they will override everything else. They are cool and like to throw their weight around, pushing to the head of the line, and stealing lunch money from the standing and walking animations.
// We're using funky operands here, the ~. But it saves bytecode, because we love the server and want to keep it happy, and is faster than doing != -1, since if we come up with -1, it doesn't exist. Also because the programming wiki tells us to.

// Typing doesn't appear to actually trigger correctly for some reason.
if (llGetAgentInfo(llGetOwner()) & AGENT_TYPING) { // Are we TYPING? Cause we do that sometimes.
if (lastAnim = "typing") { // if that's what we just were doing, do nothing.
} else { // Otherwise, we want the typing state.
lastAnim = "typing"; // reset lastAnim state so we know what to look for to skip it next time.

// Now let's check for the fist state. I'd be using ||seven more if LSL used them properly, but for some reason it still feels the need to check everything in the list, so they are less useful. Sad really. I'm still lots just because it saves a little space, and out of the hope they'll realize that this way actually is much nicer on the server at some point and change it. Also, I'd run out of ifs otherwise.
} else if (~llListFindList(curAnims, [(key)"eae8905b-271a-99e2-4c0e-31106afd100c"]) // I'm not standing here for you to take SNAPSHOTS.
|| ~llListFindList(curAnims, [(key)"5ea3991f-c293-392e-6860-91dfa01278a3"]) // IMPATIENT already? We've only just started.
|| ~llListFindList(curAnims, [(key)"709ea28e-1573-c023-8bf8-520c8bc637fa"]) // It's a bit early to be JUMPING FOR JOY.
|| ~llListFindList(curAnims, [(key)"315c3a41-a5f3-0ba4-27da-f893f769e69b"]) // I remain unimpressed by the flexing of your sexy MUSCLES.
|| ~llListFindList(curAnims, [(key)"ef62d355-c815-4816-2474-b1acc21094a6"]) // Your invisble BAZOOKA you're HOLDing doesn't scare me!
|| ~llListFindList(curAnims, [(key)"8b102617-bcba-037b-86c1-b76219f90c88"]) // Invisble BOW HOLDing, no more scary.
|| ~llListFindList(curAnims, [(key)"46bb4359-de38-4ed8-6a22-f1f52fe8f506"]) // Nor invisble BOW AIMing.
|| ~llListFindList(curAnims, [(key)"eefc79be-daae-a239-8c04-890f5d23654a"]) // Your ONE-TWO PUNCH doesn't scare me either!
|| ~llListFindList(curAnims, [(key)"f3300ad9-3462-1d07-2044-0fef80062da0"]) // Hey! No beating me up with your LEFT PUNCH!
|| ~llListFindList(curAnims, [(key)"c8e42d32-7310-6906-c903-cab5d4a34656"]) // Nor the RIGHT PUNCH either!
|| ~llListFindList(curAnims, [(key)"49aea43b-5ac3-8a44-b595-96100af0beda"]) // Ok, that's it. I'm not putting up with the abuse of your ROUNDHOUSE KICK.
|| ~llListFindList(curAnims, [(key)"11000694-3f41-adc2-606b-eee1d66f3724"])) // *sigh* Don't throw a TANTRUM. I know you only hurts me cause you loves me baby.)
fist(); // Call fist(), which covers the "are we there now?" and "what do we do again?"

// Only one pointing left hand:
} else if (~llListFindList(curAnims, [(key)"c0c4030f-c02b-49de-24ba-2331f43fe41c"])) // Your HOLLYWOOD WINK gets me every time, and you know it. Cheater.
if (lastAnim == "point") { // if that's what we just were doing, do nothing.
} else { // Otherwise, we want the pointing state.
lastAnim = "point"; // reset
// More batch checking, this time for relaxed hands
} else if (~llListFindList(curAnims, [(key)"18b3a4b5-b463-bd48-e4b6-71eaac76c515"]) // Don't you laugh that deep BELLY LAUGH at me!
|| ~llListFindList(curAnims, [(key)"b906c4ba-703b-1940-32a3-0c7f7d791510"]) // And now you are BORED by my antics. Well then, who needs you!
|| ~llListFindList(curAnims, [(key)"92624d3e-1068-f1aa-a5ec-8244585193ed"]) // Aw, don't CRY. I didn't really mean it.
|| ~llListFindList(curAnims, [(key)"42ecd00b-9947-a97c-400a-bbc9174c7aeb"])) // You aren't really going to prove to me you're a higher spiritual being with your FLOATING YOGA.
relaxed(); // Call relaxed(), which covers the "are we there now?" and "what do we do again?"

// More batch processing for spread
} else if (~llListFindList(curAnims, [(key)"313b9881-4302-73c0-c7d0-0e7a36b6c224"]) // Ha! I knew I could SURPRISE you!
// Left hand only:
|| ~llListFindList(curAnims, [(key)"ea633413-8006-180a-c3ba-96dd1d756720"]) // Eek! Don't AIM that RIFLE at me!
|| ~llListFindList(curAnims, [(key)"35db4f7e-28c2-6679-cea9-3ee108f7fc7f"]) // Moving on to COUNTing for a game of Rock Paper Scissors? You're very competative tonight.
|| ~llListFindList(curAnims, [(key)"42dd95d5-0bc6-6392-f650-777304946c0f"]) // Ha! Take that! My scissors shatter your ROCK.
|| ~llListFindList(curAnims, [(key)"0836b67f-7f7b-f37b-c00a-460dc1521f5a"]) // Rock Paper Scissors, again? My PAPER wraps around your paper!
|| ~llListFindList(curAnims, [(key)"16803a9f-5140-e042-4d7b-d28ba247c325"]) // And again I beat you! My paper wraps your SCISSORS!
|| ~llListFindList(curAnims, [(key)"42b46214-4b44-79ae-deb8-0df61424ff4b"]) // STAND 3 has one hand one hand spread out- the right hand is in a fist instead.
// Both hands:
|| ~llListFindList(curAnims, [(key)"62c5de58-cb33-5743-3d07-9e4cd4352864"])) // Fly away! HOVER UP is both hands spread. Put at the bottom here because it is a lesser priority than the gestures.
if (lastAnim == "spread") { // if that's what we just were doing, do nothing.
} else { // Otherwise, we want the spread state.
lastAnim = "spread"; // reset
// A few of the standing states involve balling hands into fists, as does the run state. So, if we don't get overridden by any of the above, we direct the state now.
} else if (~llListFindList(curAnims, [(key)"370f3a20-6ca6-9971-848c-9a01bc42ae3c"]) // STAND 2 is hands on hips in fists default.
|| ~llListFindList(curAnims, [(key)"05ddbff8-aaa9-92a1-2b74-8fe77a29b445"]) // RUN away!
|| ~llListFindList(curAnims, [(key)"20f063ea-8306-2562-0b07-5c853b37b31e"])) // Dive! Dive! HOVER DOWN!
fist(); // Call fist(), which covers the "are we there now?" and "what do we do again?"

// Catch all for all other states is relaxed hands. We may have missed something up there, but I did try to be exhaustive.
} else {
relaxed(); // Call relaxed(), which covers the "are we there now?" and "what do we do again?"

// Initialize timer, and reset a variable so we don't carry forward the last state erronerously.
initialize() {
lastAnim = "";

// The stuff that actually starts doing things. Because all the above is just waiting impatiently to be chosen by one of the default states, otherwise it just sits there in its corner and rots.
default {
attach(key attached) { // Start the script when it is attached. Otherwise you have to run after this tiny prim object all over a sim as it flees from you. Trust me, it isn't fun.
if (attached != NULL_KEY) {// We are attached! So let's run!
detectHand(); // Run Meaty McMeatFingers!
initialize(); // Set the timer and blank out lastAnim so we don't get into trouble from the last wearing.
} else {// No, really. We aren't attached. Stop the timer.

timer() { // Our timer, so we keep checking states, instead of checking once and them being done with it. Because that would defeat the whole purpose of keeping track where our hands were.
detectHand(); // Run Meaty McMeatFingers at regular intervals!


// aim_r_rifle and stand_3 have different hand states for left/right- fists right, spread left for both. All the rock/paper/scissors also have different hand states- left is always spread.

No comments: