From 835dee4581d4deff4d06fe8ccbad45b04a0a7ffc Mon Sep 17 00:00:00 2001 From: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Date: Mon, 12 Apr 2021 00:12:40 +0200 Subject: [PATCH] [MIRROR] TGUI Outfit Manager and Editor (#4828) * TGUI Outfit Manager and Editor * Update persistence.dm Co-authored-by: Trigg <36010999+TriggeredBoi@users.noreply.github.com> Co-authored-by: Gandalf --- code/__DEFINES/vv.dm | 3 + code/controllers/subsystem/persistence.dm | 34 +++ code/datums/outfit.dm | 75 ++++-- code/modules/admin/outfit_editor.dm | 201 ++++++++++++++++ code/modules/admin/outfit_manager.dm | 73 ++++++ code/modules/admin/outfits.dm | 227 +----------------- code/modules/admin/topic.dm | 22 -- tgstation.dme | 2 + tgui/packages/tgui/interfaces/OutfitEditor.js | 146 +++++++++++ .../packages/tgui/interfaces/OutfitManager.js | 81 +++++++ 10 files changed, 603 insertions(+), 261 deletions(-) create mode 100644 code/modules/admin/outfit_editor.dm create mode 100644 code/modules/admin/outfit_manager.dm create mode 100644 tgui/packages/tgui/interfaces/OutfitEditor.js create mode 100644 tgui/packages/tgui/interfaces/OutfitManager.js diff --git a/code/__DEFINES/vv.dm b/code/__DEFINES/vv.dm index 1f65488110d..eb0c9429e46 100644 --- a/code/__DEFINES/vv.dm +++ b/code/__DEFINES/vv.dm @@ -136,6 +136,9 @@ // paintings #define VV_HK_REMOVE_PAINTING "remove_painting" +//outfits +#define VV_HK_TO_OUTFIT_EDITOR "outfit_editor" + // /obj/effect/proc_holder/spell /// Require casting_clothes to cast spell. #define VV_HK_SPELL_SET_ROBELESS "spell_set_robeless" diff --git a/code/controllers/subsystem/persistence.dm b/code/controllers/subsystem/persistence.dm index 14a212dbf70..8cbf5b7407a 100644 --- a/code/controllers/subsystem/persistence.dm +++ b/code/controllers/subsystem/persistence.dm @@ -34,6 +34,7 @@ SUBSYSTEM_DEF(persistence) LoadRandomizedRecipes() LoadPaintings() LoadPanicBunker() //SKYRAT EDIT ADDITION - PANICBUNKER + load_custom_outfits() GLOB.explorer_drone_adventures = load_adventures() return ..() @@ -185,6 +186,7 @@ SUBSYSTEM_DEF(persistence) SavePaintings() SaveScars() SavePanicBunker()//SKYRAT EDIT ADDITION - PANICBUNKER + save_custom_outfits() /datum/controller/subsystem/persistence/proc/GetPhotoAlbums() var/album_path = file("data/photo_albums.json") @@ -413,3 +415,35 @@ SUBSYSTEM_DEF(persistence) original_human.save_persistent_scars(TRUE) else original_human.save_persistent_scars() + + +/datum/controller/subsystem/persistence/proc/load_custom_outfits() + var/file = file("data/custom_outfits.json") + if(!fexists(file)) + return + var/outfits_json = file2text(file) + var/list/outfits = json_decode(outfits_json) + if(!islist(outfits)) + return + + for(var/outfit_data in outfits) + if(!islist(outfit_data)) + continue + + var/outfittype = text2path(outfit_data["outfit_type"]) + if(!ispath(outfittype, /datum/outfit)) + continue + var/datum/outfit/outfit = new outfittype + if(!outfit.load_from(outfit_data)) + continue + GLOB.custom_outfits += outfit + +/datum/controller/subsystem/persistence/proc/save_custom_outfits() + var/file = file("data/custom_outfits.json") + fdel(file) + + var/list/data = list() + for(var/datum/outfit/outfit in GLOB.custom_outfits) + data += list(outfit.get_json_data()) + + WRITE_FILE(file, json_encode(data)) diff --git a/code/datums/outfit.dm b/code/datums/outfit.dm index eff0e415f2c..d3f8673a5dd 100755 --- a/code/datums/outfit.dm +++ b/code/datums/outfit.dm @@ -273,42 +273,42 @@ if(!istype(H)) return if(H.back) - H.back.add_fingerprint(H,1) //The 1 sets a flag to ignore gloves + H.back.add_fingerprint(H, ignoregloves = TRUE) for(var/obj/item/I in H.back.contents) - I.add_fingerprint(H,1) + I.add_fingerprint(H, ignoregloves = TRUE) if(H.wear_id) - H.wear_id.add_fingerprint(H,1) + H.wear_id.add_fingerprint(H, ignoregloves = TRUE) if(H.w_uniform) - H.w_uniform.add_fingerprint(H,1) + H.w_uniform.add_fingerprint(H, ignoregloves = TRUE) if(H.wear_suit) - H.wear_suit.add_fingerprint(H,1) + H.wear_suit.add_fingerprint(H, ignoregloves = TRUE) if(H.wear_mask) - H.wear_mask.add_fingerprint(H,1) + H.wear_mask.add_fingerprint(H, ignoregloves = TRUE) if(H.wear_neck) - H.wear_neck.add_fingerprint(H,1) + H.wear_neck.add_fingerprint(H, ignoregloves = TRUE) if(H.head) - H.head.add_fingerprint(H,1) + H.head.add_fingerprint(H, ignoregloves = TRUE) if(H.shoes) - H.shoes.add_fingerprint(H,1) + H.shoes.add_fingerprint(H, ignoregloves = TRUE) if(H.gloves) - H.gloves.add_fingerprint(H,1) + H.gloves.add_fingerprint(H, ignoregloves = TRUE) if(H.ears) - H.ears.add_fingerprint(H,1) + H.ears.add_fingerprint(H, ignoregloves = TRUE) if(H.glasses) - H.glasses.add_fingerprint(H,1) + H.glasses.add_fingerprint(H, ignoregloves = TRUE) if(H.belt) - H.belt.add_fingerprint(H,1) + H.belt.add_fingerprint(H, ignoregloves = TRUE) for(var/obj/item/I in H.belt.contents) - I.add_fingerprint(H,1) + I.add_fingerprint(H, ignoregloves = TRUE) if(H.s_store) - H.s_store.add_fingerprint(H,1) + H.s_store.add_fingerprint(H, ignoregloves = TRUE) if(H.l_store) - H.l_store.add_fingerprint(H,1) + H.l_store.add_fingerprint(H, ignoregloves = TRUE) if(H.r_store) - H.r_store.add_fingerprint(H,1) + H.r_store.add_fingerprint(H, ignoregloves = TRUE) for(var/obj/item/I in H.held_items) - I.add_fingerprint(H,1) - return 1 + I.add_fingerprint(H, ignoregloves = TRUE) + return TRUE /// Return a list of all the types that are required to disguise as this outfit type /datum/outfit/proc/get_chameleon_disguise_info() @@ -347,6 +347,33 @@ .["implants"] = implants .["accessory"] = accessory +/// Copy most vars from another outfit to this one +/datum/outfit/proc/copy_from(datum/outfit/target) + name = target.name + uniform = target.uniform + suit = target.suit + toggle_helmet = target.toggle_helmet + back = target.back + belt = target.belt + gloves = target.gloves + shoes = target.shoes + head = target.head + mask = target.mask + neck = target.neck + ears = target.ears + glasses = target.glasses + id = target.id + l_pocket = target.l_pocket + r_pocket = target.r_pocket + suit_store = target.suit_store + r_hand = target.r_hand + l_hand = target.l_hand + internals_slot = target.internals_slot + backpack_contents = target.backpack_contents + box = target.box + implants = target.implants + accessory = target.accessory + /// Prompt the passed in mob client to download this outfit as a json blob /datum/outfit/proc/save_to_file(mob/admin) var/stored_data = get_json_data() @@ -395,3 +422,13 @@ implants += imptype accessory = text2path(outfit_data["accessory"]) return TRUE + +/datum/outfit/vv_get_dropdown() + . = ..() + VV_DROPDOWN_OPTION("", "---") + VV_DROPDOWN_OPTION(VV_HK_TO_OUTFIT_EDITOR, "Outfit Editor") + +/datum/outfit/vv_do_topic(list/href_list) + . = ..() + if(href_list[VV_HK_TO_OUTFIT_EDITOR]) + usr.client.open_outfit_editor(src) diff --git a/code/modules/admin/outfit_editor.dm b/code/modules/admin/outfit_editor.dm new file mode 100644 index 00000000000..68d9ad93ea9 --- /dev/null +++ b/code/modules/admin/outfit_editor.dm @@ -0,0 +1,201 @@ + +/client/proc/open_outfit_editor(datum/outfit/target) + var/datum/outfit_editor/ui = new(usr, target) + ui.ui_interact(usr) + +#define OUTFIT_EDITOR_NAME "Outfit-O-Tron 9000" +/datum/outfit_editor + var/client/owner + + var/dummy_key + + var/datum/outfit/drip + +/datum/outfit_editor/New(user, datum/outfit/target) + owner = CLIENT_FROM_VAR(user) + + if(ispath(target)) + drip = new /datum/outfit + drip.copy_from(new target) + else if(istype(target)) + drip = target + else + drip = new /datum/outfit + drip.name = "New Outfit" + +/datum/outfit_editor/ui_state(mob/user) + return GLOB.admin_state + +/datum/outfit_editor/ui_status(mob/user, datum/ui_state/state) + if(QDELETED(drip)) + return UI_CLOSE + return ..() + +/datum/outfit_editor/ui_close(mob/user) + clear_human_dummy(dummy_key) + qdel(src) + +/datum/outfit_editor/proc/init_dummy() + dummy_key = "outfit_editor_[owner]" + var/mob/living/carbon/human/dummy/dummy = generate_or_wait_for_human_dummy(dummy_key) + var/mob/living/carbon/carbon_target = owner.mob + if(istype(carbon_target)) + carbon_target.dna.transfer_identity(dummy) + dummy.updateappearance() + + unset_busy_human_dummy(dummy_key) + +/datum/outfit_editor/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "OutfitEditor", OUTFIT_EDITOR_NAME) + ui.open() + ui.set_autoupdate(FALSE) + +/datum/outfit_editor/proc/entry(data) + if(ispath(data, /obj/item)) + var/obj/item/item = data + return list( + "path" = item, + "name" = initial(item.name), + "desc" = initial(item.desc), + // at this point initializing the item is probably faster tbh + "sprite" = icon2base64(icon(initial(item.icon), initial(item.icon_state))), + ) + + return data + +/datum/outfit_editor/proc/serialize_outfit() + var/list/outfit_slots = drip.get_json_data() + . = list() + for(var/key in outfit_slots) + var/val = outfit_slots[key] + . += list("[key]" = entry(val)) + +/datum/outfit_editor/ui_data(mob/user) + var/list/data = list() + + data["outfit"] = serialize_outfit() + data["saveable"] = !GLOB.custom_outfits.Find(drip) + + var/datum/preferences/prefs = owner.prefs + var/icon/dummysprite = get_flat_human_icon(null, + prefs = prefs, + dummy_key = dummy_key, + showDirs = list(SOUTH), + outfit_override = drip) + data["dummy64"] = icon2base64(dummysprite) + + return data + + +/datum/outfit_editor/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + if(..()) + return + . = TRUE + + var/slot = params["slot"] + switch(action) + if("click") + choose_item(slot) + if("ctrlClick") + choose_any_item(slot) + if("clear") + if(drip.vars.Find(slot)) + drip.vars[slot] = null + + if("rename") + var/newname = stripped_input(owner, "What do you want to name this outfit?", OUTFIT_EDITOR_NAME) + if(newname) + drip.name = newname + if("save") + GLOB.custom_outfits |= drip + SStgui.update_user_uis(owner.mob) + if("delete") + GLOB.custom_outfits -= drip + SStgui.update_user_uis(owner.mob) + if("vv") + owner.debug_variables(drip) + + +/datum/outfit_editor/proc/set_item(slot, obj/item/choice) + if(!choice) + return + if(!ispath(choice)) + alert(owner, "Invalid item", OUTFIT_EDITOR_NAME, "oh no") + return + if(initial(choice.icon_state) == null) //hacky check copied from experimentor code + var/msg = "Warning: This item's icon_state is null, indicating it is very probably not actually a usable item." + if(alert(owner, msg, OUTFIT_EDITOR_NAME, "Use it anyway", "Cancel") != "Use it anyway") + return + + if(drip.vars.Find(slot)) + drip.vars[slot] = choice + +/datum/outfit_editor/proc/choose_any_item(slot) + var/obj/item/choice = pick_closest_path(FALSE) + + if(!choice) + return + + set_item(slot, choice) + +//this proc will try to give a good selection of items that the user can choose from +//it does *not* give a selection of all items that can fit in a slot because lag; +//most notably the hand and pocket slots because they accept pretty much anything +//also stuff that fits in the belt and back slots are scattered pretty much all over the place +/datum/outfit_editor/proc/choose_item(slot) + var/list/options = list() + + switch(slot) + if("head") + options = typesof(/obj/item/clothing/head) + if("glasses") + options = typesof(/obj/item/clothing/glasses) + if("ears") + options = typesof(/obj/item/radio/headset) + + if("neck") + options = typesof(/obj/item/clothing/neck) + if("mask") + options = typesof(/obj/item/clothing/mask) + + if("uniform") + options = typesof(/obj/item/clothing/under) + if("suit") + options = typesof(/obj/item/clothing/suit) + if("gloves") + options = typesof(/obj/item/clothing/gloves) + + if("suit_store") + var/obj/item/clothing/suit/suit = drip.suit + if(suit) + suit = new suit //initial() doesn't like lists + options = suit.allowed + if(!options.len) //nothing will happen, but don't let the user think it's broken + to_chat(owner, "No options available for the current suit.") + + if("belt") + options = typesof(/obj/item/storage/belt) + if("id") + options = typesof(/obj/item/card/id) + + if("l_hand") + choose_any_item(slot) + if("back") + options = typesof(/obj/item/storage/backpack) + if("r_hand") + choose_any_item(slot) + + if("l_pocket") + choose_any_item(slot) + if("shoes") + options = typesof(/obj/item/clothing/shoes) + if("r_pocket") + choose_any_item(slot) + + if(length(options)) + set_item(slot, tgui_input_list(owner, "Choose an item", OUTFIT_EDITOR_NAME, options)) + + +#undef OUTFIT_EDITOR_NAME diff --git a/code/modules/admin/outfit_manager.dm b/code/modules/admin/outfit_manager.dm new file mode 100644 index 00000000000..9d20b645472 --- /dev/null +++ b/code/modules/admin/outfit_manager.dm @@ -0,0 +1,73 @@ +/client/proc/outfit_manager() + set category = "Debug" + set name = "Outfit Manager" + + if(!check_rights(R_DEBUG)) + return + var/datum/outfit_manager/ui = new(usr) + ui.ui_interact(usr) + + +/datum/outfit_manager + var/client/owner + +/datum/outfit_manager/New(user) + owner = CLIENT_FROM_VAR(user) + +/datum/outfit_manager/ui_state(mob/user) + return GLOB.admin_state + +/datum/outfit_manager/ui_close(mob/user) + qdel(src) + +/datum/outfit_manager/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "OutfitManager") + ui.open() + +/datum/outfit_manager/proc/entry(datum/outfit/outfit) + var/vv = FALSE + var/datum/outfit/varedit/varoutfit = outfit + if(istype(varoutfit)) + vv = length(varoutfit.vv_values) + return list( + "name" = "[outfit.name] [vv ? "(VV)" : ""]", + "ref" = REF(outfit), + ) + +/datum/outfit_manager/ui_data(mob/user) + var/list/data = list() + + var/list/outfits = list() + for(var/datum/outfit/custom_outfit in GLOB.custom_outfits) + outfits += list(entry(custom_outfit)) + data["outfits"] = outfits + + return data + +/datum/outfit_manager/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + if(..()) + return + . = TRUE + + switch(action) + if("new") + owner.open_outfit_editor(new /datum/outfit) + if("load") + owner.holder.load_outfit(owner.mob) + if("copy") + var/datum/outfit/outfit = tgui_input_list(owner, "Pick an outfit to copy from", "Outfit Manager", subtypesof(/datum/outfit)) + if(ispath(outfit)) + owner.open_outfit_editor(new outfit) + + var/datum/outfit/target_outfit = locate(params["outfit"]) + if(!istype(target_outfit)) + return + switch(action) //wow we're switching through action again this is horrible optimization smh + if("edit") + owner.open_outfit_editor(target_outfit) + if("save") + owner.holder.save_outfit(owner.mob, target_outfit) + if("delete") + owner.holder.delete_outfit(owner.mob, target_outfit) diff --git a/code/modules/admin/outfits.dm b/code/modules/admin/outfits.dm index 42790835001..05bb2a47017 100644 --- a/code/modules/admin/outfits.dm +++ b/code/modules/admin/outfits.dm @@ -1,35 +1,14 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits -/client/proc/outfit_manager() - set category = "Debug" - set name = "Outfit Manager" - if(!check_rights(R_DEBUG)) - return - holder.outfit_manager(usr) - -/datum/admins/proc/outfit_manager(mob/admin) - var/list/dat = list("" - dat += "Create
" - dat += "Load from file" - admin << browse(dat.Join(),"window=outfitmanager") - -/datum/admins/proc/save_outfit(mob/admin,datum/outfit/O) +/datum/admins/proc/save_outfit(mob/admin, datum/outfit/O) O.save_to_file(admin) - outfit_manager(admin) + SStgui.update_user_uis(admin) -/datum/admins/proc/delete_outfit(mob/admin,datum/outfit/O) +/datum/admins/proc/delete_outfit(mob/admin, datum/outfit/O) GLOB.custom_outfits -= O qdel(O) - to_chat(admin,"Outfit deleted.", confidential = TRUE) - outfit_manager(admin) + to_chat(admin,"Outfit deleted.") SStgui.update_user_uis(admin) /datum/admins/proc/load_outfit(mob/admin) @@ -39,208 +18,16 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits var/filedata = file2text(outfit_file) var/json = json_decode(filedata) if(!json) - to_chat(admin,"JSON decode error.", confidential = TRUE) + to_chat(admin,"JSON decode error.") return var/otype = text2path(json["outfit_type"]) if(!ispath(otype,/datum/outfit)) - to_chat(admin,"Malformed/Outdated file.", confidential = TRUE) + to_chat(admin,"Malformed/Outdated file.") return var/datum/outfit/O = new otype if(!O.load_from(json)) - to_chat(admin,"Malformed/Outdated file.", confidential = TRUE) + to_chat(admin,"Malformed/Outdated file.") return GLOB.custom_outfits += O - outfit_manager(admin) SStgui.update_user_uis(admin) -/datum/admins/proc/create_outfit(mob/admin) - var/list/uniforms = typesof(/obj/item/clothing/under) - var/list/suits = typesof(/obj/item/clothing/suit) - var/list/gloves = typesof(/obj/item/clothing/gloves) - var/list/shoes = typesof(/obj/item/clothing/shoes) - var/list/headwear = typesof(/obj/item/clothing/head) - var/list/glasses = typesof(/obj/item/clothing/glasses) - var/list/masks = typesof(/obj/item/clothing/mask) - var/list/ids = typesof(/obj/item/card/id) - - var/uniform_select = "" - - var/suit_select = "" - - var/gloves_select = "" - - var/shoes_select = "" - - var/head_select = "" - - var/glasses_select = "" - - var/mask_select = "" - - var/id_select = "" - - var/dat = {" - Create Outfit -
- - [HrefTokenFormField()] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name: - -
Uniform: - [uniform_select] -
Suit: - [suit_select] -
Back: - -
Belt: - -
Gloves: - [gloves_select] -
Shoes: - [shoes_select] -
Head: - [head_select] -
Mask: - [mask_select] -
Ears: - -
Glasses: - [glasses_select] -
ID: - [id_select] -
Left Pocket: - -
Right Pocket: - -
Suit Store: - -
Right Hand: - -
Left Hand: - -
-
- -
- "} - admin << browse(dat, "window=dressup;size=550x600") - - -/datum/admins/proc/create_outfit_finalize(mob/admin, list/href_list) - var/datum/outfit/O = new - - O.name = href_list["outfit_name"] - O.uniform = text2path(href_list["outfit_uniform"]) - O.shoes = text2path(href_list["outfit_shoes"]) - O.gloves = text2path(href_list["outfit_gloves"]) - O.suit = text2path(href_list["outfit_suit"]) - O.head = text2path(href_list["outfit_head"]) - O.back = text2path(href_list["outfit_back"]) - O.mask = text2path(href_list["outfit_mask"]) - O.glasses = text2path(href_list["outfit_glasses"]) - O.r_hand = text2path(href_list["outfit_r_hand"]) - O.l_hand = text2path(href_list["outfit_l_hand"]) - O.suit_store = text2path(href_list["outfit_s_store"]) - O.l_pocket = text2path(href_list["outfit_l_pocket"]) - O.r_pocket = text2path(href_list["outfit_r_pocket"]) - O.id = text2path(href_list["outfit_id"]) - O.belt = text2path(href_list["outfit_belt"]) - O.ears = text2path(href_list["outfit_ears"]) - - GLOB.custom_outfits.Add(O) - message_admins("[key_name(usr)] created \"[O.name]\" outfit!") - SStgui.update_user_uis(admin) diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index e8f719d0049..efe0fc45d8a 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -1868,28 +1868,6 @@ else to_chat(usr, "You may only use this when the game is running.", confidential = TRUE) - else if(href_list["create_outfit_finalize"]) - if(!check_rights(R_ADMIN)) - return - create_outfit_finalize(usr,href_list) - else if(href_list["load_outfit"]) - if(!check_rights(R_ADMIN)) - return - load_outfit(usr) - else if(href_list["create_outfit_menu"]) - if(!check_rights(R_ADMIN)) - return - create_outfit(usr) - else if(href_list["delete_outfit"]) - if(!check_rights(R_ADMIN)) - return - var/datum/outfit/O = locate(href_list["chosen_outfit"]) in GLOB.custom_outfits - delete_outfit(usr,O) - else if(href_list["save_outfit"]) - if(!check_rights(R_ADMIN)) - return - var/datum/outfit/O = locate(href_list["chosen_outfit"]) in GLOB.custom_outfits - save_outfit(usr,O) else if(href_list["set_selfdestruct_code"]) if(!check_rights(R_ADMIN)) return diff --git a/tgstation.dme b/tgstation.dme index fd3dd48de1f..5715764c7b5 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1528,6 +1528,8 @@ #include "code\modules\admin\holder2.dm" #include "code\modules\admin\ipintel.dm" #include "code\modules\admin\IsBanned.dm" +#include "code\modules\admin\outfit_editor.dm" +#include "code\modules\admin\outfit_manager.dm" #include "code\modules\admin\outfits.dm" #include "code\modules\admin\permissionedit.dm" #include "code\modules\admin\player_panel.dm" diff --git a/tgui/packages/tgui/interfaces/OutfitEditor.js b/tgui/packages/tgui/interfaces/OutfitEditor.js new file mode 100644 index 00000000000..5fdedbbcf66 --- /dev/null +++ b/tgui/packages/tgui/interfaces/OutfitEditor.js @@ -0,0 +1,146 @@ +import { useBackend } from '../backend'; +import { Box, Button, Icon, Section, Stack } from '../components'; +import { Window } from '../layouts'; + +export const OutfitEditor = (props, context) => { + const { act, data } = useBackend(context); + const { outfit, saveable, dummy64 } = data; + return ( + + + +
+ +
+
+
+ ); +}; + +const OutfitSlot = (props, context) => { + const { act, data } = useBackend(context); + const { name, icon, iconRot, slot } = props; + const { outfit } = data; + const currItem = outfit[slot]; + return ( + + + + {currItem?.sprite && ( + <> + + act("clear", { slot })} /> + + )} + + + {currItem?.name || "Empty"} + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/OutfitManager.js b/tgui/packages/tgui/interfaces/OutfitManager.js new file mode 100644 index 00000000000..2ffe8cd5205 --- /dev/null +++ b/tgui/packages/tgui/interfaces/OutfitManager.js @@ -0,0 +1,81 @@ +import { useBackend } from '../backend'; +import { Button, Section, Stack } from '../components'; +import { Window } from '../layouts'; + +export const OutfitManager = (props, context) => { + const { act, data } = useBackend(context); + const { outfits } = data; + return ( + + +
+
+
+
+ ); +}; +