{ 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) { toy.sessionAborted = 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...", "Keeping going..."]]); if (mightCum(getRandom(20) + 5)) return; } present(imageSpec, [ ["Hands off!", "Stop!"]]); if (mightCum(getRandom(5) + 5)) return; allowedTime /= 2; }; }; 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.addActivity("playEdges", { toy.playEdges() }); toy.addActivity("preEdge", { toy.preEdge() }); }