diff --git a/hyperstation/code/modules/arousal/arousalhud.dm b/hyperstation/code/modules/arousal/arousalhud.dm index 757fd2330..586e6608c 100644 --- a/hyperstation/code/modules/arousal/arousalhud.dm +++ b/hyperstation/code/modules/arousal/arousalhud.dm @@ -334,7 +334,7 @@ obj/screen/arousal/proc/kiss() return //We got hands, let's pick an organ var/obj/item/organ/genital/picked_organ - picked_organ = pick_masturbate_genitals() + picked_organ = pick_climax_genitals(masturbation=TRUE, title="Masturbation") if(picked_organ) src << browse(null, "window=arousal") //closes the window mob_masturbate(picked_organ) @@ -378,7 +378,7 @@ obj/screen/arousal/proc/kiss() to_chat(src, "You cannot climax without choosing genitals.") return src << browse(null, "window=arousal") // close arousal window - mob_climax_partner_spillage(picked_organ, partner, FALSE) + mob_climax_partner_spillage(picked_organ, partner, impreg=FALSE) /mob/living/carbon/human/proc/climaxover(mob/living/carbon/human/partner) @@ -398,7 +398,7 @@ obj/screen/arousal/proc/kiss() if(P.sounding) to_chat(src, "You cannot do this action with a sounding in.") return - mob_climax_partner(picked_organ, partner, FALSE, FALSE, TRUE) + mob_climax_cover(picked_organ, partner) /mob/living/carbon/human/proc/clothesplosion() @@ -435,7 +435,7 @@ obj/screen/arousal/proc/kiss() to_chat(src, "You cannot impregnate someone with a sounding rod in.") return src << browse(null, "window=arousal") //alls fine, we can close the window now. - mob_climax_partner_spillage(picked_organ, partner, TRUE) + mob_climax_partner_spillage(picked_organ, partner, impreg=TRUE) /mob/living/carbon/human/proc/cumcontainer(mob/living/T) @@ -476,6 +476,11 @@ obj/screen/arousal/proc/kiss() cum_splatter_icon.Blend(icon('hyperstation/icons/effects/cumoverlay.dmi', "cum_obj"), ICON_MULTIPLY) add_overlay(cum_splatter_icon) +/mob/living/carbon/add_cum_overlay(type="normal") + var/mutable_appearance/cumoverlay = mutable_appearance('hyperstation/icons/effects/cumoverlay.dmi') + cumoverlay.icon_state = "cum_[type]" + add_overlay(cumoverlay) + /atom/proc/wash_cum() cut_overlay(mutable_appearance('hyperstation/icons/effects/cumoverlay.dmi', "cum_normal")) cut_overlay(mutable_appearance('hyperstation/icons/effects/cumoverlay.dmi', "cum_large")) @@ -499,7 +504,7 @@ obj/screen/arousal/proc/kiss() return //We got hands, let's pick an organ var/obj/item/organ/genital/picked_organ - picked_organ = pick_masturbate_genitals() + picked_organ = pick_climax_genitals(masturbation=TRUE, title="Masturbation") if(picked_organ) src << browse(null, "window=arousal") //closes the window mob_masturbate(picked_organ, cover = TRUE) diff --git a/hyperstation/code/modules/arousal/organs/genitals.dm b/hyperstation/code/modules/arousal/organs/genitals.dm new file mode 100644 index 000000000..9da552021 --- /dev/null +++ b/hyperstation/code/modules/arousal/organs/genitals.dm @@ -0,0 +1,6 @@ +/obj/item/organ/genital/proc/get_fluid_source() + if(producing) + return src.reagents + if(linked_organ && linked_organ.producing) + return linked_organ.reagents + return null \ No newline at end of file diff --git a/hyperstation/code/obj/fleshlight.dm b/hyperstation/code/obj/fleshlight.dm index 8a6932906..3d5c7ab48 100644 --- a/hyperstation/code/obj/fleshlight.dm +++ b/hyperstation/code/obj/fleshlight.dm @@ -102,29 +102,29 @@ else . += "The device is paired, and awaiting input. " -/obj/item/portallight/attack(mob/living/carbon/holder, mob/living/user) +/obj/item/portallight/attack(mob/living/carbon/user, mob/living/holder) if(inuse) //just to stop stacking and causing people to cum instantly return if(!UpdateUsability()) - to_chat(user, "\The [src] appears to be currently unusable.") + to_chat(holder, "\The [src] appears to be currently unusable.") return var/option = "Fuck" - if(holder == user) + if(user == holder) option = input(usr, "Choose action", "Portal Fleshlight", "Fuck") in list("Fuck", "Lick", "Touch") var/obj/item/organ/genital/target_genital = portalunderwear.loc var/mob/living/carbon/human/target = target_genital.owner - var/obj/item/organ/genital/penis/P = holder.getorganslot("penis") + var/obj/item/organ/genital/penis/P = user.getorganslot("penis") if(option == "Fuck" && !P.is_exposed()) - to_chat(user, "You don't see anywhere to use this on.") + to_chat(holder, "You don't see anywhere to use this on.") return /* WARMUP START - prevents spam/instant climax */ inuse = TRUE - if(holder != user) - holder.visible_message("[user] is trying to use [src] on [holder]'s penis.",\ - "[user] is trying to use [src] on your penis.") - if(!do_mob(user, holder, 3 SECONDS)) + if(user != holder) + user.visible_message("[holder] is trying to use [src] on [user]'s penis.",\ + "[holder] is trying to use [src] on your penis.") + if(!do_mob(holder, user, 3 SECONDS)) inuse = FALSE return inuse = FALSE @@ -134,31 +134,32 @@ switch(option) if("Fuck") playsound(src, 'sound/lewd/slaps.ogg', 30, 1, -1) - if(holder == user) - user.visible_message("[user] pumps [src] on their penis.", + if(user == holder) + holder.visible_message("[holder] pumps [src] on their penis.", "You pump the fleshlight on your penis.") else - holder.visible_message("[user] pumps [src] on [holder]'s penis.",\ - "[user] pumps [src] up and down on your penis.") + user.visible_message("[holder] pumps [src] on [user]'s penis.",\ + "[holder] pumps [src] up and down on your penis.") to_chat(target, "You feel a [P.length] inch, [P.shape] shaped penis pumping through the portal into your [target_genital.name].") if(prob(30)) - holder.emote("moan") - holder.adjustArousalLoss(20) + user.emote("moan") + user.adjustArousalLoss(20) target.adjustArousalLoss(20) target.do_jitter_animation() if(target.can_orgasm() && prob(5)) target.mob_climax_outside(target_genital, spillage = target_genital.is_exposed()) - if(holder.can_orgasm()) - var/mob/living/carbon/human/O = holder + if(user.can_orgasm()) + var/mob/living/carbon/human/O = user var/impreg_chance = target_genital.name == "vagina" && !P.condom && !P.sounding - O.mob_climax_partner(P, target, FALSE, impreg_chance, FALSE, TRUE) + if(O.mob_climax_partner(P, target, spillage=FALSE, remote=TRUE) && impreg_chance) + target.impregnate(by=O) if("Lick") - user.visible_message("[user] licks into [src].",\ + holder.visible_message("[holder] licks into [src].",\ "You lick into [src].") to_chat(target, "You feel a tongue lick you through the portal against your [target_genital.name].") target.adjustArousalLoss(10) if("Touch") - user.visible_message("[user] touches softly against [src].",\ + holder.visible_message("[holder] touches softly against [src].",\ "You touch softly on [src].") to_chat(target, "You feel someone touching your [target_genital.name] through the portal.") target.adjustArousalLoss(5) diff --git a/modular_citadel/code/modules/arousal/arousal.dm b/modular_citadel/code/modules/arousal/arousal.dm index f41d1ee58..8d2de3d34 100644 --- a/modular_citadel/code/modules/arousal/arousal.dm +++ b/modular_citadel/code/modules/arousal/arousal.dm @@ -189,19 +189,30 @@ if(M.canbearoused) ui_interact(usr) +/** + * Checks if this mob can orgasm. Criteria includes: + * - Mob is human + * - Mob is capable of arousal + * - Mob has DNA + * - Mob is aroused past a certain threshold + * + * Arguments: + * - `arousal` - The minimum arousal needed to pass the check. Default is `100`. + * + * Returns: `TRUE/FALSE` - Whether or not the mob passes all of the above checks. + */ /mob/living/proc/can_orgasm(arousal = 100) return src.canbearoused && src.getArousalLoss() >= arousal && ishuman(src) && src.has_dna() + /mob/living/proc/mob_climax() -// This is just so I can test this shit without being forced to add actual content -// to get rid of arousal. Will be a very basic proc for a while. set name = "Masturbate" set category = "IC" if(canbearoused && !restrained() && !stat) if(mb_cd_timer <= world.time) //start the cooldown even if it fails mb_cd_timer = world.time + mb_cd_length - if(getArousalLoss() >= isPercentAroused(33))//33% arousal or greater required + if(isPercentAroused(33))//33% arousal or greater required src.visible_message("[src] starts masturbating!", \ "You start masturbating.") if(do_after(src, 30, target = src)) @@ -213,33 +224,24 @@ to_chat(src, "You aren't aroused enough for that.") -//These are various procs that we'll use later, split up for readability instead of having one, huge proc. -//For all of these, we assume the arguments given are proper and have been checked beforehand. + /mob/living/carbon/human/proc/mob_masturbate(obj/item/organ/genital/G, cover = FALSE, mb_time = 30) -//Masturbation, keep it gender-neutral var/total_fluids = 0 - var/datum/reagents/fluid_source = null - var/condomed = 0 - var/sounded = 0 - if(G.name == "penis")//if the select organ is a penis + var/datum/reagents/fluid_source = G.get_fluid_source() + var/condomed = FALSE + var/sounded = FALSE + if(G.name == "penis") var/obj/item/organ/genital/penis/P = src.getorganslot("penis") - if(P.condom) //if the penis is condomed - condomed = 1 - if(P.sounding) - sounded = 1 - if(G.producing) //Can it produce its own fluids, such as breasts? - fluid_source = G.reagents - else - if(!G.linked_organ) - to_chat(src, "Your [G.name] is unable to produce it's own fluids, it's missing the organs for it.") - return - fluid_source = G.linked_organ.reagents + condomed = P.condom + sounded = P.sounding + if(!fluid_source) + to_chat(src, "Your [G.name] is unable to produce it's own fluids, it's missing the organs for it.") + return total_fluids = fluid_source.total_volume if(mb_time) src.visible_message("[src] starts to [G.masturbation_verb] [p_their()] [G.name].", \ "You start to [G.masturbation_verb] your [G.name].", \ "You start to [G.masturbation_verb] your [G.name].") - if(do_after(src, mb_time, target = src)) if(total_fluids > 5 &&!condomed &&!sounded) fluid_source.reaction(src.loc, TOUCH, 1, 0) @@ -248,14 +250,11 @@ src.visible_message("[src] orgasms, cumming[istype(src.loc, /turf/open/floor) ? " onto [src.loc]" : ""]!", \ "You cum[istype(src.loc, /turf/open/floor) ? " onto [src.loc]" : ""].", \ "You have relieved yourself.") - if(!condomed &&!sounded && cover)//For when you want to make a mess of yourself. fluid_source.trans_to(src, total_fluids*G.fluid_transfer_factor) total_fluids -= total_fluids*G.fluid_transfer_factor if(total_fluids > 80) // now thats a big cum! - var/mutable_appearance/cumoverlaylarge = mutable_appearance('hyperstation/icons/effects/cumoverlay.dmi') - cumoverlaylarge.icon_state = "cum_large" - src.add_overlay(cumoverlaylarge) + src.add_cum_overlay("large") if(total_fluids > 5) fluid_source.reaction(src.loc, TOUCH, 1, 0) var/mob/living/carbon/human/H = src @@ -265,10 +264,7 @@ src.visible_message("[src] climaxes over [p_their()] self, using [p_their()] [G.name]!", \ "You orgasm over yourself, using your [G.name].", \ "You have climaxed over something, using your [G.name].") - var/mutable_appearance/cumoverlay = mutable_appearance('hyperstation/icons/effects/cumoverlay.dmi') - cumoverlay.icon_state = "cum_normal" - src.add_overlay(cumoverlay) - + src.add_cum_overlay() if(condomed) //condomed src.visible_message("[src] orgasms, climaxing into [p_their()] condom!", \ "You cum into your condom.", \ @@ -279,7 +275,6 @@ "You don't quite feel totally relieved.") if(total_fluids > 0 &&condomed &&!sounded) src.condomclimax() - SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) if(G.can_climax) setArousalLoss(min_arousal) @@ -288,19 +283,12 @@ /mob/living/carbon/human/proc/mob_climax_outside(obj/item/organ/genital/G, mb_time = 30, spillage = TRUE) //This is used for forced orgasms and other hands-free climaxes var/total_fluids = 0 - var/datum/reagents/fluid_source = null + var/datum/reagents/fluid_source = G.get_fluid_source() var/unable_to_come = FALSE - - if(G.producing) //Can it produce its own fluids, such as breasts? - fluid_source = G.reagents + if(fluid_source) total_fluids = fluid_source.total_volume else - if(!G.linked_organ) - unable_to_come = TRUE - else - fluid_source = G.linked_organ.reagents - total_fluids = fluid_source.total_volume - + unable_to_come = TRUE if(unable_to_come) src.visible_message("[src] shudders, their [G.name] unable to cum.", \ "Your [G.name] cannot cum, giving no relief.", \ @@ -323,108 +311,107 @@ src.visible_message("[src] orgasms with [p_their()] [G.name]!", \ "You climax with your [G.name].", \ "You climax using your [G.name].") - SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) if(G.can_climax) setArousalLoss(min_arousal) -/mob/living/carbon/human/proc/mob_climax_partner(obj/item/organ/genital/G, atom/partner, spillage = TRUE, impreg = FALSE,cover = FALSE,remote = FALSE, mb_time = 30) //Used for climaxing with any person - if(isnoncarbon(partner)) // deny animals - return +/mob/living/carbon/human/proc/mob_climax_cover(obj/item/organ/genital/genital, atom/partner, mb_time=3 SECONDS) var/total_fluids = 0 - var/datum/reagents/fluid_source = null - if(G.producing) //Can it produce its own fluids, such as breasts? - fluid_source = G.reagents - else - if(!G.linked_organ) - to_chat(src, "Your [G.name] is unable to produce it's own fluids, it's missing the organs for it.") - return - fluid_source = G.linked_organ.reagents + var/datum/reagents/fluid_source = genital.get_fluid_source() + if(!fluid_source) + to_chat(src, "Your [genital.name] is unable to produce it's own fluids, it's missing the organs for it.") + return total_fluids = fluid_source.total_volume - if(mb_time && !remote) //Skip warning if this is an instant climax. + var/mob/living/carbon/partner_carbon = partner + if(mb_time) src.visible_message("[src] is about to climax with [partner]!", \ "You're about to climax with [partner]!", \ "You're preparing to climax with something!") + if(!do_after(src, mb_time, target = src)) + return + if(!in_range(src, partner)) + return + var/transferred_units = total_fluids * genital.fluid_transfer_factor + fluid_source.trans_to(partner, transferred_units) + total_fluids -= transferred_units + if(total_fluids > 80 && iscarbon(partner)) // now thats a big cum! + partner_carbon.add_cum_overlay("large") + if(total_fluids > 5) + fluid_source.reaction(partner.loc, TOUCH, 1, 0) + var/mob/living/carbon/human/H = partner + if(H) + H.cumdrip_rate += rand(5,10) + fluid_source.clear_reagents() + if(iscarbon(partner)) + partner_carbon.add_cum_overlay("normal") + else + partner.add_cum_overlay() + src.visible_message("[src] climaxes over [partner], using [p_their()] [genital.name]!", \ + "You orgasm over [partner], using your [genital.name].", \ + "You have climaxed over something, using your [genital.name].") + SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) + setArousalLoss(min_arousal) + + +/mob/living/carbon/human/proc/mob_climax_partner(obj/item/organ/genital/G, atom/partner, spillage=TRUE, remote=FALSE, mb_time = 3 SECONDS) + if(isnoncarbon(partner)) // deny animals + return FALSE + var/total_fluids = 0 + var/datum/reagents/fluid_source = G.get_fluid_source() + if(!fluid_source) + to_chat(src, "Your [G.name] is unable to produce it's own fluids, it's missing the organs for it.") + return FALSE + total_fluids = fluid_source.total_volume if(remote) src.visible_message("[src] is about to climax with someone!", \ "You're about to climax with someone!", \ "You're preparing to climax with something!") - if(cover)//covering the partner in cum, this overrides other options. - if(do_after(src, mb_time, target = src) && in_range(src, partner)) - fluid_source.trans_to(partner, total_fluids*G.fluid_transfer_factor) - total_fluids -= total_fluids*G.fluid_transfer_factor - if(total_fluids > 80 && iscarbon(partner)) // now thats a big cum! - var/mutable_appearance/cumoverlaylarge = mutable_appearance('hyperstation/icons/effects/cumoverlay.dmi') - cumoverlaylarge.icon_state = "cum_large" - partner.add_overlay(cumoverlaylarge) - if(total_fluids > 5) - fluid_source.reaction(partner.loc, TOUCH, 1, 0) - var/mob/living/carbon/human/H = partner - if(H) - H.cumdrip_rate += rand(5,10) - fluid_source.clear_reagents() - src.visible_message("[src] climaxes over [partner], using [p_their()] [G.name]!", \ - "You orgasm over [partner], using your [G.name].", \ - "You have climaxed over something, using your [G.name].") - SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) - if(iscarbon(partner)) - var/mutable_appearance/cumoverlay = mutable_appearance('hyperstation/icons/effects/cumoverlay.dmi') - cumoverlay.icon_state = "cum_normal" - partner.add_overlay(cumoverlay) - else - partner.add_cum_overlay() - setArousalLoss(min_arousal) - if(G.can_climax) - setArousalLoss(min_arousal) - if(spillage && !cover) - if(do_after(src, mb_time, target = src)) - if(!in_range(src, partner) && !remote) - return - fluid_source.trans_to(partner, total_fluids*G.fluid_transfer_factor) - total_fluids -= total_fluids*G.fluid_transfer_factor - if(total_fluids > 5) - fluid_source.reaction(partner.loc, TOUCH, 1, 0) - var/mob/living/carbon/human/H = partner - if(H) - H.cumdrip_rate += rand(5,10) - fluid_source.clear_reagents() - src.visible_message("[src] climaxes with [partner][spillage ? ", overflowing and spilling":""], using [p_their()] [G.name]!", \ - "You orgasm with [partner][spillage ? ", spilling out of them":""], using your [G.name].", \ - "You have climaxed with someone[spillage ? ", spilling out of them":""], using your [G.name].") - SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) - SEND_SIGNAL(partner, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) - if(G.can_climax) - setArousalLoss(min_arousal) + else if(mb_time) + src.visible_message("[src] is about to climax with [partner]!", \ + "You're about to climax with [partner]!", \ + "You're preparing to climax with something!") + if(!do_after(src, mb_time, target = src)) + return FALSE + if(!remote && !in_range(src, partner)) + return FALSE + if(spillage) + fluid_source.trans_to(partner, total_fluids*G.fluid_transfer_factor) + total_fluids -= total_fluids*G.fluid_transfer_factor + if(total_fluids > 5) + fluid_source.reaction(partner.loc, TOUCH, 1, 0) + var/mob/living/carbon/human/H = partner + if(H) + H.cumdrip_rate += rand(5,10) + fluid_source.clear_reagents() + src.visible_message("[src] climaxes with [partner][spillage ? ", overflowing and spilling":""], using [p_their()] [G.name]!",\ + "You orgasm with [partner][spillage ? ", spilling out of them":""], using your [G.name].",\ + "You have climaxed with someone[spillage ? ", spilling out of them":""], using your [G.name].") + SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) + SEND_SIGNAL(partner, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) else //knots, portal fleshlights, and other non-spilling orgasms - if(!cover) - if(!remote && !in_range(src, partner)) - return - if(do_after(src, mb_time, target = src)) - var/mob/living/carbon/H = partner - if(!spillage && total_fluids > 80) //hyper inflation; requires a big cumshot to expand - H.expand_belly(1) - var/obj/item/organ/genital/penis/P = G - if (P.condom)//condomed. - src.condomclimax() - else - fluid_source.trans_to(partner, total_fluids) - total_fluids = 0 - if(!remote) - src.visible_message("[src] climaxes with [partner], [p_their()] [G.name] spilling nothing!", \ - "You ejaculate with [partner], your [G.name] spilling nothing.", \ - "You have climaxed inside someone, your [G.name] spilling nothing.") - else - src.visible_message("[src] climaxes with someone, using [p_their()] [G.name]!", \ - "You ejaculate with someone, using your [G.name].", \ - "You have climaxed inside someone, using your [G.name].") - to_chat(partner, "You feel someone ejaculate inside you.") - - SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) - SEND_SIGNAL(partner, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) - - if(G.can_climax) - setArousalLoss(min_arousal) + var/mob/living/carbon/H = partner + if(!spillage && total_fluids > 80) //hyper inflation; requires a big cumshot to expand + H.expand_belly(1) + var/obj/item/organ/genital/penis/P = G + if(P.condom)//condomed. + src.condomclimax() + else + fluid_source.trans_to(partner, total_fluids) + total_fluids = 0 + if(!remote) + src.visible_message("[src] climaxes with [partner], [p_their()] [G.name] spilling nothing!", \ + "You ejaculate with [partner], your [G.name] spilling nothing.", \ + "You have climaxed inside someone, your [G.name] spilling nothing.") + else + src.visible_message("[src] climaxes with someone, using [p_their()] [G.name]!", \ + "You ejaculate with someone, using your [G.name].", \ + "You have climaxed inside someone, using your [G.name].") + to_chat(partner, "You feel someone ejaculate inside you.") + SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) + SEND_SIGNAL(partner, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) + if(G.can_climax) + setArousalLoss(min_arousal) //Hyper - antag code var/mob/living/carbon/partner_carbon = partner if(src.mind.special_role == ROLE_LEWD_TRAITOR) @@ -432,98 +419,107 @@ if (partner_carbon.mind == obj.target) partner_carbon.mind.sexed = TRUE //sexed to_chat(src, "You feel deep satisfaction with yourself.") - //Hyper - impreg - if(impreg) - partner_carbon.impregnate(by = src) + return TRUE /mob/living/carbon/human/proc/mob_climax_partner_spillage(obj/item/organ/genital/picked_organ, mob/living/carbon/partner, impreg = FALSE) var/obj/item/organ/genital/penis/_penis = picked_organ if(picked_organ.name != "penis" || _penis.sounding || _penis.condom) - mob_climax_partner(picked_organ, partner, FALSE, FALSE, FALSE) + mob_climax_partner(picked_organ, partner, spillage=FALSE) return var/_question = "Would your fluids spill outside?" var/_title = "Choose overflowing option" var/spillage = input(src, _question, _title, "Yes") as anything in list("Yes", "No") - mob_climax_partner(picked_organ, partner, spillage == "Yes", impreg, FALSE) + if(mob_climax_partner(picked_organ, partner, spillage == "Yes") && impreg) + partner.impregnate(by=src) -/mob/living/carbon/human/proc/mob_fill_container(obj/item/organ/genital/G, obj/item/reagent_containers/container, mb_time = 30) -// For beaker-filling, beware the bartender - var/total_fluids = 0 - var/datum/reagents/fluid_source = null - if(G.name == "penis") - var/obj/item/organ/genital/penis/P = src.getorganslot("penis") - if(P.condom) - to_chat(src, "You cannot fill containers when there is a condom over your [G.name].") - return - if(P.sounding) - to_chat(src, "You cannot fill containers when there is a rod inside your [G.name].") - return - if(G.producing) //Can it produce its own fluids, such as breasts? - fluid_source = G.reagents - else - if(!G.linked_organ) - to_chat(src, "Your [G.name] is unable to produce it's own fluids, it's missing the organs for it.") - return - fluid_source = G.linked_organ.reagents - total_fluids = fluid_source.total_volume - +/** + * Attempt to climax into a container it, filling it with whatever fluids are + * associated to the selected genitals. + * + * Arguments: + * - `genital`: The genital being used for climax. + * - `container`: The container being targeted. + * - `mb_time`: The warm-up time for this interaction. + */ +/mob/living/carbon/human/proc/mob_fill_container(obj/item/organ/genital/genital, obj/item/reagent_containers/container, mb_time = 30) if(!container) //Something weird happened to_chat(src, "You need a container to do this!") return - - src.visible_message("[src] starts to [G.masturbation_verb] their [G.name] over [container].", \ - "You start to [G.masturbation_verb] your [G.name] over [container].", \ - "You start to [G.masturbation_verb] your [G.name] over something.") + var/total_fluids = 0 + var/datum/reagents/fluid_source = genital.get_fluid_source() + if(!fluid_source) + to_chat(src, "Your [genital.name] is unable to produce it's own fluids, it's missing the organs for it.") + return + total_fluids = fluid_source.total_volume + if(genital.name == "penis") + var/obj/item/organ/genital/penis/P = src.getorganslot("penis") + if(P.condom) + to_chat(src, "You cannot fill containers when there is a condom over your [genital.name].") + return + if(P.sounding) + to_chat(src, "You cannot fill containers when there is a rod inside your [genital.name].") + return + src.visible_message("[src] starts to [genital.masturbation_verb] their [genital.name] over [container].", \ + "You start to [genital.masturbation_verb] your [genital.name] over [container].", \ + "You start to [genital.masturbation_verb] your [genital.name] over something.") if(do_after(src, mb_time, target = src) && in_range(src, container)) fluid_source.trans_to(container, total_fluids) - src.visible_message("[src] uses [p_their()] [G.name] to fill [container]!", \ - "You used your [G.name] and fill [container] with a total of [total_fluids]u's.", \ - "You have relieved some pressure.") + src.visible_message("[src] uses [p_their()] [genital.name] to fill [container]!", \ + "You used your [genital.name] and fill [container] with a total of [total_fluids]u's.", \ + "You have relieved some pressure.") SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) container.add_cum_overlay() //your aim is bad... - if(G.can_climax) + if(genital.can_climax) setArousalLoss(min_arousal) -/mob/living/carbon/human/proc/pick_masturbate_genitals() - var/obj/item/organ/genital/ret_organ - var/list/genitals_list = list() - for(var/obj/item/organ/genital/G in internal_organs) - if(G.can_masturbate_with) //filter out what you can't masturbate with - if(G.is_exposed()) //Nude or through_clothing - if(!G.dontlist) - genitals_list += G - if(genitals_list.len) - ret_organ = input(src, "with what?", "Masturbate", null) as null|obj in genitals_list - return ret_organ - return null //error stuff - -/mob/living/carbon/human/proc/target_genitals(mob/living/carbon/human/T) //used for targeting others +/** + * Prompts the user to select one of their target's genitals to interact with. + * The target must have their genitals visible to be selectable. + * + * Returns: `null`|`obj/item/organ/genital`: The selected genital. + */ +/mob/living/carbon/human/proc/target_genitals(mob/living/carbon/human/T) var/obj/item/organ/genital/ret_organ var/list/genitals_list = list() for(var/obj/item/organ/genital/G in T.internal_organs) - if(G.is_exposed()) //Nude or through_clothing - if(!G.dontlist) - genitals_list += G + if(G.is_exposed() && !G.dontlist) + genitals_list += G if(genitals_list.len) ret_organ = input(src, "", "Genitals", null) as null|obj in genitals_list return ret_organ return null //error stuff -/mob/living/carbon/human/proc/pick_climax_genitals() + +/** + * Prompts the user to select genitals. The genitals must be exposed to be selectable. + * + * Arguments: + * - `masturbation`: Whether or not this is being used for masturbation interactions. + * - `title`: The title of the prompt window. + * + * Returns: `null`|`obj/item/organ/genital`: The selected genital. + */ +/mob/living/carbon/human/proc/pick_climax_genitals(masturbation=FALSE, title="Climax") var/obj/item/organ/genital/ret_organ var/list/genitals_list = list() for(var/obj/item/organ/genital/G in internal_organs) if(G.can_climax && G.is_exposed() && !G.dontlist) - genitals_list += G + if(!masturbation || G.can_masturbate_with) + genitals_list += G if(genitals_list.len) - ret_organ = input(src, "with what?", "Climax", null) as null|obj in genitals_list + ret_organ = input(src, "with what?", title, null) as null|obj in genitals_list return ret_organ return null //error stuff +/** + * Prompts the user to select a mob that they are either pulling or being pulled by. + * + * Returns: `null`|`mob/living/carbon`: The selected mob. + */ /mob/living/carbon/human/proc/pick_partner(needs_exposed = TRUE) var/list/partners = list(src.pulling, src.pulledby) for(var/I in partners) @@ -537,9 +533,14 @@ partners -= I if(!partners.len) return null - return input(src, "With whom?", "Sexual partner", null) as null|anything in partners + return input(src, "With whom?", "Sexual partner", null) as null|mob in partners +/** + * Prompts the user to provide a container (or food item) based on what's in their hands. + * + * Returns: `null`|`obj/item/reagent_containers` - The selected container. + */ /mob/living/carbon/human/proc/pick_climax_container() var/obj/item/reagent_containers/SC = null var/list/containers_list = list() @@ -551,12 +552,14 @@ if(SC) if(in_range(src, SC)) return SC - return null //If nothing correct, give null. + return null +/** + * The old arousal menu. This shows a BYOND-style input selection menu that + * emulates the same functionality as the newer one. + */ /mob/living/carbon/human/mob_climax() - if(stat == DEAD) //corpses can't cum - return if(!canbearoused || !has_dna()) return if(mb_cd_timer > world.time) @@ -627,7 +630,7 @@ return //We got hands, let's pick an organ var/obj/item/organ/genital/picked_organ - picked_organ = pick_masturbate_genitals() + picked_organ = pick_climax_genitals(masturbation=TRUE, title="Masturbation") if(picked_organ) mob_masturbate(picked_organ) return @@ -684,7 +687,7 @@ if(picked_organ) var/mob/living/carbon/partner = pick_partner(FALSE) //Get your partner, clothed or not. if(partner) - mob_climax_partner(picked_organ, partner, FALSE, FALSE, TRUE) + mob_climax_cover(picked_organ, partner) else to_chat(src, "You cannot do this alone.") @@ -722,6 +725,12 @@ return //Just end it in that case. +/** + * Causes a mob to climax on the spot. There is no warmup time, nor a + * menu for selecting genitals. Instead, the mob will climax using all + * climaxable genitals, either onto their location (floor) or on whatever + * is pulling/being pulled by them. + */ /mob/living/carbon/proc/mob_climax_instant() if(stat == DEAD) //corpses can't cum to_chat(src, "You can't do that while dead!") @@ -756,6 +765,12 @@ return +/** + * Rolls a chance for the mob to get pregnant, assuming they are capable of doing so. + * + * Arguments: + * - `by` - The mob that is impregnating this one. + */ /mob/living/carbon/proc/impregnate(mob/living/carbon/by) var/obj/item/organ/genital/womb/W = getorganslot("womb") if(!W || W.pregnant || !breedable) diff --git a/tgstation.dme b/tgstation.dme index 7ab0343dd..b6c7c761e 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -3145,6 +3145,7 @@ #include "hyperstation\code\modules\arousal\arousalhud.dm" #include "hyperstation\code\modules\arousal\creampie.dm" #include "hyperstation\code\modules\arousal\expansion.dm" +#include "hyperstation\code\modules\arousal\organs\genitals.dm" #include "hyperstation\code\modules\busy_space\air_traffic.dm" #include "hyperstation\code\modules\busy_space\atc_controller.dm" #include "hyperstation\code\modules\busy_space\loremaster.dm"