aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/1ring.lsl
diff options
context:
space:
mode:
authoronefang2019-06-30 11:16:41 +1000
committeronefang2019-06-30 11:16:41 +1000
commit1e5b49a55b7ebd03c6e57be993668f38ac20f841 (patch)
tree5cabe2fdc2e51e46bcb6f5ca5ce5110b5be3bdb0 /1ring.lsl
parentLine wrapping is a thing, but not in notecards. (diff)
download1ring-1e5b49a55b7ebd03c6e57be993668f38ac20f841.zip
1ring-1e5b49a55b7ebd03c6e57be993668f38ac20f841.tar.gz
1ring-1e5b49a55b7ebd03c6e57be993668f38ac20f841.tar.bz2
1ring-1e5b49a55b7ebd03c6e57be993668f38ac20f841.tar.xz
Include the actual source code this time.
Diffstat (limited to '1ring.lsl')
-rw-r--r--1ring.lsl1424
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
5string Version = "1ring v0.1 test version";
6
7// BEGIN boilerplate.
8integer DEBUG = FALSE;
9float Start;
10string ScriptName;
11key ScriptKey;
12key LibraryKey;
13key Owner;
14string URL;
15
16// Settings.
17list Aliases;
18list Settings;
19integer sNAME = 0;
20integer sTYPE = 1;
21integer sVALUE = 2;
22integer sAUTH = 3;
23integer sSTRIDE = 4;
24
25// Access "f"rom some source -
26integer fINT = 0; // Internal, not sure we need this, means the same script.
27integer fCARD = 1; // Read from a note card.
28integer fCHAT = 2; // Chat channel.
29integer fLINK = 3; // Link message.
30integer fMENU = 4; // Menu, in reality this is just a chat channel.
31integer fRLV = 5; // RLV, again from a chat channel.
32integer fRELAY = 6; // RLV relay.
33integer fOSM = 7; // osMessageObject()
34integer fHTTP = 8; // From the web.
35
36// utilities commands, "l"ibrary.
37string lSEP = "$!#"; // Used to seperate lists when sending them as strings.
38integer lRESET = -1;
39integer lRESET_DONE = -2;
40integer lALIAS = -3;
41integer lALIAS_DONE = -4;
42integer lSETTING = -5;
43integer lSETTING_DONE = -6;
44integer lSUBSTITUTE = -7;
45integer lSUBSTITUTE_DONE = -8;
46integer lNEXT_WORD = -9;
47integer lNEXT_WORD_DONE = -10;
48integer lCONTROL = -13;
49integer lCONTROL_DONE = -14;
50integer lCMD = -15;
51integer lCMD_DONE = -16;
52integer lSETTINGS = -17;
53integer lSETTINGS_DONE = -18;
54integer lSCAN = -19;
55integer lDYNAMIC = -20;
56integer lMENU = -21;
57
58// OhSillyThreat detector
59list OhSillyThreats = [];
60list 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
74d(string m) {if (DEBUG) llInstantMessage(Owner, llGetScriptName() + ": " + m);}
75D(string m) {llRegionSay(DEBUG_CHANNEL, llGetScriptName() + ": " + m);}
76s(string m) {s(Owner, m);}
77s(key id, string m) {if (id == Owner) llOwnerSay(m); else llInstantMessage(id, m);}
78sendScript(integer cmd, list args) {sendScript(LibraryKey, cmd, ScriptName, args);}
79sendScript(key them, integer cmd, list args) {sendScript(them, cmd, inKey2Name(them), args);}
80sendScript(integer cmd, string name, list args) {sendScript(LibraryKey, cmd, name, args);}
81sendScript(key them, integer cmd, string name, list args)
82{
83 llMessageLinked(LINK_SET, cmd, llDumpList2String([ScriptKey, name] + args, lSEP), them);
84}
85sendPrim(key them, string cmd, list args)
86{
87 osMessageObject(them, llDumpList2String([ScriptName, cmd] + args, lSEP));
88}
89addEvent(float delay, string cmds)
90{
91 sendScript(lCMD, [fINT, ScriptKey, "TimerEvent", "TIMER", ((string) delay) + " " + cmds]);
92}
93
94string 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
106integer 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.
125string 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
134list 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
145string 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
156setSetting(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
177dumpSettings(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
195doSettings(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
211dynamicMenu(key id, string menu, string name, string title, string entries, string command)
212{
213 sendScript(lDYNAMIC, [id, menu, name, title, entries, command]);
214}
215
216linky(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 {
237d("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
285showAccess(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
314integer 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
427integer 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 {
454llSay(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 {
462llShout(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 {
522s("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);
630d("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
646integer LMchannel = -8888;
647integer LMhandle0 = 0;
648integer LMhandle1 = 0;
649integer LGchannel = -9119;
650key LeashKey = NULL_KEY;
651key someBoss = NULL_KEY;
652
653// TP tracker.
654list TPtDestination = [];
655
656// Movement
657integer mode = 0;
658integer MODE_NONE = 0; // Do nothing.
659integer MODE_SIT = 1; // Original sit tester - menu for adjusting the sit.
660integer MODE_DRIVE = 2; // Drivable box using controls and set pos / rot.
661integer MODE_SINGLE = 3; // llMoveToTarget(), single scan, stop when we get there.
662integer MODE_SCAN = 4; // llMoveToTarget(), multiple scans, stop when we get there.
663integer MODE_FOLLOW = 5; // llMoveToTarget(), multiple scans, keep following after getting there.
664integer MODE_LEASH = 5; // llMoveToTarget(), multiple scans, keep following after getting there, with leash.
665
666list 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
679list 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
689string OurName;
690integer meListening;
691integer bareListening;
692integer meChannel = 12;
693integer bareChannel = 123;
694
695list 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
717integer 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
730leashTo(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
774unleash()
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
789key Stalker;
790key Stalkee;
791integer tid = 0;
792float RANGE = 4.0; // Meters away that we stop walking towards.
793float TAU = 0.1; // Make smaller for more rushed following.
794
795float bb = 0.0;
796float tweak = 0.0;
797float sr = 10;
798integer Lag = 100;
799
800vector 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
835goto(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
893stopGoto(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.
918integer RLVchannel;
919//integer RLVCHAN = -1812221819; // RLV relay channel.
920string RLVversion;
921list RLVq; // Queue of RLV commands.
922integer qCHAN = 0;
923integer qCMD = 1;
924integer qNUM = 2;
925integer qRSP = 3;
926integer qTIME = 4;
927integer qTRIES = 5;
928integer qLSTN = 6;
929integer qSTRIDE = 7;
930integer RLVl; // Current RLV channel.
931integer RLVd; // RLV startup is done.
932list RLVa; // all RLV commands.
933list RLVb; // RLV comands in blacklist.
934list RLVs; // RLV commands currently restricted.
935list 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
943justRLV(string cmd)
944{
945 sendRLV(cmd, -1.0);
946}
947sendRLV(string cmd)
948{
949 sendRLV(cmd, 10.0);
950}
951sendRLV(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}
958doRLV()
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}
965doRLV(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
1002getRLV(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
1012gotStatus(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 {
1028d("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 {
1040d("gotStatus adding " + r);
1041 RLVs += [r];
1042 }
1043 else
1044 {
1045 j = llListFindList(RLVs, [r]);
1046 if (-1 != j)
1047 {
1048d("gotStatus subtracting " + r);
1049 RLVs = llListReplaceList(RLVs, [], j, j);
1050 }
1051 else
1052d("gotStatus already got " + r);
1053 }
1054 }
1055 }
1056 RLVs = deDup(RLVs);
1057 s(llGetListLength(RLVs) + " RLV restrictions - " + llDumpList2String(RLVs, " / "));
1058 }
1059}
1060
1061list 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
1082string 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 {
1180d("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
1233init()
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
1244laterInit()
1245{
1246 showAccess(Owner);
1247}
1248
1249default
1250{
1251 state_entry()
1252 {
1253 llSleep(0.2);
1254 Start = llGetTimeOfDay();
1255 Owner = llGetOwner();
1256d("\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);
1325d("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}