From eef848044486a7705709661ebe9bc0f3206ce9a2 Mon Sep 17 00:00:00 2001 From: Leshana Date: Sun, 9 Apr 2017 16:16:32 -0400 Subject: [PATCH] Adds the Body Designer for resleeving * Adds Body Designer computer, which allows you to customize body designs similarly to character setup, but in game. * Designs are savable to disks which can be loaded into the the resleeving controller console to print and resleeve into. * Fixes line breaks on OOC notes in the resleeving computer. * Added circuits so the body designer is constructable, and designs so the circuit is researchable. * Added a proc for reverse lookup of size_multiplier -> scale name (0.25 -> Micro etc) --- .../preference_setup/vore/04_resleeving.dm | 4 +- code/modules/research/designs_vr.dm | 7 + code/modules/resleeving/circuitboards.dm | 8 +- code/modules/resleeving/computers.dm | 15 +- code/modules/resleeving/designer.dm | 390 ++++++++++++++++++ code/modules/resleeving/infocore.dm | 50 ++- code/modules/resleeving/machines.dm | 10 +- code/modules/vore/persist/persist_vr.dm | 33 ++ code/modules/vore/resizing/resize_vr.dm | 8 + icons/obj/computer_vr.dmi | Bin 0 -> 1165 bytes nano/templates/body_designer.tmpl | 129 ++++++ nano/templates/sleever.tmpl | 4 +- vorestation.dme | 1 + 13 files changed, 649 insertions(+), 10 deletions(-) create mode 100644 code/modules/resleeving/designer.dm create mode 100644 icons/obj/computer_vr.dmi create mode 100644 nano/templates/body_designer.tmpl diff --git a/code/modules/client/preference_setup/vore/04_resleeving.dm b/code/modules/client/preference_setup/vore/04_resleeving.dm index 36d82ae0fd..93174ad2dd 100644 --- a/code/modules/client/preference_setup/vore/04_resleeving.dm +++ b/code/modules/client/preference_setup/vore/04_resleeving.dm @@ -24,7 +24,9 @@ /datum/category_item/player_setup_item/vore/resleeve/copy_to_mob(var/mob/living/carbon/human/character) if(character && !istype(character,/mob/living/carbon/human/dummy)) spawn(50) - new/datum/transhuman/body_record/(character, pref.resleeve_scan, pref.resleeve_lock) //Clearly related to size. + if(pref.resleeve_scan) + var/datum/transhuman/body_record/BR = new() //Clearly related to size. + BR.init_from_mob(character, pref.resleeve_scan, pref.resleeve_lock) if(pref.resleeve_lock) character.resleeve_lock = character.ckey diff --git a/code/modules/research/designs_vr.dm b/code/modules/research/designs_vr.dm index b502767ee5..4e5ebe6ca3 100644 --- a/code/modules/research/designs_vr.dm +++ b/code/modules/research/designs_vr.dm @@ -107,6 +107,13 @@ build_path = /obj/item/weapon/circuitboard/resleeving_control sort_string = "HAADE" +/datum/design/circuit/body_designer + name = "Body design console" + id = "body_designer" + req_tech = list(TECH_DATA = 5) + build_path = /obj/item/weapon/circuitboard/body_designer + sort_string = "HAADF" + /datum/design/circuit/partslathe name = "Parts lathe" id = "partslathe" diff --git a/code/modules/resleeving/circuitboards.dm b/code/modules/resleeving/circuitboards.dm index 26a02e0430..5acf4c0744 100644 --- a/code/modules/resleeving/circuitboards.dm +++ b/code/modules/resleeving/circuitboards.dm @@ -32,10 +32,16 @@ req_components = list( /obj/item/stack/cable_coil = 2, /obj/item/weapon/stock_parts/scanning_module = 2, - /obj/item/weapon/stock_parts/manipulator = 2, + /obj/item/weapon/stock_parts/manipulator = 2, /obj/item/weapon/stock_parts/console_screen = 1) /obj/item/weapon/circuitboard/resleeving_control name = T_BOARD("resleeving control console") build_path = /obj/machinery/computer/transhuman/resleeving origin_tech = list(TECH_DATA = 5) + + +/obj/item/weapon/circuitboard/body_designer + name = T_BOARD("body design console") + build_path = /obj/machinery/computer/transhuman/designer + origin_tech = list(TECH_DATA = 5) diff --git a/code/modules/resleeving/computers.dm b/code/modules/resleeving/computers.dm index 0e2bcc98b3..7ca7de8912 100644 --- a/code/modules/resleeving/computers.dm +++ b/code/modules/resleeving/computers.dm @@ -68,7 +68,7 @@ if(istype(W, /obj/item/device/multitool)) var/obj/item/device/multitool/M = W var/obj/machinery/clonepod/transhuman/P = M.connecting - if(P && !(P in pods)) + if(istype(P) && !(P in pods)) pods += P P.connected = src P.name = "[initial(P.name)] #[pods.len]" @@ -78,6 +78,17 @@ disk = W disk.forceMove(src) user << "You insert \the [W] into \the [src]." + if(istype(W, /obj/item/weapon/disk/body_record)) + var/obj/item/weapon/disk/body_record/brDisk = W + if(!brDisk.stored) + to_chat(user, "\The [W] does not contain a stored body record.") + return + user.unEquip(W) + W.forceMove(get_turf(src)) // Drop on top of us + active_br = new /datum/transhuman/body_record(brDisk.stored) // Loads a COPY! + menu = 4 + to_chat(user, "\The [src] loads the body record from \the [W] before ejecting it.") + attack_hand(user) else ..() return @@ -192,7 +203,7 @@ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) if (!ui) - ui = new(user, src, ui_key, "sleever.tmpl", src.name, 400, 450) + ui = new(user, src, ui_key, "sleever.tmpl", "Resleeving Control Console", 400, 450) ui.set_initial_data(data) ui.open() ui.set_auto_update(5) diff --git a/code/modules/resleeving/designer.dm b/code/modules/resleeving/designer.dm new file mode 100644 index 0000000000..6eda1ea918 --- /dev/null +++ b/code/modules/resleeving/designer.dm @@ -0,0 +1,390 @@ +// 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)]" + +/obj/machinery/computer/transhuman/designer + name = "body design console" + icon = 'icons/obj/computer_vr.dmi' + icon_keyboard = "med_key" + icon_screen = "dna" + light_color = "#315ab4" + circuit = /obj/item/weapon/circuitboard/body_designer + req_access = list(access_medical) // Used for loading people's designs + var/temp = "" + var/menu = 1 //Which menu screen to display + var/datum/transhuman/body_record/active_br = null + var/icon/preview_icon = null + // Mannequins are somewhat expensive to create, so cache it + var/mob/living/carbon/human/dummy/mannequin/mannequin = null + var/datum/transhuman/infocore/TC //Easy debugging access + var/obj/item/weapon/disk/body_record/disk = null + +/obj/machinery/computer/transhuman/designer/initialize() + ..() + TC = transcore + +/obj/machinery/computer/transhuman/designer/Destroy() + active_br = null + preview_icon = null + mannequin = null + disk = null + ..() + +/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/weapon/disk/body_record)) + user.unEquip(W) + disk = W + disk.forceMove(src) + to_chat(user, "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 + ui_interact(user) + +/obj/machinery/computer/transhuman/designer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + // log_debug("designer.ui_interact([user], force_open = [force_open])") + user.set_machine(src) + + var/data[0] + + if(menu == "2") + var/bodyrecords_list_ui[0] + for(var/N in TC.body_scans) + var/datum/transhuman/body_record/BR = TC.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 == "3") + var/stock_bodyrecords_list_ui[0] + for (var/N in all_species) + var/datum/species/S = 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() + ) + if(!preview_icon) + update_preview_icon() + force_open = 1 // Force a refresh to send the new image + data["previewIconUrl"] = "body_preview_icon.png" + user << browse_rsc(preview_icon, "body_preview_icon.png") + + 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 + styles["Ears"] = 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" + styles["Tail"] = 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)) + + var/datum/preferences/designer/P = new() + apply_markings_to_prefs(mannequin, P) + data["activeBodyRecord"]["markings"] = P.body_markings + + data["menu"] = menu + data["temp"] = temp + data["disk"] = disk ? 1 : 0 + data["diskStored"] = disk && disk.stored ? 1 : 0 + + ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "body_designer.tmpl", "Body Design Console", 400, 600) + ui.set_initial_data(data) + ui.open() + +/obj/machinery/computer/transhuman/designer/Topic(href, href_list) + if(..()) + return 1 + + else if(href_list["debug_load_my_body"]) + active_br = new /datum/transhuman/body_record(usr, FALSE, FALSE) + preview_icon = null + + else if (href_list["view_brec"]) + var/datum/transhuman/body_record/BR = locate(href_list["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! + preview_icon = null + menu = 4 + else + active_br = null + temp = "Access denied: Body records are confidential" + else + active_br = null + temp = "ERROR: Record missing." + + else if(href_list["view_stock_brec"]) + var/datum/species/S = all_species[href_list["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 + active_br = new(mannequin, FALSE, FALSE) + active_br.speciesname = "Custom Sleeve" + preview_icon = null + menu = 4 + else + active_br = null + temp = "ERROR: Stock Record missing." + + else if (href_list["boocnotes"]) + menu = 6 + + else if (href_list["loadfromdisk"]) + if(disk && disk.stored) + active_br = new /datum/transhuman/body_record(disk.stored) // Loads a COPY! + preview_icon = null + + else if (href_list["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 + + else if (href_list["ejectdisk"]) + disk.forceMove(get_turf(src)) + disk = null + + else if (href_list["menu"]) + menu = href_list["menu"] + temp = "" + else + OnTopic(href, href_list, 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) + + preview_icon = icon('icons/effects/effects.dmi', "nothing") + preview_icon.Scale(48+32, 16+32) + + mannequin.dir = NORTH + var/icon/stamp = getFlatIcon(mannequin) + preview_icon.Blend(stamp, ICON_OVERLAY, 25, 17) + + mannequin.dir = WEST + stamp = getFlatIcon(mannequin) + preview_icon.Blend(stamp, ICON_OVERLAY, 1, 9) + + mannequin.dir = SOUTH + stamp = getFlatIcon(mannequin) + preview_icon.Blend(stamp, ICON_OVERLAY, 49, 1) + + preview_icon.Scale(preview_icon.Width() * 2, preview_icon.Height() * 2) // Scaling here to prevent blurring in the browser. + return preview_icon + +/obj/machinery/computer/transhuman/designer/proc/update_preview_mob(var/mob/living/carbon/human/H) + ASSERT(!deleted(H)) + ASSERT(!deleted(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.size_multiplier = active_br.sizemult + + // 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/OnTopic(var/href,var/list/href_list, var/mob/user) + if(!mannequin || !preview_icon || !active_br) + return + + if(href_list["size_multiplier"]) + var/new_size = input(user, "Choose your character's size:", "Character Preference", player_size_name(active_br.sizemult)) as null|anything in player_sizes_list + if(new_size && (new_size in player_sizes_list)) + active_br.sizemult = player_sizes_list[new_size] + preview_icon = null + 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)) + CG = CC.categories_by_name["VORE"] + var/datum/category_item/player_setup_item/vore/ears/E = CG.items_by_name["Appearance"] + ASSERT(istype(E)) + + if(href_list["bio_gender"]) + var/new_gender = input(user, "Choose your character's biological gender:", "Character Preference", active_br.bodygender) as null|anything in G.get_genders() + if(new_gender) + active_br.bodygender = new_gender + active_br.mydna.dna.SetUIState(DNA_UI_GENDER, new_gender!=MALE, 1) + preview_icon = null + return 1 + + var/action = 0 + action = B.OnTopic(href, href_list, user) + if(action & TOPIC_UPDATE_PREVIEW && mannequin && active_br) + B.copy_to_mob(mannequin) + active_br.mydna.dna.ResetUIFrom(mannequin) + preview_icon = null + return 1 + action = E.OnTopic(href, href_list, user) + if(action & TOPIC_UPDATE_PREVIEW && mannequin && active_br) + E.copy_to_mob(mannequin) + active_br.mydna.dna.ResetUIFrom(mannequin) + preview_icon = null + 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/weapon/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/cloning.dmi' + icon_state = "datadisk2" + item_state = "card-id" + w_class = ITEMSIZE_SMALL + var/datum/transhuman/body_record/stored = null + +/* + * Diskette Box + */ + +/obj/item/weapon/storage/box/body_record_disk + name = "body record disk box" + desc = "A box of body record disks, apparently." + icon_state = "disk_kit" + +/obj/item/weapon/storage/box/disks/New() + ..() + for(var/i = 0 to 7) + new /obj/item/weapon/disk/body_record(src) + +#undef MOB_HEX_COLOR diff --git a/code/modules/resleeving/infocore.dm b/code/modules/resleeving/infocore.dm index e764734a89..bd76b13984 100644 --- a/code/modules/resleeving/infocore.dm +++ b/code/modules/resleeving/infocore.dm @@ -155,8 +155,24 @@ var/datum/transhuman/infocore/transcore = new/datum/transhuman/infocore var/toocomplex var/sizemult -/datum/transhuman/body_record/New(var/mob/living/carbon/human/M,var/add_to_db = 1,var/ckeylock = 0) - ASSERT(M) +/datum/transhuman/body_record/New(var/copyfrom, var/add_to_db = 0, var/ckeylock = 0) + ..() + if(istype(copyfrom, /datum/transhuman/body_record)) + init_from_br(copyfrom) + else if(ishuman(copyfrom)) + init_from_mob(copyfrom, add_to_db, ckeylock) + +/datum/transhuman/body_record/Destroy() + mydna = null + client_ref = null + mind_ref = null + limb_data.Cut() + organ_data.Cut() + ..() + +/datum/transhuman/body_record/proc/init_from_mob(var/mob/living/carbon/human/M, var/add_to_db = 0, var/ckeylock = 0) + ASSERT(!deleted(M)) + ASSERT(istype(M)) //Person OOCly doesn't want people impersonating them locked = ckeylock @@ -232,3 +248,33 @@ var/datum/transhuman/infocore/transcore = new/datum/transhuman/infocore if(add_to_db) transcore.add_body(src) + + +/** + * Make a deep copy of this record so it can be saved on a disk without mofidications + * to the original affecting the copy. + * Just to be clear, this has nothing to do do with acutal biological cloning, body printing, resleeving, + * or anything like that! This is the computer science concept of "cloning" a data structure! + */ +/datum/transhuman/body_record/proc/init_from_br(var/datum/transhuman/body_record/orig) + ASSERT(!deleted(orig)) + ASSERT(istype(orig)) + src.mydna = new () + src.mydna.dna = orig.mydna.dna.Clone() + src.mydna.ckey = orig.mydna.ckey + src.mydna.id = orig.mydna.id + src.mydna.name = orig.mydna.name + src.mydna.types = orig.mydna.types + src.mydna.flavor = orig.mydna.flavor.Copy() + src.ckey = orig.ckey + src.locked = orig.locked + src.client_ref = orig.client_ref + src.mind_ref = orig.mind_ref + src.synthetic = orig.synthetic + src.speciesname = orig.speciesname + src.bodygender = orig.bodygender + src.body_oocnotes = orig.body_oocnotes + src.limb_data = orig.limb_data.Copy() + src.organ_data = orig.organ_data.Copy() + src.toocomplex = orig.toocomplex + src.sizemult = orig.sizemult diff --git a/code/modules/resleeving/machines.dm b/code/modules/resleeving/machines.dm index 9c94914ce2..52c3a7b67b 100644 --- a/code/modules/resleeving/machines.dm +++ b/code/modules/resleeving/machines.dm @@ -46,7 +46,7 @@ var/status = current_project.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[name] + 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 @@ -496,6 +496,12 @@ occupant.ooc_notes = MR.mind_oocnotes occupant.apply_vore_prefs() //Cheap hack for now to give them SOME bellies. + // If it was a custom sleeve (not owned by anyone), update namification sequences + if(!occupant.original_player) + occupant.real_name = occupant.mind.name + occupant.name = occupant.real_name + occupant.dna.real_name = occupant.real_name + //Give them a backup implant var/obj/item/weapon/implant/backup/new_imp = new() if(new_imp.implanted(occupant)) @@ -516,7 +522,7 @@ occupant.confused = max(occupant.confused, confuse_amount) occupant.eye_blurry = max(occupant.eye_blurry, blur_amount) - if(occupant.mind && occupant.real_name != occupant.mind.name) + if(occupant.mind && occupant.original_player && ckey(occupant.mind.key) != occupant.original_player) log_and_message_admins("is now a cross-sleeved character. Body originally belonged to [occupant.real_name]. Mind is now [occupant.mind.name].",occupant) return 1 diff --git a/code/modules/vore/persist/persist_vr.dm b/code/modules/vore/persist/persist_vr.dm index 7420acdcbb..9a3cfb7948 100644 --- a/code/modules/vore/persist/persist_vr.dm +++ b/code/modules/vore/persist/persist_vr.dm @@ -84,6 +84,39 @@ prefs.save_character() return +// Saves mob's current coloration state to prefs +// This basically needs to be the reverse of /datum/category_item/player_setup_item/general/body/copy_to_mob() ~Leshana +/proc/apply_coloration_to_prefs(var/mob/living/carbon/human/character, var/datum/preferences/prefs) + if(!istype(character)) return + prefs.r_eyes = character.r_eyes + prefs.g_eyes = character.g_eyes + prefs.b_eyes = character.b_eyes + prefs.h_style = character.h_style + prefs.r_hair = character.r_hair + prefs.g_hair = character.g_hair + prefs.b_hair = character.b_hair + prefs.f_style = character.f_style + prefs.r_facial = character.r_facial + prefs.g_facial = character.g_facial + prefs.b_facial = character.b_facial + prefs.r_skin = character.r_skin + prefs.g_skin = character.g_skin + prefs.b_skin = character.b_skin + prefs.s_tone = character.s_tone + prefs.h_style = character.h_style + prefs.f_style = character.f_style + prefs.b_type = character.b_type + +// Saves mob's current custom species, ears, and tail state to prefs +// This basically needs to be the reverse of /datum/category_item/player_setup_item/vore/ears/copy_to_mob() ~Leshana +/proc/apply_ears_to_prefs(var/mob/living/carbon/human/character, var/datum/preferences/prefs) + if(character.ear_style) prefs.ear_style = character.ear_style.type + if(character.tail_style) prefs.tail_style = character.tail_style.type + prefs.r_tail = character.r_tail + prefs.b_tail = character.b_tail + prefs.g_tail = character.g_tail + prefs.custom_species = character.custom_species + // Saves mob's current organ state to prefs. // This basically needs to be the reverse of /datum/category_item/player_setup_item/general/body/copy_to_mob() ~Leshana /proc/apply_organs_to_prefs(var/mob/living/carbon/human/character, var/datum/preferences/prefs) diff --git a/code/modules/vore/resizing/resize_vr.dm b/code/modules/vore/resizing/resize_vr.dm index b5f8b28029..ac1ca2f352 100644 --- a/code/modules/vore/resizing/resize_vr.dm +++ b/code/modules/vore/resizing/resize_vr.dm @@ -22,6 +22,14 @@ var/const/RESIZE_A_SMALLTINY = (RESIZE_SMALL + RESIZE_TINY) / 2 /mob/living/carbon/human holder_type = /obj/item/weapon/holder/micro +// The reverse lookup of player_sizes_list, number to name. +/proc/player_size_name(var/size_multiplier) + // (This assumes list is sorted big->small) + for(var/N in player_sizes_list) + . = N // So we return the smallest if we get to the end + if(size_multiplier >= player_sizes_list[N]) + return N + /** * Scale up the size of a mob's icon by the size_multiplier. * NOTE: mob/living/carbon/human/update_icons() has a more complicated system and diff --git a/icons/obj/computer_vr.dmi b/icons/obj/computer_vr.dmi new file mode 100644 index 0000000000000000000000000000000000000000..b7fb897fa309870cc3f756e488f52c26d4088b93 GIT binary patch literal 1165 zcmV;81akX{P)fFDZ*Bkpc$`yKaB_9` z^iy#0_2eo`Eh^5;&r`5fFwryM;w;ZhDainGjE%TBGg33tGfE(w;*!LYR3KAHiHkEO zv#1!zH00t;D@x2wg|OiYlJj#5N=s6Uh*lV%RFt2cnnzH5N@{UtIu2!KK+)8k#7dB` z4jiKEjn7W4B+?p?()j$eG+fG+62x3*bG}cE8IQswp;@%rXNJw(i<>Agw4meFYW6npy>y|DSUZB7rSATGRXi(th zkC3n5!AAn+gO*yg5x=x*0fJzq5hMgIKIBJH9LEe6RFXvEXS8hd*Xwl(T6gs%fPPl1 zO{2F)knjYs`Ut>k0+AoHh~Y0C0Fl4l3WBs&9Iv|oo6T;wdy};90@!>6z}yHzvkdYX zj0KBj0EmBo-~I-IG|%%7P$~nc-Kzi=zXpJjhuB1nL%ntZ(k&3c5CB2?QA-6-`&R+X zz5vK@=NVsSF*af80f;F;fCFlD05SOq=P(WrF$Lh$J_5b|F(3$B{2Cw=`Jg1j(SRdD z0!Y)(6o(--9RvD+cpN}e1~{KL3=Ow`Yy@>*6#uYZEV5ks{tVVq3ZNsmZEdfe}VmD0Ob$P54k~j6F!pu z2mt2C4ZP4${%`>EF?JLHgg=gW$QR1~z$OUb+{quD&vBx$yY3X@mVmqXgY)5x=KYgg zY~l|7PzCkw^bT^|!-~`XaDd8qoL6G*34a`bD&W#mDF7CK1OQObTf|20;136|oa!$A zV3{tpytxPvzL(d3A^S7i9KN-FpWLVF_sRWU{jQWws;`z-t$)?l|9wA~eX4%{fq(rg zpQ_(Az9vBU*V@+sbp3-4z+Auoz_*Nm#qZbeI7xB`s-NeCPkmnju-5OB`{xV0*tm2LMsO&kLaW3&tz}w)%Z~0HS`M9N^XW0z$X@?asq6 zn*5I28ow;cLii>AZ$XKEf!jvSN8K)`)8&S(em<&1zr@z4`SnXHd{6jzV;WZS8!O89 f^78WX^0Mm><}|Mdg^80r00000NkvXXu0mjff{Gkq literal 0 HcmV?d00001 diff --git a/nano/templates/body_designer.tmpl b/nano/templates/body_designer.tmpl new file mode 100644 index 0000000000..43b23af8f4 --- /dev/null +++ b/nano/templates/body_designer.tmpl @@ -0,0 +1,129 @@ + +{{:data.temp}} + +{{if data.disk}} + {{:helper.link('Save To Disk', 'disk', {'savetodisk' : 1}, data.activeBodyRecord ? null : 'linkOff')}} + {{:helper.link('Load From Disk', 'folder-open', {'loadfromdisk' : 1}, data.diskStored ? null : 'linkOff')}} + {{:helper.link('Eject Disk', 'eject', {'ejectdisk' : 1})}} +{{/if}} + + + + +{{if data.menu == 1}} + +

Database Functions

+
+ {{:helper.link('View Individual Body Records', 'list', {'menu' : 2})}} +
+
+ {{:helper.link('View Stock Body Records', 'list', {'menu' : 3})}} +
+ + +{{else data.menu == 2}} +

Current body records

+ {{:helper.link('Back', 'arrowreturn-1-w', {'menu' : 1})}} +
+ {{for data.bodyrecords}} + {{:helper.link(value.name, 'document', {'view_brec' : value.recref})}} + {{/for}} +
+ + +{{else data.menu == 3}} +

Stock body records

+ {{:helper.link('Back', 'arrowreturn-1-w', {'menu' : 1})}} +
+ {{for data.stock_bodyrecords}} + {{:helper.link(value, 'document', {'view_stock_brec' : value})}} + {{/for}} +
+ + +{{else data.menu == 4}} +

Selected Body Record

+
{{:helper.link('Back', 'arrowreturn-1-w', {'menu' : 1})}}
+ + {{if data.activeBodyRecord}} +
+
Name:
+
{{:data.activeBodyRecord.real_name}}
+
+
+
Species:
+
{{:data.activeBodyRecord.speciesname}}
+
+
+
Bio. Sex:
+
{{:helper.link(data.activeBodyRecord.gender, null, {'bio_gender' : 1})}}
+
+
+
Synthetic:
+
{{:data.activeBodyRecord.synthetic}}
+
+
+
Mind compat.:
+
{{:data.activeBodyRecord.locked}}
+
{{:helper.link('View OOC Notes', null, {'boocnotes' : 1}, data.activeBodyRecord.booc ? null : 'linkOff')}}
+
+ +
+
+ +
+
+
+
Scale:
+
{{:helper.link(data.activeBodyRecord.scale, null, {'size_multiplier' : 1})}}
+
+ + {{props data.activeBodyRecord.styles}} +
+
{{:key}}:
+
+ {{if value.styleHref}} + + {{:helper.link(value.style, null, (a={},a[value.styleHref]=1,a))}} + {{/if}} + {{if value.colorHref}} + {{:helper.link(value.color, null, (a={},a[value.colorHref]=1,a))}} +
 
+ {{/if}} +
+
+ {{/props}} + +
+
+
Body Markings
+
+ {{:helper.link('Add Marking', 'plus', {'marking_style' : 1})}} +
+
+ {{props data.activeBodyRecord.markings}} +
+ {{:key}}  + + {{:helper.link('', 'minus', {'marking_remove' : key})}} +
+ {{props}} +
+ {{else}} +
ERROR: Record not found.
+ {{/if}} + + +{{else data.menu == 6}} +

Body OOC Notes (This is OOC!)

+
{{:helper.link('Back', 'arrowreturn-1-w', {'menu' : 4})}}
+ {{if data.activeBodyRecord}} +
Notes:
+
{{:data.activeBodyRecord.booc}}
+ {{else}} +
ERROR: Record not found.
+ {{/if}} + +{{/if}} +
{{:JSON.stringify(data, null, 2)}}
diff --git a/nano/templates/sleever.tmpl b/nano/templates/sleever.tmpl index 83908768fa..89b41551a1 100644 --- a/nano/templates/sleever.tmpl +++ b/nano/templates/sleever.tmpl @@ -150,7 +150,7 @@
{{:helper.link('Back', 'arrowreturn-1-w', {'menu' : 4})}}
{{if data.activeBodyRecord}}
Notes:
-
{{:data.activeBodyRecord.booc}}
+
{{:data.activeBodyRecord.booc}}
{{else}}
ERROR: Record not found.
{{/if}} @@ -161,7 +161,7 @@
{{:helper.link('Back', 'arrowreturn-1-w', {'menu' : 5})}}
{{if data.activeMindRecord}}
Notes:
-
{{:data.activeMindRecord.mooc}}
+
{{:data.activeMindRecord.mooc}}
{{else}}
ERROR: Record not found.
{{/if}} diff --git a/vorestation.dme b/vorestation.dme index 136359e664..6a5b9bf851 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -2157,6 +2157,7 @@ #include "code\modules\resleeving\_defines.dm" #include "code\modules\resleeving\circuitboards.dm" #include "code\modules\resleeving\computers.dm" +#include "code\modules\resleeving\designer.dm" #include "code\modules\resleeving\documents.dm" #include "code\modules\resleeving\implant.dm" #include "code\modules\resleeving\infocore.dm"