PMAC Core v1.02.lsl 53 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227
  1. // :SHOW:
  2. // :CATEGORY:NPC
  3. // :NAME:PMAC
  4. // :AUTHOR:Aine Caoimhe
  5. // :KEYWORDS:
  6. // :CREATED:2015-11-24 20:38:40
  7. // :EDITED:2015-11-24 19:38:40
  8. // :ID:1095
  9. // :NUM:1877
  10. // :REV:1
  11. // :WORLD:OpenSim
  12. // :DESCRIPTION:
  13. // PARAMOUR MULTI-ANIMATION CONTROLLER (PMAC) v1.02 (OSSL)
  14. // :CODE:
  15. // PARAMOUR MULTI-ANIMATION CONTROLLER (PMAC) v1.02
  16. // by Aine Caoimhe (Mata Hari)(c. LACM) March 2015-May 2015
  17. // Provided under Creative Commons Attribution-Non-Commercial-ShareAlike 4.0 International license.
  18. // Please be sure you read and adhere to the terms of this license: https://creativecommons.org/licenses/by-nc-sa/4.0/
  19. //
  20. // UserConfig added by Seth Nygard March 2015
  21. // modified to store positions for NC_PROPS addon Neo Cortex April 2015
  22. // additional tweaks by Aine Caoimhe May 2015
  23. // *** THIS SCRIPT REQUIRES (AND WILL ONLY WORK) IN REGIONS WHERE THE SCRIPT OWNER HAS OSSL FUNCTION PERMISSIONS ***
  24. // *** THIS SCRIPT MUST ALWAYS BE LOCATED IN THE ROOT PRIM OF A LINKSET ***
  25. //
  26. // *********************************************************
  27. // ***** GENERAL USER SETTINGS - ADJUST AS PREFERED *****
  28. // *********************************************************
  29. string defaultGroup="Dance"; // name of a group (not the full card name!) to load by default (regardless of its permission setting)
  30. //
  31. integer resetOnQuit=FALSE; // TRUE = when no more sitters, reset the script (will also load default group again)
  32. // FALSE = leave most recently loaded animation active
  33. integer ownerUseReq=FALSE; // TRUE = the owner must be the first to sit and other people can only sit while the owner is still present and seated
  34. // FALSE = no restriction...anyone can sit and use it at any time
  35. integer ownerOnlyMenus=FALSE; // TRUE = only the owner can access the dialog menus (can be turned off in options menu until script is reset or it is turned on again) - NOT RECOMMENDED unless ownerUseReq=TRUE
  36. // FALSE = anyone can be the controller
  37. integer ownerUseUnlocksPerms=FALSE; // TRUE = if owner is a current user, all users then have access to all groups and NPCs
  38. // FALSE = only the owner can ever load an owner-only Group or NPC
  39. integer autoOn=TRUE; // TRUE = will start in auto mode after a reset
  40. // FALSE = will start in manual mode after a reset -- after use will remain in whatever state it was left in unless resetOnQuit=TRUE
  41. float autoTimer=120.0; // default time to use for the autotimer (in seconds) - after use will remain at whatever timer was last set to unless resetOnQuit=TRUE;
  42. string gs_ConfigName=".PMAC-CONFIG"; // Name of optional notecard with user defined configuration that overide the above values if present
  43. integer showGroupsMenuFirst=FALSE; // TRUE = when first initiating dialog, show the groups menu instead of the current group's animation menu; FALSE = show current group's animation menu
  44. integer allowSoloNPC=TRUE; // TRUE = NPCs can be left rezzed even if there are no avatars seated; FALSE = kill all NPCs if there are no remaining avatars
  45. //
  46. // ***************************************
  47. // ***** ADVANCED/BUILDER SETTINGS *****
  48. // ***************************************
  49. string handleName="~~~positioner"; // inventory object to rez as a handle for positioning
  50. list handleColours=[<1.000, 0.004, 0.667>,<0.004, 0.667, 1.000>,<0.667, 1.000, 0.004>, // colours to use for handles (must supply 9)
  51. <1.000, 0.004, 0.004>,<0.004, 0.004, 1.000>,<1.000, 0.667, 0.004>,
  52. <0.667, 0.004, 1.000>,<0.004, 1.000, 0.667>,<0.004, 1.000, 0.004>];
  53. vector handleSize=<0.2,0.2,3.0>; // size of handles
  54. float handleAlpha=0.5; // alpha of handles
  55. string baseAn="~~~~~base_DO_NOT_DELETE_ME!!!!!"; // name of the P1 animation to use for synch
  56. //
  57. // ************************************************
  58. // ***** DO NOT CHANGE ANYTHING BELOW HERE *****
  59. // ***** UNLESS YOU KNOW WHAT YOU'RE DOING! *****
  60. // ************************************************
  61. key user;
  62. list positions;
  63. list invNpc; // fullName | A/G/O | buttonName
  64. integer invNpcStride=3;
  65. list npcList;
  66. integer npcPage;
  67. list invGroups; // fullName | positions | A/G/O | buttonName
  68. integer invGroupStride=4;
  69. list groupList;
  70. integer groupPage;
  71. integer myChannel;
  72. integer diaHandle;
  73. string menu;
  74. string txtDia;
  75. list butDia;
  76. string currentGroup;
  77. list anData; // anName | command | A1Name | A1 Pos | A1 Rot | ...
  78. integer anStride;
  79. list anList;
  80. list currentAn; // groupName | anName | command | A1Name | A1 Pos | A1 Rot | ...
  81. integer anPage;
  82. list editHandles;
  83. integer rezzingHandles;
  84. float editTimer=0.2;
  85. string myState="INITIALIZING";
  86. list specials; //buttonName | stringToSend
  87. integer specPage;
  88. integer gi_HaveConfig=0;
  89. // NC_PROP ADDON
  90. integer gi_NC_PROP_CHANGED=FALSE; // global integer flag to indicate if new prop values have been sent form NC_PROPS addon
  91. string gs_NC_PROP_DATA=""; // global string containing changed props data
  92. showAnMenu()
  93. {
  94. integer maxAn=llGetListLength(anList);
  95. integer showStart=(anPage+1);
  96. integer showEnd=(anPage+6);
  97. if (showEnd>maxAn) showEnd=maxAn;
  98. txtDia=""+"ANIMATION MENU: Select an animation\n"+currentlyPlaying()+"\nShowing animation";
  99. if (showEnd!=showStart) txtDia+="s "+(string)showStart+" to "+(string)showEnd;
  100. else txtDia+=" "+(string)showStart;
  101. txtDia+=" of "+(string)maxAn+ " total animations in this group\n\n"+llDumpList2String(llList2List(anList,anPage,anPage+5),"\n");
  102. butDia=[]+llList2List(anList,anPage,anPage+5);
  103. while (llGetListLength(butDia)<6) { butDia=[]+butDia+["-"]; }
  104. butDia=[]+butDia+["< PREV","SYNCH","NEXT >","GROUPS","OPTIONS","QUIT"];
  105. menu="MENU_ANIM";
  106. startListening();
  107. }
  108. showGroupsMenu()
  109. {
  110. txtDia=""+"GROUPS MENU: Select a group of animations\n"+currentlyPlaying()+"\nShowing groups "+(string)(groupPage+1)+" to "+(string)(groupPage+6)+" of "+(string)llGetListLength(groupList)+ " total groups\n\n"+llDumpList2String(llList2List(groupList,groupPage,groupPage+5),"\n");
  111. butDia=[]+llList2List(groupList,groupPage,groupPage+5);
  112. while (llGetListLength(butDia)<6) { butDia=[]+butDia+["-"]; }
  113. butDia=[]+butDia+["< PREV","SYNCH","NEXT >","<< BACK","OPTIONS","QUIT"];
  114. menu="MENU_GROUPS";
  115. startListening();
  116. }
  117. showEditMenu()
  118. {
  119. txtDia=""+"EDIT MODE ACTIVE!!!\n"+currentlyPlaying()+" (#"+(string)(llListFindList(anList,[llList2String(currentAn,1)])+1)+" of "+(string)llGetListLength(anList)+")\n\nPlease ensure you have read and are familiar with the PMAC instructions for using the edit menu, particularly if you use add-on modules. You have been warned...";
  120. butDia=[]+["< PREV","SYNCH","NEXT >","REVERT THIS","STORE THIS","STORE ADDON","EDIT OFF","SAVE CARD","SAVE NEW"];
  121. menu="MENU_EDIT";
  122. startListening();
  123. }
  124. showOptionsMenu()
  125. {
  126. txtDia=""+"OPTIONS MENU:\n\n";
  127. butDia=[];
  128. if (user==llGetOwner())
  129. {
  130. if (ownerOnlyMenus)
  131. {
  132. txtDia+="EDIT ON enters edit mode (all positions must be filled)\nMENUS UNLOCK allows other users to take control\n";
  133. butDia=[]+butDia+["EDIT ON","MENUS UNLOCK","-"];
  134. }
  135. else
  136. {
  137. txtDia+="EDIT ON enters edit mode (all positions must be filled)\nMENUS LOCK prevents other users from taking control\n";
  138. butDia=[]+butDia+["EDIT ON","MENUS LOCK","-"];
  139. }
  140. }
  141. txtDia+="AUTO is used to enable, disable or adjust auto mode\n";
  142. butDia=[]+butDia+["AUTO","-"];
  143. if (llGetListLength(specials)>0)
  144. {
  145. txtDia+="SPECIAL access special add-on menus\n";
  146. butDia=[]+butDia+["SPECIAL"];
  147. }
  148. else butDia=[]+butDia+["-"];
  149. txtDia+="SWAP to swap positions\nUNSIT to force someone to stand up or remove a npc\n";
  150. butDia=[]+butDia+["SWAP","UNSIT"];
  151. if (llListFindList(positions,[NULL_KEY])>-1)
  152. {
  153. txtDia+="ADD NPC to have a npc join you\n";
  154. butDia=[]+butDia+["ADD NPC"];
  155. }
  156. else butDia=[]+butDia+["-"];
  157. butDia=[]+butDia+["<< BACK","SYNCH","QUIT"];
  158. menu="MENU_OPTIONS";
  159. startListening();
  160. }
  161. showSpecialsMenu()
  162. {
  163. txtDia=""+"SPECIALS MENU\n\nThese are options supplied by any add-ons you have installed. Please consult their instructions for details.\n";
  164. butDia=[];
  165. integer i=specPage;
  166. while (llGetListLength(butDia)<9)
  167. {
  168. if (i<llGetListLength(specials))
  169. {
  170. butDia=[]+butDia+llList2String(specials,i);
  171. txtDia+="\n"+llList2String(specials,i);
  172. i+=2;
  173. }
  174. else butDia=[]+butDia+["-"];
  175. }
  176. butDia=[]+butDia+["< PREV","CANCEL","NEXT >"];
  177. menu="MENU_SPECIALS";
  178. startListening();
  179. }
  180. showAutoMenu()
  181. {
  182. txtDia="AUTO MENU\n\nAuto mode is currently ";
  183. butDia=[]+["120","300","600","30","60","90"];
  184. if (autoOn)
  185. {
  186. txtDia+="ON and set to "+(string)llRound(autoTimer)+" seconds\n\nSelect a different time if you wish, or AUTO OFF to switch to manual mode";
  187. butDia=[]+butDia+["AUTO OFF","-","CANCEL"];
  188. }
  189. else
  190. {
  191. txtDia+="OFF\n\nSelect AUTO ON to use the last preset time or pick your preferred timer";
  192. butDia=[]+butDia+["AUTO ON","-","CANCEL"];
  193. }
  194. menu="MENU_SELECT_AUTO_MODE";
  195. startListening();
  196. }
  197. showAddNpcMenu()
  198. {
  199. txtDia=""+"ADD NPC\n\nSelect the NPC to add. It will occupy the first available position\n\n"+llDumpList2String(llList2List(npcList,npcPage,npcPage+8),"\n");
  200. butDia=[]+llList2List(npcList,npcPage,npcPage+8);
  201. while (llGetListLength(butDia)<9) { butDia=[]+butDia+["-"]; }
  202. butDia=[]+butDia+["< PREV","CANCEL","NEXT >"];
  203. menu="MENU_ADD_NPC";
  204. startListening();
  205. }
  206. playAnimation(string name)
  207. {
  208. if (autoOn) llSensorRemove();
  209. integer i=llGetListLength(positions);
  210. while (--i>=0) { if (llList2Key(positions,i)!=NULL_KEY) osAvatarStopAnimation(llList2Key(positions,i),llList2String(currentAn,3+i*3)); }
  211. integer indexAn=llListFindList(anData,[name]);
  212. list nextAn=[currentGroup]+llList2List(anData,indexAn,indexAn+anStride-1);
  213. integer have=llGetListLength(positions);
  214. integer need=llRound(((float)llGetListLength(nextAn)-3.0)/3.0);
  215. while(have<need) { positions=[]+positions+[NULL_KEY];have++; }
  216. while ((have>need) && (llListFindList(positions,[NULL_KEY])>=0)) { positions=[]+llDeleteSubList(positions,llListFindList(positions,[NULL_KEY]),llListFindList(positions,[NULL_KEY]));have--; }
  217. if (have>need)
  218. {
  219. llSay(0,"Encountered an error in attempting to start a new animation: there are currently too many users seated. You will need to reduce the number of seated users by "+(string)(have-need)+" to play it, or select a different group of animations");
  220. return;
  221. }
  222. i=llGetListLength(positions);
  223. while (--i>=0)
  224. {
  225. key who=llList2Key(positions,i);
  226. if (who!=NULL_KEY)
  227. {
  228. osAvatarPlayAnimation(who,llList2String(nextAn,3+i*3));
  229. setPosition(who,getUserLink(who),llList2Vector(nextAn,4+i*3),llList2Rot(nextAn,5+i*3));
  230. if (myState=="EDIT") setHandle(llList2Key(editHandles,i),llList2Vector(nextAn,4+i*3),llList2Rot(nextAn,5+i*3));
  231. }
  232. }
  233. currentAn=[]+nextAn;
  234. llMessageLinked(LINK_THIS,0,"GLOBAL_NEXT_AN|"+llList2String(currentAn,2),llDumpList2String(positions,"|"));
  235. if (autoOn) llSensorRepeat("THIS_WILL_NEVER_RETURN_A_SENSOR_RESULT",NULL_KEY,AGENT,0.001,0.0,autoTimer);
  236. }
  237. doSynch()
  238. {
  239. integer i=llGetListLength(positions);
  240. while (--i>=0)
  241. {
  242. key who=llList2Key(positions,i);
  243. if (who!=NULL_KEY)
  244. {
  245. osAvatarStopAnimation(who,llList2String(currentAn,3+i*3));
  246. osAvatarPlayAnimation(who,llList2String(currentAn,3+i*3));
  247. }
  248. }
  249. llMessageLinked(LINK_THIS,0,"GLOBAL_ANIMATION_SYNCH_CALLED",llDumpList2String(positions,"|"));
  250. }
  251. doSwapPositions(integer indFrom,integer indTo)
  252. {
  253. key whoFrom=llList2Key(positions,indFrom);
  254. if (whoFrom!=NULL_KEY)
  255. {
  256. osAvatarStopAnimation(whoFrom,llList2String(currentAn,3+indFrom*3));
  257. setPosition(whoFrom,getUserLink(whoFrom),llList2Vector(currentAn,4+indTo*3),llList2Rot(currentAn,5+indTo*3));
  258. }
  259. key whoTo=llList2Key(positions,indTo);
  260. if (whoTo!=NULL_KEY)
  261. {
  262. osAvatarStopAnimation(whoTo,llList2String(currentAn,3+indTo*3));
  263. setPosition(whoTo,getUserLink(whoTo),llList2Vector(currentAn,4+indFrom*3),llList2Rot(currentAn,5+indFrom*3));
  264. }
  265. positions=[]+llListReplaceList(positions,[whoFrom],indTo,indTo);
  266. positions=[]+llListReplaceList(positions,[whoTo],indFrom,indFrom);
  267. doSynch();
  268. }
  269. doChangeUser(key who)
  270. {
  271. if (user!=NULL_KEY) llRegionSayTo(user,0,llGetUsername(who)+" has now taken control of me");
  272. user=who;
  273. buildGroupList();
  274. specials=[];
  275. llMessageLinked(LINK_THIS,0,"GLOBAL_NEW_USER_ASSUMED_CONTROL|"+(string)user,llDumpList2String(positions,"|"));
  276. if (llListFindList(groupList,[currentGroup])==-1) loadGroup(llList2String(groupList,0));
  277. else if (autoOn) showGroupsMenu();
  278. else if (showGroupsMenuFirst) showGroupsMenu();
  279. else showAnMenu();
  280. }
  281. doQuit()
  282. {
  283. if (myState=="EDIT") removeHandles();
  284. integer i=llGetListLength(positions);
  285. while (--i>=0)
  286. {
  287. key who=llList2Key(positions,i);
  288. if (who!=NULL_KEY)
  289. {
  290. if (osIsNpc(who)) osNpcRemove(who);
  291. else
  292. {
  293. llRegionSayTo(who,0,"Quit called");
  294. llUnSit(who);
  295. }
  296. }
  297. }
  298. }
  299. loadGroup(string name)
  300. {
  301. if (name==currentGroup)
  302. {
  303. if (user!=NULL_KEY) showAnMenu();
  304. return;
  305. }
  306. integer indToLoad=llListFindList(invGroups,[name])-3;
  307. integer newUserCount=llList2Integer(invGroups,indToLoad+1);
  308. if (newUserCount<llGetListLength(positions))
  309. {
  310. integer agents;
  311. integer a=llGetListLength(positions);
  312. while (--a>-1) { if (llList2Key(positions,a)!=NULL_KEY) agents++; }
  313. if (agents>newUserCount)
  314. {
  315. llRegionSayTo(user,0,"You cannot use animations from the "+name+" group at the moment because it is for a maximum of "+(string)newUserCount+" users and there are currently "+(string)agents+" seated. Please select a different group of animations, remove NPCs, or ask someone to stand.");
  316. if (user!=NULL_KEY) startListening();
  317. return;
  318. }
  319. }
  320. currentGroup=name;
  321. anData=[]+llParseString2List(osGetNotecard(llList2String(invGroups,indToLoad)),["|","\n"],[""]);
  322. anStride=2+(3*newUserCount);
  323. anPage=0;
  324. anList=llList2ListStrided(anData,0,-1,anStride);
  325. if (autoOn && (myState=="RUNNING"))
  326. {
  327. playAnimation(llList2String(anList,0));
  328. if (user!=NULL_KEY)
  329. {
  330. llRegionSayTo(user,0,"Now automatically playing animations from "+name);
  331. showGroupsMenu();
  332. }
  333. }
  334. else if (user!=NULL_KEY) showAnMenu();
  335. }
  336. loadConfig()
  337. {
  338. integer i;
  339. float n;
  340. string sParam;
  341. string sValue;
  342. list lParams=[];
  343. for(i=0;i<osGetNumberOfNotecardLines(gs_ConfigName); i++) {
  344. lParams=llParseString2List(osGetNotecardLine(gs_ConfigName,i),["="],"");
  345. if (llGetListLength(lParams)==2){
  346. sParam=llList2String(lParams,0);
  347. sValue=llList2String(lParams,1);
  348. if ((sParam=="defaultGroup") || (sParam=="DefaultGroup")) {
  349. defaultGroup=sValue;
  350. }else if ((sParam=="resetOnQuit")||(sParam=="ResetOnQuit")) {
  351. resetOnQuit=(sValue=="TRUE");
  352. }else if ((sParam=="ownerUseReq")||(sParam=="OwnerUseReq")) {
  353. ownerUseReq=(sValue=="TRUE");
  354. }else if ((sParam=="ownerOnlyMenus")||(sParam=="OwnerOnlyMenus")) {
  355. ownerOnlyMenus=(sValue=="TRUE");
  356. }else if ((sParam=="ownerUseUnlocksPerms")||(sParam=="OwnerUseUnlockPerms")) {
  357. ownerUseUnlocksPerms=(sValue=="TRUE");
  358. }else if ((sParam=="autoTimer")||(sParam=="AutoTimerValue")) {
  359. autoTimer=(float)sValue;
  360. }else if ((sParam=="baseAn")||(sParam=="BaseAnimation")) {
  361. baseAn=sValue;
  362. }else if ((sParam=="showGroupsMenuFirst") || (sParam=="ShowGroupsMenuFirst")) {
  363. showGroupsMenuFirst=(sValue=="TRUE");
  364. }else if ((sParam=="allowSoloNPC") || (sParam=="AllowSoloNPC")) {
  365. allowSoloNPC=(sValue=="TRUE");
  366. }else {
  367. llOwnerSay("Warn: Unable to parse user defined configuration "+llDumpList2String(lParams,"="));
  368. }
  369. }else if (llGetListLength(lParams)!=0){
  370. llOwnerSay("Warn: Skipping user defined configuration line "+(string)i+" "+llDumpList2String(lParams,"="));
  371. }
  372. }
  373. }
  374. buildGroupList()
  375. {
  376. groupList=[];
  377. groupPage=0;
  378. integer i;
  379. while (i<llGetListLength(invGroups))
  380. {
  381. if ( ( llList2String(invGroups,i+2)=="A" ) || ( (llList2String(invGroups,i+2)=="G") && llSameGroup(user)) || (user==llGetOwner()) || ( ownerUseUnlocksPerms && (llListFindList(positions,[llGetOwner()])>=0) ) ) groupList=[]+groupList+[llList2String(invGroups,i+3)];
  382. i+=invGroupStride;
  383. }
  384. }
  385. buildNpcList()
  386. {
  387. npcList=[];
  388. npcPage=0;
  389. integer i;
  390. while (i<llGetListLength(invNpc))
  391. {
  392. if ( ( llList2String(invNpc,i+1)=="A" ) || ( (llList2String(invNpc,i+2)=="G") && llSameGroup(user)) || (user==llGetOwner()) || ( ownerUseUnlocksPerms && (llListFindList(positions,[llGetOwner()])>=0) ) ) npcList=[]+npcList+[llList2String(invNpc,i+2)];
  393. i+=invNpcStride;
  394. }
  395. showAddNpcMenu();
  396. }
  397. buildInventoryLists()
  398. {
  399. invGroups=[];
  400. invNpc=[];
  401. integer i=llGetInventoryNumber(INVENTORY_NOTECARD);
  402. while (--i>-1)
  403. {
  404. string name=llGetInventoryName(INVENTORY_NOTECARD,i);
  405. if (llSubStringIndex(name,".menu")==0)
  406. {
  407. if (llListFindList(invGroups,[llGetSubString(name,10,-1)])>-1) llOwnerSay("ERROR! You have two groups notecards where the name \""+llGetSubString(name,10,-1)+"\" is used for the button! Group button names must be unique");
  408. else invGroups=[]+[name,(integer)(llGetSubString(name,7,7)),llGetSubString(name,8,8),llGetSubString(name,10,-1)]+invGroups;
  409. }
  410. else if (llSubStringIndex(name,".NPC")==0)
  411. {
  412. if (llListFindList(invNpc,[llGetSubString(name,8,-1)])>-1) llOwnerSay("ERROR! You have two NPC notecards where the NPC name is \""+llGetSubString(name,8,-1)+"\" but NPC names must be unique");
  413. else invNpc=[]+[name,llGetSubString(name,6,6),llGetSubString(name,8,-1)]+invNpc;
  414. }
  415. else if (llSubStringIndex(name,gs_ConfigName)==0)
  416. {
  417. gi_HaveConfig=1;
  418. }
  419. }
  420. }
  421. saveCard(string cardName)
  422. {
  423. if (llGetInventoryType(cardName)==INVENTORY_NOTECARD)
  424. {
  425. llRemoveInventory(cardName);
  426. llSleep(0.25);
  427. }
  428. integer i;
  429. integer l=llGetListLength(anData);
  430. string dataToStore;
  431. while (i<l)
  432. {
  433. dataToStore+=llDumpList2String(llList2List(anData,i,i+anStride-1),"|")+"\n";
  434. i+=anStride;
  435. }
  436. osMakeNotecard(cardName,dataToStore);
  437. llMessageLinked(LINK_THIS,0,"GLOBAL_EDIT_STORE_TO_CARD|"+cardName,llDumpList2String(positions,"|"));
  438. }
  439. integer getUserLink(key who)
  440. {
  441. integer ret=FALSE;
  442. if (who!=NULL_KEY)
  443. {
  444. integer link=llGetNumberOfPrims();
  445. while ((link>1) && (ret==FALSE))
  446. {
  447. key this=llGetLinkKey(link);
  448. if(this==who) ret=link;
  449. link--;
  450. }
  451. }
  452. return ret;
  453. }
  454. startListening()
  455. {
  456. llDialog(user,txtDia,llList2List(butDia,9,11)+llList2List(butDia,6,8)+llList2List(butDia,3,5)+llList2List(butDia,0,2),myChannel);
  457. }
  458. string currentlyPlaying()
  459. {
  460. string strToReturn="Currently playing: "+llList2String(currentAn,0)+" > "+llList2String(currentAn,1);
  461. if (autoOn) strToReturn+="\nAUTO mode is on and set to "+(string)llRound(autoTimer)+" seconds";
  462. return strToReturn;
  463. }
  464. persistChanges()
  465. {
  466. integer u=llGetListLength(positions);
  467. while(--u>=0)
  468. {
  469. list avData=getPosition(llList2Key(positions,u),getUserLink(llList2Key(positions,u)));
  470. vector pos=llList2Vector(avData,0);
  471. rotation rot=llList2Rot(avData,1);
  472. string strPos="<"+trimF(pos.x)+","+trimF(pos.y)+","+trimF(pos.z)+">";
  473. string strRot="<"+trimF(rot.x)+","+trimF(rot.y)+","+trimF(rot.z)+","+trimF(rot.s)+">";
  474. currentAn=[]+llListReplaceList(currentAn,[strPos,strRot],4+u*3,5+u*3);
  475. }
  476. integer anIndex=llListFindList(anData,llList2List(currentAn,1,1));
  477. if(gi_NC_PROP_CHANGED) { // NC_PROPS
  478. string old_cmd=llList2String(currentAn,2);
  479. integer NC_PROP_data_start=llSubStringIndex(old_cmd,"NC_PROP{")+8; // find start
  480. // test is little weird, as i added +8 to compensate for search string length
  481. if (NC_PROP_data_start != 7) {
  482. // search ending delimiter in rest of string
  483. integer NC_PROP_data_end=NC_PROP_data_start+llSubStringIndex(llGetSubString(old_cmd, NC_PROP_data_start,-1),"}");
  484. string new_cmd=llGetSubString(old_cmd, 0, NC_PROP_data_start -1) + gs_NC_PROP_DATA + llGetSubString(old_cmd, NC_PROP_data_end,-1);
  485. currentAn=[]+llListReplaceList(currentAn,[new_cmd],2,2);
  486. }
  487. gi_NC_PROP_CHANGED=FALSE;
  488. }
  489. anData=[]+llListReplaceList(anData,llList2List(currentAn,1,-1),anIndex,anIndex+anStride-1);
  490. llMessageLinked(LINK_THIS,0,"GLOBAL_EDIT_PERSIST_CHANGES",llDumpList2String(positions,"|"));
  491. }
  492. rezHandles()
  493. {
  494. if (llGetListLength(editHandles)<llGetListLength(positions)) llRezObject(handleName,llGetPos(),ZERO_VECTOR,ZERO_ROTATION,0);
  495. else
  496. {
  497. rezzingHandles=FALSE;
  498. integer h=llGetListLength(editHandles);
  499. while (--h>=0) { osSetPrimitiveParams( llList2Key(editHandles,h),[PRIM_SIZE,handleSize,PRIM_COLOR,ALL_SIDES,llList2Vector(handleColours,h),handleAlpha,PRIM_TEXT,"pos "+(string)(h+1),llList2Vector(handleColours,h),1.0,PRIM_NAME,"pos "+(string)(h+1)]); }
  500. playAnimation(llList2String(currentAn,1));
  501. llSetTimerEvent(editTimer);
  502. doSynch();
  503. showEditMenu();
  504. }
  505. }
  506. removeHandles()
  507. {
  508. llSetTimerEvent(0.0);
  509. myState="RUNNING";
  510. llMessageLinked(LINK_THIS,0,"GLOBAL_NOTICE_LEAVING_EDIT_MODE",llDumpList2String(positions,"|"));
  511. integer l=llGetListLength(editHandles);
  512. while (--l>=0) { osMessageObject(llList2Key(editHandles,l),"HANDLE_DIE"); }
  513. editHandles=[];
  514. showOptionsMenu();
  515. }
  516. setHandle(key prim, vector relPos, rotation relRot)
  517. {
  518. vector pos=relPos*llGetRot()+llGetPos();
  519. rotation rot=relRot*llGetRot();
  520. osSetPrimitiveParams(prim,[PRIM_POSITION,pos,PRIM_ROTATION,rot]);
  521. }
  522. setPosition(key who, integer link, vector pos, rotation rot)
  523. {
  524. vector size = llGetAgentSize(who);
  525. float fAdjust = ((((0.008906 * size.z) + -0.049831) * size.z) + 0.088967) * size.z;
  526. llSetLinkPrimitiveParamsFast(link,[PRIM_POS_LOCAL, ((pos + <0.0, 0.0, 0.4>) - (llRot2Up(rot) * fAdjust)), PRIM_ROT_LOCAL, rot]);
  527. }
  528. list getPosition(key who, integer link)
  529. {
  530. vector size = llGetAgentSize(who);
  531. float fAdjust = ((((0.008906 * size.z) + -0.049831) * size.z) + 0.088967) * size.z;
  532. list avData=llGetLinkPrimitiveParams(link,[PRIM_POS_LOCAL,PRIM_ROT_LOCAL]);
  533. vector avPos=llList2Vector(avData,0);
  534. rotation avRot=llList2Rot(avData,1);
  535. vector avPosUnadjusted=(avPos - <0.0, 0.0, 0.4>) + (llRot2Up(avRot) * fAdjust);
  536. return [avPosUnadjusted,avRot];
  537. }
  538. list regToRel(vector regionPos,rotation regionRot)
  539. {
  540. vector relPos=(regionPos - llGetPos()) / llGetRot();
  541. rotation relRot=regionRot/ llGetRot();
  542. return [relPos,relRot];
  543. }
  544. list relToReg(vector refPos,rotation refRot)
  545. {
  546. vector regionPos=refPos*llGetRot()+llGetPos();
  547. rotation regionRot=refRot*llGetRot();
  548. return [regionPos,regionRot];
  549. }
  550. string trimF(float value)
  551. {
  552. integer newVal=llRound(value*10000);
  553. integer negFlag=FALSE;
  554. if (newVal<0)
  555. {
  556. negFlag=TRUE;
  557. newVal*=-1;
  558. }
  559. integer strLength;
  560. string retStr;
  561. if (newVal==0) retStr="0";
  562. else if (newVal<10) retStr="0.000"+(string)newVal;
  563. else if (newVal<100) retStr="0.00"+(string)newVal;
  564. else if (newVal<1000) retStr="0.0"+(string)newVal;
  565. else if (newVal<10000) retStr="0."+(string)newVal;
  566. else
  567. {
  568. retStr=(string)newVal;
  569. strLength=llStringLength(retStr);
  570. retStr=llGetSubString(retStr,0,strLength-5)+"."+llGetSubString(retStr,strLength-4,strLength-1);
  571. }
  572. while (llGetSubString(retStr,strLength,strLength)=="0")
  573. {
  574. retStr=llGetSubString(retStr,0,strLength-1);
  575. strLength-=1;
  576. }
  577. if (negFlag) retStr="-"+retStr;
  578. return retStr;
  579. }
  580. default
  581. {
  582. state_entry()
  583. {
  584. if (llGetAttached()) return;
  585. if (llGetLinkNumber()>1)
  586. {
  587. myState="ERROR";
  588. llOwnerSay("ERROR! The main PMAC controller script must always be located in the root prim of a linkset!");
  589. return;
  590. }
  591. myChannel=0x80000000|(integer)("0x"+(string)llGetKey());
  592. user=NULL_KEY;
  593. buildInventoryLists();
  594. if (gi_HaveConfig)
  595. loadConfig();
  596. if (llGetInventoryType(baseAn)!=INVENTORY_ANIMATION)
  597. {
  598. llOwnerSay("ERROR! Unable to find the base priority 1 animation to use for synch: "+baseAn);
  599. myState="ERROR";
  600. return;
  601. }
  602. if (llListFindList(invGroups,[defaultGroup])==-1)
  603. {
  604. myState="ERROR";
  605. llOwnerSay("ERROR! Unable to find the specified default group \""+defaultGroup+"\" in inventory. Make sure you supplied the simple group name, not the full card name");
  606. return;
  607. }
  608. loadGroup(defaultGroup);
  609. integer i=llList2Integer(invGroups,llListFindList(invGroups,[defaultGroup])-2);
  610. positions=[];
  611. while (--i>=0) { positions=[]+positions+[NULL_KEY]; }
  612. currentAn=[]+[currentGroup]+llList2List(anData,0,anStride-1);
  613. myState="READY";
  614. llMessageLinked(LINK_THIS,0,"GLOBAL_SYSTEM_RESET",NULL_KEY);
  615. llSitTarget(<0.0,0,0.001>,ZERO_ROTATION);
  616. llOwnerSay("Initialization complete and ready to use");
  617. }
  618. on_rez(integer num)
  619. {
  620. llResetScript();
  621. }
  622. object_rez(key id)
  623. {
  624. if (!rezzingHandles) return;
  625. editHandles=[]+editHandles+[id];
  626. rezHandles();
  627. }
  628. sensor(integer num)
  629. {
  630. llOwnerSay("ERROR! Sensor event inexplicably returned a result!");
  631. llSensorRemove();
  632. autoOn=FALSE;
  633. }
  634. no_sensor()
  635. {
  636. if (!autoOn)
  637. {
  638. llOwnerSay("ERROR! Sensor repeat triggered but auto mode is off. Figure out how this can happen and fix. Sensor removed");
  639. llSensorRemove();
  640. return;
  641. }
  642. if (myState!="RUNNING")
  643. {
  644. llOwnerSay("ERROR! Sensor repeat triggered while not in normal running state: Please figure out how this happened and fix. Sensor removed and auto turned off.\nState was=: "+myState);
  645. autoOn=FALSE;
  646. llSensorRemove();
  647. return;
  648. }
  649. integer i=llListFindList(anList,[llList2String(currentAn,1)]);
  650. if (i==-1)
  651. {
  652. llOwnerSay("ERROR! Auto timer was unable to determine the index of the currently playing animation!");
  653. llSensorRemove();
  654. autoOn=FALSE;
  655. }
  656. else
  657. {
  658. i++;
  659. if (i>=llGetListLength(anList)) i=0;
  660. playAnimation(llList2String(anList,i));
  661. }
  662. }
  663. timer()
  664. {
  665. if(myState=="EDIT")
  666. {
  667. llSetTimerEvent(0.0);
  668. integer l=llGetListLength(positions);
  669. while (--l>-1)
  670. {
  671. key who=llList2Key(positions,l);
  672. if (who==NULL_KEY)
  673. {
  674. llOwnerSay("ERROR! NULL_KEY user while processing timer event edit mode. Leaving edit mode without saving changes.");
  675. removeHandles();
  676. return;
  677. }
  678. if (llGetAgentSize(who)==ZERO_VECTOR)
  679. {
  680. llOwnerSay("ERROR! Cannot detect user in region while processing timer event edit mode. Leaving edit mode without saving changes");
  681. removeHandles();
  682. return;
  683. }
  684. list handleData=llGetObjectDetails(llList2Key(editHandles,l),[OBJECT_POS,OBJECT_ROT]);
  685. if (llGetListLength(handleData)==0)
  686. {
  687. llOwnerSay("ERROR! Unable to detect a handle! Leaving edit mode without saving changes");
  688. removeHandles();
  689. return;
  690. }
  691. handleData=[]+regToRel(llList2Vector(handleData,0),llList2Rot(handleData,1));
  692. setPosition(who,getUserLink(who),llList2Vector(handleData,0),llList2Rot(handleData,1));
  693. }
  694. llSetTimerEvent(editTimer);
  695. }
  696. else llSetTimerEvent(0.0);
  697. }
  698. link_message(integer sender,integer num,string message,key command)
  699. {
  700. if (num!=-1) return;
  701. if (message=="MAIN_RESUME_MAIN_DIALOG") showOptionsMenu();
  702. else if (llSubStringIndex(message,"MAIN_REGISTER_MENU_BUTTON")==0)
  703. {
  704. string buttonName=llList2String(llParseString2List(message,["|"],[]),1);
  705. integer locationToAdd=llListFindList(specials,[buttonName]);
  706. if (locationToAdd==-1) specials=[]+specials+[buttonName,command];
  707. else specials=[]+llListReplaceList(specials,[buttonName,command],locationToAdd,locationToAdd+1);
  708. specials=[]+llListSort(specials,2,TRUE);
  709. }
  710. else if (llSubStringIndex(message,"MAIN_UNREGISTER_MENU_BUTTON")==0)
  711. {
  712. string buttonName=llList2String(llParseString2List(message,["|"],[]),1);
  713. integer locationToKill=llListFindList(specials,[buttonName,command]);
  714. if (locationToKill==-1) return;
  715. else specials=[]+llDeleteSubList(specials,locationToKill,locationToKill+1);
  716. specials=[]+llListSort(specials,2,TRUE);
  717. }
  718. else if (llSubStringIndex(message,"NC_PROP_UPDATE")==0) // NC_PROP addon
  719. {
  720. gi_NC_PROP_CHANGED=TRUE;
  721. gs_NC_PROP_DATA=llGetSubString(message,15,-1);
  722. }
  723. }
  724. changed (integer change)
  725. {
  726. if (change & CHANGED_LINK)
  727. {
  728. if (llGetLinkNumber()>1)
  729. {
  730. llOwnerSay("ERROR! You have changed the linkset and the PMAC main script is no longer located in the root prim!");
  731. myState="ERROR";
  732. return;
  733. }
  734. integer i=llGetNumberOfPrims();
  735. integer l=llGetObjectPrimCount(llGetKey());
  736. list seated;
  737. integer realAvi;
  738. while (i>l)
  739. {
  740. key who=llGetLinkKey(i);
  741. if ((myState=="ERROR") || (myState=="INITIALIZING"))
  742. {
  743. if (osIsNpc(who)) osNpcRemove(who);
  744. else
  745. {
  746. if (myState=="ERROR") llRegionSayTo(who,0,"Sorry, I have encountered an error and must shut down until it is corrected");
  747. else llRegionSayTo(who,0,"Sorry, you cannot sit while I am initializing. Please wait a moment and try again.");
  748. llUnSit(who);
  749. }
  750. }
  751. else
  752. {
  753. seated=[]+[who]+seated;
  754. if (!osIsNpc(who)) realAvi++;
  755. if (llListFindList(positions,[who])==-1)
  756. {
  757. // new sitter
  758. integer indexToSit=llListFindList(positions,[NULL_KEY]);
  759. if (ownerUseReq && (who!=llGetOwner()) && (llListFindList(positions,[llGetOwner()])==-1))
  760. {
  761. llRegionSayTo(who,0,"Sorry, the system is set to require that the owner is using me before anyone else may sit.");
  762. llUnSit(who);
  763. }
  764. else if (indexToSit==-1)
  765. {
  766. llRegionSayTo(who,0,"Sorry, there are no available positions for you to occupy. Please wait for someone to stand");
  767. llUnSit(who);
  768. }
  769. else
  770. {
  771. positions=[]+llListReplaceList(positions,[who],indexToSit,indexToSit);
  772. llSleep(0.2);
  773. list anToStop=llGetAnimationList(who);
  774. osAvatarPlayAnimation(who,baseAn);
  775. integer stop=llGetListLength(anToStop);
  776. key dontStop=llGetInventoryKey(baseAn);
  777. while (--stop>-1) { osAvatarStopAnimation(who,llList2Key(anToStop,stop)); }
  778. if (myState=="READY")
  779. {
  780. if ((osIsNpc(who)) && (!allowSoloNPC))
  781. {
  782. llSay(0,"Sorry, an NPC cannot sit until there is a human user in control. Unsitting your NPC.");
  783. llUnSit(who);
  784. positions=[]+llListReplaceList(positions,[NULL_KEY],indexToSit,indexToSit);
  785. osAvatarStopAnimation(who,baseAn);
  786. }
  787. else
  788. {
  789. myState="RUNNING";
  790. llMessageLinked(LINK_THIS,0,"GLOBAL_START_USING",llDumpList2String(positions,"|"));
  791. playAnimation(llList2String(currentAn,1));
  792. }
  793. }
  794. else playAnimation(llList2String(currentAn,1));
  795. }
  796. }
  797. }
  798. i--;
  799. }
  800. i=llGetListLength(positions);
  801. while (--i>=0)
  802. {
  803. key who=llList2Key(positions,i);
  804. if (who!=NULL_KEY)
  805. {
  806. if ((!realAvi)&&osIsNpc(who))
  807. {
  808. if (!allowSoloNPC)
  809. {
  810. osNpcRemove(who);
  811. positions=[]+llListReplaceList(positions,[NULL_KEY],i,i);
  812. }
  813. }
  814. else if (llListFindList(seated,[who])==-1)
  815. {
  816. if (myState=="EDIT")
  817. {
  818. llOwnerSay("WARNING! Someone stood! Leaving edit mode and no changes will be stored to card");
  819. removeHandles();
  820. if (who!=llGetOwner())showOptionsMenu();
  821. }
  822. if (llGetAgentSize(who)!=ZERO_VECTOR)
  823. {
  824. osAvatarPlayAnimation(who,"stand");
  825. osAvatarStopAnimation(who,llList2String(currentAn,3+i*3));
  826. osAvatarStopAnimation(who,baseAn);
  827. if (who==user) user=NULL_KEY;
  828. }
  829. positions=[]+llListReplaceList(positions,[NULL_KEY],i,i);
  830. llMessageLinked(LINK_THIS,0,"GLOBAL_USER_STOOD|"+(string)i+"|"+(string)who,llDumpList2String(positions,"|"));
  831. }
  832. }
  833. }
  834. if (!realAvi)
  835. {
  836. if (diaHandle)
  837. {
  838. llListenRemove(diaHandle);
  839. diaHandle=FALSE;
  840. }
  841. if (llGetListLength(seated)<1)
  842. {
  843. if (autoOn) llSensorRemove();
  844. myState="READY";
  845. llMessageLinked(LINK_THIS,0,"GLOBAL_SYSTEM_GOING_DORMANT",NULL_KEY);
  846. if (resetOnQuit) llResetScript();
  847. }
  848. }
  849. }
  850. else if (change & CHANGED_REGION_START) llResetScript();
  851. else if (change & CHANGED_OWNER) llResetScript();
  852. }
  853. touch_start(integer num)
  854. {
  855. if (llGetAttached()) return;
  856. key who=llDetectedKey(0);
  857. if (osIsNpc(llDetectedKey(0))) return;
  858. if (myState=="ERROR")
  859. {
  860. if (who==llGetOwner()) llOwnerSay("PMAC somehow entered ERROR state. Please check your chat log for one or more messages indicating the nature of the error, correct it, then reset the script");
  861. else llRegionSayTo(who,0,"Sorry, I encountered an error and am waiting for the owner to correct the issue and restart me");
  862. }
  863. else if (myState=="INITIALIZING") llRegionSayTo(who,0,"Please wait until initialization is complete, then try again");
  864. else if (myState=="READY") llRegionSayTo(who,0,"Please sit to begin using me");
  865. else if (myState=="EDIT")
  866. {
  867. if (who!=llGetOwner()) llRegionSayTo(who,0,"Sorry, the owner is currently editting positions and must remain in control of the dialog until this is complete.");
  868. else startListening();
  869. }
  870. else if (myState=="RUNNING")
  871. {
  872. if (llListFindList(positions,[who])==-1) llRegionSayTo(who,0,"Only current users may access the controls. Please sit, then try again");
  873. else if (ownerOnlyMenus && (who!=llGetOwner())) llRegionSayTo(who,0,"Sorry, this item is currently set to only allow the owner to access the controls.");
  874. else if (who!=user)
  875. {
  876. if (user==NULL_KEY)
  877. {
  878. if (!diaHandle) diaHandle=llListen(myChannel,"",NULL_KEY,"");
  879. doChangeUser(who);
  880. }
  881. else llDialog(who,"Please confirm that you want to take control of me",["TAKE CONTROL","CANCEL"],myChannel);
  882. }
  883. else startListening();
  884. }
  885. else llOwnerSay("ERROR! Unexpected state when touched. State is: "+myState);
  886. }
  887. listen (integer channel, string name, key who, string message)
  888. {
  889. if (who!=user)
  890. {
  891. if (message=="TAKE CONTROL")
  892. {
  893. if (myState!="RUNNING")
  894. {
  895. if (who==llGetOwner()) llOwnerSay("Cannot give you control because I am not in RUNNING state. Current state is: "+myState);
  896. else llRegionSayTo(who,0,"Sorry, you cannot take control at the moment because I am not currently in normal operation mode.");
  897. return;
  898. }
  899. doChangeUser(who);
  900. }
  901. else if (message=="CANCEL") return;
  902. else return;
  903. }
  904. else if (message=="-") startListening();
  905. else if (message=="SYNCH")
  906. {
  907. doSynch();
  908. startListening();
  909. }
  910. else if (message=="OPTIONS") showOptionsMenu();
  911. else if (message=="GROUPS") showGroupsMenu();
  912. else if (message=="<< BACK") loadGroup(llList2String(currentAn,0));
  913. else if (message=="QUIT") doQuit();
  914. else if (menu=="MENU_GROUPS")
  915. {
  916. if ((message=="< PREV") || (message=="NEXT >"))
  917. {
  918. if (message=="< PREV") groupPage-=6;
  919. else groupPage+=6;
  920. if (groupPage>=llGetListLength(groupList)) groupPage=0;
  921. else if (groupPage<=-6) groupPage=llGetListLength(groupList)-6;
  922. if (groupPage<0) groupPage=0;
  923. showGroupsMenu();
  924. }
  925. else loadGroup(message);
  926. }
  927. else if (menu=="MENU_ANIM")
  928. {
  929. if ((message=="< PREV") || (message=="NEXT >"))
  930. {
  931. if (message=="< PREV") anPage-=6;
  932. else anPage+=6;
  933. if (anPage>=llGetListLength(anList)) anPage=0;
  934. else if (anPage<=-6) anPage=llGetListLength(anList)-6;
  935. if (anPage<0) anPage=0;
  936. showAnMenu();
  937. }
  938. else
  939. {
  940. playAnimation(message);
  941. showAnMenu();
  942. }
  943. }
  944. else if (menu=="MENU_ADD_NPC")
  945. {
  946. if (message=="CANCEL") showOptionsMenu();
  947. else if (llListFindList(positions,[NULL_KEY])==-1)
  948. {
  949. llRegionSayTo(user,0,"Cannot add a NPC as there are no longer any vacant positions");
  950. showOptionsMenu();
  951. }
  952. else if ((message=="< PREV") || (message=="NEXT >"))
  953. {
  954. if (message=="< PREV") npcPage-=9;
  955. else npcPage+=9;
  956. if (npcPage>=llGetListLength(npcList)) npcPage=0;
  957. else if (npcPage<=-9) npcPage=llGetListLength(npcList)-9;
  958. if (npcPage<0) npcPage=0;
  959. showAddNpcMenu();
  960. }
  961. else
  962. {
  963. string npcToLoad=llList2String(invNpc,llListFindList(invNpc,[message])-2);
  964. if (llGetInventoryType(npcToLoad)!=INVENTORY_NOTECARD)
  965. {
  966. llRegionSayTo(user,0,"ERROR! Unable to locate the appearance notecard for this NPC.\nButton was: "+message+"\nCard should be: "+npcToLoad);
  967. startListening();
  968. return;
  969. }
  970. list thisNames=llParseString2List(message,[" "],[]);
  971. if (llGetListLength(thisNames)==1) thisNames=[]+thisNames+["~"];
  972. key npc=osNpcCreate(llList2String(thisNames,0),llList2String(thisNames,1),llGetPos()+<0.0,0.0,2.0>,npcToLoad,OS_NPC_SENSE_AS_AGENT);
  973. osNpcSit(npc,llGetKey(),OS_NPC_SIT_NOW);
  974. showOptionsMenu();
  975. }
  976. }
  977. else if (menu=="MENU_SWAP_TO_POSITION")
  978. {
  979. if (message=="CANCEL") showOptionsMenu();
  980. else doSwapPositions(llListFindList(positions,[user]),((integer)message)-1);
  981. showOptionsMenu();
  982. }
  983. else if (menu=="MENU_UNSIT_POSITION")
  984. {
  985. if (message=="CANCEL") showOptionsMenu();
  986. else
  987. {
  988. integer posToUnsit=(integer)message-1;
  989. key thisKey=llList2Key(positions,posToUnsit);
  990. if (osIsNpc(thisKey))
  991. {
  992. positions=[]+llListReplaceList(positions,[NULL_KEY],posToUnsit,posToUnsit);
  993. osNpcRemove(thisKey);
  994. }
  995. else llUnSit(thisKey);
  996. showOptionsMenu();
  997. }
  998. }
  999. else if (menu=="MENU_SELECT_AUTO_MODE")
  1000. {
  1001. if (message=="AUTO OFF")
  1002. {
  1003. autoOn=FALSE;
  1004. llSensorRemove();
  1005. llRegionSayTo(user,0,"Auto mode now off");
  1006. }
  1007. else if (message!="CANCEL")
  1008. {
  1009. autoOn=TRUE;
  1010. if(message!="AUTO ON") autoTimer=(float)message;
  1011. llSensorRepeat("THIS_WILL_NEVER_RETURN_A_SENSOR_RESULT",NULL_KEY,AGENT,0.001,0.0,autoTimer);
  1012. llRegionSayTo(user,0,"Auto mode is on and set to "+(string)llRound(autoTimer)+" seconds");
  1013. }
  1014. showOptionsMenu();
  1015. }
  1016. else if (menu=="MENU_SPECIALS")
  1017. {
  1018. if (message=="CANCEL") showOptionsMenu();
  1019. else if ((message=="< PREV") || (message=="NEXT >"))
  1020. {
  1021. if (message=="< PREV") specPage-=18;
  1022. else specPage+=18;
  1023. if (specPage>=llGetListLength(specials)) specPage=0;
  1024. else if (specPage<=-18) specPage=llGetListLength(specials)-18;
  1025. if (specPage<0) specPage=0;
  1026. showSpecialsMenu();
  1027. }
  1028. else llMessageLinked(LINK_THIS,0,llList2String(specials,llListFindList(specials,[message])+1)+"|"+user,llDumpList2String(positions,"|"));
  1029. }
  1030. else if (menu=="MENU_OPTIONS")
  1031. {
  1032. if (message=="AUTO") showAutoMenu();
  1033. else if (message=="SPECIAL") showSpecialsMenu();
  1034. else if (llSubStringIndex(message,"MENUS")==0)
  1035. {
  1036. ownerOnlyMenus=!ownerOnlyMenus;
  1037. if (ownerOnlyMenus) llRegionSayTo(who,0,"Menus are now locked. Only the owner can take control of me");
  1038. else llRegionSayTo(who,0,"Menus are now unlocked. Any user can now take control of me");
  1039. showOptionsMenu();
  1040. }
  1041. else if (message=="EDIT ON")
  1042. {
  1043. if (llListFindList(positions,[NULL_KEY])>=0)
  1044. {
  1045. llRegionSayTo(who,0,"Sorry, all positions must be filled before you can enter edit mode and there is currently at least one that is empty.");
  1046. showOptionsMenu();
  1047. }
  1048. else if (currentGroup!=llList2String(currentAn,0))
  1049. {
  1050. llRegionSayTo(user,0,"Cannot enter edit mode because the animation currently playing is not from the same notecard as your currently loaded group. Either select an animation from this card or load the group that the current animation belongs to.");
  1051. showOptionsMenu();
  1052. }
  1053. else
  1054. {
  1055. if (autoOn)
  1056. {
  1057. autoOn=FALSE;
  1058. llSensorRemove();
  1059. llOwnerSay("Auto mode switched off");
  1060. }
  1061. integer l=llGetListLength(positions);
  1062. while (--l>=0) { if (!osIsNpc(llList2Key(positions,l))) llRegionSayTo(llList2Key(positions,l),0,"The Owner has entered edit mode and all functions are temporarily disabled. Please do not stand while these adjustments are being made."); }
  1063. myState="EDIT";
  1064. llMessageLinked(LINK_THIS,0,"GLOBAL_NOTICE_ENTERING_EDIT_MODE",llDumpList2String(positions,"|"));
  1065. editHandles=[];
  1066. rezzingHandles=TRUE;
  1067. rezHandles();
  1068. }
  1069. }
  1070. else if (message=="SWAP")
  1071. {
  1072. integer i=llGetListLength(positions);
  1073. if (i==1)
  1074. {
  1075. llRegionSayTo(user,0,"The current animation group is for only one position so cannot swap positions.");
  1076. showOptionsMenu();
  1077. }
  1078. else if (i==2)
  1079. {
  1080. doSwapPositions(0,1);
  1081. showOptionsMenu();
  1082. }
  1083. else
  1084. {
  1085. txtDia="";
  1086. butDia=[];
  1087. while (--i>=0)
  1088. {
  1089. key thisKey=llList2Key(positions,i);
  1090. if (thisKey==user) txtDia="\n"+(string)(i+1)+". (you are curently here)"+txtDia;
  1091. else
  1092. {
  1093. butDia=[]+[(string)(i+1)]+butDia;
  1094. if (thisKey==NULL_KEY) txtDia="\n"+(string)(i+1)+". (empty)"+txtDia;
  1095. else txtDia="\n"+(string)(i+1)+". "+llGetUsername(thisKey)+txtDia;
  1096. }
  1097. }
  1098. txtDia=""+"SWAP POSITION\n\nSelect the position number you would like to swap with\n"+txtDia;
  1099. while (llGetListLength(butDia)<8) { butDia=[]+butDia+["-"]; }
  1100. butDia=[]+butDia+["CANCEL"];
  1101. while (llListFindList(butDia,["-","-","-"])>=0) { butDia=[]+llDeleteSubList(butDia,llListFindList(butDia,["-","-","-"]),llListFindList(butDia,["-","-","-"])+2); }
  1102. menu="MENU_SWAP_TO_POSITION";
  1103. startListening();
  1104. }
  1105. }
  1106. else if (message=="UNSIT")
  1107. {
  1108. list whoCanUnsit;
  1109. integer i=llGetListLength(positions);
  1110. while (--i>=0)
  1111. {
  1112. key thisKey=llList2Key(positions,i);
  1113. if ((thisKey!=user) && (thisKey!=llGetOwner()) && (thisKey!=NULL_KEY)) whoCanUnsit=[]+[i,thisKey]+whoCanUnsit;
  1114. }
  1115. integer l=llGetListLength(whoCanUnsit);
  1116. if (l==0)
  1117. {
  1118. llRegionSayTo(user,0,"There are no users you can remove");
  1119. showOptionsMenu();
  1120. }
  1121. else if (l==2)
  1122. {
  1123. integer thisInd=llList2Integer(whoCanUnsit,0);
  1124. key thisUser=llList2Key(whoCanUnsit,1);
  1125. if (osIsNpc(thisUser))
  1126. {
  1127. positions=[]+llListReplaceList(positions,[NULL_KEY],thisInd,thisInd);
  1128. osNpcRemove(thisUser);
  1129. }
  1130. else llUnSit(thisUser);
  1131. showOptionsMenu();
  1132. }
  1133. else
  1134. {
  1135. txtDia="UNSIT A USER\n\nSelect the user to unsit (selecting NPC it will remove it). The number is the position they currently occupy.\n";
  1136. butDia=[];
  1137. i=0;
  1138. while (i<l)
  1139. {
  1140. txtDia+="\n"+(string)(llList2Integer(whoCanUnsit,i)+1)+". "+llGetUsername(llList2Key(whoCanUnsit,i+1));
  1141. butDia=[]+butDia+[(string)(llList2Integer(whoCanUnsit,i)+1)];
  1142. i+=2;
  1143. }
  1144. while (llGetListLength(butDia)<8) { butDia=[]+butDia+["-"]; }
  1145. butDia=[]+butDia+["CANCEL"];
  1146. while (llListFindList(butDia,["-","-","-"])>=0) { butDia=[]+llDeleteSubList(butDia,llListFindList(butDia,["-","-","-"]),llListFindList(butDia,["-","-","-"])+2); }
  1147. menu="MENU_UNSIT_POSITION";
  1148. startListening();
  1149. }
  1150. }
  1151. else if (message=="ADD NPC")
  1152. {
  1153. if (llListFindList(positions,[NULL_KEY])==-1)
  1154. {
  1155. llRegionSayTo(user,0,"Cannot add a new NPC at this time as there are no vacant positions");
  1156. showOptionsMenu();
  1157. }
  1158. else buildNpcList();
  1159. }
  1160. else llOwnerSay("ERROR! Received unexpected message from Options menu: "+message);
  1161. }
  1162. else if (menu=="MENU_EDIT")
  1163. {
  1164. if (myState!="EDIT")
  1165. {
  1166. llOwnerSay("No longer in edit mode so unable to handle a response from that menu.");
  1167. showOptionsMenu();
  1168. }
  1169. llSetTimerEvent(0.0);
  1170. integer valid=TRUE;
  1171. integer check=llGetListLength(positions);
  1172. while (valid && (--check>=0)) { if (!getUserLink(llList2Key(positions,check))) valid=FALSE; }
  1173. if (!valid)
  1174. {
  1175. llOwnerSay("ERROR! A user appears to have stood. Cannot remain in edit mode unless all positions are occupied. Ignoring your selection, reverting to stored position for this animation, and leaving edit mode without saving any changes. Once all positions are filled once more you may return to edit mode to resume work or save the stored data.");
  1176. playAnimation(llList2String(currentAn,1));
  1177. removeHandles();
  1178. return;
  1179. }
  1180. if (message=="STORE ADDON") llMessageLinked(LINK_THIS,0,"GLOBAL_STORE_ADDON_NOTICE",llDumpList2String(positions,"|"));
  1181. else if (message=="REVERT THIS") playAnimation(llList2String(currentAn,1));
  1182. else
  1183. {
  1184. persistChanges();
  1185. if (message=="EDIT OFF") removeHandles();
  1186. else if ((message=="STORE THIS") || (message=="< PREV") || (message=="NEXT >"))
  1187. {
  1188. integer anToPlay=llListFindList(anList,[llList2String(currentAn,1)]);
  1189. if (message=="< PREV") anToPlay--;
  1190. else if (message=="NEXT >") anToPlay++;
  1191. if (anToPlay<0) anToPlay=llGetListLength(anList)-1;
  1192. if (anToPlay>=llGetListLength(anList)) anToPlay=0;
  1193. playAnimation(llList2String(anList,anToPlay));
  1194. }
  1195. else if ((message=="SAVE CARD") || (message=="SAVE NEW"))
  1196. {
  1197. string cardName=llList2String(invGroups,llListFindList(invGroups,[currentGroup])-3);
  1198. string strToSay;
  1199. if (message=="SAVE NEW")
  1200. {
  1201. integer num=2;
  1202. while (llGetInventoryType(cardName+(string)num)==INVENTORY_NOTECARD) { num++; }
  1203. cardName=""+cardName+(string)num;
  1204. currentGroup=llGetSubString(cardName,10,-1);
  1205. invGroups=[]+invGroups+[cardName,(integer)(llGetSubString(cardName,7,7)),llGetSubString(cardName,8,8),currentGroup];
  1206. groupList=[]+groupList+[currentGroup];
  1207. currentAn=[]+llListReplaceList(currentAn,[currentGroup],0,0);
  1208. strToSay="All data stored to a new notecard and now working with the newly created group: "+currentGroup;
  1209. }
  1210. else strToSay="All data now updated in notecard for the group: "+currentGroup;
  1211. saveCard(cardName);
  1212. llRegionSayTo(user,0,strToSay);
  1213. }
  1214. else llOwnerSay("ERROR! Message not expected in Edit menu handling: "+message);
  1215. }
  1216. if (myState=="EDIT")
  1217. {
  1218. llSetTimerEvent(editTimer);
  1219. showEditMenu();
  1220. }
  1221. }
  1222. }
  1223. }