Sequencer.lsl 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. // :SHOW:1
  2. // :CATEGORY:NPC
  3. // :NAME:Hypergrid Story One
  4. // :AUTHOR:Ferd Frederix
  5. // :KEYWORDS:Game, Collider
  6. // :CREATED:2015-11-24 20:36:34
  7. // :EDITED:2015-11-24 19:36:34
  8. // :ID:1089
  9. // :NUM:1856
  10. // :REV:3.0
  11. // :WORLD:Opensim
  12. // :DESCRIPTION:
  13. // Sample sequencer script for NPC animator. It sequences multiple NPCs in order thru a scenarios
  14. // each 'things' entry is the NPC number, a (float) time to take between sending commands ( 0 is not allowed, but a small number is()
  15. ///and a @command that is sent to the NPC.
  16. // :CODE:
  17. // Rev: 2 fixes the Linux bug for collisions.
  18. //
  19. // Rev: 3 Neo Cortex: modified to become a standalone sequencer for Scene one
  20. //
  21. // Rev: 4 Aine Caoimhe: added support for custom appearance change slave scripts for finale sequence with 2 new commands:
  22. // @dumpkeys must be called when all NPCs have been rezzed (ie when npcList has been populated with their keys)
  23. // this relays the key data to the slave scripts which are numbered from 01 to 12
  24. // @slaveap=target|appearance_notecard_name where:
  25. // target 1 = namaka (for final one)
  26. // target 2 = mirror namakas (for final one)
  27. // target 3 = all namaka (for all the other ones for her)
  28. // target -1 = dylan (for the final one)
  29. // target -2 = mirror dylans (for the final one)
  30. // target -3 = all dylans (for all the ones for him)
  31. // in both of the above commands you can use any non-zero npc number but it's ignored so I'd just use 1 each time
  32. // it will respect a time delay value if you set one
  33. // example:
  34. // after all NPCs have been rezzed you need to call (but you only need to do it once):
  35. // things += [1,0,"@dumpkeys"];
  36. // then to have all of the dylans change to appearance notecard "dylan scene 2" with no delay after it you would do:
  37. // things += [1,0,"@slaveap=-3|dylan scene 2"];
  38. string myStoryNotecard = "!Story";
  39. integer debug = FALSE;
  40. integer LSLEditor = FALSE; // set to to TRUE to working in LSLEditor, FALSE for in-world.
  41. integer iTitleText = TRUE; // set to TRUE to see debug info in text above the controller
  42. integer SENSE = FALSE; // sensor for an avatar
  43. float RATE = 5; // every 5 seconds
  44. float RANGE = 2; // a very short range as this goes into a ring around the avatar
  45. integer COLLIDE = TRUE; // if they collide, trigger the sequence
  46. integer isRunning = FALSE; // used to stop collision from firing twice
  47. list npcList = [ (key) "00000000-0000-0000-0000-000000000000", "empty npc0", // this one is ignored
  48. (key) "00000000-0000-0000-0000-000000000000", "empty npc1",
  49. (key) "00000000-0000-0000-0000-000000000000", "empty npc2",
  50. (key) "00000000-0000-0000-0000-000000000000", "empty npc3",
  51. (key) "00000000-0000-0000-0000-000000000000", "empty npc4",
  52. (key) "00000000-0000-0000-0000-000000000000", "empty npc5",
  53. (key) "00000000-0000-0000-0000-000000000000", "empty npc6",
  54. (key) "00000000-0000-0000-0000-000000000000", "empty npc7",
  55. (key) "00000000-0000-0000-0000-000000000000", "empty npc8",
  56. (key) "00000000-0000-0000-0000-000000000000", "empty npc9",
  57. (key) "00000000-0000-0000-0000-000000000000", "empty npc10",
  58. (key) "00000000-0000-0000-0000-000000000000", "empty npc11",
  59. (key) "00000000-0000-0000-0000-000000000000", "empty npc12"];
  60. list myObjects = [];
  61. list myObjectKeys = [];
  62. integer myObjectsInitialized = FALSE;
  63. string npcAction = "";
  64. string npcParams = "";
  65. key npcKey = "";
  66. integer NPCOptions = OS_NPC_CREATOR_OWNED; // only the owner of this box can control this NPC.
  67. key oldCollider; //remember who collided last
  68. key newCollider;
  69. // CODE follows
  70. // For rezzing in
  71. RezIn()
  72. {
  73. things = [1,5,"@stop"];
  74. things += [2,5,"@stop"];
  75. things += [3,10,"@stop"];
  76. Speak();
  77. }
  78. DoIt()
  79. {
  80. things = myStory;
  81. isRunning = TRUE;
  82. llVolumeDetect(FALSE);
  83. llSleep(0.1);
  84. }
  85. list things ;
  86. list myStory;
  87. // DEBUG(string) will chat a string or display it as hovertext if debug == TRUE
  88. DEBUG(string str) {
  89. if (debug && ! LSLEditor)
  90. llOwnerSay( str); // Send the owner debug info
  91. if (debug && LSLEditor)
  92. llSay(0, str); // Send to the Console in LSLEDitor
  93. if (iTitleText) {
  94. llSetText(str,<1.0,1.0,1.0>,1.0); // show hovertext
  95. }
  96. }
  97. list ListStridedUpdate(list dest, list src, integer start, integer end, integer stride) {
  98. return llListReplaceList(dest, src, start * stride, ((end + 1) * stride) - 1 );
  99. }
  100. spawn(integer who,string msg) {
  101. DEBUG("spawning " + msg);
  102. list data = llParseString2List(msg, ["|"], []);
  103. //DEBUG((string) data);
  104. list npcName = llParseString2List(llList2String(data,0), [" "], []);
  105. npcKey = osNpcCreate(llList2String(npcName, 0), llList2String(npcName, 1), llList2Vector(data,1), llList2String(data, 2), NPCOptions);
  106. npcList=ListStridedUpdate(npcList,[npcKey,npcName],who,who,2);
  107. //DEBUG("npcList " + (string) npcList);
  108. }
  109. delete(integer who) {
  110. DEBUG(llList2String(npcList,who*2+1) + " is removed");
  111. osNpcRemove (llList2Key(npcList,who*2));
  112. npcList=ListStridedUpdate(npcList,[(key) "00000000-0000-0000-0000-000000000000","removed NPC"],who,who,2);
  113. //llSay(0, "npcList " + (string) npcList);
  114. }
  115. say(integer who,string msg) {
  116. DEBUG(llList2String(npcList,who*2+1) + " says " + msg);
  117. osNpcSay(llList2Key(npcList,who*2),0, msg);
  118. }
  119. sit(integer who,string msg) {
  120. DEBUG(llList2String(npcList,who*2+1) + " sits on " + (string) msg);
  121. // look up object named "msg" in myObjects, get key from myObjectsKeys, make "who" NPC sit on it
  122. osNpcSit(llList2Key(npcList,who*2), llList2Key(myObjectKeys,llListFindList(myObjects,[msg])), OS_NPC_SIT_NOW);
  123. }
  124. touchit(integer who,string msg) {
  125. DEBUG(llList2String(npcList,who*2+1) + " touches " + (string) msg);
  126. // look up object named "msg" in myObjects, get key from myObjectsKeys, make "who" NPC touch it
  127. osNpcTouch(llList2Key(npcList,who*2),llList2Key(myObjectKeys,llListFindList(myObjects,[msg])), LINK_THIS);
  128. }
  129. stand(integer who) {
  130. DEBUG(llList2String(npcList,who*2+1) + " stands");
  131. osNpcStand(llList2Key(npcList,who*2));
  132. list anToStop=llGetAnimationList(llList2Key(npcList,who*2));
  133. integer stop=llGetListLength(anToStop);
  134. while (--stop>-1) { osAvatarStopAnimation(llList2Key(npcList,who*2),llList2Key(anToStop,stop)); }
  135. osNpcPlayAnimation(llList2Key(npcList,who*2),"Stand");
  136. }
  137. walk(integer who,vector pos) {
  138. DEBUG(llList2String(npcList,who*2+1) + " walks to " + (string) pos);
  139. osNpcMoveToTarget(llList2Key(npcList,who*2),pos,OS_NPC_NO_FLY);
  140. osNpcPlayAnimation(llList2Key(npcList,who*2),"Walk");
  141. }
  142. animate(integer who, string ani) {
  143. DEBUG(llList2String(npcList,who*2+1) + " plays " + (string) ani);
  144. osNpcPlayAnimation(llList2Key(npcList,who*2),ani);
  145. }
  146. appearance(integer who, string app) {
  147. DEBUG(llList2String(npcList,who*2+1) + " changes appearance to " + (string) app);
  148. osNpcLoadAppearance(llList2Key(npcList,who*2),app);
  149. }
  150. rotate(integer who,float rot) {
  151. DEBUG(llList2String(npcList,who*2+1) + " rotates " + (string) rot);
  152. osNpcSetRot(llList2Key(npcList,who*2),llEuler2Rot(<0,0,rot> * DEG_TO_RAD));
  153. }
  154. // <<<< Added by Aine
  155. dumpKeys() {
  156. llMessageLinked(LINK_THIS,0,"NPC_UUID_LIST",llDumpList2String(llList2ListStrided(npcList,0,-1,2),"|"));
  157. }
  158. slaveap(string com) {
  159. list parse=llParseString2List(com,["|"],[]);
  160. llMessageLinked(LINK_THIS,llList2Integer(parse,0),"NPC_CHANGE_APPEARANCE",llList2String(parse,1));
  161. }
  162. // <<<< End added by Aine
  163. Speak() {
  164. integer npc = llList2Integer(things,0);
  165. float time = llList2Float(things,1);
  166. string msg = llList2String(things,2);
  167. DEBUG("npc:" + (string) npc + " time:" + (string) time + " Msg:" + msg);
  168. if (npc) {
  169. things = llDeleteSubList(things,0,2);
  170. // llMessageLinked(npc,0, msg,""); // <<<<< Aine: I don't see any need for having this here still since this script is handling it
  171. list data = llParseString2List(msg, ["="], []);
  172. npcAction = llToLower(llStringTrim(llList2String(data, 0), STRING_TRIM));
  173. DEBUG("Action:" + npcAction);
  174. npcParams = llStringTrim(llList2String(data, 1), STRING_TRIM);
  175. DEBUG("Params:" + npcParams);
  176. if (npcAction == "@say") {
  177. say(npc,npcParams);
  178. } else if (npcAction == "@spawn") {
  179. spawn(npc,npcParams);
  180. } else if (npcAction == "@delete") {
  181. delete(npc);
  182. } else if (npcAction == "@sit") {
  183. sit(npc,npcParams);
  184. } else if (npcAction == "@touch") {
  185. touchit(npc,npcParams);
  186. } else if (npcAction == "@stand") {
  187. stand(npc);
  188. } else if (npcAction == "@walk") {
  189. walk(npc,npcParams);
  190. } else if (npcAction == "@animate") {
  191. animate(npc,npcParams);
  192. } else if (npcAction == "@appearance") {
  193. appearance(npc,npcParams);
  194. } else if (npcAction == "@rotate") {
  195. rotate(npc,npcParams);
  196. // >>>> Added by Aine
  197. } else if (npcAction == "@dumpkeys") {
  198. dumpKeys();
  199. } else if (npcAction == "@slaveap") {
  200. slaveap(npcParams);
  201. } // >>>> end of add by Aine
  202. if (time > 0) {
  203. llSetTimerEvent(time);
  204. } else {
  205. llOwnerSay("Whooops, time = 0!");
  206. DEBUG("npc:" + (string) npc + " time:" + (string) time + " Msg:" + msg);
  207. llSetTimerEvent(0);
  208. }
  209. } else {
  210. DEBUG("Done");
  211. isRunning = FALSE;
  212. Reset();
  213. llSetTimerEvent(0);
  214. if (SENSE)
  215. llSensorRepeat("","",AGENT,RANGE,PI,RATE);
  216. }
  217. }
  218. Reset()
  219. {
  220. llSetStatus(STATUS_PHANTOM, FALSE); // Rev 2
  221. llVolumeDetect(FALSE);
  222. llSleep(0.1);
  223. llVolumeDetect(TRUE);
  224. }
  225. default
  226. {
  227. state_entry()
  228. {
  229. DEBUG("entering state default");
  230. if (! myObjectsInitialized){
  231. state initial;
  232. }
  233. llSetText("",<1,1,1>,1.0);
  234. Reset();
  235. llSetTimerEvent(0);
  236. if (SENSE)
  237. llSensorRepeat("","",AGENT,RANGE,PI,RATE);
  238. }
  239. sensor(integer n) {
  240. DEBUG("Bumped");
  241. if (! osIsNpc(llDetectedKey(0))) {
  242. DEBUG("Sensed avatar");
  243. llSensorRemove();
  244. RezIn();
  245. }
  246. }
  247. timer()
  248. {
  249. Speak();
  250. }
  251. collision_start(integer n) {
  252. newCollider = llKey2Name(llDetectedKey(0));
  253. DEBUG("Collided with " + llKey2Name(llDetectedKey(0)));
  254. if (! osIsNpc(llDetectedKey(0)))
  255. {
  256. if ( newCollider != oldCollider) {
  257. DoIt();
  258. oldCollider = llKey2Name(llDetectedKey(0));
  259. Speak();
  260. }
  261. }
  262. }
  263. collision_end(integer num_detected) {
  264. DEBUG(llDetectedName(0) + " has stopped colliding with me!");
  265. if (! isRunning && (newCollider == oldCollider) ) {
  266. oldCollider = NULL_KEY;
  267. }
  268. }
  269. touch_start(integer n) {
  270. DEBUG("Touched by " + llKey2Name(llDetectedKey(0)));
  271. if (isRunning) {
  272. //insert restart code here
  273. }
  274. if (! osIsNpc(llDetectedKey(0))) {
  275. DoIt();
  276. Speak();
  277. }
  278. }
  279. on_rez(integer p)
  280. {
  281. llResetScript();
  282. }
  283. changed(integer what)
  284. {
  285. if (what & CHANGED_REGION_START|CHANGED_INVENTORY)
  286. {
  287. llResetScript();
  288. }
  289. }
  290. }
  291. state initial
  292. {
  293. state_entry()
  294. {
  295. DEBUG("entering state initial");
  296. myStory = [];
  297. list myStoryLines = llParseString2List(osGetNotecard(myStoryNotecard), ["\n"], []);
  298. DEBUG("text = " + (string) myStoryLines);
  299. DEBUG("lines = " + (string) llGetListLength(myStoryLines));
  300. integer i;
  301. for (i=0; i<llGetListLength(myStoryLines); i++) {
  302. string myStoryLine = llList2String(myStoryLines,i);
  303. integer npc = (integer) llGetSubString(myStoryLine,0,llSubStringIndex(myStoryLine,","));
  304. string rest = llGetSubString(myStoryLine,llSubStringIndex(myStoryLine,",")+1,-1);
  305. float time = llGetSubString(rest,0,llSubStringIndex(rest,","));
  306. string msg = llGetSubString(rest,llSubStringIndex(rest,",")+2,-2);
  307. //now parse for sit and touch events to find the names of the objects needed
  308. list data = llParseString2List(msg, ["="], []);
  309. npcAction = llToLower(llStringTrim(llList2String(data, 0), STRING_TRIM));
  310. DEBUG("Action:" + npcAction);
  311. npcParams = llStringTrim(llList2String(data, 1), STRING_TRIM);
  312. DEBUG("Params:" + npcParams);
  313. if (npcAction == "@sit") {
  314. myObjects += npcParams;
  315. } else if (npcAction == "@touch") {
  316. myObjects += npcParams;
  317. }
  318. DEBUG("npc: " + (string) npc + " time: " + (string) time + " msg: " + msg);
  319. myStory += [npc, time, msg];
  320. }
  321. DEBUG("Story read, initializing objects: " + llDumpList2String(myObjects," "));
  322. if (llGetListLength(myObjects) == 0) {
  323. DEBUG("myObjects is empty upon entering state initial");
  324. myObjectsInitialized = TRUE;
  325. state default;
  326. }
  327. llSensor(llList2String(myObjects,0),"",ACTIVE|PASSIVE,96,PI);
  328. DEBUG("initially looking for: " + llList2String(myObjects,0));
  329. }
  330. //sensor is used to scan surrounding for named prims
  331. sensor(integer num) {
  332. myObjectKeys = myObjectKeys + llDetectedKey(0);
  333. DEBUG("myObjectKeys: " + llDumpList2String(myObjectKeys," "));
  334. myObjects = llDeleteSubList(myObjects,0,0);
  335. if (llGetListLength(myObjects) == 0) {
  336. myObjectsInitialized = TRUE;
  337. state default;
  338. } else {
  339. llSensor(llList2String(myObjects,0),"",ACTIVE|PASSIVE,96,PI);
  340. DEBUG("now looking for: " + llList2String(myObjects,0));
  341. }
  342. }
  343. no_sensor(){
  344. DEBUG ("no target prim located");
  345. }
  346. }