summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Goodliffe <dan@randomdan.homeip.net>2019-04-20 12:32:11 +0100
committerDan Goodliffe <dan@randomdan.homeip.net>2019-04-20 12:32:11 +0100
commitc39d908a64c02f4d504ea178d794855938a78c0a (patch)
treee360523ab13f98c92fe2670bb93acd989d4e788a
parentFix bug around undressing/exposing/unlocking (diff)
parentTypesafe events (diff)
downloadtoy-c39d908a64c02f4d504ea178d794855938a78c0a.zip
Merge branch 'refactor'
-rw-r--r--scripts/toy.groovy237
1 files changed, 139 insertions, 98 deletions
diff --git a/scripts/toy.groovy b/scripts/toy.groovy
index a6d2a3e..4f30fa8 100644
--- a/scripts/toy.groovy
+++ b/scripts/toy.groovy
@@ -153,81 +153,116 @@ return new Object() {
save("toy.owner.outfitTime", getTime());
DOMME = loadDomme(OWNER, outfit.set);
};
+
// Utils
- def localTimeOffset = {
+ int localTimeOffset() {
return java.time.ZoneId.systemDefault()
.getRules()
.getOffset(java.time.Instant.now())
.getTotalSeconds();
- };
- def localTimeOf = { s ->
- java.time.LocalDateTime.ofInstant(
- java.time.Instant.ofEpochSecond(s), java.time.ZoneId.systemDefault())
}
- def localTime = {
+
+ java.time.LocalDateTime localTimeOf(int s) {
+ return java.time.LocalDateTime.ofInstant(
+ java.time.Instant.ofEpochSecond(s), java.time.ZoneId.systemDefault());
+ }
+
+ float localTime() {
// A float between 4.0 (4am) and 28.0 (4am)
def wallclock = ((getTime() + localTimeOffset()) % DAY) / HOUR;
if (wallclock < 4) {
wallclock += 24;
}
return wallclock;
- };
- def getDay = { (int)(getTime() / DAY) * DAY };
- def getProp = {i, f, d = null ->
- final v = f("toy.$i".toString());
+ }
+
+ int getDay() {
+ return (getTime() / DAY) * DAY;
+ }
+
+ public <T> T getProp(String i, java.util.function.Function<String, T> f, T d = null) {
+ final T v = f.apply("toy.$i".toString());
if (v == null) return d;
return v;
- };
- def getDommeProp = { i, d = null ->
+ }
+
+ Object getDommeProp = { String i, Object d = null ->
if (DOMME == null) return d;
final v = DOMME[i];
if (v == null) return d;
return v;
- };
- def dommeTitle = { getDommeProp("title", "Mistress") };
- def dommeName = { getDommeProp("name", "") };
- def loadB = { p -> loadBoolean(p)};
- def loadI = { p -> loadInteger(p)};
- def loadS = { p -> loadString(p)};
- def setProp = {i, v -> save("toy.$i".toString(), v)};
- def has = {i -> loadBoolean("toys.$i") == true};
- def likes = {i -> loadBoolean("fetish.$i") == true};
- def can = {i -> loadBoolean("toy.permission.$i") == true};
- def perm = can;
- def getPermission = {i -> getProp("permission.$i", loadB)};
- def setPermission = {i, v -> setProp("permission.$i", v)};
- def givePermission = {i -> setPermission(i, true)};
- def revokePermission = {i -> setPermission(i, false)};
- def is = {i -> loadBoolean("toy.state.$i") == true};
- def set = {i, s -> save("toy.state.$i", s)};
- def positioned = { i -> loadString("toy.position") == i };
- def getPunish = { loadInteger("toy.punishment") ?: 0; };
- def adjustPunish = { p -> save("toy.punishment", Math.max(getPunish() + (int)p, 0)); };
- def getAway = { loadString("toy.owner.away") };
- def setAway = { a -> save("toy.owner.away", a) };
- def randRange = { min, max -> (int)min + getRandom((int)(1 + max - min)) }
- def loadEvents = {
- return (loadMap("toy.events") ?: [:]);
}
- def saveEvents = { events ->
- save("toy.events", events);
+
+ String dommeTitle() { getDommeProp("title", "Mistress") }
+ String dommeName() { getDommeProp("name", "") }
+ final java.util.function.Function<String, Boolean> loadB = this.&loadBoolean;
+ final java.util.function.Function<String, Integer> loadI = this.&loadInteger;
+ final java.util.function.Function<String, String> loadS = this.&loadString;
+ void setProp(String i, Object v) { save("toy.$i".toString(), v) }
+ boolean has(String i) { loadBoolean("toys.$i") == true }
+ boolean likes(String i) { loadBoolean("fetish.$i") == true }
+ boolean can(String i) { loadBoolean("toy.permission.$i") == true }
+ boolean perm(String i) { can(it) }
+ boolean getPermission(String i) { getProp("permission.$i", loadB) }
+ void setPermission(String i, boolean v) { setProp("permission.$i", v) }
+ void givePermission(String i) { setPermission(i, true) }
+ void revokePermission(String i) { setPermission(i, false) }
+ boolean stateIs(String i) { loadBoolean("toy.state.$i") == true }
+ void set(String i, boolean s) { save("toy.state.$i", s) }
+ boolean positioned(String i) { ("toy.position") == i }
+ int getPunish() { loadInteger("toy.punishment") ?: 0 }
+ void adjustPunish(int p) { save("toy.punishment", Math.max(getPunish() + p, 0)) }
+ void adjustPunish(double p) { adjustPunish((int)p) }
+ String getAway() { loadString("toy.owner.away") }
+ void setAway(String a) { save("toy.owner.away", a) }
+ int randRange(int min, int max) { min + getRandom(1 + max - min) }
+ int randRange(double min, double max) { randRange((int)min, (int)max) }
+
+ // Events
+ static class Event {
+ int time;
+ String func;
+ Object arg;
+ def map = {
+ [
+ time: time,
+ func: func,
+ arg: arg
+ ]
+ }
}
- def nextEvent = { events ->
- def first = null;
- events.each{ k, v ->
+
+ static class NamedEvent {
+ String name;
+ Event event;
+ }
+
+ Map<String, Event> loadEvents() {
+ return (loadMap("toy.events") ?: [:])
+ .collectEntries { k, v -> [ k, new Event(v) ] };
+ }
+
+ void saveEvents(Map<String, Event> events) {
+ save("toy.events", events.collectEntries { k, v -> [ k, v.map() ] });
+ }
+
+ NamedEvent nextEvent(Map<String, Event> events) {
+ NamedEvent first = null;
+ events.each{ String k, Event v ->
if (!first || first.event.time > v.time) {
- first = [ name: k, event: v ];
+ first = new NamedEvent(name: k, event: v);
}
};
return first;
- };
- def setEvent = { events, name, time, func, arg = null ->
+ }
+
+ boolean setEvent(Map<String, Event> events, String name, int time, String func, Object arg = null) {
if (namedEvents.containsKey(func)) {
- events[name] = [
+ events[name] = new Event(
time: time,
func: func,
arg: arg
- ];
+ );
saveEvents(events);
return true;
}
@@ -235,23 +270,28 @@ return new Object() {
showPopup("No such event $func");
return false;
}
- };
- def addEvent = { name, time, func, arg = null ->
+ }
+
+ boolean addEvent(String name, int time, String func, Object arg = null) {
def events = loadEvents();
return setEvent(events, name, time, func, arg);
}
- def addEventIfMissing = { name, time, func, arg = null ->
+
+ boolean addEventIfMissing(String name, int time, String func, Object arg = null) {
def events = loadEvents();
if (!events.containsKey(name)) {
return setEvent(events, name, time, func, arg);
}
- };
- def removeEvent = { name ->
+ return false;
+ }
+
+ void removeEvent(String name) {
def events = loadEvents();
events.remove(name);
saveEvents(events);
- };
- def execEvents = { rt ->
+ }
+
+ void execEvents(boolean rt) {
for (def e = nextEvent(loadEvents()); e && e.event.time <= getTime(); e = nextEvent(loadEvents())) {
def f = namedEvents[e.event.func];
removeEvent(e.name);
@@ -259,11 +299,12 @@ return new Object() {
f(e.name, e.event.arg, e.event.time, rt);
}
}
- };
+ }
+
def sessionAborted = null;
def sessionToys = [:];
def gagText = { t, p ->
- if (!is(GAGGED)) return t.toString();
+ if (!stateIs(GAGGED)) return t.toString();
return t.split(/\s+/)
.collect {
'm' * getRandom((int)Math.max(1.0, it.length() * 1.0)) +
@@ -309,7 +350,7 @@ return new Object() {
Collections.shuffle(tags);
switch (tags[0]) {
case ASS:
- if (!is(GAGGED))
+ if (!stateIs(GAGGED))
return compose([
["Come closer,", "Don't be shy,"],
["kiss it!", "two kisses on each cheek."]]);
@@ -322,14 +363,14 @@ return new Object() {
["You know what this is for?", "Good boys get rewards, bad ones getting a beating.", "Would you like your ass a darker shade?"]]);
break;
case BOOTS:
- if (is(GAGGED))
+ 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 (is(GAGGED))
+ 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
@@ -374,7 +415,7 @@ return new Object() {
};
def expose = { imageSpec ->
if (sessionToys[COCK]) return;
- if (!is(NAKED)) {
+ 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!"]]);
@@ -662,8 +703,8 @@ return new Object() {
};
// Pre-tease
def preRelease = {
- if (!is(CHASTE)) return;
- if (!is(NAKED)) {
+ 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..."],
@@ -700,7 +741,7 @@ return new Object() {
showButtonGT("Thank you, ${dommeTitle()}", "ok", 80, 1);
};
def preEdge = {
- if (is(CHASTE)) return;
+ if (stateIs(CHASTE)) return;
harden([DRESSED]);
edge(4, [DRESSED]);
present([DRESSED], [
@@ -709,7 +750,7 @@ return new Object() {
wait(getRandom(5) + 5);
};
def preGag = {
- if (is(GAGGED)) return 1.2;
+ if (stateIs(GAGGED)) return 1.2;
if (!has(BALLGAG)) return;
present([DRESSED], [
["Go fetch your gag, toy...", "Bring me your gag, toy..."],
@@ -729,7 +770,7 @@ return new Object() {
return 1.2;
};
def clamps = {
- if (is(CLAMPED)) return;
+ if (stateIs(CLAMPED)) return;
if (!has(CLAMPS)) return;
present([DRESSED],
likes(PAIN) ? [
@@ -763,7 +804,7 @@ return new Object() {
};
def preClamps = {
if (!has(CLAMPS)) return;
- if (is(CLAMPED)) {
+ if (stateIs(CLAMPED)) {
clampsShow();
}
else {
@@ -773,7 +814,7 @@ return new Object() {
return 1.8;
};
def preCollar = {
- if (is(COLLARED)) return 1.1;
+ 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."],
@@ -788,7 +829,7 @@ return new Object() {
return 1.1;
};
def preStrip = { imageSpec = [DRESSED] ->
- if (is(NAKED)) return 1.1;
+ if (stateIs(NAKED)) return 1.1;
present(imageSpec, [
["Clothes off!", "Get naked, slut!"]]);
wait(10);
@@ -808,12 +849,12 @@ return new Object() {
// Post
def postEdge = {
- if (is(CHASTE)) return;
+ if (stateIs(CHASTE)) return;
if (!can(EDGE)) return;
edge(6, [TEASE]);
};
def postCum = {
- if (is(CHASTE)) return;
+ 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?"]]);
@@ -825,7 +866,7 @@ return new Object() {
}
};
def chastity = { pre ->
- if (is(CHASTE)) return;
+ if (stateIs(CHASTE)) return;
if (!has(CHASTITY)) return;
if (pre) {
present([DRESSED,TEASE], [
@@ -864,7 +905,7 @@ return new Object() {
}
def postChastity = {
chastity(false);
- if (is(CHASTE) && (getPermission(PERM_CHASTE) == null ||
+ 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,"],
@@ -1181,13 +1222,13 @@ return new Object() {
};
def playClamps = {
if (!has(CLAMPS)) return;
- if (!is(CLAMPED)) clamps();
+ if (!stateIs(CLAMPED)) clamps();
clampsShow();
return clampPulls(3 + getRandom(10));
};
def intClamps = {
if (!has(CLAMPS)) return;
- if (!is(CLAMPED)) return;
+ if (!stateIs(CLAMPED)) return;
clampsShow();
return clampPulls(getRandom(5));
};
@@ -1224,7 +1265,7 @@ return new Object() {
return null;
};
def removeToy = { toy, imageSpec ->
- if (!is(toy)) return;
+ if (!stateIs(toy)) return;
def toyName = stateToyName(toy);
present(imageSpec, [
["OK,"],
@@ -1295,15 +1336,15 @@ return new Object() {
'postChastity': postChastity
];
def sessionPlay = {
- if (is(CHASTE)) sessionToys[CHASTITY] = getTime();
+ if (stateIs(CHASTE)) sessionToys[CHASTITY] = getTime();
def playScope = [
- 'randRange': randRange,
- 'currentPunishment': getPunish,
+ 'randRange': { min, max -> randRange(min, max) },
+ 'currentPunishment': { getPunish() },
'sessionAborted': { return sessionAborted },
- 'can': can,
- 'has': has,
- 'is': is,
- 'likes': likes,
+ 'can': { return can(it) },
+ 'has': { return has(it) },
+ 'is': { return stateIs(it) },
+ 'likes': { return likes(it) },
'punishMultiple': (float)1.0,
'prop': load('toy') ?: [:]
];
@@ -1355,7 +1396,7 @@ return new Object() {
'activities': activityList
];
final eval = { expr -> Eval.me('toy', playScope, expr.toString()) };
- if (is("DEBUG")) {
+ if (stateIs("DEBUG")) {
// Check everything in DOMME evaluates and resolves
final checkIsFunc = { group, name ->
if (!name) return;
@@ -1410,7 +1451,7 @@ return new Object() {
postChastity();
}
wait(getRandom(10) + 5);
- def toys = TOYTOYS.findAll { t -> is(t) };
+ def toys = TOYTOYS.findAll { t -> stateIs(t) };
Collections.shuffle(toys);
if (toys) {
if (!goodToy) {
@@ -1430,7 +1471,7 @@ return new Object() {
}
};
}
- if (is(NAKED)) {
+ if (stateIs(NAKED)) {
if (goodToy && getRandom(1)) {
present([DRESSED], [
["Put some clothes back on.", "Cover yourself up!"]]);
@@ -1516,7 +1557,7 @@ return new Object() {
dress([[LINGERIE,nTITS]]);
if (rt && sessionSummon([LINGERIE,nTITS])) {
preStrip([LINGERIE,nTITS]);
- def toys = TOYTOYS.findAll { t -> is(t) };
+ def toys = TOYTOYS.findAll { t -> stateIs(t) };
Collections.shuffle(toys);
toys.each { removeToy(it, [LINGERIE,nTITS]) };
present([LINGERIE,nTITS], [
@@ -1530,7 +1571,7 @@ return new Object() {
removeEvent(REDRESS);
set(NAKED, false);
// Assume toy will release himself
- TOYTOYS.findAll { t -> is(t) }.each {
+ TOYTOYS.findAll { t -> stateIs(t) }.each {
removeEvent("$RELEASEFROM-$it");
set(it, false);
};
@@ -1679,10 +1720,10 @@ return new Object() {
["I'm not going to like this, am I?", "There is no way this ends well for you."],
["Out with it.", "Let me hear it."]]);
def opts = [ ];
- if (is(CHASTE)) {
+ if (stateIs(CHASTE)) {
opts.push([lbl: "Freed from chastity", act: confessChastityRelease]);
}
- if (!is(CHASTE)) {
+ if (!stateIs(CHASTE)) {
opts.push([lbl: "Stroked", act: confessStroked]);
}
opts.push([ lbl: "Nothing listed (phew!)", act: null ]);
@@ -1759,7 +1800,7 @@ return new Object() {
[ "Will you be good while I'm gone?", "You will behave as I expect?" ]]);
wait(10);
postChastity();
- def toys = TOYTOYS.findAll { t -> is(t) };
+ def toys = TOYTOYS.findAll { t -> stateIs(t) };
Collections.shuffle(toys);
toys.each { removeToy(it, [DRESSED,nTEASE]) };
removeEvent(REDRESS);
@@ -1768,7 +1809,7 @@ return new Object() {
[ "Back later!", "See you soon, toy" ]]);
wait(5);
}
- else if (has(CHASTITY) && !is(CHASTE)) {
+ else if (has(CHASTITY) && !stateIs(CHASTE)) {
leaveNote("I want you in chastity until I return!", CHASTE);
}
DOMME = loadDomme(OWNER);
@@ -1856,7 +1897,7 @@ return new Object() {
def setupShowState = {
show(
[CHASTE, COLLARED, CUFFED, CLAMPED, GAGGED, NAKED].collect {
- def v = is(it);
+ def v = stateIs(it);
return "$it: $v\n".capitalize()
}.join());
showButton("OK");
@@ -1943,7 +1984,7 @@ return new Object() {
}
}
else {
- if (is("DEBUG")) {
+ if (stateIs("DEBUG")) {
def timeStr = localTimeOf(e.event.time).format(soonFormatter);
show("$e.name: $timeStr -> ${e.event.func}(${e.event.arg})");
}
@@ -1958,23 +1999,23 @@ return new Object() {
[ lbl: "Calendar", act: setupShowCalendar ],
[ lbl: "Confess", act: confess ],
];
- if (is("DEBUG")) {
+ if (stateIs("DEBUG")) {
opts.push([ lbl: "Status", act: setupShowState ]);
opts.push([ lbl: "Play", act: playEvent, arg: true ]);
opts.push([ lbl: "Activity", act: playActivity ]);
}
- if (is(NAKED)) {
+ if (stateIs(NAKED)) {
opts.push([ lbl: "May I wear clothes", act: requestClothes ]);
}
- TOYTOYS.findAll { is(it) }.each {
+ TOYTOYS.findAll { stateIs(it) }.each {
opts.push([ lbl: "May I be un-$it".toString(), act: requestRelease, arg: it ]);
};
opts.push([ lbl: "Back", act: null ]);
- def opt = getSelectedValue(is("DEBUG") ? DOMME : "Yes, toy?", opts.collect { it.lbl });
+ def opt = getSelectedValue(stateIs("DEBUG") ? DOMME : "Yes, toy?", opts.collect { it.lbl });
def act = opts[opt].act;
if (act)
act(opts[opt].arg);
- else if (is("DEBUG"))
+ else if (stateIs("DEBUG"))
return "toy";
}
addAvail(clickTime);