low-memory Bento animation script WITHOUT a HUD

EDIT: Much of what I do here can also be done using the gestures system. This is for people that want these things without that system. To make this really useful I would want to add a randomizer.

I’m seeing a whole lot of bento stuff now which is great. But I am also seeing a lot of bloated HUDs that you have to wear all the time or your bento parts ‘splay’ or fail to animate or just get downright funky…

I fixed this for myself in my Bento tail. I noticed that everytime I took it off and on again, it broke – which included moments of login, complete outfit changes, and sometimes teleports or ‘those weird lag moments in SL’, or when I sat on furniture that reset animations…

BentoTailScript
So I wrote this (and will explain it below).

Just copy-paste this into a text editor or a script in SL, it’s tiny here to “fit” wordpress’ formating, but will be readable once you put it into noetpad, sublime, textEdit, or an SL script.


// channel to listen on.
// 43 was just a random number that I saw as unused but could remember easy.
integer LISTEN_CHANNEL=43;

// Put the default you want on rezzing here. Or set to "" to have the script randomize it
string currentAnim = "SwingCenterMedium";

// Leave these blank.
string lastAnimState = "";
integer choice = 0;

// Cap the script to this memory usage. Dummy value replaced by test.
// 18888 just happens to be what the test returned the first time I ever wrote
// this function for another script, and has no importance.
integer memoryLimit = 18888;

/*
 * clearAnims
 * This function is critical in memory management and to avoid 'animation conflicts / ghosts'. Without this, old anims
 * can sometimes 'pop back up' and start playing when the avatar transitions to a new state.
 * Sitting/standing, changing sim, etc. There will also be an eventual stack heap collision error if this is not run,
 * once you have 'too many' anims lingering. Note that this is written to specifically ONLY clear out anims that were
 * put onto the avatar by this script - anims residing in the same prim as this script. That is a security choice. We
 * don't want to go messing with other things a user might be trying to do. Besides I was unsure how to get a list of those anims
 * anyway, and didn't see a good reason to research it.
 *
 * I have encountered a few objects made by others in SL that do close all anims, even bento anims, though the objects
 * themselves were made before even mesh existed. Thus I couple this function with a timer that restarts our currentAnim if
 * the right conditions are met. See the timer() function below.
 */
clearAnims()
{
    integer i = 0;
    for(i=0;i<llGetInventoryNumber(INVENTORY_ANIMATION);i++)
    {
        llStopAnimation(llGetInventoryName(INVENTORY_ANIMATION, i));
    }
    // Specific to my brand of tail, this is a deformer that fixes how short the tail I bought was.
    // Put any other needed 'deformer' here, or remove this line.
    llStartAnimation("*LENGTH M");
    testMemory(FALSE); // rerun to avoid stack heap errors.
}

/*
 * This is a function called manually by the chat listener, to 'surprise' us with a random animation choice.
 * I have considered a feature to put this on a timer, effectively making this a cycling AO.
 */
randomAnim()
{
    integer number = llGetInventoryNumber(INVENTORY_ANIMATION);
    float rand = llFrand(number);
    choice = (integer)rand;
}

/*
 * testMemory
 * There is too much memory bloat in scripts in SL.
 * The memory a script shows is allocation, not actual - but allocation is still removal of resources from a sim.
 * So we should always allocate no more than we need.
 * There is a risk that the value set for 'memoryLimit' will be too low. Test results and adjust if there is a stack heap error.
 */
testMemory(integer verbose)
{
    llScriptProfiler(PROFILE_SCRIPT_MEMORY);
    integer usedMemory = llGetUsedMemory();

    // a tiny buffer to please my sanity. Up this by a small amount if you get stack heap errors in testing.
    // Alternatively considering changing the formular to: (usedMemory * 1.2).
    memoryLimit = usedMemory + 2000;
    llSetMemoryLimit(memoryLimit);

    // The code for verbose can be removed on a release version to save memory.
    if (verbose) {
        llOwnerSay("Limited Memory " + (string)llGetMemoryLimit() +
                   "\nUsed Memory " + (string)usedMemory +
                   "\nFree Memory " + (string)llGetFreeMemory());
        llOwnerSay("AnimationController script used at most " + (string)llGetSPMaxMemory() + " bytes of memory during Test.");
    }
    llScriptProfiler(PROFILE_NONE);
}

default
{
    state_entry()
    {
        // The value here is pointless because we change it in testMemory below.
        // But not running this locks it to a maxed value.
        llSetMemoryLimit(memoryLimit);

        if (currentAnim == "")
        {
            randomAnim();
            currentAnim = llGetInventoryName(INVENTORY_ANIMATION, choice);
        }

        llRequestPermissions(llGetOwner(),PERMISSION_TRIGGER_ANIMATION);
        // We need awareness of our current 'pose': standing, sitting, flying, etc.
        // Later when the state changes is when we make sure our animation keeps running.
        lastAnimState = llGetAnimation( llGetOwner() );
        llListen(LISTEN_CHANNEL,"",llGetOwner(),"");
        testMemory(FALSE);
    }

    run_time_permissions(integer parm)
    {
        if(parm == PERMISSION_TRIGGER_ANIMATION)
        {
            clearAnims();
            llStartAnimation(currentAnim);

            // Occasional test to see if something stopped it.
            // 2 seconds was chosen so as to not tax sim resources, but be frequent enough to 'recover' before a typical
            // user is likely to become notably frustrated.
            llSetTimerEvent(2.048);
        }
    }
    timer() {
        string curAnimState = llGetAnimation( llGetOwner() );
        // It mostly likely freezes from a script in a state change shutting off all animations.
        // I have encountered a few objects in SL that do this intentionally even for animation outside of their
        // scope of control. The timer will let the user recover from this IF the user does an animState change.
        if ( currentAnim != "" && curAnimState != lastAnimState ) {
            clearAnims();
            llStartAnimation(currentAnim);
        }
        lastAnimState = curAnimState;
    }
    on_rez(integer st)
    {
        currentAnim = "";
        lastAnimState = "";
        choice = 0;
        llResetScript();
    }

    attach(key id)
    {
        clearAnims();
    }

    /*
     * listen
     * channel - the chat channel used to control the script
     * name - unused
     * id - who has sent a message on the channel
     * msg - the message sent
     *
     * I do not like HUDs that obscure my view of SL. That was the original reason for writing this script.
     * The script's purpose grew when I realized bento animations often shut off on state changes or on login,
     * but it was originally intended to just let me play with my tail without having to click my mouse
     * somewhere where I could not see what was going on because of somebody's giant art project
     * they called a 'HUD'. 🙂
     * All of this functionality can be removed if chat based control is not desired.
     * At that point, the script's value is in the timer and clearAnims functions.
     *
     * Alternatively, chat based control can be left in as an option for people like me who dislike HUDs. Please...
     */
    listen(integer channel, string name, key id, string msg)
    {
        if (msg == "list")
        {
            string response = "\n\nYou have these tail animations:\n+-----------------------------------------+\n";
            integer i = 0;
            for(i=0;i<llGetInventoryNumber(INVENTORY_ANIMATION);i++)
                response += (string)i + ": " + llGetInventoryName(INVENTORY_ANIMATION, i) + "\n";
            llOwnerSay(response + "-----------------------------------------+\n");
        }
        else if (msg == "current")
        {
            list anims = llGetAnimationList(llGetOwner());
            string response = "\n\nYou have these current animations:\n+-----------------------------------------+\n";
            integer i = 0;
            for (i=0;i number)
                {
                    llOwnerSay("Choice invalid. Please use between 0 and " + (string)number);
                    return;
                }
            }
            clearAnims();
            currentAnim = llGetInventoryName(INVENTORY_ANIMATION, choice);
            llOwnerSay("Choice: " + (string)choice + " Anim: " + currentAnim);
            llStartAnimation(currentAnim);
        }
    }
}


How this works is that it finds all the animations in the same prim as itself, runs one of them by default, and using “/43” I get access to some chat commands to change them.

Most importantly though, it polls me everytime I change ‘animation state’ and makes sure my tail animation is running. Second most important… it clears out animations it has control of that might be running still…

See:
clearAnims();
repeated in the code. That’s a function that goes through that same list of animations, and stops them all. Right after I run that, I play my current animation.

Another key thing this does that all your fancy HUDs are not doing for you… is keep it’s memory under control. See:
llSetMemoryLimit(memoryLimit);
This sets the ‘memory allocation’ (what you see when you use something that checks your scripts) to be honest. The script runs a test when it starts to determine how much it should allocate, and then uses a tiny bit over that amount. This does not actually change how much it uses… It is just getting it to report honestly – that will make you look better on script checkers, and it will also let you actually know what you are using. Without this test, SL defaults to divisions of 64… As coded above… it is reporting using about 12kbs.

To get all my bento tail animations… I bought a couple of different tails (I was having a lot of trouble finding one I liked)… and when I didn’t like one, I would copy out its animations to my inventory:
BentoHandScriptsInInventory.png
And then I just edited the tail I did want, dumped them all in there, along with this script. I then picked my favorite and made it the defaul with this line:
string currentAnim = "Cheeky"; // Put the default you want on rezzing here. Or set to "" to have the script randomize it
That is the only part of this script you will have to change. If you make it blank:
string currentAnim = “”;
It will just use the first animation it finds (it looks in alphabetical order) as the default (good if you want to include a no-mod copy of this script in a product).

So to change this script for hands, all I did was change the chat channel and default animation. Then I made one copy for each hand. I put one in a right bento hand, the other in a left bento hand (SelinA mesh body, which is copy/mod perms)

Left hand:
integer LISTEN_CHANNEL=47; // channel to listen on
string currentAnim = "gunl"; // Put the default you want on rezzing here. Or set to "" to have the script randomize it
string lastAnimState = "";

Right hand:
integer LISTEN_CHANNEL=46; // channel to listen on
string currentAnim = "relaxr"; // Put the default you want on rezzing here. Or set to "" to have the script randomize it

And here you can see I just put that and the proper animations into the hand in question:

SelinAHands.png

SelinA Bento Hands – This is SelinA Tina, which is very large in the bosom. There are SelinA bodies all the way from flat chested to this size. And normal or curvy figure. All Copy/Mod.

And now I have the animations I want, without a bloated HUD that has other features I don’t always need:

PussycatDancing - 720p and wide

OK that video’s a little unrelated. But you can see my tail moving about back there. 🙂

Advertisements

%d bloggers like this: