diff options
Diffstat (limited to 'scripts/toy.groovy')
-rw-r--r-- | scripts/toy.groovy | 694 |
1 files changed, 694 insertions, 0 deletions
diff --git a/scripts/toy.groovy b/scripts/toy.groovy new file mode 100644 index 0000000..fcb1b2e --- /dev/null +++ b/scripts/toy.groovy @@ -0,0 +1,694 @@ +// Toy main + +setInfos(9, "Toy", "Your frustration is my amusement.", "rascalDan", "WIP", 0xFFFFFF, "en", ["", "", ""]); +final VERSION = 1; +final DAY = 86400; +final HOUR = 3600.0; +final soonFormatter = java.time.format.DateTimeFormatter.ofPattern("EEEE 'at' h:mma"); +// Notes +// toys.<name> ankle_cuffs, ballgag, blindfold, chastity_belt, clothespins, cockring, dog_collar, handcuffs, hood, nipple_clamps, ring_gag +final BALLGAG = "ballgag", COLLAR = "dog_collar", CLAMPS = "nipple_clamps", CHASTITY = "chastity_belt"; +// fetish.<name> +final BONDAGE = "bondage", CBT = "cbt", CHORES = "chores", PAIN = "pain"; +// toy.permission.<name> +final CUM = "cum", EDGE = "edge" +// toy.state.<name> +final CHASTE = "chaste", COLLARED = "collared", CUFFED = "cuffed", CLAMPED = "clamped", GAGGED = "gagged", NAKED = "naked"; +// toy.plan.<name>.{start|end|friend} UTC times +final LUNCH = "lunch", SHOPPING = "shopping", PARTY = "party"; +final START = "start", END = "end", FRIEND = "friend"; +final FRIENDS = [ "Tori", "Sophie", "Krystal" ]; +// toy.position +final KNEELING = "kneeling", ALLFOURS = "allfours", STANDING = "standing"; +final OWNER = "ancilla"; +final SITTER = "sarah-james"; + +// Imagery +final DRESSED = "dressed", TITS = "tits", PUSSY = "pussy", LINGERIE = "lingerie", TEASE = "tease", SIT = "sit", BOOTS = "boots", KNEEL = "kneel", STOOD = "stood", MEAN = "mean", CROP = "crop", ASS = "ass", SQUAT = "squat", SSSH = "sssh"; +final nDRESSED = "!$DRESSED", nTITS = "!$TITS", nTEASE = "!$TEASE"; +def IMAGEDATA = { [ + [ domme: "ancilla", sets: [ + [ set: "corset", images: [ + [ image: 1, tags: [DRESSED, STOOD] ], + [ image: 2, tags: [DRESSED, STOOD] ], + [ image: 3, tags: [DRESSED, STOOD] ], + [ image: 4, tags: [DRESSED, STOOD] ], + [ image: 5, tags: [DRESSED, STOOD] ], + [ image: 6, tags: [DRESSED, STOOD] ], + [ image: 7, tags: [DRESSED, STOOD, TEASE] ], + [ image: 8, tags: [DRESSED, STOOD, ASS] ], + [ image: 9, tags: [DRESSED, STOOD, TEASE] ], + [ image: 10, tags: [DRESSED, STOOD, TEASE] ], + [ image: 11, tags: [KNEEL, TITS] ], + [ image: 12, tags: [KNEEL, TITS, ASS] ], + [ image: 13, tags: [KNEEL, TITS, LINGERIE] ], + [ image: 14, tags: [KNEEL, TITS, LINGERIE] ], + [ image: 15, tags: [SIT, TITS, LINGERIE] ], + [ image: 16, tags: [STOOD, TITS, LINGERIE] ], + ] ], + [ set: "crop", images: [ + [ image: 1, tags: [TITS, KNEEL, SSSH] ], + [ image: 2, tags: [TITS, KNEEL, MEAN, CROP] ], + [ image: 3, tags: [TITS, KNEEL, CROP] ], + [ image: 4, tags: [TEASE, KNEEL] ], + [ image: 5, tags: [STOOD, TITS] ], + [ image: 6, tags: [KNEEL, TITS] ], + [ image: 7, tags: [KNEEL, TITS] ], + [ image: 8, tags: [DRESSED, STOOD, CROP] ], + [ image: 9, tags: [DRESSED, STOOD, CROP] ], + [ image: 10, tags: [DRESSED, STOOD, CROP] ], + [ image: 11, tags: [DRESSED, STOOD, CROP] ], + [ image: 12, tags: [DRESSED, STOOD, CROP] ], + [ image: 13, tags: [DRESSED, STOOD, CROP] ], + [ image: 14, tags: [DRESSED, SIT, CROP] ], + [ image: 15, tags: [TITS, SIT] ], + [ image: 16, tags: [TITS, STOOD] ], + ] ], + [ set: "dress", images: [ + [ image: 1, tags: [TITS, STOOD] ], + [ image: 2, tags: [TITS, STOOD] ], + [ image: 3, tags: [TITS, SIT] ], + [ image: 4, tags: [TITS, SIT] ], + [ image: 5, tags: [TITS, STOOD] ], + [ image: 6, tags: [TITS, STOOD, ASS] ], + [ image: 7, tags: [DRESSED, STOOD] ], + [ image: 8, tags: [DRESSED, STOOD] ], + [ image: 9, tags: [DRESSED, STOOD, ASS] ], + [ image: 10, tags: [DRESSED, STOOD, ASS] ], + [ image: 11, tags: [DRESSED, SQUAT] ], + [ image: 12, tags: [DRESSED, STOOD, TEASE] ], + [ image: 13, tags: [DRESSED, STOOD, TEASE] ], + [ image: 14, tags: [TITS, STOOD] ], + [ image: 15, tags: [TITS, STOOD] ], + ] ], + [ set: "pink", images: [ + [ image: 1, tags: [TITS, KNEEL, LINGERIE] ], + [ image: 2, tags: [TITS, KNEEL, LINGERIE] ], + [ image: 3, tags: [TITS, KNEEL, LINGERIE] ], + [ image: 4, tags: [TITS, KNEEL, LINGERIE] ], + [ image: 5, tags: [TITS, KNEEL, LINGERIE] ], + [ image: 6, tags: [TITS, KNEEL] ], + [ image: 7, tags: [DRESSED, KNEEL] ], + [ image: 8, tags: [DRESSED, STOOD] ], + [ image: 9, tags: [DRESSED, STOOD] ], + [ image: 10, tags: [DRESSED, STOOD, TEASE] ], + [ image: 11, tags: [DRESSED, STOOD, TEASE] ], + [ image: 12, tags: [KNEEL, LINGERIE] ], + [ image: 13, tags: [TITS, KNEEL, LINGERIE] ], + [ image: 14, tags: [TITS, KNEEL, LINGERIE] ], + [ image: 15, tags: [TITS, KNEEL, LINGERIE] ], + ] ], + ] + ] +] }; +def selectImage = { domme, set, spec -> + def meets = { i -> + return spec.every({ s -> + return (s[0] == "!") ? i.tags.indexOf(s.drop(1)) == -1 : i.tags.indexOf(s) != -1; + }); + }; + def matches = IMAGEDATA() + .find({ d -> d.domme == domme }) + .sets.find({ s -> s.set == set }) + .images.findAll({ i -> meets(i) }); + return matches[getRandom(matches.size())]; +}; +def showImage = { spec -> + def outfit = loadString("toy.owner.outfit"); + def image = selectImage(OWNER, outfit, spec); + setImage("toy/$OWNER/$outfit/${image.image}.jpg"); + return image.tags; +}; +def selectImageSet = { domme, specs -> + def matches = IMAGEDATA() + .find({ d -> d.domme == domme }) + .sets.findAll({ s -> specs.every({ spec -> selectImage(domme, s.set, spec)})}); + return matches[getRandom(matches.size())]; +}; +def dress = { specs -> + def outfit = loadString("toy.owner.outfit"); + def outfitTime = loadInteger("toy.owner.outfitTime"); + if (outfitTime > getTime() - 7200) { // Recent, check + def prev = IMAGEDATA() + .find({ d -> d.domme == OWNER }) + .sets.find({ s -> s.set == outfit }); + if (specs.every({ spec -> selectImage(OWNER, prev.set, spec)})) { + return outfit; + } + } + outfit = selectImageSet(OWNER, specs); + if (!outfit) { + showPopup("No outfit for $OWNER : $specs"); + } + save("toy.owner.outfit", outfit.set); + save("toy.owner.outfitTime", getTime()); +}; +// Utils +def localTimeOffset = { + return java.time.ZoneId.systemDefault() + .getRules() + .getOffset(java.time.Instant.now()) + .getTotalSeconds(); +}; +def localTimeOf = { s -> + java.time.LocalDateTime.ofInstant( + java.time.Instant.ofEpochSecond(s), java.time.ZoneId.systemDefault()) +} +def localTime = { + // A float between 4.0 (4am) and 28.0 (4am) + def wallclock = ((getTime() + localTimeOffset()) % DAY) / HOUR; + if (wallclock < 4) { + wallclock += 24; + } + return wallclock; +}; +def getDay = { (int)(getTime() / DAY) * DAY }; +def has = {i -> loadBoolean("toys.$i") == true}; +def likes = {i -> loadBoolean("fetish.$i") == true}; +def can = {i -> loadBoolean("toy.permission.$i") == true}; +def is = {i -> loadBoolean("toy.state.$i") == true}; +def set = {i, s -> save("toy.state.$i", s)}; +def positioned = { i -> loadString("toy.position") == i }; +def gagText = { t -> + if (!is(GAGGED)) return t; + return t.split(/\s+/) + .collect { + 'm' * getRandom((int)Math.max(1.0, it.length() * 1.0)) + + 'p' * getRandom((int)Math.max(1.0, it.length() * 0.4)) + + 'h' * getRandom((int)Math.max(1.0, it.length() * 0.8)) + } + .join(" ") + .capitalize(); +}; +def showButtonG = { s, t = null -> + return t != null ? showButton(gagText(s), t) : showButton(gagText(s)); +}; +def present = { imageSpec, texts -> + if (texts) { + show(texts.collect { + t -> t[getRandom(t.size())] + }.join(" ").capitalize()); + } + if (imageSpec) { + return showImage(imageSpec); + } + return null; +}; + +def harden = { imageSpec -> + present([DRESSED], [ + ["Take that cock...", "Hand on penis..."], + ["stroke it, slowly...", "slow strokes..."], + ["don't edge...", "no edging..."], + ["and do NOT cum.", "and definitely no cumming."]]); + if (showButtonG("Hard, mistress", 60) == 60) { + present(null, [ + ["What's taking so long!?", "Come on!", "Don't disappoint me."], + ["Slap it about a bit!.", "Get it hard, now!", "Pinch your nipples."]]); + showButtonG("Hard, mistress"); + } +}; +def expose = { imageSpec -> + if (!is(NAKED)) { + present(imageSpec, [ + ["Get that cock out, toy;", "Let's see that cock of mine."], + ["I want to torment it.", "It's playtime!"]]); + wait(10); + } +}; +def edge = { amount, imageSpec -> + (getRandom(amount) + 2).times { + present(imageSpec, [ + ["Stroke to edge now, toy...", "Edge!"], + ["Don't cum.", "No cumming.", "No accidents though."]]); + showButtonG("Edging, mistress"); // TODO: time check + if (getRandom(2) == 1) { + present(imageSpec, [ + ["Hold it...", "And hold...", "Keeping going..."]]); + wait(getRandom(20) + 5); + } + present(imageSpec, [ + ["Hands off!", "Stop!"]]); + wait(getRandom(5) + 5); + }; +}; +def strokes = { amount, imageSpec -> + present(imageSpec, [ + ["Stroke to the beat, toy.", "Follow the beat.", "Beat it!"], + ["No cumming unless I say so...", "Don't cum without my say so,"], + ["Tell me if you get too close.", "so tell me if you're edging.", "I don't want any messes."]]); + final BEATS = [ + // bpm:30, len:60 ], + [ bpm:90, len:45 ], + [ bpm:165, len:30 ], + [ bpm:180, len:30 ], + [ bpm:240, len:20 ], + ]; + def sto = loadInteger("toy.strokeTeaseOffset"); + def edges = 0; + (getRandom(amount) + 5).times { + def beatNo = getRandom(BEATS.size()); + def beat = BEATS[beatNo]; + playBackgroundSound("toy/${beat.bpm}bpm.mp3", 10); // Lots, to cover offset + def len = getRandom(java.lang.Math.max(1, beat.len + (sto * beatNo))); + if (showButtonG("Edging, mistress", len) < len) { + sto -= 1; + edges += 1; + playBackgroundSound(null); + switch (getRandom(3)) { + case 0: + present(imageSpec, [ + ["Stop!", "Let go!", "Hands off!"]]); + wait(getRandom(8) + 10); + break; + case 1: + present(imageSpec, [ + ["Careful now...", "No accidents..."], + ["slow it down...", "slowly now..."]]); + playBackgroundSound("toy/30bpm.mp3"); + wait(getRandom(20) + 5); + playBackgroundSound(null); + case 2: + switch(getSelectedValue("Would you like to cum, toy?", [ + gagText("Please, mistress, may I cum!?"), + gagText("No, mistress, please torment me more!") + ])) { + case 0: + present(imageSpec, [ + ["No you may not!", "Nope.", "Not a chance."]]); + break; + case 1: + present(imageSpec, [ + ["Good boy.", "Very well.", "OK then!"]]); + break; + } + wait(getRandom(8) + 5); + } + present(imageSpec, [ + ["Back to stroking, toy...", "Get to it again...."], + ["follow the beat..."], + ["no accidents.", "and concentrate."]]); + } + else { + playBackgroundSound(null); + } + } + if (edges == 0) { + sto += 1; + } + save("toy.strokeTeaseOffset", sto); +} + +// Pre-tease +def preRelease = { + if (!is(CHASTE)) return; + present([DRESSED], [ + ["I'm feeling generous, toy...", "Well, that's not good to me..."], + ["let's get that chastity device off.", "take your chastity device off."]]); + showButtonG("Yes, mistress"); + wait(15); + set(CHASTE, false); + showButtonG("Thank you, mistress"); +}; +def preEdge = { + if (is(CHASTE)) return; + expose([DRESSED]); + harden([DRESSED]); + edge(4, [DRESSED]); + present([DRESSED], [ + ["And relax...", "Hands off..."], + ["cool down a little.", "but keep it hard for me!", "for now!"]]); + wait(getRandom(5) + 5); +}; +def preGag = { + if (is(GAGGED)) return; + if (!has(BALLGAG)) return; + present([DRESSED], [ + ["Go fetch your gag, toy...", "Bring me your gag, toy..."], + ["Quickly!", "Hurry back!"]]); + showButton("Yes, mistress"); + show(null); + wait(10); + showButton("Back, mistress"); // TODO: time check + present([DRESSED], [ + ["Good boy.", "Very good."], + ["Put it on..."], + ["Nice and tight.", "Tight!"], + ["I don't want to a hear a word from you.", "A quiet toy is a good toy."]]); + set(GAGGED, true); + showButtonG("Gagged, mistress"); // TODO: time check +}; +def preClamps = { + if (is(CLAMPED)) return; + present([DRESSED], + likes(PAIN) ? [ + ["We both like to see you suffer.", "Pain is fun."], + ["Isn't that right, toy?", "Don't you agree?"]] + : [ + ["I'm sorry, toy,"], + ["but seeing you suffer is too much fun.", "but I need to find my amusement somewhere."]]); + wait(10); + present([DRESSED],[ + ["Go put your nipple clamps on...", "I want those nipples clamped..."], + ["but on your way back...", "no walking though..."], + ["crawl, down on all fours.", "on your knees."]]); + showButtonG("Yes, mistress"); + show(null); + wait(10); + set(CLAMPED, true); + showButtonG("Back, mistress"); // TODO: time check + present([DRESSED], [ + ["On your knees,", "Kneel before me"], + ["let me see.", "hands behind your back."]]); + wait(getRandom(10) + 5); + getRandom(4).times { + present([DRESSED], [ + ["Pull them tight.", "Pull them!"]]); + wait(getRandom(10) + 5); + if (getRandom(5) == 0) { + present(null, [ + ["Tighter!", "Harder!"]]); + wait(getRandom(10) + 2); + present(null, [ + ["Haha!", "Ooo, they must hurt.", "Do they hurt?"]]); + wait(3); + } + present(null, [ + ["And release.", "Let go."]]); + wait(getRandom(5) + 3); + }; +}; +def preCollar = { + if (is(COLLARED)) return; + if (!has(COLLAR)) return; + present([DRESSED], [ + ["You look underdressed, toy.", "A toy without a collar doesn't look right."], + ["Go put your collar on", "I want a collar around your neck"], + ["and crawl back like the slutty puppy dog you are.", "and kneel before me."]]); + showButtonG("Yes, mistress"); + show(null); + wait(10); + set(COLLARED, true); + showButtonG("Back, mistress"); // TODO: time check +}; +def preStrip = { imageSpec = [DRESSED] -> + if (is(NAKED)) return; + present(imageSpec, [ + ["Clothes off!", "Get naked, slut!"]]); + wait(10); + present([DRESSED], [ + ["All of them off!", "And the rest!", "Keep going."]]); + set(NAKED, true); + showButtonG("Naked, mistress"); + (getRandom(2) + 1).times { + switch (getRandom(2)) { + case 0: + present(null, [ + ["Stand up straight,", "On your feet,"], + ["hands behind your head,", "arms up,", "hands high up,"], + ["let me see you properly.", "let's see what we have.", "I want a good view."]]); + break; + case 1: + present(null, [ + ["Turn around,", "Spin"], + ["slowly", "nice and slow."]]); + break; + case 2: + present(null, [ + ["Very good. Now...", "I see..."], + ["on your knees before me,", "kneel,"], + ["don't move those hands.", "hands still."]]); + break; + } + wait(10); + }; +}; + +// Post +def postEdge = { + if (is(CHASTE)) return; + if (!can(EDGE)) return; + edge(6, [TEASE]); +}; +def postCum = { + if (is(CHASTE)) return; + present([TEASE], [ + ["How about I let you cum?", "Maybe I should let you cum."], + ["Would you like that?", "Let some of that frustration out?"]]); + wait(getRandom(5) + 5); + if (!can(CUM)) { + present([TEASE], [ + ["Or not.", "No, not right now.", "Maybe later.", "Not yet."]]); + wait(getRandom(5) + 5); + return; + } +}; +def postChastity = { + if (is(CHASTE)) return; + if (!has(CHASTITY)) return; + present([TEASE], [ + ["I can't keep an eye on you all time.", "It's not that I don't trust you."], + ["But...", "I just like to be sure."]]); + wait(getRandom(5) + 10); + present([TEASE], [ + ["Go to your room,", "Run along now,"], + ["get your chastity device and"], + ["lock it on.", "get that cock secured away."], + ["Bring back the key.", "I want the key of course."]]); + wait(10); + show(null); + showButtonG("Locked, mistress. Here's the key."); + set(CHASTE, true); + present([DRESSED,TEASE], [ + ["Good boy.", "Thank you, toy."]]); +}; + +// Play +def playStrokes = { + preRelease(); + harden([TITS]); + strokes(15, [TITS]); +}; +def playEdges = { + preRelease(); + harden([TITS]); + edge(6, [TITS]); +}; +def playNothing = { + (getRandom(5) + 4).times { + def tags = showImage([TITS]).intersect( + [ASS, CROP, BOOTS, PUSSY, SSSH]); + Collections.shuffle(tags); + if (tags.isEmpty()) { + show(null); + } + else { + switch (tags[0]) { + case ASS: + if (!is(GAGGED)) + present(null, [ + ["Come closer,", "Don't be shy,"], + ["kiss it!", "two kisses on each cheek."]]); + break; + case CROP: + present(null, [ + ["You know what this is for?", "Good boys get rewards, bad ones getting a beating.", "Would you like your ass a darker shade?"]]); + break; + case BOOTS: + if (is(GAGGED)) + present(null, [ + ["I know you'd like to worship them.", "Too bad that tongue can't be put to good use."]]); + else + break; + case PUSSY: + if (is(GAGGED)) + present(null, [ + ["I know you'd like to pleasure me.", "Too bad that tongue can't be put to good use."]]); + else + present(null, [ + ["How about you start licking it?", "How much would like your tongue in there?"]]); + break; + case SSSH: + present(null, [ + ["Quiet now.", "Sssssh.", "Patience."]]); + break; + default: + show(null); + } + } + wait(getRandom(10) + 10); + }; +}; + +// Session +def sessionSummon = { imageSpec -> + final limit = 5; + final timeMax = 10; + + for (def n = 1; n <= limit; n++) { + switch (n) { + case limit: + present(imageSpec, [ + ["TOY!", "SLAVE!", "SLUT!"], + ["Last chance!", "Final warning!"]]); + break; + case limit - 1: + case limit - 2: + present(imageSpec, [ + ["Toy!", "Slave!", "Slut!"], + ["Where are you!?", "Get yourself here, now!"]]); + break; + default: + present(imageSpec, [ + ["Toy!", "Slave!", "Slut!"]]); + break; + } + playBackgroundSound(n == limit ? "shortwhip.wav" : "bell.wav"); + if (showButton("Yes, Miss?", timeMax) < timeMax) { + return true; + }; + } + return false; +}; +def sessionPlay = { + def play = { amount, activities -> + (getRandom(amount) + 1).times { + activities[getRandom(activities.size())](); + }; + }; + play(3, [preRelease, preEdge, preGag, preClamps, preCollar, preStrip]); + play(3, [playStrokes, playEdges, playNothing]); + play(2, [postEdge, postCum, postChastity]); +}; +def sessionRelease = { + present([DRESSED], [ + ["Ahhh,", "OK,"], + ["I'm done with you for now.", "That was fun... for me at least."]]); + wait(getRandom(10) + 5); + if (is(CUFFED) || is(COLLARED) || is(CLAMPED) || is(GAGGED)) { + present([DRESSED], [ + ["You may release yourself", "Free yourself"]]); + wait(getRandom(5) + 5); + set(CUFFED, false); + set(COLLARED, false); + set(CLAMPED, false); + set(GAGGED, false); + } + if (is(NAKED)) { + present([DRESSED], [ + ["Put some clothes back on.", "Cover yourself up!"]]); + wait(getRandom(10) + 15); + set(NAKED, false); + } + present([DRESSED], [ + ["Off you go.", "You may leave."]]); + wait(10); + setImage(null); + show(null); +}; +def playtime = { + dress([[DRESSED,nTEASE],[DRESSED,TEASE],[TITS]]); + if (sessionSummon([DRESSED,nTEASE])) { + sessionPlay(); + sessionRelease(); + } +} +def bedtime = { + dress([[LINGERIE,nTITS]]); + if (sessionSummon([LINGERIE,nTITS])) { + preStrip([LINGERIE,nTITS]); + present([LINGERIE,nTITS], [ + ["Very good, toy.", "OK"], + ["Bedtime!", "Off to bed with you!", "Bed! Now!"]]); + showButtonG("Good night, mistress"); + } + exit(); +}; + +// Setup +// Plans - cannot overlap +def setupPlan = { plan, float startmin, float startmax, float endmin, float endmax -> + def timeWindow = { float min, float max -> (int)((min * HOUR) + getRandom((int)((max - min) * HOUR))) }; + // Unplanned or past + if (getTime() > loadInteger("toy.plan.${plan}.${END}")) { + def day = getDay() + ((getRandom(7) + 1) * DAY); + save("toy.plan.${plan}.${FRIEND}", FRIENDS[getRandom(FRIENDS.size())]); + save("toy.plan.${plan}.${START}", day + timeWindow(startmin, startmax)); + save("toy.plan.${plan}.${END}", day + timeWindow(endmin, endmax)); + } +} +def setupPlans = { + setupPlan(LUNCH, 11.5, 12.5, 13.0, 14.0) + setupPlan(SHOPPING, 14.5, 15.5, 16.0, 18.0) + setupPlan(PARTY, 19, 20, 22.0, 25.0) +}; +def setDefault = { prop, val -> + if (loadString(prop) == null) { + save(prop, val); + } +}; +// Initial - basic checks and defaults +def setupInitial = { + if (loadInteger("toy.version") == null) { + // Welcome + } + save("toy.version", VERSION); + setDefault("toy.strokeTeaseOffset", 0); + setupPlans(); +}; +def setupShowState = { + show( + [CHASTE, COLLARED, CUFFED, CLAMPED, GAGGED, NAKED].collect { + def v = is(it); + return "$it: $v\n".capitalize() + }.join()); + showButton("OK"); +}; +def setupShowCalendar = { + show( + [LUNCH, SHOPPING, PARTY].collect { + def s = loadInteger("toy.plan.${it}.${START}"); + def f = loadString("toy.plan.${it}.${FRIEND}"); + def timeStr = localTimeOf(s).format(soonFormatter); + return "$it with $f on $timeStr.\n".capitalize(); + }.join()); + showButton("OK"); +}; + +// GO! +setupInitial(); +def getDayNum = { Math.floorDiv(getTime() + localTimeOffset() - 14400, 86400) }; +def getAvail = { dayNum -> loadInteger("toy.availability.$dayNum") ?: 0 }; +def addAvail = { amount -> + def dayNum = getDayNum(); + save("toy.availability.$dayNum", getAvail(dayNum) + amount); +}; +if (localTime() > 25.75 || localTime() < 7.5) { // 1:45am - 7:30am + show("Mistress is sleeping..."); + wait(10); + exit(); +} +final cycleTime = 6; +while (true) { + if (showButton("Please, Miss?", cycleTime) < cycleTime) { + switch (getSelectedValue("Yes, toy?", [ + "Status", + "Calendar", + "Play", + "Back" + ])) { + case 0: setupShowState(); break; + case 1: setupShowCalendar(); break; + case 2: playtime(); break; + case 3: return "toy"; + } + } + else if (localTime() > 25.75) { // 1:45am + bedtime(); + } + else if (getRandom(1000) == 0) { + playtime(); + } + addAvail(cycleTime); +} + |