From 9dd74045373c39d8a35a59f3c2801b0348a44a9b Mon Sep 17 00:00:00 2001 From: Dave Seikel Date: Sun, 20 May 2018 23:17:24 +1000 Subject: Initial commit. This is the current alpha version, it was time to put it somewhere. --- ~MLP lite props.lsl | 354 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 354 insertions(+) create mode 100644 ~MLP lite props.lsl (limited to '~MLP lite props.lsl') diff --git a/~MLP lite props.lsl b/~MLP lite props.lsl new file mode 100644 index 0000000..f8042e4 --- /dev/null +++ b/~MLP lite props.lsl @@ -0,0 +1,354 @@ +// +// MLP lite v3.0 for OpenSim +// Based on the original MLP - MULTI-LOVE-POSE V1.2 - Copyright (c) 2006, by Miffy Fluffy (BSD License) +// This code has bounced around the Second Life and OpenSim for over a decade, with various people working on it. +// MLP lite for OpenSim is almost a complete rewrite by onefang rejected. + +vector RefPos; +rotation RefRot; + +string Pose = ""; + +list Props; // List of props. +integer PROP_NAME = 0; // Name of prop, which should match a POSE name. +integer PROP_OBJECT = 1; // | separated names of props in inventory. +integer PROP_POSROT = 2; // | separated position and rotation pairs. +integer PROP_STRIDE = 3; + +list Balls; // Ball / prop tracker. +integer BALL_NUM = 0; // The number of the ball, negative numbers for the props. +integer BALL_KEY = 1; // The UUID of the ball / prop. +integer BALL_AVATAR = 2; // The UUID of any avatar sitting on the ball. +integer BALL_STRIDE = 3; + +string LIST_SEP = "$!#"; // Used to seperate lists when sending them as strings. + +// The various link messages. The first lot are historical. +//integer OLD_TOUCH = -1; // msg = "PRIMTOUCH" +integer OLD_POSEB = 0; // msg = "POSEB" id = pose name +// // Also CHECK1 and CHECK2 instead of pose name. +integer OLD_STOP = 1; // msg = "STOP" +//integer OLD_REF = 8; // msg = RefPos id = RefRot +integer OLD_SITS = -11000; // msg = ball number|anim name id = avatar key +integer OLD_STANDS = -11001; // msg = ball number id = avatar key +integer OLD_ANIM = -11002; // msg = ball number|anim name id = avatar key +//integer SEQ_CMD = -12001; // msg = ~sequencer command +integer OLD_CMD = -12002; // msg = menu command for ~MLP id = user key + +integer MLP_CMD = -13001; // A command that ~MLP dealt with, so other scripts can hook it. +integer TOOL_DATA = -13002; // Get Menus, Poses, Props, or Sounds list. +integer MLP_DATA = -13003; // Set Menus, Poses, Props, or Sounds list. +integer MLP_POSE = -13004; // doPose(pose, type); +integer MLP_UNKNOWN = -13005; // ~MLP doesn't know this command, might be for other scripts. + +// Types for doPose(), +integer POSES_POSE = -14001; // Change pose, even if it's changing to the same pose. +integer POSES_SWAP = -14002; // Swap poses. +integer POSES_DUMP = -14003; // Dump or save poses. +integer POSES_Z = -14004; // Change height. + + +integer listFindString(list lst, string name, integer stride) +{ + integer f = llListFindList(lst, [name]); + integer ix = f / stride; + + // Round to nearest stride. + ix = ix * stride; + + // Sanity check, make sure we found a name, not something else, else do it the slow way. + 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; +} + +// Only used in one place, but leave it here for now. +string prStr(string str) +{ + integer ix = llSubStringIndex(str, ">"); + vector p = ((vector) llGetSubString(str, 0, ix) - RefPos) / RefRot; + vector r = llRot2Euler((rotation) llGetSubString(str, ix + 1, -1) / RefRot) * RAD_TO_DEG; + + // OpenSim likes to swap these around, which triggers the ball movement saving. + // Coz OpenSim doesn't support move events, so we gotta do things the hard way. + if (-179.9 >= r.x) r.x = 180.0; + if (-179.9 >= r.y) r.y = 180.0; + if (-179.9 >= r.z) r.z = 180.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; +} + +integer findBall(integer num) +{ + integer f = llListFindList(Balls, [num]); + integer ix = f / BALL_STRIDE; + + // Round to nearest stride. + ix = ix * BALL_STRIDE; + + if ((-1 != f) && (ix == f)) // Sanity check, make sure we found a chan, not something else. + return f; + else + return -1; +} + +saveBall(integer num, key id, key avatar) +{ + integer f = findBall(num); + + if (-1 == f) + Balls += [num, id, avatar]; + else + Balls = llListReplaceList(Balls, [num, id, avatar], f, f + BALL_STRIDE - 1); +} + +rezThing(string thing, string posRot, integer num) +{ + integer i = llSubStringIndex(posRot, ">"); + + llRezObject(thing, + ((vector) llGetSubString(posRot, 0, i)) * RefRot + RefPos, + ZERO_VECTOR, + llEuler2Rot((vector) llGetSubString(posRot, i + 1, -1) * DEG_TO_RAD) * RefRot, + num); +} + +readProps(list propCards) +{ + integer i; + integer l = llGetListLength(propCards); + + propCards = llListSort(propCards, 1, TRUE); + for (i = 0; i < l; i++) + { + string card = llList2String(propCards, i); + list crd = llParseStringKeepNulls(osGetNotecard(card), ["\n"], []); + integer m = llGetListLength(crd); + integer j; + + llOwnerSay("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); + if ("" != data) + { + // .PROPS is different from .POSITIONS, which is just inconsistant. + // There's an extra | at the beginning of the lines, for no apparent reason. + // The position and rotation has a "/" between them, and normally / is a comment. + // But we gotta stay compatible. sigh + list props = llParseStringKeepNulls(data, ["|"], []); + string name = llStringTrim(llList2String(props, 1), STRING_TRIM); + string prop = llStringTrim(llList2String(props, 2), STRING_TRIM); + list posrots = llParseString2List(llStringTrim(llList2String(props, 3), STRING_TRIM), ["/"], []); + + saveProp(name, prop, llDumpList2String(posrots, "")); + } + } + } + } +} + +integer findProp(string name) +{ + return listFindString(Props, name, PROP_STRIDE); +} + +saveProp(string name, string prop, string posRot) +{ + integer f = findProp(name); + + if (-1 != f) + { + string t; + + t = llList2String(Props, f + PROP_OBJECT); + if ("" != t) + prop += "|" + prop; + t = llList2String(Props, f + PROP_POSROT); + if ("" != t) + posRot += "|" + posRot; + Props = llListReplaceList(Props, [name, prop, posRot], f, f + PROP_STRIDE - 1); + } + else + Props += [name, prop, posRot]; +} + +// ball is either the ball number, or a POSES_* flag. +// ball on the other hand, is a negative integer for props. +doPose(string newPose, integer ball) +{ + integer f = findProp(Pose); + integer p = findBall(ball); + integer l = llGetListLength(Balls); + integer i = 0; + vector newRefPos = llGetPos(); + rotation newRefRot = llGetRot(); + + newRefPos.z += ((integer) llGetObjectDesc()) / 100.0; + + if (-1 != p) + { + i = p; + l = p + BALL_STRIDE; + } + for (; i < l; i += BALL_STRIDE) + { + integer b0 = llList2Integer(Balls, i + BALL_NUM); + integer isBall = (0 <= b0); + + if (isBall) + ; + else //if ((POSES_SWAP != ball) + { + if (-1 != f) + { + integer m = f + PROP_POSROT; + string prOld = llList2String(Props, m); + // Find out where the prop is now, and rotation. + integer q = -1 - b0; + string bpr = prStr(llDumpList2String( + llGetObjectDetails(llList2Key(Balls, i + BALL_KEY), [OBJECT_POS, OBJECT_ROT]), "")); + string result = llDumpList2String(llListReplaceList(llParseString2List(prOld, ["|"], []), [bpr], q, q), "|"); + // Actually store the props new position / rotation if it moved. + if (result != prOld) + { + llOwnerSay(" MOVEd prop " + q + ".\n\t old [" + prOld + "]\n\t new [" + result + "]"); + Props = llListReplaceList(Props, [result], m, m); + } + } + + if ((POSES_DUMP != ball) && (POSES_SWAP != ball)) + { + // Remove the prop. + osMessageObject(llList2Key(Balls, i + BALL_KEY), "DIE"); + Balls = llListReplaceList(Balls, [], i, i + BALL_STRIDE - 1); + i -= BALL_STRIDE; + l -= BALL_STRIDE; + } + } + } + RefPos = newRefPos; + RefRot = newRefRot; + + // Props are per pose, so deal with them here to. + // Assumption - props don't MOVE when we change poses, they die, and get recreated when needed. + // Note that the original MLP also assumed that props don't MOVE, though they send MOVE. + if (POSES_POSE == ball) + { + p = findProp(newPose); + if (-1 != p) + { + list o = llParseStringKeepNulls(llList2String(Props, p + PROP_OBJECT), ["|"], []); + list q = llParseStringKeepNulls(llList2String(Props, p + PROP_POSROT), ["|"], []); + + l = llGetListLength(o); + for (i = 0; i < l; i++) + rezThing(llList2String(o, i), llList2String(q, i), -1 - i); + } + } + Pose = newPose; +} + + +default +{ + link_message(integer from, integer num, string str, key id) + { + if (((0 == num) && ("POSEB" == str)) || ((1 == num) && ("STOP" == str))) + ; // Old messages we can ignore. + else if ((MLP_UNKNOWN == num) && ("LOADPROPS" == str)) + { + float then = llGetTime(); + float now = 0.0; + list propCards = []; // List of names of config cards. + string item; + integer i = llGetInventoryNumber(INVENTORY_NOTECARD); + integer PropCount; + + while (i-- > 0) + { + item = llGetInventoryName(INVENTORY_NOTECARD, i); + if (llSubStringIndex(item, ".PROPS") == 0) + propCards += (list) item; + } + Props = []; + readProps(propCards); + PropCount = llGetListLength(Props) / PROP_STRIDE; + if (0 < PropCount) + { + now = llGetTime(); + llOwnerSay("Loaded " + PropCount + " props in " + (string) (now - then) + " seconds."); + } + } + else if (MLP_POSE == num) + { + if (NULL_KEY == id) + id = Pose; + doPose((string) id, (integer) str); + } + else if (TOOL_DATA == num) + { + if ("" == str) + { + if ("Props" == id) + llMessageLinked(from, num, LIST_SEP + llDumpList2String(Props, LIST_SEP), id); + } + else + { + if ("Props" == id) + Props = llParseStringKeepNulls(llGetSubString(str, llStringLength(LIST_SEP), -1), [LIST_SEP], []); + } + } + else if ((OLD_CMD == num) || (MLP_CMD == num) || (MLP_UNKNOWN == num) + || (OLD_SITS == num) || (OLD_STANDS == num) || (OLD_ANIM == num)) + ; + else + llOwnerSay(llGetScriptName() + " Unknown link message " + num + ", " + str); + } + + dataserver(key queryId, string str) + { + list data = llParseString2List(str, ["|"], []); + + if ("ALIVE" == llList2String(data, 0)) + saveBall(llList2Integer(data, 1), queryId, NULL_KEY); + } +} -- cgit v1.1