From 1e5b49a55b7ebd03c6e57be993668f38ac20f841 Mon Sep 17 00:00:00 2001 From: onefang Date: Sun, 30 Jun 2019 11:16:41 +1000 Subject: Include the actual source code this time. --- 1AOor2.lsl | 1835 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1835 insertions(+) create mode 100644 1AOor2.lsl (limited to '1AOor2.lsl') diff --git a/1AOor2.lsl b/1AOor2.lsl new file mode 100644 index 0000000..7b460b6 --- /dev/null +++ b/1AOor2.lsl @@ -0,0 +1,1835 @@ + +// AO and couples interactions. An Animation Overrider with a swimmer and a smiler, which are also AO type things. +// evry byt cnts + +string Version = "1AOor2 v0.1 test version"; + +// BEGIN boilerplate. +integer DEBUG = FALSE; +float Start; +string ScriptName; +key ScriptKey; +key LibraryKey; +key Owner; +string URL; + +// Settings. +list Aliases; +list Settings; +integer sNAME = 0; +integer sTYPE = 1; +integer sVALUE = 2; +integer sAUTH = 3; +integer sSTRIDE = 4; + +// Access "f"rom some source - +integer fINT = 0; +integer fCARD = 1; +integer fCHAT = 2; +integer fLINK = 3; +integer fMENU = 4; +integer fRLV = 5; +integer fRELAY = 6; +integer fOSM = 7; +integer fHTTP = 8; + +// utilities commands, "l"ibrary. +string lSEP = "$!#"; +integer lRESET = -1; +integer lRESET_DONE = -2; +integer lALIAS = -3; +integer lALIAS_DONE = -4; +integer lSETTING = -5; +integer lSETTING_DONE = -6; +integer lSUBSTITUTE = -7; +integer lSUBSTITUTE_DONE = -8; +integer lNEXT_WORD = -9; +integer lNEXT_WORD_DONE = -10; +integer lCONTROL = -13; +integer lCONTROL_DONE = -14; +integer lCMD = -15; +integer lCMD_DONE = -16; +integer lSETTINGS = -17; +integer lSETTINGS_DONE = -18; +integer lSCAN = -19; +integer lDYNAMIC = -20; +integer lMENU = -21; + +// OhSillyThreat detector +list OhSillyThreats = []; +list OhSillyTreats = // OpenSim threat level. +[ +// "osKey2Name", // low +// "osGetAvatarList", // none + "osGetNotecard", // very high (describes what they where when making this decision) +// "osMakeNotecard", // high (describes what they where when making this decision) +// "osGetRezzingObject", // none + "osMessageObject", // low + "osAvatarPlayAnimation", // very high + "osAvatarStopAnimation", // very high + "osForceOtherSit", // very high + "osSetSpeed" // moderate +]; + +d(string m) {if (DEBUG) llInstantMessage(Owner, llGetScriptName() + ": " + m);} +D(string m) {llRegionSay(DEBUG_CHANNEL, llGetScriptName() + ": " + m);} +s(string m) {s(Owner, m);} +s(key id, string m) {if (id == Owner) llOwnerSay(m); else llInstantMessage(id, m);} +sendScript(integer cmd, list args) {sendScript(LibraryKey, cmd, ScriptName, args);} +sendScript(key them, integer cmd, list args) {sendScript(them, cmd, inKey2Name(them), args);} +sendScript(integer cmd, string name, list args) {sendScript(LibraryKey, cmd, name, args);} +sendScript(key them, integer cmd, string name, list args) +{ + llMessageLinked(LINK_SET, cmd, llDumpList2String([ScriptKey, name] + args, lSEP), them); +} +sendPrim(key them, string cmd, list args) +{ + osMessageObject(them, llDumpList2String([ScriptName, cmd] + args, lSEP)); +} +addEvent(float delay, string cmds) +{ + sendScript(lCMD, [fINT, ScriptKey, "TimerEvent", "TIMER", ((string) delay) + " " + cmds]); +} +string inKey2Name(key k) +{ + if (k == LibraryKey) return "1chatter"; + integer i = llGetInventoryNumber(INVENTORY_SCRIPT); + while (i-- > 0) + { + string n = llGetInventoryName(INVENTORY_SCRIPT, i); + if (llGetInventoryKey(n) == k) return n; + } + return k; +} + +integer listFindString(list lst, string name, integer stride) +{ + integer f = llListFindList(lst, [name]); + integer ix = f / stride; + ix = ix * stride; + if ((-1 != f) && (ix != f)) + { + integer l = llGetListLength(lst); + integer i; + f = -1; + for (i = 0; i < l; i += stride) + { + if (llList2String(lst, i) == name) {f = i; i = l;} + } + } + return f; +} + +// Note these next two are different from the ones in 1chatter. +string alias(string n) +{ + list v = validateName(n, "alias"); + n = llList2String(v, 1); + integer a = listFindString(Aliases, llList2String(v, 0) + llToLower(n), 2); + if (-1 != a) n = llList2String(Aliases, a + 1); + return n; +} + +list validateName(string var, string type) +{ + list v = llParseStringKeepNulls(var, ["."], []); + if (2 != llGetListLength(v)) v = ScriptName + v; + var = llList2String(v, 1); + if ("setting" == type) var = llToUpper(var); + return [llList2String(v, 0) + ".", var]; +} + +string getSetting(string var) +{ + list v = validateName(var, "setting"); + var = llList2String(v, 0) + llList2String(v, 1); + string result = ""; + integer f = listFindString(Settings, var, sSTRIDE); + if (-1 != f) result = llList2String(Settings, f + sVALUE); + return result; +} + +setSetting(key id, string var, string val, integer source) +{ + list v = validateName(var, "setting"); + string fr = llList2String(v, 0); + var = fr + llList2String(v, 1); + integer f = listFindString(Settings, var, sSTRIDE); + if (-1 != f) + { + Settings = llListReplaceList(Settings, [val], f + sVALUE, f + sVALUE); + if (llGetSubString(fr, 0, -2) == ScriptName) + { + if (fINT != source) + { + doThing(id, "SET " + var + "=" + val, fr, llList2String(v, 1), val, fINT); + sendScript(lSETTING, [source, id, llList2String(v, 1), val]); + } + } + } +} + +doSettings(key id, list settings) +{ + integer l = llGetListLength(settings); + integer i; + for (i = 0; i < l; i += sSTRIDE) + { + string var = llList2String(settings, i + sNAME); + list v = validateName(var, "setting"); + string fr = llList2String(v, 0); + var = llList2String(v, 1); + string val = llList2String(settings, i + sVALUE); + doThing(id, "SET " + var + "=" + val, fr, var, val, fINT); + } +} + +list readCard(string card) +{ + list r; + card = "~" + card + ".data"; + if (NULL_KEY != llGetInventoryKey(card)) + r = llList2List(llParseStringKeepNulls(osGetNotecard(card), ["\n"], []), 0, -2); + return r; +} + +dynamicMenu(key id, string menu, string name, string title, string entries, string command) +{ + sendScript(lDYNAMIC, [id, menu, name, title, entries, command]); +} + +integer Chosen; +linky(integer num, string message, key id) +{ + if ((id != ScriptKey) && (id != NULL_KEY)) return; + list input = llParseStringKeepNulls(message, [lSEP], []); + key them = llList2Key(input, 0); + string fr = llList2Key(input, 1); +//d("linky " + num + " " + message); + if (lCMD == num) + { +//d("link CHAT " + llDumpList2String(input, " ~ ")); + if ((fr == (ScriptName + ".")) || (fr == "*.")) + { + key a = llList2Key(input, 3); + string button = llList2String(input, 4); + integer r = doThing(a, button, fr, + llList2String(input, 5), llList2String(input, 6), llList2Integer(input, 2)); + sendScript(lCMD_DONE, [button, a, r]); + } + } + else if (lRESET_DONE == num) + { +d("linky RESET_DONE"); + LibraryKey = them; + Settings = llList2List(input, 2, -1); + setSetting(ScriptKey, ScriptName + ".VERSION", Version, fCARD); + sendScript(lSETTINGS, []); + } + else if (lALIAS_DONE == num) Aliases = llList2List(input, 2, -1); + else if (lSETTINGS_DONE == num) + { + Settings = llList2List(input, 2, -1); + doSettings(id, Settings); + laterInit(); + s(Owner, "Finished starting up " + getSetting("VERSION") + " in " + (string) (llGetTimeOfDay() - Start)); + } + else if (DEBUG_CHANNEL == num) + { + key root = llList2Key(llGetObjectDetails(id, [OBJECT_ROOT]), 0); + integer f = llListFindList(OhSillyThreats, [message]); + if (-1 == f) OhSillyThreats += [message]; + else d("OhSillyThreats detected the function " + message + "() again!" ); + D("Oh Silly threat system prevented " + message + "()" + + "\n in " + id + " \t" + llKey2Name(id) + + "\n part of " + root + " \t" + llKey2Name(root)); + } +} +// END boilerplate, mostly. + +integer doThing(key id, string button, string fr, string cmd, string data, integer source) +{ + if ("Set " == llGetSubString(button, 0, 3)) + { + integer set = listFindString(Settings, fr + cmd, sSTRIDE); + if (-1 != set) setSetting(id, fr + cmd, data, fINT); + } + if ((fr != (ScriptName + ".") && ("*." != fr))) return TRUE; + integer st = findSitter(id); + integer f; + string menu; + f = llSubStringIndex(button, "->"); + if (-1 != f) + { + menu = llGetSubString(button, 0, f - 1); + button = llGetSubString(button, f + 2, -1); + } +//d("doThing " + button + " = |" + cmd + "| -> " + data ); + if ("Keys" == cmd) updateControls(); + else if ("CONTROLS" == cmd) + { + list p = llParseStringKeepNulls(data, [","], []); + doControl(llList2Key(p, 0), llList2Integer(p, 1), llList2Integer(p, 2)); + } + else if ("checkAO" == cmd) checkAO(); + else if ("SMILE" == cmd) + { // The built in express_* animations are too short, but live with it. + osAvatarStopAnimation(llGetOwner(), Smile); + Smile = llList2String(Smiles, (integer) llFrand(2.5)); + addEvent(3.0 + llFrand(5.0), cmd); + osAvatarPlayAnimation(llGetOwner(), Smile); + } + else if ("LESS_-" == cmd) + { + integer distance = llList2Integer(Sitters, st + pDIST); + --distance; + if (0 > distance) distance = 0; + Sitters = llListReplaceList(Sitters, [distance], st + pDIST, st + pDIST); + showMenu(id); + return FALSE; + } + else if ("MORE_+" == cmd) + { + integer distance = llList2Integer(Sitters, st + pDIST); + ++distance; + if (llGetListLength(Distances) <= distance) distance = llGetListLength(Distances) - 1; + Sitters = llListReplaceList(Sitters, [distance], st + pDIST, st + pDIST); + showMenu(id); + return FALSE; + } + else if ("NEXT_AVATAR" == cmd) + { + key them = llList2Key(Sitters, st + pADJ); + integer t = findSitter(them); + if (-1 != t) + { + t += pSTRIDE; + if (llGetListLength(Sitters) <= t) + t = -1; + } + else t = 0; + if (-1 != t) + Sitters = llListReplaceList(Sitters, [llList2Key(Sitters, t + pKEY)], st + pADJ, st + pADJ); + else + Sitters = llListReplaceList(Sitters, [ScriptKey], st + pADJ, st + pADJ); + showMenu(id); + return FALSE; + } + else if ( + ("FORWARD" == cmd) || ("BACKWARDS" == cmd) || ("LEFT" == cmd) || ("RIGHT" == cmd) + || ("UP" == cmd) || ("DOWN" == cmd) || ("TURN_LEFT" == cmd) || ("TURN_RIGHT" == cmd)) + { + integer i = llSubStringIndex(cmd, "_"); + if (-1 != i) cmd = llGetSubString(cmd, 0, i - 1) + " " + llGetSubString(cmd, i + 1, -1); + adjust(id, llList2Float(Distances, llList2Integer(Sitters, st + pDIST)), llToLower(cmd)); + } + else if ("AO" == cmd) + { + if ("0" != data) + { + integer l = llGetListLength(Sitters); + Pose = ""; + for (f = 0; f < l; f += pSTRIDE) + updateSitter(llList2Key(Sitters, f + pKEY)); + checkAO(); + } + } + else if ("SYNC" == cmd) + { + integer l = llGetListLength(Sitters); + for (f = 0; f < l; f += pSTRIDE) + { + if (("R" != data) || (0 == llGetListLength(isPoseAO(f)))) + Sitters = llListReplaceList(Sitters, [""], f + pSTATE, f + pSTATE); + } + if ("R" != data) + vAnim = data; + checkAO(); + } + else if ("POSE" == cmd) + { + list p = llParseStringKeepNulls(data, [","], []); + if (Attached) + { + What = llList2String(p, 0); + Whats = llList2List(p, 1, -1); + Chosen = 0; + list b = osGetAvatarList(); + integer l = llGetListLength(b); + integer i; + string keys; + string names; + for (i = 0; i < l; i += 3) + { + keys += "|COUPLE_WITH " + llList2String(b, i); + names += "|" + llList2String(b, i + 2); + } + dynamicMenu(Owner, "couples", What, " Pick someone to '" + What + "' with -", + llGetSubString(names, 1, -1), llGetSubString(keys, 1, -1)); + PIN = (integer) llFrand(DEBUG_CHANNEL - 2) + 2; + vector RefPos = llGetPos(); + rotation RefRot = llGetRot(); + llRezObject("1AOor2 prim", ZERO_VECTOR * RefRot + RefPos, ZERO_VECTOR, (ZERO_ROTATION) * RefRot, PIN); + return FALSE; + } + else + newPose(id, llList2String(p, 0), llList2List(p, 1, -1)); + } + else if ("COUPLE_WITH" == cmd) + { + Stalkee = data; + if (osIsNpc(Stalkee)) + { + Chosen = 1; + s(Owner, llKey2Name(data) + " is an NPC, forcing them to '" + What + "' with you."); + } + else + { + s(Owner, "Asking " + llKey2Name(Stalkee) + " if they would like to do '" + What + "' with you."); + dynamicMenu(Stalkee, "", What, + llKey2Name(Owner) + " would like to do '" + What + "' with you.", + "Yes|No", "COUPLE_YES " + Stalkee + "|COUPLE_NO " + Stalkee); + sendScript(lCMD, "1ring", [fINT, id, Stalkee, "GOTO", Stalkee]); + addEvent(120.0, "COUPLE_TIMEOUT"); + } + return FALSE; + } + else if ("PIN" == cmd) + { + list p = llParseStringKeepNulls(data, [","], []); + integer i = llGetInventoryNumber(INVENTORY_NOTECARD); + TheirKey = id; + while (i-- > 0) + llGiveInventory(id, llGetInventoryName(INVENTORY_NOTECARD, i)); + i = llGetInventoryNumber(INVENTORY_ANIMATION); + while (i-- > 0) + llGiveInventory(id, llGetInventoryName(INVENTORY_ANIMATION, i)); + llGiveInventory(id, "soap-bubble"); + llRemoteLoadScriptPin(id, "1chatter", llList2Integer(p, 0), TRUE, bREZ); + llRemoteLoadScriptPin(id, ScriptName, llList2Integer(p, 0), TRUE, bREZ); + } + else if ("REZ_DONE" == cmd) + { + if (1 == Chosen) + { + sendPrim(TheirKey, "SIT", [Stalkee, What, llDumpList2String(Whats, "|")]); + sendPrim(TheirKey, "COUPLES", [Owner]); + Chosen = 0; + } + else if (-1 == Chosen) killPrim(); + else + addEvent(0.5, cmd); + } + else if ("COUPLES" == cmd) + { + if (NULL_KEY != TheirKey) + sendPrim(TheirKey, "COUPLES", [id]); + else + { + if ("" != data) id = data; + sendScript(lMENU, [id, "couples", ""]); + } + return FALSE; + } + else if ("COUPLE_NO" == cmd) + { + Chosen = -1; + addEvent(0.0, "COUPLE_TIMEOUT"); + sendScript(lCMD, "1ring", [fINT, id, Stalkee, "STOP", ""]); + s(Owner, llKey2Name(id) + " said no to your offer of '" + What + "'."); + } + else if ("COUPLE_YES" == cmd) + { + Chosen = 1; + addEvent(0.0, "COUPLE_TIMEOUT"); + sendScript(lCMD, "1ring", [fINT, id, Stalkee, "STOP", ""]); + s(Owner, llKey2Name(id) + " said yes to your offer of '" + What + "."); + } + else if ("COUPLE_TIMEOUT" == cmd) + { + if (NULL_KEY != Stalkee) + { + Chosen = -1; + s(Owner, llKey2Name(Stalkee) + " didn't answer your offer of '" + What + "."); + s(Stalkee, "You failed to answer " + llKey2Name(Owner) + "'s offer to '" + What + "' with you."); + } + } + else if ("SIT" == cmd) + { + if (!Attached) + { + list det = llGetObjectDetails(Owner, [OBJECT_POS, OBJECT_ROT]); + list p = llParseStringKeepNulls(data, [","], []); + llSetPrimitiveParams([PRIM_POSITION, llList2Vector(det, 0), PRIM_ROTATION, llList2Rot(det, 1)]); + Stalkee = llList2Key(p, 0); + d("Forcing sit of " + llKey2Name(Stalkee) + " (" + Stalkee + ") for " + llList2Key(p, 1)); + // This function only works if no one else is sitting on the object, which is why we sit them first. + // RLV has no such limitation. + if (osIsNpc(Stalkee)) + osNpcSit(Stalkee, llGetKey(), OS_NPC_SIT_NOW); + else + { + llInstantMessage(Stalkee, "Please sit on the big gold heart."); +// osForceOtherSit(Stalkee/*, llGetKey()*/); + } + newPose(id, llList2Key(p, 1), llList2List(p, 2, -1)); + } + } + else if ("SIT_DONE" == cmd) + { +d("SIT_DONE for " + llKey2Name(data)); + if(data == Stalkee) + { + llOwnerSay("@sit:" + TheirKey + "=force"); + s(Owner, "Switching AO to 1AOor2 couples object."); + Controller = Owner; + d("SIT_DONE is requesting camera and controls from " + llKey2Name(Controller)); + llRequestPermissions(Controller, PERMISSION_CONTROL_CAMERA | PERMISSION_TAKE_CONTROLS); + } + } + else if ("DIE" == cmd) die(); + else if ("DIE_DONE" == cmd) + { + TheirKey = NULL_KEY; + if (99.0 <= TPangle) + { + s(Owner, "Switching AO to 1ring object."); + oldController(Owner); + } + } + else if ("ADJUST" == cmd) + { + if (Attached) s("You need to be in a couples interaction with someone to use the ADJUST function."); + else {showMenu(id); return FALSE;} + } + else if ("SAVE" == cmd) + { + if (Attached) s(Owner, "You need to be in a couples interaction with someone to use the SAVE function."); + else sendPrim(BossKey, "SAVE_POSES", [llDumpList2String(reportPose(), "\n")]); + } + else if ("SAVE_POSES" == cmd) + { + integer i = llGetInventoryNumber(INVENTORY_NOTECARD); + list cards = []; + s(Owner, "Backing up old cards."); + while (i-- > 0) + { + string item = llGetInventoryName(INVENTORY_NOTECARD, i); + + if ((llSubStringIndex(item, ".POSITIONS") == 0) ) cards += [llGetInventoryName(INVENTORY_NOTECARD, i)]; + } + i = llGetListLength(cards); + while (i-- > 0) + { + string item = llList2String(cards, i); + osMakeNotecard(".backup" + item, llParseStringKeepNulls(osGetNotecard(item), ["\n"], [])); + llRemoveInventory(item); + } + osMakeNotecard(".POSITIONS", data); + s(Owner, "Current positions saved to the .POSITIONS notecard."); + } + else if ("TPRIM" == cmd) + { + if (Attached) + { + vector cr = <1.0, 0.0 ,0.0> * llGetRootRotation(); + TPangle = llAtan2(cr.x, cr.y); + llOwnerSay("@tpto:" + data + "=force"); + } + else + { + list crl = llParseString2List(data, ["/"], []); + vector cr = ; + integer x = (integer)(cr.x / 256); + integer y = (integer)(cr.y / 256); + cr.x = cr.x % 256; + cr.y = cr.y % 256; + osTeleportAgent(Stalkee, x, y, cr, <1.0,1.0,1.0>); + } + } + else if ("RLV" == cmd) + { + list dt = llParseString2List(data, ["|"], []); +d("RLV command requested " + llList2String(dt, 0)); + llOwnerSay("@" + llList2String(dt, 0)); + if (2 < llGetListLength(dt)) + addEvent(llList2Float(dt, 1), "RLV " + llDumpList2String(llList2List(dt, 2, -1), "|")); + } + else if ("URL" == cmd) + { + URL = data; +d("New URL " + URL); + } + else if ("▲" == cmd) + { + if (Attached) + Chosen = -1; + else + { + if (-1 != st) + { + if ("" != llList2Key(Sitters, st + pADJ)) + { + Sitters = llListReplaceList(Sitters, [""], st + pADJ, st + pADJ); + s(id, "Switched out of adjusting mode."); + addEvent(Tick * Smooth, "Keys"); + } + } + } + } + else if (-1 == listFindString(Settings, fr + cmd, sSTRIDE)) + { + if (fMENU == source) d("Unknown menu command '" + cmd + "' from - " + button); + else d("Unknown command '" + cmd + "' from - " + button); + } + return (source == fMENU); +} + +float TPangle = 999.0; + +killPrim() +{ +d("killPrim"); + if (NULL_KEY != TheirKey) + sendPrim(TheirKey, "DIE", []); + Stalkee = NULL_KEY; + What = ""; + Chosen = 0; +} + +// General variables +integer Attached; +integer Link; +list Distances = [0.01, 0.05, 0.1, 0.5, 1.0, 2.0]; +vector position = <0.0, 0.0, -0.0001>; +vector rotat = ZERO_VECTOR; + +// Rezonater +key BossKey = NULL_KEY; +key TheirKey = NULL_KEY; +integer bREZ = -501; +string HoverText = "loveness"; +string Sit0Text = "carry them"; +string Sit1Text = "be carried"; + +// DrivableBox +list Sitters = []; +integer pKEY = 0; // Key of the sitter. +integer pLINK = 1; // The "prim" link the sitter is. +integer pSTATE = 2; // The current AOstate for this person. +integer pNEW = 3; // New state for this person. +integer pANIMS = 4; // Current set of animations for this person. +integer pADJ = 5; // Key of who they are adjusting, "" means not adjusting, NULL_KEY will mean adjust all. +integer pDIST = 6; // For the adjusting this person is doing, not for who they are adjusting. +integer pKEYS = 7; // KeysLevel for this sitter. +integer pSTRIDE = 8; +list Poses; // List of poses. +integer psNAME = 0; // Name of pose. +integer psANIM = 1; // | separated animations. +integer psEMOTE = 2; // | separated emotions and timers list. +integer psPOSROT= 3; // | separated position and rotation pairs. +integer psSTRIDE= 4; + +// Smiler +integer SmileCounter = 0; +string Smile = "express_toothsmile"; +list Smiles = +[ + "express_smile", + "express_toothsmile", + "express_wink_emote", + "express_tongue_out" +]; + +// Couples +list Whats; +string What; +string Pose; +key Stalkee; +integer PIN; +key Leader = NULL_KEY; +float LeaderOffset = 0.0; +key Controller = NULL_KEY; + +// AO +integer Lag = 100; +integer Swimming; +integer Bobbing; +float Tick = 0.2; +float AOspeed = 1.0; +list flyStates; +list initialStates; // "Taking Off", "hover_up", +list ANIMATIONS; + +integer checkAnim(string a) +{ + if (("" != a) && (NULL_KEY == llGetInventoryKey(a)) && (-1 == llListFindList(ANIMATIONS, [a]))) + { + s(Owner, "Missing animation - " + a + "!"); + return FALSE; + } + return TRUE; +} + +newPose(key id, string p, list ps) +{ + integer f = findPose(p); + integer l = llGetListLength(Sitters); + if (-1 != f) + { + setSetting(id, "AO", "0", fINT); + Pose = p; + Poses = llListReplaceList(Poses, llDumpList2String(ps, "|"), f + psANIM, f + psANIM); + for (f = 0; f < l; f += pSTRIDE) + updateSitter(llList2Key(Sitters, f + pKEY)); + checkAO(); + } +} + +integer findSitter(key id) {return listFindString(Sitters, id, pSTRIDE);} + +string prStr(string str) +{ + integer ix = llSubStringIndex(str, ">"); + vector p = (vector) llGetSubString(str, 0, ix); + vector r = (vector) llGetSubString(str, ix + 1, -1); + return vround(p, r); +} + +string vround(vector p, vector r) +{ // OpenSim likes to swap these around, which triggers the ball movement saving. + if (-179.9 >= r.x) r.x += 360.0; if (-179.9 >= r.y) r.y += 360.0; if (-179.9 >= r.z) r.z += 360.0; + if ( 179.9 <= r.x) r.x -= 360.0; if ( 179.9 <= r.y) r.y -= 360.0; if ( 179.9 <= r.z) r.z -= 360.0; + return "<" + round(p.x, 3) + "," + round(p.y, 3) + "," + round(p.z, 3) + + "><" + round(r.x, 1) + "," + round(r.y, 1) + "," + round(r.z, 1) + ">"; +} + +string round(float number, integer places) +{ + float shifted; + integer rounded; + string s; + shifted = number * llPow(10.0, (float) places); + rounded = llRound(shifted); + s = (string) ((float) rounded / llPow(10.0, (float)places)); + rounded = llSubStringIndex(s, "."); + if (-1 != rounded) s = llGetSubString(s, 0, llSubStringIndex(s, ".") + places); + else + { + s += ".00000000"; + s = llGetSubString(s,0,llSubStringIndex(s, ".") + places); + } + return s; +} + +readPos(list cards) +{ + integer i; + integer l = llGetListLength(cards); + cards = llListSort(cards, 1, TRUE); + for (i = 0; i < l; i++) + { + string card = llList2String(cards, i); + list crd = llParseStringKeepNulls(osGetNotecard(card), ["\n"], []); + integer m = llGetListLength(crd); + integer j; + d("Reading '" + card + "'."); + for (j = 0; j < m; j++) + { + string data = llList2String(crd, j); + if (llGetSubString(data, 0, 0) != "/") + { // skip comments + data = llStringTrim(data, STRING_TRIM); + integer ix = llSubStringIndex(data, "{"); + integer jx = llSubStringIndex(data, "} <"); + if (ix != -1 && jx != -1) + { + string name = llStringTrim(llGetSubString(data, ix + 1, jx - 1), STRING_TRIM); + string ldata = llGetSubString(data, jx + 2, -1); + list posrots = llParseString2List(ldata, ["<"], []); + string pr = ""; + jx = llGetListLength(posrots); + for (ix = 0; ix < jx; ix += 2) + pr += "|" + prStr("<" + llStringTrim(llList2String(posrots, ix), STRING_TRIM) + + "<" + llStringTrim(llList2String(posrots, ix + 1), STRING_TRIM)); + savePose(name, "", "", llGetSubString(pr, 1, -1)); + } + } + } + } +} + +integer findPose(string name) {return listFindString(Poses, name, psSTRIDE);} + +savePose(string name, string anim, string exp, string posRot) +{ + integer f = findPose(name); + posRot = normPose(posRot); + if (-1 != f) + { + if ("" == anim) + { + anim = llList2String(Poses, f + psANIM); + exp = llList2String(Poses, f + psEMOTE); + } + else if ("" == posRot) posRot = llList2String(Poses, f + psPOSROT); + Poses = llListReplaceList(Poses, [name, anim, exp, posRot], f, f + psSTRIDE - 1); + } + else + Poses += [name, anim, exp, posRot]; +} + +string normPose(string posRot) +{ + // Subtract the x,y of the first from both to normalise it around the prim. + // NOTE - only handles the first two. + list p = llParseStringKeepNulls(posRot, ["|"], []); + string pr0 = llList2String(p, 0); + integer jx0 = llSubStringIndex(pr0, "><"); + vector minp = (vector) llGetSubString(pr0, 0, jx0); + vector minr = (vector) llGetSubString(pr0, jx0 + 1, -1); + string pr1 = llList2String(p, 1); + integer jx1 = llSubStringIndex(pr1, "><"); + vector maxp = (vector) llGetSubString(pr1, 0, jx1); + vector maxr = (vector) llGetSubString(pr1, jx1 + 1, -1); + maxp.x = maxp.x - minp.x; + maxp.y = maxp.y - minp.y; + minp.x = 0.0; minp.y = 0.0; + maxr.z = maxr.z - minr.z; + minr.z = 0.0; + return prStr(((string) minp) + ((string) minr)) + "|" + prStr(((string) maxp) + ((string) maxr)); +} + +list reportPose() +{ + list result = []; + integer l = llGetListLength(Poses); + integer i; + for (i = 0; i < l; i += psSTRIDE) + { + list prs = llParseString2List(llList2String(Poses, i + psPOSROT), ["|"], []); + string r = "{" + llList2String(Poses, i) + "} "; + integer m = llGetListLength(prs); + integer j; + + for (j = 0; j < m; j++) + r += llList2String(prs, j); + result += [r]; + } + return result; +} + +lookAtMe(key id) +{ + if ((llGetPermissionsKey() == id) && (llGetPermissions() & PERMISSION_CONTROL_CAMERA)) + { + d("setting camera for " + llKey2Name(id)); + llClearCameraParams(); + llSetCameraParams( + [ CAMERA_ACTIVE, 1, CAMERA_FOCUS_OFFSET, <0.0, 0.0, 1.0>, CAMERA_PITCH, 12.5, + CAMERA_BEHINDNESS_ANGLE, 0.1, CAMERA_BEHINDNESS_LAG, 0.0, CAMERA_DISTANCE, 2.75, + CAMERA_FOCUS_LAG, 0.0 , CAMERA_FOCUS_THRESHOLD, 0.0, + CAMERA_POSITION_LAG, 0.0, CAMERA_POSITION_THRESHOLD, 0.0 + ]); + } + else + { + if (Controller == id) + { + d("lookAtMe() requesting camera and controls from " + llKey2Name(id)); + llRequestPermissions(id, PERMISSION_CONTROL_CAMERA | PERMISSION_TAKE_CONTROLS); + } + else + { + d("lookAtMe() requesting camera from " + llKey2Name(id)); + llRequestPermissions(id, PERMISSION_CONTROL_CAMERA); + } + } +} + +float ControlTime = 0.0; +integer HeldKeys; +integer LevelKeys; +doControl(key id, integer level, integer edge) +{ + if (99.0 > TPangle) return; + if (NULL_KEY != TheirKey) + { + sendPrim(TheirKey, "CONTROLS", [id, level, edge]); + if ("Sitting" != llGetAnimation(id)) + oldController(id); + return; + } +// integer start = level & edge; +// integer end = ~level & edge; + integer held = level & ~edge; +// integer untouched = ~(level | edge); +// llSay(0, "doControl " + llKey2Name(id) + " " + llList2CSV([level, edge, start, end, held, untouched])); + integer f = findSitter(id); + if (-1 != f) + { + integer a = ("" != llList2Key(Sitters, f + pADJ)); + if ((held == (CONTROL_BACK | CONTROL_FWD)) || + (held == (CONTROL_DOWN | CONTROL_UP)) || + (held == (CONTROL_LEFT | CONTROL_RIGHT)) || + (held == (CONTROL_ROT_LEFT | CONTROL_ROT_RIGHT))) + { + if ((llGetTimeOfDay() - ControlTime) >= 1.0) + { + if (held != HeldKeys) + { + if (a) + { + Sitters = llListReplaceList(Sitters, [""], f + pADJ, f + pADJ); + s(id, "Switched out of adjusting mode."); + addEvent(Tick * Smooth, "Keys"); + } + else + showMenu(id); + } + HeldKeys = held; + } + level = 0; + } + else if (0 != HeldKeys) + { + HeldKeys = 0; + level = 0; + } + Sitters = llListReplaceList(Sitters, [level], f + pKEYS, f + pKEYS); + if (0 != edge) ControlTime = llGetTimeOfDay(); + if (level != LevelKeys) + updateControls(); + LevelKeys = level; + } +} + +float SPEED = 3.8; +float ROTAT = 8.0; +float Smooth = 0.2; // 0.2 is good. +string vAnim; +updateControls() +{ + if (Attached) return; + integer mode = vNONE; integer reverse = 1; float speed = 0.0; float rot = 0.0; + integer l = llGetListLength(Sitters); integer i; + integer keys; + for (i = l - pSTRIDE; i >= 0; i -= pSTRIDE) + { + key k = llList2Key(Sitters, i + pKEY); + keys = llList2Integer(Sitters, i + pKEYS); + if ("" == llList2Key(Sitters, i + pADJ)) + { + if (keys & (CONTROL_DOWN |CONTROL_UP)) mode = vUP; + if (keys & (CONTROL_BACK | CONTROL_FWD)) mode = vFWD; + if (keys & (CONTROL_LEFT | CONTROL_RIGHT)) mode = vLEFT; + if (keys & (CONTROL_ROT_LEFT | CONTROL_ROT_RIGHT)) mode = vFWD; + if (keys & (CONTROL_BACK | CONTROL_DOWN | CONTROL_RIGHT)) + { + speed = 0.0 - (SPEED * Smooth); + reverse = -1; + } + if (keys & (CONTROL_FWD | CONTROL_UP | CONTROL_LEFT)) speed = SPEED * Smooth; + if (keys & CONTROL_ROT_LEFT) rot = (reverse * PI) / (ROTAT / Smooth); + if (keys & CONTROL_ROT_RIGHT) rot = ((0 - reverse) * PI) / (ROTAT / Smooth); + } + else + { + float d = llList2Float(Distances, llList2Integer(Sitters, i + pDIST)); + if (keys & CONTROL_FWD) adjust(k, d, "forward"); + if (keys & CONTROL_BACK) adjust(k, d, "backwards"); + if (keys & CONTROL_LEFT) adjust(k, d, "left"); + if (keys & CONTROL_RIGHT) adjust(k, d, "right"); + if (keys & CONTROL_ROT_LEFT) adjust(k, d, "turn left"); + if (keys & CONTROL_ROT_RIGHT) adjust(k, d, "turn right"); + if (keys & CONTROL_UP) adjust(k, d, "up"); + if (keys & CONTROL_DOWN) adjust(k, d, "down"); + } + } + updateVehicle(keys, mode, speed * AOspeed, rot); + checkLag(); + float t = Tick; + if (vNONE != mode) addEvent(t * Smooth, "Keys"); + else if (vFALL == vMODE) addEvent(t * Smooth * 1.5, "Keys"); +} + +adjust(key id, float dist, string direction) +{ + integer f = findSitter(id); + integer i = -1; + list p; + i = findPose(Pose); + if (-1 != i) + p = llParseStringKeepNulls(llList2String(Poses, i + psPOSROT), ["|"], []); + else + return; + if (-1 != f) + { + key them = llList2Key(Sitters, f + pADJ); + integer l; integer t; + if (ScriptKey == them) + { + t = 0; + l = llGetListLength(Sitters); + } + else + { + t = findSitter(them); + l = t + pSTRIDE; + } + if (-1 != t) + { + if (("turn left" == direction) || ("turn right" == direction)) dist = dist * 90.0; + for (; t < l; t += pSTRIDE) + { + them = llList2Key(Sitters, t + pKEY); + integer lnk = llList2Integer(Sitters, t + pLINK); + if (-1 == lnk) return; + string prn = llList2String(p, t / pSTRIDE); + integer ix = llSubStringIndex(prn, ">"); + vector pos = (vector) llGetSubString(prn, 0, ix); + vector rot = (vector) llGetSubString(prn, ix + 1, -1); + if ("forward" == direction) pos.x = pos.x + dist; + else if ("backwards" == direction) pos.x = pos.x - dist; + else if ("left" == direction) pos.y = pos.y + dist; + else if ("right" == direction) pos.y = pos.y - dist; + else if ("up" == direction) pos.z = pos.z + dist; + else if ("down" == direction) pos.z = pos.z - dist; + else if ("turn left" == direction) rot.z = rot.z + (dist); + else if ("turn right" == direction) rot.z = rot.z - (dist); + p = llListReplaceList(p, [vround(pos, rot)], t / pSTRIDE, t / pSTRIDE); + Poses = llListReplaceList(Poses, [llDumpList2String(p, "|")], i + psPOSROT, i + psPOSROT); + updateSitter(them); + s(id, llKey2Name(id) + " adjusts " + llKey2Name(them) + " by " + (string) dist + " " + direction); + if (id != them) + s(them, llKey2Name(id) + " adjusts " + llKey2Name(them) + " by " + (string)dist + " " + direction); + } + } + } +} + +updateSitter(key id) +{ // Written by Strife Onizuka, size adjustment provided by Talarus Luan + // Using this while the object is moving may give unpredictable results. + integer f = findSitter(id); + if (-1 != f) + { + integer lnk = llList2Integer(Sitters, f + pLINK); + if (-1 == lnk) return; + vector pos = position; + rotation rot = llEuler2Rot(rotat * DEG_TO_RAD); + integer i = findPose(Pose); + if (-1 != i) + { + string prn = llList2String(llParseStringKeepNulls(llList2String(Poses, i + psPOSROT), ["|"], []), f / pSTRIDE); + integer ix = llSubStringIndex(prn, ">"); +// TODO - might be wrong, coz rotations are hard, m'kay. + pos = (vector) llGetSubString(prn, 0, ix); + rot = llEuler2Rot((vector) llGetSubString(prn, ix + 1, -1) * DEG_TO_RAD); + } + vector size = llGetAgentSize(id); + integer prim = llGetLinkNumber(); + // We need to make the position and rotation local to the current prim. + vector localpos = ZERO_VECTOR; + rotation localrot = ZERO_ROTATION; + + if (1 < prim) // Only need the local rot if it's not the root. + { + list local = llGetLinkPrimitiveParams(prim, [PRIM_POS_LOCAL, PRIM_ROT_LOCAL]); + localpos = llList2Vector(local, 0); + localrot = llList2Rot(local, 1); + } + pos += <0.0, 0.0, 0.2>; // Fudge it. Pffft + // <0.008906, -0.049831, 0.088967> are the coefficients for a parabolic curve that + // best fits real avatars. It is not a perfect fit. + float fAdjust = ((((0.008906 * size.z) + -0.049831) * size.z) + 0.088967) * size.z; + vector fa = llRot2Up(rot) * fAdjust; + llSetLinkPrimitiveParamsFast(lnk, + [ + PRIM_POS_LOCAL, ((pos - fa) * localrot) + localpos, + PRIM_ROT_LOCAL, rot * localrot // This does rotate around the avatar's centre, not the prims centre. + ]); + } +} + +showMenu(key id) +{ + integer f = findSitter(id); + if (-1 != f) + { + key them = llList2Key(Sitters, f + pADJ); + string name; + integer distance = llList2Integer(Sitters, f + pDIST); + if ("" == them) + { + them = id; + Sitters = llListReplaceList(Sitters, [them], f + pADJ, f + pADJ); + s(id, "Switched to adjusting mode. Click '▲ Exit' on the adjusting menu to switch back to moving."); + } + if (ScriptKey == them) + name = "all"; + else + name = llKey2Name(them); + sendScript(lMENU, [id, "adjust", "Adjusting position of " + name + + ". \nAdjust by " + llList2String(Distances, distance) + + ", \nusing menu or movement keys."]); + } +} + +integer vMODE; +integer vNONE = 0; +integer vFWD = 1; +integer vLEFT = 2; +integer vUP = 3; +integer vCROUCH = 4; +integer vGROUND = 5; +integer vFALL = 6; +float vTHEN; + +float LastCast; +integer CastOut; +float cast(vector gp, vector pos, float dist) +{ + vector start = gp + <0.0, 0.0, 0.0>; + vector end = pos + <0.0, 0.0, -llFabs(dist * 1.1)>; + gp = pos - gp; + float g = llGround(); + if (0 <= CastOut) + { + list results = llCastRay(start, end, [ + RC_REJECT_TYPES, RC_REJECT_AGENTS | RC_REJECT_PHYSICAL | RC_REJECT_LAND, + RC_DATA_FLAGS, RC_GET_ROOT_KEY, + RC_MAX_HITS, 2] ); + CastOut = llList2Integer(results, -1); + if (0 < CastOut) + { + vector p = llList2Vector(results, 1); + if (llGetKey() == llList2Key(results, 0)) + { + if (1 < CastOut) + { + p = llList2Vector(results, 3); + LastCast = p.z + 0.1; + } + else + LastCast = g + 0.1; + } + else + LastCast = p.z + 0.1; + } + else if (0 == CastOut) + LastCast = g + 0.1; +/* + else + { + if (RCERR_UNKNOWN == CastOut) d("llCastRay() failed for an unspecified reason."); + else if (RCERR_SIM_PERF_LOW == CastOut) d("llCastRay() failed coz sim performance is low."); + else if (RCERR_CAST_TIME_EXCEEDED == CastOut) d("llCastRay() failed coz too many raycasts."); + else d("llCastRay() returned unknown error code " + CastOut); + } +*/ + } + else CastOut += 1; + if (g > LastCast) LastCast = g + 0.1; + return LastCast - 0.1; +} + +updateVehicle(integer keys, integer mode, float move, float rotate) +{ + vector pos = llGetPos(); + vector gp = pos; + vector rotvec = llRot2Euler(llGetRot()); + if (Attached || (0.0 == LeaderOffset)) return; + vAnim = "Standing"; + if (0.0 < vTHEN) + { + if (3.0 > (llGetTimeOfDay() - vTHEN)) + { + pos.z = cast(gp, pos, 4.0) + LeaderOffset; + llSetPrimitiveParams([PRIM_POSITION, pos]); + return; + } + } + if (vUP == vMODE) + { + if (vNONE == mode) vAnim = "Hovering"; + else if (vFWD == mode) + { + vAnim = "Flying"; + pos += (<(llCos(rotvec.z)) * move, (llSin(rotvec.z)) * move, 0.0>); + } + else if (vUP == mode) + { + pos += (<0.0, 0.0, move>); + if (0.0 < move) vAnim = "Hovering Up"; + else vAnim = "Hovering Down"; + } + if (cast(gp, pos, move / Smooth) > (pos.z - LeaderOffset)) + { + vAnim = "Soft Landing"; vMODE = vFWD; + vTHEN = llGetTimeOfDay(); addEvent(3.0, "SYNC Standing"); + } + } + else if (vFALL == vMODE) + { + vAnim = "Falling"; pos.z -= 1.0; + if (cast(gp, pos, move / Smooth) > (pos.z - LeaderOffset)) + { + vAnim = "Standing Up"; vMODE = vFWD; + vTHEN = llGetTimeOfDay(); addEvent(3.0, "SYNC Standing"); + } + } + else if ((vFWD == mode) || (vLEFT == mode) || (vNONE == mode)) + { + if (vLEFT == mode) + { + if (0.0 < move) rotvec.z += 89.0 * DEG_TO_RAD; + else rotvec.z += 91.0 * DEG_TO_RAD; + } + pos += (<(llCos(rotvec.z)) * move, (llSin(rotvec.z)) * move, 0.0>); + if (0.0 != move) + { + float ground = cast(gp, pos, move / Smooth); + if (pos.z > (ground + LeaderOffset * 3.0)) + { + vMODE = vFALL; + pos.z -= 1.0; + } + else pos.z = ground + LeaderOffset; + } + if (vMODE == vFALL) vAnim = "Falling"; + else if (vCROUCH == vMODE) + { + if (0.0 == move) vAnim = "Crouching"; + else vAnim = "CrouchWalking"; + } + else if (vGROUND == vMODE) + vAnim = "Sitting on Ground"; + else + { + if (0.0 != rotate) + { + if (0.0 < rotate) vAnim = "Turning Left"; + else vAnim = "Turning Right"; + } + if (0.0 == move) vAnim = "Standing"; + else vAnim = "Walking"; + } + } + else if (vUP == mode) + { + if (keys & CONTROL_UP) + { + if (vGROUND == vMODE) {vAnim = "Crouching"; vMODE = vCROUCH;} + else if (vCROUCH == vMODE) {vAnim = "Standing"; vMODE = vFWD;} + else {pos += (<0.0, 0.0, move>); vMODE = vUP;} + } + else + { + if (vCROUCH == vMODE) {vAnim = "Sitting on Ground"; vMODE = vGROUND;} + else if (0.0 > move) {vAnim = "Crouching"; vMODE = vCROUCH;} + else {pos += (<0.0, 0.0, move>); vMODE = vUP;} + } + } + // This is useful at least, not tested on a var region yet, but should work, that'll be why it exists. + vector rs = osGetRegionSize(); + vector cr = pos - gp; + integer isBorder; + integer isEdge; + if ((pos.x < 0.0) || (pos.x > rs.x) ||(pos.y < 0.0) || (pos.y > rs.y)) + { + isBorder = TRUE; + if (llFabs(cr.x) > llFabs(cr.y)) + { + cr.y = 0.0; + if (0.0 < cr.x) {if (0.3 < cr.x) cr.x = 1.0; else cr.x = 0.0;} + else if (0.0 > cr.x) {if (-0.3 > cr.x) cr.x = -1.0; else cr.x = 0.0;} + } + else + { + cr.x = 0.0; + if (0.0 < cr.y) {if (0.3 < cr.y) cr.y = 1.0; else cr.y = 0.0;} + else if (0.0 > cr.y) {if (-0.3 > cr.y) cr.y = -1.0; else cr.y = 0.0;} + } + isEdge = llEdgeOfWorld(gp, cr); + integer l = llGetListLength(Sitters); + integer i; + for (i = 0; i < l; i += pSTRIDE) + { + key a = llList2Key(Sitters, i); + key t = llList2Key(Sitters, i + pADJ); + if (isEdge) + { + Sitters = llListReplaceList(Sitters, [a], i + pADJ, i + pADJ); + adjust(a, move, "forward"); + Sitters = llListReplaceList(Sitters, [t], i + pADJ, i + pADJ); + } + else + llSetLinkPrimitiveParamsFast(llList2Integer(Sitters, i + pLINK), + [PRIM_POS_LOCAL, <-1.0, 0.0, -0.0001>]); + } + } + if (isEdge) + d("Can't go there, you'll fall off the edge!"); + else + { + if (isBorder) + { + llWhisper(0, "Can't walk into next sim, teleporting slowly instead."); + cr = llGetRegionCorner() + pos + ((pos - gp) * 2.0); + string data = (string)cr.x + "/" + (string)cr.y + "/" + (string)cr.z; + sendPrim(BossKey, "TPRIM", [data]); + LeaderOffset = 0.0; + addEvent(1.5, "TPRIM " + (string)cr.x + "/" + (string)cr.y + "/" + (string)cr.z); + } + else + { + // This is useful at least for parcel crossing. + integer f = llGetParcelFlags(pos); + if (!f & PARCEL_FLAG_ALLOW_FLY) d("No fly."); + if (!f & PARCEL_FLAG_ALLOW_SCRIPTS) d("No scripts."); + if (!f & PARCEL_FLAG_ALLOW_CREATE_OBJECTS) d("No create object."); + if (!f & PARCEL_FLAG_ALLOW_ALL_OBJECT_ENTRY) d("No object entry."); + if (!f & PARCEL_FLAG_ALLOW_GROUP_SCRIPTS) d("No group scripts."); + if (!f & PARCEL_FLAG_ALLOW_CREATE_GROUP_OBJECTS) d("No group create objects."); + if (!f & PARCEL_FLAG_ALLOW_GROUP_OBJECT_ENTRY) d("No group object entry."); + if ((f & PARCEL_FLAG_ALLOW_SCRIPTS) && (f & PARCEL_FLAG_ALLOW_ALL_OBJECT_ENTRY)) + llSetLinkPrimitiveParamsFast(LINK_THIS, [PRIM_POSITION, pos, PRIM_ROTATION, + llEuler2Rot(<0.0, 0.0, rotate>) * llGetRot()]); + } + } + checkAO(); +} + +newLeader(key id) +{ + Leader = id; + vector bb = llGetAgentSize(Leader); LeaderOffset = (bb.z + 0.3) / 2; + updateVehicle(0, vNONE, 0.0, 0.0); + checkAO(); +d("newLeader " + llKey2Name(Leader) + ", offset " + (string) LeaderOffset); +} + +oldController(key t) +{ + // NOTE - the person just stood up, that automatically revoked the permissions. In theory. + // And sometimes in practice. + if (llGetPermissionsKey() == t) + { + if (llGetPermissions() & PERMISSION_CONTROL_CAMERA) + { + d("releasing camera for " + llKey2Name(t)); + llClearCameraParams(); + } + if (llGetPermissions() & PERMISSION_TAKE_CONTROLS) + { + d("releasing controls for " + llKey2Name(t)); + llReleaseControls(); + } + } + if (t == Controller) + { + addEvent(0.0, "Keys"); + Controller = NULL_KEY; + } +} + +checkSitters(integer del) +{ + list new = []; + integer l = llGetNumberOfPrims(); + integer lnk; + for (lnk = llGetObjectPrimCount(llGetKey()) + 1; lnk <= l; ++lnk) + new += [llGetLinkKey(lnk), lnk]; + l = llGetListLength(Sitters); + for (lnk = 0; lnk < l; lnk += pSTRIDE) + { + key t = llList2Key(Sitters, lnk + pKEY); + if (NULL_KEY != t) + { + integer j = listFindString(new, t, 2); + + if (-1 == j) // old sitter that left + { + d("unsit " + llKey2Name(t)); + stopMe(t, lnk); + oldController(t); + Sitters = llListReplaceList(Sitters, [NULL_KEY], lnk + pKEY, lnk + pKEY); + } + else // old sitter that is still here + new = llListReplaceList(new, [], j, j + 1); + } + } + l = llGetListLength(new) / 2; + for (lnk = 0; lnk < l; lnk +=2) + { + key t = llList2Key(new, lnk); + list n = [t, llList2Integer(new, lnk + 1), "", "", "", "", llGetListLength(Distances) / 3, 0]; + list gndr = llGetObjectDetails(t, [OBJECT_BODY_SHAPE_TYPE]); + stopAnims(t); + integer f = findSitter(NULL_KEY); + if (-1 != f) // new sitter replaces old + Sitters = llListReplaceList(Sitters, n, f, f + pSTRIDE - 1); + else // new sitter added on end + Sitters += n; + if (NULL_KEY != BossKey) sendPrim(BossKey, "SIT_DONE", [t]); + d("sat " + llKey2Name(t) + ", " + llList2String(gndr, 0) + " male @ " + (f / pSTRIDE)); + } + + // Wearer is the default leader, unless anyone is taller. + l = llGetListLength(Sitters); + integer m = l / pSTRIDE; + integer ldr = findSitter(Owner); + float max = 0.0; + if (-1 != ldr) + { + vector s = llGetAgentSize(Owner); + max = s.z; + } + for (lnk = 0; lnk < l; lnk += pSTRIDE) + { + key t = llList2Key(Sitters, lnk + pKEY); + vector s = llGetAgentSize(t); + if (s.z > max) + { + max = s.z; + ldr = lnk; + } + if (NULL_KEY == t) --m; + } + if ((0 != ldr) && (0 != llGetListLength(Sitters))) + { + list s = llList2List(Sitters, ldr, ldr + pSTRIDE - 1); + Sitters = s + llListReplaceList(Sitters, [], ldr, ldr + pSTRIDE - 1); + } + newLeader(llList2Key(Sitters, pKEY)); + llSitTarget(position, llEuler2Rot(rotat * DEG_TO_RAD)); + Controller = NULL_KEY; + for (lnk = 0; lnk < l; lnk += pSTRIDE) + { + updateSitter(llList2Key(Sitters, lnk + pKEY)); + key t = llList2Key(Sitters, lnk + pKEY); + if ((NULL_KEY == Controller) && (Owner != t)) + Controller = t; + } + if (NULL_KEY != Controller) + { + d("checkSitters() requesting camera and controls from " + llKey2Name(Controller)); + llRequestPermissions(Controller, PERMISSION_CONTROL_CAMERA | PERMISSION_TAKE_CONTROLS); + } + llSetSitText(Sit0Text); llSetTouchText("menu"); llSetClickAction(CLICK_ACTION_SIT); + llSetText(HoverText, <1.0, 1.0, 1.0>, 1.0); + if (0 == m) + { + if (del) + { + llSleep(5.0); + d("No one left sitting on me."); + if (NULL_KEY != BossKey) die(); + } + llSetTouchText(""); + llSetLinkAlpha(Link, 0.5, ALL_SIDES); + } + else if (1 == m) llSetLinkAlpha(Link, 0.25, ALL_SIDES); + else if (2 == m) + { + llSetSitText(Sit1Text); llSetClickAction(CLICK_ACTION_TOUCH); + llSetLinkAlpha(Link, 0.1, ALL_SIDES); llSetText("", ZERO_VECTOR, 0.0); + } + return; +} + +stopAnims(key avatar) +{ + if (NULL_KEY != avatar) + { + list anims = llGetAnimationList(avatar); + integer l = llGetListLength(anims); + integer i; + for (i = 0; i < l; i++) + { + string anim = llList2String(anims, i); + if (anim != "") osAvatarStopAnimation(avatar, anim); + } + } +} + +// AO functions. +list States = []; +integer loadCard(string card, integer casperMode) +{ + if (NULL_KEY != llGetInventoryKey(card)) + { + float now = llGetTimeOfDay(); + string section = ""; + integer l = osGetNumberOfNotecardLines(card); + integer i; + States = initialStates; + for (i = 0; i <= l; ++i) + { + string line = osGetNotecardLine(card, i); + string rest = ""; + string name = ""; + integer match = llSubStringIndex(line, "]"); + integer found = -1; + // Ignore anything that does not start with [, and has the matching ]. + // Which is a dirt cheap ZHAO II compatibility. + if (("[" == llGetSubString(line, 0, 0)) && (-1 != match)) + { + name = llStringTrim(llGetSubString(line, 1, match - 1), STRING_TRIM); + if (casperMode) section = name; + else + { + name = alias(name); + rest = llGetSubString(line, match + 1, -1); + // Corner case, no actual anims. + if ((match + 1) == llStringLength(line)) rest = ""; + } + } + // Casper AO has a different format. AOConfig, "[Walking]" on a line by itself, followed by one animation per line. + // I vaguely remember that might be the ZHAO I format. + // Though I doubt if ZHAO I ever made it out of SL, think ZHAO II was too popular at the time. + else if (casperMode) + { + rest = llStringTrim(line, STRING_TRIM); + if ("" != rest) name = section; + } + if ("" != name) + { + found = llListFindList(States, [name]); + if ((0 <= found) && ((found % 2) == 0)) + { + string data = llList2String(States, found + 1); + if ("" != data) data += "|"; + States = llListReplaceList(States, [name, data + rest], found, found + 1); + } + else States += [name, rest]; + } + } + l = llGetListLength(States); + for (i = 0; i < l; i += 2) + { + list nA = llParseStringKeepNulls(llList2String(States, i + 1), ["|"], []); + integer m = llGetListLength(nA); + integer j; + for (j = 0; j < m; ++j) + { + list a = llCSV2List(llList2String(nA, j)); + integer n = llGetListLength(a); + integer k; + for (k = 0; k < n; ++k) + checkAnim(llList2String(a, k)); + } + } + d("Read " + card + " in " + (string) (llGetTimeOfDay() - now) + " seconds."); + return TRUE; + } + return FALSE; +} + +list isPoseAO(integer f) +{ + if ("" != Pose) + { + integer p = findPose(Pose); + if (-1 != p) + { + list ps = llParseStringKeepNulls(llList2String(Poses, p + psANIM), ["|"], []); + if ("~" != llList2String(ps, f / pSTRIDE)) + return ps; + } + } + return []; +} + +checkAO() +{ + string newAnim; + integer l = llGetListLength(Sitters); + integer fast; integer i; integer f; float dpth; + if (NULL_KEY != TheirKey) return; + + if (Attached) + { +// if (llGetAgentInfo(Owner) & AGENT_ALWAYS_RUN) fast = 1; else fast = 0; + newAnim = llGetAnimation(Owner); + vAnim = newAnim; + } + else + { +// TODO - when updateControls() figures out fast mode, use that. + newAnim = vAnim; + } + if (("" == Pose) && ("0" == getSetting("AO"))) + { + stopAnims(Owner); + AOspeed = 1.0; + osSetSpeed(Owner, 1.0); + return; + } + string oldAnim = newAnim; +//d("checkAO() " + newAnim); + AOspeed = 0.0; + integer flying = llListFindList(flyStates, [vAnim]); + if (-1 != flying) + { + float water = llWater(ZERO_VECTOR); + float ground = llGround(ZERO_VECTOR); + integer fly = ("Flying" == vAnim); +// if (fly && !fast) newAnim = "FlyingSlow"; + if (water > ground) // First check if we can even be under water. + { + // In Opensim, pos.z is actually a double, and OpenSim can't do equality for doubles & floats. + vector pos = llGetPos(); float z = pos.z; // Coz OpenSim can't do llGetPos().z + if (z <= water) + { + if (0 > Bobbing) Bobbing++; + Swimming = TRUE; + dpth = water - z; + } + else + { + if (Swimming) + { // That's metres of water depth before it figures you don't have enough to swim in. + if ((z > water) && ((water - ground) > 1.5)) + { // Push you back into the water. + vector velocity = llGetVel(); + dpth = 0.1; + velocity.x = 0.0; velocity.y = 0.0; + if (10 > velocity.z) velocity.z = 10; + velocity.z *= -4; + if ("Swimming Up" == alias("~" +vAnim)) + { + Bobbing++; + if (3 < Bobbing) + { // Switch to flying. + Bobbing = 0; + Swimming = FALSE; + } + } +// TODO - this wont work in vehicle mode. + if (Swimming) + llApplyImpulse(llGetMass() * velocity, FALSE); + } + else // Switch to "walking" mode. + Swimming = FALSE; + } + } + } // No water to swim in here. + else Swimming = FALSE; + } + else // Not in fly mode. + Swimming = FALSE; +// llParticleSystem([]); + if (Swimming) + { + if (Attached) AOspeed = 0.1; + else AOspeed = 0.5; + newAnim = alias("~" + newAnim); +// updateParticles(); + } + else if (-1 != flying) + { + if (Attached) AOspeed = 1.0; + else AOspeed = 4.0; + } + else AOspeed = 1.0; + if (0.0 < AOspeed) osSetSpeed(Owner, AOspeed + ((AOspeed / 2) * (fast + (2 * (integer) getSetting("super"))))); + else AOspeed = 1.0; + for (f = 0; f < l; f += pSTRIDE) + { + string anim = llList2String(Sitters, f + pSTATE); + list anims = llCSV2List(llList2String(Sitters, f + pANIMS)); + integer g; + list states = States; + key id = llList2Key(Sitters, f + pKEY); + list ps = isPoseAO(f); + if (0 != llGetListLength(ps)) + { + states = [Pose, llList2String(ps, f / pSTRIDE)]; + newAnim = Pose; + } +//d("checkAO " + llKey2Name(id) + " " + anim + " -> " + newAnim + " @ " + (string) AOspeed + " " + Pose); + if (newAnim != anim) + { + g = listFindString(states, newAnim, 2); + if (-1 != g) + { + stopMe(id, f); + // Ignore sits, since 99.99% of the time what they are sitting on supplies an animation. + if ((("Sitting" == vAnim) && Attached) || ("" == llList2String(states, g + 1))) + ; + else + { + list newAnims = llParseString2List(llList2String(states, g + 1), ["|"], []); + // If there's more than one, randomly switch between them at random times. + i = llGetListLength(newAnims); + if (1 < i) addEvent(20.0 + llFrand(20.0), "SYNC R"); + i = (integer) llFrand((float) i); + // ZHAO II also allows multiple anims in one set, comma separated. Good idea. + anims = llCSV2List(llList2String(newAnims, i)); +//d("ANIMS " + llKey2Name(id) + " " + newAnim + " -> " + llList2String(newAnims, i)); + for (i = llGetListLength(anims) - 1; i >= 0; --i) + { + string a = llList2String(anims, i); + if (checkAnim(a)) osAvatarPlayAnimation(id, a); + } + } + } + anim = newAnim; + } + Sitters = llListReplaceList(Sitters, [anim, anim, llDumpList2String(anims, ",")], f + pSTATE, f + pANIMS); + newAnim = oldAnim; + } + if (("" == Pose) || ("Swimming Up" == vAnim)) addEvent(Tick * dpth, "checkAO"); +} + +stopMe(key id, integer f) +{ + list anims = llCSV2List(llList2String(Sitters, f + pANIMS)); + integer i; +//d("stopMe " + llKey2Name(id) + " " + llList2String(Sitters, f + pANIMS)); + for (i = llGetListLength(anims) - 1; i >= 0; --i) + osAvatarStopAnimation(id, llList2String(anims, i)); +} + +checkLag() +{ + float dil = llGetRegionTimeDilation(); // Between 0 and 1. + float fps = llGetRegionFPS(); // Frames per second, up to 50. + integer newLag = (integer) (dil * fps); + if (llAbs(Lag - newLag) > 9) + { + string l; + Lag = newLag; + Tick = ((60 - (dil * fps)) / 30) + 0.15; + if (45 <= newLag) l = "No"; + else if (35 <= newLag) l = "A little"; + else if (25 <= newLag) l = "Medium"; + else if (15 <= newLag) l = "Lots of"; + else l = "Way too much"; + if (!osIsNpc(llGetOwner())) s(Owner, l + " lag, tick is " + (string) Tick); + } +} + +init() +{ + ANIMATIONS = readCard("animations"); flyStates = readCard("flystates"); initialStates = readCard("states"); + Attached = (0 != llGetAttached()); + llSetSitText("NO SIT"); llSetTouchText("menu"); + llSetClickAction(CLICK_ACTION_TOUCH); llSetText("", ZERO_VECTOR, 0.0); + Link = llGetLinkNumber(); +// None of this works in OpenSim? +// llSetBuoyancy(-2.0); +// llSetHoverHeight(10.0, TRUE, 2.0); +// llGroundRepel(10.0, TRUE, 2.0); + checkLag(); +} + +laterInit() +{ + if (!loadCard("ZHAO II", FALSE)) // In an object full of random scripts, "Default" is a lousy name. + if (!loadCard("Default", FALSE)) + loadCard("AOConfig", TRUE); // Casper AOs are less common. + if (Attached) + { + d("AO mode."); + Sitters = [Owner, -1, "", "", "", "", llGetListLength(Distances) / 3, 0]; + checkAO(); + } + else + { + if (NULL_KEY == BossKey) + d("Vehicle mode."); + else + d("Vehicle mode, slaved to " + llKey2Name(BossKey) + "."); + integer i = llGetInventoryNumber(INVENTORY_NOTECARD); + list posCards = []; + string item; + while (i-- > 0) + { + item = llGetInventoryName(INVENTORY_NOTECARD, i); + if (llSubStringIndex(item, ".POSITIONS") == 0) posCards += (list) item; + } + readPos(posCards); + d("Loaded " + (string) (llGetListLength(Poses) / psSTRIDE) + " positions in " + + (string) (llGetTimeOfDay() - Start) + " seconds."); + checkSitters(FALSE); + if (NULL_KEY != BossKey) sendPrim(BossKey, "REZ_DONE", []); + } + addEvent(6.0 + llFrand(10.0), "SMILE"); +} + +die() +{ + integer l = llGetListLength(Sitters); + integer f; + for (f = 0; f < l; f += pSTRIDE) + stopMe(llList2Key(Sitters, f + pKEY), f); + d("Deleting myself."); + sendPrim(BossKey, "DIE_DONE", []); + if (PERM_COPY & llGetObjectPermMask(MASK_OWNER)) llDie(); + else s("This no copy object wont delete itself, please delete manually, or take into inventory."); +} + +default +{ + state_entry() + { + llSleep(0.2); + Start = llGetTimeOfDay(); + Owner = llGetOwner(); +d("\n\n1AOor2 sending RESET @ " + (string) Start + "\n"); + ScriptName = llGetScriptName(); ScriptKey = llGetInventoryKey(ScriptName); + LibraryKey = NULL_KEY; + sendScript(lRESET, []); + init(); + if (bREZ == llGetStartParameter()) BossKey = osGetRezzingObject(); + } + + control(key id, integer level, integer edge) {doControl(id, level, edge);} + link_message(integer sender, integer num, string message, key id) {linky(num, message, id);} + + changed(integer change) + { + if (change & CHANGED_LINK) checkSitters(TRUE); + if (change & CHANGED_ANIMATION) checkAO(); + if ((change & CHANGED_TELEPORT) || (change & CHANGED_REGION)) + { + if (99.0 > TPangle) + { + PIN = (integer) llFrand(DEBUG_CHANNEL - 2) + 2; + vector RefPos = llGetPos(); + rotation RefRot = llGetRot(); + llRezObject("1AOor2 prim", ZERO_VECTOR * RefRot + RefPos, ZERO_VECTOR, (ZERO_ROTATION) * RefRot, PIN); + llOwnerSay("@setrot:" + (string) TPangle + "=force"); + s(Owner, "Switching AO to 1ring object."); + oldController(Owner); + TPangle = 999.0; + Chosen = 1; + } + } + } + + run_time_permissions(integer perm) + { + if (PERMISSION_TAKE_CONTROLS & perm) + { + key id = llGetPermissionsKey(); + d("taking controls from " + llKey2Name(id)); + llTakeControls( CONTROL_FWD | CONTROL_BACK | CONTROL_LEFT | CONTROL_RIGHT | + CONTROL_ROT_LEFT | CONTROL_ROT_RIGHT | CONTROL_UP | CONTROL_DOWN, TRUE, FALSE); + ControlTime = llGetTimeOfDay(); + addEvent(Tick * Smooth, "Keys"); + } + if (PERMISSION_CONTROL_CAMERA & perm) + { + lookAtMe(llGetPermissionsKey()); + if ((llGetPermissionsKey() != Controller) && (NULL_KEY != Controller)) + { + d("PERMISSION_CONTROL_CAMERA requesting camera and controls from " + llKey2Name(Controller)); + llRequestPermissions(Controller, PERMISSION_CONTROL_CAMERA | PERMISSION_TAKE_CONTROLS); + } + } + } +} -- cgit v1.1