Open Prim Animator

// Open Prim Animator - by Todd Borst
// Extensive Modifications by SignpostMarv Martin
// Note from Todd to other editors: Please document changes you have made to get proper credit.
// Note from Todd to users: People may have edited the script from since I've posted it originally.
// You can always view the original script by clicking the history tab.
// This is provided AS IS without support. Please don't bug me demanding
// help or custom work for this free script.
// Summary: This is a simple prim animation script. Just add this script
// to your object and a dialog will automatically pop up for you to use.
// Features:
// -Single script "Prim Puppeteer" like animation tool
// -Playback controllable through external scripts
// -Animation is scalable and resizeable
// -On-touch trigger built-in
// -Completely free and open sourced
// License:
// You are welcome to use this anyway you like, even bring to other grids
// outside of Second Life. You are welcomed to sell it if you've made your
// own improvements to it. This is effectively public domain. Have fun.
integer COMMAND_CHANNEL = 32;
integer primCount;
integer commandListenerHandle = ERR_GENERIC;
list posList;
list rotList;
list scaleList;
integer currentSnapshot;
integer recordedSnapshots;
vector rootScale;
vector scaleChange = <1.0, 1.0, 1.0>;
integer maxMemory;
integer freeMemory;
// The values for playAnimationStyle means
// 0 := no animation playing
// 1 := play animation once
// 2 := play animation looping
integer playAnimationStyle;
key op_import = "6b78fcc8-e147-4105-99a6-ff19b4bf559d";
key op_export = "7c2ca168-2b64-4836-8727-8e62b78dbd44";
key op_alter_rootScale = "f9d3389e-a78c-43f8-9e35-c11adec112a5";
// _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
// _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
show_snapshot(integer snapNumber)
{
 if (!snapNumber || recordedSnapshots < snapNumber)
 return;
 vector rootPos = llGetPos();
 rotation rootRot = llGetRot();
 vector pos;
 rotation rot;
 vector scale;
 list params;
 integer i = 2;
 do
 {
 pos = llList2Vector(posList, ((snapNumber - 1)*(primCount - 1)) + (i - 2));
 rot = llList2Rot(rotList, ((snapNumber - 1)*(primCount - 1)) + (i - 2));
 scale = llList2Vector(scaleList, ((snapNumber - 1)*(primCount - 1)) + (i - 2));
 if ( rootScale.x != 1.0 || rootScale.y != 1.0 || rootScale.z != 1.0 )
 {
 pos.x *= scaleChange.x;
 pos.y *= scaleChange.y;
 pos.z *= scaleChange.z;
 scale.x *= scaleChange.x;
 scale.y *= scaleChange.y;
 scale.z *= scaleChange.z;
 }
 params += [PRIM_LINK_TARGET, i,
 PRIM_POSITION, pos,
 PRIM_ROTATION, rot/rootRot,
 PRIM_SIZE, scale
 ];
 if (64 < llGetListLength(params))
 {
 llSetLinkPrimitiveParamsFast(LINK_THIS, params);
 params = [];
 }
 }
 while (++i <= primCount);
 if (llGetListLength(params))
 {
 llSetLinkPrimitiveParamsFast(LINK_THIS, params);
 params = [];
 }
}
playAnimation(float delay, integer loop)
{
 if (delay < 0.1)
 delay = 1.0;
 if (loop == FALSE)
 playAnimationStyle = 1;
 else
 playAnimationStyle = 2;
 if (1 <= recordedSnapshots)
 llSetTimerEvent(delay);
}
showMenuDialog()
{
// return;
 string temp = (string)((float)freeMemory/(float)maxMemory * 100.0);
 string menuText = "Free Memory: " + (string)freeMemory + " (" + llGetSubString(temp, 0, 4) +"%)"
 + "\nSnapshot " + (string)currentSnapshot +" of " + (string)recordedSnapshots
 + "\n\n[ Record ] - Record a snapshot of prim positions"
 + "\n[ Play ] - Play back all the recorded snapshots"
 + "\n[ Publish ] - Finish the recording process"
 + "\n[ Show Next ] - Show the next snapshot"
 + "\n[ Show Prev ] - Show the previous snapshot";
 llDialog(llGetOwner(), menuText,
 ["Record","Play","Publish","Show Prev","Show Next","Loop","Stop","Export"], COMMAND_CHANNEL);
}
string truncate_float(float foo)
{
 if (foo == 0.0)
 return "0";
 else if (foo == (float)((integer)foo))
 return (string)((integer)foo);
 string bar = (string)foo;
 while (llGetSubString(bar, -1, -1) == "0")
 bar = llGetSubString(bar, 0, -2);
 if (llGetSubString(bar, -1, -1) == ".")
 bar = llGetSubString(bar, 0, -2);
 return bar;
}
calc_scaleChange()
{
 if (rootScale != ZERO_VECTOR)
 {
 vector newScale = llGetScale();
 if ( (newScale.x / rootScale.x) != scaleChange.x
 || (newScale.y / rootScale.y) != scaleChange.y
 || (newScale.z / rootScale.z) != scaleChange.z)
 {
 scaleChange.x = newScale.x / rootScale.x;
 scaleChange.y = newScale.y / rootScale.y;
 scaleChange.z = newScale.z / rootScale.z;
 }
 }
}
default
{
 state_entry()
 {
 maxMemory = llGetFreeMemory();
 freeMemory = llGetFreeMemory();
 primCount = llGetNumberOfPrims();
 commandListenerHandle = llListen(COMMAND_CHANNEL,"", llGetOwner(), "");
 showMenuDialog();
 rootScale = llGetScale();
 if (llGetInventoryType("OPA Notecard Import - 2011-11-03") == INVENTORY_SCRIPT){
 llResetOtherScript("OPA Notecard Import - 2011-11-03");
 }
 }
// Feel free to remove this on-touch trigger if you are using your own script to control playback
// touch_start(integer num_detected)
// {
// if (commandListenerHandle == ERR_GENERIC)
// {
// if (playAnimationStyle == 0)
// playAnimation(1.0,TRUE);
// else
// {
// playAnimationStyle = 0;
// llSetTimerEvent((float)FALSE);
// }
// }
// }
 changed(integer change)
 {
 if (change & CHANGED_SCALE)
 calc_scaleChange();
 if (change & CHANGED_LINK)
 {
 if ( primCount != llGetNumberOfPrims() )
 {
 llOwnerSay("Link change detected, reseting script.");
 llResetScript();
 }
 }
 }
 //The message link function is to allow other scripts to control the snapshot playback
 //This command will display snapshot #2:
 // llMessageLinked(LINK_ROOT, 2, "XDshow", NULL_KEY); llSleep(1.0);
 //
 //This command will play through all the recorded snapshots in ascending order. The number "1.0" is the delay speed and can be changed.
 // llMessageLinked(LINK_ROOT, 0, "XDplay", "1.0");
 //
 //This command will loop through all the recorded snapshots in ascending order. The number "1.0" is the delay speed and can be changed.
 // llMessageLinked(LINK_ROOT, 0, "XDplayLoop", "1.0");
 //
 //To stop any playing animation use
 // llMessageLinked(LINK_ROOT, 0, "XDstop", NULL_KEY);
 link_message(integer sender_num, integer num, string str, key id)
 {
 if ("XDshow" == str && 1 <= num && num <= recordedSnapshots)
 show_snapshot(num);
 else if ("XDplay" == str)
 {
 currentSnapshot = 1;
 float delay = (float)((string)id);
 playAnimation(delay, FALSE);
 }
 else if ("XDplayLoop" == str)
 {
 float delay = (float)((string)id);
 playAnimation(delay, TRUE);
 }
 else if ("XDstop" == str)
 {
 playAnimationStyle = 0;
 llSetTimerEvent((float)FALSE);
 }
 else if ("XDexport" == str && !num)
 {
 list export = [];
 string foo;
 vector bar;
 rotation baa;
 string baz;
 integer i = 2;
 integer j = primCount;
 do
 export += [llGetLinkName(i)];
 while (++i <= j);
 llMessageLinked(sender_num, 1, llDumpList2String(export,"|") , op_export);
 export = [];
 i = 0;
 j = llGetListLength(posList);
 do
 {
 bar = llList2Vector(posList,i);
 export += ["<" + truncate_float(bar.x) + ","
 + truncate_float(bar.y) + "," + truncate_float(bar.z) + ">"];
 }
 while (++i < j);
 llMessageLinked(sender_num, 2, llDumpList2String(export,"|") , op_export);
 export = [];
 i = 0;
 j = llGetListLength(rotList);
 do
 {
 baa = llList2Rot(rotList,i);
 export += ["<" + truncate_float(baa.x) + "," + truncate_float(baa.y)
 + "," + truncate_float(baa.z) + "," + truncate_float(baa.s) + ">"];
 }
 while (++i < j);
 llMessageLinked(sender_num, 3, llDumpList2String(export,"|") , op_export);
 export = [];
 i = 0;
 j = llGetListLength(scaleList);
 do
 {
 bar = llList2Vector(scaleList,i);
 export += ["<" + truncate_float(bar.x) + ","
 + truncate_float(bar.y) + "," + truncate_float(bar.z) + ">"];
 }
 while (++i < j);
 llMessageLinked(sender_num, 4, llDumpList2String(export,"|") , op_export);
 }
 else if ("XDmenu" == str)
 {
 showMenuDialog();
 }
 else if ("XDimportLength" == str && 0 < num)
 {
 list foo;
 list bar;
 integer i;
 do
 {
 foo += [ZERO_VECTOR];
 bar += [ZERO_ROTATION];
 }
 while (++i < num);
 posList = foo;
 scaleList = foo;
 rotList = bar;
 llMessageLinked(sender_num,-1,str,op_import);
 recordedSnapshots = num / (llGetNumberOfPrims() - 1);
 llMessageLinked(LINK_SET, recordedSnapshots, "XDrecordedSnapshots", NULL_KEY);
 currentSnapshot = 1;
 }
 else if ("XDrecordedSnapshots" == str && num == -1)
 {
 llMessageLinked(sender_num,recordedSnapshots,str,NULL_KEY);
 }
 else if (id == op_import && 0 <= num)
 {
 list params = llParseString2List(str, ["|"], []);
 vector impPos = (vector)llList2String(params, 0);
 rotation impRot = (rotation)llList2String(params, 1);
 vector impSize = (vector)llList2String(params, 2);
 posList = llListReplaceList(posList, [impPos], num, num);
 rotList = llListReplaceList(rotList, [impRot], num, num);
 scaleList = llListReplaceList(scaleList, [impSize], num, num);
 }
 else if (id == op_alter_rootScale)
 {
 rootScale = (vector)str;
 calc_scaleChange();
 }
 }
 listen(integer channel, string name, key id, string message)
 {
 list parsedMessage = llParseString2List(message, [" "], []);
 string firstWord = llToLower(llList2String(parsedMessage, 0));
 string secondWord = llToLower(llList2String(parsedMessage, 1));
 if ("show" == firstWord && recordedSnapshots > 0)
 {
 llSetTimerEvent((float)FALSE);
 if (secondWord == "next")
 {
 ++currentSnapshot;
 if (recordedSnapshots < currentSnapshot)
 currentSnapshot = 1;
 show_snapshot(currentSnapshot);
 }
 else if (secondWord == "prev")
 {
 --currentSnapshot;
 if (currentSnapshot < 1)
 currentSnapshot = recordedSnapshots;
 show_snapshot(currentSnapshot);
 }
 else
 {
 currentSnapshot = (integer)secondWord;
 if (currentSnapshot && currentSnapshot <= recordedSnapshots)
 {
 show_snapshot(currentSnapshot);
 llOwnerSay("Showing snapshot: " + (string)currentSnapshot);
 }
 else
 {
 llOwnerSay("Invalid snapshot number given: " + (string) currentSnapshot +
 "\nA valid snapshot number is between 1 and " + (string) recordedSnapshots);
 currentSnapshot = 1;
 }
 }
 }
 else if (firstWord == "record")
 {
 vector rootPos = llGetPos();
 integer i = 2;
 do
 {
 vector pos = llList2Vector(llGetLinkPrimitiveParams(i, [PRIM_POSITION]), 0);
 pos.x -= rootPos.x;
 pos.z -= rootPos.z;
 pos.y -= rootPos.y;
 pos = pos / llGetRot();
 posList += pos;
 rotation rot = llList2Rot(llGetLinkPrimitiveParams(i, [PRIM_ROTATION]), 0);
 rot = rot / llGetRot();
 rotList += rot;
 scaleList += llList2Vector(llGetLinkPrimitiveParams(i, [PRIM_SIZE]), 0);
 }
 while (++i <= primCount);
 ++recordedSnapshots;
 llOwnerSay("Total number of snapshots recorded: " + (string)recordedSnapshots);
 freeMemory = llGetFreeMemory();
 }
 else if (firstWord == "play")
 {
 float delay = (float)secondWord;
 currentSnapshot = 1;
 playAnimation(delay, FALSE);
 }
 else if ("publish" == firstWord)
 {
 llSetTimerEvent((float)FALSE);
 playAnimationStyle = 0;
 currentSnapshot = 1;
 llListenRemove(commandListenerHandle);
 commandListenerHandle = -1;
 llOwnerSay("Recording disabled. Publish complete.\nClick me to toggle animation on/off.");
 }
 else if ("loop" == firstWord)
 {
 llMessageLinked(LINK_THIS, 0, "XDplayLoop", NULL_KEY);
 }
 else if ("stop" == firstWord)
 {
 llMessageLinked(LINK_THIS, 0, "XDstop", NULL_KEY);
 }
 else if ("export" == firstWord)
 {
 llOwnerSay("Should be exporting");
 llMessageLinked(LINK_THIS, 0, "XDexport", NULL_KEY);
 }
 if (commandListenerHandle != ERR_GENERIC)
 showMenuDialog();
 }
 timer()
 {
 show_snapshot(currentSnapshot);
 if (currentSnapshot < recordedSnapshots)
 ++currentSnapshot;
 else
 {
 if (playAnimationStyle == 2)
 currentSnapshot = 1;
 else
 {
 llSetTimerEvent((float)FALSE);
 if (commandListenerHandle != ERR_GENERIC)
 showMenuDialog();
 }
 }
 }
}
Open Prim Animator