diff --git a/code/__DEFINES/dcs/signals/signals_clothing.dm b/code/__DEFINES/dcs/signals/signals_clothing.dm
index b48acbf97e937a..03b695aca4c3e1 100644
--- a/code/__DEFINES/dcs/signals/signals_clothing.dm
+++ b/code/__DEFINES/dcs/signals/signals_clothing.dm
@@ -1,6 +1,21 @@
// /obj/item/clothing
/// (/obj/item/clothing, visor_state) - When a clothing gets it's visor toggled.
#define COMSIG_CLOTHING_VISOR_TOGGLE "clothing_visor_toggle"
-// /obj/item/clothing
-/// Sent when mobs try to equip clothing on others through attacking
-#define COMSIG_CLOTHING_ATTACK_EQUIP "clothing_attack_equip"
+/// From an undersuit being adjusted: ()
+#define COMSIG_CLOTHING_UNDER_ADJUSTED "clothing_under_adjusted"
+
+// Accessory sending to clothing
+/// /obj/item/clothing/accessory/successful_attach : (obj/item/clothing/under/attached_to)
+/// The accessory, at the point of signal sent, is in the clothing's accessory list / loc
+#define COMSIG_CLOTHING_ACCESSORY_ATTACHED "clothing_accessory_pinned"
+/// /obj/item/clothing/accessory/detach : (obj/item/clothing/under/detach_from)
+/// The accessory, at the point of signal sent, is no longer in the accessory list but may still be in the loc
+#define COMSIG_CLOTHING_ACCESSORY_DETACHED "clothing_accessory_unpinned"
+
+// To accessories themselves
+/// /obj/item/clothing/accessory/successful_attach : (obj/item/clothing/under/attached_to)
+/// The accessory, at the point of signal sent, is in the clothing's accessory list / loc
+#define COMSIG_ACCESSORY_ATTACHED "accessory_pinned"
+/// /obj/item/clothing/accessory/detach : (obj/item/clothing/under/detach_from)
+/// The accessory, at the point of signal sent, is no longer in the accessory list but may still be in the loc
+#define COMSIG_ACCESSORY_DETACHED "accessory_unpinned"
diff --git a/code/datums/mood_events/generic_positive_events.dm b/code/datums/mood_events/generic_positive_events.dm
index d1376437a075c2..ba4252d30ae3d7 100644
--- a/code/datums/mood_events/generic_positive_events.dm
+++ b/code/datums/mood_events/generic_positive_events.dm
@@ -194,8 +194,8 @@
timeout = 3 MINUTES
/datum/mood_event/hope_lavaland
- description = "What a peculiar emblem. It makes me feel hopeful for my future."
- mood_change = 10
+ description = "What a peculiar emblem. It makes me feel hopeful for my future."
+ mood_change = 6
/datum/mood_event/confident_mane
description = "I'm feeling confident with a head full of hair."
diff --git a/code/datums/quirks/negative_quirks.dm b/code/datums/quirks/negative_quirks.dm
index 77328dfce103cd..08393519a5dcc4 100644
--- a/code/datums/quirks/negative_quirks.dm
+++ b/code/datums/quirks/negative_quirks.dm
@@ -968,8 +968,7 @@
medical_record_text = "Patient's immune system responds violently to [allergy_string]"
var/mob/living/carbon/human/human_holder = quirk_holder
- var/obj/item/clothing/accessory/allergy_dogtag/dogtag = new(get_turf(human_holder))
- dogtag.display = allergy_string
+ var/obj/item/clothing/accessory/dogtag/allergy/dogtag = new(get_turf(human_holder), allergy_string)
give_item_to_holder(dogtag, list(LOCATION_BACKPACK = ITEM_SLOT_BACKPACK, LOCATION_HANDS = ITEM_SLOT_HANDS), flavour_text = "Make sure medical staff can see this...")
diff --git a/code/game/data_huds.dm b/code/game/data_huds.dm
index bec2556aa55a17..55ef354012045a 100644
--- a/code/game/data_huds.dm
+++ b/code/game/data_huds.dm
@@ -239,19 +239,24 @@ FAN HUDs! For identifying other fans on-sight.
/mob/living/carbon/human/proc/fan_hud_set_fandom()
var/image/holder = hud_list[FAN_HUD]
- var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ var/icon/hud_icon = icon(icon, icon_state, dir)
+ holder.pixel_y = hud_icon.Height() - world.icon_size
holder.icon_state = "hudfan_no"
- var/obj/item/clothing/under/U = get_item_by_slot(ITEM_SLOT_ICLOTHING)
- if(!U)
+
+ var/obj/item/clothing/under/undershirt = w_uniform
+ if(!istype(undershirt))
set_hud_image_inactive(FAN_HUD)
return
- if(istype(U.attached_accessory, /obj/item/clothing/accessory/mime_fan_pin))
- holder.icon_state = "mime_fan_pin"
+ for(var/accessory in undershirt.attached_accessories)
+ if(istype(accessory, /obj/item/clothing/accessory/mime_fan_pin))
+ holder.icon_state = "mime_fan_pin"
+ break
+
+ if(istype(accessory, /obj/item/clothing/accessory/clown_enjoyer_pin))
+ holder.icon_state = "clown_enjoyer_pin"
+ break
- else if(istype(U.attached_accessory, /obj/item/clothing/accessory/clown_enjoyer_pin))
- holder.icon_state = "clown_enjoyer_pin"
set_hud_image_active(FAN_HUD)
return
diff --git a/code/game/machinery/washing_machine.dm b/code/game/machinery/washing_machine.dm
index 83c1717fc4485b..fdb212266c3097 100644
--- a/code/game/machinery/washing_machine.dm
+++ b/code/game/machinery/washing_machine.dm
@@ -293,7 +293,7 @@ GLOBAL_LIST_INIT(dye_registry, list(
if(.)
var/obj/item/clothing/under/U = .
can_adjust = initial(U.can_adjust)
- if(!can_adjust && adjusted) //we deadjust the uniform if it's now unadjustable
+ if(!can_adjust && adjusted == ALT_STYLE) //we deadjust the uniform if it's now unadjustable
toggle_jumpsuit_adjust()
/obj/item/clothing/head/mob_holder/machine_wash(obj/machinery/washing_machine/washer)
diff --git a/code/modules/clothing/suits/_suits.dm b/code/modules/clothing/suits/_suits.dm
index 621d77f29079ab..0e9efa5b3fb94f 100644
--- a/code/modules/clothing/suits/_suits.dm
+++ b/code/modules/clothing/suits/_suits.dm
@@ -33,14 +33,16 @@
if(GET_ATOM_BLOOD_DNA_LENGTH(src))
. += mutable_appearance('icons/effects/blood.dmi', "[blood_overlay_type]blood")
- var/mob/living/carbon/human/M = loc
- if(!ishuman(M) || !M.w_uniform)
+ var/mob/living/carbon/human/wearer = loc
+ if(!ishuman(wearer) || !wearer.w_uniform)
return
- var/obj/item/clothing/under/U = M.w_uniform
- if(istype(U) && U.attached_accessory)
- var/obj/item/clothing/accessory/A = U.attached_accessory
- if(A.above_suit)
- . += U.accessory_overlay
+ var/obj/item/clothing/under/undershirt = wearer.w_uniform
+ if(!istype(undershirt) || !LAZYLEN(undershirt.attached_accessories))
+ return
+
+ var/obj/item/clothing/accessory/displayed = undershirt.attached_accessories[1]
+ if(displayed.above_suit)
+ . += undershirt.accessory_overlay
/obj/item/clothing/suit/update_clothes_damaged_state(damaged_state = CLOTHING_DAMAGED)
..()
diff --git a/code/modules/clothing/under/_under.dm b/code/modules/clothing/under/_under.dm
index 8e461d959cc0a4..04873edd2268e1 100644
--- a/code/modules/clothing/under/_under.dm
+++ b/code/modules/clothing/under/_under.dm
@@ -11,15 +11,35 @@
drop_sound = 'sound/items/handling/cloth_drop.ogg'
pickup_sound = 'sound/items/handling/cloth_pickup.ogg'
limb_integrity = 30
+
+ /// Has this undersuit been freshly laundered and, as such, imparts a mood bonus for wearing
+ var/freshly_laundered = FALSE
+
+ // Alt style handling
+ /// Can this suit be adjustd up or down to an alt style
+ var/can_adjust = TRUE
+ /// If adjusted what style are we currently using?
+ var/adjusted = NORMAL_STYLE
+ /// For adjusted/rolled-down jumpsuits. FALSE = exposes chest and arms, TRUE = exposes arms only
+ var/alt_covers_chest = FALSE
/// The variable containing the flags for how the woman uniform cropping is supposed to interact with the sprite.
var/female_sprite_flags = FEMALE_UNIFORM_FULL
- var/has_sensor = HAS_SENSORS // For the crew computer
+
+ // Sensor handling
+ /// Does this undersuit have suit sensors in general
+ var/has_sensor = HAS_SENSORS
+ /// Does this undersuit spawn with a random sensor value
var/random_sensor = TRUE
+ /// What is the active sensor mode of this udnersuit
var/sensor_mode = NO_SENSORS
- var/can_adjust = TRUE
- var/adjusted = NORMAL_STYLE
- var/alt_covers_chest = FALSE // for adjusted/rolled-down jumpsuits, FALSE = exposes chest and arms, TRUE = exposes arms only
- var/obj/item/clothing/accessory/attached_accessory
+
+ // Accessory handling (Can be componentized eventually)
+ /// The max number of accessories we can have on this suit.
+ var/max_number_of_accessories = 5
+ /// A list of all accessories attached to us.
+ var/list/obj/item/clothing/accessory/attached_accessories
+ /// The overlay of the accessory we're demonstrating. Only index 1 will show up.
+ /// This is the overlay on the MOB, not the item itself.
var/mutable_appearance/accessory_overlay
supports_variations_flags = CLOTHING_DIGITIGRADE_VARIATION
@@ -33,32 +53,32 @@
//make the sensor mode favor higher levels, except coords.
sensor_mode = pick(SENSOR_VITALS, SENSOR_VITALS, SENSOR_VITALS, SENSOR_LIVING, SENSOR_LIVING, SENSOR_COORDS, SENSOR_COORDS, SENSOR_OFF)
register_context()
+ AddElement(/datum/element/update_icon_updates_onmob, flags = ITEM_SLOT_ICLOTHING|ITEM_SLOT_OCLOTHING, body = TRUE)
/obj/item/clothing/under/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
- var/screentip_change = FALSE
+ . = NONE
if(isnull(held_item) && has_sensor == HAS_SENSORS)
context[SCREENTIP_CONTEXT_RMB] = "Toggle suit sensors"
- screentip_change = TRUE
+ . = CONTEXTUAL_SCREENTIP_SET
+
+ if(istype(held_item, /obj/item/clothing/accessory) && length(attached_accessories) < max_number_of_accessories)
+ context[SCREENTIP_CONTEXT_LMB] = "Attach accessory"
+ . = CONTEXTUAL_SCREENTIP_SET
- if(istype(held_item, /obj/item/clothing/accessory) && !attached_accessory)
- var/obj/item/clothing/accessory/accessory = held_item
- if(accessory.can_attach_accessory(src, user))
- context[SCREENTIP_CONTEXT_LMB] = "Attach accessory"
- screentip_change = TRUE
+ if(LAZYLEN(attached_accessories))
+ context[SCREENTIP_CONTEXT_ALT_RMB] = "Remove accessory"
+ . = CONTEXTUAL_SCREENTIP_SET
if(istype(held_item, /obj/item/stack/cable_coil) && has_sensor == BROKEN_SENSORS)
context[SCREENTIP_CONTEXT_LMB] = "Repair suit sensors"
- screentip_change = TRUE
+ . = CONTEXTUAL_SCREENTIP_SET
- if(attached_accessory)
- context[SCREENTIP_CONTEXT_ALT_LMB] = "Remove accessory"
- screentip_change = TRUE
- else if(can_adjust)
- context[SCREENTIP_CONTEXT_ALT_LMB] = adjusted == ALT_STYLE ? "Wear normally" : "Wear casually"
- screentip_change = TRUE
+ if(can_adjust && adjusted != DIGITIGRADE_STYLE)
+ context[SCREENTIP_CONTEXT_ALT_LMB] = "Wear [adjusted == ALT_STYLE ? "normally" : "casually"]"
+ . = CONTEXTUAL_SCREENTIP_SET
- return screentip_change ? CONTEXTUAL_SCREENTIP_SET : NONE
+ return .
/obj/item/clothing/under/worn_overlays(mutable_appearance/standing, isinhands = FALSE)
. = ..()
@@ -72,15 +92,18 @@
if(accessory_overlay)
. += accessory_overlay
-/obj/item/clothing/under/attackby(obj/item/I, mob/user, params)
- if((has_sensor == BROKEN_SENSORS) && istype(I, /obj/item/stack/cable_coil))
- var/obj/item/stack/cable_coil/C = I
- C.use(1)
+/obj/item/clothing/under/attackby(obj/item/attacking_item, mob/user, params)
+ if(has_sensor == BROKEN_SENSORS && istype(attacking_item, /obj/item/stack/cable_coil))
+ var/obj/item/stack/cable_coil/cabling = attacking_item
+ to_chat(user, span_notice("You repair the suit sensors on [src] with [cabling]."))
+ cabling.use(1)
has_sensor = HAS_SENSORS
- to_chat(user,span_notice("You repair the suit sensors on [src] with [C]."))
return TRUE
- if(!attach_accessory(I, user))
- return ..()
+
+ if(istype(attacking_item, /obj/item/clothing/accessory))
+ return attach_accessory(attacking_item, user)
+
+ return ..()
/obj/item/clothing/under/attack_hand_secondary(mob/user, params)
. = ..()
@@ -91,65 +114,53 @@
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
/obj/item/clothing/under/update_clothes_damaged_state(damaged_state = CLOTHING_DAMAGED)
- ..()
- if(ismob(loc))
- var/mob/M = loc
- M.update_worn_undersuit()
+ . = ..()
if(damaged_state == CLOTHING_SHREDDED && has_sensor > NO_SENSORS)
has_sensor = BROKEN_SENSORS
else if(damaged_state == CLOTHING_PRISTINE && has_sensor == BROKEN_SENSORS)
has_sensor = HAS_SENSORS
+ update_appearance()
/obj/item/clothing/under/emp_act(severity)
. = ..()
if(. & EMP_PROTECT_SELF)
return
- if(has_sensor > NO_SENSORS)
- if(severity <= EMP_HEAVY)
- has_sensor = BROKEN_SENSORS
- if(ismob(loc))
- var/mob/M = loc
- to_chat(M,span_warning("[src]'s sensors short out!"))
- else
- sensor_mode = pick(SENSOR_OFF, SENSOR_OFF, SENSOR_OFF, SENSOR_LIVING, SENSOR_LIVING, SENSOR_VITALS, SENSOR_VITALS, SENSOR_COORDS)
- if(ismob(loc))
- var/mob/M = loc
- to_chat(M,span_warning("The sensors on the [src] change rapidly!"))
- if(ishuman(loc))
- var/mob/living/carbon/human/ooman = loc
- if(ooman.w_uniform == src)
- ooman.update_suit_sensors()
+ if(has_sensor == NO_SENSORS || has_sensor == BROKEN_SENSORS)
+ return
+
+ if(severity <= EMP_HEAVY)
+ has_sensor = BROKEN_SENSORS
+ if(ismob(loc))
+ var/mob/M = loc
+ to_chat(M,span_warning("[src]'s sensors short out!"))
+
+ else
+ sensor_mode = pick(SENSOR_OFF, SENSOR_OFF, SENSOR_OFF, SENSOR_LIVING, SENSOR_LIVING, SENSOR_VITALS, SENSOR_VITALS, SENSOR_COORDS)
+ if(ismob(loc))
+ var/mob/M = loc
+ to_chat(M,span_warning("The sensors on the [src] change rapidly!"))
+
+ if(ishuman(loc))
+ var/mob/living/carbon/human/ooman = loc
+ if(ooman.w_uniform == src)
+ ooman.update_suit_sensors()
/obj/item/clothing/under/visual_equipped(mob/user, slot)
- ..()
- if(adjusted)
- adjusted = NORMAL_STYLE
- female_sprite_flags = initial(female_sprite_flags)
- if(!alt_covers_chest)
- body_parts_covered |= CHEST
+ . = ..()
+ if(adjusted == ALT_STYLE)
+ adjust_to_normal()
if((supports_variations_flags & CLOTHING_DIGITIGRADE_VARIATION) && ishuman(user))
- var/mob/living/carbon/human/H = user
- if(H.dna.species.bodytype & BODYTYPE_DIGITIGRADE)
+ var/mob/living/carbon/human/wearer = user
+ if(wearer.dna.species.bodytype & BODYTYPE_DIGITIGRADE)
adjusted = DIGITIGRADE_STYLE
- H.update_worn_undersuit()
-
- if(attached_accessory && !(slot & ITEM_SLOT_HANDS) && ishuman(user))
- var/mob/living/carbon/human/H = user
- attached_accessory.on_uniform_equip(src, user)
- H.fan_hud_set_fandom()
- if(attached_accessory.above_suit)
- H.update_worn_oversuit()
-
-/obj/item/clothing/under/dropped(mob/user)
- if(attached_accessory)
- attached_accessory.on_uniform_dropped(src, user)
- if(ishuman(user))
- var/mob/living/carbon/human/H = user
- H.fan_hud_set_fandom()
- if(attached_accessory.above_suit)
- H.update_worn_oversuit()
+ update_appearance()
+
+/obj/item/clothing/under/equipped(mob/living/user, slot)
..()
+ if((slot & ITEM_SLOT_ICLOTHING) && freshly_laundered)
+ freshly_laundered = FALSE
+ user.add_mood_event("fresh_laundry", /datum/mood_event/fresh_laundry)
/mob/living/carbon/human/update_suit_sensors()
. = ..()
@@ -165,78 +176,98 @@
/mob/living/carbon/human/dummy/update_sensor_list()
return
-/obj/item/clothing/under/proc/attach_accessory(obj/item/tool, mob/user, notifyAttach = 1)
- . = FALSE
- if(!istype(tool, /obj/item/clothing/accessory))
+// End suit sensor handling
+
+/// Attach the passed accessory to the clothing item
+/obj/item/clothing/under/proc/attach_accessory(obj/item/clothing/accessory/accessory, mob/living/user, attach_message = TRUE)
+ if(!istype(accessory))
return
- var/obj/item/clothing/accessory/accessory = tool
- if(attached_accessory)
+ if(length(attached_accessories) >= max_number_of_accessories)
if(user)
- to_chat(user, span_warning("[src] already has an accessory."))
+ balloon_alert(user, "too many accessories!")
return
- if(!accessory.can_attach_accessory(src, user)) //Make sure the suit has a place to put the accessory.
+ if(!accessory.can_attach_accessory(src, user))
return
if(user && !user.temporarilyRemoveItemFromInventory(accessory))
return
if(!accessory.attach(src, user))
return
- . = TRUE
- if(user && notifyAttach)
- to_chat(user, span_notice("You attach [accessory] to [src]."))
+ LAZYADD(attached_accessories, accessory)
+ accessory.forceMove(src)
+ // Allow for accessories to react to the acccessory list now
+ accessory.successful_attach(src)
- var/accessory_color = attached_accessory.icon_state
- accessory_overlay = mutable_appearance(attached_accessory.worn_icon, "[accessory_color]")
- accessory_overlay.alpha = attached_accessory.alpha
- accessory_overlay.color = attached_accessory.color
+ if(user && attach_message)
+ balloon_alert(user, "accessory attached")
+
+ if(isnull(accessory_overlay))
+ create_accessory_overlay()
update_appearance()
- if(!ishuman(loc))
- return
+ return TRUE
- var/mob/living/carbon/human/holder = loc
- holder.update_worn_undersuit()
- holder.update_worn_oversuit()
- holder.fan_hud_set_fandom()
+/// Removes (pops) the topmost accessory from the accessories list and puts it in the user's hands if supplied
+/obj/item/clothing/under/proc/pop_accessory(mob/living/user, attach_message = TRUE)
+ var/obj/item/clothing/accessory/popped_accessory = attached_accessories[1]
+ remove_accessory(popped_accessory)
-/obj/item/clothing/under/proc/remove_accessory(mob/user)
- . = FALSE
- if(!isliving(user))
- return
- if(!can_use(user))
+ if(!user)
return
- if(!attached_accessory)
- return
+ user.put_in_hands(popped_accessory)
+ if(attach_message)
+ popped_accessory.balloon_alert(user, "accessory removed")
- . = TRUE
- var/obj/item/clothing/accessory/accessory = attached_accessory
- attached_accessory.detach(src, user)
- if(user.put_in_hands(accessory))
- to_chat(user, span_notice("You detach [accessory] from [src]."))
- else
- to_chat(user, span_notice("You detach [accessory] from [src] and it falls on the floor."))
+/// Removes the passed accesory from our accessories list
+/obj/item/clothing/under/proc/remove_accessory(obj/item/clothing/accessory/removed)
+ if(removed == attached_accessories[1])
+ accessory_overlay = null
+
+ // Remove it from the list before detaching
+ LAZYREMOVE(attached_accessories, removed)
+ removed.detach(src)
+
+ if(isnull(accessory_overlay) && LAZYLEN(attached_accessories))
+ create_accessory_overlay()
update_appearance()
- if(!ishuman(loc))
- return
- var/mob/living/carbon/human/holder = loc
- holder.update_worn_undersuit()
- holder.update_worn_oversuit()
- holder.fan_hud_set_fandom()
+/// Handles creating the worn overlay mutable appearance
+/// Only the first accessory attached is displayed (currently)
+/obj/item/clothing/under/proc/create_accessory_overlay()
+ var/obj/item/clothing/accessory/prime_accessory = attached_accessories[1]
+ accessory_overlay = mutable_appearance(prime_accessory.worn_icon, prime_accessory.icon_state)
+ accessory_overlay.alpha = prime_accessory.alpha
+ accessory_overlay.color = prime_accessory.color
+/obj/item/clothing/under/Exited(atom/movable/gone, direction)
+ . = ..()
+ // If one of our accessories was moved out, handle it
+ if(gone in attached_accessories)
+ remove_accessory(gone)
+
+/// Helper to remove all attachments to the passed location
+/obj/item/clothing/under/proc/dump_attachments(atom/drop_to = drop_location())
+ for(var/obj/item/clothing/accessory/worn_accessory as anything in attached_accessories)
+ remove_accessory(worn_accessory)
+ worn_accessory.forceMove(drop_to)
+
+/obj/item/clothing/under/atom_destruction(damage_flag)
+ dump_attachments()
+ return ..()
+
+/obj/item/clothing/under/Destroy()
+ QDEL_LAZYLIST(attached_accessories)
+ return ..()
/obj/item/clothing/under/examine(mob/user)
. = ..()
if(can_adjust)
- if(adjusted == ALT_STYLE)
- . += "Alt-click on [src] to wear it normally."
- else
- . += "Alt-click on [src] to wear it casually."
- if (has_sensor == BROKEN_SENSORS)
- . += "Its sensors appear to be shorted out."
+ . += "Alt-click on [src] to wear it [adjusted == ALT_STYLE ? "normally" : "casually"]."
+ if(has_sensor == BROKEN_SENSORS)
+ . += "Its sensors appear to be shorted out. You could repair it with some cabling."
else if(has_sensor > NO_SENSORS)
switch(sensor_mode)
if(SENSOR_OFF)
@@ -247,48 +278,34 @@
. += "Its vital tracker appears to be enabled."
if(SENSOR_COORDS)
. += "Its vital tracker and tracking beacon appear to be enabled."
- if(attached_accessory)
- . += "\A [attached_accessory] is attached to it."
+ if(LAZYLEN(attached_accessories))
+ var/list/accessories = list_accessories_with_icon(user)
+ . += "It has [english_list(accessories)] attached."
+ . += "Alt-Right-Click to remove [attached_accessories[1]]."
+
+/// Helper to list out all accessories with an icon besides it, for use in examine
+/obj/item/clothing/under/proc/list_accessories_with_icon(mob/user)
+ var/list/all_accessories = list()
+ for(var/obj/item/clothing/accessory/attached as anything in attached_accessories)
+ all_accessories += attached.get_examine_string(user)
+
+ return all_accessories
/obj/item/clothing/under/verb/toggle()
set name = "Adjust Suit Sensors"
set category = "Object"
set src in usr
var/mob/user_mob = usr
- if (isdead(user_mob))
- return
- if (!can_use(user_mob))
- return
- if(has_sensor == LOCKED_SENSORS)
- to_chat(user_mob, "The controls are locked.")
- return
- if(has_sensor == BROKEN_SENSORS)
- to_chat(user_mob, "The sensors have shorted out!")
- return
- if(has_sensor <= NO_SENSORS)
- to_chat(user_mob, "This suit does not have any sensors.")
+ if(!can_toggle_sensors(user_mob))
return
var/list/modes = list("Off", "Binary vitals", "Exact vitals", "Tracking beacon")
var/switchMode = tgui_input_list(user_mob, "Select a sensor mode", "Suit Sensors", modes, modes[sensor_mode + 1])
if(isnull(switchMode))
return
-
- if (!can_use(user_mob)) //make sure they didn't hold the window open.
- return
- if(get_dist(user_mob, src) > 1)
- to_chat(user_mob, span_warning("You have moved too far away!"))
+ if(!can_toggle_sensors(user_mob))
return
- if(has_sensor == LOCKED_SENSORS)
- to_chat(user_mob, "The controls are locked.")
- return
- if(has_sensor == BROKEN_SENSORS)
- to_chat(user_mob, "The sensors have shorted out!")
- return
- if(has_sensor <= NO_SENSORS)
- to_chat(user_mob, "This suit does not have any sensors.")
- return
sensor_mode = modes.Find(switchMode) - 1
if (loc == user_mob)
switch(sensor_mode)
@@ -306,80 +323,116 @@
if(H.w_uniform == src)
H.update_suit_sensors()
+/// Checks if the toggler is allowed to toggle suit sensors currently
+/obj/item/clothing/under/proc/can_toggle_sensors(mob/toggler)
+ if(!can_use(toggler) || toggler.stat == DEAD) //make sure they didn't hold the window open.
+ return FALSE
+ if(get_dist(toggler, src) > 1)
+ balloon_alert(toggler, "too far!")
+ return FALSE
+
+ switch(has_sensor)
+ if(LOCKED_SENSORS)
+ balloon_alert(toggler, "sensor controls locked!")
+ return FALSE
+ if(BROKEN_SENSORS)
+ balloon_alert(toggler, "sensors shorted!")
+ return FALSE
+ if(NO_SENSORS)
+ balloon_alert(toggler, "no sensors to ajdust!")
+ return FALSE
+
+ return TRUE
+
/obj/item/clothing/under/AltClick(mob/user)
. = ..()
if(.)
return
+ if(!can_adjust)
+ balloon_alert(user, "can't be adjusted!")
+ return
+ if(!can_use(user))
+ return
+ rolldown()
+
+/obj/item/clothing/under/alt_click_secondary(mob/user)
+ . = ..()
+ if(.)
+ return
+
+ if(!LAZYLEN(attached_accessories))
+ balloon_alert(user, "no accessories to remove!")
+ return
if(!user.can_perform_action(src, NEED_DEXTERITY))
return
- if(attached_accessory)
- remove_accessory(user)
- else
- rolldown()
+
+ pop_accessory(user)
/obj/item/clothing/under/verb/jumpsuit_adjust()
set name = "Adjust Jumpsuit Style"
set category = null
set src in usr
- rolldown()
-/obj/item/clothing/under/proc/rolldown()
- if(!can_use(usr))
- return
if(!can_adjust)
- to_chat(usr, span_warning("You cannot wear this suit any differently!"))
+ balloon_alert(usr, "can't be adjusted!")
+ return
+ if(!can_use(usr))
return
+ rolldown()
+
+/obj/item/clothing/under/proc/rolldown()
if(toggle_jumpsuit_adjust())
to_chat(usr, span_notice("You adjust the suit to wear it more casually."))
else
to_chat(usr, span_notice("You adjust the suit back to normal."))
- if(ishuman(usr))
- var/mob/living/carbon/human/H = usr
- H.update_worn_undersuit()
- H.update_body()
+ update_appearance()
+
+/// Helper to toggle the jumpsuit style, if possible
+/// Returns the new state
/obj/item/clothing/under/proc/toggle_jumpsuit_adjust()
- if(adjusted == DIGITIGRADE_STYLE)
- return
- adjusted = !adjusted
- if(adjusted)
- if(alt_covers_chest) //For snowflake suits that do NOT expose the chest. //MONKESTATION EDIT
+ switch(adjusted)
+ if(DIGITIGRADE_STYLE)
return
- if(!(female_sprite_flags & FEMALE_UNIFORM_TOP_ONLY))
- female_sprite_flags = NO_FEMALE_UNIFORM
- if(!alt_covers_chest) // for the special snowflake suits that expose the chest when adjusted (and also the arms, realistically)
- body_parts_covered &= ~CHEST
- body_parts_covered &= ~ARMS
- else
- female_sprite_flags = initial(female_sprite_flags)
- if(!alt_covers_chest)
- body_parts_covered |= CHEST
- body_parts_covered |= ARMS
- if(!LAZYLEN(damage_by_parts))
- return adjusted
- for(var/zone in list(BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM)) // ugly check to make sure we don't reenable protection on a disabled part
- if(damage_by_parts[zone] > limb_integrity)
- body_parts_covered &= body_zone2cover_flags(zone)
- return adjusted
-/obj/item/clothing/under/rank
- dying_key = DYE_REGISTRY_UNDER
+ if(NORMAL_STYLE)
+ adjust_to_alt()
-/obj/item/clothing/under/proc/dump_attachment()
- if(!attached_accessory)
- return
- var/atom/drop_location = drop_location()
- attached_accessory.transform *= 2
- attached_accessory.pixel_x -= 8
- attached_accessory.pixel_y += 8
- if(drop_location)
- attached_accessory.forceMove(drop_location)
- cut_overlays()
- attached_accessory = null
- accessory_overlay = null
- update_appearance()
+ if(ALT_STYLE)
+ adjust_to_normal()
-/obj/item/clothing/under/rank/atom_destruction(damage_flag)
- dump_attachment()
+ SEND_SIGNAL(src, COMSIG_CLOTHING_UNDER_ADJUSTED)
+ return adjusted
+
+/// Helper to reset to normal jumpsuit state
+/obj/item/clothing/under/proc/adjust_to_normal()
+ adjusted = NORMAL_STYLE
+ female_sprite_flags = initial(female_sprite_flags)
+ if(!alt_covers_chest)
+ body_parts_covered |= CHEST
+ body_parts_covered |= ARMS
+ if(LAZYLEN(damage_by_parts))
+ // ugly check to make sure we don't reenable protection on a disabled part
+ for(var/zone in list(BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
+ if(damage_by_parts[zone] > limb_integrity)
+ body_parts_covered &= body_zone2cover_flags(zone)
+
+/// Helper to adjust to alt jumpsuit state
+/obj/item/clothing/under/proc/adjust_to_alt()
+ adjusted = ALT_STYLE
+ if(alt_covers_chest) //For snowflake suits that do NOT expose the chest. //MONKESTATION EDIT
+ return
+ if(!(female_sprite_flags & FEMALE_UNIFORM_TOP_ONLY))
+ female_sprite_flags = NO_FEMALE_UNIFORM
+ if(!alt_covers_chest) // for the special snowflake suits that expose the chest when adjusted (and also the arms, realistically)
+ body_parts_covered &= ~CHEST
+ body_parts_covered &= ~ARMS
+
+/obj/item/clothing/under/can_use(mob/user)
+ if(ismob(user) && !user.can_perform_action(src, NEED_DEXTERITY|NEED_HANDS|ALLOW_RESTING))
+ return FALSE
return ..()
+
+/obj/item/clothing/under/rank
+ dying_key = DYE_REGISTRY_UNDER
diff --git a/code/modules/clothing/under/accessories.dm b/code/modules/clothing/under/accessories.dm
deleted file mode 100755
index a10cabfaf06549..00000000000000
--- a/code/modules/clothing/under/accessories.dm
+++ /dev/null
@@ -1,505 +0,0 @@
-/obj/item/clothing/accessory //Ties moved to neck slot items, but as there are still things like medals and armbands, this accessory system is being kept as-is
- name = "Accessory"
- desc = "Something has gone wrong!"
- icon = 'icons/obj/clothing/accessories.dmi'
- worn_icon = 'icons/mob/clothing/accessories.dmi'
- icon_state = "plasma"
- inhand_icon_state = "" //no inhands
- slot_flags = 0
- w_class = WEIGHT_CLASS_SMALL
- /// Whether or not the accessory displays through suits and the like.
- var/above_suit = TRUE
- /// TRUE if shown as a small icon in corner, FALSE if overlayed
- var/minimize_when_attached = TRUE
- /// What equipment slot the accessory attaches to.
- var/attachment_slot = CHEST
-
-/obj/item/clothing/accessory/proc/can_attach_accessory(obj/item/clothing/U, mob/user)
- if(!attachment_slot || (U && U.body_parts_covered & attachment_slot))
- return TRUE
- if(user)
- to_chat(user, span_warning("There doesn't seem to be anywhere to put [src]..."))
-
-/obj/item/clothing/accessory/proc/attach(obj/item/clothing/under/U, user)
- if(atom_storage)
- if(U.atom_storage)
- return FALSE
- U.clone_storage(atom_storage)
- U.atom_storage.set_real_location(src)
- U.attached_accessory = src
- forceMove(U)
- layer = FLOAT_LAYER
- plane = FLOAT_PLANE
- if(minimize_when_attached)
- transform *= 0.5 //halve the size so it doesn't overpower the under
- pixel_x += 8
- pixel_y -= 8
- U.add_overlay(src)
-
- U.set_armor(U.get_armor().add_other_armor(get_armor()))
-
- if(isliving(user))
- on_uniform_equip(U, user)
-
- return TRUE
-
-/obj/item/clothing/accessory/proc/detach(obj/item/clothing/under/U, user)
- if(U.atom_storage && U.atom_storage.real_location?.resolve() == src)
- QDEL_NULL(U.atom_storage)
-
- U.set_armor(U.get_armor().subtract_other_armor(get_armor()))
-
- if(isliving(user))
- on_uniform_dropped(U, user)
-
- if(minimize_when_attached)
- transform *= 2
- pixel_x -= 8
- pixel_y += 8
- layer = initial(layer)
- SET_PLANE_IMPLICIT(src, initial(plane))
- U.cut_overlays()
- U.attached_accessory = null
- U.accessory_overlay = null
-
-
-/obj/item/clothing/accessory/proc/on_uniform_equip(obj/item/clothing/under/U, user)
- return
-
-/obj/item/clothing/accessory/proc/on_uniform_dropped(obj/item/clothing/under/U, user)
- return
-
-/obj/item/clothing/accessory/attack_self_secondary(mob/user)
- if(user.can_perform_action(src, NEED_DEXTERITY))
- above_suit = !above_suit
- to_chat(user, "[src] will be worn [above_suit ? "above" : "below"] your suit.")
- return
-
- return ..()
-
-/obj/item/clothing/accessory/examine(mob/user)
- . = ..()
- . += span_notice("\The [src] can be attached to a uniform. Alt-click to remove it once attached.")
- . += span_notice("\The [src] can be worn above or below your suit. Right-click to toggle.")
-
-/obj/item/clothing/accessory/waistcoat
- name = "waistcoat"
- desc = "For some classy, murderous fun."
- icon_state = "waistcoat"
- inhand_icon_state = "wcoat"
- lefthand_file = 'icons/mob/inhands/clothing/suits_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/clothing/suits_righthand.dmi'
- minimize_when_attached = FALSE
- attachment_slot = null
- greyscale_config = /datum/greyscale_config/waistcoat
- greyscale_config_worn = /datum/greyscale_config/waistcoat_worn
- greyscale_colors = "#414344"
- flags_1 = IS_PLAYER_COLORABLE_1
-
-/obj/item/clothing/accessory/vest_sheriff
- name = "sheriff vest"
- desc = "Now you just have to pick your favourite deputy."
- icon_state = "vest_sheriff"
- lefthand_file = 'icons/mob/inhands/clothing/suits_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/clothing/suits_righthand.dmi'
- inhand_icon_state = "vest_sheriff"
- minimize_when_attached = TRUE
- attachment_slot = null
-
-/obj/item/clothing/accessory/maidcorset
- name = "maid corset"
- desc = "The final touch that holds it all together."
- icon_state = "maidcorset"
- inhand_icon_state = "maidapron"
- lefthand_file = 'icons/mob/inhands/clothing/suits_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/clothing/suits_righthand.dmi'
- minimize_when_attached = FALSE
- attachment_slot = null
-
-/obj/item/clothing/accessory/maidapron
- name = "maid apron"
- desc = "The best part of a maid costume."
- icon_state = "maidapron"
- inhand_icon_state = "maidapron"
- lefthand_file = 'icons/mob/inhands/clothing/suits_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/clothing/suits_righthand.dmi'
- minimize_when_attached = FALSE
- attachment_slot = null
-
-//////////
-//Medals//
-//////////
-
-/obj/item/clothing/accessory/medal
- name = "bronze medal"
- desc = "A bronze medal."
- icon_state = "bronze"
- custom_materials = list(/datum/material/iron=HALF_SHEET_MATERIAL_AMOUNT)
- resistance_flags = FIRE_PROOF
- var/medaltype = "medal" //Sprite used for medalbox
- var/commended = FALSE
-
-//Pinning medals on people
-/obj/item/clothing/accessory/medal/attack(mob/living/carbon/human/M, mob/living/user)
- if(ishuman(M) && !(user.istate & ISTATE_HARM))
-
- if(M.wear_suit)
- if((M.wear_suit.flags_inv & HIDEJUMPSUIT)) //Check if the jumpsuit is covered
- to_chat(user, span_warning("Medals can only be pinned on jumpsuits."))
- return
-
- if(M.w_uniform)
- var/obj/item/clothing/under/U = M.w_uniform
- var/delay = 20
- if(user == M)
- delay = 0
- else
- user.visible_message(span_notice("[user] is trying to pin [src] on [M]'s chest."), \
- span_notice("You try to pin [src] on [M]'s chest."))
- var/input
- if(!commended && user != M)
- input = tgui_input_text(user, "Reason for this commendation? It will be recorded by Nanotrasen.", "Commendation", max_length = 140)
- if(do_after(user, delay, target = M))
- if(U.attach_accessory(src, user, 0)) //Attach it, do not notify the user of the attachment
- if(user == M)
- to_chat(user, span_notice("You attach [src] to [U]."))
- else
- user.visible_message(span_notice("[user] pins \the [src] on [M]'s chest."), \
- span_notice("You pin \the [src] on [M]'s chest."))
- if(input)
- SSblackbox.record_feedback("associative", "commendation", 1, list("commender" = "[user.real_name]", "commendee" = "[M.real_name]", "medal" = "[src]", "reason" = input))
- GLOB.commendations += "[user.real_name] awarded [M.real_name] the [name]! \n- [input]"
- commended = TRUE
- desc += "
The inscription reads: [input] - [user.real_name]"
- M.log_message("was given the following commendation by [key_name(user)]: [input]", LOG_GAME, color = "green")
- message_admins("[key_name_admin(M)] was given the following commendation by [key_name_admin(user)]: [input]")
- add_memory_in_range(M, 7, /datum/memory/received_medal, protagonist = M, deuteragonist = user, medal_type = src, medal_text = input)
-
- else
- to_chat(user, span_warning("Medals can only be pinned on jumpsuits!"))
- else
- ..()
-
-/obj/item/clothing/accessory/medal/conduct
- name = "distinguished conduct medal"
- desc = "A bronze medal awarded for distinguished conduct. Whilst a great honor, this is the most basic award given by Nanotrasen. It is often awarded by a captain to a member of his crew."
-
-/obj/item/clothing/accessory/medal/bronze_heart
- name = "bronze heart medal"
- desc = "A bronze heart-shaped medal awarded for sacrifice. It is often awarded posthumously or for severe injury in the line of duty."
- icon_state = "bronze_heart"
-
-/obj/item/clothing/accessory/medal/ribbon
- name = "ribbon"
- desc = "A ribbon"
- icon_state = "cargo"
-
-/obj/item/clothing/accessory/medal/ribbon/cargo
- name = "\"cargo tech of the shift\" award"
- desc = "An award bestowed only upon those cargotechs who have exhibited devotion to their duty in keeping with the highest traditions of Cargonia."
-
-/obj/item/clothing/accessory/medal/silver
- name = "silver medal"
- desc = "A silver medal."
- icon_state = "silver"
- medaltype = "medal-silver"
- custom_materials = list(/datum/material/silver=HALF_SHEET_MATERIAL_AMOUNT)
-
-/obj/item/clothing/accessory/medal/silver/valor
- name = "medal of valor"
- desc = "A silver medal awarded for acts of exceptional valor."
-
-/obj/item/clothing/accessory/medal/silver/security
- name = "robust security award"
- desc = "An award for distinguished combat and sacrifice in defence of Nanotrasen's commercial interests. Often awarded to security staff."
-
-/obj/item/clothing/accessory/medal/silver/excellence
- name = "\proper the head of personnel award for outstanding achievement in the field of excellence"
- desc = "Nanotrasen's dictionary defines excellence as \"the quality or condition of being excellent\". This is awarded to those rare crewmembers who fit that definition."
-
-/obj/item/clothing/accessory/medal/silver/bureaucracy
- name = "\improper Excellence in Bureaucracy Medal"
- desc = "Awarded for exemplary managerial services rendered while under contract with Nanotrasen."
-
-/obj/item/clothing/accessory/medal/gold
- name = "gold medal"
- desc = "A prestigious golden medal."
- icon_state = "gold"
- medaltype = "medal-gold"
- custom_materials = list(/datum/material/gold=HALF_SHEET_MATERIAL_AMOUNT)
-
-/obj/item/clothing/accessory/medal/med_medal
- name = "exemplary performance medal"
- desc = "A medal awarded to those who have shown distinguished conduct, performance, and initiative within the medical department."
- icon_state = "med_medal"
-
-/obj/item/clothing/accessory/medal/med_medal2
- name = "excellence in medicine medal"
- desc = "A medal awarded to those who have shown legendary performance, competence, and initiative beyond all expectations within the medical department."
- icon_state = "med_medal2"
-
-/obj/item/clothing/accessory/medal/gold/captain
- name = "medal of captaincy"
- desc = "A golden medal awarded exclusively to those promoted to the rank of captain. It signifies the codified responsibilities of a captain to Nanotrasen, and their undisputable authority over their crew."
- resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
-
-/obj/item/clothing/accessory/medal/gold/heroism
- name = "medal of exceptional heroism"
- desc = "An extremely rare golden medal awarded only by CentCom. To receive such a medal is the highest honor and as such, very few exist. This medal is almost never awarded to anybody but commanders."
-
-/obj/item/clothing/accessory/medal/plasma
- name = "plasma medal"
- desc = "An eccentric medal made of plasma."
- icon_state = "plasma"
- medaltype = "medal-plasma"
- armor_type = /datum/armor/medal_plasma
- custom_materials = list(/datum/material/plasma=HALF_SHEET_MATERIAL_AMOUNT)
-
-/datum/armor/medal_plasma
- fire = -10
-
-/obj/item/clothing/accessory/medal/plasma/Initialize(mapload)
- . = ..()
- AddElement(/datum/element/atmos_sensitive, mapload)
-
-/obj/item/clothing/accessory/medal/plasma/should_atmos_process(datum/gas_mixture/air, exposed_temperature)
- return exposed_temperature > 300
-
-/obj/item/clothing/accessory/medal/plasma/atmos_expose(datum/gas_mixture/air, exposed_temperature)
- atmos_spawn_air("plasma=20;TEMP=[exposed_temperature]")
- visible_message(span_danger("\The [src] bursts into flame!"), span_userdanger("Your [src] bursts into flame!"))
- qdel(src)
-
-/obj/item/clothing/accessory/medal/plasma/nobel_science
- name = "nobel sciences award"
- desc = "A plasma medal which represents significant contributions to the field of science or engineering."
-
-
-
-////////////
-//Armbands//
-////////////
-
-/obj/item/clothing/accessory/armband
- name = "red armband"
- desc = "A fancy red armband!"
- icon_state = "redband"
- attachment_slot = null
-
-/obj/item/clothing/accessory/armband/deputy
- name = "security deputy armband"
- desc = "An armband, worn by personnel authorized to act as a deputy of station security."
-
-/obj/item/clothing/accessory/armband/cargo
- name = "cargo bay guard armband"
- desc = "An armband, worn by the station's security forces to display which department they're assigned to. This one is brown."
- icon_state = "cargoband"
-
-/obj/item/clothing/accessory/armband/engine
- name = "engineering guard armband"
- desc = "An armband, worn by the station's security forces to display which department they're assigned to. This one is orange with a reflective strip!"
- icon_state = "engieband"
-
-/obj/item/clothing/accessory/armband/science
- name = "science guard armband"
- desc = "An armband, worn by the station's security forces to display which department they're assigned to. This one is purple."
- icon_state = "rndband"
-
-/obj/item/clothing/accessory/armband/hydro
- name = "hydroponics guard armband"
- desc = "An armband, worn by the station's security forces to display which department they're assigned to. This one is green and blue."
- icon_state = "hydroband"
-
-/obj/item/clothing/accessory/armband/med
- name = "medical guard armband"
- desc = "An armband, worn by the station's security forces to display which department they're assigned to. This one is white."
- icon_state = "medband"
-
-/obj/item/clothing/accessory/armband/medblue
- name = "medical guard armband"
- desc = "An armband, worn by the station's security forces to display which department they're assigned to. This one is white and blue."
- icon_state = "medblueband"
-
-//////////////
-//OBJECTION!//
-//////////////
-
-/obj/item/clothing/accessory/lawyers_badge
- name = "attorney's badge"
- desc = "Fills you with the conviction of JUSTICE. Lawyers tend to want to show it to everyone they meet."
- icon_state = "lawyerbadge"
-
-/obj/item/clothing/accessory/lawyers_badge/attack_self(mob/user)
- if(prob(1))
- user.say("The testimony contradicts the evidence!", forced = "attorney's badge")
- user.visible_message(span_notice("[user] shows [user.p_their()] attorney's badge."), span_notice("You show your attorney's badge."))
-
-/obj/item/clothing/accessory/lawyers_badge/on_uniform_equip(obj/item/clothing/under/U, mob/living/user)
- RegisterSignal(user, COMSIG_LIVING_SLAM_TABLE, PROC_REF(table_slam))
- user.bubble_icon = "lawyer"
-
-/obj/item/clothing/accessory/lawyers_badge/on_uniform_dropped(obj/item/clothing/under/U, mob/living/user)
- UnregisterSignal(user, COMSIG_LIVING_SLAM_TABLE)
- user.bubble_icon = initial(user.bubble_icon)
-
-/obj/item/clothing/accessory/lawyers_badge/proc/table_slam(mob/living/source, obj/structure/table/the_table)
- SIGNAL_HANDLER
- INVOKE_ASYNC(src, PROC_REF(handle_table_slam), source)
-
-/obj/item/clothing/accessory/lawyers_badge/proc/handle_table_slam(mob/living/user)
- user.say("Objection!!", spans = list(SPAN_YELL), forced=TRUE)
-
-////////////////
-//HA HA! NERD!//
-////////////////
-/obj/item/clothing/accessory/pocketprotector
- name = "pocket protector"
- desc = "Can protect your clothing from ink stains, but you'll look like a nerd if you're using one."
- icon_state = "pocketprotector"
-
-/obj/item/clothing/accessory/pocketprotector/Initialize(mapload)
- . = ..()
-
- create_storage(storage_type = /datum/storage/pockets/pocketprotector)
-
-/obj/item/clothing/accessory/pocketprotector/detach(obj/item/clothing/under/U, user)
- var/drop_loc = drop_location()
- for(var/atom/movable/held as anything in src)
- held.forceMove(drop_loc)
- return ..()
-
-/obj/item/clothing/accessory/pocketprotector/full/Initialize(mapload)
- . = ..()
-
- new /obj/item/pen/red(src)
- new /obj/item/pen(src)
- new /obj/item/pen/blue(src)
-
-/obj/item/clothing/accessory/pocketprotector/cosmetology/Initialize(mapload)
- . = ..()
- for(var/i in 1 to 3)
- new /obj/item/lipstick/random(src)
-
-////////////////
-//REAL BIG FAN//
-////////////////
-
-/obj/item/clothing/accessory/clown_enjoyer_pin
- name = "\improper Clown Pin"
- desc = "A pin to show off your appreciation for clowns and clowning!"
- icon_state = "clown_enjoyer_pin"
-
-/obj/item/clothing/accessory/clown_enjoyer_pin/on_uniform_equip(obj/item/clothing/under/U, user)
- var/mob/living/L = user
- if(HAS_TRAIT(L, TRAIT_CLOWN_ENJOYER))
- L.add_mood_event("clown_enjoyer_pin", /datum/mood_event/clown_enjoyer_pin)
-
-/obj/item/clothing/accessory/clown_enjoyer_pin/on_uniform_dropped(obj/item/clothing/under/U, user)
- var/mob/living/L = user
- if(HAS_TRAIT(L, TRAIT_CLOWN_ENJOYER))
- L.clear_mood_event("clown_enjoyer_pin")
-
-/obj/item/clothing/accessory/mime_fan_pin
- name = "\improper Mime Pin"
- desc = "A pin to show off your appreciation for mimes and miming!"
- icon_state = "mime_fan_pin"
-
-/obj/item/clothing/accessory/mime_fan_pin/on_uniform_equip(obj/item/clothing/under/U, user)
- var/mob/living/L = user
- if(HAS_TRAIT(L, TRAIT_MIME_FAN))
- L.add_mood_event("mime_fan_pin", /datum/mood_event/mime_fan_pin)
-
-/obj/item/clothing/accessory/mime_fan_pin/on_uniform_dropped(obj/item/clothing/under/U, user)
- var/mob/living/L = user
- if(HAS_TRAIT(L, TRAIT_MIME_FAN))
- L.clear_mood_event("mime_fan_pin")
-
-////////////////
-//OONGA BOONGA//
-////////////////
-
-/obj/item/clothing/accessory/talisman
- name = "bone talisman"
- desc = "A hunter's talisman, some say the old gods smile on those who wear it."
- icon_state = "talisman"
- armor_type = /datum/armor/accessory_talisman
- attachment_slot = null
-
-/datum/armor/accessory_talisman
- melee = 5
- bullet = 5
- laser = 5
- energy = 5
- bomb = 20
- bio = 20
- acid = 25
-
-/obj/item/clothing/accessory/skullcodpiece
- name = "skull codpiece"
- desc = "A skull shaped ornament, intended to protect the important things in life."
- icon_state = "skull"
- armor_type = /datum/armor/accessory_skullcodpiece
- attachment_slot = GROIN
-
-/datum/armor/accessory_skullcodpiece
- melee = 5
- bullet = 5
- laser = 5
- energy = 5
- bomb = 20
- bio = 20
- acid = 25
-
-/obj/item/clothing/accessory/skilt
- name = "Sinew Skirt"
- desc = "For the last time. IT'S A KILT not a skirt."
- icon_state = "skilt"
- minimize_when_attached = FALSE
- armor_type = /datum/armor/accessory_skilt
- attachment_slot = GROIN
-
-/datum/armor/accessory_skilt
- melee = 5
- bullet = 5
- laser = 5
- energy = 5
- bomb = 20
- bio = 20
- acid = 25
-
-/obj/item/clothing/accessory/allergy_dogtag
- name = "Allergy dogtag"
- desc = "Dogtag with a list of your allergies"
- icon_state = "allergy"
- minimize_when_attached = TRUE
- attachment_slot = CHEST
- ///Display message
- var/display
-
-/obj/item/clothing/accessory/allergy_dogtag/examine(mob/user)
- . = ..()
- . += "The dogtag has a listing of allergies : [display]"
-
-/obj/item/clothing/accessory/allergy_dogtag/on_uniform_equip(obj/item/clothing/under/U, user)
- . = ..()
- RegisterSignal(U,COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
-
-/obj/item/clothing/accessory/allergy_dogtag/on_uniform_dropped(obj/item/clothing/under/U, user)
- . = ..()
- UnregisterSignal(U,COMSIG_ATOM_EXAMINE)
-
-///What happens when we examine the uniform
-/obj/item/clothing/accessory/allergy_dogtag/proc/on_examine(datum/source, mob/user, list/examine_list)
- SIGNAL_HANDLER
- examine_list += "The dogtag has a listing of allergies : [display]"
-
-/obj/item/clothing/accessory/deaf_pin
- name = "deaf personnel pin"
- desc = "Indicates that the wearer is deaf."
- icon_state = "deaf_pin"
-
-///Awarded for being dutiful and extinguishing the debt from the "Indebted" quirk.
-/obj/item/clothing/accessory/debt_payer_pin
- name = "debt payer pin"
- desc = "I've paid my debt and all I've got was this pin."
- icon_state = "debt_payer_pin"
diff --git a/code/modules/clothing/under/accessories/_accessories.dm b/code/modules/clothing/under/accessories/_accessories.dm
new file mode 100644
index 00000000000000..89f1008479aaf9
--- /dev/null
+++ b/code/modules/clothing/under/accessories/_accessories.dm
@@ -0,0 +1,185 @@
+/**
+ * Clothing accessories.
+ *
+ * These items can be slotted onto an undershirt to provide a bit of flair.
+ *
+ * These should be very light on their effects. Armor should be avoided entirely.
+ *
+ * Multiple accessories can be equipped on a mob, and only the firstmost one is shown on their sprite.
+ * The rest are still shown on examine, but this may create unfair circumstances when you can't examine someone.
+ */
+/obj/item/clothing/accessory
+ name = "Accessory"
+ desc = "Something has gone wrong!"
+ icon = 'icons/obj/clothing/accessories.dmi'
+ worn_icon = 'icons/mob/clothing/accessories.dmi'
+ icon_state = "plasma"
+ inhand_icon_state = "" //no inhands
+ slot_flags = NONE
+ w_class = WEIGHT_CLASS_SMALL
+ /// Whether or not the accessory displays through suits and the like.
+ var/above_suit = TRUE
+ /// TRUE if shown as a small icon in corner, FALSE if overlayed
+ var/minimize_when_attached = TRUE
+ /// What equipment slot the accessory attaches to.
+ /// If NONE, can always attach, while if supplied, can only attach if the clothing covers this slot.
+ var/attachment_slot = CHEST
+
+/obj/item/clothing/accessory/Initialize(mapload)
+ . = ..()
+ register_context()
+
+/**
+ * Can we be attached to the passed clothing article?
+ */
+/obj/item/clothing/accessory/proc/can_attach_accessory(obj/item/clothing/under/attach_to, mob/living/user)
+ if(!istype(attach_to))
+ CRASH("[type] - can_attach_accessory called with an invalid item to attach to. (got: [attach_to])")
+
+ if(atom_storage && attach_to.atom_storage)
+ if(user)
+ attach_to.balloon_alert(user, "isn't compatible!")
+ return FALSE
+
+ if(attachment_slot && !(attach_to.body_parts_covered & attachment_slot))
+ if(user)
+ attach_to.balloon_alert(user, "can't attach there!")
+ return FALSE
+
+ return TRUE
+
+/**
+ * Actually attach this accessory to the passed clothing article.
+ *
+ * The accessory is not yet within the clothing's loc at this point, this hapens after success.
+ */
+/obj/item/clothing/accessory/proc/attach(obj/item/clothing/under/attach_to, mob/living/attacher)
+ SHOULD_CALL_PARENT(TRUE)
+
+ if(atom_storage)
+ attach_to.clone_storage(atom_storage)
+ attach_to.atom_storage.set_real_location(src)
+
+ var/num_other_accessories = LAZYLEN(attach_to.attached_accessories)
+ layer = FLOAT_LAYER + clamp(attach_to.max_number_of_accessories - num_other_accessories, 0, 10)
+ plane = FLOAT_PLANE
+
+ if(minimize_when_attached)
+ transform *= 0.5
+ pixel_x += 8
+ pixel_y += (-8 + LAZYLEN(attach_to.attached_accessories) * 2)
+
+ RegisterSignal(attach_to, COMSIG_ITEM_EQUIPPED, PROC_REF(on_uniform_equipped))
+ RegisterSignal(attach_to, COMSIG_ITEM_DROPPED, PROC_REF(on_uniform_dropped))
+ RegisterSignal(attach_to, COMSIG_CLOTHING_UNDER_ADJUSTED, PROC_REF(on_uniform_adjusted))
+ RegisterSignal(attach_to, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(on_uniform_update))
+
+ return TRUE
+
+/// Called after attach is completely successful and the accessory is in the clothing's loc
+/obj/item/clothing/accessory/proc/successful_attach(obj/item/clothing/under/attached_to)
+ SHOULD_CALL_PARENT(TRUE)
+
+ // Do on-equip effects if we're already equipped
+ var/mob/worn_on = attached_to.loc
+ if(istype(worn_on))
+ on_uniform_equipped(attached_to, worn_on, worn_on.get_slot_by_item(attached_to))
+
+ SEND_SIGNAL(src, COMSIG_ACCESSORY_ATTACHED, attached_to)
+ SEND_SIGNAL(attached_to, COMSIG_CLOTHING_ACCESSORY_ATTACHED, src)
+
+/**
+ * Detach this accessory from the passed clothing article
+ *
+ * We may have exited the clothing's loc at this point
+ */
+/obj/item/clothing/accessory/proc/detach(obj/item/clothing/under/detach_from)
+ SHOULD_CALL_PARENT(TRUE)
+
+ if(IS_WEAKREF_OF(src, detach_from.atom_storage?.real_location))
+ // Ensure void items do not stick around
+ atom_storage.close_all()
+ detach_from.atom_storage.close_all()
+ // And clean up the storage we made
+ QDEL_NULL(detach_from.atom_storage)
+
+ UnregisterSignal(detach_from, list(COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED, COMSIG_CLOTHING_UNDER_ADJUSTED, COMSIG_ATOM_UPDATE_OVERLAYS))
+ var/mob/dropped_from = detach_from.loc
+ if(istype(dropped_from))
+ on_uniform_dropped(detach_from, dropped_from)
+
+ SEND_SIGNAL(src, COMSIG_ACCESSORY_DETACHED, detach_from)
+ SEND_SIGNAL(detach_from, COMSIG_CLOTHING_ACCESSORY_DETACHED, src)
+
+ if(minimize_when_attached)
+ transform *= 2
+ // just randomize position
+ pixel_x = rand(4, -4)
+ pixel_y = rand(4, -4)
+
+ layer = initial(layer)
+ SET_PLANE_IMPLICIT(src, initial(plane))
+ return TRUE
+
+/// Signal proc for [COMSIG_ITEM_EQUIPPED] on the uniform we're pinned to
+/obj/item/clothing/accessory/proc/on_uniform_equipped(obj/item/clothing/under/source, mob/living/user, slot)
+ SIGNAL_HANDLER
+
+ if(!(slot & source.slot_flags))
+ return
+
+ accessory_equipped(source, user)
+
+/// Signal proc for [COMSIG_ITEM_DROPPED] on the uniform we're pinned to
+/obj/item/clothing/accessory/proc/on_uniform_dropped(obj/item/clothing/under/source, mob/living/user)
+ SIGNAL_HANDLER
+
+ accessory_dropped(source, user)
+ user.update_clothing(ITEM_SLOT_ICLOTHING|ITEM_SLOT_OCLOTHING)
+
+/// Called when the uniform this accessory is pinned to is equipped in a valid slot
+/obj/item/clothing/accessory/proc/accessory_equipped(obj/item/clothing/under/clothes, mob/living/user)
+ return
+
+/// Called when the uniform this accessory is pinned to is dropped
+/obj/item/clothing/accessory/proc/accessory_dropped(obj/item/clothing/under/clothes, mob/living/user)
+ return
+
+/// Signal proc for [COMSIG_CLOTHING_UNDER_ADJUSTED] on the uniform we're pinned to
+/// Checks if we can no longer be attached to the uniform, and if so, drops us
+/obj/item/clothing/accessory/proc/on_uniform_adjusted(obj/item/clothing/under/source)
+ SIGNAL_HANDLER
+
+ if(can_attach_accessory(source))
+ return
+
+ source.remove_accessory(src)
+ forceMove(source.drop_location())
+ source.visible_message(span_warning("[src] falls off of [source]!"))
+
+/// Signal proc for [COMSIG_ATOM_UPDATE_OVERLAYS] on the uniform we're pinned to to add our overlays to the inventory icon
+/obj/item/clothing/accessory/proc/on_uniform_update(obj/item/source, list/overlays)
+ SIGNAL_HANDLER
+
+ overlays |= src
+
+/obj/item/clothing/accessory/attack_self_secondary(mob/user)
+ . = ..()
+ if(.)
+ return
+ if(user.can_perform_action(src, NEED_DEXTERITY))
+ above_suit = !above_suit
+ balloon_alert(user, "wearing [above_suit ? "above" : "below"] suits")
+ return TRUE
+
+/obj/item/clothing/accessory/examine(mob/user)
+ . = ..()
+ . += "It can be attached to a uniform."
+ . += "It can be worn above or below your suit. Right-click to toggle."
+
+/obj/item/clothing/accessory/add_context(atom/source, list/context, obj/item/held_item, mob/user)
+ if(!isnull(held_item))
+ return NONE
+
+ context[SCREENTIP_CONTEXT_RMB] = "Wear [above_suit ? "below" : "above"] suit"
+ return CONTEXTUAL_SCREENTIP_SET
diff --git a/code/modules/clothing/under/accessories/armbands.dm b/code/modules/clothing/under/accessories/armbands.dm
new file mode 100644
index 00000000000000..fb26192561182e
--- /dev/null
+++ b/code/modules/clothing/under/accessories/armbands.dm
@@ -0,0 +1,40 @@
+// Armbands, which go around a sleeve of a shirt.
+/obj/item/clothing/accessory/armband
+ name = "red armband"
+ desc = "A fancy red armband!"
+ icon_state = "redband"
+ attachment_slot = NONE
+
+/obj/item/clothing/accessory/armband/deputy
+ name = "security deputy armband"
+ desc = "An armband, worn by personnel authorized to act as a deputy of station security."
+
+/obj/item/clothing/accessory/armband/cargo
+ name = "cargo bay guard armband"
+ desc = "An armband, worn by the station's security forces to display which department they're assigned to. This one is brown."
+ icon_state = "cargoband"
+
+/obj/item/clothing/accessory/armband/engine
+ name = "engineering guard armband"
+ desc = "An armband, worn by the station's security forces to display which department they're assigned to. This one is orange with a reflective strip!"
+ icon_state = "engieband"
+
+/obj/item/clothing/accessory/armband/science
+ name = "science guard armband"
+ desc = "An armband, worn by the station's security forces to display which department they're assigned to. This one is purple."
+ icon_state = "rndband"
+
+/obj/item/clothing/accessory/armband/hydro
+ name = "hydroponics guard armband"
+ desc = "An armband, worn by the station's security forces to display which department they're assigned to. This one is green and blue."
+ icon_state = "hydroband"
+
+/obj/item/clothing/accessory/armband/med
+ name = "medical guard armband"
+ desc = "An armband, worn by the station's security forces to display which department they're assigned to. This one is white."
+ icon_state = "medband"
+
+/obj/item/clothing/accessory/armband/medblue
+ name = "medical guard armband"
+ desc = "An armband, worn by the station's security forces to display which department they're assigned to. This one is white and blue."
+ icon_state = "medblueband"
diff --git a/code/modules/clothing/under/accessories/badges.dm b/code/modules/clothing/under/accessories/badges.dm
new file mode 100644
index 00000000000000..f5c68023128bd9
--- /dev/null
+++ b/code/modules/clothing/under/accessories/badges.dm
@@ -0,0 +1,205 @@
+// Badges, pins, and other very small items that slot onto a shirt.
+/obj/item/clothing/accessory/lawyers_badge
+ name = "attorney's badge"
+ desc = "Fills you with the conviction of JUSTICE. Lawyers tend to want to show it to everyone they meet."
+ icon_state = "lawyerbadge"
+
+/obj/item/clothing/accessory/lawyers_badge/interact(mob/user)
+ . = ..()
+ if(prob(1))
+ user.say("The testimony contradicts the evidence!", forced = "[src]")
+ user.visible_message(span_notice("[user] shows [user.p_their()] attorney's badge."), span_notice("You show your attorney's badge."))
+
+/obj/item/clothing/accessory/lawyers_badge/accessory_equipped(obj/item/clothing/under/clothes, mob/living/user)
+ RegisterSignal(user, COMSIG_LIVING_SLAM_TABLE, PROC_REF(table_slam))
+ user.bubble_icon = "lawyer"
+
+/obj/item/clothing/accessory/lawyers_badge/accessory_dropped(obj/item/clothing/under/clothes, mob/living/user)
+ UnregisterSignal(user, COMSIG_LIVING_SLAM_TABLE)
+ user.bubble_icon = initial(user.bubble_icon)
+
+/obj/item/clothing/accessory/lawyers_badge/proc/table_slam(mob/living/source, obj/structure/table/the_table)
+ SIGNAL_HANDLER
+
+ ASYNC
+ source.say("Objection!!", spans = list(SPAN_YELL), forced = "[src]")
+
+/obj/item/clothing/accessory/clown_enjoyer_pin
+ name = "\improper Clown Pin"
+ desc = "A pin to show off your appreciation for clowns and clowning!"
+ icon_state = "clown_enjoyer_pin"
+
+/obj/item/clothing/accessory/clown_enjoyer_pin/can_attach_accessory(obj/item/clothing/under/attach_to, mob/living/user)
+ . = ..()
+ if(!.)
+ return
+ if(locate(/obj/item/clothing/accessory/mime_fan_pin) in attach_to.attached_accessories)
+ if(user)
+ attach_to.balloon_alert(user, "can't pick both sides!")
+ return FALSE
+ return TRUE
+
+/obj/item/clothing/accessory/clown_enjoyer_pin/accessory_equipped(obj/item/clothing/under/clothes, mob/living/user)
+ if(HAS_TRAIT(user, TRAIT_CLOWN_ENJOYER))
+ user.add_mood_event("clown_enjoyer_pin", /datum/mood_event/clown_enjoyer_pin)
+ if(ishuman(user))
+ var/mob/living/carbon/human/human_equipper = user
+ human_equipper.fan_hud_set_fandom()
+
+/obj/item/clothing/accessory/clown_enjoyer_pin/accessory_dropped(obj/item/clothing/under/clothes, mob/living/user)
+ user.clear_mood_event("clown_enjoyer_pin")
+ if(ishuman(user))
+ var/mob/living/carbon/human/human_equipper = user
+ human_equipper.fan_hud_set_fandom()
+
+/obj/item/clothing/accessory/mime_fan_pin
+ name = "\improper Mime Pin"
+ desc = "A pin to show off your appreciation for mimes and miming!"
+ icon_state = "mime_fan_pin"
+
+/obj/item/clothing/accessory/mime_fan_pin/can_attach_accessory(obj/item/clothing/under/attach_to, mob/living/user)
+ . = ..()
+ if(!.)
+ return
+ if(locate(/obj/item/clothing/accessory/clown_enjoyer_pin) in attach_to.attached_accessories)
+ if(user)
+ attach_to.balloon_alert(user, "can't pick both sides!")
+ return FALSE
+ return TRUE
+
+/obj/item/clothing/accessory/mime_fan_pin/accessory_equipped(obj/item/clothing/under/clothes, mob/living/user)
+ if(HAS_TRAIT(user, TRAIT_MIME_FAN))
+ user.add_mood_event("mime_fan_pin", /datum/mood_event/mime_fan_pin)
+ if(ishuman(user))
+ var/mob/living/carbon/human/human_equipper = user
+ human_equipper.fan_hud_set_fandom()
+
+/obj/item/clothing/accessory/mime_fan_pin/accessory_dropped(obj/item/clothing/under/clothes, mob/living/user)
+ user.clear_mood_event("mime_fan_pin")
+ if(ishuman(user))
+ var/mob/living/carbon/human/human_equipper = user
+ human_equipper.fan_hud_set_fandom()
+
+/obj/item/clothing/accessory/pocketprotector
+ name = "pocket protector"
+ desc = "Can protect your clothing from ink stains, but you'll look like a nerd if you're using one."
+ icon_state = "pocketprotector"
+
+/obj/item/clothing/accessory/pocketprotector/Initialize(mapload)
+ . = ..()
+ create_storage(storage_type = /datum/storage/pockets/pocketprotector)
+
+/obj/item/clothing/accessory/pocketprotector/can_attach_accessory(obj/item/clothing/under/attach_to, mob/living/user)
+ . = ..()
+ if(!.)
+ return
+
+ if(!isnull(attach_to.atom_storage))
+ if(user)
+ attach_to.balloon_alert(user, "not compatible!")
+ return FALSE
+ return TRUE
+
+/obj/item/clothing/accessory/pocketprotector/full
+
+/obj/item/clothing/accessory/pocketprotector/full/Initialize(mapload)
+ . = ..()
+ new /obj/item/pen/red(src)
+ new /obj/item/pen(src)
+ new /obj/item/pen/blue(src)
+
+/obj/item/clothing/accessory/pocketprotector/cosmetology
+
+/obj/item/clothing/accessory/pocketprotector/cosmetology/Initialize(mapload)
+ . = ..()
+ for(var/i in 1 to 3)
+ new /obj/item/lipstick/random(src)
+
+/obj/item/clothing/accessory/dogtag
+ name = "Dogtag"
+ desc = "Can't wear a collar, but this is fine?"
+ icon_state = "allergy"
+ attachment_slot = NONE // actually NECK but that doesn't make sense
+ /// What message is displayed when our dogtags / its clothes / its wearer is examined
+ var/display = "Nothing!"
+
+/obj/item/clothing/accessory/dogtag/examine(mob/user)
+ . = ..()
+ . += display
+
+// Examining the clothes will display the examine message of the dogtag
+/obj/item/clothing/accessory/dogtag/attach(obj/item/clothing/under/attach_to, mob/living/attacher)
+ . = ..()
+ if(!.)
+ return
+ RegisterSignal(attach_to, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
+
+/obj/item/clothing/accessory/dogtag/detach(obj/item/clothing/under/detach_from)
+ . = ..()
+ UnregisterSignal(detach_from, COMSIG_ATOM_EXAMINE)
+
+// Double examining the person wearing the clothes will display the examine message of the dogtag
+/obj/item/clothing/accessory/dogtag/accessory_equipped(obj/item/clothing/under/clothes, mob/living/user)
+ RegisterSignal(user, COMSIG_ATOM_EXAMINE_MORE, PROC_REF(on_examine))
+
+/obj/item/clothing/accessory/dogtag/accessory_dropped(obj/item/clothing/under/clothes, mob/living/user)
+ UnregisterSignal(user, COMSIG_ATOM_EXAMINE_MORE)
+
+/// Adds the examine message to the clothes and mob.
+/obj/item/clothing/accessory/dogtag/proc/on_examine(datum/source, mob/user, list/examine_list)
+ SIGNAL_HANDLER
+
+ // Only show the examine message if we're close (2 tiles)
+ if(!IN_GIVEN_RANGE(get_turf(user), get_turf(src), 2))
+ return
+
+ if(ismob(source))
+ // Examining a mob wearing the clothes, wearing the dogtag will also show the message
+ examine_list += "A dogtag is hanging around [source.p_their()] neck: [display]"
+ else
+ examine_list += "A dogtag is attached to [source]: [display]"
+
+/obj/item/clothing/accessory/dogtag/allergy
+ name = "Allergy dogtag"
+ desc = "A dogtag with a listing of allergies."
+
+/obj/item/clothing/accessory/dogtag/allergy/Initialize(mapload, allergy_string)
+ . = ..()
+ if(allergy_string)
+ display = span_notice("The dogtag has a listing of allergies: [allergy_string]")
+ else
+ display = span_notice("The dogtag is all scratched up.")
+
+/// Reskins for the pride pin accessory, mapped by display name to icon state
+GLOBAL_LIST_INIT(pride_pin_reskins, list(
+ "Rainbow Pride" = "pride",
+ "Bisexual Pride" = "pride_bi",
+ "Pansexual Pride" = "pride_pan",
+ "Asexual Pride" = "pride_ace",
+ "Non-binary Pride" = "pride_enby",
+ "Transgender Pride" = "pride_trans",
+ "Intersex Pride" = "pride_intersex",
+ "Lesbian Pride" = "pride_lesbian",
+))
+
+/obj/item/clothing/accessory/pride
+ name = "pride pin"
+ desc = "A Nanotrasen Diversity & Inclusion Center-sponsored holographic pin to show off your pride, reminding the crew of their unwavering commitment to equity, diversity, and inclusion!"
+ icon_state = "pride"
+ obj_flags = UNIQUE_RENAME
+ infinite_reskin = TRUE
+
+/obj/item/clothing/accessory/pride/Initialize(mapload)
+ . = ..()
+ unique_reskin = GLOB.pride_pin_reskins
+
+///Awarded for being dutiful and extinguishing the debt from the "Indebted" quirk.
+/obj/item/clothing/accessory/debt_payer_pin
+ name = "debt payer pin"
+ desc = "I've paid my debt and all I've got was this pin."
+ icon_state = "debt_payer_pin"
+
+/obj/item/clothing/accessory/deaf_pin
+ name = "deaf personnel pin"
+ desc = "Indicates that the wearer is deaf."
+ icon_state = "deaf_pin"
diff --git a/code/modules/clothing/under/accessories/medals.dm b/code/modules/clothing/under/accessories/medals.dm
new file mode 100644
index 00000000000000..01f134fa1ba0f0
--- /dev/null
+++ b/code/modules/clothing/under/accessories/medals.dm
@@ -0,0 +1,140 @@
+/obj/item/clothing/accessory/medal
+ name = "bronze medal"
+ desc = "A bronze medal."
+ icon_state = "bronze"
+ custom_materials = list(/datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT)
+ resistance_flags = FIRE_PROOF
+ /// Sprite used for medalbox
+ var/medaltype = "medal"
+ /// Has this been use for a commendation?
+ var/commended = FALSE
+
+// If someone adds SHOULD_NOT_SLEEP anywhere up the chain, this will need to be reworked
+/obj/item/clothing/accessory/medal/attach(obj/item/clothing/under/attach_to, mob/living/attacher)
+ if(isnull(attacher))
+ // Do normal attach
+ return ..()
+
+ var/mob/living/distinguished = attach_to.loc
+ if(!istype(distinguished) || distinguished == attacher)
+ // Do normal attach
+ return ..()
+
+ // Do a do_after before we attach, and allow us to include a commendation message.
+ attacher.visible_message(
+ span_notice("[attacher] is trying to pin [src] on [distinguished]'s chest."),
+ span_notice("You try to pin [src] on [distinguished]'s chest."),
+ )
+
+ var/input
+ if(!commended)
+ input = tgui_input_text(attacher, "Reason for this commendation? It will be recorded by Nanotrasen.", "Commendation", max_length = 140)
+
+ if(!do_after(attacher, 2 SECONDS, distinguished))
+ return FALSE
+
+ attacher.visible_message(
+ span_notice("[attacher] pins [src] on [distinguished]'s chest."),
+ span_notice("You pin [src] on [distinguished]'s chest."),
+ )
+ if(!input)
+ return FALSE
+
+ commended = TRUE
+ SSblackbox.record_feedback("associative", "commendation", 1, list("commender" = "[attacher.real_name]", "commendee" = "[distinguished.real_name]", "medal" = "[src]", "reason" = input))
+ GLOB.commendations += "[attacher.real_name] awarded [distinguished.real_name] the [name]! \n- [input]"
+ desc += "
The inscription reads: [input] - [attacher.real_name]"
+ distinguished.log_message("was given the following commendation by [key_name(attacher)]: [input]", LOG_GAME, color = "green")
+ message_admins("[key_name_admin(distinguished)] was given the following commendation by [key_name_admin(attacher)]: [input]")
+ add_memory_in_range(distinguished, 7, /datum/memory/received_medal, protagonist = distinguished, deuteragonist = attacher, medal_type = src, medal_text = input)
+ return ..()
+
+/obj/item/clothing/accessory/medal/conduct
+ name = "distinguished conduct medal"
+ desc = "A bronze medal awarded for distinguished conduct. Whilst a great honor, this is the most basic award given by Nanotrasen. It is often awarded by a captain to a member of his crew."
+
+/obj/item/clothing/accessory/medal/bronze_heart
+ name = "bronze heart medal"
+ desc = "A bronze heart-shaped medal awarded for sacrifice. It is often awarded posthumously or for severe injury in the line of duty."
+ icon_state = "bronze_heart"
+
+/obj/item/clothing/accessory/medal/ribbon
+ name = "ribbon"
+ desc = "A ribbon"
+ icon_state = "cargo"
+
+/obj/item/clothing/accessory/medal/ribbon/cargo
+ name = "\"cargo tech of the shift\" award"
+ desc = "An award bestowed only upon those cargotechs who have exhibited devotion to their duty in keeping with the highest traditions of Cargonia."
+
+/obj/item/clothing/accessory/medal/silver
+ name = "silver medal"
+ desc = "A silver medal."
+ icon_state = "silver"
+ medaltype = "medal-silver"
+ custom_materials = list(/datum/material/silver = HALF_SHEET_MATERIAL_AMOUNT)
+
+/obj/item/clothing/accessory/medal/silver/valor
+ name = "medal of valor"
+ desc = "A silver medal awarded for acts of exceptional valor."
+
+/obj/item/clothing/accessory/medal/silver/security
+ name = "robust security award"
+ desc = "An award for distinguished combat and sacrifice in defence of Nanotrasen's commercial interests. Often awarded to security staff."
+
+/obj/item/clothing/accessory/medal/silver/excellence
+ name = "\proper the head of personnel award for outstanding achievement in the field of excellence"
+ desc = "Nanotrasen's dictionary defines excellence as \"the quality or condition of being excellent\". This is awarded to those rare crewmembers who fit that definition."
+
+/obj/item/clothing/accessory/medal/silver/bureaucracy
+ name = "\improper Excellence in Bureaucracy Medal"
+ desc = "Awarded for exemplary managerial services rendered while under contract with Nanotrasen."
+
+/obj/item/clothing/accessory/medal/gold
+ name = "gold medal"
+ desc = "A prestigious golden medal."
+ icon_state = "gold"
+ medaltype = "medal-gold"
+ custom_materials = list(/datum/material/gold = HALF_SHEET_MATERIAL_AMOUNT)
+
+/obj/item/clothing/accessory/medal/med_medal
+ name = "exemplary performance medal"
+ desc = "A medal awarded to those who have shown distinguished conduct, performance, and initiative within the medical department."
+ icon_state = "med_medal"
+
+/obj/item/clothing/accessory/medal/med_medal2
+ name = "excellence in medicine medal"
+ desc = "A medal awarded to those who have shown legendary performance, competence, and initiative beyond all expectations within the medical department."
+ icon_state = "med_medal2"
+
+/obj/item/clothing/accessory/medal/gold/captain
+ name = "medal of captaincy"
+ desc = "A golden medal awarded exclusively to those promoted to the rank of captain. It signifies the codified responsibilities of a captain to Nanotrasen, and their undisputable authority over their crew."
+ resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
+
+/obj/item/clothing/accessory/medal/gold/heroism
+ name = "medal of exceptional heroism"
+ desc = "An extremely rare golden medal awarded only by CentCom. To receive such a medal is the highest honor and as such, very few exist. This medal is almost never awarded to anybody but commanders."
+
+/obj/item/clothing/accessory/medal/plasma
+ name = "plasma medal"
+ desc = "An eccentric medal made of plasma."
+ icon_state = "plasma"
+ medaltype = "medal-plasma"
+ custom_materials = list(/datum/material/plasma = HALF_SHEET_MATERIAL_AMOUNT)
+
+/obj/item/clothing/accessory/medal/plasma/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/atmos_sensitive, mapload)
+
+/obj/item/clothing/accessory/medal/plasma/should_atmos_process(datum/gas_mixture/air, exposed_temperature)
+ return exposed_temperature > 300
+
+/obj/item/clothing/accessory/medal/plasma/atmos_expose(datum/gas_mixture/air, exposed_temperature)
+ atmos_spawn_air("plasma=20;TEMP=[exposed_temperature]")
+ visible_message(span_danger("\The [src] bursts into flame!"), span_userdanger("Your [src] bursts into flame!"))
+ qdel(src)
+
+/obj/item/clothing/accessory/medal/plasma/nobel_science
+ name = "nobel sciences award"
+ desc = "A plasma medal which represents significant contributions to the field of science or engineering."
diff --git a/code/modules/clothing/under/accessories/tribal.dm b/code/modules/clothing/under/accessories/tribal.dm
new file mode 100644
index 00000000000000..ad55b26fa89fdb
--- /dev/null
+++ b/code/modules/clothing/under/accessories/tribal.dm
@@ -0,0 +1,19 @@
+// Tribal undershirt accessories, made from bone or sinew.
+/obj/item/clothing/accessory/talisman
+ name = "bone talisman"
+ desc = "A hunter's talisman, some say the old gods smile on those who wear it."
+ icon_state = "talisman"
+ attachment_slot = NONE
+
+/obj/item/clothing/accessory/skullcodpiece
+ name = "skull codpiece"
+ desc = "A skull shaped ornament, intended to protect the important things in life."
+ icon_state = "skull"
+ attachment_slot = GROIN
+
+/obj/item/clothing/accessory/skilt
+ name = "Sinew Skirt"
+ desc = "For the last time. IT'S A KILT not a skirt."
+ icon_state = "skilt"
+ minimize_when_attached = FALSE
+ attachment_slot = GROIN
diff --git a/code/modules/clothing/under/accessories/vests.dm b/code/modules/clothing/under/accessories/vests.dm
new file mode 100644
index 00000000000000..a6603941dd08d8
--- /dev/null
+++ b/code/modules/clothing/under/accessories/vests.dm
@@ -0,0 +1,44 @@
+// Accessories that mostly or entirely cover a shirt.
+/obj/item/clothing/accessory/waistcoat
+ name = "waistcoat"
+ desc = "For some classy, murderous fun."
+ icon_state = "waistcoat"
+ inhand_icon_state = "wcoat"
+ lefthand_file = 'icons/mob/inhands/clothing/suits_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/clothing/suits_righthand.dmi'
+ minimize_when_attached = FALSE
+ attachment_slot = NONE
+ greyscale_config = /datum/greyscale_config/waistcoat
+ greyscale_config_worn = /datum/greyscale_config/waistcoat_worn
+ greyscale_colors = "#414344"
+ flags_1 = IS_PLAYER_COLORABLE_1
+
+/obj/item/clothing/accessory/vest_sheriff
+ name = "sheriff vest"
+ desc = "Now you just have to pick your favourite deputy."
+ icon_state = "vest_sheriff"
+ lefthand_file = 'icons/mob/inhands/clothing/suits_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/clothing/suits_righthand.dmi'
+ inhand_icon_state = "vest_sheriff"
+ minimize_when_attached = TRUE
+ attachment_slot = NONE
+
+/obj/item/clothing/accessory/maidcorset
+ name = "maid corset"
+ desc = "The final touch that holds it all together."
+ icon_state = "maidcorset"
+ inhand_icon_state = "maidapron"
+ lefthand_file = 'icons/mob/inhands/clothing/suits_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/clothing/suits_righthand.dmi'
+ minimize_when_attached = FALSE
+ attachment_slot = NONE
+
+/obj/item/clothing/accessory/maidapron
+ name = "maid apron"
+ desc = "The best part of a maid costume."
+ icon_state = "maidapron"
+ inhand_icon_state = "maidapron"
+ lefthand_file = 'icons/mob/inhands/clothing/suits_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/clothing/suits_righthand.dmi'
+ minimize_when_attached = FALSE
+ attachment_slot = NONE
diff --git a/code/modules/holodeck/computer.dm b/code/modules/holodeck/computer.dm
index df3c48f9883f77..085225e34f3799 100644
--- a/code/modules/holodeck/computer.dm
+++ b/code/modules/holodeck/computer.dm
@@ -327,9 +327,9 @@ GLOBAL_LIST_INIT(typecache_holodeck_linked_floorcheck_ok, typecacheof(list(/turf
for(var/atom/movable/atom_contents as anything in holo_atom) //make sure that things inside of a holoitem are moved outside before destroying it
atom_contents.forceMove(target_turf)
- if(istype(holo_atom, /obj/item/clothing/under/rank))
+ if(istype(holo_atom, /obj/item/clothing/under))
var/obj/item/clothing/under/holo_clothing = holo_atom
- holo_clothing.dump_attachment()
+ holo_clothing.dump_attachments()
if(!silent)
visible_message(span_notice("[holo_atom] fades away!"))
diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm
index f0f402d9228d10..8ea15e6f16289f 100644
--- a/code/modules/mob/living/carbon/human/examine.dm
+++ b/code/modules/mob/living/carbon/human/examine.dm
@@ -27,13 +27,14 @@
//uniform
if(w_uniform && !(obscured & ITEM_SLOT_ICLOTHING) && !(w_uniform.item_flags & EXAMINE_SKIP))
//accessory
- var/accessory_msg
+ var/accessory_message = ""
if(istype(w_uniform, /obj/item/clothing/under))
- var/obj/item/clothing/under/U = w_uniform
- if(U.attached_accessory)
- accessory_msg += " with [icon2html(U.attached_accessory, user)] \a [U.attached_accessory]"
+ var/obj/item/clothing/under/undershirt = w_uniform
+ var/list/accessories = undershirt.list_accessories_with_icon(user)
+ if(length(accessories))
+ accessory_message = " with [english_list(accessories)] attached"
- . += "[t_He] [t_is] wearing [w_uniform.get_examine_string(user)][accessory_msg]."
+ . += "[t_He] [t_is] wearing [w_uniform.get_examine_string(user)][accessory_message]."
//head
if(head && !(obscured & ITEM_SLOT_HEAD) && !(head.item_flags & EXAMINE_SKIP))
. += "[t_He] [t_is] wearing [head.get_examine_string(user)] on [t_his] head."
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/pandora.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/pandora.dm
index b4f171a10cebfb..0693daeb6f415e 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/pandora.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/pandora.dm
@@ -184,15 +184,11 @@
icon_state = "hope"
resistance_flags = FIRE_PROOF
-/obj/item/clothing/accessory/pandora_hope/on_uniform_equip(obj/item/clothing/under/U, user)
- var/mob/living/L = user
- if(L?.mind)
- L.add_mood_event("hope_lavaland", /datum/mood_event/hope_lavaland)
-
-/obj/item/clothing/accessory/pandora_hope/on_uniform_dropped(obj/item/clothing/under/U, user)
- var/mob/living/L = user
- if(L?.mind)
- L.clear_mood_event("hope_lavaland")
+/obj/item/clothing/accessory/pandora_hope/accessory_equipped(obj/item/clothing/under/clothes, mob/living/user)
+ user.add_mood_event("hope_lavaland", /datum/mood_event/hope_lavaland)
+
+/obj/item/clothing/accessory/pandora_hope/accessory_dropped(obj/item/clothing/under/clothes, mob/living/user)
+ user.clear_mood_event("hope_lavaland")
#undef SINGULAR_SHOT
#undef MAGIC_BOX
diff --git a/icons/mob/clothing/accessories.dmi b/icons/mob/clothing/accessories.dmi
index c51d64a625df06..dbe0275bc8c0ca 100644
Binary files a/icons/mob/clothing/accessories.dmi and b/icons/mob/clothing/accessories.dmi differ
diff --git a/icons/obj/clothing/accessories.dmi b/icons/obj/clothing/accessories.dmi
index 05a4a886a9bcdd..f130b71d329540 100644
Binary files a/icons/obj/clothing/accessories.dmi and b/icons/obj/clothing/accessories.dmi differ
diff --git a/monkestation/code/modules/clothing/under/_under.dm b/monkestation/code/modules/clothing/under/_under.dm
new file mode 100644
index 00000000000000..0cf4a471e76c7a
--- /dev/null
+++ b/monkestation/code/modules/clothing/under/_under.dm
@@ -0,0 +1,32 @@
+
+/obj/item/clothing/under
+ /// A weak reference to the current accessory that's providing armor.
+ var/datum/weakref/current_armored_accessory
+
+/obj/item/clothing/under/proc/refresh_armor()
+ SIGNAL_HANDLER
+ var/obj/item/clothing/accessory/armored_accesory = current_armored_accessory?.resolve()
+ if(armored_accesory)
+ set_armor(get_armor().subtract_other_armor(armored_accesory.get_armor()))
+ current_armored_accessory = null
+ for(var/obj/item/clothing/accessory/accessory as anything in attached_accessories)
+ if(QDELETED(accessory))
+ continue
+ var/datum/armor/armor = accessory.get_armor()
+ if(!armor || istype(armor, /datum/armor/none))
+ continue
+ set_armor(get_armor().add_other_armor(accessory.get_armor()))
+ current_armored_accessory = WEAKREF(accessory)
+ return
+
+/obj/item/clothing/under/attach_accessory(obj/item/clothing/accessory/accessory, mob/living/user, attach_message = TRUE)
+ . = ..()
+ if(!.)
+ return
+ RegisterSignal(accessory, COMSIG_QDELETING, PROC_REF(refresh_armor))
+ refresh_armor()
+
+/obj/item/clothing/under/remove_accessory(obj/item/clothing/accessory/removed)
+ . = ..()
+ UnregisterSignal(removed, COMSIG_QDELETING)
+ refresh_armor()
diff --git a/monkestation/code/modules/clothing/under/accessories/medals.dm b/monkestation/code/modules/clothing/under/accessories/medals.dm
new file mode 100644
index 00000000000000..d3b2619ec9cb92
--- /dev/null
+++ b/monkestation/code/modules/clothing/under/accessories/medals.dm
@@ -0,0 +1,6 @@
+
+/obj/item/clothing/accessory/medal/plasma
+ armor_type = /datum/armor/medal_plasma
+
+/datum/armor/medal_plasma
+ fire = -10
diff --git a/monkestation/code/modules/clothing/under/accessories/tribal.dm b/monkestation/code/modules/clothing/under/accessories/tribal.dm
new file mode 100644
index 00000000000000..fb197fba32cc1b
--- /dev/null
+++ b/monkestation/code/modules/clothing/under/accessories/tribal.dm
@@ -0,0 +1,35 @@
+/obj/item/clothing/accessory/talisman
+ armor_type = /datum/armor/accessory_talisman
+
+/datum/armor/accessory_talisman
+ melee = 5
+ bullet = 5
+ laser = 5
+ energy = 5
+ bomb = 20
+ bio = 20
+ acid = 25
+
+/obj/item/clothing/accessory/skullcodpiece
+ armor_type = /datum/armor/accessory_skullcodpiece
+
+/datum/armor/accessory_skullcodpiece
+ melee = 5
+ bullet = 5
+ laser = 5
+ energy = 5
+ bomb = 20
+ bio = 20
+ acid = 25
+
+/obj/item/clothing/accessory/skilt
+ armor_type = /datum/armor/accessory_skilt
+
+/datum/armor/accessory_skilt
+ melee = 5
+ bullet = 5
+ laser = 5
+ energy = 5
+ bomb = 20
+ bio = 20
+ acid = 25
diff --git a/tgstation.dme b/tgstation.dme
index 9a1b8e81c4ceea..cef1effec27f76 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -3451,7 +3451,6 @@
#include "code\modules\clothing\suits\wintercoats.dm"
#include "code\modules\clothing\suits\wiz_robe.dm"
#include "code\modules\clothing\under\_under.dm"
-#include "code\modules\clothing\under\accessories.dm"
#include "code\modules\clothing\under\color.dm"
#include "code\modules\clothing\under\costume.dm"
#include "code\modules\clothing\under\ethereal.dm"
@@ -3462,6 +3461,12 @@
#include "code\modules\clothing\under\suits.dm"
#include "code\modules\clothing\under\syndicate.dm"
#include "code\modules\clothing\under\trek.dm"
+#include "code\modules\clothing\under\accessories\_accessories.dm"
+#include "code\modules\clothing\under\accessories\armbands.dm"
+#include "code\modules\clothing\under\accessories\badges.dm"
+#include "code\modules\clothing\under\accessories\medals.dm"
+#include "code\modules\clothing\under\accessories\tribal.dm"
+#include "code\modules\clothing\under\accessories\vests.dm"
#include "code\modules\clothing\under\jobs\cargo.dm"
#include "code\modules\clothing\under\jobs\centcom.dm"
#include "code\modules\clothing\under\jobs\command.dm"
@@ -6118,8 +6123,11 @@
#include "monkestation\code\modules\clothing\suits\coats.dm"
#include "monkestation\code\modules\clothing\suits\costume.dm"
#include "monkestation\code\modules\clothing\suits\toggles.dm"
+#include "monkestation\code\modules\clothing\under\_under.dm"
#include "monkestation\code\modules\clothing\under\costume.dm"
#include "monkestation\code\modules\clothing\under\undersuit.dm"
+#include "monkestation\code\modules\clothing\under\accessories\medals.dm"
+#include "monkestation\code\modules\clothing\under\accessories\tribal.dm"
#include "monkestation\code\modules\clothing\under\civilian\clown_mime.dm"
#include "monkestation\code\modules\clothing\under\civilian\rank.dm"
#include "monkestation\code\modules\clothing\~donator\clothing.dm"