From f1abde00dadf832e302827479dc386365e315f08 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Fri, 5 Apr 2019 21:28:04 +0100 Subject: Wrap everything in an anonymous object --- scripts/toy.groovy | 3675 ++++++++++++++++++++++++++-------------------------- 1 file changed, 1840 insertions(+), 1835 deletions(-) diff --git a/scripts/toy.groovy b/scripts/toy.groovy index 93cd179..246dd63 100644 --- a/scripts/toy.groovy +++ b/scripts/toy.groovy @@ -9,1970 +9,1975 @@ // CC by-NC, see http://creativecommons.org/licenses/by-nc/3.0/ // setInfos(9, "Toy", "Become my new plaything.", "rascalDan", "WIP", 0xFFFFFF, "en", ["bondage", "femaledom", "formale", "long", "pain", "toys", "joi"]); -final VERSION = 1; -final DAY = 86400; -final HOUR = 3600.0; -final soonFormatter = java.time.format.DateTimeFormatter.ofPattern("EEEE 'at' h:mma"); -// Notes -// toys. 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", HANDCUFFS = "handcuffs", DILDO = "dildo"; -final SPOON = "spoon"; -// fetish. -final BONDAGE = "bondage", CBT = "cbt", CHORES = "chores", PAIN = "pain"; -// toy.permission. -final CUM = "cum", EDGE = "edge" -final PERMIT = "permit"; -final PLAY = "play"; -final PERM_CHASTE = "perm_chaste", PERM_CHASTE_ASK = "perm_chaste_ask"; -// toy.state. -final CHASTE = "chaste", COLLARED = "collared", CUFFED = "cuffed", CLAMPED = "clamped", GAGGED = "gagged", NAKED = "naked"; -final REDRESS = "redress"; -final TOYTOYS = [CUFFED, COLLARED, CLAMPED, GAGGED]; -final RELEASEFROM = "releaseFrom"; -final LUNCH = "lunch", SHOPPING = "shopping", PARTY = "party", SLEEPING = "sleeping"; -final BEDTIME = "bedtime", WAKEUP = "wakeup", BEDTIMECHECK = "bedtimeCheck"; -final BEGINPLAN = "beginPlan", ENDPLAN = "endPlan"; -final FRIENDS = [ "Tori", "Sophie", "Krystal" ]; -// toy.position -final KNEELING = "kneeling", ALLFOURS = "allfours", STANDING = "standing"; -def OWNER = null; -def DOMME = null; -final SITTER = "sarah-james"; -final SOFT = "soft"; -final DATAFOLDER = getDataFolder(); -// 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"; -final IMAGEDATA = { - return new File("$DATAFOLDER/images/toy") - .listFiles() - .findAll { d -> d.isDirectory(); } - .collectEntries { d -> [ - d.getName(), [ - domme: d.getName(), - sets: d.listFiles() - .findAll { s -> s.isDirectory(); } - .collect { s -> [ sub: s, f: new File(s.getPath() + "/tags")] } - .findAll { s -> s.f.canRead() } - .collectEntries { s -> [ - s.sub.getName(), [ - set: s.sub.getName(), - images: s.f - .readLines() - .collect { l -> - def fields = l.split(":"); - return [ - image: fields[0], - tags: fields[1] - .split(",") - .findAll { t -> !t.isEmpty() } - ]; - } - ] - ]} +return new Object() { + final VERSION = 1; + final DAY = 86400; + final HOUR = 3600.0; + final soonFormatter = java.time.format.DateTimeFormatter.ofPattern("EEEE 'at' h:mma"); + // Notes + // toys. 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", HANDCUFFS = "handcuffs", DILDO = "dildo"; + final SPOON = "spoon"; + // fetish. + final BONDAGE = "bondage", CBT = "cbt", CHORES = "chores", PAIN = "pain"; + // toy.permission. + final CUM = "cum", EDGE = "edge" + final PERMIT = "permit"; + final PLAY = "play"; + final PERM_CHASTE = "perm_chaste", PERM_CHASTE_ASK = "perm_chaste_ask"; + // toy.state. + final CHASTE = "chaste", COLLARED = "collared", CUFFED = "cuffed", CLAMPED = "clamped", GAGGED = "gagged", NAKED = "naked"; + final REDRESS = "redress"; + final TOYTOYS = [CUFFED, COLLARED, CLAMPED, GAGGED]; + final RELEASEFROM = "releaseFrom"; + final LUNCH = "lunch", SHOPPING = "shopping", PARTY = "party", SLEEPING = "sleeping"; + final BEDTIME = "bedtime", WAKEUP = "wakeup", BEDTIMECHECK = "bedtimeCheck"; + final BEGINPLAN = "beginPlan", ENDPLAN = "endPlan"; + final FRIENDS = [ "Tori", "Sophie", "Krystal" ]; + // toy.position + final KNEELING = "kneeling", ALLFOURS = "allfours", STANDING = "standing"; + def OWNER = null; + def DOMME = null; + final SITTER = "sarah-james"; + final SOFT = "soft"; + + final DATAFOLDER = getDataFolder(); + // 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"; + final IMAGEDATA = { + return new File("$DATAFOLDER/images/toy") + .listFiles() + .findAll { d -> d.isDirectory(); } + .collectEntries { d -> [ + d.getName(), [ + domme: d.getName(), + sets: d.listFiles() + .findAll { s -> s.isDirectory(); } + .collect { s -> [ sub: s, f: new File(s.getPath() + "/tags")] } + .findAll { s -> s.f.canRead() } + .collectEntries { s -> [ + s.sub.getName(), [ + set: s.sub.getName(), + images: s.f + .readLines() + .collect { l -> + def fields = l.split(":"); + return [ + image: fields[0], + tags: fields[1] + .split(",") + .findAll { t -> !t.isEmpty() } + ]; + } + ] + ]} + ] ] - ] + }; + }(); + def loadDomme = { domme, set = null -> + final readDomme = { path -> + if (!path) return [:]; + final f = new File(path); + if (!f.exists()) return [:]; + return Eval.me(f.text); }; -}(); -def loadDomme = { domme, set = null -> - final readDomme = { path -> - if (!path) return [:]; - final f = new File(path); - if (!f.exists()) return [:]; - return Eval.me(f.text); - }; - return [ - "$DATAFOLDER/images/toy/domme.groovy", - "$DATAFOLDER/images/toy/$domme/person.groovy", - "$DATAFOLDER/images/toy/$domme/$set/set.groovy" ] - .collect { readDomme(it) } - .sum() -}; -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; - }); + return [ + "$DATAFOLDER/images/toy/domme.groovy", + "$DATAFOLDER/images/toy/$domme/person.groovy", + "$DATAFOLDER/images/toy/$domme/$set/set.groovy" ] + .collect { readDomme(it) } + .sum() }; - def matches = IMAGEDATA[domme]; - if (!matches) return null; - matches = matches.sets[set]; - if (!matches) return null; - matches = matches - .images.findAll({ i -> meets(i) }); - if (matches.isEmpty()) return null; - return matches[getRandom(matches.size())]; -}; -def showImage = { spec -> - def outfit = loadString("toy.owner.outfit"); - def image = selectImage(OWNER, outfit, spec); - if (image) { - setImage("toy/$OWNER/$outfit/${image.image}.jpg"); - return image.tags; - } - else { - setImage(null); - return null; - } -}; -def showLounge = { - setImage("toy/$OWNER/lounge.jpg"); -}; -def selectImageSet = { domme, specs -> - def matches = IMAGEDATA[domme]; - if (!matches) return null; - matches = matches.sets.values().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[OWNER]; - if (prev) { - prev = prev.sets[outfit]; - if (prev && specs.every({ spec -> selectImage(OWNER, prev.set, spec)})) { - DOMME = loadDomme(OWNER, outfit); - return outfit; + 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[domme]; + if (!matches) return null; + matches = matches.sets[set]; + if (!matches) return null; + matches = matches + .images.findAll({ i -> meets(i) }); + if (matches.isEmpty()) return null; + return matches[getRandom(matches.size())]; + }; + def showImage = { spec -> + def outfit = loadString("toy.owner.outfit"); + def image = selectImage(OWNER, outfit, spec); + if (image) { + setImage("toy/$OWNER/$outfit/${image.image}.jpg"); + return image.tags; + } + else { + setImage(null); + return null; + } + }; + def showLounge = { + setImage("toy/$OWNER/lounge.jpg"); + }; + def selectImageSet = { domme, specs -> + def matches = IMAGEDATA[domme]; + if (!matches) return null; + matches = matches.sets.values().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[OWNER]; + if (prev) { + prev = prev.sets[outfit]; + if (prev && specs.every({ spec -> selectImage(OWNER, prev.set, spec)})) { + DOMME = loadDomme(OWNER, outfit); + return outfit; + } } } - } - outfit = selectImageSet(OWNER, specs); - if (!outfit) { - showPopup("No outfit for $OWNER : $specs"); - save("toy.owner.outfit", null); - save("toy.owner.outfitTime", null); - DOMME = loadDomme(OWNER, outfit); - return; - } - save("toy.owner.outfit", outfit.set); - save("toy.owner.outfitTime", getTime()); - DOMME = loadDomme(OWNER, outfit.set); -}; -// 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 getProp = {i, f, d = null -> - final v = f("toy.$i".toString()); - if (v == null) return d; - return v; -}; -def getDommeProp = { i, d = null -> - if (DOMME == null) return d; - final v = DOMME[i]; - if (v == null) return d; - return v; -}; -def dommeTitle = { getDommeProp("title", "Mistress") }; -def dommeName = { getDommeProp("name", "") }; -def loadB = { p -> loadBoolean(p)}; -def loadI = { p -> loadInteger(p)}; -def loadS = { p -> loadString(p)}; -def setProp = {i, v -> save("toy.$i".toString(), v)}; -def has = {i -> loadBoolean("toys.$i") == true}; -def likes = {i -> loadBoolean("fetish.$i") == true}; -def can = {i -> loadBoolean("toy.permission.$i") == true}; -def perm = can; -def getPermission = {i -> getProp("permission.$i", loadB)}; -def setPermission = {i, v -> setProp("permission.$i", v)}; -def givePermission = {i -> setPermission(i, true)}; -def revokePermission = {i -> setPermission(i, false)}; -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 getPunish = { loadInteger("toy.punishment") ?: 0; }; -def adjustPunish = { p -> save("toy.punishment", Math.max(getPunish() + (int)p, 0)); }; -def getAway = { loadString("toy.owner.away") }; -def setAway = { a -> save("toy.owner.away", a) }; -def randRange = { min, max -> (int)min + getRandom((int)(1 + max - min)) } -def namedEvents; -def loadEvents = { - return (loadMap("toy.events") ?: [:]); -} -def saveEvents = { events -> - save("toy.events", events); -} -def nextEvent = { events -> - def first = null; - events.each{ k, v -> - if (!first || first.event.time > v.time) { - first = [ name: k, event: v ]; - } - }; - return first; -}; -def setEvent = { events, name, time, func, arg = null -> - if (namedEvents.containsKey(func)) { - events[name] = [ - time: time, - func: func, - arg: arg - ]; - saveEvents(events); - return true; - } - else { - showPopup("No such event $func"); - return false; - } -}; -def addEvent = { name, time, func, arg = null -> - def events = loadEvents(); - return setEvent(events, name, time, func, arg); -} -def addEventIfMissing = { name, time, func, arg = null -> - def events = loadEvents(); - if (!events.containsKey(name)) { + outfit = selectImageSet(OWNER, specs); + if (!outfit) { + showPopup("No outfit for $OWNER : $specs"); + save("toy.owner.outfit", null); + save("toy.owner.outfitTime", null); + DOMME = loadDomme(OWNER, outfit); + return; + } + save("toy.owner.outfit", outfit.set); + save("toy.owner.outfitTime", getTime()); + DOMME = loadDomme(OWNER, outfit.set); + }; + // 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 getProp = {i, f, d = null -> + final v = f("toy.$i".toString()); + if (v == null) return d; + return v; + }; + def getDommeProp = { i, d = null -> + if (DOMME == null) return d; + final v = DOMME[i]; + if (v == null) return d; + return v; + }; + def dommeTitle = { getDommeProp("title", "Mistress") }; + def dommeName = { getDommeProp("name", "") }; + def loadB = { p -> loadBoolean(p)}; + def loadI = { p -> loadInteger(p)}; + def loadS = { p -> loadString(p)}; + def setProp = {i, v -> save("toy.$i".toString(), v)}; + def has = {i -> loadBoolean("toys.$i") == true}; + def likes = {i -> loadBoolean("fetish.$i") == true}; + def can = {i -> loadBoolean("toy.permission.$i") == true}; + def perm = can; + def getPermission = {i -> getProp("permission.$i", loadB)}; + def setPermission = {i, v -> setProp("permission.$i", v)}; + def givePermission = {i -> setPermission(i, true)}; + def revokePermission = {i -> setPermission(i, false)}; + 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 getPunish = { loadInteger("toy.punishment") ?: 0; }; + def adjustPunish = { p -> save("toy.punishment", Math.max(getPunish() + (int)p, 0)); }; + def getAway = { loadString("toy.owner.away") }; + def setAway = { a -> save("toy.owner.away", a) }; + def randRange = { min, max -> (int)min + getRandom((int)(1 + max - min)) } + def loadEvents = { + return (loadMap("toy.events") ?: [:]); + } + def saveEvents = { events -> + save("toy.events", events); + } + def nextEvent = { events -> + def first = null; + events.each{ k, v -> + if (!first || first.event.time > v.time) { + first = [ name: k, event: v ]; + } + }; + return first; + }; + def setEvent = { events, name, time, func, arg = null -> + if (namedEvents.containsKey(func)) { + events[name] = [ + time: time, + func: func, + arg: arg + ]; + saveEvents(events); + return true; + } + else { + showPopup("No such event $func"); + return false; + } + }; + def addEvent = { name, time, func, arg = null -> + def events = loadEvents(); return setEvent(events, name, time, func, arg); } -}; -def removeEvent = { name -> - def events = loadEvents(); - events.remove(name); - saveEvents(events); -}; -def execEvents = { rt -> - for (def e = nextEvent(loadEvents()); e && e.event.time <= getTime(); e = nextEvent(loadEvents())) { - def f = namedEvents[e.event.func]; - removeEvent(e.name); - if (f) { - f(e.name, e.event.arg, e.event.time, rt); + def addEventIfMissing = { name, time, func, arg = null -> + def events = loadEvents(); + if (!events.containsKey(name)) { + return setEvent(events, name, time, func, arg); } - } -}; -def sessionAborted = null; -def sessionToys = [:]; -def gagText = { t, p -> - if (!is(GAGGED)) return t.toString(); - 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() + " ($p)"; -}; -def showButtonG = { s, p, t = null -> - return t != null ? showButton(gagText(s, p), t) : showButton(gagText(s, p)); -}; -def compose = { texts -> - if (!texts || texts.isEmpty()) return null; - return texts.collect { - t -> t[getRandom(t.size())] - }.join(" ").capitalize(); -}; -def present = { imageSpec, texts -> - if (texts) { - show(compose(texts)); - } - if (imageSpec) { - return showImage(imageSpec); - } - return null; -}; -def showButtonGT = { s, p, t, pc -> - def tt = showButton(gagText(s, p), t); - if (tt == t) { - present(null, [ - ["Don't keep me waiting!", "Hurry up!", "Quicker, toy!"]]); - def pt = showButton(gagText(s, p)); - adjustPunish(pt * pc); - return pt + tt; - } - return tt; -}; -def imageTagsComment = { tags -> - if (!tags || tags.isEmpty()) return null; - tags = tags.intersect([ASS, CROP, BOOTS, PUSSY, SSSH]) - if (tags.isEmpty()) return null; - Collections.shuffle(tags); - switch (tags[0]) { - case ASS: - if (!is(GAGGED)) - return compose([ - ["Come closer,", "Don't be shy,"], - ["kiss it!", "two kisses on each cheek."]]); - else - return compose([ - ["Too bad you can't kiss it like that.", "Maybe I should ungag you so you can kiss it?"]]); - break; - case CROP: - return compose([ - ["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)) - return compose([ - ["I know you'd like to worship them.", "Too bad that tongue can't be put to good use."]]); - else - return compose([ - ["Get down there, worship my boots.", "See any dirt on my boots? Maybe you should lick them clean!"]]); - case PUSSY: - if (is(GAGGED)) + }; + def removeEvent = { name -> + def events = loadEvents(); + events.remove(name); + saveEvents(events); + }; + def execEvents = { rt -> + for (def e = nextEvent(loadEvents()); e && e.event.time <= getTime(); e = nextEvent(loadEvents())) { + def f = namedEvents[e.event.func]; + removeEvent(e.name); + if (f) { + f(e.name, e.event.arg, e.event.time, rt); + } + } + }; + def sessionAborted = null; + def sessionToys = [:]; + def gagText = { t, p -> + if (!is(GAGGED)) return t.toString(); + 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() + " ($p)"; + }; + def showButtonG = { s, p, t = null -> + return t != null ? showButton(gagText(s, p), t) : showButton(gagText(s, p)); + }; + def compose = { texts -> + if (!texts || texts.isEmpty()) return null; + return texts.collect { + t -> t[getRandom(t.size())] + }.join(" ").capitalize(); + }; + def present = { imageSpec, texts -> + if (texts) { + show(compose(texts)); + } + if (imageSpec) { + return showImage(imageSpec); + } + return null; + }; + def showButtonGT = { s, p, t, pc -> + def tt = showButton(gagText(s, p), t); + if (tt == t) { + present(null, [ + ["Don't keep me waiting!", "Hurry up!", "Quicker, toy!"]]); + def pt = showButton(gagText(s, p)); + adjustPunish(pt * pc); + return pt + tt; + } + return tt; + }; + def imageTagsComment = { tags -> + if (!tags || tags.isEmpty()) return null; + tags = tags.intersect([ASS, CROP, BOOTS, PUSSY, SSSH]) + if (tags.isEmpty()) return null; + Collections.shuffle(tags); + switch (tags[0]) { + case ASS: + if (!is(GAGGED)) + return compose([ + ["Come closer,", "Don't be shy,"], + ["kiss it!", "two kisses on each cheek."]]); + else + return compose([ + ["Too bad you can't kiss it like that.", "Maybe I should ungag you so you can kiss it?"]]); + break; + case CROP: return compose([ - ["I know you'd like to pleasure me.", "Too bad that tongue can't be put to good use."]]); - else + ["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)) + return compose([ + ["I know you'd like to worship them.", "Too bad that tongue can't be put to good use."]]); + else + return compose([ + ["Get down there, worship my boots.", "See any dirt on my boots? Maybe you should lick them clean!"]]); + case PUSSY: + if (is(GAGGED)) + return compose([ + ["I know you'd like to pleasure me.", "Too bad that tongue can't be put to good use."]]); + else + return compose([ + ["How about you start licking it?", "How much would like your tongue in there?"]]); + break; + case SSSH: return compose([ - ["How about you start licking it?", "How much would like your tongue in there?"]]); - break; - case SSSH: - return compose([ - ["Quiet now.", "Sssssh.", "Patience."]]); - break; - } - return null; -}; -def harden = { imageSpec -> - present(imageSpec, [ - ["I want to see you hard.", "I need you hard now, very hard."], - ["Stroke it, slowly...", "Slow worship strokes..."], - ["don't edge...", "no edging..."], - ["and do NOT cum.", "and definitely no cumming."], - ["Not yet.", "Maybe soon."]]); - playBackgroundSound("toy/90bpm.mp3", 2); //45sec - if (showButtonG("Hard, ${dommeTitle()}", "hard", 60) == 60) { - playBackgroundSound(null); + ["Quiet now.", "Sssssh.", "Patience."]]); + break; + } + return null; + }; + def harden = { imageSpec -> present(imageSpec, [ - ["What's taking so long!?", "Come on!", "Don't disappoint me."], - ["Slap it about a bit!.", "Get it hard, now!", "Pinch your nipples."]]); - playBackgroundSound("toy/165bpm.mp3"); - if (showButtonG("Hard, ${dommeTitle()}", "hard", 30) == 30) { - playBackgroundSound("toy/180bpm.mp3"); + ["I want to see you hard.", "I need you hard now, very hard."], + ["Stroke it, slowly...", "Slow worship strokes..."], + ["don't edge...", "no edging..."], + ["and do NOT cum.", "and definitely no cumming."], + ["Not yet.", "Maybe soon."]]); + playBackgroundSound("toy/90bpm.mp3", 2); //45sec + if (showButtonG("Hard, ${dommeTitle()}", "hard", 60) == 60) { + playBackgroundSound(null); + present(imageSpec, [ + ["What's taking so long!?", "Come on!", "Don't disappoint me."], + ["Slap it about a bit!.", "Get it hard, now!", "Pinch your nipples."]]); + playBackgroundSound("toy/165bpm.mp3"); if (showButtonG("Hard, ${dommeTitle()}", "hard", 30) == 30) { - sessionAborted = SOFT; - present(imageSpec, [ - ["Bah!", "Pathetic!"], - ["That's no good to me!", "How do I have fun with that!?"]]); - adjustPunish(100); - showButtonG("Sorry, ${dommeTitle()}", "sorry"); + playBackgroundSound("toy/180bpm.mp3"); + if (showButtonG("Hard, ${dommeTitle()}", "hard", 30) == 30) { + sessionAborted = SOFT; + present(imageSpec, [ + ["Bah!", "Pathetic!"], + ["That's no good to me!", "How do I have fun with that!?"]]); + adjustPunish(100); + showButtonG("Sorry, ${dommeTitle()}", "sorry"); + } } } - } - playBackgroundSound(null); -}; -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); - } -}; -// Return whether toy came or not -def mightCum = { time, lenient = false, reason = "without permission" -> - final taken = showButtonG("Sorry, ${dommeTitle()}, I'm cumming $reason", "cumming", time); - if (taken < time) { playBackgroundSound(null); - if (lenient && taken < 2) { - present(null, [ - ["Awww.", "Damn."], - ["That was close;", "I'll let you have that one;"], - ["And I was looking forward to punishing you.", "No punishment this time."]]); + }; + 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); } - else { - present(null, [ - ["Bah!", "Pfft. I'm very disappointed in you."], - ["Ruin it.", "Don't touch it."]]); - sessionAborted = CUM; - adjustPunish(80); + }; + // Return whether toy came or not + def mightCum = { time, lenient = false, reason = "without permission" -> + final taken = showButtonG("Sorry, ${dommeTitle()}, I'm cumming $reason", "cumming", time); + if (taken < time) { + playBackgroundSound(null); + if (lenient && taken < 2) { + present(null, [ + ["Awww.", "Damn."], + ["That was close;", "I'll let you have that one;"], + ["And I was looking forward to punishing you.", "No punishment this time."]]); + } + else { + present(null, [ + ["Bah!", "Pfft. I'm very disappointed in you."], + ["Ruin it.", "Don't touch it."]]); + sessionAborted = CUM; + adjustPunish(80); + } + adjustPunish(taken * 10); + wait(10); + present([DRESSED], [["Clean up your mess!"]]); + showButtonG("Yes, ${dommeTitle()}", "ok"); + wait(10); + showButtonG("Cleaned up, ${dommeTitle()}, back", "back"); + return true; } - adjustPunish(taken * 10); - wait(10); - present([DRESSED], [["Clean up your mess!"]]); - showButtonG("Yes, ${dommeTitle()}", "ok"); + return false; + }; + // Return whether toy came or not, despite being denied + def cumChanceDenied = { lenient = false -> + present([TEASE], [ + ["Stop!", "Nope!", "Haha! No!"], + ["Let go.", "Hands off!", "Hands behind your back."], + ["Not this time.", "Maaaaybe next time, toy."]]); + return mightCum(15, lenient); + }; + def cumChanceCountdown = { + present([TEASE], [ + ["OK, toy, I'm going to count you down.", "Would you like a countdown, toy?"], + ["Get stroking!", "Jerk it!"]]); + if (mightCum(5 + getRandom(5))) return true; + present([TITS], []); + def numbers = ""; + for (def n = 10; n > 0; n--) { + numbers += "${n}... "; + show(numbers); + mightCum(1, false, "too soon"); + if (getRandom(100) > 85 || !can(CUM)) { + return cumChanceDenied(); + } + } + present([TITS], [ + ["Cum, cum", "Cum for me"], + ["you little slut.", "my lucky toy.", "you dirty boy."]]); wait(10); - showButtonG("Cleaned up, ${dommeTitle()}, back", "back"); + showButtonG("Thank you, ${dommeTitle()}.", "ok"); return true; - } - return false; -}; -// Return whether toy came or not, despite being denied -def cumChanceDenied = { lenient = false -> - present([TEASE], [ - ["Stop!", "Nope!", "Haha! No!"], - ["Let go.", "Hands off!", "Hands behind your back."], - ["Not this time.", "Maaaaybe next time, toy."]]); - return mightCum(15, lenient); -}; -def cumChanceCountdown = { - present([TEASE], [ - ["OK, toy, I'm going to count you down.", "Would you like a countdown, toy?"], - ["Get stroking!", "Jerk it!"]]); - if (mightCum(5 + getRandom(5))) return true; - present([TITS], []); - def numbers = ""; - for (def n = 10; n > 0; n--) { - numbers += "${n}... "; - show(numbers); - mightCum(1, false, "too soon"); - if (getRandom(100) > 85 || !can(CUM)) { + }; + def cumChanceWindow = { + present([TEASE], [ + ["OK, toy, I'm going to give you a chance to cum.", "Would you like a small chance to cum, toy?"], + ["Get stroking!", "Stroke it, you'll need to be close!"], + ["You won't get long.", "You might not get long.", "Wait for my say so..."]]); + if (mightCum(15 + getRandom(60), false, "too soon")) return true; + if (getRandom(5) > 0 || !can(CUM)) { return cumChanceDenied(); } - } - present([TITS], [ - ["Cum, cum", "Cum for me"], - ["you little slut.", "my lucky toy.", "you dirty boy."]]); - wait(10); - showButtonG("Thank you, ${dommeTitle()}.", "ok"); - return true; -}; -def cumChanceWindow = { - present([TEASE], [ - ["OK, toy, I'm going to give you a chance to cum.", "Would you like a small chance to cum, toy?"], - ["Get stroking!", "Stroke it, you'll need to be close!"], - ["You won't get long.", "You might not get long.", "Wait for my say so..."]]); - if (mightCum(15 + getRandom(60), false, "too soon")) return true; - if (getRandom(5) > 0 || !can(CUM)) { - return cumChanceDenied(); - } - present([TITS], [ - ["Cum, cum", "Cum for me"], - ["you little slut.", "my lucky toy.", "you dirty boy."]]); - def w = 4 + getRandom(4); - if (showButtonG("Cumming, ${dommeTitle()}", "cumming", w) == w) { - w = 3; - present(null, [ - ["Quickly now!", "Hurry!"]]); + present([TITS], [ + ["Cum, cum", "Cum for me"], + ["you little slut.", "my lucky toy.", "you dirty boy."]]); + def w = 4 + getRandom(4); if (showButtonG("Cumming, ${dommeTitle()}", "cumming", w) == w) { - present([TEASE], [ - ["Stop!", "Let go.", "Hands off!"], - ["Time's up!", "You had your chance."]]); - return mightCum(15, true, "too late"); + w = 3; + present(null, [ + ["Quickly now!", "Hurry!"]]); + if (showButtonG("Cumming, ${dommeTitle()}", "cumming", w) == w) { + present([TEASE], [ + ["Stop!", "Let go.", "Hands off!"], + ["Time's up!", "You had your chance."]]); + return mightCum(15, true, "too late"); + } } - } - wait(10); - showButtonG("Thank you, ${dommeTitle()}.", "ok"); - return true; -}; -def cumChanceRuin = { - present([TEASE], [ - ["Stroke!", "Jerk it."], - ["Get close to the edge, toy...", "Get yourself close..."]]); - if (mightCum(5 + getRandom(10), false, "too soon")) return true; - if (getRandom(5) > 0 || !can(CUM)) { - return cumChanceDenied(); - } - for (def n = 3 + getRandom(6); n >= 0; n--) { - present([TITS], [ - ["Try to cum!"]]); - def w = 3 + getRandom(8); - if (showButtonG("Cumming, ${dommeTitle()}", "cumming", w) < w) { - present([TEASE], [ - ["Hands off!", "Leave it."], - ["Let it all ooze out.", "You're not getting a proper release."]]); - wait(10); - showButtonG("Thank you, ${dommeTitle()}.", "ok"); - return true; + wait(10); + showButtonG("Thank you, ${dommeTitle()}.", "ok"); + return true; + }; + def cumChanceRuin = { + present([TEASE], [ + ["Stroke!", "Jerk it."], + ["Get close to the edge, toy...", "Get yourself close..."]]); + if (mightCum(5 + getRandom(10), false, "too soon")) return true; + if (getRandom(5) > 0 || !can(CUM)) { + return cumChanceDenied(); } - if (n > 0) { + for (def n = 3 + getRandom(6); n >= 0; n--) { present([TITS], [ - ["Stop!", "Not now!", "Wait!"], - ["Hands off!", "Hands by your side."]]); - w = (w - 2) + getRandom(8); - if (mightCum(w, true, "but it's ruined")) return true; + ["Try to cum!"]]); + def w = 3 + getRandom(8); + if (showButtonG("Cumming, ${dommeTitle()}", "cumming", w) < w) { + present([TEASE], [ + ["Hands off!", "Leave it."], + ["Let it all ooze out.", "You're not getting a proper release."]]); + wait(10); + showButtonG("Thank you, ${dommeTitle()}.", "ok"); + return true; + } + if (n > 0) { + present([TITS], [ + ["Stop!", "Not now!", "Wait!"], + ["Hands off!", "Hands by your side."]]); + w = (w - 2) + getRandom(8); + if (mightCum(w, true, "but it's ruined")) return true; + } } - } - return cumChanceDenied(true); -}; -def edge = { amount, imageSpec -> - def allowedTime = 128; - (getRandom(amount) + 2).times { - if (sessionAborted) return; - present(imageSpec, [ - ["Stroke to edge now, toy...", "Edge!"], - ["Don't cum.", "No cumming.", "No accidents though."]]); - showButtonGT("Edging, ${dommeTitle()}", "edging", allowedTime + 20, 1); - if (getRandom(2) == 1) { + return cumChanceDenied(true); + }; + def edge = { amount, imageSpec -> + def allowedTime = 128; + (getRandom(amount) + 2).times { + if (sessionAborted) return; present(imageSpec, [ - ["Hold it...", "And hold...", "Keeping going..."]]); - if (mightCum(getRandom(20) + 5)) return; - } - present(imageSpec, [ - ["Hands off!", "Stop!"]]); - if (mightCum(getRandom(5) + 5)) return; - allowedTime /= 2; - }; -}; -def strokes = { amount, imageSpec -> - def tags = 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; - def itr = 0; - (getRandom(amount) + 5).times { - if (sessionAborted) return; - switch (itr) { - case 2: - final cmt = imageTagsComment(tags); - if (cmt) { - show(cmt); - } - break; - case 4: - present(null, [ - ["Don't stop stroking!", "Keep jerking.", "Keep stroking."]]); - break; - } - itr += 1; - def beatNo = getRandom(BEATS.size()); - def beat = BEATS[beatNo]; - playBackgroundSound("toy/${beat.bpm}bpm.mp3", 10); // Lots, to cover offset - def len = getRandom(Math.max(5, beat.len + (sto * beatNo))); - if (showButtonG("Edging, ${dommeTitle()}", "edging", len) < len) { - sto -= 1; - edges += 1; - itr = 0; - playBackgroundSound(null); - switch (getRandom(3)) { - case 0: - present(imageSpec, [ - ["Stop!", "Let go!", "Hands off!"]]); - if (mightCum(getRandom(8) + 10)) return; - break; - case 1: - present(imageSpec, [ - ["Careful now...", "No accidents..."], - ["slow it down...", "slowly now..."]]); - playBackgroundSound("toy/30bpm.mp3"); - if (mightCum(getRandom(20) + 5)) { - playBackgroundSound(null); - return; - } + ["Stroke to edge now, toy...", "Edge!"], + ["Don't cum.", "No cumming.", "No accidents though."]]); + showButtonGT("Edging, ${dommeTitle()}", "edging", allowedTime + 20, 1); + if (getRandom(2) == 1) { + present(imageSpec, [ + ["Hold it...", "And hold...", "Keeping going..."]]); + if (mightCum(getRandom(20) + 5)) return; + } + present(imageSpec, [ + ["Hands off!", "Stop!"]]); + if (mightCum(getRandom(5) + 5)) return; + allowedTime /= 2; + }; + }; + def strokes = { amount, imageSpec -> + def tags = 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; + def itr = 0; + (getRandom(amount) + 5).times { + if (sessionAborted) return; + switch (itr) { case 2: - switch(getSelectedValue("Would you like to cum, toy?", [ - gagText("Please, ${dommeTitle()}, may I cum!?", "yes"), - gagText("No, ${dommeTitle()}, please torment me more!", "no") - ])) { - case 0: - if (can(CUM)) { - present(imageSpec, [ - ["Maybe...", "Perhaps.", "We'll see."]]); - } - else { - present(imageSpec, [ - ["No you may not!", "Nope.", "Not a chance."]]); - adjustPunish(5); - } - break; - case 1: - present(imageSpec, [ - ["Good boy.", "Very well.", "OK then!"]]); - adjustPunish(-5); - break; + final cmt = imageTagsComment(tags); + if (cmt) { + show(cmt); } - if (mightCum(getRandom(8) + 5)) { + break; + case 4: + present(null, [ + ["Don't stop stroking!", "Keep jerking.", "Keep stroking."]]); + break; + } + itr += 1; + def beatNo = getRandom(BEATS.size()); + def beat = BEATS[beatNo]; + playBackgroundSound("toy/${beat.bpm}bpm.mp3", 10); // Lots, to cover offset + def len = getRandom(Math.max(5, beat.len + (sto * beatNo))); + if (showButtonG("Edging, ${dommeTitle()}", "edging", len) < len) { + sto -= 1; + edges += 1; + itr = 0; + playBackgroundSound(null); + switch (getRandom(3)) { + case 0: + present(imageSpec, [ + ["Stop!", "Let go!", "Hands off!"]]); + if (mightCum(getRandom(8) + 10)) return; + break; + case 1: + present(imageSpec, [ + ["Careful now...", "No accidents..."], + ["slow it down...", "slowly now..."]]); + playBackgroundSound("toy/30bpm.mp3"); + if (mightCum(getRandom(20) + 5)) { + playBackgroundSound(null); + return; + } + case 2: + switch(getSelectedValue("Would you like to cum, toy?", [ + gagText("Please, ${dommeTitle()}, may I cum!?", "yes"), + gagText("No, ${dommeTitle()}, please torment me more!", "no") + ])) { + case 0: + if (can(CUM)) { + present(imageSpec, [ + ["Maybe...", "Perhaps.", "We'll see."]]); + } + else { + present(imageSpec, [ + ["No you may not!", "Nope.", "Not a chance."]]); + adjustPunish(5); + } + break; + case 1: + present(imageSpec, [ + ["Good boy.", "Very well.", "OK then!"]]); + adjustPunish(-5); + break; + } + if (mightCum(getRandom(8) + 5)) { + playBackgroundSound(null); + return; + } playBackgroundSound(null); - return; - } - playBackgroundSound(null); + } + tags = present(imageSpec, [ + ["Back to stroking, toy...", "Get to it again...."], + ["follow the beat..."], + ["no accidents.", "and concentrate."]]); + } + else { + playBackgroundSound(null); } - tags = 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); } - if (edges == 0) { - sto += 1; - } - save("toy.strokeTeaseOffset", sto); -} -def clampPulls = { amount -> - getRandom(1 + amount).times { - if (getRandom(2)) { - present([DRESSED], [ - ["Take them off.", "Remove them."]]); - wait(6 + getRandom(8)); + def clampPulls = { amount -> + getRandom(1 + amount).times { if (getRandom(2)) { present([DRESSED], [ - ["And put them back on", "Put them back"], - ["right where they came from.", "where they were."]]); + ["Take them off.", "Remove them."]]); + wait(6 + getRandom(8)); + if (getRandom(2)) { + present([DRESSED], [ + ["And put them back on", "Put them back"], + ["right where they came from.", "where they were."]]); + } + else { + present([DRESSED], [ + ["Flip them", "Turn them"], + ["90 degrees", "around"], + ["and put them back.", "and replace them."]]); + } + wait(10 + getRandom(8)); } - else { + else if (getRandom(2)) { present([DRESSED], [ - ["Flip them", "Turn them"], - ["90 degrees", "around"], - ["and put them back.", "and replace them."]]); + ["Twist them"], + ["for me.", "... twist those nipples."]]); + wait(6 + getRandom(5)); + if (getRandom(2)) { + present(null, [ + ["More!", "A little more!", "Further!"]]); + wait(getRandom(10) + 2); + } } - wait(10 + getRandom(8)); - } - else if (getRandom(2)) { present([DRESSED], [ - ["Twist them"], - ["for me.", "... twist those nipples."]]); - wait(6 + getRandom(5)); - if (getRandom(2)) { + ["Pull them tight.", "Pull them!"]]); + wait(getRandom(10) + 5); + if (getRandom(5) == 0) { present(null, [ - ["More!", "A little more!", "Further!"]]); + ["Tighter!", "Harder!", "Further!"]]); wait(getRandom(10) + 2); + present(null, [ + ["Haha!", "Ooo, they must hurt.", "Do they hurt?"]]); + wait(3); } - } - present([DRESSED], [ - ["Pull them tight.", "Pull them!"]]); - wait(getRandom(10) + 5); - if (getRandom(5) == 0) { - present(null, [ - ["Tighter!", "Harder!", "Further!"]]); - wait(getRandom(10) + 2); present(null, [ - ["Haha!", "Ooo, they must hurt.", "Do they hurt?"]]); - wait(3); + ["Release them.", "Let them go."]]); + wait(getRandom(5) + 3); + }; + return amount; + }; + // Pre-tease + def preRelease = { + if (!is(CHASTE)) return; + if (!is(NAKED)) { + present([DRESSED], [ + ["Reach down... and through your clothes...", "Stay clothed,"], + ["rub that chastity device...", "run your hand around that locked cock..."], + ["imagine if that was me doing it.", "that looks frustrating."]]); + wait(15); + present(null, [["Show me.", "Let me see it."]]); + wait(10); + } + else { + present([DRESSED], [ + ["Look what we have here.", "Hnmm, look at that."], + ["Locked away and useless.", "Under lock and key as it should be."], + ["Too bad I have the keys.", "And I have the keys somewhere safe."]]); + wait(10); } - present(null, [ - ["Release them.", "Let them go."]]); - wait(getRandom(5) + 3); - }; - return amount; -}; -// Pre-tease -def preRelease = { - if (!is(CHASTE)) return; - if (!is(NAKED)) { present([DRESSED], [ - ["Reach down... and through your clothes...", "Stay clothed,"], - ["rub that chastity device...", "run your hand around that locked cock..."], - ["imagine if that was me doing it.", "that looks frustrating."]]); + ["Play with your balls,", "Tease those balls a little,"], + ["I want to see how horny you are", "see how hard you can get"], + ["before I let you out.", "before I hand over the keys."], + ["You should be", "When you're"], + ["practically breaking out,", "about to break it for me,"], + ["we'll continue.", "then I'll see about letting it out."]]); + showButtonG("Hard, ${dommeTitle()}, please let me out", "hard"); + present([DRESSED,TEASE], [ + ["OK then.", "Fine, fine."], + ["I'm feeling generous, toy...", "Well, it's no good to me like that..."], + ["I have the keys right here,", "I have the keys here somewhere... OK,"], + ["unlock yourself and", "take your chastity device off"], + ["let me see my cock.", "let's see it."]]); + showButtonG("Yes, ${dommeTitle()}", "ok"); wait(15); - present(null, [["Show me.", "Let me see it."]]); - wait(10); - } - else { + set(CHASTE, false); + showButtonGT("Thank you, ${dommeTitle()}", "ok", 80, 1); + }; + def preEdge = { + if (is(CHASTE)) return; + expose([DRESSED]); + harden([DRESSED]); + edge(4, [DRESSED]); present([DRESSED], [ - ["Look what we have here.", "Hnmm, look at that."], - ["Locked away and useless.", "Under lock and key as it should be."], - ["Too bad I have the keys.", "And I have the keys somewhere safe."]]); - wait(10); - } - present([DRESSED], [ - ["Play with your balls,", "Tease those balls a little,"], - ["I want to see how horny you are", "see how hard you can get"], - ["before I let you out.", "before I hand over the keys."], - ["You should be", "When you're"], - ["practically breaking out,", "about to break it for me,"], - ["we'll continue.", "then I'll see about letting it out."]]); - showButtonG("Hard, ${dommeTitle()}, please let me out", "hard"); - present([DRESSED,TEASE], [ - ["OK then.", "Fine, fine."], - ["I'm feeling generous, toy...", "Well, it's no good to me like that..."], - ["I have the keys right here,", "I have the keys here somewhere... OK,"], - ["unlock yourself and", "take your chastity device off"], - ["let me see my cock.", "let's see it."]]); - showButtonG("Yes, ${dommeTitle()}", "ok"); - wait(15); - set(CHASTE, false); - showButtonGT("Thank you, ${dommeTitle()}", "ok", 80, 1); -}; -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 1.2; - if (!has(BALLGAG)) return; - present([DRESSED], [ - ["Go fetch your gag, toy...", "Bring me your gag, toy..."], - ["Quickly!", "Hurry back!"]]); - showButton("Yes, ${dommeTitle()}"); - show(null); - wait(10); - showButtonGT("Back, ${dommeTitle()}", "back", 50, 2); - 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); - showButtonGT("Gagged, ${dommeTitle()}", "gagged", 20, 2); - sessionToys[BALLGAG] = getTime(); - return 1.2; -}; -def clamps = { - if (is(CLAMPED)) return; - if (!has(CLAMPS)) 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, ${dommeTitle()}", "ok"); - show(null); - wait(10); - set(CLAMPED, true); - showButtonGT("Back, ${dommeTitle()}", "back", 60, 1); - sessionToys[CLAMPS] = getTime(); - present([DRESSED], [ - ["On your knees,", "Kneel before me"], - ["let me see.", "hands behind your back."]]); - wait(getRandom(10) + 5); -}; -def clampsShow = { - present([DRESSED,TEASE], [ - ["Let me see", "Show me"], - ["those nipple clamps,", "those nasty clamps,"], - ["they look painful.", "and jiggle them about for me!"]]); - wait(10); -}; -def preClamps = { - if (!has(CLAMPS)) return; - if (is(CLAMPED)) { - clampsShow(); - } - else { - clamps(); - } - clampPulls(getRandom(4)); - return 1.8; -}; -def preCollar = { - if (is(COLLARED)) return 1.1; - 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, ${dommeTitle()}", "ok"); - show(null); - wait(10); - set(COLLARED, true); - showButtonGT("Back, ${dommeTitle()}", "back", 60, 1); - sessionToys[COLLAR] = getTime(); - return 1.1; -}; -def preStrip = { imageSpec = [DRESSED] -> - if (is(NAKED)) return 1.1; - present(imageSpec, [ - ["Clothes off!", "Get naked, slut!"]]); - wait(10); - present(imageSpec, [ - ["All of them off!", "And the rest!", "Keep going."]]); - set(NAKED, true); - showButtonGT("Naked, ${dommeTitle()}", "naked", 60, 5); - present(null, [ - ["Very good. Now...", "I see..."], - ["on your knees before me,", "kneel,", "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."]]); - wait(10); - return 1.1; -}; - -// 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); - def ways = [cumChanceRuin, cumChanceWindow, cumChanceCountdown]; - if (ways[getRandom(ways.size())]()) { - // Once toy has cum, revoke permission for a while - revokePermission(CUM); - } -}; -def chastity = { pre -> - if (is(CHASTE)) return; - if (!has(CHASTITY)) return; - if (pre) { - present([DRESSED,TEASE], [ - ["Fun time is over for you.", "You'd have enough pleasure lately, my turn."]]); - } - else { - 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); - if (sessionToys.containsKey(CHASTITY)) { - present([DRESSED,TEASE], [ - ["Time to", "Get your chastity device and"], - ["lock my cock away again now.", "get that cock back in its cage."], - ["And of course", "And then"], - ["hand over the key.", "give me the key back."]]); - } - else { - present([DRESSED,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); - showButtonGT("Locked, ${dommeTitle()}. Here's the key.", "locked", 180, 1); - set(CHASTE, true); - present([DRESSED,TEASE], [ - ["Good boy.", "Thank you, toy."]]); - wait(5); -} -def preChastity = { - chastity(true); -} -def postChastity = { - chastity(false); - if (is(CHASTE) && (getPermission(PERM_CHASTE) == null || - (getPermission(PERM_CHASTE) == false && getProp(PERM_CHASTE_ASK, loadI, 0) < getTime() - (DAY * 30)))) { - present([DRESSED,TEASE], [ - ["Now I have you locked up,", "Now my cock is secured away,"], - ["would you let me", "can I"], - ["keep you like that"], - ["all the time?", "permanently?"]]); - def sh = getSelectedValue(null, [ - gagText("Yes, ${dommeTitle()}", "yes"), gagText("Please, ${dommeTitle()}, no", "no")]); - if (sh == 0) { - present([DRESSED,TEASE], [ - ["Good boy.", "Thank you, toy."]]); - setPermission(PERM_CHASTE, true); - setProp(PERM_CHASTE_ASK, null); - } - else { - present([DRESSED,nTEASE], [ - ["Awwww.", "Shame."], - ["One day..."]]); - setPermission(PERM_CHASTE, false); - setProp(PERM_CHASTE_ASK, getTime()); - } - wait(5); - } -}; - -// Play -def playStrokes = { - preRelease(); - harden([TITS]); - strokes(15, [TITS]); -}; -def playEdges = { - preRelease(); - harden([TITS]); - edge(6, [TITS]); -}; -def playNothing = { - (getRandom(3) + 2).times { - show(imageTagsComment(showImage([TITS]))); - wait(getRandom(10) + 10); + ["And relax...", "Hands off..."], + ["cool down a little.", "but keep it hard for me!", "for now!"]]); + wait(getRandom(5) + 5); }; -}; -def getSpoon = { - if (sessionToys.containsKey(SPOON)) { + def preGag = { + if (is(GAGGED)) return 1.2; + if (!has(BALLGAG)) return; present([DRESSED], [ - ["Pick up", "Grab"], - ["your", "that"], - ["wooden spoon"], - ["again."]]); + ["Go fetch your gag, toy...", "Bring me your gag, toy..."], + ["Quickly!", "Hurry back!"]]); + showButton("Yes, ${dommeTitle()}"); + show(null); wait(10); - } - else { + showButtonGT("Back, ${dommeTitle()}", "back", 50, 2); present([DRESSED], [ - ["Go to the kitchen...", "From the kitchen..."], - ["bring me", "get yourself"], - ["a wooden spoon or similar."]]); + ["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); + showButtonGT("Gagged, ${dommeTitle()}", "gagged", 20, 2); + sessionToys[BALLGAG] = getTime(); + return 1.2; + }; + def clamps = { + if (is(CLAMPED)) return; + if (!has(CLAMPS)) 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, ${dommeTitle()}", "ok"); - wait(5); - showButtonGT("Back, ${dommeTitle()}", "back", 20, 1); - sessionToys[SPOON] = getTime(); - } -}; -def playBeatBalls = { - final BEATS = [ - // bpm:30, len:60 ], - [ bpm:90, len:45 ], - [ bpm:165, len:30 ], - [ bpm:180, len:30 ], - [ bpm:240, len:20 ], - ]; - getSpoon(); - present([DRESSED], [ - ["Good.", "Right."], - ["Take those balls in one hand", "In one hand, hold your balls"], - ["and in the other, get ready to beat them.", "and get ready to beat them with other."]]); - showButtonGT("Yes, ${dommeTitle()}", "ok", 10, 1); - present([DRESSED], [ - ["You concentrate on beating,", "Don't worry,"], - ["I'll be keeping count.", "I'll count them for you."]]); - wait(3); - present(null, [["Ready"]]); - wait(1); - present(null, [["Set"]]); - wait(1); - present(null, [["Go!"]]); - def beats = 0; - (4 + getRandom(7)).times({ - def beatNo = getRandom(BEATS.size()); - def beat = BEATS[beatNo]; - def len = getRandom(beat.len - 5) + 5; - playBackgroundSound("toy/${beat.bpm}bpm.mp3"); - wait(len); - playBackgroundSound(null); - beats += (int)((beat.bpm * len) / 60.0); - present(null, [ - ["$beats.", "That's $beats.", "$beats so far."], - ["Keep going.", "Don't stop!", "Just a few more.", "Harder!"]]); - }); - present([DRESSED], [ - ["Stop!", "Ok, you can stop now."], - ["They look a little sore.", "Is that a nice tingly feeling?"]]); - showButtonG("Thank you, ${dommeTitle()}.", "ok"); - show(null); - wait(5); - return beats / 10.0; -}; -def playBeatCock = { - preRelease(); - harden([DRESSED,nTITS]); - getSpoon(); - present([DRESSED], [ - ["Good.", "Right."], - ["When I crack my whip... you beat the head my little cock."], - ["Don't hold back,", "Be brave for me,"], - ["it's meant to hurt!", "it's fun for me to watch."]]); - wait(5); - def count = 0; - def n = (2 + getRandom((int)(getPunish() / 150))); - n.times({ - randRange(10, 20).times({ - playBackgroundSound("shortwhip.wav"); - count += 1; - wait(randRange(3, 5)); - }); - if (it < n - 1) { - def sh = getSelectedValue("Still hard?", [ - gagText("Yes, ${dommeTitle()}", "yes"), gagText("No, ${dommeTitle()}", "no")]); - if (sh == 1) { - harden([DRESSED, nTITS]); - } - else { - present([DRESSED], [ - ["Good", "Good boy", "Excellent"]]); - wait(randRange(3, 5)); - } - present([DRESSED], [ - ["Again!", "More!"]]); - wait(1); - } - }) - return count * 5; -}; -def playKneel = { - present([DRESSED], [ - ["Right, slut.", "Come here, toy."], - ["On the floor, right there.", "Kneel at my feet."]]); - showButtonGT("Yes, ${dommeTitle()}.", "ok", 20, 1); - present([DRESSED], [ - ["When I say go,", "In a moment,"], - ["you'll crawl", "turn and crawl"], - ["away until I ring my bell."], - ["You'll stay there,", "And then you won't move,"], - ["head bowed", "face down"], - ["until I call you back.", "until you're summoned."]]); - showButtonG("Yes, ${dommeTitle()}.", "ok"); - present(null, [["Wait..."]]); - wait(getRandom(10) + 10); - present([DRESSED], [["Ok, go!", "Now! Off you go."]]); - wait(getRandom(5) + 5); - playBackgroundSound("bell.wav"); - show(null); - def kneelTime = 60 + getRandom(240); - wait(kneelTime); - while (playBackgroundSound("bell.wav") || true) { - if (showButtonG("Back, ${dommeTitle()}", "back", 20) != 20) break; - } - present([DRESSED], [ - ["Good boy.", "Very good."], - ["Stay down there.", "Don't move."]]); - wait(getRandom(5) + 5); - return kneelTime / 20.0; -}; -def playBondage = { - if (!has(HANDCUFFS)) return; - def prep = [ preStrip, preCollar, preClamps, preGag ]; - Collections.shuffle(prep); - prep.collect { p -> p(); }; - present([DRESSED, nTEASE], [ - ["Go fetch your handcuffs"]]); - showButtonG("Yes, ${dommeTitle()}", "ok"); - wait(10); - showButtonGT("Back, ${dommeTitle()}", "back", 60, 1); - sessionToys[HANDCUFFS] = getTime(); - present([DRESSED, nTEASE], [ - ["Good."], - ["Down on the floor.", "On your knees.", "Down there, where you are."]]); - wait(15); - present([DRESSED, nTEASE], [ - ["You know what's next.", "Take your cuffs."], - ["Cuff yourself,", "Hands cuffed,"], - ["behind your back of course.", "behind you."]]); - wait(15); - present([DRESSED, TEASE], [ - ["Don't go anywhere!", "Do not move!"]]); - wait(15); - present([STOOD], [ - ["I'll be back for you soon.", "I won't leave you too long."]]); - wait(15); - setImage(null); - show(null); - def waitTime = 300 + getRandom(Math.min(1500, 1 + getPunish() * 10)); - wait(waitTime); - playBackgroundSound("bell.wav"); - present([STOOD], [ - ["Hi slave!", "Hello again."], - ["You don't look very comfortable.", "That looks quite awkward."], - ["Maybe you've been there long enough.", "It's been a while now."]]); - wait(20); - present([DRESSED, TEASE], [ - ["OK,", "Mmmm,"], - ["soon.", "just a while longer."]]); - wait(60 + getRandom(60)); - playBackgroundSound("bell.wav"); - present([DRESSED, TEASE], [ - ["Go find the keys,", "Very well,"], - ["let yourself out,", "unlock the cuffs,"], - ["and come back.", "and hurry back."]]); - wait(10); - setImage(null); - showButtonGT("Freed, ${dommeTitle()}", "freed", 120, 1); - return waitTime; -}; -def playSuck = { - def playWait = { file, time -> - playBackgroundSound(file); - wait(time); - playBackgroundSound(null); + show(null); + wait(10); + set(CLAMPED, true); + showButtonGT("Back, ${dommeTitle()}", "back", 60, 1); + sessionToys[CLAMPS] = getTime(); + present([DRESSED], [ + ["On your knees,", "Kneel before me"], + ["let me see.", "hands behind your back."]]); + wait(getRandom(10) + 5); }; - def suck = { file, n, delay -> - n.times { - playWait(file, delay); - }; + def clampsShow = { + present([DRESSED,TEASE], [ + ["Let me see", "Show me"], + ["those nipple clamps,", "those nasty clamps,"], + ["they look painful.", "and jiggle them about for me!"]]); + wait(10); }; - def suckBeat = { beat, n -> - suck("toy/${beat}bpm.mp3", n, 2 * (60 / beat)); + def preClamps = { + if (!has(CLAMPS)) return; + if (is(CLAMPED)) { + clampsShow(); + } + else { + clamps(); + } + clampPulls(getRandom(4)); + return 1.8; }; - if (sessionToys.containsKey(DILDO)) { + def preCollar = { + if (is(COLLARED)) return 1.1; + if (!has(COLLAR)) return; present([DRESSED], [ - ["Back to your dildo.", "Amuse me some more with that dildo."], - ["Same as before, listen for my bell, slut."]]); + ["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, ${dommeTitle()}", "ok"); - } - else { - present([DRESSED], [ - ["Time for you to amuse me, you little slut."], - ["Get your dildo and secure it somewhere nearby, about eye level when kneeling."]]); + show(null); wait(10); - showButtonGT("Done, ${dommeTitle()}", "done", 60, 1); - sessionToys[DILDO] = getTime(); - present([DRESSED], [ - ["The rules are simple:\n"], - ["On my first bell, you take the head in your mouth and start to pleasure it.\n"], - ["When you hear the beat, you suck the shaft, one beat in, one beat out again.\n"], - ["Lastly, on the crack of my whip, you take the whole thing in and suck from the base.\n"], - ["I know you'll enjoy it, don't be shy.\n"], - ["One more thing..."], - ["That cock doesn't doesn't leave your mouth until I say so, listen for second bell and then return to me."]]); - showButtonGT("Understood, ${dommeTitle()}", "understood", 30, 1); + set(COLLARED, true); + showButtonGT("Back, ${dommeTitle()}", "back", 60, 1); + sessionToys[COLLAR] = getTime(); + return 1.1; + }; + def preStrip = { imageSpec = [DRESSED] -> + if (is(NAKED)) return 1.1; + present(imageSpec, [ + ["Clothes off!", "Get naked, slut!"]]); + wait(10); + present(imageSpec, [ + ["All of them off!", "And the rest!", "Keep going."]]); + set(NAKED, true); + showButtonGT("Naked, ${dommeTitle()}", "naked", 60, 5); + present(null, [ + ["Very good. Now...", "I see..."], + ["on your knees before me,", "kneel,", "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."]]); + wait(10); + return 1.1; + }; + + // Post + def postEdge = { + if (is(CHASTE)) return; + if (!can(EDGE)) return; + edge(6, [TEASE]); + }; + def postCum = { + if (is(CHASTE)) return; present([TEASE], [ - ["Get ready"]]); - showButtonGT("Ready, ${dommeTitle()}", "ready", 10, 1); + ["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); + def ways = [cumChanceRuin, cumChanceWindow, cumChanceCountdown]; + if (ways[getRandom(ways.size())]()) { + // Once toy has cum, revoke permission for a while + revokePermission(CUM); + } + }; + def chastity = { pre -> + if (is(CHASTE)) return; + if (!has(CHASTITY)) return; + if (pre) { + present([DRESSED,TEASE], [ + ["Fun time is over for you.", "You'd have enough pleasure lately, my turn."]]); + } + else { + 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); + if (sessionToys.containsKey(CHASTITY)) { + present([DRESSED,TEASE], [ + ["Time to", "Get your chastity device and"], + ["lock my cock away again now.", "get that cock back in its cage."], + ["And of course", "And then"], + ["hand over the key.", "give me the key back."]]); + } + else { + present([DRESSED,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); + showButtonGT("Locked, ${dommeTitle()}. Here's the key.", "locked", 180, 1); + set(CHASTE, true); + present([DRESSED,TEASE], [ + ["Good boy.", "Thank you, toy."]]); + wait(5); } - show(null); - - final target = loadInteger("toy.deepthroat.goal") ?: 5; - def completed = loadInteger("toy.deepthroat.completed") ?: 0; - def session = 0; - - wait(randRange(10, 20)); - playWait("bell.wav", randRange(10, 40)); // Head - while (session < target) { - suckBeat(30, randRange(5, 15)); // Slow - suckBeat(90, randRange(10, 30)); // Quick - wait(0.5); - final deep = randRange(target / 4, target / 2); - suck("shortwhip.wav", deep, 2.5); // Deep - session += deep; + def preChastity = { + chastity(true); } - suckBeat(30, randRange(5, 15)); // Slow - wait(randRange(10, 20)); // Head - playBackgroundSound("bell.wav"); // End - - showButtonGT("Back, ${dommeTitle()}", "back", 15, 1); - if (getBoolean("Did you perform as ordered?", "Yes, ${dommeTitle()}", "No, ${dommeTitle()}")) { - present([DRESSED], [ - ["Good boy"]]); - adjustPunish(-4 * target); - completed += 1; - if (completed >= 5) { - save("toy.deepthroat.goal", target + 1); - completed = 0; + def postChastity = { + chastity(false); + if (is(CHASTE) && (getPermission(PERM_CHASTE) == null || + (getPermission(PERM_CHASTE) == false && getProp(PERM_CHASTE_ASK, loadI, 0) < getTime() - (DAY * 30)))) { + present([DRESSED,TEASE], [ + ["Now I have you locked up,", "Now my cock is secured away,"], + ["would you let me", "can I"], + ["keep you like that"], + ["all the time?", "permanently?"]]); + def sh = getSelectedValue(null, [ + gagText("Yes, ${dommeTitle()}", "yes"), gagText("Please, ${dommeTitle()}, no", "no")]); + if (sh == 0) { + present([DRESSED,TEASE], [ + ["Good boy.", "Thank you, toy."]]); + setPermission(PERM_CHASTE, true); + setProp(PERM_CHASTE_ASK, null); + } + else { + present([DRESSED,nTEASE], [ + ["Awwww.", "Shame."], + ["One day..."]]); + setPermission(PERM_CHASTE, false); + setProp(PERM_CHASTE_ASK, getTime()); + } + wait(5); } - } - else { - final missed = getInteger("How many deep sucks did you miss?", 1); - if (missed > 5) { + }; + + // Play + def playStrokes = { + preRelease(); + harden([TITS]); + strokes(15, [TITS]); + }; + def playEdges = { + preRelease(); + harden([TITS]); + edge(6, [TITS]); + }; + def playNothing = { + (getRandom(3) + 2).times { + show(imageTagsComment(showImage([TITS]))); + wait(getRandom(10) + 10); + }; + }; + def getSpoon = { + if (sessionToys.containsKey(SPOON)) { present([DRESSED], [ - ["Very disappointing."]]); + ["Pick up", "Grab"], + ["your", "that"], + ["wooden spoon"], + ["again."]]); + wait(10); } else { present([DRESSED], [ - ["Mmm"], - ["More practice required."]]); + ["Go to the kitchen...", "From the kitchen..."], + ["bring me", "get yourself"], + ["a wooden spoon or similar."]]); + showButtonG("Yes, ${dommeTitle()}", "ok"); + wait(5); + showButtonGT("Back, ${dommeTitle()}", "back", 20, 1); + sessionToys[SPOON] = getTime(); } - adjustPunish(10 * missed); - completed -= 1; - if (completed <= -5) { - save("toy.deepthroat.goal", target - 1); - completed = 0; + }; + def playBeatBalls = { + final BEATS = [ + // bpm:30, len:60 ], + [ bpm:90, len:45 ], + [ bpm:165, len:30 ], + [ bpm:180, len:30 ], + [ bpm:240, len:20 ], + ]; + getSpoon(); + present([DRESSED], [ + ["Good.", "Right."], + ["Take those balls in one hand", "In one hand, hold your balls"], + ["and in the other, get ready to beat them.", "and get ready to beat them with other."]]); + showButtonGT("Yes, ${dommeTitle()}", "ok", 10, 1); + present([DRESSED], [ + ["You concentrate on beating,", "Don't worry,"], + ["I'll be keeping count.", "I'll count them for you."]]); + wait(3); + present(null, [["Ready"]]); + wait(1); + present(null, [["Set"]]); + wait(1); + present(null, [["Go!"]]); + def beats = 0; + (4 + getRandom(7)).times({ + def beatNo = getRandom(BEATS.size()); + def beat = BEATS[beatNo]; + def len = getRandom(beat.len - 5) + 5; + playBackgroundSound("toy/${beat.bpm}bpm.mp3"); + wait(len); + playBackgroundSound(null); + beats += (int)((beat.bpm * len) / 60.0); + present(null, [ + ["$beats.", "That's $beats.", "$beats so far."], + ["Keep going.", "Don't stop!", "Just a few more.", "Harder!"]]); + }); + present([DRESSED], [ + ["Stop!", "Ok, you can stop now."], + ["They look a little sore.", "Is that a nice tingly feeling?"]]); + showButtonG("Thank you, ${dommeTitle()}.", "ok"); + show(null); + wait(5); + return beats / 10.0; + }; + def playBeatCock = { + preRelease(); + harden([DRESSED,nTITS]); + getSpoon(); + present([DRESSED], [ + ["Good.", "Right."], + ["When I crack my whip... you beat the head my little cock."], + ["Don't hold back,", "Be brave for me,"], + ["it's meant to hurt!", "it's fun for me to watch."]]); + wait(5); + def count = 0; + def n = (2 + getRandom((int)(getPunish() / 150))); + n.times({ + randRange(10, 20).times({ + playBackgroundSound("shortwhip.wav"); + count += 1; + wait(randRange(3, 5)); + }); + if (it < n - 1) { + def sh = getSelectedValue("Still hard?", [ + gagText("Yes, ${dommeTitle()}", "yes"), gagText("No, ${dommeTitle()}", "no")]); + if (sh == 1) { + harden([DRESSED, nTITS]); + } + else { + present([DRESSED], [ + ["Good", "Good boy", "Excellent"]]); + wait(randRange(3, 5)); + } + present([DRESSED], [ + ["Again!", "More!"]]); + wait(1); + } + }) + return count * 5; + }; + def playKneel = { + present([DRESSED], [ + ["Right, slut.", "Come here, toy."], + ["On the floor, right there.", "Kneel at my feet."]]); + showButtonGT("Yes, ${dommeTitle()}.", "ok", 20, 1); + present([DRESSED], [ + ["When I say go,", "In a moment,"], + ["you'll crawl", "turn and crawl"], + ["away until I ring my bell."], + ["You'll stay there,", "And then you won't move,"], + ["head bowed", "face down"], + ["until I call you back.", "until you're summoned."]]); + showButtonG("Yes, ${dommeTitle()}.", "ok"); + present(null, [["Wait..."]]); + wait(getRandom(10) + 10); + present([DRESSED], [["Ok, go!", "Now! Off you go."]]); + wait(getRandom(5) + 5); + playBackgroundSound("bell.wav"); + show(null); + def kneelTime = 60 + getRandom(240); + wait(kneelTime); + while (playBackgroundSound("bell.wav") || true) { + if (showButtonG("Back, ${dommeTitle()}", "back", 20) != 20) break; } - } - save("toy.deepthroat.completed", completed); - wait(10); -}; -def playClamps = { - if (!has(CLAMPS)) return; - if (!is(CLAMPED)) clamps(); - clampsShow(); - return clampPulls(3 + getRandom(10)); -}; -def intClamps = { - if (!has(CLAMPS)) return; - if (!is(CLAMPED)) return; - clampsShow(); - return clampPulls(getRandom(5)); -}; -def intSqueeze = { - (2 + getRandom(4)).times { n -> - if (n > 0) { - present([DRESSED,TEASE], [ - ["And again,", "Again,", "Once more,"], - ["harder!", "tighter!"]]); + present([DRESSED], [ + ["Good boy.", "Very good."], + ["Stay down there.", "Don't move."]]); + wait(getRandom(5) + 5); + return kneelTime / 20.0; + }; + def playBondage = { + if (!has(HANDCUFFS)) return; + def prep = [ preStrip, preCollar, preClamps, preGag ]; + Collections.shuffle(prep); + prep.collect { p -> p(); }; + present([DRESSED, nTEASE], [ + ["Go fetch your handcuffs"]]); + showButtonG("Yes, ${dommeTitle()}", "ok"); + wait(10); + showButtonGT("Back, ${dommeTitle()}", "back", 60, 1); + sessionToys[HANDCUFFS] = getTime(); + present([DRESSED, nTEASE], [ + ["Good."], + ["Down on the floor.", "On your knees.", "Down there, where you are."]]); + wait(15); + present([DRESSED, nTEASE], [ + ["You know what's next.", "Take your cuffs."], + ["Cuff yourself,", "Hands cuffed,"], + ["behind your back of course.", "behind you."]]); + wait(15); + present([DRESSED, TEASE], [ + ["Don't go anywhere!", "Do not move!"]]); + wait(15); + present([STOOD], [ + ["I'll be back for you soon.", "I won't leave you too long."]]); + wait(15); + setImage(null); + show(null); + def waitTime = 300 + getRandom(Math.min(1500, 1 + getPunish() * 10)); + wait(waitTime); + playBackgroundSound("bell.wav"); + present([STOOD], [ + ["Hi slave!", "Hello again."], + ["You don't look very comfortable.", "That looks quite awkward."], + ["Maybe you've been there long enough.", "It's been a while now."]]); + wait(20); + present([DRESSED, TEASE], [ + ["OK,", "Mmmm,"], + ["soon.", "just a while longer."]]); + wait(60 + getRandom(60)); + playBackgroundSound("bell.wav"); + present([DRESSED, TEASE], [ + ["Go find the keys,", "Very well,"], + ["let yourself out,", "unlock the cuffs,"], + ["and come back.", "and hurry back."]]); + wait(10); + setImage(null); + showButtonGT("Freed, ${dommeTitle()}", "freed", 120, 1); + return waitTime; + }; + def playSuck = { + def playWait = { file, time -> + playBackgroundSound(file); + wait(time); + playBackgroundSound(null); + }; + def suck = { file, n, delay -> + n.times { + playWait(file, delay); + }; + }; + def suckBeat = { beat, n -> + suck("toy/${beat}bpm.mp3", n, 2 * (60 / beat)); + }; + if (sessionToys.containsKey(DILDO)) { + present([DRESSED], [ + ["Back to your dildo.", "Amuse me some more with that dildo."], + ["Same as before, listen for my bell, slut."]]); + showButtonG("Yes, ${dommeTitle()}", "ok"); } else { - present([DRESSED,TEASE], [ - ["Squeeze yours balls", "Grab your balls and squeeze them"], - ["good and tight", "firm and hard"], - ["until they hurt a little.", "as if I was doing it."]]); + present([DRESSED], [ + ["Time for you to amuse me, you little slut."], + ["Get your dildo and secure it somewhere nearby, about eye level when kneeling."]]); + wait(10); + showButtonGT("Done, ${dommeTitle()}", "done", 60, 1); + sessionToys[DILDO] = getTime(); + present([DRESSED], [ + ["The rules are simple:\n"], + ["On my first bell, you take the head in your mouth and start to pleasure it.\n"], + ["When you hear the beat, you suck the shaft, one beat in, one beat out again.\n"], + ["Lastly, on the crack of my whip, you take the whole thing in and suck from the base.\n"], + ["I know you'll enjoy it, don't be shy.\n"], + ["One more thing..."], + ["That cock doesn't doesn't leave your mouth until I say so, listen for second bell and then return to me."]]); + showButtonGT("Understood, ${dommeTitle()}", "understood", 30, 1); + present([TEASE], [ + ["Get ready"]]); + showButtonGT("Ready, ${dommeTitle()}", "ready", 10, 1); } - wait(3 + getRandom(6)); - present(null, [ - ["Let them go.", "Hands off."]]); - wait(3 + getRandom(6)); - }; -}; -def stateToyName = { state -> - switch (state) { - case CUFFED: - return "cuffs"; - case COLLARED: - return "collar"; - case CLAMPED: - return "nipple clamps"; - case GAGGED: - return "gag"; - } - return null; -}; -def removeToy = { toy, imageSpec -> - if (!is(toy)) return; - def toyName = stateToyName(toy); - present(imageSpec, [ - ["OK,"], - ["you may remove your $toyName.", "the $toyName can come off.", "take the $toyName off."]]); - showButtonG("Thank you, ${dommeTitle()}", "ok"); - wait(5); - set(toy, false); - removeEvent("$RELEASEFROM-$toy"); - showButtonGT("Removed, ${dommeTitle()}", "removed", 20, 1); -}; -// Session -def sessionSummon = { imageSpec -> - final limit = 5; - final timeMax = 10; + show(null); - for (def n = 1; n <= limit; n++) { - switch (n) { - case limit: - present(imageSpec, [ - ["TOY!", "SLAVE!", "SLUT!"], - ["Last chance!", "Final warning!"]]); - adjustPunish(5); - 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; + final target = loadInteger("toy.deepthroat.goal") ?: 5; + def completed = loadInteger("toy.deepthroat.completed") ?: 0; + def session = 0; + + wait(randRange(10, 20)); + playWait("bell.wav", randRange(10, 40)); // Head + while (session < target) { + suckBeat(30, randRange(5, 15)); // Slow + suckBeat(90, randRange(10, 30)); // Quick + wait(0.5); + final deep = randRange(target / 4, target / 2); + suck("shortwhip.wav", deep, 2.5); // Deep + session += deep; } - playBackgroundSound(n == limit ? "shortwhip.wav" : "bell.wav"); - if (showButton("Yes, ${dommeTitle()}?", timeMax) < timeMax) { - return true; + suckBeat(30, randRange(5, 15)); // Slow + wait(randRange(10, 20)); // Head + playBackgroundSound("bell.wav"); // End + + showButtonGT("Back, ${dommeTitle()}", "back", 15, 1); + if (getBoolean("Did you perform as ordered?", "Yes, ${dommeTitle()}", "No, ${dommeTitle()}")) { + present([DRESSED], [ + ["Good boy"]]); + adjustPunish(-4 * target); + completed += 1; + if (completed >= 5) { + save("toy.deepthroat.goal", target + 1); + completed = 0; + } + } + else { + final missed = getInteger("How many deep sucks did you miss?", 1); + if (missed > 5) { + present([DRESSED], [ + ["Very disappointing."]]); + } + else { + present([DRESSED], [ + ["Mmm"], + ["More practice required."]]); + } + adjustPunish(10 * missed); + completed -= 1; + if (completed <= -5) { + save("toy.deepthroat.goal", target - 1); + completed = 0; + } + } + save("toy.deepthroat.completed", completed); + wait(10); + }; + def playClamps = { + if (!has(CLAMPS)) return; + if (!is(CLAMPED)) clamps(); + clampsShow(); + return clampPulls(3 + getRandom(10)); + }; + def intClamps = { + if (!has(CLAMPS)) return; + if (!is(CLAMPED)) return; + clampsShow(); + return clampPulls(getRandom(5)); + }; + def intSqueeze = { + (2 + getRandom(4)).times { n -> + if (n > 0) { + present([DRESSED,TEASE], [ + ["And again,", "Again,", "Once more,"], + ["harder!", "tighter!"]]); + } + else { + present([DRESSED,TEASE], [ + ["Squeeze yours balls", "Grab your balls and squeeze them"], + ["good and tight", "firm and hard"], + ["until they hurt a little.", "as if I was doing it."]]); + } + wait(3 + getRandom(6)); + present(null, [ + ["Let them go.", "Hands off."]]); + wait(3 + getRandom(6)); }; - } - showLounge(); - show(null); - adjustPunish(5); - return false; -}; -final activityList = [ - 'intSqueeze': intSqueeze, - 'intClamps': intClamps, - 'preRelease': preRelease, - 'preChastity': preChastity, - 'preEdge': preEdge, - 'preGag': preGag, - 'preStrip': preStrip, - 'preCollar': preCollar, - 'preClamps': preClamps, - 'playStrokes': playStrokes, - 'playEdges': playEdges, - 'playKneel': playKneel, - 'playBeatBalls': playBeatBalls, - 'playBeatCock': playBeatCock, - 'playClamps': playClamps, - 'playNothing': playNothing, - 'playBondage': playBondage, - 'playSuck': playSuck, - 'postCum': postCum, - 'postPermitCum': { - addEventIfMissing(CUM, getTime() + (DAY * 2) + getRandom(DAY * 2), PERMIT, CUM); // 2 - 4 days from now - }, - 'postChastity': postChastity - ]; -def sessionPlay = { - if (is(CHASTE)) sessionToys[CHASTITY] = getTime(); - def playScope = [ - 'randRange': randRange, - 'currentPunishment': getPunish, - 'sessionAborted': { return sessionAborted }, - 'can': can, - 'has': has, - 'is': is, - 'likes': likes, - 'punishMultiple': (float)1.0, - 'prop': load('toy') ?: [:] - ]; - def apply = { activitity, use = null -> - if (!activitity) return; - def val = activitity(); - playBackgroundSound(null); - if (use && val instanceof Number) { - use(val); + }; + def stateToyName = { state -> + switch (state) { + case CUFFED: + return "cuffs"; + case COLLARED: + return "collar"; + case CLAMPED: + return "nipple clamps"; + case GAGGED: + return "gag"; } + return null; }; - def interval = { activities -> - if (activities) { - apply(activities[getRandom(activities.size())], null); + def removeToy = { toy, imageSpec -> + if (!is(toy)) return; + def toyName = stateToyName(toy); + present(imageSpec, [ + ["OK,"], + ["you may remove your $toyName.", "the $toyName can come off.", "take the $toyName off."]]); + showButtonG("Thank you, ${dommeTitle()}", "ok"); + wait(5); + set(toy, false); + removeEvent("$RELEASEFROM-$toy"); + showButtonGT("Removed, ${dommeTitle()}", "removed", 20, 1); + }; + // 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!"]]); + adjustPunish(5); + 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, ${dommeTitle()}?", timeMax) < timeMax) { + return true; + }; } + showLounge(); + show(null); + adjustPunish(5); + return false; }; - def funcMap = [ - 'use': [ - 'punishMultiply': { val -> - if (val) { - playScope.punishMultiple *= val; + final activityList = [ + 'intSqueeze': intSqueeze, + 'intClamps': intClamps, + 'preRelease': preRelease, + 'preChastity': preChastity, + 'preEdge': preEdge, + 'preGag': preGag, + 'preStrip': preStrip, + 'preCollar': preCollar, + 'preClamps': preClamps, + 'playStrokes': playStrokes, + 'playEdges': playEdges, + 'playKneel': playKneel, + 'playBeatBalls': playBeatBalls, + 'playBeatCock': playBeatCock, + 'playClamps': playClamps, + 'playNothing': playNothing, + 'playBondage': playBondage, + 'playSuck': playSuck, + 'postCum': postCum, + 'postPermitCum': { + addEventIfMissing(CUM, getTime() + (DAY * 2) + getRandom(DAY * 2), PERMIT, CUM); // 2 - 4 days from now + }, + 'postChastity': postChastity + ]; + def sessionPlay = { + if (is(CHASTE)) sessionToys[CHASTITY] = getTime(); + def playScope = [ + 'randRange': randRange, + 'currentPunishment': getPunish, + 'sessionAborted': { return sessionAborted }, + 'can': can, + 'has': has, + 'is': is, + 'likes': likes, + 'punishMultiple': (float)1.0, + 'prop': load('toy') ?: [:] + ]; + def apply = { activitity, use = null -> + if (!activitity) return; + def val = activitity(); + playBackgroundSound(null); + if (use && val instanceof Number) { + use(val); + } + }; + def interval = { activities -> + if (activities) { + apply(activities[getRandom(activities.size())], null); + } + }; + def funcMap = [ + 'use': [ + 'punishMultiply': { val -> + if (val) { + playScope.punishMultiple *= val; + } + }, + 'punishApply': { val -> + if (val) { + adjustPunish(-val * playScope.punishMultiple); + } } - }, - 'punishApply': { val -> - if (val) { - adjustPunish(-val * playScope.punishMultiple); + ], + 'select': [ + 'repeat': { amount, activities, intervals, use = null -> + amount.times { + if (!sessionAborted) { + apply(activities[getRandom(activities.size())], use); + interval(intervals); + } + }; + }, + 'take': { amount, activities, intervals, use = null -> + Collections.shuffle(activities); + activities.take(amount).each { + if (!sessionAborted) { + apply(it, use); + interval(intervals); + } + }; + } + ], + 'activities': activityList + ]; + final eval = { expr -> Eval.me('toy', playScope, expr.toString()) }; + if (is("DEBUG")) { + // Check everything in DOMME evaluates and resolves + final checkIsFunc = { group, name -> + if (!name) return; + show("checking $group:$name is a function"); + if (!(funcMap[group][name] instanceof Closure)) { + showPopup("$group:$name is not a function"); } } - ], - 'select': [ - 'repeat': { amount, activities, intervals, use = null -> - amount.times { - if (!sessionAborted) { - apply(activities[getRandom(activities.size())], use); - interval(intervals); + final checkEval = { expr -> + show("checking $expr evaluates"); + eval(expr); + }; + DOMME.sessions.forEach { session -> + checkEval(session.require ?: true); + checkEval(session.probability); + session.phases.forEach { phase -> + checkEval(phase.require ?: true); + checkIsFunc("select", phase.select); + checkEval(phase.number ?: 1); + phase.activities.forEach { a -> + checkIsFunc("activities", a); } - }; - }, - 'take': { amount, activities, intervals, use = null -> - Collections.shuffle(activities); - activities.take(amount).each { - if (!sessionAborted) { - apply(it, use); - interval(intervals); + (phase.intervals ?: []).forEach { i -> + checkIsFunc("activities", i); } + checkIsFunc("use", phase.use); }; - } - ], - 'activities': activityList - ]; - final eval = { expr -> Eval.me('toy', playScope, expr.toString()) }; - if (is("DEBUG")) { - // Check everything in DOMME evaluates and resolves - final checkIsFunc = { group, name -> - if (!name) return; - show("checking $group:$name is a function"); - if (!(funcMap[group][name] instanceof Closure)) { - showPopup("$group:$name is not a function"); - } + }; } - final checkEval = { expr -> - show("checking $expr evaluates"); - eval(expr); + final sessions = DOMME.sessions.findAll { s -> eval(s.require ?: true) }; + final probabilities = sessions.withIndex().collect { s, idx -> [idx] * s.probability }.sum(); + final sessionIdx = probabilities[getRandom(probabilities.size)]; + sessions[sessionIdx].phases.forEach { phase -> + if (eval(phase.require ?: true)) { + funcMap.select[phase.select ?: 'take'](eval(phase.number ?: 1), + (phase.activities ?: []).collect { f -> funcMap.activities[f] }, + (phase.intervals ?: []).collect { f -> funcMap.activities[f] }, + funcMap.use[phase.use]); + } }; - DOMME.sessions.forEach { session -> - checkEval(session.require ?: true); - checkEval(session.probability); - session.phases.forEach { phase -> - checkEval(phase.require ?: true); - checkIsFunc("select", phase.select); - checkEval(phase.number ?: 1); - phase.activities.forEach { a -> - checkIsFunc("activities", a); + }; + def sessionRelease = { goodToy -> + if (goodToy) { + present([DRESSED], [ + ["Ahhh,", "OK,"], + ["I'm done with you for now.", "That was fun... for me at least."]]); + } + else { + present([DRESSED], [ + ["I don't ask much of you, toy."]]); + wait(getRandom(10) + 5); + postChastity(); + } + wait(getRandom(10) + 5); + def toys = TOYTOYS.findAll { t -> is(t) }; + Collections.shuffle(toys); + if (toys) { + if (!goodToy) { + present([DRESSED], [ + ["I should leave you like that.", "I suppose you still want letting free?"], + ["Maybe.", "Let me ponder on it."]]); + wait(getRandom(20) + 20); + } + toys.each { toy -> + if (getRandom(100) > ((goodToy && (getPunish() < 100)) ? 10 : 80)) { + removeToy(toy, [DRESSED]); } - (phase.intervals ?: []).forEach { i -> - checkIsFunc("activities", i); + else { + addEvent("$RELEASEFROM-$toy", getTime() + 100 + getRandom(10 * (getPunish() + 1)), + RELEASEFROM, toy); + adjustPunish(-5); } - checkIsFunc("use", phase.use); }; - }; - } - final sessions = DOMME.sessions.findAll { s -> eval(s.require ?: true) }; - final probabilities = sessions.withIndex().collect { s, idx -> [idx] * s.probability }.sum(); - final sessionIdx = probabilities[getRandom(probabilities.size)]; - sessions[sessionIdx].phases.forEach { phase -> - if (eval(phase.require ?: true)) { - funcMap.select[phase.select ?: 'take'](eval(phase.number ?: 1), - (phase.activities ?: []).collect { f -> funcMap.activities[f] }, - (phase.intervals ?: []).collect { f -> funcMap.activities[f] }, - funcMap.use[phase.use]); - } - }; -}; -def sessionRelease = { goodToy -> - if (goodToy) { - present([DRESSED], [ - ["Ahhh,", "OK,"], - ["I'm done with you for now.", "That was fun... for me at least."]]); - } - else { - present([DRESSED], [ - ["I don't ask much of you, toy."]]); - wait(getRandom(10) + 5); - postChastity(); - } - wait(getRandom(10) + 5); - def toys = TOYTOYS.findAll { t -> is(t) }; - Collections.shuffle(toys); - if (toys) { - if (!goodToy) { - present([DRESSED], [ - ["I should leave you like that.", "I suppose you still want letting free?"], - ["Maybe.", "Let me ponder on it."]]); - wait(getRandom(20) + 20); } - toys.each { toy -> - if (getRandom(100) > ((goodToy && (getPunish() < 100)) ? 10 : 80)) { - removeToy(toy, [DRESSED]); + if (is(NAKED)) { + if (goodToy && getRandom(1)) { + present([DRESSED], [ + ["Put some clothes back on.", "Cover yourself up!"]]); + wait(getRandom(10) + 15); + set(NAKED, false); } else { - addEvent("$RELEASEFROM-$toy", getTime() + 100 + getRandom(10 * (getPunish() + 1)), - RELEASEFROM, toy); - adjustPunish(-5); + present([DRESSED], [ + ["No clothes now for a while,", "Keep those clothes off,"], + ["you don't get clothes until I say.", "I like seeing you naked."]]); + addEvent(REDRESS, getTime() + 600 + getRandom(1200), "redress"); + showButtonG("Yes, ${dommeTitle()}", "ok"); } - }; - } - if (is(NAKED)) { - if (goodToy && getRandom(1)) { + } + if (goodToy) { present([DRESSED], [ - ["Put some clothes back on.", "Cover yourself up!"]]); - wait(getRandom(10) + 15); - set(NAKED, false); + ["Off you go.", "You may leave."]]); } else { present([DRESSED], [ - ["No clothes now for a while,", "Keep those clothes off,"], - ["you don't get clothes until I say.", "I like seeing you naked."]]); - addEvent(REDRESS, getTime() + 600 + getRandom(1200), "redress"); - showButtonG("Yes, ${dommeTitle()}", "ok"); + ["Get out of my sight.", "Go!"]]); + removeEvent(CUM); + revokePermission(CUM); } - } - if (goodToy) { - present([DRESSED], [ - ["Off you go.", "You may leave."]]); - } - else { - present([DRESSED], [ - ["Get out of my sight.", "Go!"]]); - removeEvent(CUM); - revokePermission(CUM); - } - wait(10); - save("toy.lastPlay", getTime()); - showLounge(); - sessionAborted = null; - sessionToys = [:]; - if (goodToy) { - adjustPunish(-20); - } - show(null); -}; -def playActivity = { - final keys = new ArrayList(activityList.keySet()); - final act = getSelectedValue("Choose activity", keys); - activityList[keys[act]](); -}; -def playWait = { - present([DRESSED,nTEASE], [ - ["Hello,", "Hi,", "Hey there,"], - ["toy.", "slut.", "slave."], - ["On your knees,", "On the floor,", "Down... at my feet,",], - ["wait there.", "wait patiently.", "don't move."], - ["I'll be with you shortly.", "I won't be a moment."]]); - showButtonGT("Yes, ${dommeTitle()}", "ok", 10, 1); - showLounge(); - show(null); - wait(20 + getRandom(20)); -}; -def playSchedule = { - if (getAway()) return; - final lastPlay = loadInteger("toy.lastPlay") ?: 0; - if (lastPlay < getTime() - (4 * HOUR)) { - addEventIfMissing(PLAY, getTime() + 120 + getRandom(480), PLAY, true); // 2-10mins - } - else { - addEventIfMissing(PLAY, getTime() + 1200 + getRandom(5400), PLAY, false); // 20-90mins - } -}; -def playEvent = { rt = true, first = true -> - if (!rt || getAway()) return; - dress([[DRESSED,nTEASE],[DRESSED,TEASE],[TITS],[STOOD]]); - if (sessionSummon([DRESSED,nTEASE])) { - if (first.asBoolean()) { - playWait(); - } - sessionPlay(); - sessionRelease(sessionAborted == null); - } - else { - adjustPunish(25); - } - playSchedule(); - showLounge(); -} -def sleepSchedule = { - def t = (int)(getDay() - localTimeOffset() + (25.1 * HOUR) + getRandom((int)HOUR)); - addEventIfMissing(SLEEPING, t, BEDTIME); -}; -def bedtime = { rt, schedTime -> - dress([[LINGERIE,nTITS]]); - if (rt && sessionSummon([LINGERIE,nTITS])) { - preStrip([LINGERIE,nTITS]); - def toys = TOYTOYS.findAll { t -> is(t) }; - Collections.shuffle(toys); - toys.each { removeToy(it, [LINGERIE,nTITS]) }; - present([LINGERIE,nTITS], [ - ["Very good, toy.", "OK."], - ["Bedtime for you!", "Off to bed with you!", "Bed! Now!"]]); - showButtonG("Good night, ${dommeTitle()}", "night"); - addEvent(BEDTIMECHECK, getTime() + 300 + getRandom(600), BEDTIMECHECK); + wait(10); + save("toy.lastPlay", getTime()); showLounge(); - } - // Assume toy will return dressed tomorrow - removeEvent(REDRESS); - set(NAKED, false); - // Assume toy will release himself - TOYTOYS.findAll { t -> is(t) }.each { - removeEvent("$RELEASEFROM-$it"); - set(it, false); - }; - setAway(SLEEPING); - def t = schedTime + (6 * (int)HOUR) + getRandom(2 * (int)HOUR); - addEvent(SLEEPING, t, WAKEUP); - removeEvent(PLAY); -}; -def bedtimeCheck = { rt -> - dress([[LINGERIE,nTITS]]); - if (rt && sessionSummon([LINGERIE,nTITS])) { - present([LINGERIE,nTITS], [ - ["Hey!", "Ah ha!"], - ["What are you doing still up?", "Didn't I send you to bed?"]]); - showButtonG("Sorry, ${dommeTitle()}", "sorry"); - present([LINGERIE,nTITS], [ - ["Get yourself to bed", "In bed"], - ["right now!", "this instant!"], - ["I'll deal with you later."]]); - showButtonG("Yes, ${dommeTitle()}", "ok"); - adjustPunish(75); + sessionAborted = null; + sessionToys = [:]; + if (goodToy) { + adjustPunish(-20); + } + show(null); + }; + def playActivity = { + final keys = new ArrayList(activityList.keySet()); + final act = getSelectedValue("Choose activity", keys); + activityList[keys[act]](); + }; + def playWait = { + present([DRESSED,nTEASE], [ + ["Hello,", "Hi,", "Hey there,"], + ["toy.", "slut.", "slave."], + ["On your knees,", "On the floor,", "Down... at my feet,",], + ["wait there.", "wait patiently.", "don't move."], + ["I'll be with you shortly.", "I won't be a moment."]]); + showButtonGT("Yes, ${dommeTitle()}", "ok", 10, 1); showLounge(); - } -}; -def wakeup = { rt -> - // Unset outfit, stops bedtime wear always being selected first - save("toy.owner.outfit", null); - dress([]); - setAway(null); - playSchedule(); - sleepSchedule(); -}; -def redress = { rt -> - if (!rt || !sessionSummon([DRESSED])) { - addEvent(REDRESS, getTime() + 300, "redress"); - return; - } - present([DRESSED], [ - ["OK, toy,"], - ["you can put some clothes back on now.", "get yourself covered up."]]); - wait(getRandom(10) + 15); - showButtonG("Thank you, ${dommeTitle()}", "ok"); - set(NAKED, false); - showLounge(); -}; -def releaseFrom = { rt, arg, name -> - if (!rt || !sessionSummon([DRESSED])) { - addEvent(name, getTime() + 300, RELEASEFROM, arg); - return; - } - removeToy(arg, [DRESSED]); - showLounge(); -}; -def requestClothes = { - adjustPunish(5); - present([DRESSED], [ - ["Really?", "Disappointing."], - ["You want to clothes back on?", "You wish to cover up?"]]); - if (getBoolean(null)) { - present([DRESSED], [["Hmm, very well.", "If you must."]]); - adjustPunish(50); - set(NAKED, false); - removeEvent(REDRESS); - } - else { - present([DRESSED], [["Good boy."]]); - } - showButtonG("Thank you, ${dommeTitle()}", "ok"); - showLounge(); -}; -def requestRelease = { toy -> - adjustPunish(5); - present([DRESSED], [ - ["Really?", "Disappointing."], - ["You wish to be un-$toy?"]]); - if (getBoolean(null)) { - adjustPunish(10); - if (getRandom(100) > getPunish()) { - present([DRESSED], [["No.", "You may not."], - ["Not yet.", "Not until later."]]); + show(null); + wait(20 + getRandom(20)); + }; + def playSchedule = { + if (getAway()) return; + final lastPlay = loadInteger("toy.lastPlay") ?: 0; + if (lastPlay < getTime() - (4 * HOUR)) { + addEventIfMissing(PLAY, getTime() + 120 + getRandom(480), PLAY, true); // 2-10mins } else { - present([DRESSED], [["Hmm, very well.", "If you must."]]); - set(toy, false); - removeEvent("$RELEASEFROM-$toy"); + addEventIfMissing(PLAY, getTime() + 1200 + getRandom(5400), PLAY, false); // 20-90mins } - } - else { - present([DRESSED], [["Good boy."]]); - } - showButtonG("Thank you, ${dommeTitle()}", "ok"); - showLounge(); -}; -// Confession -def confessChastityRelease = { - adjustPunish(75); - present([DRESSED, nTEASE], [ - ["Bad toy!", "Naughty little slut."], - ["I don't keep it locked for nothing.", "It's locked for a reason."], - ["It better have been a good reason.", "I'll presume you had a valid excuse."], - ["Is it locked again now?", "Are you back in chastity now?"]]); - if (getBoolean(null)) { - present([DRESSED, nTEASE], [ - ["Good!", "There's that at least."]]); - showButtonG("Yes, ${dommeTitle()}", "ok"); - } - else { - adjustPunish(300); - present([DRESSED, nTEASE], [ - ["disobedient", "dirty", "cheeky"], - ["little"], - ["slut!", "whore!", "brat!"], - ["Get it locked back up", "Back in chastity"], - ["right now!", "this instant!"]]); - showButtonGT("Locked, ${dommeTitle()}. Here's the key.", "locked", 180, 1); - } - if (getBoolean("Did you get erect whilst out?")) { - adjustPunish(50); - if (getBoolean("Did you cum!?")) { - adjustPunish(200); + }; + def playEvent = { rt = true, first = true -> + if (!rt || getAway()) return; + dress([[DRESSED,nTEASE],[DRESSED,TEASE],[TITS],[STOOD]]); + if (sessionSummon([DRESSED,nTEASE])) { + if (first.asBoolean()) { + playWait(); + } + sessionPlay(); + sessionRelease(sessionAborted == null); } - } - adjustPunish(getInteger("How many hours were you out?", 1) * 10); -}; -def confessStroked = { - adjustPunish(40); - present([DRESSED,nTEASE], [ - ["Awww, poor little toy.", "Naught little toy!"], - ["You know you aren't allowed.", "You get to stroke only when I say."]]); - showButtonG("Sorry, ${dommeTitle()}", "sorry"); - if (getBoolean("Did you get to the edge?")) { - adjustPunish(40); - adjustPunish(getInteger("How many times?", 1) * 5); - if (getBoolean("Did you cum!?")) { - adjustPunish(200); + else { + adjustPunish(25); } + playSchedule(); + showLounge(); } -}; -def confess = { - // 5 points just for feeling the need to confess - def op = getPunish(); - adjustPunish(5); - dress([[DRESSED,nTEASE], [MEAN]]); - while (true) { - present([DRESSED, nTEASE], [ - ["I'm not going to like this, am I?", "There is no way this ends well for you."], - ["Out with it.", "Let me hear it."]]); - def opts = [ ]; - if (is(CHASTE)) { - opts.push([lbl: "Freed from chastity", act: confessChastityRelease]); - } - if (!is(CHASTE)) { - opts.push([lbl: "Stroked", act: confessStroked]); - } - opts.push([ lbl: "Nothing listed (phew!)", act: null ]); - def opt = getSelectedValue(null, opts.collect { it.lbl }); - def act = opts[opt].act; - if (!act) { + def sleepSchedule = { + def t = (int)(getDay() - localTimeOffset() + (25.1 * HOUR) + getRandom((int)HOUR)); + addEventIfMissing(SLEEPING, t, BEDTIME); + }; + def bedtime = { rt, schedTime -> + dress([[LINGERIE,nTITS]]); + if (rt && sessionSummon([LINGERIE,nTITS])) { + preStrip([LINGERIE,nTITS]); + def toys = TOYTOYS.findAll { t -> is(t) }; + Collections.shuffle(toys); + toys.each { removeToy(it, [LINGERIE,nTITS]) }; + present([LINGERIE,nTITS], [ + ["Very good, toy.", "OK."], + ["Bedtime for you!", "Off to bed with you!", "Bed! Now!"]]); + showButtonG("Good night, ${dommeTitle()}", "night"); + addEvent(BEDTIMECHECK, getTime() + 300 + getRandom(600), BEDTIMECHECK); showLounge(); - return; } - act(); - if (!getBoolean("Anything else?")) break; - } - // If sufficient points accrued, punish immediately.. with a bonus - if (getPunish() > 150 + op) { - present([MEAN], [ - ["I'm very disappointed in you.", "You've been a very bad toy!"], - ["Immediate punishment!", "So you will suffer... right now!"]]); - adjustPunish(100); - showButtonG("Yes, ${dommeTitle()}", "ok"); + // Assume toy will return dressed tomorrow + removeEvent(REDRESS); + set(NAKED, false); + // Assume toy will release himself + TOYTOYS.findAll { t -> is(t) }.each { + removeEvent("$RELEASEFROM-$it"); + set(it, false); + }; + setAway(SLEEPING); + def t = schedTime + (6 * (int)HOUR) + getRandom(2 * (int)HOUR); + addEvent(SLEEPING, t, WAKEUP); removeEvent(PLAY); - sessionPlay(); - sessionRelease(sessionAborted == null); + }; + def bedtimeCheck = { rt -> + dress([[LINGERIE,nTITS]]); + if (rt && sessionSummon([LINGERIE,nTITS])) { + present([LINGERIE,nTITS], [ + ["Hey!", "Ah ha!"], + ["What are you doing still up?", "Didn't I send you to bed?"]]); + showButtonG("Sorry, ${dommeTitle()}", "sorry"); + present([LINGERIE,nTITS], [ + ["Get yourself to bed", "In bed"], + ["right now!", "this instant!"], + ["I'll deal with you later."]]); + showButtonG("Yes, ${dommeTitle()}", "ok"); + adjustPunish(75); + showLounge(); + } + }; + def wakeup = { rt -> + // Unset outfit, stops bedtime wear always being selected first + save("toy.owner.outfit", null); + dress([]); + setAway(null); playSchedule(); - } - else { - present([DRESSED, nTEASE], [ - ["Good!", "There better not be!"]]); - showButtonG("Yes, ${dommeTitle()}", "ok"); + sleepSchedule(); + }; + def redress = { rt -> + if (!rt || !sessionSummon([DRESSED])) { + addEvent(REDRESS, getTime() + 300, "redress"); + return; + } + present([DRESSED], [ + ["OK, toy,"], + ["you can put some clothes back on now.", "get yourself covered up."]]); + wait(getRandom(10) + 15); + showButtonG("Thank you, ${dommeTitle()}", "ok"); + set(NAKED, false); showLounge(); - } -}; - -// Setup -// Plans - cannot overlap -def setupPlan = { plan, float startmin, float startmax, float endmin, float endmax -> - def timeWindow = { float min, float max -> (int)((min * HOUR) - localTimeOffset() + getRandom((int)((max - min) * HOUR))) }; - final friendName = FRIENDS[getRandom(FRIENDS.size())]; - def day = getDay() + ((getRandom(3) + 1) * DAY); - addEventIfMissing(plan, day + timeWindow(startmin, startmax), BEGINPLAN, - [friendName: friendName, returnTime: day + timeWindow(endmin, endmax)]); -} -def setupPlans = { - save("toy.plan", null); - 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 setupEvents = { - playSchedule(); - sleepSchedule(); - setupPlans(); -}; -def setDefault = { prop, val -> - if (loadString(prop) == null) { - save(prop, val); - } -}; -def leaveNote = { msg, instr -> - save("toy.note", msg); - save("toy.noteInstr", instr); -}; -def readNote = { - leaveNote(null, null); -}; -def plannedCheck = { plan -> - return (getTime() > loadInteger("toy.plan.${plan}.${START}") && getTime() < loadInteger("toy.plan.${plan}.${END}")); -} -def beginPlan = { rt, plan, args -> - addEvent(plan, args.returnTime, ENDPLAN, args.friendName); - dress([[DRESSED,nTEASE], [DRESSED,TEASE]]); - if (rt && sessionSummon([DRESSED,nTEASE])) { - present([DRESSED,nTEASE], [ - [ "I'm going out now, toy.", "$plan time!" ], - [ "Will you be good while I'm gone?", "You will behave as I expect?" ]]); - wait(10); - postChastity(); - def toys = TOYTOYS.findAll { t -> is(t) }; - Collections.shuffle(toys); - toys.each { removeToy(it, [DRESSED,nTEASE]) }; - present([DRESSED,TEASE], [ - [ "Back later!", "See you soon, toy" ]]); - wait(5); - } - else if (has(CHASTITY) && !is(CHASTE)) { - leaveNote("I want you in chastity until I return!", CHASTE); - } - DOMME = loadDomme(OWNER); - setAway(plan); - removeEvent(PLAY); - showLounge(); -}; -def endPlan = { rt, plan -> - save("toy.owner.outfitTime", getTime()); - setAway(null); - if (rt && sessionSummon([DRESSED])) { - present([DRESSED], [["Hello, toy!"], ["I'm back home now."]]); - wait(10); + }; + def releaseFrom = { rt, arg, name -> + if (!rt || !sessionSummon([DRESSED])) { + addEvent(name, getTime() + 300, RELEASEFROM, arg); + return; + } + removeToy(arg, [DRESSED]); showLounge(); - } - readNote(); - playSchedule(); - setupPlans(); -}; -// Initial - basic checks and defaults -namedEvents = [ - bedtime: { name, arg, schedTime, rt -> bedtime(rt, schedTime) }, - bedtimeCheck: { name, arg, schedTime, rt -> bedtimeCheck(rt) }, - wakeup: { name, arg, schedTime, rt -> wakeup(rt) }, - beginPlan: { name, arg, schedTime, rt -> beginPlan(rt, name, arg) }, - endPlan: { name, arg, schedTime, rt -> endPlan(rt, name) }, - play: { name, arg, schedTime, rt -> playEvent(rt, arg) }, - releaseFrom: { name, arg, schedTime, rt -> releaseFrom(rt, arg, name) }, - redress: { name, arg, schedTime, rt -> redress(rt) }, - permit: { name, arg, schedTime, rt -> givePermission(arg) } -]; -def setupInitial = { - if (loadInteger("toy.version") == null) { - // Welcome - dress([[DRESSED,nTEASE], [DRESSED,TEASE], [TITS]]); - def name = loadString("intro.name"); - present([DRESSED,nTEASE], [ - ["Welcome, $name, to your new life as my plaything, my toy."]]); - showButton("Thank you, ${dommeTitle()}"); - present([DRESSED,nTEASE], [ - ["My rules are simple, do what I say, when I say, and you'll be rewarded... eventually."]]); - showButton("Thank you, ${dommeTitle()}"); - present([DRESSED,nTEASE], [ - ["But if you disappointment me, disobey me or disrespect me, then you'll be punished."]]); - showButton("Yes, ${dommeTitle()}"); - present([DRESSED,TEASE], [ - ["I will tease you... and I will torment you..."]]); - showButton("Yes, ${dommeTitle()}"); - present([TITS], [ - ["... and I will break... and then you will suffer!"]]); - showButton("Thank you, ${dommeTitle()}"); - present([TITS], [ - ["Now, your devotion is important to me."], - ["I have expectations my toy be available to play with as much as possible."]]); - showButton("Yes, ${dommeTitle()}"); - present([TITS], [ - ["You can indicate your availability by running this script."], - ["I will summon you when I wish to play."]]); - showButton("Understood, ${dommeTitle()}"); - present([DRESSED,TEASE], [ - ["Would you like to set this script as default?"]]); + }; + def requestClothes = { + adjustPunish(5); + present([DRESSED], [ + ["Really?", "Disappointing."], + ["You want to clothes back on?", "You wish to cover up?"]]); if (getBoolean(null)) { - save("intro.start_script", "toy"); - present([DRESSED,nTEASE], [ - ["Good boy."]]); - showButton("Thank you, ${dommeTitle()}"); + present([DRESSED], [["Hmm, very well.", "If you must."]]); + adjustPunish(50); + set(NAKED, false); + removeEvent(REDRESS); + } + else { + present([DRESSED], [["Good boy."]]); + } + showButtonG("Thank you, ${dommeTitle()}", "ok"); + showLounge(); + }; + def requestRelease = { toy -> + adjustPunish(5); + present([DRESSED], [ + ["Really?", "Disappointing."], + ["You wish to be un-$toy?"]]); + if (getBoolean(null)) { + adjustPunish(10); + if (getRandom(100) > getPunish()) { + present([DRESSED], [["No.", "You may not."], + ["Not yet.", "Not until later."]]); + } + else { + present([DRESSED], [["Hmm, very well.", "If you must."]]); + set(toy, false); + removeEvent("$RELEASEFROM-$toy"); + } + } + else { + present([DRESSED], [["Good boy."]]); } + showButtonG("Thank you, ${dommeTitle()}", "ok"); + showLounge(); + }; + // Confession + def confessChastityRelease = { + adjustPunish(75); + present([DRESSED, nTEASE], [ + ["Bad toy!", "Naughty little slut."], + ["I don't keep it locked for nothing.", "It's locked for a reason."], + ["It better have been a good reason.", "I'll presume you had a valid excuse."], + ["Is it locked again now?", "Are you back in chastity now?"]]); + if (getBoolean(null)) { + present([DRESSED, nTEASE], [ + ["Good!", "There's that at least."]]); + showButtonG("Yes, ${dommeTitle()}", "ok"); + } + else { + adjustPunish(300); + present([DRESSED, nTEASE], [ + ["disobedient", "dirty", "cheeky"], + ["little"], + ["slut!", "whore!", "brat!"], + ["Get it locked back up", "Back in chastity"], + ["right now!", "this instant!"]]); + showButtonGT("Locked, ${dommeTitle()}. Here's the key.", "locked", 180, 1); + } + if (getBoolean("Did you get erect whilst out?")) { + adjustPunish(50); + if (getBoolean("Did you cum!?")) { + adjustPunish(200); + } + } + adjustPunish(getInteger("How many hours were you out?", 1) * 10); + }; + def confessStroked = { + adjustPunish(40); present([DRESSED,nTEASE], [ - ["Run along now, toy."], - ["I'll summon you with a bell when I want you."], - ["Lisen carefully!"]]); - showButton("Yes, ${dommeTitle()}"); - present(null, [ - ["All images are the property of their respective owners, see watermarks (OnlyTease, ForeverVamp).\n"], - ["Don't do anything you aren't 100% comfortable doing.\n"], - ["Be sure to have configured your toys!"]]); - showButton("OK"); - } - save("toy.version", VERSION); - setDefault("toy.strokeTeaseOffset", 0); - setDefault("toy.punishment", 0); - execEvents(false); - setupEvents(); -}; -def setupShowState = { - show( - [CHASTE, COLLARED, CUFFED, CLAMPED, GAGGED, NAKED].collect { - def v = is(it); - return "$it: $v\n".capitalize() - }.join()); - showButton("OK"); - show( - [BALLGAG, COLLAR, CLAMPS, CHASTITY, HANDCUFFS, DILDO].collect { - def v = has(it); - return "$it: $v\n".capitalize() - }.join()); - showButton("OK"); - show( - [BONDAGE, CBT, CHORES, PAIN].collect { - def v = likes(it); - return "$it: $v\n".capitalize() - }.join()); - showButton("OK"); -}; -def showFaq = { - useUrl("http://toy.randomdan.homeip.net/"); -}; -def setupShowCalendar = { - final events = loadEvents(); - show( - [LUNCH, SHOPPING, PARTY].collect { - final event = events[it]; - if (!event) return ""; - def s = event.time; - def f = event.arg.friendName; - def timeStr = localTimeOf(s).format(soonFormatter); - return "$it with $f on $timeStr.\n".capitalize(); - }.join()); - showButton("OK"); -}; - -// GO! -setDefault("toy.owner", "ancilla"); -OWNER = loadString("toy.owner"); -DOMME = loadDomme(OWNER); -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", (int)(getAvail(dayNum) + amount)); -}; -def tidyAvail = { - def p = load("toy.availability"); - if (!p) return; - def p2 = [:]; - final firstDay = getDayNum() - 20; - p.eachWithIndex{ avail, day -> - if (day >= firstDay && avail > 0) { - p2["$day"] = avail; + ["Awww, poor little toy.", "Naught little toy!"], + ["You know you aren't allowed.", "You get to stroke only when I say."]]); + showButtonG("Sorry, ${dommeTitle()}", "sorry"); + if (getBoolean("Did you get to the edge?")) { + adjustPunish(40); + adjustPunish(getInteger("How many times?", 1) * 5); + if (getBoolean("Did you cum!?")) { + adjustPunish(200); + } } - } - save("toy.availability", p2); -}; -setupInitial(); -tidyAvail(); - -final cycleTime = 60; -showLounge(); -while (true) { - def away = getAway(); - def e = nextEvent(loadEvents()); - if (!e) return "toy"; // Force restart, should create some events - - if (away) { - def note = loadString("toy.note"); - def noteInstr = loadString("toy.noteInstr"); - if (note) { - show("${dommeTitle()} ${dommeName()} is away ($away)... but you find a note:\n$note"); - showButton("OK"); - if (noteInstr) { - set(noteInstr, true); + }; + def confess = { + // 5 points just for feeling the need to confess + def op = getPunish(); + adjustPunish(5); + dress([[DRESSED,nTEASE], [MEAN]]); + while (true) { + present([DRESSED, nTEASE], [ + ["I'm not going to like this, am I?", "There is no way this ends well for you."], + ["Out with it.", "Let me hear it."]]); + def opts = [ ]; + if (is(CHASTE)) { + opts.push([lbl: "Freed from chastity", act: confessChastityRelease]); } - readNote(); + if (!is(CHASTE)) { + opts.push([lbl: "Stroked", act: confessStroked]); + } + opts.push([ lbl: "Nothing listed (phew!)", act: null ]); + def opt = getSelectedValue(null, opts.collect { it.lbl }); + def act = opts[opt].act; + if (!act) { + showLounge(); + return; + } + act(); + if (!getBoolean("Anything else?")) break; + } + // If sufficient points accrued, punish immediately.. with a bonus + if (getPunish() > 150 + op) { + present([MEAN], [ + ["I'm very disappointed in you.", "You've been a very bad toy!"], + ["Immediate punishment!", "So you will suffer... right now!"]]); + adjustPunish(100); + showButtonG("Yes, ${dommeTitle()}", "ok"); + removeEvent(PLAY); + sessionPlay(); + sessionRelease(sessionAborted == null); + playSchedule(); } else { - show("${dommeTitle()} ${dommeName()} is away ($away)..."); + present([DRESSED, nTEASE], [ + ["Good!", "There better not be!"]]); + showButtonG("Yes, ${dommeTitle()}", "ok"); + showLounge(); } - if (e && e.event && e.event.time > getTime()) { - wait(e.event.time - getTime()); + }; + + // Setup + // Plans - cannot overlap + def setupPlan = { plan, float startmin, float startmax, float endmin, float endmax -> + def timeWindow = { float min, float max -> (int)((min * HOUR) - localTimeOffset() + getRandom((int)((max - min) * HOUR))) }; + final friendName = FRIENDS[getRandom(FRIENDS.size())]; + def day = getDay() + ((getRandom(3) + 1) * DAY); + addEventIfMissing(plan, day + timeWindow(startmin, startmax), BEGINPLAN, + [friendName: friendName, returnTime: day + timeWindow(endmin, endmax)]); + } + def setupPlans = { + save("toy.plan", null); + 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 setupEvents = { + playSchedule(); + sleepSchedule(); + setupPlans(); + }; + def setDefault = { prop, val -> + if (loadString(prop) == null) { + save(prop, val); } + }; + def leaveNote = { msg, instr -> + save("toy.note", msg); + save("toy.noteInstr", instr); + }; + def readNote = { + leaveNote(null, null); + }; + def plannedCheck = { plan -> + return (getTime() > loadInteger("toy.plan.${plan}.${START}") && getTime() < loadInteger("toy.plan.${plan}.${END}")); } - else { - if (is("DEBUG")) { - def timeStr = localTimeOf(e.event.time).format(soonFormatter); - show("$e.name: $timeStr -> ${e.event.func}(${e.event.arg})"); + def beginPlan = { rt, plan, args -> + addEvent(plan, args.returnTime, ENDPLAN, args.friendName); + dress([[DRESSED,nTEASE], [DRESSED,TEASE]]); + if (rt && sessionSummon([DRESSED,nTEASE])) { + present([DRESSED,nTEASE], [ + [ "I'm going out now, toy.", "$plan time!" ], + [ "Will you be good while I'm gone?", "You will behave as I expect?" ]]); + wait(10); + postChastity(); + def toys = TOYTOYS.findAll { t -> is(t) }; + Collections.shuffle(toys); + toys.each { removeToy(it, [DRESSED,nTEASE]) }; + present([DRESSED,TEASE], [ + [ "Back later!", "See you soon, toy" ]]); + wait(5); } - else { - show("${dommeTitle()} ${dommeName()} will summon you when required."); - } - final waitTime = Math.min(cycleTime, e.event.time - getTime()); - final clickTime = showButton("Please, ${dommeTitle()}?", waitTime); - if (clickTime < waitTime) { - def opts = [ - [ lbl: "FAQ (webpage)", act: showFaq ], - [ lbl: "Calendar", act: setupShowCalendar ], - [ lbl: "Confess", act: confess ], - ]; - if (is("DEBUG")) { - opts.push([ lbl: "Status", act: setupShowState ]); - opts.push([ lbl: "Play", act: playEvent, arg: true ]); - opts.push([ lbl: "Activity", act: playActivity ]); + else if (has(CHASTITY) && !is(CHASTE)) { + leaveNote("I want you in chastity until I return!", CHASTE); + } + DOMME = loadDomme(OWNER); + setAway(plan); + removeEvent(PLAY); + showLounge(); + }; + def endPlan = { rt, plan -> + save("toy.owner.outfitTime", getTime()); + setAway(null); + if (rt && sessionSummon([DRESSED])) { + present([DRESSED], [["Hello, toy!"], ["I'm back home now."]]); + wait(10); + showLounge(); + } + readNote(); + playSchedule(); + setupPlans(); + }; + // Initial - basic checks and defaults + def namedEvents = [ + bedtime: { name, arg, schedTime, rt -> bedtime(rt, schedTime) }, + bedtimeCheck: { name, arg, schedTime, rt -> bedtimeCheck(rt) }, + wakeup: { name, arg, schedTime, rt -> wakeup(rt) }, + beginPlan: { name, arg, schedTime, rt -> beginPlan(rt, name, arg) }, + endPlan: { name, arg, schedTime, rt -> endPlan(rt, name) }, + play: { name, arg, schedTime, rt -> playEvent(rt, arg) }, + releaseFrom: { name, arg, schedTime, rt -> releaseFrom(rt, arg, name) }, + redress: { name, arg, schedTime, rt -> redress(rt) }, + permit: { name, arg, schedTime, rt -> givePermission(arg) } + ]; + def setupInitial = { + if (loadInteger("toy.version") == null) { + // Welcome + dress([[DRESSED,nTEASE], [DRESSED,TEASE], [TITS]]); + def name = loadString("intro.name"); + present([DRESSED,nTEASE], [ + ["Welcome, $name, to your new life as my plaything, my toy."]]); + showButton("Thank you, ${dommeTitle()}"); + present([DRESSED,nTEASE], [ + ["My rules are simple, do what I say, when I say, and you'll be rewarded... eventually."]]); + showButton("Thank you, ${dommeTitle()}"); + present([DRESSED,nTEASE], [ + ["But if you disappointment me, disobey me or disrespect me, then you'll be punished."]]); + showButton("Yes, ${dommeTitle()}"); + present([DRESSED,TEASE], [ + ["I will tease you... and I will torment you..."]]); + showButton("Yes, ${dommeTitle()}"); + present([TITS], [ + ["... and I will break... and then you will suffer!"]]); + showButton("Thank you, ${dommeTitle()}"); + present([TITS], [ + ["Now, your devotion is important to me."], + ["I have expectations my toy be available to play with as much as possible."]]); + showButton("Yes, ${dommeTitle()}"); + present([TITS], [ + ["You can indicate your availability by running this script."], + ["I will summon you when I wish to play."]]); + showButton("Understood, ${dommeTitle()}"); + present([DRESSED,TEASE], [ + ["Would you like to set this script as default?"]]); + if (getBoolean(null)) { + save("intro.start_script", "toy"); + present([DRESSED,nTEASE], [ + ["Good boy."]]); + showButton("Thank you, ${dommeTitle()}"); + } + present([DRESSED,nTEASE], [ + ["Run along now, toy."], + ["I'll summon you with a bell when I want you."], + ["Lisen carefully!"]]); + showButton("Yes, ${dommeTitle()}"); + present(null, [ + ["All images are the property of their respective owners, see watermarks (OnlyTease, ForeverVamp).\n"], + ["Don't do anything you aren't 100% comfortable doing.\n"], + ["Be sure to have configured your toys!"]]); + showButton("OK"); + } + save("toy.version", VERSION); + setDefault("toy.strokeTeaseOffset", 0); + setDefault("toy.punishment", 0); + execEvents(false); + setupEvents(); + }; + def setupShowState = { + show( + [CHASTE, COLLARED, CUFFED, CLAMPED, GAGGED, NAKED].collect { + def v = is(it); + return "$it: $v\n".capitalize() + }.join()); + showButton("OK"); + show( + [BALLGAG, COLLAR, CLAMPS, CHASTITY, HANDCUFFS, DILDO].collect { + def v = has(it); + return "$it: $v\n".capitalize() + }.join()); + showButton("OK"); + show( + [BONDAGE, CBT, CHORES, PAIN].collect { + def v = likes(it); + return "$it: $v\n".capitalize() + }.join()); + showButton("OK"); + }; + def showFaq = { + useUrl("http://toy.randomdan.homeip.net/"); + }; + def setupShowCalendar = { + final events = loadEvents(); + show( + [LUNCH, SHOPPING, PARTY].collect { + final event = events[it]; + if (!event) return ""; + def s = event.time; + def f = event.arg.friendName; + def timeStr = localTimeOf(s).format(soonFormatter); + return "$it with $f on $timeStr.\n".capitalize(); + }.join()); + showButton("OK"); + }; + + String main() + { + // GO! + setDefault("toy.owner", "ancilla"); + OWNER = loadString("toy.owner"); + DOMME = loadDomme(OWNER); + 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", (int)(getAvail(dayNum) + amount)); + }; + def tidyAvail = { + def p = load("toy.availability"); + if (!p) return; + def p2 = [:]; + final firstDay = getDayNum() - 20; + p.eachWithIndex{ avail, day -> + if (day >= firstDay && avail > 0) { + p2["$day"] = avail; + } } - if (is(NAKED)) { - opts.push([ lbl: "May I wear clothes", act: requestClothes ]); + save("toy.availability", p2); + }; + setupInitial(); + tidyAvail(); + + final cycleTime = 60; + showLounge(); + while (true) { + def away = getAway(); + def e = nextEvent(loadEvents()); + if (!e) return "toy"; // Force restart, should create some events + + if (away) { + def note = loadString("toy.note"); + def noteInstr = loadString("toy.noteInstr"); + if (note) { + show("${dommeTitle()} ${dommeName()} is away ($away)... but you find a note:\n$note"); + showButton("OK"); + if (noteInstr) { + set(noteInstr, true); + } + readNote(); + } + else { + show("${dommeTitle()} ${dommeName()} is away ($away)..."); + } + if (e && e.event && e.event.time > getTime()) { + wait(e.event.time - getTime()); + } } - TOYTOYS.findAll { is(it) }.each { - opts.push([ lbl: "May I be un-$it".toString(), act: requestRelease, arg: it ]); - }; - opts.push([ lbl: "Back", act: null ]); - def opt = getSelectedValue(is("DEBUG") ? DOMME : "Yes, toy?", opts.collect { it.lbl }); - def act = opts[opt].act; - if (act) - act(opts[opt].arg); - else if (is("DEBUG")) - return "toy"; + else { + if (is("DEBUG")) { + def timeStr = localTimeOf(e.event.time).format(soonFormatter); + show("$e.name: $timeStr -> ${e.event.func}(${e.event.arg})"); + } + else { + show("${dommeTitle()} ${dommeName()} will summon you when required."); + } + final waitTime = Math.min(cycleTime, e.event.time - getTime()); + final clickTime = showButton("Please, ${dommeTitle()}?", waitTime); + if (clickTime < waitTime) { + def opts = [ + [ lbl: "FAQ (webpage)", act: showFaq ], + [ lbl: "Calendar", act: setupShowCalendar ], + [ lbl: "Confess", act: confess ], + ]; + if (is("DEBUG")) { + opts.push([ lbl: "Status", act: setupShowState ]); + opts.push([ lbl: "Play", act: playEvent, arg: true ]); + opts.push([ lbl: "Activity", act: playActivity ]); + } + if (is(NAKED)) { + opts.push([ lbl: "May I wear clothes", act: requestClothes ]); + } + TOYTOYS.findAll { is(it) }.each { + opts.push([ lbl: "May I be un-$it".toString(), act: requestRelease, arg: it ]); + }; + opts.push([ lbl: "Back", act: null ]); + def opt = getSelectedValue(is("DEBUG") ? DOMME : "Yes, toy?", opts.collect { it.lbl }); + def act = opts[opt].act; + if (act) + act(opts[opt].arg); + else if (is("DEBUG")) + return "toy"; + } + addAvail(clickTime); + } + def eStart = getTime(); + execEvents(true); + addAvail(getTime() - eStart); } - addAvail(clickTime); } - def eStart = getTime(); - execEvents(true); - addAvail(getTime() - eStart); -} +}.main(); /* -- cgit v1.2.3