summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
authorDan Goodliffe <dan.goodliffe@octal.co.uk>2026-06-18 12:47:33 +0100
committerDan Goodliffe <dan.goodliffe@octal.co.uk>2026-06-18 12:47:33 +0100
commit978b7acefda65595eddf140941688ca611920f87 (patch)
treefc3312fc07d74c99e54794d4c1b3a2c99126740d /scripts
parentActually call the size method (diff)
downloadtoy-llm.zip
Initial dirty commit replacing most the back end with an LLMllm
Removes almost all the code for explicit activities and drops in calls to an OpenAI compatible API. Lots of pending stuff to restore a lot of functionality and leverage better use of the LLM, but a basic summon/session loop functions.
Diffstat (limited to 'scripts')
-rw-r--r--scripts/toy.groovy216
-rw-r--r--scripts/toy/availability.groovy24
-rw-r--r--scripts/toy/bondage.groovy104
-rw-r--r--scripts/toy/cbt.groovy108
-rw-r--r--scripts/toy/chastity.groovy114
-rw-r--r--scripts/toy/confession.groovy96
-rw-r--r--scripts/toy/debug.groovy29
-rw-r--r--scripts/toy/humiliation.groovy209
-rw-r--r--scripts/toy/imagery.groovy61
-rw-r--r--scripts/toy/intro.groovy71
-rw-r--r--scripts/toy/llmSession.groovy62
-rw-r--r--scripts/toy/misc.groovy13
-rw-r--r--scripts/toy/orgasmControl.groovy152
-rw-r--r--scripts/toy/pain.groovy139
-rw-r--r--scripts/toy/play.groovy239
-rw-r--r--scripts/toy/sleep.groovy68
-rw-r--r--scripts/toy/social.groovy92
-rw-r--r--scripts/toy/tease.groovy194
-rw-r--r--scripts/toy/toys.groovy98
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;
-}