Clubmaster Server Version
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
- Make a prim and place the "~Clubmaster poseball v2.lsl" script into the prim.
- Take this prim to your inventory and call it "~club poseball" with the quotation marks
- Make a new prim which will be the main dance ball
- Add the "~club poseball" into the dance balls inventory along with all other scripts and notecards from this repository.
Upgrade Existing Clubmaster V2
- Replace the existing Clubmaster control script with this version
- 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 <>