low-memory Bento animation script WITHOUT a HUD

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):



integer LISTEN_CHANNEL=43; // channel to listen on
string currentAnim = "Cheeky"; // Put the default you want on rezzing here. Or set to "" to have the script randomize it
string lastAnimState = "";

integer choice = 0; // Leave this value alone.
integer memoryLimit = 12888; // Cap the script to this memory usage. Dummy value replaced by test.

clearAnims()
{
    integer i = 0;
    for(i=0;i<llGetInventoryNumber(INVENTORY_ANIMATION);i++)
    {
        llStopAnimation(llGetInventoryName(INVENTORY_ANIMATION, i));
    }
    testMemory(FALSE); // rerun to avoid stack heap errors.
}

randomAnim()
{
    integer number = llGetInventoryNumber(INVENTORY_ANIMATION);
    float rand = llFrand(number);
    choice = (integer)rand;
}

testMemory(integer verbose)
{
    llScriptProfiler(PROFILE_SCRIPT_MEMORY);
    integer usedMemory = llGetUsedMemory();
    memoryLimit = usedMemory + 500; // a tiny buffer to please my sanity.
    llSetMemoryLimit(memoryLimit);
    
    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 sets it to a maxed value.
        llSetMemoryLimit(memoryLimit);
        if (currentAnim == "")
        {
            randomAnim();
            currentAnim = llGetInventoryName(INVENTORY_ANIMATION, choice);
        }
        llRequestPermissions(llGetOwner(),PERMISSION_TRIGGER_ANIMATION);
        lastAnimState = llGetAnimation( llGetOwner() );
        llListen(LISTEN_CHANNEL,"",llGetOwner(),"");
        testMemory(FALSE);
    }

    run_time_permissions(integer parm)
    {
        if(parm == PERMISSION_TRIGGER_ANIMATION)
        {
            clearAnims();
            llStartAnimation(currentAnim);
            llSetTimerEvent(2.048); // occasional test to see if something stopped it.
        }
    }
    timer() {
        string curAnimState = llGetAnimation( llGetOwner() );
        // It mostly likely freezes from a script in a state change shutting off all animations
        if ( currentAnim != "" && curAnimState != lastAnimState ) {
            clearAnims();
            llStartAnimation(currentAnim);
        }
        lastAnimState = curAnimState;
    }
    on_rez(integer st)
    {
        currentAnim = "";
        lastAnimState = "";
        choice = 0;
        llResetScript();
    }

    attach(key id)
    {
        clearAnims();
    }
    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<llGetListLength(anims);i++)
            {
                response += llList2String(anims, i) + "\n";
            }
            llOwnerSay(response + "-----------------------------------------+\n(This is all sources: your AO, furniture, any HUDs or attachments, etc.)\n");
        }
        else if (msg == "stop")
        {
            clearAnims();
            currentAnim = "";
        }
        else if (msg == "memory" || msg == "test")
        {
            testMemory(TRUE);
        }
        else
        {
            if (msg == "random" || msg == "rnd" || msg == "rng" || msg == "r")
            {
                randomAnim();
            }
            else
            {
                choice = (integer)msg;
                integer number = llGetInventoryNumber(INVENTORY_ANIMATION) -1;
                if (choice > 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. 🙂

Freebie Script release -Visitor Tracking By Parcel Landowner or Region

I put out a new freebie yesterday. I’ve long been frustrated that visitor scripts always gave me most of what I wanted but not exactly what I wanted… so I decided to look and see if writing one would be too complex or not. Figured it all out, and decided to put it out there as a freebie.

You can get it here:

https://marketplace.secondlife.com/p/Visitor-Tracking-By-Parcel-Landowner-or-Region/8125839

The feature set here is simple and yet, I hope, powerful. As configured the script will scan to the parcel limits every 20 seconds and add any names it has never seen before to the list of visitors. So it will only record a person once, and doesn’t do any complex metrics like how long or when or how often. Its just a listing of who has been by before, ever.

When you ask it for the list, it will give them back to you as a link to their profile, shown as “display name (name)”. I’m not aware of any other visitor tracker that gives them back as like like that, and yet that line of the code is directly from the wiki on SL’s LSL scripting language.

VisitorListByParcel

The script also manually updates the list everytime you ask it for the list, right before telling it to you – so that if somebody suddenly appears way out there on your land (and you happen to be near the script) you can use it to just get instant feedback with a link. You can request the list by touching it, or with a chat command to either say the list to just you or to public chat.

And that is fullperms so you can edit it and read what I did. There are some other fullperm visitor scripts out there, but they are very old and use ranged based laggier scanning methods. This one uses a newer LSL command added in 2012 (so not so new actually) that scans by parcels, landowners, or regions. And there are configs in the script to change which of these it will use, as well as how often it will scan.

My included notecard about the script:

This is a free fullperm script to record all visitors to a parcel (or region).

Unlike most visitor recorders this is not based on range, but by parcel or region boundaries.

Names are only recorded once, no matter how often they visit, so this is not a traffic monitor.
The list will reset if the script is resaved or reset.

Names are listed back as profile links, for easy access to information about your visitors.

The owner can either touch the object with the script or say ‘list’ to see the list,
or optionally say ‘listPublic’ to send the list to public chat.

If anyone else touches the object it merely quietly reruns the scan. Otherwise it will scane every ‘scanHowOften’ seconds.

Simply place this script into any object and, as it is currently configured it will scan the current parcel every 20 seconds

You can edit the script and change how often it scans, or change it to scan the region or all parcels in the region owned by the same owner as the current parcel:
integer scanDistance = AGENT_LIST_PARCEL; // this parcel
nteger scanDistance = AGENT_LIST_PARCEL_OWNER; // parcels with the same owner
integer scanDistance = AGENT_LIST_REGION; // this region

Grab a free fullperms copy here:
https://marketplace.secondlife.com/p/Visitor-Tracking-By-Parcel-Landowner-or-Region/8125839

And if you have any suggested modifications / improvements, let me know.

%d bloggers like this: