diff options
Diffstat (limited to 'scripts')
-rw-r--r-- | scripts/toy.groovy | 237 |
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); |