aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authoronefang2019-06-30 11:16:41 +1000
committeronefang2019-06-30 11:16:41 +1000
commit1e5b49a55b7ebd03c6e57be993668f38ac20f841 (patch)
tree5cabe2fdc2e51e46bcb6f5ca5ce5110b5be3bdb0
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.
-rw-r--r--1AOor2.lsl1835
-rw-r--r--1chatter.lsl1597
-rw-r--r--1ring.lsl1424
-rw-r--r--OhSillyThreatDetector.lsl59
-rw-r--r--onefang's leash holder.lsl44
5 files changed, 4959 insertions, 0 deletions
diff --git a/1AOor2.lsl b/1AOor2.lsl
new file mode 100644
index 0000000..7b460b6
--- /dev/null
+++ b/1AOor2.lsl
@@ -0,0 +1,1835 @@
1
2// AO and couples interactions. An Animation Overrider with a swimmer and a smiler, which are also AO type things.
3// evry byt cnts
4
5string Version = "1AOor2 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;
27integer fCARD = 1;
28integer fCHAT = 2;
29integer fLINK = 3;
30integer fMENU = 4;
31integer fRLV = 5;
32integer fRELAY = 6;
33integer fOSM = 7;
34integer fHTTP = 8;
35
36// utilities commands, "l"ibrary.
37string lSEP = "$!#";
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}
93string inKey2Name(key k)
94{
95 if (k == LibraryKey) return "1chatter";
96 integer i = llGetInventoryNumber(INVENTORY_SCRIPT);
97 while (i-- > 0)
98 {
99 string n = llGetInventoryName(INVENTORY_SCRIPT, i);
100 if (llGetInventoryKey(n) == k) return n;
101 }
102 return k;
103}
104
105integer listFindString(list lst, string name, integer stride)
106{
107 integer f = llListFindList(lst, [name]);
108 integer ix = f / stride;
109 ix = ix * stride;
110 if ((-1 != f) && (ix != f))
111 {
112 integer l = llGetListLength(lst);
113 integer i;
114 f = -1;
115 for (i = 0; i < l; i += stride)
116 {
117 if (llList2String(lst, i) == name) {f = i; i = l;}
118 }
119 }
120 return f;
121}
122
123// Note these next two are different from the ones in 1chatter.
124string alias(string n)
125{
126 list v = validateName(n, "alias");
127 n = llList2String(v, 1);
128 integer a = listFindString(Aliases, llList2String(v, 0) + llToLower(n), 2);
129 if (-1 != a) n = llList2String(Aliases, a + 1);
130 return n;
131}
132
133list validateName(string var, string type)
134{
135 list v = llParseStringKeepNulls(var, ["."], []);
136 if (2 != llGetListLength(v)) v = ScriptName + v;
137 var = llList2String(v, 1);
138 if ("setting" == type) var = llToUpper(var);
139 return [llList2String(v, 0) + ".", var];
140}
141
142string getSetting(string var)
143{
144 list v = validateName(var, "setting");
145 var = llList2String(v, 0) + llList2String(v, 1);
146 string result = "";
147 integer f = listFindString(Settings, var, sSTRIDE);
148 if (-1 != f) result = llList2String(Settings, f + sVALUE);
149 return result;
150}
151
152setSetting(key id, string var, string val, integer source)
153{
154 list v = validateName(var, "setting");
155 string fr = llList2String(v, 0);
156 var = fr + llList2String(v, 1);
157 integer f = listFindString(Settings, var, sSTRIDE);
158 if (-1 != f)
159 {
160 Settings = llListReplaceList(Settings, [val], f + sVALUE, f + sVALUE);
161 if (llGetSubString(fr, 0, -2) == ScriptName)
162 {
163 if (fINT != source)
164 {
165 doThing(id, "SET " + var + "=" + val, fr, llList2String(v, 1), val, fINT);
166 sendScript(lSETTING, [source, id, llList2String(v, 1), val]);
167 }
168 }
169 }
170}
171
172doSettings(key id, list settings)
173{
174 integer l = llGetListLength(settings);
175 integer i;
176 for (i = 0; i < l; i += sSTRIDE)
177 {
178 string var = llList2String(settings, i + sNAME);
179 list v = validateName(var, "setting");
180 string fr = llList2String(v, 0);
181 var = llList2String(v, 1);
182 string val = llList2String(settings, i + sVALUE);
183 doThing(id, "SET " + var + "=" + val, fr, var, val, fINT);
184 }
185}
186
187list readCard(string card)
188{
189 list r;
190 card = "~" + card + ".data";
191 if (NULL_KEY != llGetInventoryKey(card))
192 r = llList2List(llParseStringKeepNulls(osGetNotecard(card), ["\n"], []), 0, -2);
193 return r;
194}
195
196dynamicMenu(key id, string menu, string name, string title, string entries, string command)
197{
198 sendScript(lDYNAMIC, [id, menu, name, title, entries, command]);
199}
200
201integer Chosen;
202linky(integer num, string message, key id)
203{
204 if ((id != ScriptKey) && (id != NULL_KEY)) return;
205 list input = llParseStringKeepNulls(message, [lSEP], []);
206 key them = llList2Key(input, 0);
207 string fr = llList2Key(input, 1);
208//d("linky " + num + " " + message);
209 if (lCMD == num)
210 {
211//d("link CHAT " + llDumpList2String(input, " ~ "));
212 if ((fr == (ScriptName + ".")) || (fr == "*."))
213 {
214 key a = llList2Key(input, 3);
215 string button = llList2String(input, 4);
216 integer r = doThing(a, button, fr,
217 llList2String(input, 5), llList2String(input, 6), llList2Integer(input, 2));
218 sendScript(lCMD_DONE, [button, a, r]);
219 }
220 }
221 else if (lRESET_DONE == num)
222 {
223d("linky RESET_DONE");
224 LibraryKey = them;
225 Settings = llList2List(input, 2, -1);
226 setSetting(ScriptKey, ScriptName + ".VERSION", Version, fCARD);
227 sendScript(lSETTINGS, []);
228 }
229 else if (lALIAS_DONE == num) Aliases = llList2List(input, 2, -1);
230 else if (lSETTINGS_DONE == num)
231 {
232 Settings = llList2List(input, 2, -1);
233 doSettings(id, Settings);
234 laterInit();
235 s(Owner, "Finished starting up " + getSetting("VERSION") + " in " + (string) (llGetTimeOfDay() - Start));
236 }
237 else if (DEBUG_CHANNEL == num)
238 {
239 key root = llList2Key(llGetObjectDetails(id, [OBJECT_ROOT]), 0);
240 integer f = llListFindList(OhSillyThreats, [message]);
241 if (-1 == f) OhSillyThreats += [message];
242 else d("OhSillyThreats detected the function " + message + "() again!" );
243 D("Oh Silly threat system prevented " + message + "()"
244 + "\n in " + id + " \t" + llKey2Name(id)
245 + "\n part of " + root + " \t" + llKey2Name(root));
246 }
247}
248// END boilerplate, mostly.
249
250integer doThing(key id, string button, string fr, string cmd, string data, integer source)
251{
252 if ("Set " == llGetSubString(button, 0, 3))
253 {
254 integer set = listFindString(Settings, fr + cmd, sSTRIDE);
255 if (-1 != set) setSetting(id, fr + cmd, data, fINT);
256 }
257 if ((fr != (ScriptName + ".") && ("*." != fr))) return TRUE;
258 integer st = findSitter(id);
259 integer f;
260 string menu;
261 f = llSubStringIndex(button, "->");
262 if (-1 != f)
263 {
264 menu = llGetSubString(button, 0, f - 1);
265 button = llGetSubString(button, f + 2, -1);
266 }
267//d("doThing " + button + " = |" + cmd + "| -> " + data );
268 if ("Keys" == cmd) updateControls();
269 else if ("CONTROLS" == cmd)
270 {
271 list p = llParseStringKeepNulls(data, [","], []);
272 doControl(llList2Key(p, 0), llList2Integer(p, 1), llList2Integer(p, 2));
273 }
274 else if ("checkAO" == cmd) checkAO();
275 else if ("SMILE" == cmd)
276 { // The built in express_* animations are too short, but live with it.
277 osAvatarStopAnimation(llGetOwner(), Smile);
278 Smile = llList2String(Smiles, (integer) llFrand(2.5));
279 addEvent(3.0 + llFrand(5.0), cmd);
280 osAvatarPlayAnimation(llGetOwner(), Smile);
281 }
282 else if ("LESS_-" == cmd)
283 {
284 integer distance = llList2Integer(Sitters, st + pDIST);
285 --distance;
286 if (0 > distance) distance = 0;
287 Sitters = llListReplaceList(Sitters, [distance], st + pDIST, st + pDIST);
288 showMenu(id);
289 return FALSE;
290 }
291 else if ("MORE_+" == cmd)
292 {
293 integer distance = llList2Integer(Sitters, st + pDIST);
294 ++distance;
295 if (llGetListLength(Distances) <= distance) distance = llGetListLength(Distances) - 1;
296 Sitters = llListReplaceList(Sitters, [distance], st + pDIST, st + pDIST);
297 showMenu(id);
298 return FALSE;
299 }
300 else if ("NEXT_AVATAR" == cmd)
301 {
302 key them = llList2Key(Sitters, st + pADJ);
303 integer t = findSitter(them);
304 if (-1 != t)
305 {
306 t += pSTRIDE;
307 if (llGetListLength(Sitters) <= t)
308 t = -1;
309 }
310 else t = 0;
311 if (-1 != t)
312 Sitters = llListReplaceList(Sitters, [llList2Key(Sitters, t + pKEY)], st + pADJ, st + pADJ);
313 else
314 Sitters = llListReplaceList(Sitters, [ScriptKey], st + pADJ, st + pADJ);
315 showMenu(id);
316 return FALSE;
317 }
318 else if (
319 ("FORWARD" == cmd) || ("BACKWARDS" == cmd) || ("LEFT" == cmd) || ("RIGHT" == cmd)
320 || ("UP" == cmd) || ("DOWN" == cmd) || ("TURN_LEFT" == cmd) || ("TURN_RIGHT" == cmd))
321 {
322 integer i = llSubStringIndex(cmd, "_");
323 if (-1 != i) cmd = llGetSubString(cmd, 0, i - 1) + " " + llGetSubString(cmd, i + 1, -1);
324 adjust(id, llList2Float(Distances, llList2Integer(Sitters, st + pDIST)), llToLower(cmd));
325 }
326 else if ("AO" == cmd)
327 {
328 if ("0" != data)
329 {
330 integer l = llGetListLength(Sitters);
331 Pose = "";
332 for (f = 0; f < l; f += pSTRIDE)
333 updateSitter(llList2Key(Sitters, f + pKEY));
334 checkAO();
335 }
336 }
337 else if ("SYNC" == cmd)
338 {
339 integer l = llGetListLength(Sitters);
340 for (f = 0; f < l; f += pSTRIDE)
341 {
342 if (("R" != data) || (0 == llGetListLength(isPoseAO(f))))
343 Sitters = llListReplaceList(Sitters, [""], f + pSTATE, f + pSTATE);
344 }
345 if ("R" != data)
346 vAnim = data;
347 checkAO();
348 }
349 else if ("POSE" == cmd)
350 {
351 list p = llParseStringKeepNulls(data, [","], []);
352 if (Attached)
353 {
354 What = llList2String(p, 0);
355 Whats = llList2List(p, 1, -1);
356 Chosen = 0;
357 list b = osGetAvatarList();
358 integer l = llGetListLength(b);
359 integer i;
360 string keys;
361 string names;
362 for (i = 0; i < l; i += 3)
363 {
364 keys += "|COUPLE_WITH " + llList2String(b, i);
365 names += "|" + llList2String(b, i + 2);
366 }
367 dynamicMenu(Owner, "couples", What, " Pick someone to '" + What + "' with -",
368 llGetSubString(names, 1, -1), llGetSubString(keys, 1, -1));
369 PIN = (integer) llFrand(DEBUG_CHANNEL - 2) + 2;
370 vector RefPos = llGetPos();
371 rotation RefRot = llGetRot();
372 llRezObject("1AOor2 prim", ZERO_VECTOR * RefRot + RefPos, ZERO_VECTOR, (ZERO_ROTATION) * RefRot, PIN);
373 return FALSE;
374 }
375 else
376 newPose(id, llList2String(p, 0), llList2List(p, 1, -1));
377 }
378 else if ("COUPLE_WITH" == cmd)
379 {
380 Stalkee = data;
381 if (osIsNpc(Stalkee))
382 {
383 Chosen = 1;
384 s(Owner, llKey2Name(data) + " is an NPC, forcing them to '" + What + "' with you.");
385 }
386 else
387 {
388 s(Owner, "Asking " + llKey2Name(Stalkee) + " if they would like to do '" + What + "' with you.");
389 dynamicMenu(Stalkee, "", What,
390 llKey2Name(Owner) + " would like to do '" + What + "' with you.",
391 "Yes|No", "COUPLE_YES " + Stalkee + "|COUPLE_NO " + Stalkee);
392 sendScript(lCMD, "1ring", [fINT, id, Stalkee, "GOTO", Stalkee]);
393 addEvent(120.0, "COUPLE_TIMEOUT");
394 }
395 return FALSE;
396 }
397 else if ("PIN" == cmd)
398 {
399 list p = llParseStringKeepNulls(data, [","], []);
400 integer i = llGetInventoryNumber(INVENTORY_NOTECARD);
401 TheirKey = id;
402 while (i-- > 0)
403 llGiveInventory(id, llGetInventoryName(INVENTORY_NOTECARD, i));
404 i = llGetInventoryNumber(INVENTORY_ANIMATION);
405 while (i-- > 0)
406 llGiveInventory(id, llGetInventoryName(INVENTORY_ANIMATION, i));
407 llGiveInventory(id, "soap-bubble");
408 llRemoteLoadScriptPin(id, "1chatter", llList2Integer(p, 0), TRUE, bREZ);
409 llRemoteLoadScriptPin(id, ScriptName, llList2Integer(p, 0), TRUE, bREZ);
410 }
411 else if ("REZ_DONE" == cmd)
412 {
413 if (1 == Chosen)
414 {
415 sendPrim(TheirKey, "SIT", [Stalkee, What, llDumpList2String(Whats, "|")]);
416 sendPrim(TheirKey, "COUPLES", [Owner]);
417 Chosen = 0;
418 }
419 else if (-1 == Chosen) killPrim();
420 else
421 addEvent(0.5, cmd);
422 }
423 else if ("COUPLES" == cmd)
424 {
425 if (NULL_KEY != TheirKey)
426 sendPrim(TheirKey, "COUPLES", [id]);
427 else
428 {
429 if ("" != data) id = data;
430 sendScript(lMENU, [id, "couples", ""]);
431 }
432 return FALSE;
433 }
434 else if ("COUPLE_NO" == cmd)
435 {
436 Chosen = -1;
437 addEvent(0.0, "COUPLE_TIMEOUT");
438 sendScript(lCMD, "1ring", [fINT, id, Stalkee, "STOP", ""]);
439 s(Owner, llKey2Name(id) + " said no to your offer of '" + What + "'.");
440 }
441 else if ("COUPLE_YES" == cmd)
442 {
443 Chosen = 1;
444 addEvent(0.0, "COUPLE_TIMEOUT");
445 sendScript(lCMD, "1ring", [fINT, id, Stalkee, "STOP", ""]);
446 s(Owner, llKey2Name(id) + " said yes to your offer of '" + What + ".");
447 }
448 else if ("COUPLE_TIMEOUT" == cmd)
449 {
450 if (NULL_KEY != Stalkee)
451 {
452 Chosen = -1;
453 s(Owner, llKey2Name(Stalkee) + " didn't answer your offer of '" + What + ".");
454 s(Stalkee, "You failed to answer " + llKey2Name(Owner) + "'s offer to '" + What + "' with you.");
455 }
456 }
457 else if ("SIT" == cmd)
458 {
459 if (!Attached)
460 {
461 list det = llGetObjectDetails(Owner, [OBJECT_POS, OBJECT_ROT]);
462 list p = llParseStringKeepNulls(data, [","], []);
463 llSetPrimitiveParams([PRIM_POSITION, llList2Vector(det, 0), PRIM_ROTATION, llList2Rot(det, 1)]);
464 Stalkee = llList2Key(p, 0);
465 d("Forcing sit of " + llKey2Name(Stalkee) + " (" + Stalkee + ") for " + llList2Key(p, 1));
466 // This function only works if no one else is sitting on the object, which is why we sit them first.
467 // RLV has no such limitation.
468 if (osIsNpc(Stalkee))
469 osNpcSit(Stalkee, llGetKey(), OS_NPC_SIT_NOW);
470 else
471 {
472 llInstantMessage(Stalkee, "Please sit on the big gold heart.");
473// osForceOtherSit(Stalkee/*, llGetKey()*/);
474 }
475 newPose(id, llList2Key(p, 1), llList2List(p, 2, -1));
476 }
477 }
478 else if ("SIT_DONE" == cmd)
479 {
480d("SIT_DONE for " + llKey2Name(data));
481 if(data == Stalkee)
482 {
483 llOwnerSay("@sit:" + TheirKey + "=force");
484 s(Owner, "Switching AO to 1AOor2 couples object.");
485 Controller = Owner;
486 d("SIT_DONE is requesting camera and controls from " + llKey2Name(Controller));
487 llRequestPermissions(Controller, PERMISSION_CONTROL_CAMERA | PERMISSION_TAKE_CONTROLS);
488 }
489 }
490 else if ("DIE" == cmd) die();
491 else if ("DIE_DONE" == cmd)
492 {
493 TheirKey = NULL_KEY;
494 if (99.0 <= TPangle)
495 {
496 s(Owner, "Switching AO to 1ring object.");
497 oldController(Owner);
498 }
499 }
500 else if ("ADJUST" == cmd)
501 {
502 if (Attached) s("You need to be in a couples interaction with someone to use the ADJUST function.");
503 else {showMenu(id); return FALSE;}
504 }
505 else if ("SAVE" == cmd)
506 {
507 if (Attached) s(Owner, "You need to be in a couples interaction with someone to use the SAVE function.");
508 else sendPrim(BossKey, "SAVE_POSES", [llDumpList2String(reportPose(), "\n")]);
509 }
510 else if ("SAVE_POSES" == cmd)
511 {
512 integer i = llGetInventoryNumber(INVENTORY_NOTECARD);
513 list cards = [];
514 s(Owner, "Backing up old cards.");
515 while (i-- > 0)
516 {
517 string item = llGetInventoryName(INVENTORY_NOTECARD, i);
518
519 if ((llSubStringIndex(item, ".POSITIONS") == 0) ) cards += [llGetInventoryName(INVENTORY_NOTECARD, i)];
520 }
521 i = llGetListLength(cards);
522 while (i-- > 0)
523 {
524 string item = llList2String(cards, i);
525 osMakeNotecard(".backup" + item, llParseStringKeepNulls(osGetNotecard(item), ["\n"], []));
526 llRemoveInventory(item);
527 }
528 osMakeNotecard(".POSITIONS", data);
529 s(Owner, "Current positions saved to the .POSITIONS notecard.");
530 }
531 else if ("TPRIM" == cmd)
532 {
533 if (Attached)
534 {
535 vector cr = <1.0, 0.0 ,0.0> * llGetRootRotation();
536 TPangle = llAtan2(cr.x, cr.y);
537 llOwnerSay("@tpto:" + data + "=force");
538 }
539 else
540 {
541 list crl = llParseString2List(data, ["/"], []);
542 vector cr = <llList2Float(crl, 0), llList2Float(crl, 1), llList2Float(crl, 2)>;
543 integer x = (integer)(cr.x / 256);
544 integer y = (integer)(cr.y / 256);
545 cr.x = cr.x % 256;
546 cr.y = cr.y % 256;
547 osTeleportAgent(Stalkee, x, y, cr, <1.0,1.0,1.0>);
548 }
549 }
550 else if ("RLV" == cmd)
551 {
552 list dt = llParseString2List(data, ["|"], []);
553d("RLV command requested " + llList2String(dt, 0));
554 llOwnerSay("@" + llList2String(dt, 0));
555 if (2 < llGetListLength(dt))
556 addEvent(llList2Float(dt, 1), "RLV " + llDumpList2String(llList2List(dt, 2, -1), "|"));
557 }
558 else if ("URL" == cmd)
559 {
560 URL = data;
561d("New URL " + URL);
562 }
563 else if ("▲" == cmd)
564 {
565 if (Attached)
566 Chosen = -1;
567 else
568 {
569 if (-1 != st)
570 {
571 if ("" != llList2Key(Sitters, st + pADJ))
572 {
573 Sitters = llListReplaceList(Sitters, [""], st + pADJ, st + pADJ);
574 s(id, "Switched out of adjusting mode.");
575 addEvent(Tick * Smooth, "Keys");
576 }
577 }
578 }
579 }
580 else if (-1 == listFindString(Settings, fr + cmd, sSTRIDE))
581 {
582 if (fMENU == source) d("Unknown menu command '" + cmd + "' from - " + button);
583 else d("Unknown command '" + cmd + "' from - " + button);
584 }
585 return (source == fMENU);
586}
587
588float TPangle = 999.0;
589
590killPrim()
591{
592d("killPrim");
593 if (NULL_KEY != TheirKey)
594 sendPrim(TheirKey, "DIE", []);
595 Stalkee = NULL_KEY;
596 What = "";
597 Chosen = 0;
598}
599
600// General variables
601integer Attached;
602integer Link;
603list Distances = [0.01, 0.05, 0.1, 0.5, 1.0, 2.0];
604vector position = <0.0, 0.0, -0.0001>;
605vector rotat = ZERO_VECTOR;
606
607// Rezonater
608key BossKey = NULL_KEY;
609key TheirKey = NULL_KEY;
610integer bREZ = -501;
611string HoverText = "loveness";
612string Sit0Text = "carry them";
613string Sit1Text = "be carried";
614
615// DrivableBox
616list Sitters = [];
617integer pKEY = 0; // Key of the sitter.
618integer pLINK = 1; // The "prim" link the sitter is.
619integer pSTATE = 2; // The current AOstate for this person.
620integer pNEW = 3; // New state for this person.
621integer pANIMS = 4; // Current set of animations for this person.
622integer pADJ = 5; // Key of who they are adjusting, "" means not adjusting, NULL_KEY will mean adjust all.
623integer pDIST = 6; // For the adjusting this person is doing, not for who they are adjusting.
624integer pKEYS = 7; // KeysLevel for this sitter.
625integer pSTRIDE = 8;
626list Poses; // List of poses.
627integer psNAME = 0; // Name of pose.
628integer psANIM = 1; // | separated animations.
629integer psEMOTE = 2; // | separated emotions and timers list.
630integer psPOSROT= 3; // | separated position and rotation pairs.
631integer psSTRIDE= 4;
632
633// Smiler
634integer SmileCounter = 0;
635string Smile = "express_toothsmile";
636list Smiles =
637[
638 "express_smile",
639 "express_toothsmile",
640 "express_wink_emote",
641 "express_tongue_out"
642];
643
644// Couples
645list Whats;
646string What;
647string Pose;
648key Stalkee;
649integer PIN;
650key Leader = NULL_KEY;
651float LeaderOffset = 0.0;
652key Controller = NULL_KEY;
653
654// AO
655integer Lag = 100;
656integer Swimming;
657integer Bobbing;
658float Tick = 0.2;
659float AOspeed = 1.0;
660list flyStates;
661list initialStates; // "Taking Off", "hover_up",
662list ANIMATIONS;
663
664integer checkAnim(string a)
665{
666 if (("" != a) && (NULL_KEY == llGetInventoryKey(a)) && (-1 == llListFindList(ANIMATIONS, [a])))
667 {
668 s(Owner, "Missing animation - " + a + "!");
669 return FALSE;
670 }
671 return TRUE;
672}
673
674newPose(key id, string p, list ps)
675{
676 integer f = findPose(p);
677 integer l = llGetListLength(Sitters);
678 if (-1 != f)
679 {
680 setSetting(id, "AO", "0", fINT);
681 Pose = p;
682 Poses = llListReplaceList(Poses, llDumpList2String(ps, "|"), f + psANIM, f + psANIM);
683 for (f = 0; f < l; f += pSTRIDE)
684 updateSitter(llList2Key(Sitters, f + pKEY));
685 checkAO();
686 }
687}
688
689integer findSitter(key id) {return listFindString(Sitters, id, pSTRIDE);}
690
691string prStr(string str)
692{
693 integer ix = llSubStringIndex(str, ">");
694 vector p = (vector) llGetSubString(str, 0, ix);
695 vector r = (vector) llGetSubString(str, ix + 1, -1);
696 return vround(p, r);
697}
698
699string vround(vector p, vector r)
700{ // OpenSim likes to swap these around, which triggers the ball movement saving.
701 if (-179.9 >= r.x) r.x += 360.0; if (-179.9 >= r.y) r.y += 360.0; if (-179.9 >= r.z) r.z += 360.0;
702 if ( 179.9 <= r.x) r.x -= 360.0; if ( 179.9 <= r.y) r.y -= 360.0; if ( 179.9 <= r.z) r.z -= 360.0;
703 return "<" + round(p.x, 3) + "," + round(p.y, 3) + "," + round(p.z, 3) +
704 "><" + round(r.x, 1) + "," + round(r.y, 1) + "," + round(r.z, 1) + ">";
705}
706
707string round(float number, integer places)
708{
709 float shifted;
710 integer rounded;
711 string s;
712 shifted = number * llPow(10.0, (float) places);
713 rounded = llRound(shifted);
714 s = (string) ((float) rounded / llPow(10.0, (float)places));
715 rounded = llSubStringIndex(s, ".");
716 if (-1 != rounded) s = llGetSubString(s, 0, llSubStringIndex(s, ".") + places);
717 else
718 {
719 s += ".00000000";
720 s = llGetSubString(s,0,llSubStringIndex(s, ".") + places);
721 }
722 return s;
723}
724
725readPos(list cards)
726{
727 integer i;
728 integer l = llGetListLength(cards);
729 cards = llListSort(cards, 1, TRUE);
730 for (i = 0; i < l; i++)
731 {
732 string card = llList2String(cards, i);
733 list crd = llParseStringKeepNulls(osGetNotecard(card), ["\n"], []);
734 integer m = llGetListLength(crd);
735 integer j;
736 d("Reading '" + card + "'.");
737 for (j = 0; j < m; j++)
738 {
739 string data = llList2String(crd, j);
740 if (llGetSubString(data, 0, 0) != "/")
741 { // skip comments
742 data = llStringTrim(data, STRING_TRIM);
743 integer ix = llSubStringIndex(data, "{");
744 integer jx = llSubStringIndex(data, "} <");
745 if (ix != -1 && jx != -1)
746 {
747 string name = llStringTrim(llGetSubString(data, ix + 1, jx - 1), STRING_TRIM);
748 string ldata = llGetSubString(data, jx + 2, -1);
749 list posrots = llParseString2List(ldata, ["<"], []);
750 string pr = "";
751 jx = llGetListLength(posrots);
752 for (ix = 0; ix < jx; ix += 2)
753 pr += "|" + prStr("<" + llStringTrim(llList2String(posrots, ix), STRING_TRIM)
754 + "<" + llStringTrim(llList2String(posrots, ix + 1), STRING_TRIM));
755 savePose(name, "", "", llGetSubString(pr, 1, -1));
756 }
757 }
758 }
759 }
760}
761
762integer findPose(string name) {return listFindString(Poses, name, psSTRIDE);}
763
764savePose(string name, string anim, string exp, string posRot)
765{
766 integer f = findPose(name);
767 posRot = normPose(posRot);
768 if (-1 != f)
769 {
770 if ("" == anim)
771 {
772 anim = llList2String(Poses, f + psANIM);
773 exp = llList2String(Poses, f + psEMOTE);
774 }
775 else if ("" == posRot) posRot = llList2String(Poses, f + psPOSROT);
776 Poses = llListReplaceList(Poses, [name, anim, exp, posRot], f, f + psSTRIDE - 1);
777 }
778 else
779 Poses += [name, anim, exp, posRot];
780}
781
782string normPose(string posRot)
783{
784 // Subtract the x,y of the first from both to normalise it around the prim.
785 // NOTE - only handles the first two.
786 list p = llParseStringKeepNulls(posRot, ["|"], []);
787 string pr0 = llList2String(p, 0);
788 integer jx0 = llSubStringIndex(pr0, "><");
789 vector minp = (vector) llGetSubString(pr0, 0, jx0);
790 vector minr = (vector) llGetSubString(pr0, jx0 + 1, -1);
791 string pr1 = llList2String(p, 1);
792 integer jx1 = llSubStringIndex(pr1, "><");
793 vector maxp = (vector) llGetSubString(pr1, 0, jx1);
794 vector maxr = (vector) llGetSubString(pr1, jx1 + 1, -1);
795 maxp.x = maxp.x - minp.x;
796 maxp.y = maxp.y - minp.y;
797 minp.x = 0.0; minp.y = 0.0;
798 maxr.z = maxr.z - minr.z;
799 minr.z = 0.0;
800 return prStr(((string) minp) + ((string) minr)) + "|" + prStr(((string) maxp) + ((string) maxr));
801}
802
803list reportPose()
804{
805 list result = [];
806 integer l = llGetListLength(Poses);
807 integer i;
808 for (i = 0; i < l; i += psSTRIDE)
809 {
810 list prs = llParseString2List(llList2String(Poses, i + psPOSROT), ["|"], []);
811 string r = "{" + llList2String(Poses, i) + "} ";
812 integer m = llGetListLength(prs);
813 integer j;
814
815 for (j = 0; j < m; j++)
816 r += llList2String(prs, j);
817 result += [r];
818 }
819 return result;
820}
821
822lookAtMe(key id)
823{
824 if ((llGetPermissionsKey() == id) && (llGetPermissions() & PERMISSION_CONTROL_CAMERA))
825 {
826 d("setting camera for " + llKey2Name(id));
827 llClearCameraParams();
828 llSetCameraParams(
829 [ CAMERA_ACTIVE, 1, CAMERA_FOCUS_OFFSET, <0.0, 0.0, 1.0>, CAMERA_PITCH, 12.5,
830 CAMERA_BEHINDNESS_ANGLE, 0.1, CAMERA_BEHINDNESS_LAG, 0.0, CAMERA_DISTANCE, 2.75,
831 CAMERA_FOCUS_LAG, 0.0 , CAMERA_FOCUS_THRESHOLD, 0.0,
832 CAMERA_POSITION_LAG, 0.0, CAMERA_POSITION_THRESHOLD, 0.0
833 ]);
834 }
835 else
836 {
837 if (Controller == id)
838 {
839 d("lookAtMe() requesting camera and controls from " + llKey2Name(id));
840 llRequestPermissions(id, PERMISSION_CONTROL_CAMERA | PERMISSION_TAKE_CONTROLS);
841 }
842 else
843 {
844 d("lookAtMe() requesting camera from " + llKey2Name(id));
845 llRequestPermissions(id, PERMISSION_CONTROL_CAMERA);
846 }
847 }
848}
849
850float ControlTime = 0.0;
851integer HeldKeys;
852integer LevelKeys;
853doControl(key id, integer level, integer edge)
854{
855 if (99.0 > TPangle) return;
856 if (NULL_KEY != TheirKey)
857 {
858 sendPrim(TheirKey, "CONTROLS", [id, level, edge]);
859 if ("Sitting" != llGetAnimation(id))
860 oldController(id);
861 return;
862 }
863// integer start = level & edge;
864// integer end = ~level & edge;
865 integer held = level & ~edge;
866// integer untouched = ~(level | edge);
867// llSay(0, "doControl " + llKey2Name(id) + " " + llList2CSV([level, edge, start, end, held, untouched]));
868 integer f = findSitter(id);
869 if (-1 != f)
870 {
871 integer a = ("" != llList2Key(Sitters, f + pADJ));
872 if ((held == (CONTROL_BACK | CONTROL_FWD)) ||
873 (held == (CONTROL_DOWN | CONTROL_UP)) ||
874 (held == (CONTROL_LEFT | CONTROL_RIGHT)) ||
875 (held == (CONTROL_ROT_LEFT | CONTROL_ROT_RIGHT)))
876 {
877 if ((llGetTimeOfDay() - ControlTime) >= 1.0)
878 {
879 if (held != HeldKeys)
880 {
881 if (a)
882 {
883 Sitters = llListReplaceList(Sitters, [""], f + pADJ, f + pADJ);
884 s(id, "Switched out of adjusting mode.");
885 addEvent(Tick * Smooth, "Keys");
886 }
887 else
888 showMenu(id);
889 }
890 HeldKeys = held;
891 }
892 level = 0;
893 }
894 else if (0 != HeldKeys)
895 {
896 HeldKeys = 0;
897 level = 0;
898 }
899 Sitters = llListReplaceList(Sitters, [level], f + pKEYS, f + pKEYS);
900 if (0 != edge) ControlTime = llGetTimeOfDay();
901 if (level != LevelKeys)
902 updateControls();
903 LevelKeys = level;
904 }
905}
906
907float SPEED = 3.8;
908float ROTAT = 8.0;
909float Smooth = 0.2; // 0.2 is good.
910string vAnim;
911updateControls()
912{
913 if (Attached) return;
914 integer mode = vNONE; integer reverse = 1; float speed = 0.0; float rot = 0.0;
915 integer l = llGetListLength(Sitters); integer i;
916 integer keys;
917 for (i = l - pSTRIDE; i >= 0; i -= pSTRIDE)
918 {
919 key k = llList2Key(Sitters, i + pKEY);
920 keys = llList2Integer(Sitters, i + pKEYS);
921 if ("" == llList2Key(Sitters, i + pADJ))
922 {
923 if (keys & (CONTROL_DOWN |CONTROL_UP)) mode = vUP;
924 if (keys & (CONTROL_BACK | CONTROL_FWD)) mode = vFWD;
925 if (keys & (CONTROL_LEFT | CONTROL_RIGHT)) mode = vLEFT;
926 if (keys & (CONTROL_ROT_LEFT | CONTROL_ROT_RIGHT)) mode = vFWD;
927 if (keys & (CONTROL_BACK | CONTROL_DOWN | CONTROL_RIGHT))
928 {
929 speed = 0.0 - (SPEED * Smooth);
930 reverse = -1;
931 }
932 if (keys & (CONTROL_FWD | CONTROL_UP | CONTROL_LEFT)) speed = SPEED * Smooth;
933 if (keys & CONTROL_ROT_LEFT) rot = (reverse * PI) / (ROTAT / Smooth);
934 if (keys & CONTROL_ROT_RIGHT) rot = ((0 - reverse) * PI) / (ROTAT / Smooth);
935 }
936 else
937 {
938 float d = llList2Float(Distances, llList2Integer(Sitters, i + pDIST));
939 if (keys & CONTROL_FWD) adjust(k, d, "forward");
940 if (keys & CONTROL_BACK) adjust(k, d, "backwards");
941 if (keys & CONTROL_LEFT) adjust(k, d, "left");
942 if (keys & CONTROL_RIGHT) adjust(k, d, "right");
943 if (keys & CONTROL_ROT_LEFT) adjust(k, d, "turn left");
944 if (keys & CONTROL_ROT_RIGHT) adjust(k, d, "turn right");
945 if (keys & CONTROL_UP) adjust(k, d, "up");
946 if (keys & CONTROL_DOWN) adjust(k, d, "down");
947 }
948 }
949 updateVehicle(keys, mode, speed * AOspeed, rot);
950 checkLag();
951 float t = Tick;
952 if (vNONE != mode) addEvent(t * Smooth, "Keys");
953 else if (vFALL == vMODE) addEvent(t * Smooth * 1.5, "Keys");
954}
955
956adjust(key id, float dist, string direction)
957{
958 integer f = findSitter(id);
959 integer i = -1;
960 list p;
961 i = findPose(Pose);
962 if (-1 != i)
963 p = llParseStringKeepNulls(llList2String(Poses, i + psPOSROT), ["|"], []);
964 else
965 return;
966 if (-1 != f)
967 {
968 key them = llList2Key(Sitters, f + pADJ);
969 integer l; integer t;
970 if (ScriptKey == them)
971 {
972 t = 0;
973 l = llGetListLength(Sitters);
974 }
975 else
976 {
977 t = findSitter(them);
978 l = t + pSTRIDE;
979 }
980 if (-1 != t)
981 {
982 if (("turn left" == direction) || ("turn right" == direction)) dist = dist * 90.0;
983 for (; t < l; t += pSTRIDE)
984 {
985 them = llList2Key(Sitters, t + pKEY);
986 integer lnk = llList2Integer(Sitters, t + pLINK);
987 if (-1 == lnk) return;
988 string prn = llList2String(p, t / pSTRIDE);
989 integer ix = llSubStringIndex(prn, ">");
990 vector pos = (vector) llGetSubString(prn, 0, ix);
991 vector rot = (vector) llGetSubString(prn, ix + 1, -1);
992 if ("forward" == direction) pos.x = pos.x + dist;
993 else if ("backwards" == direction) pos.x = pos.x - dist;
994 else if ("left" == direction) pos.y = pos.y + dist;
995 else if ("right" == direction) pos.y = pos.y - dist;
996 else if ("up" == direction) pos.z = pos.z + dist;
997 else if ("down" == direction) pos.z = pos.z - dist;
998 else if ("turn left" == direction) rot.z = rot.z + (dist);
999 else if ("turn right" == direction) rot.z = rot.z - (dist);
1000 p = llListReplaceList(p, [vround(pos, rot)], t / pSTRIDE, t / pSTRIDE);
1001 Poses = llListReplaceList(Poses, [llDumpList2String(p, "|")], i + psPOSROT, i + psPOSROT);
1002 updateSitter(them);
1003 s(id, llKey2Name(id) + " adjusts " + llKey2Name(them) + " by " + (string) dist + " " + direction);
1004 if (id != them)
1005 s(them, llKey2Name(id) + " adjusts " + llKey2Name(them) + " by " + (string)dist + " " + direction);
1006 }
1007 }
1008 }
1009}
1010
1011updateSitter(key id)
1012{ // Written by Strife Onizuka, size adjustment provided by Talarus Luan
1013 // Using this while the object is moving may give unpredictable results.
1014 integer f = findSitter(id);
1015 if (-1 != f)
1016 {
1017 integer lnk = llList2Integer(Sitters, f + pLINK);
1018 if (-1 == lnk) return;
1019 vector pos = position;
1020 rotation rot = llEuler2Rot(rotat * DEG_TO_RAD);
1021 integer i = findPose(Pose);
1022 if (-1 != i)
1023 {
1024 string prn = llList2String(llParseStringKeepNulls(llList2String(Poses, i + psPOSROT), ["|"], []), f / pSTRIDE);
1025 integer ix = llSubStringIndex(prn, ">");
1026// TODO - might be wrong, coz rotations are hard, m'kay.
1027 pos = (vector) llGetSubString(prn, 0, ix);
1028 rot = llEuler2Rot((vector) llGetSubString(prn, ix + 1, -1) * DEG_TO_RAD);
1029 }
1030 vector size = llGetAgentSize(id);
1031 integer prim = llGetLinkNumber();
1032 // We need to make the position and rotation local to the current prim.
1033 vector localpos = ZERO_VECTOR;
1034 rotation localrot = ZERO_ROTATION;
1035
1036 if (1 < prim) // Only need the local rot if it's not the root.
1037 {
1038 list local = llGetLinkPrimitiveParams(prim, [PRIM_POS_LOCAL, PRIM_ROT_LOCAL]);
1039 localpos = llList2Vector(local, 0);
1040 localrot = llList2Rot(local, 1);
1041 }
1042 pos += <0.0, 0.0, 0.2>; // Fudge it. Pffft
1043 // <0.008906, -0.049831, 0.088967> are the coefficients for a parabolic curve that
1044 // best fits real avatars. It is not a perfect fit.
1045 float fAdjust = ((((0.008906 * size.z) + -0.049831) * size.z) + 0.088967) * size.z;
1046 vector fa = llRot2Up(rot) * fAdjust;
1047 llSetLinkPrimitiveParamsFast(lnk,
1048 [
1049 PRIM_POS_LOCAL, ((pos - fa) * localrot) + localpos,
1050 PRIM_ROT_LOCAL, rot * localrot // This does rotate around the avatar's centre, not the prims centre.
1051 ]);
1052 }
1053}
1054
1055showMenu(key id)
1056{
1057 integer f = findSitter(id);
1058 if (-1 != f)
1059 {
1060 key them = llList2Key(Sitters, f + pADJ);
1061 string name;
1062 integer distance = llList2Integer(Sitters, f + pDIST);
1063 if ("" == them)
1064 {
1065 them = id;
1066 Sitters = llListReplaceList(Sitters, [them], f + pADJ, f + pADJ);
1067 s(id, "Switched to adjusting mode. Click '▲ Exit' on the adjusting menu to switch back to moving.");
1068 }
1069 if (ScriptKey == them)
1070 name = "all";
1071 else
1072 name = llKey2Name(them);
1073 sendScript(lMENU, [id, "adjust", "Adjusting position of " + name
1074 + ". \nAdjust by " + llList2String(Distances, distance)
1075 + ", \nusing menu or movement keys."]);
1076 }
1077}
1078
1079integer vMODE;
1080integer vNONE = 0;
1081integer vFWD = 1;
1082integer vLEFT = 2;
1083integer vUP = 3;
1084integer vCROUCH = 4;
1085integer vGROUND = 5;
1086integer vFALL = 6;
1087float vTHEN;
1088
1089float LastCast;
1090integer CastOut;
1091float cast(vector gp, vector pos, float dist)
1092{
1093 vector start = gp + <0.0, 0.0, 0.0>;
1094 vector end = pos + <0.0, 0.0, -llFabs(dist * 1.1)>;
1095 gp = pos - gp;
1096 float g = llGround(<gp.x, gp.y, 0.0 - (LeaderOffset * 3.0)>);
1097 if (0 <= CastOut)
1098 {
1099 list results = llCastRay(start, end, [
1100 RC_REJECT_TYPES, RC_REJECT_AGENTS | RC_REJECT_PHYSICAL | RC_REJECT_LAND,
1101 RC_DATA_FLAGS, RC_GET_ROOT_KEY,
1102 RC_MAX_HITS, 2] );
1103 CastOut = llList2Integer(results, -1);
1104 if (0 < CastOut)
1105 {
1106 vector p = llList2Vector(results, 1);
1107 if (llGetKey() == llList2Key(results, 0))
1108 {
1109 if (1 < CastOut)
1110 {
1111 p = llList2Vector(results, 3);
1112 LastCast = p.z + 0.1;
1113 }
1114 else
1115 LastCast = g + 0.1;
1116 }
1117 else
1118 LastCast = p.z + 0.1;
1119 }
1120 else if (0 == CastOut)
1121 LastCast = g + 0.1;
1122/*
1123 else
1124 {
1125 if (RCERR_UNKNOWN == CastOut) d("llCastRay() failed for an unspecified reason.");
1126 else if (RCERR_SIM_PERF_LOW == CastOut) d("llCastRay() failed coz sim performance is low.");
1127 else if (RCERR_CAST_TIME_EXCEEDED == CastOut) d("llCastRay() failed coz too many raycasts.");
1128 else d("llCastRay() returned unknown error code " + CastOut);
1129 }
1130*/
1131 }
1132 else CastOut += 1;
1133 if (g > LastCast) LastCast = g + 0.1;
1134 return LastCast - 0.1;
1135}
1136
1137updateVehicle(integer keys, integer mode, float move, float rotate)
1138{
1139 vector pos = llGetPos();
1140 vector gp = pos;
1141 vector rotvec = llRot2Euler(llGetRot());
1142 if (Attached || (0.0 == LeaderOffset)) return;
1143 vAnim = "Standing";
1144 if (0.0 < vTHEN)
1145 {
1146 if (3.0 > (llGetTimeOfDay() - vTHEN))
1147 {
1148 pos.z = cast(gp, pos, 4.0) + LeaderOffset;
1149 llSetPrimitiveParams([PRIM_POSITION, pos]);
1150 return;
1151 }
1152 }
1153 if (vUP == vMODE)
1154 {
1155 if (vNONE == mode) vAnim = "Hovering";
1156 else if (vFWD == mode)
1157 {
1158 vAnim = "Flying";
1159 pos += (<(llCos(rotvec.z)) * move, (llSin(rotvec.z)) * move, 0.0>);
1160 }
1161 else if (vUP == mode)
1162 {
1163 pos += (<0.0, 0.0, move>);
1164 if (0.0 < move) vAnim = "Hovering Up";
1165 else vAnim = "Hovering Down";
1166 }
1167 if (cast(gp, pos, move / Smooth) > (pos.z - LeaderOffset))
1168 {
1169 vAnim = "Soft Landing"; vMODE = vFWD;
1170 vTHEN = llGetTimeOfDay(); addEvent(3.0, "SYNC Standing");
1171 }
1172 }
1173 else if (vFALL == vMODE)
1174 {
1175 vAnim = "Falling"; pos.z -= 1.0;
1176 if (cast(gp, pos, move / Smooth) > (pos.z - LeaderOffset))
1177 {
1178 vAnim = "Standing Up"; vMODE = vFWD;
1179 vTHEN = llGetTimeOfDay(); addEvent(3.0, "SYNC Standing");
1180 }
1181 }
1182 else if ((vFWD == mode) || (vLEFT == mode) || (vNONE == mode))
1183 {
1184 if (vLEFT == mode)
1185 {
1186 if (0.0 < move) rotvec.z += 89.0 * DEG_TO_RAD;
1187 else rotvec.z += 91.0 * DEG_TO_RAD;
1188 }
1189 pos += (<(llCos(rotvec.z)) * move, (llSin(rotvec.z)) * move, 0.0>);
1190 if (0.0 != move)
1191 {
1192 float ground = cast(gp, pos, move / Smooth);
1193 if (pos.z > (ground + LeaderOffset * 3.0))
1194 {
1195 vMODE = vFALL;
1196 pos.z -= 1.0;
1197 }
1198 else pos.z = ground + LeaderOffset;
1199 }
1200 if (vMODE == vFALL) vAnim = "Falling";
1201 else if (vCROUCH == vMODE)
1202 {
1203 if (0.0 == move) vAnim = "Crouching";
1204 else vAnim = "CrouchWalking";
1205 }
1206 else if (vGROUND == vMODE)
1207 vAnim = "Sitting on Ground";
1208 else
1209 {
1210 if (0.0 != rotate)
1211 {
1212 if (0.0 < rotate) vAnim = "Turning Left";
1213 else vAnim = "Turning Right";
1214 }
1215 if (0.0 == move) vAnim = "Standing";
1216 else vAnim = "Walking";
1217 }
1218 }
1219 else if (vUP == mode)
1220 {
1221 if (keys & CONTROL_UP)
1222 {
1223 if (vGROUND == vMODE) {vAnim = "Crouching"; vMODE = vCROUCH;}
1224 else if (vCROUCH == vMODE) {vAnim = "Standing"; vMODE = vFWD;}
1225 else {pos += (<0.0, 0.0, move>); vMODE = vUP;}
1226 }
1227 else
1228 {
1229 if (vCROUCH == vMODE) {vAnim = "Sitting on Ground"; vMODE = vGROUND;}
1230 else if (0.0 > move) {vAnim = "Crouching"; vMODE = vCROUCH;}
1231 else {pos += (<0.0, 0.0, move>); vMODE = vUP;}
1232 }
1233 }
1234 // This is useful at least, not tested on a var region yet, but should work, that'll be why it exists.
1235 vector rs = osGetRegionSize();
1236 vector cr = pos - gp;
1237 integer isBorder;
1238 integer isEdge;
1239 if ((pos.x < 0.0) || (pos.x > rs.x) ||(pos.y < 0.0) || (pos.y > rs.y))
1240 {
1241 isBorder = TRUE;
1242 if (llFabs(cr.x) > llFabs(cr.y))
1243 {
1244 cr.y = 0.0;
1245 if (0.0 < cr.x) {if (0.3 < cr.x) cr.x = 1.0; else cr.x = 0.0;}
1246 else if (0.0 > cr.x) {if (-0.3 > cr.x) cr.x = -1.0; else cr.x = 0.0;}
1247 }
1248 else
1249 {
1250 cr.x = 0.0;
1251 if (0.0 < cr.y) {if (0.3 < cr.y) cr.y = 1.0; else cr.y = 0.0;}
1252 else if (0.0 > cr.y) {if (-0.3 > cr.y) cr.y = -1.0; else cr.y = 0.0;}
1253 }
1254 isEdge = llEdgeOfWorld(gp, cr);
1255 integer l = llGetListLength(Sitters);
1256 integer i;
1257 for (i = 0; i < l; i += pSTRIDE)
1258 {
1259 key a = llList2Key(Sitters, i);
1260 key t = llList2Key(Sitters, i + pADJ);
1261 if (isEdge)
1262 {
1263 Sitters = llListReplaceList(Sitters, [a], i + pADJ, i + pADJ);
1264 adjust(a, move, "forward");
1265 Sitters = llListReplaceList(Sitters, [t], i + pADJ, i + pADJ);
1266 }
1267 else
1268 llSetLinkPrimitiveParamsFast(llList2Integer(Sitters, i + pLINK),
1269 [PRIM_POS_LOCAL, <-1.0, 0.0, -0.0001>]);
1270 }
1271 }
1272 if (isEdge)
1273 d("Can't go there, you'll fall off the edge!");
1274 else
1275 {
1276 if (isBorder)
1277 {
1278 llWhisper(0, "Can't walk into next sim, teleporting slowly instead.");
1279 cr = llGetRegionCorner() + pos + ((pos - gp) * 2.0);
1280 string data = (string)cr.x + "/" + (string)cr.y + "/" + (string)cr.z;
1281 sendPrim(BossKey, "TPRIM", [data]);
1282 LeaderOffset = 0.0;
1283 addEvent(1.5, "TPRIM " + (string)cr.x + "/" + (string)cr.y + "/" + (string)cr.z);
1284 }
1285 else
1286 {
1287 // This is useful at least for parcel crossing.
1288 integer f = llGetParcelFlags(pos);
1289 if (!f & PARCEL_FLAG_ALLOW_FLY) d("No fly.");
1290 if (!f & PARCEL_FLAG_ALLOW_SCRIPTS) d("No scripts.");
1291 if (!f & PARCEL_FLAG_ALLOW_CREATE_OBJECTS) d("No create object.");
1292 if (!f & PARCEL_FLAG_ALLOW_ALL_OBJECT_ENTRY) d("No object entry.");
1293 if (!f & PARCEL_FLAG_ALLOW_GROUP_SCRIPTS) d("No group scripts.");
1294 if (!f & PARCEL_FLAG_ALLOW_CREATE_GROUP_OBJECTS) d("No group create objects.");
1295 if (!f & PARCEL_FLAG_ALLOW_GROUP_OBJECT_ENTRY) d("No group object entry.");
1296 if ((f & PARCEL_FLAG_ALLOW_SCRIPTS) && (f & PARCEL_FLAG_ALLOW_ALL_OBJECT_ENTRY))
1297 llSetLinkPrimitiveParamsFast(LINK_THIS, [PRIM_POSITION, pos, PRIM_ROTATION,
1298 llEuler2Rot(<0.0, 0.0, rotate>) * llGetRot()]);
1299 }
1300 }
1301 checkAO();
1302}
1303
1304newLeader(key id)
1305{
1306 Leader = id;
1307 vector bb = llGetAgentSize(Leader); LeaderOffset = (bb.z + 0.3) / 2;
1308 updateVehicle(0, vNONE, 0.0, 0.0);
1309 checkAO();
1310d("newLeader " + llKey2Name(Leader) + ", offset " + (string) LeaderOffset);
1311}
1312
1313oldController(key t)
1314{
1315 // NOTE - the person just stood up, that automatically revoked the permissions. In theory.
1316 // And sometimes in practice.
1317 if (llGetPermissionsKey() == t)
1318 {
1319 if (llGetPermissions() & PERMISSION_CONTROL_CAMERA)
1320 {
1321 d("releasing camera for " + llKey2Name(t));
1322 llClearCameraParams();
1323 }
1324 if (llGetPermissions() & PERMISSION_TAKE_CONTROLS)
1325 {
1326 d("releasing controls for " + llKey2Name(t));
1327 llReleaseControls();
1328 }
1329 }
1330 if (t == Controller)
1331 {
1332 addEvent(0.0, "Keys");
1333 Controller = NULL_KEY;
1334 }
1335}
1336
1337checkSitters(integer del)
1338{
1339 list new = [];
1340 integer l = llGetNumberOfPrims();
1341 integer lnk;
1342 for (lnk = llGetObjectPrimCount(llGetKey()) + 1; lnk <= l; ++lnk)
1343 new += [llGetLinkKey(lnk), lnk];
1344 l = llGetListLength(Sitters);
1345 for (lnk = 0; lnk < l; lnk += pSTRIDE)
1346 {
1347 key t = llList2Key(Sitters, lnk + pKEY);
1348 if (NULL_KEY != t)
1349 {
1350 integer j = listFindString(new, t, 2);
1351
1352 if (-1 == j) // old sitter that left
1353 {
1354 d("unsit " + llKey2Name(t));
1355 stopMe(t, lnk);
1356 oldController(t);
1357 Sitters = llListReplaceList(Sitters, [NULL_KEY], lnk + pKEY, lnk + pKEY);
1358 }
1359 else // old sitter that is still here
1360 new = llListReplaceList(new, [], j, j + 1);
1361 }
1362 }
1363 l = llGetListLength(new) / 2;
1364 for (lnk = 0; lnk < l; lnk +=2)
1365 {
1366 key t = llList2Key(new, lnk);
1367 list n = [t, llList2Integer(new, lnk + 1), "", "", "", "", llGetListLength(Distances) / 3, 0];
1368 list gndr = llGetObjectDetails(t, [OBJECT_BODY_SHAPE_TYPE]);
1369 stopAnims(t);
1370 integer f = findSitter(NULL_KEY);
1371 if (-1 != f) // new sitter replaces old
1372 Sitters = llListReplaceList(Sitters, n, f, f + pSTRIDE - 1);
1373 else // new sitter added on end
1374 Sitters += n;
1375 if (NULL_KEY != BossKey) sendPrim(BossKey, "SIT_DONE", [t]);
1376 d("sat " + llKey2Name(t) + ", " + llList2String(gndr, 0) + " male @ " + (f / pSTRIDE));
1377 }
1378
1379 // Wearer is the default leader, unless anyone is taller.
1380 l = llGetListLength(Sitters);
1381 integer m = l / pSTRIDE;
1382 integer ldr = findSitter(Owner);
1383 float max = 0.0;
1384 if (-1 != ldr)
1385 {
1386 vector s = llGetAgentSize(Owner);
1387 max = s.z;
1388 }
1389 for (lnk = 0; lnk < l; lnk += pSTRIDE)
1390 {
1391 key t = llList2Key(Sitters, lnk + pKEY);
1392 vector s = llGetAgentSize(t);
1393 if (s.z > max)
1394 {
1395 max = s.z;
1396 ldr = lnk;
1397 }
1398 if (NULL_KEY == t) --m;
1399 }
1400 if ((0 != ldr) && (0 != llGetListLength(Sitters)))
1401 {
1402 list s = llList2List(Sitters, ldr, ldr + pSTRIDE - 1);
1403 Sitters = s + llListReplaceList(Sitters, [], ldr, ldr + pSTRIDE - 1);
1404 }
1405 newLeader(llList2Key(Sitters, pKEY));
1406 llSitTarget(position, llEuler2Rot(rotat * DEG_TO_RAD));
1407 Controller = NULL_KEY;
1408 for (lnk = 0; lnk < l; lnk += pSTRIDE)
1409 {
1410 updateSitter(llList2Key(Sitters, lnk + pKEY));
1411 key t = llList2Key(Sitters, lnk + pKEY);
1412 if ((NULL_KEY == Controller) && (Owner != t))
1413 Controller = t;
1414 }
1415 if (NULL_KEY != Controller)
1416 {
1417 d("checkSitters() requesting camera and controls from " + llKey2Name(Controller));
1418 llRequestPermissions(Controller, PERMISSION_CONTROL_CAMERA | PERMISSION_TAKE_CONTROLS);
1419 }
1420 llSetSitText(Sit0Text); llSetTouchText("menu"); llSetClickAction(CLICK_ACTION_SIT);
1421 llSetText(HoverText, <1.0, 1.0, 1.0>, 1.0);
1422 if (0 == m)
1423 {
1424 if (del)
1425 {
1426 llSleep(5.0);
1427 d("No one left sitting on me.");
1428 if (NULL_KEY != BossKey) die();
1429 }
1430 llSetTouchText("");
1431 llSetLinkAlpha(Link, 0.5, ALL_SIDES);
1432 }
1433 else if (1 == m) llSetLinkAlpha(Link, 0.25, ALL_SIDES);
1434 else if (2 == m)
1435 {
1436 llSetSitText(Sit1Text); llSetClickAction(CLICK_ACTION_TOUCH);
1437 llSetLinkAlpha(Link, 0.1, ALL_SIDES); llSetText("", ZERO_VECTOR, 0.0);
1438 }
1439 return;
1440}
1441
1442stopAnims(key avatar)
1443{
1444 if (NULL_KEY != avatar)
1445 {
1446 list anims = llGetAnimationList(avatar);
1447 integer l = llGetListLength(anims);
1448 integer i;
1449 for (i = 0; i < l; i++)
1450 {
1451 string anim = llList2String(anims, i);
1452 if (anim != "") osAvatarStopAnimation(avatar, anim);
1453 }
1454 }
1455}
1456
1457// AO functions.
1458list States = [];
1459integer loadCard(string card, integer casperMode)
1460{
1461 if (NULL_KEY != llGetInventoryKey(card))
1462 {
1463 float now = llGetTimeOfDay();
1464 string section = "";
1465 integer l = osGetNumberOfNotecardLines(card);
1466 integer i;
1467 States = initialStates;
1468 for (i = 0; i <= l; ++i)
1469 {
1470 string line = osGetNotecardLine(card, i);
1471 string rest = "";
1472 string name = "";
1473 integer match = llSubStringIndex(line, "]");
1474 integer found = -1;
1475 // Ignore anything that does not start with [, and has the matching ].
1476 // Which is a dirt cheap ZHAO II compatibility.
1477 if (("[" == llGetSubString(line, 0, 0)) && (-1 != match))
1478 {
1479 name = llStringTrim(llGetSubString(line, 1, match - 1), STRING_TRIM);
1480 if (casperMode) section = name;
1481 else
1482 {
1483 name = alias(name);
1484 rest = llGetSubString(line, match + 1, -1);
1485 // Corner case, no actual anims.
1486 if ((match + 1) == llStringLength(line)) rest = "";
1487 }
1488 }
1489 // Casper AO has a different format. AOConfig, "[Walking]" on a line by itself, followed by one animation per line.
1490 // I vaguely remember that might be the ZHAO I format.
1491 // Though I doubt if ZHAO I ever made it out of SL, think ZHAO II was too popular at the time.
1492 else if (casperMode)
1493 {
1494 rest = llStringTrim(line, STRING_TRIM);
1495 if ("" != rest) name = section;
1496 }
1497 if ("" != name)
1498 {
1499 found = llListFindList(States, [name]);
1500 if ((0 <= found) && ((found % 2) == 0))
1501 {
1502 string data = llList2String(States, found + 1);
1503 if ("" != data) data += "|";
1504 States = llListReplaceList(States, [name, data + rest], found, found + 1);
1505 }
1506 else States += [name, rest];
1507 }
1508 }
1509 l = llGetListLength(States);
1510 for (i = 0; i < l; i += 2)
1511 {
1512 list nA = llParseStringKeepNulls(llList2String(States, i + 1), ["|"], []);
1513 integer m = llGetListLength(nA);
1514 integer j;
1515 for (j = 0; j < m; ++j)
1516 {
1517 list a = llCSV2List(llList2String(nA, j));
1518 integer n = llGetListLength(a);
1519 integer k;
1520 for (k = 0; k < n; ++k)
1521 checkAnim(llList2String(a, k));
1522 }
1523 }
1524 d("Read " + card + " in " + (string) (llGetTimeOfDay() - now) + " seconds.");
1525 return TRUE;
1526 }
1527 return FALSE;
1528}
1529
1530list isPoseAO(integer f)
1531{
1532 if ("" != Pose)
1533 {
1534 integer p = findPose(Pose);
1535 if (-1 != p)
1536 {
1537 list ps = llParseStringKeepNulls(llList2String(Poses, p + psANIM), ["|"], []);
1538 if ("~" != llList2String(ps, f / pSTRIDE))
1539 return ps;
1540 }
1541 }
1542 return [];
1543}
1544
1545checkAO()
1546{
1547 string newAnim;
1548 integer l = llGetListLength(Sitters);
1549 integer fast; integer i; integer f; float dpth;
1550 if (NULL_KEY != TheirKey) return;
1551
1552 if (Attached)
1553 {
1554// if (llGetAgentInfo(Owner) & AGENT_ALWAYS_RUN) fast = 1; else fast = 0;
1555 newAnim = llGetAnimation(Owner);
1556 vAnim = newAnim;
1557 }
1558 else
1559 {
1560// TODO - when updateControls() figures out fast mode, use that.
1561 newAnim = vAnim;
1562 }
1563 if (("" == Pose) && ("0" == getSetting("AO")))
1564 {
1565 stopAnims(Owner);
1566 AOspeed = 1.0;
1567 osSetSpeed(Owner, 1.0);
1568 return;
1569 }
1570 string oldAnim = newAnim;
1571//d("checkAO() " + newAnim);
1572 AOspeed = 0.0;
1573 integer flying = llListFindList(flyStates, [vAnim]);
1574 if (-1 != flying)
1575 {
1576 float water = llWater(ZERO_VECTOR);
1577 float ground = llGround(ZERO_VECTOR);
1578 integer fly = ("Flying" == vAnim);
1579// if (fly && !fast) newAnim = "FlyingSlow";
1580 if (water > ground) // First check if we can even be under water.
1581 {
1582 // In Opensim, pos.z is actually a double, and OpenSim can't do equality for doubles & floats.
1583 vector pos = llGetPos(); float z = pos.z; // Coz OpenSim can't do llGetPos().z
1584 if (z <= water)
1585 {
1586 if (0 > Bobbing) Bobbing++;
1587 Swimming = TRUE;
1588 dpth = water - z;
1589 }
1590 else
1591 {
1592 if (Swimming)
1593 { // That's metres of water depth before it figures you don't have enough to swim in.
1594 if ((z > water) && ((water - ground) > 1.5))
1595 { // Push you back into the water.
1596 vector velocity = llGetVel();
1597 dpth = 0.1;
1598 velocity.x = 0.0; velocity.y = 0.0;
1599 if (10 > velocity.z) velocity.z = 10;
1600 velocity.z *= -4;
1601 if ("Swimming Up" == alias("~" +vAnim))
1602 {
1603 Bobbing++;
1604 if (3 < Bobbing)
1605 { // Switch to flying.
1606 Bobbing = 0;
1607 Swimming = FALSE;
1608 }
1609 }
1610// TODO - this wont work in vehicle mode.
1611 if (Swimming)
1612 llApplyImpulse(llGetMass() * velocity, FALSE);
1613 }
1614 else // Switch to "walking" mode.
1615 Swimming = FALSE;
1616 }
1617 }
1618 } // No water to swim in here.
1619 else Swimming = FALSE;
1620 }
1621 else // Not in fly mode.
1622 Swimming = FALSE;
1623// llParticleSystem([]);
1624 if (Swimming)
1625 {
1626 if (Attached) AOspeed = 0.1;
1627 else AOspeed = 0.5;
1628 newAnim = alias("~" + newAnim);
1629// updateParticles();
1630 }
1631 else if (-1 != flying)
1632 {
1633 if (Attached) AOspeed = 1.0;
1634 else AOspeed = 4.0;
1635 }
1636 else AOspeed = 1.0;
1637 if (0.0 < AOspeed) osSetSpeed(Owner, AOspeed + ((AOspeed / 2) * (fast + (2 * (integer) getSetting("super")))));
1638 else AOspeed = 1.0;
1639 for (f = 0; f < l; f += pSTRIDE)
1640 {
1641 string anim = llList2String(Sitters, f + pSTATE);
1642 list anims = llCSV2List(llList2String(Sitters, f + pANIMS));
1643 integer g;
1644 list states = States;
1645 key id = llList2Key(Sitters, f + pKEY);
1646 list ps = isPoseAO(f);
1647 if (0 != llGetListLength(ps))
1648 {
1649 states = [Pose, llList2String(ps, f / pSTRIDE)];
1650 newAnim = Pose;
1651 }
1652//d("checkAO " + llKey2Name(id) + " " + anim + " -> " + newAnim + " @ " + (string) AOspeed + " " + Pose);
1653 if (newAnim != anim)
1654 {
1655 g = listFindString(states, newAnim, 2);
1656 if (-1 != g)
1657 {
1658 stopMe(id, f);
1659 // Ignore sits, since 99.99% of the time what they are sitting on supplies an animation.
1660 if ((("Sitting" == vAnim) && Attached) || ("" == llList2String(states, g + 1)))
1661 ;
1662 else
1663 {
1664 list newAnims = llParseString2List(llList2String(states, g + 1), ["|"], []);
1665 // If there's more than one, randomly switch between them at random times.
1666 i = llGetListLength(newAnims);
1667 if (1 < i) addEvent(20.0 + llFrand(20.0), "SYNC R");
1668 i = (integer) llFrand((float) i);
1669 // ZHAO II also allows multiple anims in one set, comma separated. Good idea.
1670 anims = llCSV2List(llList2String(newAnims, i));
1671//d("ANIMS " + llKey2Name(id) + " " + newAnim + " -> " + llList2String(newAnims, i));
1672 for (i = llGetListLength(anims) - 1; i >= 0; --i)
1673 {
1674 string a = llList2String(anims, i);
1675 if (checkAnim(a)) osAvatarPlayAnimation(id, a);
1676 }
1677 }
1678 }
1679 anim = newAnim;
1680 }
1681 Sitters = llListReplaceList(Sitters, [anim, anim, llDumpList2String(anims, ",")], f + pSTATE, f + pANIMS);
1682 newAnim = oldAnim;
1683 }
1684 if (("" == Pose) || ("Swimming Up" == vAnim)) addEvent(Tick * dpth, "checkAO");
1685}
1686
1687stopMe(key id, integer f)
1688{
1689 list anims = llCSV2List(llList2String(Sitters, f + pANIMS));
1690 integer i;
1691//d("stopMe " + llKey2Name(id) + " " + llList2String(Sitters, f + pANIMS));
1692 for (i = llGetListLength(anims) - 1; i >= 0; --i)
1693 osAvatarStopAnimation(id, llList2String(anims, i));
1694}
1695
1696checkLag()
1697{
1698 float dil = llGetRegionTimeDilation(); // Between 0 and 1.
1699 float fps = llGetRegionFPS(); // Frames per second, up to 50.
1700 integer newLag = (integer) (dil * fps);
1701 if (llAbs(Lag - newLag) > 9)
1702 {
1703 string l;
1704 Lag = newLag;
1705 Tick = ((60 - (dil * fps)) / 30) + 0.15;
1706 if (45 <= newLag) l = "No";
1707 else if (35 <= newLag) l = "A little";
1708 else if (25 <= newLag) l = "Medium";
1709 else if (15 <= newLag) l = "Lots of";
1710 else l = "Way too much";
1711 if (!osIsNpc(llGetOwner())) s(Owner, l + " lag, tick is " + (string) Tick);
1712 }
1713}
1714
1715init()
1716{
1717 ANIMATIONS = readCard("animations"); flyStates = readCard("flystates"); initialStates = readCard("states");
1718 Attached = (0 != llGetAttached());
1719 llSetSitText("NO SIT"); llSetTouchText("menu");
1720 llSetClickAction(CLICK_ACTION_TOUCH); llSetText("", ZERO_VECTOR, 0.0);
1721 Link = llGetLinkNumber();
1722// None of this works in OpenSim?
1723// llSetBuoyancy(-2.0);
1724// llSetHoverHeight(10.0, TRUE, 2.0);
1725// llGroundRepel(10.0, TRUE, 2.0);
1726 checkLag();
1727}
1728
1729laterInit()
1730{
1731 if (!loadCard("ZHAO II", FALSE)) // In an object full of random scripts, "Default" is a lousy name.
1732 if (!loadCard("Default", FALSE))
1733 loadCard("AOConfig", TRUE); // Casper AOs are less common.
1734 if (Attached)
1735 {
1736 d("AO mode.");
1737 Sitters = [Owner, -1, "", "", "", "", llGetListLength(Distances) / 3, 0];
1738 checkAO();
1739 }
1740 else
1741 {
1742 if (NULL_KEY == BossKey)
1743 d("Vehicle mode.");
1744 else
1745 d("Vehicle mode, slaved to " + llKey2Name(BossKey) + ".");
1746 integer i = llGetInventoryNumber(INVENTORY_NOTECARD);
1747 list posCards = [];
1748 string item;
1749 while (i-- > 0)
1750 {
1751 item = llGetInventoryName(INVENTORY_NOTECARD, i);
1752 if (llSubStringIndex(item, ".POSITIONS") == 0) posCards += (list) item;
1753 }
1754 readPos(posCards);
1755 d("Loaded " + (string) (llGetListLength(Poses) / psSTRIDE) + " positions in "
1756 + (string) (llGetTimeOfDay() - Start) + " seconds.");
1757 checkSitters(FALSE);
1758 if (NULL_KEY != BossKey) sendPrim(BossKey, "REZ_DONE", []);
1759 }
1760 addEvent(6.0 + llFrand(10.0), "SMILE");
1761}
1762
1763die()
1764{
1765 integer l = llGetListLength(Sitters);
1766 integer f;
1767 for (f = 0; f < l; f += pSTRIDE)
1768 stopMe(llList2Key(Sitters, f + pKEY), f);
1769 d("Deleting myself.");
1770 sendPrim(BossKey, "DIE_DONE", []);
1771 if (PERM_COPY & llGetObjectPermMask(MASK_OWNER)) llDie();
1772 else s("This no copy object wont delete itself, please delete manually, or take into inventory.");
1773}
1774
1775default
1776{
1777 state_entry()
1778 {
1779 llSleep(0.2);
1780 Start = llGetTimeOfDay();
1781 Owner = llGetOwner();
1782d("\n\n1AOor2 sending RESET @ " + (string) Start + "\n");
1783 ScriptName = llGetScriptName(); ScriptKey = llGetInventoryKey(ScriptName);
1784 LibraryKey = NULL_KEY;
1785 sendScript(lRESET, []);
1786 init();
1787 if (bREZ == llGetStartParameter()) BossKey = osGetRezzingObject();
1788 }
1789
1790 control(key id, integer level, integer edge) {doControl(id, level, edge);}
1791 link_message(integer sender, integer num, string message, key id) {linky(num, message, id);}
1792
1793 changed(integer change)
1794 {
1795 if (change & CHANGED_LINK) checkSitters(TRUE);
1796 if (change & CHANGED_ANIMATION) checkAO();
1797 if ((change & CHANGED_TELEPORT) || (change & CHANGED_REGION))
1798 {
1799 if (99.0 > TPangle)
1800 {
1801 PIN = (integer) llFrand(DEBUG_CHANNEL - 2) + 2;
1802 vector RefPos = llGetPos();
1803 rotation RefRot = llGetRot();
1804 llRezObject("1AOor2 prim", ZERO_VECTOR * RefRot + RefPos, ZERO_VECTOR, (ZERO_ROTATION) * RefRot, PIN);
1805 llOwnerSay("@setrot:" + (string) TPangle + "=force");
1806 s(Owner, "Switching AO to 1ring object.");
1807 oldController(Owner);
1808 TPangle = 999.0;
1809 Chosen = 1;
1810 }
1811 }
1812 }
1813
1814 run_time_permissions(integer perm)
1815 {
1816 if (PERMISSION_TAKE_CONTROLS & perm)
1817 {
1818 key id = llGetPermissionsKey();
1819 d("taking controls from " + llKey2Name(id));
1820 llTakeControls( CONTROL_FWD | CONTROL_BACK | CONTROL_LEFT | CONTROL_RIGHT |
1821 CONTROL_ROT_LEFT | CONTROL_ROT_RIGHT | CONTROL_UP | CONTROL_DOWN, TRUE, FALSE);
1822 ControlTime = llGetTimeOfDay();
1823 addEvent(Tick * Smooth, "Keys");
1824 }
1825 if (PERMISSION_CONTROL_CAMERA & perm)
1826 {
1827 lookAtMe(llGetPermissionsKey());
1828 if ((llGetPermissionsKey() != Controller) && (NULL_KEY != Controller))
1829 {
1830 d("PERMISSION_CONTROL_CAMERA requesting camera and controls from " + llKey2Name(Controller));
1831 llRequestPermissions(Controller, PERMISSION_CONTROL_CAMERA | PERMISSION_TAKE_CONTROLS);
1832 }
1833 }
1834 }
1835}
diff --git a/1chatter.lsl b/1chatter.lsl
new file mode 100644
index 0000000..fdad263
--- /dev/null
+++ b/1chatter.lsl
@@ -0,0 +1,1597 @@
1
2// Library of generic functions for other scripts to use.
3
4string Version = "1chatter v0.1 dev version";
5
6// BEGIN boilerplate.
7integer DEBUG = FALSE;
8float Start;
9string ScriptName;
10key ScriptKey;
11key LibraryKey;
12key Owner;
13string URL;
14
15list Commands;
16integer cNAME = 0;
17integer cARGS = 1;
18integer cAUTH = 2;
19integer cSTRIDE = 3;
20
21list Scanners = []; // Current set of scans.
22integer nMENU = 0; // Menu that started the scan.
23integer nID = 1; // Avatar UUID we are doing this scan for.
24integer nTYPE = 2; // The type of scan.
25integer nTITLE = 3; // Title of the menu to show.
26integer nCMD = 4; // Command to run with the result.
27integer nSTRIDE = 5;
28
29// utilities commands, "l"ibrary.
30string lSEP = "$!#"; // Used to seperate lists when sending them as strings.
31integer lRESET = -1;
32integer lRESET_DONE = -2;
33integer lALIAS = -3;
34integer lALIAS_DONE = -4;
35integer lSETTING = -5;
36integer lSETTING_DONE = -6;
37integer lSUBSTITUTE = -7;
38integer lSUBSTITUTE_DONE = -8;
39integer lNEXT_WORD = -9;
40integer lNEXT_WORD_DONE = -10;
41integer lCONTROL = -13;
42integer lCONTROL_DONE = -14;
43integer lCMD = -15;
44integer lCMD_DONE = -16;
45integer lSETTINGS = -17;
46integer lSETTINGS_DONE = -18;
47integer lSCAN = -19;
48integer lDYNAMIC = -20;
49integer lMENU = -21;
50
51d(string m) {if (DEBUG) llInstantMessage(Owner, llGetScriptName() + ": " + m);}
52D(string m) {llRegionSay(DEBUG_CHANNEL, llGetScriptName() + ": " + m);}
53s(string m) {s(Owner, m);}
54s(key id, string m) {if (id == Owner) llOwnerSay(m); else llInstantMessage(id, m);}
55sendScript(integer cmd, list args) {sendScript(LibraryKey, cmd, ScriptName, args);}
56sendScript(key them, integer cmd, list args) {sendScript(them, cmd, inKey2Name(them), args);}
57sendScript(integer cmd, string name, list args) {sendScript(LibraryKey, cmd, name, args);}
58sendScript(key them, integer cmd, string name, list args)
59{
60 llMessageLinked(LINK_SET, cmd, llDumpList2String([ScriptKey, name] + args, lSEP), them);
61}
62sendPrim(key them, string cmd, list args) {osMessageObject(them, llDumpList2String([ScriptName] + args, lSEP));}
63
64string inKey2Name(key k)
65{
66 if (k == LibraryKey) return "1chatter";
67 integer i = llGetInventoryNumber(INVENTORY_SCRIPT);
68 while (i-- > 0)
69 {
70 string n = llGetInventoryName(INVENTORY_SCRIPT, i);
71 if (llGetInventoryKey(n) == k) return n;
72 }
73 return k;
74}
75
76integer uSubStringLastIndex(string hay, string pin)
77{
78 integer i2 = -1;
79 integer i;
80
81 if (pin == "")
82 return 0;
83 while (~i)
84 {
85 i = llSubStringIndex(llGetSubString(hay, ++i2, -1), pin);
86 i2 += i;
87 }
88 return i2;
89}
90
91integer listFindString(list lst, string name, integer stride)
92{
93 integer f = llListFindList(lst, [name]);
94 integer ix = f / stride;
95
96 // Round to nearest stride.
97 ix = ix * stride;
98
99 // Sanity check, make sure we found a name, not something else, else do it the slow way.
100 if ((-1 != f) && (ix != f))
101 {
102 integer l = llGetListLength(lst);
103 integer i;
104
105 f = -1;
106 for (i = 0; i < l; i += stride)
107 {
108 if (llList2String(lst, i) == name)
109 {
110 f = i;
111 i = l;
112 }
113 }
114 }
115 return f;
116}
117
118string getFor(string name)
119{
120 string fr;
121 integer i = llSubStringIndex(name, ".");
122
123 if (-1 != i)
124 fr = llGetSubString(name, 0, i);
125 return fr;
126}
127
128string noFor(string name)
129{
130 string r = name;
131 integer i = llSubStringIndex(name, ".");
132
133 if (-1 != i)
134 r = llGetSubString(name, i + 1, -1);
135 return r;
136}
137
138list validateName(string var, string type)
139{
140 list v = llParseStringKeepNulls(var, ["."], []);
141 if (2 != llGetListLength(v))
142 {
143 d("Invalid " + type + " name - " + var);
144 return ["", var];
145 }
146 var = llList2String(v, 1);
147 if ("setting" == type)
148 var = llToUpper(var);
149 return [llList2String(v, 0) + ".", var];
150}
151
152
153list Aliases;
154integer aALIAS = 0;
155integer aNAME = 1;
156integer aSTRIDE = 2;
157
158string alias(string n)
159{
160 integer a = listFindString(Aliases, llToLower(n), 2);
161 if (-1 != a)
162 n = llList2String(Aliases, a + 1);
163 return n;
164}
165
166
167// Access "f"rom some source -
168integer fINT = 0; // Internal, not sure we need this, means the same script.
169integer fCARD = 1; // Read from a note card.
170integer fCHAT = 2; // Chat channel.
171integer fLINK = 3; // Link message.
172integer fMENU = 4; // Menu, in reality this is just a chat channel.
173integer fRLV = 5; // RLV, again from a chat channel.
174integer fRELAY = 6; // RLV relay.
175integer fOSM = 7; // osMessageObject()
176integer fHTTP = 8; // From the web.
177
178// LSL security is entirely hopeless, see the notecard "1ring security".
179// "a"ccess level -
180integer aNONE = 0;
181integer aBOSS = 1;
182integer aTRUST = 2;
183integer aGROUP = 4;
184integer aBOSSES = 3;
185integer aWEARER = 8;
186integer aPRIV = 9;
187integer aMORE = 11;
188integer aMOST = 15;
189integer aPUBLIC = 16;
190integer aALL = 31;
191list Access =
192[
193 "NONE", 0,
194 "BOSS", 1,
195 "TRUST", 2,
196 "GROUP", 4,
197 "BOSSES", 3,
198 "WEARER", 8,
199 "PRIV", 9,
200 "MORE", 11,
201 "MOST", 15,
202 "PUBLIC", 16,
203 "ALL", 31
204];
205
206integer decodeAccess(string a)
207{
208 integer r = aALL;
209 integer f = listFindString(Access, a, 2);
210 if (-1 != f)
211 r = llList2Integer(Access, f + 1);
212 return r;
213}
214
215integer access(key id, string what, string fr, integer auth, integer bitch)
216{
217 list bosses = llParseString2List(getSetting(fr + "BOSS"), [","], []);
218 integer l = llGetListLength(bosses);
219 integer j = aPUBLIC;
220 integer i;
221 integer b = FALSE;
222
223//d("access " + id + " " + what + " FOR " + fr + " " + auth);
224 if (id == Owner) j = j | aWEARER | aGROUP;
225 if (llSameGroup(id)) j = j | aGROUP;
226 if ("1" == getSetting(fr + "PUBLIC")) j = j | aTRUST;
227 for (i = 0; i < l; ++i)
228 {
229 if (id == llList2Key(bosses, i))
230 {
231 j = j | aBOSS | aTRUST;
232 i = l;
233 }
234 }
235 bosses = llParseString2List(getSetting(fr + "TRUSTEE"), [","], []);
236 l = llGetListLength(bosses);
237 for (i = 0; i < l; ++i)
238 {
239 if (id == llList2Key(bosses, i))
240 {
241 j = j | aTRUST;
242 i = l;
243 }
244 }
245 bosses = llParseString2List(getSetting(fr + "BLOCKED"), [","], []);
246 l = llGetListLength(bosses);
247 for (i = 0; i < l; ++i)
248 {
249 if (id == llList2Key(bosses, i))
250 {
251 b = TRUE;
252 j = 0;
253 i = l;
254 }
255 }
256 j = auth & j;
257
258 if (bitch && (0 == j))
259 {
260 if (b)
261 {
262 s(Owner, "Blocked user " + llKey2Name(id) + " failed to use this.");
263 s(id, "You are blocked from using this.");
264 }
265 else
266 {
267 string who;
268
269 if (aNONE == auth) who = "no ";
270 if (auth & aWEARER) who += "'" + llKey2Name(llGetKey()) + "' owner, ";
271 if (auth & aBOSS) who += noFor(alias(fr + "boss")) + ", ";
272 if (auth & aTRUST) who += noFor(alias(fr + "trusted")) + ", ";
273 if (auth & aGROUP) who += "group, ";
274 if (auth & aPUBLIC) who += "public, ";
275 if (", " == llGetSubString(who, -2, -1))
276 who = llGetSubString(who, 0, -3) + " ";
277 i = uSubStringLastIndex(who, ", ");
278 if (-1 != i)
279 who = llGetSubString(who, 0, i + 1) + "and " + llGetSubString(who, i + 2, -1);
280 s(id, "The " + what + " is allowed only for " + who + "users.");
281 }
282 }
283 return j;
284}
285
286
287string TRUEs = "t1aopswy";
288string FALSEs = "f0bgnuz";
289integer isBool(string b)
290{
291 integer r = FALSE;
292
293 if ("" != b)
294 r = (-1 != llSubStringIndex(TRUEs, llToLower(llGetSubString(b, 0, 0))));
295 return r;
296}
297
298// "s"ettings list -
299list Settings;
300integer sNAME = 0;
301integer sTYPE = 1;
302integer sVALUE = 2;
303integer sAUTH = 3;
304integer sSTRIDE = 4;
305
306string getSetting(string var)
307{
308 list v = validateName(var, "setting");
309 var = llList2String(v, 0) + llList2String(v, 1);
310 string result;
311 integer f = listFindString(Settings, var, sSTRIDE);
312 if (-1 != f)
313 result = llList2String(Settings, f + sVALUE);
314 return result;
315}
316
317integer setSetting(key id, string var, string val, integer source)
318{
319 list v = validateName(var, "setting");
320 string fr = llList2String(v, 0);
321 var = fr + llList2String(v, 1);
322 integer f = listFindString(Settings, var, sSTRIDE);
323
324 if (-1 != f)
325 {
326 integer acs = access(id, var + " setting", fr, llList2Integer(Settings, f + sAUTH), TRUE);
327 if (0 == acs)
328 return acs;
329
330 string type = llToUpper(llList2String(Settings, f + sTYPE));
331
332 if ("Y" == type)
333 val = (string) isBool(val);
334 Settings = llListReplaceList(Settings, [val], f + sVALUE, f + sVALUE);
335//d("setSetting " + var + " = " + val);
336 if (fCARD != source)
337 {
338 string card = "." + llGetSubString(fr, 0, -2) + ".settings";
339 list data;
340 if (NULL_KEY != llGetInventoryKey(card))
341 data = llParseString2List(osGetNotecard(card), ["\n"], []);
342 integer l = llGetListLength(data);
343 string ar = llList2String(v, 1);
344
345// TODO - could use a bit more sophistication here, but it works as is.
346 for (f = 0; f < l; ++f)
347 {
348 list p = llParseString2List(llList2String(data, f), ["="], []);
349 string k = llList2String(p, 0);
350 if (k == var)
351 {
352 data = llListReplaceList(data, [k + "=" + val], f, f);
353 f = l + 1;
354 }
355 if (k == ar)
356 {
357 data = llListReplaceList(data, [k + "=" + val], f, f);
358 f = l + 1;
359 }
360 }
361 if (f == l)
362 data += [ar + "=" + val];
363 llRemoveInventory(card);
364 osMakeNotecard(card, data);
365d("setSetting " + var + " saved to " + card);
366 }
367 // Hopefully this wont result in any recursive setSettings calls.
368 doThing(id, "SET " + var + "=" + val, fr, llList2String(v, 1), val, source);
369
370 return acs;
371 }
372 return 0;
373}
374
375// TODO - Hmmm, only called once.
376readSettings(string card)
377{
378 float now = llGetTimeOfDay();
379 key boss = llGetOwner();
380 string fr = card + ".";
381
382 card = "." + card + ".settings";
383 if (NULL_KEY != llGetInventoryKey(card))
384 {
385 list d = llParseString2List(osGetNotecard(card), ["\n"], []);
386 integer l = llGetListLength(d);
387 integer i;
388
389 for (i = 0; i < l; ++i)
390 {
391 string cmd = alias(llList2String(d, i));
392 integer j = llSubStringIndex(cmd, "=");
393 if (-1 == j)
394 j = llSubStringIndex(cmd, " ");
395 if (-1 == j)
396 j = llStringLength(cmd);
397 string f = fr;
398 string var = llStringTrim(llGetSubString(cmd, 0, j - 1), STRING_TRIM);
399
400 if (-1 != llSubStringIndex(var, "."))
401 f = "";
402 setSetting(boss, f + var, llStringTrim(llGetSubString(cmd, j + 1, -1), STRING_TRIM), fCARD);
403 }
404 }
405 else
406 setSetting(boss, fr + "BOSS", Owner, fCARD);
407// d("Read " + card + " in " + (string) (llGetTimeOfDay() - now) + " seconds.");
408}
409
410dumpAliases(list s, string title)
411{
412 integer l = llGetListLength(s);
413 integer i;
414
415 d("v--------------- " + title);
416 for (i = 0; i < l; i += aSTRIDE)
417 {
418 d(
419 llList2String(s, i + aALIAS) + "~" +
420 llList2String(s, i + aNAME)
421 );
422 }
423 d("^---------------- LIBRARY");
424}
425
426dumpCommands(list s, string title)
427{
428 integer l = llGetListLength(s);
429 integer i;
430
431 d("v--------------- " + title);
432 for (i = 0; i < l; i += cSTRIDE)
433 {
434 d(
435 llList2String(s, i + cNAME) + "~" +
436 llList2String(s, i + cARGS) + "~" +
437 llList2String(s, i + cAUTH)
438 );
439 }
440 d("^---------------- LIBRARY");
441}
442
443dumpSettings(list s, string title)
444{
445 integer l = llGetListLength(s);
446 integer i;
447
448 d("v--------------- " + title);
449 for (i = 0; i < l; i += sSTRIDE)
450 {
451 d(
452 llList2String(s, i + sNAME) + "~" +
453 llList2String(s, i + sTYPE) + "~" +
454 llList2String(s, i + sVALUE) + "~" +
455 llList2String(s, i + sAUTH)
456 );
457 }
458 d("^---------------- LIBRARY");
459}
460
461
462
463// "e"vents
464float NextEvent = 307584000.0; // Ten years.
465list Events = [];
466integer eTIME = 0; // Time of it's next event.
467integer eSCRIPT = 1; // Rest of scriptlet after the delay command.
468integer eKEY = 2; // Key of script asking for this timer.
469integer eSTRIDE = 3;
470
471
472addEvent(float delay, string cmds, key id)
473{
474 float now = llGetTimeOfDay();
475 list ev = [now + delay, cmds, id];
476 integer f = findEventByID(cmds);
477
478 if (0.0 >= delay)
479 {
480 if (-1 != f)
481 Events = llListReplaceList(Events, [], f, f + eSTRIDE - 1);
482 }
483 else if (-1 == f)
484 Events += ev;
485 else
486 Events = llListReplaceList(Events, ev, f, f + eSTRIDE - 1);
487 Events = llListSort(Events, eSTRIDE, TRUE);
488 if (llList2Float(Events, 0) < NextEvent)
489 {
490 NextEvent = llList2Float(Events, 0);
491 llSetTimerEvent(NextEvent - now);
492 }
493}
494
495integer findEventByID(string id)
496{
497 integer f = llListFindList(Events, [id]);
498 integer ix = f / eSTRIDE;
499
500 // Round to nearest stride.
501 ix = ix * eSTRIDE;
502
503 if ((-1 != f) && ((ix + eSCRIPT) == f))
504 return f - eSCRIPT;
505 else
506 return -1;
507}
508
509
510// For marking up menu buttons.
511string Enclosed = "ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ⓪⓵⓶⓷⓸⓹⓺⓻⓼⓽ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏ";
512string DownsideUp = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
513string UpsideDown = "ɐqɔpǝɟɓɥᴉſʞןɯuodbɹsʇnʌʍxʎz0lᘔƐᔭ59Ɫ86∀qↃpƎℲ⅁HIᒋʞ⅂ꟽNOԀÒᴚS⊥∩ɅMX⅄Z";
514
515string enclosed(string text)
516{
517 string result;
518 integer l = llStringLength(text);
519 integer i;
520
521 for (i = 0; i < l; ++i)
522 {
523 string c = llGetSubString(text, i, i);
524 integer f = llSubStringIndex(DownsideUp, c);
525 if (-1 != f)
526 c = llGetSubString(Enclosed, f, f);
527 result += c;
528 }
529 return result;
530}
531
532string upsideDown(string text)
533{
534 string result;
535 integer i = llStringLength(text) - 1;
536
537 for (; i > -1; --i)
538 {
539 string c = llGetSubString(text, i, i);
540 integer f = llSubStringIndex(DownsideUp, c);
541 if (-1 != f)
542 c = llGetSubString(UpsideDown, f, f);
543 result += c;
544 }
545 return result;
546}
547
548
549// "m"enus
550string MainMenu;
551list Menus; // List of menus.
552integer mNAME = 0; // Name of menu.
553integer mTITLE = 1; // Title for the menu.
554integer mAUTH = 2; // Authorised users of menu - 0 = wearer, 1 = boss, 2 = all
555integer mENTRIES = 3; // | separated list of entries.
556integer mCMDS = 4; // | separated list of commands, matching the entries.
557integer mDYN = 5; // Dynamic menu, autoremove it when done.
558integer mSTRIDE = 6;
559
560integer findMenu(string name)
561{
562 return listFindString(Menus, name, mSTRIDE);
563}
564
565saveMenu(string name, string title, integer auth, string entries, string commands, integer dyn)
566{
567 integer f = findMenu(name);
568
569 if (-1 == f)
570 Menus += [name, title, auth, entries, commands, dyn];
571 else
572 {
573 if ("" == title)
574 title = llList2String(Menus, f + mTITLE);
575 if ("" == entries)
576 entries = llList2String(Menus, f + mENTRIES);
577 if ("" == commands)
578 commands = llList2String(Menus, f + mCMDS);
579 Menus = llListReplaceList(Menus, [name, title, auth, entries, commands, dyn], f, f + mSTRIDE - 1);
580 }
581}
582
583addMenuItem(string fr, string menu, string cmd, list args)
584{
585 integer f = findMenu(menu);
586
587 if (-1 != f)
588 {
589 string e = llList2String(Menus, f + mENTRIES);
590 string c = llList2String(Menus, f + mCMDS);
591
592 if (llGetListLength(args) < 2) args += [" "];
593 if ("" != e) e += "|";
594 if ("" != c) c += "|";
595 if ("TOGGLE" == cmd)
596 e += getToggle(fr, llList2String(args, 0));
597 else
598 e += llList2String(args, 0);
599 if ("TOMENU" == cmd) e += "…";
600 c += llDumpList2String(llListReplaceList(args, [], 0, 0), " ");
601 saveMenu(menu, llList2String(Menus, f + mTITLE), llList2Integer(Menus, f + mAUTH), e, c, llList2Integer(Menus, f + mDYN));
602 }
603}
604
605dynamicMenu(key id, string menu, string name, string title, string entries, string command)
606{
607//d("dynamicMenu " + menu + " -> " + name + "\n" + id + "@" + name);
608 saveMenu(id + "@" + name, title, aALL, entries, command, TRUE);
609 saveMuser(id, id + "@" + name, entries, -1, menu);
610 showMenu(id);
611}
612
613string getToggle(string fr, string item)
614{
615 string result = getSetting(fr + item);
616
617 if ("1" != result)
618 result = "☐";
619 else
620 result = "▣";
621 return result + " " + item;
622}
623
624toggleMenu(key id, string fr, list entries, integer e, integer m)
625{
626 integer on = 0;
627 string this = llList2String(entries, e);
628 string result = llStringTrim(llGetSubString(this, 1, -1), STRING_TRIM);
629 string t = llGetSubString(this, 0, 0);
630 if ("☐" == t) {t = "▣"; on = 1;}
631 else if ("▣" == t) {t = "☐"; on = 0;}
632 else if ("○" == t) {t = "◉"; on = 1;}
633 else if ("◉" == t) {t = "○"; on = 0;}
634 else {s(id, "Not a menu toggle - " + result); return;}
635 if (0 == setSetting(id, fr + result, (string) on, fMENU))
636 return;
637//d("toggleMenu " + result + " " + on);
638// TODO - if it's a radio group, make sure one and only on is turned on.
639 entries = llListReplaceList(entries, [t + " " + result], e, e);
640 saveMenu( llList2String(Menus, m + mNAME),
641 llList2String(Menus, m + mTITLE),
642 llList2Integer(Menus, m + mAUTH),
643 llDumpList2String(entries, "|"),
644 llList2String(Menus, m + mCMDS),
645 llList2Integer(Menus, m + mDYN));
646}
647
648
649// Menu "u"sers
650list Musers; // List of menu users.
651integer uKEY = 0; // Key of user.
652integer uCHAN = 1; // Listen channel for user.
653integer uLSTN = 2; // Listen handle.
654integer uCURRENT = 3; // Current menu of user.
655integer uENTRIES = 4; // Current entries for currently in flight menu.
656integer uPAGE = 5; // Current page of long menu of user.
657integer uSTACK = 6; // | separated menu stack list.
658integer uTIME = 7; // Timestamp of last menu shown.
659integer uSTRIDE = 8;
660
661// page = -1 means the menu items fit on a single dialog, otherwise it's the page of items.
662saveMuser(key avatar, string current, string entries, integer page, string stack)
663{
664 integer f = listFindString(Musers, avatar, uSTRIDE);
665 float n = llGetTimeOfDay();
666
667//d("saveMuser " + f + " " + llKey2Name(avatar) + " = " + current + " ~ " + stack + "\n" + entries);
668 if (-1 == f)
669 {
670 stack = "|";
671 integer c = (integer) ("0x" + llGetSubString((string) avatar, -4, -1)) + (integer) llFrand(12345);
672 integer h = llListen(c, "", avatar, "");
673 Musers += [avatar, c, h, current, entries, page, stack, n];
674 }
675 else
676 {
677 string old = llList2String(Musers, f + uSTACK);
678 if ("-1" == stack)
679 stack = old;
680 else if ("-1" == current)
681 {
682 list lst = llParseStringKeepNulls(old, ["|"], []);
683 current = llList2String(lst, 0);
684 lst = llListReplaceList(lst, [], 0, 0);
685 if (1 == llGetListLength(lst))
686 {
687 llListenRemove(llList2Integer(Musers, f + uLSTN));
688 Musers = llListReplaceList(Musers, [], f, f + uSTRIDE - 1);
689 return;
690 }
691 stack = llDumpList2String(lst, "|");
692 entries = "";
693 }
694 else if (-1 != page)
695 stack = old;
696 else if ("" == stack)
697 ;
698 else if ("" != old)
699 stack += "|" + old;
700 Musers = llListReplaceList(Musers, [avatar,
701 llList2Integer(Musers, f + uCHAN), llList2Integer(Musers, f + uLSTN),
702 current, entries, page, stack, n], f, f + uSTRIDE - 1);
703 }
704//d("saveMu2er " + avatar + " = " + current + " ~ " + stack);
705}
706
707removeMenu(string r)
708{
709 integer f = listFindString(Menus, r, mSTRIDE);
710 if (-1 != f)
711 Menus = llListReplaceList(Menus, [], f, f + mSTRIDE - 1);
712 else
713 D("removeMenu() " + r + " not found!");
714}
715
716lastMenu(key id, string r)
717{
718 saveMuser(id, "-1", "", -1, "");
719 if ("" != r) removeMenu(r);
720}
721
722dumpMenus(list menus)
723{
724 integer l = llGetListLength(menus);
725 integer i;
726 for (i = 0; i < l; i += mSTRIDE)
727 {
728 d(
729 llList2String(menus, i + mNAME) + "~" +
730 llList2String(menus, i + mTITLE) + "~" +
731 llList2String(menus, i + mAUTH) + "~" +
732 llList2String(menus, i + mENTRIES) + "~" +
733 llList2String(menus, i + mCMDS) + "~" +
734 llList2String(menus, i + mDYN)
735 );
736 }
737}
738
739showMenu(key id)
740{
741 integer f = listFindString(Musers, id, uSTRIDE);
742
743 if (-1 != f)
744 {
745 string menu = llList2String (Musers, f + uCURRENT);
746 integer chan = llList2Integer(Musers, f + uCHAN);
747 integer page = llList2Integer(Musers, f + uPAGE);
748 string fr;
749
750 if ("" != menu)
751 {
752 list v = validateName(menu, "menu");
753 fr = llList2String(v, 0);
754 menu = fr + llList2String(v, 1);
755 }
756 integer m = findMenu(menu);
757
758//d("showMenu " + menu + "... " + fr);
759 if (-1 != m)
760 {
761 integer dM = llList2Integer(Menus, m + mDYN);
762 if (dM)
763 fr = llGetSubString(fr, 37, -1);
764 string version = getSetting(fr + "VERSION");
765 list entries = llParseStringKeepNulls(llList2String(Menus, m + mENTRIES), ["|"], []);
766 list cmds = llParseStringKeepNulls(llList2String(Menus, m + mCMDS), ["|"], []);
767 string title = llList2String(Menus, m + mTITLE);
768
769 if (access(id, menu + " menu", fr, llList2Integer(Menus, m + mAUTH), TRUE))
770 {
771 integer l = llGetListLength(entries);
772 integer n;
773
774 if (llList2Integer(Menus, m + mDYN))
775 {
776 for (; n < l; ++n)
777 {
778 string button = llGetSubString(llList2String(entries, n), 0, 23);
779 entries = llListReplaceList(entries, [button], n, n);
780 }
781 n = l;
782 }
783 // Show missing items as upside down text.
784 // Show no access items enclosed.
785 for (; n < l; ++n)
786 {
787 string button = llGetSubString(llList2String(entries, n), 0, 23);
788 string first = llGetSubString(button, 0, 0);
789 string last = llGetSubString(button, -1, -1);
790 entries = llListReplaceList(entries, [button], n, n);
791 if (("▲" == first) || ("◄" == first) || ("►" == last))
792 {
793 }
794 else if ("…" == last)
795 {
796 string t = button;
797 button = llGetSubString(button, 0, -2);
798 integer o = findMenu(fr + button);
799 if (" " == llList2String(cmds, n))
800 {
801 if (-1 == o)
802 t = upsideDown(last + button);
803 else if (!access(id, t, fr, llList2Integer(Menus, o + mAUTH), FALSE))
804 t = enclosed(button + last);
805 entries = llListReplaceList(entries, [t], n, n);
806 }
807 }
808 else if (("☐" == first) || ("▣" == first) || ("○" == first) || ("◉" == first))
809 {
810 button = llStringTrim(llGetSubString(button, 1, -1), STRING_TRIM);
811 string t = getToggle(fr, button);
812 integer o = listFindString(Settings, fr + llToUpper(button), sSTRIDE);
813
814 if (-1 == o)
815 t = upsideDown(button + " " + llGetSubString(t, 0, 0));
816 else if (!access(id, t, fr, llList2Integer(Settings, o + sAUTH), FALSE))
817 t = enclosed(t);
818 entries = llListReplaceList(entries, [t], n, n);
819 }
820 else
821 {
822 string frr = fr;
823 string t = button;
824 string c = llList2String(cmds, n);
825 integer o = llSubStringIndex(c, " ");
826 if (-1 != o)
827 c = llGetSubString(c, 0, o - 1);
828 o = llSubStringIndex(c, ".");
829 if (-1 != o)
830 {
831 frr = llGetSubString(c, 0, o);
832 c = llGetSubString(c, o + 1, -1);
833 }
834 o = listFindString(Commands, frr + llToUpper(c), cSTRIDE);
835 if (("" == c) || (-1 == o))
836 t = upsideDown(t);
837 else if (!access(id, c, frr, llList2Integer(Commands, o + cAUTH), FALSE))
838 t = enclosed(t);
839 entries = llListReplaceList(entries, [t], n, n);
840 }
841 }
842 string mn = menu;
843 if (dM)
844 mn = llGetSubString(mn, 37, -1);
845
846 if (l > 11)
847 {
848 if (-1 == page)
849 page = 1;
850 integer offset = page * 9;
851 llDialog(id, version + " - " + mn + "\n\n" + title,
852 ["◄ Previous", "▲ Exit", "Next ►"]
853 + llList2List(entries, offset-3, offset-1)
854 + llList2List(entries, offset-6, offset-4)
855 + llList2List(entries, offset-9, offset-7),
856 chan);
857 }
858 else
859 {
860 llDialog(id, version + " - " + mn + "\n\n" + title,
861 llList2List(entries, -2, -1) + ["▲ Exit"]
862 + llList2List(entries, -5, -3)
863 + llList2List(entries, -8, -6)
864 + llList2List(entries, -11, -9),
865 chan);
866 }
867 saveMuser(id, menu, llDumpList2String(entries, "|"), page, "-1");
868 }
869 }
870 else if ("" != menu)
871 {
872 d("'" + menu + "' menu not found!");
873 if (DEBUG)
874 dumpMenus(Menus);
875 }
876 else
877 {
878 llListenRemove(llList2Integer(Musers, f + uLSTN));
879 Musers = llListReplaceList(Musers, [], f, f + uSTRIDE - 1);
880 }
881 }
882}
883
884// return TRUE if caller should showMenu()
885integer handleMenu(integer f, key id, string button)
886{
887 string cmd = button;
888 string data;
889 string menu = llList2String(Musers, f + uCURRENT);
890 string dM;
891 string fr;
892 list entries = llParseStringKeepNulls(llList2String(Musers, f + uENTRIES), ["|"], []);
893 list lst;
894 integer page = llList2Integer(Musers, f + uPAGE);
895 integer m = findMenu(menu); // This was already checked before it was stuffed into Musers.
896 integer e = 0;
897
898//d("handleMenu(" + llKey2Name(id) + "," + button + ") " + menu + " -> " + cmd);
899 if (llList2Integer(Menus, m + mDYN))
900 dM = menu;
901 // Find the corresponding command for this button.
902 lst = llParseStringKeepNulls(llList2String(Menus, m + mCMDS), ["|"], []);
903 if (("◄ Previous" != button) && ("▲ Exit" != button) && ("Next ►" != button))
904 {
905 if (llGetListLength(lst) != 1)
906 e = llListFindList(entries, [button]);
907 if (-1 == e)
908 D("handleMenu() button |" + button + "| not found, OOPS! Was looking in -\n"
909 + llList2String(Musers, f + uENTRIES));
910 else
911 cmd = llList2String(lst, e);
912 }
913
914 f = listFindString(Aliases, llToLower(cmd), 2);
915 if (-1 != f)
916 cmd = llList2String(Aliases, f + 1);
917 f = llSubStringIndex(cmd, "=");
918 if (-1 != f)
919 {
920 data = llGetSubString(cmd, f + 1, -1);
921 cmd = llGetSubString(cmd, 0, f - 1);
922 }
923 else
924 {
925 f = llSubStringIndex(cmd, " ");
926 if (-1 != f)
927 {
928 data = llGetSubString(cmd, f + 1, -1);
929 cmd = llGetSubString(cmd, 0, f - 1);
930 }
931 }
932
933 f = llSubStringIndex(cmd, ".");
934 if (-1 != f)
935 {
936 fr = llGetSubString(cmd, 0, f);
937 cmd = llGetSubString(cmd, f + 1, -1);
938 }
939 else
940 {
941 f = llSubStringIndex(menu, ".");
942 if (-1 != f)
943 {
944 fr = llGetSubString(menu, 0, f);
945 f = llSubStringIndex(fr, "@");
946 if (-1 != f)
947 fr = llGetSubString(fr, f + 1, -1);
948 }
949 }
950 cmd = llToUpper(llStringTrim(cmd, STRING_TRIM));
951 data = llStringTrim(data, STRING_TRIM);
952
953 if (!access(id, menu + " menu", fr, llList2Integer(Menus, m + mAUTH), TRUE))
954 return FALSE;
955
956 string first = llGetSubString(button, 0, 0);
957 string last = llGetSubString(button, -1, -1);
958
959d("handleMenu(" + llKey2Name(id) + "," + button + ") " + menu + " -> " + fr + cmd + " -> " + data);
960
961 // Check if it's a special menu item type.
962 // If this is a TOMENU entry and there was no command
963 if (("…" == last) && (" " == llList2String(lst, e)))
964 {
965 data = llGetSubString(button, 0, -2);
966 f = findMenu(fr + data);
967 if (-1 != f)
968 saveMuser(id, fr + data, "", -1, menu);
969 }
970 else if ("▲" == first)
971 {
972 f = doThing(id, menu + "->" + button, fr, cmd, data, fMENU);
973 lastMenu(id, dM);
974 return f;
975 }
976 else if ("◄" == first)
977 {
978 --page;
979 if (0 > page)
980 page = llGetListLength(entries) / 9;
981 saveMuser(id, menu, "", page, "");
982 }
983 else if ("►" == last)
984 {
985 ++page;
986 if (llGetListLength(entries) < ((page - 1) * 9))
987 page = 1;
988 saveMuser(id, menu, "", page, "");
989 }
990 else if (("☐" == first) || ("▣" == first) || ("○" == first) || ("◉" == first))
991 {
992 toggleMenu(id, fr, entries, e, m);
993 return FALSE;
994 }
995 else if ("MENU" == cmd)
996 {
997 if ("" == getFor(data))
998 data = fr + data;
999 saveMuser(id, data, "", -1, menu);
1000 }
1001 else
1002 {
1003 integer i = TRUE;
1004 f = listFindString(Commands, fr + cmd, cSTRIDE);
1005 if (-1 != f)
1006 i = doThing(id, menu + "->" + button, fr, cmd, data, fMENU);
1007 if ("" != dM)
1008 lastMenu(id, dM);
1009 return i;
1010 }
1011
1012 return TRUE;
1013}
1014
1015integer doThing(key id, string command, string fr, string cmd, string data, integer source)
1016{
1017 if ("*.." == fr) return TRUE;
1018 // Coz the card reader is about to send the lot anyway.
1019 if (fCARD == source) return TRUE;
1020 key them = getSetting(fr + "SCRIPTKEY");
1021//d("doThing(" + id + "," + command + " -> " + fr + " . " + cmd + " |" + data + "|");
1022
1023 if (("" == them) && ("*." != fr))
1024 D("Client script " + llGetSubString(fr, 0, -1) + " not found!\ndoThing("
1025 + id + " does " + command + " - " + fr + " " + cmd + " | " + data + ")");
1026 else
1027 {
1028 integer f = listFindString(Commands, fr + cmd, cSTRIDE);
1029 if (-1 != f)
1030 {
1031 if (0 == access(id, cmd + " command", fr, llList2Integer(Commands, f + cAUTH), TRUE))
1032 return TRUE;
1033 }
1034
1035 if ("*." == fr)
1036 them = NULL_KEY;
1037 if ("TIMER" == cmd)
1038 {
1039 list dt = llParseString2List(data, [" "], []);
1040 float time = llList2Float(dt, 0);
1041 dt = llListReplaceList(dt, [], 0, 0);
1042 addEvent(time, fr + llDumpList2String(dt, " "), id);
1043 }
1044 else
1045 {
1046 sendScript(them, lCMD, fr, [source, id, command, cmd, data]);
1047 return FALSE;
1048 }
1049 }
1050 return TRUE;
1051}
1052
1053// TODO - Could use this from the various "save*()" functions?
1054list saveThing(list in, integer stride, list add, integer l, string fr)
1055{
1056 if (l == stride)
1057 {
1058 string first = llList2String(add, 0);
1059
1060 if ("" != fr)
1061 {
1062 first = fr + first;
1063 add = llListReplaceList(add, [first], 0, 0);
1064 }
1065
1066 integer f = listFindString(in, first, stride);
1067
1068 if (-1 == f)
1069 in += add;
1070 else
1071 in = llListReplaceList(in, add, f, f + stride - 1);
1072 }
1073 else
1074 D("Wrong number of arguments, should be " + stride + " but is " + l + "!\n"
1075 + llDumpList2String(add, " ~ "));
1076
1077 return in;
1078}
1079
1080readTheme(string card)
1081{
1082 float now = llGetTimeOfDay();
1083 string cd = card;
1084
1085 card = "~" + cd + ".alias.data";
1086 if (NULL_KEY != llGetInventoryKey(card))
1087 Aliases += llList2List(llParseStringKeepNulls(osGetNotecard(card), ["\n"], []), 0, -2);
1088 card = "~" + cd + ".command.data";
1089 if (NULL_KEY != llGetInventoryKey(card))
1090 Commands += llList2List(llParseStringKeepNulls(osGetNotecard(card), ["\n"], []), 0, -2);
1091 card = "~" + cd + ".setting.data";
1092 if (NULL_KEY != llGetInventoryKey(card))
1093 Settings += llList2List(llParseStringKeepNulls(osGetNotecard(card), ["\n"], []), 0, -2);
1094// s("Read " + cd + " data in " + (string) (llGetTimeOfDay() - now) + " seconds.");
1095
1096 now = llGetTimeOfDay();
1097 card = "." + cd + ".theme";
1098 if (NULL_KEY != llGetInventoryKey(card))
1099 {
1100 list data = llParseString2List(osGetNotecard(card), ["\n", ";"], []);
1101 integer l = llGetListLength(data);
1102 integer i;
1103 string fr;
1104 string menu;
1105
1106 for (i = 0; i < l; ++i)
1107 {
1108 string line = llStringTrim(llList2String(
1109 llParseStringKeepNulls(llList2String(data, i), ["//", "#"], []), 0), STRING_TRIM);
1110 if ("" != line)
1111 {
1112 list args = llParseStringKeepNulls(line, ["|", ","], []);
1113 list first;
1114 integer m = llGetListLength(args);
1115 integer j;
1116
1117 for (j = 0; j < m; ++j)
1118 first += [llStringTrim(llList2String(args, j), STRING_TRIM)];
1119 args = first;
1120 first = llParseStringKeepNulls(llList2String(args, 0), [" ", "="], []);
1121
1122 string cmd = llToUpper(llStringTrim(llList2String(first, 0), STRING_TRIM));
1123
1124 first = llListReplaceList(first, [], 0, 0);
1125 args = [llDumpList2String(first, " ")] + llListReplaceList(args, [], 0, 0);
1126 m = llGetListLength(args);
1127
1128 if ("ALIAS" == cmd)
1129 {
1130 args = llListReplaceList(args, [llToLower(llList2String(args, 0))], 0, 0);
1131 Aliases = saveThing(Aliases, aSTRIDE, args, m, fr);
1132 }
1133 else if ("COMMAND" == cmd)
1134 Commands = saveThing(Commands, cSTRIDE,
1135 llListReplaceList(args, [decodeAccess(llList2String(args, 2))], cAUTH, cAUTH),
1136 m, fr);
1137 else if ("SETTING" == cmd)
1138 {
1139// TODO - should do isBool() here if needed.
1140 Settings = saveThing(Settings, sSTRIDE,
1141 llListReplaceList(args, [decodeAccess(llList2String(args, 3))], sAUTH, sAUTH),
1142 m, fr);
1143 }
1144 else if ("FOR" == cmd)
1145 {
1146 fr = llList2String(args, 0) + ".";
1147 menu = "";
1148 }
1149 else if ("MAIN" == cmd)
1150 MainMenu = fr + llList2String(args, 0);
1151 else if ("MENU" == cmd)
1152 {
1153 menu = llList2String(args, 0);
1154 if ("" != fr) menu = fr + menu;
1155 saveMenu(menu, llList2String(args, 1), decodeAccess(llList2String(args, 2)), "", "", FALSE);
1156 }
1157 else if (("BUTTON" == cmd) || ("TOGGLE" == cmd) || ("TOMENU" == cmd))
1158 addMenuItem(fr, menu, cmd, args);
1159 else
1160 addMenuItem(fr, menu, cmd, [llList2String(args, 0), cmd + " " + llDumpList2String(args, ",")]);
1161 }
1162 }
1163 d("Read " + card + " in " + (string) (llGetTimeOfDay() - now) + " seconds.");
1164 }
1165 if ("" == MainMenu)
1166 MainMenu = llList2String(Menus, 0);
1167}
1168
1169list wipe(list o, string fr, integer stride)
1170{
1171 return wipe(o, fr, stride, 0);
1172}
1173
1174list wipe(list o, string fr, integer stride, integer f)
1175{
1176 integer l = llGetListLength(o);
1177 integer i;
1178 list n;
1179
1180 for (i = 0; i < l; i += stride)
1181 {
1182 if ((fr + ".") != getFor(llList2String(o, i + f)))
1183 n += llList2List(o, i, i + stride - 1);
1184 }
1185 return n;
1186}
1187
1188key URLrequestID;
1189key URLCheckrequestID;
1190integer Attached;
1191float VelTime;
1192vector VelPos;
1193init()
1194{
1195 Attached = (0 != llGetAttached());
1196 if (Attached)
1197 {
1198 integer i = llGetInventoryNumber(INVENTORY_SCRIPT);
1199 while (i-- > 0)
1200 {
1201 string s = llGetInventoryName(INVENTORY_SCRIPT, i);
1202 if (osGetInventoryDesc(s) == "1chatter client")
1203 {
1204 d("RESETTING " + s);
1205 llResetOtherScript(s);
1206 }
1207 }
1208 }
1209 llListen(DEBUG_CHANNEL, "", NULL_KEY, "");
1210 VelTime = llGetTimeOfDay();
1211 VelPos = llGetPos();
1212 addEvent(300, "Musers", ScriptKey);
1213 URLrequestID = llRequestURL();
1214}
1215
1216default
1217{
1218 state_entry()
1219 {
1220 Start = llGetTimeOfDay();
1221 Owner = llGetOwner();
1222d("\n\n1chatter resetting client scripts @ " + (string) Start + "\n");
1223 ScriptName = llGetScriptName(); ScriptKey = llGetInventoryKey(ScriptName);
1224 LibraryKey = ScriptKey;
1225 init();
1226 }
1227
1228 on_rez(integer param)
1229 {
1230// TODO - should clear any listeners first.
1231 Musers = [];
1232 }
1233
1234 changed(integer change)
1235 {
1236// if (change & CHANGED_INVENTORY)
1237// llResetScript();
1238 if (change & CHANGED_OWNER)
1239 llResetScript();
1240 if (change & CHANGED_REGION)
1241 {
1242 llReleaseURL(URL);
1243 URL = "";
1244 URLrequestID = llRequestURL();
1245 }
1246 }
1247
1248 dataserver(key id, string data)
1249 {
1250 list input = llParseStringKeepNulls(data, [lSEP], []);
1251 string fr = llList2String(input, 0);
1252 string cmd = llList2String(input, 1);
1253
1254 if ("PIN" == fr)
1255 {
1256 fr = "*";
1257fr="1AOor2";
1258 cmd = "PIN";
1259 input = [fr, cmd, llList2String(input, 1)];
1260 }
1261 else
1262 id = llGetOwnerKey(id);
1263 doThing(id, llDumpList2String(input, "|"), fr + ".", cmd, llDumpList2String(llList2List(input, 2, -1), ","), fOSM);
1264 }
1265
1266 http_request(key id, string method, string body)
1267 {
1268 integer responseStatus = 400;
1269 string responseBody = "Unsupported method";
1270//d("HTTP " + method + " -> " + body);
1271 if (method == URL_REQUEST_DENIED)
1272 d("Error trying to get a URL - " + body);
1273 else if (method == URL_REQUEST_GRANTED)
1274 {
1275 URLrequestID = NULL_KEY;
1276 URL = body;
1277 llSleep(0.01); // The other scripts wont have recovered from the TP yet.
1278 sendScript(NULL_KEY, lCMD, "*.", [fHTTP, ScriptKey, method, "URL", URL]);
1279 // Check for dropped URL.
1280// llSetTimerEvent(30.0);
1281 }
1282 else if (method == "POST")
1283 {
1284 list bdy = llParseStringKeepNulls(llUnescapeURL(body), ["|"], []);
1285 sendScript(NULL_KEY, lCMD, "*.", [fHTTP, ScriptKey, method, llList2String(bdy, 0), body]);
1286// responseBody = llEscapeURL("|REGION" + llGetRegionName() + "|" + (string)llGetPos());
1287 responseStatus = 200;
1288 responseBody = "";
1289 llHTTPResponse(id, responseStatus, responseBody);
1290 }
1291 }
1292
1293 http_response(key id, integer status, list metaData, string body)
1294 {
1295 if (id == URLCheckrequestID)
1296 {
1297 URLCheckrequestID = NULL_KEY;
1298 if (status != 200)
1299 d("HTTP error code " + status + "\n" + body);
1300// else
1301// llOwnerSay("self check worked - " + body);
1302 }
1303 else if (id == NULL_KEY)
1304 d("Too many HTTP requests too fast!");
1305 }
1306
1307 // Handle commands from other scripts.
1308 link_message(integer sender_num, integer num, string message, key id)
1309 {
1310 if ((id != ScriptKey) && (id != NULL_KEY)) return;
1311 list input = llParseStringKeepNulls(message, [lSEP], []);
1312 key them = llList2Key(input, 0);
1313 string fr = llList2Key(input, 1);
1314
1315//d("linky " + num + " " + message + " " + fr);
1316 if (lRESET == num)
1317 {
1318 if ("" != fr)
1319 {
1320 Aliases = wipe(Aliases, fr, aSTRIDE);
1321 Commands = wipe(Commands, fr, cSTRIDE);
1322 Events = wipe(Events, fr, eSTRIDE, eSCRIPT);
1323 Menus = wipe(Menus, fr, mSTRIDE);
1324 Musers = wipe(Musers, fr, uSTRIDE, uSTACK);
1325 Settings = wipe(Settings, fr, sSTRIDE);
1326 readTheme(fr);
1327 Settings = saveThing(Settings, sSTRIDE, ["PREFIX", "S", fr, aALL], 4, fr + ".");
1328 Settings = saveThing(Settings, sSTRIDE, ["SCRIPTKEY", "K", them, aALL], 4, fr + ".");
1329 Settings = saveThing(Settings, sSTRIDE, ["VERSION", "S", "", aALL], 4, fr + ".");
1330 sendScript(them, lALIAS_DONE, Aliases);
1331 sendScript(them, lRESET_DONE, Settings);
1332 sendScript(them, lCMD, "*.", [fHTTP, ScriptKey, URL_REQUEST_GRANTED, "URL", URL]);
1333 }
1334// resetPrimShit();
1335 }
1336 else if (lSETTINGS == num)
1337 {
1338 if ("" != fr)
1339 {
1340 readSettings(fr);
1341 sendScript(them, lSETTINGS_DONE, Settings);
1342 }
1343 }
1344 else if (lSETTING == num)
1345 {
1346 if (id == ScriptKey)
1347 {
1348 integer source = llList2Integer(input, 2);
1349 key a = llList2Key(input, 3);
1350
1351 if ("" != fr)
1352 {
1353 integer l = llGetListLength(input);
1354 integer i;
1355
1356 for (i = 4; i < l; i += 2)
1357 {
1358 setSetting(a, fr + "." + llList2String(input, i),
1359 llList2String(input, i + 1), source);
1360 }
1361// TODO - track the listen handle, delete old one here.
1362 integer channel = (integer) getSetting("1ring.CHANNEL");
1363 if (channel)
1364 llListen(channel, "", "", "");
1365//dumpSettings(Settings, "SETTING");
1366 }
1367 }
1368 }
1369 else if (lCMD == num)
1370 {
1371 doThing(llList2Key(input, 3), llList2String(input, 4), fr + ".",
1372 llList2String(input, 5), llList2String(input, 6), llList2Integer(input, 2));
1373 }
1374 else if (lCMD_DONE == num)
1375 {
1376//d("lCMD_DONE " + llKey2Name(llList2Key(input, 3)) + " -> " + llList2String(input, 2) + "| returned " + llList2String(input, 4));
1377 if (0 != llList2Integer(input, 4))
1378 showMenu(llList2Key(input, 3));
1379 }
1380 else if (lDYNAMIC == num)
1381 {
1382 string menu = llList2String(input, 3);
1383 if ("" != menu)
1384 menu = fr + "." + menu;
1385 dynamicMenu(llList2Key(input, 2), menu, fr + "." + llList2String(input, 4),
1386 llList2String(input, 5), llList2String(input, 6), llList2String(input, 7));
1387 }
1388 else if (lMENU == num)
1389 {
1390//d("lMENU " + llKey2Name(llList2Key(input, 2)) + " -> " + fr + "." + llList2String(input, 3) + " " + llList2String(input, 4));
1391 key a = llList2String(input, 2);
1392 string mn = llList2String(input, 3);
1393 string t = llList2String(input, 4);
1394 string menu;
1395 integer f = listFindString(Musers, a, uSTRIDE);
1396 integer m = findMenu(fr + "." + mn);
1397
1398 if (-1 != f)
1399 menu = llList2String(Musers, f + uCURRENT);
1400 if (-1 != m)
1401 {
1402 if ("" != t)
1403 Menus = llListReplaceList(Menus, t, m + mTITLE, m + mTITLE);
1404 if ((fr + "." + mn) == menu)
1405 menu = "-1";
1406 saveMuser(a, fr + "." + mn, "", -1, menu);
1407 showMenu(a);
1408 }
1409 }
1410 else if (lSCAN == num)
1411 {
1412d("lSCAN " + llDumpList2String(input, " ~ "));
1413 key a = llList2Key(input, 2);
1414 integer f = listFindString(Musers, a, uSTRIDE);
1415
1416 if (-1 != f)
1417 {
1418 Scanners += [llList2String(Musers, f + uCURRENT), a] + llList2List(input, 3, -1);
1419 if (nSTRIDE == llGetListLength(Scanners))
1420 llSensor("", NULL_KEY, llList2Integer(input, 3), 100.0, PI);
1421 }
1422 }
1423 }
1424
1425 listen(integer channel, string name, key id, string message)
1426 {
1427//d("listen " + channel + ", " + llKey2Name(id) + "-> " + message + " " + llGetListLength(Musers));
1428 // See if it's a prefix on the prefix channel, strip off the prefix and feed the remains to doThing().
1429 if (channel == (integer) getSetting("1ring.CHANNEL"))
1430 {
1431d("listen 0ring.CHANNEL " + message);
1432 string p = getSetting("1ring.PREFIX");
1433 if (llGetSubString(message, 0, llStringLength(p) - 1) == p)
1434 {
1435 string button = llGetSubString(message, llStringLength(p), -1);
1436 list lst = llParseStringKeepNulls(llStringTrim(button, STRING_TRIM), [" "], []);
1437 string cmd = llList2String(lst, 0);
1438 string data = llDumpList2String(llListReplaceList(lst, [], 1, 1), " ");
1439d("listen 1ring.CHANNEL " + message + " -> " + button + " " + cmd + " " + data);
1440 doThing(id, message, "1ring.", cmd, data, fCHAT);
1441 return;
1442 }
1443 }
1444
1445 integer l = llGetListLength(Musers);
1446 integer i;
1447
1448 for (i = 0; i < l; i += uSTRIDE)
1449 {
1450 if (channel == llList2Integer(Musers, i + uCHAN))
1451 {
1452 if (handleMenu(i, id, message)) showMenu(id);
1453 return;
1454 }
1455 }
1456
1457 // Catch OhSillyThreatLevel messages.
1458 if (channel == DEBUG_CHANNEL)
1459 {
1460 key root = llList2Key(llGetObjectDetails(id, [OBJECT_ROOT]), 0);
1461 if ((llGetLinkKey(LINK_ROOT) == root) && ("OSSL Runtime Error: os" == llGetSubString(message, 0, 21)))
1462 {
1463 string function = llGetSubString(message, 20, -1);
1464 integer f = llSubStringIndex(function, " ");
1465 if (-1 != f)
1466 {
1467 if (" permission denied. Script creator is not in the list of users allowed to execute this function and prim owner also has no permission." == llGetSubString(function, f, -1))
1468 {
1469 llMessageLinked(LINK_SET, DEBUG_CHANNEL, llGetSubString(function, 0, f - 1), id);
1470 llRegionSay(DEBUG_CHANNEL, "OPENSIM SUCKS!");
1471 return;
1472 }
1473 }
1474 }
1475 }
1476 }
1477
1478 no_sensor()
1479 {
1480 if (0 != llGetListLength(Scanners))
1481 {
1482 key id = llList2Key(Scanners, nID);
1483 Scanners = llListReplaceList(Scanners, [], 0, nSTRIDE - 1);
1484 if (0 != llGetListLength(Scanners))
1485 llSensor("", NULL_KEY, llList2Integer(Scanners, nTYPE), 100.0, PI);
1486 showMenu(id);
1487 }
1488 }
1489
1490 sensor(integer num)
1491 {
1492 if (0 != llGetListLength(Scanners))
1493 {
1494 string cmd = llList2String(Scanners, nCMD);
1495 list items = [];
1496 list keys = [];
1497 key id = llList2Key(Scanners, nID);
1498 string menu = llList2String(Scanners, nMENU);
1499 integer i;
1500 for (i = 0; i < num; ++i)
1501 {
1502 items += [llGetSubString(llDetectedName(i), 0, 23)];
1503 keys += [cmd + " " + llDetectedKey(i)];
1504 }
1505 Scanners = llListReplaceList(Scanners, [], 0, nSTRIDE - 1);
1506 if (0 != llGetListLength(Scanners))
1507 llSensor("", NULL_KEY, llList2Integer(Scanners, nTYPE), 100.0, PI);
1508 dynamicMenu(id, menu, menu,
1509 llList2String(Scanners, nTITLE),
1510 llDumpList2String(items, "|"), llDumpList2String(keys, "|"));
1511 }
1512 }
1513
1514 timer()
1515 {
1516 float now = llGetTimeOfDay();
1517 integer i;
1518
1519 while (NextEvent <= now)
1520 {
1521 string script = llList2String(Events, eSCRIPT);
1522 key them = llList2Key(Events, eKEY);
1523
1524 Events = llListReplaceList(Events, [], 0, eSTRIDE - 1);
1525 if (0 != llGetListLength(Events))
1526 NextEvent = llList2Float(Events, 0);
1527 else
1528 NextEvent = 307584000.0;
1529 now = llGetTimeOfDay();
1530
1531 if ("Musers" == script)
1532 {
1533 integer f = llGetListLength(Musers);
1534 list t = [];
1535
1536 for (i = 0; i < f; i += uSTRIDE)
1537 {
1538 key a = llList2Key(Musers, i + uKEY);
1539
1540 if (ZERO_VECTOR != llGetAgentSize(a))
1541 {
1542 if (now < (llList2Float(Musers, i + uTIME) + 300.0))
1543 t += llList2List(Musers, i, i + uSTRIDE - 1);
1544 else
1545 {
1546 s(a, "You have taken too long to respond to the menu, it's disabled now.");
1547 a = NULL_KEY;
1548 }
1549 }
1550 else
1551 a = NULL_KEY;
1552 if (NULL_KEY == a) // Remove those no longer in the sim, or menu timeouts.
1553 llListenRemove(llList2Integer(Musers, i + uLSTN));
1554 }
1555 Musers = t;
1556 addEvent(300, script, them);
1557 }
1558 else
1559 {
1560 list t = llParseStringKeepNulls(script, ["."], []);
1561 string fr = llList2Key(t, 0);
1562 list dt = llParseString2List(llList2String(t, 1), [" "], []);
1563 string cmd = llList2String(dt, 0);
1564 dt = llListReplaceList(dt, [], 0, 0);
1565//d("timer " + script + "->" + fr + " ... " + cmd + "=" + llDumpList2String(dt, " "));
1566 sendScript(them, lCMD, fr + ".", [fINT, them, "TimerEvent", cmd, llDumpList2String(dt, " ")]);
1567 }
1568 }
1569 llSetTimerEvent(NextEvent - now);
1570 now = llGetTimeOfDay();
1571 float time = now - VelTime;
1572 vector vp = llGetPos();
1573 float dist = llVecDist(VelPos, vp);
1574 float speed = dist / time;
1575 VelTime = now;
1576 VelPos = vp;
1577// llSetText("Velocity " + (string) speed + " m/s\n@ " + (string) now, <1.0, 1.0, 1.0>, 1.0);
1578 llSetText("", <1.0, 1.0, 1.0>, 1.0);
1579 }
1580
1581 touch_start(integer num)
1582 {
1583 for (--num; 0 <= num; --num)
1584 {
1585 key id = llDetectedKey(num);
1586 string menu = MainMenu;
1587 list v = validateName(menu, "menu");
1588 string fr = llList2String(v, 0);
1589 menu = fr + llList2String(v, 1);
1590 if (access(id, menu + " menu", fr, llList2Integer(Menus, 0 + mAUTH), TRUE))
1591 {
1592 saveMuser(id, menu, "", -1, "");
1593 showMenu(id);
1594 }
1595 }
1596 }
1597}
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}
diff --git a/OhSillyThreatDetector.lsl b/OhSillyThreatDetector.lsl
new file mode 100644
index 0000000..4f17ed6
--- /dev/null
+++ b/OhSillyThreatDetector.lsl
@@ -0,0 +1,59 @@
1
2// Detect when OpenSim reports a function was used that triggered a threat level warning.
3// Coz their official method doesn't work, and often their threat levels are set way too high.
4
5// This script will send a link message with number DEBUG_CHANNEL, message being the name of the naughty function,
6// and key of the prim containing the naughty script. It's up to the naughty script to catch this link message
7// and do something less naughty.
8
9// Since it sniffs on DEBUG_CHANNEL messages, and a script can't hear any message sent by scripts in the same prim,
10// you'll need to put this script in a prim without the naughty scripts you want to check.
11
12default
13{
14 state_entry()
15 {
16// llSay(DEBUG_CHANNEL, "G'day, I'm " + llGetKey() + " part of " + llGetLinkKey(LINK_ROOT));
17 llListen(DEBUG_CHANNEL, "", NULL_KEY, "");
18 }
19
20 listen(integer channel, string name, key id, string message)
21 {
22 key root = llList2Key(llGetObjectDetails(id, [OBJECT_ROOT]), 0);
23 // name is the name of the prim, id is the UUID of the prim.
24 // Reports the name and UUID of the prim itself, not the object it's part of.
25 // Naturally can't detect debugs from our own prim, even if it's a different script.
26 // Works from another prim in the same object.
27
28 /* Threat level errors look like this (at least in OpenSim 0.8.2) -
29OSSL Runtime Error: osSetStateEvents permission denied. Script creator is not in the list of users allowed to execute this function and prim owner also has no permission.
30
31There is also (OpenSim 0.9 at least) -
32Max Zephyr script
33OSSL Runtime Error: permission denied. All OS functions are disabled.(script: oc_settings event: changed primID:487baa88-d6e5-420c-b18c-f47f5bbd7de4 at <127.9408, 128.0009, 25.75689>)
34
35My script
36OSSL Runtime Error: osSetSpeed permission denied. All OS functions are disabled.(script: 1AOor2 event: changed primID:c7db8d3c-ae33-44ce-a685-cf63a37f0cf5 at <123.933, 203.0375, 24.29473>)
37
38From the source (NOTE: first version has no function name) -
39("{0} permission denied. All OS functions are disabled.")
40("{0} permission denied. All OS functions are disabled.", function)
41("{0} permission denied. Allowed threat level is {1} but function threat level is {2}.", function,
42("{0} permission denied. Script creator is not in the list of users allowed to execute this function and prim owner also has no permission.", function
43("{0} permission denied. Script permissions error.", function
44
45OpenSim 0.9.0.1 doesn't include the "OSSL Runtime Error: " bit at the beginning anymore.
46OpenSim 0.9.2 seems to not even send it to the DEBUG_CHANNEL for scripts anymore.
47 */
48 if ((llGetLinkKey(LINK_ROOT) == root) && ("OSSL Runtime Error: " == llGetSubString(message, 0, 19)))
49 {
50 list e = llParseStringKeepNulls(llGetSubString(message, 20, -1), [" "], []);
51//llOwnerSay(llList2String(e, 0));
52 if (("permission" == llList2String(e, 1)) && ("denied" == llList2String(e, 2)))
53 {
54 string function = llList2String(e, 0);
55 llMessageLinked(LINK_SET, DEBUG_CHANNEL, function, id);
56 }
57 }
58 }
59}
diff --git a/onefang's leash holder.lsl b/onefang's leash holder.lsl
new file mode 100644
index 0000000..6d7b360
--- /dev/null
+++ b/onefang's leash holder.lsl
@@ -0,0 +1,44 @@
1integer mychannel = -8888;
2integer particle_chan = -270510;
3string listenfor;
4string response;
5string detach;
6
7key masterid;
8key handle;
9
10default
11{
12 state_entry()
13 {
14 masterid = llGetOwner();
15 handle = llGetKey();
16 listenfor = (string)masterid + "handle";
17 response = (string)masterid + "handle ok";
18 detach = (string)masterid + "handle detached";
19 llListen(mychannel, "", NULL_KEY, listenfor);
20 llSay(mychannel, response);
21 }
22
23 attach(key id)
24 {
25 if (NULL_KEY == id)
26 llSay(mychannel, detach);
27 }
28
29 listen(integer channel, string name, key id, string message)
30 {
31 llSay(mychannel, response);
32 llSay(particle_chan, handle + "#" + masterid);
33 }
34
35 on_rez(integer param)
36 {
37 llResetScript();
38 }
39
40 touch_start(integer total_number)
41 {
42 llSay(particle_chan, handle + "#" + masterid);
43 }
44}