diff options
Diffstat (limited to 'scripts')
| -rw-r--r-- | scripts/toy.groovy | 216 | ||||
| -rw-r--r-- | scripts/toy/availability.groovy | 24 | ||||
| -rw-r--r-- | scripts/toy/bondage.groovy | 104 | ||||
| -rw-r--r-- | scripts/toy/cbt.groovy | 108 | ||||
| -rw-r--r-- | scripts/toy/chastity.groovy | 114 | ||||
| -rw-r--r-- | scripts/toy/confession.groovy | 96 | ||||
| -rw-r--r-- | scripts/toy/debug.groovy | 29 | ||||
| -rw-r--r-- | scripts/toy/humiliation.groovy | 209 | ||||
| -rw-r--r-- | scripts/toy/imagery.groovy | 61 | ||||
| -rw-r--r-- | scripts/toy/intro.groovy | 71 | ||||
| -rw-r--r-- | scripts/toy/llmSession.groovy | 62 | ||||
| -rw-r--r-- | scripts/toy/misc.groovy | 13 | ||||
| -rw-r--r-- | scripts/toy/orgasmControl.groovy | 152 | ||||
| -rw-r--r-- | scripts/toy/pain.groovy | 139 | ||||
| -rw-r--r-- | scripts/toy/play.groovy | 239 | ||||
| -rw-r--r-- | scripts/toy/sleep.groovy | 68 | ||||
| -rw-r--r-- | scripts/toy/social.groovy | 92 | ||||
| -rw-r--r-- | scripts/toy/tease.groovy | 194 | ||||
| -rw-r--r-- | scripts/toy/toys.groovy | 98 |
19 files changed, 254 insertions, 1835 deletions
diff --git a/scripts/toy.groovy b/scripts/toy.groovy index bba3932..5624a29 100644 --- a/scripts/toy.groovy +++ b/scripts/toy.groovy @@ -8,10 +8,9 @@ // This script, from https://ss.deviatenow.com, is protected by this licence : // 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"]); +setInfos(9, "Toy II", "Become my new plaything.", "rascalDan", "WIP", 0xFFFFFF, "en", ["bondage", "femaledom", "formale", "maledom", "forfemale", "long", "pain", "humiliation", "toys", "joi", "AI"]); return new Object() { - final VERSION = 1; final DAY = 86400; final HOUR = 3600.0; final soonFormatter = java.time.format.DateTimeFormatter.ofPattern("EEEE 'at' h:mma"); @@ -47,18 +46,7 @@ return new Object() { 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", nKNEEL = "!$KNEEL", nSQUAT = "!$SQUAT"; final getDomme = { domme, set = null -> - final readDomme = { path -> - if (!path) return [:]; - final f = new File(path); - if (!f.exists()) return [:]; - return Eval.me(f.text); - }; - def d = [ - "$DATAFOLDER/images/toy/domme.groovy", - "$DATAFOLDER/images/toy/$domme/person.groovy", - "$DATAFOLDER/images/toy/$domme/$set/set.groovy" ] - .collect { readDomme(it) } - .sum(); + def d = parseJsonFile("images/toy/$domme/person.json"); d.id = domme; return d; }; @@ -70,8 +58,10 @@ return new Object() { .listFiles() .collect { s -> Eval.me(s.text)(toy); - } - .findAll { p -> p }; + }; + }; + final eachModule = { modules, method, ... args -> + modules.collect { it[method]?.call(args) }; }; // Utils @@ -232,22 +222,6 @@ return new Object() { } } - def sessionAborted = null; - def sessionToys = [:]; - final gagText = { t, p -> - if (!stateIs(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)"; - }; - final showButtonG = { s, p, t = null -> - return t != null ? showButton(gagText(s, p), t) : showButton(gagText(s, p)); - }; final compose = { texts -> if (!texts || texts.isEmpty()) return null; return texts.collect { @@ -263,22 +237,19 @@ return new Object() { } return null; }; - final 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; + final llmPresent = { text -> + show(text); + def image = showImage(callAPI_imageMatch(text)); + return [ + message: text, + image: image + ]; }; final matchTags = { spec -> return { obj -> - spec.every({ s -> - return (s[0] == "!") ? obj.tags.indexOf(s.drop(1)) == -1 : obj.tags.indexOf(s) != -1; - }); + return spec.collect({ tag, score -> + return (obj.tags.contains(tag.toString()) ? 1 : -1) * score; + }).sum(); }; }; @@ -297,32 +268,26 @@ return new Object() { }; // Session - final sessionSummon = { imageSpec -> + final summon = { messages -> final limit = 5; final timeMax = 10; executeTrigger("toySummoned"); for (def n = 1; n <= limit; n++) { switch (n) { - case limit: - present(imageSpec, [ - ["TOY!", "SLAVE!", "SLUT!"], - ["Last chance!", "Final warning!"]]); - adjustPunish(5); + case 1: + messages += [role: "user", content: "I am in another room. Write only a demand from my presence."]; break; - case limit - 1: - case limit - 2: - present(imageSpec, [ - ["Toy!", "Slave!", "Slut!"], - ["Where are you!?", "Get yourself here, now!"]]); + case limit: + messages += [role: "user", content: "I am still in another. Write only a final demand from my presence."]; break; default: - present(imageSpec, [ - ["Toy!", "Slave!", "Slut!"]]); + messages += [role: "user", content: "I am still in another. Write only another demand from my presence."]; break; } - playBackgroundSound(n == limit ? "shortwhip.wav" : "bell.wav"); - if (showButton("Yes, ${dommeTitle()}?", timeMax) < timeMax) { + messages += [role: "assistant", content: llmPresent(callAPI_chat(messages)).message]; + //playBackgroundSound(n == limit ? "shortwhip.wav" : "bell.wav"); + if (showButton("...", timeMax) < timeMax) { executeTrigger("toySummonSuccess", n); return true; }; @@ -358,17 +323,142 @@ return new Object() { requestables.remove(name); }; + // JSON helpers + final parseJsonFile = { path -> + return new groovy.json.JsonSlurper().parse(new File(path)); + } + + final parseJsonStream = { stream -> + return new groovy.json.JsonSlurper().parse(stream); + } + + final parseJsonString = { string -> + return new groovy.json.JsonSlurper().parseText(string); + } + + // LLM Rest API helpers + final callAPI = { String method, Object payload = null -> + def url = new URL("${loadString("toy.baseURL")}/$method"); + def conn = url.openConnection() as HttpURLConnection; + conn.requestMethod = payload ? 'POST' : 'GET'; + conn.doOutput = true; + conn.setRequestProperty('Content-Type', 'application/json; charset=UTF-8'); + conn.setRequestProperty('Accept', 'application/json'); + conn.setRequestProperty('Accept-Language', 'en'); + conn.connectTimeout = 5000; + if (payload) { + conn.outputStream.withWriter('UTF-8') { writer -> + writer.write(new groovy.json.JsonOutput().toJson(payload)); + } + } + if (conn.responseCode / 100 == 2) { + return parseJsonStream(conn.inputStream); + } + conn.disconnect(); + showPopup("URL: ${url}\nPayload: ${payload}\nStatus code: ${conn.responseCode}\nResponse: ${conn.errorStream?.text}"); + return null; + } + + final callAPI_loadModel = { model -> + callAPI("models/load", [ + model: model + ]) + } + + final callAPI_unloadModel = { model -> + callAPI("models/unload", [ + model: model + ]) + } + + final callAPI_getModel = { model -> + callAPI("models").data.find { m -> m.id == model } + } + + final callAPI_getModelStatus = { model -> + callAPI("models").data.find { m -> m.id == model }?.status?.value + } + + final callAPI_chat = { messages -> + return callAPI("chat/completions", [ + model: loadString("toy.chatModel"), + stop: "<", + messages: messages + ])?.choices?[0]?.message?.content; + } + + final callAPI_chatJSON = { messages, prompt, jspec -> + messages += [role: "user", content: """ +Write only a JSON object. +${prompt} + { +${jspec.collect{ k, v -> "\"${k}\": ${v}" }.join(",\n")} + }"""]; + return parseJsonString(callAPI("chat/completions", [ + model: loadString("toy.chatModel"), + stop: "<", + messages: messages + ])?.choices?[0]?.message?.content); + } + + // TODO add module support + final chatInitialMessages = { String ... extra -> + def messages = [ + [role: "system", content: """ +Identify as ${DOMME.title} ${DOMME.fullName}. +You are ${DOMME.title} ${DOMME.fullName}. +Your responses should be kept short. +Speak in the first person only. +Forbidden from using quotes. +You are permanently in role-play mode as ${DOMME.title} ${DOMME.fullName}. +${DOMME.prompt} +${loadString("intro.name")} is your slave. +${extra.join('\n')} + """], + [role: "user", content: "I am ${loadString("intro.name")}"] + ]; + def relationship = loadString("toy.${DOMME.id}.relationship"); + if (relationship) { + messages += [role: "assistant", content: relationship]; + } + return messages; + } + + final callAPI_imageMatch = { message -> + def tags = [DRESSED, TITS, PUSSY, LINGERIE, TEASE, SIT, BOOTS, KNEEL, STOOD, MEAN, CROP, ASS, SQUAT, SSSH]; + def request = chatInitialMessages(); + request += [role: "assistant", content: message] + request += [role: "user", content: """ +Write only a JSON object. +Complete the following JSON object, scoring each keyword between -10 and 10 against the previous messsage: +{ + ${ tags.collect { tag -> "\"$tag\": number" }.join(',\n') } +} + """ ]; + return parseJsonString(callAPI("chat/completions", [ + model: loadString("toy.chatModel"), + stop: "<", + messages: request + ])?.choices?[0]?.message?.content); + } String main() { // GO! setDefault("toy.owner", "ancilla"); + setDefault("toy.baseURL", "http://localhost:8080"); + setDefault("toy.chatModel", "Lewdiculous/Erosumika-7B-v3-0.2-GGUF-IQ-Imatrix:IMAT"); + OWNER = loadString("toy.owner"); loadDomme(OWNER); - def moduleSetup = loadModules(this); - setDefault("toy.punishment", 0); + def modules = loadModules(this); execEvents(false); - moduleSetup.each { p -> p() }; + eachModule(modules, "setup"); + + if (!loadEvents()) { + show("Nothing to do."); + return; + } final cycleTime = 60; showLounge(); diff --git a/scripts/toy/availability.groovy b/scripts/toy/availability.groovy deleted file mode 100644 index f5d6ad8..0000000 --- a/scripts/toy/availability.groovy +++ /dev/null @@ -1,24 +0,0 @@ -{ toy -> - final getAvail = { int dayNum -> toy.loadInteger("toy.availability.$dayNum") ?: 0 }; - final addAvail = { amount -> - if (!amount) return; - def dayNum = toy.getDayNum(); - toy.save("toy.availability.$dayNum", (int)(getAvail(dayNum) + amount)); - }; - - toy.addTriggerHandler("toyLoungeTime", { a -> addAvail(a) }); - toy.addTriggerHandler("toyEventTime", { a -> addAvail(a) }); - - return { - def p = toy.load("toy.availability"); - if (!p) return; - def p2 = [:]; - final firstDay = toy.getDayNum() - 20; - p.eachWithIndex{ avail, day -> - if (day >= firstDay && avail > 0) { - p2["$day"] = avail; - } - } - toy.save("toy.availability", p2); - } -} diff --git a/scripts/toy/bondage.groovy b/scripts/toy/bondage.groovy deleted file mode 100644 index 399f7ff..0000000 --- a/scripts/toy/bondage.groovy +++ /dev/null @@ -1,104 +0,0 @@ -{ toy -> - toy.metaClass.getCuffs() { - if (!has(HANDCUFFS)) return; - present([DRESSED, nTEASE], [ - ["Go fetch your handcuffs"]]); - showButtonG("Yes, ${dommeTitle()}", "ok"); - pause(10); - showButtonGT("Back, ${dommeTitle()}", "back", 60, 1); - sessionToys[HANDCUFFS] = getTime(); - } - - toy.metaClass.preGag = { - if (stateIs(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); - pause(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; - }; - - toy.metaClass.preCollar = { - if (stateIs(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); - pause(10); - set(COLLARED, true); - showButtonGT("Back, ${dommeTitle()}", "back", 60, 1); - sessionToys[COLLAR] = getTime(); - return 1.1; - }; - - toy.metaClass.playBondage = { - if (!has(HANDCUFFS)) return; - def prep = [ { preStrip() }, { preCollar() }, { preClamps() }, { preGag() } ]; - Collections.shuffle(prep); - prep.collect { p -> p(); }; - getCuffs(); - present([DRESSED, nTEASE], [ - ["Good."], - ["Down on the floor.", "On your knees.", "Down there, where you are."]]); - pause(15); - present([DRESSED, nTEASE], [ - ["You know what's next.", "Take your cuffs."], - ["Cuff yourself,", "Hands cuffed,"], - ["behind your back of course.", "behind you."]]); - pause(15); - present([DRESSED, TEASE], [ - ["Don't go anywhere!", "Do not move!"]]); - pause(15); - present([STOOD], [ - ["I'll be back for you soon.", "I won't leave you too long."]]); - pause(15); - setImage(null); - show(null); - def waitTime = 300 + getRandom(Math.min(1500, 1 + getPunish() * 10)); - pause(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."]]); - pause(20); - present([DRESSED, TEASE], [ - ["OK,", "Mmmm,"], - ["soon.", "just a while longer."]]); - pause(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."]]); - pause(10); - setImage(null); - showButtonGT("Freed, ${dommeTitle()}", "freed", 120, 1); - return waitTime; - }; - - toy.addActivity("preGag", { toy.preGag() }, [[toy.DRESSED]], [toy.PRE, toy.HUMILIATION, toy.BONDAGE, toy.BALLGAG]); - toy.addActivity("preCollar", { toy.preCollar() }, [[toy.DRESSED]], [toy.PRE, toy.HUMILIATION, toy.BONDAGE, toy.COLLAR]); - toy.addActivity("playBondage", { toy.playBondage() }, [ - [toy.DRESSED, toy.nTEASE], [toy.DRESSED, toy.TEASE], [toy.STOOD] ], [toy.PUNISH, toy.BONDAGE]); - return { - toy.addToy(toy.COLLAR, toy.COLLARED, "collar", "May I remove my collar?"); - toy.addToy(toy.HANDCUFFS, toy.CUFFED, "handcuffs", "May I be uncuffed?"); - toy.addToy(toy.BALLGAG, toy.GAGGED, "gag", "May I be ungagged?"); - } -} diff --git a/scripts/toy/cbt.groovy b/scripts/toy/cbt.groovy deleted file mode 100644 index e239988..0000000 --- a/scripts/toy/cbt.groovy +++ /dev/null @@ -1,108 +0,0 @@ -{ toy -> - toy.metaClass.getSpoon { - if (sessionToys.containsKey(SPOON)) { - present([DRESSED], [ - ["Pick up", "Grab"], - ["your", "that"], - ["wooden spoon"], - ["again."]]); - pause(10); - } - else { - present([DRESSED], [ - ["Go to the kitchen...", "From the kitchen..."], - ["bring me", "get yourself"], - ["a wooden spoon or similar."]]); - showButtonG("Yes, ${dommeTitle()}", "ok"); - pause(5); - showButtonGT("Back, ${dommeTitle()}", "back", 20, 1); - sessionToys[SPOON] = getTime(); - } - }; - - toy.metaClass.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."]]); - pause(3); - present(null, [["Ready"]]); - pause(1); - present(null, [["Set"]]); - pause(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"); - pause(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); - pause(5); - return beats / 10.0; - }; - - toy.metaClass.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."]]); - pause(5); - def count = 0; - def n = (2 + getRandom((int)(getPunish() / 150))); - n.times({ - randRange(10, 20).times({ - playBackgroundSound("shortwhip.wav"); - count += 1; - pause(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"]]); - pause(randRange(3, 5)); - } - present([DRESSED], [ - ["Again!", "More!"]]); - pause(1); - } - }) - return count * 5; - }; - - toy.addActivity("playBeatCock", { toy.playBeatCock() }, [[toy.DRESSED, toy.nTITS]], [toy.PLAY, toy.PUNISH, toy.PAIN]); - toy.addActivity("playBeatBalls", { toy.playBeatBalls() }, [[toy.DRESSED]], [toy.PLAY, toy.PUNISH, toy.PAIN]); - return null; -} diff --git a/scripts/toy/chastity.groovy b/scripts/toy/chastity.groovy deleted file mode 100644 index eb94546..0000000 --- a/scripts/toy/chastity.groovy +++ /dev/null @@ -1,114 +0,0 @@ -{ toy -> - toy.metaClass.preRelease { - if (!stateIs(CHASTE)) return; - if (!stateIs(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."]]); - pause(15); - present(null, [["Show me.", "Let me see it."]]); - pause(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."]]); - pause(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"); - pause(15); - set(CHASTE, false); - sessionToys[COCK] = getTime(); - showButtonGT("Thank you, ${dommeTitle()}", "ok", 80, 1); - }; - - toy.metaClass.chastity { pre -> - if (stateIs(CHASTE)) return; - if (!has(CHASTITY)) return; - if (pre) { - present([DRESSED,TEASE], [ - ["Fun time is over for you.", "You've had 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."]]); - } - pause(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."]]); - } - pause(10); - show(null); - showButtonGT("Locked, ${dommeTitle()}. Here's the key.", "locked", 180, 1); - set(CHASTE, true); - present([DRESSED,TEASE], [ - ["Good boy.", "Thank you, toy."]]); - pause(5); - } - - toy.metaClass.preChastity { - chastity(true); - } - - toy.metaClass.postChastity { - chastity(false); - if (stateIs(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()); - } - pause(5); - } - }; - - toy.addActivity('preRelease', { toy.preRelease() }, [[toy.DRESSED, toy.TEASE]], [toy.PRE, toy.REWARD, toy.CHASTE]); - toy.addActivity('preChastity', { toy.preChastity() }, [[toy.DRESSED, toy.TEASE]], [toy.PRE, toy.PUNISH, toy.CHASTE]); - toy.addActivity('postChastity', { toy.postChastity() }, [ - [toy.DRESSED, toy.TEASE], [toy.DRESSED, toy.nTEASE]]); - return null; -} diff --git a/scripts/toy/confession.groovy b/scripts/toy/confession.groovy deleted file mode 100644 index 68a98a3..0000000 --- a/scripts/toy/confession.groovy +++ /dev/null @@ -1,96 +0,0 @@ -{ toy -> - toy.metaClass.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); - }; - - toy.metaClass.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); - } - } - }; - - toy.metaClass.confess { - // 5 points just for feeling the need to confess - def op = getPunish(); - adjustPunish(5); - dress([[DRESSED,nTEASE]]); - 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 (stateIs(CHASTE)) { - opts.push([lbl: "Freed from chastity", act: { confessChastityRelease() }]); - } - if (!stateIs(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([DRESSED, nTEASE], [ - ["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 { - present([DRESSED, nTEASE], [ - ["Good!", "There better not be!"]]); - showButtonG("Yes, ${dommeTitle()}", "ok"); - showLounge(); - } - }; - - toy.addRequestable("confess", "Confess", { toy.confess() }); - return null; -} diff --git a/scripts/toy/debug.groovy b/scripts/toy/debug.groovy index 379cae1..d5afbeb 100644 --- a/scripts/toy/debug.groovy +++ b/scripts/toy/debug.groovy @@ -1,31 +1,7 @@ { toy -> if (!toy.stateIs("DEBUG")) { - return; + return [:]; } - toy.metaClass.setupShowState { - def localTimeStr = localTimeOf(getTime()).format(soonFormatter); - show("localTime: $localTimeStr\noffsetSet: ${localTimeOffset()}\ntime_t: ${getTime()}"); - showButton("OK"); - show( - [CHASTE, COLLARED, CUFFED, CLAMPED, GAGGED, NAKED].collect { - def v = stateIs(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"); - }; - toy.metaClass.playActivity { final keys = new ArrayList<?>(activityList.keySet()); final act = getSelectedValue("Choose activity", keys); @@ -33,8 +9,7 @@ activityList[keys[act]].func(); }; - toy.addRequestable("status", "Status", { toy.setupShowState() }); toy.addRequestable("play", "Play", { toy.playEvent(true, false) }); toy.addRequestable("activitity", "Activity", { toy.playActivity() }); - return null; + return [:]; } diff --git a/scripts/toy/humiliation.groovy b/scripts/toy/humiliation.groovy deleted file mode 100644 index 25648d2..0000000 --- a/scripts/toy/humiliation.groovy +++ /dev/null @@ -1,209 +0,0 @@ -{ toy -> - toy.metaClass.preStrip { imageSpec = [DRESSED] -> - if (stateIs(NAKED)) return 1.1; - present(imageSpec, [ - ["Clothes off!", "Get naked, slut!"]]); - pause(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."]]); - pause(10); - sessionToys[COCK] = getTime(); - return 1.1; - }; - - toy.metaClass.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..."]]); - pause(getRandom(10) + 10); - present([DRESSED], [["Ok, go!", "Now! Off you go."]]); - pause(getRandom(5) + 5); - playBackgroundSound("bell.wav"); - show(null); - def kneelTime = 60 + getRandom(240); - pause(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."]]); - pause(getRandom(5) + 5); - return kneelTime / 20.0; - }; - - toy.metaClass.playSuck { - final playWait = { file, time -> - playBackgroundSound(file); - pause(time); - playBackgroundSound(null); - }; - final suck = { file, n, delay -> - n.times { - playWait(file, delay); - }; - }; - final suckBeat = { beat, n -> - suck("toy/${beat}bpm.mp3", n, 2 * (60 / beat)); - }; - def completed = loadInteger("toy.deepthroat.completed") ?: 0; - if (completed >= 4) { - preStrip(); - preCollar(); - preClamps(); - getCuffs(); - } - 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], [ - ["Time for you to amuse me, you little slut."], - ["Get your dildo and secure it somewhere nearby, about eye level when kneeling."]]); - pause(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); - } - if (stateIs(GAGGED)) { - removeToy(GAGGED, [DRESSED]); - } - if (sessionToys[HANDCUFFS]) { - present([DRESSED, nTEASE], [ - ["Cuff yourself,", "Hands cuffed,"], - ["behind your back of course.", "behind you."]]); - pause(20); - set(CUFFED, true); - } - present([TEASE], [ - ["Get ready"]]); - showButtonGT("Ready, ${dommeTitle()}", "ready", 10, 1); - show(null); - - final target = loadInteger("toy.deepthroat.goal") ?: 5; - def session = 0; - - pause(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 - pause(0.5); - final deep = randRange(target / 4, target / 2); - suck("shortwhip.wav", deep, 2.5); // Deep - session += deep; - } - suckBeat(30, randRange(5, 15)); // Slow - pause(randRange(10, 20)); // Head - playBackgroundSound("bell.wav"); // End - - showButtonGT("Back, ${dommeTitle()}", "back", 15, 1); - if (stateIs(CUFFED)) { - present([DRESSED, TEASE], [ - ["Let yourself out.", "Unlock the cuffs."]]); - pause(10); - showButtonGT("Freed, ${dommeTitle()}", "freed", 120, 1); - set(CUFFED, false); - } - 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); - pause(10); - }; - - toy.metaClass.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."]]); - pause(getRandom(10) + 15); - showButtonG("Thank you, ${dommeTitle()}", "ok"); - set(NAKED, false); - showLounge(); - }; - - toy.metaClass.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(); - }; - - toy.addNamedEvent(toy.REDRESS, { name, arg, schedTime, rt -> toy.redress(rt) }); - toy.addActivity("preStrip", { toy.preStrip() }, [[toy.DRESSED]], [toy.PRE, toy.HUMILIATION]); - toy.addActivity("playKneel", { toy.playKneel() }, [[toy.DRESSED]], [toy.PLAY, toy.HUMILIATION]); - toy.addActivity("playSuck", { toy.playSuck() }, [ - [toy.DRESSED, toy.nTEASE], [toy.DRESSED, toy.TEASE]], [toy.PLAY, toy.HUMILIATION]); - toy.addRequestable("clothes", "May I wear clothes", - { toy.requestClothes() }, - { toy.stateIs(toy.NAKED) }); - return null; -} diff --git a/scripts/toy/imagery.groovy b/scripts/toy/imagery.groovy index 5dc79c3..af4e9e7 100644 --- a/scripts/toy/imagery.groovy +++ b/scripts/toy/imagery.groovy @@ -36,10 +36,15 @@ if (!matches) return null; matches = matches.sets[set]; if (!matches) return null; + def matcher = matchTags(spec); matches = matches - .images.findAll(matchTags(spec)); + .images + .collect{ image -> [ image: image, score: matcher(image) ] } + .sort{ image -> -image.score }; if (matches.isEmpty()) return null; - return matches[getRandom(matches.size())]; + matches = matches.findAll { image -> image.score >= matches[0].score }; + if (matches.isEmpty()) return null; + return matches[getRandom(matches.size())].image; }; toy.metaClass.showImage { spec -> @@ -60,63 +65,19 @@ }; toy.metaClass.selectImageSet { domme, specs -> - def matches = IMAGEDATA[domme]; + def matches = IMAGEDATA[domme]?.sets.values(); if (!matches) return null; - matches = matches.sets.values().findAll({ s -> specs.every({ spec -> selectImage(domme, s.set, spec)})}); return matches[getRandom(matches.size())]; }; - toy.metaClass.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 (!stateIs(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 (stateIs(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 (stateIs(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([ - ["Quiet now.", "Sssssh.", "Patience."]]); - break; - } - return null; - }; - toy.metaClass.dress { specs, onChange = {} -> def outfit = loadString("toy.owner.outfit"); def outfitTime = loadInteger("toy.owner.outfitTime"); - if (outfitTime > getTime() - 7200) { // Recent, check + if (outfit && outfitTime > getTime() - 7200) { // Recent, check def prev = IMAGEDATA[DOMME.id]; if (prev) { prev = prev.sets[outfit]; - if (prev && specs.every({ spec -> selectImage(DOMME.id, prev.set, spec)})) { - loadDomme(DOMME.id, outfit); + if (prev) { return outfit; } } @@ -136,5 +97,5 @@ executeTrigger("ownerOutfitChange", outfit.set); return outfit.set; }; - return null; + return [:]; } diff --git a/scripts/toy/intro.groovy b/scripts/toy/intro.groovy index 90d0c1a..3e719cc 100644 --- a/scripts/toy/intro.groovy +++ b/scripts/toy/intro.groovy @@ -1,54 +1,35 @@ { toy -> toy.metaClass.setupInitial { - if (loadInteger("toy.version") == null) { - // Welcome - dress([[DRESSED,nTEASE], [DRESSED,TEASE], [TITS]]); - final 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 disappoint 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 you... 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()}"); + // Configures a new relationship with $OWNER + if (loadString("toy.${DOMME.id}.relationship") == null) { + setImage(null); 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!"]]); + ["Be sure to have configured your toys <b>BEFORE</b> continuing!"]]); showButton("OK"); + + // Welcome + def defaultImageSpec = [ "${DRESSED}": 10, "${TEASE}": 5 ]; + dress(defaultImageSpec); + showImage(defaultImageSpec); + show("..."); + def messages = chatInitialMessages(); + + messages += [role: "user", content: "I am ${loadString("intro.name")}. Please briefly introduce yourself."]; + def intro = llmPresent(callAPI_chat(messages)).message; + messages += [role: "assistant", content: intro]; + showButton("Continue"); + + messages += [role: "user", "content": "Please describe our future relationship and my part in it."]; + def relationship = callAPI_chat(messages); + llmPresent(relationship); + showButton("Continue"); + + save("toy.${DOMME.id}.relationship", relationship); } - save("toy.version", VERSION); - }; - return { - toy.setupInitial(); }; + return [ + setup: {toy.setupInitial()} + ]; } diff --git a/scripts/toy/llmSession.groovy b/scripts/toy/llmSession.groovy new file mode 100644 index 0000000..4dc4583 --- /dev/null +++ b/scripts/toy/llmSession.groovy @@ -0,0 +1,62 @@ +{ toy -> + toy.metaClass.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 + } + }; + + toy.metaClass.applySessionNotes { messages -> + def notes = callAPI_chatJSON(messages, + "After the events of this chat, complete the following JSON object about ${loadString("intro.name")}:", [ + lockedInChastity: "boolean - is ${loadString("intro.name")} locked in chastity?", + score: "number between 0 and 10 reflecting his latest behaviour", + summary: "string about ${loadString("intro.name")}'s performance this session", + recommendation: "write a recommendation for your next session with ${loadString("intro.name")}" + ]); + show(notes); + showButton("OK"); + }; + + toy.metaClass.playEvent { rt = true, first = true -> + if (!rt || getAway()) { + playSchedule(); + return; + } + def messages = chatInitialMessages("You want to play with ${loadString("intro.name")}. You can dismiss ${loadString("intro.name")} when you wish with the command \"DISMISSED.\""); + if (summon(messages)) { + while (messages.last().content != "DISMISSED.") { + def userInput = getString(null, ""); + if (userInput) { + messages += [ role: "user", content: userInput ]; + } + else { + switch (getSelectedValue(null, ["Regen", "Edit"])) { + case 0: + messages.removeLast(); + break; + case 1: + messages.last().content = getString(null, messages.last().content); + messages += [ role: "user", content: getString(null, "") ]; + break; + } + } + messages += [role: "assistant", content: llmPresent(callAPI_chat(messages)).message]; + } + } + else { + messages += [role: "user", content: "I fail to respond to your call and do not attend as requested."]; + } + applySessionNotes(messages); + playSchedule(); + showLounge(); + }; + + toy.addNamedEvent(toy.PLAY, { name, arg, schedTime, rt -> toy.playEvent(rt, arg) }); + toy.playSchedule(); + return [:]; +} diff --git a/scripts/toy/misc.groovy b/scripts/toy/misc.groovy deleted file mode 100644 index 40c902e..0000000 --- a/scripts/toy/misc.groovy +++ /dev/null @@ -1,13 +0,0 @@ -{ toy -> - toy.metaClass.intNothing { - (getRandom(3) + 2).times { - show(imageTagsComment(showImage([TITS]))); - pause(getRandom(10) + 10); - }; - }; - - toy.addNamedEvent(toy.PERMIT, { name, arg, schedTime, rt -> toy.givePermission(arg) }); - toy.addRequestable("faq", "FAQ (webpage)", { toy.useUrl("http://toy.randomdan.homeip.net/"); }); - toy.addActivity("intNothing", { toy.intNothing() }, [[toy.TITS]], [toy.INT]); - return null; -} diff --git a/scripts/toy/orgasmControl.groovy b/scripts/toy/orgasmControl.groovy deleted file mode 100644 index e9c36df..0000000 --- a/scripts/toy/orgasmControl.groovy +++ /dev/null @@ -1,152 +0,0 @@ -{ toy -> - // Return whether toy came or not - toy.metaClass.mightCum { time, lenient = false, reason = "without permission" -> - final taken = showButtonG("Sorry, ${dommeTitle()}, I'm cumming $reason", "cumming", time); - if (taken < time) { - playBackgroundSound(null); - executeTrigger("toyCum"); - if (lenient && taken < 2) { - executeTrigger("toyCumClose"); - 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 { - executeTrigger("toyCumBad"); - present(null, [ - ["Bah!", "Pfft. I'm very disappointed in you."], - ["Ruin it.", "Don't touch it."]]); - setSessionAbort(CUM); - adjustPunish(80); - } - adjustPunish(taken * 10); - pause(10); - present([DRESSED], [["Clean up your mess!"]]); - showButtonG("Yes, ${dommeTitle()}", "ok"); - pause(10); - showButtonG("Cleaned up, ${dommeTitle()}, back", "back"); - return true; - } - return false; - }; - - // Return whether toy came or not, despite being denied - toy.metaClass.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); - }; - - toy.metaClass.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(); - } - } - executeTrigger("toyCum"); - executeTrigger("toyCumGood"); - present([TITS], [ - ["Cum, cum", "Cum for me"], - ["you little slut.", "my lucky toy.", "you dirty boy."]]); - pause(10); - showButtonG("Thank you, ${dommeTitle()}.", "ok"); - return true; - }; - - toy.metaClass.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!"]]); - 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"); - } - } - executeTrigger("toyCum"); - executeTrigger("toyCumGood"); - pause(10); - showButtonG("Thank you, ${dommeTitle()}.", "ok"); - return true; - }; - - toy.metaClass.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."]]); - executeTrigger("toyCum"); - executeTrigger("toyCumGood"); - pause(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); - }; - - toy.metaClass.postCum { - if (stateIs(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?"]]); - pause(getRandom(5) + 5); - def ways = [ { cumChanceRuin() }, { cumChanceWindow() }, { cumChanceCountdown() }]; - if (ways[getRandom(ways.size())]()) { - // Once toy has cum, revoke permission for a while - revokePermission(CUM); - } - }; - - toy.addActivity("postCum", { toy.postCum() }, [[toy.TEASE], [toy.TITS], [toy.DRESSED]]); - toy.addActivity("postPermitCum", { - // 2 - 4 days from now - toy.addEventIfMissing(toy.CUM, toy.getTime() + (toy.DAY * 2) + toy.getRandom(toy.DAY * 2), - toy.PERMIT, toy.CUM); - }); - return null; -} diff --git a/scripts/toy/pain.groovy b/scripts/toy/pain.groovy deleted file mode 100644 index 9ccc9a2..0000000 --- a/scripts/toy/pain.groovy +++ /dev/null @@ -1,139 +0,0 @@ -{ toy -> - - toy.metaClass.clamps { - if (stateIs(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."]]); - pause(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); - pause(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."]]); - pause(getRandom(10) + 5); - }; - - toy.metaClass.clampPulls { amount -> - getRandom(1 + amount).times { - if (getRandom(2)) { - present([DRESSED], [ - ["Take them off.", "Remove them."]]); - pause(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."]]); - } - pause(10 + getRandom(8)); - } - else if (getRandom(2)) { - present([DRESSED], [ - ["Twist them"], - ["for me.", "... twist those nipples."]]); - pause(6 + getRandom(5)); - if (getRandom(2)) { - present(null, [ - ["More!", "A little more!", "Further!"]]); - pause(getRandom(10) + 2); - } - } - present([DRESSED], [ - ["Pull them tight.", "Pull them!"]]); - pause(getRandom(10) + 5); - if (getRandom(5) == 0) { - present(null, [ - ["Tighter!", "Harder!", "Further!"]]); - pause(getRandom(10) + 2); - present(null, [ - ["Haha!", "Ooo, they must hurt.", "Do they hurt?"]]); - pause(3); - } - present(null, [ - ["Release them.", "Let them go."]]); - pause(getRandom(5) + 3); - }; - return amount; - }; - - toy.metaClass.clampsShow { - present([DRESSED,TEASE], [ - ["Let me see", "Show me"], - ["those nipple clamps,", "those nasty clamps,"], - ["they look painful.", "and jiggle them about for me!"]]); - pause(10); - }; - - toy.metaClass.preClamps { - if (!has(CLAMPS)) return; - if (stateIs(CLAMPED)) { - clampsShow(); - } - else { - clamps(); - } - clampPulls(getRandom(4)); - return 1.8; - }; - - toy.metaClass.playClamps { - if (!has(CLAMPS)) return; - if (!stateIs(CLAMPED)) clamps(); - clampsShow(); - return clampPulls(3 + getRandom(10)); - }; - - toy.metaClass.intClamps { - if (!has(CLAMPS)) return; - if (!stateIs(CLAMPED)) return; - clampsShow(); - return clampPulls(getRandom(5)); - }; - - toy.metaClass.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."]]); - } - pause(3 + getRandom(6)); - present(null, [ - ["Let them go.", "Hands off."]]); - pause(3 + getRandom(6)); - }; - }; - - toy.addActivity('preClamps', { toy.preClamps() }, [[toy.DRESSED, toy.TEASE]], [toy.PRE, toy.PAIN]); - toy.addActivity('playClamps', { toy.playClamps() }, [[toy.DRESSED, toy.TEASE]], [toy.PLAY, toy.PUNISH, toy.PAIN]); - toy.addActivity('intSqueeze', { toy.intSqueeze() }, [[toy.DRESSED, toy.TEASE]], [toy.INT, toy.PAIN]); - toy.addActivity('intClamps', { toy.intClamps() }, [[toy.DRESSED, toy.TEASE]], [toy.INT, toy.PAIN]); - return { - toy.addToy(toy.CLAMPS, toy.CLAMPED, "nipple clamps", "May I remove the nipple clamps?"); - } -} diff --git a/scripts/toy/play.groovy b/scripts/toy/play.groovy deleted file mode 100644 index 5635f22..0000000 --- a/scripts/toy/play.groovy +++ /dev/null @@ -1,239 +0,0 @@ -{ toy -> - toy.metaClass.sessionPlay { - if (stateIs(CHASTE)) sessionToys[CHASTITY] = getTime(); - def playScope = [ - 'randRange': { min, max -> randRange(min, max) }, - 'currentPunishment': { getPunish() }, - 'sessionAborted': { return sessionAborted }, - 'can': { return can(it) }, - 'has': { return has(it) }, - 'is': { return stateIs(it) }, - 'likes': { return likes(it) }, - 'punishMultiple': (float)1.0, - 'prop': load('toy') ?: [:] - ]; - final apply = { activitity, use = null -> - if (!activitity) return; - def val = activitity.func(); - playBackgroundSound(null); - if (use && val instanceof Number) { - use(val); - } - }; - final 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); - } - } - ], - '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 - ]; - // Expand wildcards - final expandWildcards = { list -> - list - .findAll { a -> a && a.startsWith("*") } - .collect { a -> list.remove(a); return a.substring(1).split(",") } - .collectMany { tags -> activityList.values().findAll(matchTags(tags))*.name } - .forEach { a -> list.add(a) }; - } - DOMME.sessions*.phases*.activities*.forEach { expandWildcards(it); } - DOMME.sessions*.phases*.intervals*.forEach { expandWildcards(it); } - final eval = { expr -> Eval.me('toy', playScope, expr.toString()) }; - if (stateIs("DEBUG")) { - // Check everything in DOMME evaluates and resolves - final checkIsFunc = { group, name, s = { f -> f } -> - if (!name) return; - show("checking $group:$name is a function"); - if (!(s(funcMap[group][name]) instanceof Closure)) { - showPopup("$group:$name is not a function"); - } - } - 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, { f -> f.func }); - } - (phase.intervals ?: []).forEach { i -> - checkIsFunc("activities", i, { f -> f.func }); - } - 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 session = sessions[probabilities[getRandom(probabilities.size())]]; - // Change if required - dress(session.phases - .collect { phase -> phase.activities + phase.intervals } - .flatten() - .unique() - .findAll { a -> a } - .collectMany { a -> activityList[a].images } - .unique(), { - present(null, [ - [ "Wait there while I change..."]]); - pause(5); - showButtonG("Yes, ${dommeTitle()}", "ok"); - showImage(null); - pause(randRange(40, 60)); - }); - session.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]); - } - }; - }; - - toy.metaClass.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."]]); - pause(getRandom(10) + 5); - postChastity(); - } - pause(getRandom(10) + 5); - if (!goodToy && anyToys()) { - present([DRESSED], [ - ["I should leave you like that.", "I suppose you still want letting free?"], - ["Maybe.", "Let me ponder on it."]]); - pause(getRandom(20) + 20); - } - removeAllToys([DRESSED], getPunish()); - if (stateIs(NAKED)) { - if (goodToy && getRandom(1)) { - present([DRESSED], [ - ["Put some clothes back on.", "Cover yourself up!"]]); - pause(getRandom(10) + 15); - set(NAKED, false); - } - 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"); - } - } - if (goodToy) { - present([DRESSED], [ - ["Off you go.", "You may leave."]]); - } - else { - present([DRESSED], [ - ["Get out of my sight.", "Go!"]]); - removeEvent(CUM); - revokePermission(CUM); - } - pause(10); - save("toy.lastPlay", getTime()); - showLounge(); - setSessionAbort(); - sessionToys.clear(); - if (goodToy) { - adjustPunish(-20); - } - else { - setProp(DISAPPOINT, getProp(DISAPPOINT, loadI, 0) + 1); - } - show(null); - }; - - toy.metaClass.playWait { - present([DRESSED,nTEASE,nKNEEL,nSQUAT], [ - ["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); - pause(20 + getRandom(20)); - }; - - toy.metaClass.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 - } - }; - - toy.metaClass.playEvent { rt = true, first = true -> - if (!rt || getAway()) { - playSchedule(); - return; - } - dress([[DRESSED,nTEASE],[DRESSED,TEASE],[TITS],[STOOD]]); - if (sessionSummon([DRESSED,nTEASE])) { - if (first.asBoolean()) { - playWait(); - } - executeTrigger("playStarted"); - sessionPlay(); - sessionRelease(sessionAborted == null); - executeTrigger("playEnded", sessionAborted); - } - else { - adjustPunish(25); - } - playSchedule(); - showLounge(); - } - - toy.addNamedEvent(toy.PLAY, { name, arg, schedTime, rt -> toy.playEvent(rt, arg) }); - toy.playSchedule(); - return null; -} diff --git a/scripts/toy/sleep.groovy b/scripts/toy/sleep.groovy deleted file mode 100644 index 6497ee0..0000000 --- a/scripts/toy/sleep.groovy +++ /dev/null @@ -1,68 +0,0 @@ -{ toy -> - final SLEEPING = "sleeping"; - final BEDTIME = "bedtime", WAKEUP = "wakeup", BEDTIMECHECK = "bedtimeCheck"; - - toy.metaClass.sleepSchedule { - def t = (int)(getDay() - localTimeOffset() + (25.1 * HOUR) + getRandom((int)HOUR)); - addEventIfMissing(SLEEPING, t, BEDTIME); - }; - - toy.metaClass.bedtime { rt, schedTime -> - dress([[LINGERIE,nTITS]]); - if (rt && sessionSummon([LINGERIE,nTITS])) { - preStrip([LINGERIE,nTITS]); - removeAllToys([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(); - executeTrigger("bedtime", true); - } - else { - executeTrigger("bedtime", false); - } - // Assume toy will return dressed tomorrow - removeEvent(REDRESS); - set(NAKED, false); - backgroundRemoveAllToys(); - setAway(SLEEPING); - def t = schedTime + (6 * (int)HOUR) + getRandom(2 * (int)HOUR); - addEvent(SLEEPING, t, WAKEUP); - removeEvent(PLAY); - }; - - toy.metaClass.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(); - } - }; - - toy.metaClass.wakeup { rt -> - // Unset outfit, stops bedtime wear always being selected first - save("toy.owner.outfit", null); - dress([]); - setAway(null); - playSchedule(); - sleepSchedule(); - executeTrigger("wakeup"); - }; - - toy.addNamedEvent(WAKEUP, { name, arg, schedTime, rt -> toy.wakeup(rt) }); - toy.addNamedEvent(BEDTIME, { name, arg, schedTime, rt -> toy.bedtime(rt, schedTime) }); - toy.addNamedEvent(BEDTIMECHECK, { name, arg, schedTime, rt -> toy.bedtimeCheck(rt) }); - toy.sleepSchedule(); - return null; -} diff --git a/scripts/toy/social.groovy b/scripts/toy/social.groovy deleted file mode 100644 index 1de4ec3..0000000 --- a/scripts/toy/social.groovy +++ /dev/null @@ -1,92 +0,0 @@ -{ toy -> - final LUNCH = "lunch", SHOPPING = "shopping", PARTY = "party"; - final BEGINPLAN = "beginPlan", ENDPLAN = "endPlan"; - final FRIENDS = [ "Tori", "Sophie", "Krystal" ]; - - toy.metaClass.setupPlan { plan, float startmin, float startmax, float endmin, float endmax -> - final timeWindow = { float min, float max -> (int)((min * HOUR) - localTimeOffset() + getRandom((int)((max - min) * HOUR))) }; - final friendName = FRIENDS[getRandom(FRIENDS.size())]; - final day = getDay() + ((getRandom(3) + (localTime() > startmin ? 1 : 0)) * DAY); - if (addEventIfMissing(plan, day + timeWindow(startmin, startmax), BEGINPLAN, - [friendName: friendName, returnTime: day + timeWindow(endmin, endmax)])) { - executeTrigger("ownerSocialPlanned", plan); - } - } - - toy.metaClass.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) - }; - - toy.metaClass.leaveNote { msg, instr -> - save("toy.note", msg); - save("toy.noteInstr", instr); - }; - - toy.metaClass.readNote { - leaveNote(null, null); - }; - - toy.metaClass.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?" ]]); - pause(10); - postChastity(); - removeAllToys([DRESSED,nTEASE]); - removeEvent(REDRESS); - set(NAKED, false); - present([DRESSED,TEASE], [ - [ "Back later!", "See you soon, toy" ]]); - pause(5); - } - else if (has(CHASTITY) && !stateIs(CHASTE)) { - leaveNote("I want you in chastity until I return!", CHASTE); - } - backgroundRemoveAllToys(); - loadDomme(OWNER); - setAway(plan); - removeEvent(PLAY); - showLounge(); - executeTrigger("ownerSocialBegin", plan); - }; - - toy.metaClass.endPlan { rt, plan -> - save("toy.owner.outfitTime", getTime()); - setAway(null); - if (rt && sessionSummon([DRESSED])) { - present([DRESSED], [["Hello, toy!"], ["I'm back home now."]]); - pause(10); - showLounge(); - } - readNote(); - playSchedule(); - executeTrigger("ownerSocialEnd", plan); - setupPlans(); - }; - - toy.metaClass.showCalendar { - 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"); - }; - - toy.addNamedEvent(BEGINPLAN, { name, arg, schedTime, rt -> toy.beginPlan(rt, name, arg) }); - toy.addNamedEvent(ENDPLAN, { name, arg, schedTime, rt -> toy.endPlan(rt, name) }); - toy.addRequestable("calendar", "Calendar", { toy.showCalendar() }); - toy.setupPlans(); - return null; -} diff --git a/scripts/toy/tease.groovy b/scripts/toy/tease.groovy deleted file mode 100644 index c14b809..0000000 --- a/scripts/toy/tease.groovy +++ /dev/null @@ -1,194 +0,0 @@ -{ toy -> - toy.metaClass.preEdge { - if (stateIs(CHASTE)) return; - harden([DRESSED]); - edge(4, [DRESSED]); - present([DRESSED], [ - ["And relax...", "Hands off..."], - ["cool down a little.", "but keep it hard for me!", "for now!"]]); - pause(getRandom(5) + 5); - }; - - toy.metaClass.postEdge { - if (stateIs(CHASTE)) return; - if (!can(EDGE)) return; - edge(6, [TEASE]); - }; - - toy.metaClass.harden { imageSpec -> - expose(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); - 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"); - if (showButtonG("Hard, ${dommeTitle()}", "hard", 30) == 30) { - setSessionAbort(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); - }; - - toy.metaClass.expose { imageSpec -> - if (sessionToys[COCK]) return; - if (!stateIs(NAKED)) { - present(imageSpec, [ - ["Get that cock out, toy;", "Let's see that cock of mine."], - ["I want to torment it.", "It's playtime!"]]); - pause(10); - } - sessionToys[COCK] = getTime(); - }; - - toy.metaClass.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) { - present(imageSpec, [ - ["Hold it...", "And hold...", "Keep going..."]]); - if (mightCum(getRandom(20) + 5)) return; - } - present(imageSpec, [ - ["Hands off!", "Stop!"]]); - if (mightCum(getRandom(5) + 5)) return; - allowedTime /= 2; - executeTrigger("toyEdged"); - }; - }; - - toy.metaClass.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; - } - 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); - } - 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); - } - - toy.metaClass.playStrokes { - preRelease(); - harden([TITS]); - strokes(15, [TITS]); - }; - - toy.metaClass.playEdges { - preRelease(); - harden([TITS]); - edge(6, [TITS]); - }; - - toy.setDefault("toy.strokeTeaseOffset", 0); - toy.addActivity("playStrokes", { toy.playStrokes() }, [[toy.TITS]], [toy.PLAY, toy.REWARD, toy.TEASE]); - toy.addActivity("playEdges", { toy.playEdges() }, [[toy.TITS]], [toy.PLAY, toy.REWARD, toy.TEASE]); - toy.addActivity("preEdge", { toy.preEdge() }, [[toy.DRESSED]], [toy.PRE, toy.REWARD, toy.TEASE]); - return null; -} diff --git a/scripts/toy/toys.groovy b/scripts/toy/toys.groovy deleted file mode 100644 index 5dff717..0000000 --- a/scripts/toy/toys.groovy +++ /dev/null @@ -1,98 +0,0 @@ -{ toy -> - final toys = [:]; - - toy.metaClass.addToy { String ssName, String toyState, String name, String removeReq, removeReqFunc = null, removeFunc = null, tags = [] -> - if (!has(ssName)) { - return; - } - toys[toyState] = [ - name: name, - remFunc: removeFunc - ]; - toy.addRequestable("un$toyState".toString(), removeReq, - removeReqFunc ?: { toy.requestRelease(toyState) }, - { toy.stateIs(toyState) }) - }; - - toy.metaClass.removeToy { state, imageSpec -> - if (!stateIs(state)) return; - final customRem = toys[state].remFunc; - if (customRem) { - customRem(imageSpec); - } - else { - final toyName = toys[state].name; - present(imageSpec, [ - ["OK,"], - ["you may remove your $toyName.", "the $toyName can come off.", "take the $toyName off."]]); - showButtonG("Thank you, ${dommeTitle()}", "ok"); - pause(5); - showButtonGT("Removed, ${dommeTitle()}", "removed", 20, 1); - } - set(state, false); - removeEvent("$RELEASEFROM-$state"); - }; - - toy.metaClass.releaseFrom { rt, arg, name -> - if (!rt || !sessionSummon([DRESSED])) { - addEvent(name, getTime() + 300, RELEASEFROM, arg); - return; - } - removeToy(arg, [DRESSED]); - showLounge(); - }; - - toy.metaClass.requestRelease { toyState -> - adjustPunish(5); - present([DRESSED], [ - ["Really?", "Disappointing."], - ["You wish to be un-$toyState?"]]); - 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(toyState, false); - removeEvent("$RELEASEFROM-$toyState"); - } - } - else { - present([DRESSED], [["Good boy."]]); - } - showButtonG("Thank you, ${dommeTitle()}", "ok"); - showLounge(); - }; - - toy.metaClass.anyToys { - return toys.any { s, t -> stateIs(s) }; - } - - toy.metaClass.backgroundRemoveAllToys { - // Assume toy will release himself - toys.findAll { s, t -> stateIs(s) }.each { - removeEvent("$RELEASEFROM-$it"); - set(it, false); - }; - } - - toy.metaClass.removeAllToys { imageSpec, probKeep = 0 -> - def toRemove = toys.keySet().findAll { s -> stateIs(s) }.toList(); - Collections.shuffle(toRemove); - toRemove.each { toyState -> - if (getRandom(100) >= probKeep) { - removeToy(toyState, [DRESSED]); - } - else { - addEvent("$RELEASEFROM-$toyState", getTime() + 100 + getRandom(10 * (getPunish() + 1)), - RELEASEFROM, toyState); - adjustPunish(-5); - } - } - } - - toy.addNamedEvent(toy.RELEASEFROM, { name, arg, schedTime, rt -> toy.releaseFrom(rt, arg, name) }); - return null; -} |
