diff --git a/code/__DEFINES/DNA.dm b/code/__DEFINES/DNA.dm index 4ed099d9e2..95c2c70801 100644 --- a/code/__DEFINES/DNA.dm +++ b/code/__DEFINES/DNA.dm @@ -122,3 +122,8 @@ #define ORGAN_SLOT_BRAIN_ANTISTUN "brain_antistun" #define ORGAN_SLOT_TAIL "tail" #define ORGAN_SLOT_PENIS "penis" +#define ORGAN_SLOT_WOMB "womb" +#define ORGAN_SLOT_VAGINA "vagina" +#define ORGAN_SLOT_TESTICLES "testicles" +#define ORGAN_SLOT_BREASTS "breasts" + diff --git a/code/__DEFINES/citadel_defines.dm b/code/__DEFINES/citadel_defines.dm index 2abe0db04e..8b75784ab2 100644 --- a/code/__DEFINES/citadel_defines.dm +++ b/code/__DEFINES/citadel_defines.dm @@ -18,6 +18,15 @@ #define CIT_FILTER_STAMINACRIT filter(type="drop_shadow", x=0, y=0, size=-3, border=0, color="#04080F") //organ defines +#define VAGINA_LAYER_INDEX 1 +#define TESTICLES_LAYER_INDEX 2 +#define GENITAL_LAYER_INDEX 3 +#define PENIS_LAYER_INDEX 4 + +#define GENITAL_LAYER_INDEX_LENGTH 4 //keep it updated with each new index added, thanks. + +#define MASTURBATE_LINKED_ORGAN 2 //special value used to pass our mission to the linked organ + #define COCK_SIZE_MIN 1 #define COCK_SIZE_MAX 20 diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 7bcb4881f0..1546717c7a 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -136,13 +136,14 @@ #define TRAIT_SKITTISH "skittish" #define TRAIT_POOR_AIM "poor_aim" #define TRAIT_PROSOPAGNOSIA "prosopagnosia" -#define TRAIT_DRUNK_HEALING "drunk_healing" -#define TRAIT_TAGGER "tagger" -#define TRAIT_PHOTOGRAPHER "photographer" -#define TRAIT_MUSICIAN "musician" -#define TRAIT_CROCRIN_IMMUNE "crocin_immune" +#define TRAIT_DRUNK_HEALING "drunk_healing" +#define TRAIT_TAGGER "tagger" +#define TRAIT_PHOTOGRAPHER "photographer" +#define TRAIT_MUSICIAN "musician" +#define TRAIT_CROCRIN_IMMUNE "crocin_immune" #define TRAIT_NYMPHO "nymphomania" #define TRAIT_MASO "masochism" +#define TRAIT_EXHIBITIONIST "exhibitionist" #define TRAIT_PARA "paraplegic" #define TRAIT_EMPATH "empath" #define TRAIT_FRIENDLY "friendly" diff --git a/code/__HELPERS/_cit_helpers.dm b/code/__HELPERS/_cit_helpers.dm index 668b151b6e..d75dd31b46 100644 --- a/code/__HELPERS/_cit_helpers.dm +++ b/code/__HELPERS/_cit_helpers.dm @@ -56,6 +56,7 @@ GLOBAL_LIST_EMPTY(ipc_screens_list) GLOBAL_LIST_EMPTY(ipc_antennas_list) //Genitals and Arousal Lists +GLOBAL_LIST_EMPTY(genitals_list) GLOBAL_LIST_EMPTY(cock_shapes_list)//global_lists.dm for the list initializations //Now also _DATASTRUCTURES globals.dm GLOBAL_LIST_EMPTY(cock_shapes_icons) //Associated list for names->icon_states for cockshapes. GLOBAL_LIST_EMPTY(balls_shapes_list) @@ -124,41 +125,39 @@ GLOBAL_VAR_INIT(miscreants_allowed, FALSE) SSblackbox.record_feedback("tally", "admin_verb", 1, "TLOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /mob/living/carbon/proc/has_penis() - if(getorganslot("penis"))//slot shared with ovipositor - if(istype(getorganslot("penis"), /obj/item/organ/genital/penis)) - return TRUE + var/obj/item/organ/genital/G = getorganslot(ORGAN_SLOT_PENIS) + if(G && istype(G, /obj/item/organ/genital/penis)) + return TRUE return FALSE /mob/living/carbon/proc/has_balls() - if(getorganslot("balls")) - if(istype(getorganslot("balls"), /obj/item/organ/genital/testicles)) - return TRUE + var/obj/item/organ/genital/G = getorganslot(ORGAN_SLOT_TESTICLES) + if(G && istype(G, /obj/item/organ/genital/testicles)) + return TRUE return FALSE /mob/living/carbon/proc/has_vagina() - if(getorganslot("vagina")) + if(getorganslot(ORGAN_SLOT_VAGINA)) return TRUE return FALSE /mob/living/carbon/proc/has_breasts() - if(getorganslot("breasts")) + if(getorganslot(ORGAN_SLOT_BREASTS)) return TRUE return FALSE /mob/living/carbon/proc/has_ovipositor() - if(getorganslot("penis"))//shared slot - if(istype(getorganslot("penis"), /obj/item/organ/genital/ovipositor)) - return TRUE + var/obj/item/organ/genital/G = getorganslot(ORGAN_SLOT_PENIS) + if(G && istype(G, /obj/item/organ/genital/ovipositor)) + return TRUE return FALSE /mob/living/carbon/human/proc/has_eggsack() - if(getorganslot("balls")) - if(istype(getorganslot("balls"), /obj/item/organ/genital/eggsack)) - return TRUE + var/obj/item/organ/genital/G = getorganslot(ORGAN_SLOT_TESTICLES) + if(G && istype(G, /obj/item/organ/genital/eggsack)) + return TRUE return FALSE -/mob/living/carbon/human/proc/is_bodypart_exposed(bodypart) - /mob/living/carbon/proc/is_groin_exposed(var/list/L) if(!L) L = get_equipped_items() @@ -188,9 +187,9 @@ GLOBAL_VAR_INIT(miscreants_allowed, FALSE) message_admins("[src] gave everyone genitals.") for(var/mob/living/carbon/human/H in GLOB.mob_list) if(H.gender == MALE) - H.give_penis() - H.give_balls() + H.give_genital(/obj/item/organ/genital/penis) + H.give_genital(/obj/item/organ/genital/testicles) else - H.give_vagina() - H.give_womb() - H.give_breasts() + H.give_genital(/obj/item/organ/genital/vagina) + H.give_genital(/obj/item/organ/genital/womb) + H.give_genital(/obj/item/organ/genital/breasts) diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm index c8a33959ed..b0b0f32345 100644 --- a/code/__HELPERS/global_lists.dm +++ b/code/__HELPERS/global_lists.dm @@ -61,6 +61,10 @@ for(var/K in GLOB.balls_shapes_list) var/datum/sprite_accessory/testicles/value = GLOB.balls_shapes_list[K] GLOB.balls_shapes_icons[K] = value.icon_state + + for(var/gpath in subtypesof(/obj/item/organ/genital)) + var/obj/item/organ/genital/G = gpath + GLOB.genitals_list[initial(G.name)] = gpath //END OF CIT CHANGES //Species diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index 5ec839130a..81801cd6a2 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -154,7 +154,6 @@ "xenodorsal" = "Standard", "xenohead" = "Standard", "xenotail" = "Xenomorph Tail", - "exhibitionist" = FALSE, "genitals_use_skintone" = FALSE, "has_cock" = FALSE, "cock_shape" = pick(GLOB.cock_shapes_list), diff --git a/code/datums/traits/neutral.dm b/code/datums/traits/neutral.dm index f70e3a3c68..156ee00d75 100644 --- a/code/datums/traits/neutral.dm +++ b/code/datums/traits/neutral.dm @@ -94,6 +94,41 @@ lose_text = "You don't feel as prudish as before." medical_record_text = "Patient exhibits a special gene that makes them immune to Crocin and Hexacrocin." +/datum/quirk/libido + name = "Nymphomania" + desc = "You're always feeling a bit in heat. Also, you get aroused faster than usual." + value = 0 + mob_trait = TRAIT_NYMPHO + gain_text = "You are feeling extra wild." + lose_text = "You don't feel that burning sensation anymore." + +/datum/quirk/libido/add() + var/mob/living/M = quirk_holder + M.min_arousal = 16 + M.arousal_rate = 3 + +/datum/quirk/libido/remove() + var/mob/living/M = quirk_holder + M.min_arousal = initial(M.min_arousal) + M.arousal_rate = initial(M.arousal_rate) + +/datum/quirk/maso + name = "Masochism" + desc = "You are aroused by pain." + value = 0 + mob_trait = TRAIT_MASO + gain_text = "You desire to be hurt." + lose_text = "Pain has become less exciting for you." + +/datum/quirk/exhibitionism + name = "Exhibitionism" + desc = "You don't mind showing off your bare body to strangers, in fact you find it quite satistying. Not the best excuse to break Space Law anyway." //as if they'd care anyway. + value = 0 + medical_record_text = "Patient has been diagnosed with exhibitionistic disorder." + mob_trait = TRAIT_EXHIBITIONIST + gain_text = "You feel like exposing yourself to the world." + lose_text = "Indecent exposure doesn't sound as charming to you anymore." + /datum/quirk/assblastusa name = "Buns of Steel" desc = "You've never skipped ass day. With this trait, you are completely immune to all forms of ass slapping and anyone who tries to slap your rock hard ass usually gets a broken hand." diff --git a/code/modules/admin/create_mob.dm b/code/modules/admin/create_mob.dm index fad7410a6a..eb856237bf 100644 --- a/code/modules/admin/create_mob.dm +++ b/code/modules/admin/create_mob.dm @@ -25,6 +25,9 @@ H.facial_hair_color = H.hair_color H.eye_color = random_eye_color() H.dna.blood_type = random_blood_type() + H.saved_underwear = H.underwear + H.saved_undershirt = H.undershirt + H.saved_socks = H.socks // Mutant randomizing, doesn't affect the mob appearance unless it's the specific mutant. H.dna.features["mcolor"] = random_short_color() diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 54d75e8437..bac9ed0c89 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -108,7 +108,6 @@ GLOBAL_LIST_EMPTY(preferences_datums) "xenohead" = "Standard", "xenotail" = "Xenomorph Tail", "taur" = "None", - "exhibitionist" = FALSE, "genitals_use_skintone" = FALSE, "has_cock" = FALSE, "cock_shape" = "Human", @@ -790,7 +789,6 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat +="" dat += "

Citadel Preferences

" //Because fuck me if preferences can't be fucking modularized and expected to update in a reasonable timeframe. dat += "Arousal:[arousable == TRUE ? "Enabled" : "Disabled"]
" - dat += "Exhibitionist:[features["exhibitionist"] == TRUE ? "Yes" : "No"]
" dat += "Voracious MediHound sleepers: [(cit_toggles & MEDIHOUND_SLEEPER) ? "Yes" : "No"]
" dat += "Hear Vore Sounds: [(cit_toggles & EATING_NOISES) ? "Yes" : "No"]
" dat += "Hear Vore Digestion Sounds: [(cit_toggles & DIGESTION_NOISES) ? "Yes" : "No"]
" @@ -2048,8 +2046,6 @@ GLOBAL_LIST_EMPTY(preferences_datums) features["has_womb"] = FALSE if("has_womb") features["has_womb"] = !features["has_womb"] - if("exhibitionist") - features["exhibitionist"] = !features["exhibitionist"] if("widescreenpref") widescreenpref = !widescreenpref user.client.change_view(CONFIG_GET(string/default_view)) diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index b9c5cb7ef9..175cd9613a 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -5,7 +5,7 @@ // You do not need to raise this if you are adding new values that have sane defaults. // Only raise this value when changing the meaning/format/name/layout of an existing value // where you would want the updater procs below to run -#define SAVEFILE_VERSION_MAX 20 +#define SAVEFILE_VERSION_MAX 21 /* SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Carn @@ -49,6 +49,11 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car pda_style = "mono" if(current_version < 20) pda_color = "#808000" + if(current_version < 21 && S["feature_exhibitionist"]) + var/datum/quirk/exhibitionism/E + var/quirk_name = initial(E.name) + neutral_quirks += quirk_name + all_quirks += quirk_name /datum/preferences/proc/load_path(ckey,filename="preferences.sav") if(!ckey) diff --git a/modular_citadel/code/datums/traits/neutral.dm b/modular_citadel/code/datums/traits/neutral.dm deleted file mode 100644 index 05aeb27361..0000000000 --- a/modular_citadel/code/datums/traits/neutral.dm +++ /dev/null @@ -1,33 +0,0 @@ -// Citadel-specific Neutral Traits - -/datum/quirk/libido - name = "Nymphomania" - desc = "You're always feeling a bit in heat. Also, you get aroused faster than usual." - value = 0 - mob_trait = TRAIT_NYMPHO - gain_text = "You are feeling extra wild." - lose_text = "You don't feel that burning sensation anymore." - -/datum/quirk/libido/add() - var/mob/living/M = quirk_holder - M.min_arousal = 16 - M.arousal_rate = 3 - -/datum/quirk/libido/remove() - var/mob/living/M = quirk_holder - M.min_arousal = initial(M.min_arousal) - M.arousal_rate = initial(M.arousal_rate) - -/datum/quirk/libido/on_process() - var/mob/living/M = quirk_holder - if(M.canbearoused == FALSE) - to_chat(quirk_holder, "Having high libido is useless when you can't feel arousal at all!") - qdel(src) - -/datum/quirk/maso - name = "Masochism" - desc = "You are aroused by pain." - value = 0 - mob_trait = TRAIT_MASO - gain_text = "You desire to be hurt." - lose_text = "Pain has become less exciting for you." diff --git a/modular_citadel/code/modules/arousal/arousal.dm b/modular_citadel/code/modules/arousal/arousal.dm index 27f7576e7f..8826cb0774 100644 --- a/modular_citadel/code/modules/arousal/arousal.dm +++ b/modular_citadel/code/modules/arousal/arousal.dm @@ -19,11 +19,6 @@ var/hidden_undershirt = FALSE var/hidden_socks = FALSE -/mob/living/carbon/human/New() - ..() - saved_underwear = underwear - saved_undershirt = undershirt - //Species vars /datum/species var/arousal_gain_rate = AROUSAL_START_VALUE //Rate at which this species becomes aroused @@ -35,60 +30,53 @@ //Mob procs /mob/living/carbon/human/proc/underwear_toggle() set name = "Toggle undergarments" - set category = "Object" - if(ishuman(src)) - var/mob/living/carbon/human/humz = src - var/confirm = input(src, "Select what part of your form to alter", "Undergarment Toggling", "Cancel") in list("Top", "Bottom", "Socks", "All", "Cancel") - if(confirm == "Top") - humz.hidden_undershirt = !humz.hidden_undershirt + set category = "IC" - if(confirm == "Bottom") - humz.hidden_underwear = !humz.hidden_underwear + var/confirm = input(src, "Select what part of your form to alter", "Undergarment Toggling") as null|anything in list("Top", "Bottom", "Socks", "All") + if(!confirm) + return + if(confirm == "Top") + hidden_undershirt = !hidden_undershirt - if(confirm == "Socks") - humz.hidden_socks = !humz.hidden_socks + if(confirm == "Bottom") + hidden_underwear = !hidden_underwear - if(confirm == "All") - humz.hidden_undershirt = !humz.hidden_undershirt - humz.hidden_underwear = !humz.hidden_underwear - humz.hidden_socks = !humz.hidden_socks + if(confirm == "Socks") + hidden_socks = !hidden_socks - if(confirm == "Cancel") - return - src.update_body() + if(confirm == "All") + var/on_off = (hidden_undershirt || hidden_underwear || hidden_socks) ? FALSE : TRUE + hidden_undershirt = on_off + hidden_underwear = on_off + hidden_socks = on_off - else - to_chat(src, "Humans only. How the fuck did you get this verb anyway.") + update_body() -/mob/living/proc/handle_arousal() - - -/mob/living/carbon/handle_arousal() - if(canbearoused && dna) - var/datum/species/S - S = dna.species - if(S && !(SSmobs.times_fired % 36) && getArousalLoss() < max_arousal)//Totally stolen from breathing code. Do this every 36 ticks. - adjustArousalLoss(arousal_rate * S.arousal_gain_rate) - if(dna.features["exhibitionist"] && client) - var/amt_nude = 0 - if(is_chest_exposed() && (getorganslot("breasts"))) - amt_nude++ - if(is_groin_exposed()) - if(getorganslot("penis")) - amt_nude++ - if(getorganslot("vagina")) - amt_nude++ - if(amt_nude) - var/watchers = 0 - for(var/mob/_M in view(world.view, src)) - var/mob/living/M = _M - if(!istype(M)) - continue - if(M.client && !M.stat && !M.eye_blind && (locate(src) in viewers(world.view,M))) - watchers++ - if(watchers) - adjustArousalLoss((amt_nude * watchers) + S.arousal_gain_rate) +/mob/living/proc/handle_arousal(times_fired) + return +/mob/living/carbon/handle_arousal(times_fired) + if(!canbearoused || !dna) + return + var/datum/species/S = dna.species + if(!S || (times_fired % 36) || !getArousalLoss() >= max_arousal)//Totally stolen from breathing code. Do this every 36 ticks. + return + var/our_loss = arousal_rate * S.arousal_gain_rate + if(HAS_TRAIT(src, TRAIT_EXHIBITIONIST) && client) + var/amt_nude = 0 + for(var/obj/item/organ/genital/G in internal_organs) + if(G.is_exposed()) + amt_nude++ + if(amt_nude) + var/watchers = 0 + for(var/mob/living/L in view(world.view, src)) + if(!istype(L)) + continue + if(L.client && !L.stat && !L.eye_blind && (locate(src) in viewers(world.view, L))) + watchers++ + if(watchers) + our_loss += (amt_nude * watchers) + S.arousal_gain_rate + adjustArousalLoss(our_loss) /mob/living/proc/getArousalLoss() return arousalloss @@ -138,8 +126,6 @@ S = GLOB.breasts_shapes_list[G.shape] if(S?.alt_aroused) G.aroused_state = isPercentAroused(G.aroused_amount) - if(getArousalLoss() >= ((max_arousal / 100) * 33)) - G.aroused_state = TRUE else G.aroused_state = FALSE G.update_appearance() @@ -147,54 +133,18 @@ /mob/living/proc/update_arousal_hud() return FALSE -/datum/species/proc/update_arousal_hud(mob/living/carbon/human/H) - return FALSE - /mob/living/carbon/human/update_arousal_hud() - if(!client || !hud_used) - return FALSE - if(dna.species.update_arousal_hud()) + if(!client || !(hud_used?.arousal)) return FALSE if(!canbearoused) hud_used.arousal.icon_state = "" return FALSE + if(stat == DEAD) + hud_used.arousal.icon_state = "arousal0" else - if(hud_used.arousal) - if(stat == DEAD) - hud_used.arousal.icon_state = "arousal0" - return TRUE - if(getArousalLoss() == max_arousal) - hud_used.arousal.icon_state = "arousal100" - return TRUE - if(getArousalLoss() >= (max_arousal / 100) * 90)//M O D U L A R , W O W - hud_used.arousal.icon_state = "arousal90" - return TRUE - if(getArousalLoss() >= (max_arousal / 100) * 80)//M O D U L A R , W O W - hud_used.arousal.icon_state = "arousal80" - return TRUE - if(getArousalLoss() >= (max_arousal / 100) * 70)//M O D U L A R , W O W - hud_used.arousal.icon_state = "arousal70" - return TRUE - if(getArousalLoss() >= (max_arousal / 100) * 60)//M O D U L A R , W O W - hud_used.arousal.icon_state = "arousal60" - return TRUE - if(getArousalLoss() >= (max_arousal / 100) * 50)//M O D U L A R , W O W - hud_used.arousal.icon_state = "arousal50" - return TRUE - if(getArousalLoss() >= (max_arousal / 100) * 40)//M O D U L A R , W O W - hud_used.arousal.icon_state = "arousal40" - return TRUE - if(getArousalLoss() >= (max_arousal / 100) * 30)//M O D U L A R , W O W - hud_used.arousal.icon_state = "arousal30" - return TRUE - if(getArousalLoss() >= (max_arousal / 100) * 20)//M O D U L A R , W O W - hud_used.arousal.icon_state = "arousal10" - return TRUE - if(getArousalLoss() >= (max_arousal / 100) * 10)//M O D U L A R , W O W - hud_used.arousal.icon_state = "arousal10" - return TRUE - else - hud_used.arousal.icon_state = "arousal0" + var/value = FLOOR(getPercentAroused(), 10) + hud_used.arousal.icon_state = "arousal[value]" + return TRUE /obj/screen/arousal name = "arousal" @@ -213,7 +163,6 @@ to_chat(M, "Arousal is disabled. Feature is unavailable.") - /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" @@ -221,212 +170,150 @@ if(mb_cd_timer <= world.time) //start the cooldown even if it fails mb_cd_timer = world.time + mb_cd_length - if(getArousalLoss() >= ((max_arousal / 100) * 33))//33% arousal or greater required - src.visible_message("[src] starts masturbating!", \ + if(getArousalLoss() >= 33)//one third of average max_arousal or greater required + visible_message("[src] starts masturbating!", \ "You start masturbating.") if(do_after(src, 30, target = src)) - src.visible_message("[src] relieves [p_them()]self!", \ + visible_message("[src] relieves [p_them()]self!", \ "You have relieved yourself.") SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) setArousalLoss(min_arousal) else to_chat(src, "You aren't aroused enough for that.") +/obj/item/organ/genital/proc/climaxable(mob/living/carbon/human/H, silent = FALSE) //returns the fluid source (ergo reagents holder) if found. + if(producing) + . = reagents + else + if(linked_organ) + . = linked_organ.reagents + else if(!silent) + to_chat(H, "Your [name] is unable to produce it's own fluids, it's missing the organs for it.") + +/mob/living/carbon/human/proc/do_climax(datum/reagents/R, atom/target, obj/item/organ/genital/G, spill = TRUE) + if(!G) + return + SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) + if(!target || !R) + return + var/turfing = isturf(target) + if(spill & R.total_volume >= 5) + R.reaction(turfing ? target : target.loc, TOUCH, 1, 0) + if(!turfing) + R.trans_to(target, R.total_volume * (spill ? G.fluid_transfer_factor : 1)) + R.clear_reagents() //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 + var/datum/reagents/fluid_source = G.climaxable(src) + if(!fluid_source) + return + var/obj/item/organ/genital/PP = G.can_masturbate_with == MASTURBATE_LINKED_ORGAN ? G.linked_organ : G + if(!PP) + to_chat(src, "You shudder, unable to cum with your [name].") 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].", \ + visible_message("[src] starts to [G.masturbation_verb] [p_their()] [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.") - SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) - if(G.can_climax) - setArousalLoss(min_arousal) - + if(!do_after(src, mb_time, target = src) || !G.climaxable(src, TRUE)) + return + visible_message("[src] orgasms, [PP.orgasm_verb][isturf(loc) ? " onto [loc]" : ""]!", \ + "You orgasm, [PP.orgasm_verb][isturf(loc) ? " onto [loc]" : ""].") + 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 - 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.", \ + var/datum/reagents/fluid_source = G.climaxable(src, TRUE) + if(!fluid_source) + visible_message("[src] shudders, their [G.name] unable to cum.", \ "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].") - SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) - if(G.can_climax) - setArousalLoss(min_arousal) - + return + if(mb_time) //as long as it's not instant, give a warning + visible_message("[src] looks like they're about to cum.", \ + "You feel yourself about to orgasm.") + if(!do_after(src, mb_time, target = src) || !G.climaxable(src, TRUE)) + return + visible_message("[src] orgasms[isturf(loc) ? " onto [loc]" : ""], using [p_their()] [G.name]!", \ + "You climax[isturf(loc) ? " onto [loc]" : ""] with your [G.name].") + do_climax(fluid_source, loc, G) /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 + var/datum/reagents/fluid_source = G.climaxable(src) + if(!fluid_source) + return 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!") + visible_message("[src] is about to climax with [L]!", \ + "You're about to climax with [L]!") + if(!do_after(src, mb_time, target = src) || !in_range(src, L) || !G.climaxable(src, TRUE)) + return 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].") - SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) - SEND_SIGNAL(L, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) - if(G.can_climax) - setArousalLoss(min_arousal) + visible_message("[src] climaxes with [L], overflowing and spilling, using [p_their()] [G.name]!", \ + "You orgasm with [L], spilling out of them, using your [G.name].") 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.") - SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) - SEND_SIGNAL(L, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) - if(G.can_climax) - setArousalLoss(min_arousal) + visible_message("[src] climaxes with [L], [p_their()] [G.name] spilling nothing!", \ + "You ejaculate with [L], your [G.name] spilling nothing.") + SEND_SIGNAL(L, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) + do_climax(fluid_source, spillage ? loc : L, G, spillage) /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.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.") + var/datum/reagents/fluid_source = G.climaxable(src) + if(!fluid_source) + return + if(mb_time) + visible_message("[src] starts to [G.masturbation_verb] their [G.name] over [container].", \ + "You start to [G.masturbation_verb] your [G.name] over [container].") + if(!do_after(src, mb_time, target = src) || !in_range(src, container) || !G.climaxable(src, TRUE)) 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.") - SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm) - if(G.can_climax) - setArousalLoss(min_arousal) + visible_message("[src] uses [p_their()] [G.name] to fill [container]!", \ + "You used your [G.name] to fill [container].") + do_climax(fluid_source, container, G, FALSE) /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(G.can_masturbate_with && G.is_exposed(worn_stuff)) //filter out what you can't masturbate with + if(G.can_masturbate_with == MASTURBATE_LINKED_ORGAN && !G.linked_organ) + continue + genitals_list += G if(genitals_list.len) - ret_organ = input(src, "with what?", "Masturbate", null) as null|obj in genitals_list + var/obj/item/organ/genital/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(G.can_climax && G.is_exposed(worn_stuff)) //filter out what you can't masturbate with + genitals_list += G if(genitals_list.len) - ret_organ = input(src, "with what?", "Climax", null) as null|obj in genitals_list + var/obj/item/organ/genital/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 + if(pulling) + partners += pulling + if(pulledby) + partners += 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 + for(var/mob/living/L in partners) + if(iscarbon(L)) + var/mob/living/carbon/C = L + if(!C.exposed_genitals.len && !C.is_groin_exposed() && !C.is_chest_exposed()) //Nothing through_clothing, no proper partner. + partners -= C //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 + 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() - var/obj/item/reagent_containers/SC = null var/list/containers_list = list() for(var/obj/item/reagent_containers/container in held_items) @@ -434,12 +321,21 @@ 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 + 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)) + return SC return null //If nothing correct, give null. +/mob/living/carbon/human/proc/available_rosie_palms(silent = FALSE) + if(restrained(TRUE)) //TRUE ignores grabs + if(!silent) + to_chat(src, "You can't do that while restrained!") + return FALSE + if(!get_num_arms() || !get_empty_held_indexes()) + if(!silent) + to_chat(src, "You need at least one free arm.") + return FALSE + return TRUE //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 @@ -447,49 +343,47 @@ if(!forced_climax) //Don't spam the message to the victim if forced to come too fast to_chat(src, "You need to wait [DisplayTimeText((mb_cd_timer - world.time), TRUE)] before you can do that again!") return - mb_cd_timer = (world.time + mb_cd_length) - + mb_cd_timer = world.time + mb_cd_length if(canbearoused && has_dna()) - if(stat==2) - to_chat(src, "You can't do that while dead!") + if(stat == DEAD) + if(!forced_climax) + to_chat(src, "You can't do that while dead!") return if(forced_climax) //Something forced us to cum, this is not a masturbation thing and does not progress to the other checks - for(var/obj/item/organ/O in internal_organs) - if(istype(O, /obj/item/organ/genital)) - var/obj/item/organ/genital/G = O - if(!G.can_climax) //Skip things like wombs and testicles - continue - var/mob/living/partner - var/check_target - var/list/worn_stuff = get_equipped_items() + for(var/obj/item/organ/genital/G in internal_organs) + 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 + 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(stat==1) //No sleep-masturbation, you're unconscious. + if(stat == UNCONSCIOUS) //No sleep-masturbation, you're unconscious. to_chat(src, "You must be conscious to do that!") return if(getArousalLoss() < 33) //flat number instead of percentage @@ -497,91 +391,38 @@ return //Ok, now we check what they want to do. - var/choice = input(src, "Select sexual activity", "Sexual activity:") in list("Masturbate", "Climax alone", "Climax with partner", "Fill container") + var/choice = input(src, "Select sexual activity", "Sexual activity:") as null|anything in list("Masturbate", "Climax alone", "Climax with partner", "Fill container") 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.") + if(!available_rosie_palms()) return //We got hands, let's pick an organ - var/obj/item/organ/genital/picked_organ - picked_organ = pick_masturbate_genitals() - if(picked_organ) + var/obj/item/organ/genital/picked_organ = pick_masturbate_genitals() + if(picked_organ && available_rosie_palms(TRUE)) mob_masturbate(picked_organ) return - else //They either lack organs that can masturbate, or they didn't pick one. - to_chat(src, "You cannot masturbate without choosing genitals.") - return if("Climax alone") - if(restrained(TRUE)) //TRUE ignores grabs - to_chat(src, "You can't do that while restrained!") + if(!available_rosie_palms()) 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) + var/obj/item/organ/genital/picked_organ = pick_climax_genitals() + if(picked_organ && available_rosie_palms(TRUE)) mob_climax_outside(picked_organ) - return - else //They either lack organs that can masturbate, or they didn't pick one. - to_chat(src, "You cannot climax without choosing genitals.") - return if("Climax 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() + 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 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 + 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(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.") + if(!available_rosie_palms()) return //We got hands, let's pick an organ var/obj/item/organ/genital/picked_organ @@ -589,14 +430,6 @@ 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) + if(fluid_container && available_rosie_palms(TRUE)) 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. + return \ No newline at end of file diff --git a/modular_citadel/code/modules/arousal/organs/genitals.dm b/modular_citadel/code/modules/arousal/organs/_genitals.dm similarity index 60% rename from modular_citadel/code/modules/arousal/organs/genitals.dm rename to modular_citadel/code/modules/arousal/organs/_genitals.dm index 63d6834409..2222624050 100644 --- a/modular_citadel/code/modules/arousal/organs/genitals.dm +++ b/modular_citadel/code/modules/arousal/organs/_genitals.dm @@ -6,8 +6,9 @@ var/list/genital_flags = list() var/can_masturbate_with = FALSE var/masturbation_verb = "masturbate" + var/orgasm_verb = "cumming" //present continous var/can_climax = FALSE - var/fluid_transfer_factor = 0.0 //How much would a partner get in them if they climax using this? + var/fluid_transfer_factor = 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 @@ -18,48 +19,49 @@ 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/linked_organ_slot //only one of the two organs needs this to be set up. update_link() will handle linking the rest. var/through_clothes = FALSE var/internal = FALSE var/hidden = FALSE + var/layer_index = GENITAL_LAYER_INDEX //Order should be very important. FIRST vagina, THEN testicles, THEN penis, as this affects the order they are rendered in. /obj/item/organ/genital/Initialize() . = ..() - if(!reagents) + if(fluid_id) create_reagents(fluid_max_volume) + if(producing) + reagents.add_reagent(fluid_id, fluid_max_volume) update() /obj/item/organ/genital/Destroy() - remove_ref() + if(linked_organ) + update_link(TRUE)//this should remove any other links it has if(owner) - Remove(owner, 1)//this should remove references to it, so it can be GCd correctly - update_link()//this should remove any other links it has + Remove(owner, TRUE)//this should remove references to it, so it can be GCd correctly return ..() -/obj/item/organ/genital/proc/update() +/obj/item/organ/genital/proc/update(removing = FALSE) if(QDELETED(src)) return update_size() update_appearance() - update_link() + if(linked_organ_slot || (linked_organ && removing)) + update_link(removing) //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(hidden) - return FALSE - if(internal) + if(!owner || hidden || internal) return FALSE if(through_clothes) return TRUE switch(zone) //update as more genitals are added - if("chest") + if(BODY_ZONE_CHEST) return owner.is_chest_exposed() - if("groin") + if(BODY_ZONE_PRECISE_GROIN) return owner.is_groin_exposed() return FALSE @@ -107,22 +109,37 @@ picked_organ.toggle_visibility(picked_visibility) return - - - /obj/item/organ/genital/proc/update_size() return /obj/item/organ/genital/proc/update_appearance() return -/obj/item/organ/genital/proc/update_link() - return +/obj/item/organ/genital/on_life() + if(!reagents || !owner) + return + reagents.maximum_volume = fluid_max_volume + if(fluid_id && producing) + generate_fluid() -/obj/item/organ/genital/proc/remove_ref() - if(linked_organ) - linked_organ.linked_organ = null +/obj/item/organ/genital/proc/generate_fluid() + if(owner.stat != DEAD && reagents.total_volume < reagents.maximum_volume) + 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 + return TRUE + return FALSE + +/obj/item/organ/genital/proc/update_link(removing = FALSE) + if(!removing && owner) + linked_organ = owner.getorganslot(linked_organ_slot) + if(linked_organ) + linked_organ.linked_organ = src + return TRUE + else + if(linked_organ) + linked_organ.linked_organ = null linked_organ = null + return FALSE /obj/item/organ/genital/Insert(mob/living/carbon/M, special = 0) ..() @@ -130,127 +147,42 @@ /obj/item/organ/genital/Remove(mob/living/carbon/M, special = 0) ..() - update() + update(TRUE) //proc to give a player their genitals and stuff when they log in -/mob/living/carbon/human/proc/give_genitals(clean=0)//clean will remove all pre-existing genitals. proc will then give them any genitals that are enabled in their DNA +/mob/living/carbon/human/proc/give_genitals(clean = FALSE)//clean will remove all pre-existing genitals. proc will then give them any genitals that are enabled in their DNA if(clean) - var/obj/item/organ/genital/GtoClean - for(GtoClean in internal_organs) - qdel(GtoClean) + for(var/obj/item/organ/genital/G in internal_organs) + qdel(G) if (NOGENITALS in dna.species.species_traits) return - //Order should be very important. FIRST vagina, THEN testicles, THEN penis, as this affects the order they are rendered in. if(dna.features["has_vag"]) - give_vagina() + give_genital(/obj/item/organ/genital/vagina) if(dna.features["has_womb"]) - give_womb() + give_genital(/obj/item/organ/genital/womb) if(dna.features["has_balls"]) - give_balls() + give_genital(/obj/item/organ/genital/testicles) if(dna.features["has_breasts"]) // since we have multi-boobs as a thing, we'll want to at least draw over these. but not over the pingas. - give_breasts() + give_genital(/obj/item/organ/genital/breasts) if(dna.features["has_cock"]) - give_penis() + give_genital(/obj/item/organ/genital/penis) + /* if(dna.features["has_ovi"]) - give_ovipositor() + give_genital(/obj/item/organ/genital/ovipositor) if(dna.features["has_eggsack"]) - give_eggsack() + give_genital(/obj/item/organ/genital/eggsack) + */ -/mob/living/carbon/human/proc/give_penis() - if(!dna) +/mob/living/carbon/human/proc/give_genital(obj/item/organ/genital/G) + if(!dna || (NOGENITALS in dna.species.species_traits) || getorganslot(initial(G.slot))) return FALSE - if(NOGENITALS in dna.species.species_traits) - return FALSE - if(!getorganslot("penis")) - var/obj/item/organ/genital/penis/P = new - P.Insert(src) - if(P) - if(dna.species.use_skintones && dna.features["genitals_use_skintone"]) - P.color = "#[skintone2hex(skin_tone)]" - else - P.color = "#[dna.features["cock_color"]]" - P.length = dna.features["cock_length"] - P.girth_ratio = dna.features["cock_girth_ratio"] - P.shape = dna.features["cock_shape"] - P.update() + G = new + if(istype(G, /obj/item/organ/genital)) //badminnery-proofing. + G.get_features(src) + G.Insert(src) -/mob/living/carbon/human/proc/give_balls() - if(!dna) - return FALSE - if(NOGENITALS in dna.species.species_traits) - return FALSE - if(!getorganslot("testicles")) - var/obj/item/organ/genital/testicles/T = new - T.Insert(src) - if(T) - if(dna.species.use_skintones && dna.features["genitals_use_skintone"]) - T.color = "#[skintone2hex(skin_tone)]" - else - T.color = "#[dna.features["balls_color"]]" - T.size = dna.features["balls_size"] - T.sack_size = dna.features["balls_sack_size"] - T.shape = dna.features["balls_shape"] - if(dna.features["balls_shape"] == "Hidden") - T.internal = TRUE - else - T.internal = FALSE - T.fluid_id = dna.features["balls_fluid"] - T.fluid_rate = dna.features["balls_cum_rate"] - T.fluid_mult = dna.features["balls_cum_mult"] - T.fluid_efficiency = dna.features["balls_efficiency"] - T.update() - -/mob/living/carbon/human/proc/give_breasts() - if(!dna) - return FALSE - if(NOGENITALS in dna.species.species_traits) - return FALSE - if(!getorganslot("breasts")) - var/obj/item/organ/genital/breasts/B = new - B.Insert(src) - if(B) - if(dna.species.use_skintones && dna.features["genitals_use_skintone"]) - B.color = "#[skintone2hex(skin_tone)]" - else - B.color = "#[dna.features["breasts_color"]]" - B.size = dna.features["breasts_size"] - B.shape = dna.features["breasts_shape"] - B.fluid_id = dna.features["breasts_fluid"] - B.update() - - -/mob/living/carbon/human/proc/give_ovipositor() +/obj/item/organ/genital/proc/get_features(datum/dna/D) return -/mob/living/carbon/human/proc/give_eggsack() - return - -/mob/living/carbon/human/proc/give_vagina() - if(!dna) - return FALSE - if(NOGENITALS in dna.species.species_traits) - return FALSE - if(!getorganslot("vagina")) - var/obj/item/organ/genital/vagina/V = new - V.Insert(src) - if(V) - if(dna.species.use_skintones && dna.features["genitals_use_skintone"]) - V.color = "#[skintone2hex(skin_tone)]" - else - V.color = "[dna.features["vag_color"]]" - V.shape = "[dna.features["vag_shape"]]" - V.update() - -/mob/living/carbon/human/proc/give_womb() - if(!dna) - return FALSE - if(NOGENITALS in dna.species.species_traits) - return FALSE - if(!getorganslot("womb")) - var/obj/item/organ/genital/womb/W = new - W.Insert(src) - if(W) - W.update() - /datum/species/proc/genitals_layertext(layer) switch(layer) @@ -267,7 +199,7 @@ if(ishuman(user)) var/mob/living/carbon/human/H = user H.update_genitals() - ..() + . = ..() /mob/living/carbon/human/doUnEquip(obj/item/I, force) . = ..() @@ -276,20 +208,16 @@ update_genitals() /mob/living/carbon/human/proc/update_genitals() - if(src && !QDELETED(src)) + if(!QDELETED(src)) dna.species.handle_genitals(src) /datum/species/proc/handle_genitals(mob/living/carbon/human/H) if(!H)//no args CRASH("H = null") - if(!LAZYLEN(H.internal_organs))//if they have no organs, we're done - return - if(NOGENITALS in species_traits)//golems and such - return - if(HAS_TRAIT(H, TRAIT_HUSK)) + if(!LAZYLEN(H.internal_organs) || (NOGENITALS in species_traits) || HAS_TRAIT(H, TRAIT_HUSK)) return - var/list/genitals_to_add = list() + var/list/genitals_to_add[GENITAL_LAYER_INDEX_LENGTH] var/list/relevant_layers = list(GENITALS_BEHIND_LAYER, GENITALS_ADJ_LAYER, GENITALS_FRONT_LAYER) var/list/standing = list() var/size @@ -300,18 +228,16 @@ //start scanning for genitals //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(isgenital(O)) - var/obj/item/organ/genital/G = O - if(G.hidden) - return //we're gunna just hijack this for updates. - if(G.is_exposed()) //Checks appropriate clothing slot and if it's through_clothes - genitals_to_add += H.getorganslot(G.slot) + for(var/obj/item/organ/genital/G in H.internal_organs) + if(G.hidden) + return //we're gunna just hijack this for updates. + if(G.is_exposed()) //Checks appropriate clothing slot and if it's through_clothes + LAZYADD(genitals_to_add[G.layer_index], G) //Now we added all genitals that aren't internal and should be rendered //start applying overlays for(var/layer in relevant_layers) - var/layertext = genitals_layertext(layer) + var/layertext = flatten_list(genitals_layertext(layer)) for(var/obj/item/organ/genital/G in genitals_to_add) var/datum/sprite_accessory/S size = G.size @@ -357,3 +283,4 @@ for(var/L in relevant_layers) H.apply_overlay(L) + diff --git a/modular_citadel/code/modules/arousal/organs/breasts.dm b/modular_citadel/code/modules/arousal/organs/breasts.dm index 1223f0b616..f6d5673a53 100644 --- a/modular_citadel/code/modules/arousal/organs/breasts.dm +++ b/modular_citadel/code/modules/arousal/organs/breasts.dm @@ -1,38 +1,20 @@ /obj/item/organ/genital/breasts - name = "breasts" - desc = "Female milk producing organs." - icon_state = "breasts" - icon = 'modular_citadel/icons/obj/genitals/breasts.dmi' - zone = "chest" - slot = "breasts" - size = BREASTS_SIZE_DEF - fluid_id = "milk" - 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() - . = ..() - reagents.add_reagent(fluid_id, fluid_max_volume) - -/obj/item/organ/genital/breasts/on_life() - if(QDELETED(src)) - return - if(!reagents || !owner) - return - reagents.maximum_volume = fluid_max_volume - if(fluid_id && producing) - generate_milk() - -/obj/item/organ/genital/breasts/proc/generate_milk() - if(owner.stat == DEAD) - return FALSE - reagents.isolate_reagent(fluid_id) - reagents.add_reagent(fluid_id, (fluid_mult * fluid_rate)) + name = "breasts" + desc = "Female milk producing organs." + icon_state = "breasts" + icon = 'modular_citadel/icons/obj/genitals/breasts.dmi' + zone = BODY_ZONE_CHEST + slot = ORGAN_SLOT_BREASTS + size = BREASTS_SIZE_DEF + fluid_id = "milk" + var/amount = 2 + producing = TRUE + shape = "pair" + can_masturbate_with = TRUE + masturbation_verb = "massage" + orgasm_verb = "leaking" + can_climax = TRUE + fluid_transfer_factor = 0.5 /obj/item/organ/genital/breasts/update_appearance() var/lowershape = lowertext(shape) @@ -65,3 +47,13 @@ var/mob/living/carbon/human/H = owner icon_state = sanitize_text(string) H.update_genitals() + +/obj/item/organ/genital/breasts/get_features(mob/living/carbon/human/H) + var/datum/dna/D = H.dna + if(D.species.use_skintones && D.features["genitals_use_skintone"]) + color = "#[skintone2hex(H.skin_tone)]" + else + color = "#[D.features["breasts_color"]]" + size = D.features["breasts_size"] + shape = D.features["breasts_shape"] + fluid_id = D.features["breasts_fluid"] diff --git a/modular_citadel/code/modules/arousal/organs/eggsack.dm b/modular_citadel/code/modules/arousal/organs/eggsack.dm index 402d246e40..3282bad5f9 100644 --- a/modular_citadel/code/modules/arousal/organs/eggsack.dm +++ b/modular_citadel/code/modules/arousal/organs/eggsack.dm @@ -1,14 +1,14 @@ /obj/item/organ/genital/eggsack - name = "Egg sack" - desc = "An egg producing reproductive organ." - icon_state = "egg_sack" - icon = 'modular_citadel/icons/obj/genitals/ovipositor.dmi' - zone = "groin" - slot = "testicles" - color = null //don't use the /genital color since it already is colored + name = "Egg sack" + desc = "An egg producing reproductive organ." + icon_state = "egg_sack" + icon = 'modular_citadel/icons/obj/genitals/ovipositor.dmi' + zone = BODY_ZONE_PRECISE_GROIN + slot = ORGAN_SLOT_TESTICLES + linked_organ_slot = ORGAN_SLOT_PENIS + color = null //don't use the /genital color since it already is colored internal = TRUE var/egg_girth = EGG_GIRTH_DEF var/cum_mult = CUM_RATE_MULT var/cum_rate = CUM_RATE var/cum_efficiency = CUM_EFFICIENCY - var/obj/item/organ/ovipositor/linked_ovi diff --git a/modular_citadel/code/modules/arousal/organs/genitals_sprite_accessories.dm b/modular_citadel/code/modules/arousal/organs/genitals_sprite_accessories.dm index b913a90fb6..4ba2e373a2 100644 --- a/modular_citadel/code/modules/arousal/organs/genitals_sprite_accessories.dm +++ b/modular_citadel/code/modules/arousal/organs/genitals_sprite_accessories.dm @@ -5,11 +5,9 @@ //DICKS,COCKS,PENISES,WHATEVER YOU WANT TO CALL THEM /datum/sprite_accessory/penis icon = 'modular_citadel/icons/obj/genitals/penis_onmob.dmi' - icon_state = null name = "penis" //the preview name of the accessory - gender_specific = 0 //Might be needed somewhere down the list. color_src = "cock_color" - locked = 0 + alt_aroused = TRUE /datum/sprite_accessory/penis/human icon_state = "human" @@ -62,27 +60,21 @@ icon_state = "testicle" name = "testicle" //the preview name of the accessory color_src = "balls_color" - locked = 0 /datum/sprite_accessory/testicles/hidden - icon_state = "hidden" + icon_state = "none" name = "Hidden" - alt_aroused = TRUE /datum/sprite_accessory/testicles/single icon_state = "single" name = "Single" - alt_aroused = TRUE //Vaginas /datum/sprite_accessory/vagina icon = 'modular_citadel/icons/obj/genitals/vagina_onmob.dmi' icon_state = null name = "vagina" - gender_specific = 0 color_src = "vag_color" - locked = 0 - alt_aroused = FALSE //if this is TRUE, then the genitals will use an alternate sprite for aroused states /datum/sprite_accessory/vagina/human icon_state = "human" @@ -114,35 +106,28 @@ //BREASTS BE HERE /datum/sprite_accessory/breasts icon = 'modular_citadel/icons/obj/genitals/breasts_onmob.dmi' - icon_state = null name = "breasts" - gender_specific = 0 color_src = "breasts_color" - locked = 0 + alt_aroused = TRUE /datum/sprite_accessory/breasts/pair icon_state = "pair" name = "Pair" - alt_aroused = TRUE /datum/sprite_accessory/breasts/quad icon_state = "quad" name = "Quad" - alt_aroused = TRUE /datum/sprite_accessory/breasts/sextuple icon_state = "sextuple" name = "Sextuple" - alt_aroused = TRUE //OVIPOSITORS BE HERE /datum/sprite_accessory/ovipositor icon = 'modular_citadel/icons/obj/genitals/penis_onmob.dmi' icon_state = null name = "Ovipositor" //the preview name of the accessory - gender_specific = 0 //Might be needed somewhere down the list. color_src = "cock_color" - locked = 0 /datum/sprite_accessory/ovipositor/knotted icon_state = "knotted" diff --git a/modular_citadel/code/modules/arousal/organs/ovipositor.dm b/modular_citadel/code/modules/arousal/organs/ovipositor.dm index 76bf60d93c..92b608f905 100644 --- a/modular_citadel/code/modules/arousal/organs/ovipositor.dm +++ b/modular_citadel/code/modules/arousal/organs/ovipositor.dm @@ -3,14 +3,13 @@ desc = "An egg laying reproductive organ." icon_state = "ovi_knotted_2" icon = 'modular_citadel/icons/obj/genitals/ovipositor.dmi' - zone = "groin" - slot = "penis" - w_class = 3 + zone = BODY_ZONE_PRECISE_GROIN + slot = ORGAN_SLOT_PENIS shape = "knotted" size = 3 + layer_index = PENIS_LAYER_INDEX var/length = 6 //inches var/girth = 0 var/girth_ratio = COCK_GIRTH_RATIO_DEF //citadel_defines.dm for these defines var/knot_girth_ratio = KNOT_GIRTH_RATIO_DEF var/list/oviflags = list() - var/obj/item/organ/eggsack/linked_eggsack diff --git a/modular_citadel/code/modules/arousal/organs/penis.dm b/modular_citadel/code/modules/arousal/organs/penis.dm index b6cb8fa4b2..2593460a32 100644 --- a/modular_citadel/code/modules/arousal/organs/penis.dm +++ b/modular_citadel/code/modules/arousal/organs/penis.dm @@ -1,22 +1,23 @@ /obj/item/organ/genital/penis - name = "penis" - desc = "A male reproductive organ." - icon_state = "penis" - icon = 'modular_citadel/icons/obj/genitals/penis.dmi' - zone = "groin" - slot = ORGAN_SLOT_PENIS - 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", "barbed, knotted") + name = "penis" + desc = "A male reproductive organ." + icon_state = "penis" + icon = 'modular_citadel/icons/obj/genitals/penis.dmi' + zone = BODY_ZONE_PRECISE_GROIN + slot = ORGAN_SLOT_PENIS + 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. + layer_index = PENIS_LAYER_INDEX + 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", "barbed, knotted") /obj/item/organ/genital/penis/update_size() if(length == cached_length) @@ -38,7 +39,7 @@ /obj/item/organ/genital/penis/update_appearance() var/string var/lowershape = lowertext(shape) - desc = "You see [aroused_state ? "an erect" : "a flaccid"] [lowershape] penis. You estimate it's about [round(length, 0.25)] inch[round(length, 0.25) != 1 ? "es" : ""] long and [round(girth, 0.25)] inch[round(girth, 0.25) != 1 ? "es" : ""] in girth." + desc = "You see [aroused_state ? "an erect" : "a flaccid"] [lowershape] [name]. You estimate it's about [round(length, 0.25)] inch[round(length, 0.25) != 1 ? "es" : ""] long and [round(girth, 0.25)] inch[round(girth, 0.25) != 1 ? "es" : ""] in girth." if(owner) if(owner.dna.species.use_skintones && owner.dna.features["genitals_use_skintone"]) @@ -54,13 +55,12 @@ icon_state = sanitize_text(string) H.update_genitals() -/obj/item/organ/genital/penis/update_link() - if(owner) - linked_organ = (owner.getorganslot("testicles")) - if(linked_organ) - linked_organ.linked_organ = src - linked_organ.size = size +/obj/item/organ/genital/penis/get_features(mob/living/carbon/human/H) + var/datum/dna/D = H.dna + if(D.species.use_skintones && D.features["genitals_use_skintone"]) + color = "#[skintone2hex(H.skin_tone)]" else - if(linked_organ) - linked_organ.linked_organ = null - linked_organ = null + color = "#[D.features["cock_color"]]" + length = D.features["cock_length"] + girth_ratio = D.features["cock_girth_ratio"] + shape = D.features["cock_shape"] diff --git a/modular_citadel/code/modules/arousal/organs/testicles.dm b/modular_citadel/code/modules/arousal/organs/testicles.dm index 1e6b4d62d4..de7b6d6f70 100644 --- a/modular_citadel/code/modules/arousal/organs/testicles.dm +++ b/modular_citadel/code/modules/arousal/organs/testicles.dm @@ -1,56 +1,34 @@ /obj/item/organ/genital/testicles - name = "testicles" - desc = "A male reproductive organ." - icon_state = "testicles" - icon = 'modular_citadel/icons/obj/genitals/testicles.dmi' - zone = "groin" - slot = "testicles" - size = BALLS_SIZE_MIN - var/size_name = "average" - shape = "single" - var/sack_size = BALLS_SACK_SIZE_DEF - fluid_id = "semen" - producing = TRUE - can_masturbate_with = FALSE - masturbation_verb = "massage" - can_climax = TRUE - var/sent_full_message = TRUE //defaults to 1 since they're full to start + name = "testicles" + desc = "A male reproductive organ." + icon_state = "testicles" + icon = 'modular_citadel/icons/obj/genitals/testicles.dmi' + zone = BODY_ZONE_PRECISE_GROIN + slot = ORGAN_SLOT_TESTICLES + size = BALLS_SIZE_MIN + linked_organ_slot = ORGAN_SLOT_PENIS + var/size_name = "average" + shape = "single" + var/sack_size = BALLS_SACK_SIZE_DEF + fluid_id = "semen" + producing = TRUE + can_masturbate_with = MASTURBATE_LINKED_ORGAN + masturbation_verb = "massage" + layer_index = TESTICLES_LAYER_INDEX + var/size_linked = FALSE -/obj/item/organ/genital/testicles/Initialize() +/obj/item/organ/genital/testicles/generate_fluid() + if(!linked_organ && !update_link()) + return FALSE . = ..() - reagents.add_reagent(fluid_id, fluid_max_volume) + if(.) + send_full_message() -/obj/item/organ/genital/testicles/on_life() - if(QDELETED(src)) - return - if(reagents && producing) - generate_cum() - -/obj/item/organ/genital/testicles/proc/generate_cum() - reagents.maximum_volume = fluid_max_volume - if(reagents.total_volume >= reagents.maximum_volume) - if(!sent_full_message) - send_full_message() - sent_full_message = TRUE - return FALSE - sent_full_message = FALSE - update_link() - 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_organ = (owner.getorganslot("penis")) - if(linked_organ) - linked_organ.linked_organ = src - size = linked_organ.size - - else - if(linked_organ) - linked_organ.linked_organ = null - linked_organ = null +/obj/item/organ/genital/testicles/update_link(removing = FALSE) + . = ..() + if(. && !size_linked) + size = linked_organ.size + size_linked = TRUE /obj/item/organ/genital/testicles/proc/send_full_message(msg = "Your balls finally feel full, again.") if(owner && istext(msg)) @@ -59,19 +37,16 @@ /obj/item/organ/genital/testicles/update_appearance() switch(size) - if(0.1 to 1) + if(BALLS_SIZE_MIN) size_name = "average" - if(1.1 to 2) + if(BALLS_SIZE_DEF) size_name = "enlarged" - if(2.1 to INFINITY) + if(BALLS_SIZE_MAX) size_name = "engorged" else size_name = "nonexistant" - if(!internal) - desc = "You see an [size_name] pair of testicles." - else - desc = "They don't have any testicles you can see." + desc = "You see an [size_name] pair of testicles." if(owner) var/string @@ -87,3 +62,21 @@ var/mob/living/carbon/human/H = owner icon_state = sanitize_text(string) H.update_genitals() + +/obj/item/organ/genital/testicles/get_features(mob/living/carbon/human/H) + var/datum/dna/D = H.dna + if(D.species.use_skintones && D.features["genitals_use_skintone"]) + color = "#[skintone2hex(H.skin_tone)]" + else + color = "#[D.features["balls_color"]]" + size = D.features["balls_size"] + sack_size = D.features["balls_sack_size"] + shape = D.features["balls_shape"] + if(D.features["balls_shape"] == "Hidden") + internal = TRUE + else + internal = FALSE + fluid_id = D.features["balls_fluid"] + fluid_rate = D.features["balls_cum_rate"] + fluid_mult = D.features["balls_cum_mult"] + fluid_efficiency = D.features["balls_efficiency"] diff --git a/modular_citadel/code/modules/arousal/organs/vagina.dm b/modular_citadel/code/modules/arousal/organs/vagina.dm index 8c15aa5437..af2beb5f79 100644 --- a/modular_citadel/code/modules/arousal/organs/vagina.dm +++ b/modular_citadel/code/modules/arousal/organs/vagina.dm @@ -1,25 +1,24 @@ /obj/item/organ/genital/vagina - name = "vagina" - desc = "A female reproductive organ." - icon = 'modular_citadel/icons/obj/genitals/vagina.dmi' - icon_state = "vagina" - zone = "groin" - slot = "vagina" - size = 1 //There is only 1 size right now - can_masturbate_with = TRUE - masturbation_verb = "finger" - can_climax = TRUE + name = "vagina" + desc = "A female reproductive organ." + icon = 'modular_citadel/icons/obj/genitals/vagina.dmi' + icon_state = ORGAN_SLOT_VAGINA + zone = BODY_ZONE_PRECISE_GROIN + slot = "vagina" + size = 1 //There is only 1 size right now + 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 + layer_index = VAGINA_LAYER_INDEX + var/cap_length = 8//D E P T H (cap = capacity) + var/cap_girth = 12 var/cap_girth_ratio = 1.5 - var/clits = 1 - var/clit_diam = 0.25 - var/clit_len = 0.25 + var/clits = 1 + var/clit_diam = 0.25 + var/clit_len = 0.25 var/list/vag_types = list("tentacle", "dentata", "hairy", "spade", "furred") - /obj/item/organ/genital/vagina/update_appearance() var/string //Keeping this code here, so making multiple sprites for the different kinds is easier. var/lowershape = lowertext(shape) @@ -63,12 +62,10 @@ icon_state = sanitize_text(string) H.update_genitals() -/obj/item/organ/genital/vagina/update_link() - if(owner) - linked_organ = (owner.getorganslot("womb")) - if(linked_organ) - linked_organ.linked_organ = src +/obj/item/organ/genital/vagina/get_features(mob/living/carbon/human/H) + var/datum/dna/D = H.dna + if(D.species.use_skintones && D.features["genitals_use_skintone"]) + color = "#[skintone2hex(H.skin_tone)]" else - if(linked_organ) - linked_organ.linked_organ = null - linked_organ = null + color = "[D.features["vag_color"]]" + shape = "[D.features["vag_shape"]]" diff --git a/modular_citadel/code/modules/arousal/organs/womb.dm b/modular_citadel/code/modules/arousal/organs/womb.dm index 686d9059a0..d99206d0f1 100644 --- a/modular_citadel/code/modules/arousal/organs/womb.dm +++ b/modular_citadel/code/modules/arousal/organs/womb.dm @@ -1,41 +1,11 @@ /obj/item/organ/genital/womb - name = "womb" - desc = "A female reproductive organ." - icon = 'modular_citadel/icons/obj/genitals/vagina.dmi' - icon_state = "womb" - zone = "groin" - slot = "womb" - internal = TRUE - fluid_id = "femcum" - producing = TRUE - -/obj/item/organ/genital/womb/Initialize() - . = ..() - reagents.add_reagent(fluid_id, fluid_max_volume) - -/obj/item/organ/genital/womb/on_life() - if(QDELETED(src)) - return - if(reagents && producing) - generate_femcum() - -/obj/item/organ/genital/womb/proc/generate_femcum() - reagents.maximum_volume = fluid_max_volume - update_link() - 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_organ = (owner.getorganslot("vagina")) - if(linked_organ) - linked_organ.linked_organ = src - else - if(linked_organ) - linked_organ.linked_organ = null - linked_organ = null - -/obj/item/organ/genital/womb/Destroy() - return ..() + name = "womb" + desc = "A female reproductive organ." + icon = 'modular_citadel/icons/obj/genitals/vagina.dmi' + icon_state = "womb" + zone = BODY_ZONE_PRECISE_GROIN + slot = ORGAN_SLOT_WOMB + internal = TRUE + fluid_id = "femcum" + producing = TRUE + linked_organ_slot = ORGAN_SLOT_VAGINA \ No newline at end of file diff --git a/modular_citadel/code/modules/client/preferences_savefile.dm b/modular_citadel/code/modules/client/preferences_savefile.dm index 2921f70684..be9e92d4c3 100644 --- a/modular_citadel/code/modules/client/preferences_savefile.dm +++ b/modular_citadel/code/modules/client/preferences_savefile.dm @@ -32,7 +32,6 @@ WRITE_FILE(S["feature_ipc_antenna"], features["ipc_antenna"]) //Citadel WRITE_FILE(S["feature_genitals_use_skintone"], features["genitals_use_skintone"]) - WRITE_FILE(S["feature_exhibitionist"], features["exhibitionist"]) WRITE_FILE(S["feature_mcolor2"], features["mcolor2"]) WRITE_FILE(S["feature_mcolor3"], features["mcolor3"]) WRITE_FILE(S["feature_mam_body_markings"], features["mam_body_markings"]) diff --git a/modular_citadel/code/modules/mob/living/carbon/human/life.dm b/modular_citadel/code/modules/mob/living/carbon/human/life.dm index e728d70c97..fbaa1b4b58 100644 --- a/modular_citadel/code/modules/mob/living/carbon/human/life.dm +++ b/modular_citadel/code/modules/mob/living/carbon/human/life.dm @@ -1,7 +1,7 @@ -/mob/living/carbon/human/Life() +/mob/living/carbon/human/Life(seconds, times_fired) //citadel code if(stat != DEAD) - handle_arousal() + handle_arousal(times_fired) . = ..() /mob/living/carbon/human/calculate_affecting_pressure(pressure) diff --git a/modular_citadel/code/modules/mob/living/carbon/human/species_types/jellypeople.dm b/modular_citadel/code/modules/mob/living/carbon/human/species_types/jellypeople.dm index fa6ded8065..0e6c12053a 100644 --- a/modular_citadel/code/modules/mob/living/carbon/human/species_types/jellypeople.dm +++ b/modular_citadel/code/modules/mob/living/carbon/human/species_types/jellypeople.dm @@ -78,42 +78,30 @@ else H.facial_hair_style = "Shaved" //handle normal hair - var/new_style = input(owner, "Select a hair style", "Hair Alterations") as null|anything in GLOB.hair_styles_list + var/new_style = input(owner, "Select a hair style", "Hair Alterations") as null|anything in GLOB.hair_styles_list if(new_style) H.hair_style = new_style H.update_hair() else if (select_alteration == "Genitals") - var/list/organs = list() var/operation = input("Select organ operation.", "Organ Manipulation", "cancel") in list("add sexual organ", "remove sexual organ", "cancel") switch(operation) if("add sexual organ") - var/new_organ = input("Select sexual organ:", "Organ Manipulation") in list("Penis", "Testicles", "Breasts", "Vagina", "Womb", "Cancel") - if(new_organ == "Penis") - H.give_penis() - else if(new_organ == "Testicles") - H.give_balls() - else if(new_organ == "Breasts") - H.give_breasts() - else if(new_organ == "Vagina") - H.give_vagina() - else if(new_organ == "Womb") - H.give_womb() - else + var/new_organ = input("Select sexual organ:", "Organ Manipulation") as null|anything in GLOB.genitals_list + if(!new_organ) return + H.give_genital(GLOB.genitals_list[new_organ]) + if("remove sexual organ") + var/list/organs = list() for(var/obj/item/organ/genital/X in H.internal_organs) var/obj/item/organ/I = X organs["[I.name] ([I.type])"] = I - var/obj/item/organ = input("Select sexual organ:", "Organ Manipulation", null) in organs - organ = organs[organ] - if(!organ) + var/obj/item/O = input("Select sexual organ:", "Organ Manipulation", null) as null|anything in organs + var/obj/item/organ/genital/G = organs[O] + if(!G) return - var/obj/item/organ/genital/O - if(isorgan(organ)) - O = organ - O.Remove(H) - organ.forceMove(get_turf(H)) - qdel(organ) + G.forceMove(get_turf(H)) + qdel(G) H.update_genitals() else if (select_alteration == "Ears") @@ -203,8 +191,8 @@ if(new_shape) H.dna.features["cock_shape"] = new_shape H.update_genitals() - H.give_balls() - H.give_penis() + H.give_genital(/obj/item/organ/genital/testicles) + H.give_genital(/obj/item/organ/genital/penis) H.apply_overlay() @@ -216,8 +204,8 @@ if(new_shape) H.dna.features["vag_shape"] = new_shape H.update_genitals() - H.give_womb() - H.give_vagina() + H.give_genital(/obj/item/organ/genital/womb) + H.give_genital(/obj/item/organ/genital/vagina) H.apply_overlay() else if (select_alteration == "Penis Length") @@ -229,8 +217,8 @@ H.dna.features["cock_length"] = max(min( round(text2num(new_length)), COCK_SIZE_MAX),COCK_SIZE_MIN) H.update_genitals() H.apply_overlay() - H.give_balls() - H.give_penis() + H.give_genital(/obj/item/organ/genital/testicles) + H.give_genital(/obj/item/organ/genital/penis) else if (select_alteration == "Breast Size") for(var/obj/item/organ/genital/breasts/X in H.internal_organs) @@ -241,7 +229,7 @@ H.dna.features["breasts_size"] = new_size H.update_genitals() H.apply_overlay() - H.give_breasts() + H.give_genital(/obj/item/organ/genital/breasts) else if (select_alteration == "Breast Shape") for(var/obj/item/organ/genital/breasts/X in H.internal_organs) @@ -252,7 +240,7 @@ H.dna.features["breasts_shape"] = new_shape H.update_genitals() H.apply_overlay() - H.give_breasts() + H.give_genital(/obj/item/organ/genital/breasts) else return diff --git a/modular_citadel/icons/obj/genitals/testicles_onmob.dmi b/modular_citadel/icons/obj/genitals/testicles_onmob.dmi index 33659cf13c..7d4d2610c5 100644 Binary files a/modular_citadel/icons/obj/genitals/testicles_onmob.dmi and b/modular_citadel/icons/obj/genitals/testicles_onmob.dmi differ diff --git a/tgstation.dme b/tgstation.dme index 722ebae9be..7c9fa1e899 100755 --- a/tgstation.dme +++ b/tgstation.dme @@ -2844,7 +2844,6 @@ #include "modular_citadel\code\datums\mood_events\moodular.dm" #include "modular_citadel\code\datums\mutations\hulk.dm" #include "modular_citadel\code\datums\status_effects\debuffs.dm" -#include "modular_citadel\code\datums\traits\neutral.dm" #include "modular_citadel\code\datums\wires\airlock.dm" #include "modular_citadel\code\datums\wires\autoylathe.dm" #include "modular_citadel\code\game\area\cit_areas.dm" @@ -2912,9 +2911,9 @@ #include "modular_citadel\code\modules\admin\secrets.dm" #include "modular_citadel\code\modules\admin\topic.dm" #include "modular_citadel\code\modules\arousal\arousal.dm" +#include "modular_citadel\code\modules\arousal\organs\_genitals.dm" #include "modular_citadel\code\modules\arousal\organs\breasts.dm" #include "modular_citadel\code\modules\arousal\organs\eggsack.dm" -#include "modular_citadel\code\modules\arousal\organs\genitals.dm" #include "modular_citadel\code\modules\arousal\organs\genitals_sprite_accessories.dm" #include "modular_citadel\code\modules\arousal\organs\ovipositor.dm" #include "modular_citadel\code\modules\arousal\organs\penis.dm"