/* HS_DollyCam - Marker helper (separate script to save Controller memory) - Receives link_message MC_CMD: "SHOW|N" -> rez markers for first N presets found "HIDE" -> cleanup markers - Listens MARKER_CH for HELLO/CLICK - On CLICK sends MC_EVT_CLICK with payload idx to Controller */ integer MC_CMD = 5100; integer MC_EVT_CLICK = 5101; string MARKER_OBJECT = "HS_CamMarker"; integer MARKER_CH = -880088; key gOwner; integer gShowToken = 0; // [key objKey, integer idx, vector pos, rotation rot] repeating list gMarkers; string PRE_KEY(integer idx) { return "P" + (string)idx; } vector gTmpPos; vector gTmpFoc; rotation gTmpRot; say(string s) { llOwnerSay(s); } integer isValidIdx(integer idx) { return (idx > 0); } integer loadPreset(integer idx) { string data = llLinksetDataRead(PRE_KEY(idx)); if (data == "") return FALSE; list p = llParseString2List(data, ["|"], []); if (llGetListLength(p) < 10) return FALSE; gTmpPos = <(float)llList2String(p,0),(float)llList2String(p,1),(float)llList2String(p,2)>; gTmpFoc = <(float)llList2String(p,3),(float)llList2String(p,4),(float)llList2String(p,5)>; gTmpRot = <(float)llList2String(p,6),(float)llList2String(p,7),(float)llList2String(p,8),(float)llList2String(p,9)>; return TRUE; } rotation markerRotFromPosFocus(vector pos, vector focus) { vector fwd = llVecNorm(focus - pos); vector up = <0,0,1>; vector left = up % fwd; if (llVecMag(left) < 0.0001) left = <1,0,0>; else left = llVecNorm(left); vector up2 = llVecNorm(fwd % left); return llAxes2Rot(left, up2, fwd); } markersClearLocal() { gMarkers = []; } integer markersFind(key obj) { integer i; for (i = 0; i < llGetListLength(gMarkers); i += 4) { if (llList2Key(gMarkers, i) == obj) return i; } return -1; } markersHide() { integer i; for (i = 0; i < llGetListLength(gMarkers); i += 4) { key obj = llList2Key(gMarkers, i); if (obj != NULL_KEY) llRegionSayTo(obj, MARKER_CH, "DIE|" + (string)gShowToken); } markersClearLocal(); say("Markers cleaned up."); } showCams(integer want) { if (llGetInventoryType(MARKER_OBJECT) != INVENTORY_OBJECT) { say("Missing marker object in HUD contents: " + MARKER_OBJECT); return; } markersHide(); gShowToken = (integer)llFrand(2147483647.0); vector aPos = llList2Vector(llGetObjectDetails(gOwner, [OBJECT_POS]), 0); vector rezPos = aPos + <0,0,1>; integer found = 0; integer idx; for (idx = 1; idx <= 999 && found < want; ++idx) { if (!loadPreset(idx)) jump cont; vector pos = gTmpPos; vector foc = gTmpFoc; rotation mrot = markerRotFromPosFocus(pos, foc); llRezObject(MARKER_OBJECT, rezPos, ZERO_VECTOR, ZERO_ROTATION, idx); gMarkers += [NULL_KEY, idx, pos, mrot]; found++; @cont; } say("Showing cams: " + (string)found + " (token " + (string)gShowToken + ")"); } default { state_entry() { gOwner = llGetOwner(); llListen(MARKER_CH, "", "", ""); } attach(key id) { if (id == NULL_KEY) { markersHide(); } else { gOwner = llGetOwner(); } } link_message(integer sender, integer num, string str, key id) { if (num != MC_CMD) return; if (id != gOwner) return; str = llStringTrim(str, STRING_TRIM); if (str == "") return; list p = llParseString2List(str, ["|"], []); string cmd = llToUpper(llList2String(p, 0)); if (cmd == "HIDE") { markersHide(); return; } if (cmd == "SHOW") { integer want = 12; if (llGetListLength(p) >= 2) want = (integer)llList2String(p, 1); if (want < 1) want = 1; if (want > 30) want = 30; showCams(want); return; } } object_rez(key id) { integer i; for (i = 0; i < llGetListLength(gMarkers); i += 4) { if (llList2Key(gMarkers, i) == NULL_KEY) { integer idx = llList2Integer(gMarkers, i+1); vector pos = llList2Vector(gMarkers, i+2); rotation rot = llList2Rot(gMarkers, i+3); gMarkers = llListReplaceList(gMarkers, [id], i, i); llRegionSayTo(id, MARKER_CH, "SET|" + (string)gShowToken + "|" + (string)idx + "|" + (string)pos + "|" + (string)rot ); return; } } } listen(integer channel, string name, key id, string msg) { if (channel != MARKER_CH) return; list p = llParseString2List(msg, ["|"], []); if (llGetListLength(p) < 2) return; string typ = llToUpper(llList2String(p,0)); integer tok = (integer)llList2String(p,1); if (tok != gShowToken) return; if (typ == "HELLO") { integer at = markersFind(id); if (at >= 0) { integer idx = llList2Integer(gMarkers, at+1); vector pos = llList2Vector(gMarkers, at+2); rotation rot = llList2Rot(gMarkers, at+3); llRegionSayTo(id, MARKER_CH, "SET|" + (string)gShowToken + "|" + (string)idx + "|" + (string)pos + "|" + (string)rot ); } return; } if (typ == "CLICK" && llGetListLength(p) >= 3) { integer idx2 = (integer)llList2String(p,2); if (!isValidIdx(idx2)) return; // Inform Controller (it will stop playlist + move camera) llMessageLinked(LINK_SET, MC_EVT_CLICK, (string)idx2, gOwner); return; } } }