diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm index a046034e3a9..2732c53ff41 100644 --- a/code/__HELPERS/_lists.dm +++ b/code/__HELPERS/_lists.dm @@ -870,3 +870,33 @@ return (call(cmp)(L[i],A) > 0) ? i : i+1 else return i + +/// Takes a list, and applies a weight multiplier to each element, starting at 1 and multiplying by weight_mult each time +/// Makes a weighted list that heavily favors the first elements +/// If reverse is true, it starts at the end of the list and works backwards +/// Only feed it an unassociated list, or an associated list with only numeric values +/proc/WeightedCascadingPicker(list/input, weight_mult = 2, reverse, numberify) + if(!LAZYLEN(input)) + return list() + var/list/ret = list() // Retival mankind's batman + var/weight = 1 + if(reverse) + weight = (abs(weight_mult) ** (length(input) - 1)) + weight_mult = (1 / abs(weight_mult)) + for(var/i in 1 to length(input)) + ret["[i]"] = weight + weight = abs(round(weight *= weight_mult)) + var/index = text2num(pickweight(ret)) + var/out = LAZYACCESS(input, index) + if(numberify) + out = text2num(out) + return out //outeval mankind's batman + +/// Like the above, but it takes a range instead of a list +/proc/WeightedCascadingPickerRange(low, high, weight_mult = 2, reverse) + var/list/input = list() + for(var/i in low to high) + input += "[i]" + return WeightedCascadingPicker(input, weight_mult, reverse, TRUE) + + diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index cf6dd29baa4..ddab3eb0411 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -1672,5 +1672,23 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new) return LAZYACCESS(first_last, 1) return rname - - +/// Makes a gaussian distribution, returning a positive integer +/proc/GaussianReacharound(mean, stddev, min, max) + var/cool_input = gaussian(mean, stddev) + cool_input = abs(cool_input) + cool_input = round(cool_input) + cool_input = clamp(cool_input, min, max) + return cool_input + +/proc/GaussianListPicker(list/input, mean, stddev) + if(!LAZYLEN(input)) + return + var/index = GaussianReacharound(mean, stddev, 0, LAZYLEN(input)) + var/output = LAZYACCESS(input, index) + if(!output) + output = pick(input) // shruggaroni + return output + +/proc/GaussianRangePicker(min, max, mean, stddev) + var/index = GaussianReacharound(mean, stddev, min, max) + return index diff --git a/code/controllers/subsystem/dummies.dm b/code/controllers/subsystem/dummies.dm index 0ad8d1fc6d5..525b94013fb 100644 --- a/code/controllers/subsystem/dummies.dm +++ b/code/controllers/subsystem/dummies.dm @@ -20,6 +20,9 @@ SUBSYSTEM_DEF(dummy) // who ya callin dummy, dummy? var/list/naked_player_cache = list() /// Pictures of everyone at various moments, clothed~ /// list("ckey" = list(image, image, image)) + var/list/very_naked_player_cache = list() + /// Pictures of everyone at various moments, clothed~ + /// list("ckey" = list(image, image, image)) var/list/clothed_player_cache = list() /// Pictures of everyone at various moments, clothed~ /// list("ckey" = list(image, image, image)) @@ -73,7 +76,8 @@ SUBSYSTEM_DEF(dummy) // who ya callin dummy, dummy? random_clothes, copy_equipment, genitals, - cache = TRUE + cache = TRUE, + underwear = TRUE, ) var/image_key = generate_key(slotkey, spec, clothes, template, client_or_prefs, loadout, random_body, random_species, random_clothes, copy_equipment, genitals, cache) if(LAZYACCESS(image_cache, image_key)) @@ -88,35 +92,39 @@ SUBSYSTEM_DEF(dummy) // who ya callin dummy, dummy? else if (istype(client_or_prefs, /datum/preferences)) var/datum/preferences/P = client_or_prefs clint = P.parent + else if(istype(template) && template.client) + clint = template.client + if(!template && ishuman(clint.mob)) + template = clint.mob + + if(istype(clint)) + var/datum/preferences/P = clint.prefs + P?.copy_to(mannequin) if(istype(template)) copy_human_mob(template, mannequin) if(copy_equipment) copy_equipped(template, mannequin) - else if(istype(clint)) - var/datum/preferences/P = clint.prefs - P.copy_to(mannequin) - if(copy_equipment && ishuman(clint.mob)) - copy_equipped(clint.mob, mannequin) else if(random_species && !ispath(spec)) spec = pick(list(/datum/species/human, /datum/species/lizard, /datum/species/mammal)) if(ispath(spec)) mannequin.set_species(spec) if(random_body) - randomize_human(mannequin, spec) - if(copy_equipment && (istype(template) || istype(clint))) - copy_equipped(template, mannequin) - else - if(random_clothes && !ispath(clothes)) + randomize_human(mannequin, spec, underwear, genitals) + if(random_clothes) + if(!ispath(clothes)) clothes = pick(subtypesof(/datum/outfit/job)) - if(ispath(clothes)) + else random_clothes = FALSE clothes = new clothes() - var/datum/preferences/P = clint?.prefs - clothes.equip(mannequin, TRUE, P) + clothes.equip(mannequin, TRUE, clint) if(!genitals) for(var/obj/item/organ/genital/nad in mannequin.internal_organs) qdel(nad) // say bye to ur naddies + if(!underwear) + mannequin.underwear = "Nude" + mannequin.socks = "Nude" + mannequin.undershirt = "Nude" mannequin.update_body(TRUE) mannequin.update_hair() @@ -263,10 +271,7 @@ SUBSYSTEM_DEF(dummy) // who ya callin dummy, dummy? return LAZYACCESS(image_cache, slutkey) return get_dummy_image("PLAYER", template = H, client_or_prefs = clont, copy_equipment = equipped) -/datum/controller/subsystem/dummy/proc/capture_snapshot_of_players(obey_cooldown) - if(obey_cooldown && !COOLDOWN_FINISHED(src, snapshot_cooldown)) - return - COOLDOWN_START(src, snapshot_cooldown, 5 MINUTES) +/datum/controller/subsystem/dummy/proc/capture_snapshot_of_players() for(var/client/C in GLOB.clients) snapshot_player(C) @@ -283,6 +288,8 @@ SUBSYSTEM_DEF(dummy) // who ya callin dummy, dummy? H = C.mob if(!ishuman(H)) return + if(!LAZYACCESS(very_naked_player_cache, "[C.ckey]%%[H.real_name]")) + very_naked_player_cache["[C.ckey]%%[H.real_name]"] = list() if(!LAZYACCESS(naked_player_cache, "[C.ckey]%%[H.real_name]")) naked_player_cache["[C.ckey]%%[H.real_name]"] = list() if(!LAZYACCESS(clothed_player_cache, "[C.ckey]%%[H.real_name]")) @@ -290,10 +297,13 @@ SUBSYSTEM_DEF(dummy) // who ya callin dummy, dummy? if(!LAZYACCESS(randomclothed_player_cache, "[C.ckey]%%[H.real_name]")) randomclothed_player_cache["[C.ckey]%%[H.real_name]"] = list() var/image/naked = get_dummy_image("PLAYER", template = H, client_or_prefs = C, random_body = FALSE, random_species = FALSE, random_clothes = FALSE) + var/image/supernaked = get_dummy_image("PLAYER", template = H, client_or_prefs = C, random_body = FALSE, random_species = FALSE, random_clothes = FALSE, underwear = FALSE, genitals = TRUE) var/image/clothed = get_dummy_image("PLAYER", template = H, client_or_prefs = C, random_body = FALSE, random_species = FALSE, random_clothes = FALSE, copy_equipment = TRUE) var/image/clothed2 = get_dummy_image("PLAYER", template = H, client_or_prefs = C, random_body = FALSE, random_species = FALSE, random_clothes = TRUE) if(naked) naked_player_cache["[C.ckey]%%[H.real_name]"] |= naked + if(supernaked) + very_naked_player_cache["[C.ckey]%%[H.real_name]"] |= supernaked if(clothed) clothed_player_cache["[C.ckey]%%[H.real_name]"] |= clothed if(clothed2) diff --git a/code/game/objects/items/mannequin.dm b/code/game/objects/items/mannequin.dm index 07265026b6c..f0c87be80aa 100644 --- a/code/game/objects/items/mannequin.dm +++ b/code/game/objects/items/mannequin.dm @@ -163,6 +163,7 @@ var/my_ckey var/my_name var/nude = TRUE + var/very_nude = FALSE var/random_clothes = FALSE /obj/item/ckey_mannequin/clothed @@ -171,6 +172,9 @@ /obj/item/ckey_mannequin/clothed/random random_clothes = TRUE +/obj/item/ckey_mannequin/very_nude + very_nude = TRUE + /obj/item/ckey_mannequin/attack_hand(mob/user, act_intent, attackchain_flags) if(!attune_to(user)) return ..() @@ -187,13 +191,13 @@ visible_message("[user] touches [src], and it transforms!") my_ckey = user.ckey my_name = user.real_name + SSdummy.snapshot_player(my_ckey) START_PROCESSING(SSobj, src) update_icon() return TRUE /obj/item/ckey_mannequin/process() - update_icon() - if(prob(1)) + if(prob(5)) switch(rand(1,5)) if(1) step_rand(src) @@ -205,6 +209,7 @@ TOGGLE_VAR(nude) if(5) TOGGLE_VAR(random_clothes) + update_icon() /obj/item/ckey_mannequin/update_overlays() . = ..() @@ -215,10 +220,11 @@ name = initial(name) desc = initial(desc) return - SSdummy.snapshot_player(my_ckey) var/list/imglist var/list/cool_list - if(nude) + if(very_nude) + cool_list = SSdummy.very_naked_player_cache + else if(nude) cool_list = SSdummy.naked_player_cache else if(random_clothes) cool_list = SSdummy.randomclothed_player_cache diff --git a/code/modules/admin/create_mob.dm b/code/modules/admin/create_mob.dm index 17da8a90066..0881b002732 100644 --- a/code/modules/admin/create_mob.dm +++ b/code/modules/admin/create_mob.dm @@ -10,14 +10,26 @@ user << browse(create_panel_helper(create_mob_html), "window=create_mob;size=425x475") -/proc/randomize_human(mob/living/carbon/human/H, species) +/mob/living/carbon/human/proc/randomize(species, undies, genitals) + return randomize_human(src, species, undies, genitals) + +/proc/randomize_human(mob/living/carbon/human/H, species, undies = TRUE, genitals) + if(!ishuman(H)) + return H.gender = pick(MALE, FEMALE) H.real_name = random_unique_name(H.gender) H.name = H.real_name - H.underwear = random_underwear(H.gender) + if(undies) + H.underwear = random_underwear(H.gender) + H.undershirt = random_undershirt(H.gender) + H.socks = random_socks(H.gender) + else + H.underwear = "Nude" + H.undershirt = "Nude" + H.socks = "Nude" H.undie_color = random_short_color() - H.undershirt = random_undershirt(H.gender) H.shirt_color = random_short_color() + H.socks_color = random_short_color() H.dna.skin_tone_override = null H.skin_tone = random_skin_tone() H.hair_style = random_hair_style(H.gender) @@ -95,8 +107,55 @@ // H.dna.features["deco_wings"] = pick(GLOB.deco_wings_list) // H.dna.features["insect_fluff"] = pick(GLOB.insect_fluffs_list) // H.dna.features["legs"] = "Digitigrade" + if(genitals) + var/static/list/boob_cup_sizes + if(!boob_cup_sizes) + var/list/L = CONFIG_GET(keyed_list/breasts_cups_prefs) + boob_cup_sizes = L.Copy() + var/static/penis_inches_min + if(!penis_inches_min) + penis_inches_min = CONFIG_GET(number/penis_min_inches_prefs) + var/static/penis_inches_max + if(!penis_inches_max) + penis_inches_max = CONFIG_GET(number/penis_max_inches_prefs) + var/static/butt_size_min + if(!butt_size_min) + butt_size_min = CONFIG_GET(number/butt_min_size_prefs) + var/static/butt_size_max + if(!butt_size_max) + butt_size_max = CONFIG_GET(number/butt_max_size_prefs) + var/static/belly_size_min + if(!belly_size_min) + belly_size_min = CONFIG_GET(number/belly_min_size_prefs) + var/static/belly_size_max + if(!belly_size_max) + belly_size_max = CONFIG_GET(number/belly_max_size_prefs) + if(prob(50)) + H.dna.features["has_butt"] = TRUE + H.dna.features["butt_color"] = H.dna.features["mcolor"] + H.dna.features["butt_size"] = GaussianRangePicker(butt_size_min, butt_size_max, 4, 3) + if(prob(80)) + H.dna.features["has_belly"] = TRUE + H.dna.features["belly_color"] = H.dna.features["mcolor"] + H.dna.features["belly_size"] = GaussianRangePicker(belly_size_min, belly_size_max, 4, 3) + H.dna.features["belly_shape"] = pick(GLOB.belly_shapes_list) + if(H.gender == FEMALE || H.gender == NEUTER || H.gender == PLURAL || prob(10)) + H.dna.features["has_vag"] = TRUE // they only get the normal vag cus the rest are scary D: + H.dna.features["vag_color"] = H.dna.features["mcolor"] + + H.dna.features["has_womb"] = TRUE + H.dna.features["has_breasts"] = TRUE + H.dna.features["breasts_color"] = H.dna.features["mcolor"] + H.dna.features["breasts_size"] = GaussianListPicker(boob_cup_sizes, 5, 2) // The wasteland has a buxomness epidemic + if(H.gender == MALE || H.gender == NEUTER || H.gender == PLURAL || prob(10)) + H.dna.features["has_balls"] = TRUE + H.dna.features["balls_color"] = H.dna.features["mcolor"] + H.dna.features["has_cock"] = TRUE + H.dna.features["cock_size"] = GaussianRangePicker(penis_inches_min, penis_inches_max, 7, 3) // fuck it, everyone's got a HUUUUUGE COCK =3 + H.dna.features["cock_shape"] = pick(GLOB.cock_shapes_list) + H.give_genitals(TRUE) SEND_SIGNAL(H, COMSIG_HUMAN_ON_RANDOMIZE) H.update_body(TRUE) diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm index e2af845c441..f0b7c522e8d 100644 --- a/code/modules/awaymissions/corpse.dm +++ b/code/modules/awaymissions/corpse.dm @@ -18,7 +18,7 @@ var/important_info = "" var/faction = null var/permanent = FALSE //If true, the spawner will not disappear upon running out of uses. - var/random = FALSE //Don't set a name or gender, just go random + var/random = TRUE //Don't set a name or gender, just go random var/antagonist_type var/objectives = null var/uses = 1 //how many times can we spawn from it. set to -1 for infinite. @@ -96,6 +96,8 @@ if(!mob_gender) mob_gender = pick(MALE, FEMALE) M.gender = mob_gender + else + randomize_human(M, null, prob(50), TRUE) if(faction) M.faction = list(faction) if(disease) @@ -336,7 +338,7 @@ name = "Cook" outfit = /datum/outfit/job/wasteland/f13wastelander - +random /obj/effect/mob_spawn/human/doctor name = "Doctor" outfit = /datum/outfit/job/doctor diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 1607f4f3e49..5008e7538fe 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -1208,7 +1208,7 @@ stomach_contents.Add(C) log_combat(src, C, "devoured") -/mob/living/carbon/proc/create_bodyparts() +/mob/living/carbon/proc/create_bodyparts(actually_dont) var/l_arm_index_next = -1 var/r_arm_index_next = 0 for(var/X in bodyparts) @@ -1224,6 +1224,11 @@ r_arm_index_next += 2 O.held_index = r_arm_index_next //2, 4, 6, 8... hand_bodyparts += O + if(actually_dont) + for(var/obj/item/bodypart/O in bodyparts) + if(O.body_zone == BODY_ZONE_CHEST) + continue + qdel(O) /mob/living/carbon/do_after_coefficent() . = ..() diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 1ea7a0b0919..97ce240e698 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -7,6 +7,15 @@ GLOBAL_VAR_INIT(crotch_call_cooldown, 0) icon = 'icons/mob/human.dmi' icon_state = "caucasian_m" appearance_flags = KEEP_TOGETHER|TILE_BOUND|PIXEL_SCALE|LONG_GLIDE + var/potato = FALSE + +/mob/living/carbon/human/twoman + +/mob/living/carbon/human/twoman/Initialize() + . = ..() + underwear = "Nude" + undershirt = "Nude" + update_body(TRUE) /mob/living/carbon/human/Initialize() add_verb(src, /mob/living/proc/mob_sleep) @@ -19,7 +28,7 @@ GLOBAL_VAR_INIT(crotch_call_cooldown, 0) //initialize dna. for spawned humans; overwritten by other code create_dna(src) - randomize_human(src) + randomize_human(src, TRUE, TRUE) dna.initialize_dna() if(dna.species) @@ -53,51 +62,6 @@ GLOBAL_VAR_INIT(crotch_call_cooldown, 0) AddElement(/datum/element/flavor_text, _name = "OOC Notes", _addendum = "Put information on ERP/lewd-related preferences here. THIS SHOULD NOT CONTAIN REGULAR FLAVORTEXT!!", _always_show = TRUE, _save_key = "ooc_notes", _examine_no_preview = TRUE) RegisterSignal(src, COMSIG_HUMAN_UPDATE_GENITALS, .proc/signal_update_genitals) -/mob/living/carbon/twoman - name = "Unknown" - real_name = "Unknown" - icon = 'icons/mob/human.dmi' - icon_state = "caucasian_m" - appearance_flags = KEEP_TOGETHER|TILE_BOUND|PIXEL_SCALE|LONG_GLIDE - -/mob/living/carbon/human/Initialize() - add_verb(src, /mob/living/proc/mob_sleep) - add_verb(src, /mob/living/proc/lay_down) - add_verb(src, /mob/living/carbon/human/verb/underwear_toggle) - add_verb(src, /mob/living/verb/subtle) - add_verb(src, /mob/living/verb/subtler) - - //initialize dna. for spawned humans; overwritten by other code - create_dna(src) - randomize_human(src) - dna.initialize_dna() - - if(dna.species) - set_species(dna.species.type) - - - AddComponent(/datum/component/personal_crafting) - AddComponent(/datum/component/footstep, FOOTSTEP_MOB_HUMAN, 0.3, 5) - . = ..() - - if(CONFIG_GET(flag/disable_stambuffer)) - enable_intentional_sprint_mode() - - RegisterSignal(src, COMSIG_COMPONENT_CLEAN_ACT, /atom.proc/clean_blood) - GLOB.human_list += src - - update_body(TRUE) - -/mob/living/carbon/twoman/ComponentInitialize() - . = ..() - if(!CONFIG_GET(flag/disable_human_mood)) - AddComponent(/datum/component/mood) - AddComponent(/datum/component/combat_mode) - AddElement(/datum/element/flavor_text/carbon, _name = "Flavor Text", _save_key = "flavor_text") - AddElement(/datum/element/flavor_text, "", "Set Pose/Leave OOC Message", "This should be used only for things pertaining to the current round!") - AddElement(/datum/element/flavor_text, _name = "OOC Notes", _addendum = "Put information on ERP/lewd-related preferences here. THIS SHOULD NOT CONTAIN REGULAR FLAVORTEXT!!", _always_show = TRUE, _save_key = "ooc_notes", _examine_no_preview = TRUE) - - /mob/living/carbon/human/Destroy() QDEL_NULL(physiology) GLOB.human_list -= src diff --git a/code/modules/research/designs/limbgrower_designs.dm b/code/modules/research/designs/limbgrower_designs.dm index 2c9c2cbbac3..a4b406d6de9 100644 --- a/code/modules/research/designs/limbgrower_designs.dm +++ b/code/modules/research/designs/limbgrower_designs.dm @@ -111,7 +111,7 @@ id = "torso" build_type = LIMBGROWER reagents_list = list(/datum/reagent/medicine/synthflesh = 300) - build_path = /mob/living/carbon/twoman + build_path = /mob/living/carbon/human/twoman category = list("initial","human") /datum/design/vagina