Climaxing/masturbation changes, exposing through clothing, breastmilk and masturbation (#2357)

* Genital refactoring and masturbation rework

Refactors a lot of genitalia code.
Reworks masturbation code.
Adds the ability to expose onself through clothing (Still bugfixing it).

* Fixes it so everything works

Toggling exposing your genitals now works properly. You can now expose
genitals through clothing.
Genitalia is rendered a bit more modularly and less hardcoded.
Genitalia no longer has linked_penis and such, but instead linked_organ.
More variables made general for genitals.
Breasts can now be used to masturbate and climax, also letting you fill
beakers and others with milk.
Both being pulled and pulling someone can let you count them as a
partner for climaxing verbs.
Mob_climax split up into more sub-procs.
Hexacrocin made less verbose, and not as spam-happy with forced orgasms.
Hexacamphor also made less verbose.

* Removes outcommented code

Forgot to remove the old masturbation code that I commented out.

* Changes one tab further on line 248

As pointed out to me ^^.

* Climaxing with partner tabulation

Also copied the command for the spillage version.

* capitalisation and timers

Removed timers from forced climaxes and made it a default value
parameter instead, so it's adjustable.
Changed capitalisation of genitals and the mentions of them now use the
.name to avoid including a "The" in the mention.

* I accidentally runtimestation

Accidentally committed changing the map to runtimestation when I
debugged my code.
Changed it back to basemap now.
This commit is contained in:
ktccd
2017-08-20 04:01:02 +02:00
committed by kevinz000
parent 6541ea749c
commit 5731ea7cad
9 changed files with 495 additions and 295 deletions
+339 -199
View File
@@ -181,224 +181,364 @@
else
to_chat(src, "<span class='notice'>You aren't aroused enough for that.</span>")
//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, "<span class='warning'>Your [G.name] is unable to produce it's own fluids, it's missing the organs for it.</span>")
return
fluid_source = G.linked_organ.reagents
total_fluids = fluid_source.total_volume
if(mb_time)
src.visible_message("<span class='danger'>[src] starts to [G.masturbation_verb] [p_their()] [G.name].</span>", \
"<span class='green'>You start to [G.masturbation_verb] your [G.name].</span>", \
"<span class='green'>You start to [G.masturbation_verb] your [G.name].</span>")
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("<span class='danger'>[src] orgasms, cumming[istype(src.loc, /turf/open/floor) ? " onto [src.loc]" : ""]!</span>", \
"<span class='green'>You cum[istype(src.loc, /turf/open/floor) ? " onto [src.loc]" : ""].</span>", \
"<span class='green'>You have relieved yourself.</span>")
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("<span class='danger'>[src] shudders, their [G.name] unable to cum.</span>", \
"<span class='userdanger'>Your [G.name] cannot cum, giving no relief.</span>", \
"<span class='userdanger'>Your [G.name] cannot cum, giving no relief.</span>")
else
total_fluids = fluid_source.total_volume
if(mb_time) //as long as it's not instant, give a warning
src.visible_message("<span class='danger'>[src] looks like they're about to cum.</span>", \
"<span class='green'>You feel yourself about to orgasm.</span>", \
"<span class='green'>You feel yourself about to orgasm.</span>")
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("<span class='danger'>[src] orgasms[istype(src.loc, /turf/open/floor) ? ", spilling onto [src.loc]" : ""], using [p_their()] [G.name]!</span>", \
"<span class='green'>You climax[istype(src.loc, /turf/open/floor) ? ", spilling onto [src.loc]" : ""] with your [G.name].</span>", \
"<span class='green'>You climax using your [G.name].</span>")
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, "<span class='warning'>Your [G.name] is unable to produce it's own fluids, it's missing the organs for it.</span>")
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]!", \
"<span class='danger'>You're preparing to climax with someone!</span>")
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("<span class='danger'>[src] climaxes with [L][spillage ? ", overflowing and spilling":""], using [p_their()] [G.name]!</span>", \
"<span class='green'>You orgasm with [L][spillage ? ", spilling out of them":""], using your [G.name].</span>", \
"<span class='green'>You have climaxed with someone[spillage ? ", spilling out of them":""], using your [G.name].</span>")
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("<span class='danger'>[src] climaxes with [L], [p_their()] [G.name] spilling nothing!</span>", \
"<span class='green'>You ejaculate with [L], your [G.name] spilling nothing.</span>", \
"<span class='green'>You have climaxed inside someone, your [G.name] spilling nothing.</span>")
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, "<span class='warning'>Your [G.name] is unable to produce it's own fluids, it's missing the organs for it.</span>")
return
fluid_source = G.linked_organ.reagents
total_fluids = fluid_source.total_volume
//if(!container) //Something weird happened
// to_chat(src, "<span class='warning'>You need a container to do this!</span>")
// return
src.visible_message("<span class='danger'>[src] starts to [G.masturbation_verb] their [G.name] over [container].</span>", \
"<span class='userdanger'>You start to [G.masturbation_verb] your [G.name] over [container].</span>", \
"<span class='userdanger'>You start to [G.masturbation_verb] your [G.name] over something.</span>")
if(do_after(src, mb_time, target = src) && in_range(src, container))
fluid_source.trans_to(container, total_fluids)
src.visible_message("<span class='danger'>[src] uses [p_their()] [G.name] to fill [container]!</span>", \
"<span class='green'>You used your [G.name] to fill [container].</span>", \
"<span class='green'>You have relieved some pressure.</span>")
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, "<span class='warning'>You need to wait [round((mb_cd_timer - world.time)/(20))] seconds before you can do that again!</span>")
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, "<span class='warning'>You can't do that while dead!</span>")
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("<span class='danger'>[src] shudders, their [G.name] unable to cum.</span>", \
"<span class='userdanger'>Your [G.name] cannot cum, giving no relief.</span>", \
"<span class='userdanger'>Your [G.name] cannot cum, giving no relief.</span>")
else
if(fluid_source)
total_cum = fluid_source.total_volume
src.visible_message("<span class='danger'>[src] looks like they're about to cum.</span>", \
"<span class='green'>You feel yourself about to orgasm.</span>", \
"<span class='green'>You feel yourself about to orgasm.</span>")
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("<span class='danger'>[src] orgasms, cumming[istype(src.loc, /turf/open/floor) ? " onto [src.loc]" : ""]!</span>", \
"<span class='green'>You're forced to cum[istype(src.loc, /turf/open/floor) ? " onto [src.loc]" : ""] with your [G].</span>", \
"<span class='green'>Your [G] have been forced to climax.</span>")
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, "<span class='warning'>You must be conscious to do that!</span>")
return
if(restrained())
to_chat(src, "<span class='warning'>You can't do that while restrained!</span>")
return
if(getArousalLoss() < 33)//flat number instead of percentage
if(getArousalLoss() < 33) //flat number instead of percentage
to_chat(src, "<span class='warning'>You aren't aroused enough for that!</span>")
return
if(!is_groin_exposed())
to_chat(src, "<span class='warning'>You need to undress, first!</span>")
return
if(!free_hands)
to_chat(src, "<span class='warning'>You need at least one free arm.</span>")
return
for(var/helditem in held_items)//how many hands are free
if(isobj(helditem))
free_hands--
if(free_hands <= 0)
to_chat(src, "<span class='warning'>You need at least one free hand.</span>")
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, "<span class='warning'>You need a pair of testicles to do this.</span>")
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("<span class='danger'>[src] starts [pick("jerking off","stroking")] their [SG.name] over [SC].</span>", \
"<span class='userdanger'>You start jerking off over [SC.name].</span>", \
"<span class='userdanger'>You start masturbating.</span>")
if(do_after(src, mb_time, target = src) && in_range(src, SC))
fluid_source.trans_to(SC, total_cum)
src.visible_message("<span class='danger'>[src] orgasms, [pick("cumming into", "emptying themself into")] [SC]!</span>", \
"<span class='green'>You cum into [SC].</span>", \
"<span class='green'>You have relieved yourself.</span>")
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, "<span class='warning'>You must undress someone to climax inside them.</span>")
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]!", \
"<span class='danger'>You're preparing to climax inside someone!</span>")
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("<span class='danger'>[src] ejaculates inside [partner][did_spill ? ", overflowing and spilling":""]!</span>", \
"<span class='green'>You ejaculate inside [partner][did_spill ? ", spilling out of them":""].</span>", \
"<span class='green'>You have climaxed inside someone[did_spill ? ", spilling out of them":""].</span>")
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, "<span class='warning'>You can't do that while restrained!</span>")
return
var/free_hands = get_num_arms()
if(!free_hands)
to_chat(src, "<span class='warning'>You need at least one free arm.</span>")
return
for(var/helditem in held_items)//how many hands are free
if(isobj(helditem))
free_hands--
if(free_hands <= 0)
to_chat(src, "<span class='warning'>You're holding too many things.</span>")
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, "<span class='warning'>You cannot masturbate without choosing genitals.</span>")
return
src.visible_message("<span class='danger'>[src] ejaculates inside [partner], spilling nothing!</span>", \
"<span class='green'>You ejaculate inside [partner], spilling nothing.</span>", \
"<span class='green'>You have climaxed inside someone, spilling nothing.</span>")
finished = 1
//Don't care, not coding you fucking a unanchored girder
else //No pulling, or pulling non-living things
src.visible_message("<span class='danger'>[src] starts [pick("jerking off","stroking")] their [SG].</span>", \
"<span class='green'>You start masturbating.</span>", \
"<span class='green'>You start masturbating.</span>")
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, "<span class='warning'>You can't do that while restrained!</span>")
return
var/free_hands = get_num_arms()
if(!free_hands)
to_chat(src, "<span class='warning'>You need at least one free arm.</span>")
return
for(var/helditem in held_items)//how many hands are free
if(isobj(helditem))
free_hands--
if(free_hands <= 0)
to_chat(src, "<span class='warning'>You're holding too many things.</span>")
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, "<span class='warning'>You cannot climax without choosing genitals.</span>")
return
src.visible_message("<span class='danger'>[src] orgasms, cumming[istype(src.loc, /turf/open/floor) ? " onto [src.loc]" : ""]!</span>", \
"<span class='green'>You cum[istype(src.loc, /turf/open/floor) ? " onto [src.loc]" : ""].</span>", \
"<span class='green'>You have relieved yourself.</span>")
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, "<span class='warning'>You cannot do this alone.</span>")
return
else //They either lack organs that can masturbate, or they didn't pick one.
to_chat(src, "<span class='warning'>You cannot climax without choosing genitals.</span>")
return
if(/obj/item/organ/genital/vagina)
var/obj/item/organ/genital/vagina/V = SG
if(!V.linked_womb)
to_chat(src, "<span class='warning'>You need a womb to do this.</span>")
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("<span class='danger'>[src] starts fingering their [SG.name] over [SC].</span>", \
"<span class='userdanger'>You start fingering over [SC.name].</span>", \
"<span class='userdanger'>You start masturbating.</span>")
if(do_after(src, mb_time, target = src) && in_range(src, SC))
fluid_source.trans_to(SC, total_cum)
src.visible_message("<span class='danger'>[src] orgasms, [pick("cumming into", "emptying themself into")] [SC]!</span>", \
"<span class='green'>You cum into [SC].</span>", \
"<span class='green'>You have relieved yourself.</span>")
finished = 1
else//not into a container
src.visible_message("<span class='danger'>[src] starts fingering their vagina.</span>", \
"<span class='userdanger'>You start fingering your vagina.</span>", \
"<span class='userdanger'>You start masturbating.</span>")
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("<span class='danger'>[src] orgasms, cumming[istype(src.loc, /turf/open/floor) ? " onto [src.loc]" : ""]!</span>", \
"<span class='green'>You cum[istype(src.loc, /turf/open/floor) ? " onto [src.loc]" : ""].</span>", \
"<span class='green'>You have relieved yourself.</span>")
finished = 1
else//backup message, just in case
src.visible_message("<span class='danger'>[src] starts masturbating!</span>", \
"<span class='userdanger'>You start masturbating.</span>")
if(do_after(src, mb_time, target = src))
src.visible_message("<span class='danger'>[src] [pick("relieves themself!", "shudders and moans in orgasm!")]</span>", \
"<span class='userdanger'>You have relieved yourself.</span>")
finished = 1
if(finished)
setArousalLoss(min_arousal)
else
to_chat(src, "<span class='warning'>You have no genitals!</span>")
return
if("Fill container")
//We'll need hands and no restraints.
if(restrained(TRUE)) //TRUE ignores grabs
to_chat(src, "<span class='warning'>You can't do that while restrained!</span>")
return
var/free_hands = get_num_arms()
if(!free_hands)
to_chat(src, "<span class='warning'>You need at least one free arm.</span>")
return
for(var/helditem in held_items)//how many hands are free
if(isobj(helditem))
free_hands--
if(free_hands <= 0)
to_chat(src, "<span class='warning'>You're holding too many things.</span>")
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, "<span class='warning'>You cannot do this without anything to fill.</span>")
return
else //They either lack organs that can climax, or they didn't pick one.
to_chat(src, "<span class='warning'>You cannot fill anything without choosing genitals.</span>")
return
else //Somehow another option was taken, maybe something interrupted the selection or it was cancelled
return //Just end it in that case.
+3 -6
View File
@@ -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, "<span class='love'>Your libido is going haywire!</span>")
H.mob_climax(forced_climax=TRUE)
if(prob(50)) //Less spam
to_chat(H, "<span class='love'>Your libido is going haywire!</span>")
H.mob_climax(forced_climax=TRUE)
if(M.min_arousal < 50)
M.min_arousal += 1
to_chat(M, "<span class='love'>You're having a hard time thinkin about things other than sex!</span>")
if(M.min_arousal < M.max_arousal)
M.min_arousal += 1
to_chat(M, "<span class='love'>You feel your libido permanently increasing.</span>")
M.adjustArousalLoss(2)
..()
@@ -197,10 +196,8 @@
if(prob(33))
if(M.min_arousal > 0)
M.min_arousal -= 1
to_chat(M, "<span class='notice'>You feel your libido returning to more normal levels.</span>")
if(M.min_arousal > 50)
M.min_arousal -= 1
to_chat(M, "<span class='notice'>You feel like your overactive libido is calming down.</span>")
M.adjustArousalLoss(-2)
..()
+12 -2
View File
@@ -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"]]"
color = "#[owner.dna.features["breasts_color"]]"
/obj/item/organ/genital/breasts/is_exposed()
. = ..()
if(.)
return TRUE
return owner.is_chest_exposed()
+1 -1
View File
@@ -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
+76 -23
View File
@@ -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)
+29 -26
View File
@@ -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()
+9 -10
View File
@@ -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))
+16 -13
View File
@@ -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()
+10 -15
View File
@@ -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 ..()