mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-10 10:12:45 +00:00
Co-authored-by: silicons <2003111+silicons@users.noreply.github.com> Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
490 lines
17 KiB
Plaintext
490 lines
17 KiB
Plaintext
// Little define makes it cleaner to read the tripple color values out of mobs.
|
|
#define MOB_HEX_COLOR(M, V) "#[num2hex(M.r_##V, 2)][num2hex(M.g_##V, 2)][num2hex(M.b_##V, 2)]"
|
|
|
|
#define MENU_MAIN "Main"
|
|
#define MENU_BODYRECORDS "Body Records"
|
|
#define MENU_STOCKRECORDS "Stock Records"
|
|
#define MENU_SPECIFICRECORD "Specific Record"
|
|
#define MENU_OOCNOTES "OOC Notes"
|
|
|
|
/obj/machinery/computer/transhuman/designer
|
|
name = "body design console"
|
|
catalogue_data = list(/datum/category_item/catalogue/information/organization/vey_med,
|
|
/datum/category_item/catalogue/technology/resleeving)
|
|
icon = 'icons/obj/computer.dmi'
|
|
icon_keyboard = "med_key"
|
|
icon_screen = "explosive"
|
|
light_color = "#315ab4"
|
|
circuit = /obj/item/circuitboard/body_designer
|
|
req_access = list(access_medical) // Used for loading people's designs
|
|
var/temp = ""
|
|
var/menu = MENU_MAIN //Which menu screen to display
|
|
var/datum/transhuman/body_record/active_br = null
|
|
//Mob preview
|
|
var/map_name
|
|
var/obj/screen/south_preview = null
|
|
var/obj/screen/east_preview = null
|
|
var/obj/screen/west_preview = null
|
|
// Mannequins are somewhat expensive to create, so cache it
|
|
var/mob/living/carbon/human/dummy/mannequin/mannequin = null
|
|
var/obj/item/disk/body_record/disk = null
|
|
|
|
// Resleeving database this machine interacts with. Blank for default database
|
|
// Needs a matching /datum/transcore_db with key defined in code
|
|
var/db_key
|
|
var/datum/transcore_db/our_db // These persist all round and are never destroyed, just keep a hard ref
|
|
|
|
/obj/machinery/computer/transhuman/designer/Initialize()
|
|
. = ..()
|
|
map_name = "transhuman_designer_[REF(src)]_map"
|
|
|
|
south_preview = new
|
|
south_preview.name = ""
|
|
south_preview.assigned_map = map_name
|
|
south_preview.del_on_map_removal = FALSE
|
|
south_preview.screen_loc = "[map_name]:1,1"
|
|
|
|
east_preview = new
|
|
east_preview.name = ""
|
|
east_preview.assigned_map = map_name
|
|
east_preview.del_on_map_removal = FALSE
|
|
east_preview.screen_loc = "[map_name]:2,1"
|
|
|
|
west_preview = new
|
|
west_preview.name = ""
|
|
west_preview.assigned_map = map_name
|
|
west_preview.del_on_map_removal = FALSE
|
|
west_preview.screen_loc = "[map_name]:0,1"
|
|
|
|
our_db = SStranscore.db_by_key(db_key)
|
|
|
|
/obj/machinery/computer/transhuman/designer/Destroy()
|
|
active_br = null
|
|
mannequin = null
|
|
disk = null
|
|
return ..()
|
|
|
|
/obj/machinery/computer/transhuman/designer/dismantle()
|
|
if(disk)
|
|
disk.forceMove(get_turf(src))
|
|
disk = null
|
|
..()
|
|
|
|
/obj/machinery/computer/transhuman/designer/attackby(obj/item/W as obj, mob/user as mob)
|
|
if(istype(W, /obj/item/disk/body_record))
|
|
user.unEquip(W)
|
|
disk = W
|
|
disk.forceMove(src)
|
|
to_chat(user, span_notice("You insert \the [W] into \the [src]."))
|
|
updateUsrDialog()
|
|
else
|
|
..()
|
|
return
|
|
|
|
/obj/machinery/computer/transhuman/designer/attack_ai(mob/user as mob)
|
|
return attack_hand(user)
|
|
|
|
/obj/machinery/computer/transhuman/designer/attack_hand(mob/user as mob)
|
|
add_fingerprint(user)
|
|
if(inoperable())
|
|
return
|
|
tgui_interact(user)
|
|
|
|
/obj/machinery/computer/transhuman/designer/tgui_interact(mob/user, datum/tgui/ui)
|
|
ui = SStgui.try_update_ui(user, src, ui)
|
|
if(!ui)
|
|
give_client_previews(user.client)
|
|
ui = new(user, src, "BodyDesigner", name)
|
|
ui.open()
|
|
|
|
/obj/machinery/computer/transhuman/designer/tgui_static_data(mob/user)
|
|
var/list/data = ..()
|
|
data["mapRef"] = map_name
|
|
return data
|
|
|
|
/obj/machinery/computer/transhuman/designer/tgui_data(mob/user)
|
|
var/list/data = list()
|
|
|
|
if(menu == MENU_BODYRECORDS)
|
|
var/bodyrecords_list_ui[0]
|
|
for(var/N in our_db.body_scans)
|
|
var/datum/transhuman/body_record/BR = our_db.body_scans[N]
|
|
bodyrecords_list_ui[++bodyrecords_list_ui.len] = list("name" = N, "recref" = "\ref[BR]")
|
|
if(bodyrecords_list_ui.len)
|
|
data["bodyrecords"] = bodyrecords_list_ui
|
|
|
|
if(menu == MENU_STOCKRECORDS)
|
|
var/stock_bodyrecords_list_ui[0]
|
|
for (var/N in GLOB.all_species)
|
|
var/datum/species/S = GLOB.all_species[N]
|
|
if((S.spawn_flags & (SPECIES_IS_WHITELISTED|SPECIES_CAN_JOIN)) != SPECIES_CAN_JOIN) continue
|
|
stock_bodyrecords_list_ui += N
|
|
if(stock_bodyrecords_list_ui.len)
|
|
data["stock_bodyrecords"] = stock_bodyrecords_list_ui
|
|
|
|
if(active_br)
|
|
data["activeBodyRecord"] = list(
|
|
"real_name" = active_br.mydna.name,
|
|
"speciesname" = active_br.speciesname ? active_br.speciesname : active_br.mydna.dna.species,
|
|
"gender" = active_br.bodygender,
|
|
"synthetic" = active_br.synthetic ? "Yes" : "No",
|
|
"locked" = active_br.locked ? "Low" : "High",
|
|
"scale" = player_size_name(active_br.sizemult),
|
|
"booc" = active_br.body_oocnotes,
|
|
"styles" = list()
|
|
)
|
|
|
|
var/list/styles = data["activeBodyRecord"]["styles"]
|
|
var/list/temp
|
|
|
|
temp = list("styleHref" = "ear_style", "style" = "Normal")
|
|
if(mannequin.ear_style)
|
|
temp["style"] = mannequin.ear_style.name
|
|
if(mannequin.ear_style.do_colouration)
|
|
temp["color"] = MOB_HEX_COLOR(mannequin, ears)
|
|
temp["colorHref"] = "ear_color"
|
|
if(mannequin.ear_style.extra_overlay)
|
|
temp["color2"] = MOB_HEX_COLOR(mannequin, ears2)
|
|
temp["colorHref2"] = "ear_color2"
|
|
styles["Ears"] = temp
|
|
|
|
temp = list("styleHref" = "ear_style", "style" = "Normal")
|
|
if(mannequin.ear_secondary_style)
|
|
temp["style"] = mannequin.ear_secondary_style.name
|
|
if(length(mannequin.ear_secondary_colors) >= 1)
|
|
temp["color"] = mannequin.ear_secondary_colors[1]
|
|
temp["colorHref"] = list("act" = "ear_secondary_color", "channel" = 1)
|
|
if(length(mannequin.ear_secondary_colors) >= 2)
|
|
temp["color"] = mannequin.ear_secondary_colors[2]
|
|
temp["colorHref"] = list("act" = "ear_secondary_color", "channel" = 2)
|
|
styles["Horns"] = temp
|
|
|
|
temp = list("styleHref" = "tail_style", "style" = "Normal")
|
|
if(mannequin.tail_style)
|
|
temp["style"] = mannequin.tail_style.name
|
|
if(mannequin.tail_style.do_colouration)
|
|
temp["color"] = MOB_HEX_COLOR(mannequin, tail)
|
|
temp["colorHref"] = "tail_color"
|
|
if(mannequin.tail_style.extra_overlay)
|
|
temp["color2"] = MOB_HEX_COLOR(mannequin, tail2)
|
|
temp["colorHref2"] = "tail_color2"
|
|
styles["Tail"] = temp
|
|
|
|
temp = list("styleHref" = "wing_style", "style" = "Normal")
|
|
if(mannequin.wing_style)
|
|
temp["style"] = mannequin.wing_style.name
|
|
if(mannequin.wing_style.do_colouration)
|
|
temp["color"] = MOB_HEX_COLOR(mannequin, wing)
|
|
temp["colorHref"] = "wing_color"
|
|
if(mannequin.wing_style.extra_overlay)
|
|
temp["color2"] = MOB_HEX_COLOR(mannequin, wing2)
|
|
temp["colorHref2"] = "wing_color2"
|
|
styles["Wing"] = temp
|
|
|
|
temp = list("styleHref" = "hair_style", "style" = mannequin.h_style)
|
|
if(mannequin.species && (mannequin.species.appearance_flags & HAS_HAIR_COLOR))
|
|
temp["color"] = MOB_HEX_COLOR(mannequin, hair)
|
|
temp["colorHref"] = "hair_color"
|
|
styles["Hair"] = temp
|
|
|
|
temp = list("styleHref" = "facial_style", "style" = mannequin.f_style)
|
|
if(mannequin.species && (mannequin.species.appearance_flags & HAS_HAIR_COLOR))
|
|
temp["color"] = MOB_HEX_COLOR(mannequin, facial)
|
|
temp["colorHref"] = "facial_color"
|
|
styles["Facial"] = temp
|
|
|
|
if(mannequin.species && (mannequin.species.appearance_flags & HAS_EYE_COLOR))
|
|
styles["Eyes"] = list("colorHref" = "eye_color", "color" = MOB_HEX_COLOR(mannequin, eyes))
|
|
|
|
if(mannequin.species && (mannequin.species.appearance_flags & HAS_SKIN_COLOR))
|
|
styles["Body Color"] = list("colorHref" = "skin_color", "color" = MOB_HEX_COLOR(mannequin, skin))
|
|
|
|
if (mannequin.species && mannequin.species.selects_bodytype)
|
|
if (!mannequin.species.base_species)
|
|
mannequin.species.base_species = mannequin.species.name
|
|
styles["Bodytype"] = list("styleHref" = "custom_base", "style" = mannequin.species.base_species)
|
|
|
|
var/datum/preferences/designer/P = new()
|
|
apply_markings_to_prefs(mannequin, P)
|
|
data["activeBodyRecord"]["markings"] = P.body_markings
|
|
data["activeBodyRecord"]["digitigrade"] = mannequin.digitigrade
|
|
|
|
data["menu"] = menu
|
|
data["temp"] = temp
|
|
data["disk"] = disk ? 1 : 0
|
|
data["diskStored"] = disk && disk.stored ? 1 : 0
|
|
|
|
return data
|
|
|
|
/obj/machinery/computer/transhuman/designer/tgui_act(action, params)
|
|
if(..())
|
|
return TRUE
|
|
|
|
switch(action)
|
|
if("debug_load_my_body")
|
|
active_br = new /datum/transhuman/body_record(usr, FALSE, FALSE)
|
|
update_preview_icon()
|
|
menu = MENU_SPECIFICRECORD
|
|
|
|
if("view_brec")
|
|
var/datum/transhuman/body_record/BR = locate(params["view_brec"])
|
|
if(BR && istype(BR.mydna))
|
|
if(allowed(usr) || BR.ckey == usr.ckey)
|
|
active_br = new /datum/transhuman/body_record(BR) // Load a COPY!
|
|
update_preview_icon()
|
|
menu = MENU_SPECIFICRECORD
|
|
else
|
|
active_br = null
|
|
temp = "Access denied: Body records are confidential."
|
|
else
|
|
active_br = null
|
|
temp = "ERROR: Record missing."
|
|
|
|
if("view_stock_brec")
|
|
var/datum/species/S = GLOB.all_species[params["view_stock_brec"]]
|
|
if(S && (S.spawn_flags & (SPECIES_IS_WHITELISTED|SPECIES_CAN_JOIN)) == SPECIES_CAN_JOIN)
|
|
// Generate body record from species!
|
|
mannequin = new(null, S.name)
|
|
mannequin.real_name = "Stock [S.name] Body"
|
|
mannequin.name = mannequin.real_name
|
|
mannequin.dna.real_name = mannequin.real_name
|
|
mannequin.dna.base_species = mannequin.species.base_species
|
|
active_br = new(mannequin, FALSE, FALSE)
|
|
active_br.speciesname = "Custom Sleeve"
|
|
update_preview_icon()
|
|
menu = MENU_SPECIFICRECORD
|
|
else
|
|
active_br = null
|
|
temp = "ERROR: Stock Record missing."
|
|
|
|
if("boocnotes")
|
|
menu = MENU_OOCNOTES
|
|
|
|
if("loadfromdisk")
|
|
if(disk && disk.stored)
|
|
active_br = new /datum/transhuman/body_record(disk.stored) // Loads a COPY!
|
|
update_preview_icon()
|
|
menu = MENU_SPECIFICRECORD
|
|
|
|
if("savetodisk")
|
|
if(disk && active_br)
|
|
disk.stored = new /datum/transhuman/body_record(active_br) // Saves a COPY!
|
|
disk.name = "[initial(disk.name)] ([active_br.mydna.name])"
|
|
disk.forceMove(get_turf(src))
|
|
disk = null
|
|
|
|
if("ejectdisk")
|
|
disk.forceMove(get_turf(src))
|
|
disk = null
|
|
|
|
if("menu")
|
|
menu = params["menu"]
|
|
temp = ""
|
|
|
|
if("href_conversion")
|
|
PrefHrefMiddleware(params, usr)
|
|
|
|
add_fingerprint(usr)
|
|
return 1 // Return 1 to refresh UI
|
|
|
|
//
|
|
// Code below is for generating preview icons based on a body_record
|
|
//
|
|
|
|
// Based on /datum/preferences/proc/update_preview_icon()
|
|
/obj/machinery/computer/transhuman/designer/proc/update_preview_icon()
|
|
if(!mannequin)
|
|
mannequin = new ()
|
|
mannequin.delete_inventory(TRUE)
|
|
update_preview_mob(mannequin)
|
|
mannequin.ImmediateOverlayUpdate()
|
|
|
|
var/mutable_appearance/MA = new(mannequin)
|
|
south_preview.appearance = MA
|
|
south_preview.dir = SOUTH
|
|
south_preview.screen_loc = "[map_name]:1,1"
|
|
south_preview.name = ""
|
|
east_preview.appearance = MA
|
|
east_preview.dir = EAST
|
|
east_preview.screen_loc = "[map_name]:2,1"
|
|
east_preview.name = ""
|
|
west_preview.appearance = MA
|
|
west_preview.dir = WEST
|
|
west_preview.screen_loc = "[map_name]:0,1"
|
|
west_preview.name = ""
|
|
|
|
/obj/machinery/computer/transhuman/designer/proc/give_client_previews(client/C)
|
|
C.register_map_obj(south_preview)
|
|
C.register_map_obj(east_preview)
|
|
C.register_map_obj(west_preview)
|
|
|
|
|
|
/obj/machinery/computer/transhuman/designer/proc/update_preview_mob(var/mob/living/carbon/human/H)
|
|
ASSERT(!QDELETED(H))
|
|
ASSERT(!QDELETED(active_br))
|
|
//log_debug("designer.update_preview_mob([H]) active_br = \ref[active_br]")
|
|
//Get the DNA and generate a new mob
|
|
var/datum/dna2/record/R = active_br.mydna
|
|
H.set_species(R.dna.species) // This needs to happen before anything else becuase it sets some variables.
|
|
|
|
// Update the external organs
|
|
for(var/part in active_br.limb_data)
|
|
var/status = active_br.limb_data[part]
|
|
if(status == null) continue //Species doesn't have limb? Child of amputated limb?
|
|
|
|
var/obj/item/organ/external/O = H.organs_by_name[part]
|
|
if(!O) continue //Not an organ. Perhaps another amputation removed it already.
|
|
|
|
if(status == 1) //Normal limbs
|
|
continue
|
|
else if(status == 0) //Missing limbs
|
|
O.remove_rejuv()
|
|
else if(status) //Anything else is a manufacturer
|
|
if(active_br.synthetic)
|
|
O.robotize(status)
|
|
else
|
|
O.remove_rejuv()
|
|
|
|
// Then the internal organs. I think only O_EYES acutally counts, but lets do all just in case
|
|
for(var/part in active_br.organ_data)
|
|
var/status = active_br.organ_data[part]
|
|
if(status == null) continue //Species doesn't have organ? Child of missing part?
|
|
|
|
var/obj/item/organ/I = H.internal_organs_by_name[part]
|
|
if(!I) continue//Not an organ. Perhaps external conversion changed it already?
|
|
|
|
if(status == 0) //Normal organ
|
|
continue
|
|
else if(status == 1) //Assisted organ
|
|
I.mechassist()
|
|
else if(status == 2) //Mechanical organ
|
|
I.robotize()
|
|
else if(status == 3) //Digital organ
|
|
I.digitize()
|
|
|
|
// Apply DNA
|
|
H.dna = R.dna.Clone()
|
|
H.UpdateAppearance() // Update all appearance stuff from the DNA record
|
|
H.sync_organ_dna() // Do this because sprites depend on DNA-gender of organs (chest etc)
|
|
H.resize(active_br.sizemult, FALSE)
|
|
|
|
// Emissiive...
|
|
if(H.ear_style)
|
|
H.ear_style.em_block = FALSE
|
|
if(H.tail_style)
|
|
H.tail_style.em_block = FALSE
|
|
if(H.wing_style)
|
|
H.wing_style.em_block = FALSE
|
|
|
|
// And as for clothing...
|
|
// We don't actually dress them! This is a medical machine, handle the nakedness DOCTOR!
|
|
|
|
H.regenerate_icons()
|
|
return 0 // Success!
|
|
|
|
// HORROR SHOW BELOW
|
|
// In order to avoid duplicating the many lines of code in player_setup that handle customizing
|
|
// body setup, we acutally are invoking those methods in order to let people customize the body here.
|
|
// Problem is, those procs save their data to /datum/preferences, not a body_record.
|
|
// Luckily the procs to convert from body_record to /datum/preferences and back already exist.
|
|
// Its ugly, but I think its still better than duplicating and maintaining all that code.
|
|
/obj/machinery/computer/transhuman/designer/proc/PrefHrefMiddleware(list/params, var/mob/user)
|
|
if(!mannequin || !active_br)
|
|
return
|
|
|
|
if(params["target_href"] == "size_multiplier")
|
|
var/new_size = tgui_input_number(user, "Choose your character's size, ranging from 25% to 200%", "Character Preference", null, 200, 25)
|
|
if(new_size && ISINRANGE(new_size,25,200))
|
|
active_br.sizemult = (new_size/100)
|
|
update_preview_icon()
|
|
return 1
|
|
|
|
// The black magic horror show begins
|
|
var/datum/preferences/designer/P = new()
|
|
|
|
// We did DNA to mob, now mob to prefs!
|
|
P.species = mannequin.species.name
|
|
apply_coloration_to_prefs(mannequin, P)
|
|
apply_organs_to_prefs(mannequin, P)
|
|
apply_markings_to_prefs(mannequin, P)
|
|
apply_ears_to_prefs(mannequin, P)
|
|
|
|
// Now we start using the player_setup objects to do stuff!
|
|
var/datum/category_collection/CC = P.player_setup
|
|
var/datum/category_group/CG = CC.categories_by_name["General"]
|
|
var/datum/category_item/player_setup_item/general/body/B = CG.items_by_name["Body"]
|
|
ASSERT(istype(B))
|
|
var/datum/category_item/player_setup_item/general/basic/G = CG.items_by_name["Basic"]
|
|
ASSERT(istype(G))
|
|
var/datum/category_item/player_setup_item/vore/traits/V = CC.categories_by_name["VORE"].items_by_name["Traits"]
|
|
ASSERT(istype(V))
|
|
var/list/use_different_category = list("custom_base" = V) //add more here if needed
|
|
|
|
if(params["target_href"] == "bio_gender")
|
|
var/new_gender = tgui_input_list(user, "Choose your character's biological gender:", "Character Preference", G.get_genders())
|
|
if(new_gender)
|
|
active_br.bodygender = new_gender
|
|
active_br.mydna.dna.SetUIState(DNA_UI_GENDER, new_gender!=MALE, 1)
|
|
update_preview_icon()
|
|
return 1
|
|
|
|
var/href_list = list()
|
|
href_list["src"] = "\ref[src]"
|
|
var/list/target_href_maybe = params["target_href"]
|
|
// convert list-form inputs as needed
|
|
if(islist(target_href_maybe))
|
|
href_list[target_href_maybe["act"]] = TRUE
|
|
for(var/key in target_href_maybe["params"])
|
|
var/val = target_href_maybe["params"][key]
|
|
href_list[key] = "[val]"
|
|
else
|
|
href_list[target_href_maybe] = params["target_value"]
|
|
var/datum/category_item/player_setup_item/to_use = (params["target_href"] in use_different_category) ? use_different_category[params["target_href"]] : B
|
|
|
|
var/action = 0
|
|
action = to_use.OnTopic(list2params(href_list), href_list, user)
|
|
if(action & TOPIC_UPDATE_PREVIEW && mannequin && active_br)
|
|
to_use.copy_to_mob(mannequin)
|
|
active_br.mydna.dna.ResetUIFrom(mannequin)
|
|
update_preview_icon()
|
|
return 1
|
|
|
|
// Fake subtype of preferences we can use to steal code from player_setup
|
|
/datum/preferences/designer/New()
|
|
player_setup = new(src)
|
|
// Do NOT call ..(), it expects real stuff
|
|
|
|
// Disk for manually moving body records between the designer and sleever console etc.
|
|
/obj/item/disk/body_record
|
|
name = "Body Design Disk"
|
|
desc = "It has a small label: \n\
|
|
\"Portable Body Record Storage Disk. \n\
|
|
Insert into resleeving control console\""
|
|
icon = 'icons/obj/discs_vr.dmi'
|
|
icon_state = "data-green"
|
|
item_state = "card-id"
|
|
w_class = ITEMSIZE_SMALL
|
|
var/datum/transhuman/body_record/stored = null
|
|
|
|
/*
|
|
* Diskette Box
|
|
*/
|
|
|
|
/obj/item/storage/box/body_record_disk
|
|
name = "body record disk box"
|
|
desc = "A box of body record disks, apparently."
|
|
icon_state = "disk_kit"
|
|
|
|
/obj/item/storage/box/body_record_disk/New()
|
|
..()
|
|
for(var/i = 0 to 7)
|
|
new /obj/item/disk/body_record(src)
|
|
|
|
#undef MOB_HEX_COLOR
|
|
|
|
#undef MENU_MAIN
|
|
#undef MENU_BODYRECORDS
|
|
#undef MENU_STOCKRECORDS
|
|
#undef MENU_SPECIFICRECORD
|
|
#undef MENU_OOCNOTES
|