integer hidden = FALSE; // Stores whether the object is visible
vector ROTATION = <0,0,0>; //Euler in degrees (like the edit box)
string TITLE = ""; //This text will appear in the floating title above the ball
string ANIMATION = ""; //Put the name of the pose/animation here
vector offset = <1.17,-0.12,-0.25>; // You can play with these numbers to adjust
// how far the person sits from the ball. ( <X,Y,Z> )
integer use_voice = TRUE;
string gNotecard = "poseBall.conf";
integer gLine = 0;
integer listenHandle;
integer masterswitch = TRUE;
integer visible = TRUE;
float base_alpha = 1.0;
key avatar;
key trigger;
key dataserver_key = "";
rotation rot;
integer lowest = 0x7FFFFFFF;
integer me;
string animation;
show()
{
visible = TRUE;
llSetText(TITLE, <1.0,1.0,1.0>,1.0);
llSetAlpha(base_alpha, ALL_SIDES);
}
hide()
{
visible = FALSE;
llSetText("", <1.0,1.0,1.0>,1.0);
llSetAlpha(0.0, ALL_SIDES);
}
init()
{
if(llGetInventoryType(ANIMATION) != INVENTORY_ANIMATION)
{
if(llGetInventoryNumber(INVENTORY_ANIMATION) == 0) // Make sure we actually got something to pose with.
{
llWhisper(0,"Error: No animation found. Cannot pose."

;
animation = "sit";
}
else
animation = llGetInventoryName(INVENTORY_ANIMATION, 0);
}
else
animation = ANIMATION;
if(llGetInventoryType(gNotecard) == INVENTORY_NOTECARD)
{
dataserver_key = llGetNotecardLine(gNotecard, gLine = 0);
}
else //If we are here no configuration notecard was found... lets use the defaults.
{
llSetSitText(TITLE);
if(visible)
show();
else
hide();
}
}
default
{
state_entry()
{
llSetText("Starting up", <1.0,1.0,1.0>, 1.0);
rot = llEuler2Rot(ROTATION * DEG_TO_RAD);
llSitTarget(offset, rot);
if(use_voice)
listenHandle = llListen(1, "", "", ""

;
me = llGetLinkNumber();
init();
}
link_message(integer sender_num, integer num, string str, key id)
{
if(num == 99)
{
if(str == "hide"

{
masterswitch = FALSE;
hide();
}
else if(str == "show"

{
masterswitch = TRUE;
if(llKey2Name(trigger) == ""

show();
}
if(use_voice)
{
if(lowest > sender_num)
{//only adjust if it is lower then lowest.
lowest = sender_num;
if(sender_num < llGetLinkNumber())
{
llListenRemove(listenHandle);
listenHandle = 0;
}
else if(!listenHandle)
{
listenHandle = llListen(1, "", "", ""

;
llMessageLinked(LINK_ALL_OTHERS, num, "", id);
}
}
}
}
}
touch_start(integer total_number)
{
if(hidden)
{
hidden = FALSE;
llSetAlpha(1.0, ALL_SIDES);
llSetText("", <1,1,1>, 1.0);
}
else
{
hidden = TRUE;
llSetAlpha(0.0, ALL_SIDES);
llSetText("", <0,0,0>, 1.0);
}
}
changed(integer change)
{
if(change & CHANGED_LINK)
{
avatar = llAvatarOnSitTarget();
if(llKey2Name(avatar))
{
if(trigger != avatar)
llRequestPermissions(avatar, PERMISSION_TRIGGER_ANIMATION);
hide();
}
else if(trigger)
{
if(trigger == llGetPermissionsKey())//right user?
if(llGetPermissions() & PERMISSION_TRIGGER_ANIMATION)//got permissions?
if(llKey2Name(trigger)) //user in the sim? modern permision system makes this last check unnecessary.
llStopAnimation(animation);
if(masterswitch)
show();
trigger = "";
}
else if(use_voice)
{
change = llGetLinkNumber();
if(change != me)
{
if(me < change)
{
lowest += change - me;
if(listenHandle)
llMessageLinked(LINK_ALL_OTHERS, 99, "", ""

;
}
else
{
if(!listenHandle)
listenHandle = llListen(1, "", "", ""

;
if(change)//intended to reduce packet storm on large linksets.
llMessageLinked(LINK_ALL_CHILDREN, 99, "", ""

;
}
me = change;
}
}
}
if(change & CHANGED_INVENTORY)
{
llSetText("Reloading configuration...",<1.0,1.0,1.0>,1.0);
init();
}
}
run_time_permissions(integer perm)
{
avatar = llAvatarOnSitTarget();
if(perm & PERMISSION_TRIGGER_ANIMATION && llKey2Name(avatar) != "" && avatar == llGetPermissionsKey())
{
trigger = avatar;
llStopAnimation("sit"

;
llStartAnimation(animation);
if(visible == TRUE)
base_alpha = llGetAlpha(ALL_SIDES);
hide();
}
}
listen(integer channel, string name, key id, string message)
{
if(llStringLength(message) == 4)
{
message = llToLower(message);
if(message == "show"

{
masterswitch = TRUE;
if(llKey2Name(trigger) == ""

show();
}
else if(message == "hide"

{
masterswitch = FALSE;
hide();
}
else
return;
llMessageLinked(LINK_ALL_OTHERS, 99, message, ""

;
}
}
dataserver(key queryid, string data)
{
if(queryid == dataserver_key)
{
if(data != EOF)
{
integer command = llListFindList([";","title", "offset","rotation","voice","sit_button"], [llToLower(llList2String(llParseString2List(data, [" "], [":",";"]),0))]);
if(command > 0)
{
data = llDeleteSubString(data, 0, llSubStringIndex(data,":"

);
if(command == 1)//title
{
if(llGetSubString(data, 0, 0) != " "

TITLE = data;
else
TITLE = llDeleteSubString(data, 0, 0);
llSetSitText(TITLE);
if(visible && masterswitch)
show();
}
else if(command == 2)//offset
{
offset = (vector)data;
if(offset); else
{
if((llSubStringIndex(data, "<"

& 0x7FFFFFFF) < llSubStringIndex(data, ">"

)//could be a valid vector
offset = <0.0, 0.0, 0.01>;
else
{//definately isn't a valid vector
llSay(0,"Error: The numbers in the offset value lack the '<' and '>' signs. (Should be something like <3,1,6> )"

;
offset = <0.0, 0.0, 0.5>;
}
}
rot = llEuler2Rot(ROTATION * DEG_TO_RAD);
llSitTarget(offset, rot);
}
else if(command == 3)//rotation
{
ROTATION = (vector)data;
rot = (rotation)data;
if(<rot.x,rot.y,rot.z> != ROTATION)//allows us to use raw rotations & vectors at the same time.
rot = llEuler2Rot(ROTATION * DEG_TO_RAD);
llSitTarget(offset, rot);
}
else if(command == 4)//voice
{
if(llGetSubString(data, 0, 0) == " "

data = llDeleteSubString(data, 0, 0);
use_voice = (llListFindList(["yes", "true", "on", "lag"], [llToLower(data)]) >= 0);
if(use_voice)
{
if(!listenHandle)
listenHandle = llListen(1, "", "", ""

;
}
else
{
llListenRemove(listenHandle);
listenHandle = 0;
}
}
else if(command == 5)//sit_button
{
if(llGetSubString(data, 0, 0) != " "

llSetSitText(data);
else
llSetSitText(llDeleteSubString(data, 0, 0));
}
}
++gLine;
dataserver_key = llGetNotecardLine(gNotecard, gLine);
}
else
{
if(visible)
show();
else
hide();
}
}
}
}