· 6 years ago · Oct 13, 2019, 06:14 AM
1// Diaper Application Number
2#define DIAPER_KEY 121111
3
4#define MODEL "Pullup"
5
6#define WHITELIST_OFFSET 10
7
8// Genders
9#define GENDER_MALE 0
10#define GENDER_FEMALE 1
11
12// Accident types
13#define WET_TYPE 0
14#define MESS_TYPE 1
15#define SQUEEZE_TYPE 2
16#define TICKLE_TYPE 3
17#define WET_AID_TYPE 4
18#define MESS_AID_TYPE 5
19#define ENEMA_AID_TYPE 6
20
21// Accident maximums
22#define WET_MAX 2
23#define MESS_MAX 1
24
25// Enema Phases
26#define ENEMA_PHASE_INACTIVE -1
27#define ENEMA_PHASE_SETUP 0
28#define ENEMA_PHASE_ONE 1
29#define ENEMA_PHASE_TWO 2
30#define ENEMA_PHASE_THREE 3
31#define ENEMA_PHASE_RELEASE 4
32#define ENEMA_PHASE_PLUGGED 5
33
34// These serve as the 'num' param of llMessageLinked.
35#define LINK_PRINTOUTS_2 -4
36#define LINK_PRINTOUTS_1 -2
37#define LINK_ALL -1
38#define LINK_MAIN 0
39#define LINK_RENDER 1
40#define LINK_POTTY 2
41#define LINK_ACCIDENTS 3
42#define LINK_PRINTOUT 4
43#define LINK_ADD_ON 5
44
45// User roles
46#define ROLE_OWNER 0
47#define ROLE_CARER 1
48#define ROLE_ANONYMOUS 2
49
50// Potty training states
51#define BABY 0
52#define TODDLER 1
53#define BED_WETTER 2
54#define POTTY_TRAINED 3
55
56// Accident Aid Accident Intervals
57#define AID_BATCH_SIZE 3
58#define WET_AID_INTERVAL 3
59#define MESS_AID_INTERVAL 3
60
61// Access Modes
62#define ACCESS_SELF 0
63#define ACCESS_CARER 1
64#define ACCESS_ALL 2
65#define ACCESS_CARER_ONLY 3
66
67// Chatter Modes
68#define CHATTER_PRIVATE 0
69#define CHATTER_OPEN_CHAT 1
70#define CHATTER_UNAWARE 2
71
72// Caretaker settings
73#define MAX_CARETAKERS 12
74
75// Menus
76#define USER_MENU ["Get❤Soggy", "Get❤Stinky", "Check", "Change", "Settings", "Show/Hide", "On/Off", "Caretakers", "Whitelist"]
77#define USER_SELF_MENU ["Get❤Soggy", "Get❤Stinky", "Check", "Change", "Settings", "Show/Hide", "On/Off", "Caretakers", "Lock/Unlock", "Tricks", "Whitelist"]
78#define USER_SELF_LOCKED_MENU ["Get❤Soggy", "Get❤Stinky", "Check", "Lock/Unlock"]
79#define USER_LOCKED_MENU ["Get❤Soggy", "Get❤Stinky", "Check", "Safeword"]
80#define ANON_MENU ["❤Tickle❤", "❤Squeeze❤", "Check", "Change", "Raspberry", "Spank", "Wedgie"]
81#define ANON_MENU_TRICKS ["❤Tickle❤", "❤Squeeze❤", "Check", "Change", "Raspberry", "Spank", "Wedgie", "Tricks"]
82#define SETTING_MENU ["Training", "Wet❤Timer", "Mess❤Timer", "Gender", "Textures", "Access", "Chatter", "Nickname", "Tattle"]
83#define ACCESS_MENU ["Self", "Self+Carers", "Carers❤Only", "All"]
84#define CHATTER_MENU ["Private", "Open Chat", "Unaware"]
85#define TRAINING_MENU ["Baby", "Toddler", "Bedwetter", "Potty❤Trained"]
86#define GENDER_MENU ["Male", "Female", "Reload"]
87#define MANAGE_CARETAKER_MENU ["Add", "Remove", "Show", "Toggle Open"]
88#define CARER_MENU ["❤Tickle❤", "❤Squeeze❤", "Check", "Change", "Training", "Raspberry", "Spank", "Wedgie", "Tricks", "Show/Hide", "Lock/Unlock", "Nickname"]
89#define CARER_TRICKS_MENU ["Mudpie❤Lax", "Puddle❤Aid", "Tushie❤Rush", "Potty❤Plug"]
90#define CARER_TRICKS_MENU_UNPLUG ["Puddle❤Aid", "❤Unplug❤"]
91#define LOCK_TIMES ["15", "30", "45", "60", "120", "180", "Forever"]
92
93// Timer tick rate
94#define TICK_RATE 30
95
96// Tech helpers
97#define SECONDS_IN_YEAR 31536000
98
99key owner;
100
101// Link number of touch box
102integer touchPrim;
103
104// Listen Handlers
105integer channelHandle;
106integer manageCarersChannelHandle;
107integer setNicknameChannelHandle;
108integer whitelistChannelHandle;
109
110// Channels
111integer channel;
112integer manageCarersChannel;
113integer setNicknameChannel;
114integer whitelistChannel;
115
116// Diaper settings
117integer isOn = TRUE;
118integer isHidden = FALSE;
119integer accessMode = 2; // All have access
120integer chatter = 1; // Public by default
121integer isTattleTale = FALSE;
122integer openCaretaker = FALSE; // Everyone is a caretaker
123integer isSuppressed = FALSE; // Whether or not the pullup accepts touch commands.
124
125// Accident settings
126integer training = 1; // Training is used to identify preset training tiers.
127integer wetness = 0;
128integer messiness = 0;
129integer wetTimer = 0;
130integer messTimer = 0;
131integer wetInterval = 30;
132integer messInterval = 45;
133integer wetTraining = 50;
134integer messTraining = 50;
135integer wetHoldAttempts = 0;
136integer messHoldAttempts = 0;
137
138integer enemaTimer = 0;
139
140// Accident Aids:
141// - Added in increment of three
142// - Decremented each time an accident occurs.
143// - While at least one is in a user's system, their linked accident interval drops to 3 minutes.
144integer mudpies;
145integer puddles;
146integer isEnemaActive = FALSE; // True/False, indicates whether an enema is in progress.
147integer enemaPhase = ENEMA_PHASE_INACTIVE; // Current active phase
148integer phasePlugged = ENEMA_PHASE_INACTIVE; // Which phase the user was plugged during.
149integer isPlugged = FALSE; // True/False
150
151// User settings
152integer gender = 0;
153string nickname = "";
154
155// Timer helpers
156integer isIntervalBeingSet;
157integer isLockIntervalBeingSet;
158
159// Carers
160list carers;
161list detectedKeys;
162
163// RLV
164integer isLocked = FALSE;
165integer lockTimer; // Tracks how much time has passed.
166integer unlockTime; // How much time must pass before the pullup unlocks.
167
168// HTTP Requests
169integer isSynced = FALSE;
170key settingRequestId;
171key caretakerRequestId;
172key userRequestId;
173
174integer modelToggle = FALSE;
175
176/*
177* Used to generate unique channel for pullup to communicate on.
178*/
179integer generateChan(key id) {
180 return 0x80000000 | ((integer)("0x"+(string)id) ^ DIAPER_KEY);
181}
182
183/*
184* Dialog boxes are currently pretty sparse. I'd like to be able to blindly append basic pullup stats to them.
185* 1. Time Lock Duration
186* 2. Current Training Level
187* 3. Current Access Level
188* 4. Current Chatter Level
189*/
190string getMenuInfoText() {
191 integer timeLeft = ((unlockTime * 60) - lockTimer);
192 string lock;
193 string duration;
194 string potty;
195 string chat;
196 string access;
197 string sex;
198
199 if(isLocked) {
200 lock = "\nLocked";
201 } else {
202 lock = "\nUnlocked";
203 }
204 if(wetTraining < 0) {
205 wetTraining = 0;
206 }
207 if(messTraining < 0) {
208 messTraining = 0;
209 }
210 if(timeLeft == 0) {
211 duration = "\nNo Timelock Active";
212 } else if (timeLeft > 10800) {
213 duration = "\nTimelock Duration: Forever";
214 } else {
215 duration = "\nTimelock Duration: " + (string) timeLeft + " seconds";
216 }
217
218 if(training) {
219 potty = "\nPotty Training: In Training";
220 } else {
221 potty = "\nPotty Training: Not Training";
222 }
223
224 if(wetTraining < 50 && messTraining < 50) {
225 potty += "\n|-Title: Baby";
226 } else if ( (wetTraining >= 50 && wetTraining < 100) && (messTraining >= 50 && messTraining < 100) ) {
227 potty += "\n|-Title: Training: Toddler";
228 } else if ( wetTraining < 100 && messTraining == 100 ) {
229 potty += "\n|-Title: Bedwetter";
230 } else if (wetTraining == 100 && messTraining == 100) {
231 potty += "\n|-Title: Trained";
232 }
233
234 potty += "\n|-Wet Training: " + (string) wetTraining;
235 potty += "\n|-Mess Training: " + (string) messTraining;
236
237 if(chatter == 0) {
238 chat = "\nChatter Level: Private";
239 } else if(chatter == 1) {
240 chat = "\nChatter Level: Open Chat";
241 } else if(chatter == 2) {
242 chat = "\nChatter Level: Unaware";
243 }
244
245 // Open caretaker overwrites all
246 if(openCaretaker == TRUE) {
247 access = "\nOpen Caretaker is ON";
248 }
249
250 if(accessMode == 0) {
251 access += "\nAccess Level: Self";
252 } else if (accessMode == 1) {
253 access += "\nAccess Level: Self+Carer";
254 } else if (accessMode == 2) {
255 access += "\nAccess Level: All";
256 } else {
257 access += "\nAccess Level: Carer❤Only";
258 }
259
260 if(gender == 0) {
261 sex = "\nGender: Male";
262 } else if (gender == 1) {
263 sex = "\nGender: Female";
264 }
265
266 string info = lock + duration + potty + chat + access + sex + "\nFree Memory; " + (string) llGetFreeMemory();
267
268 return info;
269}
270
271/*
272* This function is responsible for sending out the current value for every database-saved setting
273* to all of our plugins and addons. It is ONLY called immediately after a re-sync with the server.
274*
275* Worth noting, DrizzleMain will hear this broadcast, too! It's not a big deal, though.
276*/
277broadcastSettings() {
278 llMessageLinked(LINK_THIS, LINK_ALL, "gender", (string) gender);
279 llMessageLinked(LINK_THIS, LINK_ALL, "training", (string) training);
280 llMessageLinked(LINK_THIS, LINK_ALL, "wetness", (string) wetness);
281 llMessageLinked(LINK_THIS, LINK_ALL, "messiness", (string) messiness);
282 llMessageLinked(LINK_THIS, LINK_ALL, "wetInterval", (string) wetInterval);
283 llMessageLinked(LINK_THIS, LINK_ALL, "messInterval", (string) messInterval);
284 llMessageLinked(LINK_THIS, LINK_ALL, "puddles", (string) puddles);
285 llMessageLinked(LINK_THIS, LINK_ALL, "mudpies", (string) mudpies);
286 llMessageLinked(LINK_THIS, LINK_ALL, "isLocked", (string) isLocked);
287 llMessageLinked(LINK_THIS, LINK_ALL, "accessMode", (string) accessMode);
288 llMessageLinked(LINK_THIS, LINK_ALL, "chatter", (string) chatter);
289 llMessageLinked(LINK_THIS, LINK_ALL, "isPlugged", (string) isPlugged);
290 llMessageLinked(LINK_THIS, LINK_ALL, "nickname", (string) nickname);
291 llMessageLinked(LINK_THIS, LINK_ALL, "carers", llList2CSV(carers));
292 llMessageLinked(LINK_THIS, LINK_ALL, "openCaretaker", (string) openCaretaker);
293 llMessageLinked(LINK_THIS, LINK_ALL, "isTattleTale", (string) isTattleTale);
294 llMessageLinked(LINK_THIS, LINK_ALL, "wetTraining", (string) wetTraining);
295 llMessageLinked(LINK_THIS, LINK_ALL, "messTraining", (string) messTraining);
296 llMessageLinked(LINK_THIS, LINK_ALL, "wetHoldAttempts", (string) wetHoldAttempts);
297 llMessageLinked(LINK_THIS, LINK_ALL, "messHoldAttempts", (string) messHoldAttempts);
298}
299
300/*
301* Send accident check request to DrizzlePotty
302*/
303accidentCheck(integer type, key id) {
304 if(type == TICKLE_TYPE) {
305 llMessageLinked(LINK_THIS, LINK_POTTY, "Tickle", id);
306 } else if (type == SQUEEZE_TYPE) {
307 llMessageLinked(LINK_THIS, LINK_POTTY, "Squeeze", id);
308 }
309}
310
311/*
312* Used to convert list of keys to list of names.
313*/
314list getNamesFromKeys(list keys) {
315 list names;
316
317 integer i;
318 integer length = llGetListLength(keys);
319
320 for(i = 0; i < length; i++) {
321
322 string name = llKey2Name(llList2Key(keys, i));
323
324 // Truncate names to ensure they fit in the button.
325 if(llStringLength(name) > 24) {
326 name = llGetSubString(name, 0, 23);
327 }
328
329 names += name;
330 }
331
332 return names;
333}
334
335/*
336* The user is allowed to set a nickname for themselves which is specific to the pullup.
337* If one exists, this returns their nickname. Otherwise, it returns their display name.
338*/
339string getNickname() {
340 if(nickname != "") {
341 return nickname;
342 } else {
343 return llGetDisplayName(owner);
344 }
345
346}
347
348/*
349* Sets the user's new nickname, stripping out carriage returns.
350*/
351setNickname(string name) {
352
353 string newline = llUnescapeURL("%0A");
354 integer location = llSubStringIndex(name, newline);
355
356 // If newlines exist in the nickname, loop through all of them, and remove them.
357 while(~location) {
358 name = llDeleteSubString(name, location, location); // Delete single character found.
359 location = llSubStringIndex(name, newline); // Check for more...
360 }
361
362 nickname = name;
363 llOwnerSay("You just got a new nickname! Say hello, '" + nickname + "'");
364
365 // Alert potty system that nickname changed.
366 llMessageLinked(LINK_THIS, LINK_ALL, "nickname", nickname);
367
368 saveSettings();
369}
370
371// Dispatch update message to DrizzleRender
372updateUsagePrims() {
373 llMessageLinked(LINK_THIS, LINK_RENDER, (string) wetness + "," + (string) messiness, owner);
374}
375
376/*
377* Returns the user's role in the system. Owner, Carer, or Anonymous
378*/
379integer getRole(key id) {
380 if(id == owner) {
381 return ROLE_OWNER;
382 }
383 else if(openCaretaker == TRUE || isCarer(id)) {
384 return ROLE_CARER;
385 }
386 else {
387 return ROLE_ANONYMOUS;
388 }
389}
390
391/**
392* Lock a user's pullup. Trigger printout. Save change.
393*/
394lock(key id) {
395 isLocked = TRUE;
396
397 integer role = getRole(id);
398
399 llOwnerSay("@detach=n");
400
401 llMessageLinked(LINK_THIS, LINK_ALL, "Locked", id);
402
403 if(role == ROLE_OWNER) {
404 llMessageLinked(LINK_THIS, -4, (string) gender + ":" + (string) chatter + ":" + (string) wetness + ":" + (string) messiness + ":" + "Self Lock" + ":" + getNickname(), id);
405 } else {
406 llMessageLinked(LINK_THIS, -4, (string) gender + ":" + (string) chatter + ":" + (string) wetness + ":" + (string) messiness + ":" + "Other Lock" + ":" + getNickname(), id);
407 }
408
409 saveSettings();
410}
411
412/**
413* Unlock a user's pullup. Trigger printout. Save change.
414*/
415unlock(key id) {
416 isLocked = FALSE;
417
418 integer role = getRole(id);
419 llOwnerSay("@detach=y");
420
421 llMessageLinked(LINK_THIS, LINK_ALL, "Unlocked", id);
422
423 if(role == ROLE_OWNER) {
424 llMessageLinked(LINK_THIS, -4, (string) gender + ":" + (string) chatter + ":" + (string) wetness + ":" + (string) messiness + ":" + "Self Unlock" + ":" + getNickname(), id);
425 } else {
426 llMessageLinked(LINK_THIS, -4, (string) gender + ":" + (string) chatter + ":" + (string) wetness + ":" + (string) messiness + ":" + "Other Unlock" + ":" + getNickname(), id);
427 }
428
429 // Reset lock values.
430 lockTimer = 0;
431 unlockTime = 0;
432
433 saveSettings();
434}
435/**
436* Handles the locking and unlocking of the pullup.
437*/
438toggleLock(key id) {
439 // Toggle lock.
440 if(isLocked) {
441 unlock(id);
442 } else {
443 lock(id);
444 }
445}
446
447/*
448* When an avatar logs out, then back in, if they were locked, we need to re-acquire the RLV lock.
449*/
450reApplyLock() {
451 if(isLocked) {
452 llOwnerSay("@detach=n");
453
454 string name = llGetObjectName();
455
456 llSetObjectName("A");
457 llOwnerSay("/me lock snaps back into place on " + getNickname() + "'s pullup.");
458 llSetObjectName(name);
459 }
460}
461
462/*
463* Handles plugging/unplugging a user
464*/
465togglePlug(key id) {
466 llMessageLinked(LINK_THIS, LINK_POTTY, "Toggle Plug", id);
467}
468
469/*
470* Handles the caretaker administering laxatives/diuretics.
471*/
472giveAid(integer type, key id) {
473 if(type == WET_AID_TYPE) {
474 llMessageLinked(LINK_THIS, LINK_POTTY, "Diuretic", id);
475 } else if(type == MESS_AID_TYPE) {
476 llMessageLinked(LINK_THIS, LINK_POTTY, "Laxative", id);
477 } else if(type == ENEMA_AID_TYPE) {
478 llMessageLinked(LINK_THIS, LINK_POTTY, "Start Enema", id);
479 }
480}
481
482/*
483* Handles the user wetting.
484*/
485wet(integer type, key id) {
486 llMessageLinked(LINK_THIS, LINK_POTTY, "Wet", id);
487}
488
489/*
490* Handles the user messing.
491*/
492mess(integer type, key id) {
493 llMessageLinked(LINK_THIS, LINK_POTTY, "Mess", id);
494}
495
496/*
497* Handles pullup checks.
498*/
499check(integer role, key id) {
500 llMessageLinked(LINK_THIS, LINK_POTTY, "Check", id);
501}
502
503/*
504* Handles various pullup changes.
505*/
506change(integer role, key id) {
507 llMessageLinked(LINK_THIS, LINK_POTTY, "Change", id);
508}
509
510/*
511* Carer changes are special, because caretakers get to choose which type of pullup their baby wears.
512* With this in mind, change() gets called only after the texture switches in these cases.
513*/
514carerChange(integer role, key id) {
515 if(role == ROLE_CARER) {
516 // Dispatch texture request to DrizzleRender
517 llMessageLinked(LINK_THIS, LINK_RENDER, "Textures", id);
518 }
519}
520
521/*
522* Send Hide command to renderer.
523*/
524toggleHide()
525{
526 llMessageLinked(LINK_THIS, LINK_RENDER, "Toggle Hide", "");
527}
528
529//This function flips the value of a boolean variable, and
530//turns the Timer event on and off as appropriate.
531toggleOnOff()
532{
533 isOn = !isOn;
534
535 if(isOn == FALSE)
536 {
537 llSetTimerEvent(0.0);
538 }
539 else
540 {
541 llSetTimerEvent(TICK_RATE);
542 }
543
544 llMessageLinked(LINK_THIS, LINK_ALL, "isOn", (string)isOn);
545}
546
547setChatter(integer newChatter) {
548 if(chatter != newChatter) {
549 chatter = newChatter;
550 // Alert potty system that chatter level changed
551 llMessageLinked(LINK_THIS, LINK_ALL, "chatter", (string)chatter);
552 saveSettings();
553 }
554}
555
556setAccess(integer newAccessMode) {
557 if(accessMode != newAccessMode) {
558 accessMode = newAccessMode;
559 saveSettings();
560 }
561}
562
563// Handles gender swaps, letting our printout system know to reload itself.
564setGender(integer newGender) {
565 gender = newGender;
566
567 // Send message to printout scripts to read gender notecard.
568 llMessageLinked(LINK_THIS, -2, (string) gender + ":" + (string) chatter + ":" + "Update", NULL_KEY);
569 llMessageLinked(LINK_THIS, -4, (string) gender + ":" + (string) chatter + ":" + "Update", NULL_KEY);
570
571 // Alert potty system that gender changed. (if I save gender inside the printout scripts, I can remove it from the needed parameter list...)
572 llMessageLinked(LINK_THIS, LINK_ALL, "gender", (string)gender);
573
574 // Sync settings to server
575 saveSettings();
576}
577
578/*
579* Given a list of keys and an avatar name, return the key mapping to the given name.
580* Returns empty string if no match is found.
581*/
582key getKeyByName(list keyList, string name) {
583 integer i;
584 integer length = llGetListLength(keyList);
585
586 for(i = 0; i < length; i++) {
587 string currKey = llList2Key(keyList, i);
588 string currName = llGetDisplayName(currKey);
589
590 if(name == currName) {
591 return currKey;
592 }
593 }
594
595 return "";
596}
597
598/*
599* Simple check to see if someone is a caretaker.
600*/
601integer isCarer(key id) {
602 string toucherName = llKey2Name(id);
603
604 // Carer name was truncated when saved, due to button limits.
605 if(llStringLength(toucherName) > 24) {
606 toucherName = llGetSubString(toucherName, 0, 23);
607 }
608
609 if(contains(carers, toucherName)) {
610 return TRUE;
611 }
612 else return FALSE;
613
614}
615
616toggleTattle() {
617 isTattleTale = !isTattleTale;
618
619 if(isTattleTale) {
620 llOwnerSay("Tattle Enabled: You'll now know when others click your pullup!");
621 } else {
622 llOwnerSay("Tattle Disabled");
623 }
624
625 saveSettings();
626}
627
628/* Simple utility function to determine whether a list contained a specified element.
629 * @l - A list to test against
630 * @test - An element to search for inside l
631 * Returns TRUE if l contains test, FALSE otherwise.
632*/
633integer contains(list l, string test)
634{
635 if(~llListFindList(l, [test])) // test found, it's in the list!
636 {
637 return TRUE;
638 }
639 else return FALSE;
640}
641
642/*
643* Raincloud provides user settings in a JSON format. This function extracts the values and updates them in the pullup.
644*/
645loadSettings(string jsonData) {
646
647 gender = (integer) llJsonGetValue(jsonData, ["gender"]);
648 training = (integer) llJsonGetValue(jsonData, ["training"]);
649 wetness = (integer) llJsonGetValue(jsonData, ["wetness"]);
650 messiness = (integer) llJsonGetValue(jsonData, ["messiness"]);
651 wetInterval = (integer) llJsonGetValue(jsonData, ["wetInterval"]);
652 messInterval = (integer) llJsonGetValue(jsonData, ["messInterval"]);
653 puddles = (integer) llJsonGetValue(jsonData, ["puddles"]);
654 mudpies = (integer) llJsonGetValue(jsonData, ["mudpies"]);
655 isLocked = (integer) llJsonGetValue(jsonData, ["locked"]);
656 accessMode = (integer) llJsonGetValue(jsonData, ["access"]);
657 chatter = (integer) llJsonGetValue(jsonData, ["chatter"]);
658 isPlugged = (integer) llJsonGetValue(jsonData, ["plugged"]);
659 nickname = llJsonGetValue(jsonData, ["nickname"]);
660 openCaretaker = (integer) llJsonGetValue(jsonData, ["openCaretaker"]);
661 isTattleTale = (integer) llJsonGetValue(jsonData, ["tattle"]);
662 wetTraining = (integer) llJsonGetValue(jsonData, ["wetTraining"]);
663 messTraining = (integer) llJsonGetValue(jsonData, ["messTraining"]);
664 wetHoldAttempts = (integer) llJsonGetValue(jsonData, ["wetHoldAttempts"]);
665 messHoldAttempts = (integer) llJsonGetValue(jsonData, ["messHoldAttempts"]);
666
667 if(nickname == "NO-NICKNAME") {
668 nickname = "";
669 }
670
671 broadcastSettings();
672 updateUsagePrims();
673}
674
675saveSettings() {
676
677 // Only 8 custom headers allowed, pack settings into CSVs for efficiency.
678 string chunk1 = (string) gender + "," + (string) messiness + "," + (string) messInterval + "," + (string) training;
679 string chunk2 = (string) wetness + "," + (string) wetInterval + "," + (string) puddles + "," + (string) mudpies;
680 string chunk3 = (string) isLocked + "," + (string) accessMode + "," + (string) chatter + "," + (string) isPlugged;
681
682 // Temporarily shift nickname to empty token.
683 if(nickname == "") {
684 nickname = "NO-NICKNAME";
685 }
686
687 string chunk4 = nickname + "," + (string) openCaretaker + "," + (string) isTattleTale + "," + (string) wetTraining;
688 string chunk5 = (string) messTraining + "," + (string) wetHoldAttempts + "," + (string) messHoldAttempts;
689
690
691 // Encode data as headers, send to server.
692 settingRequestId = llHTTPRequest("http://23.253.106.210/settings", [HTTP_METHOD, "PUT",
693 HTTP_CUSTOM_HEADER, "gender-messiness-messInterval-training", chunk1,
694 HTTP_CUSTOM_HEADER, "wetness-wetInterval-puddles-mudpies", chunk2,
695 HTTP_CUSTOM_HEADER, "locked-access-chatter-plugged", chunk3,
696 HTTP_CUSTOM_HEADER, "nickname-openCaretaker-tattle-wetTraining", chunk4,
697 HTTP_CUSTOM_HEADER, "messTraining-wetHoldAttempts-messHoldAttempts", chunk5], "");
698 // Restore nickname back to empty string
699 if(nickname == "NO-NICKNAME") {
700 nickname = "";
701 }
702}
703
704default
705{
706 /*
707 * Exists purely to ensure the owner and their channels are registered!
708 */
709 state_entry() {
710 owner = llGetOwner();
711
712 channel = generateChan(owner);
713 manageCarersChannel = channel + 1;
714 setNicknameChannel = channel + 3;
715
716 channelHandle = llListen(channel, "", "", "");
717 manageCarersChannelHandle = llListen(manageCarersChannel, "", owner, "");
718 setNicknameChannelHandle = llListen(setNicknameChannel, "", "", "");
719
720 if(isOn == TRUE){
721 llSetTimerEvent(TICK_RATE);
722 } else {
723 llSetTimerEvent(0.0);
724 }
725
726 if(isSynced == FALSE) {
727 isSynced = TRUE;
728
729 // Add/Welcome User
730 userRequestId = llHTTPRequest("http://23.253.106.210/users", [HTTP_METHOD, "POST"], "");
731
732 llSleep(1.0);
733
734 // Request Settings
735 settingRequestId = llHTTPRequest("http://23.253.106.210/settings", [], "");
736
737 llSleep(1.0);
738
739 // Request Carers
740 caretakerRequestId = llHTTPRequest("http://23.253.106.210/caretakers", [], "");
741 }
742 }
743
744 touch_start(integer total_number)
745 {
746 key toucher = llDetectedOwner(0);
747 integer role = getRole(toucher);
748
749 llMessageLinked(LINK_THIS, LINK_ALL, "Diaper Clicked", toucher);
750
751 if(!isSuppressed) {
752
753 if((role != ROLE_OWNER) && isTattleTale) {
754 llOwnerSay(llKey2Name(toucher) + " clicked your pullup.");
755 }
756
757 llSensor("", NULL_KEY, AGENT_BY_LEGACY_NAME, 20.0, PI);
758
759 if(isOn)
760 {
761 // Special exception. If this is turned on ONLY CARERS can get menus.
762 if(accessMode == ACCESS_CARER_ONLY && role != ROLE_CARER)
763 return;
764
765 if(role == ROLE_OWNER && isLocked && accessMode != ACCESS_SELF) {
766 // Even locked users have a mini menu.
767 llDialog(toucher, "Locked menu for " + getNickname() + "'s pullup." + getMenuInfoText(), USER_LOCKED_MENU, channel);
768 }
769 else if(role == ROLE_OWNER && isLocked && accessMode == ACCESS_SELF) {
770 // If self locked with a private access pullup, a user can unlock themselves.
771 llDialog(toucher, "Locked menu for " + getNickname() + "'s pullup." + getMenuInfoText(), USER_SELF_LOCKED_MENU, channel);
772 }
773 else if(role == ROLE_OWNER && !isLocked && accessMode == ACCESS_SELF) {
774 llDialog(toucher, "Menu for " + getNickname() + "'s pullup." + getMenuInfoText(), USER_SELF_MENU, channel);
775 }
776 else if(role == ROLE_OWNER && !isLocked && (accessMode == ACCESS_CARER || accessMode == ACCESS_ALL)) {
777 // If the user's pullup is set to carer/public access, they cannot lock themselves.
778 llDialog(toucher, "Menu for " + getNickname() + "'s pullup." + getMenuInfoText(), USER_MENU, channel);
779 }
780 else if (role == ROLE_CARER && (accessMode == ACCESS_CARER || accessMode == ACCESS_ALL)) {
781 llDialog(toucher, "Menu for " + getNickname() + "'s pullup." + getMenuInfoText(), CARER_MENU, channel);
782 }
783 else if (role == ROLE_ANONYMOUS && accessMode == ACCESS_ALL) {
784 llDialog(toucher, "Menu for " + getNickname() + "'s pullup.", ANON_MENU, channel);
785 }
786 }
787 else {
788 if(role == ROLE_OWNER || role == ROLE_CARER) {
789 llDialog(toucher, "Turn on?", ["On", "Nevermind"], channel);
790 }
791 }
792 }
793
794 }
795
796 /*
797 * The changed inventory trigger is here so that when add-ons are drag n' dropped in, main will re-broadcast settings to them.
798 */
799 changed(integer change)
800 {
801 if(change & CHANGED_OWNER) // If the owner changes, we need to re-initialize.
802 llResetScript();
803 }
804
805 // Types of requests:
806 // GET = llHTTPRequest("http://23.253.106.210/caretakers", [HTTP_METHOD, "GET", HTTP_CUSTOM_HEADER, "Caretaker-Name", "aaaaaaa"], "");
807 // PUT = llHTTPRequest("http://23.253.106.210/settings", [HTTP_METHOD, "PUT", HTTP_CUSTOM_HEADER, "wetInterval", "45"], "");
808 // DELETE = llHTTPRequest("http://23.253.106.210/caretakers", [HTTP_METHOD, "DELETE", HTTP_CUSTOM_HEADER, "Caretaker-Name", "aaaaaaa"], "");
809 attach(key id)
810 {
811 if(id) // Attached
812 {
813 reApplyLock();
814
815 channel = generateChan(owner);
816 manageCarersChannel = channel + 1;
817 setNicknameChannel = channel + 3;
818
819 // Kill any previous channel handlers, if they happen to have clung to life.
820 llListenRemove(channelHandle);
821 llListenRemove(manageCarersChannelHandle);
822 llListenRemove(setNicknameChannelHandle);
823
824 channelHandle = llListen(channel, "", "", "");
825 manageCarersChannelHandle = llListen(manageCarersChannel, "", owner, "");
826 setNicknameChannelHandle = llListen(setNicknameChannel, "", "", "");
827
828 if(isSynced == FALSE) {
829 isSynced = TRUE;
830 // Add/Welcome User
831 userRequestId = llHTTPRequest("http://23.253.106.210/users", [HTTP_METHOD, "POST"], "");
832
833 llSleep(1.0);
834
835 // Request Settings
836 settingRequestId = llHTTPRequest("http://23.253.106.210/settings", [], "");
837
838 llSleep(1.0);
839
840 // Request Carers
841 caretakerRequestId = llHTTPRequest("http://23.253.106.210/caretakers", [], "");
842 }
843
844 llOwnerSay("It's crinkle time~");
845 }
846 else // Removed
847 {
848 // Prep sync to occur on next attach.
849 isSynced = FALSE;
850
851 // De-register listens. Even if they manage to somehow survive, they'll get squished on startup too.
852 llListenRemove(channelHandle);
853 llListenRemove(manageCarersChannelHandle);
854 llListenRemove(setNicknameChannelHandle);
855
856 llOwnerSay("Thanks for crinkling with my script! Later! ~Ryhn");
857 }
858 }
859
860 http_response(key request_id, integer status, list metadata, string body) {
861 if(status == 500) {
862 return;
863 }
864
865 if(request_id == caretakerRequestId) { // Server replied to Get/Post/Delete caretaker
866 if(status == 200) {
867 if(body != "") {
868 carers = llCSV2List(body);
869 llMessageLinked(LINK_THIS, LINK_ALL, "carers", llList2CSV(carers));
870 }
871 } else if(status == 201) {
872 llOwnerSay("Caretaker added.");
873 llMessageLinked(LINK_THIS, LINK_ALL, "carers", llList2CSV(carers));
874 } else if(status == 204) {
875 llOwnerSay("Caretaker removed.");
876 llMessageLinked(LINK_THIS, LINK_ALL, "carers", llList2CSV(carers));
877 }
878 }
879 else if(request_id == settingRequestId) {
880 if(status == 201) {
881 llOwnerSay("Settings uploaded.");
882 } else if(status == 202) {
883 // Nothin' to say.
884 } else if(status == 404) {
885 llOwnerSay("No settings found...");
886 } else if(status == 200) {
887 loadSettings(body);
888 }
889 }
890 else if(request_id == userRequestId) {
891 if(status == 201) {
892 llOwnerSay("DrizzleUser account created!");
893 } else if (status == 208) {
894 llOwnerSay("Welcome back, " + getNickname());
895 } else if (status == 404) {
896 llOwnerSay("No DrizzleUser account found...");
897 }
898 }
899 }
900
901 timer()
902 {
903 // Only mess with the lock timer is the pullup is locked, and the unlock duration is actually set.
904 if(isLocked && unlockTime > 0)
905 {
906 // Lock timer is stable.
907 lockTimer += TICK_RATE;
908 if(lockTimer >= (unlockTime * 60)) {
909 toggleLock(owner); // TODO: Add custom timer lock/unlock printout.
910 }
911
912 }
913 }
914
915
916 listen(integer chan, string name, key id, string msg)
917 {
918 integer role = getRole(id); // Used to gurantee the correct version of each action is executed.
919 if(isOn) {
920
921 if(chan == setNicknameChannel && (role == ROLE_OWNER || role == ROLE_CARER)) {
922 setNickname(msg);
923 }
924
925 if(chan == manageCarersChannel && role == ROLE_OWNER) // Special case, handle carer add/remove
926 {
927 if(msg != "OK") // If sensor finds no-one, this is in the list. Ignore it.
928 {
929 if(~llListFindList(carers, [msg])) {
930 // If carer already exists, remove carer.
931 integer index = llListFindList(carers, [msg]);
932 carers = llDeleteSubList(carers, index, index);
933
934 // Remove carer from DB.
935 caretakerRequestId = llHTTPRequest("http://23.253.106.210/caretakers", [HTTP_METHOD, "DELETE", HTTP_CUSTOM_HEADER, "Caretaker-Name", msg], "");
936 }
937 else {
938 // Not already a carer, add a new carer
939 if(llGetListLength(carers) < MAX_CARETAKERS) {
940 carers += [msg];
941
942 // Add carer to DB.
943 caretakerRequestId = llHTTPRequest("http://23.253.106.210/caretakers", [HTTP_METHOD, "POST", HTTP_CUSTOM_HEADER, "Caretaker-Name", msg], "");
944 } else {
945 llOwnerSay("Could not add caretaker. You're at the limit of " + (string) MAX_CARETAKERS + ", you need to remove one to add another.");
946 }
947 }
948 }
949
950 }
951
952 // Handle Timer Tweaks
953 if(isIntervalBeingSet) {
954 integer time = 0;
955
956 if(msg == "Forever") {
957 time = SECONDS_IN_YEAR; // One year
958 } else {
959 time = (integer) msg;
960 }
961
962 if(isLockIntervalBeingSet) {
963 unlockTime = time;
964 lockTimer = 0;
965
966 // After the timer is set, lock the pullup.
967 toggleLock(id);
968 }
969
970 // Clear for next time.
971 isLockIntervalBeingSet = FALSE;
972
973 // Propagate settings to server.
974 saveSettings();
975 }
976
977 // User only options...
978 if(role == ROLE_OWNER) {
979 if(msg == "Get❤Soggy")
980 {
981 llMessageLinked(LINK_THIS, LINK_POTTY, "Wet", id);
982 //wet(WET_TYPE, id);
983 }
984 else if(msg == "Get❤Stinky")
985 {
986 llMessageLinked(LINK_THIS, LINK_POTTY, "Mess", id);
987 //mess(MESS_TYPE, id);
988 }
989 else if(msg == "Settings")
990 {
991 llDialog(id, "Settings" + getMenuInfoText(), SETTING_MENU, channel);
992 }
993 else if(msg == "Textures")
994 {
995 // Dispatch texture request to DrizzleRender
996 llMessageLinked(LINK_THIS, LINK_RENDER, msg, id);
997 }
998 else if(msg == "Access") {
999 llDialog(id, "Set public access level.", ACCESS_MENU, channel);
1000 }
1001 else if(msg == "Self") {
1002 setAccess(ACCESS_SELF);
1003 }
1004 else if(msg == "Self+Carers") {
1005 setAccess(ACCESS_CARER);
1006 }
1007 else if(msg == "Carers❤Only") {
1008 setAccess(ACCESS_CARER_ONLY);
1009 }
1010 else if(msg == "All") {
1011 setAccess(ACCESS_ALL);
1012 }
1013 else if(msg == "Chatter") {
1014 llDialog(id, "Set how public printouts are!", CHATTER_MENU, channel);
1015 }
1016 else if(msg == "Private") {
1017 setChatter(CHATTER_PRIVATE);
1018 }
1019 else if(msg == "Open Chat") {
1020 setChatter(CHATTER_OPEN_CHAT);
1021 }
1022 else if(msg == "Unaware") {
1023 setChatter(CHATTER_UNAWARE);
1024 }
1025 else if(msg == "Gender")
1026 {
1027 llDialog(id, "Gender", GENDER_MENU, channel);
1028 }
1029 else if(msg == "Male")
1030 {
1031 setGender(GENDER_MALE);
1032 }
1033 else if(msg == "Female")
1034 {
1035 setGender(GENDER_FEMALE);
1036 }
1037 else if(msg == "Reload")
1038 {
1039 setGender(gender);
1040 }
1041 else if(msg == "Wet❤Timer")
1042 {
1043 llMessageLinked(LINK_THIS, LINK_POTTY, "Wet Interval", id);
1044 }
1045 else if(msg == "Mess❤Timer")
1046 {
1047 llMessageLinked(LINK_THIS, LINK_POTTY, "Mess Interval", id);
1048 }
1049 else if(msg == "Caretakers")
1050 {
1051 llDialog(id, "Manage Caretakers", MANAGE_CARETAKER_MENU, channel);
1052 }
1053 else if(msg == "Add")
1054 {
1055 llDialog(id, "Who would you like to add?", getNamesFromKeys(detectedKeys), manageCarersChannel);
1056 }
1057 else if(msg == "Remove")
1058 {
1059 llDialog(id, "Which caretaker would you like to remove?", carers, manageCarersChannel);
1060 }
1061 else if(msg == "Toggle Open") {
1062 openCaretaker = !openCaretaker;
1063
1064 llMessageLinked(LINK_THIS, LINK_ALL, "openCaretaker", (string) openCaretaker);
1065 if(openCaretaker) {
1066 llOwnerSay("Open caretaker access enabled.");
1067 }
1068 else {
1069 llOwnerSay("Open caretaker access disabled.");
1070 }
1071
1072 saveSettings();
1073 }
1074 else if(msg == "Show")
1075 {
1076 llOwnerSay("Carers: " + llList2CSV(carers));
1077 }
1078 else if(msg == "Tattle")
1079 {
1080 toggleTattle();
1081 }
1082 else if(msg == "Tricks")
1083 {
1084 if(isPlugged) {
1085 llDialog(id, "Baby need help going? Try these.", CARER_TRICKS_MENU_UNPLUG, channel);
1086 } else {
1087 llDialog(id, "Baby need help going? Try these.", CARER_TRICKS_MENU, channel);
1088 }
1089 }
1090 else if(msg == "Puddle❤Aid")
1091 {
1092 giveAid(WET_AID_TYPE, id);
1093 }
1094 else if(msg == "Mudpie❤Lax")
1095 {
1096 giveAid(MESS_AID_TYPE, id);
1097 }
1098 else if(msg == "Tushie❤Rush")
1099 {
1100 giveAid(ENEMA_AID_TYPE, id);
1101 }
1102 else if(msg == "Potty❤Plug")
1103 {
1104 togglePlug(id);
1105 }
1106 else if(msg == "❤Unplug❤")
1107 {
1108 togglePlug(id);
1109 }
1110 else if(msg == "Whitelist")
1111 {
1112 llMessageLinked(LINK_THIS, LINK_ADD_ON, "Whitelist", "");
1113 }
1114 else if(msg == "Safeword") {
1115 toggleLock(id);
1116 }
1117 }
1118
1119 // Carer only options...
1120 if(role == ROLE_CARER) {
1121 if(msg == "Tricks")
1122 {
1123 if(isPlugged) {
1124 llDialog(id, "Baby need help going? Try these.", CARER_TRICKS_MENU_UNPLUG, channel);
1125 } else {
1126 llDialog(id, "Baby need help going? Try these.", CARER_TRICKS_MENU, channel);
1127 }
1128 }
1129 else if(msg == "Puddle❤Aid")
1130 {
1131 giveAid(WET_AID_TYPE, id);
1132 }
1133 else if(msg == "Mudpie❤Lax")
1134 {
1135 giveAid(MESS_AID_TYPE, id);
1136 }
1137 else if(msg == "Tushie❤Rush")
1138 {
1139 giveAid(ENEMA_AID_TYPE, id);
1140 }
1141 else if(msg == "Potty❤Plug")
1142 {
1143 togglePlug(id);
1144 }
1145 else if(msg == "❤Unplug❤")
1146 {
1147 togglePlug(id);
1148 }
1149 }
1150
1151 // User + Carer only options...
1152 if(role == ROLE_OWNER || role == ROLE_CARER) {
1153 // Handle Standard Use
1154 if(msg == "Lock/Unlock") {
1155 if(!isLocked) { // Prompt for a timer if not locked.
1156 isIntervalBeingSet = TRUE;
1157 isLockIntervalBeingSet = TRUE;
1158 llDialog(id, "How long would you like to lock this pullup? [Minutes]", LOCK_TIMES, channel);
1159 } else {
1160 toggleLock(id); // Simply unlock if already locked.
1161 }
1162 }
1163 else if(msg == "Show/Hide")
1164 {
1165 toggleHide(); // Needs to keep in mind what Should and SHOULD NOT be visible
1166 }
1167 else if(msg == "On/Off" || msg == "On")
1168 {
1169 toggleOnOff();
1170 }
1171 else if(msg == "Training")
1172 {
1173 llMessageLinked(LINK_THIS, LINK_POTTY, "Training", id); // Pass off to DrizzlePotty
1174 }
1175 else if(msg == "Nickname") {
1176 llTextBox(id, "Give this baby a nickname!", setNicknameChannel);
1177 }
1178 }
1179
1180 // From here down, these are available to anyone. No security checks.
1181 if(msg == "Check")
1182 {
1183 check(role, id);
1184 }
1185 else if(msg == "Change")
1186 {
1187 if(role == ROLE_CARER) {
1188 carerChange(role, id);
1189 } else {
1190 change(role, id);
1191 }
1192 }
1193 else if(msg == "❤Squeeze❤")
1194 {
1195 accidentCheck(SQUEEZE_TYPE, id);
1196 }
1197 else if(msg == "❤Tickle❤")
1198 {
1199 accidentCheck(TICKLE_TYPE, id);
1200 }
1201 else if(msg == "Spank")
1202 {
1203 llMessageLinked(LINK_THIS, -4, (string) gender + ":" + (string) chatter + ":" + (string) wetness + ":" + (string) messiness + ":" + "Spank" + ":" + getNickname(), id);
1204 llMessageLinked(LINK_THIS, LINK_ALL, "Spanked", id);
1205 }
1206 else if(msg == "Raspberry")
1207 {
1208 llMessageLinked(LINK_THIS, -2, (string) gender + ":" + (string) chatter + ":" + (string) wetness + ":" + (string) messiness + ":" + "Raspberry" + ":" + getNickname(), id);
1209 llMessageLinked(LINK_THIS, LINK_ALL, "Raspberried", id);
1210 }
1211 else if(msg == "Wedgie")
1212 {
1213 llMessageLinked(LINK_THIS, -4, (string) gender + ":" + (string) chatter + ":" + (string) wetness + ":" + (string) messiness + ":" + "Wedgie" + ":" + getNickname(), id);
1214 llMessageLinked(LINK_THIS, LINK_ALL, "Wedgied", id);
1215 }
1216 }
1217 else
1218 { // Diaper is off, only listen for On/Off or "On" messages
1219 // User + Carer only options...
1220 if(role == ROLE_OWNER || role == ROLE_CARER) {
1221 if(msg == "On/Off" || msg == "On") {
1222 toggleOnOff();
1223 }
1224 }
1225 }
1226 }
1227
1228 //Searches the area, stashing detected avatar keys if not already caretakers/previously detected.
1229 sensor(integer num_detected)
1230 {
1231 detectedKeys = [];
1232
1233 integer i = 0;
1234
1235 while(i < num_detected && i < 4) // Show the four closest avatars.
1236 {
1237 key detected = llDetectedKey(i);
1238
1239 integer isCarer = isCarer(detected);
1240 if(~llListFindList(detectedKeys, [detected]) || isCarer)
1241 {
1242 // Avatar already detected/already a carer, ignore them.
1243 }
1244 else
1245 {
1246 // New Avatar, Add to list.
1247 detectedKeys += llDetectedKey(i);
1248 }
1249
1250 i++;
1251 }
1252 }
1253
1254 /*
1255 * API for main script.
1256 * cmd = Command to execute
1257 * val = Extra information, can be a user key or custom printout.
1258 */
1259 link_message(integer link, integer num, string cmd, key val) {
1260 if(num == LINK_MAIN) { // Message for main!
1261 if(cmd == "Toggle Lock") {
1262 toggleLock(val);
1263 } else if(cmd == "Lock") {
1264 lock(val);
1265 } else if(cmd == "Unlock") {
1266 unlock(val);
1267 } else if(cmd == "Suppress") {
1268 isSuppressed = TRUE; // Make pullup unresponsive to clicks.
1269 } else if(cmd == "Unsuppress") {
1270 isSuppressed = FALSE; // Make pullup unresponsive to clicks.
1271 } else if(cmd == "getNickname") { // Getters
1272 llMessageLinked(LINK_THIS, LINK_ALL, "nickname", getNickname());
1273 } else if(cmd == "getGender") {
1274 llMessageLinked(LINK_THIS, LINK_ALL, "gender", (string) gender);
1275 } else if(cmd == "getAccessMode") {
1276 llMessageLinked(LINK_THIS, LINK_ALL, "accessMode", (string) accessMode);
1277 } else if(cmd == "getChatter") {
1278 llMessageLinked(LINK_THIS, LINK_ALL, "chatter", (string) chatter);
1279 } else if(cmd == "getCarers") {
1280 llMessageLinked(LINK_THIS, LINK_ALL, "carers", llList2CSV(carers));
1281 } else if(cmd == "isOpenCaretaker") {
1282 llMessageLinked(LINK_THIS, LINK_ALL, "openCaretaker", (string) openCaretaker);
1283 } else if(cmd == "isTattleTale") {
1284 llMessageLinked(LINK_THIS, LINK_ALL, "isTattleTale", (string) isTattleTale);
1285 } else if(cmd == "setNickname") { // Setters
1286 setNickname((string) val);
1287 } else if(cmd == "isLocked") {
1288 llMessageLinked(LINK_THIS, LINK_ALL, "isLocked", (string) isLocked);
1289 } else if(cmd == "isSuppressed") {
1290 llMessageLinked(LINK_THIS, LINK_ALL, "isSuppressed", (string) isSuppressed);
1291 }
1292
1293 } else if(num == LINK_ALL) { // General messages. DrizzleMain listens for updates to ALL variables which are persisted to the database. It takes care of saving overall settings.
1294
1295 integer saveRequired = FALSE;
1296
1297 if(cmd == "Wet/Mess Levels") {
1298 list levels = llCSV2List((string)val);
1299 wetness = llList2Integer(levels, 0);
1300 messiness = llList2Integer(levels, 1);
1301
1302 saveRequired = TRUE;
1303 } else if(cmd == "Wet/Mess Intervals") {
1304 list intervals = llCSV2List((string)val);
1305 wetInterval = llList2Integer(intervals, 0);
1306 messInterval = llList2Integer(intervals, 1);
1307
1308 saveRequired = TRUE;
1309 } else if(cmd == "Wet/Mess Training") {
1310 list trainings = llCSV2List((string)val);
1311 wetTraining = llList2Integer(trainings, 0);
1312 messTraining = llList2Integer(trainings, 1);
1313
1314 saveRequired = TRUE;
1315 } else if(cmd == "Wet/Mess Hold Attempts") {
1316 list attempts = llCSV2List((string)val);
1317 wetHoldAttempts = llList2Integer(attempts, 0);
1318 messHoldAttempts = llList2Integer(attempts, 1);
1319
1320 saveRequired = TRUE;
1321 } else if(cmd == "training") {
1322 training = (integer) ((string)val);
1323 saveRequired = TRUE;
1324 } else if(cmd == "puddles") {
1325 puddles = (integer) ((string)val);
1326 saveRequired = TRUE;
1327 } else if(cmd == "mudpies") {
1328 mudpies = (integer) ((string)val);
1329 saveRequired = TRUE;
1330 } else if(cmd == "isHidden") {
1331 isHidden = (integer) ((string)val);
1332 saveRequired = TRUE;
1333 } else if (cmd == "isPlugged") {
1334 isPlugged = (integer) ((string)val);
1335 saveRequired = TRUE;
1336 } else if(cmd == "wetTraining") {
1337 wetTraining = (integer) ((string)val);
1338 saveRequired = TRUE;
1339 } else if(cmd == "messTraining") {
1340 messTraining = (integer) ((string)val);
1341 saveRequired = TRUE;
1342 }
1343
1344 if(saveRequired) {
1345 // Persist change in pullup state to the database.
1346 saveSettings();
1347 }
1348 }
1349 }
1350}