diff --git a/code/__DEFINES/hyper_arousal.dm b/code/__DEFINES/hyper_arousal.dm
index d3f9d6ceb..a0450b138 100644
--- a/code/__DEFINES/hyper_arousal.dm
+++ b/code/__DEFINES/hyper_arousal.dm
@@ -5,4 +5,9 @@
#define BELLY_STRETCH_SIZE 6 // for flavor text
#define BUTT_MIN_SIZE 0
-#define BUTT_MAX_SIZE 8
\ No newline at end of file
+#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/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm
index c91c0fda4..363d4c38b 100644
--- a/code/__DEFINES/is_helpers.dm
+++ b/code/__DEFINES/is_helpers.dm
@@ -60,6 +60,8 @@ GLOBAL_LIST_INIT(turfs_without_ground, typecacheof(list(
#define isbrain(A) (istype(A, /mob/living/brain))
+#define isnoncarbon(A) (isliving(A) && !iscarbon(A))
+
//Carbon mobs
#define iscarbon(A) (istype(A, /mob/living/carbon))
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
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index a036dbb7f..fc87bd816 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -1647,8 +1647,8 @@ GLOBAL_LIST_EMPTY(roundstart_races)
)
if (target.canbearoused)
target.adjustArousalLoss(5)
- if (target.getArousalLoss() >= 100 && ishuman(target) && HAS_TRAIT(target, TRAIT_MASO) && target.has_dna())
- target.mob_climax(forced_climax=TRUE)
+ 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.getArousalLoss() >= 100 && ishuman(H) && H.has_dna())
- H.mob_climax(forced_climax=TRUE)
+ 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 ca11c14d0..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.getArousalLoss() >= 100 && ishuman(B) && B.has_dna())
- B.mob_climax(forced_climax=TRUE)
+ 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 18e90b3a6..5fba1aa4b 100644
--- a/code/modules/ruins/objects_and_mobs/sin_ruins.dm
+++ b/code/modules/ruins/objects_and_mobs/sin_ruins.dm
@@ -282,16 +282,16 @@
. = ..()
if(.)
return
- if(chalice_taken == 1)
+ if(chalice_taken)
to_chat(user, "You hear a voice in your head... \"My chalice has already been taken, dear. I cannot give you another...\"")
return
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 (ishuman(user) && user.has_dna())
- user.mob_climax(forced_climax=TRUE)
- to_chat(user, "You hear a voice in your head... \"You are worth 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 = 1
\ 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/code/modules/surgery/organs/vocal_cords.dm b/code/modules/surgery/organs/vocal_cords.dm
index b2d19c586..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_climax=TRUE)
+ 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_climax=TRUE)
+ 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/modules/arousal/arousalhud.dm b/hyperstation/code/modules/arousal/arousalhud.dm
index 509438813..318edefd0 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] (Visable)" : "[G.name] (Visable)"))]
"
+ 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")
@@ -45,20 +53,17 @@
if(Belly.inflatable)
dat += "Deflate belly"
dat += "(Shrink your belly down a size)
"
-
-
- if(user.pulling)
+
+ if(user.pulling && !isnoncarbon(user.pulling)) // do not fuck animals
dat += "Climax over [user.pulling]" //you can cum on objects if you really want...
dat += "(Orgasm over a person or object.)
"
- if(isliving(user.pulling))
- if(iscarbon(user.pulling))
- dat += "Climax with [user.pulling]"
- dat += {"(Orgasm with another person.)
"}
-
- var/mob/living/carbon/human/H = user.pulling
- if(H.breedable && P && H)
- dat += "Impregnate [U.pulling] ([clamp(U.impregchance,0,100)]%)"
- dat += "(Climax inside another person, and attempt to knock them up.)
"
+ if(iscarbon(user.pulling))
+ dat += "Climax with [user.pulling]"
+ dat += {"(Orgasm with another person.)
"}
+ var/mob/living/carbon/human/H = user.pulling
+ if(H && H.breedable && P)
+ dat += "Impregnate [U.pulling] ([clamp(U.impregchance,0,100)]%)"
+ dat += "(Climax inside another person, and attempt to knock them up.)
"
else
dat += "Climax over"
dat += "(Requires a partner)
"
@@ -81,11 +86,9 @@
dat += "Old Menu"
dat += "Toggle Undergarments "
dat += "
"
-
var/datum/browser/popup = new(user, "arousal", "Arousal Panel")
popup.set_content(dat)
popup.set_title_image(user.browse_rsc_icon(icon, icon_state), 500,600)
-
popup.open()
@@ -140,7 +143,7 @@
T.toggle_visibility(picked_visibility)
if(href_list["masturbate"])
- if (H.arousalloss >= (H.max_arousal / 100) * 33) //requires 33% arousal.
+ if (H.isPercentAroused(33)) //requires 33% arousal.
H.solomasturbate()
return
else
@@ -148,7 +151,7 @@
return
if(href_list["container"])
- if (H.arousalloss >= (H.max_arousal / 100) * 33) //requires 33% arousal.
+ if (H.isPercentAroused(33)) //requires 33% arousal.
H.cumcontainer()
return
else
@@ -156,7 +159,7 @@
return
if(href_list["clothesplosion"])
- if (H.arousalloss >= (H.max_arousal / 100) * 33) //Requires 33% arousal.
+ if (H.isPercentAroused(33)) //Requires 33% arousal.
H.clothesplosion()
return
else
@@ -164,7 +167,7 @@
return
if(href_list["climaxself"])
- if (H.arousalloss >= (H.max_arousal / 100) * 33) //requires 33% arousal.
+ if (H.isPercentAroused(33)) //requires 33% arousal.
H.climaxself()
return
else
@@ -172,7 +175,7 @@
return
if(href_list["climax"])
- if (H.arousalloss >= (H.max_arousal / 100) * 33) //requires 33% arousal.
+ if (H.isPercentAroused(33)) //requires 33% arousal.
H.climaxalone(FALSE)
return
else
@@ -180,7 +183,7 @@
return
if(href_list["climaxover"])
- if (H.arousalloss >= (H.max_arousal / 100) * 33) //requires 33% arousal.
+ if (H.isPercentAroused(33)) //requires 33% arousal.
H.climaxover(usr.pulling)
return
else
@@ -188,7 +191,7 @@
return
if(href_list["climaxwith"])
- if (H.arousalloss >= (H.max_arousal / 100) * 33) //requires 33% arousal.
+ if (H.isPercentAroused(33)) //requires 33% arousal.
H.climaxwith(usr.pulling)
return
else
@@ -196,7 +199,7 @@
return
if(href_list["impreg"])
- if (H.arousalloss >= (H.max_arousal / 100) * 33) //requires 33% arousal.
+ if (H.isPercentAroused(33)) //requires 33% arousal.
H.impregwith(usr.pulling)
return
else
@@ -331,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)
@@ -365,55 +368,31 @@ obj/screen/arousal/proc/kiss()
to_chat(src, "You cannot climax without choosing genitals.")
return
-/mob/living/carbon/human/proc/climaxwith(mob/living/T)
-
- var/mob/living/carbon/human/L = pick_partner()
+/mob/living/carbon/human/proc/climaxwith(mob/living/carbon/human/partner)
+ if(!partner)
+ to_chat(src, "You cannot do this alone.")
+ return
var/obj/item/organ/genital/picked_organ
picked_organ = pick_climax_genitals()
- if(picked_organ)
- var/mob/living/partner = L
- if(partner)
- src << browse(null, "window=arousal") //alls fine, we can close the window now.
- var/obj/item/organ/genital/penis/P = picked_organ
- var/spillage = "No" //default to no, just incase player has items on to prevent climax
- if(!P.condom == 1&&!P.sounding == 1) //you cant climax with a condom on or sounding in.
- spillage = input(src, "Would your fluids spill outside?", "Choose overflowing option", "Yes") as anything in list("Yes", "No")
- if(spillage == "Yes")
- mob_climax_partner(picked_organ, partner, TRUE, FALSE, FALSE)
- else
- mob_climax_partner(picked_organ, partner, FALSE, FALSE, FALSE)
- else
- to_chat(src, "You cannot do this alone.")
- return
- else //They either lack organs that can masturbate, or they didn't pick one.
+ if(!picked_organ)
to_chat(src, "You cannot climax without choosing genitals.")
return
+ src << browse(null, "window=arousal") // close arousal window
+ mob_climax_inside_spillage(picked_organ, partner, impreg=FALSE)
-
-/mob/living/carbon/human/proc/climaxover(mob/living/T)
-
- var/mob/living/carbon/human/L = T
+/mob/living/carbon/human/proc/climaxover(atom/partner)
var/obj/item/organ/genital/picked_organ
picked_organ = pick_climax_genitals()
- if(picked_organ)
- src << browse(null, "window=arousal") //alls fine, we can close the window now.
- var/mob/living/partner = L
- if(partner)
- var/obj/item/organ/genital/penis/P = picked_organ
- if(P.condom == 1)
- to_chat(src, "You cannot do this action with a condom on.")
- return
- if(P.sounding == 1)
- to_chat(src, "You cannot do this action with a sounding in.")
- return
- mob_climax_partner(picked_organ, partner, FALSE, FALSE, TRUE)
- else
- to_chat(src, "You cannot do this alone.")
- return
- else //They either lack organs that can masturbate, or they didn't pick one.
+ if(!picked_organ)
to_chat(src, "You cannot climax without choosing genitals.")
return
+ src << browse(null, "window=arousal") //alls fine, we can close the window now.
+ if(!partner || isnoncarbon(partner))
+ to_chat(src, "You cannot do this alone.")
+ return
+ mob_climax_cover(picked_organ, partner)
+
/mob/living/carbon/human/proc/clothesplosion()
if(usr.restrained(TRUE))
@@ -428,39 +407,29 @@ obj/screen/arousal/proc/kiss()
H.visible_message("[H] explodes out of their clothes!'")
-
-/mob/living/carbon/human/proc/impregwith(mob/living/T)
-
- var/mob/living/carbon/human/L = pick_partner()
- var/obj/item/organ/genital/picked_organ
- picked_organ = src.getorganslot("penis") //Impregnation must be done with a penis.
- if(picked_organ)
- var/mob/living/partner = L
- if(partner)
- if(!partner.breedable)//check if impregable.
- to_chat(src, "Your partner cannot be impregnated.")//some fuckary happening, you shouldnt even get to this point tbh.
- return
- var/obj/item/organ/genital/penis/P = picked_organ
- //you cant impreg with a condom on or sounding in.
- if(P.condom == 1)
- to_chat(src, "You cannot do this action with a condom on.")
- return
- if(P.sounding == 1)
- to_chat(src, "You cannot do this action with a sounding in.")
- return
- src << browse(null, "window=arousal") //alls fine, we can close the window now.
- //Keeping this for messy fun
- var/spillage = input(src, "Would your fluids spill outside?", "Choose overflowing option", "Yes") as anything in list("Yes", "No")
- if(spillage == "Yes")
- mob_climax_partner(picked_organ, partner, TRUE, TRUE, FALSE)
- else
- mob_climax_partner(picked_organ, partner, FALSE, TRUE, FALSE)
- else
- to_chat(src, "You cannot do this alone.")
- return
- else //no penis :(
- to_chat(src, "You cannot impregnate without a penis.")
+/mob/living/carbon/human/proc/impregwith(mob/living/carbon/T)
+ var/mob/living/carbon/human/partner = pick_partner()
+ var/obj/item/organ/genital/penis/picked_organ = src.getorganslot("penis")
+ // wow that is a lot of conditionals huh
+ if(!picked_organ) // no penis :(
+ to_chat(src, "You cannot impregnate someone without a penis.")
return
+ if(!partner)
+ to_chat(src, "You cannot do this alone.")
+ return
+ if(!partner.breedable) //check if impregable.
+ to_chat(src, "Your partner cannot be impregnated.")
+ //some fuckery happening, you shouldnt even get to this point tbh.
+ return
+ if(picked_organ.condom)
+ to_chat(src, "You cannot impregnate someone with a condom on.")
+ return
+ if(picked_organ.sounding)
+ 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_inside_spillage(picked_organ, partner, impreg=TRUE)
+
/mob/living/carbon/human/proc/cumcontainer(mob/living/T)
//We'll need hands and no restraints.
@@ -500,6 +469,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"))
@@ -523,7 +497,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/condom.dm b/hyperstation/code/obj/condom.dm
index 727ff4739..2c3122fe2 100644
--- a/hyperstation/code/obj/condom.dm
+++ b/hyperstation/code/obj/condom.dm
@@ -115,7 +115,6 @@ obj/item/condom/update_icon()
return
if(!P.condom)
return
-
var/obj/item/condom/filled/C = new
P.linked_organ.reagents.trans_to(C, P.linked_organ.reagents.total_volume)
C.loc = loc
diff --git a/hyperstation/code/obj/farming/farming.dm b/hyperstation/code/obj/farming/farming.dm
index 8c203d78d..ad4671774 100644
--- a/hyperstation/code/obj/farming/farming.dm
+++ b/hyperstation/code/obj/farming/farming.dm
@@ -1,5 +1,5 @@
-/obj/item/reagent_containers/food/snacks/grown/wheat/
- var stacktype = /obj/item/stack/tile/hay
+/obj/item/reagent_containers/food/snacks/grown/wheat
+ var/stacktype = /obj/item/stack/tile/hay
var/tile_coefficient = 0.02 // same as grass
/obj/item/reagent_containers/food/snacks/grown/wheat/attack_self(mob/user)
diff --git a/hyperstation/code/obj/fleshlight.dm b/hyperstation/code/obj/fleshlight.dm
index 125c7ec34..421cfb8f6 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,27 +55,22 @@
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.getArousalLoss() >= 100 && ishuman(C) && C.has_dna())
- C.mob_climax(forced_climax=TRUE) //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.")
-
inuse = 0
..()
//Hyperstation 13 portal fleshlight
//kinky!
-
/obj/item/portallight
name = "portal fleshlight"
desc = "A silver love(TM) fleshlight, used to stimulate someones penis, with bluespace tech that allows lovers to hump at a distance."
@@ -95,7 +88,6 @@
var/paired = 0
var/obj/item/portalunderwear
var/useable = FALSE
- var/option = ""
/obj/item/portallight/examine(mob/user)
. = ..()
@@ -104,136 +96,109 @@
else
. += "The device is paired, and awaiting input. "
-/obj/item/portallight/attack(mob/living/carbon/C, mob/living/user) //use portallight! nearly the same as the fleshlight apart from you have a buddy!
- var/obj/item/organ/genital/penis/P = C.getorganslot("penis")
-
+/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(!useable)
- to_chat(user, "It seems the device has failed or your partner is not wearing their device.")
-
- if(C == user)//if your using it on yourself, more options! otherwise, just fuck.
+ if(!UpdateUsability())
+ to_chat(holder, "\The [src] appears to be currently unusable.")
+ return
+ var/option = "Fuck"
+ if(user == holder)
option = input(usr, "Choose action", "Portal Fleshlight", "Fuck") in list("Fuck", "Lick", "Touch")
- else
- option = "Fuck"
-
- var/obj/item/organ/genital/G
- if(istype(portalunderwear.loc, /obj/item/organ/genital)) //Sanity check. Without this it will runtime error.
- G = portalunderwear.loc
- if(!G)
+ 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 = user.getorganslot("penis")
+ if(option == "Fuck" && !P.is_exposed())
+ to_chat(holder, "You don't see anywhere to use this on.")
return
- var/mob/living/carbon/human/M = G.owner
-
- if(option == "Fuck"&&!P.is_exposed()) //we are trying to fuck with no penis!
- to_chat(user, "You don't see anywhere to use this on.")
- return
- else //other options dont need checks
- 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.
- if(option == "Fuck")
- playsound(src, 'sound/lewd/slaps.ogg', 30, 1, -1) //slapping sound for fuck.
-
- inuse = 0
- if(!(C == user))
- C.visible_message("[user] pumps [src] on [C]'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.
- M.emote("moan")
-
- if(option == "Fuck")// normal fuck
- to_chat(M, "You feel a [P.length] inch, [P.shape] shaped penis pumping through the portal into your [G.name].")//message your partner, and kinky!
- if(prob(30)) //30% chance to make them moan.
- C.emote("moan")
- if(prob(30)) //30% chance to make your partner moan.
- M.emote("moan")
- C.adjustArousalLoss(20)
- M.adjustArousalLoss(20)
- M.do_jitter_animation() //make your partner shake too!
-
- if (M.getArousalLoss() >= 100 && ishuman(M) && prob(5))//Why not have a probability to cum when someone's getting nailed with max arousal?~
- if(G.is_exposed()) //Oh yea, if vagina is not exposed, the climax will not cause a spill
- M.mob_climax_outside(G, spillage = TRUE)
- else
- M.mob_climax_outside(G, spillage = FALSE)
-
- if (C.getArousalLoss() >= 100 && ishuman(C) && C.has_dna())
- var/mob/living/carbon/human/O = C
-
- if( (P.condom == 1) || (P.sounding == 1)) //If coundomed and/or sounded, do not fire impreg chance
- O.mob_climax_partner(P, M, FALSE, FALSE, FALSE, TRUE)
- else //Else, fire impreg chance
- if(G.name == "vagina") //no more spontaneous impregnations through the butt!
- O.mob_climax_partner(P, M, FALSE, TRUE, FALSE, TRUE)
- else
- O.mob_climax_partner(P, M, FALSE, FALSE, FALSE, TRUE)
-
- if(option == "Lick")
- to_chat(M, "You feel a tongue lick you through the portal against your [G.name].")
- M.adjustArousalLoss(10)
- if(option == "Touch")
- to_chat(M, "You feel someone touching your [G.name] through the portal.")
- M.adjustArousalLoss(5)
-
+ /*
+ WARMUP START - prevents spam/instant climax
+ */
+ inuse = TRUE
+ 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
+ /*
+ WARMUP END - proceed with the action
+ */
+ switch(option)
+ if("Fuck")
+ playsound(src, 'sound/lewd/slaps.ogg', 30, 1, -1)
+ if(user == holder)
+ holder.visible_message("[holder] pumps [src] on their penis.",
+ "You pump the fleshlight on your penis.")
+ else
+ 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))
+ 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(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_in_partner(P, target, spillage=FALSE, remote=TRUE) && impreg_chance)
+ target.impregnate(by=O)
+ if("Lick")
+ 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")
+ 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)
+ 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/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)
- var/mob/living/carbon/human/H = G.owner
+/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
- 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
//Hyperstation 13 portal underwear
//can be attached to vagina or anus, just like the vibrator, still requires pairing with the portallight
-
/obj/item/portalpanties
name = "portal panties"
desc = "A silver love(TM) pair of portal underwear, with bluespace tech allows lovers to hump at a distance. Needs to be paired with a portal fleshlight before use."
@@ -252,7 +217,6 @@
else
. += "The device is paired, and awaiting attachment. "
-
/obj/item/portalpanties/attackby(obj/item/I, mob/living/user) //pairing
if(istype(I, /obj/item/portallight))
var/obj/item/portallight/P = I
@@ -268,11 +232,9 @@
..() //just allows people to hit it with other objects, if they so wished.
/obj/item/portalpanties/attack(mob/living/carbon/C, mob/living/user)
-
if(!portallight) //we arent paired yet! noobie trap, let them know.
to_chat(user, "[src] can only be attached once paired with a portal fleshlight.")
return
-
var/obj/item/organ/genital/picked_organ
var/mob/living/carbon/human/S = user
var/mob/living/carbon/human/T = C
@@ -282,21 +244,17 @@
"[user] is trying to put [src] on you!")
if(!do_mob(user, C, 3 SECONDS))//warn them and have a delay of 5 seconds to apply.
return
-
if((picked_organ.name == "vagina")||(picked_organ.name == "anus")) //only fits on a vagina or anus
-
src.shapetype = picked_organ.name
if(!picked_organ.equipment)
to_chat(user, "You wrap [src] around [T]'s [picked_organ.name].")
else
to_chat(user, "They already have [picked_organ.equipment.name] there.")
return
-
if(!user.transferItemToLoc(src, picked_organ)) //check if you can put it in
return
src.attached = TRUE
picked_organ.equipment = src
-
var/obj/item/portallight/P = portallight
//now we need to send what they look like, but saddly if the person changes colour for what ever reason, it wont update. but dont tell people shh.
if(P) //just to make sure
@@ -312,6 +270,7 @@
var/obj/item/portallight/P = portallight
P.updatesleeve()
+
/obj/item/storage/box/portallight
name = "Portal Fleshlight and Underwear"
icon = 'hyperstation/icons/obj/fleshlight.dmi'
diff --git a/hyperstation/code/obj/pregnancytester.dm b/hyperstation/code/obj/pregnancytester.dm
index 974fda3ab..d08610271 100644
--- a/hyperstation/code/obj/pregnancytester.dm
+++ b/hyperstation/code/obj/pregnancytester.dm
@@ -8,7 +8,6 @@
var/results = "null"
w_class = WEIGHT_CLASS_TINY
-
/obj/item/pregnancytest/attack_self(mob/user)
if(QDELETED(src))
return
@@ -16,28 +15,26 @@
return
if(isAI(user))
return
- if(user.stat > 0)//unconscious or dead
+ if(user.stat > CONSCIOUS)//unconscious or dead
return
- if(status == 1)
- return //Already been used once, pregnancy tests only work once.
- test(user)
-
-/obj/item/pregnancytest/proc/force(mob/living/user)
- //Force it negative
- icon_state = "negative"
- name = "[results] pregnancy test"
- status = 1
- to_chat(user, "You use the pregnancy test, the display reads negative!")
-
-
-/obj/item/pregnancytest/proc/test(mob/living/user)
+ Test(user)
+/obj/item/pregnancytest/proc/Test(mob/living/user)
+ if(status)
+ return
+ var/_results = FALSE
var/obj/item/organ/genital/womb/W = user.getorganslot("womb")
- if(W.pregnant == 1)
- results = "positive"
- icon_state = "positive"
- name = "[results] pregnancy test"
- status = 1
- to_chat(user, "You use the pregnancy test, the display reads positive!")
- else
- force(user)
\ No newline at end of file
+ if(!W)
+ return // no reason to waste the single-use on them
+ if(W.pregnant)
+ _results = TRUE
+ UpdateResult(user, _results)
+
+/obj/item/pregnancytest/proc/UpdateResult(mob/living/user, results)
+ var/result_text = results ? "positive" : "negative"
+ src.results = result_text
+ icon_state = result_text
+ name = "[results] preganancy test"
+ status = TRUE
+ if(user)
+ to_chat(user, "You use the pregnancy test, the display reads [results]!")
\ No newline at end of file
diff --git a/hyperstation/code/obj/vibrator.dm b/hyperstation/code/obj/vibrator.dm
index 51f495945..40e1446e9 100644
--- a/hyperstation/code/obj/vibrator.dm
+++ b/hyperstation/code/obj/vibrator.dm
@@ -132,14 +132,13 @@ 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.getArousalLoss() >= 100 && ishuman(U) && U.has_dna())
- U.mob_climax(forced_climax=TRUE)
+ 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.getArousalLoss() >= 100 && ishuman(U) && U.has_dna())
- U.mob_climax(forced_climax=TRUE)
+ if (U.can_orgasm())
+ 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 92aa3b21b..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_climax=TRUE)
+ 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 5e37cbbe7..8b8780b46 100644
--- a/modular_citadel/code/modules/arousal/arousal.dm
+++ b/modular_citadel/code/modules/arousal/arousal.dm
@@ -11,7 +11,6 @@
/mob/living/carbon/human
canbearoused = TRUE
-
var/saved_underwear = ""//saves their underwear so it can be toggled later
var/saved_undershirt = ""
var/saved_socks = ""
@@ -62,7 +61,6 @@
/mob/living/proc/handle_arousal()
-
/mob/living/carbon/handle_arousal()
if(canbearoused && dna)
var/datum/species/S
@@ -110,8 +108,11 @@
if(updating_arousal)
updatearousal()
+/**
+ * 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.
@@ -127,26 +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)
- if(getArousalLoss() >= ((max_arousal / 100) * 33))
- G.aroused_state = TRUE
+ genital.aroused_state = isPercentAroused(genital.aroused_amount)
+ if(getArousalLoss() >= isPercentAroused(33))
+ genital.aroused_state = TRUE
else
- G.aroused_state = FALSE
- G.update_appearance()
+ genital.aroused_state = FALSE
+ genital.update_appearance()
@@ -166,41 +166,13 @@
return FALSE
else
if(hud_used.arousal)
- if(stat == DEAD)
- hud_used.arousal.icon_state = "arousal0"
- return TRUE
- if(getArousalLoss() == max_arousal)
- hud_used.arousal.icon_state = "arousal100"
- return TRUE
- if(getArousalLoss() >= (max_arousal / 100) * 90)//M O D U L A R , W O W
- hud_used.arousal.icon_state = "arousal90"
- return TRUE
- if(getArousalLoss() >= (max_arousal / 100) * 80)//M O D U L A R , W O W
- hud_used.arousal.icon_state = "arousal80"
- return TRUE
- if(getArousalLoss() >= (max_arousal / 100) * 70)//M O D U L A R , W O W
- hud_used.arousal.icon_state = "arousal70"
- return TRUE
- if(getArousalLoss() >= (max_arousal / 100) * 60)//M O D U L A R , W O W
- hud_used.arousal.icon_state = "arousal60"
- return TRUE
- if(getArousalLoss() >= (max_arousal / 100) * 50)//M O D U L A R , W O W
- hud_used.arousal.icon_state = "arousal50"
- return TRUE
- if(getArousalLoss() >= (max_arousal / 100) * 40)//M O D U L A R , W O W
- hud_used.arousal.icon_state = "arousal40"
- return TRUE
- if(getArousalLoss() >= (max_arousal / 100) * 30)//M O D U L A R , W O W
- hud_used.arousal.icon_state = "arousal30"
- return TRUE
- if(getArousalLoss() >= (max_arousal / 100) * 20)//M O D U L A R , W O W
- hud_used.arousal.icon_state = "arousal10"
- return TRUE
- if(getArousalLoss() >= (max_arousal / 100) * 10)//M O D U L A R , W O W
- hud_used.arousal.icon_state = "arousal10"
- return TRUE
- else
- hud_used.arousal.icon_state = "arousal0"
+ var/arousal_state = "arousal0"
+ if(stat != DEAD)
+ var/arousal_percent = getPercentAroused()
+ var/arousal_rounded = FLOOR(arousal_percent, 10)
+ arousal_state = "arousal[arousal_rounded]"
+ hud_used.arousal.icon_state = arousal_state
+ return TRUE
/obj/screen/arousal
name = "arousal"
@@ -217,637 +189,654 @@
if(M.canbearoused)
ui_interact(usr)
-/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.
+/**
+ * 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)
+ if(src.canbearoused && src.getArousalLoss() >= arousal && ishuman(src) && src.has_dna())
+ return TRUE
+ return FALSE
+
+
+/mob/living/proc/mob_climax()
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() >= ((max_arousal / 100) * 33))//33% arousal or greater required
+ if(isPercentAroused(33))//33% arousal or greater required
src.visible_message("[src] starts masturbating!", \
- "You start masturbating.")
+ "You start masturbating.")
if(do_after(src, 30, target = src))
src.visible_message("[src] relieves [p_them()]self!", \
- "You have relieved yourself.")
+ "You have relieved yourself.")
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm)
setArousalLoss(min_arousal)
else
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
+/**
+ * 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=3 SECONDS)
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 = genital.get_fluid_source()
+ var/condomed = FALSE
+ var/sounded = FALSE
+ if(genital.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 [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!
- if(isliving(src))
- var/mutable_appearance/cumoverlaylarge = mutable_appearance('hyperstation/icons/effects/cumoverlay.dmi')
- cumoverlaylarge.icon_state = "cum_large"
- src.add_overlay(cumoverlaylarge)
-
- 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].")
- if(isliving(src))
- var/mutable_appearance/cumoverlay = mutable_appearance('hyperstation/icons/effects/cumoverlay.dmi')
- cumoverlay.icon_state = "cum_normal"
- src.add_overlay(cumoverlay)
- else
- src.add_cum_overlay()
-
- if(condomed) //condomed
- src.visible_message("[src] orgasms, climaxing into [p_their()] condom ", \
- "You cum into your condom.", \
- "You have relieved yourself.")
- if(sounded) //sounded
- 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)
- src.condomclimax()
-
- 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_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
-
- if(G.producing) //Can it produce its own fluids, such as breasts?
- fluid_source = G.reagents
- 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
-
- 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.")
- else
- total_fluids = fluid_source.total_volume
- if(mb_time) //as long as it's not instant, give a warning
- src.visible_message("[src] looks like they're about to cum.", \
- "You feel yourself about to orgasm.", \
- "You feel yourself about to orgasm.")
- if(do_after(src, mb_time, target = src))
- if(spillage)
+ 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] 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].")
- else //Else from spillage check, also note subtle text change
- 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, mob/living/L, spillage = TRUE, impreg = FALSE,cover = FALSE,remote = FALSE, mb_time = 30) //Used for climaxing with any living thing
- 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
- total_fluids = fluid_source.total_volume
-
- if(mb_time && !remote) //Skip warning if this is an instant climax.
- src.visible_message("[src] is about to climax with [L]!", \
- "You're about to climax with [L]!", \
- "You're preparing to climax with something!")
- 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, L))
- fluid_source.trans_to(L, total_fluids*G.fluid_transfer_factor)
- total_fluids -= total_fluids*G.fluid_transfer_factor
- if(total_fluids > 80) // now thats a big cum!
- if(isliving(L))
- var/mutable_appearance/cumoverlaylarge = mutable_appearance('hyperstation/icons/effects/cumoverlay.dmi')
- cumoverlaylarge.icon_state = "cum_large"
- L.add_overlay(cumoverlaylarge)
-
- if(total_fluids > 5)
- fluid_source.reaction(L.loc, TOUCH, 1, 0)
- var/mob/living/carbon/human/H = L
- if(H)
- H.cumdrip_rate += rand(5,10)
- fluid_source.clear_reagents()
- src.visible_message("[src] climaxes over [L], using [p_their()] [G.name]!", \
- "You orgasm over [L], 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(isliving(L))
- var/mutable_appearance/cumoverlay = mutable_appearance('hyperstation/icons/effects/cumoverlay.dmi')
- cumoverlay.icon_state = "cum_normal"
- L.add_overlay(cumoverlay)
+ 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
- L.add_cum_overlay()
-
- setArousalLoss(min_arousal)
- if(G.can_climax)
+ 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.", \
+ "You have relieved yourself.")
+ if(sounded) //sounded
+ 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)
+ src.condomclimax()
+ SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm)
+ if(genital.can_climax)
setArousalLoss(min_arousal)
- if(spillage && !cover)
- if(do_after(src, mb_time, target = src))
- if(!in_range(src, L) && !remote)
- return
- fluid_source.trans_to(L, total_fluids*G.fluid_transfer_factor)
- total_fluids -= total_fluids*G.fluid_transfer_factor
+
+/**
+ * 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=3 SECONDS)
+ var/total_fluids = 0
+ 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 [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
+ src.visible_message("[src] looks like they're about to cum.", \
+ "You feel yourself about to orgasm.", \
+ "You feel yourself about to orgasm.")
+ if(do_after(src, mb_time, target = src))
+ if(spillage)
if(total_fluids > 5)
- fluid_source.reaction(L.loc, TOUCH, 1, 0)
- var/mob/living/carbon/human/H = L
- if(H)
- H.cumdrip_rate += rand(5,10)
+ fluid_source.reaction(src.loc, TOUCH, 1, 0)
fluid_source.clear_reagents()
- src.visible_message("[src] climaxes with [L][spillage ? ", overflowing and spilling":""], using [p_their()] [G.name]!", \
- "You orgasm with [L][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(L, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm)
+ 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()] [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(genital.can_climax)
+ setArousalLoss(min_arousal)
- if(G.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)
+ 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
+ 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 > 5)
+ fluid_source.reaction(partner.loc, TOUCH, 1, 0)
+ fluid_source.clear_reagents()
+ 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]!", \
+ "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)
+
+
+/**
+ * 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 = 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 FALSE
+ total_fluids = fluid_source.total_volume
+ var/partner_text = partner
+ if(remote)
+ 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))
+ return FALSE
+ if(spillage)
+ 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()] [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
- if(!cover)
- if(!remote && !in_range(src, L))
- return
- if(do_after(src, mb_time, target = src))
- var/mob/living/carbon/H = L
- 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(L, total_fluids)
- total_fluids = 0
- if(!remote)
- src.visible_message("[src] climaxes with [L], [p_their()] [G.name] spilling nothing!", \
- "You ejaculate with [L], 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(L, "You feel someone ejaculate inside you.")
-
- SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm)
- SEND_SIGNAL(L, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm)
-
- if(G.can_climax)
- setArousalLoss(min_arousal)
-
+ var/can_inflate = TRUE
+ 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()
+ if(can_inflate)
+ 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()] [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(genital.can_climax)
+ setArousalLoss(min_arousal)
//Hyper - antag code
+ var/mob/living/carbon/partner_carbon = partner
if(src.mind.special_role == ROLE_LEWD_TRAITOR)
for(var/datum/objective/obj in src.mind.objectives)
- if (L.mind == obj.target)
- L.mind.sexed = TRUE //sexed
+ 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)
- //Role them odds, only people with the dicks can send the chance to the person with the settings enabled at the momment.
- if(prob(L.impregchance))
- var/obj/item/organ/genital/womb/W = L.getorganslot("womb")
- if(W) //check if they have a womb.
- if (L.breedable == 1 && W.pregnant == 0) //Dont get pregnant again, if you are pregnant.
- log_game("Debug: [L] has been impregnated by [src]")
- to_chat(L, "You feel your hormones change, and a motherly instinct take over.") //leting them know magic has happened.
- W.pregnant = 1
- if (HAS_TRAIT(L, TRAIT_HEAT))
- SEND_SIGNAL(L, COMSIG_ADD_MOOD_EVENT, "heat", /datum/mood_event/heat) //well done you perv.
- REMOVE_TRAIT(L, TRAIT_HEAT, ROUNDSTART_TRAIT) //take the heat away, you satisfied it!
-
- //Make breasts produce quicker.
- var/obj/item/organ/genital/breasts/B = L.getorganslot("breasts")
- if (B.fluid_mult < 0.5 && B)
- B.fluid_mult = 0.5
+ return TRUE
-/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
+/**
+ * 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_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_in_partner(picked_organ, partner, spillage == "Yes") && impreg)
+ partner.impregnate(by=src)
- if(G.name == "penis")//if the select organ is a penis
- var/obj/item/organ/genital/penis/P = src.getorganslot("penis")
- if(P.condom) //if the penis is condomed
- to_chat(src, "You cannot fill containers when there is a condom over your [G.name].")
- return
- if(P.sounding) //if the penis is sounded
- 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=3 SECONDS)
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()
+/**
+ * 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()
- 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.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
- 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.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
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()
- 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
+ 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?", "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
-/mob/living/carbon/human/proc/pick_partner_overide() //used for cumming on people without genitals exposed
- var/list/partners = list()
- if(src.pulling)
- partners += src.pulling //Yes, even objects for now
- if(src.pulledby)
- partners += src.pulledby
- //Now we got both of them, let's check if they're proper
- for(var/I in partners)
- if(isliving(I))
- else
- partners -= I //No fucking objects
- //NOW the list should only contain correct partners
- if(!partners.len)
- return null //No one left.
- return input(src, "With whom?", "Sexual partner", null) in partners //pick one, default to null
-/mob/living/carbon/human/proc/pick_partner()
- var/list/partners = list()
- if(src.pulling)
- partners += src.pulling //Yes, even objects for now
- if(src.pulledby)
- partners += src.pulledby
- //Now we got both of them, let's check if they're proper
+/**
+ * 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)
+ var/list/partners = list(src.pulling, src.pulledby)
for(var/I in partners)
- if(isliving(I))
- if(iscarbon(I))
- var/mob/living/carbon/C = I
- if(!C.exposed_genitals.len) //Nothing through_clothing
- if(!C.is_groin_exposed()) //No pants undone
- if(!C.is_chest_exposed()) //No chest exposed
- partners -= I //Then not proper, remove them
- else
- partners -= I //No fucking objects
- //NOW the list should only contain correct partners
+ if(!iscarbon(I))
+ partners -= I
+ continue
+ if(!needs_exposed)
+ continue
+ var/mob/living/carbon/C = I
+ if(!C.exposed_genitals.len && !C.is_groin_exposed() && !C.is_chest_exposed())
+ partners -= I
if(!partners.len)
- return null //No one left.
- return input(src, "With whom?", "Sexual partner", null) in partners //pick one, default to null
+ return null
+ 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()
-
for(var/obj/item/reagent_containers/container in held_items)
if(container.is_open_container() || istype(container, /obj/item/reagent_containers/food/snacks))
containers_list += container
-
if(containers_list.len)
- SC = input(src, "Into or onto what?(Cancel for nowhere)", null) as null|obj in containers_list
+ SC = input(src, "Into or onto what? (Cancel for nowhere)", null) as null|obj in containers_list
if(SC)
if(in_range(src, SC))
return SC
- return null //If nothing correct, give null.
+ return null
-//Here's the main proc itself
-/mob/living/carbon/human/mob_climax(forced_climax=FALSE) //Forced is instead of the other proc, makes you cum if you have the tools for it, ignoring restraints
- if(stat == DEAD) //corpses can't cum
+/**
+ * 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(!canbearoused || !has_dna())
return
if(mb_cd_timer > world.time)
- if(!forced_climax) //Don't spam the message to the victim if forced to come too fast
- to_chat(src, "You need to wait [DisplayTimeText((mb_cd_timer - world.time), TRUE)] before you can do that again!")
+ to_chat(src, "You need to wait [DisplayTimeText((mb_cd_timer - world.time), TRUE)] before you can do that again!")
return
mb_cd_timer = (world.time + mb_cd_length)
-
-
- if(canbearoused && has_dna())
- if(stat==2)
- to_chat(src, "You can't do that while dead!")
- return
- if(forced_climax) //Something forced us to cum, this is not a masturbation thing and does not progress to the other checks
- for(var/obj/item/organ/O in internal_organs)
- if(istype(O, /obj/item/organ/genital))
- var/obj/item/organ/genital/G = O
- if(!G.can_climax) //Skip things like wombs and testicles
- continue
- var/mob/living/partner
- var/check_target
- var/list/worn_stuff = get_equipped_items()
-
- if(G.is_exposed(worn_stuff))
- if(src.pulling) //Are we pulling someone? Priority target, we can't be making option menus for this, has to be quick
- if(isliving(src.pulling)) //Don't fuck objects
- check_target = src.pulling
- if(src.pulledby && !check_target) //prioritise pulled over pulledby
- if(isliving(src.pulledby))
- check_target = src.pulledby
- //Now we should have a partner, or else we have to come alone
- if(check_target)
- if(iscarbon(check_target)) //carbons can have clothes
- var/mob/living/carbon/C = check_target
- if(C.exposed_genitals.len || C.is_groin_exposed() || C.is_chest_exposed()) //Are they naked enough?
- partner = C
- if(partner) //Did they pass the clothing checks?
- mob_climax_partner(G, partner, mb_time = 0) //Instant climax due to forced
- continue //You've climaxed once with this organ, continue on
- //not exposed OR if no partner was found while exposed, climax alone
- mob_climax_outside(G, mb_time = 0) //removed climax timer for sudden, forced orgasms
- //Now all genitals that could climax, have.
- //Since this was a forced climax, we do not need to continue with the other stuff
- return
- //If we get here, then this is not a forced climax and we gotta check a few things.
-
- if(stat==1) //No sleep-masturbation, you're unconscious.
- to_chat(src, "You must be conscious to do that!")
- return
- if(getArousalLoss() < 33) //flat number instead of percentage
- to_chat(src, "You aren't aroused enough for that!")
+ if(stat == DEAD)
+ to_chat(src, "You can't do that while dead!")
+ return
+ if(stat == UNCONSCIOUS)
+ to_chat(src, "You must be conscious to do that!")
+ return
+ if(getArousalLoss() < 33)
+ to_chat(src, "You aren't aroused enough for that!")
+ return
+ var/list/choices = list("Masturbate", "Climax alone", "Climax with partner","Climax over partner", "Fill container", "Remove condom", "Remove sounding rod")
+ var/choice = input(src, "Select sexual activity", "Sexual activity:") as null|anything in choices
+ switch(choice)
+ if("Remove sounding rod")
+ if(restrained(TRUE)) //TRUE ignores grabs
+ to_chat(src, "You can't do that while restrained!")
+ return
+ var/free_hands = get_num_arms()
+ if(!free_hands)
+ to_chat(src, "You need at least one free arm.")
+ return
+ var/obj/item/organ/genital/penis/P = src.getorganslot("penis")
+ if(!P.sounding)
+ to_chat(src, "You don't have a rod inside!")
+ return
+ if(P.sounding)
+ to_chat(src, "You pull the rod off from the tip of your penis!")
+ src.removesounding()
+ return
return
- //Ok, now we check what they want to do.
- var/choice = input(src, "Select sexual activity", "Sexual activity:") in list("Masturbate", "Climax alone", "Climax with partner","Climax over partner", "Fill container", "Remove condom", "Remove sounding rod")
+ if("Remove condom")
+ if(restrained(TRUE)) //TRUE ignores grabs
+ to_chat(src, "You can't do that while restrained!")
+ return
+ var/free_hands = get_num_arms()
+ if(!free_hands)
+ to_chat(src, "You need at least one free arm.")
+ return
+ var/obj/item/organ/genital/penis/P = src.getorganslot("penis")
+ if(!P.condom)
+ to_chat(src, "You don't have a condom on!")
+ return
+ if(P.condom)
+ to_chat(src, "You tug the condom off the end of your penis!")
+ src.removecondom()
+ return
+ return
- switch(choice)
- if("Remove sounding rod")
- if(restrained(TRUE)) //TRUE ignores grabs
- to_chat(src, "You can't do that while restrained!")
- return
- var/free_hands = get_num_arms()
- if(!free_hands)
- to_chat(src, "You need at least one free arm.")
- return
- var/obj/item/organ/genital/penis/P = src.getorganslot("penis")
- if(!P.sounding)
- to_chat(src, "You don't have a rod inside!")
- return
- if(P.sounding)
- to_chat(src, "You pull the rod off from the tip of your penis!")
- src.removesounding()
- return
+ if("Masturbate")
+ if(restrained(TRUE)) //TRUE ignores grabs
+ to_chat(src, "You can't do that while restrained!")
+ return
+ var/free_hands = get_num_arms()
+ if(!free_hands)
+ to_chat(src, "You need at least one free arm.")
+ return
+ for(var/helditem in held_items)//how many hands are free
+ if(isobj(helditem))
+ free_hands--
+ if(free_hands <= 0)
+ to_chat(src, "You're holding too many things.")
+ return
+ //We got hands, let's pick an organ
+ var/obj/item/organ/genital/picked_organ
+ picked_organ = pick_climax_genitals(masturbation=TRUE, title="Masturbation")
+ if(picked_organ)
+ mob_masturbate(picked_organ)
+ return
+ else //They either lack organs that can masturbate, or they didn't pick one.
+ to_chat(src, "You cannot masturbate without choosing genitals.")
return
- if("Remove condom")
- if(restrained(TRUE)) //TRUE ignores grabs
- to_chat(src, "You can't do that while restrained!")
- return
- var/free_hands = get_num_arms()
- if(!free_hands)
- to_chat(src, "You need at least one free arm.")
- return
- var/obj/item/organ/genital/penis/P = src.getorganslot("penis")
- if(!P.condom)
- to_chat(src, "You don't have a condom on!")
- return
- if(P.condom)
- to_chat(src, "You tug the condom off the end of your penis!")
- src.removecondom()
- return
+ if("Climax alone")
+ if(restrained(TRUE)) //TRUE ignores grabs
+ to_chat(src, "You can't do that while restrained!")
+ return
+ var/free_hands = get_num_arms()
+ if(!free_hands)
+ to_chat(src, "You need at least one free arm.")
+ return
+ for(var/helditem in held_items)//how many hands are free
+ if(isobj(helditem))
+ free_hands--
+ if(free_hands <= 0)
+ to_chat(src, "You're holding too many things.")
+ return
+ //We got hands, let's pick an organ
+ var/obj/item/organ/genital/picked_organ
+ picked_organ = pick_climax_genitals()
+ if(picked_organ)
+ mob_climax_outside(picked_organ)
+ return
+ else //They either lack organs that can masturbate, or they didn't pick one.
+ to_chat(src, "You cannot climax without choosing genitals.")
return
- if("Masturbate")
- if(restrained(TRUE)) //TRUE ignores grabs
- to_chat(src, "You can't do that while restrained!")
- return
- var/free_hands = get_num_arms()
- if(!free_hands)
- to_chat(src, "You need at least one free arm.")
- return
- for(var/helditem in held_items)//how many hands are free
- if(isobj(helditem))
- free_hands--
- if(free_hands <= 0)
- to_chat(src, "You're holding too many things.")
- return
- //We got hands, let's pick an organ
- var/obj/item/organ/genital/picked_organ
- picked_organ = pick_masturbate_genitals()
- if(picked_organ)
- mob_masturbate(picked_organ)
- return
- else //They either lack organs that can masturbate, or they didn't pick one.
- to_chat(src, "You cannot masturbate without choosing genitals.")
- return
+ if("Climax with partner")
+ //We need no hands, we can be restrained and so on, so let's pick an organ
+ var/obj/item/organ/genital/picked_organ
+ picked_organ = pick_climax_genitals()
+ if(!picked_organ)
+ to_chat(src, "You cannot climax without choosing genitals.")
+ return
+ var/mob/living/carbon/partner = pick_partner() //Get someone
+ if(!partner)
+ to_chat(src, "You cannot do this alone.")
+ return
+ var/obj/item/organ/genital/penis/P = picked_organ
+ var/impreg = "No"
+ if(partner.breedable && picked_organ.name == "penis" && !P.condom && !P.sounding)
+ 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_inside_spillage(picked_organ, partner, impreg == "Yes")
- if("Climax alone")
- if(restrained(TRUE)) //TRUE ignores grabs
- to_chat(src, "You can't do that while restrained!")
- return
- var/free_hands = get_num_arms()
- if(!free_hands)
- to_chat(src, "You need at least one free arm.")
- return
- for(var/helditem in held_items)//how many hands are free
- if(isobj(helditem))
- free_hands--
- if(free_hands <= 0)
- to_chat(src, "You're holding too many things.")
- return
- //We got hands, let's pick an organ
- var/obj/item/organ/genital/picked_organ
- picked_organ = pick_climax_genitals()
- if(picked_organ)
- mob_climax_outside(picked_organ)
- return
- else //They either lack organs that can masturbate, or they didn't pick one.
- to_chat(src, "You cannot climax without choosing genitals.")
- return
+ if("Climax over partner")
+ var/obj/item/organ/genital/picked_organ
+ picked_organ = pick_climax_genitals()
+ if(picked_organ)
+ 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
+ to_chat(src, "You cannot do this alone.")
- if("Climax with partner")
- //We need no hands, we can be restrained and so on, so let's pick an organ
- var/obj/item/organ/genital/picked_organ
- picked_organ = pick_climax_genitals()
- if(picked_organ)
- var/mob/living/partner = pick_partner() //Get someone
- if(partner)
- var/obj/item/organ/genital/penis/P = picked_organ
- if(partner.breedable == 1 && picked_organ.name == "penis"&&!P.condom == 1&&!P.sounding == 1)
- var/impreg = input(src, "Would this action carry the risk of pregnancy?", "Choose a option", "Yes") as anything in list("Yes", "No")
- if(impreg == "Yes") //If we are impregging
- var/spillage = input(src, "Would your fluids spill outside?", "Choose overflowing option", "Yes") as anything in list("Yes", "No")
- if(spillage == "Yes")
- mob_climax_partner(picked_organ, partner, TRUE, TRUE, FALSE)
- else
- mob_climax_partner(picked_organ, partner, FALSE, TRUE, FALSE)
- else
- var/spillage = input(src, "Would your fluids spill outside?", "Choose overflowing option", "Yes") as anything in list("Yes", "No")
- if(spillage == "Yes")
- mob_climax_partner(picked_organ, partner, TRUE, FALSE, FALSE)
- else
- mob_climax_partner(picked_organ, partner, FALSE, FALSE, FALSE) //Wow, im trash at coding, I need to find a better way of coding this, ill rewrite it later.-quote
- return
+ if("Fill container")
+ //We'll need hands and no restraints.
+ if(restrained(TRUE)) //TRUE ignores grabs
+ to_chat(src, "You can't do that while restrained!")
+ return
+ var/free_hands = get_num_arms()
+ if(!free_hands)
+ to_chat(src, "You need at least one free arm.")
+ return
+ for(var/helditem in held_items)//how many hands are free
+ if(isobj(helditem))
+ free_hands--
+ if(free_hands <= 0)
+ to_chat(src, "You're holding too many things.")
+ return
+ //We got hands, let's pick an organ
+ var/obj/item/organ/genital/picked_organ
+ picked_organ = pick_climax_genitals() //Gotta be climaxable, not just masturbation, to fill with fluids.
+ if(picked_organ)
+ //Good, got an organ, time to pick a container
+ var/obj/item/reagent_containers/fluid_container = pick_climax_container()
+ if(fluid_container)
+ mob_fill_container(picked_organ, fluid_container)
+ return
+ else
+ to_chat(src, "You cannot do this without anything to fill.")
+ return
+ else //They either lack organs that can climax, or they didn't pick one.
+ to_chat(src, "You cannot fill anything without choosing genitals.")
+ return
+ else //Somehow another option was taken, maybe something interrupted the selection or it was cancelled
+ return //Just end it in that case.
- else //If we arent impregging
- var/spillage = input(src, "Would your fluids spill outside?", "Choose overflowing option", "Yes") as anything in list("Yes", "No")
- if(spillage == "Yes")
- mob_climax_partner(picked_organ, partner, TRUE, FALSE, FALSE)
- else
- mob_climax_partner(picked_organ, partner, FALSE, FALSE, FALSE)
- return
- else
- to_chat(src, "You cannot do this alone.")
- return
- else //They either lack organs that can masturbate, or they didn't pick one.
- to_chat(src, "You cannot climax without choosing genitals.")
- return
- if("Climax over partner")
- var/obj/item/organ/genital/picked_organ
- picked_organ = pick_climax_genitals()
- if(picked_organ)
- var/mob/living/partner = pick_partner_overide() //Get your partner, clothed or not.
- if(partner)
- mob_climax_partner(picked_organ, partner, FALSE, FALSE, TRUE)
- else
- to_chat(src, "You cannot do this alone.")
+/**
+ * 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(arousal=100)
+ if(stat == DEAD) //corpses can't cum
+ to_chat(src, "You can't do that while dead!")
+ return
+ 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))
+ 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())
+ 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
- if("Fill container")
- //We'll need hands and no restraints.
- if(restrained(TRUE)) //TRUE ignores grabs
- to_chat(src, "You can't do that while restrained!")
- return
- var/free_hands = get_num_arms()
- if(!free_hands)
- to_chat(src, "You need at least one free arm.")
- return
- for(var/helditem in held_items)//how many hands are free
- if(isobj(helditem))
- free_hands--
- if(free_hands <= 0)
- to_chat(src, "You're holding too many things.")
- return
- //We got hands, let's pick an organ
- var/obj/item/organ/genital/picked_organ
- picked_organ = pick_climax_genitals() //Gotta be climaxable, not just masturbation, to fill with fluids.
- if(picked_organ)
- //Good, got an organ, time to pick a container
- var/obj/item/reagent_containers/fluid_container = pick_climax_container()
- if(fluid_container)
- mob_fill_container(picked_organ, fluid_container)
- return
- else
- to_chat(src, "You cannot do this without anything to fill.")
- return
- else //They either lack organs that can climax, or they didn't pick one.
- to_chat(src, "You cannot fill anything without choosing genitals.")
- return
- else //Somehow another option was taken, maybe something interrupted the selection or it was cancelled
- return //Just end it in that case.
+/**
+ * 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)
+ return
+ if(!prob(impregchance))
+ return
+ W.pregnant = TRUE
+ log_game("DEBUG: [src] has been impregnated by [by]")
+ to_chat(src, "You feel your hormones change, and a motherly instinct take over.")
+ if(HAS_TRAIT(src, TRAIT_HEAT))
+ SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "heat", /datum/mood_event/heat)
+ REMOVE_TRAIT(src, TRAIT_HEAT, ROUNDSTART_TRAIT)
+ var/obj/item/organ/genital/breasts/B = src.getorganslot("breasts")
+ if (B && B.fluid_mult < 0.5)
+ B.fluid_mult = 0.5
\ No newline at end of file
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()
diff --git a/modular_citadel/code/modules/integrated_electronics/subtypes/manipulation.dm b/modular_citadel/code/modules/integrated_electronics/subtypes/manipulation.dm
index 547c1a576..e3eab59c5 100644
--- a/modular_citadel/code/modules/integrated_electronics/subtypes/manipulation.dm
+++ b/modular_citadel/code/modules/integrated_electronics/subtypes/manipulation.dm
@@ -25,12 +25,12 @@
if(ismob(M) && M.canbearoused && arousal_gain != 0)
var/orgasm = FALSE
if(arousal_gain > 0)
- if(M.getArousalLoss() >= 100 && ishuman(M) && M.has_dna())
+ if(M.can_orgasm())
var/mob/living/carbon/human/H = M
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_climax=TRUE)
+ H.mob_climax_instant()
orgasm = TRUE
else
M.adjustArousalLoss(arousal_gain)
diff --git a/modular_citadel/code/modules/reagents/reagents/cit_reagents.dm b/modular_citadel/code/modules/reagents/reagents/cit_reagents.dm
index 324b44db3..30c528cc0 100644
--- a/modular_citadel/code/modules/reagents/reagents/cit_reagents.dm
+++ b/modular_citadel/code/modules/reagents/reagents/cit_reagents.dm
@@ -197,7 +197,7 @@
/datum/reagent/drug/aphrodisiacplus/overdose_process(mob/living/M)
if(M && M.canbearoused && !(M.client?.prefs.cit_toggles & NO_APHRO) && prob(33))
- if(prob(5) && M.getArousalLoss() >= 100 && ishuman(M) && M.has_dna())
+ if(prob(5) && M.can_orgasm())
if(prob(5)) //Less spam
to_chat(M, "Your libido is going haywire!")
if(M.min_arousal < 50)
diff --git a/modular_citadel/icons/obj/genitals/hud.dmi b/modular_citadel/icons/obj/genitals/hud.dmi
index bf2adcb1f..e090dbd84 100644
Binary files a/modular_citadel/icons/obj/genitals/hud.dmi and b/modular_citadel/icons/obj/genitals/hud.dmi differ
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"