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