diff --git a/code/citadel/cit_arousal.dm b/code/citadel/cit_arousal.dm
index cf47dab1da..6005758144 100644
--- a/code/citadel/cit_arousal.dm
+++ b/code/citadel/cit_arousal.dm
@@ -181,224 +181,364 @@
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, mb_time = 30) //Masturbation, keep it gender-neutral
+ 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)
+ 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)
+ fluid_source.reaction(src.loc, TOUCH, 1, 0)
+ fluid_source.clear_reagents()
+ 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(G.can_climax)
+ setArousalLoss(min_arousal)
+
+
+/mob/living/carbon/human/proc/mob_climax_outside(obj/item/organ/genital/G, mb_time = 30) //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(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]" : ""], using [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].")
+ 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, 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) //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 someone!")
+ if(spillage)
+ 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 > 5)
+ fluid_source.reaction(L.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].")
+ if(G.can_climax)
+ setArousalLoss(min_arousal)
+ else //knots and other non-spilling orgasms
+ if(do_after(src, mb_time, target = src) && in_range(src, L))
+ fluid_source.trans_to(L, total_fluids)
+ total_fluids = 0
+ 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.")
+ if(G.can_climax)
+ setArousalLoss(min_arousal)
+
+
+/mob/living/carbon/human/proc/mob_fill_container(obj/item/organ/genital/G, obj/item/weapon/reagent_containers/container, mb_time = 30) //For beaker-filling, beware the bartender
+ 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(!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.")
+ 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] to fill [container].", \
+ "You have relieved some pressure.")
+ if(G.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()
+ 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
+ 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/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
+ genitals_list += G
+ if(genitals_list.len)
+ ret_organ = input(src, "with what?", "Climax", null) as null|obj in genitals_list
+ return ret_organ
+ return null //error stuff
+
+
+/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
+ 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(!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_climax_container()
+ var/obj/item/weapon/reagent_containers/SC = null
+ var/list/containers_list = list()
+
+ for(var/obj/item/weapon/reagent_containers/container in held_items)
+ if(container.is_open_container() || istype(container, /obj/item/weapon/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
+ if(SC)
+ if(in_range(src, SC))
+ return SC
+ return null //If nothing correct, give 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(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 [round((mb_cd_timer - world.time)/(20))] seconds before you can do that again!")
return
mb_cd_timer = (world.time + mb_cd_length)
- var/list/genitals_list = list()
- var/obj/item/organ/genital/SG = null//originally selected_genital
- var/list/containers_list = list()
- var/obj/item/weapon/reagent_containers/SC = null
- var/datum/reagents/fluid_source = null
- var/into_container = 0
- var/free_hands = get_num_arms() //arms was only used to know if we had ANY at all
- var/total_cum = 0
- var/finished = 0
- var/mb_time = 30
+
+
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/genital/G in internal_organs)
- if(G.can_masturbate_with) //All capable genitals will orgasm with this
- var/unable_to_come = FALSE
- switch(G.type)
- if(/obj/item/organ/genital/penis)
- var/obj/item/organ/genital/penis/P = G
- if(!P.linked_balls)
- unable_to_come = TRUE
- else
- fluid_source = P.linked_balls.reagents
+ 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
+ else //A cat is fine too
+ partner = check_target
+ 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(/obj/item/organ/genital/vagina)
- var/obj/item/organ/genital/vagina/V = G
- if(!V.linked_womb)
- unable_to_come = TRUE
- else
- fluid_source = V.linked_womb.reagents
- else //Weird, undefined genitalia behaviour
- 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.")
- else
- if(fluid_source)
- total_cum = fluid_source.total_volume
- 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(total_cum > 5)
- fluid_source.reaction(src.loc, TOUCH, 1, 0)
- fluid_source.clear_reagents()
- fluid_source = null //cleanup so this can be used for the next genitalia
-
- src.visible_message("[src] orgasms, cumming[istype(src.loc, /turf/open/floor) ? " onto [src.loc]" : ""]!", \
- "You're forced to cum[istype(src.loc, /turf/open/floor) ? " onto [src.loc]" : ""] with your [G].", \
- "Your [G] have been forced to climax.")
- finished = 1
- if(finished)
- setArousalLoss(min_arousal)
- return //Do not proceed to masturbating if all genitals have been forced to orgasm.
- if(stat==1) //Sleeping people can be forced chemically or with electrical stimulants, for example.
+ if(stat==1) //No sleep-masturbation, you're unconscious.
to_chat(src, "You must be conscious to do that!")
return
- if(restrained())
- to_chat(src, "You can't do that while restrained!")
- return
- if(getArousalLoss() < 33)//flat number instead of percentage
+ if(getArousalLoss() < 33) //flat number instead of percentage
to_chat(src, "You aren't aroused enough for that!")
return
- if(!is_groin_exposed())
- to_chat(src, "You need to undress, first!")
- return
- 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 need at least one free hand.")
- return
- for(var/obj/item/organ/genital/G in internal_organs)
- if(G.can_masturbate_with)//filter out what you can't masturbate with
- genitals_list += G
- if(genitals_list.len)
- SG = input(src, "with what?", "Masturbate") as null|obj in genitals_list
- if(SG)
- for(var/obj/item/weapon/reagent_containers/container in held_items)
- if(container.is_open_container() || istype(container, /obj/item/weapon/reagent_containers/food/snacks/pie))
- containers_list += container
- if(containers_list.len)
- SC = input(src, "Into or onto what?(Cancel for nowhere)", "Masturbate") as null|obj in containers_list
- if(SC)
- if(in_range(src, SC))
- into_container = 1
- SG.update()
- switch(SG.type)
- //Penis
- if(/obj/item/organ/genital/penis)
- var/obj/item/organ/genital/penis/P = SG
- if(!P.linked_balls)
- to_chat(src, "You need a pair of testicles to do this.")
- return
- fluid_source = P.linked_balls.reagents
- total_cum = fluid_source.total_volume
- if(into_container)//into a glass or beaker or whatever
- src.visible_message("[src] starts [pick("jerking off","stroking")] their [SG.name] over [SC].", \
- "You start jerking off over [SC.name].", \
- "You start masturbating.")
- if(do_after(src, mb_time, target = src) && in_range(src, SC))
- fluid_source.trans_to(SC, total_cum)
- src.visible_message("[src] orgasms, [pick("cumming into", "emptying themself into")] [SC]!", \
- "You cum into [SC].", \
- "You have relieved yourself.")
- finished = 1
- else //Not in a container
- if(src.pulling)
- if(iscarbon(src.pulling))
- var/mob/living/carbon/C = src.pulling
- if(!C.is_groin_exposed())
- to_chat(src, "You must undress someone to climax inside them.")
- return
- if(isliving(src.pulling)) //Gotta be alive to fuck it, don't wanna have to code fucking objects that ain't containers...
- var/mob/living/partner = src.pulling
- src.visible_message("[src] is about to climax inside [partner]!", \
- "You're about to climax inside [partner]!", \
- "You're preparing to climax inside someone!")
- switch(grab_state)
- if(GRAB_PASSIVE)
- if(do_after(src, mb_time, target = src) && in_range(src, partner))
- var/spillage = 0.5 //Leaks a bit on passive grab
- var/did_spill = FALSE
- fluid_source.trans_to(partner, total_cum*(1-spillage))
- total_cum = total_cum*spillage
- if(total_cum > 5)
- fluid_source.reaction(partner.loc, TOUCH, 1, 0)
- did_spill = TRUE
- fluid_source.clear_reagents()
+ //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", "Fill container")
- src.visible_message("[src] ejaculates inside [partner][did_spill ? ", overflowing and spilling":""]!", \
- "You ejaculate inside [partner][did_spill ? ", spilling out of them":""].", \
- "You have climaxed inside someone[did_spill ? ", spilling out of them":""].")
- finished = 1
- else //Aggressive or higher
- if(do_after(src, mb_time, target = src) && in_range(src, partner))
- var/spillage = 0.0 //Leakproofing seals
- fluid_source.trans_to(partner, total_cum*(1-spillage))
- total_cum = total_cum*spillage
- if(total_cum > 5)
- fluid_source.reaction(partner.loc, TOUCH, 1, 0)
- fluid_source.clear_reagents()
+ switch(choice)
+ 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
- src.visible_message("[src] ejaculates inside [partner], spilling nothing!", \
- "You ejaculate inside [partner], spilling nothing.", \
- "You have climaxed inside someone, spilling nothing.")
- finished = 1
- //Don't care, not coding you fucking a unanchored girder
- else //No pulling, or pulling non-living things
- src.visible_message("[src] starts [pick("jerking off","stroking")] their [SG].", \
- "You start masturbating.", \
- "You start masturbating.")
- if(do_after(src, mb_time, target = src))
- if(total_cum > 5)
- fluid_source.reaction(src.loc, TOUCH, 1, 0)
- fluid_source.clear_reagents()
+ 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
- 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.")
- finished = 1
+ 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/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)
+ else
+ mob_climax_partner(picked_organ, partner, 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(/obj/item/organ/genital/vagina)
- var/obj/item/organ/genital/vagina/V = SG
- if(!V.linked_womb)
- to_chat(src, "You need a womb to do this.")
- return
- fluid_source = V.linked_womb.reagents
- total_cum = fluid_source.total_volume
- if(into_container)//into a glass or beaker or whatever
- src.visible_message("[src] starts fingering their [SG.name] over [SC].", \
- "You start fingering over [SC.name].", \
- "You start masturbating.")
- if(do_after(src, mb_time, target = src) && in_range(src, SC))
- fluid_source.trans_to(SC, total_cum)
- src.visible_message("[src] orgasms, [pick("cumming into", "emptying themself into")] [SC]!", \
- "You cum into [SC].", \
- "You have relieved yourself.")
- finished = 1
-
- else//not into a container
- src.visible_message("[src] starts fingering their vagina.", \
- "You start fingering your vagina.", \
- "You start masturbating.")
- if(do_after(src, mb_time, target = src))
- if(total_cum > 5)
- fluid_source.reaction(src.loc, TOUCH, 1, 0)
- fluid_source.clear_reagents()
- 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.")
- finished = 1
-
- else//backup message, just in case
- src.visible_message("[src] starts masturbating!", \
- "You start masturbating.")
- if(do_after(src, mb_time, target = src))
- src.visible_message("[src] [pick("relieves themself!", "shudders and moans in orgasm!")]", \
- "You have relieved yourself.")
- finished = 1
- if(finished)
- setArousalLoss(min_arousal)
-
- else
- to_chat(src, "You have no genitals!")
- 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/weapon/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.
diff --git a/code/citadel/cit_reagents.dm b/code/citadel/cit_reagents.dm
index 428729d30f..4a0e91479b 100644
--- a/code/citadel/cit_reagents.dm
+++ b/code/citadel/cit_reagents.dm
@@ -153,14 +153,13 @@
if(prob(33))
if(M.getArousalLoss() >= 100 && ishuman(M) && M.has_dna())
var/mob/living/carbon/human/H = M
- to_chat(H, "Your libido is going haywire!")
- H.mob_climax(forced_climax=TRUE)
+ if(prob(50)) //Less spam
+ to_chat(H, "Your libido is going haywire!")
+ H.mob_climax(forced_climax=TRUE)
if(M.min_arousal < 50)
M.min_arousal += 1
- to_chat(M, "You're having a hard time thinkin about things other than sex!")
if(M.min_arousal < M.max_arousal)
M.min_arousal += 1
- to_chat(M, "You feel your libido permanently increasing.")
M.adjustArousalLoss(2)
..()
@@ -197,10 +196,8 @@
if(prob(33))
if(M.min_arousal > 0)
M.min_arousal -= 1
- to_chat(M, "You feel your libido returning to more normal levels.")
if(M.min_arousal > 50)
M.min_arousal -= 1
- to_chat(M, "You feel like your overactive libido is calming down.")
M.adjustArousalLoss(-2)
..()
diff --git a/code/citadel/organs/breasts.dm b/code/citadel/organs/breasts.dm
index ce45b88cfb..dda5e248ee 100644
--- a/code/citadel/organs/breasts.dm
+++ b/code/citadel/organs/breasts.dm
@@ -1,5 +1,5 @@
/obj/item/organ/genital/breasts
- name = "Breasts"
+ name = "breasts"
desc = "Female milk producing organs."
icon_state = "breasts"
icon = 'code/citadel/icons/breasts.dmi'
@@ -11,6 +11,10 @@
var/amount = 2
producing = TRUE
shape = "pair"
+ can_masturbate_with = TRUE
+ masturbation_verb = "massage"
+ can_climax = TRUE
+ fluid_transfer_factor =0.5
/obj/item/organ/genital/breasts/Initialize()
. = ..()
@@ -54,4 +58,10 @@
var/mob/living/carbon/human/H = owner // only human mobs have skin_tone, which we need.
color = "#[skintone2hex(H.skin_tone)]"
else
- color = "#[owner.dna.features["breasts_color"]]"
\ No newline at end of file
+ color = "#[owner.dna.features["breasts_color"]]"
+
+/obj/item/organ/genital/breasts/is_exposed()
+ . = ..()
+ if(.)
+ return TRUE
+ return owner.is_chest_exposed()
\ No newline at end of file
diff --git a/code/citadel/organs/eggsack.dm b/code/citadel/organs/eggsack.dm
index 718a353b87..1486310d61 100644
--- a/code/citadel/organs/eggsack.dm
+++ b/code/citadel/organs/eggsack.dm
@@ -7,7 +7,7 @@
slot = "testicles"
color = null //don't use the /genital color since it already is colored
w_class = 3
- var/internal = TRUE
+ internal = TRUE
var/egg_girth = EGG_GIRTH_DEF
var/cum_mult = CUM_RATE_MULT
var/cum_rate = CUM_RATE
diff --git a/code/citadel/organs/genitals.dm b/code/citadel/organs/genitals.dm
index 6bd86e7146..1f46778aaf 100644
--- a/code/citadel/organs/genitals.dm
+++ b/code/citadel/organs/genitals.dm
@@ -3,7 +3,10 @@
var/shape = "human"
var/sensitivity = 1
var/list/genital_flags = list()
- var/can_masturbate_with = 0
+ var/can_masturbate_with = FALSE
+ var/masturbation_verb = "masturbate"
+ var/can_climax = FALSE
+ var/fluid_transfer_factor = 0.0 //How much would a partner get in them if they climax using this?
var/size = 2 //can vary between num or text, just used in icon_state strings
var/fluid_id = null
var/fluid_max_volume = 50
@@ -13,6 +16,9 @@
var/producing = FALSE
var/aroused_state = FALSE //Boolean used in icon_state strings
var/aroused_amount = 50 //This is a num from 0 to 100 for arousal percentage for when to use arousal state icons.
+ var/obj/item/organ/genital/linked_organ
+ var/through_clothes = FALSE
+ var/internal = FALSE
/obj/item/organ/genital/Initialize()
. = ..()
@@ -34,6 +40,52 @@
update_appearance()
update_link()
+//exposure and through-clothing code
+/mob/living/carbon
+ var/list/exposed_genitals = list() //Keeping track of them so we don't have to iterate through every genitalia and see if exposed
+
+/obj/item/organ/genital/proc/is_exposed()
+ if(!owner)
+ return FALSE
+ if(internal)
+ return FALSE
+ if(through_clothes)
+ return TRUE
+
+/obj/item/organ/genital/proc/toggle_through_clothes()
+ if(through_clothes)
+ through_clothes = FALSE
+ owner.exposed_genitals -= src
+ else
+ through_clothes = TRUE
+ owner.exposed_genitals += src
+ if(ishuman(owner)) //recast to use update genitals proc
+ var/mob/living/carbon/human/H = owner
+ H.update_genitals()
+
+/mob/living/carbon/verb/toggle_genitals()
+ set category = "IC"
+ set name = "Expose/Hide genitals"
+ set desc = "Allows you to toggle which genitals should show through clothes or not."
+
+ var/list/genital_list = list()
+ 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.internal)
+ genital_list += G
+ if(!genital_list.len) //There is nothing to expose
+ return
+ //Full list of exposable genitals created
+ var/obj/item/organ/genital/picked_organ
+ picked_organ = input(src, "Expose/Hide genitals", "Choose which genitalia to expose/hide", null) in genital_list
+ if(picked_organ)
+ picked_organ.toggle_through_clothes()
+ return
+
+
+
+
/obj/item/organ/genital/proc/update_size()
/obj/item/organ/genital/proc/update_appearance()
@@ -41,6 +93,9 @@
/obj/item/organ/genital/proc/update_link()
/obj/item/organ/genital/proc/remove_ref()
+ if(linked_organ)
+ linked_organ.linked_organ = null
+ linked_organ = null
/obj/item/organ/genital/Insert(mob/living/carbon/M, special = 0)
..()
@@ -58,21 +113,21 @@
var/obj/item/organ/genital/GtoClean
for(GtoClean in internal_organs)
qdel(GtoClean)
-
- if(dna.features["has_cock"])
- give_penis()
- if(dna.features["has_balls"])
- give_balls()
- else if(dna.features["has_ovi"])
- give_ovipositor()
- if(dna.features["has_eggsack"])
- give_eggsack()
+ //Order should be very important. FIRST vagina, THEN testicles, THEN penis, as this affects the order they are rendered in.
if(dna.features["has_breasts"])
give_breasts()
if(dna.features["has_vag"])
give_vagina()
- if(dna.features["has_womb"])
- give_womb()
+ if(dna.features["has_womb"])
+ give_womb()
+ if(dna.features["has_balls"])
+ give_balls()
+ if(dna.features["has_cock"])
+ give_penis()
+ if(dna.features["has_ovi"])
+ give_ovipositor()
+ if(dna.features["has_eggsack"])
+ give_eggsack()
/mob/living/carbon/human/proc/give_penis()
if(!dna)
@@ -208,17 +263,15 @@
if(H.disabilities & HUSK)
return
//start scanning for genitals
- var/list/worn_stuff = H.get_equipped_items()//cache this list so it's not built again
- if(H.is_groin_exposed(worn_stuff))
- //ORDER is important here. Vaginas first, theoretical testes after, and penis LAST.
- //The latter is always drawn on top of the former.
- if(H.has_vagina())
- genitals_to_add += H.getorganslot("vagina")
- if(H.has_penis())
- genitals_to_add += H.getorganslot("penis")
- if(H.is_chest_exposed(worn_stuff))
- if(H.has_breasts())
- genitals_to_add += H.getorganslot("breasts")
+ //var/list/worn_stuff = H.get_equipped_items()//cache this list so it's not built again
+
+ for(var/obj/item/organ/O in H.internal_organs)
+ if(istype(O, /obj/item/organ/genital))
+ var/obj/item/organ/genital/G = O
+ if(G.is_exposed()) //Checks appropriate clothing slot and if it's through_clothes
+ genitals_to_add += H.getorganslot(G.slot)
+ //Now we added all genitals that aren't internal and should be rendered
+
var/image/I
//start applying overlays
for(var/layer in relevant_layers)
diff --git a/code/citadel/organs/penis.dm b/code/citadel/organs/penis.dm
index 9e2ce00666..7348e8c969 100644
--- a/code/citadel/organs/penis.dm
+++ b/code/citadel/organs/penis.dm
@@ -1,21 +1,23 @@
/obj/item/organ/genital/penis
- name = "Penis"
- desc = "A male reproductive organ."
- icon_state = "penis"
- icon = 'code/citadel/icons/penis.dmi'
- zone = "groin"
- slot = "penis"
- w_class = 3
- can_masturbate_with = 1
- size = 2 //arbitrary value derived from length and girth for sprites.
- var/length = 6 //inches
+ name = "penis"
+ desc = "A male reproductive organ."
+ icon_state = "penis"
+ icon = 'code/citadel/icons/penis.dmi'
+ zone = "groin"
+ slot = "penis"
+ w_class = 3
+ can_masturbate_with = TRUE
+ masturbation_verb = "stroke"
+ can_climax = TRUE
+ fluid_transfer_factor = 0.5
+ size = 2 //arbitrary value derived from length and girth for sprites.
+ var/length = 6 //inches
var/cached_length //used to detect a change in length
- var/girth = 0
- var/girth_ratio = COCK_GIRTH_RATIO_DEF //0.73; check citadel_defines.dm
- var/knot_girth_ratio = KNOT_GIRTH_RATIO_DEF
- var/list/dickflags = list()
- var/list/knotted_types = list("knotted", "barbknot")
- var/obj/item/organ/genital/testicles/linked_balls
+ var/girth = 0
+ var/girth_ratio = COCK_GIRTH_RATIO_DEF //0.73; check citadel_defines.dm
+ var/knot_girth_ratio = KNOT_GIRTH_RATIO_DEF
+ var/list/dickflags = list()
+ var/list/knotted_types = list("knotted", "barbknot")
/obj/item/organ/genital/penis/update_size()
if(length == cached_length)
@@ -55,15 +57,16 @@
/obj/item/organ/genital/penis/update_link()
if(owner)
- linked_balls = (owner.getorganslot("testicles"))
- if(linked_balls)
- linked_balls.linked_penis = src
+ linked_organ = (owner.getorganslot("testicles"))
+ if(linked_organ)
+ linked_organ.linked_organ = src
else
- if(linked_balls)
- linked_balls.linked_penis = null
- linked_balls = null
+ if(linked_organ)
+ linked_organ.linked_organ = null
+ linked_organ = null
-/obj/item/organ/genital/penis/remove_ref()
- if(linked_balls)
- linked_balls.linked_penis = null
- linked_balls = null
+/obj/item/organ/genital/penis/is_exposed()
+ . = ..()
+ if(.)
+ return TRUE
+ return owner.is_groin_exposed()
\ No newline at end of file
diff --git a/code/citadel/organs/testicles.dm b/code/citadel/organs/testicles.dm
index a759aeab52..bb3ade6048 100644
--- a/code/citadel/organs/testicles.dm
+++ b/code/citadel/organs/testicles.dm
@@ -1,18 +1,17 @@
/obj/item/organ/genital/testicles
- name = "Testicles"
+ name = "testicles"
desc = "A male reproductive organ."
icon_state = "testicles"
icon = 'code/citadel/icons/penis.dmi'
zone = "groin"
slot = "testicles"
w_class = 3
- var/internal = FALSE
+ internal = TRUE
size = BALLS_SIZE_DEF
var/sack_size = BALLS_SACK_SIZE_DEF
fluid_id = "semen"
producing = TRUE
var/sent_full_message = 1 //defaults to 1 since they're full to start
- var/obj/item/organ/genital/penis/linked_penis
/obj/item/organ/genital/testicles/Initialize()
. = ..()
@@ -33,20 +32,20 @@
return FALSE
sent_full_message = 0
update_link()
- if(!linked_penis)
+ if(!linked_organ)
return FALSE
reagents.isolate_reagent(fluid_id)//remove old reagents if it changed and just clean up generally
reagents.add_reagent(fluid_id, (fluid_mult * fluid_rate))//generate the cum
/obj/item/organ/genital/testicles/update_link()
if(owner && !QDELETED(src))
- linked_penis = (owner.getorganslot("penis"))
- if(linked_penis)
- linked_penis.linked_balls = src
+ linked_organ = (owner.getorganslot("penis"))
+ if(linked_organ)
+ linked_organ.linked_organ = src
else
- if(linked_penis)
- linked_penis.linked_balls = null
- linked_penis = null
+ if(linked_organ)
+ linked_organ.linked_organ = null
+ linked_organ = null
/obj/item/organ/genital/testicles/proc/send_full_message(msg = "Your balls finally feel full, again.")
if(owner && istext(msg))
diff --git a/code/citadel/organs/vagina.dm b/code/citadel/organs/vagina.dm
index fa1f00bda2..f170ced9b6 100644
--- a/code/citadel/organs/vagina.dm
+++ b/code/citadel/organs/vagina.dm
@@ -1,12 +1,15 @@
/obj/item/organ/genital/vagina
- name = "Vagina"
+ name = "vagina"
desc = "A female reproductive organ."
icon = 'code/citadel/icons/vagina.dmi'
icon_state = "vagina"
zone = "groin"
slot = "vagina"
size = 1 //There is only 1 size right now
- can_masturbate_with = 1
+ can_masturbate_with = TRUE
+ masturbation_verb = "finger"
+ can_climax = TRUE
+ fluid_transfer_factor = 0.1 //Yes, some amount is exposed to you, go get your AIDS
w_class = 3
var/cap_length = 8//D E P T H (cap = capacity)
var/cap_girth = 12
@@ -15,7 +18,6 @@
var/clit_diam = 0.25
var/clit_len = 0.25
var/list/vag_types = list("tentacle", "dentata", "hairy")
- var/obj/item/organ/genital/womb/linked_womb
/obj/item/organ/genital/vagina/update_appearance()
@@ -56,15 +58,16 @@
/obj/item/organ/genital/vagina/update_link()
if(owner)
- linked_womb = (owner.getorganslot("womb"))
- if(linked_womb)
- linked_womb.linked_vag = src
+ linked_organ = (owner.getorganslot("womb"))
+ if(linked_organ)
+ linked_organ.linked_organ = src
else
- if(linked_womb)
- linked_womb.linked_vag = null
- linked_womb = null
+ if(linked_organ)
+ linked_organ.linked_organ = null
+ linked_organ = null
-/obj/item/organ/genital/vagina/remove_ref()
- if(linked_womb)
- linked_womb.linked_vag = null
- linked_womb = null
+/obj/item/organ/genital/vagina/is_exposed()
+ . = ..()
+ if(.)
+ return TRUE
+ return owner.is_groin_exposed()
\ No newline at end of file
diff --git a/code/citadel/organs/womb.dm b/code/citadel/organs/womb.dm
index 3ebe5c2f98..433f005623 100644
--- a/code/citadel/organs/womb.dm
+++ b/code/citadel/organs/womb.dm
@@ -1,15 +1,15 @@
/obj/item/organ/genital/womb
- name = "Womb"
+ name = "womb"
desc = "A female reproductive organ."
icon = 'code/citadel/icons/vagina.dmi'
icon_state = "womb"
zone = "groin"
slot = "womb"
w_class = 3
- var/internal = FALSE
+ internal = TRUE
fluid_id = "femcum"
producing = TRUE
- var/obj/item/organ/genital/vagina/linked_vag
+
/obj/item/organ/genital/womb/Initialize()
. = ..()
@@ -24,25 +24,20 @@
/obj/item/organ/genital/womb/proc/generate_femcum()
reagents.maximum_volume = fluid_max_volume
update_link()
- if(!linked_vag)
+ if(!linked_organ)
return FALSE
reagents.isolate_reagent(fluid_id)//remove old reagents if it changed and just clean up generally
reagents.add_reagent(fluid_id, (fluid_mult * fluid_rate))//generate the cum
/obj/item/organ/genital/womb/update_link()
if(owner)
- linked_vag = (owner.getorganslot("vagina"))
- if(linked_vag)
- linked_vag.linked_womb = src
+ linked_organ = (owner.getorganslot("vagina"))
+ if(linked_organ)
+ linked_organ.linked_organ = src
else
- if(linked_vag)
- linked_vag.linked_womb = null
- linked_vag = null
-
-/obj/item/organ/genital/womb/remove_ref()
- if(linked_vag)
- linked_vag.linked_womb = null
- linked_vag = null
+ if(linked_organ)
+ linked_organ.linked_organ = null
+ linked_organ = null
/obj/item/organ/genital/womb/Destroy()
return ..()