Merge pull request #8948 from Ghommie/Ghommie-cit156

Cleaning up the arousal module.
This commit is contained in:
kevinz000
2019-10-31 17:27:03 -07:00
committed by GitHub
39 changed files with 1263 additions and 1599 deletions
+5
View File
@@ -131,6 +131,11 @@
#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"
////organ defines
#define STANDARD_ORGAN_THRESHOLD 100
+17 -21
View File
@@ -18,6 +18,23 @@
#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.
//genital flags
#define GENITAL_BLACKLISTED (1<<0) //for genitals that shouldn't be added to GLOB.genitals_list.
#define GENITAL_INTERNAL (1<<1)
#define GENITAL_HIDDEN (1<<2)
#define GENITAL_THROUGH_CLOTHES (1<<3)
#define GENITAL_FUID_PRODUCTION (1<<4)
#define CAN_MASTURBATE_WITH (1<<5)
#define MASTURBATE_LINKED_ORGAN (1<<6) //used to pass our mission to the linked organ
#define CAN_CLIMAX_WITH (1<<7)
#define COCK_SIZE_MIN 1
#define COCK_SIZE_MAX 20
@@ -50,27 +67,6 @@
#define BREASTS_VOLUME_BASE 50 //base volume for the reagents in the breasts, multiplied by the size then multiplier. 50u for A cups, 850u for HH cups.
#define BREASTS_VOLUME_MULT 1 //global multiplier for breast volume.
#define BREASTS_SIZE_FLAT 0
#define BREASTS_SIZE_A 1
#define BREASTS_SIZE_AA 1.5
#define BREASTS_SIZE_B 2
#define BREASTS_SIZE_BB 2.5
#define BREASTS_SIZE_C 3
#define BREASTS_SIZE_CC 3.5
#define BREASTS_SIZE_D 4
#define BREASTS_SIZE_DD 4.5
#define BREASTS_SIZE_E 5
#define BREASTS_SIZE_EE 5.5
#define BREASTS_SIZE_F 6
#define BREASTS_SIZE_FF 6.5
#define BREASTS_SIZE_G 7
#define BREASTS_SIZE_GG 7.5//Are these even real sizes? The world may never know because cup sizes make no fucking sense.
#define BREASTS_SIZE_H 8
#define BREASTS_SIZE_HH 8.5//Largest size, ever. For now.
#define BREASTS_SIZE_MIN BREASTS_SIZE_A
#define BREASTS_SIZE_DEF BREASTS_SIZE_C
#define BREASTS_SIZE_MAX BREASTS_SIZE_HH
#define MILK_RATE 5
#define MILK_RATE_MULT 1
+3
View File
@@ -243,3 +243,6 @@ GLOBAL_LIST_INIT(security_wintercoat_allowed, typecacheof(list(
//Internals checker
#define GET_INTERNAL_SLOTS(C) list(C.head, C.wear_mask)
//Slots that won't trigger humans' update_genitals() on equip().
GLOBAL_LIST_INIT(no_genitals_update_slots, list(SLOT_L_STORE, SLOT_R_STORE, SLOT_S_STORE, SLOT_IN_BACKPACK, SLOT_LEGCUFFED, SLOT_HANDCUFFED, SLOT_HANDS, SLOT_GENERC_DEXTROUS_STORAGE))
+14 -13
View File
@@ -51,24 +51,25 @@ Will print: "/mob/living/carbon/human/death" (you can optionally embed it in a s
//Human Overlays Indexes/////////
//LOTS OF CIT CHANGES HERE. BE CAREFUL WHEN UPSTREAM ADDS MORE LAYERS
#define MUTATIONS_LAYER 31 //mutations. Tk headglows, cold resistance glow, etc
#define GENITALS_BEHIND_LAYER 30 //Some genitalia needs to be behind everything, such as with taurs (Taurs use body_behind_layer
#define BODY_BEHIND_LAYER 29 //certain mutantrace features (tail when looking south) that must appear behind the body parts
#define BODYPARTS_LAYER 28 //Initially "AUGMENTS", this was repurposed to be a catch-all bodyparts flag
#define MARKING_LAYER 27 //Matrixed body markings because clashing with snouts?
#define BODY_ADJ_LAYER 26 //certain mutantrace features (snout, body markings) that must appear above the body parts
#define GENITALS_FRONT_LAYER 25 //Draws some genitalia above clothes and the TAUR body if need be.
#define BODY_LAYER 24 //underwear, undershirts, socks, eyes, lips(makeup)
#define FRONT_MUTATIONS_LAYER 23 //mutations that should appear above body, body_adj and bodyparts layer (e.g. laser eyes)
#define DAMAGE_LAYER 22 //damage indicators (cuts and burns)
#define UNIFORM_LAYER 21
#define ID_LAYER 20
#define MUTATIONS_LAYER 32 //mutations. Tk headglows, cold resistance glow, etc
#define GENITALS_BEHIND_LAYER 31 //Some genitalia needs to be behind everything, such as with taurs (Taurs use body_behind_layer
#define BODY_BEHIND_LAYER 30 //certain mutantrace features (tail when looking south) that must appear behind the body parts
#define BODYPARTS_LAYER 29 //Initially "AUGMENTS", this was repurposed to be a catch-all bodyparts flag
#define MARKING_LAYER 28 //Matrixed body markings because clashing with snouts?
#define BODY_ADJ_LAYER 27 //certain mutantrace features (snout, body markings) that must appear above the body parts
#define GENITALS_FRONT_LAYER 26 //Draws some genitalia above clothes and the TAUR body if need be.
#define BODY_LAYER 25 //underwear, undershirts, socks, eyes, lips(makeup)
#define FRONT_MUTATIONS_LAYER 24 //mutations that should appear above body, body_adj and bodyparts layer (e.g. laser eyes)
#define DAMAGE_LAYER 23 //damage indicators (cuts and burns)
#define UNIFORM_LAYER 22
#define ID_LAYER 21
#define HANDS_PART_LAYER 20
#define SHOES_LAYER 19
#define GLOVES_LAYER 18
#define EARS_LAYER 17
#define BODY_TAUR_LAYER 16
#define SUIT_LAYER 15
#define GENITALS_EXPOSED_LAYER 14
#define GLASSES_LAYER 13
#define BELT_LAYER 12 //Possible make this an overlay of somethign required to wear a belt?
#define SUIT_STORE_LAYER 11
@@ -82,7 +83,7 @@ Will print: "/mob/living/carbon/human/death" (you can optionally embed it in a s
#define HANDS_LAYER 3
#define BODY_FRONT_LAYER 2
#define FIRE_LAYER 1 //If you're on fire
#define TOTAL_LAYERS 30 //KEEP THIS UP-TO-DATE OR SHIT WILL BREAK ;_;
#define TOTAL_LAYERS 32 //KEEP THIS UP-TO-DATE OR SHIT WILL BREAK ;_;
//Human Overlay Index Shortcuts for alternate_worn_layer, layers
//Because I *KNOW* somebody will think layer+1 means "above"
+3
View File
@@ -74,6 +74,9 @@
#define STATUS_EFFECT_ICHORIAL_STAIN /datum/status_effect/ichorial_stain //Prevents a servant from being revived by vitality matrices for one minute.
#define STATUS_EFFECT_BREASTS_ENLARGEMENT /datum/status_effect/chem/breast_enlarger //Applied slowdown due to the ominous bulk.
#define STATUS_EFFECT_PENIS_ENLARGEMENT /datum/status_effect/chem/penis_enlarger //More applied slowdown, just like the above.
/////////////
// NEUTRAL //
/////////////
+6 -5
View File
@@ -145,13 +145,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_HIGH_BLOOD "high_blood"
#define TRAIT_PHARMA "hepatic_pharmacokinesis"
#define TRAIT_PARA "paraplegic"
+26 -25
View File
@@ -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(gentlemans_organ_names)
@@ -131,53 +132,53 @@ 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)
/mob/living/carbon/proc/is_groin_exposed(list/L)
if(!L)
L = get_equipped_items()
for(var/obj/item/I in L)
for(var/A in L)
var/obj/item/I = A
if(I.body_parts_covered & GROIN)
return FALSE
return TRUE
/mob/living/carbon/proc/is_chest_exposed(var/list/L)
/mob/living/carbon/proc/is_chest_exposed(list/L)
if(!L)
L = get_equipped_items()
for(var/obj/item/I in L)
for(var/A in L)
var/obj/item/I = A
if(I.body_parts_covered & CHEST)
return FALSE
return TRUE
@@ -195,9 +196,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)
+4
View File
@@ -72,6 +72,10 @@
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
if(!CHECK_BITFIELD(initial(G.genital_flags), GENITAL_BLACKLISTED))
GLOB.genitals_list[initial(G.name)] = gpath
//END OF CIT CHANGES
//Species
-1
View File
@@ -160,7 +160,6 @@
"xenodorsal" = "Standard",
"xenohead" = "Standard",
"xenotail" = "Xenomorph Tail",
"exhibitionist" = FALSE,
"genitals_use_skintone" = FALSE,
"has_cock" = FALSE,
"cock_shape" = pick(GLOB.cock_shapes_list),
+11 -1
View File
@@ -202,4 +202,14 @@ GLOBAL_LIST_INIT(bitfields, list(
"ORGAN_VITAL" = ORGAN_VITAL,
"ORGAN_NO_SPOIL" = ORGAN_NO_SPOIL
),
))
"genital_flags" = list(
"GENITAL_BLACKLISTED" = GENITAL_BLACKLISTED,
"GENITAL_INTERNAL" = GENITAL_INTERNAL,
"GENITAL_HIDDEN" = GENITAL_HIDDEN,
"GENITAL_THROUGH_CLOTHES" = GENITAL_THROUGH_CLOTHES,
"GENITAL_FUID_PRODUCTION" = GENITAL_FUID_PRODUCTION,
"CAN_MASTURBATE_WITH" = CAN_MASTURBATE_WITH,
"MASTURBATE_LINKED_ORGAN" = MASTURBATE_LINKED_ORGAN,
"CAN_CLIMAX_WITH" = CAN_CLIMAX_WITH
)
))
+45
View File
@@ -94,6 +94,51 @@
lose_text = "<span class='notice'>You don't feel as prudish as before.</span>"
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 = "<span class='notice'>You are feeling extra wild.</span>"
lose_text = "<span class='notice'>You don't feel that burning sensation anymore.</span>"
/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 = "<span class='notice'>You desire to be hurt.</span>"
lose_text = "<span class='notice'>Pain has become less exciting for you.</span>"
/datum/quirk/exhibitionism
name = "Exhibitionism"
desc = "You don't mind showing off your bare body to strangers, in fact you find it quite satistying."
value = 0
medical_record_text = "Patient has been diagnosed with exhibitionistic disorder."
mob_trait = TRAIT_EXHIBITIONIST
gain_text = "<span class='notice'>You feel like exposing yourself to the world.</span>"
lose_text = "<span class='notice'>Indecent exposure doesn't sound as charming to you anymore.</span>"
/datum/quirk/pharmacokinesis //Prevents unwanted organ additions.
name = "Acute hepatic pharmacokinesis"
desc = "You've a rare genetic disorder that causes Incubus draft and Sucubus milk to be absorbed by your liver instead."
value = 0
mob_trait = TRAIT_PHARMA
lose_text = "<span class='notice'>Your liver feels different.</span>"
var/active = FALSE
var/power = 0
var/cachedmoveCalc = 1
/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."
@@ -89,30 +89,23 @@
else
to_chat(user, "<span class='notice'>Anomalous error. Summon a coder.</span>")
if(istype(target, /mob/living))
var/mob/living/victim = target
if(istype(victim, /mob/living/carbon/human))
if(user.zone_selected == "groin") // pp smol. There's probably a smarter way to do this but im retarded. If you have a simpler method let me know.
var/list/organs = victim.getorganszone("groin")
for(var/internal_organ in organs)
if(istype(internal_organ, /obj/item/organ/genital/penis))
var/obj/item/organ/genital/penis/O = internal_organ
playsound(get_turf(src), 'sound/weapons/flash.ogg', 50, 1)
victim.visible_message("<span class='warning'>[user] is preparing to shrink [victim]\'s [O.name] with their bluespace compression kit!</span>")
if(do_mob(user, victim, 40) && charges > 0 && O.length > 0)
victim.visible_message("<span class='warning'>[user] has shrunk [victim]\'s [O.name]!</span>")
playsound(get_turf(src), 'sound/weapons/emitter2.ogg', 50, 1)
sparks()
flash_lighting_fx(3, 3, LIGHT_COLOR_CYAN)
charges -= 1
O.length -= 5
if(O.length < 1)
victim.visible_message("<span class='warning'>[user]\'s [O.name] vanishes!</span>")
qdel(O) // no pp for you
else
O.update_size()
O.update_appearance()
else if(ishuman(target) && user.zone_selected == BODY_ZONE_PRECISE_GROIN)
var/mob/living/carbon/human/H = target
var/obj/item/organ/genital/penis/P = H.getorganslot(ORGAN_SLOT_PENIS)
if(!P)
return
playsound(get_turf(src), 'sound/weapons/flash.ogg', 50, 1)
H.visible_message("<span class='warning'>[user] is preparing to shrink [H]\'s [P.name] with their bluespace compression kit!</span>")
if(do_mob(user, H, 40) && charges > 0 && P.length > 0)
H.visible_message("<span class='warning'>[user] has shrunk [H]\'s [P.name]!</span>")
playsound(get_turf(src), 'sound/weapons/emitter2.ogg', 50, 1)
sparks()
flash_lighting_fx(3, 3, LIGHT_COLOR_CYAN)
charges -= 1
var/p_name = P.name
P.modify_size(-5)
if(QDELETED(P))
H.visible_message("<span class='warning'>[H]\'s [p_name] vanishes!</span>")
/obj/item/compressionkit/attackby(obj/item/I, mob/user, params)
+3
View File
@@ -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()
-4
View File
@@ -113,7 +113,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",
@@ -826,7 +825,6 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat +="<td width='300px' height='300px' valign='top'>"
dat += "<h2>Citadel Preferences</h2>" //Because fuck me if preferences can't be fucking modularized and expected to update in a reasonable timeframe.
dat += "<b>Arousal:</b><a href='?_src_=prefs;preference=arousable'>[arousable == TRUE ? "Enabled" : "Disabled"]</a><BR>"
dat += "<b>Exhibitionist:</b><a href='?_src_=prefs;preference=exhibitionist'>[features["exhibitionist"] == TRUE ? "Yes" : "No"]</a><BR>"
dat += "<b>Voracious MediHound sleepers:</b> <a href='?_src_=prefs;preference=hound_sleeper'>[(cit_toggles & MEDIHOUND_SLEEPER) ? "Yes" : "No"]</a><br>"
dat += "<b>Hear Vore Sounds:</b> <a href='?_src_=prefs;preference=toggleeatingnoise'>[(cit_toggles & EATING_NOISES) ? "Yes" : "No"]</a><br>"
dat += "<b>Hear Vore Digestion Sounds:</b> <a href='?_src_=prefs;preference=toggledigestionnoise'>[(cit_toggles & DIGESTION_NOISES) ? "Yes" : "No"]</a><br>"
@@ -2057,8 +2055,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))
+7 -1
View File
@@ -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 23
#define SAVEFILE_VERSION_MAX 24
/*
SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Carn
@@ -109,6 +109,12 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
else if(current_version < 23) // we are fixing a gamebreaking bug.
job_preferences = list() //It loaded null from nonexistant savefile field.
if(current_version < 24 && 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)
return
@@ -79,7 +79,8 @@
//This is an UNSAFE proc. Use mob_can_equip() before calling this one! Or rather use equip_to_slot_if_possible() or advanced_equip_to_slot_if_possible()
/mob/living/carbon/human/equip_to_slot(obj/item/I, slot)
if(!..()) //a check failed or the item has already found its slot
. = ..()
if(!.) //a check failed or the item has already found its slot
return
var/not_handled = FALSE //Added in case we make this type path deeper one day
@@ -136,6 +137,7 @@
update_inv_s_store()
else
to_chat(src, "<span class='danger'>You are trying to equip this item to an unsupported inventory slot. Report this to a coder!</span>")
not_handled = TRUE
//Item is handled and in slot, valid to call callback, for this proc should always be true
if(!not_handled)
+2 -2
View File
@@ -18,7 +18,7 @@
#define THERMAL_PROTECTION_HAND_LEFT 0.025
#define THERMAL_PROTECTION_HAND_RIGHT 0.025
/mob/living/carbon/human/Life()
/mob/living/carbon/human/Life(seconds, times_fired)
set invisibility = 0
if (notransform)
return
@@ -41,7 +41,7 @@
if(stat != DEAD)
//process your dick energy
handle_arousal()
handle_arousal(times_fired)
//Update our name based on whether our face is obscured/disfigured
name = get_visible_name()
@@ -545,7 +545,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(H.hidden_underwear)
H.underwear = "Nude"
else
H.saved_underwear = H.underwear
H.underwear = H.saved_underwear
var/datum/sprite_accessory/underwear/bottom/B = GLOB.underwear_list[H.underwear]
if(B)
var/mutable_appearance/MA = mutable_appearance(B.icon, B.icon_state, -BODY_LAYER)
@@ -557,7 +557,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(H.hidden_undershirt)
H.undershirt = "Nude"
else
H.saved_undershirt = H.undershirt
H.undershirt = H.saved_undershirt
var/datum/sprite_accessory/underwear/top/T = GLOB.undershirt_list[H.undershirt]
if(T)
var/mutable_appearance/MA
@@ -573,7 +573,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(H.hidden_socks)
H.socks = "Nude"
else
H.saved_socks = H.socks
H.socks = H.saved_socks
var/datum/sprite_accessory/underwear/socks/S = GLOB.socks_list[H.socks]
if(S)
var/digilegs = (DIGITIGRADE in species_traits) ? "_d" : ""
@@ -472,37 +472,25 @@
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")
@@ -592,8 +580,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()
@@ -605,8 +593,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")
@@ -618,8 +606,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)
@@ -630,7 +618,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)
@@ -641,7 +629,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
@@ -2265,24 +2265,6 @@
M.emote("nya")
..()
//Kept for legacy, I think it will break everything if you enable it.
/datum/reagent/penis_enlargement
name = "Penis Enlargement"
id = "penis_enlargement"
description = "A patented chemical forumula by Doctor Ronald Hyatt that is guaranteed to bring maximum GROWTH and LENGTH to your penis, today!"
color = "#888888"
taste_description = "chinese dragon powder"
metabolization_rate = INFINITY //So it instantly removes all of itself. Don't want to put strain on the system.
/datum/reagent/penis_enlargement/on_mob_life(mob/living/carbon/C)
var/obj/item/organ/genital/penis/P = C.getorganslot(ORGAN_SLOT_PENIS)
if(P)
var/added_length = round(volume/30,0.01) //Every 30u gives an extra inch. Rounded to the nearest 0.01 so float fuckery doesn't occur with the division by 30.
if(added_length >= 0.20) //Only add the length if it's greater than or equal to 0.2. This is to prevent people from smoking the reagents and causing the penis to update constantly.
P.length += added_length
P.update()
..()
/datum/reagent/changeling_string
name = "UNKNOWN"
id = "changeling_sting_real"
@@ -31,7 +31,7 @@
/obj/item/organ/proc/Insert(mob/living/carbon/M, special = 0, drop_if_replaced = TRUE)
if(!iscarbon(M) || owner == M)
return
return FALSE
var/obj/item/organ/replaced = M.getorganslot(slot)
if(replaced)
@@ -53,6 +53,8 @@
A.Grant(M)
STOP_PROCESSING(SSobj, src)
return TRUE
//Special is for instant replacement like autosurgeons
/obj/item/organ/proc/Remove(mob/living/carbon/M, special = FALSE)
owner = null
@@ -67,6 +69,8 @@
A.Remove(M)
START_PROCESSING(SSobj, src)
return TRUE
/obj/item/organ/proc/on_find(mob/living/finder)
return
@@ -37,64 +37,62 @@
alert_type = null
var/moveCalc = 1
var/cachedmoveCalc = 1
var/last_checked_size //used to prevent potential cpu waste from happening every tick.
/datum/status_effect/chem/breast_enlarger/on_apply(mob/living/carbon/human/H)//Removes clothes, they're too small to contain you. You belong to space now.
/datum/status_effect/chem/breast_enlarger/on_apply()//Removes clothes, they're too small to contain you. You belong to space now.
log_game("FERMICHEM: [owner]'s breasts has reached comical sizes. ID: [owner.key]")
var/mob/living/carbon/human/o = owner
var/items = o.get_contents()
for(var/obj/item/W in items)
if(W == o.w_uniform || W == o.wear_suit)
o.dropItemToGround(W, TRUE)
playsound(o.loc, 'sound/items/poster_ripped.ogg', 50, 1)
to_chat(o, "<span class='warning'>Your clothes give, ripping into peices under the strain of your swelling breasts! Unless you manage to reduce the size of your breasts, there's no way you're going to be able to put anything on over these melons..!</b></span>")
o.visible_message("<span class='boldnotice'>[o]'s chest suddenly bursts forth, ripping their clothes off!'</span>")
else
to_chat(o, "<span class='notice'>Your bountiful bosom is so rich with mass, you seriously doubt you'll be able to fit any clothes over it.</b></span>")
return ..()
var/mob/living/carbon/human/H = owner
var/message = FALSE
if(H.w_uniform)
H.dropItemToGround(H.w_uniform, TRUE)
message = TRUE
if(H.wear_suit)
H.dropItemToGround(H.wear_suit, TRUE)
message = TRUE
if(message)
playsound(H.loc, 'sound/items/poster_ripped.ogg', 50, 1)
H.visible_message("<span class='boldnotice'>[H]'s chest suddenly bursts forth, ripping their clothes off!'</span>", \
"<span class='warning'>Your clothes give, ripping into peices under the strain of your swelling breasts! Unless you manage to reduce the size of your breasts, there's no way you're going to be able to put anything on over these melons..!</b></span>")
else
to_chat(H, "<span class='notice'>Your bountiful bosom is so rich with mass, you seriously doubt you'll be able to fit any clothes over it.</b></span>")
return ..()
/datum/status_effect/chem/breast_enlarger/tick(mob/living/carbon/human/H)//If you try to wear clothes, you fail. Slows you down if you're comically huge
var/mob/living/carbon/human/o = owner
var/obj/item/organ/genital/breasts/B = o.getorganslot("breasts")
moveCalc = 1+((round(B.cached_size) - 9)/3) //Afffects how fast you move, and how often you can click.
/datum/status_effect/chem/breast_enlarger/tick()//If you try to wear clothes, you fail. Slows you down if you're comically huge
var/mob/living/carbon/human/H = owner
var/obj/item/organ/genital/breasts/B = H.getorganslot(ORGAN_SLOT_BREASTS)
if(!B)
o.remove_movespeed_modifier(BREAST_MOVEMENT_SPEED)
sizeMoveMod(1)
owner.remove_status_effect(src)
var/items = o.get_contents()
for(var/obj/item/W in items)
if(W == o.w_uniform || W == o.wear_suit)
o.dropItemToGround(W, TRUE)
playsound(o.loc, 'sound/items/poster_ripped.ogg', 50, 1)
to_chat(owner, "<span class='warning'>Your enormous breasts are way too large to fit anything over them!</b></span>")
H.remove_status_effect(src)
return
moveCalc = 1+((round(B.cached_size) - 9)/3) //Afffects how fast you move, and how often you can click.
var/message = FALSE
if(H.w_uniform)
H.dropItemToGround(H.w_uniform, TRUE)
message = TRUE
if(H.wear_suit)
H.dropItemToGround(H.wear_suit, TRUE)
message = TRUE
if(message)
playsound(H.loc, 'sound/items/poster_ripped.ogg', 50, 1)
to_chat(H, "<span class='warning'>Your enormous breasts are way too large to fit anything over them!</b></span>")
if(last_checked_size != B.cached_size)
H.add_movespeed_modifier(BREAST_MOVEMENT_SPEED, TRUE, 100, NONE, override = TRUE, multiplicative_slowdown = moveCalc)
sizeMoveMod(moveCalc)
if (B.size == "huge")
if(prob(1))
to_chat(owner, "<span class='notice'>Your back is feeling sore.</span>")
var/target = o.get_bodypart(BODY_ZONE_CHEST)
o.apply_damage(0.1, BRUTE, target)
if(!B.cached_size == B.breast_values[B.prev_size])
o.add_movespeed_modifier(BREAST_MOVEMENT_SPEED, TRUE, 100, NONE, override = TRUE, multiplicative_slowdown = moveCalc)
sizeMoveMod(moveCalc)
return ..()
else if (B.breast_values[B.size] > B.breast_values[B.prev_size])
o.add_movespeed_modifier(BREAST_MOVEMENT_SPEED, TRUE, 100, NONE, override = TRUE, multiplicative_slowdown = moveCalc)
sizeMoveMod(moveCalc)
else if (B.breast_values[B.size] < B.breast_values[B.prev_size])
o.add_movespeed_modifier(BREAST_MOVEMENT_SPEED, TRUE, 100, NONE, override = TRUE, multiplicative_slowdown = moveCalc)
sizeMoveMod(moveCalc)
if((B.cached_size) < 16)
switch(round(B.cached_size))
if(9)
if (B.breast_values[B.prev_size] != B.breast_values[B.size])
to_chat(o, "<span class='notice'>Your expansive chest has become a more managable size, liberating your movements.</b></span>")
if(10 to INFINITY)
if (B.breast_values[B.prev_size] != B.breast_values[B.size])
to_chat(H, "<span class='warning'>Your indulgent busom is so substantial, it's affecting your movements!</b></span>")
var/target = H.get_bodypart(BODY_ZONE_CHEST)
H.apply_damage(0.1, BRUTE, target)
else
if(prob(1))
to_chat(owner, "<span class='notice'>Your back is feeling a little sore.</span>")
..()
to_chat(H, "<span class='notice'>Your back is feeling a little sore.</span>")
last_checked_size = B.cached_size
..()
/datum/status_effect/chem/breast_enlarger/on_remove(mob/living/carbon/M)
/datum/status_effect/chem/breast_enlarger/on_remove()
log_game("FERMICHEM: [owner]'s breasts has reduced to an acceptable size. ID: [owner.key]")
to_chat(owner, "<span class='notice'>Your expansive chest has become a more managable size, liberating your movements.</b></span>")
owner.remove_movespeed_modifier(BREAST_MOVEMENT_SPEED)
sizeMoveMod(1)
@@ -112,51 +110,57 @@
alert_type = null
var/bloodCalc
var/moveCalc
var/last_checked_size //used to prevent potential cpu waste, just like the above.
/datum/status_effect/chem/penis_enlarger/on_apply(mob/living/carbon/human/H)//Removes clothes, they're too small to contain you. You belong to space now.
/datum/status_effect/chem/penis_enlarger/on_apply()//Removes clothes, they're too small to contain you. You belong to space now.
log_game("FERMICHEM: [owner]'s dick has reached comical sizes. ID: [owner.key]")
var/mob/living/carbon/human/o = owner
var/items = o.get_contents()
if(o.w_uniform || o.wear_suit)
to_chat(o, "<span class='warning'>Your clothes give, ripping into peices under the strain of your swelling pecker! Unless you manage to reduce the size of your emancipated trouser snake, there's no way you're going to be able to put anything on over this girth..!</b></span>")
owner.visible_message("<span class='boldnotice'>[o]'s schlong suddenly bursts forth, ripping their clothes off!'</span>")
var/mob/living/carbon/human/H = owner
var/message = FALSE
if(H.w_uniform)
H.dropItemToGround(H.w_uniform, TRUE)
message = TRUE
if(H.wear_suit)
H.dropItemToGround(H.wear_suit, TRUE)
message = TRUE
if(message)
playsound(H.loc, 'sound/items/poster_ripped.ogg', 50, 1)
H.visible_message("<span class='boldnotice'>[H]'s schlong suddenly bursts forth, ripping their clothes off!'</span>", \
"<span class='warning'>Your clothes give, ripping into peices under the strain of your swelling pecker! Unless you manage to reduce the size of your emancipated trouser snake, there's no way you're going to be able to put anything on over this girth..!</b></span>")
else
to_chat(o, "<span class='notice'>Your emancipated trouser snake is so ripe with girth, you seriously doubt you'll be able to fit any clothes over it.</b></span>")
for(var/obj/item/W in items)
if(W == o.w_uniform || W == o.wear_suit)
o.dropItemToGround(W, TRUE)
playsound(o.loc, 'sound/items/poster_ripped.ogg', 50, 1)
to_chat(H, "<span class='notice'>Your emancipated trouser snake is so ripe with girth, you seriously doubt you'll be able to fit any clothes over it.</b></span>")
return ..()
/datum/status_effect/chem/penis_enlarger/tick(mob/living/carbon/M)
var/mob/living/carbon/human/o = owner
var/obj/item/organ/genital/penis/P = o.getorganslot("penis")
/datum/status_effect/chem/penis_enlarger/tick()
var/mob/living/carbon/human/H = owner
var/obj/item/organ/genital/penis/P = H.getorganslot(ORGAN_SLOT_PENIS)
if(!P)
owner.remove_status_effect(src)
return
moveCalc = 1+((round(P.length) - 21)/3) //effects how fast you can move
bloodCalc = 1+((round(P.length) - 21)/15) //effects how much blood you need (I didn' bother adding an arousal check because I'm spending too much time on this organ already.)
if(!P)
o.remove_movespeed_modifier(DICK_MOVEMENT_SPEED)
o.ResetBloodVol()
owner.remove_status_effect(src)
var/items = o.get_contents()
for(var/obj/item/W in items)
if(W == o.w_uniform || W == o.wear_suit)
o.dropItemToGround(W, TRUE)
playsound(o.loc, 'sound/items/poster_ripped.ogg', 50, 1)
to_chat(owner, "<span class='warning'>Your enormous package is way to large to fit anything over!</b></span>")
switch(round(P.cached_length))
if(21)
to_chat(o, "<span class='notice'>Your rascally willy has become a more managable size, liberating your movements.</b></span>")
o.remove_movespeed_modifier(DICK_MOVEMENT_SPEED)
o.AdjustBloodVol(bloodCalc)
if(22 to INFINITY)
if(prob(2))
to_chat(o, "<span class='warning'>Your indulgent johnson is so substantial, it's taking all your blood and affecting your movements!</b></span>")
o.add_movespeed_modifier(DICK_MOVEMENT_SPEED, TRUE, 100, NONE, override = TRUE, multiplicative_slowdown = moveCalc)
o.AdjustBloodVol(bloodCalc)
var/message = FALSE
if(H.w_uniform)
H.dropItemToGround(H.w_uniform, TRUE)
message = TRUE
if(H.wear_suit)
H.dropItemToGround(H.wear_suit, TRUE)
message = TRUE
if(message)
playsound(H.loc, 'sound/items/poster_ripped.ogg', 50, 1)
to_chat(H, "<span class='warning'>Your enormous package is way to large to fit anything over!</b></span>")
if(P.length < 22 && H.has_movespeed_modifier(DICK_MOVEMENT_SPEED))
to_chat(owner, "<span class='notice'>Your rascally willy has become a more managable size, liberating your movements.</b></span>")
H.remove_movespeed_modifier(DICK_MOVEMENT_SPEED)
else if(P.length >= 22 && !H.has_movespeed_modifier(DICK_MOVEMENT_SPEED))
to_chat(H, "<span class='warning'>Your indulgent johnson is so substantial, it's taking all your blood and affecting your movements!</b></span>")
H.add_movespeed_modifier(DICK_MOVEMENT_SPEED, TRUE, 100, NONE, override = TRUE, multiplicative_slowdown = moveCalc)
H.AdjustBloodVol(bloodCalc)
..()
/datum/status_effect/chem/penis_enlarger/on_remove(mob/living/carbon/human/o)
/datum/status_effect/chem/penis_enlarger/on_remove()
log_game("FERMICHEM: [owner]'s dick has reduced to an acceptable size. ID: [owner.key]")
owner.remove_movespeed_modifier(DICK_MOVEMENT_SPEED)
owner.ResetBloodVol()
@@ -1,43 +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 = "<span class='notice'>You are feeling extra wild.</span>"
lose_text = "<span class='notice'>You don't feel that burning sensation anymore.</span>"
/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, "<span class='notice'>Having high libido is useless when you can't feel arousal at all!</span>")
qdel(src)
/datum/quirk/maso
name = "Masochism"
desc = "You are aroused by pain."
value = 0
mob_trait = TRAIT_MASO
gain_text = "<span class='notice'>You desire to be hurt.</span>"
lose_text = "<span class='notice'>Pain has become less exciting for you.</span>"
/datum/quirk/pharmacokinesis //Prevents unwanted organ additions.
name = "Acute hepatic pharmacokinesis"
desc = "You've a rare genetic disorder that causes Incubus draft and Sucubus milk to be absorbed by your liver instead."
value = 0
mob_trait = TRAIT_PHARMA
lose_text = "<span class='notice'>Your liver feels different.</span>"
var/active = FALSE
var/power = 0
var/cachedmoveCalc = 1
+261 -413
View File
@@ -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,51 @@
//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(src))
if(L.client && !L.stat && !L.eye_blind && (src in 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 +124,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 +131,16 @@
/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
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 +159,6 @@
to_chat(M, "<span class='warning'>Arousal is disabled. Feature is unavailable.</span>")
/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,225 +166,187 @@
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("<span class='danger'>[src] starts masturbating!</span>", \
if(getArousalLoss() >= 33)//one third of average max_arousal or greater required
visible_message("<span class='danger'>[src] starts masturbating!</span>", \
"<span class='userdanger'>You start masturbating.</span>")
if(do_after(src, 30, target = src))
src.visible_message("<span class='danger'>[src] relieves [p_them()]self!</span>", \
visible_message("<span class='danger'>[src] relieves [p_them()]self!</span>", \
"<span class='userdanger'>You have relieved yourself.</span>")
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm)
setArousalLoss(min_arousal)
else
to_chat(src, "<span class='notice'>You aren't aroused enough for that.</span>")
/obj/item/organ/genital/proc/climaxable(mob/living/carbon/human/H, silent = FALSE) //returns the fluid source (ergo reagents holder) if found.
if(CHECK_BITFIELD(genital_flags, GENITAL_FUID_PRODUCTION))
. = reagents
else
if(linked_organ)
. = linked_organ.reagents
if(!. && !silent)
to_chat(H, "<span class='warning'>Your [name] is unable to produce it's own fluids, it's missing the organs for it.</span>")
/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)
setArousalLoss(min_arousal)
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, "<span class='warning'>Your [G.name] is unable to produce it's own fluids, it's missing the organs for it.</span>")
return
fluid_source = G.linked_organ.reagents
total_fluids = fluid_source.total_volume
var/datum/reagents/fluid_source = G.climaxable(src)
if(!fluid_source)
return
var/obj/item/organ/genital/PP = CHECK_BITFIELD(G.genital_flags, MASTURBATE_LINKED_ORGAN) ? G.linked_organ : G
if(!PP)
to_chat(src, "<span class='warning'>You shudder, unable to cum with your [name].</span>")
if(mb_time)
src.visible_message("<span class='love'>[src] starts to [G.masturbation_verb] [p_their()] [G.name].</span>", \
"<span class='userlove'>You start to [G.masturbation_verb] your [G.name].</span>", \
visible_message("<span class='love'>[src] starts to [G.masturbation_verb] [p_their()] [G.name].</span>", \
"<span class='userlove'>You start to [G.masturbation_verb] your [G.name].</span>")
if(do_after(src, mb_time, target = src))
if(total_fluids > 5)
fluid_source.reaction(src.loc, TOUCH, 1, 0)
fluid_source.clear_reagents()
src.visible_message("<span class='love'>[src] orgasms, cumming[istype(src.loc, /turf/open/floor) ? " onto [src.loc]" : ""]!</span>", \
"<span class='userlove'>You cum[istype(src.loc, /turf/open/floor) ? " onto [src.loc]" : ""].</span>", \
"<span class='userlove'>You have relieved yourself.</span>")
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("<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
var/total_fluids = 0
var/datum/reagents/fluid_source = null
var/unable_to_come = FALSE
if(G.producing) //Can it produce its own fluids, such as breasts?
fluid_source = G.reagents
total_fluids = fluid_source.total_volume
else
if(!G.linked_organ)
unable_to_come = TRUE
else
fluid_source = G.linked_organ.reagents
total_fluids = fluid_source.total_volume
if(unable_to_come)
src.visible_message("<span class='danger'>[src] shudders, their [G.name] unable to cum.</span>", \
"<span class='userdanger'>Your [G.name] cannot cum, giving no relief.</span>", \
var/datum/reagents/fluid_source = G.climaxable(src, TRUE)
if(!fluid_source)
visible_message("<span class='danger'>[src] shudders, their [G.name] unable to cum.</span>", \
"<span class='userdanger'>Your [G.name] cannot cum, giving no relief.</span>")
else
total_fluids = fluid_source.total_volume
if(mb_time) //as long as it's not instant, give a warning
src.visible_message("<span class='love'>[src] looks like they're about to cum.</span>", \
"<span class='userlove'>You feel yourself about to orgasm.</span>", \
"<span class='userlove'>You feel yourself about to orgasm.</span>")
if(do_after(src, mb_time, target = src))
if(total_fluids > 5)
fluid_source.reaction(src.loc, TOUCH, 1, 0)
fluid_source.clear_reagents()
src.visible_message("<span class='love'>[src] orgasms[istype(src.loc, /turf/open/floor) ? ", spilling onto [src.loc]" : ""], using [p_their()] [G.name]!</span>", \
"<span class='userlove'>You climax[istype(src.loc, /turf/open/floor) ? ", spilling onto [src.loc]" : ""] with your [G.name].</span>", \
"<span class='userlove'>You climax using your [G.name].</span>")
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("<span class='love'>[src] looks like they're about to cum.</span>", \
"<span class='userlove'>You feel yourself about to orgasm.</span>")
if(!do_after(src, mb_time, target = src) || !G.climaxable(src, TRUE))
return
visible_message("<span class='love'>[src] orgasms[isturf(loc) ? " onto [loc]" : ""], using [p_their()] [G.name]!</span>", \
"<span class='userlove'>You climax[isturf(loc) ? " onto [loc]" : ""] with your [G.name].</span>")
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, "<span class='warning'>Your [G.name] is unable to produce it's own fluids, it's missing the organs for it.</span>")
return
fluid_source = G.linked_organ.reagents
total_fluids = fluid_source.total_volume
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("<span class='love'>[src] is about to climax with [L]!</span>", \
"<span class='userlove'>You're about to climax with [L]!</span>", \
"<span class='userlove'>You're preparing to climax with someone!</span>")
visible_message("<span class='love'>[src] is about to climax with [L]!</span>", \
"<span class='userlove'>You're about to climax with [L]!</span>")
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("<span class='love'>[src] climaxes with [L][spillage ? ", overflowing and spilling":""], using [p_their()] [G.name]!</span>", \
"<span class='userlove'>You orgasm with [L][spillage ? ", spilling out of them":""], using your [G.name].</span>", \
"<span class='userlove'>You have climaxed with someone[spillage ? ", spilling out of them":""], using your [G.name].</span>")
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("<span class='love'>[src] climaxes with [L], overflowing and spilling, using [p_their()] [G.name]!</span>", \
"<span class='userlove'>You orgasm with [L], spilling out of them, using your [G.name].</span>")
else //knots and other non-spilling orgasms
if(do_after(src, mb_time, target = src) && in_range(src, L))
fluid_source.trans_to(L, total_fluids)
total_fluids = 0
src.visible_message("<span class='love'>[src] climaxes with [L], [p_their()] [G.name] spilling nothing!</span>", \
"<span class='userlove'>You ejaculate with [L], your [G.name] spilling nothing.</span>", \
"<span class='userlove'>You have climaxed inside someone, your [G.name] spilling nothing.</span>")
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("<span class='love'>[src] climaxes with [L], [p_their()] [G.name] spilling nothing!</span>", \
"<span class='userlove'>You ejaculate with [L], your [G.name] spilling nothing.</span>")
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, "<span class='warning'>Your [G.name] is unable to produce it's own fluids, it's missing the organs for it.</span>")
var/datum/reagents/fluid_source = G.climaxable(src)
if(!fluid_source)
return
if(mb_time)
visible_message("<span class='love'>[src] starts to [G.masturbation_verb] their [G.name] over [container].</span>", \
"<span class='userlove'>You start to [G.masturbation_verb] your [G.name] over [container].</span>")
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
visible_message("<span class='love'>[src] uses [p_their()] [G.name] to fill [container]!</span>", \
"<span class='userlove'>You used your [G.name] to fill [container].</span>")
do_climax(fluid_source, container, G, FALSE)
//if(!container) //Something weird happened
// to_chat(src, "<span class='warning'>You need a container to do this!</span>")
// return
src.visible_message("<span class='love'>[src] starts to [G.masturbation_verb] their [G.name] over [container].</span>", \
"<span class='userlove'>You start to [G.masturbation_verb] your [G.name] over [container].</span>", \
"<span class='userlove'>You start to [G.masturbation_verb] your [G.name] over something.</span>")
if(do_after(src, mb_time, target = src) && in_range(src, container))
fluid_source.trans_to(container, total_fluids)
src.visible_message("<span class='love'>[src] uses [p_their()] [G.name] to fill [container]!</span>", \
"<span class='userlove'>You used your [G.name] to fill [container].</span>", \
"<span class='userlove'>You have relieved some pressure.</span>")
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm)
if(G.can_climax)
setArousalLoss(min_arousal)
/mob/living/carbon/human/proc/pick_masturbate_genitals()
var/obj/item/organ/genital/ret_organ
var/list/genitals_list = list()
/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(G.can_masturbate_with) //filter out what you can't masturbate with
if(G.is_exposed(worn_stuff)) //Nude or through_clothing
genitals_list += G
if(genitals_list.len)
ret_organ = input(src, "with what?", "Masturbate", null) as null|obj in genitals_list
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
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
return null //error stuff
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/obj/item/organ/genital/ret_organ
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(G.can_climax) //filter out what you can't masturbate with
if(G.is_exposed(worn_stuff)) //Nude or through_clothing
genitals_list += G
if(genitals_list.len)
ret_organ = input(src, "with what?", "Climax", null) as null|obj in genitals_list
if(CHECK_BITFIELD(G.genital_flags, CAN_CLIMAX_WITH) && G.is_exposed(worn_stuff)) //filter out what you can't masturbate with
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
return null //error stuff
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(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
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()
var/obj/item/reagent_containers/SC = null
/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)
SC = input(src, "Into or onto what?(Cancel for nowhere)", null) as null|obj in containers_list
if(SC)
if(in_range(src, SC))
return SC
return null //If nothing correct, give null.
var/obj/item/reagent_containers/SC = input(src, "Into or onto what?(Cancel for nowhere)", null) as null|obj in containers_list
if(SC && CanReach(SC))
return SC
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, 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
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,156 +354,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==2)
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/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()
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(src.pulling) //Are we pulling someone? Priority target, we can't be making option menus for this, has to be quick
if(isliving(src.pulling)) //Don't fuck objects
check_target = src.pulling
if(src.pulledby && !check_target) //prioritise pulled over pulledby
if(isliving(src.pulledby))
check_target = src.pulledby
//Now we should have a partner, or else we have to come alone
if(check_target)
if(iscarbon(check_target)) //carbons can have clothes
var/mob/living/carbon/C = check_target
if(C.exposed_genitals.len || C.is_groin_exposed() || C.is_chest_exposed()) //Are they naked enough?
partner = C
else //A cat is fine too
partner = check_target
if(partner) //Did they pass the clothing checks?
mob_climax_partner(G, partner, mb_time = 0) //Instant climax due to forced
continue //You've climaxed once with this organ, continue on
//not exposed OR if no partner was found while exposed, climax alone
mob_climax_outside(G, mb_time = 0) //removed climax timer for sudden, forced orgasms
//Now all genitals that could climax, have.
//Since this was a forced climax, we do not need to continue with the other stuff
return
//If we get here, then this is not a forced climax and we gotta check a few things.
if(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==1) //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:") 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(restrained(TRUE)) //TRUE ignores grabs
to_chat(src, "<span class='warning'>You can't do that while restrained!</span>")
return
var/free_hands = get_num_arms()
if(!free_hands)
to_chat(src, "<span class='warning'>You need at least one free arm.</span>")
return
for(var/helditem in held_items)//how many hands are free
if(isobj(helditem))
free_hands--
if(free_hands <= 0)
to_chat(src, "<span class='warning'>You're holding too many things.</span>")
return
//We got hands, let's pick an organ
var/obj/item/organ/genital/picked_organ
picked_organ = pick_masturbate_genitals()
if(picked_organ)
mob_masturbate(picked_organ)
return
else //They either lack organs that can masturbate, or they didn't pick one.
to_chat(src, "<span class='warning'>You cannot masturbate without choosing genitals.</span>")
return
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(restrained(TRUE)) //TRUE ignores grabs
to_chat(src, "<span class='warning'>You can't do that while restrained!</span>")
return
var/free_hands = get_num_arms()
if(!free_hands)
to_chat(src, "<span class='warning'>You need at least one free arm.</span>")
return
for(var/helditem in held_items)//how many hands are free
if(isobj(helditem))
free_hands--
if(free_hands <= 0)
to_chat(src, "<span class='warning'>You're holding too many things.</span>")
return
//We got hands, let's pick an organ
var/obj/item/organ/genital/picked_organ
picked_organ = pick_climax_genitals()
if(picked_organ)
mob_climax_outside(picked_organ)
return
else //They either lack organs that can masturbate, or they didn't pick one.
to_chat(src, "<span class='warning'>You cannot climax without choosing genitals.</span>")
return
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
picked_organ = pick_climax_genitals()
if(picked_organ)
var/mob/living/partner = pick_partner() //Get someone
if(partner)
var/spillage = input(src, "Would your fluids spill outside?", "Choose overflowing option", "Yes") as anything in list("Yes", "No")
if(spillage == "Yes")
mob_climax_partner(picked_organ, partner, TRUE)
else
mob_climax_partner(picked_organ, partner, FALSE)
return
else
to_chat(src, "<span class='warning'>You cannot do this alone.</span>")
return
else //They either lack organs that can masturbate, or they didn't pick one.
to_chat(src, "<span class='warning'>You cannot climax without choosing genitals.</span>")
return
if("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(restrained(TRUE)) //TRUE ignores grabs
to_chat(src, "<span class='warning'>You can't do that while restrained!</span>")
return
var/free_hands = get_num_arms()
if(!free_hands)
to_chat(src, "<span class='warning'>You need at least one free arm.</span>")
return
for(var/helditem in held_items)//how many hands are free
if(isobj(helditem))
free_hands--
if(free_hands <= 0)
to_chat(src, "<span class='warning'>You're holding too many things.</span>")
return
//We got hands, let's pick an organ
var/obj/item/organ/genital/picked_organ
picked_organ = pick_climax_genitals() //Gotta be climaxable, not just masturbation, to fill with fluids.
if(picked_organ)
//Good, got an organ, time to pick a container
var/obj/item/reagent_containers/fluid_container = pick_climax_container()
if(fluid_container)
mob_fill_container(picked_organ, fluid_container)
return
else
to_chat(src, "<span class='warning'>You cannot do this without anything to fill.</span>")
return
else //They either lack organs that can climax, or they didn't pick one.
to_chat(src, "<span class='warning'>You cannot fill anything without choosing genitals.</span>")
return
else //Somehow another option was taken, maybe something interrupted the selection or it was cancelled
return //Just end it in that case.
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
@@ -0,0 +1,339 @@
/obj/item/organ/genital
color = "#fcccb3"
w_class = WEIGHT_CLASS_NORMAL
var/shape = "human"
var/sensitivity = AROUSAL_START_VALUE
var/genital_flags //see citadel_defines.dm
var/masturbation_verb = "masturbate"
var/orgasm_verb = "cumming" //present continous
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
var/fluid_efficiency = 1
var/fluid_rate = CUM_RATE
var/fluid_mult = 1
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 //used for linking an apparatus' organ to its other half on update_link().
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(mapload, mob/living/carbon/human/H)
. = ..()
if(fluid_id)
create_reagents(fluid_max_volume)
if(CHECK_BITFIELD(genital_flags, GENITAL_FUID_PRODUCTION))
reagents.add_reagent(fluid_id, fluid_max_volume)
if(H)
get_features(H)
Insert(H)
else
update()
/obj/item/organ/genital/Destroy()
if(linked_organ)
update_link(TRUE)//this should remove any other links it has
if(owner)
Remove(owner, TRUE)//this should remove references to it, so it can be GCd correctly
return ..()
/obj/item/organ/genital/proc/update(removing = FALSE)
if(QDELETED(src))
return
update_size()
update_appearance()
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 || CHECK_BITFIELD(genital_flags, GENITAL_INTERNAL) || CHECK_BITFIELD(genital_flags, GENITAL_HIDDEN))
return FALSE
if(CHECK_BITFIELD(genital_flags, GENITAL_THROUGH_CLOTHES))
return TRUE
switch(zone) //update as more genitals are added
if(BODY_ZONE_CHEST)
return owner.is_chest_exposed()
if(BODY_ZONE_PRECISE_GROIN)
return owner.is_groin_exposed()
/obj/item/organ/genital/proc/toggle_visibility(visibility)
switch(visibility)
if("Always visible")
ENABLE_BITFIELD(genital_flags, GENITAL_THROUGH_CLOTHES)
DISABLE_BITFIELD(genital_flags, GENITAL_HIDDEN)
if(!(src in owner.exposed_genitals))
owner.exposed_genitals += src
if("Hidden by clothes")
DISABLE_BITFIELD(genital_flags, GENITAL_THROUGH_CLOTHES)
DISABLE_BITFIELD(genital_flags, GENITAL_HIDDEN)
if(src in owner.exposed_genitals)
owner.exposed_genitals -= src
if("Always hidden")
DISABLE_BITFIELD(genital_flags, GENITAL_THROUGH_CLOTHES)
ENABLE_BITFIELD(genital_flags, GENITAL_HIDDEN)
if(src in owner.exposed_genitals)
owner.exposed_genitals -= src
if(ishuman(owner)) //recast to use update genitals proc
var/mob/living/carbon/human/H = owner
H.update_genitals()
/mob/living/carbon/verb/toggle_genitals()
set category = "IC"
set name = "Expose/Hide genitals"
set desc = "Allows you to toggle which genitals should show through clothes or not."
var/list/genital_list = list()
for(var/obj/item/organ/O in internal_organs)
if(isgenital(O))
var/obj/item/organ/genital/G = O
if(!CHECK_BITFIELD(G.genital_flags, GENITAL_INTERNAL))
genital_list += G
if(!genital_list.len) //There is nothing to expose
return
//Full list of exposable genitals created
var/obj/item/organ/genital/picked_organ
picked_organ = input(src, "Choose which genitalia to expose/hide", "Expose/Hide genitals", null) in genital_list
if(picked_organ)
var/picked_visibility = input(src, "Choose visibility setting", "Expose/Hide genitals", "Hidden by clothes") in list("Always visible", "Hidden by clothes", "Always hidden")
picked_organ.toggle_visibility(picked_visibility)
return
/obj/item/organ/genital/proc/modify_size(modifier, min = -INFINITY, max = INFINITY)
return
/obj/item/organ/genital/proc/update_size()
return
/obj/item/organ/genital/proc/update_appearance()
if(!owner || owner.stat == DEAD)
aroused_state = FALSE
/obj/item/organ/genital/on_life()
if(!reagents || !owner)
return
reagents.maximum_volume = fluid_max_volume
if(fluid_id && CHECK_BITFIELD(genital_flags, GENITAL_FUID_PRODUCTION))
generate_fluid()
/obj/item/organ/genital/proc/generate_fluid()
var/amount = fluid_rate
if(!reagents.total_volume && amount < 0.1) // Apparently, 0.015 gets rounded down to zero and no reagents are created if we don't start it with 0.1 in the tank.
amount += 0.1
var/multiplier = fluid_mult
if(reagents.total_volume >= 5)
multiplier *= 0.5
if(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, (amount * multiplier))//generate the cum
return TRUE
return FALSE
/obj/item/organ/genital/proc/update_link(removing = FALSE)
if(!removing && owner)
if(linked_organ)
return
linked_organ = owner.getorganslot(linked_organ_slot)
if(linked_organ)
linked_organ.linked_organ = src
linked_organ.upon_link()
upon_link()
return TRUE
else
if(linked_organ)
linked_organ.linked_organ = null
linked_organ = null
return FALSE
//post organ duo making arrangements.
/obj/item/organ/genital/proc/upon_link()
return
/obj/item/organ/genital/Insert(mob/living/carbon/M, special = FALSE, drop_if_replaced = TRUE)
. = ..()
if(.)
update()
RegisterSignal(owner, COMSIG_MOB_DEATH, .proc/update_appearance)
/obj/item/organ/genital/Remove(mob/living/carbon/M, special = FALSE, drop_if_replaced = TRUE)
. = ..()
if(.)
update(TRUE)
UnregisterSignal(M, COMSIG_MOB_DEATH)
//proc to give a player their genitals and stuff when they log in
/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)
for(var/obj/item/organ/genital/G in internal_organs)
qdel(G)
if (NOGENITALS in dna.species.species_traits)
return
if(dna.features["has_vag"])
give_genital(/obj/item/organ/genital/vagina)
if(dna.features["has_womb"])
give_genital(/obj/item/organ/genital/womb)
if(dna.features["has_balls"])
give_genital(/obj/item/organ/genital/testicles)
if(dna.features["has_breasts"])
give_genital(/obj/item/organ/genital/breasts)
if(dna.features["has_cock"])
give_genital(/obj/item/organ/genital/penis)
/*
if(dna.features["has_ovi"])
give_genital(/obj/item/organ/genital/ovipositor)
if(dna.features["has_eggsack"])
give_genital(/obj/item/organ/genital/eggsack)
*/
/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
G = new G(null, src)
return G
/obj/item/organ/genital/proc/get_features(mob/living/carbon/human/H)
return
/datum/species/proc/genitals_layertext(layer)
switch(layer)
if(GENITALS_BEHIND_LAYER)
return "BEHIND"
if(GENITALS_FRONT_LAYER)
return "FRONT"
//procs to handle sprite overlays being applied to humans
/mob/living/carbon/human/equip_to_slot(obj/item/I, slot)
. = ..()
if(!. && I && slot && !(slot in GLOB.no_genitals_update_slots)) //the item was successfully equipped, and the chosen slot wasn't merely storage, hands or cuffs.
update_genitals()
/mob/living/carbon/human/doUnEquip(obj/item/I, force, newloc, no_move, invdrop = TRUE)
var/no_update = FALSE
if(!I || I == l_store || I == r_store || I == s_store || I == handcuffed || I == legcuffed || get_held_index_of_item(I)) //stops storages, cuffs and held items from triggering it.
no_update = TRUE
. = ..()
if(!. || no_update)
return
update_genitals()
/mob/living/carbon/human/proc/update_genitals()
if(!QDELETED(src))
dna.species.handle_genitals(src)
//Checks to see if organs are new on the mob, and changes their colours so that they don't get crazy colours.
/mob/living/carbon/human/proc/emergent_genital_call()
if(!canbearoused)
return FALSE
var/organCheck = locate(/obj/item/organ/genital) in internal_organs
var/breastCheck = getorganslot(ORGAN_SLOT_BREASTS)
var/willyCheck = getorganslot(ORGAN_SLOT_PENIS)
if(organCheck == FALSE)
if(ishuman(src) && dna.species.id == "human")
dna.features["genitals_use_skintone"] = TRUE
dna.species.use_skintones = TRUE
if(MUTCOLORS)
if(src.dna.species.fixed_mut_color)
dna.features["cock_color"] = "[dna.species.fixed_mut_color]"
dna.features["breasts_color"] = "[dna.species.fixed_mut_color]"
return
//So people who haven't set stuff up don't get rainbow surprises.
dna.features["cock_color"] = "[dna.features["mcolor"]]"
dna.features["breasts_color"] = "[dna.features["mcolor"]]"
else //If there's a new organ, make it the same colour.
if(breastCheck == FALSE)
dna.features["breasts_color"] = dna.features["cock_color"]
else if (willyCheck == FALSE)
dna.features["cock_color"] = dna.features["breasts_color"]
return TRUE
/datum/species/proc/handle_genitals(mob/living/carbon/human/H)//more like handle sadness
if(!H)//no args
CRASH("H = null")
if(!LAZYLEN(H.internal_organs) || ((NOGENITALS in species_traits) && !H.genital_override) || HAS_TRAIT(H, TRAIT_HUSK))
return
var/list/relevant_layers = list(GENITALS_BEHIND_LAYER, GENITALS_FRONT_LAYER)
for(var/L in relevant_layers) //Less hardcode
H.remove_overlay(L)
H.remove_overlay(GENITALS_EXPOSED_LAYER)
//start scanning for genitals
var/list/gen_index[GENITAL_LAYER_INDEX_LENGTH]
var/list/genitals_to_add
var/list/fully_exposed
for(var/obj/item/organ/genital/G in H.internal_organs)
if(G.is_exposed()) //Checks appropriate clothing slot and if it's through_clothes
LAZYADD(gen_index[G.layer_index], G)
for(var/L in gen_index)
if(L) //skip nulls
LAZYADD(genitals_to_add, L)
if(!genitals_to_add)
return
//Now we added all genitals that aren't internal and should be rendered
//start applying overlays
for(var/layer in relevant_layers)
var/list/standing = list()
var/layertext = genitals_layertext(layer)
for(var/A in genitals_to_add)
var/obj/item/organ/genital/G = A
var/datum/sprite_accessory/S
var/size = G.size
var/aroused_state = G.aroused_state
switch(G.type)
if(/obj/item/organ/genital/penis)
S = GLOB.cock_shapes_list[G.shape]
if(/obj/item/organ/genital/testicles)
S = GLOB.balls_shapes_list[G.shape]
if(/obj/item/organ/genital/vagina)
S = GLOB.vagina_shapes_list[G.shape]
if(/obj/item/organ/genital/breasts)
S = GLOB.breasts_shapes_list[G.shape]
if(!S || S.icon_state == "none")
continue
var/mutable_appearance/genital_overlay = mutable_appearance(S.icon, layer = -layer)
genital_overlay.icon_state = "[G.slot]_[S.icon_state]_[size]_[aroused_state]_[layertext]"
if(S.center)
genital_overlay = center_image(genital_overlay, S.dimension_x, S.dimension_y)
if(use_skintones && H.dna.features["genitals_use_skintone"])
genital_overlay.color = "#[skintone2hex(H.skin_tone)]"
genital_overlay.icon_state = "[G.slot]_[S.icon_state]_[size]-s_[aroused_state]_[layertext]"
else
switch(S.color_src)
if("cock_color")
genital_overlay.color = "#[H.dna.features["cock_color"]]"
if("balls_color")
genital_overlay.color = "#[H.dna.features["balls_color"]]"
if("breasts_color")
genital_overlay.color = "#[H.dna.features["breasts_color"]]"
if("vag_color")
genital_overlay.color = "#[H.dna.features["vag_color"]]"
if(layer == GENITALS_FRONT_LAYER && CHECK_BITFIELD(G.genital_flags, GENITAL_THROUGH_CLOTHES))
genital_overlay.layer = -GENITALS_EXPOSED_LAYER
LAZYADD(fully_exposed, genital_overlay) // to be added to a layer with higher priority than clothes, hence the name of the bitflag.
else
standing += genital_overlay
if(LAZYLEN(standing))
H.overlays_standing[layer] = standing
if(LAZYLEN(fully_exposed))
H.overlays_standing[GENITALS_EXPOSED_LAYER] = fully_exposed
H.apply_overlay(GENITALS_EXPOSED_LAYER)
for(var/L in relevant_layers)
H.apply_overlay(L)
@@ -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"
@@ -75,27 +73,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"
@@ -131,35 +123,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"
@@ -1,50 +1,29 @@
/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"
w_class = 3
size = BREASTS_SIZE_DEF //SHOULD BE A LETTER, starts as a number...???
var/cached_size = null //for enlargement SHOULD BE A NUMBER
var/prev_size //For flavour texts SHOULD BE A LETTER
//var/breast_sizes = list ("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "huge", "flat")
var/breast_values = list ("a" = 1, "b" = 2, "c" = 3, "d" = 4, "e" = 5, "f" = 6, "g" = 7, "h" = 8, "i" = 9, "j" = 10, "k" = 11, "l" = 12, "m" = 13, "n" = 14, "o" = 15, "huge" = 16, "flat" = 0)
var/statuscheck = FALSE
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
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 = "c" //refer to the breast_values static list below for the cups associated number values
fluid_id = "milk"
shape = "pair"
genital_flags = CAN_MASTURBATE_WITH|CAN_CLIMAX_WITH|GENITAL_FUID_PRODUCTION
masturbation_verb = "massage"
orgasm_verb = "leaking"
fluid_transfer_factor = 0.5
var/static/list/breast_values = list("a" = 1, "b" = 2, "c" = 3, "d" = 4, "e" = 5, "f" = 6, "g" = 7, "h" = 8, "i" = 9, "j" = 10, "k" = 11, "l" = 12, "m" = 13, "n" = 14, "o" = 15, "huge" = 16, "flat" = 0)
var/cached_size //these two vars pertain size modifications and so should be expressed in NUMBERS.
var/prev_size //former cached_size value, to allow update_size() to early return should be there no significant changes.
/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)
if(reagents.total_volume == 0) // Apparently, 0.015 gets rounded down to zero and no reagents are created if we don't start it with 0.1 in the tank.
fluid_rate = 0.1
else
fluid_rate = CUM_RATE
if(reagents.total_volume >= 5)
fluid_mult = 0.5
else
fluid_mult = 1
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))
/obj/item/organ/genital/breasts/Initialize(mapload, mob/living/carbon/human/H)
if(!H)
cached_size = breast_values[size]
prev_size = cached_size
return ..()
/obj/item/organ/genital/breasts/update_appearance()
. = ..()
var/lowershape = lowertext(shape)
switch(lowershape)
if("pair")
@@ -55,16 +34,15 @@
desc = "You see three sets of breasts, running from their chest to their belly."
else
desc = "You see some breasts, they seem to be quite exotic."
if(cached_size > 16)
if(size == "huge")
desc = "You see [pick("some serious honkers", "a real set of badonkers", "some dobonhonkeros", "massive dohoonkabhankoloos", "two big old tonhongerekoogers", "a couple of giant bonkhonagahoogs", "a pair of humongous hungolomghnonoloughongous")]. Their volume is way beyond cupsize now, measuring in about [round(cached_size)]cm in diameter."
else if (!isnum(size))
else
if (size == "flat")
desc += " They're very small and flatchested, however."
else
desc += " You estimate that they're [uppertext(size)]-cups."
//string = "breasts_[lowertext(shape)]_[size]-s"
if(producing && aroused_state)
if(CHECK_BITFIELD(genital_flags, GENITAL_FUID_PRODUCTION) && aroused_state)
desc += " They're leaking [fluid_id]."
var/string
if(owner)
@@ -80,57 +58,70 @@
var/mob/living/carbon/human/H = owner
icon_state = sanitize_text(string)
H.update_genitals()
icon_state = sanitize_text(string)
//Allows breasts to grow and change size, with sprite changes too.
//maximum wah
//Comical sizes slow you down in movement and actions.
//Rediculous sizes makes you more cumbersome.
//this is far too lewd wah
/obj/item/organ/genital/breasts/update_size()//wah
if(!ishuman(owner) || !owner)
/obj/item/organ/genital/breasts/modify_size(modifier, min = -INFINITY, max = INFINITY)
var/new_value = CLAMP(cached_size + modifier, min, max)
if(new_value == cached_size)
return
if(cached_size < 0)//I don't actually know what round() does to negative numbers, so to be safe!!fixed
to_chat(owner, "<span class='warning'>You feel your breasts shrinking away from your body as your chest flattens out.</b></span>")
src.Remove(owner)
switch(round(cached_size))
if(0) //If flatchested
size = "flat"
if(owner.has_status_effect(/datum/status_effect/chem/breast_enlarger))
owner.remove_status_effect(/datum/status_effect/chem/breast_enlarger)
statuscheck = FALSE
if(1 to 8) //If modest size
size = breast_values[round(cached_size)]
if(owner.has_status_effect(/datum/status_effect/chem/breast_enlarger))
owner.remove_status_effect(/datum/status_effect/chem/breast_enlarger)
statuscheck = FALSE
if(9 to 15) //If massive
size = breast_values[round(cached_size)]
if(!owner.has_status_effect(/datum/status_effect/chem/breast_enlarger))
owner.apply_status_effect(/datum/status_effect/chem/breast_enlarger)
statuscheck = TRUE
if(16 to INFINITY) //if Rediculous
size = cached_size
prev_size = cached_size
cached_size = new_value
update()
if(round(cached_size) < 16)//Because byond doesn't count from 0, I have to do this.
if (prev_size == 0)
prev_size = "flat"
if(size == 0)//Bloody byond with it's counting from 1
/obj/item/organ/genital/breasts/update_size()//wah
var/rounded_cached = round(cached_size)
if(cached_size < 0)//I don't actually know what round() does to negative numbers, so to be safe!!fixed
if(owner)
to_chat(owner, "<span class='warning'>You feel your breasts shrinking away from your body as your chest flattens out.</span>")
QDEL_IN(src, 1)
return
var/enlargement = FALSE
switch(rounded_cached)
if(0) //flatchested
size = "flat"
if(isnum(prev_size))
prev_size = breast_values[prev_size]
if (breast_values[size] > breast_values[prev_size])
to_chat(owner, "<span class='warning'>Your breasts [pick("swell up to", "flourish into", "expand into", "burst forth into", "grow eagerly into", "amplify into")] a [uppertext(size)]-cup.</b></span>")
var/mob/living/carbon/human/H = owner
H.Force_update_genitals()
else if ((breast_values[size] < breast_values[prev_size]) && (breast_values[size] > 0.5))
to_chat(owner, "<span class='warning'>Your breasts [pick("shrink down to", "decrease into", "diminish into", "deflate into", "shrivel regretfully into", "contracts into")] a [uppertext(size)]-cup.</b></span>")
var/mob/living/carbon/human/H = owner
H.Force_update_genitals()
prev_size = size
else if (cached_size >= 16)
size = "huge"
if(1 to 8) //modest
size = breast_values[rounded_cached]
if(9 to 15) //massive
size = breast_values[rounded_cached]
enlargement = TRUE
if(16 to INFINITY) //rediculous
size = "huge"
enlargement = TRUE
if(owner)
var/status_effect = owner.has_status_effect(STATUS_EFFECT_BREASTS_ENLARGEMENT)
if(enlargement && !status_effect)
owner.apply_status_effect(STATUS_EFFECT_BREASTS_ENLARGEMENT)
else if(!enlargement && status_effect)
owner.remove_status_effect(STATUS_EFFECT_BREASTS_ENLARGEMENT)
if(rounded_cached < 16 && owner)//Because byond doesn't count from 0, I have to do this.
var/mob/living/carbon/human/H = owner
var/r_prev_size = round(prev_size)
if (rounded_cached > r_prev_size)
to_chat(H, "<span class='warning'>Your breasts [pick("swell up to", "flourish into", "expand into", "burst forth into", "grow eagerly into", "amplify into")] a [uppertext(size)]-cup.</span>")
else if (rounded_cached < r_prev_size)
to_chat(H, "<span class='warning'>Your breasts [pick("shrink down to", "decrease into", "diminish into", "deflate into", "shrivel regretfully into", "contracts into")] a [uppertext(size)]-cup.</span>")
/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"]
if(!D.features["breasts_producing"])
DISABLE_BITFIELD(genital_flags, GENITAL_FUID_PRODUCTION)
if(!isnum(size))
cached_size = breast_values[size]
else
cached_size = size
size = breast_values[size]
prev_size = cached_size
@@ -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
internal = TRUE
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
genital_flags = GENITAL_INTERNAL|GENITAL_BLACKLISTED //unimplemented
linked_organ_slot = ORGAN_SLOT_PENIS
color = null //don't use the /genital color since it already is colored
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
@@ -1,414 +0,0 @@
/obj/item/organ/genital
color = "#fcccb3"
w_class = WEIGHT_CLASS_NORMAL
var/shape = "Human" //Changed to be uppercase, let me know if this breaks everything..!!
var/sensitivity = AROUSAL_START_VALUE
var/list/genital_flags = list()
var/can_masturbate_with = FALSE
var/masturbation_verb = "masturbate"
var/can_climax = FALSE
var/fluid_transfer_factor = 0.0 //How much would a partner get in them if they climax using this?
var/size = 2 //can vary between num or text, just used in icon_state strings
var/fluid_id = null
var/fluid_max_volume = 15
var/fluid_efficiency = 1
var/fluid_rate = 1
var/fluid_mult = 1
var/producing = FALSE
var/aroused_state = FALSE //Boolean used in icon_state strings
var/aroused_amount = 50 //This is a num from 0 to 100 for arousal percentage for when to use arousal state icons.
var/obj/item/organ/genital/linked_organ
var/through_clothes = FALSE
var/internal = FALSE
var/hidden = FALSE
/obj/item/organ/genital/Initialize()
. = ..()
if(!reagents)
create_reagents(fluid_max_volume)
update()
/obj/item/organ/genital/Destroy()
remove_ref()
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
return ..()
/obj/item/organ/genital/proc/update()
if(QDELETED(src))
return
update_size()
update_appearance()
update_link()
//exposure and through-clothing code
/mob/living/carbon
var/list/exposed_genitals = list() //Keeping track of them so we don't have to iterate through every genitalia and see if exposed
/obj/item/organ/genital/proc/is_exposed()
if(!owner)
return FALSE
if(hidden)
return FALSE
if(internal)
return FALSE
if(through_clothes)
return TRUE
switch(zone) //update as more genitals are added
if("chest")
return owner.is_chest_exposed()
if("groin")
return owner.is_groin_exposed()
return FALSE
/obj/item/organ/genital/proc/toggle_visibility(visibility)
switch(visibility)
if("Always visible")
through_clothes = TRUE
hidden = FALSE
if(!(src in owner.exposed_genitals))
owner.exposed_genitals += src
if("Hidden by clothes")
through_clothes = FALSE
hidden = TRUE
if(src in owner.exposed_genitals)
owner.exposed_genitals -= src
if("Always hidden")
through_clothes = FALSE
hidden = TRUE
if(src in owner.exposed_genitals)
owner.exposed_genitals -= src
if(ishuman(owner)) //recast to use update genitals proc
var/mob/living/carbon/human/H = owner
H.update_genitals()
/mob/living/carbon/verb/toggle_genitals()
set category = "IC"
set name = "Expose/Hide genitals"
set desc = "Allows you to toggle which genitals should show through clothes or not."
var/list/genital_list = list()
for(var/obj/item/organ/O in internal_organs)
if(isgenital(O))
var/obj/item/organ/genital/G = O
if(!G.internal)
genital_list += G
if(!genital_list.len) //There is nothing to expose
return
//Full list of exposable genitals created
var/obj/item/organ/genital/picked_organ
picked_organ = input(src, "Choose which genitalia to expose/hide", "Expose/Hide genitals", null) in genital_list
if(picked_organ)
var/picked_visibility = input(src, "Choose visibility setting", "Expose/Hide genitals", "Hidden by clothes") in list("Always visible", "Hidden by clothes", "Always hidden")
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/proc/remove_ref()
if(linked_organ)
linked_organ.linked_organ = null
linked_organ = null
/obj/item/organ/genital/Insert(mob/living/carbon/M, special = 0)
..()
update()
/obj/item/organ/genital/Remove(mob/living/carbon/M, special = 0)
..()
update()
//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
if(clean)
var/obj/item/organ/genital/GtoClean
for(GtoClean in internal_organs)
qdel(GtoClean)
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()
if(dna.features["has_womb"])
give_womb()
if(dna.features["has_balls"])
give_balls()
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()
if(dna.features["has_cock"])
give_penis()
if(dna.features["has_ovi"])
give_ovipositor()
if(dna.features["has_eggsack"])
give_eggsack()
/mob/living/carbon/human/proc/give_penis()
if(!dna)
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.prev_length = P.length
P.cached_length = P.length
P.update()
/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"]
if(!isnum(B.size))
if(B.size == "flat")
B.cached_size = 0
B.prev_size = 0
else if (B.cached_size == "huge")
B.prev_size = "huge"
else
B.cached_size = B.breast_values[B.size]
B.prev_size = B.size
else
B.cached_size = B.size
B.prev_size = B.size
B.shape = dna.features["breasts_shape"]
B.fluid_id = dna.features["breasts_fluid"]
B.producing = dna.features["breasts_producing"]
B.update()
/mob/living/carbon/human/proc/give_ovipositor()
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)
if(GENITALS_BEHIND_LAYER)
return "BEHIND"
/*if(GENITALS_ADJ_LAYER)
return "ADJ"*/
if(GENITALS_FRONT_LAYER)
return "FRONT"
//procs to handle sprite overlays being applied to humans
/obj/item/equipped(mob/user, slot)
if(ishuman(user))
var/mob/living/carbon/human/H = user
H.update_genitals()
..()
/mob/living/carbon/human/doUnEquip(obj/item/I, force)
. = ..()
if(!.)
return
update_genitals()
/mob/living/carbon/human/proc/update_genitals()
if(src && !QDELETED(src))
dna.species.handle_genitals(src)
//fermichem procs
/mob/living/carbon/human/proc/Force_update_genitals(mob/living/carbon/human/H) //called in fermiChem
dna.species.handle_genitals(src)//should work.
//dna.species.handle_breasts(src)
//Checks to see if organs are new on the mob, and changes their colours so that they don't get crazy colours.
/mob/living/carbon/human/proc/emergent_genital_call()
var/organCheck = FALSE
var/breastCheck = FALSE
var/willyCheck = FALSE
if(!canbearoused)
ADD_TRAIT(src, TRAIT_PHARMA, "pharma")//Prefs prevent unwanted organs.
return
for(var/obj/item/organ/O in internal_organs)
if(istype(O, /obj/item/organ/genital))
organCheck = TRUE
if(/obj/item/organ/genital/penis)
//dna.features["has_cock"] = TRUE
willyCheck = TRUE
if(/obj/item/organ/genital/breasts)
//dna.features["has_breasts"] = TRUE//Goddamnit get in there.
breastCheck = TRUE
if(organCheck == FALSE)
if(ishuman(src) && dna.species.id == "human")
dna.features["genitals_use_skintone"] = TRUE
dna.species.use_skintones = TRUE
if(MUTCOLORS)
if(src.dna.species.fixed_mut_color)
dna.features["cock_color"] = "[src.dna.species.fixed_mut_color]"
dna.features["breasts_color"] = "[src.dna.species.fixed_mut_color]"
return
//So people who haven't set stuff up don't get rainbow surprises.
dna.features["cock_color"] = "[dna.features["mcolor"]]"
dna.features["breasts_color"] = "[dna.features["mcolor"]]"
else //If there's a new organ, make it the same colour.
if(breastCheck == FALSE)
dna.features["breasts_color"] = dna.features["cock_color"]
else if (willyCheck == FALSE)
dna.features["cock_color"] = dna.features["breasts_color"]
return
/datum/species/proc/handle_genitals(mob/living/carbon/human/H)//more like handle sadness
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) && (H.genital_override = FALSE))//golems and such - things that shouldn't
return
if(HAS_TRAIT(H, TRAIT_HUSK))
return
var/list/genitals_to_add = list()
var/list/relevant_layers = list(GENITALS_BEHIND_LAYER, GENITALS_FRONT_LAYER) //GENITALS_ADJ_LAYER removed
var/list/standing = list()
var/size
var/aroused_state
for(var/L in relevant_layers) //Less hardcode
H.remove_overlay(L)
//start scanning for genitals
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)
//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)
for(var/obj/item/organ/genital/G in genitals_to_add)
var/datum/sprite_accessory/S
size = G.size
aroused_state = G.aroused_state
switch(G.type)
if(/obj/item/organ/genital/penis)
S = GLOB.cock_shapes_list[G.shape]
if(/obj/item/organ/genital/testicles)
S = GLOB.balls_shapes_list[G.shape]
if(/obj/item/organ/genital/vagina)
S = GLOB.vagina_shapes_list[G.shape]
if(/obj/item/organ/genital/breasts)
S = GLOB.breasts_shapes_list[G.shape]
if(!S || S.icon_state == "none")
continue
var/mutable_appearance/genital_overlay = mutable_appearance(S.icon, layer = -layer)
genital_overlay.icon_state = "[G.slot]_[S.icon_state]_[size]_[aroused_state]_[layertext]"
if(S.center)
genital_overlay = center_image(genital_overlay, S.dimension_x, S.dimension_y)
if(use_skintones && H.dna.features["genitals_use_skintone"])
genital_overlay.color = "#[skintone2hex(H.skin_tone)]"
genital_overlay.icon_state = "[G.slot]_[S.icon_state]_[size]-s_[aroused_state]_[layertext]"
else
switch(S.color_src)
if("cock_color")
genital_overlay.color = "#[H.dna.features["cock_color"]]"
if("balls_color")
genital_overlay.color = "#[H.dna.features["balls_color"]]"
if("breasts_color")
genital_overlay.color = "#[H.dna.features["breasts_color"]]"
if("vag_color")
genital_overlay.color = "#[H.dna.features["vag_color"]]"
standing += genital_overlay
if(LAZYLEN(standing))
H.overlays_standing[layer] = standing.Copy()
standing = list()
for(var/L in relevant_layers)
H.apply_overlay(L)
@@ -3,14 +3,14 @@
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
genital_flags = GENITAL_BLACKLISTED //unimplemented
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
@@ -1,77 +1,78 @@
/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 = 4.38
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")
var/prev_length = 6 //really should be renamed to prev_length
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
masturbation_verb = "stroke"
genital_flags = CAN_MASTURBATE_WITH|CAN_CLIMAX_WITH
linked_organ_slot = ORGAN_SLOT_TESTICLES
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/prev_length = 6 //really should be renamed to prev_length
var/girth = 4.38
var/girth_ratio = COCK_GIRTH_RATIO_DEF //0.73; check citadel_defines.dm
/obj/item/organ/genital/penis/Initialize()
. = ..()
/* I hate genitals.*/
/obj/item/organ/genital/penis/update_size()
var/mob/living/carbon/human/o = owner
if(!ishuman(o) || !o)
/obj/item/organ/genital/penis/modify_size(modifier, min = -INFINITY, max = INFINITY)
var/new_value = CLAMP(length + modifier, min, max)
if(new_value == length)
return
if(cached_length < 0)//I don't actually know what round() does to negative numbers, so to be safe!!
var/obj/item/organ/genital/penis/P = o.getorganslot("penis")
to_chat(o, "<span class='warning'>You feel your tallywacker shrinking away from your body as your groin flattens out!</b></span>")
P.Remove(o)
switch(round(cached_length))
if(0 to 4) //If modest size
length = cached_length
size = 1
if(owner.has_status_effect(/datum/status_effect/chem/penis_enlarger))
o.remove_status_effect(/datum/status_effect/chem/penis_enlarger)
if(5 to 10) //If modest size
length = cached_length
size = 2
if(owner.has_status_effect(/datum/status_effect/chem/penis_enlarger))
o.remove_status_effect(/datum/status_effect/chem/penis_enlarger)
if(11 to 20) //If massive
length = cached_length
size = 3
if(owner.has_status_effect(/datum/status_effect/chem/penis_enlarger))
o.remove_status_effect(/datum/status_effect/chem/penis_enlarger)
if(21 to 35) //If massive and due for large effects
length = cached_length
size = 3
if(!owner.has_status_effect(/datum/status_effect/chem/penis_enlarger))
o.apply_status_effect(/datum/status_effect/chem/penis_enlarger)
if(36 to INFINITY) //If comical
length = cached_length
size = 4 //no new sprites for anything larger yet
if(!owner.has_status_effect(/datum/status_effect/chem/penis_enlarger))
o.apply_status_effect(/datum/status_effect/chem/penis_enlarger)
if (round(length) > round(prev_length))
to_chat(o, "<span class='warning'>Your [pick(GLOB.gentlemans_organ_names)] [pick("swells up to", "flourishes into", "expands into", "bursts forth into", "grows eagerly into", "amplifys into")] a [uppertext(round(length))] inch penis.</b></span>")
else if ((round(length) < round(prev_length)) && (length > 0.5))
to_chat(o, "<span class='warning'>Your [pick(GLOB.gentlemans_organ_names)] [pick("shrinks down to", "decreases into", "diminishes into", "deflates into", "shrivels regretfully into", "contracts into")] a [uppertext(round(length))] inch penis.</b></span>")
prev_length = length
length = CLAMP(length + modifier, min, max)
update()
/obj/item/organ/genital/penis/update_size(modified = FALSE)
if(length < 0)//I don't actually know what round() does to negative numbers, so to be safe!!
if(owner)
to_chat(owner, "<span class='warning'>You feel your tallywacker shrinking away from your body as your groin flattens out!</b></span>")
QDEL_IN(src, 1)
if(linked_organ)
QDEL_IN(linked_organ, 1)
return
var/rounded_length = round(length)
var/new_size
var/enlargement = FALSE
switch(rounded_length)
if(0 to 6) //If modest size
new_size = 1
if(7 to 11) //If large
new_size = 2
if(12 to 20) //If massive
new_size = 3
if(21 to 34) //If massive and due for large effects
new_size = 3
enlargement = TRUE
if(35 to INFINITY) //If comical
new_size = 4 //no new sprites for anything larger yet
enlargement = TRUE
if(owner)
var/status_effect = owner.has_status_effect(STATUS_EFFECT_PENIS_ENLARGEMENT)
if(enlargement && !status_effect)
owner.apply_status_effect(STATUS_EFFECT_PENIS_ENLARGEMENT)
else if(!enlargement && status_effect)
owner.remove_status_effect(STATUS_EFFECT_PENIS_ENLARGEMENT)
if(linked_organ)
linked_organ.size = CLAMP(size + new_size, BALLS_SIZE_MIN, BALLS_SIZE_MAX)
linked_organ.update()
size = new_size
if(owner)
if (round(length) > round(prev_length))
to_chat(owner, "<span class='warning'>Your [pick(GLOB.gentlemans_organ_names)] [pick("swells up to", "flourishes into", "expands into", "bursts forth into", "grows eagerly into", "amplifys into")] a [uppertext(round(length))] inch penis.</b></span>")
else if ((round(length) < round(prev_length)) && (length > 0.5))
to_chat(owner, "<span class='warning'>Your [pick(GLOB.gentlemans_organ_names)] [pick("shrinks down to", "decreases into", "diminishes into", "deflates into", "shrivels regretfully into", "contracts into")] a [uppertext(round(length))] inch penis.</b></span>")
icon_state = sanitize_text("penis_[shape]_[size]")
girth = (length * girth_ratio)//Is it just me or is this ludicous, why not make it exponentially decay?
//I have no idea on how to update sprites and I hate it
/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"])
@@ -87,13 +88,13 @@
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"]
prev_length = length
@@ -1,82 +1,46 @@
/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
genital_flags = CAN_MASTURBATE_WITH|MASTURBATE_LINKED_ORGAN|GENITAL_FUID_PRODUCTION
var/size_name = "average"
shape = "Single"
var/sack_size = BALLS_SACK_SIZE_DEF
fluid_id = "semen"
masturbation_verb = "massage"
layer_index = TESTICLES_LAYER_INDEX
/obj/item/organ/genital/testicles/on_life()
if(QDELETED(src))
return
if(reagents && producing)
if(reagents.total_volume == 0) // Apparently, 0.015 gets rounded down to zero and no reagents are created if we don't start it with 0.1 in the tank.
fluid_rate = 0.1
else
fluid_rate = CUM_RATE
if(reagents.total_volume >= 5)
fluid_mult = 0.5
else
fluid_mult = 1
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
/obj/item/organ/genital/testicles/generate_fluid()
if(!linked_organ && !update_link())
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
. = ..()
if(. && reagents.holder_full())
to_chat(owner, "Your balls finally feel full, again.")
/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
/obj/item/organ/genital/testicles/upon_link()
size = linked_organ.size
update_size()
update_appearance()
else
if(linked_organ)
linked_organ.linked_organ = null
linked_organ = null
/obj/item/organ/genital/testicles/proc/send_full_message(msg = "Your balls finally feel full, again.")
if(owner && istext(msg))
to_chat(owner, msg)
return TRUE
/obj/item/organ/genital/testicles/update_appearance()
/obj/item/organ/genital/testicles/update_size(modified = FALSE)
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."
/obj/item/organ/genital/testicles/update_appearance()
. = ..()
desc = "You see an [size_name] pair of testicles."
if(owner)
var/string
if(owner.dna.species.use_skintones && owner.dna.features["genitals_use_skintone"])
@@ -91,3 +55,18 @@
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"]]"
sack_size = D.features["balls_sack_size"]
shape = D.features["balls_shape"]
if(D.features["balls_shape"] == "Hidden")
ENABLE_BITFIELD(genital_flags, GENITAL_INTERNAL)
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"]
@@ -1,26 +1,25 @@
/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
genital_flags = CAN_MASTURBATE_WITH|CAN_CLIMAX_WITH
masturbation_verb = "finger"
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)
var/details
@@ -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"]]"
@@ -1,45 +1,10 @@
/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/on_life()
if(QDELETED(src))
return
if(reagents && producing)
if(reagents.total_volume == 0) // Apparently, 0.015 gets rounded down to zero and no reagents are created if we don't start it with 0.1 in the tank.
fluid_rate = 0.1
else
fluid_rate = CUM_RATE
if(reagents.total_volume >= 5)
fluid_mult = 0.5
else
fluid_mult = 1
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
genital_flags = GENITAL_INTERNAL|GENITAL_FUID_PRODUCTION
fluid_id = "femcum"
linked_organ_slot = ORGAN_SLOT_VAGINA
@@ -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"])
@@ -30,17 +30,15 @@
inverse_chem_val = 0.35
inverse_chem = "BEsmaller" //At really impure vols, it just becomes 100% inverse
can_synth = FALSE
var/message_spam = FALSE
/datum/reagent/fermi/breast_enlarger/on_mob_add(mob/living/carbon/M)
/datum/reagent/fermi/breast_enlarger/on_mob_metabolize(mob/living/M)
. = ..()
if(!ishuman(M)) //The monkey clause
if(volume >= 15) //To prevent monkey breast farms
var/turf/T = get_turf(M)
var/obj/item/organ/genital/breasts/B = new /obj/item/organ/genital/breasts(T)
var/list/seen = viewers(8, T)
for(var/mob/S in seen)
to_chat(S, "<span class='warning'>A pair of breasts suddenly fly out of the [M]!</b></span>")
//var/turf/T2 = pick(turf in view(5, M))
M.visible_message("<span class='warning'>A pair of breasts suddenly fly out of the [M]!</b></span>")
var/T2 = get_random_station_turf()
M.adjustBruteLoss(25)
M.Knockdown(50)
@@ -48,94 +46,82 @@
B.throw_at(T2, 8, 1)
M.reagents.remove_reagent(id, volume)
return
log_game("FERMICHEM: [M] ckey: [M.key] has ingested Sucubus milk")
var/mob/living/carbon/human/H = M
H.genital_override = TRUE
var/obj/item/organ/genital/breasts/B = H.getorganslot("breasts")
if(!B)
H.emergent_genital_call()
return
if(!B.size == "huge")
var/sizeConv = list("a" = 1, "b" = 2, "c" = 3, "d" = 4, "e" = 5)
B.prev_size = B.size
B.cached_size = sizeConv[B.size]
if(!H.getorganslot(ORGAN_SLOT_BREASTS) && H.emergent_genital_call())
H.genital_override = TRUE
/datum/reagent/fermi/breast_enlarger/on_mob_life(mob/living/carbon/M) //Increases breast size
if(!ishuman(M))//Just in case
return..()
var/mob/living/carbon/human/H = M
var/obj/item/organ/genital/breasts/B = M.getorganslot("breasts")
var/obj/item/organ/genital/breasts/B = M.getorganslot(ORGAN_SLOT_BREASTS)
if(!B) //If they don't have breasts, give them breasts.
//If they have Acute hepatic pharmacokinesis, then route processing though liver.
if(HAS_TRAIT(M, TRAIT_PHARMA))
var/obj/item/organ/liver/L = M.getorganslot("liver")
if(HAS_TRAIT(H, TRAIT_PHARMA) || !H.canbearoused)
var/obj/item/organ/liver/L = H.getorganslot(ORGAN_SLOT_LIVER)
if(L)
L.swelling+= 0.05
return..()
L.swelling += 0.05
else
M.adjustToxLoss(1)
return..()
H.adjustToxLoss(1)
return..()
//otherwise proceed as normal
var/obj/item/organ/genital/breasts/nB = new
nB.Insert(M)
if(nB)
if(M.dna.species.use_skintones && M.dna.features["genitals_use_skintone"])
nB.color = skintone2hex(H.skin_tone)
else if(M.dna.features["breasts_color"])
nB.color = "#[M.dna.features["breasts_color"]]"
else
nB.color = skintone2hex(H.skin_tone)
nB.size = "flat"
nB.cached_size = 0
nB.prev_size = 0
to_chat(M, "<span class='warning'>Your chest feels warm, tingling with newfound sensitivity.</b></span>")
M.reagents.remove_reagent(id, 5)
B = nB
B = new
if(H.dna.species.use_skintones && H.dna.features["genitals_use_skintone"])
B.color = skintone2hex(H.skin_tone)
else if(M.dna.features["breasts_color"])
B.color = "#[M.dna.features["breasts_color"]]"
else
B.color = skintone2hex(H.skin_tone)
B.size = "flat"
B.cached_size = 0
B.prev_size = 0
to_chat(H, "<span class='warning'>Your chest feels warm, tingling with newfound sensitivity.</b></span>")
H.reagents.remove_reagent(id, 5)
B.Insert(H)
//If they have them, increase size. If size is comically big, limit movement and rip clothes.
B.cached_size = B.cached_size + 0.05
if (B.cached_size >= 8.5 && B.cached_size < 9)
if(H.w_uniform || H.wear_suit)
var/target = M.get_bodypart(BODY_ZONE_CHEST)
to_chat(M, "<span class='warning'>Your breasts begin to strain against your clothes tightly!</b></span>")
M.adjustOxyLoss(5, 0)
M.apply_damage(1, BRUTE, target)
B.update()
..()
B.modify_size(0.05)
if (ISINRANGE_EX(B.cached_size, 8.5, 9) && (H.w_uniform || H.wear_suit))
var/target = H.get_bodypart(BODY_ZONE_CHEST)
if(!message_spam)
to_chat(H, "<span class='danger'>Your breasts begin to strain against your clothes tightly!</b></span>")
message_spam = TRUE
H.adjustOxyLoss(5, 0)
H.apply_damage(1, BRUTE, target)
return ..()
/datum/reagent/fermi/breast_enlarger/overdose_process(mob/living/carbon/M) //Turns you into a female if male and ODing, doesn't touch nonbinary and object genders.
//Acute hepatic pharmacokinesis.
if(HAS_TRAIT(M, TRAIT_PHARMA))
var/obj/item/organ/liver/L = M.getorganslot("liver")
if(HAS_TRAIT(M, TRAIT_PHARMA) || !M.canbearoused)
var/obj/item/organ/liver/L = M.getorganslot(ORGAN_SLOT_LIVER)
L.swelling+= 0.05
return ..()
var/obj/item/organ/genital/penis/P = M.getorganslot("penis")
var/obj/item/organ/genital/testicles/T = M.getorganslot("testicles")
var/obj/item/organ/genital/vagina/V = M.getorganslot("vagina")
var/obj/item/organ/genital/womb/W = M.getorganslot("womb")
var/obj/item/organ/genital/penis/P = M.getorganslot(ORGAN_SLOT_PENIS)
var/obj/item/organ/genital/testicles/T = M.getorganslot(ORGAN_SLOT_TESTICLES)
var/obj/item/organ/genital/vagina/V = M.getorganslot(ORGAN_SLOT_VAGINA)
var/obj/item/organ/genital/womb/W = M.getorganslot(ORGAN_SLOT_WOMB)
if(M.gender == MALE)
M.gender = FEMALE
M.visible_message("<span class='boldnotice'>[M] suddenly looks more feminine!</span>", "<span class='boldwarning'>You suddenly feel more feminine!</span>")
if(P)
P.cached_length = P.cached_length - 0.05
P.update()
P.modify_size(-0.05)
if(T)
T.Remove(M)
qdel(T)
if(!V)
var/obj/item/organ/genital/vagina/nV = new
nV.Insert(M)
V = nV
V = new
V.Insert(M)
if(!W)
var/obj/item/organ/genital/womb/nW = new
nW.Insert(M)
W = nW
..()
W = new
W.Insert(M)
return ..()
/datum/reagent/fermi/BEsmaller
name = "Modesty milk"
@@ -147,19 +133,18 @@
can_synth = FALSE
/datum/reagent/fermi/BEsmaller/on_mob_life(mob/living/carbon/M)
var/obj/item/organ/genital/breasts/B = M.getorganslot("breasts")
var/obj/item/organ/genital/breasts/B = M.getorganslot(ORGAN_SLOT_BREASTS)
if(!B)
//Acute hepatic pharmacokinesis.
if(HAS_TRAIT(M, TRAIT_PHARMA))
var/obj/item/organ/liver/L = M.getorganslot("liver")
if(HAS_TRAIT(M, TRAIT_PHARMA) || !M.canbearoused)
var/obj/item/organ/liver/L = M.getorganslot(ORGAN_SLOT_LIVER)
L.swelling-= 0.05
return ..()
//otherwise proceed as normal
return..()
B.cached_size = B.cached_size - 0.05
B.update()
..()
B.modify_size(-0.05)
return ..()
/datum/reagent/fermi/BEsmaller_hypo
name = "Rectify milk" //Rectify
@@ -171,31 +156,28 @@
var/sizeConv = list("a" = 1, "b" = 2, "c" = 3, "d" = 4, "e" = 5)
can_synth = TRUE
/datum/reagent/fermi/BEsmaller_hypo/on_mob_add(mob/living/carbon/M)
/datum/reagent/fermi/BEsmaller_hypo/on_mob_metabolize(mob/living/M)
. = ..()
if(!M.getorganslot("vagina"))
if(M.dna.features["has_vag"])
var/obj/item/organ/genital/vagina/nV = new
nV.Insert(M)
if(!M.getorganslot("womb"))
if(M.dna.features["has_womb"])
var/obj/item/organ/genital/womb/nW = new
nW.Insert(M)
if(!ishuman(M))
return
var/mob/living/carbon/human/H = M
if(!H.getorganslot(ORGAN_SLOT_VAGINA) && H.dna.features["has_vag"])
H.give_genital(/obj/item/organ/genital/vagina)
if(!H.getorganslot(ORGAN_SLOT_WOMB) && H.dna.features["has_womb"])
H.give_genital(/obj/item/organ/genital/womb)
/datum/reagent/fermi/BEsmaller_hypo/on_mob_life(mob/living/carbon/M)
var/obj/item/organ/genital/breasts/B = M.getorganslot("breasts")
var/obj/item/organ/genital/breasts/B = M.getorganslot(ORGAN_SLOT_BREASTS)
if(!B)
return..()
if(!M.dna.features["has_breasts"])//Fast fix for those who don't want it.
B.cached_size = B.cached_size - 0.1
B.update()
else if(B.cached_size > (sizeConv[M.dna.features["breasts_size"]]+0.1))
B.cached_size = B.cached_size - 0.05
B.update()
else if(B.cached_size < (sizeConv[M.dna.features["breasts_size"]])+0.1)
B.cached_size = B.cached_size + 0.05
B.update()
..()
var/optimal_size = B.breast_values[M.dna.features["breasts_size"]]
if(!optimal_size)//Fast fix for those who don't want it.
B.modify_size(-0.1)
else if(B.cached_size > optimal_size)
B.modify_size(-0.05, optimal_size)
else if(B.cached_size < optimal_size)
B.modify_size(0.05, 0, optimal_size)
return ..()
////////////////////////////////////////////////////////////////////////////////////////////////////
// PENIS ENLARGE
@@ -215,16 +197,15 @@
inverse_chem_val = 0.35
inverse_chem = "PEsmaller" //At really impure vols, it just becomes 100% inverse and shrinks instead.
can_synth = FALSE
var/message_spam = FALSE
/datum/reagent/fermi/penis_enlarger/on_mob_add(mob/living/carbon/M)
/datum/reagent/fermi/penis_enlarger/on_mob_metabolize(mob/living/M)
. = ..()
if(!ishuman(M)) //Just monkeying around.
if(volume >= 15) //to prevent monkey penis farms
var/turf/T = get_turf(M)
var/obj/item/organ/genital/penis/P = new /obj/item/organ/genital/penis(T)
var/list/seen = viewers(8, T)
for(var/mob/S in seen)
to_chat(S, "<span class='warning'>A penis suddenly flies out of the [M]!</b></span>")
M.visible_message("<span class='warning'>A penis suddenly flies out of the [M]!</b></span>")
var/T2 = get_random_station_turf()
M.adjustBruteLoss(25)
M.Knockdown(50)
@@ -233,80 +214,71 @@
M.reagents.remove_reagent(id, volume)
return
var/mob/living/carbon/human/H = M
H.genital_override = TRUE
var/obj/item/organ/genital/penis/P = M.getorganslot("penis")
if(!P)
H.emergent_genital_call()
return
P.prev_length = P.length
P.cached_length = P.length
if(!H.getorganslot(ORGAN_SLOT_PENIS) && H.emergent_genital_call())
H.genital_override = TRUE
/datum/reagent/fermi/penis_enlarger/on_mob_life(mob/living/carbon/M) //Increases penis size, 5u = +1 inch.
if(!ishuman(M))
return
return ..()
var/mob/living/carbon/human/H = M
var/obj/item/organ/genital/penis/P = M.getorganslot("penis")
var/obj/item/organ/genital/penis/P = H.getorganslot(ORGAN_SLOT_PENIS)
if(!P)//They do have a preponderance for escapism, or so I've heard.
//If they have Acute hepatic pharmacokinesis, then route processing though liver.
if(HAS_TRAIT(M, TRAIT_PHARMA))
var/obj/item/organ/liver/L = M.getorganslot("liver")
if(HAS_TRAIT(H, TRAIT_PHARMA) || !H.canbearoused)
var/obj/item/organ/liver/L = H.getorganslot(ORGAN_SLOT_LIVER)
if(L)
L.swelling+= 0.05
return..()
L.swelling += 0.05
else
M.adjustToxLoss(1)
return..()
H.adjustToxLoss(1)
return ..()
//otherwise proceed as normal
var/obj/item/organ/genital/penis/nP = new
nP.Insert(M)
if(nP)
nP.length = 1
to_chat(M, "<span class='warning'>Your groin feels warm, as you feel a newly forming bulge down below.</b></span>")
nP.cached_length = 1
nP.prev_length = 1
M.reagents.remove_reagent(id, 5)
P = nP
P = new
P.length = 1
to_chat(H, "<span class='warning'>Your groin feels warm, as you feel a newly forming bulge down below.</b></span>")
P.prev_length = 1
H.reagents.remove_reagent(id, 5)
P.Insert(H)
P.cached_length = P.cached_length + 0.1
if (P.cached_length >= 20.5 && P.cached_length < 21)
if(H.w_uniform || H.wear_suit)
var/target = M.get_bodypart(BODY_ZONE_CHEST)
to_chat(M, "<span class='warning'>Your cock begin to strain against your clothes tightly!</b></span>")
M.apply_damage(2.5, BRUTE, target)
P.modify_size(0.1)
if (ISINRANGE_EX(P.length, 20.5, 21) && (H.w_uniform || H.wear_suit))
var/target = H.get_bodypart(BODY_ZONE_CHEST)
if(!message_spam)
to_chat(H, "<span class='danger'>Your cock begin to strain against your clothes tightly!</b></span>")
message_spam = TRUE
H.apply_damage(2.5, BRUTE, target)
P.update()
..()
return ..()
/datum/reagent/fermi/penis_enlarger/overdose_process(mob/living/carbon/M) //Turns you into a male if female and ODing, doesn't touch nonbinary and object genders.
/datum/reagent/fermi/penis_enlarger/overdose_process(mob/living/carbon/human/M) //Turns you into a male if female and ODing, doesn't touch nonbinary and object genders.
if(!istype(M))
return ..()
//Acute hepatic pharmacokinesis.
if(HAS_TRAIT(M, TRAIT_PHARMA))
var/obj/item/organ/liver/L = M.getorganslot("liver")
if(HAS_TRAIT(M, TRAIT_PHARMA) || !M.canbearoused)
var/obj/item/organ/liver/L = M.getorganslot(ORGAN_SLOT_LIVER)
L.swelling+= 0.05
return..()
var/obj/item/organ/genital/breasts/B = M.getorganslot("breasts")
var/obj/item/organ/genital/testicles/T = M.getorganslot("testicles")
var/obj/item/organ/genital/vagina/V = M.getorganslot("vagina")
var/obj/item/organ/genital/womb/W = M.getorganslot("womb")
var/obj/item/organ/genital/breasts/B = M.getorganslot(ORGAN_SLOT_BREASTS)
var/obj/item/organ/genital/testicles/T = M.getorganslot(ORGAN_SLOT_TESTICLES)
var/obj/item/organ/genital/vagina/V = M.getorganslot(ORGAN_SLOT_VAGINA)
var/obj/item/organ/genital/womb/W = M.getorganslot(ORGAN_SLOT_WOMB)
if(M.gender == FEMALE)
M.gender = MALE
M.visible_message("<span class='boldnotice'>[M] suddenly looks more masculine!</span>", "<span class='boldwarning'>You suddenly feel more masculine!</span>")
if(B)
B.cached_size = B.cached_size - 0.05
B.update()
if(V)
V.Remove(M)
B.modify_size(-0.05)
if(M.getorganslot(ORGAN_SLOT_VAGINA))
qdel(V)
if(W)
W.Remove(M)
qdel(W)
if(!T)
var/obj/item/organ/genital/testicles/nT = new
nT.Insert(M)
T = nT
..()
T = new
T.Insert(M)
return ..()
/datum/reagent/fermi/PEsmaller // Due to cozmo's request...!
name = "Chastity draft"
@@ -318,19 +290,18 @@
can_synth = FALSE
/datum/reagent/fermi/PEsmaller/on_mob_life(mob/living/carbon/M)
if(!ishuman(M))
return ..()
var/mob/living/carbon/human/H = M
var/obj/item/organ/genital/penis/P = H.getorganslot("penis")
var/obj/item/organ/genital/penis/P = H.getorganslot(ORGAN_SLOT_PENIS)
if(!P)
//Acute hepatic pharmacokinesis.
if(HAS_TRAIT(M, TRAIT_PHARMA))
var/obj/item/organ/liver/L = M.getorganslot("liver")
var/obj/item/organ/liver/L = M.getorganslot(ORGAN_SLOT_LIVER)
L.swelling-= 0.05
return..()
//otherwise proceed as normal
return..()
P.cached_length = P.cached_length - 0.1
P.update()
P.modify_size(-0.1)
..()
/datum/reagent/fermi/PEsmaller_hypo
@@ -342,24 +313,25 @@
metabolization_rate = 0.5
can_synth = TRUE
/datum/reagent/fermi/PEsmaller_hypo/on_mob_add(mob/living/carbon/M)
/datum/reagent/fermi/PEsmaller_hypo/on_mob_metabolize(mob/living/M)
. = ..()
if(!M.getorganslot("testicles"))
if(M.dna.features["has_balls"])
var/obj/item/organ/genital/testicles/nT = new
nT.Insert(M)
if(!ishuman(M))
return
var/mob/living/carbon/human/H = M
if(!H.getorganslot(ORGAN_SLOT_PENIS) && H.dna.features["has_cock"])
H.give_genital(/obj/item/organ/genital/penis)
if(!H.getorganslot(ORGAN_SLOT_TESTICLES) && H.dna.features["has_balls"])
H.give_genital(/obj/item/organ/genital/testicles)
/datum/reagent/fermi/PEsmaller_hypo/on_mob_life(mob/living/carbon/M)
var/obj/item/organ/genital/penis/P = M.getorganslot("penis")
var/obj/item/organ/genital/penis/P = M.getorganslot(ORGAN_SLOT_PENIS)
if(!P)
return ..()
if(!M.dna.features["has_cock"])//Fast fix for those who don't want it.
P.cached_length = P.cached_length - 0.2
P.update()
else if(P.cached_length > (M.dna.features["cock_length"]+0.1))
P.cached_length = P.cached_length - 0.1
P.update()
else if(P.cached_length < (M.dna.features["cock_length"]+0.1))
P.cached_length = P.cached_length + 0.1
P.update()
..()
var/optimal_size = M.dna.features["cock_length"]
if(!optimal_size)//Fast fix for those who don't want it.
P.modify_size(-0.2)
else if(P.length > optimal_size)
P.modify_size(-0.1, optimal_size)
else if(P.length < optimal_size)
P.modify_size(0.1, 0, optimal_size)
return ..()
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 759 B

After

Width:  |  Height:  |  Size: 561 B

+2 -3
View File
@@ -2960,7 +2960,6 @@
#include "modular_citadel\code\datums\status_effects\chems.dm"
#include "modular_citadel\code\datums\status_effects\debuffs.dm"
#include "modular_citadel\code\datums\traits\negative.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"
@@ -3018,10 +3017,10 @@
#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\genitals.dm"
#include "modular_citadel\code\modules\arousal\genitals_sprite_accessories.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"
#include "modular_citadel\code\modules\arousal\organs\testicles.dm"