Animator- stor.lsl 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. // :CATEGORY:Prim Animator
  2. // :NAME:Archipelis scripts for prim animator
  3. // :AUTHOR:Ferd Frederix
  4. // :CREATED:2013-09-06
  5. // :EDITED:2013-09-18 15:38:48
  6. // :ID:52
  7. // :NUM:72
  8. // :REV:2
  9. // :WORLD:Second Life
  10. // :DESCRIPTION:
  11. // An almost unlimited space prim animator. It uses multiple Store Scripts, that can hold thousands of prim animations
  12. // This is the latest version of the script. Must be used with Multiple copies of Store 0..1,2, to animate prims
  13. // :CODE:
  14. // 06/20/2011
  15. // Author Ferd Frederix
  16. // Use with one or more copies of the script 'Store 0..Store 1.. Store N'
  17. // Copyright 2011 Fred Beckhusen aka Ferd Frederix
  18. //
  19. // Licensed under the GNU Version3 license at http://www.gnu.org/copyleft/gpl.html
  20. // This program and the STORE scipts it uses must be left Copy/Transfer/MOD
  21. // Any items that use these scripts may have more restrictive permissions.
  22. // for help and how to use, see "http://meteverse.mitsi.com/secondlife/Posts/Prim-Animator-DB
  23. // Prim position root script
  24. // Put this script in the root prim.
  25. // touch the prim to start recording
  26. // Record - store a new set of child prim positions
  27. // Clear RAM - wipe out all recording in the prim. Leaves the database intact
  28. // Pause - insert a 0.1 second pause
  29. // Erase - Erases the Server data
  30. // Load - Loads the data from the Server
  31. // Publisgh - Saves the data to the database
  32. // Recordings - Lists all recordings ( up to 16)
  33. // Name - name a new recording
  34. // Play All Plays back all recordings with a 1 second Pause.
  35. // Publish - Saves to database
  36. // Help - Get Web Help
  37. // More - TBD
  38. // configurable things
  39. string MyName ; // The creator of the animation
  40. string MyItemName; // a Name just for this unique item and any copies
  41. integer debug = FALSE; // if set to TRUE, debug info appears
  42. // Code follows - no need to edit below this line.
  43. // remote db reading
  44. key kHttpRequestID;
  45. integer countLoaded = 0; // how many nanims have been loaded via HTTP
  46. integer iPlayChannel = 1; // the playback channel, this is the channel you use in LinkMessages
  47. integer countchannel= 2001; // how many have been saved
  48. integer iDialogChannel ; // dialog boxes
  49. integer iPrimCount ; // total number of prims
  50. integer iWantNameFlag; // flag indicating we are waiting for a name to be chatted
  51. integer imax; // total records in store
  52. integer runtime = 0; // set to 1 after Recording and publishing.
  53. // animation store
  54. string sCurrentAnimationName; // The name of the animation we are playing
  55. string sPriorAniName; // the prior animation name so we can spot differences as we read them in
  56. list lLastPrimList; // storage of the last prim position we learned
  57. integer iLastSTRIDE = 4; // size of the last prim list
  58. integer iListenerHandle; // temp iListenerHandle when we are waiting for them to give us a name
  59. list lUserAnimNames; // animations read from the notecard added to the Menu display
  60. // Commands
  61. integer Clear = 99; // erase local db
  62. integer Erase = 100; // erase Server DB command
  63. integer Add = 101; // add a coordinate
  64. integer Publish = 102; // save to db
  65. integer Name = 103; // send Name to db
  66. integer thisName = 104; // send thisName to db
  67. integer Play = 105; // play channel to make an animation run
  68. Debug( string msg)
  69. {
  70. if (debug) llOwnerSay(msg);
  71. }
  72. // clear any old listeners and make a new on to control lag
  73. NewListen()
  74. {
  75. iDialogChannel = (integer) (llFrand(400) + 300);
  76. llListenRemove(iListenerHandle);
  77. iListenerHandle = llListen(iDialogChannel,"","","");
  78. }
  79. // prompt the user for an animation name. Uses text box or chat
  80. AskForName()
  81. {
  82. iWantNameFlag = TRUE;
  83. llSetTimerEvent(60);
  84. NewListen();
  85. llOwnerSay("Enter the name in the text box or type the new animation name on channel /" + (string) iDialogChannel );
  86. llTextBox(llGetOwner(),"Type the new animation name",iDialogChannel);
  87. }
  88. Get(string data)
  89. {
  90. Debug("Data was " + data);
  91. list lLine = (llParseString2List(data, ["|"], []));
  92. string sAniName = llList2String(lLine,0);
  93. string sNum = (string) Getline(lLine,1);
  94. float fNum = (float) sNum;
  95. vector vPos = (vector) Getline(lLine,2); // global for calcChild()
  96. rotation rRot = (rotation) Getline(lLine,3); // global for calcChild()
  97. vector vprimSize = (vector) Getline(lLine,4);
  98. countLoaded++;
  99. // the first record is always the root prim size
  100. if (fNum == 0)
  101. {
  102. FetchNext();
  103. return;
  104. }
  105. if (sAniName != sPriorAniName)
  106. {
  107. sPriorAniName = sAniName;
  108. sCurrentAnimationName = sAniName;
  109. if (llListFindList(lUserAnimNames,[sAniName]) == -1)
  110. {
  111. Debug("Loading animation " + sAniName);
  112. lUserAnimNames += sAniName;
  113. }
  114. }
  115. // Debug("Loading Prim #" + (string) fNum);
  116. if(fNum != 1) // skip root prim
  117. {
  118. sCurrentAnimationName = sAniName;
  119. llMessageLinked(LINK_THIS,Add,sAniName
  120. +"|"
  121. + (string) fNum
  122. +"|"
  123. + (string) vPos
  124. + "|"
  125. + (string) rRot
  126. + "|"
  127. + (string) vprimSize,"");
  128. llSetText("Initialising... \n" + (string) (countLoaded) , <1,1,1>, 1.0);
  129. }
  130. llSleep(1); // http throttle avoidance
  131. FetchNext();
  132. }
  133. // in case of hand editing, we wupe out extra stuff at end
  134. string Getline(list Input, integer line)
  135. {
  136. return llStringTrim(llList2String(Input, line),STRING_TRIM);
  137. }
  138. Record()
  139. {
  140. if (llStringLength(sCurrentAnimationName) > 0)
  141. {
  142. integer ifoundmovement = 0; // will be set if any child moved
  143. integer iPrimCounter ; // skip past the root prim
  144. for (iPrimCounter =2; iPrimCounter <= iPrimCount ; iPrimCounter++ )
  145. {
  146. list my_list = llGetLinkPrimitiveParams(iPrimCounter,[PRIM_POSITION,PRIM_ROTATION, PRIM_SIZE ]);
  147. // position is always in region coordinates, even if the prim is a child or the root prim of an attachment.
  148. // rot is always the global rotation, even if the prim is a child or the root prim of an attachment.
  149. // get current prim pos, rot and size
  150. vector vrealPrimPos = llList2Vector (my_list,0) - llGetPos(); // position subtract Global Pos
  151. vrealPrimPos /= llGetRot();
  152. rotation rrealPrimRot = llList2Rot (my_list,1) / llGetRot(); // rotation subtract Global Rot
  153. vector vrealPrimSize = llList2Vector (my_list,2); // size
  154. // compare it to the last one we had, stride of list is a 4, and it is already sorted
  155. integer iindex = (iPrimCounter - 2) * iLastSTRIDE; // zeroth position is fPrimCounter - start, or 2
  156. // get the last thing we remembered about this prim
  157. integer iprimNum = llList2Integer (lLastPrimList, iindex); // must be 0,1,2, in order
  158. vector vlastPrimPos = llList2Vector (lLastPrimList, iindex+1);
  159. rotation rlastPrimRot = llList2Rot (lLastPrimList, iindex+2);
  160. vector vlastPrimSize = llList2Vector (lLastPrimList, iindex+3);
  161. // if anything changed on this prim, we must record it.
  162. if (vlastPrimPos != vrealPrimPos ||
  163. rlastPrimRot != rrealPrimRot ||
  164. vlastPrimSize!= vrealPrimSize
  165. )
  166. {
  167. ifoundmovement++;
  168. //Save them in the master list of all animations
  169. llMessageLinked(LINK_THIS,Add,sCurrentAnimationName+ "|"
  170. + (string) iPrimCounter
  171. + "|"
  172. + (string) vrealPrimPos
  173. + "|"
  174. + (string) rrealPrimRot
  175. + "|"
  176. + (string) vrealPrimSize,"");
  177. imax = llGetListLength(lLastPrimList);
  178. // save the changes in the last prim list so we can keep our lists smaller
  179. lLastPrimList = llListReplaceList(lLastPrimList,[vrealPrimPos],iPrimCounter+1,iPrimCounter+1);
  180. lLastPrimList = llListReplaceList(lLastPrimList,[rrealPrimRot],iPrimCounter+2,iPrimCounter+2);
  181. lLastPrimList = llListReplaceList(lLastPrimList,[vrealPrimSize],iPrimCounter+3,iPrimCounter+3);
  182. //Debug("Saved in history at prim # " + (string) (iPrimCounter));
  183. llSleep(0.1);
  184. } // if
  185. else
  186. {
  187. Debug("Same @" + (string) iPrimCounter);
  188. }
  189. } // for
  190. if (!ifoundmovement)
  191. {
  192. llOwnerSay("You must move at least one child prim.");
  193. }
  194. }
  195. else
  196. {
  197. AskForName();
  198. }
  199. }
  200. // on reset, record the base position in history so we can see changes
  201. ClearRAM()
  202. {
  203. iPrimCount = llGetNumberOfPrims(); // how many we are recording
  204. Debug("Prim Count = " + (string) iPrimCount);
  205. sCurrentAnimationName = ""; // no name in use
  206. lLastPrimList = []; // wipe last animations list
  207. integer iPrimCounter ;
  208. integer primCounter = 0;
  209. for (iPrimCounter=2; iPrimCounter <= iPrimCount ; iPrimCounter++ ) // skip 1, which is the root prim
  210. {
  211. list my_list = llGetLinkPrimitiveParams(iPrimCounter,[PRIM_POSITION, PRIM_ROTATION, PRIM_SIZE ]);
  212. // save the local pos and rot, since llGetLinkPrimitiveParams returns global pos and rot
  213. vector primpos = llList2Vector (my_list,0) - llGetPos();
  214. primpos /= llGetRot();
  215. rotation primrot = llList2Rot (my_list,1) / llGetRot();
  216. vector primsize = llList2Vector (my_list,2) ;
  217. lLastPrimList += iPrimCounter;
  218. lLastPrimList += primpos;
  219. lLastPrimList += primrot;
  220. lLastPrimList += primsize;
  221. primCounter++;
  222. }
  223. llOwnerSay("RAM is cleared - Initial Settings saved for " + (string) primCounter + " child prims");
  224. }
  225. rotation calcChildRot(rotation rdeltaRot)
  226. {
  227. if (llGetAttached())
  228. return rdeltaRot/llGetLocalRot();
  229. else
  230. return rdeltaRot/llGetRootRotation();
  231. }
  232. MakeMenu()
  233. {
  234. if (runtime)
  235. {
  236. MakeMenu2();
  237. return;
  238. }
  239. list amenu = ["Name", "Record","Pause"] +
  240. ["Erase","Load","Publish"] +
  241. ["Recordings","-","Play All"] +
  242. ["Help","-", "Finish"];
  243. string menuText = "Available Memory: " + (string) llGetFreeMemory() +
  244. "\n\n" +
  245. "Recordings: Select a named animation\n" +
  246. "Name: Set the animation name\n" +
  247. "Play All: Play all animations\n" +
  248. "Erase: Delete Recorded Data\n" +
  249. "Load: Get from Server\n" +
  250. "Publish: Save to Server\n" +
  251. "Record: Record a snapshot of prim positions\n" +
  252. "Pause: Insert 0.1 second pause\n" +
  253. "Finish: End all programming\n" +
  254. "Help: Help";
  255. NewListen();
  256. llDialog(llGetOwner(), menuText,amenu,iDialogChannel);
  257. }
  258. MakeMenu2()
  259. {
  260. string menuText ;
  261. list menu2 = lUserAnimNames;
  262. menu2 = llDeleteSubList(menu2,11,999); // 12 max
  263. if (runtime)
  264. menu2 += "Unlock";
  265. if (llGetListLength(lUserAnimNames) == 0)
  266. {
  267. menu2 += ["Back"] ;
  268. menuText = " There are no animation names to display\n\n" +
  269. "Back:- Go back to programming menu";
  270. }
  271. else
  272. menuText = "Pick an animation to play.";
  273. NewListen();
  274. llDialog(llGetOwner(), menuText,menu2,iDialogChannel);
  275. }
  276. FetchNext()
  277. {
  278. string url = "http://www.outworldz.com/cgi/animate.plx?Type=Load&Count=" + (string) countLoaded + "&Name=" + llEscapeURL(MyName) + "&Password=" + llEscapeURL(MyItemName);
  279. Debug(url);
  280. kHttpRequestID = llHTTPRequest(url, [], "");
  281. }
  282. default
  283. {
  284. state_entry()
  285. {
  286. MyName = llGetCreator(); // assign the creator if we get reset
  287. MyItemName = llGetObjectDesc(); // and get the name of the Object
  288. llOwnerSay("Animation name from the description is " + MyItemName);
  289. if (MyItemName == "(No Description)" || llStringLength(MyItemName) == 0)
  290. {
  291. MyItemName = "Archipelis_" + (string) (llCeil(llFrand(200000) + 5000000)); // pick a large random number for a name
  292. llOwnerSay("Animations will be saved as '" + MyItemName + "'");
  293. llSetObjectDesc(MyItemName);
  294. }
  295. llMessageLinked(LINK_THIS,Name,MyName,"");
  296. llMessageLinked(LINK_THIS,thisName,MyItemName,"");
  297. if (runtime)
  298. {
  299. ClearRAM();
  300. FetchNext(); // start with first line in DB
  301. }
  302. else
  303. {
  304. ClearRAM();
  305. }
  306. }
  307. http_response(key queryid, integer status, list metadata, string data)
  308. {
  309. if (queryid == kHttpRequestID )
  310. {
  311. if (status == 200) // HTTP success status
  312. {
  313. if (data == "nothing to do")
  314. {
  315. llOwnerSay("Initialized with " + (string) countLoaded + " settings");
  316. llSetText("" , <1,1,1>, 1.0);
  317. return;
  318. }
  319. Get(data);
  320. }
  321. else
  322. {
  323. llOwnerSay("Server Error loading animations");
  324. }
  325. }
  326. }
  327. touch_start(integer total_number)
  328. {
  329. if (llDetectedKey(0) == llGetOwner() )
  330. {
  331. MakeMenu();
  332. }
  333. }
  334. // timed out waitig for a name of an animation
  335. timer()
  336. {
  337. iWantNameFlag = FALSE;
  338. llSetTimerEvent(0);
  339. llOwnerSay(" Timeout waiting for animation name");
  340. llListenRemove(iListenerHandle);
  341. }
  342. listen( integer channel, string name, key id, string message )
  343. {
  344. llListenRemove(iListenerHandle);
  345. // Debug(message);
  346. if (message == "Finish")
  347. {
  348. runtime++;
  349. llOwnerSay("The animator is now locked. Further touches will bring up a list of animations. You must unlock the menu to add more animations");
  350. }
  351. else if (message == "Unlock")
  352. {
  353. runtime = 0;
  354. llOwnerSay("The animator is now unlocked for programming.");
  355. }
  356. else if (message == "Erase")
  357. {
  358. ClearRAM();
  359. llMessageLinked(LINK_THIS,Erase,"",""); // erase the db
  360. MakeMenu();
  361. }
  362. else if (message =="Pause")
  363. {
  364. llMessageLinked(LINK_THIS,Add,"sCurrentAnimationName|"
  365. + "-0.1" // sleep 1 tenth a second
  366. + "|"
  367. + "<0,0,0>" // null pos
  368. + "<0,0,0,1>" // null rot
  369. + "|"
  370. + "<0,0,0>",""); // null size
  371. MakeMenu();
  372. }
  373. else if (message == "Recordings")
  374. {
  375. MakeMenu2();
  376. }
  377. else if (message == "Load")
  378. {
  379. countLoaded = 0;
  380. FetchNext(); // start with first line in DB
  381. }
  382. else if (message == "Record")
  383. {
  384. Record();
  385. MakeMenu();
  386. }
  387. else if (message == "Publish")
  388. {
  389. llMessageLinked(LINK_THIS,Name,MyName,""); // in case we reset just the stores
  390. llMessageLinked(LINK_THIS,thisName,MyItemName,"");
  391. llMessageLinked(LINK_THIS,Publish,"","");
  392. MakeMenu();
  393. }
  394. else if (message == "Name")
  395. {
  396. AskForName();
  397. }
  398. else if (message == "Back")
  399. {
  400. MakeMenu();
  401. }
  402. else if (message == "More")
  403. {
  404. MakeMenu();
  405. }
  406. else if (message == "Play All")
  407. {
  408. integer l = llGetListLength(lUserAnimNames);
  409. if (l == 0)
  410. llOwnerSay("Nothing to do");
  411. else
  412. {
  413. integer i = 0;
  414. for ( i = 0; i < l; i++)
  415. {
  416. string ani = llList2String(lUserAnimNames,i);
  417. llMessageLinked(LINK_THIS,Play,ani,"");
  418. llSleep(1);
  419. }
  420. }
  421. MakeMenu();
  422. }
  423. else if (message == "Help")
  424. {
  425. llLoadURL(llGetOwner(),"View online help", "http://www.outworldz.com/secondlife/Posts/Prim-Animator-DB");
  426. }
  427. else if (iWantNameFlag)
  428. {
  429. sCurrentAnimationName = message;
  430. if (llGetListLength(lUserAnimNames) < 11)
  431. {
  432. if (llListFindList(lUserAnimNames,[sCurrentAnimationName]) == -1)
  433. lUserAnimNames += message;
  434. }
  435. else
  436. {
  437. lUserAnimNames = llDeleteSubList(lUserAnimNames,11,99);
  438. }
  439. MakeMenu();
  440. llOwnerSay("Ready to record animation '" + sCurrentAnimationName + "'");
  441. llOwnerSay("Position all child prims, then select the Menu item 'Record', and repeat as necessary. When finished, click 'Recordings' to play back the animation. Click 'Name' again to start a new animation sequence.");
  442. iWantNameFlag = 0;
  443. llSetTimerEvent(0);
  444. }
  445. else
  446. {
  447. if (llListFindList(lUserAnimNames,[message]) != -1)
  448. {
  449. Debug("playback animation " + message);
  450. llMessageLinked(LINK_THIS,Play,message,"");
  451. MakeMenu();
  452. }
  453. }
  454. }
  455. link_message(integer sender_num, integer num, string message, key id)
  456. {
  457. if (num == countchannel)
  458. {
  459. llSetText("Loaded " + message,<1,1,1>,1.0);
  460. }
  461. else if (num == iPlayChannel) // external messages come in channel 1 - change them to channel Play
  462. {
  463. Debug("playback animation " + message);
  464. llMessageLinked(LINK_THIS,Play,message,"");
  465. }
  466. }
  467. }