From d9c1c0248a79ce50f1098af7ba7e37af27966b30 Mon Sep 17 00:00:00 2001
From: sarcoph <83266791+sarcoph@users.noreply.github.com>
Date: Tue, 3 May 2022 23:45:30 -0800
Subject: [PATCH 01/28] cleanup and typos
---
.../code/modules/arousal/arousalhud.dm | 83 +++++--------
hyperstation/code/obj/farming/farming.dm | 4 +-
hyperstation/code/obj/fleshlight.dm | 22 ++--
hyperstation/code/obj/pregnancytester.dm | 3 +-
.../code/modules/arousal/arousal.dm | 110 +++++++-----------
5 files changed, 81 insertions(+), 141 deletions(-)
diff --git a/hyperstation/code/modules/arousal/arousalhud.dm b/hyperstation/code/modules/arousal/arousalhud.dm
index 509438813..2058307e9 100644
--- a/hyperstation/code/modules/arousal/arousalhud.dm
+++ b/hyperstation/code/modules/arousal/arousalhud.dm
@@ -11,7 +11,7 @@
for(var/obj/item/organ/genital/G in U.internal_organs)
if(!G.nochange)
if(!G.dontlist)
- dat += "[G.mode == "hidden" ? "[G.name] (Hidden)" : (G.mode == "clothes" ? "[G.name] (Hidden by Clothes)" : (G.mode == "visable" ? "[G.name] (Visable)" : "[G.name] (Visable)"))]
"
+ dat += "[G.mode == "hidden" ? "[G.name] (Hidden)" : (G.mode == "clothes" ? "[G.name] (Hidden by Clothes)" : (G.mode == "visable" ? "[G.name] (Visible)" : "[G.name] (Visible)"))]
"
dat += {"
Contexual Options
=Fw$(?IFsa%|>bcC%pG{?n~Xi`RU zFxH5yo?h2mc#&k%$sn3WQ5oZFX8_3Ymquaa5a=o7&KBG^_c6{Jg&^#-x1J!Xht|F} z&om#An;dej*72?myUmTX{?9j2s9Kd(R_BnAfU23~9zT(1y;THb-!Ip2-pAyDx>xy$ zR%`t6)y9$p-RE!(B3xhZK?*=QD)QS+qU~bd?FU?yWk}-Wp4L8M!R_&EJ2QJIeE01 z=+D+#KlvV+9(=Q1_&(1%l-Y>t{InkMz-395-cBcMMi}K?-r6xce2p~0dAqX~#d@8^ zH|k5-iT933wK%Q4f(6{OdX@0z(gqqQyP1BbBzI8pEHEB8ejdq2sa 8XgrLXd&{ z?Q+{$(K%jc8G@KM3ecTLW``o+flh35_8ss2=KUXAo$RAWFI0g3m%0!6eo$CT;T-=A zi2rFvFLaSnt+I0aI*~Qmdaflc2gs7 _ijY>eP3@Wy6yzJTrpJkNJ&Wc3PVdO|nj*{l z5oKd+y4~;M*R!G@-uj6EKB;W_^%u#*L3HJ!%{Ryy_K$U{{=3q9S4RVW>3=L@{!8nt zlUN!~*3~2V3anr&Cj#BR<7rp2*b6 X`yL)ec5q=Jz?9V$`=ymot*mZ@#wA;8_Q7yOtQ{snM)`knNA2Jo+CS{AWu4vn7px zH@Cb+d7th+9BViYg=4=hz%Svyw|CzxeiaCIJm?)eC=mm`;(+lxTNaYx7Hlsbs2#&` zqvPhLH_@>1^Y 6EA+gK%7L}viuT{h?h8r*DDcqOH_#bQh_uvS9 zWfGAGns@yfcP2ysLF3cn^)%@wyvdxI2?Z2$h{qO|Yy)Zih)Ssh i#6<(e#HAVNEXM$UK6d*BE8)zB9@nkXn8HZK5bRc=9fzjbrYU zOK %x6+1ldd2Xh_CG>kv5gGFk#<)5_6FIPt#xbEmsVJ5otg4O;;0!k$H-S z7Zs5^Ut}zPSVO_9*}3!_**V+rMLr83Iw<(YI9H;i1T}+GvoJh1GrpIjsj}N&pM)i% zj6_<^yo1wL|~j#y?x2>5cBp~Y!qF&%J;NcY~{9a)s)J3(M(efMOef*AC{^Tyd&c* zqxoZL8ze;)9OF=3mHvL?FrgZv9T3HieYcD~_r9a+X(H*mfat3n??>c>`Qao%uZO{I zdzi8cSPkaX3lt(UWeP~Ki~=Jf_!JPt%U+yWQh7eym`B;xuF}>)oz3@D1}-NTawtWQ zE2SEue;u~>^P?2~CpG}*j%K) rOE3>>=E`j3aclP;gEM 5G>1Wk>}5 zmD=y~=m*omI$0s?w0?&tdv6bOmMIaeNn4xJ$Of~?j rJ$oMoCet@!GuD)}^&UeN7S<&Lz?yPKdl+ zi$!;T2LJs5_jJ;LYU?o)3a2Te@tO+vq F5b4joFSc*OwKF$qhmlbEo3|CZ)Y23j8>wVzLqM34Q`oufef9c>9?|gxcdtUcFLJX@X{! zmV3w)FvMJ?kv4FO8~tc^kEvZkQ)W4@GWZ*aiJt%-U_wg$$MVq>)lCiTXzWKUvab!5 zA2;!JcGG@+)@)N`lk1}(l9PIbjiR#M{?SI<(XnIT#Euhph};>*q6YTYZnT}hRbzm@ z{RKe(H+}xiFsa^r1oAcA(zKs;E!n}^5in2s0j7(AH=q-Av(p)PQd1qs6QET4(uWzI z`K(+4GEr;b=b=b);d%} 4AL~V)Rxo z;(x1dpiR}W8td?5Q6Ex Sl5(dR0Q#%E?@! z0=U0(B{0PGOqLpya;na`C&}czfj}DjL!VP%>4VzO^npR=nHBj3ly>n(`Xy)i9LA zx; uwBXLr?QD+=|_2lQ?zts>RBF_&US ze6swR`R)0kR>#{@gX1WC0ZIlgi9vSB4DBg|X mqeS-q_?`sFzIkjD8k?(nzf z?mAUeJWpG+aWsr@~ jzOve+CUQr6KI@%-d#59glSu`d~)7L2!N9tH)vTG~~X4Rt$^;Q3UplvVVt zH95XwKPyrtGA4n(il%S(Q-fvnG2i+KQtX^$s=&y$OdIp%sS`;UjWHy%vvabmLZf0L zervgc0x}0c2icUR7$RicSP8q*G&=@8TKr7#? `#irIy1B+y96@s RgpKRHk0m}Ay^1C8skRyGD7dj=JE$31Cm ~#wP2!^u~t5SIO?_1BYDu*Vyro3)v|k+$JGwNBAT` z8t@fjejX#_kj)+t3(J)+ijhP*1H$Q;1EN&F&!MujZ%b*%&>|_q+2zIl3Cqq_27^?t zx+?mp5j2nCI9;aZ-HH#Z;Ft_!^WxcLT4ywe>ddM*>^<$#A8W7R^-C8hpl4QV6EBJ= zdbdM!=p*mRjv $_%gkb{>>9okBYG0gaexU$`YPKoh& 3VcPwH)TP2N zj7$-y2=GtvQg%!I!dji{<$}Sdd2(ggiHXNwhEmZ-jGljp$?w5%Nd(O8xPKBU2I3(A zU8V|wpO0>$O3JFY(YXgxObqGAUGXG!6410o3E9uh`%i!;kbm@n }3PTO^cuWODu>Al^N!x4~)_N^{-#z9X+USg|<`ZKLFe;y6^x1 literal 2632 zcmY*Y2~-kT7bdba$w`|MwHnK^$;MJtYOEC33^g@%Dp$xQwNxB8TtEf4axKd(RJ2Jb zQ!};PP%yW01NWs8+#pw!R4|Z!JO7z;rgzRg-?{hPci#KH``!CCR%SBNO40xTKn8xr z#1;V9VeliQc5YX`S^br>4fZyc_NLo7UR@~?i8ePk`}_L`Nl$xwdvD#k#pm;RJl^8s z;?&gC$jHccTUS@t+S(e0LYbSJqtoewgM(}~yP~3^wzjskv~+oSnZaOCsnn{fDp#pU zh3yMFf^Dtr00!*Q+uKQdg#FE66VD*`K%anMACx};5SEuY*eD&Vc_3j@w=StLvQL!a zl2mlT-tbeKo1?b3h2{FjAJR+1-~2dn*@*^6k6oM#%Bq^l==)IHr@uSYgC~1U>*FxG zOXW}1{MY 1#Jgy$;Z{hsa tF8h7QVh6>p?z51QhsVEp;rEK8D7gh9uMt<{w)%7!|8IBzxxdU z61^9nMUrvlf64Z uBdJs{Gzx46^-toUvrF=ELi`Vx>Z*7glqEMe7 z(vu=b6R9m?-2UpRchfEIG~yBPhM;O13pV;fp@x|0t642VuO7EcV&Lr}nQQu_=E5B# z!9DS5k =;L2g=Wt3|Z$W*$p||K;;(t+SRZ+?3^i5gAY;AVQDkTV`UeA zIQ8EXeO8rt`}?QJuNrc1uSx}V#W8Yf2I57YF4*1CS8pi7czuf=qbIH@ %Y0&(=7317opQMS* z7jn$eR}T;|xp8X${`CJy(<)VN5{63ghOS2-V>J3m$aL} 6bvLslD{u5$&9S1)IrRX%|kO-DeD^KPX zbw0WuicZ@#i$Uc3CN!8xr;P1QZNI`~KUIvCu8sqf!dwYt`PeB`Cx5OQ-#oX~)&P$y z-g1uNZIu&zWaV*DVX6$b3aQ{B2`jF~L@pC?cn#om!Y14KGvznho$~HszJ_wMD)UAe zu!ce%O? (He3$svuaU3yW+LJJZ_A;!iJ3e!Uo3Xhi)hiQ3%L>1(nzI_f^q!$ zeLu-?y6VpfbVCfn< qt%p zw}BegZjZ~x5Q~9Bom#m1XGK87tZ?}?uwmsi(`(Oj9{{Szz*yh9C3foS_VlPQoNiv| zwx@s@6Le*QuFLIkJR*&si6QE;ZXD#FW}?$;d^TA!o)HVZl=}^A;c_2hQ*R&YyWY?A z0Hg0G$-1S}vR#O$Lo67iwa(MyVRT9lx4=zYS?)?O&bzGK0|X14Y8iw}ZP+krVFNVm ziYZbHlwu)q*5S(%lrP*eY${tAVXqu`)NPBYRDNfXrKSAN5i40{H2rNz_r%ipqdLId z 7eQGP29Gt`!H_{<4yT;AEEsxCvXLkGQr5x+5=Z?=6hq%*F zn5ZcESV(6n-f)%A6 3 z_?^_XGV8YYXAKZ3)9ih5!g!f()6u9Ffgq ~}-ZQAl8l
zL(bZm9i4X{Rk3lpnZ>OdtRoc+I_ElE+;kO|`0IcU zd*1YUL~~*CtJ;daK1>7`{Mk9Sz0JjTsWCHfGmWK}I|p`OR*Xs^l($aT&ZuZP?#zgg zQ?&kT9(8`Hu)irpG8J9mJ5aP_HN-=ksreraE4jvsmgvaPY*YR(cN&> @GNGW>AIW>yz; uTKi2qDk;kat=`tMQP5y>+5emR(382z3AJ-)e@fp8tcpsv{UY+ zr4PvojGO33HP3wDyR@TvIhJzf$uSV(#ep31K?xF^6}~^`I;h+0%NT~~DcTsQO{3P7 z>a&p^F!tvghREz)p`5dz#bKgEM-;pXrL;ZM!jLuhiIp6o!?a3&$O|dU{AMEQ5O`Lo z!@4=AOVgfFI{b2w$a^YnS~y=N5K5$&B8#`k=kXd5TA~v1e+2;m<^%ohGEljs4a`C> z#>o5*`HW(L6Ph#daO-~E+ipm0Mlwws=$A(}KJUV*S!Q@mU(yp)3|ZXA)86FS{gFra u%Acgube{!Ekz-&WPN@}iYJ2rrFxGd+h-5S1@@Mdm=isJRCgny*{Qm&g-ZIbt From 975eec750370b1dd911559811a0161ca27f8c079 Mon Sep 17 00:00:00 2001 From: sarcoph <83266791+sarcoph@users.noreply.github.com> Date: Thu, 5 May 2022 03:02:43 -0800 Subject: [PATCH 18/28] makes selectable butt size a variable --- code/__DEFINES/hyper_arousal.dm | 1 + code/modules/client/preferences.dm | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/code/__DEFINES/hyper_arousal.dm b/code/__DEFINES/hyper_arousal.dm index d3f9d6ceb..6b16fba9b 100644 --- a/code/__DEFINES/hyper_arousal.dm +++ b/code/__DEFINES/hyper_arousal.dm @@ -5,4 +5,5 @@ #define BELLY_STRETCH_SIZE 6 // for flavor text #define BUTT_MIN_SIZE 0 +#define BUTT_MAX_SIZE_SELECTABLE 5 #define BUTT_MAX_SIZE 8 \ No newline at end of file diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index fb1a9fa74..d0abb7d46 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -2435,9 +2435,10 @@ GLOBAL_LIST_EMPTY(preferences_datums) features["belly_size"] = clamp(new_bellysize, BELLY_MIN_SIZE, BELLY_MAX_SIZE) if("butt_size") - var/new_buttsize = input(user, "Butt size :\n([BUTT_MIN_SIZE]-5)", "Character Preference") as num|null + var/new_buttsize = input(user, "Butt size :\n([BUTT_MIN_SIZE]-[BUTT_MAX_SIZE_SELECTABLE])", "Character Preference") as num|null if(new_buttsize != null) - features["butt_size"] = clamp(new_buttsize, BUTT_MIN_SIZE, 5) //Restricted to 5 in menu, because we have chems to make them big IC, like with breasts and what not. + features["butt_size"] = clamp(new_buttsize, BUTT_MIN_SIZE, BUTT_MAX_SIZE_SELECTABLE) + //Restricted to 5 in menu, because we have chems to make them big IC, like with breasts and what not. if("vag_shape") var/new_shape From 03bf3afede57e7697bbc4ba010f8d293bccf453e Mon Sep 17 00:00:00 2001 From: sarcoph <83266791+sarcoph@users.noreply.github.com> Date: Thu, 5 May 2022 03:07:13 -0800 Subject: [PATCH 19/28] getpercentaroused a little more sane to read --- modular_citadel/code/modules/arousal/arousal.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modular_citadel/code/modules/arousal/arousal.dm b/modular_citadel/code/modules/arousal/arousal.dm index 3da56d68b..d465c1f61 100644 --- a/modular_citadel/code/modules/arousal/arousal.dm +++ b/modular_citadel/code/modules/arousal/arousal.dm @@ -112,7 +112,7 @@ * despite the name of this, it actually returns a number between 0 and 100 */ /mob/living/proc/getPercentAroused() - var/percentage = ((100 / max_arousal) * arousalloss) + var/percentage = ((arousalloss / max_arousal) * 100) return percentage /mob/living/proc/isPercentAroused(percentage)//returns true if the mob's arousal (measured in a percent of 100) is greater than the arg percentage. From e555f0ea9143d8b83b70fdf4271eeaffb950fd25 Mon Sep 17 00:00:00 2001 From: sarcoph <83266791+sarcoph@users.noreply.github.com> Date: Thu, 5 May 2022 03:19:23 -0800 Subject: [PATCH 20/28] standardize genital visibility --- code/__DEFINES/hyper_arousal.dm | 7 ++++++- .../code/modules/arousal/arousalhud.dm | 10 +++++++++- .../code/modules/arousal/arousal.dm | 19 +++++-------------- .../code/modules/arousal/organs/genitals.dm | 7 +++---- 4 files changed, 23 insertions(+), 20 deletions(-) diff --git a/code/__DEFINES/hyper_arousal.dm b/code/__DEFINES/hyper_arousal.dm index 6b16fba9b..438a97898 100644 --- a/code/__DEFINES/hyper_arousal.dm +++ b/code/__DEFINES/hyper_arousal.dm @@ -6,4 +6,9 @@ #define BUTT_MIN_SIZE 0 #define BUTT_MAX_SIZE_SELECTABLE 5 -#define BUTT_MAX_SIZE 8 \ No newline at end of file +#define BUTT_MAX_SIZE 8 + + +#define GENITALS_HIDDEN "hidden" +#define GENITALS_CLOTHES "clothes" +#define GENITALS_VISIBLE "visible" diff --git a/hyperstation/code/modules/arousal/arousalhud.dm b/hyperstation/code/modules/arousal/arousalhud.dm index 375728cf5..757fd2330 100644 --- a/hyperstation/code/modules/arousal/arousalhud.dm +++ b/hyperstation/code/modules/arousal/arousalhud.dm @@ -11,7 +11,15 @@ for(var/obj/item/organ/genital/G in U.internal_organs) if(!G.nochange) if(!G.dontlist) - dat += "[G.mode == "hidden" ? "[G.name] (Hidden)" : (G.mode == "clothes" ? "[G.name] (Hidden by Clothes)" : (G.mode == "visable" ? "[G.name] (Visible)" : "[G.name] (Visible)"))]
" + var/genital_visibility = "[G.name] (Visible)" + switch(G.mode) + if(GENITALS_HIDDEN) + genital_visibility = "[G.name] (Hidden)" + if(GENITALS_CLOTHES) + genital_visibility = "[G.name] (Hidden by Clothes)" + if(GENITALS_VISIBLE) + genital_visibility = "[G.name] (Visible)" + dat += "[genital_visibility]
" dat += {"
Contexual Options
"} var/obj/item/organ/genital/penis/P = user.getorganslot("penis") diff --git a/modular_citadel/code/modules/arousal/arousal.dm b/modular_citadel/code/modules/arousal/arousal.dm index d465c1f61..e7f8b4f17 100644 --- a/modular_citadel/code/modules/arousal/arousal.dm +++ b/modular_citadel/code/modules/arousal/arousal.dm @@ -489,11 +489,9 @@ /mob/living/carbon/human/proc/pick_masturbate_genitals() var/obj/item/organ/genital/ret_organ var/list/genitals_list = list() - var/list/worn_stuff = get_equipped_items() - for(var/obj/item/organ/genital/G in internal_organs) if(G.can_masturbate_with) //filter out what you can't masturbate with - if(G.is_exposed(worn_stuff)) //Nude or through_clothing + if(G.is_exposed()) //Nude or through_clothing if(!G.dontlist) genitals_list += G if(genitals_list.len) @@ -504,10 +502,8 @@ /mob/living/carbon/human/proc/target_genitals(mob/living/carbon/human/T) //used for targeting others var/obj/item/organ/genital/ret_organ var/list/genitals_list = list() - var/list/worn_stuff = get_equipped_items() - for(var/obj/item/organ/genital/G in T.internal_organs) - if(G.is_exposed(worn_stuff)) //Nude or through_clothing + if(G.is_exposed()) //Nude or through_clothing if(!G.dontlist) genitals_list += G if(genitals_list.len) @@ -518,13 +514,9 @@ /mob/living/carbon/human/proc/pick_climax_genitals() var/obj/item/organ/genital/ret_organ var/list/genitals_list = list() - var/list/worn_stuff = get_equipped_items() - for(var/obj/item/organ/genital/G in internal_organs) - if(G.can_climax) //filter out what you can't masturbate with - if(G.is_exposed(worn_stuff)) //Nude or through_clothing - if(!G.dontlist) - genitals_list += G + if(G.can_climax && G.is_exposed() && !G.dontlist) + genitals_list += G if(genitals_list.len) ret_organ = input(src, "with what?", "Climax", null) as null|obj in genitals_list return ret_organ @@ -744,8 +736,7 @@ continue // no wombs or testicles var/mob/living/carbon/partner var/check_target - var/list/worn_stuff = get_equipped_items() - if(current_genital.is_exposed(worn_stuff)) + if(current_genital.is_exposed()) if(src.pulling) if(iscarbon(src.pulling)) check_target = src.pulling diff --git a/modular_citadel/code/modules/arousal/organs/genitals.dm b/modular_citadel/code/modules/arousal/organs/genitals.dm index bdf4d7856..9edea910a 100644 --- a/modular_citadel/code/modules/arousal/organs/genitals.dm +++ b/modular_citadel/code/modules/arousal/organs/genitals.dm @@ -78,22 +78,21 @@ if("Always visible") through_clothes = TRUE hidden = FALSE - mode = "visible" + mode = GENITALS_VISIBLE if(!(src in owner.exposed_genitals)) owner.exposed_genitals += src if("Hidden by clothes") through_clothes = FALSE hidden = FALSE - mode = "clothes" + mode = GENITALS_CLOTHES if(src in owner.exposed_genitals) owner.exposed_genitals -= src if("Always hidden") through_clothes = FALSE hidden = TRUE - mode = "hidden" + mode = GENITALS_HIDDEN if(src in owner.exposed_genitals) owner.exposed_genitals -= src - if(ishuman(owner)) //recast to use update genitals proc var/mob/living/carbon/human/H = owner H.update_genitals() From 42f33b2194b186879370ac35e964a74dea9783a5 Mon Sep 17 00:00:00 2001 From: sarcoph <83266791+sarcoph@users.noreply.github.com> Date: Thu, 5 May 2022 05:01:50 -0800 Subject: [PATCH 21/28] portallight cleanup --- code/__DEFINES/hyper_arousal.dm | 1 - hyperstation/code/obj/fleshlight.dm | 153 +++++++++--------- .../code/modules/arousal/arousal.dm | 34 ++-- 3 files changed, 94 insertions(+), 94 deletions(-) diff --git a/code/__DEFINES/hyper_arousal.dm b/code/__DEFINES/hyper_arousal.dm index 438a97898..a0450b138 100644 --- a/code/__DEFINES/hyper_arousal.dm +++ b/code/__DEFINES/hyper_arousal.dm @@ -8,7 +8,6 @@ #define BUTT_MAX_SIZE_SELECTABLE 5 #define BUTT_MAX_SIZE 8 - #define GENITALS_HIDDEN "hidden" #define GENITALS_CLOTHES "clothes" #define GENITALS_VISIBLE "visible" diff --git a/hyperstation/code/obj/fleshlight.dm b/hyperstation/code/obj/fleshlight.dm index a7410a204..2bbd063db 100644 --- a/hyperstation/code/obj/fleshlight.dm +++ b/hyperstation/code/obj/fleshlight.dm @@ -94,7 +94,6 @@ var/paired = 0 var/obj/item/portalunderwear var/useable = FALSE - var/option = "" /obj/item/portallight/examine(mob/user) . = ..() @@ -104,103 +103,103 @@ . += "The device is paired, and awaiting input. " /obj/item/portallight/attack(mob/living/carbon/holder, mob/living/user) -//use portallight! nearly the same as the fleshlight apart from you have a buddy! - var/obj/item/organ/genital/penis/P = holder.getorganslot("penis") if(inuse) //just to stop stacking and causing people to cum instantly return - if(!useable) - to_chat(user, "It seems the device has failed or your partner is not wearing their device.") - if(holder == user)//if your using it on yourself, more options! otherwise, just fuck. - option = input(usr, "Choose action", "Portal Fleshlight", "Fuck") in list("Fuck", "Lick", "Touch") - else - option = "Fuck" - var/obj/item/organ/genital/target_genital - if(istype(portalunderwear.loc, /obj/item/organ/genital)) //Sanity check. Without this it will runtime error. - target_genital = portalunderwear.loc - if(!target_genital) + if(!UpdateUsability()) + to_chat(user, "\The [src] appears to be currently unusable.") return + var/option = "Fuck" + if(holder == user) + option = input(usr, "Choose action", "Portal Fleshlight", "Fuck") in list("Fuck", "Lick", "Touch") + var/obj/item/organ/genital/target_genital = portalunderwear.loc var/mob/living/carbon/human/target = target_genital.owner - if(option == "Fuck"&&!P.is_exposed()) //we are trying to fuck with no penis! + var/obj/item/organ/genital/penis/P = holder.getorganslot("penis") + if(option == "Fuck" && !P.is_exposed()) to_chat(user, "You don't see anywhere to use this on.") return - else //other options dont need checks - inuse = 1 - if(!(holder == user)) //if we are targeting someone else. - holder.visible_message("[user] is trying to use [src] on [holder]'s penis.", "[user] is trying to use [src] on your penis.") - if(!do_mob(user, holder, 3 SECONDS)) //3 second delay - inuse = 0 - return - //checked if not used on yourself, if not, carry on. - if(option == "Fuck") - playsound(src, 'sound/lewd/slaps.ogg', 30, 1, -1) //slapping sound for fuck. - inuse = 0 - if(!(holder == user)) - holder.visible_message("[user] pumps [src] on [holder]'s penis.", "[user] pumps [src] up and down on your penis.") - else - if(option == "Fuck") - user.visible_message("[user] pumps [src] on their penis.", "You pump the fleshlight on your penis.") - if(option == "Lick") - user.visible_message("[user] licks into [src].", "You lick into [src].") - if(option == "Touch") - user.visible_message("[user] touches softly against [src].", "You touch softly on [src].") - if(prob(30)) //30% chance to make your partner moan. - target.emote("moan") - if(option == "Fuck")// normal fuck - to_chat(target, "You feel a [P.length] inch, [P.shape] shaped penis pumping through the portal into your [target_genital.name].")//message your partner, and kinky! - if(prob(30)) //30% chance to make them moan. + /* + WARMUP START - prevents spam/instant climax + */ + inuse = TRUE + if(holder != user) + holder.visible_message("[user] is trying to use [src] on [holder]'s penis.",\ + "[user] is trying to use [src] on your penis.") + if(!do_mob(user, holder, 3 SECONDS)) + inuse = FALSE + return + inuse = FALSE + /* + WARMUP END - proceed with the action + */ + switch(option) + if("Fuck") + playsound(src, 'sound/lewd/slaps.ogg', 30, 1, -1) + if(holder == user) + user.visible_message("[user] pumps [src] on their penis.", + "You pump the fleshlight on your penis.") + else + holder.visible_message("[user] pumps [src] on [holder]'s penis.",\ + "[user] pumps [src] up and down on your penis.") + to_chat(target, "You feel a [P.length] inch, [P.shape] shaped penis pumping through the portal into your [target_genital.name].") + if(prob(30)) holder.emote("moan") - if(prob(30)) //30% chance to make your partner moan. - target.emote("moan") holder.adjustArousalLoss(20) target.adjustArousalLoss(20) - target.do_jitter_animation() //make your partner shake too! - // Why not have a probability to cum when someone's getting nailed with max arousal?~ - if (target.can_orgasm() && prob(5)) - // if vagina is not exposed, the climax will not cause a spill - target.mob_climax_outside(target_genital, target_genital.is_exposed()) - if (holder.can_orgasm()) + target.do_jitter_animation() + if(target.can_orgasm() && prob(5)) + target.mob_climax_outside(target_genital, spillage = target_genital.is_exposed()) + if(holder.can_orgasm()) var/mob/living/carbon/human/O = holder var/impreg_chance = target_genital.name == "vagina" && !P.condom && !P.sounding O.mob_climax_partner(P, target, FALSE, impreg_chance, FALSE, TRUE) - if(option == "Lick") + if("Lick") + user.visible_message("[user] licks into [src].",\ + "You lick into [src].") to_chat(target, "You feel a tongue lick you through the portal against your [target_genital.name].") target.adjustArousalLoss(10) - if(option == "Touch") + if("Touch") + user.visible_message("[user] touches softly against [src].",\ + "You touch softly on [src].") to_chat(target, "You feel someone touching your [target_genital.name] through the portal.") target.adjustArousalLoss(5) - return + if(prob(30)) + target.emote("moan") ..() /obj/item/portallight/proc/updatesleeve() //get their looks and vagina colour! - cut_overlays()//remove current overlays - var/obj/item/organ/genital/G - if(istype(portalunderwear.loc, /obj/item/organ/genital)) //Sanity check. Without this it will runtime. - G = portalunderwear.loc - if(!G) - useable = FALSE + cut_overlays() + if(!UpdateUsability()) return - var/mob/living/carbon/human/H = G.owner - if(H) //if the portal panties are on someone. - sleeve = mutable_appearance('hyperstation/icons/obj/fleshlight.dmi', "portal_sleeve_normal") - if(H.dna.species.name == "Lizardperson") // lizard nerd - sleeve = mutable_appearance('hyperstation/icons/obj/fleshlight.dmi', "portal_sleeve_lizard") - if(H.dna.species.name == "Slimeperson") // slime nerd - sleeve = mutable_appearance('hyperstation/icons/obj/fleshlight.dmi', "portal_sleeve_slime") - if(H.dna.species.name == "Avian") // bird nerd - sleeve = mutable_appearance('hyperstation/icons/obj/fleshlight.dmi', "portal_sleeve_avian") - sleeve.color = "#" + H.dna.features["mcolor"] - add_overlay(sleeve) - if(G.name == "vagina") - organ = mutable_appearance('hyperstation/icons/obj/fleshlight.dmi', "portal_vag") - organ.color = portalunderwear.loc.color - if(G.name == "anus") - organ = mutable_appearance('hyperstation/icons/obj/fleshlight.dmi', "portal_anus") - organ.color = "#" + H.dna.features["mcolor"] - useable = TRUE - add_overlay(organ) - else - useable = FALSE + var/obj/item/organ/genital/G = portalunderwear.loc + var/mob/living/carbon/human/H = G.owner // H is verified by UpdateUsability() + var/sleeve_species = "normal" + switch(H.dna.species.name) + if("Lizardperson") + sleeve_species = "lizard" + if("Slimeperson") + sleeve_species = "slime" + if("Avian") + sleeve_species = "avian" + sleeve = mutable_appearance('hyperstation/icons/obj/fleshlight.dmi', "portal_sleeve_[sleeve_species]") + sleeve.color = "#" + H.dna.features["mcolor"] + add_overlay(sleeve) + if(G.name == "vagina") + organ = mutable_appearance('hyperstation/icons/obj/fleshlight.dmi', "portal_vag") + organ.color = portalunderwear.loc.color + if(G.name == "anus") + organ = mutable_appearance('hyperstation/icons/obj/fleshlight.dmi', "portal_anus") + organ.color = "#" + H.dna.features["mcolor"] + add_overlay(organ) + +/obj/item/portallight/proc/UpdateUsability() + var/useable = FALSE + if(portalunderwear && istype(portalunderwear.loc, /obj/item/organ/genital)) + var/obj/item/organ/genital/loc_genitals = portalunderwear.loc + if(loc_genitals.owner && ishuman(loc_genitals.owner)) + useable = TRUE + src.useable = useable + return useable //Hyperstation 13 portal underwear diff --git a/modular_citadel/code/modules/arousal/arousal.dm b/modular_citadel/code/modules/arousal/arousal.dm index e7f8b4f17..682edba75 100644 --- a/modular_citadel/code/modules/arousal/arousal.dm +++ b/modular_citadel/code/modules/arousal/arousal.dm @@ -285,7 +285,8 @@ setArousalLoss(min_arousal) -/mob/living/carbon/human/proc/mob_climax_outside(obj/item/organ/genital/G, mb_time = 30, spillage = TRUE) //This is used for forced orgasms and other hands-free climaxes +/mob/living/carbon/human/proc/mob_climax_outside(obj/item/organ/genital/G, mb_time = 30, spillage = TRUE) +//This is used for forced orgasms and other hands-free climaxes var/total_fluids = 0 var/datum/reagents/fluid_source = null var/unable_to_come = FALSE @@ -736,21 +737,22 @@ continue // no wombs or testicles var/mob/living/carbon/partner var/check_target - if(current_genital.is_exposed()) - if(src.pulling) - if(iscarbon(src.pulling)) - check_target = src.pulling - else if(src.pulledby) - if(iscarbon(src.pulledby)) - check_target = src.pulledby - if(check_target) - var/mob/living/carbon/C = check_target - if(C.exposed_genitals.len || C.is_groin_exposed() || C.is_chest_exposed()) - partner = C - if(partner) - user_human.mob_climax_partner(current_genital, partner, mb_time = 0) - continue - user_human.mob_climax_outside(current_genital, mb_time = 0) + if(!current_genital.is_exposed()) + user_human.mob_climax_outside(current_genital, mb_time = 0) + continue + if(src.pulling) + if(iscarbon(src.pulling)) + check_target = src.pulling + else if(src.pulledby) + if(iscarbon(src.pulledby)) + check_target = src.pulledby + if(check_target) + var/mob/living/carbon/C = check_target + if(C.exposed_genitals.len || C.is_groin_exposed() || C.is_chest_exposed()) + partner = C + if(partner) + user_human.mob_climax_partner(current_genital, partner, mb_time = 0) + continue return From 5bd0427c22178328d88cfb25e8218f81d9464140 Mon Sep 17 00:00:00 2001 From: sarcoph <83266791+sarcoph@users.noreply.github.com> Date: Thu, 5 May 2022 05:08:18 -0800 Subject: [PATCH 22/28] mob_climax_forced -> mob_climax_instant "mob_climax_instant()" feels like a better representation of what the proc does. "forced" has a very negative connotation even though contextually, it's just used for any climaxes caused by indirect arousal gain, with no warmup time or selection menus. --- code/modules/mob/living/carbon/human/species.dm | 4 ++-- code/modules/mob/living/carbon/life.dm | 2 +- code/modules/ruins/objects_and_mobs/sin_ruins.dm | 2 +- code/modules/surgery/organs/vocal_cords.dm | 4 ++-- hyperstation/code/obj/fleshlight.dm | 2 +- hyperstation/code/obj/vibrator.dm | 4 ++-- modular_citadel/code/datums/status_effects/chems.dm | 2 +- modular_citadel/code/modules/arousal/arousal.dm | 2 +- .../modules/integrated_electronics/subtypes/manipulation.dm | 2 +- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index 57ce2307c..7238bae03 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -1648,7 +1648,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) if (target.canbearoused) target.adjustArousalLoss(5) if (target.can_orgasm() && HAS_TRAIT(target, TRAIT_MASO)) - target.mob_climax_forced() + target.mob_climax_instant() if (!HAS_TRAIT(target, TRAIT_NYMPHO)) stop_wagging_tail(target) @@ -2006,7 +2006,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) if(HAS_TRAIT(H, TRAIT_MASO)) H.adjustArousalLoss(damage * brutemod * H.physiology.brute_mod) if (H.can_orgasm()) - H.mob_climax_forced() + H.mob_climax_instant() else//no bodypart, we deal damage with a more general method. H.adjustBruteLoss(damage * hit_percent * brutemod * H.physiology.brute_mod) diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index 7484bdbd6..310eb3d7f 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -78,7 +78,7 @@ if(HAS_TRAIT(B, TRAIT_CHOKE_SLUT)) B.adjustArousalLoss(7) if (B.can_orgasm()) - B.mob_climax_forced() + B.mob_climax_instant() else SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "suffocation", /datum/mood_event/suffocation) else diff --git a/code/modules/ruins/objects_and_mobs/sin_ruins.dm b/code/modules/ruins/objects_and_mobs/sin_ruins.dm index c4ca810ba..edd7734a8 100644 --- a/code/modules/ruins/objects_and_mobs/sin_ruins.dm +++ b/code/modules/ruins/objects_and_mobs/sin_ruins.dm @@ -290,7 +290,7 @@ return if(user.can_orgasm()) var/mob/living/carbon/user_carbon = user - user_carbon.mob_climax_forced() + user_carbon.mob_climax_instant() to_chat(user, "You hear a voice in your head... \"You are worthy of my blessing, dear~\"") to_chat(user, "You feel overpowering pleasure surge through your entire body.") var/A = new /obj/item/reagent_containers/chalice/lust diff --git a/code/modules/surgery/organs/vocal_cords.dm b/code/modules/surgery/organs/vocal_cords.dm index 3d9b5d637..7c6fe05ca 100644 --- a/code/modules/surgery/organs/vocal_cords.dm +++ b/code/modules/surgery/organs/vocal_cords.dm @@ -581,7 +581,7 @@ for(var/V in listeners) var/mob/living/carbon/human/H = V if(H.canbearoused && H.has_dna() && HAS_TRAIT(H, TRAIT_NYMPHO)) // probably a redundant check but for good measure - H.mob_climax_forced() + H.mob_climax_instant() */ else if((findtext(message, family_friendly_words))) cooldown = COOLDOWN_MEME @@ -1123,7 +1123,7 @@ if(E.phase > 1) if(HAS_TRAIT(H, TRAIT_NYMPHO) && H.canbearoused && E.lewd) // probably a redundant check but for good measure addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, H, "Your [E.enthrallGender] pushes you over the limit, overwhelming your body with pleasure."), 5) - H.mob_climax_forced() + H.mob_climax_instant() H.SetStun(20) H.setArousalLoss(H.min_arousal) E.resistanceTally = 0 //makes resistance 0, but resets arousal, resistance buildup is faster unaroused (massively so). diff --git a/hyperstation/code/obj/fleshlight.dm b/hyperstation/code/obj/fleshlight.dm index 2bbd063db..8a6932906 100644 --- a/hyperstation/code/obj/fleshlight.dm +++ b/hyperstation/code/obj/fleshlight.dm @@ -64,7 +64,7 @@ C.do_jitter_animation() C.adjustArousalLoss(20) //make the target more aroused. if (C.can_orgasm()) - C.mob_climax_forced() //make them cum if they are over the edge. + C.mob_climax_instant() //make them cum if they are over the edge. return diff --git a/hyperstation/code/obj/vibrator.dm b/hyperstation/code/obj/vibrator.dm index f0911b4f3..bef2a7969 100644 --- a/hyperstation/code/obj/vibrator.dm +++ b/hyperstation/code/obj/vibrator.dm @@ -133,13 +133,13 @@ Code: to_chat(U, "You feel intense pleasure surge through your [G.name]") U.do_jitter_animation() if (U.can_orgasm()) - U.mob_climax_forced() + U.mob_climax_instant() if(3) //high, makes you stun to_chat(U, "You feel overpowering pleasure surge through your [G.name]") U.Jitter(3) U.Stun(30) if (U.can_orgasm()) - U.mob_climax_forced() + U.mob_climax_instant() if(prob(50)) U.emote("moan") diff --git a/modular_citadel/code/datums/status_effects/chems.dm b/modular_citadel/code/datums/status_effects/chems.dm index 2720bc97c..433c20135 100644 --- a/modular_citadel/code/datums/status_effects/chems.dm +++ b/modular_citadel/code/datums/status_effects/chems.dm @@ -620,7 +620,7 @@ else if (lowertext(customTriggers[trigger]) == "cum")//aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa if (HAS_TRAIT(C, TRAIT_NYMPHO) && lewd) if (C.getArousalLoss() > 80) - C.mob_climax_forced() + C.mob_climax_instant() C.SetStun(10)//We got your stun effects in somewhere, Kev. else C.adjustArousalLoss(10) diff --git a/modular_citadel/code/modules/arousal/arousal.dm b/modular_citadel/code/modules/arousal/arousal.dm index 682edba75..f41d1ee58 100644 --- a/modular_citadel/code/modules/arousal/arousal.dm +++ b/modular_citadel/code/modules/arousal/arousal.dm @@ -722,7 +722,7 @@ return //Just end it in that case. -/mob/living/carbon/proc/mob_climax_forced() +/mob/living/carbon/proc/mob_climax_instant() if(stat == DEAD) //corpses can't cum to_chat(src, "You can't do that while dead!") return diff --git a/modular_citadel/code/modules/integrated_electronics/subtypes/manipulation.dm b/modular_citadel/code/modules/integrated_electronics/subtypes/manipulation.dm index 6ec3687b4..e3eab59c5 100644 --- a/modular_citadel/code/modules/integrated_electronics/subtypes/manipulation.dm +++ b/modular_citadel/code/modules/integrated_electronics/subtypes/manipulation.dm @@ -30,7 +30,7 @@ var/orgasm_message = pick("A sharp pulse of electricity pushes you to orgasm!", "You feel a jolt of electricity force you into orgasm!") H.visible_message("\The [assembly] electrodes shock [H]!", "[orgasm_message]") playsound(src, "sound/effects/light_flicker.ogg", 30, 1) - H.mob_climax_forced() + H.mob_climax_instant() orgasm = TRUE else M.adjustArousalLoss(arousal_gain) From 8db668700610846ef28692e85087e7c5667d4b2a Mon Sep 17 00:00:00 2001 From: sarcoph <83266791+sarcoph@users.noreply.github.com> Date: Thu, 5 May 2022 08:17:59 -0800 Subject: [PATCH 23/28] documentation and modularization "mob_climax_cover" is now separate from "mob_climax_partner", and impreg chance is separated from those as well --- .../code/modules/arousal/arousalhud.dm | 15 +- .../code/modules/arousal/organs/genitals.dm | 6 + hyperstation/code/obj/fleshlight.dm | 41 +- .../code/modules/arousal/arousal.dm | 391 +++++++++--------- tgstation.dme | 1 + 5 files changed, 241 insertions(+), 213 deletions(-) create mode 100644 hyperstation/code/modules/arousal/organs/genitals.dm diff --git a/hyperstation/code/modules/arousal/arousalhud.dm b/hyperstation/code/modules/arousal/arousalhud.dm index 757fd2330..586e6608c 100644 --- a/hyperstation/code/modules/arousal/arousalhud.dm +++ b/hyperstation/code/modules/arousal/arousalhud.dm @@ -334,7 +334,7 @@ obj/screen/arousal/proc/kiss() return //We got hands, let's pick an organ var/obj/item/organ/genital/picked_organ - picked_organ = pick_masturbate_genitals() + picked_organ = pick_climax_genitals(masturbation=TRUE, title="Masturbation") if(picked_organ) src << browse(null, "window=arousal") //closes the window mob_masturbate(picked_organ) @@ -378,7 +378,7 @@ obj/screen/arousal/proc/kiss() to_chat(src, "You cannot climax without choosing genitals.") return src << browse(null, "window=arousal") // close arousal window - mob_climax_partner_spillage(picked_organ, partner, FALSE) + mob_climax_partner_spillage(picked_organ, partner, impreg=FALSE) /mob/living/carbon/human/proc/climaxover(mob/living/carbon/human/partner) @@ -398,7 +398,7 @@ obj/screen/arousal/proc/kiss() if(P.sounding) to_chat(src, "You cannot do this action with a sounding in.") return - mob_climax_partner(picked_organ, partner, FALSE, FALSE, TRUE) + mob_climax_cover(picked_organ, partner) /mob/living/carbon/human/proc/clothesplosion() @@ -435,7 +435,7 @@ obj/screen/arousal/proc/kiss() to_chat(src, "You cannot impregnate someone with a sounding rod in.") return src << browse(null, "window=arousal") //alls fine, we can close the window now. - mob_climax_partner_spillage(picked_organ, partner, TRUE) + mob_climax_partner_spillage(picked_organ, partner, impreg=TRUE) /mob/living/carbon/human/proc/cumcontainer(mob/living/T) @@ -476,6 +476,11 @@ obj/screen/arousal/proc/kiss() cum_splatter_icon.Blend(icon('hyperstation/icons/effects/cumoverlay.dmi', "cum_obj"), ICON_MULTIPLY) add_overlay(cum_splatter_icon) +/mob/living/carbon/add_cum_overlay(type="normal") + var/mutable_appearance/cumoverlay = mutable_appearance('hyperstation/icons/effects/cumoverlay.dmi') + cumoverlay.icon_state = "cum_[type]" + add_overlay(cumoverlay) + /atom/proc/wash_cum() cut_overlay(mutable_appearance('hyperstation/icons/effects/cumoverlay.dmi', "cum_normal")) cut_overlay(mutable_appearance('hyperstation/icons/effects/cumoverlay.dmi', "cum_large")) @@ -499,7 +504,7 @@ obj/screen/arousal/proc/kiss() return //We got hands, let's pick an organ var/obj/item/organ/genital/picked_organ - picked_organ = pick_masturbate_genitals() + picked_organ = pick_climax_genitals(masturbation=TRUE, title="Masturbation") if(picked_organ) src << browse(null, "window=arousal") //closes the window mob_masturbate(picked_organ, cover = TRUE) diff --git a/hyperstation/code/modules/arousal/organs/genitals.dm b/hyperstation/code/modules/arousal/organs/genitals.dm new file mode 100644 index 000000000..9da552021 --- /dev/null +++ b/hyperstation/code/modules/arousal/organs/genitals.dm @@ -0,0 +1,6 @@ +/obj/item/organ/genital/proc/get_fluid_source() + if(producing) + return src.reagents + if(linked_organ && linked_organ.producing) + return linked_organ.reagents + return null \ No newline at end of file diff --git a/hyperstation/code/obj/fleshlight.dm b/hyperstation/code/obj/fleshlight.dm index 8a6932906..3d5c7ab48 100644 --- a/hyperstation/code/obj/fleshlight.dm +++ b/hyperstation/code/obj/fleshlight.dm @@ -102,29 +102,29 @@ else . += "The device is paired, and awaiting input. " -/obj/item/portallight/attack(mob/living/carbon/holder, mob/living/user) +/obj/item/portallight/attack(mob/living/carbon/user, mob/living/holder) if(inuse) //just to stop stacking and causing people to cum instantly return if(!UpdateUsability()) - to_chat(user, "\The [src] appears to be currently unusable.") + to_chat(holder, "\The [src] appears to be currently unusable.") return var/option = "Fuck" - if(holder == user) + if(user == holder) option = input(usr, "Choose action", "Portal Fleshlight", "Fuck") in list("Fuck", "Lick", "Touch") var/obj/item/organ/genital/target_genital = portalunderwear.loc var/mob/living/carbon/human/target = target_genital.owner - var/obj/item/organ/genital/penis/P = holder.getorganslot("penis") + var/obj/item/organ/genital/penis/P = user.getorganslot("penis") if(option == "Fuck" && !P.is_exposed()) - to_chat(user, "You don't see anywhere to use this on.") + to_chat(holder, "You don't see anywhere to use this on.") return /* WARMUP START - prevents spam/instant climax */ inuse = TRUE - if(holder != user) - holder.visible_message("[user] is trying to use [src] on [holder]'s penis.",\ - "[user] is trying to use [src] on your penis.") - if(!do_mob(user, holder, 3 SECONDS)) + if(user != holder) + user.visible_message("[holder] is trying to use [src] on [user]'s penis.",\ + "[holder] is trying to use [src] on your penis.") + if(!do_mob(holder, user, 3 SECONDS)) inuse = FALSE return inuse = FALSE @@ -134,31 +134,32 @@ switch(option) if("Fuck") playsound(src, 'sound/lewd/slaps.ogg', 30, 1, -1) - if(holder == user) - user.visible_message("[user] pumps [src] on their penis.", + if(user == holder) + holder.visible_message("[holder] pumps [src] on their penis.", "You pump the fleshlight on your penis.") else - holder.visible_message("[user] pumps [src] on [holder]'s penis.",\ - "[user] pumps [src] up and down on your penis.") + user.visible_message("[holder] pumps [src] on [user]'s penis.",\ + "[holder] pumps [src] up and down on your penis.") to_chat(target, "You feel a [P.length] inch, [P.shape] shaped penis pumping through the portal into your [target_genital.name].") if(prob(30)) - holder.emote("moan") - holder.adjustArousalLoss(20) + user.emote("moan") + user.adjustArousalLoss(20) target.adjustArousalLoss(20) target.do_jitter_animation() if(target.can_orgasm() && prob(5)) target.mob_climax_outside(target_genital, spillage = target_genital.is_exposed()) - if(holder.can_orgasm()) - var/mob/living/carbon/human/O = holder + if(user.can_orgasm()) + var/mob/living/carbon/human/O = user var/impreg_chance = target_genital.name == "vagina" && !P.condom && !P.sounding - O.mob_climax_partner(P, target, FALSE, impreg_chance, FALSE, TRUE) + if(O.mob_climax_partner(P, target, spillage=FALSE, remote=TRUE) && impreg_chance) + target.impregnate(by=O) if("Lick") - user.visible_message("[user] licks into [src].",\ + holder.visible_message("[holder] licks into [src].",\ "You lick into [src].") to_chat(target, "You feel a tongue lick you through the portal against your [target_genital.name].") target.adjustArousalLoss(10) if("Touch") - user.visible_message("[user] touches softly against [src].",\ + holder.visible_message("[holder] touches softly against [src].",\ "You touch softly on [src].") to_chat(target, "You feel someone touching your [target_genital.name] through the portal.") target.adjustArousalLoss(5) diff --git a/modular_citadel/code/modules/arousal/arousal.dm b/modular_citadel/code/modules/arousal/arousal.dm index f41d1ee58..8d2de3d34 100644 --- a/modular_citadel/code/modules/arousal/arousal.dm +++ b/modular_citadel/code/modules/arousal/arousal.dm @@ -189,19 +189,30 @@ if(M.canbearoused) ui_interact(usr) +/** + * Checks if this mob can orgasm. Criteria includes: + * - Mob is human + * - Mob is capable of arousal + * - Mob has DNA + * - Mob is aroused past a certain threshold + * + * Arguments: + * - `arousal` - The minimum arousal needed to pass the check. Default is `100`. + * + * Returns: `TRUE/FALSE` - Whether or not the mob passes all of the above checks. + */ /mob/living/proc/can_orgasm(arousal = 100) return src.canbearoused && src.getArousalLoss() >= arousal && ishuman(src) && src.has_dna() + /mob/living/proc/mob_climax() -// This is just so I can test this shit without being forced to add actual content -// to get rid of arousal. Will be a very basic proc for a while. set name = "Masturbate" set category = "IC" if(canbearoused && !restrained() && !stat) if(mb_cd_timer <= world.time) //start the cooldown even if it fails mb_cd_timer = world.time + mb_cd_length - if(getArousalLoss() >= isPercentAroused(33))//33% arousal or greater required + if(isPercentAroused(33))//33% arousal or greater required src.visible_message("[src] starts masturbating!", \ "You start masturbating.") if(do_after(src, 30, target = src)) @@ -213,33 +224,24 @@ to_chat(src, "You aren't aroused enough for that.") -//These are various procs that we'll use later, split up for readability instead of having one, huge proc. -//For all of these, we assume the arguments given are proper and have been checked beforehand. + /mob/living/carbon/human/proc/mob_masturbate(obj/item/organ/genital/G, cover = FALSE, mb_time = 30) -//Masturbation, keep it gender-neutral var/total_fluids = 0 - var/datum/reagents/fluid_source = null - var/condomed = 0 - var/sounded = 0 - if(G.name == "penis")//if the select organ is a penis + var/datum/reagents/fluid_source = G.get_fluid_source() + var/condomed = FALSE + var/sounded = FALSE + if(G.name == "penis") var/obj/item/organ/genital/penis/P = src.getorganslot("penis") - if(P.condom) //if the penis is condomed - condomed = 1 - if(P.sounding) - sounded = 1 - if(G.producing) //Can it produce its own fluids, such as breasts? - fluid_source = G.reagents - else - if(!G.linked_organ) - to_chat(src, "Your [G.name] is unable to produce it's own fluids, it's missing the organs for it.") - return - fluid_source = G.linked_organ.reagents + condomed = P.condom + sounded = P.sounding + if(!fluid_source) + to_chat(src, "Your [G.name] is unable to produce it's own fluids, it's missing the organs for it.") + return total_fluids = fluid_source.total_volume if(mb_time) src.visible_message("[src] starts to [G.masturbation_verb] [p_their()] [G.name].", \ "You start to [G.masturbation_verb] your [G.name].", \ "You start to [G.masturbation_verb] your [G.name].") - if(do_after(src, mb_time, target = src)) if(total_fluids > 5 &&!condomed &&!sounded) fluid_source.reaction(src.loc, TOUCH, 1, 0) @@ -248,14 +250,11 @@ src.visible_message("[src] orgasms, cumming[istype(src.loc, /turf/open/floor) ? " onto [src.loc]" : ""]!", \ "You cum[istype(src.loc, /turf/open/floor) ? " onto [src.loc]" : ""].", \ "You have relieved yourself.") - if(!condomed &&!sounded && cover)//For when you want to make a mess of yourself. fluid_source.trans_to(src, total_fluids*G.fluid_transfer_factor) total_fluids -= total_fluids*G.fluid_transfer_factor if(total_fluids > 80) // now thats a big cum! - var/mutable_appearance/cumoverlaylarge = mutable_appearance('hyperstation/icons/effects/cumoverlay.dmi') - cumoverlaylarge.icon_state = "cum_large" - src.add_overlay(cumoverlaylarge) + src.add_cum_overlay("large") if(total_fluids > 5) fluid_source.reaction(src.loc, TOUCH, 1, 0) var/mob/living/carbon/human/H = src @@ -265,10 +264,7 @@ src.visible_message("[src] climaxes over [p_their()] self, using [p_their()] [G.name]!", \ "You orgasm over yourself, using your [G.name].", \ "You have climaxed over something, using your [G.name].") - var/mutable_appearance/cumoverlay = mutable_appearance('hyperstation/icons/effects/cumoverlay.dmi') - cumoverlay.icon_state = "cum_normal" - src.add_overlay(cumoverlay) - + src.add_cum_overlay() if(condomed) //condomed src.visible_message("[src] orgasms, climaxing into [p_their()] condom!", \ "You cum into your condom.", \ @@ -279,7 +275,6 @@ "You don't quite feel totally relieved.") if(total_fluids > 0 &&condomed &&!sounded) src.condomclimax() - SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) if(G.can_climax) setArousalLoss(min_arousal) @@ -288,19 +283,12 @@ /mob/living/carbon/human/proc/mob_climax_outside(obj/item/organ/genital/G, mb_time = 30, spillage = TRUE) //This is used for forced orgasms and other hands-free climaxes var/total_fluids = 0 - var/datum/reagents/fluid_source = null + var/datum/reagents/fluid_source = G.get_fluid_source() var/unable_to_come = FALSE - - if(G.producing) //Can it produce its own fluids, such as breasts? - fluid_source = G.reagents + if(fluid_source) total_fluids = fluid_source.total_volume else - if(!G.linked_organ) - unable_to_come = TRUE - else - fluid_source = G.linked_organ.reagents - total_fluids = fluid_source.total_volume - + unable_to_come = TRUE if(unable_to_come) src.visible_message("[src] shudders, their [G.name] unable to cum.", \ "Your [G.name] cannot cum, giving no relief.", \ @@ -323,108 +311,107 @@ src.visible_message("[src] orgasms with [p_their()] [G.name]!", \ "You climax with your [G.name].", \ "You climax using your [G.name].") - SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) if(G.can_climax) setArousalLoss(min_arousal) -/mob/living/carbon/human/proc/mob_climax_partner(obj/item/organ/genital/G, atom/partner, spillage = TRUE, impreg = FALSE,cover = FALSE,remote = FALSE, mb_time = 30) //Used for climaxing with any person - if(isnoncarbon(partner)) // deny animals - return +/mob/living/carbon/human/proc/mob_climax_cover(obj/item/organ/genital/genital, atom/partner, mb_time=3 SECONDS) var/total_fluids = 0 - var/datum/reagents/fluid_source = null - if(G.producing) //Can it produce its own fluids, such as breasts? - fluid_source = G.reagents - else - if(!G.linked_organ) - to_chat(src, "Your [G.name] is unable to produce it's own fluids, it's missing the organs for it.") - return - fluid_source = G.linked_organ.reagents + var/datum/reagents/fluid_source = genital.get_fluid_source() + if(!fluid_source) + to_chat(src, "Your [genital.name] is unable to produce it's own fluids, it's missing the organs for it.") + return total_fluids = fluid_source.total_volume - if(mb_time && !remote) //Skip warning if this is an instant climax. + var/mob/living/carbon/partner_carbon = partner + if(mb_time) src.visible_message("[src] is about to climax with [partner]!", \ "You're about to climax with [partner]!", \ "You're preparing to climax with something!") + if(!do_after(src, mb_time, target = src)) + return + if(!in_range(src, partner)) + return + var/transferred_units = total_fluids * genital.fluid_transfer_factor + fluid_source.trans_to(partner, transferred_units) + total_fluids -= transferred_units + if(total_fluids > 80 && iscarbon(partner)) // now thats a big cum! + partner_carbon.add_cum_overlay("large") + if(total_fluids > 5) + fluid_source.reaction(partner.loc, TOUCH, 1, 0) + var/mob/living/carbon/human/H = partner + if(H) + H.cumdrip_rate += rand(5,10) + fluid_source.clear_reagents() + if(iscarbon(partner)) + partner_carbon.add_cum_overlay("normal") + else + partner.add_cum_overlay() + src.visible_message("[src] climaxes over [partner], using [p_their()] [genital.name]!", \ + "You orgasm over [partner], using your [genital.name].", \ + "You have climaxed over something, using your [genital.name].") + SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) + setArousalLoss(min_arousal) + + +/mob/living/carbon/human/proc/mob_climax_partner(obj/item/organ/genital/G, atom/partner, spillage=TRUE, remote=FALSE, mb_time = 3 SECONDS) + if(isnoncarbon(partner)) // deny animals + return FALSE + var/total_fluids = 0 + var/datum/reagents/fluid_source = G.get_fluid_source() + if(!fluid_source) + to_chat(src, "Your [G.name] is unable to produce it's own fluids, it's missing the organs for it.") + return FALSE + total_fluids = fluid_source.total_volume if(remote) src.visible_message("[src] is about to climax with someone!", \ "You're about to climax with someone!", \ "You're preparing to climax with something!") - if(cover)//covering the partner in cum, this overrides other options. - if(do_after(src, mb_time, target = src) && in_range(src, partner)) - fluid_source.trans_to(partner, total_fluids*G.fluid_transfer_factor) - total_fluids -= total_fluids*G.fluid_transfer_factor - if(total_fluids > 80 && iscarbon(partner)) // now thats a big cum! - var/mutable_appearance/cumoverlaylarge = mutable_appearance('hyperstation/icons/effects/cumoverlay.dmi') - cumoverlaylarge.icon_state = "cum_large" - partner.add_overlay(cumoverlaylarge) - if(total_fluids > 5) - fluid_source.reaction(partner.loc, TOUCH, 1, 0) - var/mob/living/carbon/human/H = partner - if(H) - H.cumdrip_rate += rand(5,10) - fluid_source.clear_reagents() - src.visible_message("[src] climaxes over [partner], using [p_their()] [G.name]!", \ - "You orgasm over [partner], using your [G.name].", \ - "You have climaxed over something, using your [G.name].") - SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) - if(iscarbon(partner)) - var/mutable_appearance/cumoverlay = mutable_appearance('hyperstation/icons/effects/cumoverlay.dmi') - cumoverlay.icon_state = "cum_normal" - partner.add_overlay(cumoverlay) - else - partner.add_cum_overlay() - setArousalLoss(min_arousal) - if(G.can_climax) - setArousalLoss(min_arousal) - if(spillage && !cover) - if(do_after(src, mb_time, target = src)) - if(!in_range(src, partner) && !remote) - return - fluid_source.trans_to(partner, total_fluids*G.fluid_transfer_factor) - total_fluids -= total_fluids*G.fluid_transfer_factor - if(total_fluids > 5) - fluid_source.reaction(partner.loc, TOUCH, 1, 0) - var/mob/living/carbon/human/H = partner - if(H) - H.cumdrip_rate += rand(5,10) - fluid_source.clear_reagents() - src.visible_message("[src] climaxes with [partner][spillage ? ", overflowing and spilling":""], using [p_their()] [G.name]!", \ - "You orgasm with [partner][spillage ? ", spilling out of them":""], using your [G.name].", \ - "You have climaxed with someone[spillage ? ", spilling out of them":""], using your [G.name].") - SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) - SEND_SIGNAL(partner, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) - if(G.can_climax) - setArousalLoss(min_arousal) + else if(mb_time) + src.visible_message("[src] is about to climax with [partner]!", \ + "You're about to climax with [partner]!", \ + "You're preparing to climax with something!") + if(!do_after(src, mb_time, target = src)) + return FALSE + if(!remote && !in_range(src, partner)) + return FALSE + if(spillage) + fluid_source.trans_to(partner, total_fluids*G.fluid_transfer_factor) + total_fluids -= total_fluids*G.fluid_transfer_factor + if(total_fluids > 5) + fluid_source.reaction(partner.loc, TOUCH, 1, 0) + var/mob/living/carbon/human/H = partner + if(H) + H.cumdrip_rate += rand(5,10) + fluid_source.clear_reagents() + src.visible_message("[src] climaxes with [partner][spillage ? ", overflowing and spilling":""], using [p_their()] [G.name]!",\ + "You orgasm with [partner][spillage ? ", spilling out of them":""], using your [G.name].",\ + "You have climaxed with someone[spillage ? ", spilling out of them":""], using your [G.name].") + SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) + SEND_SIGNAL(partner, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) else //knots, portal fleshlights, and other non-spilling orgasms - if(!cover) - if(!remote && !in_range(src, partner)) - return - if(do_after(src, mb_time, target = src)) - var/mob/living/carbon/H = partner - if(!spillage && total_fluids > 80) //hyper inflation; requires a big cumshot to expand - H.expand_belly(1) - var/obj/item/organ/genital/penis/P = G - if (P.condom)//condomed. - src.condomclimax() - else - fluid_source.trans_to(partner, total_fluids) - total_fluids = 0 - if(!remote) - src.visible_message("[src] climaxes with [partner], [p_their()] [G.name] spilling nothing!", \ - "You ejaculate with [partner], your [G.name] spilling nothing.", \ - "You have climaxed inside someone, your [G.name] spilling nothing.") - else - src.visible_message("[src] climaxes with someone, using [p_their()] [G.name]!", \ - "You ejaculate with someone, using your [G.name].", \ - "You have climaxed inside someone, using your [G.name].") - to_chat(partner, "You feel someone ejaculate inside you.") - - SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) - SEND_SIGNAL(partner, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) - - if(G.can_climax) - setArousalLoss(min_arousal) + var/mob/living/carbon/H = partner + if(!spillage && total_fluids > 80) //hyper inflation; requires a big cumshot to expand + H.expand_belly(1) + var/obj/item/organ/genital/penis/P = G + if(P.condom)//condomed. + src.condomclimax() + else + fluid_source.trans_to(partner, total_fluids) + total_fluids = 0 + if(!remote) + src.visible_message("[src] climaxes with [partner], [p_their()] [G.name] spilling nothing!", \ + "You ejaculate with [partner], your [G.name] spilling nothing.", \ + "You have climaxed inside someone, your [G.name] spilling nothing.") + else + src.visible_message("[src] climaxes with someone, using [p_their()] [G.name]!", \ + "You ejaculate with someone, using your [G.name].", \ + "You have climaxed inside someone, using your [G.name].") + to_chat(partner, "You feel someone ejaculate inside you.") + SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) + SEND_SIGNAL(partner, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) + if(G.can_climax) + setArousalLoss(min_arousal) //Hyper - antag code var/mob/living/carbon/partner_carbon = partner if(src.mind.special_role == ROLE_LEWD_TRAITOR) @@ -432,98 +419,107 @@ if (partner_carbon.mind == obj.target) partner_carbon.mind.sexed = TRUE //sexed to_chat(src, "You feel deep satisfaction with yourself.") - //Hyper - impreg - if(impreg) - partner_carbon.impregnate(by = src) + return TRUE /mob/living/carbon/human/proc/mob_climax_partner_spillage(obj/item/organ/genital/picked_organ, mob/living/carbon/partner, impreg = FALSE) var/obj/item/organ/genital/penis/_penis = picked_organ if(picked_organ.name != "penis" || _penis.sounding || _penis.condom) - mob_climax_partner(picked_organ, partner, FALSE, FALSE, FALSE) + mob_climax_partner(picked_organ, partner, spillage=FALSE) return var/_question = "Would your fluids spill outside?" var/_title = "Choose overflowing option" var/spillage = input(src, _question, _title, "Yes") as anything in list("Yes", "No") - mob_climax_partner(picked_organ, partner, spillage == "Yes", impreg, FALSE) + if(mob_climax_partner(picked_organ, partner, spillage == "Yes") && impreg) + partner.impregnate(by=src) -/mob/living/carbon/human/proc/mob_fill_container(obj/item/organ/genital/G, obj/item/reagent_containers/container, mb_time = 30) -// For beaker-filling, beware the bartender - var/total_fluids = 0 - var/datum/reagents/fluid_source = null - if(G.name == "penis") - var/obj/item/organ/genital/penis/P = src.getorganslot("penis") - if(P.condom) - to_chat(src, "You cannot fill containers when there is a condom over your [G.name].") - return - if(P.sounding) - to_chat(src, "You cannot fill containers when there is a rod inside your [G.name].") - return - if(G.producing) //Can it produce its own fluids, such as breasts? - fluid_source = G.reagents - else - if(!G.linked_organ) - to_chat(src, "Your [G.name] is unable to produce it's own fluids, it's missing the organs for it.") - return - fluid_source = G.linked_organ.reagents - total_fluids = fluid_source.total_volume - +/** + * Attempt to climax into a container it, filling it with whatever fluids are + * associated to the selected genitals. + * + * Arguments: + * - `genital`: The genital being used for climax. + * - `container`: The container being targeted. + * - `mb_time`: The warm-up time for this interaction. + */ +/mob/living/carbon/human/proc/mob_fill_container(obj/item/organ/genital/genital, obj/item/reagent_containers/container, mb_time = 30) if(!container) //Something weird happened to_chat(src, "You need a container to do this!") return - - src.visible_message("[src] starts to [G.masturbation_verb] their [G.name] over [container].", \ - "You start to [G.masturbation_verb] your [G.name] over [container].", \ - "You start to [G.masturbation_verb] your [G.name] over something.") + var/total_fluids = 0 + var/datum/reagents/fluid_source = genital.get_fluid_source() + if(!fluid_source) + to_chat(src, "Your [genital.name] is unable to produce it's own fluids, it's missing the organs for it.") + return + total_fluids = fluid_source.total_volume + if(genital.name == "penis") + var/obj/item/organ/genital/penis/P = src.getorganslot("penis") + if(P.condom) + to_chat(src, "You cannot fill containers when there is a condom over your [genital.name].") + return + if(P.sounding) + to_chat(src, "You cannot fill containers when there is a rod inside your [genital.name].") + return + src.visible_message("[src] starts to [genital.masturbation_verb] their [genital.name] over [container].", \ + "You start to [genital.masturbation_verb] your [genital.name] over [container].", \ + "You start to [genital.masturbation_verb] your [genital.name] over something.") if(do_after(src, mb_time, target = src) && in_range(src, container)) fluid_source.trans_to(container, total_fluids) - src.visible_message("[src] uses [p_their()] [G.name] to fill [container]!", \ - "You used your [G.name] and fill [container] with a total of [total_fluids]u's.", \ - "You have relieved some pressure.") + src.visible_message("[src] uses [p_their()] [genital.name] to fill [container]!", \ + "You used your [genital.name] and fill [container] with a total of [total_fluids]u's.", \ + "You have relieved some pressure.") SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) container.add_cum_overlay() //your aim is bad... - if(G.can_climax) + if(genital.can_climax) setArousalLoss(min_arousal) -/mob/living/carbon/human/proc/pick_masturbate_genitals() - var/obj/item/organ/genital/ret_organ - var/list/genitals_list = list() - for(var/obj/item/organ/genital/G in internal_organs) - if(G.can_masturbate_with) //filter out what you can't masturbate with - if(G.is_exposed()) //Nude or through_clothing - if(!G.dontlist) - genitals_list += G - if(genitals_list.len) - ret_organ = input(src, "with what?", "Masturbate", null) as null|obj in genitals_list - return ret_organ - return null //error stuff - -/mob/living/carbon/human/proc/target_genitals(mob/living/carbon/human/T) //used for targeting others +/** + * Prompts the user to select one of their target's genitals to interact with. + * The target must have their genitals visible to be selectable. + * + * Returns: `null`|`obj/item/organ/genital`: The selected genital. + */ +/mob/living/carbon/human/proc/target_genitals(mob/living/carbon/human/T) var/obj/item/organ/genital/ret_organ var/list/genitals_list = list() for(var/obj/item/organ/genital/G in T.internal_organs) - if(G.is_exposed()) //Nude or through_clothing - if(!G.dontlist) - genitals_list += G + if(G.is_exposed() && !G.dontlist) + genitals_list += G if(genitals_list.len) ret_organ = input(src, "", "Genitals", null) as null|obj in genitals_list return ret_organ return null //error stuff -/mob/living/carbon/human/proc/pick_climax_genitals() + +/** + * Prompts the user to select genitals. The genitals must be exposed to be selectable. + * + * Arguments: + * - `masturbation`: Whether or not this is being used for masturbation interactions. + * - `title`: The title of the prompt window. + * + * Returns: `null`|`obj/item/organ/genital`: The selected genital. + */ +/mob/living/carbon/human/proc/pick_climax_genitals(masturbation=FALSE, title="Climax") var/obj/item/organ/genital/ret_organ var/list/genitals_list = list() for(var/obj/item/organ/genital/G in internal_organs) if(G.can_climax && G.is_exposed() && !G.dontlist) - genitals_list += G + if(!masturbation || G.can_masturbate_with) + genitals_list += G if(genitals_list.len) - ret_organ = input(src, "with what?", "Climax", null) as null|obj in genitals_list + ret_organ = input(src, "with what?", title, null) as null|obj in genitals_list return ret_organ return null //error stuff +/** + * Prompts the user to select a mob that they are either pulling or being pulled by. + * + * Returns: `null`|`mob/living/carbon`: The selected mob. + */ /mob/living/carbon/human/proc/pick_partner(needs_exposed = TRUE) var/list/partners = list(src.pulling, src.pulledby) for(var/I in partners) @@ -537,9 +533,14 @@ partners -= I if(!partners.len) return null - return input(src, "With whom?", "Sexual partner", null) as null|anything in partners + return input(src, "With whom?", "Sexual partner", null) as null|mob in partners +/** + * Prompts the user to provide a container (or food item) based on what's in their hands. + * + * Returns: `null`|`obj/item/reagent_containers` - The selected container. + */ /mob/living/carbon/human/proc/pick_climax_container() var/obj/item/reagent_containers/SC = null var/list/containers_list = list() @@ -551,12 +552,14 @@ if(SC) if(in_range(src, SC)) return SC - return null //If nothing correct, give null. + return null +/** + * The old arousal menu. This shows a BYOND-style input selection menu that + * emulates the same functionality as the newer one. + */ /mob/living/carbon/human/mob_climax() - if(stat == DEAD) //corpses can't cum - return if(!canbearoused || !has_dna()) return if(mb_cd_timer > world.time) @@ -627,7 +630,7 @@ return //We got hands, let's pick an organ var/obj/item/organ/genital/picked_organ - picked_organ = pick_masturbate_genitals() + picked_organ = pick_climax_genitals(masturbation=TRUE, title="Masturbation") if(picked_organ) mob_masturbate(picked_organ) return @@ -684,7 +687,7 @@ if(picked_organ) var/mob/living/carbon/partner = pick_partner(FALSE) //Get your partner, clothed or not. if(partner) - mob_climax_partner(picked_organ, partner, FALSE, FALSE, TRUE) + mob_climax_cover(picked_organ, partner) else to_chat(src, "You cannot do this alone.") @@ -722,6 +725,12 @@ return //Just end it in that case. +/** + * Causes a mob to climax on the spot. There is no warmup time, nor a + * menu for selecting genitals. Instead, the mob will climax using all + * climaxable genitals, either onto their location (floor) or on whatever + * is pulling/being pulled by them. + */ /mob/living/carbon/proc/mob_climax_instant() if(stat == DEAD) //corpses can't cum to_chat(src, "You can't do that while dead!") @@ -756,6 +765,12 @@ return +/** + * Rolls a chance for the mob to get pregnant, assuming they are capable of doing so. + * + * Arguments: + * - `by` - The mob that is impregnating this one. + */ /mob/living/carbon/proc/impregnate(mob/living/carbon/by) var/obj/item/organ/genital/womb/W = getorganslot("womb") if(!W || W.pregnant || !breedable) diff --git a/tgstation.dme b/tgstation.dme index 7ab0343dd..b6c7c761e 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -3145,6 +3145,7 @@ #include "hyperstation\code\modules\arousal\arousalhud.dm" #include "hyperstation\code\modules\arousal\creampie.dm" #include "hyperstation\code\modules\arousal\expansion.dm" +#include "hyperstation\code\modules\arousal\organs\genitals.dm" #include "hyperstation\code\modules\busy_space\air_traffic.dm" #include "hyperstation\code\modules\busy_space\atc_controller.dm" #include "hyperstation\code\modules\busy_space\loremaster.dm" From 7531da14916e87e00da6c50fe390f1f5fde9dfb8 Mon Sep 17 00:00:00 2001 From: sarcoph <83266791+sarcoph@users.noreply.github.com> Date: Thu, 5 May 2022 18:12:53 -0800 Subject: [PATCH 24/28] cleaning up mob_climax_partner --- .../code/modules/arousal/arousalhud.dm | 13 +-- hyperstation/code/obj/fleshlight.dm | 2 +- .../code/modules/arousal/arousal.dm | 95 +++++++++++-------- 3 files changed, 58 insertions(+), 52 deletions(-) diff --git a/hyperstation/code/modules/arousal/arousalhud.dm b/hyperstation/code/modules/arousal/arousalhud.dm index 586e6608c..318edefd0 100644 --- a/hyperstation/code/modules/arousal/arousalhud.dm +++ b/hyperstation/code/modules/arousal/arousalhud.dm @@ -378,10 +378,10 @@ obj/screen/arousal/proc/kiss() to_chat(src, "You cannot climax without choosing genitals.") return src << browse(null, "window=arousal") // close arousal window - mob_climax_partner_spillage(picked_organ, partner, impreg=FALSE) + mob_climax_inside_spillage(picked_organ, partner, impreg=FALSE) -/mob/living/carbon/human/proc/climaxover(mob/living/carbon/human/partner) +/mob/living/carbon/human/proc/climaxover(atom/partner) var/obj/item/organ/genital/picked_organ picked_organ = pick_climax_genitals() if(!picked_organ) @@ -391,13 +391,6 @@ obj/screen/arousal/proc/kiss() if(!partner || isnoncarbon(partner)) to_chat(src, "You cannot do this alone.") return - var/obj/item/organ/genital/penis/P = picked_organ - if(P.condom) - to_chat(src, "You cannot do this action with a condom on.") - return - if(P.sounding) - to_chat(src, "You cannot do this action with a sounding in.") - return mob_climax_cover(picked_organ, partner) @@ -435,7 +428,7 @@ obj/screen/arousal/proc/kiss() to_chat(src, "You cannot impregnate someone with a sounding rod in.") return src << browse(null, "window=arousal") //alls fine, we can close the window now. - mob_climax_partner_spillage(picked_organ, partner, impreg=TRUE) + mob_climax_inside_spillage(picked_organ, partner, impreg=TRUE) /mob/living/carbon/human/proc/cumcontainer(mob/living/T) diff --git a/hyperstation/code/obj/fleshlight.dm b/hyperstation/code/obj/fleshlight.dm index 3d5c7ab48..0c640b23f 100644 --- a/hyperstation/code/obj/fleshlight.dm +++ b/hyperstation/code/obj/fleshlight.dm @@ -151,7 +151,7 @@ if(user.can_orgasm()) var/mob/living/carbon/human/O = user var/impreg_chance = target_genital.name == "vagina" && !P.condom && !P.sounding - if(O.mob_climax_partner(P, target, spillage=FALSE, remote=TRUE) && impreg_chance) + if(O.mob_climax_in_partner(P, target, spillage=FALSE, remote=TRUE) && impreg_chance) target.impregnate(by=O) if("Lick") holder.visible_message("[holder] licks into [src].",\ diff --git a/modular_citadel/code/modules/arousal/arousal.dm b/modular_citadel/code/modules/arousal/arousal.dm index 8d2de3d34..f078e9e19 100644 --- a/modular_citadel/code/modules/arousal/arousal.dm +++ b/modular_citadel/code/modules/arousal/arousal.dm @@ -317,13 +317,20 @@ /mob/living/carbon/human/proc/mob_climax_cover(obj/item/organ/genital/genital, atom/partner, mb_time=3 SECONDS) + if(istype(genital, /obj/item/organ/genital/penis)) + var/obj/item/organ/genital/penis/P = genital + if(P.condom) + to_chat(src, "You cannot do this action with a condom on.") + return + if(P.sounding) + to_chat(src, "You cannot do this action with a sounding in.") + return var/total_fluids = 0 var/datum/reagents/fluid_source = genital.get_fluid_source() if(!fluid_source) to_chat(src, "Your [genital.name] is unable to produce it's own fluids, it's missing the organs for it.") return total_fluids = fluid_source.total_volume - var/mob/living/carbon/partner_carbon = partner if(mb_time) src.visible_message("[src] is about to climax with [partner]!", \ "You're about to climax with [partner]!", \ @@ -335,16 +342,19 @@ var/transferred_units = total_fluids * genital.fluid_transfer_factor fluid_source.trans_to(partner, transferred_units) total_fluids -= transferred_units - if(total_fluids > 80 && iscarbon(partner)) // now thats a big cum! - partner_carbon.add_cum_overlay("large") if(total_fluids > 5) fluid_source.reaction(partner.loc, TOUCH, 1, 0) - var/mob/living/carbon/human/H = partner - if(H) - H.cumdrip_rate += rand(5,10) fluid_source.clear_reagents() - if(iscarbon(partner)) - partner_carbon.add_cum_overlay("normal") + if(ismob(partner)) + var/mob/living/carbon/partner_carbon = partner + if(partner_carbon) + var/cum_overlay = "normal" + if(total_fluids > 80) + cum_overlay = "large" + partner_carbon.add_cum_overlay(cum_overlay) + var/mob/living/carbon/human/partner_human = partner_carbon + if(partner_human) + partner_human.cumdrip_rate += rand(5,10) else partner.add_cum_overlay() src.visible_message("[src] climaxes over [partner], using [p_their()] [genital.name]!", \ @@ -354,7 +364,7 @@ setArousalLoss(min_arousal) -/mob/living/carbon/human/proc/mob_climax_partner(obj/item/organ/genital/G, atom/partner, spillage=TRUE, remote=FALSE, mb_time = 3 SECONDS) +/mob/living/carbon/human/proc/mob_climax_in_partner(obj/item/organ/genital/G, mob/living/carbon/partner, spillage=TRUE, remote=FALSE, mb_time=3 SECONDS) if(isnoncarbon(partner)) // deny animals return FALSE var/total_fluids = 0 @@ -363,14 +373,12 @@ to_chat(src, "Your [G.name] is unable to produce it's own fluids, it's missing the organs for it.") return FALSE total_fluids = fluid_source.total_volume + var/partner_text = partner if(remote) - src.visible_message("[src] is about to climax with someone!", \ - "You're about to climax with someone!", \ - "You're preparing to climax with something!") - else if(mb_time) - src.visible_message("[src] is about to climax with [partner]!", \ - "You're about to climax with [partner]!", \ - "You're preparing to climax with something!") + partner_text = "someone" + src.visible_message("[src] is about to climax with [partner_text]!", \ + "You're about to climax with [partner_text]!", \ + "You're preparing to climax with something!") if(!do_after(src, mb_time, target = src)) return FALSE if(!remote && !in_range(src, partner)) @@ -384,29 +392,26 @@ if(H) H.cumdrip_rate += rand(5,10) fluid_source.clear_reagents() - src.visible_message("[src] climaxes with [partner][spillage ? ", overflowing and spilling":""], using [p_their()] [G.name]!",\ - "You orgasm with [partner][spillage ? ", spilling out of them":""], using your [G.name].",\ - "You have climaxed with someone[spillage ? ", spilling out of them":""], using your [G.name].") + src.visible_message("[src] climaxes with [partner], overflowing and spilling, using [p_their()] [G.name]!",\ + "You orgasm with [partner], spilling out of them, using your [G.name].",\ + "You have climaxed with someone, spilling out of them, using your [G.name].") SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) SEND_SIGNAL(partner, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) else //knots, portal fleshlights, and other non-spilling orgasms - var/mob/living/carbon/H = partner - if(!spillage && total_fluids > 80) //hyper inflation; requires a big cumshot to expand - H.expand_belly(1) - var/obj/item/organ/genital/penis/P = G - if(P.condom)//condomed. - src.condomclimax() - else + var/can_inflate = TRUE + if(istype(G, /obj/item/organ/genital/penis)) + var/obj/item/organ/genital/penis/P = G + if(P.condom)//condomed. + can_inflate = FALSE + src.condomclimax() + if(can_inflate) fluid_source.trans_to(partner, total_fluids) - total_fluids = 0 - if(!remote) - src.visible_message("[src] climaxes with [partner], [p_their()] [G.name] spilling nothing!", \ - "You ejaculate with [partner], your [G.name] spilling nothing.", \ - "You have climaxed inside someone, your [G.name] spilling nothing.") - else - src.visible_message("[src] climaxes with someone, using [p_their()] [G.name]!", \ - "You ejaculate with someone, using your [G.name].", \ - "You have climaxed inside someone, using your [G.name].") + if(!spillage && total_fluids > 80) // hyper - cum inflation, ONLY if not wearing a condom + partner.expand_belly(1) + src.visible_message("[src] climaxes with [partner_text], [p_their()] [G.name] spilling nothing!", \ + "You ejaculate with [partner_text], your [G.name] spilling nothing.", \ + "You have climaxed inside someone, your [G.name] spilling nothing.") + if(remote) to_chat(partner, "You feel someone ejaculate inside you.") SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) SEND_SIGNAL(partner, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) @@ -422,15 +427,23 @@ return TRUE -/mob/living/carbon/human/proc/mob_climax_partner_spillage(obj/item/organ/genital/picked_organ, mob/living/carbon/partner, impreg = FALSE) +/** + * User chooses whether or not their climax will spill outside of their partner. + * + * Arguments: + * - `picked_organ`: The user's genital being used. + * - `partner`: This mob's partner for the interaction. + * - `impreg`: Whether or not this interaction has a chance to impregnate. + */ +/mob/living/carbon/human/proc/mob_climax_inside_spillage(obj/item/organ/genital/picked_organ, mob/living/carbon/partner, impreg = FALSE) var/obj/item/organ/genital/penis/_penis = picked_organ if(picked_organ.name != "penis" || _penis.sounding || _penis.condom) - mob_climax_partner(picked_organ, partner, spillage=FALSE) + mob_climax_in_partner(picked_organ, partner, spillage=FALSE) return var/_question = "Would your fluids spill outside?" var/_title = "Choose overflowing option" var/spillage = input(src, _question, _title, "Yes") as anything in list("Yes", "No") - if(mob_climax_partner(picked_organ, partner, spillage == "Yes") && impreg) + if(mob_climax_in_partner(picked_organ, partner, spillage == "Yes") && impreg) partner.impregnate(by=src) @@ -679,12 +692,12 @@ var/impreg_question = "Would this action carry the risk of pregnancy?" var/impreg_title = "Choose a option" impreg = input(src, impreg_question, impreg_title, "Yes") as anything in list("Yes", "No") - mob_climax_partner_spillage(picked_organ, partner, impreg == "Yes") + mob_climax_inside_spillage(picked_organ, partner, impreg == "Yes") if("Climax over partner") var/obj/item/organ/genital/picked_organ picked_organ = pick_climax_genitals() - if(picked_organ) + if(picked_organ) var/mob/living/carbon/partner = pick_partner(FALSE) //Get your partner, clothed or not. if(partner) mob_climax_cover(picked_organ, partner) @@ -760,7 +773,7 @@ if(C.exposed_genitals.len || C.is_groin_exposed() || C.is_chest_exposed()) partner = C if(partner) - user_human.mob_climax_partner(current_genital, partner, mb_time = 0) + user_human.mob_climax_in_partner(current_genital, partner, mb_time = 0) continue return From 9507a9a4ab22a6e02a1a4261303a8bccdad56ad2 Mon Sep 17 00:00:00 2001 From: sarcoph <83266791+sarcoph@users.noreply.github.com> Date: Thu, 5 May 2022 18:49:40 -0800 Subject: [PATCH 25/28] fixed instant climax --- .../mob/living/carbon/human/species.dm | 5 +-- code/modules/mob/living/carbon/life.dm | 3 +- .../ruins/objects_and_mobs/sin_ruins.dm | 15 ++++--- hyperstation/code/obj/fleshlight.dm | 10 +---- hyperstation/code/obj/vibrator.dm | 3 +- .../code/modules/arousal/arousal.dm | 41 ++++++++++--------- 6 files changed, 33 insertions(+), 44 deletions(-) diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index 7238bae03..fc87bd816 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -1647,7 +1647,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) ) if (target.canbearoused) target.adjustArousalLoss(5) - if (target.can_orgasm() && HAS_TRAIT(target, TRAIT_MASO)) + if (HAS_TRAIT(target, TRAIT_MASO)) target.mob_climax_instant() if (!HAS_TRAIT(target, TRAIT_NYMPHO)) stop_wagging_tail(target) @@ -2005,8 +2005,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) H.update_damage_overlays() if(HAS_TRAIT(H, TRAIT_MASO)) H.adjustArousalLoss(damage * brutemod * H.physiology.brute_mod) - if (H.can_orgasm()) - H.mob_climax_instant() + H.mob_climax_instant() else//no bodypart, we deal damage with a more general method. H.adjustBruteLoss(damage * hit_percent * brutemod * H.physiology.brute_mod) diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index 310eb3d7f..fc3c25c15 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -77,8 +77,7 @@ var/mob/living/carbon/human/B = src if(HAS_TRAIT(B, TRAIT_CHOKE_SLUT)) B.adjustArousalLoss(7) - if (B.can_orgasm()) - B.mob_climax_instant() + B.mob_climax_instant() else SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "suffocation", /datum/mood_event/suffocation) else diff --git a/code/modules/ruins/objects_and_mobs/sin_ruins.dm b/code/modules/ruins/objects_and_mobs/sin_ruins.dm index edd7734a8..5fba1aa4b 100644 --- a/code/modules/ruins/objects_and_mobs/sin_ruins.dm +++ b/code/modules/ruins/objects_and_mobs/sin_ruins.dm @@ -288,11 +288,10 @@ if(user.getArousalLoss() < 100) to_chat(user, "You hear a voice in your head... \"You are not horny enough to receive my blessing, dear~\"") return - if(user.can_orgasm()) - var/mob/living/carbon/user_carbon = user - user_carbon.mob_climax_instant() - to_chat(user, "You hear a voice in your head... \"You are worthy of my blessing, dear~\"") - to_chat(user, "You feel overpowering pleasure surge through your entire body.") - var/A = new /obj/item/reagent_containers/chalice/lust - user.put_in_hands(A) - chalice_taken = TRUE \ No newline at end of file + var/mob/living/carbon/user_carbon = user + user_carbon.mob_climax_instant() + to_chat(user, "You hear a voice in your head... \"You are worthy of my blessing, dear~\"") + to_chat(user, "You feel overpowering pleasure surge through your entire body.") + var/A = new /obj/item/reagent_containers/chalice/lust + user.put_in_hands(A) + chalice_taken = TRUE \ No newline at end of file diff --git a/hyperstation/code/obj/fleshlight.dm b/hyperstation/code/obj/fleshlight.dm index 0c640b23f..b1174156e 100644 --- a/hyperstation/code/obj/fleshlight.dm +++ b/hyperstation/code/obj/fleshlight.dm @@ -45,11 +45,9 @@ inuse = 1 if(!(C == user)) //if we are targeting someone else. C.visible_message("[user] is trying to use [src] on [C]'s penis.", "[user] is trying to use [src] on your penis.") - if(!do_mob(user, C, 3 SECONDS)) //3 second delay inuse = 0 return - //checked if not used on yourself, if not, carry on. playsound(src, 'sound/lewd/slaps.ogg', 30, 1, -1) //slapping sound inuse = 0 @@ -57,20 +55,14 @@ C.visible_message("[user] pumps [src] on [C]'s penis.", "[user] pumps [src] up and down on your penis.") else user.visible_message("[user] pumps [src] on their penis.", "You pump the fleshlight on your penis.") - if(prob(30)) //30% chance to make them moan. C.emote("moan") - C.do_jitter_animation() C.adjustArousalLoss(20) //make the target more aroused. - if (C.can_orgasm()) - C.mob_climax_instant() //make them cum if they are over the edge. - + C.mob_climax_instant() //make them cum if they are over the edge. return - else to_chat(user, "You don't see anywhere to use this on.") - inuse = 0 ..() diff --git a/hyperstation/code/obj/vibrator.dm b/hyperstation/code/obj/vibrator.dm index bef2a7969..40e1446e9 100644 --- a/hyperstation/code/obj/vibrator.dm +++ b/hyperstation/code/obj/vibrator.dm @@ -132,8 +132,7 @@ Code: if(2) //med, can make you cum to_chat(U, "You feel intense pleasure surge through your [G.name]") U.do_jitter_animation() - if (U.can_orgasm()) - U.mob_climax_instant() + U.mob_climax_instant() if(3) //high, makes you stun to_chat(U, "You feel overpowering pleasure surge through your [G.name]") U.Jitter(3) diff --git a/modular_citadel/code/modules/arousal/arousal.dm b/modular_citadel/code/modules/arousal/arousal.dm index f078e9e19..df888287d 100644 --- a/modular_citadel/code/modules/arousal/arousal.dm +++ b/modular_citadel/code/modules/arousal/arousal.dm @@ -202,7 +202,9 @@ * Returns: `TRUE/FALSE` - Whether or not the mob passes all of the above checks. */ /mob/living/proc/can_orgasm(arousal = 100) - return src.canbearoused && src.getArousalLoss() >= arousal && ishuman(src) && src.has_dna() + if(src.canbearoused && src.getArousalLoss() >= arousal && ishuman(src) && src.has_dna()) + return TRUE + return FALSE /mob/living/proc/mob_climax() @@ -744,37 +746,36 @@ * climaxable genitals, either onto their location (floor) or on whatever * is pulling/being pulled by them. */ -/mob/living/carbon/proc/mob_climax_instant() +/mob/living/carbon/proc/mob_climax_instant(arousal=100) if(stat == DEAD) //corpses can't cum to_chat(src, "You can't do that while dead!") return - if(!canbearoused || !has_dna()) + if(!can_orgasm(arousal)) return var/mob/living/carbon/human/user_human = src for(var/obj/item/organ/O in internal_organs) if(!istype(O, /obj/item/organ/genital)) - return + continue var/obj/item/organ/genital/current_genital = O if(!current_genital.can_climax) continue // no wombs or testicles var/mob/living/carbon/partner var/check_target - if(!current_genital.is_exposed()) - user_human.mob_climax_outside(current_genital, mb_time = 0) - continue - if(src.pulling) - if(iscarbon(src.pulling)) - check_target = src.pulling - else if(src.pulledby) - if(iscarbon(src.pulledby)) - check_target = src.pulledby - if(check_target) - var/mob/living/carbon/C = check_target - if(C.exposed_genitals.len || C.is_groin_exposed() || C.is_chest_exposed()) - partner = C - if(partner) - user_human.mob_climax_in_partner(current_genital, partner, mb_time = 0) - continue + if(current_genital.is_exposed()) + if(src.pulling) + if(iscarbon(src.pulling)) + check_target = src.pulling + else if(src.pulledby) + if(iscarbon(src.pulledby)) + check_target = src.pulledby + if(check_target) + var/mob/living/carbon/C = check_target + if(C.exposed_genitals.len || C.is_groin_exposed() || C.is_chest_exposed()) + partner = C + if(partner) + user_human.mob_climax_in_partner(current_genital, partner, mb_time = 0) + continue + user_human.mob_climax_outside(current_genital, mb_time = 0) return From d09d138aa61e37fa713fdc7aa1cf749c81d7097e Mon Sep 17 00:00:00 2001 From: sarcoph <83266791+sarcoph@users.noreply.github.com> Date: Thu, 5 May 2022 19:26:58 -0800 Subject: [PATCH 26/28] documenting arousal code --- hyperstation/code/obj/fleshlight.dm | 6 +- .../code/modules/arousal/arousal.dm | 194 +++++++++++------- 2 files changed, 121 insertions(+), 79 deletions(-) diff --git a/hyperstation/code/obj/fleshlight.dm b/hyperstation/code/obj/fleshlight.dm index b1174156e..421cfb8f6 100644 --- a/hyperstation/code/obj/fleshlight.dm +++ b/hyperstation/code/obj/fleshlight.dm @@ -59,7 +59,9 @@ C.emote("moan") C.do_jitter_animation() C.adjustArousalLoss(20) //make the target more aroused. - C.mob_climax_instant() //make them cum if they are over the edge. + if(C.can_orgasm() && ishuman(C)) + var/mob/living/carbon/human/user_human = C + user_human.mob_climax_outside(P, mb_time=0) // instant, because we already did a warmup return else to_chat(user, "You don't see anywhere to use this on.") @@ -139,7 +141,7 @@ target.adjustArousalLoss(20) target.do_jitter_animation() if(target.can_orgasm() && prob(5)) - target.mob_climax_outside(target_genital, spillage = target_genital.is_exposed()) + target.mob_climax_outside(target_genital, spillage=target_genital.is_exposed()) if(user.can_orgasm()) var/mob/living/carbon/human/O = user var/impreg_chance = target_genital.name == "vagina" && !P.condom && !P.sounding diff --git a/modular_citadel/code/modules/arousal/arousal.dm b/modular_citadel/code/modules/arousal/arousal.dm index df888287d..ca9b99bf9 100644 --- a/modular_citadel/code/modules/arousal/arousal.dm +++ b/modular_citadel/code/modules/arousal/arousal.dm @@ -128,25 +128,25 @@ /mob/living/carbon/updatearousal() . = ..() - for(var/obj/item/organ/genital/G in internal_organs) - if(istype(G)) + for(var/obj/item/organ/genital/genital in internal_organs) + if(istype(genital)) var/datum/sprite_accessory/S - switch(G.type) + switch(genital.type) if(/obj/item/organ/genital/penis) - S = GLOB.cock_shapes_list[G.shape] + S = GLOB.cock_shapes_list[genital.shape] if(/obj/item/organ/genital/testicles) - S = GLOB.balls_shapes_list[G.shape] + S = GLOB.balls_shapes_list[genital.shape] if(/obj/item/organ/genital/vagina) - S = GLOB.vagina_shapes_list[G.shape] + S = GLOB.vagina_shapes_list[genital.shape] if(/obj/item/organ/genital/breasts) - S = GLOB.breasts_shapes_list[G.shape] + S = GLOB.breasts_shapes_list[genital.shape] if(S?.alt_aroused) - G.aroused_state = isPercentAroused(G.aroused_amount) + genital.aroused_state = isPercentAroused(genital.aroused_amount) if(getArousalLoss() >= isPercentAroused(33)) - G.aroused_state = TRUE + genital.aroused_state = TRUE else - G.aroused_state = FALSE - G.update_appearance() + genital.aroused_state = FALSE + genital.update_appearance() @@ -199,7 +199,7 @@ * Arguments: * - `arousal` - The minimum arousal needed to pass the check. Default is `100`. * - * Returns: `TRUE/FALSE` - Whether or not the mob passes all of the above checks. + * Returns: `TRUE`/`FALSE` - Whether or not the mob passes all of the above checks. */ /mob/living/proc/can_orgasm(arousal = 100) if(src.canbearoused && src.getArousalLoss() >= arousal && ishuman(src) && src.has_dna()) @@ -226,47 +226,53 @@ to_chat(src, "You aren't aroused enough for that.") - -/mob/living/carbon/human/proc/mob_masturbate(obj/item/organ/genital/G, cover = FALSE, mb_time = 30) +/** + * Arguments: + * - `genital`: Genitals being used for this interaction. + * - `cover`: Whether or not the mob is climaxing over themselves. + * - `mb_time`: Warm-up time for this interaction. + */ +/mob/living/carbon/human/proc/mob_masturbate(obj/item/organ/genital/genital, cover = FALSE, mb_time = 30) var/total_fluids = 0 - var/datum/reagents/fluid_source = G.get_fluid_source() + var/datum/reagents/fluid_source = genital.get_fluid_source() var/condomed = FALSE var/sounded = FALSE - if(G.name == "penis") + if(genital.name == "penis") var/obj/item/organ/genital/penis/P = src.getorganslot("penis") condomed = P.condom sounded = P.sounding if(!fluid_source) - to_chat(src, "Your [G.name] is unable to produce it's own fluids, it's missing the organs for it.") + to_chat(src, "Your [genital.name] is unable to produce it's own fluids, it's missing the organs for it.") return total_fluids = fluid_source.total_volume if(mb_time) - src.visible_message("[src] starts to [G.masturbation_verb] [p_their()] [G.name].", \ - "You start to [G.masturbation_verb] your [G.name].", \ - "You start to [G.masturbation_verb] your [G.name].") + src.visible_message("[src] starts to [genital.masturbation_verb] [p_their()] [genital.name].", \ + "You start to [genital.masturbation_verb] your [genital.name].", \ + "You start to [genital.masturbation_verb] your [genital.name].") if(do_after(src, mb_time, target = src)) if(total_fluids > 5 &&!condomed &&!sounded) fluid_source.reaction(src.loc, TOUCH, 1, 0) fluid_source.clear_reagents() - if(!condomed && !sounded && !cover) - src.visible_message("[src] orgasms, cumming[istype(src.loc, /turf/open/floor) ? " onto [src.loc]" : ""]!", \ - "You cum[istype(src.loc, /turf/open/floor) ? " onto [src.loc]" : ""].", \ - "You have relieved yourself.") - if(!condomed &&!sounded && cover)//For when you want to make a mess of yourself. - fluid_source.trans_to(src, total_fluids*G.fluid_transfer_factor) - total_fluids -= total_fluids*G.fluid_transfer_factor - if(total_fluids > 80) // now thats a big cum! - src.add_cum_overlay("large") - if(total_fluids > 5) - fluid_source.reaction(src.loc, TOUCH, 1, 0) - var/mob/living/carbon/human/H = src - if(H && G.name == "penis") - H.cumdrip_rate += rand(5,10) - fluid_source.clear_reagents() - src.visible_message("[src] climaxes over [p_their()] self, using [p_their()] [G.name]!", \ - "You orgasm over yourself, using your [G.name].", \ - "You have climaxed over something, using your [G.name].") - src.add_cum_overlay() + if(!condomed && !sounded)//For when you want to make a mess of yourself. + if(cover) + fluid_source.trans_to(src, total_fluids*genital.fluid_transfer_factor) + total_fluids -= total_fluids*genital.fluid_transfer_factor + if(total_fluids > 80) // now thats a big cum! + src.add_cum_overlay("large") + if(total_fluids > 5) + fluid_source.reaction(src.loc, TOUCH, 1, 0) + var/mob/living/carbon/human/H = src + if(H && genital.name == "penis") + H.cumdrip_rate += rand(5,10) + fluid_source.clear_reagents() + src.visible_message("[src] climaxes over [p_their()] self, using [p_their()] [genital.name]!", \ + "You orgasm over yourself, using your [genital.name].", \ + "You have climaxed over something, using your [genital.name].") + src.add_cum_overlay() + else + src.visible_message("[src] orgasms, cumming[istype(src.loc, /turf/open/floor) ? " onto [src.loc]" : ""]!", \ + "You cum[istype(src.loc, /turf/open/floor) ? " onto [src.loc]" : ""].", \ + "You have relieved yourself.") if(condomed) //condomed src.visible_message("[src] orgasms, climaxing into [p_their()] condom!", \ "You cum into your condom.", \ @@ -275,26 +281,34 @@ src.visible_message("[src] orgasms, but the rod blocks anything from leaking out!", \ "You cum with the rod inside.", \ "You don't quite feel totally relieved.") - if(total_fluids > 0 &&condomed &&!sounded) + if(total_fluids > 0 && condomed && !sounded) src.condomclimax() SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) - if(G.can_climax) + if(genital.can_climax) setArousalLoss(min_arousal) -/mob/living/carbon/human/proc/mob_climax_outside(obj/item/organ/genital/G, mb_time = 30, spillage = TRUE) -//This is used for forced orgasms and other hands-free climaxes +/** + * Mob climaxes using provided genitals without a partner. This is in contrast with `mob_climax_instant()`, + * which does not have a warm-up time and uses all genitals at once to climax. + * + * Arguments: + * - `genital`: The selected genitals for this interaction. Determines fluid type. + * - `mb_time`: The warm-up time for this interaction. + * - `spillage`: Whether or not climaxing causes this mob to spill fluids on the floor. + */ +/mob/living/carbon/human/proc/mob_climax_outside(obj/item/organ/genital/genital, spillage = TRUE, mb_time = 30) var/total_fluids = 0 - var/datum/reagents/fluid_source = G.get_fluid_source() + var/datum/reagents/fluid_source = genital.get_fluid_source() var/unable_to_come = FALSE if(fluid_source) total_fluids = fluid_source.total_volume else unable_to_come = TRUE if(unable_to_come) - src.visible_message("[src] shudders, their [G.name] unable to cum.", \ - "Your [G.name] cannot cum, giving no relief.", \ - "Your [G.name] cannot cum, giving no relief.") + src.visible_message("[src] shudders, their [genital.name] unable to cum.", \ + "Your [genital.name] cannot cum, giving no relief.", \ + "Your [genital.name] cannot cum, giving no relief.") return total_fluids = fluid_source.total_volume if(mb_time) //as long as it's not instant, give a warning @@ -306,19 +320,31 @@ if(total_fluids > 5) fluid_source.reaction(src.loc, TOUCH, 1, 0) fluid_source.clear_reagents() - src.visible_message("[src] orgasms[istype(src.loc, /turf/open/floor) ? ", spilling onto [src.loc]" : ""], with [p_their()] [G.name]!", \ - "You climax[istype(src.loc, /turf/open/floor) ? ", spilling onto [src.loc]" : ""] with your [G.name].", \ - "You climax using your [G.name].") + src.visible_message("[src] orgasms[istype(src.loc, /turf/open/floor) ? ", spilling onto [src.loc]" : ""], with [p_their()] [genital.name]!", \ + "You climax[istype(src.loc, /turf/open/floor) ? ", spilling onto [src.loc]" : ""] with your [genital.name].", \ + "You climax using your [genital.name].") else - src.visible_message("[src] orgasms with [p_their()] [G.name]!", \ - "You climax with your [G.name].", \ - "You climax using your [G.name].") + src.visible_message("[src] orgasms with [p_their()] [genital.name]!", \ + "You climax with your [genital.name].", \ + "You climax using your [genital.name].") SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) - if(G.can_climax) + if(genital.can_climax) setArousalLoss(min_arousal) +/** + * Climax over a partner with a selected organ. This is external and creates spills on the partner/floor, + * and does not have a chance to impregnate. For this interaction, the partner may be an object instead of + * a person (but it cannot be an animal). + * + * Arguments: + * - `genital`: The genital being used for this interaction. Determines fluid type and volume. + * - `partner`: The thing being used as a partner. Generally an object or a person. + * - `mb_time`: The warm-up time for this interaction. Default is 3 seconds + */ /mob/living/carbon/human/proc/mob_climax_cover(obj/item/organ/genital/genital, atom/partner, mb_time=3 SECONDS) + if(isnoncarbon(partner)) // deny animals + return FALSE if(istype(genital, /obj/item/organ/genital/penis)) var/obj/item/organ/genital/penis/P = genital if(P.condom) @@ -366,13 +392,25 @@ setArousalLoss(min_arousal) -/mob/living/carbon/human/proc/mob_climax_in_partner(obj/item/organ/genital/G, mob/living/carbon/partner, spillage=TRUE, remote=FALSE, mb_time=3 SECONDS) +/** + * Climax inside a partner using a selected organ. + * + * Arguments: + * - `genital`: The genital being used for this interaction. Determines type/volume of fluids. + * - `partner`: The person that this mob is climaxing in. + * - `spillage`: Whether or not the fluids will spill on the ground. + * - `remote`: Whether or not this interaction is being triggered long-distance. + * - `mb_time`: Warm-up time for this interaction. + * + * Returns: `TRUE`/`FALSE` - whether or not this interaction succeeded. + */ +/mob/living/carbon/human/proc/mob_climax_in_partner(obj/item/organ/genital/genital, mob/living/carbon/partner, spillage=TRUE, remote=FALSE, mb_time=3 SECONDS) if(isnoncarbon(partner)) // deny animals return FALSE var/total_fluids = 0 - var/datum/reagents/fluid_source = G.get_fluid_source() + var/datum/reagents/fluid_source = genital.get_fluid_source() if(!fluid_source) - to_chat(src, "Your [G.name] is unable to produce it's own fluids, it's missing the organs for it.") + to_chat(src, "Your [genital.name] is unable to produce it's own fluids, it's missing the organs for it.") return FALSE total_fluids = fluid_source.total_volume var/partner_text = partner @@ -386,23 +424,23 @@ if(!remote && !in_range(src, partner)) return FALSE if(spillage) - fluid_source.trans_to(partner, total_fluids*G.fluid_transfer_factor) - total_fluids -= total_fluids*G.fluid_transfer_factor + fluid_source.trans_to(partner, total_fluids*genital.fluid_transfer_factor) + total_fluids -= total_fluids*genital.fluid_transfer_factor if(total_fluids > 5) fluid_source.reaction(partner.loc, TOUCH, 1, 0) var/mob/living/carbon/human/H = partner if(H) H.cumdrip_rate += rand(5,10) fluid_source.clear_reagents() - src.visible_message("[src] climaxes with [partner], overflowing and spilling, using [p_their()] [G.name]!",\ - "You orgasm with [partner], spilling out of them, using your [G.name].",\ - "You have climaxed with someone, spilling out of them, using your [G.name].") + src.visible_message("[src] climaxes with [partner], overflowing and spilling, using [p_their()] [genital.name]!",\ + "You orgasm with [partner], spilling out of them, using your [genital.name].",\ + "You have climaxed with someone, spilling out of them, using your [genital.name].") SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) SEND_SIGNAL(partner, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) else //knots, portal fleshlights, and other non-spilling orgasms var/can_inflate = TRUE - if(istype(G, /obj/item/organ/genital/penis)) - var/obj/item/organ/genital/penis/P = G + if(istype(genital, /obj/item/organ/genital/penis)) + var/obj/item/organ/genital/penis/P = genital if(P.condom)//condomed. can_inflate = FALSE src.condomclimax() @@ -410,14 +448,14 @@ fluid_source.trans_to(partner, total_fluids) if(!spillage && total_fluids > 80) // hyper - cum inflation, ONLY if not wearing a condom partner.expand_belly(1) - src.visible_message("[src] climaxes with [partner_text], [p_their()] [G.name] spilling nothing!", \ - "You ejaculate with [partner_text], your [G.name] spilling nothing.", \ - "You have climaxed inside someone, your [G.name] spilling nothing.") + src.visible_message("[src] climaxes with [partner_text], [p_their()] [genital.name] spilling nothing!", \ + "You ejaculate with [partner_text], your [genital.name] spilling nothing.", \ + "You have climaxed inside someone, your [genital.name] spilling nothing.") if(remote) to_chat(partner, "You feel someone ejaculate inside you.") SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) SEND_SIGNAL(partner, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) - if(G.can_climax) + if(genital.can_climax) setArousalLoss(min_arousal) //Hyper - antag code var/mob/living/carbon/partner_carbon = partner @@ -499,9 +537,9 @@ /mob/living/carbon/human/proc/target_genitals(mob/living/carbon/human/T) var/obj/item/organ/genital/ret_organ var/list/genitals_list = list() - for(var/obj/item/organ/genital/G in T.internal_organs) - if(G.is_exposed() && !G.dontlist) - genitals_list += G + for(var/obj/item/organ/genital/genital in T.internal_organs) + if(genital.is_exposed() && !genital.dontlist) + genitals_list += genital if(genitals_list.len) ret_organ = input(src, "", "Genitals", null) as null|obj in genitals_list return ret_organ @@ -520,10 +558,10 @@ /mob/living/carbon/human/proc/pick_climax_genitals(masturbation=FALSE, title="Climax") var/obj/item/organ/genital/ret_organ var/list/genitals_list = list() - for(var/obj/item/organ/genital/G in internal_organs) - if(G.can_climax && G.is_exposed() && !G.dontlist) - if(!masturbation || G.can_masturbate_with) - genitals_list += G + for(var/obj/item/organ/genital/genital in internal_organs) + if(genital.can_climax && genital.is_exposed() && !genital.dontlist) + if(!masturbation || genital.can_masturbate_with) + genitals_list += genital if(genitals_list.len) ret_organ = input(src, "with what?", title, null) as null|obj in genitals_list return ret_organ @@ -533,9 +571,11 @@ /** * Prompts the user to select a mob that they are either pulling or being pulled by. * + * Arguments: + * - `needs_exposed`: Whether or not the partner must have exposed genitals. * Returns: `null`|`mob/living/carbon`: The selected mob. */ -/mob/living/carbon/human/proc/pick_partner(needs_exposed = TRUE) +/mob/living/carbon/human/proc/pick_partner(needs_exposed=TRUE) var/list/partners = list(src.pulling, src.pulledby) for(var/I in partners) if(!iscarbon(I)) @@ -700,7 +740,7 @@ var/obj/item/organ/genital/picked_organ picked_organ = pick_climax_genitals() if(picked_organ) - var/mob/living/carbon/partner = pick_partner(FALSE) //Get your partner, clothed or not. + var/mob/living/carbon/partner = pick_partner(needs_exposed=FALSE) //Get your partner, clothed or not. if(partner) mob_climax_cover(picked_organ, partner) else From 852eee166d4adba01330cbc699bbe9c1a6c56991 Mon Sep 17 00:00:00 2001 From: sarcoph <83266791+sarcoph@users.noreply.github.com> Date: Thu, 5 May 2022 19:42:35 -0800 Subject: [PATCH 27/28] consistency's sake --- code/game/objects/structures.dm | 4 ++-- .../objects/structures/crates_lockers/closets.dm | 2 +- .../objects/structures/crates_lockers/crates.dm | 2 +- modular_citadel/code/modules/arousal/arousal.dm | 14 +++++++------- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm index b25e58157..4f404015b 100644 --- a/code/game/objects/structures.dm +++ b/code/game/objects/structures.dm @@ -3,7 +3,7 @@ pressure_resistance = 8 max_integrity = 300 interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND | INTERACT_ATOM_UI_INTERACT - var/climb_time = 20 + var/climb_time=20 var/climb_stun = 20 var/climbable = FALSE var/mob/living/structureclimber @@ -66,7 +66,7 @@ src.add_fingerprint(user) user.visible_message("[user] starts climbing onto [src].", \ "You start climbing onto [src]...") - var/adjusted_climb_time = climb_time + var/adjusted_climb_time=climb_time if(user.restrained()) //climbing takes twice as long when restrained. adjusted_climb_time *= 2 if(isalien(user)) diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index 1d960b210..b8627c554 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -243,7 +243,7 @@ return FALSE take_contents() playsound(loc, close_sound, 15, 1, -3) - climb_time = initial(climb_time) + climb_time=initial(climb_time) opened = FALSE density = TRUE update_icon() diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm index 5b16126e0..5e7e7091c 100644 --- a/code/game/objects/structures/crates_lockers/crates.dm +++ b/code/game/objects/structures/crates_lockers/crates.dm @@ -10,7 +10,7 @@ allow_dense = TRUE dense_when_open = TRUE climbable = TRUE - climb_time = 10 //real fast, because let's be honest stepping into or onto a crate is easy + climb_time=10 //real fast, because let's be honest stepping into or onto a crate is easy climb_stun = 0 //climbing onto crates isn't hard, guys delivery_icon = "deliverycrate" var/obj/item/paper/fluff/jobs/cargo/manifest/manifest diff --git a/modular_citadel/code/modules/arousal/arousal.dm b/modular_citadel/code/modules/arousal/arousal.dm index ca9b99bf9..8b8780b46 100644 --- a/modular_citadel/code/modules/arousal/arousal.dm +++ b/modular_citadel/code/modules/arousal/arousal.dm @@ -201,7 +201,7 @@ * * Returns: `TRUE`/`FALSE` - Whether or not the mob passes all of the above checks. */ -/mob/living/proc/can_orgasm(arousal = 100) +/mob/living/proc/can_orgasm(arousal=100) if(src.canbearoused && src.getArousalLoss() >= arousal && ishuman(src) && src.has_dna()) return TRUE return FALSE @@ -232,7 +232,7 @@ * - `cover`: Whether or not the mob is climaxing over themselves. * - `mb_time`: Warm-up time for this interaction. */ -/mob/living/carbon/human/proc/mob_masturbate(obj/item/organ/genital/genital, cover = FALSE, mb_time = 30) +/mob/living/carbon/human/proc/mob_masturbate(obj/item/organ/genital/genital, cover=FALSE, mb_time=3 SECONDS) var/total_fluids = 0 var/datum/reagents/fluid_source = genital.get_fluid_source() var/condomed = FALSE @@ -297,7 +297,7 @@ * - `mb_time`: The warm-up time for this interaction. * - `spillage`: Whether or not climaxing causes this mob to spill fluids on the floor. */ -/mob/living/carbon/human/proc/mob_climax_outside(obj/item/organ/genital/genital, spillage = TRUE, mb_time = 30) +/mob/living/carbon/human/proc/mob_climax_outside(obj/item/organ/genital/genital, spillage=TRUE, mb_time=3 SECONDS) var/total_fluids = 0 var/datum/reagents/fluid_source = genital.get_fluid_source() var/unable_to_come = FALSE @@ -475,7 +475,7 @@ * - `partner`: This mob's partner for the interaction. * - `impreg`: Whether or not this interaction has a chance to impregnate. */ -/mob/living/carbon/human/proc/mob_climax_inside_spillage(obj/item/organ/genital/picked_organ, mob/living/carbon/partner, impreg = FALSE) +/mob/living/carbon/human/proc/mob_climax_inside_spillage(obj/item/organ/genital/picked_organ, mob/living/carbon/partner, impreg=FALSE) var/obj/item/organ/genital/penis/_penis = picked_organ if(picked_organ.name != "penis" || _penis.sounding || _penis.condom) mob_climax_in_partner(picked_organ, partner, spillage=FALSE) @@ -496,7 +496,7 @@ * - `container`: The container being targeted. * - `mb_time`: The warm-up time for this interaction. */ -/mob/living/carbon/human/proc/mob_fill_container(obj/item/organ/genital/genital, obj/item/reagent_containers/container, mb_time = 30) +/mob/living/carbon/human/proc/mob_fill_container(obj/item/organ/genital/genital, obj/item/reagent_containers/container, mb_time=3 SECONDS) if(!container) //Something weird happened to_chat(src, "You need a container to do this!") return @@ -813,9 +813,9 @@ if(C.exposed_genitals.len || C.is_groin_exposed() || C.is_chest_exposed()) partner = C if(partner) - user_human.mob_climax_in_partner(current_genital, partner, mb_time = 0) + user_human.mob_climax_in_partner(current_genital, partner, mb_time=0) continue - user_human.mob_climax_outside(current_genital, mb_time = 0) + user_human.mob_climax_outside(current_genital, mb_time=0) return From c469820069e1d1d54ad664d83561f9627a862c8d Mon Sep 17 00:00:00 2001 From: sarcoph <83266791+sarcoph@users.noreply.github.com> Date: Thu, 5 May 2022 19:44:25 -0800 Subject: [PATCH 28/28] oops --- code/game/objects/structures.dm | 4 ++-- code/game/objects/structures/crates_lockers/closets.dm | 2 +- code/game/objects/structures/crates_lockers/crates.dm | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm index 4f404015b..b25e58157 100644 --- a/code/game/objects/structures.dm +++ b/code/game/objects/structures.dm @@ -3,7 +3,7 @@ pressure_resistance = 8 max_integrity = 300 interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND | INTERACT_ATOM_UI_INTERACT - var/climb_time=20 + var/climb_time = 20 var/climb_stun = 20 var/climbable = FALSE var/mob/living/structureclimber @@ -66,7 +66,7 @@ src.add_fingerprint(user) user.visible_message("[user] starts climbing onto [src].", \ "You start climbing onto [src]...") - var/adjusted_climb_time=climb_time + var/adjusted_climb_time = climb_time if(user.restrained()) //climbing takes twice as long when restrained. adjusted_climb_time *= 2 if(isalien(user)) diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index b8627c554..1d960b210 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -243,7 +243,7 @@ return FALSE take_contents() playsound(loc, close_sound, 15, 1, -3) - climb_time=initial(climb_time) + climb_time = initial(climb_time) opened = FALSE density = TRUE update_icon() diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm index 5e7e7091c..5b16126e0 100644 --- a/code/game/objects/structures/crates_lockers/crates.dm +++ b/code/game/objects/structures/crates_lockers/crates.dm @@ -10,7 +10,7 @@ allow_dense = TRUE dense_when_open = TRUE climbable = TRUE - climb_time=10 //real fast, because let's be honest stepping into or onto a crate is easy + climb_time = 10 //real fast, because let's be honest stepping into or onto a crate is easy climb_stun = 0 //climbing onto crates isn't hard, guys delivery_icon = "deliverycrate" var/obj/item/paper/fluff/jobs/cargo/manifest/manifest