summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--images/toy/ancilla/person.groovy4
-rw-r--r--images/toy/ancilla/person.json5
-rw-r--r--images/toy/domme.groovy92
-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
22 files changed, 259 insertions, 1931 deletions
diff --git a/images/toy/ancilla/person.groovy b/images/toy/ancilla/person.groovy
deleted file mode 100644
index 5631510..0000000
--- a/images/toy/ancilla/person.groovy
+++ /dev/null
@@ -1,4 +0,0 @@
-[
- name: 'Ancilla',
- fullName: 'Ancilla Tilia'
-]
diff --git a/images/toy/ancilla/person.json b/images/toy/ancilla/person.json
new file mode 100644
index 0000000..91a9e4c
--- /dev/null
+++ b/images/toy/ancilla/person.json
@@ -0,0 +1,5 @@
+{
+ "fullName": "Ancilla Tilia",
+ "title": "Mistress",
+ "prompt": "You enjoy tormenting your submissive by instructing them to masturbate and edge in frustrating and/or humiliation ways for your amusement, repeatedly denying them orgasm, and then punishing them if or when they cum without permission."
+}
diff --git a/images/toy/domme.groovy b/images/toy/domme.groovy
deleted file mode 100644
index 40d374d..0000000
--- a/images/toy/domme.groovy
+++ /dev/null
@@ -1,92 +0,0 @@
-[
- title: 'Mistress',
- sessions: [
- [
- theme: 'stroking and edging',
- require: 'toy.prop.punishment < 50',
- probability: 50,
- phases: [
- [
- select: 'repeat',
- number: 'toy.randRange(1, 3)',
- activities: [ '*pre,!punish' ]
- ], [
- select: 'repeat',
- number: 'toy.randRange(1, 3)',
- activities: [ '*play,tease' ],
- intervals: [ null, '*int' ]
- ], [
- select: 'take',
- number: 'toy.randRange(0, 2)',
- activities: [ 'postCum', 'postChastity' ]
- ], [
- require: 'toy.can("perm_chaste")',
- activities: [ 'postChastity' ]
- ], [
- require: '!toy.sessionAborted() && !toy.can("cum")',
- select: 'take',
- number: 1,
- activities: [ 'postPermitCum' ]
- ]
- ]
- ], [
- theme: 'blowjob training',
- require: 'toy.has("dildo")',
- probability: 5,
- phases: [
- [
- number: 'toy.randRange(1, 3)',
- activities: [ '*pre,!reward,!ballgag' ]
- ], [
- activities: [ 'playSuck' ]
- ]
- ]
- ], [
- theme: 'reminder who\'s boss',
- probability: 5,
- phases: [
- [
- select: 'take',
- number: 'toy.randRange(1, 4)',
- activities: [ '*pre,!reward,!chaste' ],
- use: 'punishMultiply'
- ], [
- select: 'repeat',
- number: 'toy.randRange(2, 3)',
- activities: [ '*play,punish', '*play,humiliation' ],
- use: 'punishApply'
- ], [
- require: 'toy.can("perm_chaste") || toy.randRange(1, 2) == 1',
- activities: [ 'postChastity' ]
- ]
- ]
- ], [
- theme: 'punishment',
- require: 'toy.prop.punishment > 40',
- probability: 50,
- phases: [
- [
- activities: [ 'preChastity' ]
- ], [
- select: 'take',
- number: 'toy.randRange(1, 2 + ((toy.prop.punishment ?: 0) / 100))',
- activities: [ '*pre,!reward,!chaste' ],
- use: 'punishMultiply'
- ], [
- select: 'repeat',
- number: 'toy.randRange(1, 2 + ((toy.prop.punishment ?: 0) / 100))',
- activities: [ '*play,punish', '*play,humiliation' ],
- intervals: [ null, '*int' ],
- use: 'punishApply'
- ], [
- select: 'take',
- number: 'toy.randRange(1, 2 + ((toy.prop.punishment ?: 0) / 100))',
- activities: [ '*!pre,!int,punish' ],
- use: 'punishApply'
- ], [
- activities: [ 'postChastity' ]
- ]
- ]
- ]
- ]
-]
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;
-}