Clubmaster Server Version

From Fire And Ice Grid
Revision as of 19:44, 19 August 2020 by Admindiamond (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Clubmaster Introduction

Clubmaster is a widely used dance system, this Covey Mod allows it to also work with remotes reducing sim load when multiple dance balls are required on the same sim
Notecards have two extensions in this repository. .xml is for XML style notecards - but should still be copy/pasted as notecards inworld .txt is for a TEXT style notecard - just a notecard inworld
The full repository can be found on GitHub https://github.com/manwapastorelli/Clubmaster

Repository Branches

There are two branches, one contains the original Clubmaster code by Aine Caoimhe, with the Covey Modified version in the master branch.

Assembly From Scratch

  1. Make a prim and place the "~Clubmaster poseball v2.lsl" script into the prim.
  2. Take this prim to your inventory and call it "~club poseball" with the quotation marks
  3. Make a new prim which will be the main dance ball
  4. Add the "~club poseball" into the dance balls inventory along with all other scripts and notecards from this repository.

Upgrade Existing Clubmaster V2

  1. Replace the existing Clubmaster control script with this version
  2. Make a prim for the remote and add the remote script into the inventory

Clubmaster Remotes Script

1 default
2 {  
3     touch_start(integer any)
4     {
5         key toucher = llDetectedKey(0);
6         integer RemoteChannel = -741285681;
7         llRegionSay(RemoteChannel, (string)toucher);
8     }
9 }


Amended Clubmaster Script

   1 // PARAMOUR CLUBMASTER DANCE CONTROLLER (OSSL) v2.0
   2 // Aine Caoimhe Sept 2014 (v2.0 Jan 2016)
   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 // See Read Me notecard for instructions
   8 //
   9 // ******************************************************************************
  10 // *                        BASIC USER VARIABLES                                *
  11 // ******************************************************************************
  12 // this section contains settings that you may wish to change for your use and requires little or no scripting knowledge
  13 string groupStyle="01 Unisex";                         // name of a singles dance style that you want to set as the group dance...will use 1st singles style if left blank or a non-existent style is specified
  14 integer errorCheck=FALSE;                               // TRUE: do full error-checking of name, animations, contents, etc on start-up (slow) or FALSE: skip all error-checking
  15 integer showFloatyText=TRUE;                            // TRUE allows text to be displayed or FALSE to hide all floaty text during operation
  16 string floatyText="Clubmaster Dance Machine v2.0";      // text above the main dance controller
  17 vector floatyTextColour=<0.941, 0.847, 0.459>;          // colour of the text above the main controller <R,G,B> where <0,0,0> is black, <1,1,1> is white
  18 integer randomizeSinglesOrder=TRUE;                     // when initializing, randomize the order of the dances within each singles dance style (style order is always displayed alphabetical) TRUE= yes, FALSE = no
  19 integer randomizeNpcOrder=FALSE;                        // when initializing, randomize the order of NPCs (otherwise it's the order found in inventory)
  20 float autoTimer=120.0;                                  // for group dance, singles dances, and couples in auto mode, how often (in seconds) to advance to the next animation
  21 float zGlobal=0.0001;                                   // global Z offset to apply to all couples dance positions ...for regions running 0.8.2 or earlier use 0.0001....for 0.9 or later try a value of -0.15
  22 //
  23 // ******************************************************************************
  24 // *                       ADVANCED USER VARIABLES                              *
  25 // ******************************************************************************
  26 // this section contains settings for slightly more advanced users but doesn't require much expertise
  27 string singlesNotecard="~Singles Data";                 // name of the notecard containing all of the singles dances data
  28 string animNotecard="~Couples Data";                    // name of the notecard containing all of the couples dances data
  29 string npcMatcherCard="!!NPC MATCHMAKER";               // name of the notecard (if any) containing NPC matchmaker data (see instructions)
  30 string poseballName="~club poseball";                   // name of the poseball to rez from inventory for couples - update if you rename the poseball for any reason
  31 string prefixM=".NPCM";                                 // NPCs for the male poseball have notecards with name that begin with this string
  32 string prefixF=".NPCF";                                 // NPCs for the female poseball have notecards with name that begin with this string
  33 integer ballTimeout=30;                                 // seconds until a couples ball should self-destruct (passed to ball on-rez) -- must be INTEGER type since it is compared to a UNIX timestamp
  34 float zStep=0.01;                                       // when couples zAdjust is enabled, vertical adjustment (in m) to make for the avatar with each press of the page up/page down keys
  35 list firstNameM=["Dance"];                              // list of first names to randomly choose from for the male NPC (if only one name is supplied it will always be used)
  36 list lastNameM=["Partner"];                             // list of last names to randomly choose from for the male NPC  (if only one name is supplied it will always be used)
  37 list firstNameF=["Dance"];                              // list of first names to randomly choose from for the female NPC (if only one name is supplied it will always be used)
  38 list lastNameF=["Partner"];                             // list of last names to randomly choose from for the female NPC  (if only one name is supplied it will always be used)
  39 list anDefault=["DEFAULT_STAND_WAITING","Waiting to Dance","Stand_f",<-0.3,-0.3,0>,<0,0,90>,"Stand_m",<0.3,0.3,0>,<0,0,-90>]; // default stand animation and positions when waiting for a partner or inital couples dance selection
  40 integer enableParticles=TRUE;                           // enable particle effects for the main controller and poseballs - FALSE if you move scripts to different controller object and/or use different poseballs that shouldn't use them
  41 vector sparkleStartColour=<1.0,0.2,0.2>;                // for main controller particle effect - start colour of sparkles
  42 vector sparkleEndColour=<0.941, 0.847, 0.459>;          // for main controller particle effect - end colour of sparkles
  43 float sparkleBurstSpeed=6.25;                           // for main controller particle effect - this affects how far out from center the sparkles go
  44 integer sparkleBurstCount=7;                            // for main controller particle effect - number of particles per burst
  45 float rpm=4.0;                                          // how rapidly to rotate the controller on its axis (in revolutions per minute) 0.0 disables rotation completely
  46 //
  47 // ******************************************************************************
  48 // *                       Extras for remote dance poles                        *
  49 // ******************************************************************************
  50 // this section contains some extra coms channels to make the main dance ball work with remotes
  51 integer RemoteChannel = -741285681;
  52 integer RemoteChannelListen;
  53 // ******************************************************************************
  54 // * DO NOT CHANGE ANYTHING BELOW THIS LINE UNLESS YOU KNOW WHAT YOU ARE DOING! *
  55 // ******************************************************************************
  56 list anData=[]; list styles=[]; integer anStride=8; list dancers=[]; integer dancerStride=15; integer B1USER=0; integer B1ID=1; integer B1AN=2; integer B1POS=3; integer B1ROT=4; integer B2USER=5; integer B2ID=6; integer B2AN=7; integer B2POS=8; integer B2ROT=9; integer BASEPOS=10; integer SWAPFLAG=11; integer STYLE=12; integer ANNAME=13; integer AUTO=14; integer nextBallID; list ballRezFor; list singlesDances; list currentDances; list singlesUsers; list singlesStyles; list groupUsers; string groupDance; string singlesStyleCount; string singlesDanceCount; string couplesDanceCount; integer handle; integer myChannel; string txtDia; list butDia; string myState; integer tickCouples=TRUE; list partnerF; list partnerM; integer indexNpcF; integer indexNpcM;integer osNpcObjectGroup=8; list npcMatch;
  57 
  58 startGroupDancing(key who) 
  59 {
  60     groupUsers=[]+groupUsers+[who];
  61     list anToStop=llGetAnimationList(who);
  62     osAvatarPlayAnimation(who,groupDance);
  63     integer stop=llGetListLength(anToStop);
  64     while (--stop>=0) { osAvatarStopAnimation(who,llList2String(anToStop,stop)); }
  65     if (!osIsNpc(who)) llRegionSayTo(who,0,"You've joined the group dance and will be in synch when it next changes. Touch the dance ball again to stop dancing");
  66     updateText();
  67 }
  68 
  69 showSinglesMenu(key who,integer page) 
  70 {
  71     txtDia="Pick a singles dance style to use\nQUIT to stop dancing";
  72     string curStyle=llList2String(singlesUsers,llListFindList(singlesUsers,[who])+1);
  73     if (curStyle!="NO_STYLE_SELECTED") txtDia+="\n\nYour current style is: "+curStyle;
  74     if (page==-1) {
  75         if (curStyle=="NO_STYLE_SELECTED") page=0;
  76         else page=llListFindList(singlesStyles,[curStyle])/9;
  77     }    
  78     butDia=[]+llList2List(singlesStyles,page*9,page*9+8);
  79     while (llGetListLength(butDia)<9) {butDia=[]+butDia+["*"];}
  80     while (llListFindList(butDia,["*","*","*"])>=0) {butDia=[]+llDeleteSubList(butDia,llListFindList(butDia,["*","*","*"]),llListFindList(butDia,["*","*","*"])+2);}
  81     if (page==0) butDia=[]+butDia+["*","QUIT"];
  82     else butDia=[]+butDia+["PAGE "+(string)page,"QUIT"];
  83     if (llGetListLength(singlesStyles)>((page*9)+9)) butDia=[]+butDia+["PAGE "+(string)(page+2)];
  84     else butDia=[]+butDia+["*"];
  85     llDialog(who,txtDia,llList2List(butDia,9,11)+llList2List(butDia,6,8)+llList2List(butDia,3,5)+llList2List(butDia,0,2),myChannel);
  86 }
  87 
  88 startSingles(key who, string currentSinglesStyle) 
  89 {
  90     list anToStop=llGetAnimationList(who);
  91     osAvatarPlayAnimation(who,llList2String(currentDances,llListFindList(currentDances,llList2String(singlesUsers,llListFindList(singlesUsers,[who])+1))+1));
  92     integer a=llGetListLength(anToStop);
  93     while(--a>=0) {osAvatarStopAnimation(who,llList2String(anToStop,a));}
  94     updateText();
  95 }
  96 
  97 nextSingles(key who, string currentSinglesStyle) 
  98 {
  99     string curD=llList2String(currentDances,llListFindList(currentDances,[currentSinglesStyle])+1);
 100     string nextD=llList2String(currentDances,llListFindList(currentDances,[currentSinglesStyle])+2);
 101     if(curD!=nextD) {
 102         osAvatarPlayAnimation(who,nextD);
 103         osAvatarStopAnimation(who,curD);
 104     }
 105 }
 106 
 107 stopSingles(key who, string currentSinglesStyle) 
 108 {
 109     osAvatarPlayAnimation(who,"stand");
 110     osAvatarStopAnimation(who,llList2String(currentDances,llListFindList(currentDances,[currentSinglesStyle])+1));
 111 }
 112 
 113 persistCurrent(integer anToUpdate) 
 114 {
 115     list ball1Params=osGetPrimitiveParams(llList2Key(dancers,B1ID),[PRIM_POSITION,PRIM_ROTATION]);
 116     list ball2Params=osGetPrimitiveParams(llList2Key(dancers,B2ID),[PRIM_POSITION,PRIM_ROTATION]);
 117     list newBall1AnData=llParseString2List(regToRel(llList2Vector(dancers,BASEPOS),ZERO_ROTATION,llList2Vector(ball1Params,0),llList2Rot(ball1Params,1)),["|"],[]);
 118     list newBall2AnData=llParseString2List(regToRel(llList2Vector(dancers,BASEPOS),ZERO_ROTATION,llList2Vector(ball2Params,0),llList2Rot(ball2Params,1)),["|"],[]);
 119     list newAnEntry=llList2List(anData,anToUpdate,anToUpdate+2)+newBall1AnData+[llList2String(anData,anToUpdate+5)]+newBall2AnData;
 120     anData=[]+llListReplaceList(anData,newAnEntry,anToUpdate,anToUpdate+anStride-1);
 121 }
 122 
 123 saveNotecard() 
 124 {
 125     integer l=llGetListLength(anData);
 126     if (l<anStride) {llOwnerSay("ERROR! Save notecard called with no animation data in memory. Aborting save."); return;}
 127     llRemoveInventory(animNotecard); llSleep(0.2);
 128     integer i;
 129     string data="";
 130     while (i<l) {data+=llDumpList2String(llList2List(anData,i,i+anStride-1),"|")+"\n"; i+=anStride;}
 131     osMakeNotecard(animNotecard,data);
 132     llOwnerSay("The new data has been stored to your notecard");
 133 }
 134 
 135 doSynch(key who) 
 136 {
 137     integer i=getPairIndex(who);
 138     osAvatarStopAnimation(llList2Key(dancers,i+B1USER),llList2String(dancers,i+B1AN));
 139     osAvatarPlayAnimation(llList2Key(dancers,i+B1USER),llList2String(dancers,i+B1AN));
 140     osAvatarStopAnimation(llList2Key(dancers,i+B2USER),llList2String(dancers,i+B2AN));
 141     osAvatarPlayAnimation(llList2Key(dancers,i+B2USER),llList2String(dancers,i+B2AN));
 142 }
 143 
 144 showMain(key who) 
 145 {
 146     txtDia="Please select a dance style\nor SYNCH to resynch current dance";
 147     if (llList2Integer(dancers,getPairIndex(who)+AUTO)) {
 148         butDia=[]+["MANUAL","SYNCH","QUIT"]+llList2List(styles,6,8)+llList2List(styles,3,5)+llList2List(styles,0,2);
 149         txtDia+="\nor MANUAL to return to manual dance selection mode";
 150     } else {
 151         butDia=[]+["AUTO","SYNCH","QUIT"]+llList2List(styles,6,8)+llList2List(styles,3,5)+llList2List(styles,0,2);
 152         txtDia+="\nor AUTO to enter auto dance mode";
 153     }
 154     llDialog(who,txtDia,butDia,myChannel);
 155 }
 156 
 157 showPickAn(key who, string styleName) {
 158     txtDia="Pick an animation to play or...\nSTYLES to select a different style\nSYNCH to resynch animation\nOPTIONS to access options menu";
 159     list ans=[];
 160     integer i=llListFindList(anData,[styleName]);
 161     while(llList2String(anData,i)==styleName){ans=[]+ans+[llList2String(anData,i+1)]; i+=anStride;}
 162     while (llGetListLength(ans)<9){ans=[]+ans+["~"];}
 163     butDia=[]+["STYLES","SYNCH","OPTIONS"]+llList2List(ans,6,8)+llList2List(ans,3,5)+llList2List(ans,0,2);
 164     llDialog(who,txtDia,butDia,myChannel);
 165 }
 166 
 167 showOptions(key who) {
 168     if (myState=="READY") {
 169         txtDia="Please select an action:\n\nBACK to return to previous menu\nSWAP positions with partner\nAUTO to change dances automatically\nMANUAL to go back to manual\n"+"Z ADJUST to turn on height adjust\nZ STOP ADJ to turn it off\nZ RESET to revert to default position\nNEW M and NEW F will randomly select another male or female NPC dance partner";
 170         butDia=[]+["BACK","SWAP"];
 171         if (llList2Integer(dancers,getPairIndex(who)+AUTO)) butDia=[]+butDia+["MANUAL","Z ADJUST","Z STOP ADJ","Z RESET","NEW M","NEW F"];
 172         else butDia=[]+butDia+["AUTO","Z ADJUST","Z STOP ADJ","Z RESET","NEW M","NEW F"];
 173         if (who==llGetOwner()) {
 174             if((llGetListLength(dancers)>dancerStride)||(llGetListLength(singlesUsers)>0)) txtDia+="\nCannot enter edit mode when there are other couples or singles dancers using the machine";
 175             else {
 176                 txtDia+="\nEDIT ON enters edit mode (only works when owner and partner are the only users";
 177                 butDia=[]+butDia+["QUIT","EDIT ON"];
 178             }
 179         } else butDia=[]+butDia+["QUIT"];
 180     } else if (myState=="EDIT") {
 181         txtDia="EDIT MODE\nEDIT OFF exit edit mode (does not save to card)\nSAVE changes to notecard\nREVERT this animation to stored value\n"+"SYNCH to resynch animation\nNEXT/PREV to change to next/previous animation (stores changes locally but not to notecard)";
 182         butDia=[]+["EDIT OFF","SAVE","REVERT","< PREV","SYNCH","NEXT >"];
 183     } else { llOwnerSay("ERROR! Attempting to show options menu but state was "+myState); return;}
 184     llDialog(who,txtDia,butDia,myChannel);
 185 }
 186 
 187 playAnimation(integer pairIndex, integer nextAnIndex) {
 188     list thisCouple=llList2List(dancers,pairIndex,pairIndex+dancerStride-1);
 189     list nextAnData=llList2List(anData,nextAnIndex,nextAnIndex+anStride-1);
 190     list nextCouple=llList2List(thisCouple,B1USER,B1ID);
 191     if (llList2Integer(thisCouple,SWAPFLAG)==1) nextCouple+=llList2List(nextAnData,5,7)+llList2List(thisCouple,B2USER,B2ID)+llList2List(nextAnData,2,4)+llList2List(thisCouple,BASEPOS,SWAPFLAG)+llList2List(nextAnData,0,1)+llList2List(thisCouple,AUTO,AUTO);
 192     else nextCouple+=llList2List(nextAnData,2,4)+llList2List(thisCouple,B2USER,B2ID)+llList2List(nextAnData,5,7)+llList2List(thisCouple,BASEPOS,SWAPFLAG)+llList2List(nextAnData,0,1)+llList2List(thisCouple,AUTO,AUTO);
 193     dancers=[]+llListReplaceList(dancers,nextCouple,pairIndex,pairIndex+dancerStride-1);
 194     if (agentPresent(llList2Key(nextCouple,B1USER))&& agentPresent(llList2Key(nextCouple,B2USER))&& objectPresent(llList2Key(nextCouple,B1ID))&& objectPresent(llList2Key(nextCouple,B2ID))) {
 195         osMessageObject(llList2Key(nextCouple,B1ID),"MOVE_BALL|"+relToReg(llList2Vector(nextCouple,BASEPOS),ZERO_ROTATION,llList2Vector(nextCouple,B1POS),llList2Vector(nextCouple,B1ROT)));
 196         osMessageObject(llList2Key(nextCouple,B2ID),"MOVE_BALL|"+relToReg(llList2Vector(nextCouple,BASEPOS),ZERO_ROTATION,llList2Vector(nextCouple,B2POS),llList2Vector(nextCouple,B2ROT)));
 197         osAvatarStopAnimation(llList2Key(thisCouple,B1USER),llList2String(thisCouple,B1AN));
 198         osAvatarStopAnimation(llList2Key(thisCouple,B2USER),llList2String(thisCouple,B2AN));
 199         osAvatarPlayAnimation(llList2Key(nextCouple,B1USER),llList2String(nextCouple,B1AN));
 200         osAvatarPlayAnimation(llList2Key(nextCouple,B2USER),llList2String(nextCouple,B2AN));
 201     } else {
 202         key keyToMessage=llList2Key(nextCouple,B1USER);
 203         if (!agentPresent(keyToMessage)) keyToMessage=llList2Key(nextCouple,B2USER);
 204         llRegionSayTo(keyToMessage,0,"Your animation selection won't begin to play until you have a seated partner but your selection has been stored.");
 205     }
 206 }
 207 
 208 integer getPairIndex(string find) 
 209 {
 210     return dancerStride*llFloor((float)llListFindList(dancers,[find])/(float)dancerStride);
 211 }
 212 
 213 string regToRel(vector basePos, rotation baseRot, vector regionPos,rotation regionRot) 
 214 {
 215     vector refPos=(regionPos - basePos) / baseRot;
 216     vector refRot=llRot2Euler((rotation)regionRot/ baseRot)*RAD_TO_DEG;
 217     string strToReturn="<"+trimF(refPos.x)+","+trimF(refPos.y)+","+trimF(refPos.z)+">"+"|<"+trimF(refRot.x)+","+trimF(refRot.y)+","+trimF(refRot.z)+">";
 218     return strToReturn;
 219 }
 220 
 221 string relToReg(vector basePos, rotation baseRot, vector refPos,vector refRot) {
 222     vector regionPos=refPos*baseRot+basePos;
 223     rotation regionRot=llEuler2Rot(refRot*DEG_TO_RAD)*baseRot;
 224     return ((string)regionPos+"|"+(string)regionRot);
 225 }
 226 
 227 string trimF(float value) 
 228 {
 229     integer newVal=llRound(value*10000);
 230     integer negFlag=FALSE;
 231     if (newVal<0) { negFlag=TRUE; newVal*=-1; }
 232     integer strLength;
 233     string retStr;
 234     if (newVal==0) retStr="0";
 235     else if (newVal<10) retStr="0.000"+(string)newVal;
 236     else if (newVal<100) retStr="0.00"+(string)newVal;
 237     else if (newVal<1000) retStr="0.0"+(string)newVal;
 238     else if (newVal<10000) retStr="0."+(string)newVal;
 239     else {
 240         retStr=(string)newVal;
 241         strLength=llStringLength(retStr);
 242         retStr=llGetSubString(retStr,0,strLength-5)+"."+llGetSubString(retStr,strLength-4,strLength-1);
 243     }
 244     while (llGetSubString(retStr,strLength,strLength)=="0") {retStr=llGetSubString(retStr,0,strLength-1); strLength-=1;}
 245     if (negFlag) retStr="-"+retStr;
 246     return retStr;
 247 }
 248 
 249 showFirst(key who) 
 250 {
 251     txtDia="How would you like to dance?\n\nSINGLES - pick a singles style\nGROUP - join the group dance style\nCOUPLES - dance with a friend\nNPC COUPLES - dance with a NPC\nOOPS I don't want to dance";
 252     butDia=["COUPLES","NPC COUPLES","OOPS","SINGLES","GROUP"];
 253     llDialog(who,txtDia,butDia,myChannel);
 254 }
 255 
 256 doInitialize() 
 257 {
 258     if(llGetInventoryType(poseballName)!=INVENTORY_OBJECT) 
 259     {
 260         llOwnerSay("ERROR! Unable to find the poseball "+poseballName+" in inventory");
 261         myState="ERROR";
 262         return;
 263     }
 264     if (llGetInventoryType(animNotecard)!=INVENTORY_NOTECARD) 
 265     {
 266         llOwnerSay("ERROR! Unable to find the animation data notecard");
 267         myState="ERROR";
 268         return;
 269     }
 270     string cardData=osGetNotecard(animNotecard);
 271     anData=[]+llParseString2List(cardData,["|","\n"],[]);
 272     if (llGetInventoryType(singlesNotecard)!=INVENTORY_NOTECARD)
 273     {
 274         llOwnerSay("ERROR! Unable to find the single data data notecard");
 275         myState="ERROR";
 276         return;
 277     }
 278     cardData=""+osGetNotecard(singlesNotecard);
 279     singlesDances=[]+llParseString2List(cardData,["|","\n"],[]);
 280     integer i;
 281     integer l=llGetListLength(singlesDances);
 282     singlesStyles=[];
 283     while(i<l) {
 284         if (errorCheck) {
 285             singlesDances=[]+llListReplaceList(singlesDances,[llStringTrim(llList2String(singlesDances,i),STRING_TRIM)],i,i);
 286             singlesDances=[]+llListReplaceList(singlesDances,[llStringTrim(llList2String(singlesDances,i+1),STRING_TRIM)],i+1,i+1);
 287         }
 288         if (llListFindList(singlesStyles,[llList2String(singlesDances,i)])<0) singlesStyles=[]+singlesStyles+llList2String(singlesDances,i);
 289         i+=2;
 290     }
 291     if (randomizeSinglesOrder) singlesDances=[]+llListRandomize(singlesDances,2);
 292     singlesDances=[]+llListSort(singlesDances,2,TRUE);
 293     singlesStyleCount=(string)llGetListLength(singlesStyles);
 294     singlesDanceCount=(string)(llGetListLength(singlesDances)/2);
 295     i=0; l=llGetListLength(singlesStyles);
 296     currentDances=[];
 297     while (i<l) {
 298         if(llListFindList(currentDances,[llList2String(singlesStyles,i)])<0) {
 299             integer f=llListFindList(singlesDances,[llList2String(singlesStyles,i)]);
 300             currentDances=[]+currentDances+llList2List(singlesDances,f,f+1);
 301             if (llList2String(singlesDances,f+2)==llList2String(singlesDances,f)) currentDances=[]+currentDances+[llList2String(singlesDances,f+3)];
 302             else currentDances=[]+currentDances+[llList2String(singlesDances,f+1)];
 303         }
 304         i++;
 305     }
 306     if (llListFindList(singlesStyles,[groupStyle])<0) 
 307     {
 308         integer gse;
 309         if (groupStyle=="") gse=TRUE;
 310         groupStyle=llList2String(singlesStyles,0);
 311         if (gse) llOwnerSay("NOTICE: Setting "+groupStyle+" as the group dance style because no style was specified in the settings");
 312         else llOwnerSay("NOTICE: Setting "+groupStyle+" as the group dance style because the specified style wasn't found");
 313     }
 314     groupDance=llList2String(singlesDances,llListFindList(singlesDances,[groupStyle])+1);
 315     if (errorCheck) 
 316     {
 317         i=0; l=llGetListLength(singlesDances);
 318         while(i<l) {
 319             if (llListFindList(singlesStyles,[llList2String(singlesDances,i+1)])>=0) 
 320             {
 321                 llOwnerSay("ERROR! A singles dance animation name is in conflict with one of the singles styles names. Style names must be unique! Conflict name is: "+llList2String(singlesDances,i+1));
 322                 myState="ERROR";
 323             }
 324             if (llGetInventoryType(llList2String(singlesDances,i+1))!=INVENTORY_ANIMATION) 
 325             {
 326                 llOwnerSay("ERROR! Unable to locate the singles dance animation \""+llList2String(singlesDances,i+1)+"\" in inventory.");
 327                 myState="ERROR";
 328             }
 329             i+=2;
 330         }
 331         if (myState=="ERROR") 
 332         {
 333             llOwnerSay("Errors detected during singles notecard data check. Please fix and restart");
 334             return;
 335         }
 336     }
 337     i=0; l=llGetListLength(anData);
 338     if (errorCheck) 
 339     {
 340         while(--l>=0) {anData=[]+llListReplaceList(anData,[llStringTrim(llList2String(anData,l),STRING_TRIM)],l,l);}
 341         l=llGetListLength(anData);
 342     }
 343     styles=[];
 344     while (i<l) 
 345     {
 346         if (errorCheck) 
 347         {
 348             if (llListFindList(singlesStyles,[llList2String(anData,i)])>=0) 
 349             {
 350                 llOwnerSay("ERROR! A doubles dance style name is in conflict with one of the singles styles names. Style names must be unique! Please correct this and restart.");
 351                 myState="ERROR";
 352                 return;
 353             }
 354         }
 355         if (llListFindList(styles,[llList2String(anData,i)])<0) styles=[]+styles+[llList2String(anData,i)];
 356         i+=anStride;
 357     }
 358     while(llGetListLength(styles)<9) 
 359     {
 360         styles=[]+styles+["-"];
 361     }
 362     if (llGetListLength(styles)>9) llOwnerSay("WARNING! Found "+(string)(llGetListLength(styles))+" different couples dance styles but only the first 9 can be shown. Please correct this (but this is a non-fatal error so the dance machine will still work)");
 363     while (llListFindList(styles,["-","-","-"])>=0) 
 364     {
 365         styles=[]+llDeleteSubList(styles,llListFindList(styles,["-","-","-"]),llListFindList(styles,["-","-","-"])+2);
 366     }
 367     if (errorCheck) 
 368     {
 369         i=0; l=llGetListLength(anData); integer anCount; string styleName; integer warned=FALSE;
 370         while (i<l) 
 371         {
 372             if (llList2String(anData,i)!=styleName) 
 373             {
 374                 styleName=llList2String(anData,i);
 375                 anCount=1;
 376                 warned=FALSE;
 377             } else {
 378                 anCount++;
 379                 if ((anCount>9) && !warned) 
 380                 {
 381                     warned=TRUE;
 382                     llOwnerSay("WARNING! Found more than 9 animations assigned to style "+styleName+" but only the first 9 can be shown. Please correct this (but this is a non-fatal error so the dance machine will still work)");
 383                 }
 384             }
 385             string name=llList2String(anData,i+1);
 386             if (llListFindList(anData,[name])<i) 
 387             {
 388                 llOwnerSay("ERROR! The name "+name+" is being used to identify an pair of animations but is in conflict with another name -- a style name, other pair name, or individual animation name that isn't part of that pair. Please resolve this error and reset the controller (see instructions for more details about name restrictions).");
 389                 myState="ERROR";
 390             }
 391             name= llList2String(anData,i+2);
 392             if (llGetInventoryType(name)!=INVENTORY_ANIMATION) 
 393             {
 394                 myState="ERROR"; llOwnerSay("ERROR! Unable to locate the animation "+name+" in inventory. Please either supply it or update the animation data notecard.");
 395             }
 396             name= llList2String(anData,i+5);
 397             if (llGetInventoryType(name)!=INVENTORY_ANIMATION) 
 398             {
 399                 myState="ERROR"; llOwnerSay("ERROR! Unable to locate the animation "+name+" in inventory. Please either supply it or update the animation data notecard.");
 400             }
 401             i+=anStride;
 402         }
 403     }
 404     couplesDanceCount=(string)(llGetListLength(anData)/anStride);
 405     if (errorCheck) {
 406         i=llGetInventoryNumber(INVENTORY_ANIMATION);
 407         while (--i>=0) {
 408             string anName=llGetInventoryName(INVENTORY_ANIMATION,i);
 409             if ((llListFindList(anData,[anName])<0) && (llListFindList(anDefault,[anName])<0) && (llListFindList(singlesDances,[anName])<0)) llOwnerSay("NOTE: found the animation "+anName+" in inventory but it is not used for either the singles or couples dances.");
 410         }
 411     }
 412     partnerM=[]; partnerF=[]; indexNpcM=0; indexNpcF=0; i=llGetInventoryNumber(INVENTORY_NOTECARD); integer lm=llStringLength(prefixM)-1; integer lf=llStringLength(prefixF)-1;
 413     while (--i>=0) {
 414         string note=llGetInventoryName(INVENTORY_NOTECARD,i);
 415         if (llGetSubString(note,0,lm)==prefixM) partnerM=[]+[note]+partnerM;
 416         else if (llGetSubString(note,0,lf)==prefixF) partnerF=[]+[note]+partnerF;
 417     }
 418     if (randomizeNpcOrder) 
 419     {
 420         partnerM=[]+llListRandomize(partnerM,1);
 421         partnerF=[]+llListRandomize(partnerF,1);
 422     }
 423     npcMatch=[];
 424     if (npcMatcherCard!="") 
 425     {
 426         if (llGetInventoryType(npcMatcherCard)!=INVENTORY_NOTECARD) llOwnerSay("WARNING: could not find the NPC matchmaker notecard \""+npcMatcherCard+"\" so no custom NPC assignments will be used");
 427         else {
 428             npcMatch=[]+llParseString2List(osGetNotecard(npcMatcherCard),["|","\n"],[]);
 429             i=0; l=llGetListLength(npcMatch);
 430             while (i<l) {
 431                 if (llGetInventoryType(llList2String(npcMatch,i+1))!=INVENTORY_NOTECARD) {
 432                     llOwnerSay("WARNING: NPC Matchmaker notecard calls for NPC \""+llList2String(npcMatch,i+1)+"\" but it isns't in inventory. Removing that entry");
 433                     npcMatch=[]+llDeleteSubList(npcMatch,i,i+1);
 434                     l-=2;
 435                 } else i+=2;
 436             }
 437         }
 438     }
 439     if (llGetListLength(partnerM)==0) {myState="ERROR"; llOwnerSay("ERROR! No notecards were found with the specified male NPC prefix: "+prefixM);}
 440     if (llGetListLength(firstNameM)==0) {myState="ERROR"; llOwnerSay("ERROR! No male NPC first names were supplied");}
 441     if (llGetListLength(lastNameM)==0) {myState="ERROR"; llOwnerSay("ERROR! No male NPC last names were supplied");}
 442     if (llGetListLength(partnerF)==0) {myState="ERROR"; llOwnerSay("ERROR! No notecards were found with the specified female NPC prefix: "+prefixF);}
 443     if (llGetListLength(firstNameF)==0) {myState="ERROR"; llOwnerSay("ERROR! No female NPC first names were supplied");}
 444     if (llGetListLength(lastNameF)==0) {myState="ERROR"; llOwnerSay("ERROR! No female NPC last names were supplied");}
 445 }//end to initilise
 446 
 447 setPart(integer on) 
 448 {
 449     if(on && enableParticles) llParticleSystem([PSYS_PART_MAX_AGE,0.7,PSYS_PART_FLAGS,0|PSYS_PART_EMISSIVE_MASK|PSYS_PART_INTERP_COLOR_MASK|PSYS_PART_INTERP_SCALE_MASK|PSYS_PART_FOLLOW_SRC_MASK|PSYS_PART_FOLLOW_VELOCITY_MASK|PSYS_PART_TARGET_POS_MASK,PSYS_PART_START_COLOR,sparkleStartColour,PSYS_PART_END_COLOR,sparkleEndColour,PSYS_PART_START_SCALE,<0.05,0.05,0>,PSYS_PART_END_SCALE,<0.1,0.1,0>,PSYS_SRC_PATTERN, PSYS_SRC_PATTERN_EXPLODE,PSYS_SRC_BURST_RATE,0.1,PSYS_SRC_BURST_PART_COUNT,sparkleBurstCount,PSYS_SRC_BURST_RADIUS,2,PSYS_SRC_BURST_SPEED_MIN,sparkleBurstSpeed,PSYS_SRC_BURST_SPEED_MAX,sparkleBurstSpeed,PSYS_SRC_TARGET_KEY,llGetKey(),PSYS_SRC_ANGLE_BEGIN,1.55,PSYS_SRC_ANGLE_END,1.54,PSYS_PART_START_ALPHA,0.75,PSYS_PART_END_ALPHA,0.5]);
 450     else llParticleSystem([]);
 451 }
 452 setRot(integer on) 
 453 {
 454     llTargetOmega(<0.0,0.0,-rpm*(float)on/60>,TWO_PI,1.0);
 455 }
 456 updateText() 
 457 {
 458     if (myState=="READY") {
 459         setPart(TRUE);
 460         setRot(TRUE);
 461         string txt=floatyText+"\nTouch me to dance\n \nGroup Dance Style: "+groupStyle;
 462         if ((llGetListLength(singlesUsers)>0) || (llGetListLength(dancers)>0) || (llGetListLength(groupUsers)>0)) txt+="\nGroup dancers: "+(string)llGetListLength(groupUsers)+"\nSingles dancers: "+(string)(llGetListLength(singlesUsers)/2)+"\nCouples: "+(string)(llGetListLength(dancers)/dancerStride)+"\n";
 463         txt+=" \nDances Loaded\n"+singlesStyleCount+" singles styles, "+singlesDanceCount+" dances\nand "+couplesDanceCount+" couples dances";
 464         if (showFloatyText) llSetText(txt,floatyTextColour,1.0);
 465         else llSetText("",ZERO_VECTOR,0.0);
 466     } else {
 467         setPart(FALSE);
 468         setRot(FALSE);
 469         if (myState=="INITIALIZING") llSetText(floatyText+"\nInitializing...\nplease wait",<0,1,0>,1.0);
 470         else if (myState=="ERROR") llSetText(floatyText+"\nERROR...sorry\ntemporarily out of order",<1,0,0>,1.0);
 471         else if (myState=="EDIT") llSetText(floatyText+"\nEDIT MODE...\nplease wait",<1,1,0>,1.0);
 472     }
 473 }
 474 
 475 integer agentPresent(key who) 
 476 {
 477     if (who==NULL_KEY) return FALSE;
 478     else if (who=="") return FALSE;
 479     else if (!osIsUUID(who)) return FALSE;
 480     else if (llGetAgentSize(who)==ZERO_VECTOR) return FALSE;
 481     else return TRUE;
 482 }
 483 
 484 integer objectPresent(key what) 
 485 {
 486     if (what=="") return FALSE;
 487     else if (what==NULL_KEY) return FALSE;
 488     else if (!osIsUUID(what)) return FALSE;
 489     else if (llGetObjectDetails(what,[OBJECT_NAME])==[]) return FALSE;
 490     else return TRUE;
 491 }
 492 
 493 ProcessToucher(key toucher) //all this was in touch_start after the lldetectdkey.
 494 {//1
 495 if (myState=="INITIALIZING") llRegionSayTo(toucher,0,"Sorry, currently initializing the dance machine. Please wait until it is ready, then touch again");
 496 else if (myState=="ERROR") 
 497     {//2
 498     if (toucher!=llGetOwner()) llRegionSayTo(toucher,0,"Sorry, the controller has encountered an error and needs to be reset before it can resume working. Please inform the owner");
 499     else 
 500         {//3
 501         llOwnerSay("Attempting script reset");
 502         llResetScript();
 503         }//3
 504     }//2 
 505 else if (myState=="EDIT") 
 506     {//4
 507     if (toucher!=llGetOwner()) llRegionSayTo(toucher,0,"Sorry, the owner is currently editing positions...please wait until this is finished and the machine is reset");
 508     else showOptions(llGetOwner());
 509     }//4
 510 else if (myState=="READY") 
 511     {//5
 512     if (llListFindList(groupUsers,[toucher])>=0) 
 513         {//6
 514         integer indU=llListFindList(groupUsers,[toucher]);
 515         osAvatarPlayAnimation(toucher,"stand");
 516         osAvatarStopAnimation(toucher,groupDance);
 517         groupUsers=[]+llDeleteSubList(groupUsers,indU,indU);
 518         updateText();
 519         }//6
 520     else if (llListFindList(singlesUsers,[toucher])>=0) 
 521         {//7
 522         if (llGetListLength(singlesStyles)==1) 
 523             {//8
 524             integer indWho=llListFindList(singlesUsers,[toucher]);
 525             stopSingles(toucher,llList2String(singlesUsers,indWho+1));
 526             singlesUsers=[]+llDeleteSubList(singlesUsers,indWho,indWho+1);
 527             updateText();
 528             }//8 
 529         else showSinglesMenu(toucher,-1);
 530         }//7 
 531     else if (llListFindList(dancers,[toucher])>=0) showMain(toucher);
 532     else if (osIsNpc(toucher)) startGroupDancing(toucher);
 533     else showFirst(toucher);
 534     }//5
 535 }//1
 536 
 537 InitListeners()
 538 {
 539     myChannel= 0x80000000 | (integer)("0x"+(string)llGetKey());
 540     RemoteChannelListen = llListen(RemoteChannel, "", NULL_KEY, "");
 541     handle=llListen(myChannel,"",NULL_KEY,"");
 542     llListenControl(RemoteChannelListen, TRUE);
 543 }
 544 
 545 ProcessRemoteToucher(string message)
 546 {
 547     key toucher = (key)message;
 548     ProcessToucher(toucher);
 549 }
 550 
 551 default
 552 {
 553     changed (integer change)
 554     {
 555     if (change & CHANGED_OWNER) llResetScript();
 556     else if (change & CHANGED_REGION_START) llResetScript();
 557     }
 558 
 559     state_entry()
 560     {
 561         myState="INITIALIZING";
 562         llSetText(floatyText+"\nInitializing...\nplease wait",<0,1,0>,1.0);
 563         InitListeners();
 564         setPart(FALSE); setRot(FALSE);  
 565         autoTimer/=2;
 566         doInitialize();
 567         dancers=[]; singlesUsers=[]; ballRezFor=[];
 568         if (myState=="INITIALIZING") {
 569             myState="READY";
 570             updateText();
 571             llSetTimerEvent(autoTimer);
 572             llSay(0,"System reset and ready");
 573         } else if (myState=="ERROR") {
 574             llInstantMessage(llGetOwner(),"ERROR encountered during initialization!");
 575             updateText();
 576         } else llOwnerSay("Reached end of state_entry with unexpected state: "+myState);
 577     }
 578 
 579     link_message(integer sender_num, integer num, string msg, key id)
 580     {
 581         key toucher = (key)msg;
 582         ProcessToucher(toucher);
 583     }
 584     
 585     touch_start(integer num)
 586     {
 587     // -- key toucher = llDetectdKey(0);     Moved to the global variables so it can work with linked messages to. 
 588     key toucher=llDetectedKey(0);
 589     ProcessToucher(toucher); // ++ added by me as a new fuction so it can be shared with a linked message
 590     // -- lots of code removed here and moved to the ProcessToucher() function/
 591     }
 592         
 593     timer()
 594     {
 595         if (myState!="READY") 
 596         { 
 597             if (myState!="EDIT") llSetTimerEvent(0.0); return;
 598         }
 599         tickCouples=!tickCouples;
 600         integer i; integer t;
 601         if (tickCouples) 
 602         {
 603             integer l=llGetListLength(dancers);
 604             while (i<l) {
 605                 if (llList2Integer(dancers,i+AUTO)) {
 606                     string style=llList2String(dancers,i+STYLE);
 607                     string curAn=llList2String(dancers, i+ANNAME);
 608                     integer curInd=llListFindList(anData,[style,curAn]);
 609                     integer nextInd;
 610                     if (llListFindList(styles,[style])<0) nextInd=0;
 611                     else if ((curInd>-1) && (llList2String(anData,curInd+anStride)==style)) nextInd=curInd+anStride;
 612                     else nextInd=llListFindList(anData,[style]);
 613                     playAnimation(i,nextInd);
 614                 }
 615                 i+=dancerStride;
 616             }
 617         } 
 618         else 
 619         {
 620             integer l=llGetListLength(singlesUsers);
 621             while(i<l) {
 622                 if (llGetAgentSize(llList2Key(singlesUsers,i))==ZERO_VECTOR) {
 623                     singlesUsers=[]+llDeleteSubList(singlesUsers,i,i+1);
 624                     l-=2;
 625                 } else {
 626                     nextSingles(llList2Key(singlesUsers,i),llList2String(singlesUsers,i+1));
 627                     i+=2;
 628                 }
 629             }
 630             i=0;
 631             l=llGetListLength(currentDances);
 632             while(i<l) {
 633                 string thisStyle=llList2String(currentDances,i);
 634                 string moveToCurrent=llList2String(currentDances,i+2);
 635                 string newNextDance;
 636                 integer indexNewCurrent=llListFindList(singlesDances,[thisStyle,moveToCurrent]);
 637                 if(llList2String(singlesDances,indexNewCurrent+2)==thisStyle) newNextDance=llList2String(singlesDances,indexNewCurrent+3);
 638                 else newNextDance=llList2String(singlesDances,llListFindList(singlesDances,[thisStyle])+1);
 639                 currentDances=[]+llListReplaceList(currentDances,[thisStyle,moveToCurrent,newNextDance],i,i+2);
 640                 i+=3;
 641             }
 642             integer ngi=llListFindList(singlesDances,[groupStyle,groupDance])+2;
 643             if (llList2String(singlesDances,ngi)!=groupStyle) ngi=llListFindList(singlesDances,[groupStyle]);
 644             string nextGroupDance=llList2String(singlesDances,ngi+1);
 645             l=llGetListLength(groupUsers);
 646             while (--l>=0) {
 647                 if (llGetAgentSize(llList2Key(groupUsers,l))==ZERO_VECTOR) groupUsers=[]+llDeleteSubList(groupUsers,l,l);
 648                 else if (nextGroupDance!=groupDance) {
 649                     osAvatarPlayAnimation(llList2Key(groupUsers,l),nextGroupDance);
 650                     osAvatarStopAnimation(llList2Key(groupUsers,l),groupDance);
 651                 }
 652             }
 653             groupDance=nextGroupDance;
 654         }
 655         updateText();
 656     }
 657     on_rez(integer foo)
 658         {
 659         llResetScript();
 660         }
 661     
 662     dataserver (key ball, string data)
 663     {
 664         list message=llParseString2List(data,["|"],[]);
 665         string command=llList2String(message,0);
 666         if (command=="BALL_REZZED") {
 667             integer ballIndex=llListFindList(dancers,[("AWAITING_BALL_REZ_"+llList2String(message,1))]);
 668             if (ballIndex<0) { llOwnerSay("ERROR! A ball said "+data+" but when parsed I was unable to find an index for it using AWAITING_BALL_REZ_"+llList2String(message,1)); return;}
 669             dancers=[]+llListReplaceList(dancers,[ball],ballIndex,ballIndex);
 670             integer thisPair=getPairIndex(ball);
 671             if (thisPair<0) {llOwnerSay("ERROR! Unable to find an index when searching dancer list for the newly inserted ball UUID"); return;}
 672             vector basePos=llList2Vector(dancers,thisPair+BASEPOS);
 673             if (basePos==ZERO_VECTOR) {
 674                 llOwnerSay("ERROR! When attempting to retrieve the couple's base position to send a new ball the result returned ZERO_VECTOR.\n"+"The lookup for the couple index returned: "+(string)thisPair+"\nThe contents of the position field for that record is: "+llList2String(dancers,thisPair+BASEPOS));
 675                 return;
 676             }
 677             if ((llList2String(dancers,ballIndex-1)=="pending_f") || (llList2String(dancers,ballIndex-1)=="pending_npcf")) osMessageObject(ball,"MOVE_AND_SHOW_BALL|"+(string)(basePos+<-0.3, -0.3, 0.25>)+"|<0,0,0>|"+ (string)(ballTimeout*-1)+"|"+(string)zGlobal+"|"+(string)enableParticles);
 678             else if ((llList2String(dancers,ballIndex-1)=="pending_m") || (llList2String(dancers,ballIndex-1)=="pending_npcm")) osMessageObject(ball,"MOVE_AND_SHOW_BALL|"+(string)(basePos+<0.3, 0.3, 0.25>)+"|<0,0,0>|"+ (string)ballTimeout+"|"+(string)zGlobal+"|"+(string)enableParticles);
 679             else llOwnerSay("ERROR...BALL_REZZED dataserver error unable to locate record with that rez ID");
 680         } else if (command=="KILL_BALL_CALLED") {
 681             string reason=llList2String(message,1);
 682             if ((reason=="NO_USER_SAT") || (reason=="USER_STOOD_TIMEOUT") ) {
 683                 integer i=getPairIndex(ball);
 684                 if (i<0) return;
 685                 key userToMessage=NULL_KEY;
 686                 key ballToKill=NULL_KEY;
 687                 string anToStop;
 688                 if (ball==llList2Key(dancers,i+B1ID)) {
 689                     userToMessage=llList2Key(dancers,i+B2USER);
 690                     ballToKill=llList2Key(dancers,i+B2ID);
 691                     anToStop=llList2String(dancers,i+B2AN);
 692                 } else if (ball==llList2Key(dancers,i+B2ID)) {
 693                     userToMessage=llList2Key(dancers,i+B1USER);
 694                     ballToKill=llList2Key(dancers,i+B1ID);
 695                     anToStop=llList2String(dancers,i+B1AN);
 696                 } else llOwnerSay("ERROR! When handling kill ball on "+reason+" unable to find the relevant ball which should be impossible");
 697                 if(agentPresent(userToMessage)) llRegionSayTo(userToMessage,0,"Sorry, your partner's danceball has timed out and has been removed. Yours must be removed as well. Please start again when you're both ready to dance.");
 698                 if(objectPresent(ballToKill))  osMessageObject(ballToKill,"KILL_BALL");
 699                 if(agentPresent(userToMessage)) osAvatarStopAnimation(userToMessage,anToStop);
 700                 integer p=llListFindList(dancers,ball)-1;
 701                 if (agentPresent(llList2Key(dancers,p))) osAvatarStopAnimation(llList2Key(dancers,p),llList2String(dancers,p+2));
 702                 dancers=[]+llDeleteSubList(dancers,i,i+dancerStride-1);
 703                 updateText();
 704             } else {
 705                 myState="ERROR";
 706                 llSetTimerEvent(0.0);
 707                 updateText();
 708                 llShout(0,"ERROR! Sorry, the controller experienced an error and will attempt to reset. You can attempt to use it again once the system is ready. We are sorry for the inconvenience");
 709                 llInstantMessage(llGetOwner(),"Encountered error due to poseball self-destruct with the following error condition:\n"+reason);
 710                 integer l=llGetListLength(dancers);
 711                 integer i;
 712                 while (i<l) {
 713                     if (objectPresent(llList2Key(dancers,i+B1ID))) osMessageObject(llList2Key(dancers,i+B1ID),"KILL_BALL");
 714                     if (agentPresent(llList2Key(dancers,i+B1USER))) {
 715                         if (osIsNpc(llList2Key(dancers,i+B1USER))) osNpcRemove(llList2Key(dancers,i+B1USER));
 716                         else osAvatarStopAnimation(llList2Key(dancers,i+B1USER),llList2String(dancers,i+B1AN));
 717                     }
 718                     if (objectPresent(llList2Key(dancers,i+B2ID))) osMessageObject(llList2Key(dancers,i+B2ID),"KILL_BALL");
 719                     if (agentPresent(llList2Key(dancers,i+B2USER))) {
 720                         if (osIsNpc(llList2Key(dancers,i+B2USER))) osNpcRemove(llList2Key(dancers,i+B2USER));
 721                         else osAvatarStopAnimation(llList2Key(dancers,i+B2USER),llList2String(dancers,i+B2AN));
 722                     }
 723                     i+=dancerStride;
 724                 }
 725                 i=0;
 726                 l=llGetListLength(singlesUsers);
 727                 while (i<l) { stopSingles(llList2Key(singlesUsers,i),llList2String(singlesUsers,i+1)); i+=2;}
 728                 l=llGetListLength(groupUsers);
 729                 while (--l>=0) {
 730                     osAvatarPlayAnimation(llList2Key(groupUsers,l),"stand");
 731                     osAvatarStopAnimation(llList2Key(groupUsers,l),groupDance);
 732                 }
 733                 groupUsers=[];
 734                 llResetScript();
 735             }
 736         } else if (command=="USER_SAT") {
 737             key uID=llList2Key(message,1);
 738             if (llListFindList(singlesUsers,[uID])>-1) {
 739                 singlesUsers=[]+llDeleteSubList(singlesUsers,llListFindList(singlesUsers,uID),llListFindList(singlesUsers,uID)+1);
 740                 updateText();
 741             }
 742             else if (llListFindList(groupUsers,[uID])>-1) {
 743                 groupUsers=[]+llDeleteSubList(groupUsers,llListFindList(groupUsers,[uID]),llListFindList(groupUsers,[uID]));
 744                 updateText();
 745             }
 746             if (llListFindList(ballRezFor,[uID])>=0) ballRezFor=[]+llDeleteSubList(ballRezFor,llListFindList(ballRezFor,[uID]),llListFindList(ballRezFor,[uID])+1);
 747             integer i=llListFindList(dancers,[ball])-1;
 748             dancers=[]+llListReplaceList(dancers,[uID],i,i);
 749             osAvatarPlayAnimation(uID,llList2String(dancers,i+2));
 750             if (objectPresent(llList2Key(dancers,i+1))) osMessageObject(llList2Key(dancers,i+1),"MOVE_BALL|"+relToReg(llList2Vector(dancers,getPairIndex(uID)+BASEPOS),ZERO_ROTATION,llList2Vector(dancers,i+3),llList2Vector(dancers,i+4)));
 751             if (osIsNpc(uID)) return;
 752             integer thisPair=getPairIndex(uID);
 753             if (thisPair<0) return;
 754             if (llList2String(dancers,thisPair+B1USER)=="pending_npcf") {
 755                 string npcToRez;
 756                 if (llListFindList(npcMatch,[uID])>=0) npcToRez=llList2String(npcMatch,llListFindList(npcMatch,[uID])+1);
 757                 else {
 758                     indexNpcF++;
 759                     if (indexNpcF>=llGetListLength(partnerF)) indexNpcF=0;
 760                     npcToRez=llList2String(partnerF,indexNpcF);
 761                 }
 762                 key npc=osNpcCreate(llList2String(firstNameF,llFloor(llFrand(llGetListLength(firstNameF)))),llList2String(lastNameF,llFloor(llFrand(llGetListLength(lastNameF)))),(llList2Vector(dancers,thisPair+BASEPOS)+<0,0,2>),npcToRez,osNpcObjectGroup);
 763                 dancers=[]+llListReplaceList(dancers,["pending_npcf_"+(string)npc],thisPair+B1USER,thisPair+B1USER);
 764                 osNpcSit(npc,llList2Key(dancers,thisPair+B1ID),OS_NPC_SIT_NOW);
 765             } else if (llList2String(dancers,getPairIndex(uID)+B2USER)=="pending_npcm") {
 766                 string npcToRez;
 767                 if (llListFindList(npcMatch,[uID])>=0) npcToRez=llList2String(npcMatch,llListFindList(npcMatch,[uID])+1);
 768                 else {
 769                     indexNpcM++;
 770                     if (indexNpcM>=llGetListLength(partnerM)) indexNpcM=0;
 771                     npcToRez=llList2String(partnerM,indexNpcM);
 772                 }
 773                 key npc=osNpcCreate(llList2String(firstNameM,llFloor(llFrand(llGetListLength(firstNameM)))),llList2String(lastNameM,llFloor(llFrand(llGetListLength(lastNameM)))),(llList2Vector(dancers,thisPair+BASEPOS)+<0,0,2>),npcToRez,osNpcObjectGroup);
 774                 dancers=[]+llListReplaceList(dancers,["pending_npcm_"+(string)npc],thisPair+B2USER,thisPair+B2USER);
 775                 osNpcSit(npc,llList2Key(dancers,thisPair+B2ID),OS_NPC_SIT_NOW);
 776             }
 777             showMain(uID);
 778         } else if (command=="USER_STOOD") {
 779             key aviThatStood=llList2Key(message,1);
 780             integer indexAviThatStood=llListFindList(dancers,[aviThatStood]);
 781             osAvatarStopAnimation(aviThatStood,llList2String(dancers,indexAviThatStood+2));
 782             integer indexPair=getPairIndex(aviThatStood);
 783             integer indexPartner;
 784             string anToStartPartner;
 785             vector posPartner;
 786             vector rotPartner;
 787             if (indexPair==indexAviThatStood) {
 788                 indexPartner=indexPair+B2USER;
 789                 anToStartPartner=llList2String(anDefault,5);
 790                 posPartner=llList2Vector(anDefault,6);
 791                 rotPartner=llList2Vector(anDefault,7);
 792             } else {
 793                 indexPartner=indexPair;
 794                 anToStartPartner=llList2String(anDefault,2);
 795                 posPartner=llList2Vector(anDefault,3);
 796                 rotPartner=llList2Vector(anDefault,4);
 797             }
 798             key keyPartner=llList2Key(dancers,indexPartner);
 799             if (osIsUUID(keyPartner) && (keyPartner!=NULL_KEY)) {
 800                 dancers=[]+llListReplaceList(dancers,["avatar stood"],indexAviThatStood,indexAviThatStood);
 801                 dancers=[]+llListReplaceList(dancers,[0],indexPair+AUTO,indexPair+AUTO);
 802                 if (myState=="EDIT") {
 803                     myState="READY";
 804                     if (objectPresent(llList2Key(dancers,indexPartner+1))) osMessageObject(llList2Key(dancers,indexPartner+1),"HIDE_BALL");
 805                     llOwnerSay("WARNING!!! Either you or your partner stood (or crashed?) while in edit mode. Machine will immediately exit edit mode.\nChanges to the current animation ARE NOT stored.\nAny changes you've made to other animations ARE stored.\nThese will now be saved automatically to notecard to prevent their loss.\nYour dialog (if any) is now invalid so press any button to get a valid one");
 806                     saveNotecard();
 807                 }
 808                 if (osIsNpc(keyPartner)) { osNpcRemove(keyPartner); return;}
 809                 if (objectPresent(llList2Key(dancers,indexPartner+1))) osMessageObject(llList2Key(dancers,indexPartner+1),"MOVE_BALL|"+relToReg(llList2Vector(dancers,indexPair+BASEPOS),ZERO_ROTATION,posPartner,rotPartner));
 810                 osAvatarStopAnimation(keyPartner,llList2String(dancers,indexPartner+2));
 811                 osAvatarPlayAnimation(keyPartner,anToStartPartner);
 812                 llRegionSayTo(keyPartner,0,"Your partner is no longer dancing. If the ball is not re-occupied in the next "+(string)ballTimeout+" seconds both balls will be removed");
 813             } else {
 814                 if (llSubStringIndex(keyPartner,"pending_npc")==0) {
 815                     string killThis=llGetSubString(keyPartner,13,-1);
 816                     if (agentPresent((key)killThis)) osNpcRemove((key)killThis);
 817                 }
 818                 if (objectPresent(llList2Key(dancers,indexPartner+1))) osMessageObject(llList2Key(dancers,indexPartner+1),"KILL_BALL");
 819                 if (objectPresent(ball)) osMessageObject(ball,"KILL_BALL");
 820                 dancers=[]+llDeleteSubList(dancers,indexPair,indexPair+dancerStride-1);
 821                 updateText();
 822             }
 823         }
 824     }
 825     listen (integer channel, string name, key who, string message)
 826     {
 827         if (channel == RemoteChannel && llGetOwner() == llGetOwnerKey(who))
 828         {
 829             ProcessRemoteToucher(message);
 830         }
 831         else if (myState=="ERROR") 
 832         {
 833             if (who!=llGetOwner()) llRegionSayTo(who,0,"Sorry, the controller has encountered an error and needs to be reset before it can resume working. Please inform the owner");
 834             else 
 835             {
 836                 llOwnerSay("Attempting script reset");
 837                 llResetScript();
 838             }
 839         } 
 840         else if (myState=="INITIALIZING") llRegionSayTo(who,0,"Sorry, the controller is re-initializing. You will need to start dancing again once it has finished doing so");
 841         else if (myState=="EDIT") 
 842         {
 843             if (who!=llGetOwner()) { llRegionSayTo(who,0,"Sorry, the controller has been switched into edit mode will be reset once the owner has finished making changes. Then you'll be able to dance"); return;}
 844             if (message=="SYNCH") doSynch(who);
 845             else if (message=="< PREV") {
 846                 integer indexAn=llListFindList(anData,llList2List(dancers,STYLE,ANNAME));
 847                 persistCurrent(indexAn);
 848                 indexAn-=anStride;
 849                 if (indexAn<0) indexAn=llGetListLength(anData)-anStride;
 850                 playAnimation(0,indexAn);
 851                 llOwnerSay("Changes (if any) stored locally. Now editing\n    Style: "+llList2String(anData,indexAn)+"\n    Anim Name: "+llList2String(anData,indexAn+1));
 852             } else if (message=="NEXT >") {
 853                 integer indexAn=llListFindList(anData,llList2List(dancers,STYLE,ANNAME));
 854                 persistCurrent(indexAn);
 855                 indexAn+=anStride;
 856                 if (indexAn>llGetListLength(anData)) indexAn=0;
 857                 playAnimation(0,indexAn);
 858                 llOwnerSay("Changes (if any) stored locally. Now editing\n    Style: "+llList2String(anData,indexAn)+"\n    Anim Name: "+llList2String(anData,indexAn+1));
 859             } else if (message=="REVERT") {
 860                 integer indexAn=llListFindList(anData,llList2List(dancers,STYLE,ANNAME));
 861                 playAnimation(0,indexAn);
 862                 llOwnerSay("Reverted to original position. Now editing\n    Style: "+llList2String(anData,indexAn)+"\n    Anim Name: "+llList2String(anData,indexAn+1));
 863             } else if (message=="SAVE") {
 864                 integer indexAn=llListFindList(anData,llList2List(dancers,STYLE,ANNAME));
 865                 persistCurrent(indexAn);
 866                 saveNotecard();
 867             } else if (message=="EDIT OFF") {
 868                 integer indexAn=llListFindList(anData,llList2List(dancers,STYLE,ANNAME));
 869                 persistCurrent(indexAn);
 870                 myState="READY";
 871                 updateText();
 872                 osMessageObject(llList2Key(dancers,B1ID),"HIDE_BALL");
 873                 osMessageObject(llList2Key(dancers,B2ID),"HIDE_BALL");
 874             }
 875             showOptions(who);
 876         } 
 877         else if (myState=="READY") 
 878         {
 879             if (message=="OOPS") return;
 880             else if (message=="GROUP") startGroupDancing(who);
 881             else if (message=="SINGLES") 
 882             {
 883                 if (llGetListLength(singlesStyles)==1) 
 884                 {
 885                     singlesUsers=[]+singlesUsers+[who,llList2String(singlesStyles,0)];
 886                     startSingles(who,llList2String(singlesStyles,0));
 887                     llRegionSayTo(who,0,"You will start to dance momentarily...please touch me again to stop");
 888                 } 
 889                 else 
 890                 {
 891                     singlesUsers=[]+singlesUsers+[who,"NO_STYLE_SELECTED"];
 892                     showSinglesMenu(who,0);
 893                 }
 894                 return;
 895             } 
 896             else if ((message=="COUPLES") || (message=="NPC COUPLES")) 
 897             {
 898                 if (llListFindList(singlesUsers,who)>=0) {
 899                     llRegionSayTo(who,0,"Sorry, I seem to have you in my list of singles dancers. Please quit dancing first, then request a new couples dance");
 900                     showSinglesMenu(who,-1); return;
 901                 }
 902                 if (llListFindList(dancers,[who])>=0) {
 903                     llRegionSayTo(who,0,"Sorry, I already seem to have you in my list of couples dancers so I can't rez a new pair of danceballs for you. Please QUIT and inform the owner if this error continues to occur");
 904                     showMain(who); return;
 905                 }
 906                 vector toucherPos=llList2Vector(llGetObjectDetails(who,[OBJECT_POS]),0);
 907                 if (toucherPos==ZERO_VECTOR) {
 908                     llOwnerSay("ERROR! Toucher detected with ZERO_VECTOR as their location!");
 909                     llRegionSayTo(who,0,"Sorry, there appears to have been an error detecting your current location so I cannot rez any poseballs for you"); return;
 910                 }
 911                 if (llListFindList(ballRezFor,[who])>-1) {
 912                     integer indexBRF=llListFindList(ballRezFor,[who]);
 913                     if (llGetUnixTime()-llList2Integer(ballRezFor,indexBRF+1)<10) {
 914                         llRegionSayTo(who,0,"I think I have already rezzed a pair of balls for you within the last 10 seconds. If they failed to rez for some reason or you are unable to see them please wait a few more moments and try again (perhaps I detected a double-click in error)"); return;
 915                     }
 916                     else ballRezFor=[]+llDeleteSubList(ballRezFor,indexBRF,indexBRF+1);
 917                 }
 918                 if (message=="COUPLES") {
 919                     dancers=[] + dancers + ["pending_f",("AWAITING_BALL_REZ_"+(string)(nextBallID+1))] + llList2List(anDefault,2,4) + ["pending_m",("AWAITING_BALL_REZ_"+(string)(nextBallID+2))] + llList2List(anDefault,5,7) + [toucherPos, 0] + llList2List(anDefault,0,1) + [0];
 920                     llRegionSayTo(who,0,"Rezzing a pair of dance balls near you for you and your partner to sit on.");
 921                 } else {
 922                     dancers=[] + dancers + ["pending_npcf",("AWAITING_BALL_REZ_"+(string)(nextBallID+1))] + llList2List(anDefault,2,4) + ["pending_npcm",("AWAITING_BALL_REZ_"+(string)(nextBallID+2))] + llList2List(anDefault,5,7) + [toucherPos, 0] + llList2List(anDefault,0,1) + [0];
 923                     llRegionSayTo(who,0,"Rezzing a pair of dance balls. Your NPC partner will join you once you select one.");
 924                 }
 925                 llRezObject(poseballName,llGetPos(),ZERO_VECTOR,ZERO_ROTATION,(nextBallID+1));
 926                 llRezObject(poseballName,llGetPos(),ZERO_VECTOR,ZERO_ROTATION,(nextBallID+2));
 927                 nextBallID+=2;
 928                 ballRezFor=[]+ballRezFor + [who,llGetUnixTime()];
 929                 updateText();
 930             }
 931             else if ((llListFindList(dancers,[who])<0) && (llListFindList(singlesUsers,[who])<0) && (llListFindList(groupUsers,[who])<0) ) llRegionSayTo(who,0,"Sorry, you don't appear to be in my list of current dancers any more.");
 932             else if (message=="-") showMain(who);
 933             else if (message=="*") showSinglesMenu(who,-1);
 934             else if (message=="~") {
 935                 integer thisPair=getPairIndex(who);
 936                 if (llList2Integer(dancers,thisPair+AUTO)) showMain(who);
 937                 else {
 938                     string backToStyle=llList2String(dancers,thisPair+STYLE);
 939                     if (llListFindList(styles,backToStyle)<0) showMain(who);
 940                     else showPickAn(who,backToStyle);
 941                 }
 942             } else if (message=="EDIT ON") {
 943                 if ((llGetListLength(dancers)>dancerStride) || (llGetListLength(singlesUsers)>0)) {
 944                     llOwnerSay("Sorry, it appears that someone else is using the controller now so you cannot enter edit mode until they quit.");
 945                     showOptions(who);
 946                 } else {
 947                     myState="EDIT";
 948                     updateText();
 949                     osMessageObject(llList2Key(dancers,B1ID),"EDIT_BALL");
 950                     osMessageObject(llList2Key(dancers,B2ID),"EDIT_BALL");
 951                     integer indexAn=llListFindList(anData,llList2List(dancers,STYLE,ANNAME));
 952                     dancers=[]+llListReplaceList(dancers,[0],SWAPFLAG,SWAPFLAG);
 953                     playAnimation(0,indexAn);
 954                     llOwnerSay("Entering edit mode.\nVERY IMPORTANT!!!! Any changes are only stored in live memory. To remember them for future you must click SAVE before the next time the system is reset to save them to notecard.\n\nNow editing\nStyle: "+llList2String(anData,indexAn)+"\nAnim Name: "+llList2String(anData,indexAn+1));
 955                     showOptions(who);
 956                 }
 957             } else if (message=="BACK") {
 958                 string backToStyle=llList2String(dancers,getPairIndex(who)+STYLE);
 959                 if (llListFindList(styles,backToStyle)<0) showMain(who);
 960                 else showPickAn(who,backToStyle);
 961             } else if (message=="SWAP") {
 962                 integer thisPair=getPairIndex(who);
 963                 integer nextAnIndex=llListFindList(anData,llList2List(dancers,thisPair+STYLE,thisPair+ANNAME));
 964                 if (nextAnIndex<0) llRegionSayTo(who,0,"Sorry, I am unable to swap positions for the current animation");
 965                 else {
 966                     if (llList2Integer(dancers,thisPair+SWAPFLAG)==0) dancers=[]+llListReplaceList(dancers,[1],thisPair+SWAPFLAG,thisPair+SWAPFLAG);
 967                     else dancers=[]+llListReplaceList(dancers,[0],thisPair+SWAPFLAG,thisPair+SWAPFLAG);
 968                     playAnimation(thisPair,nextAnIndex);
 969                 }
 970                 showOptions(who);
 971             } else if (message=="AUTO") {
 972                 integer thisPair=getPairIndex(who);
 973                 dancers=[]+llListReplaceList(dancers,[1],thisPair+AUTO,thisPair+AUTO);
 974                 string txtToSay="Your couple is now in Auto dance mode.\nHappy dancing!";
 975                 llRegionSayTo(llList2Key(dancers,thisPair+B1USER),0,txtToSay);
 976                 llRegionSayTo(llList2Key(dancers,thisPair+B2USER),0,txtToSay);
 977                 if (llListFindList(anData,[llList2String(dancers,thisPair+STYLE)])<0) playAnimation(thisPair,0);
 978                 showMain(who);
 979             } else if (message=="MANUAL") {
 980                 integer thisPair=getPairIndex(who);
 981                 dancers=[]+llListReplaceList(dancers,[0],thisPair+AUTO,thisPair+AUTO);
 982                 string txtToSay="Your couple is now in Manual dance mode.";
 983                 llRegionSayTo(llList2Key(dancers,thisPair+B1USER),0,txtToSay);
 984                 llRegionSayTo(llList2Key(dancers,thisPair+B2USER),0,txtToSay);
 985                 showMain(who);
 986             } else if (message=="Z ADJUST") {
 987                 if (objectPresent(llList2Key(dancers,llListFindList(dancers,[who])+1))) osMessageObject(llList2Key(dancers,llListFindList(dancers,[who])+1),"ENABLE_Z|"+(string)zStep);
 988                 llRegionSayTo(who,0,"Z-Adjust enabled. Use your keyboard's PAGE UP and PAGE DOWN keys to adjust your height off the floor (this only changes your position, your partner may need to make a similar adjustment)\nThe adjustments you make will apply to all future dances you select. Use \"Z STOP ADJ\" to turn off the adjustment controls and keep your current setting, or \"Z RESET\" to return to the default position.");
 989                 showOptions(who);
 990             } else if (message=="Z STOP ADJ") {
 991                 if (objectPresent(llList2Key(dancers,llListFindList(dancers,[who])+1))) osMessageObject(llList2Key(dancers,llListFindList(dancers,[who])+1),"DISABLE_Z");
 992                 llRegionSayTo(who,0,"Z-Adjust disabled but your current adjustment will apply to any other dances you select. Press \"Z RESET\" at any time if you want to clear it");
 993                 showOptions(who);
 994             } else if (message=="Z RESET") {
 995                 if (objectPresent(llList2Key(dancers,llListFindList(dancers,[who])+1))) osMessageObject(llList2Key(dancers,llListFindList(dancers,[who])+1),"UNSET_Z");
 996                 llRegionSayTo(who,0,"Your current z-adjustment has been reset to zero");
 997                 showOptions(who);
 998             } else if ((message=="NEW M") || (message=="NEW F")) {
 999                 integer thisPair=getPairIndex(who);
1000                 key keyToChange=llList2Key(dancers,thisPair+B1USER);
1001                 if (keyToChange==who) keyToChange=llList2Key(dancers,thisPair+B2USER);
1002                 if (osIsNpc(keyToChange)) {
1003                     if (message=="NEW M") {
1004                         indexNpcM++;
1005                         if (indexNpcM>=llGetListLength(partnerM)) indexNpcM=0;
1006                         osNpcLoadAppearance(keyToChange,llList2String(partnerM,indexNpcM));
1007                     } else {
1008                         indexNpcF++;
1009                         if (indexNpcF>=llGetListLength(partnerF)) indexNpcF=0;
1010                         osNpcLoadAppearance(keyToChange,llList2String(partnerF,indexNpcF));
1011                     }
1012                 } else llRegionSayTo(who,0,"Can't change your partner's appearance...they aren't a NPC.");
1013                 showOptions(who);
1014             } else if (message=="STYLES") showMain(who);
1015             else if (message=="SYNCH") {
1016                 doSynch(who);
1017                 integer thisPair=getPairIndex(who);
1018                 if (llList2Integer(dancers,thisPair+AUTO)) showMain(who);
1019                 else {
1020                     string backToStyle=llList2String(dancers,thisPair+STYLE);
1021                     if (llListFindList(styles,backToStyle)<0) showMain(who);
1022                     else showPickAn(who,backToStyle);
1023                 }
1024             } else if (message=="OPTIONS") showOptions(who);
1025             else if (message=="QUIT") {
1026                 if (llListFindList(singlesUsers,[who])>=0) {
1027                     integer indWho=llListFindList(singlesUsers,[who]);
1028                     stopSingles(who,llList2String(singlesUsers,indWho+1));
1029                     singlesUsers=[]+llDeleteSubList(singlesUsers,indWho,indWho+1);
1030                 } else {
1031                     integer thisPair=getPairIndex(who);
1032                     string txtToSay="One of the couple has chosen to quit dancing";
1033                     if (objectPresent(llList2Key(dancers,thisPair+B1ID))) osMessageObject(llList2Key(dancers,thisPair+B1ID),"KILL_BALL");
1034                     if (osIsNpc(llList2Key(dancers,thisPair+B1USER))) osNpcRemove(llList2Key(dancers,thisPair+B1USER));
1035                     else if (osIsUUID(llList2Key(dancers,thisPair+B1USER))) {
1036                         osAvatarStopAnimation(llList2Key(dancers,thisPair+B1USER),llList2String(dancers,thisPair+B1AN));
1037                         llRegionSayTo(llList2Key(dancers,thisPair+B1USER),0,txtToSay);
1038                     } else if (llSubStringIndex(llList2Key(dancers,thisPair+B1USER),"pending_")>=0) {
1039                         string killNpcPendingF=llGetSubString(llList2String(dancers,thisPair+B1USER),13,-1);
1040                         if (osIsUUID(killNpcPendingF)) { if (llGetAgentSize(killNpcPendingF)!=ZERO_VECTOR) osNpcRemove((key)killNpcPendingF); }
1041                     }
1042                     if (objectPresent(llList2Key(dancers,thisPair+B2ID))) osMessageObject(llList2Key(dancers,thisPair+B2ID),"KILL_BALL");
1043                     if (osIsNpc(llList2Key(dancers,thisPair+B2USER))) osNpcRemove(llList2Key(dancers,thisPair+B2USER));
1044                     else if (osIsUUID(llList2Key(dancers,thisPair+B2USER))) {
1045                         osAvatarStopAnimation(llList2Key(dancers,thisPair+B2USER),llList2String(dancers,thisPair+B2AN));
1046                         llRegionSayTo(llList2Key(dancers,thisPair+B2USER),0,txtToSay);
1047                     } else if (llSubStringIndex(llList2Key(dancers,thisPair+B2USER),"pending_")>=0) {
1048                         string killNpcPendingM=llGetSubString(llList2String(dancers,thisPair+B2USER),13,-1);
1049                         if (osIsUUID(killNpcPendingM)) { if (llGetAgentSize(killNpcPendingM)!=ZERO_VECTOR) osNpcRemove((key)killNpcPendingM); }
1050                     }
1051                     dancers=[]+llDeleteSubList(dancers,thisPair,thisPair+dancerStride-1);
1052                 }
1053                 updateText();
1054             } else if ((message=="< PREV") || (message=="NEXT >") || (message=="SAVE") || (message=="REVET") || (message=="EDIT OFF")) showMain(who);
1055             else if (llListFindList(singlesUsers,[who])>=0) {
1056                 integer page=-1;
1057                 integer uInd=llListFindList(singlesUsers,[who]);
1058                 if (llSubStringIndex(message,"PAGE ")>=0) page=(integer)llGetSubString(message,5,-1)-1;
1059                 else if (message!=llList2String(singlesUsers,uInd+1)) {
1060                     singlesUsers=[]+llListReplaceList(singlesUsers,[message],uInd+1,uInd+1);
1061                     startSingles(who,message);
1062                 }
1063                 showSinglesMenu(who,page);
1064             } else if (llListFindList(styles,[message])>-1) {
1065                 integer thisPair=getPairIndex(who);
1066                 if (llList2Integer(dancers,thisPair+AUTO)) {
1067                     string txtToSay="New auto dance style selected by your couple: "+message;
1068                     if (osIsUUID(llList2Key(dancers,thisPair+B1USER))) llRegionSayTo(llList2Key(dancers,thisPair+B1USER),0,txtToSay);
1069                     if (osIsUUID(llList2Key(dancers,thisPair+B2USER))) llRegionSayTo(llList2Key(dancers,thisPair+B2USER),0,txtToSay);
1070                     dancers=[]+llListReplaceList(dancers,[message],thisPair+STYLE,thisPair+STYLE);
1071                     playAnimation(thisPair,llListFindList(anData,[message]));
1072                     showMain(who);
1073                 } else showPickAn(who,message);
1074             } else if (llListFindList(anData,[message])>-1) {
1075                 integer thisPair=getPairIndex(who);
1076                 if (llList2Integer(dancers,thisPair+AUTO)) {
1077                     string txtToSay="A dance was manually selected...switching your couple to manual dance mode";
1078                     if (osIsUUID(llList2Key(dancers,thisPair+B1USER))) llRegionSayTo(llList2Key(dancers,thisPair+B1USER),0,txtToSay);
1079                     if (osIsUUID(llList2Key(dancers,thisPair+B2USER))) llRegionSayTo(llList2Key(dancers,thisPair+B2USER),0,txtToSay);
1080                     dancers=[]+llListReplaceList(dancers,[0],thisPair+AUTO,thisPair+AUTO);
1081                 }
1082                 integer selectedDanceIndex=llListFindList(anData,[message])-1;
1083                 playAnimation(thisPair,selectedDanceIndex);
1084                 showPickAn(who,llList2String(anData,selectedDanceIndex));
1085             } else llOwnerSay("ERROR! Received a dialog response in READY state but it failed to match a button name and was neither a style nor animation name. This shouldn't be possible!");
1086         }
1087     }
1088 }

Syntax Highlighting

GeShi syntax highlighting is enabled on this wiki. <be>

When using the tabs described in the link, change the language part to "lsl". Eg.
syntaxhighlight lang="lsl" line
inside angled brackets <>