Merge pull request #520 from Pin-alternative/rework

Reworks to genitals
This commit is contained in:
BongaTheProto
2023-04-23 14:47:32 -05:00
committed by GitHub
27 changed files with 847 additions and 254 deletions

View File

@@ -330,7 +330,7 @@
var/obj/item/organ/genital/breasts/boobs = new(loc)
if(features["has_breasts"])
boobs.color = sanitize_hexcolor(features["breasts_color"], 6, TRUE)
boobs.size = features["breasts_size"]
boobs.size = GLOB.breast_values[features["breasts_size"]]
boobs.shape = features["breasts_shape"]
if(!features["breasts_producing"])
boobs.genital_flags &= ~(GENITAL_FUID_PRODUCTION|CAN_CLIMAX_WITH|CAN_MASTURBATE_WITH)

View File

@@ -190,7 +190,7 @@
aroused_state = FALSE
/obj/item/organ/genital/proc/generate_fluid(datum/reagents/R)
var/amount = clamp((fluid_rate * ((world.time - last_orgasmed) / (10 SECONDS)) * fluid_mult),0,fluid_max_volume)
var/amount = get_fluid()
R.clear_reagents()
R.maximum_volume = fluid_max_volume
if(fluid_id)
@@ -325,7 +325,7 @@
var/obj/item/organ/genital/G = A
var/datum/sprite_accessory/S
var/size = G.size
var/size = G.size_to_state()
switch(G.type)
if(/obj/item/organ/genital/penis)
S = GLOB.cock_shapes_list[G.shape]

View File

@@ -8,7 +8,7 @@
icon = 'icons/obj/genitals/breasts.dmi'
zone = BODY_ZONE_CHEST
slot = ORGAN_SLOT_BREASTS
size = BREASTS_SIZE_DEF // "c". Refer to the breast_values static list below for the cups associated number values
size = 3
fluid_id = /datum/reagent/consumable/milk
fluid_rate = MILK_RATE
shape = DEF_BREASTS_SHAPE
@@ -19,19 +19,12 @@
orgasm_verb = "leaking"
fluid_transfer_factor = 0.5
layer_index = BREASTS_LAYER_INDEX
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, "massive" = 17, "giga" = 25, "impossible" = 30, "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/Initialize(mapload, do_update = TRUE)
if(do_update)
cached_size = breast_values[size]
prev_size = cached_size
return ..()
/obj/item/organ/genital/breasts/update_appearance()
. = ..()
var/lowershape = lowertext(shape)
var/size_state = size_to_state()
switch(lowershape)
if("pair")
desc = "You see a pair of breasts."
@@ -41,13 +34,13 @@
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(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*(owner ? get_size(owner) : 1))]cm in diameter."
if(size_state == "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(size*(owner ? get_size(owner) : 1))]cm in diameter."
else
if (size == "flat")
if (size_state == "flat")
desc += " They're very small and flatchested, however."
else
desc += " You estimate that they're [uppertext(size)]-cups."
desc += " You estimate that they're [uppertext(size_state)]-cups."
if((genital_flags & GENITAL_FUID_PRODUCTION) && aroused_state)
var/datum/reagent/R = GLOB.chemical_reagents_list[fluid_id]
@@ -55,7 +48,7 @@
desc += " They're leaking [lowertext(R.name)]."
var/datum/sprite_accessory/S = GLOB.breasts_shapes_list[shape]
var/icon_shape = S ? S.icon_state : "pair"
var/icon_size = clamp(breast_values[size], BREASTS_ICON_MIN_SIZE, BREASTS_ICON_MAX_SIZE)
var/icon_size = clamp(GLOB.breast_values[size_state], BREASTS_ICON_MIN_SIZE, BREASTS_ICON_MAX_SIZE)
icon_state = "breasts_[icon_shape]_[icon_size]"
if(owner)
if(owner.dna.species.use_skintones && owner.dna.features["genitals_use_skintone"])
@@ -74,45 +67,49 @@
//this is far too lewd wah
/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)
var/new_value = clamp(size + modifier, max(min, min_size ? min_size : -INFINITY), min(max_size ? max_size : INFINITY, max))
if(new_value == size)
return
prev_size = cached_size
cached_size = new_value
prev_size = size
size = new_value
update()
..()
/obj/item/organ/genital/breasts/size_to_state()
var/str_size
switch(size)
if(0) //flatchested
str_size = "flat"
if(1 to 8) //modest
str_size = GLOB.breast_values[size]
if(9 to 15) //massive
str_size = GLOB.breast_values[size]
if(16 to 17) //ridiculous
str_size = GLOB.breast_values[size]
if(18 to 24) //AWOOOOGAAAAAAA
str_size = "massive"
if(25 to 29) //AWOOOOOOGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
str_size = "giga"
if(30 to INFINITY) //AWWWWWWWWWWWWWOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOGGGGGAAAAAAAAAAAAAAAAAAAAAA
str_size = "impossible"
return str_size
/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
var/rounded_size = round(size)
var/size_state = size_to_state()
if(rounded_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
switch(rounded_cached)
if(0) //flatchested
size = "flat"
if(1 to 8) //modest
size = breast_values[rounded_cached]
if(9 to 15) //massive
size = breast_values[rounded_cached]
if(16 to 17) //ridiculous
size = breast_values[rounded_cached]
if(18 to 24) //AWOOOOGAAAAAAA
size = "massive"
if(25 to 29) //AWOOOOOOGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
size = "giga"
if(30 to INFINITY) //AWWWWWWWWWWWWWOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOGGGGGAAAAAAAAAAAAAAAAAAAAAA
size = "impossible"
if((rounded_cached < 18 || rounded_cached == 25 || rounded_cached == 30) && owner )//Because byond doesn't count from 0, I have to do this.
if((rounded_size < 18 || rounded_size == 25 || rounded_size == 30) && 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>")
if (rounded_size > 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_state)]-cup.</span>")
else if (rounded_size < 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_state)]-cup.</span>")
/obj/item/organ/genital/breasts/get_features(mob/living/carbon/human/H)
var/datum/dna/D = H.dna
@@ -120,16 +117,13 @@
color = SKINTONE2HEX(H.skin_tone)
else
color = "#[D.features["breasts_color"]]"
size = D.features["breasts_size"]
size = GLOB.breast_values[D.features["breasts_size"]]
max_size = D.features["breasts_max_size"]
min_size = D.features["breasts_min_size"]
shape = D.features["breasts_shape"]
if(!D.features["breasts_producing"])
genital_flags &= ~ (GENITAL_FUID_PRODUCTION|CAN_CLIMAX_WITH|CAN_MASTURBATE_WITH)
if(!isnum(size))
cached_size = breast_values[size]
else
cached_size = size
size = breast_values[size]
prev_size = cached_size
prev_size = size
toggle_visibility(D.features["breasts_visibility"], FALSE)
if(D.features["breasts_stuffing"])
toggle_visibility(GEN_ALLOW_EGG_STUFFING, FALSE)

View File

@@ -7,6 +7,7 @@
slot = ORGAN_SLOT_BUTT
w_class = 3
size = 0
max_size = BUTT_SIZE_MAX
var/size_name = "nonexistent"
shape = "Pair" //turn this into a default constant if for some inexplicable reason we get more than one butt type but I doubt it.
genital_flags = UPDATE_OWNER_APPEARANCE|GENITAL_UNDIES_HIDDEN|CAN_CUM_INTO|HAS_EQUIPMENT
@@ -15,8 +16,8 @@
var/prev_size //former size value, to allow update_size() to early return should be there no significant changes.
layer_index = BUTT_LAYER_INDEX
/obj/item/organ/genital/butt/modify_size(modifier, min = -INFINITY, max = BUTT_SIZE_MAX)
var/new_value = clamp(size_cached + modifier, min, max)
/obj/item/organ/genital/butt/modify_size(modifier, min = -INFINITY, max = INFINITY)
var/new_value = clamp(size_cached + modifier, max(min, min_size ? min_size : -INFINITY), min(max_size ? max_size : INFINITY, max))
if(new_value == size_cached)
return
prev_size = size_cached
@@ -88,6 +89,8 @@
else
color = "#[D.features["butt_color"]]"
size = D.features["butt_size"]
max_size = D.features["butt_max_size"]
min_size = D.features["butt_min_size"]
prev_size = size
size_cached = size
toggle_visibility(D.features["butt_visibility"], FALSE)

View File

@@ -14,18 +14,20 @@
shape = DEF_COCK_SHAPE
size = 2 //arbitrary value derived from length and diameter for sprites.
layer_index = PENIS_LAYER_INDEX
var/length = 6 //inches
var/length = 6 //inches
var/max_length = 9
var/min_length = 2
var/prev_length = 6 //really should be renamed to prev_length
var/diameter = 4.38
var/diameter_ratio = COCK_DIAMETER_RATIO_DEF //0.25; check citadel_defines.dm
/obj/item/organ/genital/penis/modify_size(modifier, min = -INFINITY, max = INFINITY)
var/new_value = clamp(length + modifier, min, max)
var/new_value = clamp(length + modifier, max(min, min_size ? min_size : -INFINITY), min(max_length ? max_length : INFINITY, max))
if(new_value == length)
return
prev_length = length
length = clamp(length + modifier, min, max)
length = new_value
update()
..()
@@ -95,6 +97,8 @@
else
color = "#[D.features["cock_color"]]"
length = D.features["cock_length"]
max_length = D.features["cock_max_length"]
min_length = D.features["cock_min_length"]
diameter_ratio = D.features["cock_diameter_ratio"]
shape = D.features["cock_shape"]
prev_length = length

View File

@@ -844,6 +844,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
tauric_shape = TRUE
dat += "<b>Penis Shape:</b> <a style='display:block;width:120px' href='?_src_=prefs;preference=cock_shape;task=input'>[features["cock_shape"]][tauric_shape ? " (Taur)" : ""]</a>"
dat += "<b>Penis Length:</b> <a style='display:block;width:120px' href='?_src_=prefs;preference=cock_length;task=input'>[features["cock_length"]] inch(es)</a>"
dat += "<b>Max Length:</b><a style='display:block;width:120px' href='?_src_=prefs;preference=cock_max_length;task=input'>[features["cock_max_length"] ? features["cock_max_length"] : "Disabled"]</a>"
dat += "<b>Min Length:</b><a style='display:block;width:120px' href='?_src_=prefs;preference=cock_min_length;task=input'>[features["cock_min_length"] ? features["cock_min_length"] : "Disabled"]</a>"
dat += "<b>Diameter Ratio:</b> <a style='display:block;width:120px' href='?_src_=prefs;preference=cock_diameter_ratio;task=input'>[features["cock_diameter_ratio"]]</a>" //SPLURT Edit
dat += "<b>Penis Visibility:</b><a style='display:block;width:100px' href='?_src_=prefs;preference=cock_visibility;task=input'>[features["cock_visibility"]]</a>"
dat += "<b>Egg Stuffing:</b><a style='display:block;width:50px' href='?_src_=prefs;preference=cock_stuffing'>[features["cock_stuffing"] == TRUE ? "Yes" : "No"]</a>" //SPLURT Edit
@@ -859,6 +861,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "<b>Testicles Visibility:</b><a style='display:block;width:100px' href='?_src_=prefs;preference=balls_visibility;task=input'>[features["balls_visibility"]]</a>"
//SPLURT Edit
dat += "<b>Egg Stuffing:</b><a style='display:block;width:50px' href='?_src_=prefs;preference=balls_stuffing'>[features["balls_stuffing"] == TRUE ? "Yes" : "No"]</a>"
dat += "<b>Max Size:</b><a style='display:block;width:50px' href='?_src_=prefs;preference=balls_max_size;task=input'>[features["balls_max_size"] ? features["balls_max_size"] : "Disabled"]</a>"
dat += "<b>Min Size:</b><a style='display:block;width:50px' href='?_src_=prefs;preference=balls_min_size;task=input'>[features["balls_min_size"] ? features["balls_min_size"] : "Disabled"]</a>"
dat += "<b>Produces:</b>"
var/datum/reagent/balls_fluid = find_reagent_object_from_type(features["balls_fluid"])
if(balls_fluid && (balls_fluid in GLOB.genital_fluids_list))
@@ -907,6 +911,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "<b>Lactates:</b><a style='display:block;width:50px' href='?_src_=prefs;preference=breasts_producing'>[features["breasts_producing"] == TRUE ? "Yes" : "No"]</a>"
//SPLURT Edit
dat += "<b>Egg Stuffing:</b><a style='display:block;width:50px' href='?_src_=prefs;preference=breasts_stuffing'>[features["breasts_stuffing"] == TRUE ? "Yes" : "No"]</a>"
dat += "<b>Max Size:</b><a style='display:block;width:50px' href='?_src_=prefs;preference=breasts_max_size;task=input'>[features["breasts_max_size"] ? features["breasts_max_size"] : "Disabled"]</a>"
dat += "<b>Min Size:</b><a style='display:block;width:50px' href='?_src_=prefs;preference=breasts_min_size;task=input'>[features["breasts_min_size"] ? features["breasts_min_size"] : "Disabled"]</a>"
if(features["breasts_producing"] == TRUE)
dat += "<b>Produces:</b>"
var/datum/reagent/breasts_fluid = find_reagent_object_from_type(features["breasts_fluid"])
@@ -930,6 +936,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "<b>Butt Visibility:</b><a style='display:block;width:100px' href='?_src_=prefs;preference=butt_visibility;task=input'>[features["butt_visibility"]]</a>"
//SPLURT Edit
dat += "<b>Egg Stuffing:</b><a style='display:block;width:50px' href='?_src_=prefs;preference=butt_stuffing'>[features["butt_stuffing"] == TRUE ? "Yes" : "No"]</a>"
dat += "<b>Max Size:</b><a style='display:block;width:50px' href='?_src_=prefs;preference=butt_max_size;task=input'>[features["butt_max_size"] ? features["butt_max_size"] : "Disabled"]</a>"
dat += "<b>Min Size:</b><a style='display:block;width:50px' href='?_src_=prefs;preference=butt_min_size;task=input'>[features["butt_min_size"] ? features["butt_min_size"] : "Disabled"]</a>"
dat += "<b>Butthole Sprite:</b><a style='display:block;width:50px' href='?_src_=prefs;preference=has_anus'>[features["has_anus"] == TRUE ? "Yes" : "No"]</a>"
if(features["has_anus"])
dat += "<b>Butthole Color:</b></a><BR>"
@@ -953,6 +961,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "<b>Color:</b></a><BR>"
dat += "<span style='border: 1px solid #161616; background-color: #[features["belly_color"]];'><font color='[color_hex2num(features["belly_color"]) < 200 ? "FFFFFF" : "000000"]'>#[features["belly_color"]]</font></span> <a href='?_src_=prefs;preference=belly_color;task=input'>Change</a><br>"
dat += "<b>Belly Size:</b><a style='display:block;width:50px' href='?_src_=prefs;preference=belly_size;task=input'>[features["belly_size"]]</a>"
dat += "<b>Max Size:</b><a style='display:block;width:50px' href='?_src_=prefs;preference=belly_max_size;task=input'>[features["belly_max_size"] ? features["belly_max_size"] : "Disabled" ]</a>"
dat += "<b>Min Size:</b><a style='display:block;width:50px' href='?_src_=prefs;preference=belly_min_size;task=input'>[features["belly_min_size"] ? features["belly_min_size"] : "Disabled" ]</a>"
dat += "<b>Belly Visibility:</b><a style='display:block;width:100px' href='?_src_=prefs;preference=belly_visibility;task=input'>[features["belly_visibility"]]</a>"
dat += "<b>Egg Stuffing:</b><a style='display:block;width:50px' href='?_src_=prefs;preference=belly_stuffing'>[features["belly_stuffing"] == TRUE ? "Yes" : "No"]</a>"
dat += "</td>"
@@ -2987,6 +2997,82 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(n_vis)
features["belly_visibility"] = n_vis
if("cock_max_length")
var/max_B = CONFIG_GET(number/penis_max_inches_prefs)
var/new_size = input(user, "Max size:\n([features["cock_length"]]-[max_B])(0 = disabled)", "Character Preference") as num|null
if(new_size)
features["cock_max_length"] = clamp(round(new_size), features["cock_length"], max_B)
else
features -= "cock_max_length"
if("balls_max_size")
var/new_size = input(user, "Max size:\n([BALLS_SIZE_MIN]-[BALLS_SIZE_MAX])(0 = disabled)", "Character Preference") as num|null
if(new_size)
features["balls_max_size"] = clamp(round(new_size), BALLS_SIZE_MIN, BALLS_SIZE_MAX)
else
features -= "balls_max_size"
if("breasts_max_size")
var/new_size = input(user, "Breast Max Size (cancel to disable)", "Character Preference") as null|anything in GLOB.breast_values
if(new_size)
features["breasts_max_size"] = new_size
else
features -= "breasts_max_size"
if("belly_max_size")
var/max_B = CONFIG_GET(number/belly_max_size_prefs)
var/new_size = input(user, "Max size:\n([features["belly_size"]]-[max_B])(0 = disabled)", "Character Preference") as num|null
if(new_size)
features["belly_max_size"] = clamp(round(new_size), features["belly_size"], max_B)
else
features -= "belly_max_size"
if("butt_max_size")
var/max_B = CONFIG_GET(number/butt_max_size_prefs)
var/new_size = input(user, "Max size:\n([features["butt_size"]]-[max_B])(0 = disabled)", "Character Preference") as num|null
if(new_size)
features["butt_max_size"] = clamp(round(new_size), features["butt_size"], max_B)
else
features -= "butt_max_size"
if("cock_min_length")
var/min_B = CONFIG_GET(number/penis_min_inches_prefs)
var/new_size = input(user, "Min size:\n([min_B]-[features["cock_length"]])(0 = disabled)", "Character Preference") as num|null
if(new_size)
features["cock_min_length"] = clamp(round(new_size), min_B, features["cock_length"])
else
features -= "cock_min_length"
if("balls_min_size")
var/new_size = input(user, "Min size:\n([BALLS_SIZE_MIN]-[BALLS_SIZE_MAX])(0 = disabled)", "Character Preference") as num|null
if(new_size)
features["balls_min_size"] = clamp(round(new_size), BALLS_SIZE_MIN, BALLS_SIZE_MAX)
else
features -= "balls_min_size"
if("breasts_min_size")
var/new_size = input(user, "Breast Min Size (cancel to disable)", "Character Preference") as null|anything in GLOB.breast_values
if(new_size)
features["breasts_min_size"] = new_size
else
features -= "breasts_min_size"
if("belly_min_size")
var/min_B = CONFIG_GET(number/belly_min_size_prefs)
var/new_size = input(user, "Min size:\n([min_B]-[features["belly_size"]])(0 = disabled)", "Character Preference") as num|null
if(new_size)
features["belly_min_size"] = clamp(round(new_size), min_B, features["belly_size"])
else
features -= "belly_min_size"
if("butt_min_size")
var/min_B = CONFIG_GET(number/butt_min_size_prefs)
var/new_size = input(user, "Min size:\n([min_B]-[features["butt_size"]])(0 = disabled)", "Character Preference") as num|null
if(new_size)
features["butt_min_size"] = clamp(round(new_size), min_B, features["butt_size"])
else
features -= "butt_min_size"
if("ooccolor")
var/new_ooccolor = input(user, "Choose your OOC colour:", "Game Preference",ooccolor) as color|null
if(new_ooccolor)
@@ -4162,3 +4248,4 @@ GLOBAL_LIST_EMPTY(preferences_datums)
#undef DEFAULT_SLOT_AMT
#undef HANDS_SLOT_AMT
#undef BACKPACK_SLOT_AMT

View File

@@ -1054,6 +1054,28 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["feature_inert_eggs"] >> features["inert_eggs"]
if(S["features_cock_max_length"])
S["features_cock_max_length"] >> features["cock_max_length"]
if(S["features_balls_max_size"])
S["features_balls_max_size"] >> features["balls_max_size"]
if(S["features_breasts_max_size"])
S["features_breasts_max_size"] >> features["breasts_max_size"]
if(S["features_belly_max_size"])
S["features_belly_max_size"] >> features["belly_max_size"]
if(S["features_butt_max_size"])
S["features_butt_max_size"] >> features["butt_max_size"]
if(S["features_cock_min_length"])
S["features_cock_min_length"] >> features["cock_min_length"]
if(S["features_balls_min_size"])
S["features_balls_min_size"] >> features["balls_min_size"]
if(S["features_breasts_min_size"])
S["features_breasts_min_size"] >> features["breasts_min_size"]
if(S["features_belly_min_size"])
S["features_belly_min_size"] >> features["belly_min_size"]
if(S["features_butt_min_size"])
S["features_butt_min_size"] >> features["butt_min_size"]
var/char_vr_path = "[vr_path]/character_[default_slot]_v2.json"
if(fexists(char_vr_path))
var/list/json_from_file = json_decode(file2text(char_vr_path))
@@ -1413,6 +1435,19 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["feature_inert_eggs"], features["inert_eggs"])
WRITE_FILE(S["features_cock_max_length"], features["cock_max_length"])
WRITE_FILE(S["features_balls_max_size"], features["balls_max_size"])
WRITE_FILE(S["features_breasts_max_size"], features["breasts_max_size"])
WRITE_FILE(S["features_belly_max_size"], features["belly_max_size"])
WRITE_FILE(S["features_butt_max_size"], features["butt_max_size"])
WRITE_FILE(S["features_cock_min_length"], features["cock_min_length"])
WRITE_FILE(S["features_balls_min_size"], features["balls_min_size"])
WRITE_FILE(S["features_breasts_min_size"], features["breasts_min_size"])
WRITE_FILE(S["features_belly_min_size"], features["belly_min_size"])
WRITE_FILE(S["features_butt_min_size"], features["butt_min_size"])
WRITE_FILE(S["feature_neckfire"], features["neckfire"])
WRITE_FILE(S["feature_neckfire_color"], features["neckfire_color"])

View File

@@ -68,8 +68,7 @@
B.color = "#[M.dna.features["breasts_color"]]"
else
B.color = SKINTONE2HEX(H.skin_tone)
B.size = "flat"
B.cached_size = 0
B.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(type, 5)
@@ -145,12 +144,12 @@
var/obj/item/organ/genital/breasts/B = M.getorganslot(ORGAN_SLOT_BREASTS)
if(!B)
return..()
var/optimal_size = B.breast_values[M.dna.features["breasts_size"]]
var/optimal_size = GLOB.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)
else if(B.size > optimal_size)
B.modify_size(-0.05, optimal_size)
else if(B.cached_size < optimal_size)
else if(B.size < optimal_size)
B.modify_size(0.05, 0, optimal_size)
return ..()
@@ -238,7 +237,7 @@
if(B)
B.modify_size(-0.05)
if(V && (!B || B.cached_size <= 0))
if(V && (!B || B.size <= 0))
qdel(V)
if(W && (!B || B.size <= 0))
qdel(W)

View File

@@ -136,14 +136,8 @@
else
visibility = "Hidden by clothes"
var/extras = "None"
if(CHECK_BITFIELD(genital.genital_flags, GENITAL_CAN_STUFF))
extras = "Allows egg stuffing"
genital_entry["extras"] = extras
genital_entry["visibility"] = visibility
genital_entry["possible_choices"] = GLOB.genitals_visibility_toggles
genital_entry["extra_choices"] = list(GEN_ALLOW_EGG_STUFFING)
genital_entry["can_arouse"] = (
!!CHECK_BITFIELD(genital.genital_flags, GENITAL_CAN_AROUSE) \
&& !(HAS_TRAIT(get_genitals, TRAIT_PERMABONER) \
@@ -169,37 +163,6 @@
genitals += list(simulated_ass)
.["genitals"] = genitals
//Get their genitals
var/list/genital_fluids = list()
var/mob/living/carbon/target_genitals = target || self
if(istype(target_genitals))
for(var/obj/item/organ/genital/genital in target_genitals.internal_organs)
if(!(CHECK_BITFIELD(genital.genital_flags, GENITAL_FUID_PRODUCTION)))
continue
var/fluids = (clamp(genital.fluid_rate * ((world.time - genital.last_orgasmed) / (10 SECONDS)) * genital.fluid_mult, 0, genital.fluid_max_volume) / genital.fluid_max_volume)
var/list/genital_entry = list()
genital_entry["name"] = "[genital.name]"
genital_entry["key"] = REF(genital)
genital_entry["fluid"] = fluids
genital_fluids += list(genital_entry)
.["genital_fluids"] = genital_fluids
var/list/genital_interactibles = list()
if(istype(target_genitals))
for(var/obj/item/organ/genital/genital in target_genitals.internal_organs)
if(!genital.is_exposed())
continue
var/list/equipment_names = list()
for(var/obj/equipment in genital.contents)
equipment_names += equipment.name
var/list/genital_entry = list()
genital_entry["name"] = "[genital.name]"
genital_entry["key"] = REF(genital)
genital_entry["possible_choices"] = GLOB.genitals_interactions
genital_entry["equipments"] = equipment_names
genital_interactibles += list(genital_entry)
.["genital_interactibles"] = genital_interactibles
var/datum/preferences/prefs = self?.client.prefs
if(prefs)
//Getting char prefs
@@ -294,28 +257,6 @@
return TRUE
else
return FALSE
if("genital_interaction")
var/mob/living/carbon/actual_target = target || usr
var/mob/user = usr
var/obj/item/organ/genital/genital = locate(params["genital"], actual_target.internal_organs)
if(!(genital && (genital in actual_target.internal_organs)))
return FALSE
switch(params["action"])
if(GEN_INSERT_EQUIPMENT)
var/obj/item/stuff = user.get_active_held_item()
if(!istype(stuff))
to_chat(user, span_warning("You need to hold an item to insert it!"))
return FALSE
stuff.insert_item_organ(user, actual_target, genital)
if(GEN_REMOVE_EQUIPMENT)
var/obj/item/selected_item = input(user, "Pick an item to remove", "Removing item") as null|anything in genital.contents
if(selected_item)
if(!do_mob(user, actual_target, 5 SECONDS))
return FALSE
if(!user.put_in_hands(selected_item))
user.transferItemToLoc(get_turf(user))
return TRUE
return FALSE
if("char_pref")
var/datum/preferences/prefs = parent_mob.client.prefs
var/value = num_to_pref(params["value"])

View File

@@ -41,8 +41,8 @@
if("f", "g", "h")
modifier = 3
else
if(milkers.size in milkers.breast_values)
modifier = clamp(milkers.breast_values[milkers.size] - 5, 0, INFINITY)
if(milkers.size in GLOB.breast_values)
modifier = clamp(GLOB.breast_values[milkers.size] - 5, 0, INFINITY)
else
modifier = 1
target.reagents.add_reagent(milktype, rand(1,3 * modifier))

View File

@@ -22,8 +22,8 @@
if("f", "g", "h")
modifier = 3
else
if(B.size in B.breast_values)
modifier = clamp(B.breast_values[B.size] - 5, 0, INFINITY)
if(B.size in GLOB.breast_values)
modifier = clamp(GLOB.breast_values[B.size] - 5, 0, INFINITY)
else
modifier = 1
if(B.fluid_id)
@@ -42,8 +42,8 @@
if("f", "g", "h")
modifier = 3
else
if(B.size in B.breast_values)
modifier = clamp(B.breast_values[B.size] - 5, 0, INFINITY)
if(B.size in GLOB.breast_values)
modifier = clamp(GLOB.breast_values[B.size] - 5, 0, INFINITY)
else
modifier = 1
if(B.fluid_id)
@@ -63,8 +63,8 @@
if("f", "g", "h")
modifier = 3
else
if(B.size in B.breast_values)
modifier = clamp(B.breast_values[B.size] - 5, 0, INFINITY)
if(B.size in GLOB.breast_values)
modifier = clamp(GLOB.breast_values[B.size] - 5, 0, INFINITY)
else
modifier = 1
if(B.fluid_id)

View File

@@ -79,8 +79,8 @@
if("f", "g", "h")
modifier = 3
else
if(milkers.size in milkers.breast_values)
modifier = clamp(milkers.breast_values[milkers.size] - 5, 0, INFINITY)
if(milkers.size in GLOB.breast_values)
modifier = clamp(GLOB.breast_values[milkers.size] - 5, 0, INFINITY)
else
modifier = 1
user.reagents.add_reagent(milktype, rand(1,3 * modifier))

View File

@@ -15,3 +15,5 @@ GLOBAL_LIST_INIT(breast_nouns, list("breasts", "boobs", "honkers", "tatas", "tid
GLOBAL_LIST_INIT(balls_nouns, list("balls", "nuts", "ballsack", "testicles", "sack", "cum orbs", "cum spheres", "cum tanks", "cum holders", "cum churners", "spunk orbs", "spunk spheres", "spunk tanks", "spunk holders", "spunk churners", "nut orbs", "nut spheres", "nut tanks", "nut holders", "nut churners", "jizz orbs", "jizz spheres", "jizz tanks", "jizz holders", "jizz churners", "seed orbs", "seed spheres", "seed tanks", "seed holders", "seed churners"))
GLOBAL_LIST_INIT(butt_nouns, list("ass", "butt", "dumptruck", "tush", "badonk", "booty", "rump", "behind"))
GLOBAL_LIST_INIT(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, "massive" = 17, "giga" = 25, "impossible" = 30, "flat" = 0))

View File

@@ -0,0 +1,213 @@
/// Attempts to open the tgui menu
/mob/living/verb/genital_menu()
set name = "Genitals Menu"
set desc = "Manage your genital, or someone else's."
set category = "IC"
set src in view(usr.client)
if(!iscarbon(usr))
return
if(!usr.mind) //Mindless boys, honestly just don't, it's better this way
return
if(!usr.mind.genitals_menu_holder)
usr.mind.genitals_menu_holder= new(usr.mind)
usr.mind.genitals_menu_holder.target = src
usr.mind.genitals_menu_holder.ui_interact(usr)
/datum/mind
var/datum/genitals_menu/genitals_menu_holder
/datum/mind/New(key)
. = ..()
genitals_menu_holder = new(src)
/datum/genitals_menu
var/mob/living/carbon/target
/datum/genitals_menu/ui_state(mob/user)
return GLOB.conscious_state
/datum/genitals_menu/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "GenitalConfig", "Genitals")
ui.open()
/datum/genitals_menu/ui_data(mob/user)
. = ..()
var/mob/living/carbon/genital_holder = target || user
var/user_is_target = genital_holder == user
.["istargetself"] = user_is_target
if(!user_is_target)
.["target_name"] = genital_holder.name
var/list/genitals = list()
for(var/obj/item/organ/genital/genital in genital_holder.internal_organs) //Only get the genitals
if(CHECK_BITFIELD(genital.genital_flags, GENITAL_INTERNAL)) //Not those though
continue
var/list/genital_entry = list()
genital_entry["img"] = icon2base64(getFlatIcon(genital, no_anim=TRUE))
genital_entry["name"] = "[capitalize(genital.name)]" //Prevents code from adding a prefix
genital_entry["key"] = REF(genital) //The key is the reference to the object
genital_entry["description"] = genital.desc + "\n [genital.linked_organ ? "Linked organ: [genital.linked_organ.name]" : ""]"
if(user_is_target)
var/visibility = "Invalid"
if(CHECK_BITFIELD(genital.genital_flags, GENITAL_THROUGH_CLOTHES))
visibility = "Always visible"
else if(CHECK_BITFIELD(genital.genital_flags, GENITAL_UNDIES_HIDDEN))
visibility = "Hidden by underwear"
else if(CHECK_BITFIELD(genital.genital_flags, GENITAL_HIDDEN))
visibility = "Always hidden"
else
visibility = "Hidden by clothes"
var/extras = "None"
if(CHECK_BITFIELD(genital.genital_flags, GENITAL_CAN_STUFF))
extras = "Allows egg stuffing"
genital_entry["extras"] = extras
genital_entry["visibility"] = visibility
genital_entry["possible_choices"] = GLOB.genitals_visibility_toggles
genital_entry["extra_choices"] = list(GEN_ALLOW_EGG_STUFFING)
genital_entry["can_arouse"] = (
!!CHECK_BITFIELD(genital.genital_flags, GENITAL_CAN_AROUSE) \
&& !(HAS_TRAIT(genital_holder, TRAIT_PERMABONER) \
|| HAS_TRAIT(genital_holder, TRAIT_NEVERBONER)))
genital_entry["arousal_state"] = genital.aroused_state
if(istype(genital, /obj/item/organ/genital/penis))
var/obj/item/organ/genital/penis/peepee = genital
genital_entry["max_size"] = peepee.max_length ? peepee.max_length : 0
genital_entry["min_size"] = peepee.min_length ? peepee.min_length : 0
else
genital_entry["max_size"] = genital.max_size ? genital.max_size : 0
genital_entry["min_size"] = genital.min_size ? genital.min_size : 0
//fluids
if(CHECK_BITFIELD(genital.genital_flags, GENITAL_FUID_PRODUCTION) || CHECK_BITFIELD(genital?.linked_organ?.genital_flags, GENITAL_FUID_PRODUCTION))
var/fluids = genital.get_fluid_fraction()
genital_entry["fluid"] = fluids
//equipments
if(genital.is_exposed())
var/list/equipments = list()
for(var/obj/equipment in genital.contents)
var/list/equipment_entry = list()
equipment_entry["name"] = equipment.name
equipment_entry["key"] = REF(equipment)
equipments += list(equipment_entry)
genital_entry["possible_equipment_choices"] = GLOB.genitals_interactions
genital_entry["equipments"] = equipments
genitals += list(genital_entry)
if(!genital_holder.getorganslot(ORGAN_SLOT_ANUS) && user_is_target)
var/simulated_ass = list()
simulated_ass["name"] = "Anus"
simulated_ass["key"] = "anus"
var/visibility = "Invalid"
switch(genital_holder.anus_exposed)
if(1)
visibility = "Always visible"
if(0)
visibility = "Hidden by underwear"
else
visibility = "Always hidden"
simulated_ass["visibility"] = visibility
simulated_ass["possible_choices"] = GLOB.genitals_visibility_toggles - GEN_VISIBLE_NO_CLOTHES
genitals += list(simulated_ass)
.["genitals"] = genitals
/datum/genitals_menu/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
if(..())
return
switch(action)
if("genital")
var/mob/living/carbon/self = usr
if(self != target)
return FALSE
if("visibility" in params)
if(params["genital"] == "anus")
self.anus_toggle_visibility(params["visibility"])
return TRUE
var/obj/item/organ/genital/genital = locate(params["genital"], self.internal_organs)
if(genital && (genital in self.internal_organs))
genital.toggle_visibility(params["visibility"])
return TRUE
if("set_arousal" in params)
var/obj/item/organ/genital/genital = locate(params["genital"], self.internal_organs)
if(!genital || (genital \
&& (!CHECK_BITFIELD(genital.genital_flags, GENITAL_CAN_AROUSE) \
|| HAS_TRAIT(self, TRAIT_PERMABONER) \
|| HAS_TRAIT(self, TRAIT_NEVERBONER))))
return FALSE
var/original_state = genital.aroused_state
genital.set_aroused_state(params["set_arousal"])// i'm not making it just `!aroused_state` because
if(original_state != genital.aroused_state) // someone just might port skyrat's new genitals
to_chat(self, "<span class='userlove'>[genital.aroused_state ? genital.arousal_verb : genital.unarousal_verb].</span>")
. = TRUE
else
to_chat(self, "<span class='userlove'>You can't make that genital [genital.aroused_state ? "unaroused" : "aroused"]!</span>")
. = FALSE
genital.update_appearance()
if(ishuman(self))
var/mob/living/carbon/human/human = self
human.update_genitals()
return
if("max_size" in params)
var/obj/item/organ/genital/genital = locate(params["genital"], self.internal_organs)
if(!genital)
return FALSE
if(istype(genital, /obj/item/organ/genital/penis))
var/obj/item/organ/genital/penis/peepee = genital
if(params["max_size"])
var/new_max_size = clamp(params["max_size"], peepee.length, INFINITY)
peepee.max_length = new_max_size
else
peepee.max_length = 0
else
if(params["max_size"])
var/new_max_size = clamp(params["max_size"], genital.size, INFINITY)
genital.max_size = new_max_size
else
genital.max_size = 0
if("min_size" in params)
var/obj/item/organ/genital/genital = locate(params["genital"], self.internal_organs)
if(!genital)
return FALSE
if(istype(genital, /obj/item/organ/genital/penis))
var/obj/item/organ/genital/penis/peepee = genital
var/new_min_size = clamp(params["min_size"], 0, peepee.length)
peepee.min_length = new_min_size
else
var/new_min_size = clamp(params["min_size"], 0, genital.size)
genital.min_size = new_min_size
else
return FALSE
if("equipment")
var/mob/living/carbon/actual_target = target || usr
var/mob/living/carbon/self = usr
if(get_dist(actual_target, self) > 1)
to_chat(self, span_warning("You're too far away!"))
return FALSE
var/obj/item/organ/genital/genital = locate(params["genital"], actual_target.internal_organs)
if(!(genital && (genital in actual_target.internal_organs)))
return FALSE
switch(params["equipment_action"])
if("remove")
var/obj/item/selected_item = locate(params["equipment"], genital.contents)
if(selected_item)
if(!do_mob(self, actual_target, 5 SECONDS))
return FALSE
if(!self.put_in_hands(selected_item))
self.transferItemToLoc(get_turf(self))
return TRUE
return FALSE
if("insert")
var/obj/item/stuff = self.get_active_held_item()
if(!istype(stuff))
to_chat(self, span_warning("You need to hold an item to insert it!"))
return FALSE
stuff.insert_item_organ(self, self, genital)

View File

@@ -139,7 +139,7 @@
for(var/obj/item/organ/genital/genital in buckled_mob.internal_organs)
if(istype(genital, /obj/item/organ/genital/breasts))
var/obj/item/organ/genital/breasts/breasts = genital
points_awarded += breasts.fluid_rate + breasts.breast_values[breasts.size] // Breasts use letters instead of numbers!
points_awarded += breasts.fluid_rate + GLOB.breast_values[breasts.size]
continue
points_awarded += genital.fluid_rate + genital.size
points_awarded *= tier

View File

@@ -55,10 +55,17 @@
return
if(locate(src.type) in target_organ.contents)
to_chat(user, span_notice("\The <b>[target]</b>'s [target_organ] already has a rod inside!"))
if(user == target)
to_chat(user, span_notice("You already have a rod inside your [target_organ]!"))
else
to_chat(user, span_notice("\The <b>[target]</b>'s [target_organ] already has a rod inside!"))
return
target.visible_message(span_warning("\The <b>[user]</b> is trying to insert a rod inside \the <b>[target]</b>!"),\
if(user == target)
target.visible_message(span_warning("\The <b>[user]</b> is trying to insert a rod inside themselves!"),\
span_warning("You try to insert a rod inside yourself!"))
else
target.visible_message(span_warning("\The <b>[user]</b> is trying to insert a rod inside \the <b>[target]</b>!"),\
span_warning("\The <b>[user]</b> is trying to insert a rod inside you!"))
if(!do_mob(user, target, 4 SECONDS))
@@ -81,15 +88,18 @@
return
if(locate(src.type) in target_organ.contents)
to_chat(user, span_notice("\The <b>[target]</b>'s [target_organ] already has a vibrator inside!"))
if(user == target)
to_chat(user, span_notice("You already have a vibrator inside your [target_organ]!"))
else
to_chat(user, span_notice("\The <b>[target]</b>'s [target_organ] already has a vibrator inside!"))
return
if(style == "long")
target.visible_message(span_warning("\The <b>[user]</b> is trying to insert a vibrator inside \the <b>[target]</b>!"),\
span_warning("\The <b>[user]</b> is trying to insert a vibrator inside you!"))
if(user == target)
target.visible_message(span_warning("\The <b>[user]</b> is trying to [style == "long" ? "insert" : "attach"] a vibrator [style == "long" ? "inside" : "to"] themselves!"),\
span_warning("You try to [style == "long" ? "insert" : "attach"] a vibrator [style == "long" ? "inside" : "to"] yourself!"))
else
target.visible_message(span_userlove("\The <b>[user]</b> is trying to attach a vibrator to \the <b>[target]</b>!"),\
span_warning("\The <b>[user]</b> is trying to attach a vibrator to you!"))
target.visible_message(span_warning("\The <b>[user]</b> is trying to [style == "long" ? "insert" : "attach"] a vibrator [style == "long" ? "inside" : "to"] \the <b>[target]</b>!"),\
span_warning("\The <b>[user]</b> is trying to [style == "long" ? "insert" : "attach"] a vibrator [style == "long" ? "inside" : "to"] you!"))
if(!do_mob(user, target, 5 SECONDS))
return
@@ -115,7 +125,11 @@
to_chat(user, span_warning("This genital can't be stuffed!"))
return
target.visible_message(span_warning("\The <b>[user]</b> is trying to insert an egg inside \the <b>[target]</b>!"),\
if(user == target)
target.visible_message(span_warning("\The <b>[user]</b> is trying to insert an egg inside themselves!"),\
span_warning("You try to insert an egg inside yourself!"))
else
target.visible_message(span_warning("\The <b>[user]</b> is trying to insert an egg inside \the <b>[target]</b>!"),\
span_warning("\The <b>[user]</b> is trying to insert an egg inside you!"))
if(!do_mob(user, target, 5 SECONDS))

View File

@@ -41,7 +41,7 @@
if(do_after(src, rand(10, 20), 0, target)) //did they get deleted in that second?
// var/main_fluid = lowertext(fluid_source.get_master_reagent_name()) // doesn't work no more (should delete probably)
var/main_fluid = G.get_fluid_name()
var/fluid_ammount = clamp((G.fluid_rate * ((world.time - G.last_orgasmed) / (10 SECONDS)) * G.fluid_mult),0,G.fluid_max_volume)
var/fluid_ammount = G.get_fluid()
if (fluid_ammount <= 2)
to_chat(src, span_revennotice("[target.p_their(TRUE)] [G.name] spasms pitifully, almost nothing will come out."))
else

View File

@@ -1,4 +1,6 @@
/obj/item/organ/genital
var/max_size = 6
var/min_size = 1
var/datum/reagents/climax_fluids
var/datum/reagent/original_fluid_id
var/datum/reagent/default_fluid_id
@@ -10,6 +12,15 @@
fluid_max_volume += (modifier*2.5)*(get_size(owner)-1)
fluid_rate += (modifier/10)*(get_size(owner)-1)
/obj/item/organ/genital/proc/size_to_state()
return size
/obj/item/organ/genital/proc/get_fluid()
return clamp(fluid_rate * ((world.time - last_orgasmed) / (10 SECONDS)) * fluid_mult, 0, fluid_max_volume)
/obj/item/organ/genital/proc/get_fluid_fraction()
return get_fluid() / fluid_max_volume
/obj/item/organ/genital/proc/climax_modify_size(mob/living/partner, obj/item/organ/genital/source_gen)
return

View File

@@ -25,8 +25,8 @@
if(!owner)
return
/obj/item/organ/genital/belly/modify_size(modifier, min = -INFINITY, max = BELLY_SIZE_MAX)
var/new_value = clamp(size_cached + modifier, min, max)
/obj/item/organ/genital/belly/modify_size(modifier, min = -INFINITY, max = INFINITY)
var/new_value = clamp(size_cached + modifier, max(min, min_size ? min_size : -INFINITY), min(max_size ? max_size : INFINITY, max))
if(new_value == size_cached)
return
prev_size = size_cached
@@ -95,6 +95,8 @@
else
color = "#[D.features["belly_color"]]"
size = D.features["belly_size"]
max_size = D.features["belly_max_size"]
min_size = D.features["belly_min_size"]
prev_size = size
size_cached = size
original_fluid_id = fluid_id

View File

@@ -13,8 +13,8 @@
fluid_id = initial(fluid_id)
original_fluid_id = fluid_id
. = ..()
fluid_max_volume += ((cached_size - breast_values[initial(size)])*2.5)*(owner ? get_size(owner) : 1)
fluid_rate += ((cached_size - breast_values[initial(size)])/10)*(owner ? get_size(owner) : 1)
fluid_max_volume += ((size - initial(size))*2.5)*(owner ? get_size(owner) : 1)
fluid_rate += ((size - initial(size))/10)*(owner ? get_size(owner) : 1)
/obj/item/organ/genital/breasts/climax_modify_size(mob/living/partner, obj/item/organ/genital/source_gen)
if(!(owner.client?.prefs.cit_toggles & BREAST_ENLARGEMENT))

View File

@@ -1,6 +1,18 @@
/obj/item/organ/genital/testicles
default_fluid_id = /datum/reagent/consumable/semen
/obj/item/organ/genital/testicles/get_fluid()
if(linked_organ)
return clamp(linked_organ.fluid_rate * ((world.time - linked_organ.last_orgasmed) / (10 SECONDS)) * linked_organ.fluid_mult, 0, linked_organ.fluid_max_volume)
else
return 0
/obj/item/organ/genital/testicles/get_fluid_fraction()
if(linked_organ)
return get_fluid() / linked_organ.fluid_max_volume
else
return 0
/obj/item/organ/genital/testicles/get_features(mob/living/carbon/human/H)
var/datum/dna/D = H.dna
if(D.features["balls_fluid"])

View File

@@ -1,6 +1,18 @@
/obj/item/organ/genital/womb
default_fluid_id = /datum/reagent/consumable/semen/femcum
/obj/item/organ/genital/womb/get_fluid()
if(linked_organ)
return clamp(linked_organ.fluid_rate * ((world.time - linked_organ.last_orgasmed) / (10 SECONDS)) * linked_organ.fluid_mult, 0, linked_organ.fluid_max_volume)
else
return 0
/obj/item/organ/genital/womb/get_fluid_fraction()
if(linked_organ)
return get_fluid() / linked_organ.fluid_max_volume
else
return 0
/obj/item/organ/genital/womb/get_features(mob/living/carbon/human/H)
var/datum/dna/D = H.dna
if(D.features["womb_fluid"])

View File

@@ -4350,6 +4350,7 @@
#include "modular_splurt\code\datums\elements\smalltalk.dm"
#include "modular_splurt\code\datums\elements\spooky.dm"
#include "modular_splurt\code\datums\elements\wuv.dm"
#include "modular_splurt\code\datums\genitals\genitals_interface.dm"
#include "modular_splurt\code\datums\interactions\cuddling.dm"
#include "modular_splurt\code\datums\interactions\rope.dm"
#include "modular_splurt\code\datums\interactions\lewd\_lewd.dm"

View File

@@ -7,6 +7,7 @@
// Themes
import './styles/main.scss';
import './styles/themes/abductor.scss';
import './styles/themes/hotpink.scss';
import './styles/themes/cardtable.scss';
import './styles/themes/hackerman.scss';
import './styles/themes/malfunction.scss';

View File

@@ -0,0 +1,313 @@
import { filter } from 'common/collections';
import { flow } from 'common/fp';
import { createSearch } from 'common/string';
import { useBackend, useLocalState } from '../backend';
import { BlockQuote, Button, LabeledList, Icon, NumberInput, Input, Section, Table, Tabs, Stack, ProgressBar, Divider } from '../components';
import { Window } from '../layouts';
type GenitalInfo = {
istargetself: boolean,
target_name: String,
genitals: GenitalData[];
}
type GenitalData = {
img: string,
name: string,
key: string,
description: string,
visibility: string,
extras: string,
extra_choices: string[],
possible_choices: string[],
can_arouse: boolean,
arousal_state: boolean,
fluid: number,
possible_equipment_choices: string[],
min_size: number,
max_size: number,
equipments: Equipment[],
}
type Equipment = {
name: string,
key: string,
}
export const GenitalConfig = (props, context) => {
const { act, data } = useBackend<GenitalInfo>(context);
const genitals = data.genitals || [];
const [tabIndex, setTabIndex] = useLocalState(context, 'tabIndex', 0);
return (
<Window
width={850}
height={600}
theme="hotpink"
resizable>
<Window.Content scrollable={false}>
{data.target_name ? (
<Section>
Interacting with <b>{data.target_name}</b>
</Section>
) : null}
{genitals.length ? (
<>
<Section title="Genital">
<Stack vertical>
<Stack.Item>
<Tabs fluid textAlign="center">
{genitals.map((genital, index) => (
<Tabs.Tab
key={index}
selected={tabIndex === index}
onClick={() => setTabIndex(index)}
>
{genital.name}
</Tabs.Tab>
))}
</Tabs>
</Stack.Item>
<Stack.Item>
{genitals[tabIndex].description}
</Stack.Item>
</Stack>
</Section>
<Section title="Settings">
<Stack grow>
<Stack.Item grow>
<Stack vertical>
<Stack.Item>
<Section>
Fluid level:
<ProgressBar
key={genitals[tabIndex].key}
value={
genitals[tabIndex].fluid
? genitals[tabIndex].fluid
: 0.0
}
color="white"
/>
</Section>
</Stack.Item>
{data.istargetself
? <SelfConfig /> : null}
</Stack>
</Stack.Item>
</Stack>
</Section>
<Section
title="Items Inserted"
buttons={(
<Button
fluid
content="Insert Item"
onClick={() => act('equipment', {
genital: genitals[tabIndex].key,
equipment_action: "insert",
})}
/>
)}
>
<Equipments />
</Section>
</>
) : (
<Section align="center">
{data.target_name ? "They" : "You"} don&apos;t seem to have any genitals...
Or any that you could interact with.
</Section>
)}
</Window.Content>
</Window>
);
};
const SelfConfig = (props, context) => {
const [tabIndex] = useLocalState(context, 'tabIndex', 0);
const { act, data } = useBackend<GenitalInfo>(context);
const genital = data.genitals[tabIndex];
return (
<Stack.Item>
<Stack grow>
{genital.img ? (
<Stack.Item>
<img
src={`data:image/jpeg;base64,${genital.img}`}
style={{
'vertical-align': 'middle',
'horizontal-align': 'middle',
}} />
</Stack.Item>
) : null}
{typeof genital.max_size === "number" ? (
<Stack.Item>
<SizeButtons />
</Stack.Item>
) : null}
<Stack.Item grow>
<ToggleSettings />
</Stack.Item>
</Stack>
</Stack.Item>
);
};
const SizeButtons = (props, context) => {
const [tabIndex] = useLocalState(context, 'tabIndex', 0);
const { act, data } = useBackend<GenitalInfo>(context);
const genital = data.genitals[tabIndex];
return (
<Section>
<Stack>
<Stack.Item>
<Stack vertical>
Max growth:
<NumberInput
value={genital.max_size}
onChange={(e, value) => act('genital', {
genital: genital.key,
max_size: value,
}
)}
/>
</Stack>
</Stack.Item>
<Stack.Item>
<Stack vertical>
Min shrink:
<NumberInput
value={genital.min_size}
onChange={(e, value) => act('genital', {
genital: genital.key,
min_size: value,
}
)}
/>
</Stack>
</Stack.Item>
</Stack>
</Section>
);
};
const ModeToIcon = {
"Always visible": "eye",
"Hidden by clothes": "tshirt",
"Hidden by underwear": "low-vision",
"Always hidden": "eye-slash",
"Allows egg stuffing": "egg",
};
const ToggleSettings = (props, context) => {
const [tabIndex] = useLocalState(context, 'tabIndex', 0);
const { act, data } = useBackend<GenitalInfo>(context);
const genital = data.genitals[tabIndex];
return (
<Stack grow>
{genital.possible_choices.map(choice => (
<Stack.Item key={choice} grow>
<Button
textAlign="center"
key={choice}
tooltip={choice}
icon={ModeToIcon[choice]}
color={genital.visibility === choice ? "green" : "default"}
onClick={() => act('genital', {
genital: genital.key,
visibility: choice,
})}
fluid
/>
</Stack.Item>
))}
<Stack.Item grow>
<Button
textAlign="center"
key={genital.arousal_state}
tooltip={genital.can_arouse
? ((genital.arousal_state ? "Unarouse" : "Arouse") + " your " + genital.name.toLowerCase())
: "You cannot modify arousal on your " + genital.name.toLowerCase()}
icon="heart"
color={genital.can_arouse ? (genital.arousal_state ? "green" : "default") : "grey"}
onClick={() => act('genital', {
genital: genital.key,
set_arousal: !genital.arousal_state,
})}
fluid
/>
</Stack.Item>
{genital.extra_choices instanceof Array
? genital.extra_choices.map(choice => (
<Stack.Item key={choice} grow>
<Button
textAlign="center"
key={choice}
tooltip={choice}
icon={ModeToIcon[choice]}
color={genital.extras === choice ? "green" : "default"}
onClick={() => act('genital', {
genital: genital.key,
visibility: choice,
})}
fluid
/>
</Stack.Item>
)) : null}
</Stack>
);
};
const Equipments = (props, context) => {
const [tabIndex] = useLocalState(context, 'tabIndex', 0);
const { act, data } = useBackend<GenitalInfo>(context);
const genital = data.genitals[tabIndex];
const [searchText, setSearchText] = useLocalState(context, 'searchText', '');
const items = prepareSearch(genital.equipments, searchText) || [];
return (
<Stack vertical>
<Stack.Item>
<Input
fluid
placeholder="Search for items.."
onInput={(e, value) => setSearchText(value)}
/>
</Stack.Item>
<Stack.Item>
<Table mb={1}>
{items.map((item, index) =>
(
<Table.Row key={index} className="candystripe">
<Table.Cell bold>
{item.name}
</Table.Cell>
<Table.Cell collapsing textAlign="center">
<Button
fluid
content="Remove"
onClick={() => act('equipment', {
equipment: item.key,
genital: genital.key,
equipment_action: "remove",
})}
/>
</Table.Cell>
</Table.Row>
)
)}
</Table>
</Stack.Item>
</Stack>
);
};
export const prepareSearch = (items, searchText = '') => {
const testSearch = createSearch<Equipment>(searchText,
item => item.name);
return flow([
// Optional search term
searchText && filter(testSearch),
])(items);
};

View File

@@ -35,33 +35,12 @@ type GenitalData = {
name: string,
key: string,
visibility: string,
extras: string,
extra_choices: string[],
possible_choices: string[],
can_arouse: boolean,
arousal_state: boolean,
always_accessible: boolean,
}
type GenitalManagerInfo = {
isTargetSelf: boolean;
genital_fluids: GenitalFluid[];
genital_interactibles: GenitalInteractionInfos[];
}
type GenitalInteractionInfos = {
name: string,
key: string,
possible_choices: string[],
equipments: string[],
}
type GenitalFluid = {
name: string,
key: string,
fluids: number,
}
type CharacterPrefsInfo = {
erp_pref: number,
noncon_pref: number,
@@ -111,7 +90,7 @@ export const MobInteraction = (props, context) => {
return (
<Window
width={530}
width={430}
height={700}
resizable>
<Window.Content overflow="auto">
@@ -167,9 +146,6 @@ export const MobInteraction = (props, context) => {
<Tabs.Tab selected={tabIndex === 3} onClick={() => setTabIndex(3)}>
Preferences
</Tabs.Tab>
<Tabs.Tab selected={tabIndex === 4} onClick={() => setTabIndex(4)}>
Genital Manager
</Tabs.Tab>
</Tabs>
{tabIndex === 0 && (
<InteractionsTab />
@@ -179,8 +155,6 @@ export const MobInteraction = (props, context) => {
<CharacterPrefsTab />
) || tabIndex === 3 && (
<ContentPreferencesTab />
) || tabIndex === 4 && (
<GenitalManagerTab />
) || ("Somehow, you've got into an invalid page, please report this.")}
</Section>
</Window.Content>
@@ -263,7 +237,6 @@ const ModeToIcon = {
"Hidden by clothes": "tshirt",
"Hidden by underwear": "low-vision",
"Always hidden": "eye-slash",
"Allows egg stuffing": "egg",
};
/*
@@ -315,19 +288,6 @@ const GenitalTab = (props, context) => {
genital: genital.key,
set_arousal: !genital.arousal_state,
})} />
{genital.extra_choices instanceof Array
? genital.extra_choices.map(choice => (
<Button
width="50%"
key={choice}
tooltip={choice}
icon={ModeToIcon[choice]}
color={genital.extras === choice ? "green" : "default"}
onClick={() => act('genital', {
genital: genital.key,
visibility: choice,
})} />
)) : null}
<Button
width="49%"
key={genital.always_accessible}
@@ -354,71 +314,6 @@ const GenitalTab = (props, context) => {
);
};
const GenitalManagerTab = (props, context) => {
const { act, data } = useBackend<GenitalManagerInfo>(context);
const isTargetSelf = data.isTargetSelf;
const genital_fluids = data.genital_fluids || [];
const genital_interactibles = data.genital_interactibles || [];
return (
genital_fluids.length || genital_interactibles.length ? (
<>
<Section title="Genital Fluids">
<LabeledList>
{genital_fluids.map(genital => (
<LabeledList.Item key={genital['key']} label={genital['name']}>
<ProgressBar
key={genital['key']}
value={genital['fluid'] ? genital['fluid'] : 0.0}
color="white" />
</LabeledList.Item>
))}
</LabeledList>
</Section>
<Section title="Actions">
{genital_interactibles.map(genital => (
<Section key={genital.key} title={genital.name}>
{
genital.equipments.length ? (
<>
<b>Equipments:</b>
<Table direction="column">
{genital.equipments.map(equipment => (
<TableRow key={equipment}>
{equipment}
</TableRow>
))}
</Table>
<Divider />
</>
) : null
}
{genital.possible_choices.map(choice => (
<Button
key={choice}
content={choice}
tooltip={choice}
onClick={() => act('genital_interaction', {
genital: genital.key,
action: choice,
})} />
))}
</Section>
))}
</Section>
</>
) : (
<Section align="center">
{
isTargetSelf
? "You don't seem to have any genitals... Or any that you could do anything with"
: "They don't seem to have any genitals... Or any that you could do anything with"
}
</Section>
)
);
};
const CharacterPrefsTab = (props, context) => {
const { act, data } = useBackend<CharacterPrefsInfo>(context);
const {
@@ -430,7 +325,7 @@ const CharacterPrefsTab = (props, context) => {
extreme_harm,
} = data;
return (
<Stack direction="column">
<Flex direction="column">
<LabeledList>
<LabeledList.Item label="ERP Preference">
<Button
@@ -566,7 +461,7 @@ const CharacterPrefsTab = (props, context) => {
</LabeledList.Item>
) : (null)}
</LabeledList>
</Stack>
</Flex>
);
};

View File

@@ -0,0 +1,54 @@
@use 'sass:color';
@use 'sass:meta';
@use '../colors.scss' with (
$primary: #ae4ab8,
$fg-map-keys: (),
$bg-map-keys: (),
);
@use '../base.scss' with (
$color-bg: #662a6b,
$color-bg-grad-spread: 6%,
$border-radius: 2px,
);
.theme-hotpink {
// Atomic classes
@include meta.load-css('../atomic/color.scss');
// Components
@include meta.load-css('../components/Button.scss', $with: (
'color-default': colors.$primary,
'color-disabled': #363636,
'color-selected': #465899,
'color-caution': #be09a6,
'color-danger': #be0909,
));
@include meta.load-css('../components/Input.scss', $with: (
'border-color': colors.$primary,
));
@include meta.load-css('../components/NoticeBox.scss', $with: (
'background-color': #a82d55,
));
@include meta.load-css('../components/NumberInput.scss', $with: (
'border-color': colors.$primary,
));
@include meta.load-css('../components/ProgressBar.scss', $with: (
'background-color': rgba(0, 0, 0, 0.5),
));
@include meta.load-css('../components/Section.scss');
@include meta.load-css('../components/Tooltip.scss', $with: (
'background-color': #a82d55,
));
// Layouts
@include meta.load-css('../layouts/Layout.scss');
@include meta.load-css('../layouts/Window.scss');
@include meta.load-css('../layouts/TitleBar.scss', $with: (
'background-color': colors.$primary,
));
.Layout__content {
background-image: none;
}
}