|
|
|
@@ -139,8 +139,6 @@
|
|
|
|
|
if(!canbearoused)
|
|
|
|
|
hud_used.arousal.icon_state = ""
|
|
|
|
|
return FALSE
|
|
|
|
|
if(stat == DEAD)
|
|
|
|
|
hud_used.arousal.icon_state = "arousal0"
|
|
|
|
|
else
|
|
|
|
|
var/value = FLOOR(getPercentAroused(), 10)
|
|
|
|
|
hud_used.arousal.icon_state = "arousal[value]"
|
|
|
|
@@ -194,6 +192,7 @@
|
|
|
|
|
if(!G)
|
|
|
|
|
return
|
|
|
|
|
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm)
|
|
|
|
|
setArousalLoss(min_arousal)
|
|
|
|
|
if(!target || !R)
|
|
|
|
|
return
|
|
|
|
|
var/turfing = isturf(target)
|
|
|
|
@@ -217,8 +216,8 @@
|
|
|
|
|
"<span class='userlove'>You start to [G.masturbation_verb] your [G.name].</span>")
|
|
|
|
|
if(!do_after(src, mb_time, target = src) || !G.climaxable(src, TRUE))
|
|
|
|
|
return
|
|
|
|
|
visible_message("<span class='love'>[src] orgasms, [PP.orgasm_verb][isturf(loc) ? " onto [loc]" : ""]!</span>", \
|
|
|
|
|
"<span class='userlove'>You orgasm, [PP.orgasm_verb][isturf(loc) ? " onto [loc]" : ""].</span>")
|
|
|
|
|
visible_message("<span class='love'>[src] orgasms, [PP.orgasm_verb][isturf(loc) ? " onto [loc]" : ""] with [p_their()] [PP.name]!</span>", \
|
|
|
|
|
"<span class='userlove'>You orgasm, [PP.orgasm_verb][isturf(loc) ? " onto [loc]" : ""] with your [PP.name].</span>")
|
|
|
|
|
do_climax(fluid_source, loc, G)
|
|
|
|
|
|
|
|
|
|
/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
|
|
|
|
@@ -268,33 +267,35 @@
|
|
|
|
|
"<span class='userlove'>You used your [G.name] to fill [container].</span>")
|
|
|
|
|
do_climax(fluid_source, container, G, FALSE)
|
|
|
|
|
|
|
|
|
|
/mob/living/carbon/human/proc/pick_masturbate_genitals()
|
|
|
|
|
var/list/genitals_list = list()
|
|
|
|
|
/mob/living/carbon/human/proc/pick_masturbate_genitals(silent = FALSE)
|
|
|
|
|
var/list/genitals_list
|
|
|
|
|
var/list/worn_stuff = get_equipped_items()
|
|
|
|
|
|
|
|
|
|
for(var/obj/item/organ/genital/G in internal_organs)
|
|
|
|
|
if(CHECK_BITFIELD(G.genital_flags, CAN_MASTURBATE_WITH) && G.is_exposed(worn_stuff)) //filter out what you can't masturbate with
|
|
|
|
|
if(CHECK_BITFIELD(G.genital_flags, MASTURBATE_LINKED_ORGAN) && !G.linked_organ)
|
|
|
|
|
continue
|
|
|
|
|
genitals_list += G
|
|
|
|
|
if(genitals_list.len)
|
|
|
|
|
var/obj/item/organ/genital/ret_organ = input(src, "with what?", "Masturbate", null) as null|obj in genitals_list
|
|
|
|
|
LAZYADD(genitals_list, G)
|
|
|
|
|
if(LAZYLEN(genitals_list))
|
|
|
|
|
var/obj/item/organ/genital/ret_organ = input(src, "with what?", "Masturbate", null) as null|obj in genitals_list
|
|
|
|
|
return ret_organ
|
|
|
|
|
else if(!silent)
|
|
|
|
|
to_chat(src, "<span class='warning'>You cannot masturbate without available genitals.</span>")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/mob/living/carbon/human/proc/pick_climax_genitals()
|
|
|
|
|
var/list/genitals_list = list()
|
|
|
|
|
/mob/living/carbon/human/proc/pick_climax_genitals(silent = FALSE)
|
|
|
|
|
var/list/genitals_list
|
|
|
|
|
var/list/worn_stuff = get_equipped_items()
|
|
|
|
|
|
|
|
|
|
for(var/obj/item/organ/genital/G in internal_organs)
|
|
|
|
|
if(CHECK_BITFIELD(G.genital_flags, CAN_CLIMAX_WITH) && G.is_exposed(worn_stuff)) //filter out what you can't masturbate with
|
|
|
|
|
genitals_list += G
|
|
|
|
|
if(genitals_list.len)
|
|
|
|
|
var/obj/item/organ/genital/ret_organ = input(src, "with what?", "Climax", null) as null|obj in genitals_list
|
|
|
|
|
LAZYADD(genitals_list, G)
|
|
|
|
|
if(LAZYLEN(genitals_list))
|
|
|
|
|
var/obj/item/organ/genital/ret_organ = input(src, "with what?", "Climax", null) as null|obj in genitals_list
|
|
|
|
|
return ret_organ
|
|
|
|
|
else if(!silent)
|
|
|
|
|
to_chat(src, "<span class='warning'>You cannot climax without available genitals.</span>")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/mob/living/carbon/human/proc/pick_partner()
|
|
|
|
|
/mob/living/carbon/human/proc/pick_partner(silent = FALSE)
|
|
|
|
|
var/list/partners = list()
|
|
|
|
|
if(pulling)
|
|
|
|
|
partners += pulling
|
|
|
|
@@ -308,30 +309,42 @@
|
|
|
|
|
partners -= C
|
|
|
|
|
//NOW the list should only contain correct partners
|
|
|
|
|
if(!partners.len)
|
|
|
|
|
if(!silent)
|
|
|
|
|
to_chat(src, "<span class='warning'>You cannot do this alone.</span>")
|
|
|
|
|
return //No one left.
|
|
|
|
|
var/mob/living/target = input(src, "With whom?", "Sexual partner", null) as null|anything in partners //pick one, default to null
|
|
|
|
|
if(target && in_range(src, target))
|
|
|
|
|
return target
|
|
|
|
|
|
|
|
|
|
/mob/living/carbon/human/proc/pick_climax_container()
|
|
|
|
|
/mob/living/carbon/human/proc/pick_climax_container(silent = FALSE)
|
|
|
|
|
var/list/containers_list = list()
|
|
|
|
|
|
|
|
|
|
for(var/obj/item/reagent_containers/container in held_items)
|
|
|
|
|
if(container.is_open_container() || istype(container, /obj/item/reagent_containers/food/snacks))
|
|
|
|
|
containers_list += container
|
|
|
|
|
for(var/obj/item/reagent_containers/C in held_items)
|
|
|
|
|
if(C.is_open_container() || istype(C, /obj/item/reagent_containers/food/snacks))
|
|
|
|
|
containers_list += C
|
|
|
|
|
for(var/obj/item/reagent_containers/C in range(1, src))
|
|
|
|
|
if((C.is_open_container() || istype(C, /obj/item/reagent_containers/food/snacks)) && CanReach(C))
|
|
|
|
|
containers_list += C
|
|
|
|
|
|
|
|
|
|
if(containers_list.len)
|
|
|
|
|
var/obj/item/reagent_containers/SC = input(src, "Into or onto what?(Cancel for nowhere)", null) as null|obj in containers_list
|
|
|
|
|
if(SC && in_range(src, SC))
|
|
|
|
|
if(SC && CanReach(SC))
|
|
|
|
|
return SC
|
|
|
|
|
return null //If nothing correct, give null.
|
|
|
|
|
else if(!silent)
|
|
|
|
|
to_chat(src, "<span class='warning'>You cannot do this without an appropriate container.</span>")
|
|
|
|
|
|
|
|
|
|
/mob/living/carbon/human/proc/available_rosie_palms(silent = FALSE)
|
|
|
|
|
/mob/living/carbon/human/proc/available_rosie_palms(silent = FALSE, list/whitelist_typepaths = list(/obj/item/dildo))
|
|
|
|
|
if(restrained(TRUE)) //TRUE ignores grabs
|
|
|
|
|
if(!silent)
|
|
|
|
|
to_chat(src, "<span class='warning'>You can't do that while restrained!</span>")
|
|
|
|
|
return FALSE
|
|
|
|
|
if(!get_num_arms() || !get_empty_held_indexes())
|
|
|
|
|
if(whitelist_typepaths)
|
|
|
|
|
if(!islist(whitelist_typepaths))
|
|
|
|
|
whitelist_typepaths = list(whitelist_typepaths)
|
|
|
|
|
for(var/path in whitelist_typepaths)
|
|
|
|
|
if(is_holding_item_of_type(path))
|
|
|
|
|
return TRUE
|
|
|
|
|
if(!silent)
|
|
|
|
|
to_chat(src, "<span class='warning'>You need at least one free arm.</span>")
|
|
|
|
|
return FALSE
|
|
|
|
@@ -343,93 +356,97 @@
|
|
|
|
|
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 [DisplayTimeText((mb_cd_timer - world.time), TRUE)] before you can do that again!</span>")
|
|
|
|
|
return
|
|
|
|
|
mb_cd_timer = world.time + mb_cd_length
|
|
|
|
|
|
|
|
|
|
if(canbearoused && has_dna())
|
|
|
|
|
if(stat == DEAD)
|
|
|
|
|
if(!forced_climax)
|
|
|
|
|
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(!CHECK_BITFIELD(G.genital_flags, CAN_CLIMAX_WITH)) //Skip things like wombs and testicles
|
|
|
|
|
continue
|
|
|
|
|
var/mob/living/partner
|
|
|
|
|
var/check_target
|
|
|
|
|
var/list/worn_stuff = get_equipped_items()
|
|
|
|
|
if(!canbearoused || !has_dna())
|
|
|
|
|
return
|
|
|
|
|
if(stat == DEAD)
|
|
|
|
|
if(!forced_climax)
|
|
|
|
|
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(!CHECK_BITFIELD(G.genital_flags, CAN_CLIMAX_WITH)) //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(pulling) //Are we pulling someone? Priority target, we can't be making option menus for this, has to be quick
|
|
|
|
|
if(isliving(pulling)) //Don't fuck objects
|
|
|
|
|
check_target = pulling
|
|
|
|
|
if(pulledby && !check_target) //prioritise pulled over pulledby
|
|
|
|
|
if(isliving(pulledby))
|
|
|
|
|
check_target = 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(G.is_exposed(worn_stuff))
|
|
|
|
|
if(pulling) //Are we pulling someone? Priority target, we can't be making option menus for this, has to be quick
|
|
|
|
|
if(isliving(pulling)) //Don't fuck objects
|
|
|
|
|
check_target = pulling
|
|
|
|
|
if(pulledby && !check_target) //prioritise pulled over pulledby
|
|
|
|
|
if(isliving(pulledby))
|
|
|
|
|
check_target = 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
|
|
|
|
|
mb_cd_timer = world.time + mb_cd_length
|
|
|
|
|
return
|
|
|
|
|
//If we get here, then this is not a forced climax and we gotta check a few things.
|
|
|
|
|
|
|
|
|
|
if(stat == UNCONSCIOUS) //No sleep-masturbation, you're unconscious.
|
|
|
|
|
to_chat(src, "<span class='warning'>You must be conscious to do that!</span>")
|
|
|
|
|
return
|
|
|
|
|
if(getArousalLoss() < 33) //flat number instead of percentage
|
|
|
|
|
to_chat(src, "<span class='warning'>You aren't aroused enough for that!</span>")
|
|
|
|
|
return
|
|
|
|
|
if(stat == UNCONSCIOUS) //No sleep-masturbation, you're unconscious.
|
|
|
|
|
to_chat(src, "<span class='warning'>You must be conscious to do that!</span>")
|
|
|
|
|
return
|
|
|
|
|
if(getArousalLoss() < 33) //flat number instead of percentage
|
|
|
|
|
to_chat(src, "<span class='warning'>You aren't aroused enough for that!</span>")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
//Ok, now we check what they want to do.
|
|
|
|
|
var/choice = input(src, "Select sexual activity", "Sexual activity:") as null|anything in list("Masturbate", "Climax alone", "Climax with partner", "Fill container")
|
|
|
|
|
//Ok, now we check what they want to do.
|
|
|
|
|
var/choice = input(src, "Select sexual activity", "Sexual activity:") as null|anything in list("Masturbate", "Climax alone", "Climax with partner", "Fill container")
|
|
|
|
|
if(!choice)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
switch(choice)
|
|
|
|
|
if("Masturbate")
|
|
|
|
|
if(!available_rosie_palms())
|
|
|
|
|
return
|
|
|
|
|
//We got hands, let's pick an organ
|
|
|
|
|
var/obj/item/organ/genital/picked_organ = pick_masturbate_genitals()
|
|
|
|
|
if(picked_organ && available_rosie_palms(TRUE))
|
|
|
|
|
mob_masturbate(picked_organ)
|
|
|
|
|
return
|
|
|
|
|
switch(choice)
|
|
|
|
|
if("Masturbate")
|
|
|
|
|
if(!available_rosie_palms())
|
|
|
|
|
return
|
|
|
|
|
//We got hands, let's pick an organ
|
|
|
|
|
var/obj/item/organ/genital/picked_organ = pick_masturbate_genitals()
|
|
|
|
|
if(picked_organ && available_rosie_palms(TRUE))
|
|
|
|
|
mob_masturbate(picked_organ)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
if("Climax alone")
|
|
|
|
|
if(!available_rosie_palms())
|
|
|
|
|
return
|
|
|
|
|
var/obj/item/organ/genital/picked_organ = pick_climax_genitals()
|
|
|
|
|
if(picked_organ && available_rosie_palms(TRUE))
|
|
|
|
|
mob_climax_outside(picked_organ)
|
|
|
|
|
if("Climax alone")
|
|
|
|
|
if(!available_rosie_palms())
|
|
|
|
|
return
|
|
|
|
|
var/obj/item/organ/genital/picked_organ = pick_climax_genitals()
|
|
|
|
|
if(picked_organ && available_rosie_palms(TRUE))
|
|
|
|
|
mob_climax_outside(picked_organ)
|
|
|
|
|
|
|
|
|
|
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 = 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 null|anything in list("Yes", "No")
|
|
|
|
|
if(spillage && in_range(src, partner))
|
|
|
|
|
mob_climax_partner(picked_organ, partner, spillage == "Yes" ? TRUE : FALSE)
|
|
|
|
|
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 = 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 null|anything in list("Yes", "No")
|
|
|
|
|
if(spillage && in_range(src, partner))
|
|
|
|
|
mob_climax_partner(picked_organ, partner, spillage == "Yes" ? TRUE : FALSE)
|
|
|
|
|
|
|
|
|
|
if("Fill container")
|
|
|
|
|
//We'll need hands and no restraints.
|
|
|
|
|
if(!available_rosie_palms())
|
|
|
|
|
return
|
|
|
|
|
//We got hands, let's pick an organ
|
|
|
|
|
var/obj/item/organ/genital/picked_organ
|
|
|
|
|
picked_organ = pick_climax_genitals() //Gotta be climaxable, not just masturbation, to fill with fluids.
|
|
|
|
|
if(picked_organ)
|
|
|
|
|
//Good, got an organ, time to pick a container
|
|
|
|
|
var/obj/item/reagent_containers/fluid_container = pick_climax_container()
|
|
|
|
|
if(fluid_container && available_rosie_palms(TRUE))
|
|
|
|
|
mob_fill_container(picked_organ, fluid_container)
|
|
|
|
|
return
|
|
|
|
|
if("Fill container")
|
|
|
|
|
//We'll need hands and no restraints.
|
|
|
|
|
if(!available_rosie_palms(FALSE, /obj/item/reagent_containers))
|
|
|
|
|
return
|
|
|
|
|
//We got hands, let's pick an organ
|
|
|
|
|
var/obj/item/organ/genital/picked_organ
|
|
|
|
|
picked_organ = pick_climax_genitals() //Gotta be climaxable, not just masturbation, to fill with fluids.
|
|
|
|
|
if(picked_organ)
|
|
|
|
|
//Good, got an organ, time to pick a container
|
|
|
|
|
var/obj/item/reagent_containers/fluid_container = pick_climax_container()
|
|
|
|
|
if(fluid_container && available_rosie_palms(TRUE, /obj/item/reagent_containers))
|
|
|
|
|
mob_fill_container(picked_organ, fluid_container)
|
|
|
|
|
|
|
|
|
|
mb_cd_timer = world.time + mb_cd_length
|