diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm index 539019a41c8..c3d461d4c5c 100644 --- a/code/__HELPERS/global_lists.dm +++ b/code/__HELPERS/global_lists.dm @@ -78,35 +78,49 @@ GLOB.pai_software_by_key[P.id] = P // Setup loadout gear - for(var/geartype in subtypesof(/datum/gear)) - var/datum/gear/G = geartype + for(var/gear_type in subtypesof(/datum/gear)) + var/datum/gear/gear = gear_type - var/use_name = initial(G.display_name) - var/use_category = initial(G.sort_category) + if(gear == gear.path) + continue - if(G == initial(G.subtype_path)) + if(gear_type == gear.subtype_path) continue - if(!use_name) - error("Loadout - Missing display name: [G]") + if(!gear.index_name) + stack_trace("Loadout - Missing index name: [gear]") continue - if(!initial(G.cost)) - error("Loadout - Missing cost: [G]") + if(!gear.cost) + stack_trace("Loadout - Missing cost: [gear]") continue - if(!initial(G.path)) - error("Loadout - Missing path definition: [G]") + if(!gear.path) + stack_trace("Loadout - Missing path definition: [gear]") continue - - if(!GLOB.loadout_categories[use_category]) - GLOB.loadout_categories[use_category] = new /datum/loadout_category(use_category) - var/datum/loadout_category/LC = GLOB.loadout_categories[use_category] - GLOB.gear_datums[use_name] = new geartype - LC.gear[use_name] = GLOB.gear_datums[use_name] - - GLOB.loadout_categories = sortAssoc(GLOB.loadout_categories) - for(var/loadout_category in GLOB.loadout_categories) - var/datum/loadout_category/LC = GLOB.loadout_categories[loadout_category] - LC.gear = sortAssoc(LC.gear) + gear = new gear + var/obj/gear_item = gear.path + var/list/tweaks = list() + for(var/datum/gear_tweak/tweak as anything in gear.gear_tweaks) + tweaks[tweak.type] += list(list( + "name" = tweak.display_type, + "icon" = tweak.fa_icon, + "tooltip" = tweak.info, + )) + + GLOB.gear_tgui_info[gear.sort_category] += list( + "[gear_type]" = list( + "name" = ((gear.display_name == /datum/gear::display_name)? gear_item.name : gear.display_name) , + "index_name" = gear.index_name, + "desc" = gear.description, + "icon" = gear_item.icon, + "icon_state" = gear_item.icon_state, + "cost" = gear.cost, + "gear_tier" = gear.donator_tier, + "allowed_roles" = gear.allowed_roles, + "tweaks" = tweaks, + ) + ) + + GLOB.gear_datums[gear.index_name] = gear // Setup a list of robolimbs diff --git a/code/_globalvars/_regexes.dm b/code/_globalvars/_regexes.dm index 7c2a73a4548..a9ad1b03e55 100644 --- a/code/_globalvars/_regexes.dm +++ b/code/_globalvars/_regexes.dm @@ -1,3 +1,4 @@ GLOBAL_DATUM_INIT(is_http_protocol, /regex, regex("^https?://")) GLOBAL_DATUM_INIT(filename_forbidden_chars, /regex, regex(@{""|[\\\n\t/?%*:|<>]|\.\."}, "g")) +GLOBAL_DATUM_INIT(is_color, /regex, regex("^#\[0-9a-fA-F]{6}$")) GLOBAL_PROTECT(filename_forbidden_chars) diff --git a/code/game/jobs/job/job.dm b/code/game/jobs/job/job.dm index ed8fe897cae..4aac6998041 100644 --- a/code/game/jobs/job/job.dm +++ b/code/game/jobs/job/job.dm @@ -220,7 +220,7 @@ continue if(G.slot) - if(H.equip_to_slot_or_del(G.spawn_item(H, H.client.prefs.loadout_gear[G.display_name]), G.slot)) + if(H.equip_to_slot_or_del(G.spawn_item(H, H.client.prefs.get_gear_metadata(G)), G.slot, TRUE)) to_chat(H, "Equipping you with [G.display_name]!") else gear_leftovers += G @@ -239,19 +239,19 @@ if(gear_leftovers.len) for(var/datum/gear/G in gear_leftovers) - var/obj/item/placed_in = G.spawn_item(get_turf(H), H.client.prefs.loadout_gear[G.display_name]) + var/obj/item/placed_in = G.spawn_item(null, H.client.prefs.get_gear_metadata(G)) if(placed_in.equip_to_best_slot(H)) - to_chat(H, "Placing [G.display_name] in your inventory!") + to_chat(H, span_notice("Placing [G.display_name] in your inventory!")) continue if(H.put_in_hands(placed_in)) - to_chat(H, "Placing [G.display_name] in your hands!") + to_chat(H, span_notice("Placing [G.display_name] in your hands!")) continue - to_chat(H, "Failed to locate a storage object on your mob, either you spawned with no hands free and no backpack or this is a bug.") + to_chat(H, span_danger("Failed to locate a storage object on your mob, either you spawned with no hands free and no backpack or this is a bug.")) qdel(placed_in) qdel(gear_leftovers) - return 1 + return TRUE /datum/outfit/job/proc/imprint_idcard(mob/living/carbon/human/H) var/datum/job/J = SSjobs.GetJobType(jobtype) diff --git a/code/game/machinery/dye_generator.dm b/code/game/machinery/dye_generator.dm index 9da27873474..5a7fae2e55e 100644 --- a/code/game/machinery/dye_generator.dm +++ b/code/game/machinery/dye_generator.dm @@ -58,8 +58,8 @@ ..() if(stat & (BROKEN|NOPOWER)) return - var/temp = input(usr, "Choose a dye color", "Dye Color") as color|null - if(!temp) + var/temp = tgui_input_color(usr, "Choose a dye color", "Dye Color") + if(isnull(temp)) return set_light_color(temp) diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm index 1f3b0e03897..7454ac612df 100644 --- a/code/game/objects/items/crayons.dm +++ b/code/game/objects/items/crayons.dm @@ -267,7 +267,9 @@ if(!Adjacent(usr) || usr.incapacitated()) return if(href_list["color"]) - var/temp = input(usr, "Please select colour.", "Crayon colour") as color + var/temp = tgui_input_color(usr, "Please select colour.", "Crayon colour") + if(isnull(temp)) + return colour = temp update_window(usr) else @@ -303,7 +305,10 @@ if("Change Drawing") ..() if("Change Color") - colour = input(user,"Choose Color") as color + var/new_color = tgui_input_color(user,"Choose Color") + if(isnull(new_color)) + return + colour = new_color update_icon() /obj/item/toy/crayon/spraycan/afterattack(atom/target, mob/user, proximity, params) diff --git a/code/game/objects/items/devices/window_painter.dm b/code/game/objects/items/devices/window_painter.dm index 6dae12b8c5a..66f26594d7b 100644 --- a/code/game/objects/items/devices/window_painter.dm +++ b/code/game/objects/items/devices/window_painter.dm @@ -25,7 +25,10 @@ mode = "pipette" if("Choose Color") mode = "paint" - colour = input(user,"Choose Color") as color + var/new_color = tgui_input_color(user,"Choose Color") + if(isnull(new_color)) + return + colour = new_color update_icon(UPDATE_OVERLAYS) if("Color Presets") mode = "paint" diff --git a/code/game/objects/structures/curtains.dm b/code/game/objects/structures/curtains.dm index 136b42d4f9e..a2825fb3edf 100644 --- a/code/game/objects/structures/curtains.dm +++ b/code/game/objects/structures/curtains.dm @@ -48,7 +48,10 @@ if(istype(I, /obj/item/toy/crayon)) add_fingerprint(user) - color = input(user, "Choose Color") as color + var/new_color = tgui_input_color(user, "Choose Color") + if(isnull(new_color)) + return ATTACK_CHAIN_PROCEED + color = new_color return ATTACK_CHAIN_PROCEED_SUCCESS return ..() diff --git a/code/game/objects/structures/dresser.dm b/code/game/objects/structures/dresser.dm index 5653ed75f64..b5dcf580e43 100644 --- a/code/game/objects/structures/dresser.dm +++ b/code/game/objects/structures/dresser.dm @@ -31,8 +31,8 @@ if(new_underwear) var/datum/sprite_accessory/underwear/uwear = GLOB.underwear_list[new_underwear] if(uwear.allow_change_color) - var/new_underwear_color = input(user, "Choose your underwear color, else color will be white:", "Changing", "#ffffff") as color|null - H.color_underwear = new_underwear_color || "#ffffff" + var/new_underwear_color = tgui_input_color(user, "Choose your underwear color, else color will be white:", "Changing", "#ffffff") + H.color_underwear = isnull(new_underwear_color)? "#ffffff" : new_underwear_color H.underwear = new_underwear if("Undershirt") @@ -49,8 +49,8 @@ if(new_undershirt) var/datum/sprite_accessory/undershirt/ushirt = GLOB.undershirt_list[new_undershirt] if(ushirt.allow_change_color) - var/new_undershirt_color = input(user, "Choose your undershirt color, else color will be white:", "Changing", "#ffffff") as color|null - H.color_undershirt = new_undershirt_color || "#ffffff" + var/new_undershirt_color = tgui_input_color(user, "Choose your undershirt color, else color will be white:", "Changing", "#ffffff") + H.color_undershirt = isnull(new_undershirt_color )? "#ffffff" : new_undershirt_color H.undershirt = new_undershirt if("Socks") diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 5d78bba539f..9e7403ec955 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -1264,8 +1264,8 @@ GLOBAL_LIST_INIT(view_runtimes_verbs, list( return message = strip_html(message, 500) - var/message_color = input(src, "Input your message color:", "Color Selector") as color|null - if(!message_color) + var/message_color = tgui_input_color(src, "Input your message color:", "Color Selector") + if(isnull(message_color)) return var/alert_type2 = alert(src, "Do you wish to change speed of an admin alert to? (No - default speed)",,"Yes", "No") diff --git a/code/modules/antagonists/space_dragon/space_dragon.dm b/code/modules/antagonists/space_dragon/space_dragon.dm index 9eebcddb793..71536bfd7b7 100644 --- a/code/modules/antagonists/space_dragon/space_dragon.dm +++ b/code/modules/antagonists/space_dragon/space_dragon.dm @@ -239,8 +239,8 @@ * If an invalid color is given, will re-prompt the dragon until a proper color is chosen. */ /mob/living/simple_animal/hostile/space_dragon/proc/color_selection() - chosen_color = input(src,"Какого цвета вы хотите быть?","Выбор цвета", COLOR_WHITE) as color|null - if(!chosen_color) //redo proc until we get a color + chosen_color = tgui_input_color(src,"Какого цвета вы хотите быть?","Выбор цвета", COLOR_WHITE) + if(isnull(chosen_color)) //redo proc until we get a color to_chat(src, span_warning("Этот цвет некорректен, попробуйте еще раз.")) color_selection() return diff --git a/code/modules/asset_cache/asset_list.dm b/code/modules/asset_cache/asset_list.dm index a508459a9ef..ab556c41eb7 100644 --- a/code/modules/asset_cache/asset_list.dm +++ b/code/modules/asset_cache/asset_list.dm @@ -641,4 +641,27 @@ GLOBAL_LIST_EMPTY(asset_datums) /datum/asset/simple/namespaced/proc/get_htmlloader(filename) return URL2HTMLLOADER(SSassets.transport.get_asset_url(filename, assets[filename])) + +/// A subtype to generate a JSON file from a list +/datum/asset/json + _abstract = /datum/asset/json + /// The filename, will be suffixed with ".json" + var/name +/datum/asset/json/send(client) + return SSassets.transport.send_assets(client, "[name].json") +/datum/asset/json/get_url_mappings() + return list( + "[name].json" = SSassets.transport.get_asset_url("[name].json"), + ) +/datum/asset/json/register() + var/filename = "data/[name].json" + fdel(filename) + text2file(json_encode(generate()), filename) + SSassets.transport.register_asset("[name].json", fcopy_rsc(filename)) + fdel(filename) +/// Returns the data that will be JSON encoded +/datum/asset/json/proc/generate() + SHOULD_CALL_PARENT(FALSE) + CRASH("generate() not implemented for [type]!") + #undef ASSET_CROSS_ROUND_CACHE_DIRECTORY diff --git a/code/modules/asset_cache/assets/asset_icon_ref_map.dm b/code/modules/asset_cache/assets/asset_icon_ref_map.dm new file mode 100644 index 00000000000..4eef3398056 --- /dev/null +++ b/code/modules/asset_cache/assets/asset_icon_ref_map.dm @@ -0,0 +1,24 @@ +/// Maps icon names to ref values +/datum/asset/json/icon_ref_map + name = "icon_ref_map" + early = TRUE + +/datum/asset/json/icon_ref_map/generate() + var/list/data = list() //"icons/obj/drinks.dmi" => "[0xc000020]" + //var/start = "0xc000000" + var/value = 0 + while(TRUE) + value += 1 + var/ref = "\[0xc[num2text(value,6,16)]\]" + var/mystery_meat = locate(ref) + if(isicon(mystery_meat)) + if(!isfile(mystery_meat)) // Ignore the runtime icons for now + continue + var/path = get_icon_dmi_path(mystery_meat) //Try to get the icon path + if(path) + data[path] = ref + else if(mystery_meat) + continue; //Some other non-icon resource, ogg/json/whatever + else //Out of resources end this, could also try to end this earlier as soon as runtime generated icons appear but eh + break; + return data diff --git a/code/modules/client/preference/loadout/gear_tweaks.dm b/code/modules/client/preference/loadout/gear_tweaks.dm index a22307f4730..0b7a89ab01b 100644 --- a/code/modules/client/preference/loadout/gear_tweaks.dm +++ b/code/modules/client/preference/loadout/gear_tweaks.dm @@ -1,3 +1,11 @@ +/datum/gear_tweak + /// Displayed in TGUI name + var/display_type + /// Font Awesome icon + var/fa_icon + /// Explains what is this do in TGUI tooltip + var/info + /datum/gear_tweak/proc/get_contents(var/metadata) return @@ -7,13 +15,16 @@ /datum/gear_tweak/proc/get_default() return +/datum/gear_tweak/proc/get_tgui_data(param) + return + /datum/gear_tweak/proc/update_gear_intro() return /datum/gear_tweak/proc/tweak_gear_data(var/metadata, var/datum/gear_data) return -/datum/gear_tweak/proc/tweak_item(var/obj/item/I, var/metadata) +/datum/gear_tweak/proc/tweak_item(obj/item/gear, metadata) return /* @@ -21,6 +32,9 @@ */ /datum/gear_tweak/color + display_type = "Color" + fa_icon = "palette" + info = "Recolorable" var/list/valid_colors var/datum/gear/parent @@ -37,25 +51,34 @@ /datum/gear_tweak/color/get_metadata(var/user, var/metadata) if(valid_colors) - metadata = input(user, "Choose an item color.", "Character Preference", metadata) as null|anything in valid_colors + metadata = tgui_input_list(user, "Choose an item color.", "Character Preference", valid_colors, metadata) else - metadata = input(user, "Choose an item color.", "Global Preference", metadata) as color|null + metadata = tgui_input_color(user, "Choose an item color.", "Global Preference", metadata) update_gear_intro(metadata) return metadata +/datum/gear_tweak/color/get_tgui_data(param) + var/tgui_data = list() + tgui_data["display_param"] = param + tgui_data["icon"] = parent.get_gear_icon(param) + return tgui_data + /datum/gear_tweak/color/update_gear_intro(var/color) parent.update_gear_icon(color) -/datum/gear_tweak/color/tweak_item(var/obj/item/I, var/metadata) - if(valid_colors && !(metadata in valid_colors)) +/datum/gear_tweak/color/tweak_item(obj/item/gear, metadata) + if((valid_colors && !(metadata in valid_colors)) || !metadata) return - I.color = metadata + gear.color = metadata /* * Path adjustment */ /datum/gear_tweak/path + display_type = "Subtype" + fa_icon = "bars" + info = "Has subtypes" var/list/valid_paths = list() var/datum/gear/parent @@ -83,52 +106,44 @@ parent.path = valid_paths[path] parent.update_gear_icon() +/datum/gear_tweak/path/get_tgui_data(param) + var/tgui_data = list() + tgui_data["display_param"] = param + var/obj/item/path = valid_paths[param] + tgui_data["icon_file"] = path.icon + tgui_data["icon_state"] = path.icon_state + tgui_data["name"] = path.name + return tgui_data + /datum/gear_tweak/path/tweak_gear_data(var/metadata, var/datum/gear_data/gear_data) if(!(metadata in valid_paths)) return gear_data.path = valid_paths[metadata] -/* -* Content adjustment -*/ +// MARK: Rename +/datum/gear_tweak/rename + display_type = "Name" + fa_icon = "edit" + info = "Renameable" -/datum/gear_tweak/contents - var/list/valid_contents +/datum/gear_tweak/rename/get_default() + return "" -/datum/gear_tweak/contents/New() - valid_contents = args.Copy() - ..() -/datum/gear_tweak/contents/get_contents(var/metadata) - return "Contents: [english_list(metadata, and_text = ", ")]" - -/datum/gear_tweak/contents/get_default() - . = list() - for(var/i = 1 to valid_contents.len) - . += "Random" - -/datum/gear_tweak/contents/get_metadata(var/user, var/list/metadata) - . = list() - for(var/i = metadata.len to valid_contents.len) - metadata += "Random" - for(var/i = 1 to valid_contents.len) - var/entry = input(user, "Choose an entry.", "Character Preference", metadata[i]) as null|anything in (valid_contents[i] + list("Random", "None")) - if(entry) - . += entry - else - return metadata - -/datum/gear_tweak/contents/tweak_item(var/obj/item/I, var/list/metadata) - if(metadata.len != valid_contents.len) +/datum/gear_tweak/rename/get_metadata(user, metadata) + var/new_name = tgui_input_text(user, "Rename an object. Enter empty line for stock name", "Rename Gear", metadata, MAX_NAME_LEN) + if(isnull(new_name)) + return metadata + return new_name + +/datum/gear_tweak/rename/get_tgui_data(param) + var/tgui_data = list() + tgui_data["display_param"] = param + tgui_data["name"] = param + return tgui_data + +/datum/gear_tweak/rename/tweak_item(obj/item/gear, metadata) + if(!metadata) return - for(var/i = 1 to valid_contents.len) - var/path - var/list/contents = valid_contents[i] - if(metadata[i] == "Random") - path = pick(contents) - path = contents[path] - else if(metadata[i] == "None") - continue - else - path = contents[metadata[i]] - new path(I) + + gear.name = metadata diff --git a/code/modules/client/preference/loadout/loadout.dm b/code/modules/client/preference/loadout/loadout.dm index 0128066203c..b85a51e95ab 100644 --- a/code/modules/client/preference/loadout/loadout.dm +++ b/code/modules/client/preference/loadout/loadout.dm @@ -1,16 +1,8 @@ -GLOBAL_LIST_EMPTY(loadout_categories) GLOBAL_LIST_EMPTY(gear_datums) -/datum/loadout_category - var/category = "" - var/list/gear = list() - -/datum/loadout_category/New(cat) - category = cat - ..() - /datum/gear - var/display_name //Name/index. Must be unique. + var/index_name //index. Must be unique. + var/display_name = "bug" //Name var/description //Description of this gear. If left blank will default to the description of the pathed item. var/atom/path //Path to item. var/icon_state //Icon state of item @@ -24,6 +16,7 @@ GLOBAL_LIST_EMPTY(gear_datums) var/subtype_path = /datum/gear //for skipping organizational subtypes (optional) var/subtype_cost_overlap = TRUE //if subtypes can take points at the same time var/implantable = FALSE //For organ-like implants (huds, pumps, etc) + var/donator_tier = 0 /datum/gear/New() ..() @@ -33,6 +26,12 @@ GLOBAL_LIST_EMPTY(gear_datums) /datum/gear/proc/update_gear_icon(color) + var/gear_icon = get_gear_icon(color) + if(!gear_icon) + return + base64icon = gear_icon + +/datum/gear/proc/get_gear_icon(color) if(initial(icon) && initial(icon_state)) return icon_state = path::icon_state @@ -44,7 +43,7 @@ GLOBAL_LIST_EMPTY(gear_datums) var/icon/new_icon = icon(icon, icon_state, SOUTH, 1, FALSE) if(color) new_icon.Blend(color, ICON_MULTIPLY) - base64icon = icon2base64(new_icon) + return icon2base64(new_icon) /datum/gear_data var/path @@ -55,12 +54,12 @@ GLOBAL_LIST_EMPTY(gear_datums) location = nlocation /datum/gear/proc/spawn_item(location, metadata) - var/datum/gear_data/gd = new(path, location) - for(var/datum/gear_tweak/gt in gear_tweaks) - gt.tweak_gear_data(metadata["[gt]"], gd) - var/item = new gd.path(gd.location) - for(var/datum/gear_tweak/gt in gear_tweaks) - gt.tweak_item(item, metadata["[gt]"]) + var/datum/gear_data/gear_data = new(path, location) + for(var/datum/gear_tweak/tweak in gear_tweaks) + tweak.tweak_gear_data(metadata["[tweak]"], gear_data) + var/item = new gear_data.path(gear_data.location) + for(var/datum/gear_tweak/tweak in gear_tweaks) + tweak.tweak_item(item, metadata["[tweak]"]) return item /datum/gear/proc/can_select(client/cl, job_name, species_name, silent = FALSE) diff --git a/code/modules/client/preference/loadout/loadout_accessories.dm b/code/modules/client/preference/loadout/loadout_accessories.dm index ed820949674..fa8ee75e686 100644 --- a/code/modules/client/preference/loadout/loadout_accessories.dm +++ b/code/modules/client/preference/loadout/loadout_accessories.dm @@ -4,7 +4,8 @@ sort_category = "Accessories" /datum/gear/accessory/scarf - display_name = "scarf, select" + index_name = "scarf, select" + display_name = "scarf" path = /obj/item/clothing/accessory/scarf/red /datum/gear/accessory/scarf/New() @@ -23,7 +24,8 @@ gear_tweaks += new /datum/gear_tweak/path(scarfs, src, TRUE) /datum/gear/accessory/scarfstriped - display_name = "striped scarf, select" + index_name = "striped scarf, select" + display_name = "striped scarf" path = /obj/item/clothing/accessory/stripedredscarf /datum/gear/accessory/scarfstriped/New() @@ -34,22 +36,23 @@ gear_tweaks += new /datum/gear_tweak/path(scarfs, src, TRUE) /datum/gear/accessory/holobadge - display_name = "holobadge, pin" + index_name = "holobadge, pin" path = /obj/item/clothing/accessory/holobadge allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_DETECTIVE, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) /datum/gear/accessory/holobadge_n - display_name = "holobadge, cord" + index_name = "holobadge, cord" path = /obj/item/clothing/accessory/holobadge/cord allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_DETECTIVE, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) /datum/gear/accessory/holobadge/detective - display_name = "holobadge, detective" + index_name = "holobadge, detective" path = /obj/item/clothing/accessory/holobadge/detective allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_DETECTIVE) /datum/gear/accessory/tie - display_name = "tie, select" + index_name = "tie, select" + display_name = "tie" path = /obj/item/clothing/accessory/blue /datum/gear/accessory/tie/New() @@ -61,21 +64,21 @@ gear_tweaks += new /datum/gear_tweak/path(ties, src, TRUE) /datum/gear/accessory/stethoscope - display_name = "stethoscope" + index_name = "stethoscope" path = /obj/item/clothing/accessory/stethoscope allowed_roles = list(JOB_TITLE_CMO, JOB_TITLE_DOCTOR, JOB_TITLE_INTERN, JOB_TITLE_PARAMEDIC, JOB_TITLE_BRIGDOC) /datum/gear/accessory/ntrjacket - display_name = "jacket, nt rep" + index_name = "jacket, nt rep" path = /obj/item/clothing/accessory/ntrjacket allowed_roles = list(JOB_TITLE_REPRESENTATIVE) /datum/gear/accessory/waistcoat - display_name = "waistcoat" + index_name = "waistcoat" path = /obj/item/clothing/accessory/waistcoat /datum/gear/accessory/cowboyshirt - display_name = "cowboy shirt, select" + index_name = "cowboy shirt, select" path = /obj/item/clothing/accessory/cowboyshirt /datum/gear/accessory/cowboyshirt/New() @@ -93,15 +96,16 @@ gear_tweaks += new /datum/gear_tweak/path(shirts, src, TRUE) /datum/gear/accessory/locket - display_name = "gold locket" + index_name = "gold locket" path = /obj/item/clothing/accessory/necklace/locket /datum/gear/accessory/necklace - display_name = "simple necklace" + index_name = "simple necklace" path = /obj/item/clothing/accessory/necklace /datum/gear/accessory/corset - display_name = "corset, select" + index_name = "corset, select" + display_name = "corset" path = /obj/item/clothing/accessory/corset /datum/gear/accessory/corset/New() @@ -113,11 +117,11 @@ gear_tweaks += new /datum/gear_tweak/path(corsets, src, TRUE) /datum/gear/accessory/armband_red - display_name = "armband" + index_name = "armband" path = /obj/item/clothing/accessory/armband /datum/gear/accessory/armband_civ - display_name = "armband, blue-yellow" + index_name = "armband, blue-yellow" path = /obj/item/clothing/accessory/armband/yb /datum/gear/accessory/armband_job @@ -125,42 +129,43 @@ subtype_cost_overlap = FALSE /datum/gear/accessory/armband_job/sec - display_name = " armband, security" + index_name = " armband, security" path = /obj/item/clothing/accessory/armband/sec allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_DETECTIVE, JOB_TITLE_OFFICER, JOB_TITLE_BRIGDOC, JOB_TITLE_PILOT) /datum/gear/accessory/armband_job/cargo - display_name = "cargo armband" + index_name = "cargo armband" path = /obj/item/clothing/accessory/armband/cargo allowed_roles = list(JOB_TITLE_QUARTERMASTER, JOB_TITLE_CARGOTECH, JOB_TITLE_MINER) /datum/gear/accessory/armband_job/medical - display_name = "armband, medical" + index_name = "armband, medical" path = /obj/item/clothing/accessory/armband/med allowed_roles = list(JOB_TITLE_CMO, JOB_TITLE_DOCTOR, JOB_TITLE_INTERN, JOB_TITLE_CORONER, JOB_TITLE_PARAMEDIC, JOB_TITLE_BRIGDOC) /datum/gear/accessory/armband_job/emt - display_name = "armband, EMT" + index_name = "armband, EMT" path = /obj/item/clothing/accessory/armband/medgreen allowed_roles = list(JOB_TITLE_PARAMEDIC, JOB_TITLE_BRIGDOC) /datum/gear/accessory/armband_job/engineering - display_name = "armband, engineering" + index_name = "armband, engineering" path = /obj/item/clothing/accessory/armband/engine allowed_roles = list(JOB_TITLE_CHIEF, JOB_TITLE_ENGINEER, JOB_TITLE_ATMOSTECH, JOB_TITLE_ENGINEER_TRAINEE) /datum/gear/accessory/armband_job/hydro - display_name = "armband, hydroponics" + index_name = "armband, hydroponics" path = /obj/item/clothing/accessory/armband/hydro allowed_roles = list(JOB_TITLE_BOTANIST) /datum/gear/accessory/armband_job/sci - display_name = "armband, science" + index_name = "armband, science" path = /obj/item/clothing/accessory/armband/science allowed_roles = list(JOB_TITLE_RD, JOB_TITLE_SCIENTIST, JOB_TITLE_SCIENTIST_STUDENT, JOB_TITLE_ROBOTICIST) /datum/gear/accessory/holsters - display_name = "holster, select" + index_name = "holster, select" + display_name = "holster" path = /obj/item/clothing/accessory/holster/ allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_DETECTIVE, JOB_TITLE_OFFICER, JOB_TITLE_BRIGDOC, JOB_TITLE_PILOT) diff --git a/code/modules/client/preference/loadout/loadout_donor.dm b/code/modules/client/preference/loadout/loadout_donor.dm index a5d99260b3a..250d54f86ae 100644 --- a/code/modules/client/preference/loadout/loadout_donor.dm +++ b/code/modules/client/preference/loadout/loadout_donor.dm @@ -1,5 +1,5 @@ /datum/gear/donor - var/donator_tier = 2 + donator_tier = 2 sort_category = "Donor" subtype_path = /datum/gear/donor @@ -8,7 +8,7 @@ return FALSE if(!donator_tier) // why are you here?.. allowed, but - stack_trace("Item with no donator tier in loadout donor items: [display_name].") + stack_trace("Item with no donator tier in loadout donor items: [index_name].") return TRUE if(!cl.prefs) // DB loading, skip this check now @@ -18,7 +18,7 @@ return TRUE if(cl && !silent) - to_chat(cl, span_warning("Для получения \"[display_name]\" необходим [donator_tier] или более высокий уровень пожертвований.")) + to_chat(cl, span_warning("Для получения \"[index_name]\" необходим [donator_tier] или более высокий уровень пожертвований.")) return FALSE @@ -30,127 +30,127 @@ /datum/gear/donor/ussptracksuit_black donator_tier = 1 cost = 1 - display_name = "track suit (black)" + index_name = "track suit (black)" path = /obj/item/clothing/under/ussptracksuit_black /datum/gear/donor/ussptracksuit_white donator_tier = 1 cost = 1 - display_name = "track suit (white)" + index_name = "track suit (white)" path = /obj/item/clothing/under/ussptracksuit_white /datum/gear/donor/kittyears - display_name = "Kitty ears" + index_name = "Kitty ears" path = /obj/item/clothing/head/kitty /datum/gear/donor/leather_trenchcoat - display_name = "Leather Trenchcoat" + index_name = "Leather Trenchcoat" path = /obj/item/clothing/suit/storage/leather_trenchcoat/runner donator_tier = 2 cost = 1 /datum/gear/donor/furgloves - display_name = "Fur Gloves" + index_name = "Fur Gloves" path = /obj/item/clothing/gloves/furgloves /datum/gear/donor/furboots - display_name = "Fur Boots" + index_name = "Fur Boots" path = /obj/item/clothing/shoes/furboots /datum/gear/donor/noble_boot - display_name = "Noble Boots" + index_name = "Noble Boots" path = /obj/item/clothing/shoes/fluff/noble_boot /datum/gear/donor/furcape - display_name = "Fur Cape" + index_name = "Fur Cape" path = /obj/item/clothing/neck/cloak/furcape /datum/gear/donor/furcoat - display_name = "Fur Coat" + index_name = "Fur Coat" path = /obj/item/clothing/suit/furcoat /datum/gear/donor/kamina - display_name = "Spiky Orange-tinted Shades" + index_name = "Spiky Orange-tinted Shades" path = /obj/item/clothing/glasses/fluff/kamina /datum/gear/donor/green - display_name = "Spiky Green-tinted Shades" + index_name = "Spiky Green-tinted Shades" path = /obj/item/clothing/glasses/fluff/kamina/green /datum/gear/donor/threedglasses - display_name = "Threed Glasses" + index_name = "Threed Glasses" path = /obj/item/clothing/glasses/threedglasses /datum/gear/donor/blacksombrero - display_name = "Black Sombrero" + index_name = "Black Sombrero" path = /obj/item/clothing/head/fluff/blacksombrero /datum/gear/donor/guardhelm - display_name = "Plastic Guard helm" + index_name = "Plastic Guard helm" path = /obj/item/clothing/head/fluff/guardhelm /datum/gear/donor/goldtophat - display_name = "Gold-trimmed Top Hat" + index_name = "Gold-trimmed Top Hat" path = /obj/item/clothing/head/fluff/goldtophat /datum/gear/donor/goldtophat/red - display_name = "Red Gold-trimmed Top Hat" + index_name = "Red Gold-trimmed Top Hat" path = /obj/item/clothing/head/fluff/goldtophat/red /datum/gear/donor/goldtophat/blue - display_name = "Blue Gold-trimmed Top Hat" + index_name = "Blue Gold-trimmed Top Hat" path = /obj/item/clothing/head/fluff/goldtophat/blue /datum/gear/donor/mushhat - display_name = "Mushroom Hat" + index_name = "Mushroom Hat" path = /obj/item/clothing/head/fluff/mushhat /datum/gear/donor/furcap - display_name = "Fur Cap" + index_name = "Fur Cap" path = /obj/item/clothing/head/furcap /datum/gear/donor/mouse - display_name = "Mouse Headband" + index_name = "Mouse Headband" path = /obj/item/clothing/head/kitty/mouse /datum/gear/donor/fawkes - display_name = "Guy Fawkes mask" + index_name = "Guy Fawkes mask" path = /obj/item/clothing/mask/face/fawkes /datum/gear/donor/bigbrother - display_name = "Spraycan Big Brother" + index_name = "Spraycan Big Brother" path = /obj/item/toy/crayon/spraycan/paintkit/bigbrother /datum/gear/donor/slavic - display_name = "Spraycan Slavic" + index_name = "Spraycan Slavic" path = /obj/item/toy/crayon/spraycan/paintkit/slavic /datum/gear/donor/id_decal_silver - display_name = "Silver ID Decal" + index_name = "Silver ID Decal" path = /obj/item/id_decal/silver donator_tier = 3 cost = 1 /datum/gear/donor/id_decal_prisoner - display_name = "Prisoner ID Decal" + index_name = "Prisoner ID Decal" path = /obj/item/id_decal/prisoner donator_tier = 3 cost = 1 /datum/gear/donor/id_decal_emag - display_name = "Emag ID Decal" + index_name = "Emag ID Decal" path = /obj/item/id_decal/emag donator_tier = 3 cost = 1 /datum/gear/donor/id_decal_gold - display_name = "Gold ID Decal" + index_name = "Gold ID Decal" path = /obj/item/id_decal/gold donator_tier = 4 cost = 1 /datum/gear/donor/zippolghtr - display_name = "Zippo lighter" + index_name = "Zippo lighter" path = /obj/item/lighter/zippo donator_tier = 1 cost = 1 @@ -160,102 +160,102 @@ subtype_cost_overlap = FALSE /datum/gear/donor/strip/cap - display_name = "strip, Captain" + index_name = "strip, Captain" path = /obj/item/clothing/accessory/head_strip donator_tier = 2 cost = 1 allowed_roles = list(JOB_TITLE_CAPTAIN) /datum/gear/donor/strip/rd - display_name = "strip, Research Director" + index_name = "strip, Research Director" path = /obj/item/clothing/accessory/head_strip/rd donator_tier = 2 cost = 1 allowed_roles = list(JOB_TITLE_RD) /datum/gear/donor/strip/ce - display_name = "strip, Chief Engineer" + index_name = "strip, Chief Engineer" path = /obj/item/clothing/accessory/head_strip/ce donator_tier = 2 cost = 1 allowed_roles = list(JOB_TITLE_CHIEF) /datum/gear/donor/strip/t4ce - display_name = "strip, Grand Chief Engineer" + index_name = "strip, Grand Chief Engineer" path = /obj/item/clothing/accessory/head_strip/t4ce donator_tier = 4 cost = 1 allowed_roles = list(JOB_TITLE_CHIEF) /datum/gear/donor/strip/cmo - display_name = "strip, Chief Medical Officer" + index_name = "strip, Chief Medical Officer" path = /obj/item/clothing/accessory/head_strip/cmo donator_tier = 2 cost = 1 allowed_roles = list(JOB_TITLE_CMO) /datum/gear/donor/strip/hop - display_name = "strip, Head of Personnel" + index_name = "strip, Head of Personnel" path = /obj/item/clothing/accessory/head_strip/hop donator_tier = 2 cost = 1 allowed_roles = list(JOB_TITLE_HOP) /datum/gear/donor/strip/hos - display_name = "strip, Head of Security" + index_name = "strip, Head of Security" path = /obj/item/clothing/accessory/head_strip/hos donator_tier = 2 cost = 1 allowed_roles = list(JOB_TITLE_HOS) /datum/gear/donor/strip/qm - display_name = "strip, Quartermaster" + index_name = "strip, Quartermaster" path = /obj/item/clothing/accessory/head_strip/qm donator_tier = 2 cost = 1 allowed_roles = list(JOB_TITLE_QUARTERMASTER) /datum/gear/donor/strip/clown - display_name = "strip, Clown" + index_name = "strip, Clown" path = /obj/item/clothing/accessory/head_strip/clown donator_tier = 2 cost = 1 allowed_roles = list(JOB_TITLE_CLOWN) /datum/gear/donor/strip/bs - display_name = "strip, Blueshield" + index_name = "strip, Blueshield" path = /obj/item/clothing/accessory/head_strip/bs donator_tier = 3 cost = 1 allowed_roles = list(JOB_TITLE_BLUESHIELD) /datum/gear/donor/strip/ntr - display_name = "strip, NanoTrasen Representative" + index_name = "strip, NanoTrasen Representative" path = /obj/item/clothing/accessory/head_strip/ntr donator_tier = 3 cost = 1 allowed_roles = list(JOB_TITLE_REPRESENTATIVE) /datum/gear/donor/strip/syndi - display_name = "strip, Syndicate" + index_name = "strip, Syndicate" path = /obj/item/clothing/accessory/head_strip/syndicate donator_tier = 3 cost = 1 /datum/gear/donor/strip/comrad - display_name = "strip, SSSP" + index_name = "strip, SSSP" path = /obj/item/clothing/accessory/head_strip/comrad donator_tier = 3 cost = 1 /datum/gear/donor/strip/federal - display_name = "strip, TSF" + index_name = "strip, TSF" path = /obj/item/clothing/accessory/head_strip/federal donator_tier = 3 cost = 1 /datum/gear/donor/heartglasses - display_name = "heart-shaped glasses, color" + index_name = "heart-shaped glasses, color" path = /obj/item/clothing/glasses/heart donator_tier = 3 cost = 1 @@ -266,7 +266,7 @@ gear_tweaks += new /datum/gear_tweak/color(parent = src) /datum/gear/donor/heart_meson - display_name = "Heart Meson Glasses" + index_name = "Heart Meson Glasses" path = /obj/item/clothing/glasses/meson/heart donator_tier = 4 cost = 2 @@ -274,7 +274,7 @@ allowed_roles = list(JOB_TITLE_CHIEF, JOB_TITLE_ENGINEER, JOB_TITLE_ATMOSTECH, JOB_TITLE_MECHANIC, JOB_TITLE_QUARTERMASTER, JOB_TITLE_MINER, JOB_TITLE_CAPTAIN, JOB_TITLE_ENGINEER_TRAINEE) /datum/gear/donor/heart_science - display_name = "Heart Science Glasses" + index_name = "Heart Science Glasses" path = /obj/item/clothing/glasses/science/heart donator_tier = 4 cost = 2 @@ -282,7 +282,7 @@ allowed_roles = list(JOB_TITLE_CAPTAIN, JOB_TITLE_SCIENTIST, JOB_TITLE_ROBOTICIST, JOB_TITLE_RD, JOB_TITLE_GENETICIST, JOB_TITLE_CHEMIST, JOB_TITLE_SCIENTIST_STUDENT) /datum/gear/donor/heart_health - display_name = "Heart Medical Glasses" + index_name = "Heart Medical Glasses" path = /obj/item/clothing/glasses/hud/health/heart donator_tier = 4 cost = 2 @@ -290,7 +290,7 @@ allowed_roles = list(JOB_TITLE_CAPTAIN, JOB_TITLE_CMO, JOB_TITLE_INTERN, JOB_TITLE_PARAMEDIC, JOB_TITLE_VIROLOGIST, JOB_TITLE_BLUESHIELD, JOB_TITLE_PSYCHIATRIST, JOB_TITLE_DOCTOR, JOB_TITLE_CORONER) /datum/gear/donor/heart_diagnostic - display_name = "Heart Diagnostic Glasses" + index_name = "Heart Diagnostic Glasses" path = /obj/item/clothing/glasses/hud/diagnostic/heart donator_tier = 4 cost = 2 @@ -298,7 +298,7 @@ allowed_roles = list(JOB_TITLE_CAPTAIN, JOB_TITLE_RD, JOB_TITLE_ROBOTICIST) /datum/gear/donor/heart_security - display_name = "Heart Security Glasses" + index_name = "Heart Security Glasses" path = /obj/item/clothing/glasses/hud/security/sunglasses/heart donator_tier = 4 cost = 2 @@ -306,7 +306,7 @@ allowed_roles = list(JOB_TITLE_CAPTAIN, JOB_TITLE_DETECTIVE, JOB_TITLE_PILOT, JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_BLUESHIELD, JOB_TITLE_JUDGE, JOB_TITLE_OFFICER) /datum/gear/donor/heartsec_read - display_name = "Heart Security Glasses" + index_name = "Heart Security Glasses" path = /obj/item/clothing/glasses/hud/security/sunglasses/heart/read_only donator_tier = 4 cost = 2 @@ -314,7 +314,7 @@ allowed_roles = list(JOB_TITLE_LAWYER) /datum/gear/donor/heart_hydroponic - display_name = "Heart Hydroponic Glasses" + index_name = "Heart Hydroponic Glasses" path = /obj/item/clothing/glasses/hud/heart donator_tier = 4 cost = 2 @@ -322,7 +322,7 @@ allowed_roles = list(JOB_TITLE_CAPTAIN, JOB_TITLE_BOTANIST) /datum/gear/donor/heart_skills - display_name = "Heart Skills Glasses" + index_name = "Heart Skills Glasses" path = /obj/item/clothing/glasses/hud/skills/heart donator_tier = 4 cost = 2 @@ -330,7 +330,8 @@ allowed_roles = list(JOB_TITLE_CAPTAIN, JOB_TITLE_REPRESENTATIVE, JOB_TITLE_BLUESHIELD, JOB_TITLE_HOP) /datum/gear/donor/night_dress - display_name = "night dress, select" + index_name = "night dress, select" + display_name = "night dress" description = "A classic night dress." cost = 1 donator_tier = 3 @@ -346,14 +347,14 @@ gear_tweaks += new /datum/gear_tweak/path(skirts, src) /datum/gear/donor/strip/cheese_badge - display_name = "strip, Great fellow" + index_name = "strip, Great fellow" path = /obj/item/clothing/accessory/head_strip/cheese_badge donator_tier = 4 cost = 1 allowed_roles = list(JOB_TITLE_CAPTAIN, JOB_TITLE_QUARTERMASTER, JOB_TITLE_RD, JOB_TITLE_HOS, JOB_TITLE_HOP, JOB_TITLE_CMO, JOB_TITLE_CHIEF, JOB_TITLE_REPRESENTATIVE, JOB_TITLE_JUDGE) /datum/gear/donor/smile_pin - display_name = "smiling pin" + index_name = "smiling pin" path = /obj/item/clothing/accessory/medal/smile donator_tier = 4 cost = 1 @@ -361,67 +362,67 @@ /datum/gear/donor/backpack_hiking donator_tier = 3 cost = 1 - display_name = "backpack, Fancy Hiking Pack" + index_name = "backpack, Fancy Hiking Pack" path = /obj/item/storage/backpack/fluff/hiking /datum/gear/donor/backpack_brew donator_tier = 3 cost = 1 - display_name = "backpack, The brew" + index_name = "backpack, The brew" path = /obj/item/storage/backpack/fluff/thebrew /datum/gear/donor/backpack_cat donator_tier = 3 cost = 1 - display_name = "backpack, CatPack" + index_name = "backpack, CatPack" path = /obj/item/storage/backpack/fluff/ssscratches_back /datum/gear/donor/backpack_voxcaster donator_tier = 3 cost = 1 - display_name = "backpack, Voxcaster" + index_name = "backpack, Voxcaster" path = /obj/item/storage/backpack/fluff/krich_back /datum/gear/donor/backpack_syndi donator_tier = 3 cost = 1 - display_name = "backpack, Military Satchel" + index_name = "backpack, Military Satchel" path = /obj/item/storage/backpack/fluff/syndiesatchel /datum/gear/donor/spacecloak donator_tier = 3 cost = 1 - display_name = "Space cloak" + index_name = "Space cloak" path = /obj/item/clothing/neck/cloak/spacecloak /datum/gear/donor/golden_wheelchair donator_tier = 4 cost = 1 - display_name = "Golden wheelchair paintkit" + index_name = "Golden wheelchair paintkit" path = /obj/item/fluff/rapid_wheelchair_kit /datum/gear/donor/hazardbelt - display_name = "hazard vest alt" + index_name = "hazard vest alt" path = /obj/item/clothing/suit/storage/hazardvest/beltdonor donator_tier = 3 cost = 1 allowed_roles = list(JOB_TITLE_CHIEF, JOB_TITLE_ENGINEER) /datum/gear/donor/atmosbelt - display_name = "hazard vest alt (atmos)" + index_name = "hazard vest alt (atmos)" path = /obj/item/clothing/suit/storage/hazardvest/beltdonor/atmos donator_tier = 3 cost = 1 allowed_roles = list(JOB_TITLE_CHIEF, JOB_TITLE_ATMOSTECH) /datum/gear/donor/beaver - display_name = "Beaver Plushie" + index_name = "Beaver Plushie" path = /obj/item/toy/plushie/beaver donator_tier = 3 cost = 1 /datum/gear/donor/earring_NT - display_name = "Earrings NT" + index_name = "Earrings NT" path = /obj/item/clothing/ears/earrings/Nt donator_tier = 3 cost = 1 @@ -429,47 +430,47 @@ /datum/gear/donor/hijab donator_tier = 1 cost = 1 - display_name = "hijab" + index_name = "hijab" path = /obj/item/clothing/suit/hooded/hijab /datum/gear/donor/steampunkdress donator_tier = 1 cost = 1 - display_name = "victorian blue-white dress" + index_name = "victorian blue-white dress" path = /obj/item/clothing/under/steampunkdress /datum/gear/donor/plaidhoodie_green donator_tier = 1 cost = 1 - display_name = "Plaid hoodie, green" + index_name = "Plaid hoodie, green" path = /obj/item/clothing/suit/hoodie/plaidhoodie_green /datum/gear/donor/plaidhoodie_white donator_tier = 1 cost = 1 - display_name = "Plaid hoodie, white" + index_name = "Plaid hoodie, white" path = /obj/item/clothing/suit/hoodie/plaidhoodie_white /datum/gear/donor/plaidhoodie_red donator_tier = 1 cost = 1 - display_name = "Plaid hoodie, red" + index_name = "Plaid hoodie, red" path = /obj/item/clothing/suit/hoodie/plaidhoodie_red /datum/gear/donor/plaidhoodie_yellow donator_tier = 1 cost = 1 - display_name = "Plaid hoodie, yellow" + index_name = "Plaid hoodie, yellow" path = /obj/item/clothing/suit/hoodie/plaidhoodie_yellow /datum/gear/donor/blackcoat donator_tier = 2 cost = 2 - display_name = "Black Coat" + index_name = "Black Coat" path = /obj/item/clothing/suit/blackcoat /datum/gear/donor/pda_beer - display_name = "PDA case \"BEER\"" + index_name = "PDA case \"BEER\"" path = /obj/item/pda_case/beer donator_tier = 1 cost = 1 @@ -477,24 +478,24 @@ /datum/gear/donor/maid donator_tier = 2 cost = 1 - display_name = "Short maid costume" + index_name = "Short maid costume" path = /obj/item/clothing/under/maid/short /datum/gear/donor/rdplushie donator_tier = 3 cost = 1 - display_name = "RD doll" + index_name = "RD doll" path = /obj/item/toy/plushie/rdplushie /datum/gear/donor/gsbplushie donator_tier = 3 cost = 1 - display_name = "GSBussy doll" + index_name = "GSBussy doll" path = /obj/item/toy/plushie/gsbplushie /datum/gear/donor/backpack_shitsec donator_tier = 3 cost = 1 - display_name = "backpack of justice" + index_name = "backpack of justice" path = /obj/item/storage/backpack/justice allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) diff --git a/code/modules/client/preference/loadout/loadout_general.dm b/code/modules/client/preference/loadout/loadout_general.dm index 774e92169fe..2143c72aeff 100644 --- a/code/modules/client/preference/loadout/loadout_general.dm +++ b/code/modules/client/preference/loadout/loadout_general.dm @@ -1,39 +1,40 @@ /datum/gear/dice - display_name = "a d20" + index_name = "a d20" path = /obj/item/dice/d20 /datum/gear/uplift - display_name = "a pack of Uplifts" + index_name = "a pack of Uplifts" path = /obj/item/storage/fancy/cigarettes/cigpack_uplift /datum/gear/robust - display_name = "a pack of Robusts" + index_name = "a pack of Robusts" path = /obj/item/storage/fancy/cigarettes/cigpack_robust /datum/gear/carp - display_name = "a pack of Carps" + index_name = "a pack of Carps" path = /obj/item/storage/fancy/cigarettes/cigpack_carp /datum/gear/midori - display_name = "a pack of Midoris" + index_name = "a pack of Midoris" path = /obj/item/storage/fancy/cigarettes/cigpack_midori /datum/gear/smokingpipe - display_name = "smoking pipe" + index_name = "smoking pipe" path = /obj/item/clothing/mask/cigarette/pipe cost = 2 /datum/gear/robustpipe - display_name = "robust smoking pipe" + index_name = "robust smoking pipe" path = /obj/item/clothing/mask/cigarette/pipe/oldpipe cost = 2 /datum/gear/lighter - display_name = "a cheap lighter" + index_name = "a cheap lighter" path = /obj/item/lighter /datum/gear/earrings - display_name = "earrings, select" + index_name = "earrings, select" + display_name = "earrings" path = /obj/item/clothing/ears/earrings /datum/gear/earrings/New() @@ -44,62 +45,63 @@ gear_tweaks += new /datum/gear_tweak/path(earrings, src) /datum/gear/matches - display_name = "a box of matches" + index_name = "a box of matches" path = /obj/item/storage/box/matches /datum/gear/candlebox - display_name = "a box candles" + index_name = "a box candles" description = "For setting the mood or for occult rituals." path = /obj/item/storage/fancy/candle_box/full /datum/gear/camera - display_name = "a camera" + index_name = "a camera" path = /obj/item/camera /datum/gear/sechud - display_name = "a classic security HUD" + index_name = "a classic security HUD" path = /obj/item/clothing/glasses/hud/security allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_OFFICER, JOB_TITLE_PILOT, JOB_TITLE_JUDGE) /datum/gear/read_only_sechud - display_name = "a classic security HUD (read-only)" + index_name = "a classic security HUD (read-only)" path = /obj/item/clothing/glasses/hud/security/read_only allowed_roles = list(JOB_TITLE_LAWYER) /datum/gear/cryaonbox - display_name = "a box of crayons" + index_name = "a box of crayons" path = /obj/item/storage/fancy/crayons /datum/gear/cane - display_name = "a walking cane" + index_name = "a walking cane" path = /obj/item/cane /datum/gear/cards - display_name = "a deck of standard cards" + index_name = "a deck of standard cards" path = /obj/item/deck/cards /datum/gear/doublecards - display_name = "a double deck of standard cards" + index_name = "a double deck of standard cards" path = /obj/item/deck/cards/doublecards /datum/gear/tarot - display_name = "a deck of tarot cards" + index_name = "a deck of tarot cards" path = /obj/item/deck/tarot /datum/gear/headphones - display_name = "a pair of headphones" + index_name = "a pair of headphones" path = /obj/item/clothing/ears/headphones /datum/gear/fannypack - display_name = "a fannypack" + index_name = "a fannypack" path = /obj/item/storage/belt/fannypack /datum/gear/wallet - display_name = "a wallet(leather)" + index_name = "a wallet(leather)" path = /obj/item/storage/wallet /datum/gear/wallet/color - display_name = "a wallet, select" + index_name = "a wallet, select" + display_name = "a wallet" path = /obj/item/storage/wallet/color/blue /datum/gear/wallet/color/New() @@ -114,7 +116,8 @@ gear_tweaks += new /datum/gear_tweak/path(wallets, src) /datum/gear/bandana - display_name = "bandana, select" + index_name = "bandana, select" + display_name = "bandana" path = /obj/item/clothing/mask/bandana/black /datum/gear/bandana/New() @@ -131,16 +134,17 @@ gear_tweaks += new /datum/gear_tweak/path(bands, src) /datum/gear/piano_synth - display_name ="synthesizer" + index_name ="synthesizer" path = /obj/item/instrument/piano_synth cost = 2 /datum/gear/tts - display_name ="TTS device" + index_name ="TTS device" path = /obj/item/ttsdevice /datum/gear/lipstick - display_name = "lipstick, select" + index_name = "lipstick, select" + display_name = "lipstick" path = /obj/item/lipstick /datum/gear/lipstick/New() @@ -159,18 +163,18 @@ ////////////////////// /datum/gear/mug - display_name = "random coffee mug" + index_name = "random coffee mug" description = "A randomly colored coffee mug. You'll need to supply your own beverage though." path = /obj/item/reagent_containers/food/drinks/mug /datum/gear/novelty_mug - display_name = "novelty coffee mug" + index_name = "novelty coffee mug" description = "A random novelty coffee mug. You'll need to supply your own beverage though." path = /obj/item/reagent_containers/food/drinks/mug/novelty cost = 2 /datum/gear/mug/flask - display_name = "flask" + index_name = "flask" description = "A flask for drink transportation. You'll need to supply your own beverage though." path = /obj/item/reagent_containers/food/drinks/flask/barflask @@ -179,30 +183,30 @@ subtype_cost_overlap = FALSE /datum/gear/mug/department/eng - display_name = "engineer coffee mug" + index_name = "engineer coffee mug" description = "An engineer's coffee mug, emblazoned in the colors of the Engineering department." allowed_roles = list(JOB_TITLE_CHIEF, JOB_TITLE_ENGINEER, JOB_TITLE_ENGINEER_TRAINEE, JOB_TITLE_MECHANIC, JOB_TITLE_ATMOSTECH) path = /obj/item/reagent_containers/food/drinks/mug/eng /datum/gear/mug/department/med - display_name = "doctor coffee mug" + index_name = "doctor coffee mug" description = "A doctor's coffee mug, emblazoned in the colors of the Medical department." allowed_roles = list(JOB_TITLE_CMO, JOB_TITLE_DOCTOR, JOB_TITLE_INTERN, JOB_TITLE_CHEMIST, JOB_TITLE_PSYCHIATRIST, JOB_TITLE_PARAMEDIC, JOB_TITLE_VIROLOGIST, JOB_TITLE_CORONER) path = /obj/item/reagent_containers/food/drinks/mug/med /datum/gear/mug/department/sci - display_name = "scientist coffee mug" + index_name = "scientist coffee mug" description = "A scientist's coffee mug, emblazoned in the colors of the Science department." allowed_roles = list(JOB_TITLE_RD, JOB_TITLE_SCIENTIST, JOB_TITLE_SCIENTIST_STUDENT, JOB_TITLE_ROBOTICIST) path = /obj/item/reagent_containers/food/drinks/mug/sci /datum/gear/mug/department/sec - display_name = "officer coffee mug" + index_name = "officer coffee mug" description = "An officer's coffee mug, emblazoned in the colors of the Security department." allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_DETECTIVE, JOB_TITLE_OFFICER, JOB_TITLE_BRIGDOC, JOB_TITLE_PILOT, JOB_TITLE_LAWYER) path = /obj/item/reagent_containers/food/drinks/mug/sec /datum/gear/mug/department/serv - display_name = "crewmember coffee mug" + index_name = "crewmember coffee mug" description = "A crewmember's coffee mug, emblazoned in the colors of the Service department." path = /obj/item/reagent_containers/food/drinks/mug/serv diff --git a/code/modules/client/preference/loadout/loadout_glasses.dm b/code/modules/client/preference/loadout/loadout_glasses.dm index 7cb1fd8037b..a11f2d9918d 100644 --- a/code/modules/client/preference/loadout/loadout_glasses.dm +++ b/code/modules/client/preference/loadout/loadout_glasses.dm @@ -4,15 +4,15 @@ sort_category = "Glasses" /datum/gear/glasses/sunglasses - display_name = "cheap sunglasses" + index_name = "cheap sunglasses" path = /obj/item/clothing/glasses/sunglasses_fake /datum/gear/glasses/eyepatch - display_name = "Eyepatch" + index_name = "Eyepatch" path = /obj/item/clothing/glasses/eyepatch /datum/gear/glasses/blindfold - display_name = "Blindfold" + index_name = "Blindfold" path = /obj/item/clothing/glasses/sunglasses/blindfold /datum/gear/glasses/blindfold/New() @@ -20,7 +20,7 @@ gear_tweaks += new /datum/gear_tweak/color(parent = src) /datum/gear/glasses/blindfold_fake - display_name = "Fake blindfold" + index_name = "Fake blindfold" path = /obj/item/clothing/glasses/sunglasses/blindfold_fake /datum/gear/glasses/blindfold_fake/New() @@ -28,49 +28,49 @@ gear_tweaks += new /datum/gear_tweak/color(parent = src) /datum/gear/glasses/hipster - display_name = "Hipster glasses" + index_name = "Hipster glasses" path = /obj/item/clothing/glasses/regular/hipster /datum/gear/glasses/monocle - display_name = "Monocle" + index_name = "Monocle" path = /obj/item/clothing/glasses/monocle /datum/gear/glasses/prescription - display_name = "Prescription glasses" + index_name = "Prescription glasses" path = /obj/item/clothing/glasses/regular /datum/gear/glasses/sectacticool - display_name = "Security tactical glasses" + index_name = "Security tactical glasses" path = /obj/item/clothing/glasses/hud/security/sunglasses/tacticool allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) /datum/gear/glasses/medhudpatch - display_name = "Medical HUD eyepatch" + index_name = "Medical HUD eyepatch" path = /obj/item/clothing/glasses/hud/health/patch allowed_roles = list(JOB_TITLE_CMO, JOB_TITLE_DOCTOR, JOB_TITLE_INTERN, JOB_TITLE_CHEMIST, JOB_TITLE_PSYCHIATRIST, JOB_TITLE_PARAMEDIC, JOB_TITLE_VIROLOGIST, JOB_TITLE_BRIGDOC, JOB_TITLE_CORONER) /datum/gear/glasses/sechudpatch - display_name = "Security HUD eyepatch" + index_name = "Security HUD eyepatch" path = /obj/item/clothing/glasses/hud/security/patch allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_OFFICER, JOB_TITLE_PILOT, JOB_TITLE_JUDGE, JOB_TITLE_DETECTIVE) /datum/gear/glasses/sechudpatch/read_only - display_name = "Security HUD eyepatch (read only)" + index_name = "Security HUD eyepatch (read only)" path = /obj/item/clothing/glasses/hud/security/patch/read_only allowed_roles = list(JOB_TITLE_LAWYER) /datum/gear/glasses/hydrohudpatch - display_name = "Hydroponic HUD eyepatch" + index_name = "Hydroponic HUD eyepatch" path = /obj/item/clothing/glasses/hud/hydroponic/patch allowed_roles = list(JOB_TITLE_BOTANIST) /datum/gear/glasses/diaghudpatch - display_name = "Diagnostic HUD eyepatch" + index_name = "Diagnostic HUD eyepatch" path = /obj/item/clothing/glasses/hud/diagnostic/patch allowed_roles = list(JOB_TITLE_ROBOTICIST, JOB_TITLE_RD) /datum/gear/glasses/skillhudpatch - display_name = "Skills HUD eyepatch" + index_name = "Skills HUD eyepatch" path = /obj/item/clothing/glasses/hud/skills/patch allowed_roles = list(JOB_TITLE_HOP, JOB_TITLE_CAPTAIN) diff --git a/code/modules/client/preference/loadout/loadout_gloves.dm b/code/modules/client/preference/loadout/loadout_gloves.dm index 8b08281e1a8..8d676a0f992 100644 --- a/code/modules/client/preference/loadout/loadout_gloves.dm +++ b/code/modules/client/preference/loadout/loadout_gloves.dm @@ -4,17 +4,17 @@ sort_category = "Gloves" /datum/gear/gloves/fingerless - display_name = "Fingerless Gloves" + index_name = "Fingerless Gloves" path = /obj/item/clothing/gloves/fingerless /datum/gear/gloves/silverring - display_name = "Silver ring" + index_name = "Silver ring" path = /obj/item/clothing/gloves/ring/silver /datum/gear/gloves/goldring - display_name = "Gold ring" + index_name = "Gold ring" path = /obj/item/clothing/gloves/ring/gold /datum/gear/gloves/brown_short_gloves - display_name = "short leather gloves" + index_name = "short leather gloves" path = /obj/item/clothing/gloves/brown_short_gloves diff --git a/code/modules/client/preference/loadout/loadout_hat.dm b/code/modules/client/preference/loadout/loadout_hat.dm index 56318992417..06b5bfc8e62 100644 --- a/code/modules/client/preference/loadout/loadout_hat.dm +++ b/code/modules/client/preference/loadout/loadout_hat.dm @@ -4,7 +4,8 @@ sort_category = "Headwear" /datum/gear/hat/hhat - display_name = "hardhat, select" + index_name = "hardhat, select" + display_name = "hardhat" path = /obj/item/clothing/head/hardhat allowed_roles = list(JOB_TITLE_CHIEF, JOB_TITLE_ENGINEER, JOB_TITLE_ENGINEER_TRAINEE, JOB_TITLE_MECHANIC, JOB_TITLE_ATMOSTECH) @@ -16,35 +17,36 @@ gear_tweaks += new /datum/gear_tweak/path(hats, src) /datum/gear/hat/that - display_name = "top hat" + index_name = "top hat" path = /obj/item/clothing/head/that /datum/gear/hat/flatcap - display_name = "flat cap" + index_name = "flat cap" path = /obj/item/clothing/head/flatcap /datum/gear/hat/ushanka - display_name = "ushanka" + index_name = "ushanka" path = /obj/item/clothing/head/ushanka /datum/gear/hat/witch - display_name = "witch hat" + index_name = "witch hat" path = /obj/item/clothing/head/wizard/marisa/fake /datum/gear/hat/piratecaphat - display_name = "pirate captian hat" + index_name = "pirate captian hat" path = /obj/item/clothing/head/pirate /datum/gear/hat/fez - display_name = "fez" + index_name = "fez" path = /obj/item/clothing/head/fez /datum/gear/hat/rasta - display_name = "rasta hat" + index_name = "rasta hat" path = /obj/item/clothing/head/beanie/rasta /datum/gear/hat/fedora - display_name = "fedora, select" + index_name = "fedora, select" + display_name = "fedora" path = /obj/item/clothing/head/fedora /datum/gear/hat/fedora/New() @@ -55,17 +57,18 @@ gear_tweaks += new /datum/gear_tweak/path(hats, src, TRUE) /datum/gear/hat/capcsec - display_name = "security corporate cap" + index_name = "security corporate cap" path = /obj/item/clothing/head/soft/sec/corp allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) /datum/gear/hat/capsec - display_name = "security cap" + index_name = "security cap" path = /obj/item/clothing/head/soft/sec allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) /datum/gear/hat/capred - display_name = "cap, select" + index_name = "cap, select" + display_name = "cap" path = /obj/item/clothing/head/soft/red /datum/gear/hat/capred/New() @@ -82,7 +85,8 @@ /obj/item/clothing/head/soft/solgov,) gear_tweaks += new /datum/gear_tweak/path(hats, src, TRUE) /datum/gear/hat/cowboyhat - display_name = "cowboy hat, select" + index_name = "cowboy hat, select" + display_name = "cowboy hat" path = /obj/item/clothing/head/cowboyhat /datum/gear/hat/cowboyhat/New() @@ -95,7 +99,8 @@ gear_tweaks += new /datum/gear_tweak/path(hats, src, TRUE) /datum/gear/hat/beret - display_name = "beret, select" + index_name = "beret, select" + display_name = "beret" path = /obj/item/clothing/head/beret /datum/gear/hat/beret/New() @@ -111,47 +116,48 @@ subtype_cost_overlap = FALSE /datum/gear/hat/beret_job/sec - display_name = "security beret" + index_name = "security beret" path = /obj/item/clothing/head/beret/sec allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) /datum/gear/hat/beret_job/sec_black - display_name = "black security beret" + index_name = "black security beret" path = /obj/item/clothing/head/beret/sec/black allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) /datum/gear/hat/beret_job/marine - display_name = "royal marines commando beret" + index_name = "royal marines commando beret" path = /obj/item/clothing/head/beret/centcom/officer/sparkyninja_beret allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_BLUESHIELD) /datum/gear/hat/beret_job/marine_old - display_name = "marine lieutenant beret" + index_name = "marine lieutenant beret" path = /obj/item/clothing/head/beret/centcom/officer/sigholt allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_BLUESHIELD) /datum/gear/hat/beret_job/sci - display_name = "science beret" + index_name = "science beret" path = /obj/item/clothing/head/beret/sci allowed_roles = list(JOB_TITLE_RD, JOB_TITLE_SCIENTIST, JOB_TITLE_SCIENTIST_STUDENT, JOB_TITLE_ROBOTICIST, JOB_TITLE_GENETICIST) /datum/gear/hat/beret_job/med - display_name = "medical beret" + index_name = "medical beret" path = /obj/item/clothing/head/beret/med allowed_roles = list(JOB_TITLE_CMO, JOB_TITLE_DOCTOR, JOB_TITLE_INTERN, JOB_TITLE_VIROLOGIST, JOB_TITLE_BRIGDOC, JOB_TITLE_CORONER, JOB_TITLE_PARAMEDIC, JOB_TITLE_CHEMIST, JOB_TITLE_GENETICIST, JOB_TITLE_PSYCHIATRIST) /datum/gear/hat/beret_job/eng - display_name = "engineering beret" + index_name = "engineering beret" path = /obj/item/clothing/head/beret/eng allowed_roles = list(JOB_TITLE_CHIEF, JOB_TITLE_ENGINEER, JOB_TITLE_ENGINEER_TRAINEE) /datum/gear/hat/beret_job/atmos - display_name = "atmospherics beret" + index_name = "atmospherics beret" path = /obj/item/clothing/head/beret/atmos allowed_roles = list(JOB_TITLE_CHIEF, JOB_TITLE_ATMOSTECH) /datum/gear/hat/surgicalcap - display_name = "surgical cap, select" + index_name = "surgical cap, select" + display_name = "surgical cap" path = /obj/item/clothing/head/surgery/purple allowed_roles = list(JOB_TITLE_CMO, JOB_TITLE_DOCTOR, JOB_TITLE_INTERN) @@ -163,16 +169,17 @@ gear_tweaks += new /datum/gear_tweak/path(caps, src) /datum/gear/hat/flowerpin - display_name = "hair flower" + index_name = "hair flower" path = /obj/item/clothing/head/hairflower /datum/gear/hat/lwhelmet - display_name = "security lightweight helmet" + index_name = "security lightweight helmet" path = /obj/item/clothing/head/helmet/lightweighthelmet allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) /datum/gear/hat/beanie - display_name = "beanie, select" + index_name = "beanie, select" + display_name = "beanie" path = /obj/item/clothing/head/beanie /datum/gear/hat/beanie/New() diff --git a/code/modules/client/preference/loadout/loadout_implants.dm b/code/modules/client/preference/loadout/loadout_implants.dm index ddbdd98a788..a78a4ec0a20 100644 --- a/code/modules/client/preference/loadout/loadout_implants.dm +++ b/code/modules/client/preference/loadout/loadout_implants.dm @@ -9,27 +9,27 @@ //Eye implants /datum/gear/implant/meson - display_name = "Meson Scanner Implant" + index_name = "Meson Scanner Implant" path = /obj/item/organ/internal/cyberimp/eyes/meson allowed_roles = list(JOB_TITLE_CHIEF, JOB_TITLE_ATMOSTECH, JOB_TITLE_ENGINEER, JOB_TITLE_QUARTERMASTER, JOB_TITLE_MINER) /datum/gear/implant/security - display_name = "Security Hud Implant" + index_name = "Security Hud Implant" cost = 3 path = /obj/item/organ/internal/cyberimp/eyes/hud/security allowed_roles = list(JOB_TITLE_OFFICER, JOB_TITLE_PILOT, JOB_TITLE_DETECTIVE, JOB_TITLE_WARDEN, JOB_TITLE_HOS, JOB_TITLE_JUDGE) /datum/gear/implant/medical - display_name = "Medical Hud Implant" + index_name = "Medical Hud Implant" path = /obj/item/organ/internal/cyberimp/eyes/hud/medical allowed_roles = list(JOB_TITLE_CMO, JOB_TITLE_CHEMIST, JOB_TITLE_DOCTOR, JOB_TITLE_PARAMEDIC, JOB_TITLE_BRIGDOC, JOB_TITLE_VIROLOGIST) /datum/gear/implant/diagnostic - display_name = "Diagnostical Hud Implant" + index_name = "Diagnostical Hud Implant" path = /obj/item/organ/internal/cyberimp/eyes/hud/diagnostic allowed_roles = list(JOB_TITLE_RD, JOB_TITLE_ROBOTICIST) /datum/gear/implant/science - display_name = "Science Hud Implant" + index_name = "Science Hud Implant" path = /obj/item/organ/internal/cyberimp/eyes/hud/science allowed_roles = list(JOB_TITLE_CHEMIST, JOB_TITLE_SCIENTIST, JOB_TITLE_RD, JOB_TITLE_GENETICIST) diff --git a/code/modules/client/preference/loadout/loadout_neck.dm b/code/modules/client/preference/loadout/loadout_neck.dm index e43ba657a5d..cd45dc31907 100644 --- a/code/modules/client/preference/loadout/loadout_neck.dm +++ b/code/modules/client/preference/loadout/loadout_neck.dm @@ -5,7 +5,7 @@ //Mantles /datum/gear/neck/mantle - display_name = "mantle, color" + index_name = "mantle, color" path = /obj/item/clothing/neck/mantle /datum/gear/neck/mantle/New() @@ -13,15 +13,15 @@ gear_tweaks += new /datum/gear_tweak/color(parent = src) /datum/gear/neck/mantle/old_scarf - display_name = "old scarf" + index_name = "old scarf" path = /obj/item/clothing/neck/mantle/old /datum/gear/neck/mantle/regal_shawl - display_name = "regal shawl" + index_name = "regal shawl" path = /obj/item/clothing/neck/mantle/regal /datum/gear/neck/mantle/cowboy_mantle - display_name = "old wrappings" + index_name = "old wrappings" path = /obj/item/clothing/neck/mantle/cowboy /datum/gear/neck/mantle/job @@ -29,38 +29,38 @@ subtype_cost_overlap = FALSE /datum/gear/neck/mantle/job/captain - display_name = "mantle, captain" + index_name = "mantle, captain" path = /obj/item/clothing/neck/mantle/captain allowed_roles = list(JOB_TITLE_CAPTAIN) /datum/gear/neck/mantle/job/chief_engineer - display_name = "mantle, chief engineer" + index_name = "mantle, chief engineer" path = /obj/item/clothing/neck/mantle/chief_engineer allowed_roles = list(JOB_TITLE_CHIEF) /datum/gear/neck/mantle/job/chief_medical_officer - display_name = "mantle, chief medical officer" + index_name = "mantle, chief medical officer" path = /obj/item/clothing/neck/mantle/chief_medical_officer allowed_roles = list(JOB_TITLE_CMO) /datum/gear/neck/mantle/job/head_of_security - display_name = "mantle, head of security" + index_name = "mantle, head of security" path = /obj/item/clothing/neck/mantle/head_of_security allowed_roles = list(JOB_TITLE_HOS) /datum/gear/neck/mantle/job/head_of_personnel - display_name = "mantle, head of personnel" + index_name = "mantle, head of personnel" path = /obj/item/clothing/neck/mantle/head_of_personnel allowed_roles = list(JOB_TITLE_HOP) /datum/gear/neck/mantle/job/research_director - display_name = "mantle, research director" + index_name = "mantle, research director" path = /obj/item/clothing/neck/mantle/research_director allowed_roles = list(JOB_TITLE_RD) //Cloaks /datum/gear/neck/cloak - display_name = "cloak, color" + index_name = "cloak, color" path = /obj/item/clothing/neck/cloak/grey /datum/gear/neck/cloak/New() @@ -72,68 +72,69 @@ subtype_cost_overlap = FALSE /datum/gear/neck/cloak/job/healer - display_name = "cloak, healer" + index_name = "cloak, healer" path = /obj/item/clothing/neck/cloak/healer allowed_roles = list(JOB_TITLE_CMO, JOB_TITLE_DOCTOR, JOB_TITLE_INTERN, JOB_TITLE_PARAMEDIC, JOB_TITLE_BRIGDOC) /datum/gear/neck/cloak/job/captain - display_name = "cloak, captain" + index_name = "cloak, captain" path = /obj/item/clothing/neck/cloak/captain allowed_roles = list(JOB_TITLE_CAPTAIN) /datum/gear/neck/cloak/job/nanotrasen_representative - display_name = "cloak, nanotrasen representative" + index_name = "cloak, nanotrasen representative" path = /obj/item/clothing/neck/cloak/nanotrasen_representative allowed_roles = list(JOB_TITLE_REPRESENTATIVE) /datum/gear/neck/cloak/job/blueshield - display_name = "cloak, blueshield" + index_name = "cloak, blueshield" path = /obj/item/clothing/neck/cloak/blueshield allowed_roles = list(JOB_TITLE_BLUESHIELD) /datum/gear/neck/cloak/job/chief_engineer - display_name = "cloak, chief engineer" + index_name = "cloak, chief engineer" path = /obj/item/clothing/neck/cloak/chief_engineer allowed_roles = list(JOB_TITLE_CHIEF) /datum/gear/neck/cloak/job/chief_engineer/white - display_name = "cloak, chief engineer, white" + index_name = "cloak, chief engineer, white" path = /obj/item/clothing/neck/cloak/chief_engineer/white allowed_roles = list(JOB_TITLE_CHIEF) /datum/gear/neck/cloak/job/chief_medical_officer - display_name = "cloak, chief medical officer" + index_name = "cloak, chief medical officer" path = /obj/item/clothing/neck/cloak/chief_medical_officer allowed_roles = list(JOB_TITLE_CMO) /datum/gear/neck/cloak/job/head_of_security - display_name = "cloak, head of security" + index_name = "cloak, head of security" path = /obj/item/clothing/neck/cloak/head_of_security allowed_roles = list(JOB_TITLE_HOS) /datum/gear/neck/cloaksecurity - display_name = "cloak, security officer" + index_name = "cloak, security officer" path = /obj/item/clothing/neck/cloak/security allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_OFFICER, JOB_TITLE_WARDEN, JOB_TITLE_PILOT) /datum/gear/neck/cloak/job/head_of_personnel - display_name = "cloak, head of personnel" + index_name = "cloak, head of personnel" path = /obj/item/clothing/neck/cloak/head_of_personnel allowed_roles = list(JOB_TITLE_HOP) /datum/gear/neck/cloak/job/research_director - display_name = "cloak, research director" + index_name = "cloak, research director" path = /obj/item/clothing/neck/cloak/research_director allowed_roles = list(JOB_TITLE_RD) /datum/gear/neck/cloak/job/quartermaster - display_name = "cloak, quartermaster" + index_name = "cloak, quartermaster" path = /obj/item/clothing/neck/cloak/quartermaster allowed_roles = list(JOB_TITLE_QUARTERMASTER) //Ponchos /datum/gear/neck/poncho - display_name = "poncho, select" + index_name = "poncho, select" + display_name = "poncho" path = /obj/item/clothing/neck/poncho /datum/gear/neck/poncho/New() @@ -152,7 +153,7 @@ gear_tweaks += new /datum/gear_tweak/path(ponchos, src, TRUE) /datum/gear/neck/poncho/security - display_name = "poncho, corporate" + index_name = "poncho, corporate" path = /obj/item/clothing/neck/poncho/security allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_OFFICER, JOB_TITLE_WARDEN, JOB_TITLE_PILOT) diff --git a/code/modules/client/preference/loadout/loadout_plushie.dm b/code/modules/client/preference/loadout/loadout_plushie.dm index e90ab2146e2..e1ede04e043 100644 --- a/code/modules/client/preference/loadout/loadout_plushie.dm +++ b/code/modules/client/preference/loadout/loadout_plushie.dm @@ -4,47 +4,48 @@ cost = 1 /datum/gear/plushie/rock - display_name = "a pet rock" + index_name = "a pet rock" path = /obj/item/toy/pet_rock /datum/gear/plushie/redfoxplushie - display_name = "a red fox plushie" + index_name = "a red fox plushie" path = /obj/item/toy/plushie/red_fox /datum/gear/plushie/blackcatplushie - display_name = "a black cat plushie" + index_name = "a black cat plushie" path = /obj/item/toy/plushie/black_cat /datum/gear/plushie/voxplushie - display_name = "a vox plushie" + index_name = "a vox plushie" path = /obj/item/toy/plushie/voxplushie /datum/gear/plushie/lizardplushie - display_name = "a lizard plushie" + index_name = "a lizard plushie" path = /obj/item/toy/plushie/lizardplushie /datum/gear/plushie/deerplushie - display_name = "a deer plushie" + index_name = "a deer plushie" path = /obj/item/toy/plushie/deer /datum/gear/plushie/carpplushie - display_name = "a carp plushie" + index_name = "a carp plushie" path = /obj/item/toy/carpplushie /datum/gear/plushie/nianplushie - display_name = "Nian plushie" + index_name = "Nian plushie" path = /obj/item/toy/plushie/nianplushie /datum/gear/plushie/bubblegumplushie - display_name = "Bubblegum plushie" + index_name = "Bubblegum plushie" path = /obj/item/toy/plushie/bubblegumplushie /datum/gear/plushie/greyplushie - display_name = "Grey Plushie" + index_name = "Grey Plushie" path = /obj/item/toy/plushie/greyplushie /datum/gear/plushie/plasmamanplushie - display_name = "Plasmaman Plushie, select" + index_name = "Plasmaman Plushie, select" + display_name = "Plasmaman Plushie" path = /obj/item/toy/plushie/plasmamanplushie /datum/gear/plushie/plasmamanplushie/New() @@ -65,61 +66,61 @@ gear_tweaks += new /datum/gear_tweak/path(plasmamans, src, TRUE) /datum/gear/plushie/shardplushie - display_name = "Shard Plushie" + index_name = "Shard Plushie" path = /obj/item/toy/plushie/shardplushie /datum/gear/plushie/akulaplushie - display_name = "Akula Plushie" + index_name = "Akula Plushie" path = /obj/item/toy/plushie/blahaj/twohanded cost = 2 /datum/gear/plushie/hampter - display_name = "Hampter" + index_name = "Hampter" path = /obj/item/toy/plushie/hampter cost = 1 /datum/gear/plushie/hampter_assistant - display_name = "Hampter, Assitant" + index_name = "Hampter, Assitant" path = /obj/item/toy/plushie/hampter/asisstant cost = 1 /datum/gear/plushie/hampter_security - display_name = "Hampter, Security" + index_name = "Hampter, Security" path = /obj/item/toy/plushie/hampter/security cost = 1 /datum/gear/plushie/hampter_medic - display_name = "Hampter, Doctor" + index_name = "Hampter, Doctor" path = /obj/item/toy/plushie/hampter/medic cost = 1 /datum/gear/plushie/hampter_janitor - display_name = "Hampter, Janitor" + index_name = "Hampter, Janitor" path = /obj/item/toy/plushie/hampter/janitor cost = 1 /datum/gear/plushie/hampter_captain - display_name = "Hampter, Captain" + index_name = "Hampter, Captain" path = /obj/item/toy/plushie/hampter/captain cost = 1 /datum/gear/plushie/hampter_old_captain - display_name = "Hampter, Old Captain" + index_name = "Hampter, Old Captain" path = /obj/item/toy/plushie/hampter/captain/old cost = 1 /datum/gear/plushie/hampter_syndi - display_name = "Hampter, Syndi" + index_name = "Hampter, Syndi" path = /obj/item/toy/plushie/hampter/syndi cost = 1 /datum/gear/plushie/hampter_death_squad - display_name = "Hampter, Grandpa" + index_name = "Hampter, Grandpa" path = /obj/item/toy/plushie/hampter/death_squad cost = 1 /datum/gear/plushie/hampter_ert_squad - display_name = "Hampter, ERT" + index_name = "Hampter, ERT" path = /obj/item/toy/plushie/hampter/ert_squad cost = 1 diff --git a/code/modules/client/preference/loadout/loadout_racial.dm b/code/modules/client/preference/loadout/loadout_racial.dm index 9bfa31cf463..ce97ab995c0 100644 --- a/code/modules/client/preference/loadout/loadout_racial.dm +++ b/code/modules/client/preference/loadout/loadout_racial.dm @@ -9,7 +9,7 @@ return FALSE if(!LAZYLEN(whitelisted_species)) // why are we here? allowed, but - stack_trace("Item with no racial list in loadout racial items: [display_name].") + stack_trace("Item with no racial list in loadout racial items: [index_name].") return TRUE if(!species_name) // skip @@ -19,7 +19,7 @@ return TRUE if(cl && !silent) - to_chat(cl, span_warning("Ваш вид не подходит для того, чтобы использовать \"[display_name]\"!")) + to_chat(cl, span_warning("Ваш вид не подходит для того, чтобы использовать \"[index_name]\"!")) return FALSE @@ -29,7 +29,7 @@ /datum/gear/racial/taj - display_name = "embroidered veil" + index_name = "embroidered veil" description = "A common traditional nano-fiber veil worn by many Tajaran, It is rare and offensive to see it on other races." path = /obj/item/clothing/glasses/tajblind slot = ITEM_SLOT_EYES @@ -41,55 +41,55 @@ cost = 2 /datum/gear/racial/taj/job/bot - display_name = "veil, blooming" + index_name = "veil, blooming" description = "A common traditional nano-fiber veil worn by many Tajaran, It is rare and offensive to see it on other races. This one has an in-built botanical HUD." path = /obj/item/clothing/glasses/hud/hydroponic/tajblind allowed_roles = list(JOB_TITLE_BOTANIST) /datum/gear/racial/taj/job/sec - display_name = "veil, sleek" + index_name = "veil, sleek" description = "A common traditional nano-fiber veil worn by many Tajaran, It is rare and offensive to see it on other races. This one has an in-built security HUD." path = /obj/item/clothing/glasses/hud/security/sunglasses/tajblind allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_OFFICER, JOB_TITLE_PILOT, JOB_TITLE_JUDGE) /datum/gear/racial/taj/job/iaa - display_name = "veil, sleek(read-only)" + index_name = "veil, sleek(read-only)" description = "A common traditional nano-fiber veil worn by many Tajaran, It is rare and offensive to see it on other races. This one has an in-built security HUD." path = /obj/item/clothing/glasses/hud/security/sunglasses/tajblind/read_only allowed_roles = list(JOB_TITLE_LAWYER) /datum/gear/racial/taj/job/med - display_name = "veil, lightweight" + index_name = "veil, lightweight" description = "A common traditional nano-fiber veil worn by many Tajaran, It is rare and offensive to see it on other races. This one has an in-built medical HUD." path = /obj/item/clothing/glasses/hud/health/tajblind allowed_roles = list(JOB_TITLE_CMO, JOB_TITLE_DOCTOR, JOB_TITLE_INTERN, JOB_TITLE_CHEMIST, JOB_TITLE_PSYCHIATRIST, JOB_TITLE_PARAMEDIC, JOB_TITLE_VIROLOGIST, JOB_TITLE_BRIGDOC, JOB_TITLE_CORONER) /datum/gear/racial/taj/job/sci - display_name = "veil, hi-tech" + index_name = "veil, hi-tech" description = "A common traditional nano-fiber veil worn by many Tajaran, It is rare and offensive to see it on other races. This one has an in-built science goggles" path = /obj/item/clothing/glasses/tajblind/sci allowed_roles = list(JOB_TITLE_RD, JOB_TITLE_SCIENTIST, JOB_TITLE_SCIENTIST_STUDENT, JOB_TITLE_ROBOTICIST, JOB_TITLE_GENETICIST, JOB_TITLE_CHEMIST) /datum/gear/racial/taj/job/eng - display_name = "veil, industrial" + index_name = "veil, industrial" description = "A common traditional nano-fiber veil worn by many Tajaran, It is rare and offensive to see it on other races. This one has an in-built optical meson scanners and welding shields." path = /obj/item/clothing/glasses/tajblind/eng allowed_roles = list(JOB_TITLE_CHIEF, JOB_TITLE_ENGINEER, JOB_TITLE_ENGINEER_TRAINEE, JOB_TITLE_MECHANIC, JOB_TITLE_ATMOSTECH) /datum/gear/racial/taj/job/cargo - display_name = "veil, khaki" + index_name = "veil, khaki" description = "A common traditional nano-fiber veil worn by many Tajaran, It is rare and offensive to see it on other races. This one has an in-built optical meson scanners." path = /obj/item/clothing/glasses/tajblind/cargo allowed_roles = list(JOB_TITLE_QUARTERMASTER, JOB_TITLE_CARGOTECH) /datum/gear/racial/taj/job/diag - display_name = "veil, diagnostic" + index_name = "veil, diagnostic" description = "A common traditional nano-fiber veil worn by many Tajaran, It is rare and offensive to see it on other races. This one has an in-built diagnostic HUD." path = /obj/item/clothing/glasses/hud/diagnostic/tajblind allowed_roles = list(JOB_TITLE_ROBOTICIST, JOB_TITLE_RD) /datum/gear/racial/taj/job/skills - display_name = "veil, skills" + index_name = "veil, skills" description = "A common traditional nano-fiber veil worn by many Tajaran, It is rare and offensive to see it on other races. This one has an in-built skills HUD." path = /obj/item/clothing/glasses/hud/skills/tajblind allowed_roles = list(JOB_TITLE_HOP, JOB_TITLE_CAPTAIN) diff --git a/code/modules/client/preference/loadout/loadout_shoes.dm b/code/modules/client/preference/loadout/loadout_shoes.dm index 8245e9371b7..9bec05ed938 100644 --- a/code/modules/client/preference/loadout/loadout_shoes.dm +++ b/code/modules/client/preference/loadout/loadout_shoes.dm @@ -4,31 +4,32 @@ sort_category = "Shoes" /datum/gear/shoes/sandals - display_name = "sandals, wooden" + index_name = "sandals, wooden" path = /obj/item/clothing/shoes/sandal /datum/gear/shoes/winterboots - display_name = "winter boots" + index_name = "winter boots" path = /obj/item/clothing/shoes/winterboots /datum/gear/shoes/workboots - display_name = "work boots" + index_name = "work boots" path = /obj/item/clothing/shoes/workboots /datum/gear/shoes/leather - display_name = "leather shoes" + index_name = "leather shoes" path = /obj/item/clothing/shoes/leather /datum/gear/shoes/fancysandals - display_name = "sandals, fancy" + index_name = "sandals, fancy" path = /obj/item/clothing/shoes/sandal/fancy /datum/gear/shoes/dressshoes - display_name = "dress shoes" + index_name = "dress shoes" path = /obj/item/clothing/shoes/centcom /datum/gear/shoes/cowboyboots - display_name = "cowboy boots, select" + index_name = "cowboy boots, select" + display_name = "cowboy boots" path = /obj/item/clothing/shoes/cowboy /datum/gear/shoes/cowboyboots/New() @@ -40,19 +41,20 @@ gear_tweaks += new /datum/gear_tweak/path(boots, src) /datum/gear/shoes/jackboots - display_name = "jackboots" + index_name = "jackboots" path = /obj/item/clothing/shoes/jackboots /datum/gear/shoes/jacksandals - display_name = "jacksandals" + index_name = "jacksandals" path = /obj/item/clothing/shoes/jackboots/jacksandals /datum/gear/shoes/laceup - display_name = "laceup shoes" + index_name = "laceup shoes" path = /obj/item/clothing/shoes/laceup /datum/gear/shoes/shoes - display_name = "shoes, select" + index_name = "shoes, select" + display_name = "shoes" path = /obj/item/clothing/shoes/black /datum/gear/shoes/shoes/New() @@ -63,15 +65,15 @@ gear_tweaks += new /datum/gear_tweak/path(boots, src, TRUE) /datum/gear/shoes/jackcross - display_name = "jackcross" + index_name = "jackcross" path = /obj/item/clothing/shoes/jackboots/cross /datum/gear/shoes/leather_boots - display_name = "high leather boots" + index_name = "high leather boots" path = /obj/item/clothing/shoes/leather_boots /datum/gear/shoes/footwraps - display_name = "cloth footwraps, color" + index_name = "cloth footwraps, color" path = /obj/item/clothing/shoes/footwraps /datum/gear/shoes/footwraps/New() diff --git a/code/modules/client/preference/loadout/loadout_suit.dm b/code/modules/client/preference/loadout/loadout_suit.dm index 32f9586498f..bae2d9940c7 100644 --- a/code/modules/client/preference/loadout/loadout_suit.dm +++ b/code/modules/client/preference/loadout/loadout_suit.dm @@ -8,7 +8,7 @@ subtype_path = /datum/gear/suit/coat /datum/gear/suit/coat/grey - display_name = "winter coat" + index_name = "winter coat" path = /obj/item/clothing/suit/hooded/wintercoat /datum/gear/suit/coat/job @@ -16,109 +16,110 @@ subtype_cost_overlap = FALSE /datum/gear/suit/coat/job/sec - display_name = "winter coat, security" + index_name = "winter coat, security" path = /obj/item/clothing/suit/hooded/wintercoat/security allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_DETECTIVE, JOB_TITLE_OFFICER, JOB_TITLE_PILOT, JOB_TITLE_BRIGDOC) /datum/gear/suit/coat/job/hos - display_name = "winter coat, head of security" + index_name = "winter coat, head of security" path = /obj/item/clothing/suit/hooded/wintercoat/security/hos allowed_roles = list(JOB_TITLE_HOS) /datum/gear/suit/coat/job/captain - display_name = "winter coat, captain" + index_name = "winter coat, captain" path = /obj/item/clothing/suit/hooded/wintercoat/captain allowed_roles = list(JOB_TITLE_CAPTAIN) /datum/gear/suit/coat/job/med - display_name = "winter coat, medical" + index_name = "winter coat, medical" path = /obj/item/clothing/suit/hooded/wintercoat/medical allowed_roles = list(JOB_TITLE_CMO, JOB_TITLE_DOCTOR, JOB_TITLE_INTERN, JOB_TITLE_CHEMIST, JOB_TITLE_PSYCHIATRIST, JOB_TITLE_PARAMEDIC, JOB_TITLE_VIROLOGIST, JOB_TITLE_BRIGDOC , JOB_TITLE_CORONER) /datum/gear/suit/coat/job/cmo - display_name = "winter coat, chief medical officer" + index_name = "winter coat, chief medical officer" path = /obj/item/clothing/suit/hooded/wintercoat/medical/cmo allowed_roles = list(JOB_TITLE_CMO) /datum/gear/suit/coat/job/sci - display_name = "winter coat, science" + index_name = "winter coat, science" path = /obj/item/clothing/suit/hooded/wintercoat/medical/science allowed_roles = list(JOB_TITLE_SCIENTIST, JOB_TITLE_RD, JOB_TITLE_SCIENTIST_STUDENT) /datum/gear/suit/coat/job/rd - display_name = "winter coat, research director" + index_name = "winter coat, research director" path = /obj/item/clothing/suit/hooded/wintercoat/medical/science/rd allowed_roles = list(JOB_TITLE_RD) /datum/gear/suit/coat/job/engi - display_name = "winter coat, engineering" + index_name = "winter coat, engineering" path = /obj/item/clothing/suit/hooded/wintercoat/engineering allowed_roles = list(JOB_TITLE_CHIEF, JOB_TITLE_ENGINEER, JOB_TITLE_ENGINEER_TRAINEE, JOB_TITLE_MECHANIC) /datum/gear/suit/coat/job/atmos - display_name = "winter coat, atmospherics" + index_name = "winter coat, atmospherics" path = /obj/item/clothing/suit/hooded/wintercoat/engineering/atmos allowed_roles = list(JOB_TITLE_CHIEF, JOB_TITLE_ATMOSTECH) /datum/gear/suit/coat/job/ce - display_name = "winter coat, chief engineer" + index_name = "winter coat, chief engineer" path = /obj/item/clothing/suit/hooded/wintercoat/engineering/ce allowed_roles = list(JOB_TITLE_CHIEF) /datum/gear/suit/coat/job/hydro - display_name = "winter coat, hydroponics" + index_name = "winter coat, hydroponics" path = /obj/item/clothing/suit/hooded/wintercoat/hydro allowed_roles = list(JOB_TITLE_BOTANIST) /datum/gear/suit/coat/job/cargo - display_name = "winter coat, cargo" + index_name = "winter coat, cargo" path = /obj/item/clothing/suit/hooded/wintercoat/cargo allowed_roles = list(JOB_TITLE_QUARTERMASTER, JOB_TITLE_CARGOTECH) /datum/gear/suit/coat/job/qm - display_name = "winter coat, quartermaster" + index_name = "winter coat, quartermaster" path = /obj/item/clothing/suit/hooded/wintercoat/cargo/qm allowed_roles = list(JOB_TITLE_QUARTERMASTER) /datum/gear/suit/coat/job/miner - display_name = "winter coat, miner" + index_name = "winter coat, miner" path = /obj/item/clothing/suit/hooded/wintercoat/miner allowed_roles = list(JOB_TITLE_MINER) /datum/gear/suit/coat/job/hop - display_name = "winter coat, head of personnel" + index_name = "winter coat, head of personnel" path = /obj/item/clothing/suit/hooded/wintercoat/hop allowed_roles = list(JOB_TITLE_HOP) //LABCOATS /datum/gear/suit/labcoat_emt - display_name = "labcoat, paramedic" + index_name = "labcoat, paramedic" path = /obj/item/clothing/suit/storage/labcoat/emt allowed_roles = list(JOB_TITLE_CMO, JOB_TITLE_PARAMEDIC) //JACKETS /datum/gear/suit/leather_jacket - display_name = "leather jacket" + index_name = "leather jacket" path = /obj/item/clothing/suit/jacket/leather /datum/gear/suit/motojacket - display_name = "leather motorcycle jacket" + index_name = "leather motorcycle jacket" path = /obj/item/clothing/suit/jacket/motojacket /datum/gear/suit/br_tcoat - display_name = "trenchcoat, brown" + index_name = "trenchcoat, brown" path = /obj/item/clothing/suit/storage/browntrenchcoat /datum/gear/suit/bl_tcoat - display_name = "trenchcoat, black" + index_name = "trenchcoat, black" path = /obj/item/clothing/suit/storage/blacktrenchcoat /datum/gear/suit/bomber_jacket - display_name = "bomber jacket" + index_name = "bomber jacket" path = /obj/item/clothing/suit/jacket /datum/gear/suit/miljacket - display_name = "military jacket, select" + index_name = "military jacket, select" + display_name = "military jacket" path = /obj/item/clothing/suit/jacket/miljacket /datum/gear/suit/miljacket/New() @@ -131,21 +132,21 @@ gear_tweaks += new /datum/gear_tweak/path(jackets, src) /datum/gear/suit/secjacket - display_name = "security jacket" + index_name = "security jacket" path = /obj/item/clothing/suit/armor/secjacket allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_DETECTIVE, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) /datum/gear/suit/coat/russian - display_name = "russian coat" + index_name = "russian coat" path = /obj/item/clothing/suit/russiancoat /datum/gear/suit/secbomber - display_name = "security bomber" + index_name = "security bomber" path = /obj/item/clothing/suit/jacket/pilot allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_DETECTIVE, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) /datum/gear/suit/sec_rps - display_name = "security belt-shoulder system" + index_name = "security belt-shoulder system" path = /obj/item/clothing/suit/armor/vest/sec_rps allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_DETECTIVE, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) @@ -154,94 +155,95 @@ subtype_path = /datum/gear/suit/suragi_jacket /datum/gear/suit/suragi_jacket/civ - display_name = "Suragi Jacket" + index_name = "Suragi Jacket" path = /obj/item/clothing/suit/storage/suragi_jacket/civ /datum/gear/suit/suragi_jacket/sec - display_name = "Suragi Jacket - Security" + index_name = "Suragi Jacket - Security" path = /obj/item/clothing/suit/storage/suragi_jacket/sec allowed_roles = list(JOB_TITLE_WARDEN, JOB_TITLE_DETECTIVE, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) /datum/gear/suit/suragi_jacket/cargo - display_name = "Suragi Jacket - Cargo" + index_name = "Suragi Jacket - Cargo" path = /obj/item/clothing/suit/storage/suragi_jacket/cargo allowed_roles = list(JOB_TITLE_CARGOTECH) /datum/gear/suit/suragi_jacket/atmos - display_name = "Suragi Jacket - Atmospherics" + index_name = "Suragi Jacket - Atmospherics" path = /obj/item/clothing/suit/storage/suragi_jacket/atmos allowed_roles = list(JOB_TITLE_ATMOSTECH) /datum/gear/suit/suragi_jacket/eng - display_name = "Suragi Jacket - Engineering" + index_name = "Suragi Jacket - Engineering" path = /obj/item/clothing/suit/storage/suragi_jacket/eng allowed_roles = list(JOB_TITLE_ENGINEER, JOB_TITLE_ENGINEER_TRAINEE, JOB_TITLE_MECHANIC) /datum/gear/suit/suragi_jacket/botany - display_name = "Suragi Jacket - Hydroponics" + index_name = "Suragi Jacket - Hydroponics" path = /obj/item/clothing/suit/storage/suragi_jacket/botany allowed_roles = list(JOB_TITLE_BOTANIST) /datum/gear/suit/suragi_jacket/medic - display_name = "Suragi Jacket - Medical" + index_name = "Suragi Jacket - Medical" path = /obj/item/clothing/suit/storage/suragi_jacket/medic allowed_roles = list(JOB_TITLE_DOCTOR, JOB_TITLE_INTERN, JOB_TITLE_PSYCHIATRIST, JOB_TITLE_PARAMEDIC, JOB_TITLE_CORONER) /datum/gear/suit/suragi_jacket/medsec - display_name = "Suragi Jacket - Medical Security" + index_name = "Suragi Jacket - Medical Security" path = /obj/item/clothing/suit/storage/suragi_jacket/medsec allowed_roles = list(JOB_TITLE_BRIGDOC) /datum/gear/suit/suragi_jacket/virus - display_name = "Suragi Jacket - Virology" + index_name = "Suragi Jacket - Virology" path = /obj/item/clothing/suit/storage/suragi_jacket/virus allowed_roles = list(JOB_TITLE_VIROLOGIST) /datum/gear/suit/suragi_jacket/chem - display_name = "Suragi Jacket - Chemistry" + index_name = "Suragi Jacket - Chemistry" path = /obj/item/clothing/suit/storage/suragi_jacket/chem allowed_roles = list(JOB_TITLE_CHEMIST) /datum/gear/suit/suragi_jacket/genetics - display_name = "Suragi Jacket - Genetics" + index_name = "Suragi Jacket - Genetics" path = /obj/item/clothing/suit/storage/suragi_jacket/genetics allowed_roles = list(JOB_TITLE_GENETICIST) /datum/gear/suit/suragi_jacket/robot - display_name = "Suragi Jacket - Roboticist" + index_name = "Suragi Jacket - Roboticist" path = /obj/item/clothing/suit/storage/suragi_jacket/robot allowed_roles = list(JOB_TITLE_ROBOTICIST) /datum/gear/suit/suragi_jacket/sci - display_name = "Suragi Jacket - Science" + index_name = "Suragi Jacket - Science" path = /obj/item/clothing/suit/storage/suragi_jacket/sci allowed_roles = list(JOB_TITLE_SCIENTIST, JOB_TITLE_SCIENTIST_STUDENT) /datum/gear/suit/suragi_jacket/janitor - display_name = "Suragi Jacket - Janitor" + index_name = "Suragi Jacket - Janitor" path = /obj/item/clothing/suit/storage/suragi_jacket/janitor allowed_roles = list(JOB_TITLE_JANITOR) /datum/gear/suit/ianshirt - display_name = "Ian Shirt" + index_name = "Ian Shirt" path = /obj/item/clothing/suit/ianshirt /datum/gear/suit/hoodie - display_name = "hoodie, select" + index_name = "hoodie, select" + display_name = "hoodie" path = /obj/item/clothing/suit/hooded/hoodie /datum/gear/suit/hoodie/New() @@ -259,7 +261,8 @@ //SUITS! /datum/gear/suit/blacksuit - display_name = "suit jacket, select" + index_name = "suit jacket, select" + display_name = "suit jacket" path = /obj/item/clothing/suit/storage/lawyer/blackjacket /datum/gear/suit/blacksuit/New() @@ -272,14 +275,14 @@ //Robes! /datum/gear/suit/witch - display_name = "witch robes" + index_name = "witch robes" path = /obj/item/clothing/suit/wizrobe/marisa/fake //Suspenders /datum/gear/suit/suspenders - display_name = "suspenders, color" + index_name = "suspenders, color" path = /obj/item/clothing/suit/suspenders /datum/gear/suit/suspenders/New() diff --git a/code/modules/client/preference/loadout/loadout_tgui.dm b/code/modules/client/preference/loadout/loadout_tgui.dm new file mode 100644 index 00000000000..977ebac09a6 --- /dev/null +++ b/code/modules/client/preference/loadout/loadout_tgui.dm @@ -0,0 +1,61 @@ +GLOBAL_LIST_EMPTY(gear_tgui_info) + +/datum/ui_module/loadout + name = "Loadout" + +/datum/ui_module/loadout/ui_state(mob/user) + return GLOB.always_state + +/datum/ui_module/loadout/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Loadout", name) + ui.set_autoupdate(FALSE) + ui.open() + +/datum/ui_module/loadout/ui_data(mob/user) + var/list/data = list() + data["gear_slots"] = user?.client?.prefs.build_loadout() + data["selected_gears"] = user?.client?.prefs?.tgui_loadout_gear + return data + +/datum/ui_module/loadout/ui_static_data(mob/user) + var/list/data = list() + data["gears"] = GLOB.gear_tgui_info + data["max_gear_slots"] = user?.client?.prefs?.max_gear_slots + data["user_tier"] = user?.client?.donator_level + return data + +/datum/ui_module/loadout/ui_act(action, list/params) + if(..()) + return + . = TRUE + + var/mob/user = usr + var/datum/preferences/prefs = user.client.prefs + switch(action) + if("toggle_gear") + var/datum/gear/gear = GLOB.gear_datums[params["gear"]] + if(gear && ("[gear.index_name]" in prefs.loadout_gear)) + prefs.loadout_gear -= "[gear.index_name]" + return TRUE + + if(gear.donator_tier && user.client.donator_level < gear.donator_tier) + to_chat(user, "That gear is only available at a higher donation tier than you are on.") + return FALSE + + user.client.prefs.build_loadout(gear) + return TRUE + + if("set_tweak") + if(!(params["gear"] in prefs.loadout_gear)) + return FALSE + + var/datum/gear/gear = GLOB.gear_datums[params["gear"]] + var/datum/gear_tweak/tweak = locate(text2path(params["tweak"])) in gear.gear_tweaks + prefs.set_tweak_metadata(gear, tweak, tweak.get_metadata(user, prefs.get_tweak_metadata(gear, tweak))) + return TRUE + + if("clear_loadout") + prefs.loadout_gear.Cut() + return TRUE diff --git a/code/modules/client/preference/loadout/loadout_uniform.dm b/code/modules/client/preference/loadout/loadout_uniform.dm index 8e3120d644d..55ef15ce02f 100644 --- a/code/modules/client/preference/loadout/loadout_uniform.dm +++ b/code/modules/client/preference/loadout/loadout_uniform.dm @@ -10,7 +10,7 @@ //there's a lot more colors than I thought there were @_@ /datum/gear/uniform/suit/jumpsuit - display_name = "jumpsuit, select" + index_name = "jumpsuit, select" path = /obj/item/clothing/under/color/grey /datum/gear/uniform/suit/jumpsuit/New() @@ -39,27 +39,27 @@ gear_tweaks += new /datum/gear_tweak/path(suits, src, TRUE) /datum/gear/uniform/suit/soviet - display_name = "USSP uniform" + index_name = "USSP uniform" path = /obj/item/clothing/under/soviet /datum/gear/uniform/suit/federal - display_name = "Solar Federation uniform" + index_name = "Solar Federation uniform" path = /obj/item/clothing/under/solgov/civ /datum/gear/uniform/suit/kilt - display_name = "a kilt" + index_name = "a kilt" path = /obj/item/clothing/under/kilt /datum/gear/uniform/suit/executive - display_name = "executive suit" + index_name = "executive suit" path = /obj/item/clothing/under/suit_jacket/really_black /datum/gear/uniform/suit/amish_suit - display_name = "amish suit" + index_name = "amish suit" path = /obj/item/clothing/under/sl_suit /datum/gear/uniform/chaps - display_name = "chaps, select" + index_name = "chaps, select" path = /obj/item/clothing/under/red_chaps /datum/gear/uniform/chaps/New() @@ -74,11 +74,11 @@ subtype_path = /datum/gear/uniform/skirt /datum/gear/uniform/skirt/syndi - display_name = "skirt, tactical" + index_name = "skirt, tactical" path = /obj/item/clothing/under/syndicate/tacticool/skirt /datum/gear/uniform/skirt/dyeable - display_name = "dyeable skirt, color" + index_name = "dyeable skirt, color" path = /obj/item/clothing/under/colour/skirt @@ -88,7 +88,7 @@ /datum/gear/uniform/skirt/plaid - display_name = "plaid skirt, select" + index_name = "plaid skirt, select" path = /obj/item/clothing/under/dress/plaid_blue /datum/gear/uniform/skirt/plaid/New() @@ -99,11 +99,11 @@ gear_tweaks += new /datum/gear_tweak/path(skirts, src, TRUE) /datum/gear/uniform/skirt/redeveninggown - display_name = "red evening gown" + index_name = "red evening gown" path = /obj/item/clothing/under/redeveninggown /datum/gear/uniform/skirt/black - display_name = "skirt, black" + index_name = "skirt, black" path = /obj/item/clothing/under/blackskirt /datum/gear/uniform/skirt/job @@ -111,137 +111,137 @@ subtype_cost_overlap = FALSE /datum/gear/uniform/skirt/job/ce - display_name = "skirt, ce" + index_name = "skirt, ce" path = /obj/item/clothing/under/rank/chief_engineer/skirt allowed_roles = list(JOB_TITLE_CHIEF) /datum/gear/uniform/skirt/job/atmos - display_name = "skirt, atmos" + index_name = "skirt, atmos" path = /obj/item/clothing/under/rank/atmospheric_technician/skirt allowed_roles = list(JOB_TITLE_CHIEF, JOB_TITLE_ATMOSTECH) /datum/gear/uniform/skirt/job/eng - display_name = "skirt, engineer" + index_name = "skirt, engineer" path = /obj/item/clothing/under/rank/engineer/skirt allowed_roles = list(JOB_TITLE_CHIEF, JOB_TITLE_ENGINEER) /datum/gear/uniform/skirt/job/roboticist - display_name = "skirt, roboticist" + index_name = "skirt, roboticist" path = /obj/item/clothing/under/rank/roboticist/skirt allowed_roles = list(JOB_TITLE_RD, JOB_TITLE_ROBOTICIST) /datum/gear/uniform/skirt/job/cmo - display_name = "skirt, cmo" + index_name = "skirt, cmo" path = /obj/item/clothing/under/rank/chief_medical_officer/skirt allowed_roles = list(JOB_TITLE_CMO) /datum/gear/uniform/skirt/job/paramedic - display_name = "skirt, paramedic" + index_name = "skirt, paramedic" path = /obj/item/clothing/under/rank/medical/paramedic/skirt allowed_roles = list(JOB_TITLE_PARAMEDIC) /datum/gear/uniform/skirt/job/chem - display_name = "skirt, chemist" + index_name = "skirt, chemist" path = /obj/item/clothing/under/rank/chemist/skirt allowed_roles = list(JOB_TITLE_CMO, JOB_TITLE_CHEMIST) /datum/gear/uniform/skirt/job/viro - display_name = "skirt, virologist" + index_name = "skirt, virologist" path = /obj/item/clothing/under/rank/virologist/skirt allowed_roles = list(JOB_TITLE_VIROLOGIST) /datum/gear/uniform/skirt/job/med - display_name = "skirt, medical" + index_name = "skirt, medical" path = /obj/item/clothing/under/rank/medical/skirt allowed_roles = list(JOB_TITLE_CMO, JOB_TITLE_DOCTOR, JOB_TITLE_INTERN, JOB_TITLE_PSYCHIATRIST, JOB_TITLE_PARAMEDIC, JOB_TITLE_CORONER) /datum/gear/uniform/skirt/job/phys - display_name = "skirt, physician" + index_name = "skirt, physician" path = /obj/item/clothing/under/rank/security/brigphys/skirt allowed_roles = list(JOB_TITLE_BRIGDOC) /datum/gear/uniform/skirt/job/physalt - display_name = "skirt, physician alt" + index_name = "skirt, physician alt" path = /obj/item/clothing/under/rank/security/brigmedical/skirt allowed_roles = list(JOB_TITLE_BRIGDOC) /datum/gear/uniform/skirt/job/hydro - display_name = "skirt, botanist" + index_name = "skirt, botanist" path = /obj/item/clothing/under/rank/hydroponics/skirt allowed_roles = list(JOB_TITLE_BOTANIST) /datum/gear/uniform/skirt/job/sci - display_name = "skirt, scientist" + index_name = "skirt, scientist" path = /obj/item/clothing/under/rank/scientist/skirt allowed_roles = list(JOB_TITLE_RD, JOB_TITLE_SCIENTIST, JOB_TITLE_SCIENTIST_STUDENT) /datum/gear/uniform/skirt/job/cargo - display_name = "skirt, cargo" + index_name = "skirt, cargo" path = /obj/item/clothing/under/rank/cargotech/skirt allowed_roles = list(JOB_TITLE_QUARTERMASTER, JOB_TITLE_CARGOTECH) /datum/gear/uniform/skirt/job/qm - display_name = "skirt, QM" + index_name = "skirt, QM" path = /obj/item/clothing/under/rank/cargo/skirt allowed_roles = list(JOB_TITLE_QUARTERMASTER) /datum/gear/uniform/skirt/job/warden - display_name = "skirt, warden" + index_name = "skirt, warden" path = /obj/item/clothing/under/rank/warden/skirt allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN) /datum/gear/uniform/skirt/job/security - display_name = "skirt, security" + index_name = "skirt, security" path = /obj/item/clothing/under/rank/security/skirt allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_DETECTIVE, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) /datum/gear/uniform/skirt/job/podpilot - display_name = "skirt, podpilot" + index_name = "skirt, podpilot" path = /obj/item/clothing/under/rank/security/pod_pilot/skirt allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_PILOT) /datum/gear/uniform/skirt/job/head_of_security - display_name = "skirt, hos" + index_name = "skirt, hos" path = /obj/item/clothing/under/rank/head_of_security/skirt allowed_roles = list(JOB_TITLE_HOS) /datum/gear/uniform/skirt/job/ntrep - display_name = "skirt, nt rep" + index_name = "skirt, nt rep" path = /obj/item/clothing/under/rank/ntrep/skirt allowed_roles = list(JOB_TITLE_REPRESENTATIVE) /datum/gear/uniform/skirt/job/blueshield - display_name = "skirt, blueshield" + index_name = "skirt, blueshield" path = /obj/item/clothing/under/rank/blueshield/skirt allowed_roles = list(JOB_TITLE_BLUESHIELD) /datum/gear/uniform/skirt/job/librarian - display_name = "skirt, librarian" + index_name = "skirt, librarian" path = /obj/item/clothing/under/suit_jacket/red/skirt allowed_roles = list(JOB_TITLE_LIBRARIAN) /datum/gear/uniform/skirt/job/bartender - display_name = "skirt, bartender" + index_name = "skirt, bartender" path = /obj/item/clothing/under/rank/bartender/skirt allowed_roles = list(JOB_TITLE_BARTENDER) /datum/gear/uniform/skirt/job/chaplain - display_name = "skirt, chaplain" + index_name = "skirt, chaplain" path = /obj/item/clothing/under/rank/chaplain/skirt allowed_roles = list(JOB_TITLE_CHAPLAIN) /datum/gear/uniform/skirt/job/barber - display_name = "skirt, barber" + index_name = "skirt, barber" path = /obj/item/clothing/under/barber/skirt allowed_roles = list(JOB_TITLE_BARBER) /datum/gear/uniform/skirt/job/nanotrasenofficer - display_name = "skirt, NNO" + index_name = "skirt, NNO" path = /obj/item/clothing/under/rank/centcom/officer/skirt allowed_roles = list(JOB_TITLE_CCOFFICER) /datum/gear/uniform/skirt/job/internalaffairs - display_name = "skirt, internalaffairs" + index_name = "skirt, internalaffairs" path = /obj/item/clothing/under/rank/internalaffairs/skirt allowed_roles = list(JOB_TITLE_LAWYER) @@ -249,7 +249,8 @@ subtype_path = /datum/gear/uniform/medical /datum/gear/uniform/medical/scrubs - display_name = "medical scrubs, select" + index_name = "medical scrubs, select" + display_name = "medical scrubs" path = /obj/item/clothing/under/rank/medical/purple allowed_roles = list(JOB_TITLE_CMO, JOB_TITLE_DOCTOR, JOB_TITLE_INTERN) @@ -264,22 +265,22 @@ subtype_path = /datum/gear/uniform/sec /datum/gear/uniform/sec/formal - display_name = "security uniform, formal" + index_name = "security uniform, formal" path = /obj/item/clothing/under/rank/security/formal allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_DETECTIVE, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) /datum/gear/uniform/sec/secorporate - display_name = "security uniform, corporate" + index_name = "security uniform, corporate" path = /obj/item/clothing/under/rank/security/corp allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) /datum/gear/uniform/sec/dispatch - display_name = "security uniform, dispatch" + index_name = "security uniform, dispatch" path = /obj/item/clothing/under/rank/dispatch allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) /datum/gear/uniform/sec/casual - display_name = "security uniform, casual" + index_name = "security uniform, casual" path = /obj/item/clothing/under/rank/security2 allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_DETECTIVE, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) @@ -287,22 +288,23 @@ subtype_path = /datum/gear/uniform/cargo /datum/gear/uniform/cargo/qm - display_name = "quartermaster dress" + index_name = "quartermaster dress" path = /obj/item/clothing/under/rank/cargo/alt allowed_roles = list(JOB_TITLE_QUARTERMASTER) /datum/gear/uniform/cargo/tech - display_name = "cargo technician dress" + index_name = "cargo technician dress" path = /obj/item/clothing/under/rank/cargotech/alt allowed_roles = list(JOB_TITLE_QUARTERMASTER, JOB_TITLE_CARGOTECH) /datum/gear/uniform/cargo/miner - display_name = "shaft miner sweater" + index_name = "shaft miner sweater" path = /obj/item/clothing/under/rank/miner/alt allowed_roles = list(JOB_TITLE_QUARTERMASTER, JOB_TITLE_MINER) /datum/gear/uniform/shorts - display_name = "shorts, select" + index_name = "shorts, select" + display_name = "shorts" path = /obj/item/clothing/under/shorts/red /datum/gear/uniform/shorts/New() @@ -318,7 +320,8 @@ subtype_path = /datum/gear/uniform/pants /datum/gear/uniform/pants/jeans - display_name = "jeans, select" + index_name = "jeans, select" + display_name = "jeans" path = /obj/item/clothing/under/pants/classicjeans /datum/gear/uniform/pants/jeans/New() @@ -331,7 +334,8 @@ gear_tweaks += new /datum/gear_tweak/path(jeans, src, TRUE) /datum/gear/uniform/pants/pants - display_name = "pants, select" + index_name = "pants, select" + display_name = "pants" path = /obj/item/clothing/under/pants/white /datum/gear/uniform/pants/pants/New() @@ -348,48 +352,48 @@ gear_tweaks += new /datum/gear_tweak/path(pants, src, TRUE) /datum/gear/uniform/suit/tacticool - display_name = "tacticool turtleneck" + index_name = "tacticool turtleneck" description = "A sleek black turtleneck paired with some khakis (WARNING DOES NOT HAVE SUIT SENSORS)" path = /obj/item/clothing/under/syndicate/tacticool /datum/gear/uniform/hawaii - display_name = "hawaiian shirt (red)" + index_name = "hawaiian shirt (red)" description = "Sometimes you just want to shoot the guy who brought the chainsaw to the drug deal" path = /obj/item/clothing/under/redhawaiianshirt /datum/gear/uniform/hawaii/pink - display_name = "hawaiian shirt (pink)" + index_name = "hawaiian shirt (pink)" description = "Sometimes you just want some pink in your life. For what? Who knows" path = /obj/item/clothing/under/pinkhawaiianshirt /datum/gear/uniform/hawaii/blue - display_name = "hawaiian shirt (blue)" + index_name = "hawaiian shirt (blue)" description = "Be careful around water! Some guys in blue shirt like you can't swim" path = /obj/item/clothing/under/bluehawaiianshirt /datum/gear/uniform/hawaii/orange - display_name = "hawaiian shirt (orange)" + index_name = "hawaiian shirt (orange)" description = "Come one step closer and I will knock his teeth out!" path = /obj/item/clothing/under/orangehawaiianshirt /datum/gear/uniform/ussptracksuit_red - display_name = "track suit (red)" + index_name = "track suit (red)" description = "A classic track suit. There is a small tag on the clothes that says \"Made in the USSP\"." path = /obj/item/clothing/under/ussptracksuit_red /datum/gear/uniform/ussptracksuit_blue - display_name = "track suit (blue)" + index_name = "track suit (blue)" description = "A classic track suit. There is a small tag on the clothes that says \"Made in the USSP\"." path = /obj/item/clothing/under/ussptracksuit_blue /datum/gear/uniform/dress50s - display_name = "old Soviet dress" + index_name = "old Soviet dress" path = /obj/item/clothing/under/dress50s /datum/gear/uniform/galifepants - display_name = "check breeches" + index_name = "check breeches" path = /obj/item/clothing/under/pants/galifepants /datum/gear/uniform/sandpants - display_name = "long sand pants" + index_name = "long sand pants" path = /obj/item/clothing/under/pants/sandpants diff --git a/code/modules/client/preference/preferences.dm b/code/modules/client/preference/preferences.dm index 6e9a574847c..0c73ea934fa 100644 --- a/code/modules/client/preference/preferences.dm +++ b/code/modules/client/preference/preferences.dm @@ -74,9 +74,8 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts #define TAB_CHAR 0 #define TAB_GAME 1 #define TAB_SPEC 2 -#define TAB_GEAR 3 -#define TAB_KEYS 4 -#define TAB_TOGGLES 5 +#define TAB_KEYS 3 +#define TAB_TOGGLES 4 /datum/preferences var/client/parent @@ -242,6 +241,7 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts //Gear stuff var/list/loadout_gear = list() + var/list/tgui_loadout_gear = list() var/list/choosen_gears = list() var/gear_tab = "General" // Parallax @@ -313,7 +313,6 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts dat += "Character Settings" dat += "Game Preferences" dat += "Special Roles" - dat += "Loadout" dat += "Key Bindings" dat += "General Preferences" dat += "" @@ -532,7 +531,8 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts dat += "Undershirt Color: Color [color_square(undershirt_color)]
" if(S.clothing_flags & HAS_SOCKS) dat += "Socks: [socks]
" - dat += "Backpack Type: [backbag]
" + dat += "Backpack Type: [backbag]

" + dat += "Open Loadout
" dat += "" @@ -642,59 +642,6 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts dat += "

" dat += "" - if(TAB_GEAR) - var/total_cost = 0 - var/list/type_blacklist = list() - if(loadout_gear && loadout_gear.len) - for(var/i = 1, i <= loadout_gear.len, i++) - var/datum/gear/G = GLOB.gear_datums[loadout_gear[i]] - if(G) - if(!G.subtype_cost_overlap) - if(G.subtype_path in type_blacklist) - continue - type_blacklist += G.subtype_path - total_cost += G.cost - - var/fcolor = "#3366CC" - if(total_cost < max_gear_slots) - fcolor = "#E67300" - dat += "" - dat += "" - dat += "" - - var/datum/loadout_category/LC = own_categories[gear_tab] - dat += "" - for(var/gear_name in LC.gear) - var/datum/gear/G = LC.gear[gear_name] - var/datum/gear/ticked = choosen_gears[G.display_name] - dat += "" - dat += "
[total_cost]/[max_gear_slots] loadout points spent. \[Clear Loadout\]
" - - var/firstcat = 1 - var/list/own_categories = GLOB.loadout_categories.Copy() - var/datum/loadout_category/choosen = new("Selected") - choosen.gear = choosen_gears - own_categories[choosen.category] = choosen - for(var/category in own_categories) - if(firstcat) - firstcat = 0 - else - dat += " |" - if(category == gear_tab) - dat += " [category] " - else - dat += " [category] " - dat += "
[LC.category]
[G.display_name]
" - if(ticked) - for(var/datum/gear_tweak/tweak in ticked.gear_tweaks) - dat += "
[tweak.get_contents(get_tweak_metadata(ticked, tweak))]" - dat += "
[G.cost]" - if(G.allowed_roles) - dat += "Restrictions: " - for(var/role in G.allowed_roles) - dat += role + " " - dat += "" - dat += "[G.get_header_tips()][ticked ? ticked.description : G.description]
" if(TAB_KEYS) dat += "
All Key Bindings: " dat += "Reset to Default " @@ -806,10 +753,10 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts /datum/preferences/proc/get_gear_metadata(var/datum/gear/G) - . = loadout_gear[G.display_name] + . = loadout_gear[G.index_name] if(!.) . = list() - loadout_gear[G.display_name] = . + loadout_gear[G.index_name] = . /datum/preferences/proc/get_tweak_metadata(var/datum/gear/G, var/datum/gear_tweak/tweak) var/list/metadata = get_gear_metadata(G) @@ -1280,6 +1227,58 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts SetChoices(user) return 1 +/** + * Rebuilds the `loadout_gear` list of the [active_character], and returns the total end cost. + * + * Caches and cuts the existing [/datum/character_save/var/loadout_gear] list and remakes it, checking the `subtype_selection_cost` and overall cost validity of each item. + * + * If the item's [/datum/gear/var/subtype_selection_cost] is `FALSE`, any future items with the same [/datum/gear/var/main_typepath] will have their cost skipped. + * If adding the item will take the total cost over the maximum, it won't be added to the list. + * + * Arguments: + * * new_item - A new [/datum/gear] item to be added to the `loadout_gear` list. + */ +/datum/preferences/proc/build_loadout(datum/gear/new_item) + var/total_cost = 0 + var/list/type_blacklist = list() + var/list/loadout_cache = loadout_gear.Copy() + loadout_gear.Cut() + tgui_loadout_gear.Cut() + choosen_gears.Cut() + if(new_item) + loadout_cache += "[new_item.index_name]" + + for(var/item in loadout_cache) + var/datum/gear/gear = GLOB.gear_datums[item] + if(!gear) + continue + var/added_cost = gear.cost + if(!gear.subtype_cost_overlap) // If listings of the same subtype shouldn't have their cost added. + if(gear.path in type_blacklist) + added_cost = 0 + else + type_blacklist += gear.path + if((total_cost + added_cost) > max_gear_slots) + continue // If the final cost is too high, don't add the item. + var/item_cache = loadout_cache[item] + loadout_gear[item] = item_cache ? item_cache : list() + var/tgui_data = list() + for(var/datum/gear_tweak/tweak in gear.gear_tweaks) + var/text_path = "[tweak.type]" + if(!(text_path in item_cache)) + continue + var/params = item_cache[text_path] + var/list/data =tweak.get_tgui_data(params) + tgui_data[text_path] = data["display_param"] + tgui_data["name"] = data["name"] + tgui_data["icon"] = data["icon"] + tgui_data["icon_file"] = data["icon_file"] + tgui_data["icon_state"] = data["icon_state"] + tgui_loadout_gear[gear] = tgui_data + choosen_gears[item] = gear + total_cost += added_cost + return total_cost + /datum/preferences/proc/ResetJobs() job_support_high = 0 job_support_med = 0 @@ -1491,47 +1490,6 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts SetRecords(user) - if(href_list["preference"] == "gear") - if(href_list["toggle_gear"]) - var/datum/gear/TG = GLOB.gear_datums[href_list["toggle_gear"]] - if(TG.display_name in loadout_gear) - loadout_gear -= TG.display_name - choosen_gears -= TG.display_name - else - if(!TG.can_select(cl = user.client, species_name = S.name)) // all gear checks there, no jobs while prefs - return - var/total_cost = 0 - var/list/type_blacklist = list() - for(var/gear_name in loadout_gear) - var/datum/gear/G = GLOB.gear_datums[gear_name] - if(istype(G)) - if(!G.subtype_cost_overlap) - if(G.subtype_path in type_blacklist) - continue - type_blacklist += G.subtype_path - total_cost += G.cost - - if((total_cost + TG.cost) <= max_gear_slots) - loadout_gear += TG.display_name - choosen_gears[TG.display_name] += new TG.type - else if(href_list["gear"] && href_list["tweak"]) - var/datum/gear/gear = choosen_gears[href_list["gear"]] - var/datum/gear_tweak/tweak = locate(href_list["tweak"]) - if(!tweak || !istype(gear) || !(tweak in gear.gear_tweaks)) - return - var/metadata = tweak.get_metadata(user, get_tweak_metadata(gear, tweak)) - if(!metadata) - return - set_tweak_metadata(gear, tweak, metadata) - else if(href_list["select_category"]) - gear_tab = href_list["select_category"] - else if(href_list["clear_loadout"]) - loadout_gear.Cut() - choosen_gears.Cut() - - ShowChoices(user) - return - switch(href_list["task"]) if("random") var/datum/robolimb/robohead @@ -1763,16 +1721,16 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts if("hair") if(species in list(SPECIES_HUMAN, SPECIES_UNATHI, SPECIES_TAJARAN, SPECIES_SKRELL, SPECIES_MACNINEPERSON, SPECIES_VULPKANIN, SPECIES_VOX, SPECIES_WRYN)) //Species that have hair. (No HAS_HAIR flag) var/input = "Choose your character's hair colour:" - var/new_hair = input(user, input, "Character Preference", h_colour) as color|null - if(new_hair) + var/new_hair = tgui_input_color(user, input, "Character Preference", h_colour) + if(!isnull(new_hair)) h_colour = new_hair if("secondary_hair") if(species in list(SPECIES_HUMAN, SPECIES_UNATHI, SPECIES_TAJARAN, SPECIES_SKRELL, SPECIES_MACNINEPERSON, SPECIES_VULPKANIN, SPECIES_VOX)) var/datum/sprite_accessory/hair_style = GLOB.hair_styles_public_list[h_style] if(hair_style.secondary_theme && !hair_style.no_sec_colour) - var/new_hair = input(user, "Choose your character's secondary hair colour:", "Character Preference", h_sec_colour) as color|null - if(new_hair) + var/new_hair = tgui_input_color(user, "Choose your character's secondary hair colour:", "Character Preference", h_sec_colour) + if(!isnull(new_hair)) h_sec_colour = new_hair if("h_style") @@ -1820,8 +1778,8 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts h_grad_offset_y = clamp(text2num(expl[2]) || 0, -16, 16) if("h_grad_colour") - var/result = input(user, "Choose your character's hair gradient colour:", "Character Preference", h_grad_colour) as color|null - if(result) + var/result = tgui_input_color(user, "Choose your character's hair gradient colour:", "Character Preference", h_grad_colour) + if(!isnull(result)) h_grad_colour = result if("h_grad_alpha") @@ -1833,8 +1791,8 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts if("headaccessory") if(S.bodyflags & HAS_HEAD_ACCESSORY) //Species with head accessories. var/input = "Choose the colour of your your character's head accessory:" - var/new_head_accessory = input(user, input, "Character Preference", hacc_colour) as color|null - if(new_head_accessory) + var/new_head_accessory = tgui_input_color(user, input, "Character Preference", hacc_colour) + if(!isnull(new_head_accessory)) hacc_colour = new_head_accessory if("ha_style") @@ -1913,8 +1871,8 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts if("m_head_colour") if(S.bodyflags & HAS_HEAD_MARKINGS) //Species with head markings. var/input = "Choose the colour of your your character's head markings:" - var/new_markings = input(user, input, "Character Preference", m_colours["head"]) as color|null - if(new_markings) + var/new_markings = tgui_input_color(user, input, "Character Preference", m_colours["head"]) + if(!isnull(new_markings)) m_colours["head"] = new_markings if("m_style_body") @@ -1939,8 +1897,8 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts if("m_body_colour") if(S.bodyflags & HAS_BODY_MARKINGS) //Species with body markings/tattoos. var/input = "Choose the colour of your your character's body markings:" - var/new_markings = input(user, input, "Character Preference", m_colours["body"]) as color|null - if(new_markings) + var/new_markings = tgui_input_color(user, input, "Character Preference", m_colours["body"]) + if(!isnull(new_markings)) m_colours["body"] = new_markings if("m_style_tail") @@ -1969,8 +1927,8 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts if("m_tail_colour") if(S.bodyflags & HAS_TAIL_MARKINGS) //Species with tail markings. var/input = "Choose the colour of your your character's tail markings:" - var/new_markings = input(user, input, "Character Preference", m_colours["tail"]) as color|null - if(new_markings) + var/new_markings = tgui_input_color(user, input, "Character Preference", m_colours["tail"]) + if(!isnull(new_markings)) m_colours["tail"] = new_markings if("body_accessory") @@ -1995,16 +1953,16 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts if("facial") if(species in list(SPECIES_HUMAN, SPECIES_UNATHI, SPECIES_TAJARAN, SPECIES_SKRELL, SPECIES_MACNINEPERSON, SPECIES_VULPKANIN, SPECIES_VOX)) //Species that have facial hair. (No HAS_HAIR_FACIAL flag) - var/new_facial = input(user, "Choose your character's facial-hair colour:", "Character Preference", f_colour) as color|null - if(new_facial) + var/new_facial = tgui_input_color(user, "Choose your character's facial-hair colour:", "Character Preference", f_colour) + if(!isnull(new_facial)) f_colour = new_facial if("secondary_facial") if(species in list(SPECIES_HUMAN, SPECIES_UNATHI, SPECIES_TAJARAN, SPECIES_SKRELL, SPECIES_MACNINEPERSON, SPECIES_VULPKANIN, SPECIES_VOX)) var/datum/sprite_accessory/facial_hair_style = GLOB.facial_hair_styles_list[f_style] if(facial_hair_style.secondary_theme && !facial_hair_style.no_sec_colour) - var/new_facial = input(user, "Choose your character's secondary facial-hair colour:", "Character Preference", f_sec_colour) as color|null - if(new_facial) + var/new_facial = tgui_input_color(user, "Choose your character's secondary facial-hair colour:", "Character Preference", f_sec_colour) + if(!isnull(new_facial)) f_sec_colour = new_facial if("f_style") @@ -2054,8 +2012,8 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts underwear = new_underwear if("underwear_color") - var/new_uwear_color = input(user, "Choose your character's underwear colour:", "Character Preference", underwear_color) as color|null - if(new_uwear_color) + var/new_uwear_color = tgui_input_color(user, "Choose your character's underwear colour:", "Character Preference", underwear_color) + if(!isnull(new_uwear_color)) underwear_color = new_uwear_color if("undershirt") @@ -2074,8 +2032,8 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts undershirt = new_undershirt if("undershirt_color") - var/new_ushirt_color = input(user, "Choose your character's undershirt colour:", "Character Preference", undershirt_color) as color|null - if(new_ushirt_color) + var/new_ushirt_color = tgui_input_color(user, "Choose your character's undershirt colour:", "Character Preference", undershirt_color) + if(!isnull(new_ushirt_color)) undershirt_color = new_ushirt_color if("socks") @@ -2094,8 +2052,8 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts socks = new_socks if("eyes") - var/new_eyes = input(user, "Choose your character's eye colour:", "Character Preference", e_colour) as color|null - if(new_eyes) + var/new_eyes = tgui_input_color(user, "Choose your character's eye colour:", "Character Preference", e_colour) + if(!isnull(new_eyes)) e_colour = new_eyes if("s_tone") @@ -2121,13 +2079,13 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts if("skin") if((S.bodyflags & HAS_SKIN_COLOR) || ((S.bodyflags & HAS_BODYACC_COLOR) && GLOB.body_accessory_by_species[species]) || check_rights(R_ADMIN, 0, user)) - var/new_skin = input(user, "Choose your character's skin colour: ", "Character Preference", s_colour) as color|null - if(new_skin) + var/new_skin = tgui_input_color(user, "Choose your character's skin colour: ", "Character Preference", s_colour) + if(!isnull(new_skin)) s_colour = new_skin if("ooccolor") - var/new_ooccolor = input(user, "Choose your OOC colour:", "Game Preference", ooccolor) as color|null - if(new_ooccolor) + var/new_ooccolor = tgui_input_color(user, "Choose your OOC colour:", "Game Preference", ooccolor) + if(!isnull(new_ooccolor)) ooccolor = new_ooccolor if("bag") @@ -2135,6 +2093,11 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts if(new_backbag) backbag = new_backbag + if("loadout") + var/datum/ui_module/loadout/loadout = new() + loadout.ui_interact(user) + return FALSE + if("nt_relation") var/new_relation = tgui_input_list(user, "Choose your relation to NT. Note that this represents what others can find out about your character by researching your background, not what your character actually thinks.", "Character Preference", list("Loyal", "Supportive", "Neutral", "Skeptical", "Opposed")) if(new_relation) @@ -2489,8 +2452,8 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts toggles2 ^= PREFTOGGLE_2_AFKWATCH if("UIcolor") - var/UI_style_color_new = input(user, "Choose your UI color, dark colors are not recommended!", UI_style_color) as color|null - if(!UI_style_color_new) return + var/UI_style_color_new = tgui_input_color(user, "Choose your UI color, dark colors are not recommended!", UI_style_color) + if(isnull(UI_style_color_new)) return UI_style_color = UI_style_color_new if(ishuman(usr)) //mid-round preference changes, for aesthetics diff --git a/code/modules/client/preference/preferences_toggles.dm b/code/modules/client/preference/preferences_toggles.dm index d047654588f..4bbd63e44ca 100644 --- a/code/modules/client/preference/preferences_toggles.dm +++ b/code/modules/client/preference/preferences_toggles.dm @@ -434,8 +434,8 @@ blackbox_message = "Set Own OOC" /datum/preference_toggle/special_toggle/set_ooc_color/set_toggles(client/user) - var/new_ooccolor = input(usr, "Please select your OOC color.", "OOC color", user.prefs.ooccolor) as color|null - if(new_ooccolor) + var/new_ooccolor = tgui_input_color(usr, "Please select your OOC color.", "OOC color", user.prefs.ooccolor) + if(!isnull(new_ooccolor)) user.prefs.ooccolor = new_ooccolor to_chat(usr, "Your OOC color has been set to [new_ooccolor].") else diff --git a/code/modules/customitems/item_defines.dm b/code/modules/customitems/item_defines.dm index cb5b6bf4768..b92fc9ae696 100644 --- a/code/modules/customitems/item_defines.dm +++ b/code/modules/customitems/item_defines.dm @@ -111,8 +111,8 @@ /obj/item/fluff/tattoo_gun/elliot_cybernetic_tat/attack_self(mob/user as mob) if(!used) - var/ink_color = input("Please select an ink color.", "Tattoo Ink Color", rgb(tattoo_r, tattoo_g, tattoo_b)) as color|null - if(ink_color && !(user.incapacitated() || used) ) + var/ink_color = tgui_input_color("Please select an ink color.", "Tattoo Ink Color", rgb(tattoo_r, tattoo_g, tattoo_b)) + if(!isnull(ink_color) && !(user.incapacitated() || used) ) tattoo_r = color2R(ink_color) tattoo_g = color2G(ink_color) tattoo_b = color2B(ink_color) diff --git a/code/modules/mob/living/carbon/human/species/machine.dm b/code/modules/mob/living/carbon/human/species/machine.dm index c12df2472d3..853aa7a42a6 100644 --- a/code/modules/mob/living/carbon/human/species/machine.dm +++ b/code/modules/mob/living/carbon/human/species/machine.dm @@ -153,11 +153,11 @@ if(!head_organ) return if(!robohead.is_monitor) //If they've got a prosthetic head and it isn't a monitor, they've no screen to adjust. Instead, let them change the colour of their optics! - var/optic_colour = input(H, "Select optic colour", H.m_colours["head"]) as color|null + var/optic_colour = tgui_input_color(H, "Select optic colour", H.m_colours["head"]) if(H.incapacitated(INC_IGNORE_RESTRAINED|INC_IGNORE_GRABBED)) to_chat(H, "Ваша попытка сменить отображаемый цвет была прервана.") return - if(optic_colour) + if(!isnull(optic_colour)) H.change_markings(optic_colour, "head") else if(robohead.is_monitor) //Means that the character's head is a monitor (has a screen). Time to customize. diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm index 31574f8430a..57fd110b524 100644 --- a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm +++ b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm @@ -798,7 +798,10 @@ /obj/item/borg/upgrade/modkit/tracer/adjustable/attack_self(mob/user) - bolt_color = input(user,"","Choose Color",bolt_color) as color|null + var/color = tgui_input_color(user,"","Choose Color",bolt_color) + if(isnull(color)) + return + bolt_color = color #undef COMPATIBILITY_STANDART diff --git a/code/modules/spacepods/spacepod.dm b/code/modules/spacepods/spacepod.dm index 4973d3ce5a3..d98001ab186 100644 --- a/code/modules/spacepods/spacepod.dm +++ b/code/modules/spacepods/spacepod.dm @@ -103,7 +103,9 @@ if("Windows") part_type = WINDOW else - var/coloradd = input(user, "Choose a color", "Color") as color + var/coloradd = tgui_input_color(user, "Choose a color", "Color") + if(isnull(coloradd)) + return colors[part_type] = coloradd if(!has_paint) has_paint = 1 diff --git a/code/modules/tgui/modules/appearance_changer.dm b/code/modules/tgui/modules/appearance_changer.dm index 7983d32b064..ab1719a0a54 100644 --- a/code/modules/tgui/modules/appearance_changer.dm +++ b/code/modules/tgui/modules/appearance_changer.dm @@ -72,8 +72,8 @@ if("skin_color") if(can_change_skin_color()) - var/new_skin = input(usr, "Choose your character's skin colour: ", "Skin Color", owner.skin_colour) as color|null - if(new_skin && (!..()) && owner.change_skin_color(new_skin)) + var/new_skin = tgui_input_color(usr, "Choose your character's skin colour: ", "Skin Color", owner.skin_colour) + if(!isnull(new_skin) && (!..()) && owner.change_skin_color(new_skin)) update_dna() if("hair") @@ -83,14 +83,14 @@ if("hair_color") if(can_change(APPEARANCE_HAIR_COLOR)) - var/new_hair = input("Please select hair color.", "Hair Color", head_organ.hair_colour) as color|null - if(new_hair && (!..()) && owner.change_hair_color(new_hair)) + var/new_hair = tgui_input_color("Please select hair color.", "Hair Color", head_organ.hair_colour) + if(!isnull(new_hair) && (!..()) && owner.change_hair_color(new_hair)) update_dna() if("secondary_hair_color") if(can_change(APPEARANCE_SECONDARY_HAIR_COLOR)) - var/new_hair = input("Please select secondary hair color.", "Secondary Hair Color", head_organ.sec_hair_colour) as color|null - if(new_hair && (!..()) && owner.change_hair_color(new_hair, 1)) + var/new_hair = tgui_input_color("Please select secondary hair color.", "Secondary Hair Color", head_organ.sec_hair_colour) + if(!isnull(new_hair) && (!..()) && owner.change_hair_color(new_hair, 1)) update_dna() if("hair_gradient") @@ -124,21 +124,21 @@ if("facial_hair_color") if(can_change(APPEARANCE_FACIAL_HAIR_COLOR)) - var/new_facial = input("Please select facial hair color.", "Facial Hair Color", head_organ.facial_colour) as color|null - if(new_facial && (!..()) && owner.change_facial_hair_color(new_facial)) + var/new_facial = tgui_input_color("Please select facial hair color.", "Facial Hair Color", head_organ.facial_colour) + if(!isnull(new_facial) && (!..()) && owner.change_facial_hair_color(new_facial)) update_dna() if("secondary_facial_hair_color") if(can_change(APPEARANCE_SECONDARY_FACIAL_HAIR_COLOR)) - var/new_facial = input("Please select secondary facial hair color.", "Secondary Facial Hair Color", head_organ.sec_facial_colour) as color|null - if(new_facial && (!..()) && owner.change_facial_hair_color(new_facial, 1)) + var/new_facial = tgui_input_color("Please select secondary facial hair color.", "Secondary Facial Hair Color", head_organ.sec_facial_colour) + if(!isnull(new_facial) && (!..()) && owner.change_facial_hair_color(new_facial, 1)) update_dna() if("eye_color") if(can_change(APPEARANCE_EYE_COLOR)) var/obj/item/organ/internal/eyes/eyes_organ = owner.get_int_organ(/obj/item/organ/internal/eyes) - var/new_eyes = input("Please select eye color.", "Eye Color", eyes_organ.eye_colour) as color|null - if(new_eyes && (!..()) && owner.change_eye_color(new_eyes)) + var/new_eyes = tgui_input_color("Please select eye color.", "Eye Color", eyes_organ.eye_colour) + if(!isnull(new_eyes) && (!..()) && owner.change_eye_color(new_eyes)) update_dna() if("head_accessory") @@ -148,8 +148,8 @@ if("head_accessory_color") if(can_change_head_accessory()) - var/new_head_accessory = input("Please select head accessory color.", "Head Accessory Color", head_organ.headacc_colour) as color|null - if(new_head_accessory && (!..()) && owner.change_head_accessory_color(new_head_accessory)) + var/new_head_accessory = tgui_input_color("Please select head accessory color.", "Head Accessory Color", head_organ.headacc_colour) + if(!isnull(new_head_accessory) && (!..()) && owner.change_head_accessory_color(new_head_accessory)) update_dna() if("head_marking") @@ -159,8 +159,8 @@ if("head_marking_color") if(can_change_markings("head")) - var/new_markings = input("Please select head marking color.", "Marking Color", owner.m_colours["head"]) as color|null - if(new_markings && (!..()) && owner.change_marking_color(new_markings, "head")) + var/new_markings = tgui_input_color("Please select head marking color.", "Marking Color", owner.m_colours["head"]) + if(!isnull(new_markings) && (!..()) && owner.change_marking_color(new_markings, "head")) update_dna() if("body_marking") @@ -170,8 +170,8 @@ if("body_marking_color") if(can_change_markings("body")) - var/new_markings = input("Please select body marking color.", "Marking Color", owner.m_colours["body"]) as color|null - if(new_markings && (!..()) && owner.change_marking_color(new_markings, "body")) + var/new_markings = tgui_input_color("Please select body marking color.", "Marking Color", owner.m_colours["body"]) + if(!isnull(new_markings) && (!..()) && owner.change_marking_color(new_markings, "body")) update_dna() if("tail_marking") @@ -181,8 +181,8 @@ if("tail_marking_color") if(can_change_markings("tail")) - var/new_markings = input("Please select tail marking color.", "Marking Color", owner.m_colours["tail"]) as color|null - if(new_markings && (!..()) && owner.change_marking_color(new_markings, "tail")) + var/new_markings = tgui_input_color("Please select tail marking color.", "Marking Color", owner.m_colours["tail"]) + if(!isnull(new_markings) && (!..()) && owner.change_marking_color(new_markings, "tail")) update_dna() if("body_accessory") diff --git a/code/modules/tgui/tgui_datum.dm b/code/modules/tgui/tgui_datum.dm index 90372951958..960930d03b3 100644 --- a/code/modules/tgui/tgui_datum.dm +++ b/code/modules/tgui/tgui_datum.dm @@ -103,6 +103,8 @@ /datum/tgui/proc/send_assets() var/flushqueue = window.send_asset(get_asset_datum( /datum/asset/simple/namespaced/fontawesome)) + flushqueue |= window.send_asset(get_asset_datum( + /datum/asset/json/icon_ref_map)) for(var/datum/asset/asset in src_object.ui_assets(user)) flushqueue |= window.send_asset(asset) if(flushqueue) diff --git a/code/modules/tgui/tgui_input/color_input.dm b/code/modules/tgui/tgui_input/color_input.dm new file mode 100644 index 00000000000..7ba4b9ed3e0 --- /dev/null +++ b/code/modules/tgui/tgui_input/color_input.dm @@ -0,0 +1,132 @@ +/** + * Creates a TGUI color picker window and returns the user's response. + * + * This proc should be used to create a color picker that the caller will wait for a response from. + * Arguments: + * * user - The user to show the picker to. + * * title - The of the picker modal, shown on the top of the TGUI window. + * * timeout - The timeout of the picker, after which the modal will close and qdel itself. Set to zero for no timeout. + * * autofocus - The bool that controls if this picker should grab window focus. + */ +/proc/tgui_input_color(mob/user, message, title, default = "#000000", timeout = 0, autofocus = TRUE, ui_state = GLOB.always_state) + if(!user) + user = usr + if(!istype(user)) + if(!isclient(user)) + CRASH("We passed something that wasn't a user/client in a TGUI Input Color! The passed thing was [user]!") + var/client/client = user + user = client.mob + + if(isnull(user.client)) + return + + // Client does NOT have tgui_input on: Returns regular input + if(user.client?.prefs?.toggles2 & PREFTOGGLE_2_DISABLE_TGUI_INPUT) + return input(user, message, title, default) as color|null + + var/datum/tgui_input_color/picker = new(user, message, title, default, timeout, autofocus, ui_state) + picker.ui_interact(user) + picker.wait() + if(picker) + . = picker.choice + qdel(picker) + +/** + * tgui_input_color + * + * Datum used for instantiating and using a TGUI-controlled color picker. + */ +/datum/tgui_input_color + /// The title of the TGUI window + var/title + /// The message to show the user + var/message + /// The default choice, used if there is an existing value + var/default + /// The color the user selected, null if no selection has been made + var/choice + /// The time at which the tgui_input_color was created, for displaying timeout progress. + var/start_time + /// The lifespan of the tgui_input_color, after which the window will close and delete itself. + var/timeout + /// The bool that controls if this modal should grab window focus + var/autofocus + /// Boolean field describing if the tgui_input_color was closed by the user. + var/closed + /// The attached timer that handles this objects timeout deletion + var/deletion_timer + /// The TGUI UI state that will be returned in ui_state(). Default: always_state + var/datum/ui_state/state + +/datum/tgui_input_color/New(mob/user, message, title, default, timeout, autofocus, ui_state) + src.autofocus = autofocus + src.title = title + src.default = default + src.message = message + src.state = ui_state + + if(timeout) + src.timeout = timeout + start_time = world.time + deletion_timer = QDEL_IN(src, timeout) + +/datum/tgui_input_color/Destroy(force, ...) + SStgui.close_uis(src) + state = null + deltimer(deletion_timer) + return ..() + +/** + * Waits for a user's response to the tgui_input_color's prompt before returning. Returns early if + * the window was closed by the user. + */ +/datum/tgui_input_color/proc/wait() + while(!choice && !closed && !QDELETED(src)) + stoplag(1) + +/datum/tgui_input_color/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ColorPickerModal") + ui.open() + ui.set_autoupdate(timeout > 0) + +/datum/tgui_input_color/ui_close(mob/user) + closed = TRUE + +/datum/tgui_input_color/ui_state(mob/user) + return state + +/datum/tgui_input_color/ui_static_data(mob/user) + var/list/data = list() + data["autofocus"] = autofocus + data["large_buttons"] = !user.client?.prefs || (user.client.prefs.toggles2 & PREFTOGGLE_2_LARGE_INPUT_BUTTONS) + data["swapped_buttons"] = !user.client?.prefs || (user.client.prefs.toggles2 & PREFTOGGLE_2_SWAP_INPUT_BUTTONS) + data["title"] = title + data["default_color"] = default + data["message"] = message + return data + +/datum/tgui_input_color/ui_data(mob/user) + var/list/data = list() + if(timeout) + data["timeout"] = CLAMP01((timeout - (world.time - start_time) - 1 SECONDS) / (timeout - 1 SECONDS)) + return data + +/datum/tgui_input_color/ui_act(action, list/params) + . = ..() + if(.) + return + + switch(action) + if("submit") + if(!findtext(params["entry"], GLOB.is_color)) + return + choice = params["entry"] + closed = TRUE + SStgui.close_uis(src) + return TRUE + if("cancel") + closed = TRUE + SStgui.close_uis(src) + return TRUE diff --git a/paradise.dme b/paradise.dme index 3a819c87632..9659e3d1ec2 100644 --- a/paradise.dme +++ b/paradise.dme @@ -1758,6 +1758,7 @@ #include "code\modules\asset_cache\assets\asset_cloning.dm" #include "code\modules\asset_cache\assets\asset_common.dm" #include "code\modules\asset_cache\assets\asset_emoji.dm" +#include "code\modules\asset_cache\assets\asset_icon_ref_map.dm" #include "code\modules\asset_cache\assets\asset_id_card.dm" #include "code\modules\asset_cache\assets\asset_jquery.dm" #include "code\modules\asset_cache\assets\asset_lobby.dm" @@ -1899,6 +1900,7 @@ #include "code\modules\client\preference\loadout\loadout_racial.dm" #include "code\modules\client\preference\loadout\loadout_shoes.dm" #include "code\modules\client\preference\loadout\loadout_suit.dm" +#include "code\modules\client\preference\loadout\loadout_tgui.dm" #include "code\modules\client\preference\loadout\loadout_uniform.dm" #include "code\modules\clothing\clothing.dm" #include "code\modules\clothing\chameleon\_chameleon_actions.dm" @@ -3169,6 +3171,7 @@ #include "code\modules\tgui\states\strippable_state.dm" #include "code\modules\tgui\states\zlevel.dm" #include "code\modules\tgui\tgui_input\alert_input.dm" +#include "code\modules\tgui\tgui_input\color_input.dm" #include "code\modules\tgui\tgui_input\keycombo_input.dm" #include "code\modules\tgui\tgui_input\list_input.dm" #include "code\modules\tgui\tgui_input\number_input.dm" diff --git a/tgui/docs/component-reference.md b/tgui/docs/component-reference.md index af744b61793..c02ec546d68 100644 --- a/tgui/docs/component-reference.md +++ b/tgui/docs/component-reference.md @@ -31,6 +31,7 @@ Make sure to add new items to this list if you document new components. - [`Icon.Stack`](#iconstack) - [`ImageButton`](#imagebutton) - [`ImageButton.Item`](#imagebuttonitem) + - [`ImageButtonTS`](#imagebuttonts) - [`Input`](#input) - [`Knob`](#knob) - [`LabeledControls`](#labeledcontrols) @@ -70,16 +71,16 @@ Event handlers are callbacks that you can attack to various element to listen for browser events. Inferno supports camelcase (`onClick`) and lowercase (`onclick`) event names. -- Camel case names are what's called *synthetic* events, and are the -**preferred way** of handling events in React, for efficiency and -performance reasons. Please read -[Inferno Event Handling](https://infernojs.org/docs/guides/event-handling) -to understand what this is about. +- Camel case names are what's called _synthetic_ events, and are the + **preferred way** of handling events in React, for efficiency and + performance reasons. Please read + [Inferno Event Handling](https://infernojs.org/docs/guides/event-handling) + to understand what this is about. - Lower case names are native browser events and should be used sparingly, -for example when you need an explicit IE8 support. **DO NOT** use -lowercase event handlers unless you really know what you are doing. + for example when you need an explicit IE8 support. **DO NOT** use + lowercase event handlers unless you really know what you are doing. - [Button](#button) component does not support the lowercase `onclick` event. -Use the camel case `onClick` instead. + Use the camel case `onClick` instead. ## `tgui/components` @@ -91,13 +92,13 @@ This component provides animations for numeric values. - `value: number` - Value to animate. - `initial: number` - Initial value to use in animation when element -first appears. If you set initial to `0` for example, number will always -animate starting from `0`, and if omitted, it will not play an initial -animation. + first appears. If you set initial to `0` for example, number will always + animate starting from `0`, and if omitted, it will not play an initial + animation. - `format: value => value` - Output formatter. - Example: `value => Math.round(value)`. - `children: (formattedValue, rawValue) => any` - Pull the animated number to -animate more complex things deeper in the DOM tree. + animate more complex things deeper in the DOM tree. - Example: `(_, value) => ` ### `BlockQuote` @@ -133,9 +134,7 @@ To workaround this problem, the Box children accept a render props function. This way, `Button` can pull out the `className` generated by the `Box`. ```jsx - - {props => + ``` @@ -407,17 +402,17 @@ effectively places the last flex item to the very end of the flex container. - See inherited props: [Box](#box) - ~~`spacing: number`~~ - **Removed in tgui 4.3**, -use [Stack](#stack) instead. + use [Stack](#stack) instead. - `inline: boolean` - Makes flexbox container inline, with similar behavior -to an `inline` property on a `Box`. + to an `inline` property on a `Box`. - `direction: string` - This establishes the main-axis, thus defining the -direction flex items are placed in the flex container. + direction flex items are placed in the flex container. - `row` (default) - left to right. - `row-reverse` - right to left. - `column` - top to bottom. - `column-reverse` - bottom to top. - `wrap: string` - By default, flex items will all try to fit onto one line. -You can change that and allow the items to wrap as needed with this property. + You can change that and allow the items to wrap as needed with this property. - `nowrap` (default) - all flex items will be on one line - `wrap` - flex items will wrap onto multiple lines, from top to bottom. - `wrap-reverse` - flex items will wrap onto multiple lines from bottom to top. @@ -428,22 +423,22 @@ You can change that and allow the items to wrap as needed with this property. - `center` - items are centered on the cross axis. - `baseline` - items are aligned such as their baselines align. - `justify: string` - This defines the alignment along the main axis. -It helps distribute extra free space leftover when either all the flex -items on a line are inflexible, or are flexible but have reached their -maximum size. It also exerts some control over the alignment of items -when they overflow the line. + It helps distribute extra free space leftover when either all the flex + items on a line are inflexible, or are flexible but have reached their + maximum size. It also exerts some control over the alignment of items + when they overflow the line. - `flex-start` (default) - items are packed toward the start of the - flex-direction. + flex-direction. - `flex-end` - items are packed toward the end of the flex-direction. - `space-between` - items are evenly distributed in the line; first item is - on the start line, last item on the end line + on the start line, last item on the end line - `space-around` - items are evenly distributed in the line with equal space - around them. Note that visually the spaces aren't equal, since all the items - have equal space on both sides. The first item will have one unit of space - against the container edge, but two units of space between the next item - because that next item has its own spacing that applies. + around them. Note that visually the spaces aren't equal, since all the items + have equal space on both sides. The first item will have one unit of space + against the container edge, but two units of space between the next item + because that next item has its own spacing that applies. - `space-evenly` - items are distributed so that the spacing between any two - items (and the space to the edges) is equal. + items (and the space to the edges) is equal. - TBD (not all properties are supported in IE11). ### `Flex.Item` @@ -452,24 +447,24 @@ when they overflow the line. - See inherited props: [Box](#box) - `order: number` - By default, flex items are laid out in the source order. -However, the order property controls the order in which they appear in the -flex container. + However, the order property controls the order in which they appear in the + flex container. - `grow: number | boolean` - This defines the ability for a flex item to grow -if necessary. It accepts a unitless value that serves as a proportion. It -dictates what amount of the available space inside the flex container the -item should take up. This number is unit-less and is relative to other -siblings. + if necessary. It accepts a unitless value that serves as a proportion. It + dictates what amount of the available space inside the flex container the + item should take up. This number is unit-less and is relative to other + siblings. - `shrink: number | boolean` - This defines the ability for a flex item to -shrink if necessary. Inverse of `grow`. + shrink if necessary. Inverse of `grow`. - `basis: number | string` - This defines the default size of an element -before any flex-related calculations are done. Has to be a length -(e.g. `20%`, `5rem`), an `auto` or `content` keyword. + before any flex-related calculations are done. Has to be a length + (e.g. `20%`, `5rem`), an `auto` or `content` keyword. - **Important:** IE11 flex is buggy, and auto width/height calculations - can sometimes end up in a circular dependency. This usually happens, when - working with tables inside flex (they have wacky internal widths and such). - Setting basis to `0` breaks the loop and fixes all of the problems. + can sometimes end up in a circular dependency. This usually happens, when + working with tables inside flex (they have wacky internal widths and such). + Setting basis to `0` breaks the loop and fixes all of the problems. - `align: string` - This allows the default alignment (or the one specified by -align-items) to be overridden for individual flex items. See: [Flex](#flex). + align-items) to be overridden for individual flex items. See: [Flex](#flex). ### `Grid` @@ -485,14 +480,10 @@ Example: ```jsx -
- Hello world! -
+
Hello world!
-
- Hello world! -
+
Hello world!
``` @@ -518,6 +509,7 @@ Renders one of the FontAwesome icons of your choice. To smoothen the transition from v4 to v5, we have added a v4 semantic to transform names with `-o` suffixes to FA Regular icons. For example: + - `square` will get transformed to `fas square` - `square-o` will get transformed to `far square` @@ -526,10 +518,10 @@ transform names with `-o` suffixes to FA Regular icons. For example: - See inherited props: [Box](#box) - `name: string` - Icon name. - `size: number` - Icon size. `1` is normal size, `2` is two times bigger. -Fractional numbers are supported. + Fractional numbers are supported. - `rotation: number` - Icon rotation, in degrees. - `spin: boolean` - Whether an icon should be spinning. Good for load -indicators. + indicators. ### `Icon.Stack` @@ -558,26 +550,26 @@ Has support for base64, spritesheets and URLs. - `asset: boolean` - Enables spritesheets support. - `vertical: boolean` - Makes the button a inlined vertical rectangle. - `color: string` - By default, the button is semi-transparent. You can change the overall colour, -all colours are available in KitchenSink in the corresponding section. + all colours are available in KitchenSink in the corresponding section. - `title: string` - The top text, it will always be bold, and also adds a divider between title and content. -Disabled if there is no content. + Disabled if there is no content. - `content: string|any` - All main content, usually text, but you can put in other components if you like. -Makes the vertical button square if empty. + Makes the vertical button square if empty. - `selected: boolean` - Makes button selected (green) if true. - `disabled: boolean` - Makes button disabled (red) if true. Also disables onClick. - `disabledContent: string` - If button disabled and disabledContent filled, it will be used instead content. - `image: string` - Base64 image, simple. Disabled if asset support enabled. - `imageUrl: string` - PNG image or other asset. Make sure you use existing simple asset! Example: imageUrl={'image.png'} - `imageAsset: string` - If you have enabled asset support, write here which spritesheet to use. -Example: imageAsset={'spritesheet_name64x64'} + Example: imageAsset={'spritesheet_name64x64'} - `imageSize: string` - Sets the size of the image and adjusts the size of the button itself accordingly. -Example: imageSize={'64px'} + Example: imageSize={'64px'} - `tooltip: string` - A fancy, boxy tooltip, which appears when hovering -over the button. + over the button. - `tooltipPosition: string` - Position of the tooltip. See [`Popper`](#Popper) for valid options. - `ellipsis: boolean` - If button width is constrained, button text will -be truncated with an ellipsis. Be careful however, because this prop breaks -the baseline alignment. + be truncated with an ellipsis. Be careful however, because this prop breaks + the baseline alignment. - `children: ImageButton.Item|any` - Items that are added to the right of the horizontal button. - `onClick: function` - Called when element is clicked. Also enables hover effects. @@ -589,25 +581,60 @@ Additional button/s for ImageButton. > Available only in horizontal mode, if you try add it to vertical, you're gonna be disappointed **Props:** + - See inherited props: [Box](#box) - `color: string` - By default, the button is semi-transparent. You can change the overall colour, -all colours are available in KitchenSink in the corresponding section. + all colours are available in KitchenSink in the corresponding section. - `content: string|any` - All main content, usually text, but you can put in other components if you like. -Try to not make it too long. + Try to not make it too long. - `selected: boolean` - Makes button selected (green) if true. - `disabled: boolean` - Makes button disabled (red) if true. Also disables onClick. - `disabledContent: string` - If button disabled and disabledContent filled, it will be used instead content. - `tooltip: string` - A fancy, boxy tooltip, which appears when hovering -over the button. + over the button. - `tooltipPosition: string` - Position of the tooltip. See [`Popper`](#Popper) for valid options. - `icon: string` - Adds an icon to the button. By default it will be under content. - `iconColor: string` - Paints icon if it used. - `iconPosition: string` - You can make an icon above the content. -Example: iconPosition={'top'} + Example: iconPosition={'top'} - `iconSize: number` - Adjusts the size of the icon. - `children: any` - Similar to content. - `onClick: function` - Called when element is clicked. +### `ImageButtonTS` + +A Robust button is specifically for sticking a picture in it. + +**Props:** + +- See inherited props: [Box](#box) +- `asset: string[]` - Asset cache. Example: `asset={`assetname32x32, ${thing.key}`}` +- `base64: string` - Classic way to put images. Example: `base64={thing.image}` +- `buttons: any` - Special section for any component, or, content. + Quite a small area at the bottom of the image in non-fluid mode. + Has a style overrides, best to use [Button](#button) inside. +- `buttonsAlt: boolean` - Enables alternative buttons layout. + With fluid, makes buttons like a humburger. + Without, moves it to top, and disables pointer-events. +- `children: any` - Content under image. +- `className: string` - Applies a CSS class to the element. +- `color: string` - Color of the button, but without `transparent`; see [Button](#button) +- `disabled: boolean` - Makes button disabled and dark red if true. + Also disables onClick & onRightClick. +- `selected: boolean` - Makes button selected and green if true. +- `dmFallback: any` - Optional. Adds a "stub" when loading DmIcon. +- `dmIcon: string` - Parameter `icon` of component `DmIcon`. +- `dmIconState: string` - Parameter `icon_state` of component `DmIcon`. + For proper work of `DmIcon` it is necessary that both parameters are filled in! +- `fluid: boolean` - Changes the layout of the button, making it fill the entire horizontally available space. + Allows the use of `title` +- `imageSize: number` - Parameter responsible for the size of the image, component and standard "stubs". + Measured in pixels. `imageSize={64}` = 64px. +- `imageSrc: string` - Prop `src` of . Example: `imageSrc={resolveAsset(thing.image)}` +- `onClick: (e) => void` - Called when button is clicked with LMB. +- `onRightClick: (e) => void` - Called when button is clicked with RMB. +- `title: string` - Requires `fluid` for work. Bold text with divider betwen content. + ### `Input` A basic text input, which allow users to enter text into a UI. @@ -620,12 +647,12 @@ A basic text input, which allow users to enter text into a UI. - See inherited props: [Box](#box) - `value: string` - Value of an input. - `placeholder: string` - Text placed into Input box when it's empty, -otherwise nothing. Clears automatically when focused. + otherwise nothing. Clears automatically when focused. - `fluid: boolean` - Fill all available horizontal space. - `selfClear: boolean` - Clear after hitting enter, as well as remain focused -when this happens. Useful for things like chat inputs. + when this happens. Useful for things like chat inputs. - `onChange: (e, value) => void` - An event, which fires when you commit -the text by either unfocusing the input box, or by pressing the Enter key. + the text by either unfocusing the input box, or by pressing the Enter key. - `onInput: (e, value) => void` - An event, which fires on every keypress. ### `Knob` @@ -641,30 +668,30 @@ Single click opens an input box to manually type in a number. - `animated: boolean` - Animates the value if it was changed externally. - `bipolar: boolean` - Knob can be bipolar or unipolar. - `size: number` - Relative size of the knob. `1` is normal size, `2` is two -times bigger. Fractional numbers are supported. + times bigger. Fractional numbers are supported. - `color: string` - Color of the outer ring around the knob. - `value: number` - Value itself, controls the position of the cursor. - `unit: string` - Unit to display to the right of value. - `minValue: number` - Lowest possible value. - `maxValue: number` - Highest possible value. - `fillValue: number` - If set, this value will be used to set the fill -percentage of the outer ring independently of the main value. + percentage of the outer ring independently of the main value. - `ranges: { color: [from, to] }` - Applies a `color` to the outer ring around -the knob based on whether the value lands in the range between `from` and `to`. -See an example of this prop in [ProgressBar](#progressbar). + the knob based on whether the value lands in the range between `from` and `to`. + See an example of this prop in [ProgressBar](#progressbar). - `step: number` (default: 1) - Adjust value by this amount when -dragging the input. + dragging the input. - `stepPixelSize: number` (default: 1) - Screen distance mouse needs -to travel to adjust value by one `step`. + to travel to adjust value by one `step`. - `format: value => value` - Format value using this function before -displaying it. + displaying it. - `suppressFlicker: number` - A number in milliseconds, for which the input -will hold off from updating while events propagate through the backend. -Default is about 250ms, increase it if you still see flickering. + will hold off from updating while events propagate through the backend. + Default is about 250ms, increase it if you still see flickering. - `onChange: (e, value) => void` - An event, which fires when you release -the input, or successfully enter a number. + the input, or successfully enter a number. - `onDrag: (e, value) => void` - An event, which fires about every 500ms -when you drag the input up and down, on release and on manual editing. + when you drag the input up and down, on release and on manual editing. ### `Popper` @@ -702,9 +729,7 @@ column is labels, and second column is content. ```jsx - - Content - + Content ``` @@ -713,13 +738,7 @@ to perform some sort of action), there is a way to do that: ```jsx - - Click me! - - )}> + Click me!}> Content @@ -746,9 +765,7 @@ Example: ```jsx - - Content - + Content ``` @@ -794,22 +811,22 @@ to fine tune the value, or single click it to manually type a number. - `minValue: number` - Lowest possible value. - `maxValue: number` - Highest possible value. - `step: number` (default: 1) - Adjust value by this amount when -dragging the input. + dragging the input. - `stepPixelSize: number` (default: 1) - Screen distance mouse needs -to travel to adjust value by one `step`. + to travel to adjust value by one `step`. - `width: string|number` - Width of the element, in `Box` units or pixels. - `height: string|numer` - Height of the element, in `Box` units or pixels. - `lineHeight: string|number` - lineHeight of the element, in `Box` units or pixels. - `fontSize: string|number` - fontSize of the element, in `Box` units or pixels. - `format: value => value` - Format value using this function before -displaying it. + displaying it. - `suppressFlicker: number` - A number in milliseconds, for which the input -will hold off from updating while events propagate through the backend. -Default is about 250ms, increase it if you still see flickering. + will hold off from updating while events propagate through the backend. + Default is about 250ms, increase it if you still see flickering. - `onChange: (e, value) => void` - An event, which fires when you release -the input, or successfully enter a number. + the input, or successfully enter a number. - `onDrag: (e, value) => void` - An event, which fires about every 500ms -when you drag the input up and down, on release and on manual editing. + when you drag the input up and down, on release and on manual editing. ### `ProgressBar` @@ -828,18 +845,19 @@ Usage of `ranges` prop: average: [0.25, 0.5], bad: [-Infinity, 0.25], }} - value={0.6} /> + value={0.6} +/> ``` **Props:** - `value: number` - Current progress as a floating point number between -`minValue` (default: 0) and `maxValue` (default: 1). Determines the -percentage and how filled the bar is. + `minValue` (default: 0) and `maxValue` (default: 1). Determines the + percentage and how filled the bar is. - `minValue: number` - Lowest possible value. - `maxValue: number` - Highest possible value. - `ranges: { color: [from, to] }` - Applies a `color` to the progress bar -based on whether the value lands in the range between `from` and `to`. + based on whether the value lands in the range between `from` and `to`. - `color: string` - Color of the progress bar. - `children: any` - Content to render inside the progress bar. @@ -853,13 +871,14 @@ The RoundGauge component provides a visual representation of a single metric, as value={tankPressure} minValue={0} maxValue={pressureLimit} - alertAfter={pressureLimit * 0.70} + alertAfter={pressureLimit * 0.7} ranges={{ - "good": [0, pressureLimit * 0.70], - "average": [pressureLimit * 0.70, pressureLimit * 0.85], - "bad": [pressureLimit * 0.85, pressureLimit], + 'good': [0, pressureLimit * 0.7], + 'average': [pressureLimit * 0.7, pressureLimit * 0.85], + 'bad': [pressureLimit * 0.85, pressureLimit], }} - format={formatPressure} /> + format={formatPressure} +/> ``` The alert on the gauge is optional, and will only be shown if the `alertAfter` prop is defined. When defined, the alert will begin to flash the respective color upon which the needle currently rests, as defined in the `ranges` prop. @@ -886,22 +905,14 @@ clearly indicates hierarchy. Section can also be titled to clearly define its purpose. ```jsx -
- Here you can order supply crates. -
+
Here you can order supply crates.
``` If you want to have a button on the right side of an section title (for example, to perform some sort of action), there is a way to do that: ```jsx -
- Send shuttle - - )}> +
Send shuttle}> Here you can order supply crates.
``` @@ -909,7 +920,7 @@ If you want to have a button on the right side of an section title - See inherited props: [Box](#box) - `title: string` - Title of the section. - `level: number` - Section level in hierarchy. Default is 1, higher number -means deeper level of nesting. Must be an integer number. + means deeper level of nesting. Must be an integer number. - `buttons: any` - Buttons to render aside the section title. - `fill: boolean` - If true, fills all available vertical space. - `fitted: boolean` - If true, removes all section padding. @@ -933,23 +944,23 @@ Single click opens an input box to manually type in a number. - `minValue: number` - Lowest possible value. - `maxValue: number` - Highest possible value. - `fillValue: number` - If set, this value will be used to set the fill -percentage of the progress bar filler independently of the main value. + percentage of the progress bar filler independently of the main value. - `ranges: { color: [from, to] }` - Applies a `color` to the slider -based on whether the value lands in the range between `from` and `to`. -See an example of this prop in [ProgressBar](#progressbar). + based on whether the value lands in the range between `from` and `to`. + See an example of this prop in [ProgressBar](#progressbar). - `step: number` (default: 1) - Adjust value by this amount when -dragging the input. + dragging the input. - `stepPixelSize: number` (default: 1) - Screen distance mouse needs -to travel to adjust value by one `step`. + to travel to adjust value by one `step`. - `format: value => value` - Format value using this function before -displaying it. + displaying it. - `suppressFlicker: number` - A number in milliseconds, for which the input -will hold off from updating while events propagate through the backend. -Default is about 250ms, increase it if you still see flickering. + will hold off from updating while events propagate through the backend. + Default is about 250ms, increase it if you still see flickering. - `onChange: (e, value) => void` - An event, which fires when you release -the input, or successfully enter a number. + the input, or successfully enter a number. - `onDrag: (e, value) => void` - An event, which fires about every 500ms -when you drag the input up and down, on release and on manual editing. + when you drag the input up and down, on release and on manual editing. ### `Stack` @@ -965,13 +976,9 @@ Stacks can be vertical by adding a `vertical` property. ```jsx - - Button description - + Button description - + ``` @@ -986,9 +993,7 @@ Make sure to use the `fill` property. -
- Sidebar -
+
Sidebar
@@ -998,9 +1003,7 @@ Make sure to use the `fill` property.
-
- Bottom pane -
+
Bottom pane
@@ -1032,9 +1035,7 @@ Example: ```jsx - - Hello world! - + Hello world! Label @@ -1063,7 +1064,7 @@ A straight forward mapping to `
` element. - See inherited props: [Box](#box) - `collapsing: boolean` - Collapses table cell to the smallest possible size, -and stops any text inside from wrapping. + and stops any text inside from wrapping. ### `Tabs` @@ -1099,9 +1100,7 @@ Tabs also support a vertical configuration. This is usually paired with a ```jsx - - ... - + ... Tab content. @@ -1113,7 +1112,7 @@ Tabs also support a vertical configuration. This is usually paired with a - See inherited props: [Box](#box) - `vertical: boolean` - Use a vertical configuration, where tabs will be -stacked vertically. + stacked vertically. - `children: Tab[]` - This component only accepts tabs as its children. ### `Tabs.Tab` @@ -1125,8 +1124,8 @@ a lot of `Button` props. - See inherited props: [Button](#button) - `altSelection` - Whether the tab buttons select via standard select (color -change) or by adding a white indicator to the selected tab. -Intended for usage on interfaces where tab color has relevance. + change) or by adding a white indicator to the selected tab. + Intended for usage on interfaces where tab color has relevance. - `icon: string` - Tab icon. - `children: any` - Tab text. - `onClick: function` - Called when element is clicked. @@ -1143,9 +1142,7 @@ Usage: ```jsx - - Sample text. - + Sample text. ``` @@ -1153,7 +1150,7 @@ Usage: - `position?: string` - Tooltip position. See [`Popper`](#Popper) for valid options. Defaults to "auto". - `content: string` - Content of the tooltip. Must be a plain string. -Fragments or other elements are **not** supported. + Fragments or other elements are **not** supported. ## `tgui/layouts` @@ -1167,9 +1164,7 @@ Example: ```jsx - - Hello, world! - + Hello, world! ``` @@ -1184,9 +1179,9 @@ Example: - `height: number` - Window height. - `noClose: boolean` - Controls the ability to close the window. - `children: any` - Child elements, which are rendered directly inside the -window. If you use a [Dimmer](#dimmer) or [Modal](#modal) in your UI, -they should be put as direct childs of a Window, otherwise you should be -putting your content into [Window.Content](#windowcontent). + window. If you use a [Dimmer](#dimmer) or [Modal](#modal) in your UI, + they should be put as direct childs of a Window, otherwise you should be + putting your content into [Window.Content](#windowcontent). ### `Window.Content` diff --git a/tgui/packages/common/color.js b/tgui/packages/common/color.js deleted file mode 100644 index 913f50747af..00000000000 --- a/tgui/packages/common/color.js +++ /dev/null @@ -1,62 +0,0 @@ -/** - * @file - * @copyright 2020 Aleksej Komarov - * @license MIT - */ - -const EPSILON = 0.0001; - -export class Color { - constructor(r = 0, g = 0, b = 0, a = 1) { - this.r = r; - this.g = g; - this.b = b; - this.a = a; - } - - toString() { - return `rgba(${this.r | 0}, ${this.g | 0}, ${this.b | 0}, ${this.a | 0})`; - } -} - -/** - * Creates a color from the CSS hex color notation. - */ -Color.fromHex = (hex) => - new Color( - parseInt(hex.substr(1, 2), 16), - parseInt(hex.substr(3, 2), 16), - parseInt(hex.substr(5, 2), 16) - ); - -/** - * Linear interpolation of two colors. - */ -Color.lerp = (c1, c2, n) => - new Color( - (c2.r - c1.r) * n + c1.r, - (c2.g - c1.g) * n + c1.g, - (c2.b - c1.b) * n + c1.b, - (c2.a - c1.a) * n + c1.a - ); - -/** - * Loops up the color in the provided list of colors - * with linear interpolation. - */ -Color.lookup = (value, colors = []) => { - const len = colors.length; - if (len < 2) { - throw new Error('Needs at least two colors!'); - } - const scaled = value * (len - 1); - if (value < EPSILON) { - return colors[0]; - } - if (value >= 1 - EPSILON) { - return colors[len - 1]; - } - const ratio = scaled % 1; - const index = scaled | 0; - return Color.lerp(colors[index], colors[index + 1], ratio); -}; diff --git a/tgui/packages/common/color.ts b/tgui/packages/common/color.ts new file mode 100644 index 00000000000..9022cccfdc2 --- /dev/null +++ b/tgui/packages/common/color.ts @@ -0,0 +1,359 @@ +/** + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + +const EPSILON = 0.0001; + +export class Color { + r: number; + g: number; + b: number; + a: number; + + constructor(r = 0, g = 0, b = 0, a = 1) { + this.r = r; + this.g = g; + this.b = b; + this.a = a; + } + + toString() { + return `rgba(${this.r | 0}, ${this.g | 0}, ${this.b | 0}, ${this.a | 0})`; + } + + /** + * Creates a color from the CSS hex color notation. + */ + static fromHex(hex: string): Color { + return new Color( + parseInt(hex.substr(1, 2), 16), + parseInt(hex.substr(3, 2), 16), + parseInt(hex.substr(5, 2), 16) + ); + } + + /** + * Linear interpolation of two colors. + */ + static lerp(c1: Color, c2: Color, n: number): Color { + return new Color( + (c2.r - c1.r) * n + c1.r, + (c2.g - c1.g) * n + c1.g, + (c2.b - c1.b) * n + c1.b, + (c2.a - c1.a) * n + c1.a + ); + } + + /** + * Loops up the color in the provided list of colors + * with linear interpolation. + */ + static lookup(value: number, colors: Color[] = []): Color { + const len = colors.length; + if (len < 2) { + throw new Error('Needs at least two colors!'); + } + const scaled = value * (len - 1); + if (value < EPSILON) { + return colors[0]; + } + if (value >= 1 - EPSILON) { + return colors[len - 1]; + } + const ratio = scaled % 1; + const index = scaled | 0; + return Color.lerp(colors[index], colors[index + 1], ratio); + } +} + +/* + * MIT License + * https://github.com/omgovich/react-colorful/ + * + * Copyright (c) 2020 Vlad Shilov + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +const round = ( + number: number, + digits = 0, + base = Math.pow(10, digits) +): number => { + return Math.round(base * number) / base; +}; + +export interface RgbColor { + r: number; + g: number; + b: number; +} + +export interface RgbaColor extends RgbColor { + a: number; +} + +export interface HslColor { + h: number; + s: number; + l: number; +} + +export interface HslaColor extends HslColor { + a: number; +} + +export interface HsvColor { + h: number; + s: number; + v: number; +} + +export interface HsvaColor extends HsvColor { + a: number; +} + +export type ObjectColor = + | RgbColor + | HslColor + | HsvColor + | RgbaColor + | HslaColor + | HsvaColor; + +export type AnyColor = string | ObjectColor; + +/** + * Valid CSS units. + * https://developer.mozilla.org/en-US/docs/Web/CSS/angle + */ +const angleUnits: Record = { + grad: 360 / 400, + turn: 360, + rad: 360 / (Math.PI * 2), +}; + +export const hexToHsva = (hex: string): HsvaColor => rgbaToHsva(hexToRgba(hex)); + +export const hexToRgba = (hex: string): RgbaColor => { + if (hex[0] === '#') hex = hex.substring(1); + + if (hex.length < 6) { + return { + r: parseInt(hex[0] + hex[0], 16), + g: parseInt(hex[1] + hex[1], 16), + b: parseInt(hex[2] + hex[2], 16), + a: hex.length === 4 ? round(parseInt(hex[3] + hex[3], 16) / 255, 2) : 1, + }; + } + + return { + r: parseInt(hex.substring(0, 2), 16), + g: parseInt(hex.substring(2, 4), 16), + b: parseInt(hex.substring(4, 6), 16), + a: hex.length === 8 ? round(parseInt(hex.substring(6, 8), 16) / 255, 2) : 1, + }; +}; + +export const parseHue = (value: string, unit = 'deg'): number => { + return Number(value) * (angleUnits[unit] || 1); +}; + +export const hslaStringToHsva = (hslString: string): HsvaColor => { + const matcher = + /hsla?\(?\s*(-?\d*\.?\d+)(deg|rad|grad|turn)?[,\s]+(-?\d*\.?\d+)%?[,\s]+(-?\d*\.?\d+)%?,?\s*[/\s]*(-?\d*\.?\d+)?(%)?\s*\)?/i; + const match = matcher.exec(hslString); + + if (!match) return { h: 0, s: 0, v: 0, a: 1 }; + + return hslaToHsva({ + h: parseHue(match[1], match[2]), + s: Number(match[3]), + l: Number(match[4]), + a: match[5] === undefined ? 1 : Number(match[5]) / (match[6] ? 100 : 1), + }); +}; + +export const hslStringToHsva = hslaStringToHsva; + +export const hslaToHsva = ({ h, s, l, a }: HslaColor): HsvaColor => { + s *= (l < 50 ? l : 100 - l) / 100; + + return { + h: h, + s: s > 0 ? ((2 * s) / (l + s)) * 100 : 0, + v: l + s, + a, + }; +}; + +export const hsvaToHex = (hsva: HsvaColor): string => + rgbaToHex(hsvaToRgba(hsva)); + +export const hsvaToHsla = ({ h, s, v, a }: HsvaColor): HslaColor => { + const hh = ((200 - s) * v) / 100; + + return { + h: round(h), + s: round( + hh > 0 && hh < 200 + ? ((s * v) / 100 / (hh <= 100 ? hh : 200 - hh)) * 100 + : 0 + ), + l: round(hh / 2), + a: round(a, 2), + }; +}; + +export const hsvaToHslString = (hsva: HsvaColor): string => { + const { h, s, l } = hsvaToHsla(hsva); + return `hsl(${h}, ${s}%, ${l}%)`; +}; + +export const hsvaToHsvString = (hsva: HsvaColor): string => { + const { h, s, v } = roundHsva(hsva); + return `hsv(${h}, ${s}%, ${v}%)`; +}; + +export const hsvaToHsvaString = (hsva: HsvaColor): string => { + const { h, s, v, a } = roundHsva(hsva); + return `hsva(${h}, ${s}%, ${v}%, ${a})`; +}; + +export const hsvaToHslaString = (hsva: HsvaColor): string => { + const { h, s, l, a } = hsvaToHsla(hsva); + return `hsla(${h}, ${s}%, ${l}%, ${a})`; +}; + +export const hsvaToRgba = ({ h, s, v, a }: HsvaColor): RgbaColor => { + h = (h / 360) * 6; + s = s / 100; + v = v / 100; + + const hh = Math.floor(h), + b = v * (1 - s), + c = v * (1 - (h - hh) * s), + d = v * (1 - (1 - h + hh) * s), + module = hh % 6; + + return { + r: [v, c, b, b, d, v][module] * 255, + g: [d, v, v, c, b, b][module] * 255, + b: [b, b, d, v, v, c][module] * 255, + a: round(a, 2), + }; +}; + +export const hsvaToRgbString = (hsva: HsvaColor): string => { + const { r, g, b } = hsvaToRgba(hsva); + return `rgb(${round(r)}, ${round(g)}, ${round(b)})`; +}; + +export const hsvaToRgbaString = (hsva: HsvaColor): string => { + const { r, g, b, a } = hsvaToRgba(hsva); + return `rgba(${round(r)}, ${round(g)}, ${round(b)}, ${round(a, 2)})`; +}; + +export const hsvaStringToHsva = (hsvString: string): HsvaColor => { + const matcher = + /hsva?\(?\s*(-?\d*\.?\d+)(deg|rad|grad|turn)?[,\s]+(-?\d*\.?\d+)%?[,\s]+(-?\d*\.?\d+)%?,?\s*[/\s]*(-?\d*\.?\d+)?(%)?\s*\)?/i; + const match = matcher.exec(hsvString); + + if (!match) return { h: 0, s: 0, v: 0, a: 1 }; + + return roundHsva({ + h: parseHue(match[1], match[2]), + s: Number(match[3]), + v: Number(match[4]), + a: match[5] === undefined ? 1 : Number(match[5]) / (match[6] ? 100 : 1), + }); +}; + +export const hsvStringToHsva = hsvaStringToHsva; + +export const rgbaStringToHsva = (rgbaString: string): HsvaColor => { + const matcher = + /rgba?\(?\s*(-?\d*\.?\d+)(%)?[,\s]+(-?\d*\.?\d+)(%)?[,\s]+(-?\d*\.?\d+)(%)?,?\s*[/\s]*(-?\d*\.?\d+)?(%)?\s*\)?/i; + const match = matcher.exec(rgbaString); + + if (!match) return { h: 0, s: 0, v: 0, a: 1 }; + + return rgbaToHsva({ + r: Number(match[1]) / (match[2] ? 100 / 255 : 1), + g: Number(match[3]) / (match[4] ? 100 / 255 : 1), + b: Number(match[5]) / (match[6] ? 100 / 255 : 1), + a: match[7] === undefined ? 1 : Number(match[7]) / (match[8] ? 100 : 1), + }); +}; + +export const rgbStringToHsva = rgbaStringToHsva; + +const format = (number: number) => { + const hex = number.toString(16); + return hex.length < 2 ? '0' + hex : hex; +}; + +export const rgbaToHex = ({ r, g, b, a }: RgbaColor): string => { + const alphaHex = a < 1 ? format(round(a * 255)) : ''; + return ( + '#' + format(round(r)) + format(round(g)) + format(round(b)) + alphaHex + ); +}; + +export const rgbaToHsva = ({ r, g, b, a }: RgbaColor): HsvaColor => { + const max = Math.max(r, g, b); + const delta = max - Math.min(r, g, b); + + // prettier-ignore + const hh = delta + ? max === r + ? (g - b) / delta + : max === g + ? 2 + (b - r) / delta + : 4 + (r - g) / delta + : 0; + + return { + h: 60 * (hh < 0 ? hh + 6 : hh), + s: max ? (delta / max) * 100 : 0, + v: (max / 255) * 100, + a, + }; +}; + +export const roundHsva = (hsva: HsvaColor): HsvaColor => ({ + h: round(hsva.h), + s: round(hsva.s), + v: round(hsva.v), + a: round(hsva.a, 2), +}); + +export const rgbaToRgb = ({ r, g, b }: RgbaColor): RgbColor => ({ r, g, b }); + +export const hslaToHsl = ({ h, s, l }: HslaColor): HslColor => ({ h, s, l }); + +export const hsvaToHsv = (hsva: HsvaColor): HsvColor => { + const { h, s, v } = roundHsva(hsva); + return { h, s, v }; +}; + +const hexMatcher = /^#?([0-9A-F]{3,8})$/i; + +export const validHex = (value: string, alpha?: boolean): boolean => { + const match = hexMatcher.exec(value); + const length = match ? match[1].length : 0; + + return ( + length === 3 || // '#rgb' format + length === 6 || // '#rrggbb' format + (!!alpha && length === 4) || // '#rgba' format + (!!alpha && length === 8) // '#rrggbbaa' format + ); +}; diff --git a/tgui/packages/tgui/components/Interactive.tsx b/tgui/packages/tgui/components/Interactive.tsx new file mode 100644 index 00000000000..f0a0dfe1389 --- /dev/null +++ b/tgui/packages/tgui/components/Interactive.tsx @@ -0,0 +1,153 @@ +/** + * MIT License + * https://github.com/omgovich/react-colorful/ + * + * Copyright (c) 2020 Vlad Shilov + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import { clamp } from 'common/math'; +import { Component, InfernoNode, createRef, RefObject } from 'inferno'; + +export interface Interaction { + left: number; + top: number; +} + +// Finds the proper window object to fix iframe embedding issues +const getParentWindow = (node?: HTMLDivElement | null): Window => { + return (node && node.ownerDocument.defaultView) || self; +}; + +// Returns a relative position of the pointer inside the node's bounding box +const getRelativePosition = ( + node: HTMLDivElement, + event: MouseEvent +): Interaction => { + const rect = node.getBoundingClientRect(); + const pointer = event as MouseEvent; + return { + left: clamp( + (pointer.pageX - (rect.left + getParentWindow(node).pageXOffset)) / + rect.width, + 0, + 1 + ), + top: clamp( + (pointer.pageY - (rect.top + getParentWindow(node).pageYOffset)) / + rect.height, + 0, + 1 + ), + }; +}; + +export interface InteractiveProps { + onMove: (interaction: Interaction) => void; + onKey: (offset: Interaction) => void; + children: InfernoNode[]; + style?: any; +} + +export class Interactive extends Component { + containerRef: RefObject; + props: InteractiveProps; + + constructor(props: InteractiveProps) { + super(); + this.props = props; + this.containerRef = createRef(); + } + + handleMoveStart = (event: MouseEvent) => { + const el = this.containerRef?.current; + if (!el) return; + + // Prevent text selection + event.preventDefault(); + el.focus(); + this.props.onMove(getRelativePosition(el, event)); + this.toggleDocumentEvents(true); + }; + + handleMove = (event: MouseEvent) => { + // Prevent text selection + event.preventDefault(); + + // If user moves the pointer outside of the window or iframe bounds and release it there, + // `mouseup`/`touchend` won't be fired. In order to stop the picker from following the cursor + // after the user has moved the mouse/finger back to the document, we check `event.buttons` + // and `event.touches`. It allows us to detect that the user is just moving his pointer + // without pressing it down + const isDown = event.buttons > 0; + + if (isDown && this.containerRef?.current) { + this.props.onMove(getRelativePosition(this.containerRef.current, event)); + } else { + this.toggleDocumentEvents(false); + } + }; + + handleMoveEnd = () => { + this.toggleDocumentEvents(false); + }; + + handleKeyDown = (event: KeyboardEvent) => { + const keyCode = event.which || event.keyCode; + + // Ignore all keys except arrow ones + if (keyCode < 37 || keyCode > 40) return; + // Do not scroll page by arrow keys when document is focused on the element + event.preventDefault(); + // Send relative offset to the parent component. + // We use codes (37←, 38↑, 39→, 40↓) instead of keys ('ArrowRight', 'ArrowDown', etc) + // to reduce the size of the library + this.props.onKey({ + left: keyCode === 39 ? 0.05 : keyCode === 37 ? -0.05 : 0, + top: keyCode === 40 ? 0.05 : keyCode === 38 ? -0.05 : 0, + }); + }; + + toggleDocumentEvents(state?: boolean) { + const el = this.containerRef?.current; + const parentWindow = getParentWindow(el); + + // Add or remove additional pointer event listeners + const toggleEvent = state + ? parentWindow.addEventListener + : parentWindow.removeEventListener; + toggleEvent('mousemove', this.handleMove); + toggleEvent('mouseup', this.handleMoveEnd); + } + + componentDidMount() { + this.toggleDocumentEvents(true); + } + + componentWillUnmount() { + this.toggleDocumentEvents(false); + } + + render() { + return ( +
+ {this.props.children} +
+ ); + } +} diff --git a/tgui/packages/tgui/components/Pointer.tsx b/tgui/packages/tgui/components/Pointer.tsx new file mode 100644 index 00000000000..409972a7dfc --- /dev/null +++ b/tgui/packages/tgui/components/Pointer.tsx @@ -0,0 +1,46 @@ +/** + * MIT License + * https://github.com/omgovich/react-colorful/ + * + * Copyright (c) 2020 Vlad Shilov + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import { classes } from 'common/react'; +import { InfernoNode } from 'inferno'; + +interface PointerProps { + className?: string; + top?: number; + left: number; + color: string; +} + +export const Pointer = ({ + className, + color, + left, + top = 0.5, +}: PointerProps): InfernoNode => { + const nodeClassName = classes(['react-colorful__pointer', className]); + + const style = { + top: `${top * 100}%`, + left: `${left * 100}%`, + }; + + return ( +
+
+
+ ); +}; diff --git a/tgui/packages/tgui/components/index.js b/tgui/packages/tgui/components/index.js index 8876792dc99..0b254d7c48e 100644 --- a/tgui/packages/tgui/components/index.js +++ b/tgui/packages/tgui/components/index.js @@ -21,6 +21,7 @@ export { DraggableControl } from './DraggableControl'; export { Dropdown } from './Dropdown'; export { Flex } from './Flex'; export { Grid } from './Grid'; +export { Interactive } from './Interactive'; export { Icon } from './Icon'; export { ImageButton } from './ImageButton'; export { Input } from './Input'; @@ -31,6 +32,7 @@ export { Modal } from './Modal'; export { NanoMap } from './NanoMap'; export { NoticeBox } from './NoticeBox'; export { NumberInput } from './NumberInput'; +export { Pointer } from './Pointer'; export { Popper } from './Popper'; export { ProgressBar } from './ProgressBar'; export { RestrictedInput } from './RestrictedInput'; diff --git a/tgui/packages/tgui/interfaces/ColorPickerModal.tsx b/tgui/packages/tgui/interfaces/ColorPickerModal.tsx new file mode 100644 index 00000000000..3522c4442e1 --- /dev/null +++ b/tgui/packages/tgui/interfaces/ColorPickerModal.tsx @@ -0,0 +1,668 @@ +/* eslint-disable react/state-in-constructor */ +/** + * @file + * @copyright 2023 itsmeow + * @license MIT + */ + +import { Loader } from './common/Loader'; +import { useBackend, useLocalState } from '../backend'; +import { + Autofocus, + Box, + Flex, + Section, + Stack, + Pointer, + NumberInput, + Tooltip, +} from '../components'; +import { Window } from '../layouts'; +import { clamp } from 'common/math'; +import { + hexToHsva, + HsvaColor, + hsvaToHex, + hsvaToHslString, + hsvaToRgba, + rgbaToHsva, + validHex, +} from 'common/color'; +import { Interaction, Interactive } from 'tgui/components/Interactive'; +import { classes } from 'common/react'; +import { Component, FocusEvent, FormEvent, InfernoNode } from 'inferno'; +import { logger } from 'tgui/logging'; +import { InputButtons } from './common/InputButtons'; + +type ColorPickerData = { + autofocus: boolean; + buttons: string[]; + message: string; + large_buttons: boolean; + swapped_buttons: boolean; + timeout: number; + title: string; + default_color: string; +}; + +export const ColorPickerModal = (_, context) => { + const { data } = useBackend(context); + const { + timeout, + message, + title, + autofocus, + default_color = '#000000', + } = data; + let [selectedColor, setSelectedColor] = useLocalState( + context, + 'color_picker_choice', + hexToHsva(default_color) + ); + + return ( + + {!!timeout && } + + + {message && ( + +
+ + {message} + +
+
+ )} + +
+ {!!autofocus && } + +
+
+ + + +
+
+
+ ); +}; + +export const ColorSelector = ( + { + color, + setColor, + defaultColor, + }: { color: HsvaColor; setColor; defaultColor: string }, + context +) => { + const handleChange = (params: Partial) => { + setColor((current: HsvaColor) => { + return Object.assign({}, current, params); + }); + }; + const rgb = hsvaToRgba(color); + const hexColor = hsvaToHex(color); + return ( + + + + +
+ + +
+
+ + + Current + + + Previous + +
+ + + + + + +
+
+
+ + + + + + Hex: + + + { + logger.info(value); + setColor(hexToHsva(value)); + }} + prefixed + /> + + + + + + + + H: + + + + + + handleChange({ h: v })} + max={360} + unit="°" + /> + + + + + + + S: + + + + + + handleChange({ s: v })} + unit="%" + /> + + + + + + + V: + + + + + + handleChange({ v: v })} + unit="%" + /> + + + + + + + + R: + + + + + + { + rgb.r = v; + handleChange(rgbaToHsva(rgb)); + }} + max={255} + /> + + + + + + + G: + + + + + + { + rgb.g = v; + handleChange(rgbaToHsva(rgb)); + }} + max={255} + /> + + + + + + + B: + + + + + + { + rgb.b = v; + handleChange(rgbaToHsva(rgb)); + }} + max={255} + /> + + + + + +
+ ); +}; + +const TextSetter = ({ + value, + callback, + min = 0, + max = 100, + unit, +}: { + value: number; + callback: any; + min?: number; + max?: number; + unit?: string; +}) => { + return ( + + ); +}; + +/** + * MIT License + * https://github.com/omgovich/react-colorful/ + * + * Copyright (c) 2020 Vlad Shilov + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +interface HexColorInputProps + extends Omit { + /** Enables `#` prefix displaying */ + prefixed?: boolean; + /** Allows `#rgba` and `#rrggbbaa` color formats */ + alpha?: boolean; +} + +/** Adds "#" symbol to the beginning of the string */ +const prefix = (value: string) => '#' + value; + +export const HexColorInput = (props: HexColorInputProps): InfernoNode => { + const { prefixed, alpha, color, fluid, onChange, ...rest } = props; + + /** Escapes all non-hexadecimal characters including "#" */ + const escape = (value: string) => + value.replace(/([^0-9A-F]+)/gi, '').substring(0, alpha ? 8 : 6); + + /** Validates hexadecimal strings */ + const validate = (value: string) => validHex(value, alpha); + + return ( + + ); +}; + +interface ColorInputBaseProps { + fluid?: boolean; + color: string; + onChange: (newColor: string) => void; + /** Blocks typing invalid characters and limits string length */ + escape: (value: string) => string; + /** Checks that value is valid color string */ + validate: (value: string) => boolean; + /** Processes value before displaying it in the input */ + format?: (value: string) => string; +} + +export class ColorInput extends Component { + props: ColorInputBaseProps; + state: { localValue: string }; + + constructor(props: ColorInputBaseProps) { + super(); + this.props = props; + this.state = { localValue: this.props.escape(this.props.color) }; + } + + // Trigger `onChange` handler only if the input value is a valid color + handleInput = (e: FormEvent) => { + const inputValue = this.props.escape(e.currentTarget.value); + this.setState({ localValue: inputValue }); + }; + + // Take the color from props if the last typed color (in local state) is not valid + handleBlur = (e: FocusEvent) => { + if (e.currentTarget) { + if (!this.props.validate(e.currentTarget.value)) { + this.setState({ localValue: this.props.escape(this.props.color) }); // return to default; + } else { + this.props.onChange( + this.props.escape + ? this.props.escape(e.currentTarget.value) + : e.currentTarget.value + ); + } + } + }; + + componentDidUpdate(prevProps, prevState): void { + if (prevProps.color !== this.props.color) { + // Update the local state when `color` property value is changed + this.setState({ localValue: this.props.escape(this.props.color) }); + } + } + + render() { + return ( + +
.
+ +
+ ); + } +} + +const SaturationValue = ({ hsva, onChange }) => { + const handleMove = (interaction: Interaction) => { + onChange({ + s: interaction.left * 100, + v: 100 - interaction.top * 100, + }); + }; + + const handleKey = (offset: Interaction) => { + // Saturation and brightness always fit into [0, 100] range + onChange({ + s: clamp(hsva.s + offset.left * 100, 0, 100), + v: clamp(hsva.v - offset.top * 100, 0, 100), + }); + }; + + const containerStyle = { + 'background-color': `${hsvaToHslString({ h: hsva.h, s: 100, v: 100, a: 1 })} !important`, + }; + + return ( +
+ + + +
+ ); +}; + +const Hue = ({ + className, + hue, + onChange, +}: { + className?: string; + hue: number; + onChange: (newHue: { h: number }) => void; +}) => { + const handleMove = (interaction: Interaction) => { + onChange({ h: 360 * interaction.left }); + }; + + const handleKey = (offset: Interaction) => { + // Hue measured in degrees of the color circle ranging from 0 to 360 + onChange({ + h: clamp(hue + offset.left * 360, 0, 360), + }); + }; + + const nodeClassName = classes(['react-colorful__hue', className]); + + return ( +
+ + + +
+ ); +}; + +const Saturation = ({ + className, + color, + onChange, +}: { + className?: string; + color: HsvaColor; + onChange: (newSaturation: { s: number }) => void; +}) => { + const handleMove = (interaction: Interaction) => { + onChange({ s: 100 * interaction.left }); + }; + + const handleKey = (offset: Interaction) => { + // Hue measured in degrees of the color circle ranging from 0 to 100 + onChange({ + s: clamp(color.s + offset.left * 100, 0, 100), + }); + }; + + const nodeClassName = classes(['react-colorful__saturation', className]); + + return ( +
+ + + +
+ ); +}; + +const Value = ({ + className, + color, + onChange, +}: { + className?: string; + color: HsvaColor; + onChange: (newValue: { v: number }) => void; +}) => { + const handleMove = (interaction: Interaction) => { + onChange({ v: 100 * interaction.left }); + }; + + const handleKey = (offset: Interaction) => { + onChange({ + v: clamp(color.v + offset.left * 100, 0, 100), + }); + }; + + const nodeClassName = classes(['react-colorful__value', className]); + + return ( +
+ + + +
+ ); +}; + +const RGBSlider = ({ + className, + color, + onChange, + target, +}: { + className?: string; + color: HsvaColor; + onChange: (newValue: HsvaColor) => void; + target: string; +}) => { + const rgb = hsvaToRgba(color); + + const setNewTarget = (value: number) => { + rgb[target] = value; + onChange(rgbaToHsva(rgb)); + }; + + const handleMove = (interaction: Interaction) => { + setNewTarget(255 * interaction.left); + }; + + const handleKey = (offset: Interaction) => { + setNewTarget(clamp(rgb[target] + offset.left * 255, 0, 255)); + }; + + const nodeClassName = classes([`react-colorful__${target}`, className]); + + let selected = + target === 'r' + ? `rgb(${Math.round(rgb.r)},0,0)` + : target === 'g' + ? `rgb(0,${Math.round(rgb.g)},0)` + : `rgb(0,0,${Math.round(rgb.b)})`; + + return ( +
+ + + +
+ ); +}; diff --git a/tgui/packages/tgui/styles/interfaces/ColorPicker.scss b/tgui/packages/tgui/styles/interfaces/ColorPicker.scss new file mode 100644 index 00000000000..99f628c35e2 --- /dev/null +++ b/tgui/packages/tgui/styles/interfaces/ColorPicker.scss @@ -0,0 +1,153 @@ +/** + * MIT License + * https://github.com/omgovich/react-colorful/ + * + * Copyright (c) 2020 Vlad Shilov + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +@use '../colors.scss'; +@use '../base.scss'; + +.react-colorful { + position: relative; + display: flex; + flex-direction: column; + width: 200px; + height: 200px; + user-select: none; + cursor: default; +} + +.react-colorful__saturation_value { + position: relative; + flex-grow: 1; + border-color: transparent; /* Fixes https://github.com/omgovich/react-colorful/issues/139 */ + border-bottom: 12px solid #000; + border-radius: 8px 8px 0 0; + background-image: linear-gradient( + to top, + rgba(0, 0, 0, 255), + rgba(0, 0, 0, 0) + ), + linear-gradient(to right, rgba(255, 255, 255, 255), rgba(255, 255, 255, 0)); +} + +.react-colorful__pointer-fill, +.react-colorful__alpha-gradient { + content: ''; + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + pointer-events: none; + border-radius: inherit; +} + +/* Improve elements rendering on light backgrounds */ +.react-colorful__alpha-gradient, +.react-colorful__saturation_value { + box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.05); +} + +.react-colorful__hue, +.react-colorful__r, +.react-colorful__g, +.react-colorful__b, +.react-colorful__alpha, +.react-colorful__saturation, +.react-colorful__value { + position: relative; + height: 24px; +} + +.react-colorful__hue { + background: linear-gradient( + to right, + #f00 0%, + #ff0 17%, + #0f0 33%, + #0ff 50%, + #00f 67%, + #f0f 83%, + #f00 100% + ); +} + +.react-colorful__r { + background: linear-gradient(to right, #000, #f00); +} + +.react-colorful__g { + background: linear-gradient(to right, #000, #0f0); +} + +.react-colorful__b { + background: linear-gradient(to right, #000, #00f); +} + +/* Round bottom corners of the last element: `Hue` for `ColorPicker` or `Alpha` for `AlphaColorPicker` */ +.react-colorful__last-control { + border-radius: 0 0 8px 8px; +} + +.react-colorful__interactive { + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + border-radius: inherit; + outline: none; + /* Don't trigger the default scrolling behavior when the event is originating from this element */ + touch-action: none; +} + +.react-colorful__pointer { + position: absolute; + z-index: 1; + box-sizing: border-box; + width: 28px; + height: 28px; + transform: translate(-50%, -50%); + background-color: #cfcfcf; + border: 2px solid #cfcfcf; + border-radius: 50%; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.4); +} + +.react-colorful__interactive:focus .react-colorful__pointer { + transform: translate(-50%, -50%) scale(1.1); + background-color: #fff; + border-color: #fff; +} + +/* Chessboard-like pattern for alpha related elements */ +.react-colorful__alpha, +.react-colorful__alpha-pointer { + background-color: #fff; + background-image: url('data:image/svg+xml,'); +} + +.react-colorful__saturation-pointer, +.react-colorful__value-pointer, +.react-colorful__hue-pointer, +.react-colorful__r-pointer, +.react-colorful__g-pointer, +.react-colorful__b-pointer { + z-index: 1; + width: 20px; + height: 20px; +} + +/* Display the saturation value pointer over the hue one */ +.react-colorful__saturation_value-pointer { + z-index: 3; +} diff --git a/tgui/packages/tgui/styles/main.scss b/tgui/packages/tgui/styles/main.scss index 4222996bd47..d4d9af02422 100644 --- a/tgui/packages/tgui/styles/main.scss +++ b/tgui/packages/tgui/styles/main.scss @@ -50,6 +50,7 @@ @include meta.load-css('./interfaces/BrigCells.scss'); @include meta.load-css('./interfaces/CameraConsole.scss'); @include meta.load-css('./interfaces/Changelog.scss'); +@include meta.load-css('./interfaces/ColorPicker.scss'); @include meta.load-css('./interfaces/Contractor.scss'); @include meta.load-css('./interfaces/ExosuitFabricator.scss'); @include meta.load-css('./interfaces/ListInput.scss'); diff --git a/tgui/public/tgui-panel.bundle.js b/tgui/public/tgui-panel.bundle.js index 532589c6740..e4439776f39 100644 --- a/tgui/public/tgui-panel.bundle.js +++ b/tgui/public/tgui-panel.bundle.js @@ -1,30 +1,30 @@ -(function(){(function(){var xn={96376:function(y,n,t){"use strict";n.__esModule=!0,n.createPopper=void 0,n.popperGenerator=p;var e=u(t(74758)),r=u(t(28811)),o=u(t(98309)),a=u(t(44896)),s=u(t(33118)),i=u(t(10579)),c=u(t(56500)),g=u(t(17633));n.detectOverflow=g.default;var f=t(75573);function u(h){return h&&h.__esModule?h:{default:h}}var v={placement:"bottom",modifiers:[],strategy:"absolute"};function l(){for(var h=arguments.length,m=new Array(h),E=0;E0&&(0,r.round)(u.width)/c.offsetWidth||1,l=c.offsetHeight>0&&(0,r.round)(u.height)/c.offsetHeight||1);var p=(0,e.isElement)(c)?(0,o.default)(c):window,d=p.visualViewport,h=!(0,a.default)()&&f,m=(u.left+(h&&d?d.offsetLeft:0))/v,E=(u.top+(h&&d?d.offsetTop:0))/l,A=u.width/v,I=u.height/l;return{width:A,height:I,top:E,right:m+A,bottom:E+I,left:m,x:m,y:E}}},49035:function(y,n,t){"use strict";n.__esModule=!0,n.default=I;var e=t(46206),r=h(t(87991)),o=h(t(79752)),a=h(t(98309)),s=h(t(44896)),i=h(t(40600)),c=h(t(16599)),g=t(75573),f=h(t(37786)),u=h(t(57819)),v=h(t(4206)),l=h(t(12972)),p=h(t(81666)),d=t(63618);function h(T){return T&&T.__esModule?T:{default:T}}function m(T,C){var S=(0,f.default)(T,!1,C==="fixed");return S.top=S.top+T.clientTop,S.left=S.left+T.clientLeft,S.bottom=S.top+T.clientHeight,S.right=S.left+T.clientWidth,S.width=T.clientWidth,S.height=T.clientHeight,S.x=S.left,S.y=S.top,S}function E(T,C,S){return C===e.viewport?(0,p.default)((0,r.default)(T,S)):(0,g.isElement)(C)?m(C,S):(0,p.default)((0,o.default)((0,i.default)(T)))}function A(T){var C=(0,a.default)((0,u.default)(T)),S=["absolute","fixed"].indexOf((0,c.default)(T).position)>=0,b=S&&(0,g.isHTMLElement)(T)?(0,s.default)(T):T;return(0,g.isElement)(b)?C.filter(function(N){return(0,g.isElement)(N)&&(0,v.default)(N,b)&&(0,l.default)(N)!=="body"}):[]}function I(T,C,S,b){var N=C==="clippingParents"?A(T):[].concat(C),M=[].concat(N,[S]),R=M[0],L=M.reduce(function(B,U){var x=E(T,U,b);return B.top=(0,d.max)(x.top,B.top),B.right=(0,d.min)(x.right,B.right),B.bottom=(0,d.min)(x.bottom,B.bottom),B.left=(0,d.max)(x.left,B.left),B},E(T,R,b));return L.width=L.right-L.left,L.height=L.bottom-L.top,L.x=L.left,L.y=L.top,L}},74758:function(y,n,t){"use strict";n.__esModule=!0,n.default=v;var e=f(t(37786)),r=f(t(13390)),o=f(t(12972)),a=t(75573),s=f(t(79697)),i=f(t(40600)),c=f(t(10798)),g=t(63618);function f(l){return l&&l.__esModule?l:{default:l}}function u(l){var p=l.getBoundingClientRect(),d=(0,g.round)(p.width)/l.offsetWidth||1,h=(0,g.round)(p.height)/l.offsetHeight||1;return d!==1||h!==1}function v(l,p,d){d===void 0&&(d=!1);var h=(0,a.isHTMLElement)(p),m=(0,a.isHTMLElement)(p)&&u(p),E=(0,i.default)(p),A=(0,e.default)(l,m,d),I={scrollLeft:0,scrollTop:0},T={x:0,y:0};return(h||!h&&!d)&&(((0,o.default)(p)!=="body"||(0,c.default)(E))&&(I=(0,r.default)(p)),(0,a.isHTMLElement)(p)?(T=(0,e.default)(p,!0),T.x+=p.clientLeft,T.y+=p.clientTop):E&&(T.x=(0,s.default)(E))),{x:A.left+I.scrollLeft-T.x,y:A.top+I.scrollTop-T.y,width:A.width,height:A.height}}},16599:function(y,n,t){"use strict";n.__esModule=!0,n.default=o;var e=r(t(95115));function r(a){return a&&a.__esModule?a:{default:a}}function o(a){return(0,e.default)(a).getComputedStyle(a)}},40600:function(y,n,t){"use strict";n.__esModule=!0,n.default=r;var e=t(75573);function r(o){return(((0,e.isElement)(o)?o.ownerDocument:o.document)||window.document).documentElement}},79752:function(y,n,t){"use strict";n.__esModule=!0,n.default=c;var e=i(t(40600)),r=i(t(16599)),o=i(t(79697)),a=i(t(43750)),s=t(63618);function i(g){return g&&g.__esModule?g:{default:g}}function c(g){var f,u=(0,e.default)(g),v=(0,a.default)(g),l=(f=g.ownerDocument)==null?void 0:f.body,p=(0,s.max)(u.scrollWidth,u.clientWidth,l?l.scrollWidth:0,l?l.clientWidth:0),d=(0,s.max)(u.scrollHeight,u.clientHeight,l?l.scrollHeight:0,l?l.clientHeight:0),h=-v.scrollLeft+(0,o.default)(g),m=-v.scrollTop;return(0,r.default)(l||u).direction==="rtl"&&(h+=(0,s.max)(u.clientWidth,l?l.clientWidth:0)-p),{width:p,height:d,x:h,y:m}}},3073:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(e){return{scrollLeft:e.scrollLeft,scrollTop:e.scrollTop}}},28811:function(y,n,t){"use strict";n.__esModule=!0,n.default=o;var e=r(t(37786));function r(a){return a&&a.__esModule?a:{default:a}}function o(a){var s=(0,e.default)(a),i=a.offsetWidth,c=a.offsetHeight;return Math.abs(s.width-i)<=1&&(i=s.width),Math.abs(s.height-c)<=1&&(c=s.height),{x:a.offsetLeft,y:a.offsetTop,width:i,height:c}}},12972:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(e){return e?(e.nodeName||"").toLowerCase():null}},13390:function(y,n,t){"use strict";n.__esModule=!0,n.default=i;var e=s(t(43750)),r=s(t(95115)),o=t(75573),a=s(t(3073));function s(c){return c&&c.__esModule?c:{default:c}}function i(c){return c===(0,r.default)(c)||!(0,o.isHTMLElement)(c)?(0,e.default)(c):(0,a.default)(c)}},44896:function(y,n,t){"use strict";n.__esModule=!0,n.default=v;var e=g(t(95115)),r=g(t(12972)),o=g(t(16599)),a=t(75573),s=g(t(87031)),i=g(t(57819)),c=g(t(35366));function g(l){return l&&l.__esModule?l:{default:l}}function f(l){return!(0,a.isHTMLElement)(l)||(0,o.default)(l).position==="fixed"?null:l.offsetParent}function u(l){var p=/firefox/i.test((0,c.default)()),d=/Trident/i.test((0,c.default)());if(d&&(0,a.isHTMLElement)(l)){var h=(0,o.default)(l);if(h.position==="fixed")return null}var m=(0,i.default)(l);for((0,a.isShadowRoot)(m)&&(m=m.host);(0,a.isHTMLElement)(m)&&["html","body"].indexOf((0,r.default)(m))<0;){var E=(0,o.default)(m);if(E.transform!=="none"||E.perspective!=="none"||E.contain==="paint"||["transform","perspective"].indexOf(E.willChange)!==-1||p&&E.willChange==="filter"||p&&E.filter&&E.filter!=="none")return m;m=m.parentNode}return null}function v(l){for(var p=(0,e.default)(l),d=f(l);d&&(0,s.default)(d)&&(0,o.default)(d).position==="static";)d=f(d);return d&&((0,r.default)(d)==="html"||(0,r.default)(d)==="body"&&(0,o.default)(d).position==="static")?p:d||u(l)||p}},57819:function(y,n,t){"use strict";n.__esModule=!0,n.default=s;var e=a(t(12972)),r=a(t(40600)),o=t(75573);function a(i){return i&&i.__esModule?i:{default:i}}function s(i){return(0,e.default)(i)==="html"?i:i.assignedSlot||i.parentNode||((0,o.isShadowRoot)(i)?i.host:null)||(0,r.default)(i)}},24426:function(y,n,t){"use strict";n.__esModule=!0,n.default=i;var e=s(t(57819)),r=s(t(10798)),o=s(t(12972)),a=t(75573);function s(c){return c&&c.__esModule?c:{default:c}}function i(c){return["html","body","#document"].indexOf((0,o.default)(c))>=0?c.ownerDocument.body:(0,a.isHTMLElement)(c)&&(0,r.default)(c)?c:i((0,e.default)(c))}},87991:function(y,n,t){"use strict";n.__esModule=!0,n.default=i;var e=s(t(95115)),r=s(t(40600)),o=s(t(79697)),a=s(t(89331));function s(c){return c&&c.__esModule?c:{default:c}}function i(c,g){var f=(0,e.default)(c),u=(0,r.default)(c),v=f.visualViewport,l=u.clientWidth,p=u.clientHeight,d=0,h=0;if(v){l=v.width,p=v.height;var m=(0,a.default)();(m||!m&&g==="fixed")&&(d=v.offsetLeft,h=v.offsetTop)}return{width:l,height:p,x:d+(0,o.default)(c),y:h}}},95115:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(e){if(e==null)return window;if(e.toString()!=="[object Window]"){var r=e.ownerDocument;return r&&r.defaultView||window}return e}},43750:function(y,n,t){"use strict";n.__esModule=!0,n.default=o;var e=r(t(95115));function r(a){return a&&a.__esModule?a:{default:a}}function o(a){var s=(0,e.default)(a),i=s.pageXOffset,c=s.pageYOffset;return{scrollLeft:i,scrollTop:c}}},79697:function(y,n,t){"use strict";n.__esModule=!0,n.default=s;var e=a(t(37786)),r=a(t(40600)),o=a(t(43750));function a(i){return i&&i.__esModule?i:{default:i}}function s(i){return(0,e.default)((0,r.default)(i)).left+(0,o.default)(i).scrollLeft}},75573:function(y,n,t){"use strict";n.__esModule=!0,n.isElement=o,n.isHTMLElement=a,n.isShadowRoot=s;var e=r(t(95115));function r(i){return i&&i.__esModule?i:{default:i}}function o(i){var c=(0,e.default)(i).Element;return i instanceof c||i instanceof Element}function a(i){var c=(0,e.default)(i).HTMLElement;return i instanceof c||i instanceof HTMLElement}function s(i){if(typeof ShadowRoot=="undefined")return!1;var c=(0,e.default)(i).ShadowRoot;return i instanceof c||i instanceof ShadowRoot}},89331:function(y,n,t){"use strict";n.__esModule=!0,n.default=o;var e=r(t(35366));function r(a){return a&&a.__esModule?a:{default:a}}function o(){return!/^((?!chrome|android).)*safari/i.test((0,e.default)())}},10798:function(y,n,t){"use strict";n.__esModule=!0,n.default=o;var e=r(t(16599));function r(a){return a&&a.__esModule?a:{default:a}}function o(a){var s=(0,e.default)(a),i=s.overflow,c=s.overflowX,g=s.overflowY;return/auto|scroll|overlay|hidden/.test(i+g+c)}},87031:function(y,n,t){"use strict";n.__esModule=!0,n.default=o;var e=r(t(12972));function r(a){return a&&a.__esModule?a:{default:a}}function o(a){return["table","td","th"].indexOf((0,e.default)(a))>=0}},98309:function(y,n,t){"use strict";n.__esModule=!0,n.default=i;var e=s(t(24426)),r=s(t(57819)),o=s(t(95115)),a=s(t(10798));function s(c){return c&&c.__esModule?c:{default:c}}function i(c,g){var f;g===void 0&&(g=[]);var u=(0,e.default)(c),v=u===((f=c.ownerDocument)==null?void 0:f.body),l=(0,o.default)(u),p=v?[l].concat(l.visualViewport||[],(0,a.default)(u)?u:[]):u,d=g.concat(p);return v?d:d.concat(i((0,r.default)(p)))}},46206:function(y,n){"use strict";n.__esModule=!0,n.write=n.viewport=n.variationPlacements=n.top=n.start=n.right=n.reference=n.read=n.popper=n.placements=n.modifierPhases=n.main=n.left=n.end=n.clippingParents=n.bottom=n.beforeWrite=n.beforeRead=n.beforeMain=n.basePlacements=n.auto=n.afterWrite=n.afterRead=n.afterMain=void 0;var t=n.top="top",e=n.bottom="bottom",r=n.right="right",o=n.left="left",a=n.auto="auto",s=n.basePlacements=[t,e,r,o],i=n.start="start",c=n.end="end",g=n.clippingParents="clippingParents",f=n.viewport="viewport",u=n.popper="popper",v=n.reference="reference",l=n.variationPlacements=s.reduce(function(N,M){return N.concat([M+"-"+i,M+"-"+c])},[]),p=n.placements=[].concat(s,[a]).reduce(function(N,M){return N.concat([M,M+"-"+i,M+"-"+c])},[]),d=n.beforeRead="beforeRead",h=n.read="read",m=n.afterRead="afterRead",E=n.beforeMain="beforeMain",A=n.main="main",I=n.afterMain="afterMain",T=n.beforeWrite="beforeWrite",C=n.write="write",S=n.afterWrite="afterWrite",b=n.modifierPhases=[d,h,m,E,A,I,T,C,S]},95996:function(y,n,t){"use strict";n.__esModule=!0;var e={popperGenerator:!0,detectOverflow:!0,createPopperBase:!0,createPopper:!0,createPopperLite:!0};n.popperGenerator=n.detectOverflow=n.createPopperLite=n.createPopperBase=n.createPopper=void 0;var r=t(46206);Object.keys(r).forEach(function(c){c==="default"||c==="__esModule"||Object.prototype.hasOwnProperty.call(e,c)||c in n&&n[c]===r[c]||(n[c]=r[c])});var o=t(39805);Object.keys(o).forEach(function(c){c==="default"||c==="__esModule"||Object.prototype.hasOwnProperty.call(e,c)||c in n&&n[c]===o[c]||(n[c]=o[c])});var a=t(96376);n.popperGenerator=a.popperGenerator,n.detectOverflow=a.detectOverflow,n.createPopperBase=a.createPopper;var s=t(83312);n.createPopper=s.createPopper;var i=t(2473);n.createPopperLite=i.createPopper},19975:function(y,n,t){"use strict";n.__esModule=!0,n.default=void 0;var e=o(t(12972)),r=t(75573);function o(c){return c&&c.__esModule?c:{default:c}}function a(c){var g=c.state;Object.keys(g.elements).forEach(function(f){var u=g.styles[f]||{},v=g.attributes[f]||{},l=g.elements[f];!(0,r.isHTMLElement)(l)||!(0,e.default)(l)||(Object.assign(l.style,u),Object.keys(v).forEach(function(p){var d=v[p];d===!1?l.removeAttribute(p):l.setAttribute(p,d===!0?"":d)}))})}function s(c){var g=c.state,f={popper:{position:g.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(g.elements.popper.style,f.popper),g.styles=f,g.elements.arrow&&Object.assign(g.elements.arrow.style,f.arrow),function(){Object.keys(g.elements).forEach(function(u){var v=g.elements[u],l=g.attributes[u]||{},p=Object.keys(g.styles.hasOwnProperty(u)?g.styles[u]:f[u]),d=p.reduce(function(h,m){return h[m]="",h},{});!(0,r.isHTMLElement)(v)||!(0,e.default)(v)||(Object.assign(v.style,d),Object.keys(l).forEach(function(h){v.removeAttribute(h)}))})}}var i=n.default={name:"applyStyles",enabled:!0,phase:"write",fn:a,effect:s,requires:["computeStyles"]}},52744:function(y,n,t){"use strict";n.__esModule=!0,n.default=void 0;var e=u(t(83104)),r=u(t(28811)),o=u(t(4206)),a=u(t(44896)),s=u(t(41199)),i=t(28595),c=u(t(43286)),g=u(t(81447)),f=t(46206);function u(h){return h&&h.__esModule?h:{default:h}}var v=function(){function h(m,E){return m=typeof m=="function"?m(Object.assign({},E.rects,{placement:E.placement})):m,(0,c.default)(typeof m!="number"?m:(0,g.default)(m,f.basePlacements))}return h}();function l(h){var m,E=h.state,A=h.name,I=h.options,T=E.elements.arrow,C=E.modifiersData.popperOffsets,S=(0,e.default)(E.placement),b=(0,s.default)(S),N=[f.left,f.right].indexOf(S)>=0,M=N?"height":"width";if(!(!T||!C)){var R=v(I.padding,E),L=(0,r.default)(T),B=b==="y"?f.top:f.left,U=b==="y"?f.bottom:f.right,x=E.rects.reference[M]+E.rects.reference[b]-C[b]-E.rects.popper[M],G=C[b]-E.rects.reference[b],Y=(0,a.default)(T),D=Y?b==="y"?Y.clientHeight||0:Y.clientWidth||0:0,V=x/2-G/2,j=R[B],K=D-L[M]-R[U],$=D/2-L[M]/2+V,W=(0,i.within)(j,$,K),et=b;E.modifiersData[A]=(m={},m[et]=W,m.centerOffset=W-$,m)}}function p(h){var m=h.state,E=h.options,A=E.element,I=A===void 0?"[data-popper-arrow]":A;I!=null&&(typeof I=="string"&&(I=m.elements.popper.querySelector(I),!I)||(0,o.default)(m.elements.popper,I)&&(m.elements.arrow=I))}var d=n.default={name:"arrow",enabled:!0,phase:"main",fn:l,effect:p,requires:["popperOffsets"],requiresIfExists:["preventOverflow"]}},59894:function(y,n,t){"use strict";n.__esModule=!0,n.default=void 0,n.mapToStyles=l;var e=t(46206),r=f(t(44896)),o=f(t(95115)),a=f(t(40600)),s=f(t(16599)),i=f(t(83104)),c=f(t(45)),g=t(63618);function f(h){return h&&h.__esModule?h:{default:h}}var u={top:"auto",right:"auto",bottom:"auto",left:"auto"};function v(h,m){var E=h.x,A=h.y,I=m.devicePixelRatio||1;return{x:(0,g.round)(E*I)/I||0,y:(0,g.round)(A*I)/I||0}}function l(h){var m,E=h.popper,A=h.popperRect,I=h.placement,T=h.variation,C=h.offsets,S=h.position,b=h.gpuAcceleration,N=h.adaptive,M=h.roundOffsets,R=h.isFixed,L=C.x,B=L===void 0?0:L,U=C.y,x=U===void 0?0:U,G=typeof M=="function"?M({x:B,y:x}):{x:B,y:x};B=G.x,x=G.y;var Y=C.hasOwnProperty("x"),D=C.hasOwnProperty("y"),V=e.left,j=e.top,K=window;if(N){var $=(0,r.default)(E),W="clientHeight",et="clientWidth";if($===(0,o.default)(E)&&($=(0,a.default)(E),(0,s.default)($).position!=="static"&&S==="absolute"&&(W="scrollHeight",et="scrollWidth")),$=$,I===e.top||(I===e.left||I===e.right)&&T===e.end){j=e.bottom;var ft=R&&$===K&&K.visualViewport?K.visualViewport.height:$[W];x-=ft-A.height,x*=b?1:-1}if(I===e.left||(I===e.top||I===e.bottom)&&T===e.end){V=e.right;var dt=R&&$===K&&K.visualViewport?K.visualViewport.width:$[et];B-=dt-A.width,B*=b?1:-1}}var z=Object.assign({position:S},N&&u),J=M===!0?v({x:B,y:x},(0,o.default)(E)):{x:B,y:x};if(B=J.x,x=J.y,b){var nt;return Object.assign({},z,(nt={},nt[j]=D?"0":"",nt[V]=Y?"0":"",nt.transform=(K.devicePixelRatio||1)<=1?"translate("+B+"px, "+x+"px)":"translate3d("+B+"px, "+x+"px, 0)",nt))}return Object.assign({},z,(m={},m[j]=D?x+"px":"",m[V]=Y?B+"px":"",m.transform="",m))}function p(h){var m=h.state,E=h.options,A=E.gpuAcceleration,I=A===void 0?!0:A,T=E.adaptive,C=T===void 0?!0:T,S=E.roundOffsets,b=S===void 0?!0:S,N={placement:(0,i.default)(m.placement),variation:(0,c.default)(m.placement),popper:m.elements.popper,popperRect:m.rects.popper,gpuAcceleration:I,isFixed:m.options.strategy==="fixed"};m.modifiersData.popperOffsets!=null&&(m.styles.popper=Object.assign({},m.styles.popper,l(Object.assign({},N,{offsets:m.modifiersData.popperOffsets,position:m.options.strategy,adaptive:C,roundOffsets:b})))),m.modifiersData.arrow!=null&&(m.styles.arrow=Object.assign({},m.styles.arrow,l(Object.assign({},N,{offsets:m.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:b})))),m.attributes.popper=Object.assign({},m.attributes.popper,{"data-popper-placement":m.placement})}var d=n.default={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:p,data:{}}},36692:function(y,n,t){"use strict";n.__esModule=!0,n.default=void 0;var e=r(t(95115));function r(i){return i&&i.__esModule?i:{default:i}}var o={passive:!0};function a(i){var c=i.state,g=i.instance,f=i.options,u=f.scroll,v=u===void 0?!0:u,l=f.resize,p=l===void 0?!0:l,d=(0,e.default)(c.elements.popper),h=[].concat(c.scrollParents.reference,c.scrollParents.popper);return v&&h.forEach(function(m){m.addEventListener("scroll",g.update,o)}),p&&d.addEventListener("resize",g.update,o),function(){v&&h.forEach(function(m){m.removeEventListener("scroll",g.update,o)}),p&&d.removeEventListener("resize",g.update,o)}}var s=n.default={name:"eventListeners",enabled:!0,phase:"write",fn:function(){function i(){}return i}(),effect:a,data:{}}},23798:function(y,n,t){"use strict";n.__esModule=!0,n.default=void 0;var e=g(t(71376)),r=g(t(83104)),o=g(t(86459)),a=g(t(17633)),s=g(t(9041)),i=t(46206),c=g(t(45));function g(l){return l&&l.__esModule?l:{default:l}}function f(l){if((0,r.default)(l)===i.auto)return[];var p=(0,e.default)(l);return[(0,o.default)(l),p,(0,o.default)(p)]}function u(l){var p=l.state,d=l.options,h=l.name;if(!p.modifiersData[h]._skip){for(var m=d.mainAxis,E=m===void 0?!0:m,A=d.altAxis,I=A===void 0?!0:A,T=d.fallbackPlacements,C=d.padding,S=d.boundary,b=d.rootBoundary,N=d.altBoundary,M=d.flipVariations,R=M===void 0?!0:M,L=d.allowedAutoPlacements,B=p.options.placement,U=(0,r.default)(B),x=U===B,G=T||(x||!R?[(0,e.default)(B)]:f(B)),Y=[B].concat(G).reduce(function(rt,ht){return rt.concat((0,r.default)(ht)===i.auto?(0,s.default)(p,{placement:ht,boundary:S,rootBoundary:b,padding:C,flipVariations:R,allowedAutoPlacements:L}):ht)},[]),D=p.rects.reference,V=p.rects.popper,j=new Map,K=!0,$=Y[0],W=0;W=0,J=z?"width":"height",nt=(0,a.default)(p,{placement:et,boundary:S,rootBoundary:b,altBoundary:N,padding:C}),st=z?dt?i.right:i.left:dt?i.bottom:i.top;D[J]>V[J]&&(st=(0,e.default)(st));var it=(0,e.default)(st),pt=[];if(E&&pt.push(nt[ft]<=0),I&&pt.push(nt[st]<=0,nt[it]<=0),pt.every(function(rt){return rt})){$=et,K=!1;break}j.set(et,pt)}if(K)for(var Ot=R?3:1,Pt=function(){function rt(ht){var q=Y.find(function(_){var tt=j.get(_);if(tt)return tt.slice(0,ht).every(function(mt){return mt})});if(q)return $=q,"break"}return rt}(),Nt=Ot;Nt>0;Nt--){var vt=Pt(Nt);if(vt==="break")break}p.placement!==$&&(p.modifiersData[h]._skip=!0,p.placement=$,p.reset=!0)}}var v=n.default={name:"flip",enabled:!0,phase:"main",fn:u,requiresIfExists:["offset"],data:{_skip:!1}}},83761:function(y,n,t){"use strict";n.__esModule=!0,n.default=void 0;var e=t(46206),r=o(t(17633));function o(g){return g&&g.__esModule?g:{default:g}}function a(g,f,u){return u===void 0&&(u={x:0,y:0}),{top:g.top-f.height-u.y,right:g.right-f.width+u.x,bottom:g.bottom-f.height+u.y,left:g.left-f.width-u.x}}function s(g){return[e.top,e.right,e.bottom,e.left].some(function(f){return g[f]>=0})}function i(g){var f=g.state,u=g.name,v=f.rects.reference,l=f.rects.popper,p=f.modifiersData.preventOverflow,d=(0,r.default)(f,{elementContext:"reference"}),h=(0,r.default)(f,{altBoundary:!0}),m=a(d,v),E=a(h,l,p),A=s(m),I=s(E);f.modifiersData[u]={referenceClippingOffsets:m,popperEscapeOffsets:E,isReferenceHidden:A,hasPopperEscaped:I},f.attributes.popper=Object.assign({},f.attributes.popper,{"data-popper-reference-hidden":A,"data-popper-escaped":I})}var c=n.default={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:i}},39805:function(y,n,t){"use strict";n.__esModule=!0,n.preventOverflow=n.popperOffsets=n.offset=n.hide=n.flip=n.eventListeners=n.computeStyles=n.arrow=n.applyStyles=void 0;var e=u(t(19975));n.applyStyles=e.default;var r=u(t(52744));n.arrow=r.default;var o=u(t(59894));n.computeStyles=o.default;var a=u(t(36692));n.eventListeners=a.default;var s=u(t(23798));n.flip=s.default;var i=u(t(83761));n.hide=i.default;var c=u(t(61410));n.offset=c.default;var g=u(t(40107));n.popperOffsets=g.default;var f=u(t(75137));n.preventOverflow=f.default;function u(v){return v&&v.__esModule?v:{default:v}}},61410:function(y,n,t){"use strict";n.__esModule=!0,n.default=void 0,n.distanceAndSkiddingToXY=a;var e=o(t(83104)),r=t(46206);function o(c){return c&&c.__esModule?c:{default:c}}function a(c,g,f){var u=(0,e.default)(c),v=[r.left,r.top].indexOf(u)>=0?-1:1,l=typeof f=="function"?f(Object.assign({},g,{placement:c})):f,p=l[0],d=l[1];return p=p||0,d=(d||0)*v,[r.left,r.right].indexOf(u)>=0?{x:d,y:p}:{x:p,y:d}}function s(c){var g=c.state,f=c.options,u=c.name,v=f.offset,l=v===void 0?[0,0]:v,p=r.placements.reduce(function(E,A){return E[A]=a(A,g.rects,l),E},{}),d=p[g.placement],h=d.x,m=d.y;g.modifiersData.popperOffsets!=null&&(g.modifiersData.popperOffsets.x+=h,g.modifiersData.popperOffsets.y+=m),g.modifiersData[u]=p}var i=n.default={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:s}},40107:function(y,n,t){"use strict";n.__esModule=!0,n.default=void 0;var e=r(t(89951));function r(s){return s&&s.__esModule?s:{default:s}}function o(s){var i=s.state,c=s.name;i.modifiersData[c]=(0,e.default)({reference:i.rects.reference,element:i.rects.popper,strategy:"absolute",placement:i.placement})}var a=n.default={name:"popperOffsets",enabled:!0,phase:"read",fn:o,data:{}}},75137:function(y,n,t){"use strict";n.__esModule=!0,n.default=void 0;var e=t(46206),r=l(t(83104)),o=l(t(41199)),a=l(t(28066)),s=t(28595),i=l(t(28811)),c=l(t(44896)),g=l(t(17633)),f=l(t(45)),u=l(t(34780)),v=t(63618);function l(h){return h&&h.__esModule?h:{default:h}}function p(h){var m=h.state,E=h.options,A=h.name,I=E.mainAxis,T=I===void 0?!0:I,C=E.altAxis,S=C===void 0?!1:C,b=E.boundary,N=E.rootBoundary,M=E.altBoundary,R=E.padding,L=E.tether,B=L===void 0?!0:L,U=E.tetherOffset,x=U===void 0?0:U,G=(0,g.default)(m,{boundary:b,rootBoundary:N,padding:R,altBoundary:M}),Y=(0,r.default)(m.placement),D=(0,f.default)(m.placement),V=!D,j=(0,o.default)(Y),K=(0,a.default)(j),$=m.modifiersData.popperOffsets,W=m.rects.reference,et=m.rects.popper,ft=typeof x=="function"?x(Object.assign({},m.rects,{placement:m.placement})):x,dt=typeof ft=="number"?{mainAxis:ft,altAxis:ft}:Object.assign({mainAxis:0,altAxis:0},ft),z=m.modifiersData.offset?m.modifiersData.offset[m.placement]:null,J={x:0,y:0};if($){if(T){var nt,st=j==="y"?e.top:e.left,it=j==="y"?e.bottom:e.right,pt=j==="y"?"height":"width",Ot=$[j],Pt=Ot+G[st],Nt=Ot-G[it],vt=B?-et[pt]/2:0,rt=D===e.start?W[pt]:et[pt],ht=D===e.start?-et[pt]:-W[pt],q=m.elements.arrow,_=B&&q?(0,i.default)(q):{width:0,height:0},tt=m.modifiersData["arrow#persistent"]?m.modifiersData["arrow#persistent"].padding:(0,u.default)(),mt=tt[st],at=tt[it],ot=(0,s.within)(0,W[pt],_[pt]),At=V?W[pt]/2-vt-ot-mt-dt.mainAxis:rt-ot-mt-dt.mainAxis,X=V?-W[pt]/2+vt+ot+at+dt.mainAxis:ht+ot+at+dt.mainAxis,ut=m.elements.arrow&&(0,c.default)(m.elements.arrow),yt=ut?j==="y"?ut.clientTop||0:ut.clientLeft||0:0,Tt=(nt=z==null?void 0:z[j])!=null?nt:0,wt=Ot+At-Tt-yt,jt=Ot+X-Tt,Ct=(0,s.within)(B?(0,v.min)(Pt,wt):Pt,Ot,B?(0,v.max)(Nt,jt):Nt);$[j]=Ct,J[j]=Ct-Ot}if(S){var lt,gt=j==="x"?e.top:e.left,bt=j==="x"?e.bottom:e.right,St=$[K],It=K==="y"?"height":"width",Ft=St+G[gt],Vt=St-G[bt],Gt=[e.top,e.left].indexOf(Y)!==-1,Ht=(lt=z==null?void 0:z[K])!=null?lt:0,Kt=Gt?Ft:St-W[It]-et[It]-Ht+dt.altAxis,Wt=Gt?St+W[It]+et[It]-Ht-dt.altAxis:Vt,Qt=B&&Gt?(0,s.withinMaxClamp)(Kt,St,Wt):(0,s.within)(B?Kt:Ft,St,B?Wt:Vt);$[K]=Qt,J[K]=Qt-St}m.modifiersData[A]=J}}var d=n.default={name:"preventOverflow",enabled:!0,phase:"main",fn:p,requiresIfExists:["offset"]}},2473:function(y,n,t){"use strict";n.__esModule=!0,n.defaultModifiers=n.createPopper=void 0;var e=t(96376);n.popperGenerator=e.popperGenerator,n.detectOverflow=e.detectOverflow;var r=i(t(36692)),o=i(t(40107)),a=i(t(59894)),s=i(t(19975));function i(f){return f&&f.__esModule?f:{default:f}}var c=n.defaultModifiers=[r.default,o.default,a.default,s.default],g=n.createPopper=(0,e.popperGenerator)({defaultModifiers:c})},83312:function(y,n,t){"use strict";n.__esModule=!0;var e={createPopper:!0,createPopperLite:!0,defaultModifiers:!0,popperGenerator:!0,detectOverflow:!0};n.defaultModifiers=n.createPopperLite=n.createPopper=void 0;var r=t(96376);n.popperGenerator=r.popperGenerator,n.detectOverflow=r.detectOverflow;var o=d(t(36692)),a=d(t(40107)),s=d(t(59894)),i=d(t(19975)),c=d(t(61410)),g=d(t(23798)),f=d(t(75137)),u=d(t(52744)),v=d(t(83761)),l=t(2473);n.createPopperLite=l.createPopper;var p=t(39805);Object.keys(p).forEach(function(E){E==="default"||E==="__esModule"||Object.prototype.hasOwnProperty.call(e,E)||E in n&&n[E]===p[E]||(n[E]=p[E])});function d(E){return E&&E.__esModule?E:{default:E}}var h=n.defaultModifiers=[o.default,a.default,s.default,i.default,c.default,g.default,f.default,u.default,v.default],m=n.createPopperLite=n.createPopper=(0,r.popperGenerator)({defaultModifiers:h})},9041:function(y,n,t){"use strict";n.__esModule=!0,n.default=i;var e=s(t(45)),r=t(46206),o=s(t(17633)),a=s(t(83104));function s(c){return c&&c.__esModule?c:{default:c}}function i(c,g){g===void 0&&(g={});var f=g,u=f.placement,v=f.boundary,l=f.rootBoundary,p=f.padding,d=f.flipVariations,h=f.allowedAutoPlacements,m=h===void 0?r.placements:h,E=(0,e.default)(u),A=E?d?r.variationPlacements:r.variationPlacements.filter(function(C){return(0,e.default)(C)===E}):r.basePlacements,I=A.filter(function(C){return m.indexOf(C)>=0});I.length===0&&(I=A);var T=I.reduce(function(C,S){return C[S]=(0,o.default)(c,{placement:S,boundary:v,rootBoundary:l,padding:p})[(0,a.default)(S)],C},{});return Object.keys(T).sort(function(C,S){return T[C]-T[S]})}},89951:function(y,n,t){"use strict";n.__esModule=!0,n.default=i;var e=s(t(83104)),r=s(t(45)),o=s(t(41199)),a=t(46206);function s(c){return c&&c.__esModule?c:{default:c}}function i(c){var g=c.reference,f=c.element,u=c.placement,v=u?(0,e.default)(u):null,l=u?(0,r.default)(u):null,p=g.x+g.width/2-f.width/2,d=g.y+g.height/2-f.height/2,h;switch(v){case a.top:h={x:p,y:g.y-f.height};break;case a.bottom:h={x:p,y:g.y+g.height};break;case a.right:h={x:g.x+g.width,y:d};break;case a.left:h={x:g.x-f.width,y:d};break;default:h={x:g.x,y:g.y}}var m=v?(0,o.default)(v):null;if(m!=null){var E=m==="y"?"height":"width";switch(l){case a.start:h[m]=h[m]-(g[E]/2-f[E]/2);break;case a.end:h[m]=h[m]+(g[E]/2-f[E]/2);break;default:}}return h}},10579:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(e){var r;return function(){return r||(r=new Promise(function(o){Promise.resolve().then(function(){r=void 0,o(e())})})),r}}},17633:function(y,n,t){"use strict";n.__esModule=!0,n.default=v;var e=u(t(49035)),r=u(t(40600)),o=u(t(37786)),a=u(t(89951)),s=u(t(81666)),i=t(46206),c=t(75573),g=u(t(43286)),f=u(t(81447));function u(l){return l&&l.__esModule?l:{default:l}}function v(l,p){p===void 0&&(p={});var d=p,h=d.placement,m=h===void 0?l.placement:h,E=d.strategy,A=E===void 0?l.strategy:E,I=d.boundary,T=I===void 0?i.clippingParents:I,C=d.rootBoundary,S=C===void 0?i.viewport:C,b=d.elementContext,N=b===void 0?i.popper:b,M=d.altBoundary,R=M===void 0?!1:M,L=d.padding,B=L===void 0?0:L,U=(0,g.default)(typeof B!="number"?B:(0,f.default)(B,i.basePlacements)),x=N===i.popper?i.reference:i.popper,G=l.rects.popper,Y=l.elements[R?x:N],D=(0,e.default)((0,c.isElement)(Y)?Y:Y.contextElement||(0,r.default)(l.elements.popper),T,S,A),V=(0,o.default)(l.elements.reference),j=(0,a.default)({reference:V,element:G,strategy:"absolute",placement:m}),K=(0,s.default)(Object.assign({},G,j)),$=N===i.popper?K:V,W={top:D.top-$.top+U.top,bottom:$.bottom-D.bottom+U.bottom,left:D.left-$.left+U.left,right:$.right-D.right+U.right},et=l.modifiersData.offset;if(N===i.popper&&et){var ft=et[m];Object.keys(W).forEach(function(dt){var z=[i.right,i.bottom].indexOf(dt)>=0?1:-1,J=[i.top,i.bottom].indexOf(dt)>=0?"y":"x";W[dt]+=ft[J]*z})}return W}},81447:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(e,r){return r.reduce(function(o,a){return o[a]=e,o},{})}},28066:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(e){return e==="x"?"y":"x"}},83104:function(y,n,t){"use strict";n.__esModule=!0,n.default=r;var e=t(46206);function r(o){return o.split("-")[0]}},34780:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(){return{top:0,right:0,bottom:0,left:0}}},41199:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(e){return["top","bottom"].indexOf(e)>=0?"x":"y"}},71376:function(y,n){"use strict";n.__esModule=!0,n.default=e;var t={left:"right",right:"left",bottom:"top",top:"bottom"};function e(r){return r.replace(/left|right|bottom|top/g,function(o){return t[o]})}},86459:function(y,n){"use strict";n.__esModule=!0,n.default=e;var t={start:"end",end:"start"};function e(r){return r.replace(/start|end/g,function(o){return t[o]})}},45:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(e){return e.split("-")[1]}},63618:function(y,n){"use strict";n.__esModule=!0,n.round=n.min=n.max=void 0;var t=n.max=Math.max,e=n.min=Math.min,r=n.round=Math.round},56500:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(e){var r=e.reduce(function(o,a){var s=o[a.name];return o[a.name]=s?Object.assign({},s,a,{options:Object.assign({},s.options,a.options),data:Object.assign({},s.data,a.data)}):a,o},{});return Object.keys(r).map(function(o){return r[o]})}},43286:function(y,n,t){"use strict";n.__esModule=!0,n.default=o;var e=r(t(34780));function r(a){return a&&a.__esModule?a:{default:a}}function o(a){return Object.assign({},(0,e.default)(),a)}},33118:function(y,n,t){"use strict";n.__esModule=!0,n.default=o;var e=t(46206);function r(a){var s=new Map,i=new Set,c=[];a.forEach(function(f){s.set(f.name,f)});function g(f){i.add(f.name);var u=[].concat(f.requires||[],f.requiresIfExists||[]);u.forEach(function(v){if(!i.has(v)){var l=s.get(v);l&&g(l)}}),c.push(f)}return a.forEach(function(f){i.has(f.name)||g(f)}),c}function o(a){var s=r(a);return e.modifierPhases.reduce(function(i,c){return i.concat(s.filter(function(g){return g.phase===c}))},[])}},81666:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(e){return Object.assign({},e,{left:e.x,top:e.y,right:e.x+e.width,bottom:e.y+e.height})}},35366:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(){var e=navigator.userAgentData;return e!=null&&e.brands&&Array.isArray(e.brands)?e.brands.map(function(r){return r.brand+"/"+r.version}).join(" "):navigator.userAgent}},28595:function(y,n,t){"use strict";n.__esModule=!0,n.within=r,n.withinMaxClamp=o;var e=t(63618);function r(a,s,i){return(0,e.max)(a,(0,e.min)(s,i))}function o(a,s,i){var c=r(a,s,i);return c>i?i:c}},22734:function(y){"use strict";/*! @license DOMPurify 2.5.0 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.5.0/LICENSE */(function(n,t){y.exports=t()})(void 0,function(){"use strict";function n(X){"@babel/helpers - typeof";return n=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(ut){return typeof ut}:function(ut){return ut&&typeof Symbol=="function"&&ut.constructor===Symbol&&ut!==Symbol.prototype?"symbol":typeof ut},n(X)}function t(X,ut){return t=Object.setPrototypeOf||function(){function yt(Tt,wt){return Tt.__proto__=wt,Tt}return yt}(),t(X,ut)}function e(){if(typeof Reflect=="undefined"||!Reflect.construct||Reflect.construct.sham)return!1;if(typeof Proxy=="function")return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){})),!0}catch(X){return!1}}function r(X,ut,yt){return e()?r=Reflect.construct:r=function(){function Tt(wt,jt,Ct){var lt=[null];lt.push.apply(lt,jt);var gt=Function.bind.apply(wt,lt),bt=new gt;return Ct&&t(bt,Ct.prototype),bt}return Tt}(),r.apply(null,arguments)}function o(X){return a(X)||s(X)||i(X)||g()}function a(X){if(Array.isArray(X))return c(X)}function s(X){if(typeof Symbol!="undefined"&&X[Symbol.iterator]!=null||X["@@iterator"]!=null)return Array.from(X)}function i(X,ut){if(X){if(typeof X=="string")return c(X,ut);var yt=Object.prototype.toString.call(X).slice(8,-1);if(yt==="Object"&&X.constructor&&(yt=X.constructor.name),yt==="Map"||yt==="Set")return Array.from(X);if(yt==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(yt))return c(X,ut)}}function c(X,ut){(ut==null||ut>X.length)&&(ut=X.length);for(var yt=0,Tt=new Array(ut);yt1?yt-1:0),wt=1;wt/gm),Pt=h(/\${[\w\W]*}/gm),Nt=h(/^data-[\-\w.\u00B7-\uFFFF]/),vt=h(/^aria-[\-\w]+$/),rt=h(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),ht=h(/^(?:\w+script|data):/i),q=h(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),_=h(/^html$/i),tt=h(/^[a-z][.\w]*(-[.\w]+)+$/i),mt=function(){function X(){return typeof window=="undefined"?null:window}return X}(),at=function(){function X(ut,yt){if(n(ut)!=="object"||typeof ut.createPolicy!="function")return null;var Tt=null,wt="data-tt-policy-suffix";yt.currentScript&&yt.currentScript.hasAttribute(wt)&&(Tt=yt.currentScript.getAttribute(wt));var jt="dompurify"+(Tt?"#"+Tt:"");try{return ut.createPolicy(jt,{createHTML:function(){function Ct(lt){return lt}return Ct}(),createScriptURL:function(){function Ct(lt){return lt}return Ct}()})}catch(Ct){return null}}return X}();function ot(){var X=arguments.length>0&&arguments[0]!==void 0?arguments[0]:mt(),ut=function(){function O(P){return ot(P)}return O}();if(ut.version="2.5.0",ut.removed=[],!X||!X.document||X.document.nodeType!==9)return ut.isSupported=!1,ut;var yt=X.document,Tt=X.document,wt=X.DocumentFragment,jt=X.HTMLTemplateElement,Ct=X.Node,lt=X.Element,gt=X.NodeFilter,bt=X.NamedNodeMap,St=bt===void 0?X.NamedNodeMap||X.MozNamedAttrMap:bt,It=X.HTMLFormElement,Ft=X.DOMParser,Vt=X.trustedTypes,Gt=lt.prototype,Ht=j(Gt,"cloneNode"),Kt=j(Gt,"nextSibling"),Wt=j(Gt,"childNodes"),Qt=j(Gt,"parentNode");if(typeof jt=="function"){var Le=Tt.createElement("template");Le.content&&Le.content.ownerDocument&&(Tt=Le.content.ownerDocument)}var _t=at(Vt,yt),Pe=_t?_t.createHTML(""):"",Ne=Tt,me=Ne.implementation,ye=Ne.createNodeIterator,an=Ne.createDocumentFragment,un=Ne.getElementsByTagName,Tn=yt.importNode,Ye={};try{Ye=V(Tt).documentMode?Tt.documentMode:{}}catch(O){}var re={};ut.isSupported=typeof Qt=="function"&&me&&me.createHTMLDocument!==void 0&&Ye!==9;var $e=pt,We=Ot,Be=Pt,sn=Nt,An=vt,cn=ht,ln=q,In=tt,Se=rt,zt=null,te=D({},[].concat(o(K),o($),o(W),o(ft),o(z))),Yt=null,Ee=D({},[].concat(o(J),o(nt),o(st),o(it))),kt=Object.seal(Object.create(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),le=null,Ce=null,He=!0,ze=!0,fn=!1,dn=!0,be=!1,De=!0,fe=!1,Fe=!1,xe=!1,de=!1,Xt=!1,Ve=!1,vn=!0,ke=!1,hn="user-content-",ue=!0,Me=!1,Te={},Ae=null,Xe=D({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","style","svg","template","thead","title","video","xmp"]),Je=null,gn=D({},["audio","video","img","source","image","track"]),je=null,pn=D({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),ee="http://www.w3.org/1998/Math/MathML",Ue="http://www.w3.org/2000/svg",se="http://www.w3.org/1999/xhtml",Ie=se,Ze=!1,Qe=null,On=D({},[ee,Ue,se],N),ce,Pn=["application/xhtml+xml","text/html"],mn="text/html",Jt,Oe=null,Nn=Tt.createElement("form"),yn=function(){function O(P){return P instanceof RegExp||P instanceof Function}return O}(),qe=function(){function O(P){Oe&&Oe===P||((!P||n(P)!=="object")&&(P={}),P=V(P),ce=Pn.indexOf(P.PARSER_MEDIA_TYPE)===-1?ce=mn:ce=P.PARSER_MEDIA_TYPE,Jt=ce==="application/xhtml+xml"?N:b,zt="ALLOWED_TAGS"in P?D({},P.ALLOWED_TAGS,Jt):te,Yt="ALLOWED_ATTR"in P?D({},P.ALLOWED_ATTR,Jt):Ee,Qe="ALLOWED_NAMESPACES"in P?D({},P.ALLOWED_NAMESPACES,N):On,je="ADD_URI_SAFE_ATTR"in P?D(V(pn),P.ADD_URI_SAFE_ATTR,Jt):pn,Je="ADD_DATA_URI_TAGS"in P?D(V(gn),P.ADD_DATA_URI_TAGS,Jt):gn,Ae="FORBID_CONTENTS"in P?D({},P.FORBID_CONTENTS,Jt):Xe,le="FORBID_TAGS"in P?D({},P.FORBID_TAGS,Jt):{},Ce="FORBID_ATTR"in P?D({},P.FORBID_ATTR,Jt):{},Te="USE_PROFILES"in P?P.USE_PROFILES:!1,He=P.ALLOW_ARIA_ATTR!==!1,ze=P.ALLOW_DATA_ATTR!==!1,fn=P.ALLOW_UNKNOWN_PROTOCOLS||!1,dn=P.ALLOW_SELF_CLOSE_IN_ATTR!==!1,be=P.SAFE_FOR_TEMPLATES||!1,De=P.SAFE_FOR_XML!==!1,fe=P.WHOLE_DOCUMENT||!1,de=P.RETURN_DOM||!1,Xt=P.RETURN_DOM_FRAGMENT||!1,Ve=P.RETURN_TRUSTED_TYPE||!1,xe=P.FORCE_BODY||!1,vn=P.SANITIZE_DOM!==!1,ke=P.SANITIZE_NAMED_PROPS||!1,ue=P.KEEP_CONTENT!==!1,Me=P.IN_PLACE||!1,Se=P.ALLOWED_URI_REGEXP||Se,Ie=P.NAMESPACE||se,kt=P.CUSTOM_ELEMENT_HANDLING||{},P.CUSTOM_ELEMENT_HANDLING&&yn(P.CUSTOM_ELEMENT_HANDLING.tagNameCheck)&&(kt.tagNameCheck=P.CUSTOM_ELEMENT_HANDLING.tagNameCheck),P.CUSTOM_ELEMENT_HANDLING&&yn(P.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)&&(kt.attributeNameCheck=P.CUSTOM_ELEMENT_HANDLING.attributeNameCheck),P.CUSTOM_ELEMENT_HANDLING&&typeof P.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements=="boolean"&&(kt.allowCustomizedBuiltInElements=P.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements),be&&(ze=!1),Xt&&(de=!0),Te&&(zt=D({},o(z)),Yt=[],Te.html===!0&&(D(zt,K),D(Yt,J)),Te.svg===!0&&(D(zt,$),D(Yt,nt),D(Yt,it)),Te.svgFilters===!0&&(D(zt,W),D(Yt,nt),D(Yt,it)),Te.mathMl===!0&&(D(zt,ft),D(Yt,st),D(Yt,it))),P.ADD_TAGS&&(zt===te&&(zt=V(zt)),D(zt,P.ADD_TAGS,Jt)),P.ADD_ATTR&&(Yt===Ee&&(Yt=V(Yt)),D(Yt,P.ADD_ATTR,Jt)),P.ADD_URI_SAFE_ATTR&&D(je,P.ADD_URI_SAFE_ATTR,Jt),P.FORBID_CONTENTS&&(Ae===Xe&&(Ae=V(Ae)),D(Ae,P.FORBID_CONTENTS,Jt)),ue&&(zt["#text"]=!0),fe&&D(zt,["html","head","body"]),zt.table&&(D(zt,["tbody"]),delete le.tbody),d&&d(P),Oe=P)}return O}(),Sn=D({},["mi","mo","mn","ms","mtext"]),oe=D({},["foreignobject","desc","title","annotation-xml"]),Ge=D({},["title","style","font","a","script"]),Re=D({},$);D(Re,W),D(Re,et);var _e=D({},ft);D(_e,dt);var Mn=function(){function O(P){var w=Qt(P);(!w||!w.tagName)&&(w={namespaceURI:Ie,tagName:"template"});var F=b(P.tagName),H=b(w.tagName);return Qe[P.namespaceURI]?P.namespaceURI===Ue?w.namespaceURI===se?F==="svg":w.namespaceURI===ee?F==="svg"&&(H==="annotation-xml"||Sn[H]):!!Re[F]:P.namespaceURI===ee?w.namespaceURI===se?F==="math":w.namespaceURI===Ue?F==="math"&&oe[H]:!!_e[F]:P.namespaceURI===se?w.namespaceURI===Ue&&!oe[H]||w.namespaceURI===ee&&!Sn[H]?!1:!_e[F]&&(Ge[F]||!Re[F]):!!(ce==="application/xhtml+xml"&&Qe[P.namespaceURI]):!1}return O}(),ne=function(){function O(P){S(ut.removed,{element:P});try{P.parentNode.removeChild(P)}catch(w){try{P.outerHTML=Pe}catch(F){P.remove()}}}return O}(),Ke=function(){function O(P,w){try{S(ut.removed,{attribute:w.getAttributeNode(P),from:w})}catch(F){S(ut.removed,{attribute:null,from:w})}if(w.removeAttribute(P),P==="is"&&!Yt[P])if(de||Xt)try{ne(w)}catch(F){}else try{w.setAttribute(P,"")}catch(F){}}return O}(),En=function(){function O(P){var w,F;if(xe)P=""+P;else{var H=M(P,/^[\r\n\t ]+/);F=H&&H[0]}ce==="application/xhtml+xml"&&Ie===se&&(P=''+P+"");var Z=_t?_t.createHTML(P):P;if(Ie===se)try{w=new Ft().parseFromString(Z,ce)}catch(ct){}if(!w||!w.documentElement){w=me.createDocument(Ie,"template",null);try{w.documentElement.innerHTML=Ze?Pe:Z}catch(ct){}}var Q=w.body||w.documentElement;return P&&F&&Q.insertBefore(Tt.createTextNode(F),Q.childNodes[0]||null),Ie===se?un.call(w,fe?"html":"body")[0]:fe?w.documentElement:Q}return O}(),we=function(){function O(P){return ye.call(P.ownerDocument||P,P,gt.SHOW_ELEMENT|gt.SHOW_COMMENT|gt.SHOW_TEXT|gt.SHOW_PROCESSING_INSTRUCTION|gt.SHOW_CDATA_SECTION,null,!1)}return O}(),Rn=function(){function O(P){return P instanceof It&&(typeof P.nodeName!="string"||typeof P.textContent!="string"||typeof P.removeChild!="function"||!(P.attributes instanceof St)||typeof P.removeAttribute!="function"||typeof P.setAttribute!="function"||typeof P.namespaceURI!="string"||typeof P.insertBefore!="function"||typeof P.hasChildNodes!="function")}return O}(),he=function(){function O(P){return n(Ct)==="object"?P instanceof Ct:P&&n(P)==="object"&&typeof P.nodeType=="number"&&typeof P.nodeName=="string"}return O}(),ae=function(){function O(P,w,F){re[P]&&T(re[P],function(H){H.call(ut,w,F,Oe)})}return O}(),Cn=function(){function O(P){var w;if(ae("beforeSanitizeElements",P,null),Rn(P)||U(/[\u0080-\uFFFF]/,P.nodeName))return ne(P),!0;var F=Jt(P.nodeName);if(ae("uponSanitizeElement",P,{tagName:F,allowedTags:zt}),P.hasChildNodes()&&!he(P.firstElementChild)&&(!he(P.content)||!he(P.content.firstElementChild))&&U(/<[/\w]/g,P.innerHTML)&&U(/<[/\w]/g,P.textContent)||F==="select"&&U(/