diff --git a/SQL/migrate/V045__underwear_updates.sql b/SQL/migrate/V045__underwear_updates.sql
new file mode 100644
index 00000000000..194879831f3
--- /dev/null
+++ b/SQL/migrate/V045__underwear_updates.sql
@@ -0,0 +1,10 @@
+--
+-- Adds support for underwear updates in PR #6973.
+-- This collates all underwear data into one column, with an additional column for custom colours.
+--
+
+ALTER TABLE `ss13_characters`
+ CHANGE COLUMN `underwear` `all_underwear` JSON NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci' AFTER `eyes_colour`,
+ DROP COLUMN `undershirt`,
+ DROP COLUMN `socks`,
+ ADD COLUMN `all_underwear_metadata` JSON NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci' AFTER `all_underwear`;
diff --git a/aurorastation.dme b/aurorastation.dme
index 82fab7eeab7..e34455384ae 100644
--- a/aurorastation.dme
+++ b/aurorastation.dme
@@ -60,6 +60,7 @@
#include "code\__defines\vueui.dm"
#include "code\__defines\webhook.dm"
#include "code\__defines\ZAS.dm"
+#include "code\_helpers\_global_objects.dm"
#include "code\_helpers\_string_lists.dm"
#include "code\_helpers\area_movement.dm"
#include "code\_helpers\areas.dm"
@@ -351,6 +352,11 @@
#include "code\datums\trading\unique.dm"
#include "code\datums\trading\vox.dm"
#include "code\datums\trading\weaponry.dm"
+#include "code\datums\underwear\bottom.dm"
+#include "code\datums\underwear\socks.dm"
+#include "code\datums\underwear\top.dm"
+#include "code\datums\underwear\undershirt.dm"
+#include "code\datums\underwear\underwear.dm"
#include "code\datums\uplink\ammunition.dm"
#include "code\datums\uplink\announcements.dm"
#include "code\datums\uplink\badassery.dm"
diff --git a/code/_helpers/_global_objects.dm b/code/_helpers/_global_objects.dm
new file mode 100644
index 00000000000..4a77edce5fb
--- /dev/null
+++ b/code/_helpers/_global_objects.dm
@@ -0,0 +1,3 @@
+//Gear tweaks and underwear.
+var/datum/gear_tweak/color/gear_tweak_free_color_choice = new()
+var/datum/category_collection/underwear/global_underwear = new()
diff --git a/code/_helpers/global_lists.dm b/code/_helpers/global_lists.dm
index 78733de2979..b642bc8c198 100644
--- a/code/_helpers/global_lists.dm
+++ b/code/_helpers/global_lists.dm
@@ -46,7 +46,7 @@ var/global/list/poster_designs = list()
var/list/obj/item/device/uplink/world_uplinks = list()
//Preferences stuff
- //Hairstyles
+//Hairstyles
var/global/list/hair_styles_list = list() //stores /datum/sprite_accessory/hair indexed by name
var/global/list/hair_styles_male_list = list()
var/global/list/hair_styles_female_list = list()
@@ -58,34 +58,7 @@ var/global/list/body_marking_styles_list = list()
var/global/list/chargen_disabilities_list = list()
var/global/static/list/valid_player_genders = list(MALE, FEMALE, NEUTER)
- //Underwear
-var/global/list/underwear_m = list("White" = "m1", "Grey" = "m2", "Green" = "m3", "Blue" = "m4", "Black" = "m5", "Mankini" = "m6", "Boxers" = "boxers", "Green and blue boxers" = "boxers_green_and_blue","Loveheart boxers" = "boxers_loveheart","None") //Curse whoever made male/female underwear diffrent colours
-var/global/list/underwear_f = list("Red" = "f1", "White" = "f2", "Yellow" = "f3", "Blue" = "f4", "Black" = "f5", "Thong" = "f6", "Black Sports" = "f7","White Sports" = "f8","None")
- //undershirt
-var/global/list/undershirt_t = list(
- "White tank top" = "u1", "Black tank top" = "u2", "Black shirt" = "u3", "White shirt" = "u4",
- "Polo" = "polo", "Blue polo" = "bluepolo_s", "Red polo" = "redpolo_s", "Yellow polo" = "grayyellowpolo_s",
- "Striped shirt" = "shirt_stripes_s", "NanoTrasen shirt" = "shirt_nano_s", "Tiedye shirt" = "shirt_tiedye_s",
- "Green sport shirt" = "greenshirtsport_s", "Red sport shirt" = "redshirtsport_s", "Blue sport shirt" = "blueshirtsport_s",
- "Striped tank top " = "tank_stripes_s", "Rainbow tank top" = "tank_rainbow_s", "Longsleeve shirt" = "undershirt_long",
- "Striped longsleve shirt" = "longstripe", "Blue striped longsleve shirt" = "longstripe_blue", "None")
-
- //socks
-var/global/list/socks_f = list(
- "White normal" = "white_norm", "White short" = "white_short", "White knee" = "white_knee",
- "White thigh" = "white_thigh", "Black normal" = "black_norm", "Black short" = "black_short",
- "Black knee" = "black_knee", "Black thigh" = "black_thigh", "Striped normal" = "striped_norm",
- "Striped short" = "striped_short", "Striped knee" = "striped_knee", "Striped thigh" = "striped_thigh",
- "Rainbow normal" = "rainbow_norm", "Rainbow short" = "rainbow_short", "Rainbow knee" = "rainbow_knee",
- "Rainbow thigh" = "rainbow_thigh", "Pantyhose" = "pantyhose", "None")
-
-var/global/list/socks_m = list(
- "White normal" = "white_norm", "White short" = "white_short", "White knee" = "white_knee",
- "Black normal" = "black_norm", "Black short" = "black_short", "Black knee" = "black_knee",
- "Striped normal" = "striped_norm", "Striped short" = "striped_short", "Striped knee" = "striped_knee",
- "Rainbow normal" = "rainbow_norm", "Rainbow short" = "rainbow_short", "Rainbow knee" = "rainbow_knee", "None")
-
- //Backpacks
+//Backpacks
var/global/list/backbaglist = list("Nothing", "Backpack", "Satchel", "Satchel Alt", "Duffel Bag", "Messenger Bag")
var/global/list/backbagstyles = list("Job-specific", "Grey")
var/global/list/exclude_jobs = list(/datum/job/ai,/datum/job/cyborg, /datum/job/merchant)
diff --git a/code/_helpers/lists.dm b/code/_helpers/lists.dm
index 585d574cdb1..de0a9c6cb18 100644
--- a/code/_helpers/lists.dm
+++ b/code/_helpers/lists.dm
@@ -285,6 +285,122 @@
if(L[key] == value)
return key
+/proc/dd_sortedObjectList(var/list/L, var/cache=list())
+ if(L.len < 2)
+ return L
+ var/middle = L.len / 2 + 1 // Copy is first,second-1
+ return dd_mergeObjectList(dd_sortedObjectList(L.Copy(0,middle), cache), dd_sortedObjectList(L.Copy(middle), cache), cache) //second parameter null = to end of list
+
+/proc/dd_mergeObjectList(var/list/L, var/list/R, var/list/cache)
+ var/Li=1
+ var/Ri=1
+ var/list/result = new()
+ while(Li <= L.len && Ri <= R.len)
+ var/LLi = L[Li]
+ var/RRi = R[Ri]
+ var/LLiV = cache[LLi]
+ var/RRiV = cache[RRi]
+ if(!LLiV)
+ LLiV = LLi:dd_SortValue()
+ cache[LLi] = LLiV
+ if(!RRiV)
+ RRiV = RRi:dd_SortValue()
+ cache[RRi] = RRiV
+ if(LLiV < RRiV)
+ result += L[Li++]
+ else
+ result += R[Ri++]
+
+ if(Li <= L.len)
+ return (result + L.Copy(Li, 0))
+ return (result + R.Copy(Ri, 0))
+
+// Insert an object into a sorted list, preserving sortedness
+/proc/dd_insertObjectList(var/list/L, var/O)
+ var/min = 1
+ var/max = L.len
+ var/Oval = O:dd_SortValue()
+
+ while(1)
+ var/mid = min+round((max-min)/2)
+
+ if(mid == max)
+ L.Insert(mid, O)
+ return
+
+ var/Lmid = L[mid]
+ var/midval = Lmid:dd_SortValue()
+ if(Oval == midval)
+ L.Insert(mid, O)
+ return
+ else if(Oval < midval)
+ max = mid
+ else
+ min = mid+1
+
+/proc/dd_sortedtextlist(list/incoming, case_sensitive = 0)
+ // Returns a new list with the text values sorted.
+ // Use binary search to order by sortValue.
+ // This works by going to the half-point of the list, seeing if the node in question is higher or lower cost,
+ // then going halfway up or down the list and checking again.
+ // This is a very fast way to sort an item into a list.
+ var/list/sorted_text = new()
+ var/low_index
+ var/high_index
+ var/insert_index
+ var/midway_calc
+ var/current_index
+ var/current_item
+ var/list/list_bottom
+ var/sort_result
+
+ var/current_sort_text
+ for (current_sort_text in incoming)
+ low_index = 1
+ high_index = sorted_text.len
+ while (low_index <= high_index)
+ // Figure out the midpoint, rounding up for fractions. (BYOND rounds down, so add 1 if necessary.)
+ midway_calc = (low_index + high_index) / 2
+ current_index = round(midway_calc)
+ if (midway_calc > current_index)
+ current_index++
+ current_item = sorted_text[current_index]
+
+ if (case_sensitive)
+ sort_result = sorttextEx(current_sort_text, current_item)
+ else
+ sort_result = sorttext(current_sort_text, current_item)
+
+ switch(sort_result)
+ if (1)
+ high_index = current_index - 1 // current_sort_text < current_item
+ if (-1)
+ low_index = current_index + 1 // current_sort_text > current_item
+ if (0)
+ low_index = current_index // current_sort_text == current_item
+ break
+
+ // Insert before low_index.
+ insert_index = low_index
+
+ // Special case adding to end of list.
+ if (insert_index > sorted_text.len)
+ sorted_text += current_sort_text
+ continue
+
+ // Because BYOND lists don't support insert, have to do it by:
+ // 1) taking out bottom of list, 2) adding item, 3) putting back bottom of list.
+ list_bottom = sorted_text.Copy(insert_index)
+ sorted_text.Cut(insert_index)
+ sorted_text += current_sort_text
+ sorted_text += list_bottom
+ return sorted_text
+
+
+/proc/dd_sortedTextList(list/incoming)
+ var/case_sensitive = 1
+ return dd_sortedtextlist(incoming, case_sensitive)
+
/proc/count_by_type(var/list/L, type)
var/i = 0
for(var/T in L)
@@ -477,7 +593,7 @@
if (isnull(subject))
return
-
+
var/final = keys[keys.len]
if (isdatum(subject))
subject_d = subject
@@ -522,7 +638,7 @@
if (isnull(subject))
return
-
+
var/final = keys[keys.len]
if (isdatum(subject))
subject_d = subject
@@ -573,4 +689,4 @@
. = list()
for(var/entry in L)
if(istype(entry, type))
- . += entry
\ No newline at end of file
+ . += entry
diff --git a/code/_onclick/hud/human.dm b/code/_onclick/hud/human.dm
index 406860083c0..8f768afe07c 100644
--- a/code/_onclick/hud/human.dm
+++ b/code/_onclick/hud/human.dm
@@ -291,7 +291,7 @@
mymob.nutrition_icon.name = "nutrition"
mymob.nutrition_icon.screen_loc = ui_nutrition
hud_elements |= mymob.nutrition_icon
-
+
if(hud_data.has_hydration)
mymob.hydration_icon = new /obj/screen()
mymob.hydration_icon.icon = ui_style
@@ -393,7 +393,5 @@
f_style = "Shaved"
if(dna.species == "Human") //no more xenos losing ears/tentacles
h_style = pick("Bedhead", "Bedhead 2", "Bedhead 3")
- undershirt = null
- underwear = null
- socks = null
+ all_underwear.Cut()
regenerate_icons()
diff --git a/code/controllers/subsystems/garbage-debug.dm b/code/controllers/subsystems/garbage-debug.dm
index 010c8ffb509..2d70f5223bb 100644
--- a/code/controllers/subsystems/garbage-debug.dm
+++ b/code/controllers/subsystems/garbage-debug.dm
@@ -312,11 +312,6 @@
SearchVar(facial_hair_styles_female_list)
SearchVar(skin_styles_female_list)
SearchVar(body_marking_styles_list)
- SearchVar(underwear_m)
- SearchVar(underwear_f)
- SearchVar(undershirt_t)
- SearchVar(socks_f)
- SearchVar(socks_m)
SearchVar(backbaglist)
SearchVar(exclude_jobs)
SearchVar(visual_nets)
diff --git a/code/datums/category.dm b/code/datums/category.dm
index 9542f6a3e94..b2a03a43327 100644
--- a/code/datums/category.dm
+++ b/code/datums/category.dm
@@ -4,16 +4,19 @@
/datum/category_collection
var/category_group_type // The type of categories to initialize
var/list/datum/category_group/categories // The list of initialized categories
+ var/list/datum/category_group/categories_by_name //Associative list of initialised categories, by name
/datum/category_collection/New()
..()
categories = new()
+ categories_by_name = new()
for(var/category_type in typesof(category_group_type))
var/datum/category_group/category = category_type
if(initial(category.name))
category = new category(src)
categories += category
- sortTim(categories, /proc/cmp_name_asc, FALSE)
+ categories_by_name[category.name] = category
+ categories = dd_sortedObjectList(categories)
/datum/category_collection/Destroy()
for(var/category in categories)
@@ -28,21 +31,24 @@
var/name = ""
var/category_item_type // The type of items to initialize
var/list/datum/category_item/items // The list of initialized items
+ var/list/datum/category_item/items_by_name // Associative list of initialised items, by name
var/datum/category_collection/collection // The collection this group belongs to
/datum/category_group/New(var/datum/category_collection/cc)
..()
collection = cc
items = new()
+ items_by_name = new()
for(var/item_type in typesof(category_item_type))
var/datum/category_item/item = item_type
if(initial(item.name))
item = new item(src)
items += item
+ items_by_name[item.name] = item
// If you change this, confirm that character setup doesn't become completely unordered.
- sortTim(items, /proc/cmp_player_setup_group)
+ items = dd_sortedObjectList(items)
/datum/category_group/Destroy()
for(var/item in items)
@@ -51,6 +57,9 @@
collection = null
return ..()
+/datum/category_group/dd_SortValue()
+ return name
+
/*****************
* Category Items *
*****************/
@@ -65,3 +74,6 @@
/datum/category_item/Destroy()
category = null
return ..()
+
+/datum/category_item/dd_SortValue()
+ return name
diff --git a/code/datums/underwear/bottom.dm b/code/datums/underwear/bottom.dm
new file mode 100644
index 00000000000..5956b2436d4
--- /dev/null
+++ b/code/datums/underwear/bottom.dm
@@ -0,0 +1,113 @@
+/datum/category_item/underwear/bottom/none
+ name = "None"
+ always_last = TRUE
+
+/datum/category_item/underwear/bottom/briefs
+ name = "Briefs"
+ icon_state = "briefs"
+ has_color = TRUE
+
+/datum/category_item/underwear/bottom/briefs/is_default(var/gender)
+ return gender != FEMALE
+
+/datum/category_item/underwear/bottom/boxers
+ name = "Boxers"
+ icon_state = "boxers"
+ has_color = TRUE
+
+/datum/category_item/underwear/bottom/boxers_striped
+ name = "Boxers, Striped"
+ icon_state = "boxers_striped"
+ has_color = TRUE
+
+/datum/category_item/underwear/bottom/boxers_green_and_blue
+ name = "Boxers, Green & Blue Striped"
+ icon_state = "boxers_green_and_blue"
+
+/datum/category_item/underwear/bottom/boxers_loveheart
+ name = "Boxers, Loveheart"
+ icon_state = "boxers_loveheart"
+ has_color = TRUE
+
+/datum/category_item/underwear/bottom/panties
+ name = "Panties"
+ icon_state = "panties"
+ has_color = TRUE
+
+/datum/category_item/underwear/bottom/panties/is_default(var/gender)
+ return gender == FEMALE
+
+/datum/category_item/underwear/bottom/panties_striped
+ name = "Panties, Striped"
+ icon_state = "panties_striped"
+ has_color = TRUE
+
+/datum/category_item/underwear/bottom/panties_bikini
+ name = "Panties, Bikini"
+ icon_state = "panties_bikini"
+ has_color = TRUE
+
+/datum/category_item/underwear/bottom/panties_hipster
+ name = "Panties, Hipster"
+ icon_state = "panties_hipster"
+ has_color = TRUE
+
+/datum/category_item/underwear/bottom/compression_shorts
+ name = "Compression Shorts"
+ icon_state = "compression_shorts"
+ has_color = TRUE
+
+/datum/category_item/underwear/bottom/compression_shorts_alt
+ name = "Compression Shorts, Alt"
+ icon_state = "compression_shorts_alt"
+ has_color = TRUE
+
+/datum/category_item/underwear/bottom/shorts_army
+ name = "Shorts, Army"
+ icon_state = "shorts_army"
+
+/datum/category_item/underwear/bottom/shorts_expedition
+ name = "Shorts, Expedition"
+ icon_state = "shorts_expedition"
+
+/datum/category_item/underwear/bottom/shorts_fleet
+ name = "Shorts, Fleet"
+ icon_state = "shorts_fleet"
+
+/datum/category_item/underwear/bottom/shorts_necropolis
+ name = "Shorts, Necropolis Industries"
+ icon_state = "shorts_necropolis"
+
+/datum/category_item/underwear/bottom/shorts_epmc
+ name = "Shorts, EPMC"
+ icon_state = "shorts_epmc"
+
+/datum/category_item/underwear/bottom/midway
+ name = "Midway Briefs"
+ icon_state = "midway"
+ has_color = TRUE
+
+/datum/category_item/underwear/bottom/longjohnbottoms
+ name = "Long John Bottoms"
+ icon_state = "ljonb"
+ has_color = TRUE
+
+/datum/category_item/underwear/bottom/longjohnbottoms_alt
+ name = "Long John Bottoms, Alt"
+ icon_state = "ljonb_alt"
+ has_color = TRUE
+
+/datum/category_item/underwear/bottom/onepiece
+ name = "One Piece"
+ icon_state = "onepiece"
+ has_color = TRUE
+
+/datum/category_item/underwear/bottom/onepiece_striped
+ name = "One Piece, Striped"
+ icon_state = "onepiece_striped"
+ has_color = TRUE
+
+/datum/category_item/underwear/bottom/swimbottom
+ name = "Swimming Bottom"
+ icon_state = "swimbottom"
+ has_color = TRUE
\ No newline at end of file
diff --git a/code/datums/underwear/socks.dm b/code/datums/underwear/socks.dm
new file mode 100644
index 00000000000..e17029a46c9
--- /dev/null
+++ b/code/datums/underwear/socks.dm
@@ -0,0 +1,185 @@
+/datum/category_item/underwear/socks/none
+ always_last = TRUE
+ name = "None"
+
+/datum/category_item/underwear/socks/short
+ name = "Short"
+ icon_state = "socks_short"
+ has_color = TRUE
+
+/datum/category_item/underwear/socks/striped_short
+ name = "Short, Striped"
+ icon_state = "striped_short"
+ has_color = TRUE
+
+/datum/category_item/underwear/socks/thin_short
+ name = "Short, Thin"
+ icon_state = "thin_short"
+ has_color = TRUE
+
+/datum/category_item/underwear/socks/bee_short
+ name = "Short, Bee"
+ icon_state = "bee_short"
+
+/datum/category_item/underwear/socks/woolblueorange_short
+ name = "Short, Wool Blue/Orange"
+ icon_state = "woolbo_short"
+
+/datum/category_item/underwear/socks/woolredblue_short
+ name = "Short, Wool Red/Blue"
+ icon_state = "woolrb_short"
+
+/datum/category_item/underwear/socks/spider_short
+ name = "Short, Spider"
+ icon_state = "spider_short"
+
+/datum/category_item/underwear/socks/rainbow_short
+ name = "Short, Rainbow"
+ icon_state = "rainbow_short"
+
+/datum/category_item/underwear/socks/normal
+ name = "Normal"
+ icon_state = "socks_norm"
+ has_color = TRUE
+
+/datum/category_item/underwear/socks/striped_normal
+ name = "Normal, Striped"
+ icon_state = "striped_norm"
+ has_color = TRUE
+
+/datum/category_item/underwear/socks/thin_normal
+ name = "Normal, Thin"
+ icon_state = "thin_normal"
+ has_color = TRUE
+
+/datum/category_item/underwear/socks/bee_normal
+ name = "Normal, Bee"
+ icon_state = "bee_normal"
+
+/datum/category_item/underwear/socks/woolblueorange_normal
+ name = "Normal, Wool Blue/Orange"
+ icon_state = "woolbo_normal"
+
+/datum/category_item/underwear/socks/woolredblue_normal
+ name = "Normal, Wool Red/Blue"
+ icon_state = "woolrb_normal"
+
+/datum/category_item/underwear/socks/spider_normal
+ name = "Normal, Spider"
+ icon_state = "spider_normal"
+
+/datum/category_item/underwear/socks/rainbow_normal
+ name = "Normal, Rainbow"
+ icon_state = "rainbow_normal"
+
+/datum/category_item/underwear/socks/knee
+ name = "Knee"
+ icon_state = "socks_knee"
+ has_color = TRUE
+
+/datum/category_item/underwear/socks/striped_knee
+ name = "Knee, Striped"
+ icon_state = "striped_knee"
+ has_color = TRUE
+
+/datum/category_item/underwear/socks/thin_knee
+ name = "Knee, Thin"
+ icon_state = "thin_knee"
+ has_color = TRUE
+
+/datum/category_item/underwear/socks/bee_knee
+ name = "Knee, Bee"
+ icon_state = "bee_knee"
+
+/datum/category_item/underwear/socks/woolblueorange_knee
+ name = "Knee, Wool Blue/Orange"
+ icon_state = "woolbo_knee"
+
+/datum/category_item/underwear/socks/woolredblue_knee
+ name = "Knee, Wool Red/Blue"
+ icon_state = "woolrb_knee"
+
+/datum/category_item/underwear/socks/spider_knee
+ name = "Knee, Spider"
+ icon_state = "spider_knee"
+
+/datum/category_item/underwear/socks/rainbow_knee
+ name = "Knee, Rainbow"
+ icon_state = "rainbow_knee"
+
+/datum/category_item/underwear/socks/thigh
+ name = "Thigh"
+ icon_state = "socks_thigh"
+ has_color = TRUE
+
+/datum/category_item/underwear/socks/striped_thigh
+ name = "Thigh, Striped"
+ icon_state = "striped_thigh"
+ has_color = TRUE
+
+/datum/category_item/underwear/socks/thin_thigh
+ name = "Thigh, Thin"
+ icon_state = "thin_thigh"
+ has_color = TRUE
+
+/datum/category_item/underwear/socks/bee_thigh
+ name = "Thigh, Bee"
+ icon_state = "bee_thigh"
+
+/datum/category_item/underwear/socks/woolblueorange_thigh
+ name = "Thigh, Wool Blue/Orange"
+ icon_state = "woolbo_thigh"
+
+/datum/category_item/underwear/socks/woolredblue_thigh
+ name = "Thigh, Wool Red/Blue"
+ icon_state = "woolrb_thigh"
+
+/datum/category_item/underwear/socks/spider_thigh
+ name = "Thigh, Spider"
+ icon_state = "spider_thigh"
+
+/datum/category_item/underwear/socks/rainbow_thigh
+ name = "Thigh, Rainbow"
+ icon_state = "rainbow_thigh"
+
+/datum/category_item/underwear/socks/leggings
+ name = "Leggings"
+ icon_state = "leggings"
+ has_color = TRUE
+
+/datum/category_item/underwear/socks/pantyhose
+ name = "Pantyhose"
+ icon_state = "pantyhose"
+ has_color = TRUE
+
+/datum/category_item/underwear/socks/stockings_blue
+ name = "Stockings, Blue"
+ icon_state = "stockings_blue"
+
+/datum/category_item/underwear/socks/stockings_cyan
+ name = "Stockings, Cyan"
+ icon_state = "stockings_cyan"
+
+/datum/category_item/underwear/socks/stockings_dpink
+ name = "Stockings, Dark Pink"
+ icon_state = "stockings_dpink"
+
+/datum/category_item/underwear/socks/stockings_green
+ name = "Stockings, Green"
+ icon_state = "stockings_green"
+
+/datum/category_item/underwear/socks/stockings_lpink
+ name = "Stockings, Light Pink"
+ icon_state = "stockings_lpink"
+
+/datum/category_item/underwear/socks/stockings_orange
+ name = "Stockings, Orange"
+ icon_state = "stockings_orange"
+
+/datum/category_item/underwear/socks/stockings_purple
+ name = "Stockings, Purple"
+ icon_state = "stockings_purple"
+
+/datum/category_item/underwear/socks/stockings_yellow
+ name = "Stockings, Yellow"
+ icon_state = "stockings_yellow"
\ No newline at end of file
diff --git a/code/datums/underwear/top.dm b/code/datums/underwear/top.dm
new file mode 100644
index 00000000000..239cbe7ec7b
--- /dev/null
+++ b/code/datums/underwear/top.dm
@@ -0,0 +1,53 @@
+/datum/category_item/underwear/top/none
+ name = "None"
+ always_last = TRUE
+
+/datum/category_item/underwear/top/none/is_default(var/gender)
+ return gender != FEMALE
+
+/datum/category_item/underwear/top/bra
+ is_default = TRUE
+ name = "Bra"
+ icon_state = "bra"
+ has_color = TRUE
+
+/datum/category_item/underwear/top/bra/is_default(var/gender)
+ return gender == FEMALE
+
+/datum/category_item/underwear/top/bra_bikini
+ name = "Bra, Bikini"
+ icon_state = "bra_bikini"
+ has_color = TRUE
+
+/datum/category_item/underwear/top/bra_bralette
+ name = "Bra, Bralette"
+ icon_state = "bra_bralette"
+ has_color = TRUE
+
+/datum/category_item/underwear/top/bra_striped
+ name = "Bra, Striped"
+ icon_state = "bra_striped"
+ has_color = TRUE
+
+/datum/category_item/underwear/top/sports_bra
+ name = "Sports Bra"
+ icon_state = "sports_bra"
+ has_color = TRUE
+
+/datum/category_item/underwear/top/binder
+ name = "Binder"
+ icon_state = "binder"
+
+/datum/category_item/underwear/top/binder_strapless
+ name = "Binder, Strapless"
+ icon_state = "binder_strapless"
+
+/datum/category_item/underwear/top/tubetop
+ name = "Tube Top"
+ icon_state = "tubetop"
+ has_color = TRUE
+
+/datum/category_item/underwear/top/swimtop
+ name = "Swimming Top"
+ icon_state = "swimtop"
+ has_color = TRUE
diff --git a/code/datums/underwear/undershirt.dm b/code/datums/underwear/undershirt.dm
new file mode 100644
index 00000000000..da045f99e41
--- /dev/null
+++ b/code/datums/underwear/undershirt.dm
@@ -0,0 +1,417 @@
+/datum/category_item/underwear/undershirt/none
+ is_default = TRUE
+ name = "None"
+ always_last = TRUE
+
+/datum/category_item/underwear/undershirt/tank_top
+ name = "Tank Top"
+ icon_state = "tanktop"
+ has_color = TRUE
+
+/datum/category_item/underwear/undershirt/tank_top_fem
+ name = "Tank Top, Feminine"
+ icon_state = "tanktop_fem"
+ has_color = TRUE
+
+/datum/category_item/underwear/undershirt/tank_top_alt
+ name = "Tank Top, Alt"
+ icon_state = "tanktop_alt"
+ has_color = TRUE
+
+/datum/category_item/underwear/undershirt/tank_top_alt_fem
+ name = "Tank Top, Alt Feminine"
+ icon_state = "tanktop_alt_fem"
+ has_color = TRUE
+
+/datum/category_item/underwear/undershirt/tank_top_alt_vneck
+ name = "Tank Top, Alt V-Neck"
+ icon_state = "tanktop_alt_vneck"
+ has_color = TRUE
+
+/datum/category_item/underwear/undershirt/tank_top_alt_vneck_fem
+ name = "Tank Top, Alt V-Neck Feminine"
+ icon_state = "tanktop_alt_vneck_fem"
+ has_color = TRUE
+
+/datum/category_item/underwear/undershirt/tank_top_midriff
+ name = "Tank Top, Midriff"
+ icon_state = "tank_midriff"
+ has_color = TRUE
+
+/datum/category_item/underwear/undershirt/tank_top_midriff_short
+ name = "Tank Top, Midriff Short"
+ icon_state = "tank_midriff_short"
+ has_color = TRUE
+
+/datum/category_item/underwear/undershirt/tank_top_expedition
+ name = "Tank Top, Expedition"
+ icon_state = "tank_expedition"
+
+/datum/category_item/underwear/undershirt/tank_top_expedition_fem
+ name = "Tank Top, Expedition Feminine"
+ icon_state = "tank_expedition_fem"
+
+/datum/category_item/underwear/undershirt/tank_top_fleet
+ name = "Tank Top, Fleet"
+ icon_state = "tank_fleet"
+
+/datum/category_item/underwear/undershirt/tank_top_fleet_fem
+ name = "Tank Top, Fleet Feminine"
+ icon_state = "tank_fleet_fem"
+
+/datum/category_item/underwear/undershirt/tank_top_fire
+ name = "Tank Top, Fire"
+ icon_state = "tank_fire"
+
+/datum/category_item/underwear/undershirt/tank_top_fire_fem
+ name = "Tank Top, Fire Feminine"
+ icon_state = "tank_fire_fem"
+
+/datum/category_item/underwear/undershirt/tank_top_stripes
+ name = "Tank Top, Stripes"
+ icon_state = "tank_stripes"
+
+/datum/category_item/underwear/undershirt/tank_top_stripes_fem
+ name = "Tank Top, Stripes Feminine"
+ icon_state = "tank_stripes_fem"
+
+/datum/category_item/underwear/undershirt/tank_top_stripes_alt
+ name = "Tank Top, Stripes Alt"
+ icon_state = "tank_stripes_alt"
+
+/datum/category_item/underwear/undershirt/tank_top_stripes_alt_fem
+ name = "Tank Top, Stripes Alt Feminine"
+ icon_state = "tank_stripes_alt_fem"
+
+/datum/category_item/underwear/undershirt/tank_top_sun
+ name = "Tank Top, Sun"
+ icon_state = "tank_sun"
+
+/datum/category_item/underwear/undershirt/tank_top_sun_fem
+ name = "Tank Top, Sun Feminine"
+ icon_state = "tank_sun_fem"
+
+/datum/category_item/underwear/undershirt/tank_top_sun_alt
+ name = "Tank Top, Sun Alt"
+ icon_state = "tank_sun_alt"
+
+/datum/category_item/underwear/undershirt/tank_top_sun_alt_fem
+ name = "Tank Top, Sun Alt Feminine"
+ icon_state = "tank_sun_alt_fem"
+
+/datum/category_item/underwear/undershirt/tank_top_tight
+ name = "Tank Top, Tight"
+ icon_state = "tank_tight"
+
+/datum/category_item/underwear/undershirt/tank_top_tight_fem
+ name = "Tank Top, Tight Feminine"
+ icon_state = "tank_tight_fem"
+
+/datum/category_item/underwear/undershirt/shortsleeve_undershirt
+ name = "Shortsleeve Undershirt"
+ icon_state = "shortsleeve"
+ has_color = TRUE
+
+/datum/category_item/underwear/undershirt/shortsleeve_undershirt_fem
+ name = "Shortsleeve Undershirt, Feminine"
+ icon_state = "shortsleeve_fem"
+ has_color = TRUE
+
+/datum/category_item/underwear/undershirt/shortsleeve_undershirt_vneck
+ name = "Shortsleeve Undershirt, V-Neck"
+ icon_state = "shortsleeve_vneck"
+ has_color = TRUE
+
+/datum/category_item/underwear/undershirt/shortsleeve_undershirt_vneck_fem
+ name = "Shortsleeve Undershirt, V-Neck Feminine"
+ icon_state = "shortsleeve_vneck_fem"
+ has_color = TRUE
+
+/datum/category_item/underwear/undershirt/undershirt
+ name = "Undershirt"
+ icon_state = "undershirt"
+ has_color = TRUE
+
+/datum/category_item/underwear/undershirt/undershirt_fem
+ name = "Undershirt, Feminine"
+ icon_state = "undershirt_fem"
+ has_color = TRUE
+
+/datum/category_item/underwear/undershirt/undershirt_heart
+ name = "Undershirt, Heart"
+ icon_state = "heart"
+
+/datum/category_item/underwear/undershirt/undershirt_heart_fem
+ name = "Undershirt, Heart Feminine"
+ icon_state = "heart_fem"
+
+/datum/category_item/underwear/undershirt/undershirt_heart_echelon
+ name = "Undershirt, Heart Echelon"
+ icon_state = "heart_echelon"
+
+/datum/category_item/underwear/undershirt/undershirt_heart_echelon_fem
+ name = "Undershirt, Heart Echelon Feminine"
+ icon_state = "heart_echelon_fem"
+
+/datum/category_item/underwear/undershirt/shirt_love_nt
+ name = "Undershirt, I<3NT"
+ icon_state = "ilovent"
+
+/datum/category_item/underwear/undershirt/shirt_love_nt_fem
+ name = "Undershirt, I<3NT Feminine"
+ icon_state = "ilovent_fem"
+
+/datum/category_item/underwear/undershirt/shirt
+ name = "Shirt"
+ icon_state = "shirt"
+ has_color = TRUE
+
+/datum/category_item/underwear/undershirt/shirt_alien
+ name = "Shirt, Alien"
+ icon_state = "shirt_alien"
+
+/datum/category_item/underwear/undershirt/shirt_band
+ name = "Shirt, Band"
+ icon_state = "shirt_band"
+
+/datum/category_item/underwear/undershirt/shirt_clown
+ name = "Shirt, Clown"
+ icon_state = "shirt_clown"
+
+/datum/category_item/underwear/undershirt/shirt_ian
+ name = "Shirt, Ian"
+ icon_state = "shirt_ian"
+
+/datum/category_item/underwear/undershirt/shirt_jersey
+ name = "Shirt, Blue Jersey"
+ icon_state = "shirt_jersey"
+
+/datum/category_item/underwear/undershirt/shirt_jersey_red
+ name = "Shirt, Red Jersey"
+ icon_state = "shirt_jersey_red"
+
+/datum/category_item/underwear/undershirt/shirt_meat
+ name = "Shirt, Meat"
+ icon_state = "shirt_meat"
+
+/datum/category_item/underwear/undershirt/shirt_nt
+ name = "Shirt, NT"
+ icon_state = "shirt_nano"
+
+/datum/category_item/underwear/undershirt/shirt_nt_red
+ name = "Shirt, NT Red"
+ icon_state = "shirt_nano_red"
+
+/datum/category_item/underwear/undershirt/shirt_peace
+ name = "Shirt, Peace"
+ icon_state = "shirt_peace"
+
+/datum/category_item/underwear/undershirt/shirt_question
+ name = "Shirt, Question"
+ icon_state = "shirt_question"
+
+/datum/category_item/underwear/undershirt/shirt_skull
+ name = "Shirt, Skull"
+ icon_state = "shirt_skull"
+
+/datum/category_item/underwear/undershirt/shirt_ss13
+ name = "Shirt, SS13"
+ icon_state = "shirt_ss13"
+
+/datum/category_item/underwear/undershirt/shirt_blue_striped
+ name = "Shirt, Blue Stripes"
+ icon_state = "shirt_stripes"
+
+/datum/category_item/underwear/undershirt/shirt_blue_striped_alt
+ name = "Shirt, Blue Stripes Alt"
+ icon_state = "shirt_stripes_alt"
+
+/datum/category_item/underwear/undershirt/shirt_tiedye
+ name = "Shirt, Tiedye"
+ icon_state = "shirt_tiedye"
+
+/datum/category_item/underwear/undershirt/shirt_tight
+ name = "Shirt, Tight"
+ icon_state = "tight"
+
+/datum/category_item/underwear/undershirt/shirt_wing
+ name = "Shirt, Wing"
+ icon_state = "wing_shirt"
+
+/datum/category_item/underwear/undershirt/shirt_pinkblack
+ name = "Shirt, Pink-Black"
+ icon_state = "pinkblack_tshirt"
+
+/datum/category_item/underwear/undershirt/sport_shirt_blue
+ name = "Sport Shirt, Blue"
+ icon_state = "sportblue"
+
+/datum/category_item/underwear/undershirt/sport_shirt_green
+ name = "Sport Shirt, Green"
+ icon_state = "sportgreen"
+
+/datum/category_item/underwear/undershirt/sport_shirt_red
+ name = "Sport Shirt, Red"
+ icon_state = "sportred"
+
+/datum/category_item/underwear/undershirt/shirt_army
+ name = "Shirt, Army"
+ icon_state = "shirt_army"
+
+/datum/category_item/underwear/undershirt/shirt_army_fem
+ name = "Shirt, Army Feminine"
+ icon_state = "shirt_army_fem"
+
+/datum/category_item/underwear/undershirt/shirt_expedition
+ name = "Shirt, Expedition"
+ icon_state = "shirt_expedition"
+
+/datum/category_item/underwear/undershirt/shirt_expedition_fem
+ name = "Shirt, Expedition Feminine"
+ icon_state = "shirt_expedition_fem"
+
+/datum/category_item/underwear/undershirt/shirt_fleet
+ name = "Shirt, Fleet"
+ icon_state = "shirt_fleet"
+
+/datum/category_item/underwear/undershirt/shirt_fleet_fem
+ name = "Shirt, Fleet Feminine"
+ icon_state = "shirt_fleet_fem"
+
+/datum/category_item/underwear/undershirt/shirt_necropolis
+ name = "Shirt, Necropolis Industries"
+ icon_state = "shirt_necropolis"
+
+/datum/category_item/underwear/undershirt/shirt_necropolis_fem
+ name = "Shirt, Necropolis Industries Feminine"
+ icon_state = "shirt_necropolis_fem"
+
+/datum/category_item/underwear/undershirt/shirt_epmc
+ name = "Shirt, EPMC"
+ icon_state = "shirt_epmc"
+
+/datum/category_item/underwear/undershirt/shirt_epmc_fem
+ name = "Shirt, EPMC Feminine"
+ icon_state = "shirt_epmc_fem"
+
+/datum/category_item/underwear/undershirt/polo
+ name = "Polo"
+ icon_state = "polo"
+ has_color = TRUE
+
+/datum/category_item/underwear/undershirt/polo_fem
+ name = "Polo, Feminine"
+ icon_state = "polo_fem"
+ has_color = TRUE
+
+/datum/category_item/underwear/undershirt/polo_blue
+ name = "Polo, Blue"
+ icon_state = "polo_blue"
+
+/datum/category_item/underwear/undershirt/polo_blue_fem
+ name = "Polo, Blue Feminine"
+ icon_state = "polo_blue_fem"
+
+/datum/category_item/underwear/undershirt/polo_red
+ name = "Polo, Red"
+ icon_state = "polo_red"
+
+/datum/category_item/underwear/undershirt/polo_red_fem
+ name = "Polo, Red Feminine"
+ icon_state = "polo_red_fem"
+
+/datum/category_item/underwear/undershirt/polo_grayyellow
+ name = "Polo, Gray Yellow"
+ icon_state = "polo_grayyellow"
+
+/datum/category_item/underwear/undershirt/polo_grayyellow_fem
+ name = "Polo, Gray Yellow Feminine"
+ icon_state = "polo_grayyellow_fem"
+
+/datum/category_item/underwear/undershirt/polo_greenstrip
+ name = "Polo, Green Strip"
+ icon_state = "polo_corp"
+
+/datum/category_item/underwear/undershirt/polo_greenstrip_fem
+ name = "Polo, Green Strip Feminine"
+ icon_state = "polo_corp_fem"
+
+/datum/category_item/underwear/undershirt/polo_bluestrip
+ name = "Polo, Blue Strip"
+ icon_state = "polo_dais"
+
+/datum/category_item/underwear/undershirt/polo_bluestrip_fem
+ name = "Polo, Blue Strip Feminine"
+ icon_state = "polo_dais_fem"
+
+/datum/category_item/underwear/undershirt/polo_redstrip
+ name = "Polo, Red Strip"
+ icon_state = "polo_nt"
+
+/datum/category_item/underwear/undershirt/polo_redstrip_fem
+ name = "Polo, Red Strip Feminine"
+ icon_state = "polo_nt_fem"
+
+/datum/category_item/underwear/undershirt/bowlinga
+ name = "Bowling Shirt, Aqua"
+ icon_state = "bowlinga"
+
+/datum/category_item/underwear/undershirt/bowlingp
+ name = "Bowling Shirt, Pink"
+ icon_state = "bowlingp"
+
+/datum/category_item/underwear/undershirt/bowlingr
+ name = "Bowling Shirt, Red"
+ icon_state = "bowlingr"
+
+/datum/category_item/underwear/undershirt/bowlingw
+ name = "Bowling Shirt, White"
+ icon_state = "bowlingw"
+
+/datum/category_item/underwear/undershirt/longjon
+ name = "Long John Shirt"
+ icon_state = "ljont"
+ has_color = TRUE
+
+/datum/category_item/underwear/undershirt/blouse_fem
+ name = "Blouse Shirt"
+ icon_state = "blouse_fem"
+ has_color = TRUE
+
+/datum/category_item/underwear/undershirt/longsleeve_undershirt
+ name = "Longsleeve Undershirt"
+ icon_state = "undershirt_long"
+ has_color = TRUE
+
+/datum/category_item/underwear/undershirt/longsleeve_undershirt_fem
+ name = "Longsleeve Undershirt, Feminine"
+ icon_state = "undershirt_long_fem"
+ has_color = TRUE
+
+/datum/category_item/underwear/undershirt/longstripe_black
+ name = "Longsleeve Striped Shirt, Black"
+ icon_state = "longstripe"
+ has_color = TRUE
+
+/datum/category_item/underwear/undershirt/longstripe_blue
+ name = "Longsleeve Striped Shirt, Blue"
+ icon_state = "longstripe_blue"
+
+/datum/category_item/underwear/undershirt/longstripe_pink
+ name = "Longsleeve Striped Shirt, Pink"
+ icon_state = "longstripe_pink"
+
+/datum/category_item/underwear/undershirt/longsleeve_shirt
+ name = "Longsleeve Shirt"
+ icon_state = "shirt_long"
+ has_color = TRUE
+
+/datum/category_item/underwear/undershirt/longsleeve_shirt_fem
+ name = "Longsleeve Shirt, Feminine"
+ icon_state = "shirt_long_fem"
+ has_color = TRUE
+
+/datum/category_item/underwear/undershirt/turtleneck
+ name = "Turtleneck"
+ icon_state = "turtleneck"
+ has_color = TRUE
\ No newline at end of file
diff --git a/code/datums/underwear/underwear.dm b/code/datums/underwear/underwear.dm
new file mode 100644
index 00000000000..0297690f822
--- /dev/null
+++ b/code/datums/underwear/underwear.dm
@@ -0,0 +1,73 @@
+/****************************
+* Category Collection Setup *
+****************************/
+/datum/category_collection/underwear
+ category_group_type = /datum/category_group/underwear
+
+/*************
+* Categories *
+*************/
+/datum/category_group/underwear
+ var/sort_order = 5 // Lower sort order is applied as icons first
+ var/display_name
+ var/gender = NEUTER
+
+/datum/category_group/underwear/dd_SortValue()
+ return sort_order
+
+/datum/category_group/underwear/top
+ name = "Underwear, top"
+ sort_order = 1
+ display_name = "top piece"
+ category_item_type = /datum/category_item/underwear/top
+
+/datum/category_group/underwear/bottom
+ name = "Underwear, bottom"
+ sort_order = 2
+ display_name = "bottom piece"
+ category_item_type = /datum/category_item/underwear/bottom
+
+/datum/category_group/underwear/socks
+ name = "Socks"
+ sort_order = 3
+ display_name = "socks"
+ gender = PLURAL
+ category_item_type = /datum/category_item/underwear/socks
+
+/datum/category_group/underwear/undershirt
+ name = "Undershirt"
+ sort_order = 4 // Undershirts currently have the lowest sort order because they may cover both underwear and socks.
+ display_name = "undershirt"
+ category_item_type = /datum/category_item/underwear/undershirt
+
+/*******************
+* Category entries *
+*******************/
+/datum/category_item/underwear
+ var/always_last = FALSE // Should this entry be sorte last?
+ var/is_default = FALSE // Should this entry be considered the default for its type?
+ var/icon = 'icons/mob/underwear.dmi' // Which icon to get the underwear from
+ var/icon_state // And the particular item state
+ var/list/tweaks = list() // Underwear customisation
+ var/has_color = FALSE
+
+/datum/category_item/underwear/New()
+ if(has_color)
+ tweaks += gear_tweak_free_color_choice
+
+/datum/category_item/underwear/dd_SortValue()
+ if(always_last)
+ return "~"+name
+ return name
+
+/datum/category_item/underwear/proc/is_default(var/gender)
+ return is_default
+
+/datum/category_item/underwear/proc/generate_image(var/list/metadata)
+ if(!icon_state)
+ return
+
+ var/image/I = image(icon = 'icons/mob/underwear.dmi', icon_state = icon_state)
+ for(var/datum/gear_tweak/gt in tweaks)
+ gt.tweak_item(I, metadata && metadata["[gt]"] ? metadata["[gt]"] : gt.get_default())
+ return I
diff --git a/code/game/gamemodes/cult/runes/manifest_ghost.dm b/code/game/gamemodes/cult/runes/manifest_ghost.dm
index 36b0cf562c3..66f3bdc552c 100644
--- a/code/game/gamemodes/cult/runes/manifest_ghost.dm
+++ b/code/game/gamemodes/cult/runes/manifest_ghost.dm
@@ -32,7 +32,7 @@
chose_name = TRUE
break
apparition.universal_speak = TRUE
- apparition.underwear = null
+ apparition.all_underwear.Cut()
apparition.key = ghost.key
cult.add_antagonist(apparition.mind)
playsound(loc, 'sound/magic/exit_blood.ogg', 100, 1)
diff --git a/code/game/objects/structures/under_wardrobe.dm b/code/game/objects/structures/under_wardrobe.dm
index c3e76950274..ede8b9a7cf7 100644
--- a/code/game/objects/structures/under_wardrobe.dm
+++ b/code/game/objects/structures/under_wardrobe.dm
@@ -11,55 +11,84 @@
icon_state = "cabinet_closed"
density = 1
-/obj/structure/undies_wardrobe/attack_hand(mob/user as mob)
- src.add_fingerprint(user)
- var/mob/living/carbon/human/H = user
- if(!ishuman(user) || (H.species && !(H.species.appearance_flags & HAS_UNDERWEAR)) && !(H.species.appearance_flags & HAS_SOCKS))
+/obj/structure/undies_wardrobe/attack_hand(var/mob/user)
+ if(!human_who_can_use_underwear(user))
to_chat(user, "Sadly there's nothing in here for you to wear.")
- return 0
+ return
+ interact(user)
+/obj/structure/undies_wardrobe/interact(var/mob/living/carbon/human/H)
+ var/dat = list()
+ dat += "Underwear:
"
+ for(var/datum/category_group/underwear/UWC in global_underwear.categories)
+ var/datum/category_item/underwear/UWI = H.all_underwear[UWC.name]
+ var/item_name = UWI?.name || "None"
+ dat += "[UWC.name]: [item_name]"
+ if(UWI)
+ for(var/datum/gear_tweak/gt in UWI.tweaks)
+ dat += " [gt.get_contents(get_metadata(H, UWC.name, gt))]"
+ dat += " (Remove)
"
- var/list/selection_types = list()
- if (H.species.appearance_flags & HAS_UNDERWEAR)
- selection_types += list(M_UNDER, F_UNDER, U_SHIRT)
- if (H.species.appearance_flags & HAS_SOCKS)
- selection_types += list(M_SOCKS, F_SOCKS)
+ dat = jointext(dat, null)
+ show_browser(H, dat, "window=wardrobe;size=400x200")
- var/utype = input("Which section do you want to pick from?") as null|anything in selection_types
- var/list/selection
- switch(utype)
- if(M_UNDER)
- selection = underwear_m
- if(F_UNDER)
- selection = underwear_f
- if(U_SHIRT)
- selection = undershirt_t
- if(M_SOCKS)
- selection = socks_m
- if(F_SOCKS)
- selection = socks_f
- var/pick = input("Select the style") as null|anything in selection
- if(pick)
- if(get_dist(src,user) > 1)
+/obj/structure/undies_wardrobe/proc/get_metadata(var/mob/living/carbon/human/H, var/underwear_category, var/datum/gear_tweak/gt)
+ var/metadata = H.all_underwear_metadata[underwear_category]
+ if(!metadata)
+ metadata = list()
+ H.all_underwear_metadata[underwear_category] = metadata
+
+ var/tweak_data = metadata["[gt]"]
+ if(!tweak_data)
+ tweak_data = gt.get_default()
+ metadata["[gt]"] = tweak_data
+ return tweak_data
+
+/obj/structure/undies_wardrobe/proc/set_metadata(var/mob/living/carbon/human/H, var/underwear_category, var/datum/gear_tweak/gt, var/new_metadata)
+ var/list/metadata = H.all_underwear_metadata[underwear_category]
+ metadata["[gt]"] = new_metadata
+
+/obj/structure/undies_wardrobe/proc/human_who_can_use_underwear(var/mob/living/carbon/human/H)
+ if(!istype(H) || !H.species || !(H.species.appearance_flags & HAS_UNDERWEAR))
+ return FALSE
+ return TRUE
+
+/obj/structure/undies_wardrobe/CanUseTopic(var/user)
+ if(!human_who_can_use_underwear(user))
+ return STATUS_CLOSE
+
+ return ..()
+
+/obj/structure/undies_wardrobe/Topic(href, href_list, state)
+ if(..())
+ return TRUE
+
+ var/mob/living/carbon/human/H = usr
+ if(href_list["remove_underwear"])
+ if(href_list["remove_underwear"] in H.all_underwear)
+ H.all_underwear -= href_list["remove_underwear"]
+ . = TRUE
+ else if(href_list["change_underwear"])
+ var/datum/category_group/underwear/UWC = global_underwear.categories_by_name[href_list["change_underwear"]]
+ if(!UWC)
return
- switch (utype)
- if(U_SHIRT)
- H.undershirt = undershirt_t[pick]
- if(F_SOCKS)
- H.socks = selection[pick]
- if(M_SOCKS)
- H.socks = selection[pick]
- if(M_UNDER)
- H.underwear = selection[pick]
- if(F_UNDER)
- H.underwear = selection[pick]
-
+ var/datum/category_item/underwear/selected_underwear = input(H, "Choose underwear:", "Choose underwear", H.all_underwear[UWC.name]) as null|anything in UWC.items
+ if(selected_underwear && CanUseTopic(H, default_state))
+ H.all_underwear[UWC.name] = selected_underwear
+ H.hide_underwear[UWC.name] = FALSE
+ . = TRUE
+ else if(href_list["underwear"] && href_list["tweak"])
+ var/underwear = href_list["underwear"]
+ if(!(underwear in H.all_underwear))
+ return
+ var/datum/gear_tweak/gt = locate(href_list["tweak"])
+ if(!gt)
+ return
+ var/new_metadata = gt.get_metadata(usr, get_metadata(H, underwear, gt), "Wardrobe Underwear Selection")
+ if(new_metadata)
+ set_metadata(H, underwear, gt, new_metadata)
+ H.hide_underwear[underwear] = FALSE
+ . = TRUE
+ if(.)
H.update_underwear(TRUE)
-
- return 1
-
-#undef M_UNDER
-#undef F_UNDER
-#undef M_SOCKS
-#undef F_SOCKS
-#undef U_SHIRT
+ interact(H)
diff --git a/code/modules/client/preference_setup/general/03_body.dm b/code/modules/client/preference_setup/general/03_body.dm
index 1b269bffd19..26181b9d014 100644
--- a/code/modules/client/preference_setup/general/03_body.dm
+++ b/code/modules/client/preference_setup/general/03_body.dm
@@ -753,3 +753,4 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
pref.rlimb_data[organ] = null
while(null in pref.rlimb_data)
pref.rlimb_data -= null*/
+
diff --git a/code/modules/client/preference_setup/general/04_equipment.dm b/code/modules/client/preference_setup/general/04_equipment.dm
index 8e9a102ce3e..cecd8e97f5c 100644
--- a/code/modules/client/preference_setup/general/04_equipment.dm
+++ b/code/modules/client/preference_setup/general/04_equipment.dm
@@ -1,18 +1,20 @@
+/datum/preferences
+ var/list/all_underwear
+ var/list/all_underwear_metadata
+
/datum/category_item/player_setup_item/general/equipment
name = "Equipment"
sort_order = 4
/datum/category_item/player_setup_item/general/equipment/load_character(var/savefile/S)
- S["underwear"] >> pref.underwear
- S["undershirt"] >> pref.undershirt
- S["socks"] >> pref.socks
+ S["all_underwear"] >> pref.all_underwear
+ S["all_underwear_metadata"] >> pref.all_underwear_metadata
S["backbag"] >> pref.backbag
S["backbag_style"] >> pref.backbag_style
/datum/category_item/player_setup_item/general/equipment/save_character(var/savefile/S)
- S["underwear"] << pref.underwear
- S["undershirt"] << pref.undershirt
- S["socks"] << pref.socks
+ S["all_underwear"] << pref.all_underwear
+ S["all_underwear_metadata"] << pref.all_underwear_metadata
S["backbag"] << pref.backbag
S["backbag_style"] << pref.backbag_style
@@ -20,9 +22,8 @@
return list(
"ss13_characters" = list(
"vars" = list(
- "underwear",
- "undershirt",
- "socks",
+ "all_underwear",
+ "all_underwear_metadata",
"backbag",
"backbag_style"
),
@@ -36,9 +37,8 @@
/datum/category_item/player_setup_item/general/equipment/gather_save_query()
return list(
"ss13_characters" = list(
- "underwear",
- "undershirt",
- "socks",
+ "all_underwear",
+ "all_underwear_metadata",
"backbag",
"backbag_style",
"id" = 1,
@@ -48,9 +48,8 @@
/datum/category_item/player_setup_item/general/equipment/gather_save_parameters()
return list(
- "underwear" = pref.underwear,
- "undershirt" = pref.undershirt,
- "socks" = pref.socks,
+ "all_underwear" = json_encode(pref.all_underwear),
+ "all_underwear_metadata" = json_encode(pref.all_underwear_metadata),
"backbag" = pref.backbag,
"backbag_style" = pref.backbag_style,
"id" = pref.current_character,
@@ -60,58 +59,87 @@
/datum/category_item/player_setup_item/general/equipment/sanitize_character(var/sql_load = 0)
if (sql_load)
pref.backbag = text2num(pref.backbag)
+ pref.backbag_style = text2num(pref.backbag_style)
+ if(istext(pref.all_underwear))
+ var/before = pref.all_underwear
+ try
+ pref.all_underwear = json_decode(pref.all_underwear)
+ catch(var/exception/e)
+ log_debug("UNDERWEAR: Caught [e]. Initial value: [before]")
+ pref.all_underwear = list()
+ if(istext(pref.all_underwear_metadata))
+ var/before = pref.all_underwear_metadata
+ try
+ pref.all_underwear_metadata = json_decode(pref.all_underwear_metadata)
+ catch(var/exception/e)
+ log_debug("UNDERWEAR METADATA: Caught [e]. Initial value: [before]")
+ pref.all_underwear_metadata = list()
+
+ if(!istype(pref.all_underwear))
+ pref.all_underwear = list()
+
+ for(var/datum/category_group/underwear/WRC in global_underwear.categories)
+ for(var/datum/category_item/underwear/WRI in WRC.items)
+ if(WRI.is_default(pref.gender ? pref.gender : MALE))
+ pref.all_underwear[WRC.name] = WRI.name
+ break
+
+ if(!istype(pref.all_underwear_metadata))
+ pref.all_underwear_metadata = list()
+
+ for(var/underwear_category in pref.all_underwear)
+ var/datum/category_group/underwear/UWC = global_underwear.categories_by_name[underwear_category]
+ if(!UWC)
+ pref.all_underwear -= underwear_category
+ else
+ var/datum/category_item/underwear/UWI = UWC.items_by_name[pref.all_underwear[underwear_category]]
+ if(!UWI)
+ pref.all_underwear -= underwear_category
+
+ for(var/underwear_metadata in pref.all_underwear_metadata)
+ if(!(underwear_metadata in pref.all_underwear))
+ pref.all_underwear_metadata -= underwear_metadata
pref.backbag = sanitize_integer(pref.backbag, 1, backbaglist.len, initial(pref.backbag))
pref.backbag_style = sanitize_integer(pref.backbag_style, 1, backbagstyles.len, initial(pref.backbag_style))
- var/undies = get_undies()
- var/gender_socks = get_gender_socks()
- if(!get_key_by_value(undies, pref.underwear))
- pref.underwear = null
- if(!get_key_by_value(undershirt_t, pref.undershirt))
- pref.undershirt = undershirt_t[undershirt_t[1]]
- if(!get_key_by_value(gender_socks, pref.socks))
- pref.socks = null
-
/datum/category_item/player_setup_item/general/equipment/content()
- var/list/dat = list()
- dat += "Equipment:
"
- dat += "Underwear: [get_key_by_value(get_undies(),pref.underwear)]
"
- dat += "Undershirt: [get_key_by_value(undershirt_t,pref.undershirt)]
"
- dat += "Socks: [get_key_by_value(get_gender_socks(),pref.socks)]
"
- dat += "Backpack Type: [backbaglist[pref.backbag]]
"
- dat += "Backpack Style: [backbagstyles[pref.backbag_style]]
"
+ . = list()
+ . += "Equipment:
"
+ for(var/datum/category_group/underwear/UWC in global_underwear.categories)
+ var/item_name = pref.all_underwear[UWC.name] ? pref.all_underwear[UWC.name] : "None"
+ . += "[UWC.name]: [item_name]"
- . = dat.Join()
+ var/datum/category_item/underwear/UWI = UWC.items_by_name[item_name]
+ if(UWI)
+ for(var/datum/gear_tweak/gt in UWI.tweaks)
+ . += " [gt.get_contents(get_metadata(UWC.name, gt))]"
-/datum/category_item/player_setup_item/general/equipment/proc/get_undies()
- return pref.gender == MALE ? underwear_m : underwear_f
+ . += "
"
-/datum/category_item/player_setup_item/general/equipment/proc/get_gender_socks()
- return pref.gender == MALE ? socks_m : socks_f
+ . += "Backpack Type: [backbaglist[pref.backbag]]
"
+ . += "Backpack Style: [backbagstyles[pref.backbag_style]]
"
+
+ return jointext(., null)
+
+/datum/category_item/player_setup_item/general/equipment/proc/get_metadata(var/underwear_category, var/datum/gear_tweak/gt)
+ var/metadata = pref.all_underwear_metadata[underwear_category]
+ if(!metadata)
+ metadata = list()
+ pref.all_underwear_metadata[underwear_category] = metadata
+
+ var/tweak_data = metadata["[gt]"]
+ if(!tweak_data)
+ tweak_data = gt.get_default()
+ metadata["[gt]"] = tweak_data
+ return tweak_data
+
+/datum/category_item/player_setup_item/general/equipment/proc/set_metadata(var/underwear_category, var/datum/gear_tweak/gt, var/new_metadata)
+ var/list/metadata = pref.all_underwear_metadata[underwear_category]
+ metadata["[gt]"] = new_metadata
/datum/category_item/player_setup_item/general/equipment/OnTopic(var/href,var/list/href_list, var/mob/user)
- if(href_list["change_underwear"])
- var/underwear_options = get_undies()
- var/new_underwear = input(user, "Choose your character's underwear:", "Character Preference", get_key_by_value(get_undies(),pref.underwear)) as null|anything in underwear_options
- if(!isnull(new_underwear) && CanUseTopic(user))
- pref.underwear = underwear_options[new_underwear]
- return TOPIC_REFRESH
-
- else if(href_list["change_undershirt"])
- var/new_undershirt = input(user, "Choose your character's undershirt:", "Character Preference", get_key_by_value(undershirt_t,pref.undershirt)) as null|anything in undershirt_t
- if(!isnull(new_undershirt) && CanUseTopic(user))
- pref.undershirt = undershirt_t[new_undershirt]
- return TOPIC_REFRESH
-
- else if(href_list["change_socks"])
- var/socks_options = get_gender_socks()
- var/new_socks = input(user, "Choose your character's socks:", "Character Preference", get_key_by_value(get_gender_socks(),pref.socks)) as null|anything in socks_options
- if(!isnull(new_socks) && CanUseTopic(user))
- pref.socks = socks_options[new_socks]
- return TOPIC_REFRESH
-
- else if(href_list["change_backpack"])
+ if(href_list["change_backpack"])
var/new_backbag = input(user, "Choose your character's bag type:", "Character Preference", backbaglist[pref.backbag]) as null|anything in backbaglist
if(!isnull(new_backbag) && CanUseTopic(user))
pref.backbag = backbaglist.Find(new_backbag)
@@ -123,4 +151,25 @@
pref.backbag_style = backbagstyles.Find(new_backbag)
return TOPIC_REFRESH
+ else if(href_list["change_underwear"])
+ var/datum/category_group/underwear/UWC = global_underwear.categories_by_name[href_list["change_underwear"]]
+ if(!UWC)
+ return TOPIC_NOACTION
+ var/datum/category_item/underwear/selected_underwear = input(user, "Choose underwear:", "Character Preference", pref.all_underwear[UWC.name]) as null|anything in UWC.items
+ if(selected_underwear && CanUseTopic(user))
+ pref.all_underwear[UWC.name] = selected_underwear.name
+ return TOPIC_REFRESH
+ else if(href_list["underwear"] && href_list["tweak"])
+ var/underwear = href_list["underwear"]
+ if(!(underwear in pref.all_underwear))
+ return TOPIC_NOACTION
+ var/datum/gear_tweak/gt = locate(href_list["tweak"])
+ if(!gt)
+ return TOPIC_NOACTION
+ var/new_metadata = gt.get_metadata(usr, get_metadata(underwear, gt))
+ if(new_metadata)
+ set_metadata(underwear, gt, new_metadata)
+ return TOPIC_REFRESH
+
return ..()
+
diff --git a/code/modules/client/preference_setup/loadout/gear_tweaks.dm b/code/modules/client/preference_setup/loadout/gear_tweaks.dm
index 09c56ad1585..8798e7c6c44 100644
--- a/code/modules/client/preference_setup/loadout/gear_tweaks.dm
+++ b/code/modules/client/preference_setup/loadout/gear_tweaks.dm
@@ -20,8 +20,6 @@
Color adjustment
*/
-var/datum/gear_tweak/color/gear_tweak_free_color_choice = new()
-
/datum/gear_tweak/color
var/list/valid_colors
@@ -38,16 +36,15 @@ var/datum/gear_tweak/color/gear_tweak_free_color_choice = new()
/datum/gear_tweak/color/get_random()
return valid_colors ? pick(valid_colors) : COLOR_GRAY
-/datum/gear_tweak/color/get_metadata(var/user, var/metadata)
+/datum/gear_tweak/color/get_metadata(var/user, var/metadata, var/title = "Character Preference")
if(valid_colors)
- return input(user, "Choose an item color.", "Character Preference", metadata) as null|anything in valid_colors
- return input(user, "Choose an item color.", "Global Preference", metadata) as color|null
+ return input(user, "Choose a color.", title, metadata) as null|anything in valid_colors
+ return input(user, "Choose a color.", title, metadata) as color|null
/datum/gear_tweak/color/tweak_item(var/obj/item/I, var/metadata)
if(valid_colors && !(metadata in valid_colors))
return
- I.color = metadata
- I.update_icon()
+ I.color = sanitize_hexcolor(metadata, I.color)
/*
Path adjustment
diff --git a/code/modules/client/preference_setup/preference_setup.dm b/code/modules/client/preference_setup/preference_setup.dm
index 14e130ca1b4..30425b9c960 100644
--- a/code/modules/client/preference_setup/preference_setup.dm
+++ b/code/modules/client/preference_setup/preference_setup.dm
@@ -132,6 +132,9 @@
var/sql_role = SQL_CHARACTER
var/modified = 0
+/datum/category_group/player_setup_category/dd_SortValue()
+ return sort_order
+
/datum/category_group/player_setup_category/proc/sanitize_setup(sql_load = FALSE)
for(var/datum/category_item/player_setup_item/PI in items)
PI.sanitize_preferences(sql_load)
@@ -220,6 +223,9 @@
pref = null
return ..()
+/datum/category_item/player_setup_item/dd_SortValue()
+ return sort_order
+
/datum/category_item/player_setup_item/proc/to_client_chat(str)
if (pref && pref.client)
to_chat(pref.client, str)
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 742d164f589..fe5ffc2d875 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -38,9 +38,6 @@ datum/preferences
var/age = 30 //age of character
var/spawnpoint = "Arrivals Shuttle" //where this character will spawn (0-2).
var/b_type = "A+" //blood type (not-chooseable)
- var/underwear //underwear type
- var/undershirt //undershirt type
- var/socks //socks type
var/backbag = 2 //backpack type
var/backbag_style = 1
var/h_style = "Bald" //Hair type
@@ -372,11 +369,17 @@ datum/preferences
character.sync_trait_prefs_to_mob(src)
- character.underwear = underwear
-
- character.undershirt = undershirt
-
- character.socks = socks
+ character.all_underwear.Cut()
+ character.all_underwear_metadata.Cut()
+ for(var/underwear_category_name in all_underwear)
+ var/datum/category_group/underwear/underwear_category = global_underwear.categories_by_name[underwear_category_name]
+ if(underwear_category)
+ var/underwear_item_name = all_underwear[underwear_category_name]
+ character.all_underwear[underwear_category_name] = underwear_category.items_by_name[underwear_item_name]
+ if(all_underwear_metadata[underwear_category_name])
+ character.all_underwear_metadata[underwear_category_name] = all_underwear_metadata[underwear_category_name]
+ else
+ all_underwear -= underwear_category_name
if(backbag > 6 || backbag < 1)
backbag = 1 //Same as above
diff --git a/code/modules/mob/abstract/new_player/preferences_setup.dm b/code/modules/mob/abstract/new_player/preferences_setup.dm
index 3e4113b890e..07beb73e7d9 100644
--- a/code/modules/mob/abstract/new_player/preferences_setup.dm
+++ b/code/modules/mob/abstract/new_player/preferences_setup.dm
@@ -1,5 +1,6 @@
/datum/preferences
//The mob should have a gender you want before running this proc. Will run fine without H
+
/datum/preferences/proc/randomize_appearance_for(var/mob/living/carbon/human/H,var/random_gender=TRUE)
if(random_gender)
gender = pick(MALE, FEMALE)
@@ -14,17 +15,6 @@
randomize_eyes_color()
if(current_species.appearance_flags & HAS_SKIN_COLOR)
randomize_skin_color(current_species)
- if(current_species.appearance_flags & HAS_UNDERWEAR)
- if(gender == FEMALE)
- underwear = underwear_f[pick(underwear_f)]
- else
- underwear = underwear_m[pick(underwear_m)]
- undershirt = undershirt_t[pick(undershirt_t)]
- if(current_species.appearance_flags & HAS_SOCKS)
- if(gender == FEMALE)
- socks = socks_f[pick(socks_f)]
- else
- socks = socks_m[pick(socks_m)]
h_style = random_hair_style(gender, species)
f_style = random_facial_hair_style(gender, species)
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index ae9a4ee33d3..f24914d69ab 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -73,6 +73,10 @@
. = ..()
+ hide_underwear.Cut()
+ for(var/category in global_underwear.categories_by_name)
+ hide_underwear[category] = FALSE
+
if(dna)
dna.ready_dna(src)
dna.real_name = real_name
@@ -1757,6 +1761,22 @@
if (doClickAction)
..()
+/mob/living/carbon/human/verb/toggle_underwear()
+ set name = "Toggle Underwear"
+ set desc = "Shows/hides selected parts of your underwear."
+ set category = "Object"
+
+ if(stat)
+ return
+ var/datum/category_group/underwear/UWC = input(usr, "Choose underwear:", "Show/hide underwear") as null|anything in global_underwear.categories
+ var/datum/category_item/underwear/UWI = all_underwear[UWC.name]
+ if(!UWI || UWI.name == "None")
+ to_chat(src, "You do not have [UWC.gender==PLURAL ? "[UWC.display_name]" : "any [UWC.display_name]"].")
+ return
+ hide_underwear[UWC.name] = !hide_underwear[UWC.name]
+ update_underwear(1)
+ to_chat(src, "You [hide_underwear[UWC.name] ? "take off" : "put on"] your [UWC.display_name].")
+
/mob/living/carbon/human/verb/pull_punches()
set name = "Pull Punches"
set desc = "Try not to hurt them."
diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm
index b4840529a02..8b26c8a0cd9 100644
--- a/code/modules/mob/living/carbon/human/human_defines.dm
+++ b/code/modules/mob/living/carbon/human/human_defines.dm
@@ -32,9 +32,9 @@
var/age = 30 //Player's age (pure fluff)
var/b_type = "A+" //Player's bloodtype
- var/underwear = 1 //Which underwear the player wants
- var/undershirt = 0 //Which undershirt the player wants.
- var/socks = 0 //Which socks the player wants.
+ var/list/all_underwear = list()
+ var/list/all_underwear_metadata = list()
+ var/list/hide_underwear = list()
var/backbag = 2 //Which backpack type the player has chosen. Nothing, Satchel or Backpack.
var/backbag_style = 1
diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm
index 21ce4582870..12940e5cd36 100644
--- a/code/modules/mob/living/carbon/human/update_icons.dm
+++ b/code/modules/mob/living/carbon/human/update_icons.dm
@@ -308,20 +308,18 @@ There are several things that need to be remembered:
update_icons()
/mob/living/carbon/human/proc/update_underwear(update_icons = TRUE)
- var/list/ovr
+ overlays_raw[UNDERWEAR_LAYER] = list()
- if(underwear && (species.appearance_flags & HAS_UNDERWEAR))
- LAZYADD(ovr, SSicon_cache.get_state('icons/mob/human.dmi', "[underwear]"))
+ if(species.appearance_flags & HAS_UNDERWEAR)
+ for(var/category in all_underwear)
+ if(hide_underwear[category])
+ continue
+ if(category == "Underwear, top" && hide_underwear["Undershirt"] == FALSE && !istype(all_underwear["Undershirt"], /datum/category_item/underwear/undershirt/none))
+ continue //This piece of "code" is here to prevent tops from showing up over undershirts.
+ var/datum/category_item/underwear/UWI = all_underwear[category]
+ overlays_raw[UNDERWEAR_LAYER] += UWI.generate_image(all_underwear_metadata[category])
- if(undershirt && (species.appearance_flags & HAS_UNDERWEAR))
- LAZYADD(ovr, SSicon_cache.get_state('icons/mob/human.dmi', "[undershirt]"))
-
- if(socks && (species.appearance_flags & HAS_SOCKS))
- LAZYADD(ovr, SSicon_cache.get_state('icons/mob/human.dmi', "[socks]"))
-
- overlays_raw[UNDERWEAR_LAYER] = ovr
-
- if (update_icons)
+ if(update_icons)
update_icons()
// This proc generates & returns an icon representing a human's hair, using a cached icon from SSicon_cache if possible.
@@ -1424,3 +1422,4 @@ There are several things that need to be remembered:
#undef UNDERSCORE_OR_NULL
#undef GET_BODY_TYPE
#undef GET_TAIL_LAYER
+
diff --git a/html/changelogs/hot_sexy_lingerie_update.yml b/html/changelogs/hot_sexy_lingerie_update.yml
new file mode 100644
index 00000000000..58d74725a5b
--- /dev/null
+++ b/html/changelogs/hot_sexy_lingerie_update.yml
@@ -0,0 +1,44 @@
+################################
+# Example Changelog File
+#
+# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb.
+#
+# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.)
+# When it is, any changes listed below will disappear.
+#
+# Valid Prefixes:
+# bugfix
+# wip (For works in progress)
+# tweak
+# soundadd
+# sounddel
+# rscadd (general adding of nice things)
+# rscdel (general deleting of nice things)
+# imageadd
+# imagedel
+# maptweak
+# spellcheck (typo fixes)
+# experiment
+# balance
+# admin
+# backend
+# security
+# refactor
+#################################
+
+# Your name.
+author: Mwahahahaha, The Stryker
+
+# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
+delete-after: True
+
+# Any changes you've made. See valid prefix list above.
+# INDENT WITH TWO SPACES. NOT TABS. SPACES.
+# SCREW THIS UP AND IT WON'T WORK.
+# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries.
+# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog.
+changes:
+ - rscadd: "Splits the underwear into top and bottom pieces."
+ - rscadd: "Allows underwear to be removed."
+ - rscadd: "Ports some many more underwear, undershirt, socks styles from BS12, Polaris, tg, as well as some made by The Stryker, using this new system."
+ - bugfix: "Backpack styles (job-specific or grey) no longer keep changing back to job-specific."
\ No newline at end of file
diff --git a/icons/mob/human.dmi b/icons/mob/human.dmi
index ffe2d44ae9e..7ad330f892e 100644
Binary files a/icons/mob/human.dmi and b/icons/mob/human.dmi differ
diff --git a/icons/mob/underwear.dmi b/icons/mob/underwear.dmi
new file mode 100644
index 00000000000..405b1282948
Binary files /dev/null and b/icons/mob/underwear.dmi differ