corrade-lsl-templates – Blame information for rev 42

Subversion Repositories:
Rev:
Rev Author Line No. Line
2 office 1 ///////////////////////////////////////////////////////////////////////////
29 office 2 // Copyright (C) Wizardry and Steamworks 2016 - License: CC BY 2.0 //
2 office 3 ///////////////////////////////////////////////////////////////////////////
4 //
5 // This script is used to make Corrade sit on a primitive. The script first
6 // uses a Corrade command to scan the surrounding area for primitives and
7 // then sends a command to make Corrade sit on that primitive.
8 //
9 // For more information on Corrade, please see:
10 // http://grimore.org/secondlife/scripted_agents/corrade
11 //
12 ///////////////////////////////////////////////////////////////////////////
13  
14 ///////////////////////////////////////////////////////////////////////////
29 office 15 // Copyright (C) 2014 Wizardry and Steamworks - License: CC BY 2.0 //
2 office 16 ///////////////////////////////////////////////////////////////////////////
17 string wasKeyValueGet(string k, string data) {
18 if(llStringLength(data) == 0) return "";
19 if(llStringLength(k) == 0) return "";
20 list a = llParseString2List(data, ["&", "="], []);
21 integer i = llListFindList(a, [ k ]);
22 if(i != -1) return llList2String(a, i+1);
23 return "";
24 }
25  
26 ///////////////////////////////////////////////////////////////////////////
29 office 27 // Copyright (C) 2013 Wizardry and Steamworks - License: CC BY 2.0 //
2 office 28 ///////////////////////////////////////////////////////////////////////////
29 string wasKeyValueEncode(list data) {
30 list k = llList2ListStrided(data, 0, -1, 2);
31 list v = llList2ListStrided(llDeleteSubList(data, 0, 0), 0, -1, 2);
32 data = [];
33 do {
34 data += llList2String(k, 0) + "=" + llList2String(v, 0);
35 k = llDeleteSubList(k, 0, 0);
36 v = llDeleteSubList(v, 0, 0);
37 } while(llGetListLength(k) != 0);
38 return llDumpList2String(data, "&");
39 }
40  
41 ///////////////////////////////////////////////////////////////////////////
29 office 42 // Copyright (C) 2011 Wizardry and Steamworks - License: CC BY 2.0 //
2 office 43 ///////////////////////////////////////////////////////////////////////////
44 // http://was.fm/secondlife/wanderer
45 vector wasCirclePoint(float radius) {
46 float x = llPow(-1, 1 + (integer) llFrand(2)) * llFrand(radius*2);
47 float y = llPow(-1, 1 + (integer) llFrand(2)) * llFrand(radius*2);
48 if(llPow(x,2) + llPow(y,2) <= llPow(radius,2))
49 return <x, y, 0>;
50 return wasCirclePoint(radius);
51 }
52  
53 ///////////////////////////////////////////////////////////////////////////
29 office 54 // Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 //
2 office 55 ///////////////////////////////////////////////////////////////////////////
56 // escapes a string in conformance with RFC1738
57 string wasURLEscape(string i) {
58 string o = "";
59 do {
60 string c = llGetSubString(i, 0, 0);
61 i = llDeleteSubString(i, 0, 0);
62 if(c == "") jump continue;
63 if(c == " ") {
64 o += "+";
65 jump continue;
66 }
67 if(c == "\n") {
68 o += "%0D" + llEscapeURL(c);
69 jump continue;
70 }
71 o += llEscapeURL(c);
72 @continue;
73 } while(i != "");
74 return o;
75 }
76  
77 ///////////////////////////////////////////////////////////////////////////
29 office 78 // Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 //
2 office 79 ///////////////////////////////////////////////////////////////////////////
80 list wasCSVToList(string csv) {
81 list l = [];
82 list s = [];
83 string m = "";
84 do {
85 string a = llGetSubString(csv, 0, 0);
86 csv = llDeleteSubString(csv, 0, 0);
87 if(a == ",") {
88 if(llList2String(s, -1) != "\"") {
89 l += m;
90 m = "";
91 jump continue;
92 }
93 m += a;
94 jump continue;
95 }
96 if(a == "\"" && llGetSubString(csv, 0, 0) == a) {
97 m += a;
98 csv = llDeleteSubString(csv, 0, 0);
99 jump continue;
100 }
101 if(a == "\"") {
102 if(llList2String(s, -1) != a) {
103 s += a;
104 jump continue;
105 }
106 s = llDeleteSubList(s, -1, -1);
107 jump continue;
108 }
109 m += a;
110 @continue;
111 } while(csv != "");
112 // postcondition: length(s) = 0
113 return l + m;
114 }
115  
116 ///////////////////////////////////////////////////////////////////////////
29 office 117 // Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 //
2 office 118 ///////////////////////////////////////////////////////////////////////////
119 string wasListToCSV(list l) {
120 list v = [];
121 do {
122 string a = llDumpList2String(
123 llParseStringKeepNulls(
124 llList2String(
125 l,
126  
127 ),
128 ["\""],
129 []
130 ),
131 "\"\""
132 );
133 if(llParseStringKeepNulls(
134 a,
135 [" ", ",", "\n", "\""], []
136 ) !=
137 (list) a
138 ) a = "\"" + a + "\"";
139 v += a;
140 l = llDeleteSubList(l, 0, 0);
141 } while(l != []);
142 return llDumpList2String(v, ",");
143 }
144  
145 ///////////////////////////////////////////////////////////////////////////
29 office 146 // Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 //
2 office 147 ///////////////////////////////////////////////////////////////////////////
148 // unescapes a string in conformance with RFC1738
149 string wasURLUnescape(string i) {
150 return llUnescapeURL(
151 llDumpList2String(
152 llParseString2List(
153 llDumpList2String(
154 llParseString2List(
155 i,
156 ["+"],
157 []
158 ),
159 " "
160 ),
161 ["%0D%0A"],
162 []
163 ),
164 "\n"
165 )
166 );
167 }
168  
169 ///////////////////////////////////////////////////////////////////////////
29 office 170 // Copyright (C) 2013 Wizardry and Steamworks - License: CC BY 2.0 //
2 office 171 ///////////////////////////////////////////////////////////////////////////
172 integer wasMenuIndex = 0;
173 list wasDialogMenu(list input, list actions, string direction) {
174 integer cut = 11-wasListCountExclude(actions, [""]);
175 if(direction == ">" && (wasMenuIndex+1)*cut+wasMenuIndex+1 < llGetListLength(input)) {
176 ++wasMenuIndex;
177 jump slice;
178 }
179 if(direction == "<" && wasMenuIndex-1 >= 0) {
180 --wasMenuIndex;
181 jump slice;
182 }
183 @slice;
184 integer multiple = wasMenuIndex*cut;
185 input = llList2List(input, multiple+wasMenuIndex, multiple+cut+wasMenuIndex);
186 input = wasListMerge(input, actions, "");
187 return input;
188 }
189  
190 ///////////////////////////////////////////////////////////////////////////
29 office 191 // Copyright (C) 2013 Wizardry and Steamworks - License: CC BY 2.0 //
2 office 192 ///////////////////////////////////////////////////////////////////////////
193 integer wasListCountExclude(list input, list exclude) {
194 if(llGetListLength(input) == 0) return 0;
195 if(llListFindList(exclude, (list)llList2String(input, 0)) == -1)
196 return 1 + wasListCountExclude(llDeleteSubList(input, 0, 0), exclude);
197 return wasListCountExclude(llDeleteSubList(input, 0, 0), exclude);
198 }
199  
200 ///////////////////////////////////////////////////////////////////////////
29 office 201 // Copyright (C) 2013 Wizardry and Steamworks - License: CC BY 2.0 //
2 office 202 ///////////////////////////////////////////////////////////////////////////
203 list wasListMerge(list l, list m, string merge) {
204 if(llGetListLength(l) == 0 && llGetListLength(m) == 0) return [];
205 string a = llList2String(m, 0);
206 if(a != merge) return [ a ] + wasListMerge(l, llDeleteSubList(m, 0, 0), merge);
207 return [ llList2String(l, 0) ] + wasListMerge(llDeleteSubList(l, 0, 0), llDeleteSubList(m, 0, 0), merge);
208 }
209  
210 // configuration data
211 string configuration = "";
212 // callback URL
213 string callback = "";
214 // scanned primitives
215 list names = [];
216 list UUIDs = [];
217 // temporary list for button name normalization
218 list menu = [];
219 integer select = -1;
220  
32 office 221  
2 office 222 default {
223 state_entry() {
224 llSetTimerEvent(1);
225 }
226 link_message(integer sender, integer num, string message, key id) {
227 if(sender != 1 || id != "configuration") return;
228 configuration = message;
229 state off;
230 }
231 timer() {
232 llMessageLinked(LINK_ROOT, 0, "configuration", NULL_KEY);
233 }
234 attach(key id) {
235 llResetScript();
236 }
237 on_rez(integer num) {
238 llResetScript();
239 }
240 changed(integer change) {
241 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START) || (change & CHANGED_OWNER)) {
242 llResetScript();
243 }
244 }
245 state_exit() {
246 llSetTimerEvent(0);
247 }
248 }
249  
250 state off {
251 state_entry() {
252 llSetColor(<.5,0,0>, ALL_SIDES);
253 }
254 touch_end(integer num) {
255 state on;
256 }
257 attach(key id) {
258 llResetScript();
259 }
260 on_rez(integer num) {
261 llResetScript();
262 }
263 changed(integer change) {
264 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START) || (change & CHANGED_OWNER)) {
265 llResetScript();
266 }
267 }
268 }
269  
270 state on {
271 state_entry() {
272 llSetColor(<0,.5,0>, ALL_SIDES);
273 state url;
274 }
275 attach(key id) {
276 llResetScript();
277 }
278 on_rez(integer num) {
279 llResetScript();
280 }
281 changed(integer change) {
282 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START) || (change & CHANGED_OWNER)) {
283 llResetScript();
284 }
285 }
286 }
287  
288 state url {
289 state_entry() {
290 // DEBUG
291 llOwnerSay("Requesting URL...");
292 llRequestURL();
293 }
294 touch_end(integer num) {
295 llSetColor(<.5,0,0>, ALL_SIDES);
296 llResetScript();
297 }
298 http_request(key id, string method, string body) {
299 if(method != URL_REQUEST_GRANTED) return;
300 callback = body;
301 // DEBUG
302 llOwnerSay("Got URL...");
303 state scan;
304 }
305 attach(key id) {
306 llResetScript();
307 }
308 on_rez(integer num) {
309 llResetScript();
310 }
311 changed(integer change) {
312 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START) || (change & CHANGED_OWNER)) {
313 llResetScript();
314 }
315 }
316 }
317  
318 state scan {
319 state_entry() {
320 // DEBUG
321 llOwnerSay("Getting objects...");
322 llInstantMessage(
323 wasKeyValueGet(
324 "corrade",
325 configuration
326 ),
327 wasKeyValueEncode(
328 [
329 "command", "getobjectsdata",
330 "group", wasURLEscape(
331 wasKeyValueGet(
332 "group",
333 configuration
334 )
335 ),
336 "password", wasURLEscape(
337 wasKeyValueGet(
338 "password",
339 configuration
340 )
341 ),
342 "entity", "world",
343 "range", wasURLEscape(
344 wasKeyValueGet(
345 "radar",
346 configuration
347 )
348 ),
349 "data", wasListToCSV(
350 [
351 "Properties.Name",
352 "ID"
353 ]
354 ),
355 "sift", wasListToCSV(
356 [
357 "take", 32
358 ]
359 ),
360 "callback", wasURLEscape(callback)
361 ]
362 )
363 );
364 }
365 touch_end(integer num) {
366 llSetColor(<.5,0,0>, ALL_SIDES);
367 llResetScript();
368 }
369 http_request(key id, string method, string body) {
370 llHTTPResponse(id, 200, "OK");
371 if(wasKeyValueGet("command", body) != "getobjectsdata" ||
372 wasKeyValueGet("success", body) != "True") {
373 // DEBUG
374 llOwnerSay("Error querying primitives: " + wasKeyValueGet("error", body));
375 llResetScript();
376 }
377 string dataKey = wasURLUnescape(
378 wasKeyValueGet(
379 "data",
380 body
381 )
382 );
383 if(dataKey == "") {
384 // DEBUG
385 llOwnerSay("No data for scanned primitives...");
386 llResetScript();
387 }
388 list data = wasCSVToList(dataKey);
389 // Copy the names and UUIDs of the primitives.
390 names = [];
391 UUIDs = [];
392 do {
393 string v = llList2String(data, -1);
394 data = llDeleteSubList(data, -1, -1);
395 string k = llList2String(data, -1);
396 data = llDeleteSubList(data, -1, -1);
397 if(k == "Properties.Name") {
32 office 398 // Corrade may pass blank names due to SL
399 // objects not being yet discovered.
400 if(v == "") {
401 names += "Unknown";
402 jump continue;
403 }
2 office 404 names += v;
405 jump continue;
406 }
407 UUIDs += (key)v;
408 @continue;
409 } while(llGetListLength(data));
410 state choose;
411 }
412 attach(key id) {
413 llResetScript();
414 }
415 on_rez(integer num) {
416 llResetScript();
417 }
418 changed(integer change) {
419 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START) || (change & CHANGED_OWNER)) {
420 llResetScript();
421 }
422 }
423 state_exit() {
424 llSetTimerEvent(0);
425 }
426 }
427  
428 state choose {
429 state_entry() {
430 // DEBUG
431 llOwnerSay("Sending menu...");
432 menu = [];
433 integer i = 0;
434 do {
435 menu += llGetSubString(llList2String(names, i), 0, 23);
436 } while(++i < llGetListLength(names));
32 office 437 llOwnerSay("Menu: " + llDumpList2String(menu, ","));
2 office 438 llListen(-10, "", llGetOwner(), "");
439 llDialog(llGetOwner(), "\nPlease choose a primitive for Corrade to sit on from the list below:\n", wasDialogMenu(menu, ["⟵ Back", "", "Next ⟶"], ""), -10);
440 llSetTimerEvent(60);
441 }
442 touch_end(integer num) {
443 llSetColor(<.5,0,0>, ALL_SIDES);
444 llResetScript();
445 }
446 listen(integer channel, string name, key id, string message) {
447 if(message == "⟵ Back") {
448 llDialog(id, "\nPlease choose a primitive for Corrade to sit on from the list below:\n", wasDialogMenu(menu, ["⟵ Back", "", "Next ⟶"], "<"), -10);
449 return;
450 }
451 if(message == "Next ⟶") {
452 llDialog(id, "\nPlease choose a primitive for Corrade to sit on from the list below:\n", wasDialogMenu(menu, ["⟵ Back", "", "Next ⟶"], ">"), -10);
453 return;
454 }
455 integer i = llGetListLength(menu) - 1;
456 do {
457 string v = llList2String(menu, i);
458 if(llSubStringIndex(v, message) != -1)
459 jump sit;
460 } while(--i > -1);
461 // GC
462 menu = [];
463 // DEBUG
464 llOwnerSay("Invalid menu item selected...");
465 llResetScript();
466 @sit;
467 // GC
468 menu = [];
469 select = i;
470 // Got a menu item so bind to permission notifications and sit.
471 state notify;
472  
473 }
474 timer() {
475 // DEBUG
476 llOwnerSay("Dialog menu timeout...");
477 llResetScript();
478 }
479 attach(key id) {
480 llResetScript();
481 }
482 on_rez(integer num) {
483 llResetScript();
484 }
485 changed(integer change) {
486 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START) || (change & CHANGED_OWNER)) {
487 llResetScript();
488 }
489 }
490 state_exit() {
491 llSetTimerEvent(0);
492 }
493 }
494  
495 state notify {
496 state_entry() {
497 // DEBUG
498 llOwnerSay("Binding to the permission Corrade notification...");
499 llInstantMessage(
500 (key)wasKeyValueGet(
501 "corrade",
502 configuration
503 ),
504 wasKeyValueEncode(
505 [
506 "command", "notify",
507 "group", wasURLEscape(
508 wasKeyValueGet(
509 "group",
510 configuration
511 )
512 ),
513 "password", wasURLEscape(
514 wasKeyValueGet(
515 "password",
516 configuration
517 )
518 ),
519 "action", "add",
520 "type", "permission",
521 "URL", wasURLEscape(callback),
522 "callback", wasURLEscape(callback)
523 ]
524 )
525 );
526 llSetTimerEvent(60);
527 }
528 touch_end(integer num) {
529 llSetColor(<.5,0,0>, ALL_SIDES);
530 llResetScript();
531 }
532 http_request(key id, string method, string body) {
533 llHTTPResponse(id, 200, "OK");
534 if(wasKeyValueGet("command", body) != "notify" ||
535 wasKeyValueGet("success", body) != "True") {
536 // DEBUG
537 llOwnerSay("Failed to bind to the permission notification...");
538 llResetScript();
539 }
540 // DEBUG
541 llOwnerSay("Permission notification installed...");
542 state sit;
543 }
544 timer() {
545 // DEBUG
546 llOwnerSay("Timeout binding to permission notification...");
547 llResetScript();
548 }
549 attach(key id) {
550 llResetScript();
551 }
552 on_rez(integer num) {
553 llResetScript();
554 }
555 changed(integer change) {
556 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START) || (change & CHANGED_OWNER)) {
557 llResetScript();
558 }
559 }
560 state_exit() {
561 llSetTimerEvent(0);
562 }
563 }
564  
565 state sit {
566 state_entry() {
567 // DEBUG
568 llOwnerSay("Sitting on: " +
569 llList2String(names, select) +
570 " UUID: " +
571 llList2String(UUIDs, select)
572 );
573 llInstantMessage(
574 (key)wasKeyValueGet(
575 "corrade",
576 configuration
577 ),
578 wasKeyValueEncode(
579 [
580 "command", "sit",
581 "group", wasURLEscape(
582 wasKeyValueGet(
583 "group",
584 configuration
585 )
586 ),
587 "password", wasURLEscape(
588 wasKeyValueGet(
589 "password",
590 configuration
591 )
592 ),
593 "item", wasURLEscape(
594 llList2String(UUIDs, select)
595 ),
596 "range", wasURLEscape(
597 wasKeyValueGet(
598 "radar",
599 configuration
600 )
601 ),
602 "callback", wasURLEscape(callback)
603 ]
604 )
605 );
606 llSetTimerEvent(60);
607 }
608 touch_end(integer num) {
609 state unbind;
610 }
611 http_request(key id, string method, string body) {
612 llHTTPResponse(id, 200, "OK");
613 llSetTimerEvent(5);
614 if(wasKeyValueGet("type", body) != "permission" ||
615 wasKeyValueGet("permissions", body) != "TriggerAnimation") return;
616 llSetTimerEvent(10);
617 // DEBUG
618 llOwnerSay("Corrade received the permission request to trigger an animation, replying...");
619 llInstantMessage(
620 (key)wasKeyValueGet(
621 "corrade",
622 configuration
623 ),
624 wasKeyValueEncode(
625 [
626 "command", "replytoscriptpermissionrequest",
627 "group", wasKeyValueGet(
628 "group",
629 configuration
630 ),
631 "password", wasKeyValueGet(
632 "password",
633 configuration
634 ),
635 "item", wasKeyValueGet(
636 "item",
637 body
638 ),
639 "task", wasKeyValueGet(
640 "task",
641 body
642 ),
643 "region", wasKeyValueGet(
644 "region",
645 body
646 ),
647 "permissions", "TriggerAnimation",
648 "callback", wasURLEscape(callback)
649 ]
650 )
651 );
652 llResetScript();
653 }
654 timer() {
655 state unbind;
656 }
657 attach(key id) {
658 llResetScript();
659 }
660 on_rez(integer num) {
661 llResetScript();
662 }
663 changed(integer change) {
664 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START) || (change & CHANGED_OWNER)) {
665 llResetScript();
666 }
667 }
668 state_exit() {
669 llSetTimerEvent(0);
670 }
671 }
672  
673 state unbind {
674 state_entry() {
675 // DEBUG
676 llOwnerSay("Unbinding from the permission Corrade notification...");
677 llInstantMessage(
678 (key)wasKeyValueGet(
679 "corrade",
680 configuration
681 ),
682 wasKeyValueEncode(
683 [
684 "command", "notify",
685 "group", wasURLEscape(
686 wasKeyValueGet(
687 "group",
688 configuration
689 )
690 ),
691 "password", wasURLEscape(
692 wasKeyValueGet(
693 "password",
694 configuration
695 )
696 ),
697 "action", "remove",
698 "type", "permission",
699 "URL", wasURLEscape(callback),
700 "callback", wasURLEscape(callback)
701 ]
702 )
703 );
704 llSetTimerEvent(60);
705 }
706 http_request(key id, string method, string body) {
707 llHTTPResponse(id, 200, "OK");
708 if(wasKeyValueGet("command", body) != "notify" ||
709 wasKeyValueGet("success", body) != "True") {
710 // DEBUG
711 llOwnerSay("Failed to unbind from the permission notification...");
712 llResetScript();
713 }
714 // DEBUG
715 llOwnerSay("Permission notification uninstalled...");
716 llResetScript();
717 }
718 timer() {
719 // DEBUG
720 llOwnerSay("Timeout unbinding from the permission notification...");
721 llResetScript();
722 }
723 attach(key id) {
724 llResetScript();
725 }
726 on_rez(integer num) {
727 llResetScript();
728 }
729 changed(integer change) {
730 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START) || (change & CHANGED_OWNER)) {
731 llResetScript();
732 }
733 }
734 state_exit() {
735 llSetTimerEvent(0);
736 }
737 }