SHARE
TWEET

PMAC 2.5 Core

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