aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/1AOor2.lsl
diff options
context:
space:
mode:
authoronefang2019-06-30 11:16:41 +1000
committeronefang2019-06-30 11:16:41 +1000
commit1e5b49a55b7ebd03c6e57be993668f38ac20f841 (patch)
tree5cabe2fdc2e51e46bcb6f5ca5ce5110b5be3bdb0 /1AOor2.lsl
parentLine wrapping is a thing, but not in notecards. (diff)
download1ring-1e5b49a55b7ebd03c6e57be993668f38ac20f841.zip
1ring-1e5b49a55b7ebd03c6e57be993668f38ac20f841.tar.gz
1ring-1e5b49a55b7ebd03c6e57be993668f38ac20f841.tar.bz2
1ring-1e5b49a55b7ebd03c6e57be993668f38ac20f841.tar.xz
Include the actual source code this time.
Diffstat (limited to '1AOor2.lsl')
-rw-r--r--1AOor2.lsl1835
1 files changed, 1835 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}