diff options
Diffstat (limited to '1ring.lsl')
-rw-r--r-- | 1ring.lsl | 1424 |
1 files changed, 1424 insertions, 0 deletions
diff --git a/1ring.lsl b/1ring.lsl new file mode 100644 index 0000000..176e706 --- /dev/null +++ b/1ring.lsl | |||
@@ -0,0 +1,1424 @@ | |||
1 | |||
2 | // 1ring written by onefang rejected 2019. | ||
3 | // A simple, generic RLV based "collar". | ||
4 | |||
5 | string Version = "1ring v0.1 test version"; | ||
6 | |||
7 | // BEGIN boilerplate. | ||
8 | integer DEBUG = FALSE; | ||
9 | float Start; | ||
10 | string ScriptName; | ||
11 | key ScriptKey; | ||
12 | key LibraryKey; | ||
13 | key Owner; | ||
14 | string URL; | ||
15 | |||
16 | // Settings. | ||
17 | list Aliases; | ||
18 | list Settings; | ||
19 | integer sNAME = 0; | ||
20 | integer sTYPE = 1; | ||
21 | integer sVALUE = 2; | ||
22 | integer sAUTH = 3; | ||
23 | integer sSTRIDE = 4; | ||
24 | |||
25 | // Access "f"rom some source - | ||
26 | integer fINT = 0; // Internal, not sure we need this, means the same script. | ||
27 | integer fCARD = 1; // Read from a note card. | ||
28 | integer fCHAT = 2; // Chat channel. | ||
29 | integer fLINK = 3; // Link message. | ||
30 | integer fMENU = 4; // Menu, in reality this is just a chat channel. | ||
31 | integer fRLV = 5; // RLV, again from a chat channel. | ||
32 | integer fRELAY = 6; // RLV relay. | ||
33 | integer fOSM = 7; // osMessageObject() | ||
34 | integer fHTTP = 8; // From the web. | ||
35 | |||
36 | // utilities commands, "l"ibrary. | ||
37 | string lSEP = "$!#"; // Used to seperate lists when sending them as strings. | ||
38 | integer lRESET = -1; | ||
39 | integer lRESET_DONE = -2; | ||
40 | integer lALIAS = -3; | ||
41 | integer lALIAS_DONE = -4; | ||
42 | integer lSETTING = -5; | ||
43 | integer lSETTING_DONE = -6; | ||
44 | integer lSUBSTITUTE = -7; | ||
45 | integer lSUBSTITUTE_DONE = -8; | ||
46 | integer lNEXT_WORD = -9; | ||
47 | integer lNEXT_WORD_DONE = -10; | ||
48 | integer lCONTROL = -13; | ||
49 | integer lCONTROL_DONE = -14; | ||
50 | integer lCMD = -15; | ||
51 | integer lCMD_DONE = -16; | ||
52 | integer lSETTINGS = -17; | ||
53 | integer lSETTINGS_DONE = -18; | ||
54 | integer lSCAN = -19; | ||
55 | integer lDYNAMIC = -20; | ||
56 | integer lMENU = -21; | ||
57 | |||
58 | // OhSillyThreat detector | ||
59 | list OhSillyThreats = []; | ||
60 | list OhSillyTreats = // OpenSim threat level. | ||
61 | [ | ||
62 | "osKey2Name", // low | ||
63 | // "osGetAvatarList", // none | ||
64 | // "osGetNotecard", // very high (describes what they where when making this decision) | ||
65 | // "osMakeNotecard", // high (describes what they where when making this decision) | ||
66 | // "osGetRezzingObject", // none | ||
67 | "osMessageObject", // low | ||
68 | // "osAvatarPlayAnimation", // very high | ||
69 | // "osAvatarStopAnimation", // very high | ||
70 | // "osForceOtherSit", // very high | ||
71 | "osSetSpeed" // moderate | ||
72 | ]; | ||
73 | |||
74 | d(string m) {if (DEBUG) llInstantMessage(Owner, llGetScriptName() + ": " + m);} | ||
75 | D(string m) {llRegionSay(DEBUG_CHANNEL, llGetScriptName() + ": " + m);} | ||
76 | s(string m) {s(Owner, m);} | ||
77 | s(key id, string m) {if (id == Owner) llOwnerSay(m); else llInstantMessage(id, m);} | ||
78 | sendScript(integer cmd, list args) {sendScript(LibraryKey, cmd, ScriptName, args);} | ||
79 | sendScript(key them, integer cmd, list args) {sendScript(them, cmd, inKey2Name(them), args);} | ||
80 | sendScript(integer cmd, string name, list args) {sendScript(LibraryKey, cmd, name, args);} | ||
81 | sendScript(key them, integer cmd, string name, list args) | ||
82 | { | ||
83 | llMessageLinked(LINK_SET, cmd, llDumpList2String([ScriptKey, name] + args, lSEP), them); | ||
84 | } | ||
85 | sendPrim(key them, string cmd, list args) | ||
86 | { | ||
87 | osMessageObject(them, llDumpList2String([ScriptName, cmd] + args, lSEP)); | ||
88 | } | ||
89 | addEvent(float delay, string cmds) | ||
90 | { | ||
91 | sendScript(lCMD, [fINT, ScriptKey, "TimerEvent", "TIMER", ((string) delay) + " " + cmds]); | ||
92 | } | ||
93 | |||
94 | string inKey2Name(key k) | ||
95 | { | ||
96 | if (k == LibraryKey) return "1chatter"; | ||
97 | integer i = llGetInventoryNumber(INVENTORY_SCRIPT); | ||
98 | while (i-- > 0) | ||
99 | { | ||
100 | string n = llGetInventoryName(INVENTORY_SCRIPT, i); | ||
101 | if (llGetInventoryKey(n) == k) return n; | ||
102 | } | ||
103 | return k; | ||
104 | } | ||
105 | |||
106 | integer listFindString(list lst, string name, integer stride) | ||
107 | { | ||
108 | integer f = llListFindList(lst, [name]); | ||
109 | integer ix = f / stride; | ||
110 | ix = ix * stride; | ||
111 | if ((-1 != f) && (ix != f)) | ||
112 | { | ||
113 | integer l = llGetListLength(lst); | ||
114 | integer i; | ||
115 | f = -1; | ||
116 | for (i = 0; i < l; i += stride) | ||
117 | { | ||
118 | if (llList2String(lst, i) == name) {f = i; i = l;} | ||
119 | } | ||
120 | } | ||
121 | return f; | ||
122 | } | ||
123 | |||
124 | // Note these next two are different from the ones in 1chatter. | ||
125 | string alias(string n) | ||
126 | { | ||
127 | list v = validateName(n, "alias"); | ||
128 | n = llList2String(v, 1); | ||
129 | integer a = listFindString(Aliases, llList2String(v, 0) + llToLower(n), 2); | ||
130 | if (-1 != a) n = llList2String(Aliases, a + 1); | ||
131 | return n; | ||
132 | } | ||
133 | |||
134 | list validateName(string var, string type) | ||
135 | { | ||
136 | list v = llParseStringKeepNulls(var, ["."], []); | ||
137 | if (2 != llGetListLength(v)) | ||
138 | v = ScriptName + v; | ||
139 | var = llList2String(v, 1); | ||
140 | if ("setting" == type) | ||
141 | var = llToUpper(var); | ||
142 | return [llList2String(v, 0) + ".", var]; | ||
143 | } | ||
144 | |||
145 | string getSetting(string var) | ||
146 | { | ||
147 | list v = validateName(var, "setting"); | ||
148 | var = llList2String(v, 0) + llList2String(v, 1); | ||
149 | string result = ""; | ||
150 | integer f = listFindString(Settings, var, sSTRIDE); | ||
151 | if (-1 != f) | ||
152 | result = llList2String(Settings, f + sVALUE); | ||
153 | return result; | ||
154 | } | ||
155 | |||
156 | setSetting(key id, string var, string val, integer source) | ||
157 | { | ||
158 | list v = validateName(var, "setting"); | ||
159 | string fr = llList2String(v, 0); | ||
160 | var = fr + llList2String(v, 1); | ||
161 | integer f = listFindString(Settings, var, sSTRIDE); | ||
162 | |||
163 | if (-1 != f) | ||
164 | { | ||
165 | Settings = llListReplaceList(Settings, [val], f + sVALUE, f + sVALUE); | ||
166 | if (llGetSubString(fr, 0, -2) == ScriptName) | ||
167 | { | ||
168 | if (fINT != source) | ||
169 | { | ||
170 | doThing(id, "SET " + var + "=" + val, fr, llList2String(v, 1), val, fINT); | ||
171 | sendScript(lSETTING, [source, id, llList2String(v, 1), val]); | ||
172 | } | ||
173 | } | ||
174 | } | ||
175 | } | ||
176 | |||
177 | dumpSettings(list settings, string title) | ||
178 | { | ||
179 | integer l = llGetListLength(settings); | ||
180 | integer i; | ||
181 | |||
182 | d("v--------------- " + title); | ||
183 | for (i = 0; i < l; i += sSTRIDE) | ||
184 | { | ||
185 | d( | ||
186 | llList2String(settings, i + sNAME) + "~" + | ||
187 | llList2String(settings, i + sTYPE) + "~" + | ||
188 | llList2String(settings, i + sVALUE) + "~" + | ||
189 | llList2String(settings, i + sAUTH) | ||
190 | ); | ||
191 | } | ||
192 | d("^--------------- " + ScriptName); | ||
193 | } | ||
194 | |||
195 | doSettings(key id, list settings) | ||
196 | { | ||
197 | integer l = llGetListLength(settings); | ||
198 | integer i; | ||
199 | |||
200 | for (i = 0; i < l; i += sSTRIDE) | ||
201 | { | ||
202 | string var = llList2String(settings, i + sNAME); | ||
203 | list v = validateName(var, "setting"); | ||
204 | string fr = llList2String(v, 0); | ||
205 | var = llList2String(v, 1); | ||
206 | string val = llList2String(settings, i + sVALUE); | ||
207 | doThing(id, "SET " + var + "=" + val, fr, var, val, fINT); | ||
208 | } | ||
209 | } | ||
210 | |||
211 | dynamicMenu(key id, string menu, string name, string title, string entries, string command) | ||
212 | { | ||
213 | sendScript(lDYNAMIC, [id, menu, name, title, entries, command]); | ||
214 | } | ||
215 | |||
216 | linky(integer num, string message, key id) | ||
217 | { | ||
218 | if ((id != ScriptKey) && (id != NULL_KEY)) return; | ||
219 | list input = llParseStringKeepNulls(message, [lSEP], []); | ||
220 | key them = llList2Key(input, 0); | ||
221 | string fr = llList2Key(input, 1); | ||
222 | //d("linky " + num + " " + message); | ||
223 | if (lCMD == num) | ||
224 | { | ||
225 | //d("linky lCMD " + llDumpList2String(input, " ~ ")); | ||
226 | if ((fr == (ScriptName + ".")) || (fr == "*.")) | ||
227 | { | ||
228 | key a = llList2Key(input, 3); | ||
229 | string button = llList2String(input, 4); | ||
230 | integer r = doThing(a, button, fr, | ||
231 | llList2String(input, 5), llList2String(input, 6), llList2Integer(input, 2)); | ||
232 | sendScript(lCMD_DONE, [button, a, r]); | ||
233 | } | ||
234 | } | ||
235 | else if (lRESET_DONE == num) | ||
236 | { | ||
237 | d("linky RESET_DONE"); | ||
238 | LibraryKey = them; | ||
239 | Settings = llList2List(input, 2, -1); | ||
240 | |||
241 | // NOT boilerplate | ||
242 | sendRLV("versionnew", 10.0); | ||
243 | string f = llToLower(llKey2Name(Owner)); | ||
244 | string prefix = llGetSubString(f, 0, 0); | ||
245 | integer i = llSubStringIndex(f, " "); | ||
246 | if (-1 != i) | ||
247 | { | ||
248 | string l = llGetSubString(f, i + 1, -1); | ||
249 | if ("Resident" == l) | ||
250 | prefix += llGetSubString(f, 1, 1); | ||
251 | else | ||
252 | prefix += llGetSubString(f, i + 1, i + 1); | ||
253 | } | ||
254 | setSetting(ScriptKey, ScriptName + ".PREFIX", prefix, fCARD); | ||
255 | // NOT boilerplate | ||
256 | |||
257 | setSetting(ScriptKey, ScriptName + ".VERSION", Version, fCARD); | ||
258 | sendScript(lSETTINGS, []); | ||
259 | } | ||
260 | else if (lALIAS_DONE == num) | ||
261 | Aliases = llList2List(input, 2, -1); | ||
262 | else if (lSETTINGS_DONE == num) | ||
263 | { | ||
264 | Settings = llList2List(input, 2, -1); | ||
265 | doSettings(id, Settings); | ||
266 | laterInit(); | ||
267 | s("Finished starting up " + getSetting("VERSION") + " in " + (string) (llGetTimeOfDay() - Start)); | ||
268 | } | ||
269 | else if (DEBUG_CHANNEL == num) | ||
270 | { | ||
271 | key root = llList2Key(llGetObjectDetails(id, [OBJECT_ROOT]), 0); | ||
272 | integer f = llListFindList(OhSillyThreats, [message]); | ||
273 | |||
274 | if (-1 == f) | ||
275 | OhSillyThreats += [message]; | ||
276 | else | ||
277 | d("OhSillyThreats detected the function " + message + "() again!" ); | ||
278 | s("Oh Silly threat system prevented " + message + "()" | ||
279 | + "\n in " + id + " \t" + llKey2Name(id) | ||
280 | + "\n part of " + root + " \t" + llKey2Name(root)); | ||
281 | } | ||
282 | } | ||
283 | // END boilerplate, mostly. | ||
284 | |||
285 | showAccess(key id) | ||
286 | { | ||
287 | list bosses = llParseString2List(getSetting("boss"), [","], []); | ||
288 | list trusts = llParseString2List(getSetting("trustee"), [","], []); | ||
289 | list blocked = llParseString2List(getSetting("blocked"), [","], []); | ||
290 | integer l = llGetListLength(bosses); | ||
291 | integer i; | ||
292 | string report = "Access - group "; | ||
293 | |||
294 | for (i = 0; i < l; ++i) | ||
295 | s(id, osKey2Name(llList2String(bosses, i)) + " is a " + alias(ScriptName + ".boss") + "."); | ||
296 | l = llGetListLength(trusts); | ||
297 | for (i = 0; i < l; ++i) | ||
298 | s(id, osKey2Name(llList2String(trusts, i)) + " is a " + alias(ScriptName + ".trustee") + "."); | ||
299 | l = llGetListLength(blocked); | ||
300 | for (i = 0; i < l; ++i) | ||
301 | s(id, osKey2Name(llList2String(blocked, i)) + " is " + alias(ScriptName + ".blocked") + "."); | ||
302 | if ("0" == getSetting("group")) | ||
303 | report += "off"; | ||
304 | else | ||
305 | report += "on"; | ||
306 | report += ", public "; | ||
307 | if ("0" == getSetting("public")) | ||
308 | report += "off"; | ||
309 | else | ||
310 | report += "on"; | ||
311 | s(id, report + ". The command prefix is /" + getSetting("channel") + getSetting("prefix") + "."); | ||
312 | } | ||
313 | |||
314 | integer changeAccess(key id, string cmd, key person, integer source) | ||
315 | { | ||
316 | list tu = llParseString2List(cmd, ["_"], []); | ||
317 | string t = llList2String(tu, 0); | ||
318 | string u = llList2String(tu, 1); | ||
319 | string lW = "boss"; | ||
320 | string eW = "a boss"; | ||
321 | string mW = lW; | ||
322 | string c = "_" + u; | ||
323 | |||
324 | if ("BLOCKED" == u) | ||
325 | { | ||
326 | lW = "blocked"; | ||
327 | eW = lW; | ||
328 | mW = "blockee"; | ||
329 | } | ||
330 | else if ("TRUSTEE" == u) | ||
331 | { | ||
332 | lW = "trustee"; | ||
333 | eW = "trusted"; | ||
334 | mW = lW; | ||
335 | } | ||
336 | |||
337 | list lst = llParseString2List(getSetting(lW), [","], []); | ||
338 | list r; | ||
339 | integer l; | ||
340 | integer i; | ||
341 | if (("ADD" == t) || ("NEW" == t)) | ||
342 | { | ||
343 | list b = llGetAgentList(AGENT_LIST_REGION, []); | ||
344 | l = llGetListLength(b); | ||
345 | for (i = 0; i < l; ++i) | ||
346 | { | ||
347 | if (-1 == listFindString(lst, llList2String(b, i), 1)) | ||
348 | r += [llList2String(b, i)]; | ||
349 | } | ||
350 | lst = r; | ||
351 | r = []; | ||
352 | } | ||
353 | l = llGetListLength(lst); | ||
354 | for (i = 0; i < l; ++i) | ||
355 | { | ||
356 | key k = llList2String(lst, i); | ||
357 | if (!osIsNpc(k)) | ||
358 | { | ||
359 | r += osKey2Name(k); | ||
360 | if ("NEW" == t) | ||
361 | r += k; | ||
362 | } | ||
363 | } | ||
364 | |||
365 | if ("ADD" == t) | ||
366 | { | ||
367 | if (0 == llGetListLength(r)) | ||
368 | { | ||
369 | s(id, "No one here that isn't already " + eW + "."); | ||
370 | return TRUE; | ||
371 | } | ||
372 | else | ||
373 | dynamicMenu(id, "Access", cmd, "Select a new " + mW, llDumpList2String(r, "|"), "NEW" + c); | ||
374 | } | ||
375 | else if ("DEL" == t) | ||
376 | { | ||
377 | if (0 == llGetListLength(r)) | ||
378 | { | ||
379 | s(id, "There is no one on your " + eW + " list."); | ||
380 | return TRUE; | ||
381 | } | ||
382 | else | ||
383 | dynamicMenu(id, "Access", cmd, "Select a " + mW + " to be removed.", llDumpList2String(r, "|"), "OLD" + c); | ||
384 | } | ||
385 | else if ("NEW" == t) | ||
386 | { | ||
387 | integer f = llListFindList(r, [person]); | ||
388 | if (-1 != f) | ||
389 | { | ||
390 | person = llList2Key(r, f + 1); | ||
391 | lst = llParseString2List(getSetting(lW), [","], []) + [person]; | ||
392 | setSetting(id, lW, llDumpList2String(lst, ","), source); | ||
393 | if ("BLOCKED" == u) | ||
394 | s(person, "You are now blocked from using " + llKey2Name(Owner) | ||
395 | + " " + llKey2Name(llGetKey()) + "."); | ||
396 | else if ("BOSS" == u) | ||
397 | s(person, "You are now one of the parental units for " + llKey2Name(Owner) | ||
398 | + ". Remember kids are for life, not just for Christmas."); | ||
399 | else if ("TRUSTEE" == u) | ||
400 | s(person, "You are now trusted by " + llKey2Name(Owner) + "."); | ||
401 | s(id, "You added a " + mW + " - " + osKey2Name(person)); | ||
402 | } | ||
403 | else | ||
404 | s("Can't find " + eW + " " + person); | ||
405 | } | ||
406 | else if ("OLD" == t) | ||
407 | { | ||
408 | integer f = llListFindList(r, [person]); | ||
409 | if (-1 != f) | ||
410 | { | ||
411 | person = llList2Key(lst, f); | ||
412 | lst = llListReplaceList(lst, [], f, f); | ||
413 | setSetting(id, lW, llDumpList2String(lst, ","), source); | ||
414 | if ("BOSS" == u) | ||
415 | s(person, llKey2Name(Owner) + " divorces you, but keeps the cat."); | ||
416 | else | ||
417 | s(person, "You are no longer " + mW + " by " + llKey2Name(Owner) + "."); | ||
418 | s(id, "You removed a " + mW + " - " + osKey2Name(person)); | ||
419 | } | ||
420 | else | ||
421 | s("Can't find " + eW + " " + person); | ||
422 | } | ||
423 | showAccess(id); | ||
424 | return FALSE; | ||
425 | } | ||
426 | |||
427 | integer doThing(key id, string button, string fr, string cmd, string data, integer source) | ||
428 | { | ||
429 | if ("SET " == llGetSubString(button, 0, 3)) | ||
430 | { | ||
431 | integer set = listFindString(Settings, fr + cmd, sSTRIDE); | ||
432 | if (-1 != set) | ||
433 | setSetting(id, fr + cmd, data, fINT); | ||
434 | } | ||
435 | if ((fr != (ScriptName + ".") && ("*." != fr))) return TRUE; | ||
436 | |||
437 | integer f; | ||
438 | string menu; | ||
439 | f = llSubStringIndex(button, "->"); | ||
440 | if (-1 != f) | ||
441 | { | ||
442 | menu = llGetSubString(button, 0, f - 1); | ||
443 | button = llGetSubString(button, f + 2, -1); | ||
444 | } | ||
445 | |||
446 | //d("doThing " + menu + " ... " + button + " -> " + fr + " . " + cmd + " = " + data); | ||
447 | if ("MENU" == llToUpper(cmd)) | ||
448 | { | ||
449 | sendScript(lMENU, [id, "main", ""]); | ||
450 | return FALSE; | ||
451 | } | ||
452 | else if (("PANIC" == cmd) || ("RUNAWAY" == cmd)) | ||
453 | { | ||
454 | llSay(0, "I'm a teapot, I'm a teapot!"); | ||
455 | setSetting(id, "public", "0", source); | ||
456 | setSetting(id, "hide", "0", source); | ||
457 | setSetting(id, "lock", "0", source); | ||
458 | setSetting(id, "relay", "0", source); | ||
459 | justRLV("clear"); | ||
460 | if ("RUNAWAY" == cmd) | ||
461 | { | ||
462 | llShout(0, "AAAHHHHH!"); | ||
463 | setSetting(id, "boss", "", source); | ||
464 | setSetting(id, "trustee", "", source); | ||
465 | } | ||
466 | llResetScript(); | ||
467 | } | ||
468 | else if ("HIDE" == cmd) | ||
469 | { | ||
470 | if ("0" == data) | ||
471 | llSetLinkAlpha(LINK_SET, 1.0, ALL_SIDES); | ||
472 | else | ||
473 | llSetLinkAlpha(LINK_SET, 0.0, ALL_SIDES); | ||
474 | } | ||
475 | else if ("LOCK" == cmd) | ||
476 | { | ||
477 | if ("0" == data) | ||
478 | justRLV("detach=y"); | ||
479 | else | ||
480 | justRLV("detach=n"); | ||
481 | } | ||
482 | else if ("CHOOSE_ALL" == cmd) | ||
483 | { | ||
484 | list s; | ||
485 | if ("Follow…" == button) | ||
486 | s = [AGENT, "Select someone to follow", "FOLLOW"]; | ||
487 | else if ("Go to…" == button) | ||
488 | s = [ACTIVE | AGENT | PASSIVE, "Select something or someone to go to", "GOTO"]; | ||
489 | else if ("Leash…" == button) | ||
490 | s = [ACTIVE | AGENT | PASSIVE, "Select something or someone to leash to", "LEASH"]; | ||
491 | else if ("Sit…" == button) // This will only be for objects, can't sit on an avatar. | ||
492 | s = [ACTIVE | PASSIVE, "Select something to sit on", "SIT"]; | ||
493 | else | ||
494 | { | ||
495 | D("Unknown CHOOSE_ALL option - " + button); | ||
496 | return FALSE; | ||
497 | } | ||
498 | sendScript(lSCAN, [id] + s); | ||
499 | return FALSE; | ||
500 | } | ||
501 | else if ("FOLLOW" == cmd) | ||
502 | { | ||
503 | s("Following " + llKey2Name(data)); | ||
504 | mode = MODE_FOLLOW; | ||
505 | goto(data); | ||
506 | unleash(); | ||
507 | } | ||
508 | else if ("GOTO" == cmd) | ||
509 | { | ||
510 | s("Going to " + llKey2Name(data)); | ||
511 | mode = MODE_SINGLE; | ||
512 | goto(data); | ||
513 | unleash(); | ||
514 | } | ||
515 | else if ("GOTO_TICK" == cmd) | ||
516 | { | ||
517 | if ((MODE_FOLLOW == mode) || (MODE_LEASH == mode) || (MODE_SINGLE == mode) || (MODE_SCAN == mode)) | ||
518 | goto(Stalkee); | ||
519 | } | ||
520 | else if ("FREE" == cmd) | ||
521 | { | ||
522 | s("YAY! Freeeeeee at last!!!!!"); | ||
523 | stopGoto(TRUE); | ||
524 | llReleaseControls(); | ||
525 | } | ||
526 | else if ("STAY" == cmd) | ||
527 | llRequestPermissions(llGetOwner(), PERMISSION_TAKE_CONTROLS); | ||
528 | else if ("STOP" == cmd) | ||
529 | stopGoto(TRUE); | ||
530 | else if ("GRAB" == cmd) | ||
531 | return doThing(id, llKey2Name(id), fr, "LEASH", id, source); | ||
532 | else if ("LEASH" == cmd) | ||
533 | { | ||
534 | if (("SET " != llGetSubString(button, 0, 3)) && ("" != data)) | ||
535 | { | ||
536 | s("Leashing you to " + llKey2Name(data)); | ||
537 | mode = MODE_LEASH; | ||
538 | goto(data); | ||
539 | unleash(); | ||
540 | leashTo(Stalkee); | ||
541 | } | ||
542 | } | ||
543 | else if ("RELEASE" == cmd) | ||
544 | { | ||
545 | someBoss = id; | ||
546 | stopGoto(TRUE); | ||
547 | } | ||
548 | else if ("SIT" == cmd) | ||
549 | { | ||
550 | s("Sitting on " + llKey2Name(data)); | ||
551 | justRLV("sit:" + data + "=force"); | ||
552 | } | ||
553 | else if ("STAND" == cmd) | ||
554 | justRLV("unsit=force"); | ||
555 | else if (("ADD_BLOCKED" == cmd) || ("ADD_BOSS" == cmd) || ("ADD_TRUSTEE" == cmd) | ||
556 | || ("DEL_BLOCKED" == cmd) || ("DEL_BOSS" == cmd) || ("DEL_TRUSTEE" == cmd) | ||
557 | || ("NEW_BLOCKED" == cmd) || ("NEW_BOSS" == cmd) || ("NEW_TRUSTEE" == cmd) | ||
558 | || ("OLD_BLOCKED" == cmd) || ("OLD_BOSS" == cmd) || ("OLD_TRUSTEE" == cmd)) | ||
559 | return changeAccess(id, cmd, button, source); | ||
560 | else if ("LIST_BOSS" == cmd) | ||
561 | showAccess(id); | ||
562 | else if ("CHOOSE_CLOTHES" == cmd) | ||
563 | { | ||
564 | someBoss = id; | ||
565 | sendRLV("getoutfit"); | ||
566 | return FALSE; | ||
567 | } | ||
568 | else if ("TAKEOFF" == cmd) | ||
569 | justRLV("remoutfit:" + button + "=force"); | ||
570 | else if ("CHOOSE_OUTFIT" == cmd) | ||
571 | { | ||
572 | someBoss = id; | ||
573 | sendRLV("getinv"); | ||
574 | return FALSE; | ||
575 | } | ||
576 | else if ("PUTON" == cmd) | ||
577 | { | ||
578 | string type = llGetSubString(button, 0, 1); | ||
579 | |||
580 | if ("+ " == type) | ||
581 | { | ||
582 | s("Adding contents of #RLV/" + llGetSubString(button, 2, -1)); | ||
583 | justRLV("attachallover:" + llGetSubString(button, 2, -1) + "=force"); | ||
584 | } | ||
585 | else if ("- " == type) | ||
586 | { | ||
587 | s("Removing contents of #RLV/ " + llGetSubString(button, 2, -1)); | ||
588 | justRLV("detachall:" + llGetSubString(button, 2, -1) + "=force"); | ||
589 | } | ||
590 | else | ||
591 | { | ||
592 | // TODO - Doesn't seem to be a way of detaching everything else, | ||
593 | // this just replaces anything the new outfit covers. | ||
594 | // Might be better to show sub folders instead. | ||
595 | s("Replacing outfit with contents of #RLV/ " + button); | ||
596 | justRLV("attachall:" + button + "=force"); | ||
597 | } | ||
598 | } | ||
599 | else if ("CHOOSE_ATTACH" == cmd) | ||
600 | { | ||
601 | someBoss = id; | ||
602 | sendRLV("getattach"); | ||
603 | return FALSE; | ||
604 | } | ||
605 | else if ("DETACH" == cmd) | ||
606 | justRLV("detach:" + button + "=force"); | ||
607 | else if ("RLV_OUT" == cmd) | ||
608 | { | ||
609 | gotRLV(llList2Integer(llParseString2List(data, [" "], []), 0), FALSE, ""); | ||
610 | } | ||
611 | else if (("REGION" == cmd) || ("TELEPORT" == cmd)) | ||
612 | { | ||
613 | list bdy = llParseStringKeepNulls(llUnescapeURL(data), ["|"], []); | ||
614 | TPtDestination = bdy; | ||
615 | // TODO - should validate these parameters. | ||
616 | // TODO - once the stuck detector is in place, we'll know where they used to be, | ||
617 | // try to TP to a similar distance away, in a similar direction. | ||
618 | // Or at least to half the follow distance away. | ||
619 | s("Teleporting you to " + osKey2Name(Stalkee) | ||
620 | + " at " + llList2String(TPtDestination, 1) + " " + llList2String(TPtDestination, 2)); | ||
621 | osTeleportAgent(Owner, llList2String(TPtDestination, 1), llList2Vector(TPtDestination, 2), <1.0,1.0,1.0>); | ||
622 | } | ||
623 | else if ("TESTS" == cmd) | ||
624 | s("selected test " + data); | ||
625 | else if ("URL" == cmd) | ||
626 | { | ||
627 | URL = data; | ||
628 | if (NULL_KEY != LeashKey) | ||
629 | osMessageObject(LeashKey, "URL|" + data); | ||
630 | d("New URL " + URL); | ||
631 | } | ||
632 | else if ("â–²" == cmd) | ||
633 | ; | ||
634 | else if (-1 == listFindString(Settings, fr + cmd, sSTRIDE)) | ||
635 | { | ||
636 | if (fMENU == source) | ||
637 | d("Unknown menu command '" + cmd + "' from -\n\t" + button); | ||
638 | else | ||
639 | d("Unknown command '" + cmd + "' from -\n\t" + button); | ||
640 | } | ||
641 | return (source == fMENU); | ||
642 | } | ||
643 | |||
644 | |||
645 | // General variables | ||
646 | integer LMchannel = -8888; | ||
647 | integer LMhandle0 = 0; | ||
648 | integer LMhandle1 = 0; | ||
649 | integer LGchannel = -9119; | ||
650 | key LeashKey = NULL_KEY; | ||
651 | key someBoss = NULL_KEY; | ||
652 | |||
653 | // TP tracker. | ||
654 | list TPtDestination = []; | ||
655 | |||
656 | // Movement | ||
657 | integer mode = 0; | ||
658 | integer MODE_NONE = 0; // Do nothing. | ||
659 | integer MODE_SIT = 1; // Original sit tester - menu for adjusting the sit. | ||
660 | integer MODE_DRIVE = 2; // Drivable box using controls and set pos / rot. | ||
661 | integer MODE_SINGLE = 3; // llMoveToTarget(), single scan, stop when we get there. | ||
662 | integer MODE_SCAN = 4; // llMoveToTarget(), multiple scans, stop when we get there. | ||
663 | integer MODE_FOLLOW = 5; // llMoveToTarget(), multiple scans, keep following after getting there. | ||
664 | integer MODE_LEASH = 5; // llMoveToTarget(), multiple scans, keep following after getting there, with leash. | ||
665 | |||
666 | list ATTACHMENTS = | ||
667 | [ | ||
668 | "none", | ||
669 | "chest", "skull", "left shoulder", "right shoulder", "left hand", "right hand", "left foot", "right foot", | ||
670 | "spine", "pelvis", | ||
671 | "mouth", "chin", "left ear", "right ear", "left eyeball", "right eyeball", "nose", | ||
672 | "r upper arm", "r forearm", "l upper arm", "l forearm", | ||
673 | "right hip", "r upper leg", "r lower leg", "left hip", "l upper leg", "l lower leg", | ||
674 | "stomach", "left pec", "right pec", | ||
675 | "center 2", "top right", "top", "top left", "center", "bottom left", "bottom", "bottom right", | ||
676 | "neck", "root" | ||
677 | ]; | ||
678 | |||
679 | list CLOTHES = | ||
680 | [ | ||
681 | "gloves", | ||
682 | "jacket", "pants", "shirt", | ||
683 | "shoes", "skirt", "socks", | ||
684 | "underpants", "undershirt", | ||
685 | "skin", "eyes", "hair", "shape", "alpha", "tattoo", "physics" | ||
686 | ]; | ||
687 | |||
688 | // simple emoter | ||
689 | string OurName; | ||
690 | integer meListening; | ||
691 | integer bareListening; | ||
692 | integer meChannel = 12; | ||
693 | integer bareChannel = 123; | ||
694 | |||
695 | list nextWord(string separator, string message, string last) | ||
696 | { | ||
697 | list result = []; | ||
698 | integer index; | ||
699 | |||
700 | index = llSubStringIndex(message, separator); | ||
701 | if (0 <= index) | ||
702 | { | ||
703 | if (0 != index) // Check for a corner case. | ||
704 | last += llGetSubString(message, 0, index - 1); | ||
705 | if ((index + 1) < llStringLength(message)) | ||
706 | message = llGetSubString(message, index + 1, -1); | ||
707 | else | ||
708 | message = ""; | ||
709 | result += message + last; | ||
710 | } | ||
711 | else | ||
712 | result += last + message; | ||
713 | |||
714 | return(result); | ||
715 | } | ||
716 | |||
717 | integer findLink(string name) | ||
718 | { | ||
719 | integer n = llGetNumberOfPrims(); | ||
720 | integer link; | ||
721 | for (link = 1; link <= n; ++link) | ||
722 | { | ||
723 | if (llGetLinkName(link) == name) | ||
724 | return link; | ||
725 | } | ||
726 | link = -1; | ||
727 | return link; | ||
728 | } | ||
729 | |||
730 | leashTo(key id) | ||
731 | { | ||
732 | integer link = findLink("leashpoint"); | ||
733 | float age = 4.0; | ||
734 | integer count = 1; | ||
735 | float rate = 0.05; | ||
736 | list p = | ||
737 | [ | ||
738 | PSYS_PART_FLAGS, PSYS_PART_FOLLOW_VELOCITY_MASK | PSYS_PART_TARGET_POS_MASK | PSYS_PART_EMISSIVE_MASK | PSYS_PART_FOLLOW_SRC_MASK, | ||
739 | PSYS_SRC_PATTERN, PSYS_SRC_PATTERN_DROP, | ||
740 | PSYS_PART_START_SCALE, <0.3, 0.3, 0.1>, | ||
741 | PSYS_SRC_ACCEL, <0,0,-0.4>, | ||
742 | PSYS_PART_MAX_AGE, age, | ||
743 | PSYS_SRC_BURST_RADIUS, 0.1, | ||
744 | PSYS_SRC_BURST_RATE, rate, | ||
745 | PSYS_SRC_BURST_PART_COUNT, count, | ||
746 | PSYS_SRC_BURST_SPEED_MIN, 3.0, | ||
747 | PSYS_SRC_BURST_SPEED_MAX, 3.0, | ||
748 | PSYS_SRC_TEXTURE, getSetting("leash"), | ||
749 | PSYS_SRC_TARGET_KEY, id, | ||
750 | PSYS_SRC_ANGLE_BEGIN, 0.0, | ||
751 | PSYS_SRC_ANGLE_END, PI, | ||
752 | PSYS_SRC_OMEGA, <0,0,1>, | ||
753 | PSYS_SRC_MAX_AGE, 0.0 | ||
754 | ]; | ||
755 | if ( ((age / rate) * count) < 4096) | ||
756 | { | ||
757 | if (-1 != link) | ||
758 | llLinkParticleSystem(link, p); | ||
759 | else | ||
760 | llParticleSystem(p); | ||
761 | if (0 == LMhandle0) | ||
762 | { | ||
763 | LMhandle0 = llListen(LMchannel, "", NULL_KEY, (string) llGetOwnerKey(Stalkee) + "handle ok"); | ||
764 | LMhandle1 = llListen(LMchannel, "", NULL_KEY, (string) llGetOwnerKey(Stalkee) + "handle detached"); | ||
765 | llShout(LMchannel, (string) llGetOwnerKey(Stalkee) + "collar"); | ||
766 | llShout(LMchannel, (string) llGetOwnerKey(Stalkee) + "handle"); | ||
767 | llShout(LGchannel, "lockguard " + Owner + " handle link " + llGetLinkKey(link)); | ||
768 | } | ||
769 | } | ||
770 | else | ||
771 | D("Your particle system creates too many concurrent particles. Reduce count or age, or increase rate."); | ||
772 | } | ||
773 | |||
774 | unleash() | ||
775 | { | ||
776 | integer link = findLink("leashpoint"); | ||
777 | if (LMhandle0) llListenRemove(LMhandle0); | ||
778 | if (LMhandle1) llListenRemove(LMhandle1); | ||
779 | LMhandle0 = 0; | ||
780 | LMhandle1 = 0; | ||
781 | if (-1 != link) | ||
782 | llLinkParticleSystem(link, []); | ||
783 | else | ||
784 | llParticleSystem([]); | ||
785 | } | ||
786 | |||
787 | |||
788 | // Following | ||
789 | key Stalker; | ||
790 | key Stalkee; | ||
791 | integer tid = 0; | ||
792 | float RANGE = 4.0; // Meters away that we stop walking towards. | ||
793 | float TAU = 0.1; // Make smaller for more rushed following. | ||
794 | |||
795 | float bb = 0.0; | ||
796 | float tweak = 0.0; | ||
797 | float sr = 10; | ||
798 | integer Lag = 100; | ||
799 | |||
800 | vector getRange(key this) | ||
801 | { | ||
802 | list det = llGetObjectDetails(this, [OBJECT_POS, OBJECT_ROT]); | ||
803 | |||
804 | if (0 == llGetListLength(det)) | ||
805 | { | ||
806 | // Note - we can't use llGetObjectDetails() here, coz they might not be in adjacent sims either. | ||
807 | s("Can't find " + osKey2Name(this) + " in the sim."); | ||
808 | stopGoto(TRUE); | ||
809 | return ZERO_VECTOR; | ||
810 | } | ||
811 | |||
812 | vector pos = llList2Vector(det, 0); | ||
813 | // rotation rot = (rotation)llList2String(det, 1); | ||
814 | |||
815 | if (this != Stalkee) | ||
816 | { | ||
817 | Stalkee = this; | ||
818 | |||
819 | vector tSize = llGetAgentSize(this); | ||
820 | vector mSize = llGetAgentSize(llGetOwner()); | ||
821 | tweak = (mSize.z - tSize.z) * 2; | ||
822 | if (0.0 > tweak) | ||
823 | tweak = 0.0 - tweak; | ||
824 | //d("tweak " + (string) mSize.z + " " + (string) tSize.z + " " + (string) tweak); | ||
825 | if (1.6 > tweak) | ||
826 | tweak = 1.6; | ||
827 | list box = llGetBoundingBox(this); | ||
828 | bb = llVecDist(llList2Vector(box, 1), llList2Vector(box, 0)) / 2.0; | ||
829 | } | ||
830 | pos.z = pos.z - tweak; | ||
831 | // pos += offset * rot; | ||
832 | return pos; | ||
833 | } | ||
834 | |||
835 | goto(key this) | ||
836 | { | ||
837 | if (NULL_KEY == this) | ||
838 | return; | ||
839 | |||
840 | float dist = 0.0; | ||
841 | vector pos = getRange(this); | ||
842 | |||
843 | if (ZERO_VECTOR == pos) | ||
844 | return; | ||
845 | dist = llVecDist(pos, llGetPos()); | ||
846 | vector p = pos - llGetPos(); | ||
847 | float r = llAtan2(p.x, p.y); | ||
848 | if (llFabs(sr - r) > 0.25) | ||
849 | { | ||
850 | justRLV("setrot:" + (string) r + "=force"); | ||
851 | sr = r; | ||
852 | } | ||
853 | if (dist > (RANGE + bb)) | ||
854 | { | ||
855 | if (dist > (2.0 * (RANGE + bb))) | ||
856 | osSetSpeed(Owner, 2.0); | ||
857 | else | ||
858 | osSetSpeed(Owner, 1.0); | ||
859 | |||
860 | llStopMoveToTarget(); | ||
861 | if (0 != tid) | ||
862 | llTargetRemove(tid); | ||
863 | llMoveToTarget(pos, TAU); | ||
864 | } | ||
865 | else | ||
866 | { | ||
867 | if (MODE_SINGLE == mode) | ||
868 | stopGoto(TRUE); | ||
869 | else | ||
870 | stopGoto(FALSE); | ||
871 | } | ||
872 | if ((MODE_FOLLOW == mode) || (MODE_LEASH == mode) || (MODE_SINGLE == mode) || (MODE_SCAN == mode)) | ||
873 | { | ||
874 | float tick = 0.35; | ||
875 | float dil = llGetRegionTimeDilation(); // Between 0 and 1. | ||
876 | float fps = llGetRegionFPS(); // Frames per second, up to 50. | ||
877 | integer newLag = (integer) (dil * fps); | ||
878 | |||
879 | if (llAbs(Lag - newLag) > 9) | ||
880 | { | ||
881 | Lag = newLag; | ||
882 | tick = ((60 - (dil * fps)) / 30) + 0.15; | ||
883 | } | ||
884 | addEvent(tick, "GOTO_TICK"); | ||
885 | } | ||
886 | else | ||
887 | { | ||
888 | Lag = 100; | ||
889 | addEvent(0.0, "GOTO_TICK"); | ||
890 | } | ||
891 | } | ||
892 | |||
893 | stopGoto(integer all) | ||
894 | { | ||
895 | addEvent(0.0, "GOTO_TICK"); | ||
896 | llStopMoveToTarget(); | ||
897 | if (0 != tid) | ||
898 | { | ||
899 | llTargetRemove(tid); | ||
900 | tid = 0; | ||
901 | } | ||
902 | osSetSpeed(Owner, 1.0); | ||
903 | if (all) | ||
904 | { | ||
905 | Stalkee = NULL_KEY; | ||
906 | mode = MODE_NONE; | ||
907 | Lag = 100; | ||
908 | bb = 0.0; | ||
909 | tweak = 0.0; | ||
910 | sr = 10; | ||
911 | unleash(); | ||
912 | } | ||
913 | } | ||
914 | |||
915 | |||
916 | // We have to jump through hoops coz RLV wont tell us if a command failed, or was delayed. | ||
917 | // And viewer support for RLV is all kinds of broken. | ||
918 | integer RLVchannel; | ||
919 | //integer RLVCHAN = -1812221819; // RLV relay channel. | ||
920 | string RLVversion; | ||
921 | list RLVq; // Queue of RLV commands. | ||
922 | integer qCHAN = 0; | ||
923 | integer qCMD = 1; | ||
924 | integer qNUM = 2; | ||
925 | integer qRSP = 3; | ||
926 | integer qTIME = 4; | ||
927 | integer qTRIES = 5; | ||
928 | integer qLSTN = 6; | ||
929 | integer qSTRIDE = 7; | ||
930 | integer RLVl; // Current RLV channel. | ||
931 | integer RLVd; // RLV startup is done. | ||
932 | list RLVa; // all RLV commands. | ||
933 | list RLVb; // RLV comands in blacklist. | ||
934 | list RLVs; // RLV commands currently restricted. | ||
935 | list RLVu = // RLV commands we actually use. | ||
936 | [ | ||
937 | "attachallover", "attachall", "clear", "detach", "detachall", "getattach", | ||
938 | "getcommand", "getstatusall", "notify", | ||
939 | "getinv", "getoutfit", "remoutfit", "setrot", "sit", "unsit", | ||
940 | "versionnew", "versionnumbl" | ||
941 | ]; | ||
942 | |||
943 | justRLV(string cmd) | ||
944 | { | ||
945 | sendRLV(cmd, -1.0); | ||
946 | } | ||
947 | sendRLV(string cmd) | ||
948 | { | ||
949 | sendRLV(cmd, 10.0); | ||
950 | } | ||
951 | sendRLV(string cmd, float time) | ||
952 | { | ||
953 | integer c = llGetListLength(llParseString2List(cmd, [","], [])); | ||
954 | RLVq += [(string)(RLVchannel + RLVl), cmd, (string) c, 0, time, 0, 0]; | ||
955 | ++RLVl; | ||
956 | doRLV(); | ||
957 | } | ||
958 | doRLV() | ||
959 | { | ||
960 | integer i; | ||
961 | // Normally you don't get the length in the middle of the loop, but it may change on us. | ||
962 | for (i = 0; i < llGetListLength(RLVq); i += qSTRIDE) | ||
963 | doRLV(i); | ||
964 | } | ||
965 | doRLV(integer f) | ||
966 | { | ||
967 | integer chan = llList2Integer(RLVq, f + qCHAN); | ||
968 | string cmds = llList2String(RLVq, f + qCMD); | ||
969 | integer t = llList2Integer(RLVq, f + qTRIES); | ||
970 | float time = llList2Float(RLVq, f + qTIME); | ||
971 | integer h = llList2Integer(RLVq, f + qLSTN); | ||
972 | string cmd = ""; | ||
973 | if (0 != h) | ||
974 | return; | ||
975 | // TODO - check if this command is available and not blacklisted. | ||
976 | // Though since getcommands seems to miss "clear" and "setrot", even though they work, seems pointless. | ||
977 | if (0.0 < time) | ||
978 | { | ||
979 | list c = llParseString2List(cmds, [","], []); | ||
980 | integer l = llGetListLength(c); | ||
981 | integer i; | ||
982 | for (i = 0; i < l; ++i) | ||
983 | cmd += "," + llList2String(c, i) + "=" + chan; | ||
984 | cmd = llGetSubString(cmd, 1, -1); | ||
985 | if (0 == h) | ||
986 | { | ||
987 | h = llListen(chan, "", Owner, ""); | ||
988 | RLVq = llListReplaceList(RLVq, [h], f + qLSTN, f + qLSTN); | ||
989 | } | ||
990 | llOwnerSay("@" + cmd); | ||
991 | ++t; | ||
992 | RLVq = llListReplaceList(RLVq, [t], f + qTRIES, f + qTRIES); | ||
993 | addEvent(10.0, "RLV_OUT " + chan + " " + cmds); | ||
994 | } | ||
995 | else if (RLVd) | ||
996 | { | ||
997 | llOwnerSay("@" + cmds); | ||
998 | RLVq = llListReplaceList(RLVq, [], f, f + qSTRIDE - 1); | ||
999 | } | ||
1000 | } | ||
1001 | |||
1002 | getRLV(string name) | ||
1003 | { | ||
1004 | string r; | ||
1005 | integer l = llGetListLength(RLVu); | ||
1006 | integer i; | ||
1007 | for (i = 0; i < l; ++i) | ||
1008 | r += "," + name + ":" + llList2String(RLVu, i); | ||
1009 | sendRLV(llGetSubString(r, 1, -1)); | ||
1010 | } | ||
1011 | |||
1012 | gotStatus(string message) | ||
1013 | { | ||
1014 | if ("" != message) | ||
1015 | { | ||
1016 | list st = llParseString2List(message, ["/"], []); | ||
1017 | integer l = llGetListLength(st); | ||
1018 | integer i; | ||
1019 | for (i = 0; i < l; ++i) | ||
1020 | { | ||
1021 | string r = llList2String(st, i); | ||
1022 | if ("notify:" == llGetSubString(r, 0, 6)) | ||
1023 | { // Not documented anywhere I can find, but this is telling us we already have one. | ||
1024 | // Though I have seen it telling us this is the one we just created. Play it safe. | ||
1025 | integer t = (integer) llGetSubString(r, 7, -1); | ||
1026 | if((RLVchannel - 1) != t) | ||
1027 | { | ||
1028 | d("gotStatus removing " + r); | ||
1029 | justRLV("notify:" + t + "=rem"); | ||
1030 | } | ||
1031 | } | ||
1032 | else | ||
1033 | { | ||
1034 | integer a = TRUE; | ||
1035 | integer j = llSubStringIndex(r, "="); | ||
1036 | if (-1 != j) | ||
1037 | a = (llGetSubString(r, j + 1, j + 1) == "n"); | ||
1038 | if (a) | ||
1039 | { | ||
1040 | d("gotStatus adding " + r); | ||
1041 | RLVs += [r]; | ||
1042 | } | ||
1043 | else | ||
1044 | { | ||
1045 | j = llListFindList(RLVs, [r]); | ||
1046 | if (-1 != j) | ||
1047 | { | ||
1048 | d("gotStatus subtracting " + r); | ||
1049 | RLVs = llListReplaceList(RLVs, [], j, j); | ||
1050 | } | ||
1051 | else | ||
1052 | d("gotStatus already got " + r); | ||
1053 | } | ||
1054 | } | ||
1055 | } | ||
1056 | RLVs = deDup(RLVs); | ||
1057 | s(llGetListLength(RLVs) + " RLV restrictions - " + llDumpList2String(RLVs, " / ")); | ||
1058 | } | ||
1059 | } | ||
1060 | |||
1061 | list deDup(list lst) | ||
1062 | { | ||
1063 | integer l = llGetListLength(lst); | ||
1064 | integer i; | ||
1065 | lst = llListSort(lst, 1, TRUE); | ||
1066 | if (1 < l) | ||
1067 | { | ||
1068 | //d("deDup " + llDumpList2String(lst, " ~ ")); | ||
1069 | for (i = 1; i < l; ++i) | ||
1070 | { | ||
1071 | if (llList2String(lst, i - 1) == llList2String(lst, i)) | ||
1072 | { | ||
1073 | lst = llListReplaceList(lst, [], i, i); | ||
1074 | --i; --l; | ||
1075 | } | ||
1076 | } | ||
1077 | //d("deDup " + llDumpList2String(lst, " ~ ") + "\n"); | ||
1078 | } | ||
1079 | return lst; | ||
1080 | } | ||
1081 | |||
1082 | string gotRLV(integer chan, integer g, string message) | ||
1083 | { | ||
1084 | string r; | ||
1085 | integer f = listFindString(RLVq, (string) chan, qSTRIDE); | ||
1086 | if (-1 != f) | ||
1087 | { | ||
1088 | r = llList2String(RLVq, f + qCMD); | ||
1089 | integer n = llList2Integer(RLVq, f + qNUM); | ||
1090 | integer rsp = llList2Integer(RLVq, f + qRSP); | ||
1091 | integer v = ("versionnew" == r); | ||
1092 | integer t = llList2Integer(RLVq, f + qTRIES); | ||
1093 | integer h = llList2Integer(RLVq, f + qLSTN); | ||
1094 | |||
1095 | if (g) | ||
1096 | { | ||
1097 | //string rp = "FULL "; | ||
1098 | addEvent(0.0, "RLV_OUT " + chan + " " + llList2String(RLVq, f + qCMD)); | ||
1099 | ++rsp; | ||
1100 | RLVq = llListReplaceList(RLVq, [rsp], f + qRSP, f + qRSP); | ||
1101 | list c = llParseString2List(r, [","], []); | ||
1102 | r = llList2String(llParseString2List(llList2String(c, 0), [":", "="], []), 0); | ||
1103 | if (rsp >= n) | ||
1104 | { | ||
1105 | --t; | ||
1106 | if (0 < t) | ||
1107 | { | ||
1108 | RLVq = llListReplaceList(RLVq, [0], f + qRSP, f + qRSP); | ||
1109 | RLVq = llListReplaceList(RLVq, [t], f + qTRIES, f + qTRIES); | ||
1110 | //d("gotRLV " + rp + " " + chan + " " + n + "==" + rsp + " " + t + " " + r + " -> " + message + "\n" + llDumpList2String(llList2List(RLVq, f, f + qSTRIDE - 1), " ~ ")); | ||
1111 | } | ||
1112 | else | ||
1113 | { | ||
1114 | if (0 != h) | ||
1115 | llListenRemove(h); | ||
1116 | //d("gotRLV " + rp + " " + chan + " " + n + "==" + rsp + " " + " " + t + " " + r + " -> " + message); | ||
1117 | RLVq = llListReplaceList(RLVq, [], f, f + qSTRIDE - 1); | ||
1118 | } | ||
1119 | } | ||
1120 | else | ||
1121 | { | ||
1122 | //rp = "PARTIAL"; | ||
1123 | //d("gotRLV " + rp + " " + chan + " " + n + "==" + rsp + " " + " " + t + " " + r + " -> " + message + "\n" + llDumpList2String(llList2List(RLVq, f, f + qSTRIDE - 1), " ~ ")); | ||
1124 | } | ||
1125 | |||
1126 | if (v) | ||
1127 | { | ||
1128 | RLVversion = message; | ||
1129 | s(RLVversion); | ||
1130 | getRLV("getcommand"); | ||
1131 | sendRLV("versionnumbl"); | ||
1132 | } | ||
1133 | else | ||
1134 | { | ||
1135 | if ("getcommand" == r) | ||
1136 | RLVa += llParseString2List(message, ["/", ";"], []); | ||
1137 | else if ("versionnumbl" == r) | ||
1138 | { | ||
1139 | list tm = llParseString2List(message, ["/", ";"], []); | ||
1140 | if (1 < llGetListLength(tm)) | ||
1141 | RLVb += llList2List(tm, 1, -1); | ||
1142 | } | ||
1143 | else if ("getstatusall" == r) | ||
1144 | gotStatus(message); | ||
1145 | else if ("" == r) | ||
1146 | d("RLV report for unknown command - " + message); | ||
1147 | } | ||
1148 | } | ||
1149 | else | ||
1150 | { | ||
1151 | if (2 >= t) | ||
1152 | { // We are either waiting for the viewer to get it's shit together on login, | ||
1153 | // or slowly finding out these commands are not supported anyway. | ||
1154 | // 30 seconds seems about right for the former, and has been documented. | ||
1155 | d("RLV command " + r + " not responding! Trying again. " + t); | ||
1156 | if (1 == t) s("RLV could take up to 60 seconds to start up, you may have to wait for that."); | ||
1157 | if (0 != h) | ||
1158 | { | ||
1159 | llListenRemove(h); | ||
1160 | RLVq = llListReplaceList(RLVq, [0], f + qLSTN, f + qLSTN); | ||
1161 | } | ||
1162 | doRLV(f); | ||
1163 | } | ||
1164 | else | ||
1165 | { | ||
1166 | if (v) // Various docs say RLV might take upto 30 seconds to start in the viewer. | ||
1167 | s(Owner, "Your viewer is not RLV-enabled, so some things wont work."); | ||
1168 | else | ||
1169 | { | ||
1170 | d("RLV command " + r + " not responding! Giving up."); | ||
1171 | s("Your viewer is taking too long to start up RLV."); | ||
1172 | } | ||
1173 | if (0 != h) llListenRemove(h); | ||
1174 | RLVq = llListReplaceList(RLVq, [], f, f + qSTRIDE - 1); | ||
1175 | } | ||
1176 | } | ||
1177 | } | ||
1178 | else if ((RLVchannel - 1) == chan) | ||
1179 | { | ||
1180 | d("gotRLV @notify response - " + message); | ||
1181 | gotStatus(message); | ||
1182 | } | ||
1183 | else | ||
1184 | D("gotRLV() didn't find - " + g + " " + chan + " -> " + message + " " + "\n" + llDumpList2String(RLVq, " ~ ")); | ||
1185 | |||
1186 | if ((!RLVd) && ("" != RLVversion)) | ||
1187 | { | ||
1188 | integer l = llGetListLength(RLVq); | ||
1189 | integer i; | ||
1190 | RLVd = TRUE; | ||
1191 | for (i = 0; i < l; i += qSTRIDE) | ||
1192 | { | ||
1193 | if (0 != llList2Integer(RLVq, i + qLSTN)) | ||
1194 | RLVd = FALSE; | ||
1195 | } | ||
1196 | if (RLVd) | ||
1197 | { | ||
1198 | if (0 == llGetListLength(RLVa)) RLVa = RLVu; | ||
1199 | getRLV("getstatusall"); // Doing this now coz Cool VL is odd at login. | ||
1200 | // TODO - Also deal with "%f" == "=force". | ||
1201 | RLVa = deDup(RLVa); | ||
1202 | RLVb = deDup(RLVb); | ||
1203 | l = llGetListLength(RLVb); | ||
1204 | for (i = 0; i < l; ++i) | ||
1205 | { | ||
1206 | f = llListFindList(RLVa, [llList2String(RLVb, i)]); | ||
1207 | if (-1 != f) | ||
1208 | RLVa = llListReplaceList(RLVa, [], f, f); | ||
1209 | } | ||
1210 | list bl; | ||
1211 | l = llGetListLength(RLVu); | ||
1212 | for (i = 0; i < l; ++i) | ||
1213 | { | ||
1214 | f = llListFindList(RLVa, [llList2String(RLVu, i)]); | ||
1215 | if (-1 == f) | ||
1216 | bl += [llList2String(RLVu, i)]; | ||
1217 | } | ||
1218 | s("RLV started up in " + (string) (llGetTimeOfDay() - Start) + " seconds.\n" | ||
1219 | + llGetListLength(RLVa) + " RLV commands, " | ||
1220 | + llGetListLength(RLVb)+ " RLV black listed commands - " | ||
1221 | + llDumpList2String(RLVb, " / ")); | ||
1222 | if (0 != llGetListLength(bl)) | ||
1223 | s("The following RLV commands are unsupported by your viewer, or blacklisted - " | ||
1224 | + llDumpList2String(bl, ", ") | ||
1225 | + "\nSome things may or may not work. That list may not be accurate."); | ||
1226 | justRLV("notify:" + (RLVchannel - 1) + "=add"); | ||
1227 | doRLV(); | ||
1228 | } | ||
1229 | } | ||
1230 | return r; | ||
1231 | } | ||
1232 | |||
1233 | init() | ||
1234 | { | ||
1235 | stopGoto(TRUE); | ||
1236 | Stalker = llGetOwner(); | ||
1237 | OurName = llGetObjectName(); | ||
1238 | RLVchannel = llAbs((integer) ("0x" + llGetSubString((string) llGetKey(), -4, -1))) + (integer) llFrand(9999.0); | ||
1239 | llListen(RLVchannel, "", llGetOwner(), ""); | ||
1240 | meListening = llListen(meChannel, "", llGetOwner(), ""); | ||
1241 | bareListening = llListen(bareChannel, "", llGetOwner(), ""); | ||
1242 | } | ||
1243 | |||
1244 | laterInit() | ||
1245 | { | ||
1246 | showAccess(Owner); | ||
1247 | } | ||
1248 | |||
1249 | default | ||
1250 | { | ||
1251 | state_entry() | ||
1252 | { | ||
1253 | llSleep(0.2); | ||
1254 | Start = llGetTimeOfDay(); | ||
1255 | Owner = llGetOwner(); | ||
1256 | d("\n\n1ring sending RESET @ " + (string) Start + "\n"); | ||
1257 | ScriptName = llGetScriptName(); ScriptKey = llGetInventoryKey(ScriptName); | ||
1258 | LibraryKey = NULL_KEY; | ||
1259 | sendScript(lRESET, []); | ||
1260 | init(); | ||
1261 | } | ||
1262 | |||
1263 | link_message(integer sender, integer num, string message, key id) | ||
1264 | { | ||
1265 | linky(num, message, id); | ||
1266 | } | ||
1267 | |||
1268 | on_rez(integer param) | ||
1269 | { | ||
1270 | // showAccess(Owner); | ||
1271 | d("on_rez reset"); | ||
1272 | llResetScript(); | ||
1273 | } | ||
1274 | |||
1275 | attach(key id) | ||
1276 | { | ||
1277 | if ((NULL_KEY != id) && ((llGetTimeOfDay() - Start) > 60.0)) | ||
1278 | { | ||
1279 | s("Attach detected, and RLV didn't respond yet, resetting."); | ||
1280 | // llResetScript(); | ||
1281 | } | ||
1282 | } | ||
1283 | |||
1284 | at_target(integer tnum, vector targetpos, vector ourpos) | ||
1285 | { | ||
1286 | if (tnum == tid) | ||
1287 | { | ||
1288 | vector p = targetpos - ourpos; | ||
1289 | justRLV("setrot:" + (string) llAtan2(p.x, p.y) + "=force"); | ||
1290 | if ((MODE_SINGLE == mode) || (MODE_SCAN == mode)) | ||
1291 | stopGoto(TRUE); | ||
1292 | } | ||
1293 | } | ||
1294 | |||
1295 | run_time_permissions(integer perm) | ||
1296 | { | ||
1297 | if (perm & PERMISSION_TAKE_CONTROLS) | ||
1298 | { | ||
1299 | llTakeControls(0, FALSE, FALSE); | ||
1300 | s(llKey2Name(someBoss) + " has made you stay."); | ||
1301 | } | ||
1302 | } | ||
1303 | |||
1304 | listen(integer channel, string name, key id, string message) | ||
1305 | { | ||
1306 | if (channel == LMchannel) | ||
1307 | { | ||
1308 | list dt = llGetObjectDetails(id, [OBJECT_ROOT]); | ||
1309 | key t = llGetSubString(message, 0, 35); | ||
1310 | message = llGetSubString(message, 36, -1); | ||
1311 | if ((llList2Key(dt, 0) == Stalkee) || (llGetOwnerKey(id) == Stalkee)) | ||
1312 | { | ||
1313 | //leash holder announced it got detached... send particles to avi | ||
1314 | if (message == "handle detached") | ||
1315 | leashTo(Stalkee); | ||
1316 | else | ||
1317 | { | ||
1318 | // We heard from a leash holder. re-direct particles | ||
1319 | LeashKey = id; | ||
1320 | if (message == "collar ok") | ||
1321 | leashTo(id); | ||
1322 | if (message == "handle ok") | ||
1323 | leashTo(id); | ||
1324 | osMessageObject(LeashKey, "URL|" + URL); | ||
1325 | d("HEY leashy! " + URL); | ||
1326 | } | ||
1327 | } | ||
1328 | } | ||
1329 | |||
1330 | if ((RLVchannel <= channel) && (channel < (RLVchannel + RLVl))) | ||
1331 | { | ||
1332 | string rlv = gotRLV(channel, TRUE, message); | ||
1333 | list items = []; | ||
1334 | channel = 0; | ||
1335 | if ("getattach" == rlv) | ||
1336 | { | ||
1337 | do | ||
1338 | { | ||
1339 | if (llGetSubString(message, channel, channel) == "1") | ||
1340 | items += llList2String(ATTACHMENTS, channel); | ||
1341 | } while(++channel < 42); | ||
1342 | |||
1343 | dynamicMenu(someBoss, "clothes", "DETACH", "Please select the attachment to detach", | ||
1344 | llDumpList2String(items, "|"), "DETACH"); | ||
1345 | } | ||
1346 | else if ("getoutfit" == rlv) | ||
1347 | { | ||
1348 | do | ||
1349 | { | ||
1350 | if (llGetSubString(message, channel, channel) == "1") | ||
1351 | items += llList2String(CLOTHES, channel); | ||
1352 | } while(++channel < 20); | ||
1353 | |||
1354 | dynamicMenu(someBoss, "clothes", "TAKEOFF", "Please select the clothes to take off", | ||
1355 | llDumpList2String(items, "|"), "TAKEOFF"); | ||
1356 | } | ||
1357 | else if ("getinv" == rlv) | ||
1358 | { | ||
1359 | list folders = llListSort(llCSV2List(message), 1, TRUE); | ||
1360 | integer l = llGetListLength(folders); | ||
1361 | integer i; | ||
1362 | |||
1363 | for (i = 0; i < l; ++i) | ||
1364 | { | ||
1365 | string folder = llList2String(folders, i); | ||
1366 | items += [folder, "+ " + folder, "- " + folder]; | ||
1367 | } | ||
1368 | dynamicMenu(someBoss, "clothes", "PUTON", "Please select the outfit to put on", | ||
1369 | llDumpList2String(items, "|"), "PUTON"); | ||
1370 | } | ||
1371 | } | ||
1372 | |||
1373 | list result = nextWord(" ", message, ""); | ||
1374 | if (channel == meChannel) | ||
1375 | { | ||
1376 | list names = nextWord(" ", llKey2Name(llGetOwner()), ""); | ||
1377 | |||
1378 | if (llGetSubString(message, 0, 0) == "'") | ||
1379 | { | ||
1380 | list suffix = nextWord(" ", message, ""); | ||
1381 | names = ["", llList2String(names, 1) + llList2String(suffix, 1)]; | ||
1382 | message = llList2String(suffix, 0); | ||
1383 | } | ||
1384 | // Rename ourselves to the first word of owners name. | ||
1385 | llSetObjectName(llList2String(names, 1)); | ||
1386 | llSay(0, "/me " + message); | ||
1387 | } | ||
1388 | else if (channel == bareChannel) | ||
1389 | { | ||
1390 | // Rename ourselves to the first word. | ||
1391 | llSetObjectName(llList2String(result, 1)); | ||
1392 | llSay(0, "/me " + llList2String(result, 0)); | ||
1393 | } | ||
1394 | // Change our name back | ||
1395 | llSetObjectName(OurName); | ||
1396 | } | ||
1397 | |||
1398 | changed(integer change) | ||
1399 | { | ||
1400 | if (change & CHANGED_INVENTORY) | ||
1401 | { | ||
1402 | // readSettings(); | ||
1403 | // showAccess(Owner); | ||
1404 | } | ||
1405 | if (change & CHANGED_TELEPORT) | ||
1406 | { | ||
1407 | // TODO - check if this was a requested TP and we are being carried. If so, restart the carry process. | ||
1408 | if (0 != llGetListLength(TPtDestination)) | ||
1409 | { | ||
1410 | // Check if we ended up in a landing spot instead of where we thought we where going. | ||
1411 | vector pos = getRange(Stalkee); | ||
1412 | float dist = llVecDist(pos, llGetPos()); | ||
1413 | if (dist > (2 *(RANGE + bb))) | ||
1414 | { | ||
1415 | s("Double TP to get closer."); | ||
1416 | osTeleportAgent(Owner, llList2String(TPtDestination, 1), llList2Vector(TPtDestination, 2), <1.0,1.0,1.0>); | ||
1417 | } | ||
1418 | TPtDestination = []; | ||
1419 | } | ||
1420 | } | ||
1421 | if (change & CHANGED_OWNER) | ||
1422 | llResetScript(); | ||
1423 | } | ||
1424 | } | ||