From c712a3c1cb947ae66ef22dc8de49d8d6b5870e1e Mon Sep 17 00:00:00 2001 From: ihatethisengine Date: Thu, 7 Mar 2024 21:37:41 +0300 Subject: [PATCH 01/17] some attempts --- code/__DEFINES/strippable.dm | 30 ++ code/__HELPERS/_lists.dm | 7 + code/_onclick/click_hold.dm | 2 +- code/_onclick/drag_drop.dm | 1 + code/datums/elements/strippable.dm | 466 ++++++++++++++++++ code/modules/asset_cache/asset_list.dm | 22 + .../mob/living/carbon/carbon_stripping.dm | 129 +++++ code/modules/mob/living/carbon/human/human.dm | 2 +- .../living/carbon/human/human_stripping.dm | 145 ++++++ code/modules/mob/mob.dm | 2 +- colonialmarines.dme | 3 + icons/ui_icons/inventory/back.png | Bin 0 -> 1796 bytes icons/ui_icons/inventory/belt.png | Bin 0 -> 1596 bytes icons/ui_icons/inventory/collar.png | Bin 0 -> 2090 bytes icons/ui_icons/inventory/ears.png | Bin 0 -> 1688 bytes icons/ui_icons/inventory/glasses.png | Bin 0 -> 2017 bytes icons/ui_icons/inventory/gloves.png | Bin 0 -> 2107 bytes icons/ui_icons/inventory/hand_l.png | Bin 0 -> 2019 bytes icons/ui_icons/inventory/hand_r.png | Bin 0 -> 2017 bytes icons/ui_icons/inventory/head.png | Bin 0 -> 1854 bytes icons/ui_icons/inventory/id.png | Bin 0 -> 1940 bytes icons/ui_icons/inventory/mask.png | Bin 0 -> 1930 bytes icons/ui_icons/inventory/neck.png | Bin 0 -> 1852 bytes icons/ui_icons/inventory/pocket.png | Bin 0 -> 1831 bytes icons/ui_icons/inventory/shoes.png | Bin 0 -> 1817 bytes icons/ui_icons/inventory/suit.png | Bin 0 -> 2073 bytes icons/ui_icons/inventory/suit_storage.png | Bin 0 -> 1976 bytes icons/ui_icons/inventory/uniform.png | Bin 0 -> 1885 bytes tgui/packages/tgui/interfaces/StripMenu.tsx | 399 +++++++++++++++ 29 files changed, 1205 insertions(+), 3 deletions(-) create mode 100644 code/__DEFINES/strippable.dm create mode 100644 code/datums/elements/strippable.dm create mode 100644 code/modules/mob/living/carbon/carbon_stripping.dm create mode 100644 code/modules/mob/living/carbon/human/human_stripping.dm create mode 100644 icons/ui_icons/inventory/back.png create mode 100644 icons/ui_icons/inventory/belt.png create mode 100644 icons/ui_icons/inventory/collar.png create mode 100644 icons/ui_icons/inventory/ears.png create mode 100644 icons/ui_icons/inventory/glasses.png create mode 100644 icons/ui_icons/inventory/gloves.png create mode 100644 icons/ui_icons/inventory/hand_l.png create mode 100644 icons/ui_icons/inventory/hand_r.png create mode 100644 icons/ui_icons/inventory/head.png create mode 100644 icons/ui_icons/inventory/id.png create mode 100644 icons/ui_icons/inventory/mask.png create mode 100644 icons/ui_icons/inventory/neck.png create mode 100644 icons/ui_icons/inventory/pocket.png create mode 100644 icons/ui_icons/inventory/shoes.png create mode 100644 icons/ui_icons/inventory/suit.png create mode 100644 icons/ui_icons/inventory/suit_storage.png create mode 100644 icons/ui_icons/inventory/uniform.png create mode 100644 tgui/packages/tgui/interfaces/StripMenu.tsx diff --git a/code/__DEFINES/strippable.dm b/code/__DEFINES/strippable.dm new file mode 100644 index 000000000000..f62c4d6c1b76 --- /dev/null +++ b/code/__DEFINES/strippable.dm @@ -0,0 +1,30 @@ +// All of these must be matched in StripMenu.js. +#define STRIPPABLE_ITEM_HEAD "head" +#define STRIPPABLE_ITEM_BACK "back" +#define STRIPPABLE_ITEM_MASK "wear_mask" +#define STRIPPABLE_ITEM_EYES "glasses" +#define STRIPPABLE_ITEM_L_EAR "wear_l_ear" +#define STRIPPABLE_ITEM_R_EAR "wear_r_ear" +#define STRIPPABLE_ITEM_JUMPSUIT "w_uniform" +#define STRIPPABLE_ITEM_SUIT "wear_suit" +#define STRIPPABLE_ITEM_GLOVES "gloves" +#define STRIPPABLE_ITEM_FEET "shoes" +#define STRIPPABLE_ITEM_SUIT_STORAGE "j_store" +#define STRIPPABLE_ITEM_ID "id" +#define STRIPPABLE_ITEM_BELT "belt" +#define STRIPPABLE_ITEM_LPOCKET "l_store" +#define STRIPPABLE_ITEM_RPOCKET "r_store" +#define STRIPPABLE_ITEM_LHAND "l_hand" +#define STRIPPABLE_ITEM_RHAND "r_hand" +#define STRIPPABLE_ITEM_HANDCUFFS "handcuffs" +#define STRIPPABLE_ITEM_LEGCUFFS "legcuffs" + + +/// This slot is not obscured. +#define STRIPPABLE_OBSCURING_NONE 0 + +/// This slot is completely obscured, and cannot be accessed. +#define STRIPPABLE_OBSCURING_COMPLETELY 1 + +/// This slot can't be seen, but can be accessed. +#define STRIPPABLE_OBSCURING_HIDDEN 2 diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm index fe15e6d84c79..d5bc76933230 100644 --- a/code/__HELPERS/_lists.dm +++ b/code/__HELPERS/_lists.dm @@ -176,3 +176,10 @@ for(var/i in 1 to inserted_list.len - 1) inserted_list.Swap(i, rand(i, inserted_list.len)) + +/// Performs an insertion on the given lazy list with the given key and value. If the value already exists, a new one will not be made. +#define LAZYORASSOCLIST(lazy_list, key, value) \ + LAZYINITLIST(lazy_list); \ + LAZYINITLIST(lazy_list[key]); \ + lazy_list[key] |= value; + diff --git a/code/_onclick/click_hold.dm b/code/_onclick/click_hold.dm index 996f7ed2bf3b..bc5852cfdf40 100644 --- a/code/_onclick/click_hold.dm +++ b/code/_onclick/click_hold.dm @@ -97,9 +97,9 @@ /client/MouseDrop(datum/over_object, datum/src_location, over_location, src_control, over_control, params) . = ..() - if(src_location) SEND_SIGNAL(src_location, COMSIG_ATOM_DROPPED_ON, over_object, src) if(over_object) SEND_SIGNAL(over_object, COMSIG_ATOM_DROP_ON, src_location, src) + ai_announcement("[over_object] [src_location] test") diff --git a/code/_onclick/drag_drop.dm b/code/_onclick/drag_drop.dm index fff5e9200de7..4dcc0d646816 100644 --- a/code/_onclick/drag_drop.dm +++ b/code/_onclick/drag_drop.dm @@ -7,6 +7,7 @@ */ /atom/MouseDrop(atom/over) if(!usr || !over) return + if(!Adjacent(usr) || !over.Adjacent(usr)) return // should stop you from dragging through windows spawn(0) diff --git a/code/datums/elements/strippable.dm b/code/datums/elements/strippable.dm new file mode 100644 index 000000000000..782c33e99d8c --- /dev/null +++ b/code/datums/elements/strippable.dm @@ -0,0 +1,466 @@ +/// An element for atoms that, when dragged and dropped onto a mob, opens a strip panel. +/datum/element/strippable + element_flags = ELEMENT_BESPOKE | ELEMENT_DETACH + id_arg_index = 2 + + /// An assoc list of keys to /datum/strippable_item + var/list/items + + /// A proc path that returns TRUE/FALSE if we should show the strip panel for this entity. + /// If it does not exist, the strip menu will always show. + /// Will be called with (mob/user). + var/should_strip_proc_path + + /// An existing strip menus + var/list/strip_menus + +/datum/element/strippable/Attach(datum/target, list/items, should_strip_proc_path) + . = ..() + if (!isatom(target)) + return ELEMENT_INCOMPATIBLE + + RegisterSignal(target, COMSIG_ATOM_DROP_ON, .proc/mouse_drop_onto) + + src.items = items + src.should_strip_proc_path = should_strip_proc_path + +/datum/element/strippable/Detach(datum/source, force) + . = ..() + + UnregisterSignal(source, COMSIG_ATOM_DROP_ON) + + if (!isnull(strip_menus)) + QDEL_NULL(strip_menus[source]) + +/datum/element/strippable/proc/mouse_drop_onto(datum/source, atom/over, mob/user) + SIGNAL_HANDLER + if (user == source) + return + + var/mob/overmob = over + if (!istype(overmob)) + return + + if (!overmob.client) + return + + if (overmob.client != user) + return + + if (!isnull(should_strip_proc_path) && !call(source, should_strip_proc_path)(user)) + return + + var/datum/strip_menu/strip_menu + + if (isnull(strip_menu)) + strip_menu = new(source, src) + LAZYSET(strip_menus, source, strip_menu) + + INVOKE_ASYNC(strip_menu, PROC_REF(tgui_interact), overmob) + +/// A representation of an item that can be stripped down +/datum/strippable_item + /// The STRIPPABLE_ITEM_* key + var/key + + /// Should we warn about dangerous clothing? + var/warn_dangerous_clothing = TRUE + +/// Gets the item from the given source. +/datum/strippable_item/proc/get_item(atom/source) + +/// Tries to equip the item onto the given source. +/// Returns TRUE/FALSE depending on if it is allowed. +/// This should be used for checking if an item CAN be equipped. +/// It should not perform the equipping itself. +/datum/strippable_item/proc/try_equip(atom/source, obj/item/equipping, mob/user) + if ((equipping.flags_item & NODROP)) + to_chat(user, "You can't put [equipping] on [source], it's stuck to your hand!") + return FALSE + return TRUE + +/// Start the equipping process. This is the proc you should yield in. +/// Returns TRUE/FALSE depending on if it is allowed. +/datum/strippable_item/proc/start_equip(atom/source, obj/item/equipping, mob/user) + source.visible_message( + "[user] tries to put [equipping] on [source].", + "[user] tries to put [equipping] on you." + ) + + + // var/log = "[key_name(source)] is having [equipping] put on them by [key_name(user)]" + // source.log_message(log, LOG_ATTACK, color="red") + // user.log_message(log, LOG_ATTACK, color="red", log_globally=FALSE) + + return TRUE + +/// The proc that places the item on the source. This should not yield. +/datum/strippable_item/proc/finish_equip(atom/source, obj/item/equipping, mob/user) + SHOULD_NOT_SLEEP(TRUE) + +/// Tries to unequip the item from the given source. +/// Returns TRUE/FALSE depending on if it is allowed. +/// This should be used for checking if it CAN be unequipped. +/// It should not perform the unequipping itself. +/datum/strippable_item/proc/try_unequip(atom/source, mob/user) + SHOULD_NOT_SLEEP(TRUE) + + var/obj/item/item = get_item(source) + if (isnull(item)) + return FALSE + + if (ismob(source)) + if ((item.flags_inventory & CANTSTRIP) || (item.flags_item & NODROP)) + return FALSE + + return TRUE + +/// Start the unequipping process. This is the proc you should yield in. +/// Returns TRUE/FALSE depending on if it is allowed. +/datum/strippable_item/proc/start_unequip(atom/source, mob/user) + var/obj/item/item = get_item(source) + if (isnull(item)) + return FALSE + + source.visible_message( + "[user] tries to remove [source]'s [item].", + "[user] tries to remove your [item]." + ) + + to_chat(user, "You try to remove [source]'s [item]...") + // source.log_message("[key_name(source)] is being stripped of [item] by [key_name(src)]", LOG_ATTACK, color="red") + // user.log_message("[key_name(source)] is being stripped of [item] by [key_name(src)]", LOG_ATTACK, color="red", log_globally=FALSE) + item.add_fingerprint(src) + + return TRUE + +/// The proc that unequips the item from the source. This should not yield. +/datum/strippable_item/proc/finish_unequip(atom/source, mob/user) + +/// Returns a STRIPPABLE_OBSCURING_* define to report on whether or not this is obscured. +/datum/strippable_item/proc/get_obscuring(atom/source) + SHOULD_NOT_SLEEP(TRUE) + return STRIPPABLE_OBSCURING_NONE + +/// Returns the ID of this item's strippable action. +/// Return `null` if there is no alternate action. +/// Any return value of this must be in StripMenu. +/datum/strippable_item/proc/get_alternate_action(atom/source, mob/user) + return null + +/// Performs an alternative action on this strippable_item. +/// `has_alternate_action` needs to be TRUE. +/datum/strippable_item/proc/alternate_action(atom/source, mob/user) + +/// Returns whether or not this item should show. +/datum/strippable_item/proc/should_show(atom/source, mob/user) + return TRUE + +/// A preset for equipping items onto mob slots +/datum/strippable_item/mob_item_slot + /// The ITEM_SLOT_* to equip to. + var/item_slot + +/datum/strippable_item/mob_item_slot/get_item(atom/source) + if (!ismob(source)) + return null + + var/mob/mob_source = source + return mob_source.get_item_by_slot(key) + +/datum/strippable_item/mob_item_slot/try_equip(atom/source, obj/item/equipping, mob/user) + . = ..() + if (!.) + return + + if (!ismob(source)) + return FALSE + if (!equipping.mob_can_equip( + source, + item_slot + )) + to_chat(user, "\The [equipping] doesn't fit in that place!") + return FALSE + + return TRUE + +/datum/strippable_item/mob_item_slot/start_equip(atom/source, obj/item/equipping, mob/user) + . = ..() + if (!.) + return + + if (!ismob(source)) + return FALSE + + if (!do_after(user, source, 2 SECONDS + get_equip_delay(equipping))) + return FALSE + + if (!equipping.mob_can_equip( + source, + item_slot + )) + return FALSE + + if (!user.temp_drop_inv_item(equipping)) + return FALSE + + return TRUE + +/datum/strippable_item/mob_item_slot/finish_equip(atom/source, obj/item/equipping, mob/user) + if (!ismob(source)) + return FALSE + + var/mob/mob_source = source + mob_source.equip_to_slot(equipping, item_slot) + +/datum/strippable_item/mob_item_slot/get_obscuring(atom/source) + return FALSE + +/datum/strippable_item/mob_item_slot/start_unequip(atom/source, mob/user) + . = ..() + if (!.) + return + + return start_unequip_mob(get_item(source), source, user) + +/datum/strippable_item/mob_item_slot/finish_unequip(atom/source, mob/user) + var/obj/item/item = get_item(source) + if (isnull(item)) + return FALSE + + if (!ismob(source)) + return FALSE + + return finish_unequip_mob(item, source, user) + +/// Returns the delay of equipping this item to a mob +/datum/strippable_item/mob_item_slot/proc/get_equip_delay(obj/item/equipping) + return equipping.time_to_equip + +/// A utility function for `/datum/strippable_item`s to start unequipping an item from a mob. +/proc/start_unequip_mob(obj/item/item, mob/source, mob/user, strip_delay) + if (!do_after(user, 2 SECONDS + item.time_to_unequip, BUSY_ICON_HOSTILE, INTERRUPT_ALL, source, INTERRUPT_ALL)) + return FALSE + + return TRUE + +/// A utility function for `/datum/strippable_item`s to finish unequipping an item from a mob. +/proc/finish_unequip_mob(obj/item/item, mob/source, mob/user) + if (!source.drop_inv_item_on_ground(item)) + return FALSE + + // source.log_message("[key_name(source)] has been stripped of [item] by [key_name(user)]", LOG_ATTACK, color="red") + // user.log_message("[key_name(source)] has been stripped of [item] by [key_name(user)]", LOG_ATTACK, color="red", log_globally=FALSE) + + // Updates speed in case stripped speed affecting item + source.recalculate_move_delay = TRUE + +/// A representation of the stripping UI +/datum/strip_menu + /// The owner who has the element /datum/element/strippable + var/atom/movable/owner + + /// The strippable element itself + var/datum/element/strippable/strippable + + /// A lazy list of user mobs to a list of strip menu keys that they're interacting with + var/list/interactions + +/datum/strip_menu/New(atom/movable/owner, datum/element/strippable/strippable) + . = ..() + src.owner = owner + src.strippable = strippable + +/datum/strip_menu/Destroy() + owner = null + strippable = null + + return ..() + +/datum/strip_menu/tgui_interact(mob/user, datum/tgui/ui) + . = ..() + ui = SStgui.try_update_ui(user, src, ui) + if (!ui) + ui = new(user, src, "StripMenu") + ui.open() + + +/datum/strip_menu/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/simple/inventory), + ) + +/datum/strip_menu/ui_data(mob/user) + var/list/data = list() + + var/list/items = list() + + //ai_announcement("[strippable.items.len]") + + for (var/strippable_key in strippable.items) + var/datum/strippable_item/item_data = strippable.items[strippable_key] + + if (!item_data.should_show(owner, user)) + continue + + var/list/result + + if(strippable_key in LAZYACCESS(interactions, user)) + LAZYSET(result, "interacting", TRUE) + + var/obscuring = item_data.get_obscuring(owner) + if (obscuring != STRIPPABLE_OBSCURING_NONE) + LAZYSET(result, "obscured", obscuring) + items[strippable_key] = result + continue + + var/obj/item/item = item_data.get_item(owner) + if (isnull(item)) + items[strippable_key] = result + continue + + LAZYINITLIST(result) + + result["icon"] = icon2base64(icon(item.icon, item.icon_state)) + result["name"] = item.name + result["alternate"] = item_data.get_alternate_action(owner, user) + + items[strippable_key] = result + + data["items"] = items + + // While most `\the`s are implicit, this one is not. + // In this case, `\The` would otherwise be used. + // This doesn't match with what it's used for, which is to say "Stripping the alien drone", + // as opposed to "Stripping The alien drone". + // Human names will still show without "the", as they are proper nouns. + data["name"] = "\the [owner]" + + return data + +/datum/strip_menu/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if (.) + return + + . = TRUE + + var/mob/user = usr + + switch (action) + if ("use") + var/key = params["key"] + var/datum/strippable_item/strippable_item = strippable.items[key] + + if (isnull(strippable_item)) + return + + if (!strippable_item.should_show(owner, user)) + return + + if (strippable_item.get_obscuring(owner) == STRIPPABLE_OBSCURING_COMPLETELY) + return + + var/item = strippable_item.get_item(owner) + if (isnull(item)) + var/obj/item/held_item = user.get_held_item() + if (isnull(held_item)) + return + + if (strippable_item.try_equip(owner, held_item, user)) + LAZYORASSOCLIST(interactions, user, key) + + // Yielding call + var/should_finish = strippable_item.start_equip(owner, held_item, user) + + LAZYREMOVEASSOC(interactions, user, key) + + if (!should_finish) + return + + if (QDELETED(src) || QDELETED(owner)) + return + + // They equipped an item in the meantime + if (!isnull(strippable_item.get_item(owner))) + return + + if (!user.Adjacent(owner)) + return + + strippable_item.finish_equip(owner, held_item, user) + else if (strippable_item.try_unequip(owner, user)) + LAZYORASSOCLIST(interactions, user, key) + + var/should_unequip = strippable_item.start_unequip(owner, user) + + LAZYREMOVEASSOC(interactions, user, key) + + // Yielding call + if (!should_unequip) + return + + if (QDELETED(src) || QDELETED(owner)) + return + + // They changed the item in the meantime + if (strippable_item.get_item(owner) != item) + return + + if (!user.Adjacent(owner)) + return + + strippable_item.finish_unequip(owner, user) + if ("alt") + var/key = params["key"] + var/datum/strippable_item/strippable_item = strippable.items[key] + + if (isnull(strippable_item)) + return + + if (!strippable_item.should_show(owner, user)) + return + + if (strippable_item.get_obscuring(owner) == STRIPPABLE_OBSCURING_COMPLETELY) + return + + var/item = strippable_item.get_item(owner) + if (isnull(item)) + return + + if (isnull(strippable_item.get_alternate_action(owner, user))) + return + + LAZYORASSOCLIST(interactions, user, key) + + // Potentially yielding + strippable_item.alternate_action(owner, user) + + LAZYREMOVEASSOC(interactions, user, key) + +/datum/strip_menu/ui_host(mob/user) + return owner + +/datum/strip_menu/ui_status(mob/user, datum/ui_state/state) + . = ..() + + if (isliving(user)) + var/mob/living/living_user = user + + if ( + . == UI_UPDATE \ + && user.stat == CONSCIOUS \ + && living_user.body_position == LYING_DOWN \ + && user.Adjacent(owner) + ) + return UI_INTERACTIVE + +/// Creates an assoc list of keys to /datum/strippable_item +/proc/create_strippable_list(types) + var/list/strippable_items = list() + + for (var/strippable_type in types) + var/datum/strippable_item/strippable_item = new strippable_type + strippable_items[strippable_item.key] = strippable_item + + return strippable_items diff --git a/code/modules/asset_cache/asset_list.dm b/code/modules/asset_cache/asset_list.dm index 8e19a1905300..1d88df0b6a6b 100644 --- a/code/modules/asset_cache/asset_list.dm +++ b/code/modules/asset_cache/asset_list.dm @@ -341,3 +341,25 @@ GLOBAL_LIST_EMPTY(asset_datums) if (!item_filename) return . = list("[item_filename]" = SSassets.transport.get_asset_url(item_filename)) + +/datum/asset/simple/inventory + assets = list( + "inventory-glasses.png" = 'icons/ui_Icons/inventory/glasses.png', + "inventory-head.png" = 'icons/ui_Icons/inventory/head.png', + "inventory-neck.png" = 'icons/ui_Icons/inventory/neck.png', + "inventory-mask.png" = 'icons/ui_Icons/inventory/mask.png', + "inventory-ears.png" = 'icons/ui_Icons/inventory/ears.png', + "inventory-uniform.png" = 'icons/ui_Icons/inventory/uniform.png', + "inventory-suit.png" = 'icons/ui_Icons/inventory/suit.png', + "inventory-gloves.png" = 'icons/ui_Icons/inventory/gloves.png', + "inventory-hand_l.png" = 'icons/ui_Icons/inventory/hand_l.png', + "inventory-hand_r.png" = 'icons/ui_Icons/inventory/hand_r.png', + "inventory-shoes.png" = 'icons/ui_Icons/inventory/shoes.png', + "inventory-suit_storage.png" = 'icons/ui_Icons/inventory/suit_storage.png', + "inventory-id.png" = 'icons/ui_Icons/inventory/id.png', + "inventory-belt.png" = 'icons/ui_Icons/inventory/belt.png', + "inventory-back.png" = 'icons/ui_Icons/inventory/back.png', + "inventory-pocket.png" = 'icons/ui_Icons/inventory/pocket.png', + "inventory-collar.png" = 'icons/ui_Icons/inventory/collar.png', + ) + diff --git a/code/modules/mob/living/carbon/carbon_stripping.dm b/code/modules/mob/living/carbon/carbon_stripping.dm new file mode 100644 index 000000000000..c5969d57c60b --- /dev/null +++ b/code/modules/mob/living/carbon/carbon_stripping.dm @@ -0,0 +1,129 @@ +/datum/strippable_item/mob_item_slot/head + key = STRIPPABLE_ITEM_HEAD + item_slot = ITEM_SLOT_HEAD + +/datum/strippable_item/mob_item_slot/back + key = STRIPPABLE_ITEM_BACK + item_slot = ITEM_SLOT_BACK + +/datum/strippable_item/mob_item_slot/mask + key = STRIPPABLE_ITEM_MASK + item_slot = ITEM_SLOT_MASK + +/datum/strippable_item/mob_item_slot/neck + key = STRIPPABLE_ITEM_NECK + item_slot = ITEM_SLOT_NECK + +/datum/strippable_item/mob_item_slot/handcuffs + key = STRIPPABLE_ITEM_HANDCUFFS + item_slot = ITEM_SLOT_HANDCUFFED + +/datum/strippable_item/mob_item_slot/handcuffs/should_show(atom/source, mob/user) + if (!iscarbon(source)) + return FALSE + + var/mob/living/carbon/carbon_source = source + return !isnull(carbon_source.handcuffed) + +// You shouldn't be able to equip things to handcuff slots. +/datum/strippable_item/mob_item_slot/handcuffs/try_equip(atom/source, obj/item/equipping, mob/user) + return FALSE + +/datum/strippable_item/mob_item_slot/legcuffs + key = STRIPPABLE_ITEM_LEGCUFFS + item_slot = ITEM_SLOT_LEGCUFFED + +/datum/strippable_item/mob_item_slot/legcuffs/should_show(atom/source, mob/user) + if (!iscarbon(source)) + return FALSE + + var/mob/living/carbon/carbon_source = source + return !isnull(carbon_source.legcuffed) + +// You shouldn't be able to equip things to legcuff slots. +/datum/strippable_item/mob_item_slot/legcuffs/try_equip(atom/source, obj/item/equipping, mob/user) + return FALSE + +/// A strippable item for a hand +/datum/strippable_item/hand + // Putting dangerous clothing in our hand is fine. + warn_dangerous_clothing = FALSE + + /// Which hand? + var/hand_index + +/datum/strippable_item/hand/get_item(atom/source) + if (!ismob(source)) + return null + + var/mob/mob_source = source + return mob_source.get_item_for_held_index(hand_index) + +/datum/strippable_item/hand/try_equip(atom/source, obj/item/equipping, mob/user) + . = ..() + if (!.) + return FALSE + + if (!ismob(source)) + return FALSE + + var/mob/mob_source = source + + if (!mob_source.can_put_in_hand(equipping, hand_index)) + to_chat(src, "\The [equipping] doesn't fit in that place!") + return FALSE + + return TRUE + +/datum/strippable_item/hand/start_equip(atom/source, obj/item/equipping, mob/user) + . = ..() + if (!.) + return + + if (!ismob(source)) + return FALSE + + var/mob/mob_source = source + + if (!do_mob(user, source, equipping.equip_delay_other)) + return FALSE + + if (!mob_source.can_put_in_hand(equipping, hand_index)) + return FALSE + + if (!user.temporarilyRemoveItemFromInventory(equipping)) + return FALSE + + return TRUE + +/datum/strippable_item/hand/finish_equip(atom/source, obj/item/equipping, mob/user) + if (!iscarbon(source)) + return FALSE + + var/mob/mob_source = source + mob_source.put_in_hand(equipping, hand_index) + +/datum/strippable_item/hand/start_unequip(atom/source, mob/user) + . = ..() + if (!.) + return + + return start_unequip_mob(get_item(source), source, user) + +/datum/strippable_item/hand/finish_unequip(atom/source, mob/user) + var/obj/item/item = get_item(source) + if (isnull(item)) + return FALSE + + if (!ismob(source)) + return FALSE + + return finish_unequip_mob(item, source, user) + +/datum/strippable_item/hand/left + key = STRIPPABLE_ITEM_LHAND + hand_index = 1 + +/datum/strippable_item/hand/right + key = STRIPPABLE_ITEM_RHAND + hand_index = 2 diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 3ebd199b08d9..3f7b3955e878 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -13,7 +13,7 @@ create_reagents(1000) if(!real_name || !name) change_real_name(src, "unknown") - + AddElement(/datum/element/strippable, GLOB.strippable_human_items, /mob/living/carbon/human/.proc/should_strip) . = ..() prev_gender = gender // Debug for plural genders diff --git a/code/modules/mob/living/carbon/human/human_stripping.dm b/code/modules/mob/living/carbon/human/human_stripping.dm new file mode 100644 index 000000000000..98b6402c2c4a --- /dev/null +++ b/code/modules/mob/living/carbon/human/human_stripping.dm @@ -0,0 +1,145 @@ +#define INTERNALS_TOGGLE_DELAY (4 SECONDS) +#define POCKET_EQUIP_DELAY (1 SECONDS) + +GLOBAL_LIST_INIT(strippable_human_items, create_strippable_list(list( + /datum/strippable_item/mob_item_slot/head, + /datum/strippable_item/mob_item_slot/back, + /datum/strippable_item/mob_item_slot/mask, + /datum/strippable_item/mob_item_slot/eyes, + /datum/strippable_item/mob_item_slot/r_ear, + /datum/strippable_item/mob_item_slot/l_ear, + /datum/strippable_item/mob_item_slot/jumpsuit, + /datum/strippable_item/mob_item_slot/suit, + /datum/strippable_item/mob_item_slot/gloves, + /datum/strippable_item/mob_item_slot/feet, + /datum/strippable_item/mob_item_slot/suit_storage, + /datum/strippable_item/mob_item_slot/id, + /datum/strippable_item/mob_item_slot/belt, + /datum/strippable_item/mob_item_slot/pocket/left, + /datum/strippable_item/mob_item_slot/pocket/right, + /datum/strippable_item/hand/left, + /datum/strippable_item/hand/right, +))) + +/mob/living/carbon/human/proc/should_strip(mob/user) + // if (user.pulling != src || user.grab_state != GRAB_AGGRESSIVE) + // return TRUE + + // if (ishuman(user)) + // var/mob/living/carbon/human/human_user = user + // //return !human_user.can_be_firemanned(src) + + return TRUE + +/datum/strippable_item/mob_item_slot/head + key = STRIPPABLE_ITEM_HEAD + item_slot = SLOT_HEAD + +/datum/strippable_item/mob_item_slot/back + key = STRIPPABLE_ITEM_BACK + item_slot = SLOT_BACK + +/datum/strippable_item/mob_item_slot/mask + key = STRIPPABLE_ITEM_MASK + item_slot = SLOT_FACE + +/datum/strippable_item/mob_item_slot/eyes + key = STRIPPABLE_ITEM_EYES + item_slot = SLOT_EYES + +/datum/strippable_item/mob_item_slot/r_ear + key = STRIPPABLE_ITEM_R_EAR + item_slot = SLOT_EAR + +/datum/strippable_item/mob_item_slot/l_ear + key = STRIPPABLE_ITEM_L_EAR + item_slot = SLOT_EAR + +/datum/strippable_item/mob_item_slot/jumpsuit + key = STRIPPABLE_ITEM_JUMPSUIT + item_slot = SLOT_ICLOTHING + +/datum/strippable_item/mob_item_slot/suit + key = STRIPPABLE_ITEM_SUIT + item_slot = SLOT_OCLOTHING + +/datum/strippable_item/mob_item_slot/gloves + key = STRIPPABLE_ITEM_GLOVES + item_slot = SLOT_HANDS + +/datum/strippable_item/mob_item_slot/feet + key = STRIPPABLE_ITEM_FEET + item_slot = SLOT_FEET + + +/datum/strippable_item/mob_item_slot/suit_storage + key = STRIPPABLE_ITEM_SUIT_STORAGE + item_slot = SLOT_SUIT_STORE + +/datum/strippable_item/mob_item_slot/id + key = STRIPPABLE_ITEM_ID + item_slot = SLOT_ID + +/datum/strippable_item/mob_item_slot/belt + key = STRIPPABLE_ITEM_BELT + item_slot = SLOT_WAIST + +/datum/strippable_item/mob_item_slot/pocket + /// Which pocket we're referencing. Used for visible text. + var/pocket_side + +/datum/strippable_item/mob_item_slot/pocket/get_obscuring(atom/source) + return FALSE + +/datum/strippable_item/mob_item_slot/pocket/get_equip_delay(obj/item/equipping) + return POCKET_EQUIP_DELAY + +/datum/strippable_item/mob_item_slot/pocket/start_equip(atom/source, obj/item/equipping, mob/user) + . = ..() + if (!.) + warn_owner(source) + +/datum/strippable_item/mob_item_slot/pocket/start_unequip(atom/source, mob/user) + var/obj/item/item = get_item(source) + if (isnull(item)) + return FALSE + + to_chat(user, "You try to empty [source]'s [pocket_side] pocket.") + + // var/log_message = "[key_name(source)] is being pickpocketed of [item] by [key_name(user)] ([pocket_side])" + // source.log_message(log_message, LOG_ATTACK, color="red") + // user.log_message(log_message, LOG_ATTACK, color="red", log_globally=FALSE) + item.add_fingerprint(src) + + var/result = start_unequip_mob(item, source, user, POCKET_STRIP_DELAY) + + if (!result) + warn_owner(source) + + return result + +/datum/strippable_item/mob_item_slot/pocket/proc/warn_owner(atom/owner) + to_chat(owner, "You feel your [pocket_side] pocket being fumbled with!") + +/datum/strippable_item/mob_item_slot/pocket/left + key = STRIPPABLE_ITEM_LPOCKET + item_slot = SLOT_STORE + pocket_side = "left" + +/datum/strippable_item/mob_item_slot/pocket/right + key = STRIPPABLE_ITEM_RPOCKET + item_slot = SLOT_STORE + pocket_side = "right" + +// /datum/strippable_item/mob_item_slot/handcuffs +// key = STRIPPABLE_ITEM_HANDCUFFS +// item_slot = SLOT_HANDCUFFED + +/datum/strippable_item/hand/left + key = STRIPPABLE_ITEM_LHAND + +/datum/strippable_item/hand/right + key = STRIPPABLE_ITEM_RHAND + +#undef INTERNALS_TOGGLE_DELAY +#undef POCKET_EQUIP_DELAY diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index c11b8acd7f9a..92943282437a 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -459,7 +459,7 @@ if(M.pulling == src && (M.a_intent & INTENT_GRAB) && M.grab_level == GRAB_AGGRESSIVE) return - show_inv(M) + //show_inv(M) /mob/proc/swap_hand() hand = !hand diff --git a/colonialmarines.dme b/colonialmarines.dme index 2a421a818589..e6d36e02abd3 100644 --- a/colonialmarines.dme +++ b/colonialmarines.dme @@ -101,6 +101,7 @@ #include "code\__DEFINES\stamina.dm" #include "code\__DEFINES\stats.dm" #include "code\__DEFINES\status_effects.dm" +#include "code\__DEFINES\strippable.dm" #include "code\__DEFINES\STUI.dm" #include "code\__DEFINES\subsystems.dm" #include "code\__DEFINES\supply.dm" @@ -484,6 +485,7 @@ #include "code\datums\elements\light_blocking.dm" #include "code\datums\elements\mouth_drop_item.dm" #include "code\datums\elements\poor_eyesight_correction.dm" +#include "code\datums\elements\strippable.dm" #include "code\datums\elements\suturing.dm" #include "code\datums\elements\yautja_tracked_item.dm" #include "code\datums\elements\bullet_trait\damage_boost.dm" @@ -1913,6 +1915,7 @@ #include "code\modules\mob\living\carbon\human\human_dummy.dm" #include "code\modules\mob\living\carbon\human\human_helpers.dm" #include "code\modules\mob\living\carbon\human\human_movement.dm" +#include "code\modules\mob\living\carbon\human\human_stripping.dm" #include "code\modules\mob\living\carbon\human\inventory.dm" #include "code\modules\mob\living\carbon\human\life.dm" #include "code\modules\mob\living\carbon\human\login.dm" diff --git a/icons/ui_icons/inventory/back.png b/icons/ui_icons/inventory/back.png new file mode 100644 index 0000000000000000000000000000000000000000..736b9d64bf997fe7d6874db93e079c83b187ebdc GIT binary patch literal 1796 zcmbVNe{2&~9KSMfimXGNBy7{n%z2oji zyN=<)U^xB|L~scZ!em4UlVC(8BLWKO3?fl7s!q+Kk)RO4I2~h4@Lkt7peT{c-Mx43 zectE$`+MD*}V%TP&qCODdLpJQjiBlNLb2e;cv72;DcF@FE zMrBYNt@5(bX4cAKOBSPxA~YlrfXbi|AsABVhz*PL(r~UV6BrtYsLeL4OcRLKyFI8~ zlmTiskT^?{6k1|67|lgR7E2*YkrYXgC4`a0DT*e|bg>CdTo{xlbAGzUu{0qIe%Y`l zRh4Lh2#3Rlu*o3G0m5jtS_zUOC<=!NTxkudOau=pc}WHbP*|ClR9+0B8YANqTT~kc zHJvg+kW#cECE+IMFe1W8gwa51CdGjqo5D#gaxgBOV+jxh0tl%J#2QmrsYz5trAho3 z>eTi<0-$T%?v#xC>LLg!5sK57>{9OltgHeGEgHbc$qzN}#y<{R;XWT*f?J9~5oakjvSx}(o_C0Bin1exGN^Js;s~EZY#6J>A8=S=nSW>xOwop$03`j7yqJP1qF)U&GAIi`Z#^*Y z2&g=v73`E9#NSPf@7-10NjU5n+BDUw;76+2fe`F)GHhjM?&QM`soUXllzHpV4E0=l zv9omcruNU@>s~RKbIg6=z>ad_*sG45!396j+&-NzfRv>o)f7p4hsfxBK`rA3&<_meuKVpDuml?VK>* z8+)^Bj{mpSoA=A(>*ubTw)ClvBP&+V7@X9dR&ZonbNT#U-PH-DKW)$t27XWFeebTcbvF1+{EzN z#W%h0?;BrMm-c(ZP=;{o$yYKw19_9t-36D-=GXcwZj9d9qkn5jW?R;$J*!(swEvXL LS>-rV-nii}bGmM* literal 0 HcmV?d00001 diff --git a/icons/ui_icons/inventory/belt.png b/icons/ui_icons/inventory/belt.png new file mode 100644 index 0000000000000000000000000000000000000000..1be89d450a8f4d38ccf9a37072687406042faa4b GIT binary patch literal 1596 zcmbVMU1%Id9KRgbP}4T}P%T)f)2+0Aba!X>lDq8CYfSI1cURpQ(t{+THgmgkcS~+} zw!53|-BGA%sZvDr!GNL#E#iYIRuPHfgTX3=RxuB1C4zz$9}2}h2$9yJv-d@+)nb>O z-TC;<@BjTjF*2Ol&~r~uC=}X|%ce%z9|U7ZA4M891+3Eo5vr!m(4o+-!K#h5DdK?wDH&FR`}N~bIAG`rZmeI0s+}Zd zBU^XK{`zoUt50c)&JFGcyQ+v0n8d@NYE~>4RTEr;7qNM;Epng%@um{oP#_SDt0N#u z9Rgwkl+6*b;YK~!h2DL!IhzoSuOK?onrU|Cq zp|#wWo0!AIDz-&QfPqO3pssar_Ow%J2-h`{RES9|&t+JtgSE@lqi&i03w7uC1_8{q zs@jopQ(a86Bf|C4hnN{HhujqH=4&<)M~O?P9gU<9F*UaZ*4QZN5bRMWPidtcsFAkH zK$Zmv?jJKOo%-$rt1gff_DF(bQImKW1Yr}ZQr#%l{sCnHX0H^x z0m@QF#~!{VSl3XIIwocY8zwFh(Y8t)=(rIjX@xpWFw-u&dOw#;jySYvRM>$#n%M(# z>10e&VliG8q=vexigK3gVM`;qRDxs95e!2|FdiiZy-(w%Sd8$R+*jbSrWN@30Erf* zf}|@^+`gZp+H{Zt?fd%wy#JVEuoA?T+vW)hGsqp3HCz_eTB~&SlY^~U#Q=?TL0AjQ zAi-%t{E5!BwvF5NK$F%l6Gr+UdC`QqwCMTRAwwnRty|`u$dng@f^FI%{@ujJ*{b4h z!&%1&rkz&B7M*5CEY{&1*2*HuxffU){34ec%8#GDGW&hcr*Qo<*G^r2>-Y6rE`Qnm zLv82LGnXDYz7$#d{G*?WB9b=UTY2%1!xyr1r$2ab$G%AF;o!ZxQN5UX*&IH$u;sqo z!n6F!%DMT;KR;ZveJ(k5xOD2fr(Rzi3m^Msc4lj}>%2X&d;a>|#^V>aFI>5Bvg=a% z>2LPp@Ee1p*EWY=n?HEu&gHM3yV||>%vt}g_kTWcQl44-(GO2V3NI}^|KzK0^2^sH z^zFM7na9(a^UC7s$(0kY9GLvtpLyaO2f|0TE=P9su3NY1y?t*7{XLf+PQ9~d^5|b_ CA`k5V literal 0 HcmV?d00001 diff --git a/icons/ui_icons/inventory/collar.png b/icons/ui_icons/inventory/collar.png new file mode 100644 index 0000000000000000000000000000000000000000..71803b1b6c6b535c4a0d706240fdd4da64227962 GIT binary patch literal 2090 zcmbVNYiQk69FH3t+q#WCzH|d6q(eHkb93^(iM{LE-f1h|u5`s!aj=td(wMnMxhYZbeJ-yWs;R%xPHYs{RPWb)8=K}9JIpLsIfQ4{?l)S;& zh#Z(59>cQ}*mQ*6J$z5qVgx=(5MTAnL2OlXLXFpA>-4oK@HI#>krVn-hx~Y9gdd<0 z;q?pz7($sh%#5U~YBsx@m!S+rXowO7vTQ-!(iFaN2+UjLxYlU?(S|SfloLux5?Z2I zsZ=r*B}1d4D4C`yLRpk$zz`sw2@+HVLEP13$dedHUYK|^;8R9q)2SpUFs1bn{IEqE z#Emqugo#xYic$uqA=Q8mZsEeIs9baIV3CxGPl6<7SgC~#OEjTzi7rFk`h152mfAw0 z$H1iH^r+LL!cmm`+8Q?3-pXyV7VvYak*hq0tznPr&#`P- zCRtb2fI~D4P*!CZL=ccQ*+s~Wo-(9E&Zt2Se3m!O?t_8A#|YwbYiH$HdV%KWL+RBs;+37Y3MTP z5!2CNM)j#{vsSQ9Y-SJKtZoCFs5US)HfULe5JQn|?CM&Zb&aPkL%h3QCXZEV&`b5t zH2fYQlPv{&t-~#Z)5*;VIBi+t2#wd?64kx0JT~krCBjJW(#m!3F?Ev)ib!9Pb;3PI zMO;=q4W>!dCO-BWab z15l5B$rQDZ@c&6sBmKC9f+AttjVPqS)Vii#eDS}1QTx{R6K}%V&X6u!TM~O{ZE7T7 z)DhdVw(MGQhHZCOhw^=6yeGe;QIX0YyO)9-@bEw<78Lx#<}rTyFM!ZvH9iK z?d@le8Mn}m%iWd7AHH~f#V3pVZ|r{P`v(`!ym)Hm=}U{-_!F<9^H)k2x$>1SxEp;V zA8uaM*3ZpvS=8ibj+c5?o+j6Ba^aWn8J7>u&ENDU&s{kE{?Vs@y128G|Mc+MBTxSP zcL#Uk!q&aLA9Kg)^V?pz`gLy22X7bVPHvjJ+W*6fPT1SMi9d4c(BN;Yy8klPL1)*( yFMEG{u5)e&n0+m?fVjo4PAz`(k224_`&q|Xqw?Le{FQV!AKEvZKi_}&g?|BkKCR0D literal 0 HcmV?d00001 diff --git a/icons/ui_icons/inventory/ears.png b/icons/ui_icons/inventory/ears.png new file mode 100644 index 0000000000000000000000000000000000000000..e9a8f3c23c4bf20a2269121cef3797d04f616ea4 GIT binary patch literal 1688 zcmbVNZEVzJ9PcUU#vJM(f=**8L588Pch|eC6t-RG?vmZSjgxJJ59`|JcHORhy0+Wh zEg@`EjKn3z@L`KAX2v)IALdI)kT739D8wicC5VX<2bc&L!xw{LbLi9C-DN0Bq)Gd{ z{hr_d{oj4@=ElkukFCHktTGyrTG5ERZ`pn5YYz_ohK6!8(qUs*#VYspV6RWC#jxd% zXo+^GJ=P>BP^V-Sb^$7@n+T0zTef9QS?L81-UYffLnN*p|CqovRV3Q{F(zh)K}w4Z zT44KNb3z&HRYEGUZ7aSdD~};0X}V~F zZkA{byWl3|Fgh!nG)pnANqL~ElyGLhmCg%S6&j?04h+XeShj>UQ_z8S3jPaq>G&1_ z$hEOpNycq;(e;uD+i4g;W)vK9TeO|XnSgEuHte?)&@h11T<=@RA#aFib-W2}as^m+nWy;kX5pS{fbL zt&R0~v?0v1A)Y5Wip{GViwRM~c4R{VQAs3_b0|$y1%I+j<$}OZ@@kSN1FA1b23em< zvT}e)@;-)Vyh`!D1eJa_1&a68|9Ss8OG70nr|+7_Elf9egotLNsOAc#vmNvmW@!!2 zr;8vfZW)M#;>I7SL}6RIYY!~aW>Nr={zqOc!fcpyGO`8gx{g6By>HiAr^e_PyuFVk<|OhxQ&DS+lxj z=7l}g(%8)xBhCeKX4zQJyKjviQiY~cHsN6W#$z+O=W$*FS|2#i*{JkHsA7rWS2z$MH v_WAn{i(d{$uIGBO)xU2{Z*4o*iM{d5mDXP$-o54{_umn1XqKkyJ4gNk667+U literal 0 HcmV?d00001 diff --git a/icons/ui_icons/inventory/glasses.png b/icons/ui_icons/inventory/glasses.png new file mode 100644 index 0000000000000000000000000000000000000000..6e6f1ad098f6d52ccdd7001ae68b001ad0e497c9 GIT binary patch literal 2017 zcmb_dZD<@t7~Y7fHZc@z3&kIH-6(0r-JRXrZ*sQjT}%$W7?W#r{)l#VclK`0?MHSu zxyxZO7AuPSLlmWL8c-A}QjJLQM=1td3kpRLwD|pjC@3acmDYdGUha;<0xr(l(RSh?oUgKZS~R ze>FhE)q$K*9Wzvu+IA<|UD2=s2St#qI3+jKDoLtlSHtbNo2AGaBpORnJ+VVFpB*Gq zUVuo60ki=CPs%F8NrIp#caS{b0Sjc712oTTK+?nnS-&XkEif%@D1BGm7ye69#VGPM zmMxddOgX`LL4oB|Rb>Is@;r?ZbU5WkutK|GXTw4og+^fek?px;Y!T|-M3kg3)1?p` zziHMD>uJIXV=K^SIR?Zb)qtka#Q76Jspi}?SX4p|a-$GqxhB>xdXX0vy;V#%hyUdO zr#72y`dE>R<1}4_QST%Uqn?lz(P3`NN9+&^y@|j;y_1+`XB>^Mr2+&aFUWabsZprG z24#}x89;6sv0c+Ehg+9TKxr7EB!#QS(I7!{Y7U4Rr)mj>2C@dg5;W_Xwl#GT6c?Bi zH383D2E~#wVFWJ=HVw`40te!NZ3h+*>$?StY^G65c_lBvjxoE$^8QRJH5ho7UBUz5 zP~Q$R)0>hwRg!3);c9fVSuNv+5p)fdNhc|s9LBay4IvIDWI?BeL_()k9rIQ!L8WED zLzxpe(-efp{Iq9G#1d%CH}QN`=M`R2O|!pTchM@W)+QB`P37CAa03No#m7NlhZCPYOxfu!h- z&@ZAMZ$v;7g@#VAhI&1%MiIINg!fdI`Y*v;i4s?-!-b3P{|knVx6&nru@{Q3Hnwcr z#f7YO;k`U=HV+Z}(>#Wdi}MuVLuQ@&(EIp+I+IEFy^_53)m=Mxe#*X;pKgus`Av_KTW^`! z{LPlzPCpKg{IT_u*$p4flFpg2*57n<=BtI#^W!ZK%-jtYUhnMx@sVo}zqB|{s?%@I z=et0DrsemOH-A~|8h@Eq-hSrfthM`vX}49W-hS%*!HrMv8rgop=v#Z^)bWK=8@lgX z9R1;l_2IeEuaExp-Pw=#ztc4a`iSw~f9V|`JpB2I2f6k+qJ3`q*=yEq-n*yA TGWHM0hj6BMApORU-FyE5goTfO literal 0 HcmV?d00001 diff --git a/icons/ui_icons/inventory/gloves.png b/icons/ui_icons/inventory/gloves.png new file mode 100644 index 0000000000000000000000000000000000000000..2c8a16cbdb7afb31597ca4d51d3fd42a97b80d74 GIT binary patch literal 2107 zcmb_d3uxSA98c|R_o^+5vvh)4hT!Ise97gKOYHTrySkq9YUz%yL#oRq-`$1XC0Ua8 z?m8!>>|qhg7&4hC)+gJ9c4Z*UDUQM_)m1i;DN|vJ&RT{WGMQE?e!1Qq-J{zm29kV_ z-}is~{*SM}wPi!a(we0hhE;@`gKg-KyYJ#M^tF3>uA<)(vw4e+VapzH-xBP#*Q+tC z?5Gy$a5}=KHoZ=1uUR}Kr?`3Sqbox*MsLD0%^03_D@WBf7YK zodQH{I9$|mOIuQ@q6*t->PBwl3ArWOj$}}bF8a7ldmc0pz z0?hCd5Uzuwlu>0zz9U#wq?lo)WaO}xlH-sz^*DhSX_Nv+(y)+XWSwX3dMFTRwTzgS zL>qS7hV^);DZn!#&yy_0Z3pxP|HFj?}E#D5}|f>1=}A^P{AO=h8)z6}JrhgyO~@sziQSy9EPx=fSMl zbOIuwx59)vXt$NlW*B{A z^5ypCv|SX)+!E^L?yz`?ph@u>f;!^KLYK^mYZtCzSP2sfHby#j|L|(~zCKX3`}msr z{=E$=PL7g&D~Er6ujX{~p08yoH8M5%)1&WlJD-<^1LGI7Ump5nI@xe)JNMV=iH)ne zuY7=Q?eTRQ5%6z22F!!+e6F*DuK)=f9NRxqdGSGf@-Tsp8 z0>QpjieIgM>d5qX>q}MpCK^I3_Aftt*x&x=L#1nL{ywyJ(d6XYU#uUj-ghRMEII${ z!zTxNs|N0|-ls5Xu2&GflmG(=Lwd2F*YWLjVJ3KSJYbJPU*A#YcIrrn((B*RH>n6_(Xr%h0)*s$)0-i`+HwFQ*M^8zO zde>8QOq}Rnr4)w6KQ=@+6zULVOefMQisBy%vmopbhXa{EvHnCY`f|NHx~<(tF_65M z_deg>zn&W!?BCRWTRX!rn^FVGVY;%xyD>z+UbXrwU9NKm9`YEbkocrmW_c+!v;^oO2tee*t2%xS_ zr<*cX)umW$itzlt32H{&A*-Uj%%p?GVeFBTYv8^Ks^*r!8V4p^jC|r|2q`oIHPld< zm1P02_w2E3la#$}D+q8B`8dwes7X9fcuC8E2$VD!(RdJr04zh(#I&-Lmq2NNB^4^* z0w_%x6Zzsn*M~22nOkLuYXc+KFHxVC=AppWM zHX}f7&?kve3R0j!Z&LcG9t*2kjQA`FtGuaDdQCM=UXE$1rXvtjvEHEH=~}b|QDNPD zg2D`P2M$;sjp}5*bavs#>bru)*3tzcBPfG7X9V%bCRabUR`I~qS+LBuoX3>uW%6Pf z=8>#lMlSBjQEy!_@5Hr~_t3=URX0Gy0E&_26{OMNDPfb>qtOT-q2I7>$P$jq4bv}C zJ!nJ<1ac!!*Q0vftwtW%IZS)1$gSU)tINbS;&5@%{eQ-YK`UL(F!AE#tKF?=yEKrs zBihS@ZF7jwpXM0CHuaNBhs?}}U9EILeVI!3WJccq;oyUtPqEh@IAP6ykT@;8+Hrj0 z?Vlfbcx?X^7=3p0VLmkd#-Gv8wmtGf?wN(rb2CR7|FG3|Y1}zl<%#WQ z@6^vax6Y5*o!uvIXJ+Kk^U~j+C$E7XP)}_nxhMQ-~E2a>p*Xt{)d^~4G4Dv4;-fH5FVcxXYy0MQp{Oz`83A@Ja%G4a8Kgpm58J{T~8s1Jy<_mx&#AdQ#o z?d;6&|NZ;_=g`n#|EAU_S}BU!lpIK;$(s(IjVI$L%L)YY)>u4n2KJNtb=Wip(_x7BF6Lt2I;ZX z5FN)ZqU8`^H2?%UqJ(%k9F9hxr3D}W4n#N}u!5ihS(QY(_G5@OS2xsjVo%K$`HC@l z-*;4wD;A5Pq8P$%j^hZO3>l>5C}r50ClZ_b0*zF#kj64b_d~ z6$VIZQ>liHReiCnh6&H_n<8P<60$1V%S=0nOCt|Yx*F=6B5rOEqH)x?i=dC)4910e zp@!-%(}EBJ^o~)})^XA6SSElH&_^+bRE=kW$nr`CNGh+WQiKH&6@V%}=I{tuKC zm?s66RzL}5bm+tTf^|(buxmjQuxY^@;v752(2X>zaa_PIF-*J@m+vRz@gWx*W`P`d z>Hc0i*%z02MV471#8=!+rPQSD`Owx-G7)1)azdu5s|rMdD2g(Rpa|Hk9*(e4B@$*4 zFF|M^2sB-(%O|il8Bm}uuM>Gm07@2$3aeygk!?=HwuX46* z?2KcE){K5X@;j0UCM^8|**~|U2SBu?;yeExq;JuqyzSQ@1lz+c$J+JG%Ylz)6K7rZ!TOs Sb@s0yL@L=gm^j}%{^mc4t&ljBz1!V& z?WT)!`~izG;`|i`1|pj9kA^s1LWU?Brjls!|os4x!GSXZ?K`H1eY6eCV#Kz4<1IT^QCQ~q@>H+HF^v4vbDgkPTH^#<{ zAk3Q2Z*eX^)fn>UdgixMW#pbf~PmeVb%7@$hL5*|C#3`Le8c3*&McMOur zSPvOQCL{$HOUo?FlRnYK2_BE%-$wE*&oZo!;aHmIC03BUZnAt)*qW)NrFf{LYzuz{ zsH|-p62lY<1y{lCLS}~HL{Vf|p5b{KBWP<-w?UECt>y|t2wJkK8n%jb(qRNCl(z#E zR=VJVW>jf)s~jdAFs29$hI6rwOC_KpS8+z(%$1BQG6Qo^gSu^DELX)ES!5$Ci6l29d8UNXhL8kGl5X9 zlBk}FWRmAyEZMR{)fH5*S{H4AAz;G*g|o)dtefV<1nZSJQS!KH)+e#-0yKscH9h!0 zC{8fv!{}?ExMUPygPVdCSxO^Q1K43z0~yE|dWIsaVU&U>hfHi3t8*`&j|PK1CQ7S0 zykNyUw~*0rP~b#Cpm`Tpk{gRjQQfkEF2iUjK;huHR8^5YP;q-bUXJz)P@z*k4^M+s zilqe}C{U2yZm7V@{t%M$P6<@@D|kOhi@Y}tA?@*skQRL&K>JfZmUeRjulUo7->dj4 z`?s1ZK7t^3+dR&}bZSS6s20xZVEO25hyCSIP9;m_A_3Vs1_4TT@(&fNJgqJffm`!n zfwqu^nDhp9u>iAB+AaVSwr6m(Zd!NDa^fv8aAWDWmb_)8Utg!UBfT6}IScd*#66Xv z9A_A(`z$1dxjtW|wMCt(0>|yo8CLr){-}0tsAFmq_hovYIY$sRHPKLeBKg^$ug}+y zvW+9dU!8gE#a)NblC6&%>za)3-u2}hO>;XZ&vvP6;2$q-{dQpb>fga_duNkZ9;&_n zr`q1b{k3mRJ-BnYv)|e~5!^%(V>K<(2VZZyukn?c`8^{?H+&dARAc>k&q&=zW1qKu zb7-*c!rhZZZ`-+F{%P(%PE3y|S6aveowbp_hTmy=e{`o>G$ad+p{6MZf^W!NAlmMsgAX$I-fbwapvTT_t!MSXzSXezwJ4`@5qt! zktz1lu@zc=_nYDM>O>BWp1Ab#11sm(|6JQU((vqf Y!~Q}vd?9@Lob!i^hPy-WZFze1KQT;ZSpWb4 literal 0 HcmV?d00001 diff --git a/icons/ui_icons/inventory/id.png b/icons/ui_icons/inventory/id.png new file mode 100644 index 0000000000000000000000000000000000000000..4469591d36f52d65a994e06e2ce963ad6120374d GIT binary patch literal 1940 zcmb_dOKjXk7y1@p#u{?J>5K z-7FjshlHY>I8}%Pf(u*_aNq(`Ri#KAsT7Wgs$4)w<&hkE3*&v56q2-xuw>78=9~Zd z{qtX0n4cXPerlNGxRLT)X_1Y5_C7qszDc{i$A(kU+~tJh9yy)81Kj%`oZ+~k&;80$ zy5wAdm&VMJ+K*M*9*I+hy)dzW z$t@JgiUcy3IzSiaxM($Qbd3TpzNhY=QOgT_oTvrap>UM?0FV(R$~wvjB(p6AQcMMJR+LsTWD zW3J=Ca+stj#H3uZ1r{91_g$zKbY1g2B5JDXimIYx(KL}Fx~>OQk08S|%>MZj#j6|C@ZBy#v=SOJrw>-R0RdZzAj`Z$l(xaf(@! zd13U&JFG#yTP{simQLZj`}-5ylhuuv9@>J-I|E?mP4d&L?c3LX=B`e@_S@d+y&K?- zFChP^|CsZ~ttNK?#oIgAzPtLwqfh_x*49_n^41;Bokhtr(&p&y=EmTVKK#!0yJxRR zo6^Mi*~#5=++X{*w-@X)jh9<>bNTznza4z#$(uh6d_3~r&6!_68JziMklXn%*!}3s SjX$%#S)QIReRh8N?Y{ws0&39! literal 0 HcmV?d00001 diff --git a/icons/ui_icons/inventory/mask.png b/icons/ui_icons/inventory/mask.png new file mode 100644 index 0000000000000000000000000000000000000000..82e51089379638169f03a711e11b1a6312d06ba4 GIT binary patch literal 1930 zcmbVN4Qvxt9KSBv0$U&mP6!Kf-i)B(dLR8*ds&BDyN?FDEnCe-LbhG+UfavsyW8Du z{U8R=kp}t=)l@>ju$g zPd)0C6o6WdB+ii}jh5Jrl+|pu*|wlGNs|OwLQo`5(+p{4EG9I4VbGew`MJQv_KPBoWmaS)bIY zIWVYn!G*A#r4`k5m|(z&C@T|`k3}SWR=qJ95Dh?;Iu#C7c0rlNx;HZ8Q~;|< zidT|CnMBoRB%?HKB+<=Hg2+n|_4!2`fQ!|D1A|$kaMFZRb}wmRC_7`a;$#U!k_%9e z#0&n||DZ6zG{soVcR^vv@T|r@6wGssUsA#>bXW+p0YJ!N07J83WSmk+QlMd|&a`;m z?R3^Fl3xhH1+}5N0(Dn9t(4tr#c3mzlI!s>Zc)`(kppg*1B1aa3Ifki6lb9*s|6=1 zK;ytI-7Y~O_++0G3_NUgxWTv|R(Ac(=^ zc9naZ$1lEHSN9sZuIJA~wXg5nAD^YWC$7}L`16kha&0AA&!G3Gm7m16X9GE|djE$TrAB-9Pn(gh~k!;N$S(UUOkN?Y~#53 z&fN83LvncIhl$qrzyGZ}nTr@#4mgjBy?4Gnh9re;eQ)&LuHtuG>N%6>Zt<>?4-OW# z{Bi7quTPE^7OlOu^S$rpkM1e*@1Ja``lw+3r>WVMgA*mY{(XDqhW@v7SJt`)E81TD E4>iM=I{*Lx literal 0 HcmV?d00001 diff --git a/icons/ui_icons/inventory/neck.png b/icons/ui_icons/inventory/neck.png new file mode 100644 index 0000000000000000000000000000000000000000..78ad3ce3b1c7d608523bea7f3b8ea62fcc789924 GIT binary patch literal 1852 zcmbVNeQ4Zd7|+I5+Rmv^=Imf2W*=^>N#5jgxhC87V|TaqWY-?;K`RvJd&zruv3Gf6 zlIvZobgfWX{}KfUIzeS_BFwF9D(YY#;&g7v91a|;%*qhPX4xO=A0jf}ThI`1J_O99*^6!j{{6TJYR_C)0MXhM?t|ipl z?>tFS%dZ=Wez!liOVx0WgE~$ku9&k3nxeM17A>d^B9~60v|$FB+b2#jw4n!?K7S0v ztPsi=og+5t9qCGFBZHcvGp#N3_M%D%a>#{rF_$$RwHRbdyee6HuX% z`(trBgl$C29AGs7L^_~wf-FhR%}uljM8JapF922)RUoT=A6@=2#G9?B)SmFpvM=%! zWHPR6sXSjO6u5$q!*-e%6h+~I$crLN5Uew7y0FNa&iV>N7&)44SgwIh+GB)CoOgo^ zQ92hw&Z^RyPB~2^VSEuT8SG*wgBPK$ zZvV#tNo_1v^|7FqT(0WEaU(+{jB-L2L_3LL3-LY3!FgLlks%^;y%&w8hHL~~Y$q_z zRtgobNTx-R1N7!T!_;xX*)q=ng`tar45^yH0v{_V3E)>npprERr~u4CV^}v*!~cVl z0t*3EB9`-@q-AvI!ux`CO-*4t2T8z24yF-rnQ4Zurcn*yEVhYbqRuz}eKZt`+c;%p z$%fO@v4f6ALb9O9GAnXINp38rMoq_sriP;7AVZSF8HTRvq9mmJffOssP-caI1X!pi zlWf2bL|sb;BtL)^`!Lq>UJF$0bz%=SO;>z^!pc%oVxb~6v(1u3=oB3yKk`FCNLB2+ zZG#*^n0;s-?_hepqjnk&sp@d~==7o&%d4zGm)eEM@{U1}(Y*R2ohiRI7RbPZMKDKO z$RI*`kG`0LIXLAOppDwoBwP3GJAN_o4jj6-^am^6BGT{f(}$5>POFxIW*U*1$}?Ut zyf=O3io)MruhQPUNmYT9@#ih8bC*0+CpR()wN2*o#e=_pM`rq+Xt+JmKQX&6`si2S zk;qHEH+oameq8wa zui%ogzN;7Byxu@geB{`QE2sC2Czo=?t7~i9_q1*LqV0t8#ybD88{MD$b}@C~7w6}v z=-u0Drj~X5G;n^y*73$a51c$Qk-j!N>6}V^7Tq70*KPQKn%R5uj&HfXHgqk&d*^um z`{VWJF1;|duekcTqo*4ZY?E!A9XTdMh@B0>wbcK)Xc>a}t0rPNTB>(^b literal 0 HcmV?d00001 diff --git a/icons/ui_icons/inventory/pocket.png b/icons/ui_icons/inventory/pocket.png new file mode 100644 index 0000000000000000000000000000000000000000..f42399dca0f57aee93ea1c8890690e1cb09eb643 GIT binary patch literal 1831 zcmbVNe`wrP98YWKX-9`+!ba!DBdxH3xxA!F?h<-#-Q9I};&ol`pyxzvE_rtsddW+Z zw0A8-y5e9(|58zKLzrX!rJaJ(O$Ceo;U9MYh-2;kbIL$jw<=bOO!Vb?chuQ#V}T?u z?|t6q`}zKO-%R)S9d2uFZ>1=zEt!Z7kT>w}#~vk5cVglX@_O7(yx>yQ)*b%cM7?`v z7e#IP$;f2AZ0dPM!)8#`aSjD5rcKZk)qS91tJ*N~Kn@iQD?;D?;wu^$dW0SnQ!r&m zQPD_DI4C{Qm(eDMHCd+*JPW!j3L!9&r-F)EvRtJSp{u+K8T->L4XO}tI70XM20=E} z52DyXKng-egOCRyImk(Z5Dq^Bc*sK*hFA_VJg-1W5xYR`qKP#}&npA5-kL4)6`_ls zXDcjQE|-Jlt{`>_EGNq{3wf638G>Nkam!OHjOBLJ8DhxQ9K-euYyqE9&EZimLKCHH zE|_+M)^clMA^~G7s?Bmi=(|(}>RJP5k2=yAR)Q#o; z7$Bidr5ZLi)MA4Y$fU-)x%B(<5E3Q z{dLKJ=YtUJ8#F8(m)-sAHlUd5p$JW~#xbyq;p7Yy6;4(JnSmh%!Zm0L>qdV3e^8QO zF07DDuZNP7(N#~qFId-B}RpE?y@+{nuoyCzCz7^mfm-DfmSE z(gzo#7bmZ`!?T}XiS5QepPx_c4)h*-?tJIc`>)NOUAnVVi6$uU+w|4tePGMjl|3!v zm!JOS#7gPUk91?C`^a!^+l{|=&IRJ%y*zc(OE2tY!T!Mazu$S}rEmVurUN&6Ps{}N z0%+bc-Wxg@XXaPf$vrQAe5-xe?2eHiPoFz}>dURKpFUbX^@eyg6rcU}hvP>NZeJa4 ze`@Z`a?4vr)}<4d7yoIx-MrBGX4@s|SXb+O>(Dzh^7c>jtIgM@?VGQpdl#B#2Dz1M q=O#a!%XVH^Js6q#aIq!OvE1}zYpnU}3!ixYuPhnwi_IJwn)(+_K2}r! literal 0 HcmV?d00001 diff --git a/icons/ui_icons/inventory/shoes.png b/icons/ui_icons/inventory/shoes.png new file mode 100644 index 0000000000000000000000000000000000000000..d20f7ef4d10615f467b90ca65fbaaf85b49fd4ec GIT binary patch literal 1817 zcmbVNZEVzJ96yjPz}#47G`=LHD-lp^`?Po0yEfd|xI4E?=(vn?oQ^Ph?bExuYoF3~ zyW44^A!;NVrzVa?x5Pjq#18}w(I8QL`Q``02SsDXhdGplM3g9M2);bMmuBJygr>bd zeV*U*`~Utg&;RzP`+Hkg-@iH#2(+g95(D^6c+bja{OeRIzv0&^tM73q5NNr}dzJ;> zoW3^@XqwkESvMP`Ut@R4lp3Xuz=z?5%6L-NYpP9TeDS797sG`w}roA zWWjYUkzq=u5?u=Vbf^x=9S1wbgv#!CAfR1A8ma8L^@EGMB8g*kN zR$6nxuzXt6sfP&%j48_&!_vTWsR~pTA7_o)#j0^tVPFv&&~zP)WqqtwKrV6$XbEb6 z`9B75XeG(Fv8Wcq@J%>watyms56GfuCo^tAW&k>9)K*|}49i^Sd1Hxj8_F)SGYAzM ziApyl6C6haV&hKTR8h%!Xu$@UkX;xfan@K01SwX?fUw94VmL&BhzLLpDj`+Z#{UP! z31*`r&eB3CE*VvJ<(q<4MbwaO$k<`skn@nS%sff>VHD%2h-_>as|zliPsQVD8)~QBM73N*S@DiRj8we*LzS#g>x)F-);y@u zmI@G)-k>gOFb8RFNw#569!Kk@b;m3v-jT;{EdAD!w}kZT>-2V{*Tbq5WHS$OPi08Y z8OG~AwWKiD=c~H5pi?#AxczxUzwhD?zjH$qQ`@*N@9vmCk9+!TD$$e4e(}>AQp>*X z_80zo?Vg!y8$U`k&3t+M*ukO6gC7S z*>i>Cr#8Jim%Zcb3$qj9(>?1ie6wQqT4&*E$J%e#|B^cL!KMfIUuv3Wnh#t)^z_i7 z+>1y5ZtiLueSX`Dsn5Re-aYkj`mIlfcbywdc8xTh{#0{rZW!=brHXyi&>j#L=yL_WcWGY*4lU literal 0 HcmV?d00001 diff --git a/icons/ui_icons/inventory/suit.png b/icons/ui_icons/inventory/suit.png new file mode 100644 index 0000000000000000000000000000000000000000..e9c48e8069f75f8d3c100e570f4e18b4bd543697 GIT binary patch literal 2073 zcmb_dZEO@p7~a+^LTQ6WB?Q0LZ37zF-Pygn-R|0xws(cQ*sHZYp#1P-Z+7l()4Sbu zciX%6hqi=h{9!_DqKN_09|W`l^20PGn4s1wIbw|(v5CQ8V`2!zl8RztqR!q|3ba5P zFWKAKnP=XQ=Y8g#H@Z5v*Vk^WB?zKE5)O6aH|ajBSK)tqXy___tue#9ZGu>PpZio1 z@9y6~5UWmTv0kS)+95+D!zii|M@%kbVl+WCZ_Sws>_ZM2M@dZ&Q1howQ>3N_s9pXj zh?+r^(!xU)>KW>c!J$4Vsnph|$>yAl4P=m`khx4+x8+=bD%h3r*qvr6vH)@V0#vK( zknD|ikwL>kq`&|g0>G1^#BhSo*VMFyDsiLV z2~arFVhS0vVpg|HG-1NnoMN&Z1KgAfKowSSX1|p#I9DNy(kO#;$HrK$f;CfyW7sKU z8Pk>J>l|QeqtS|wC1c5CDlTlNeE_FXB4kOl9UC+e+l_3a--4)p0EgM&W@E}h3n`9a z#S9}|E>u@JWRm9@Kt8@p(^VsDKe1>73Mmc>P`GLw4ZJib#eiStB$*dzAj$v~p;1HC z5`#BEae*-uh}S`}WK_jbZVOhSoG`46f)my3$Gj_9_d=nzFh0Sc4DXqqZZ67Y#gKs4_Y{4_85 z5_FR!A{uZ|5#n9}055h_sF)lwV81JY@_rTXhtMlZekjs$2>i6y?-Oas$E&m$S9o4k zMb!tz^8RNn4Ie=zy<$7=!F07Fhcz2lb+B}FdeF*%O|y=D~Y?#93iI14j#)N9@w|z?ba{+FE-|K-n!BA&BCK^jUB%<3=cds z{MWgeO~RF(WZrW*|Kow#Lz`RFhkH)FlAdH+cVFnKZk+jg_>AYh^Wfm$#J`>sSEm-P z)y&N;H0D1z{QNV&cg#yw`6nM(J=#D{c|M<*tp2utwr&1oQ~uKF7V&;(r+wt5BH6NXvTtD{ljA#AX?;rflv+%>YacXSGh%o=^npFMB zIC1T%iDScm3|>f9kFNVP{pZLxp4hL$%*^JWQ`JB1zdQWdw!L$2ewY|tJ6reqQAm&c j^UGzXp>8}IHaw3U@oi|xPxlVEmv^MSGjzP|r9J-vG{UPI literal 0 HcmV?d00001 diff --git a/icons/ui_icons/inventory/suit_storage.png b/icons/ui_icons/inventory/suit_storage.png new file mode 100644 index 0000000000000000000000000000000000000000..9722eb10297ab295a9c9248c58ac4426acad96c4 GIT binary patch literal 1976 zcmbVN4Q$j@9PcKF$Ry73kp)F+(W#mJysus19OG_mm#kxsBbH=jI17MkNcJf#{sd3||_I0#Os4q5>u=MkkJlC<(saZWlnlLX))oLYo<+|Jr%>SX@OdQBYNZ3J!LX_8tL>Y^twr_eaOJDqUqLMfW6RPiea|n zaxi3uggRbS;}nq8Fr-p(4WTh?L1jtMwrY@Wgoc{XMOfW}WX`j^(RiN@fvM_2RgFy~ zs$oJh&T$lj-@9B^BsFQ=cisl*2PX6qC~GXq*h$tCWSl(f;T>+0aq|o_4i!{Mj;c>g?yw2YkK;U5&^w zv|u#WEW!iTJ{Rk8xk!#;vvLK24=9ES6cGmeUIGP&l4XgH0J}3%;S7^Z#8W|v5^^zY z=iFpOWbK?Nh8YJ3CieSPF=3U!#C{3wce+^UVx3Ns16&0uIUJDm0Kk&r3bzCq07N^J zx4%S}(Gdi(>*lczrd2z9t!$vIw&sq`Qn)5JiphAkTzDW_$G}U7R{o(x3+{c*DQlI@`YbY?rMh=y+yiYsbo+cby-b9i86TiX^P6Il1y<2&s;qAdsC zewYrFjP|Fx+E+{&EUmORBnBQ_7%kzn>1#);zFBv8*8a5{<3}bBbYt(99)5Axqn*cg z-M(pd+2N7jmlY1}c&Ga_$FUvLUmG?_x;>J4sc^+{ScMJte_-D{?ZB4iGodfA*ZU6r zG)E6Es>8RRrT#cGEzS5l^1uupDt=|O^-2)B}8=| sI?4@>B@1q@R%>UL?QyPJ+=b1o^uDokjEDRFvi_}s>U#gSMXT5U2WnlQ>i_@% literal 0 HcmV?d00001 diff --git a/icons/ui_icons/inventory/uniform.png b/icons/ui_icons/inventory/uniform.png new file mode 100644 index 0000000000000000000000000000000000000000..292b3324b5bd9b0a82a56fb0de737bfcd3e58bcc GIT binary patch literal 1885 zcmbVNZEO=|9KQ`&wl%{s38KU0xP8F4dtKk|ps=moOVN%}8dnIIUGJW@=X&=Xcek}` zGO~@BI46z|OF*5*7-K?W*glZ>!4EFZ511trG$9(IVu&a~nK2E~7}4jtwi&oNLX+z~ zFTdya|NUQ{|BZ)xy6S2luf;H|F4!H2pf_$m)m7-<%;oavRik$wGBM2Yi2YPxZ%%K+ zu#MN0Xv~U*o);vjQlbpwfXb>mLSxwOy;)t9`hkVVK~m9t#L~x~5V#`yh+bES3F&^2 zQo3^nIGF2+O1XZCmx;Z5@ZDJf5vaft@vNHGOd;zd3cLau+sib87a&%@k7%>?rgEUZqW|;`fma%#YTF^|vHK@zm zcNsvT4TZ`!R@I`aWfP{=F^JqK24q#V86DCA9RVgBFeK11h-5a~-spng0HOtrD1_-! zqQWJ~ILA>8{$#JB$uMI+wc-E>h!*e>C~GXqw2&+xWn2Qw3-;6_Fw8PE1Z5>L^dBfn zFvke2^DZbV8CkT%b-}VEB%q;+$YDhllYrK>B!QR1DEMI-8ptqG*Rpaw==X;Wm{8Ja z!;Eyb5(ox-1PTtND6-&XT#_fwu%yT71;|-voaDW3 zne<8`%fwk%+{2X52cR@ymq6*fTsYs-B1s93Bi+~e{}JXf;6 z-%!vM6w?pPV_!_Wc7$%lL|GjwUY&#BSaFnA@Itu=qGVqKA0gTK2QpDyR#uI`{dllU zn@Isg`agBC3^QTE%7_MNOQLA4TX*zY;!SaIJ?Zz8yfviXTc-~qy%<(0C2C24dMZuW z&d_%ESxyRlZ@vn9D>_vPj@q9+EcabBDR*w5A!-Bl<)arr-;ZGxcrefwjm`ghI<#qw zsqf?`lfPjvwn<}WPR(9U5$Vscac#?ypFiC&bhhfb8}CLBFHD9Tw!%?sPg8hTbmCg< zX2Y$T;Y?@E2bqP~*N;}tR4wj2^WEDR|M%vRkso*DjAPAHeZTlCw@DI0L z9OOR-92eLl(-Q}JM~3t7UA&Zyyz*N0Ur%po(A%fxdYT^F{Dvc0nHOqn^*hH;IxZ}Z zUfyUm&U~|Ne!M#5Xr-<+R#j-bsKZ|@e7GU4-dGx4d?@hk?7oLn=b!kf`Rdit#mS}7 z+I;uOrfK;4)K_n*P0!3tHqPI9=fw3{U+Zv1HGK8UnZEk^mwz~PGIDMzAB)ak*>QH~ w+^stC_Hpj$pM2ux$f?FI`2Owd`R^;~OMm=yKJ~)(Y5M~h?C1%cYwsKT7f3W~>i_@% literal 0 HcmV?d00001 diff --git a/tgui/packages/tgui/interfaces/StripMenu.tsx b/tgui/packages/tgui/interfaces/StripMenu.tsx new file mode 100644 index 000000000000..531a0587001c --- /dev/null +++ b/tgui/packages/tgui/interfaces/StripMenu.tsx @@ -0,0 +1,399 @@ +import { range } from 'common/collections'; +import { BooleanLike } from 'common/react'; +import { resolveAsset } from '../assets'; +import { useBackend } from '../backend'; +import { Box, Button, Icon, Stack } from '../components'; +import { Window } from '../layouts'; + +const ROWS = 5; +const COLUMNS = 6; + +const BUTTON_DIMENSIONS = '50px'; + +type GridSpotKey = string; + +const getGridSpotKey = (spot: [number, number]): GridSpotKey => { + return `${spot[0]}/${spot[1]}`; +}; + +const CornerText = (props: { + readonly align: 'left' | 'right'; + readonly children: string; +}): JSX.Element => { + const { align, children } = props; + + return ( + + {children} + + ); +}; + +type AlternateAction = { + icon: string; + text: string; +}; + +const ALTERNATE_ACTIONS: Record = { + knot: { + icon: 'shoe-prints', + text: 'Knot', + }, + + untie: { + icon: 'shoe-prints', + text: 'Untie', + }, + + unknot: { + icon: 'shoe-prints', + text: 'Unknot', + }, + + enable_internals: { + icon: 'tg-air-tank', + text: 'Enable internals', + }, + + disable_internals: { + icon: 'tg-air-tank-slash', + text: 'Disable internals', + }, + + adjust_jumpsuit: { + icon: 'tshirt', + text: 'Adjust jumpsuit', + }, +}; + +const SLOTS: Record< + string, + { + displayName: string; + gridSpot: GridSpotKey; + image?: string; + additionalComponent?: JSX.Element; + } +> = { + glasses: { + displayName: 'glasses', + gridSpot: getGridSpotKey([0, 1]), + image: 'inventory-glasses.png', + }, + + head: { + displayName: 'headwear', + gridSpot: getGridSpotKey([0, 2]), + image: 'inventory-head.png', + }, + + wear_mask: { + displayName: 'mask', + gridSpot: getGridSpotKey([1, 2]), + image: 'inventory-mask.png', + }, + + wear_r_ear: { + displayName: 'right earwear', + gridSpot: getGridSpotKey([0, 3]), + image: 'inventory-ears.png', + }, + + wear_l_ear: { + displayName: 'left earwear', + gridSpot: getGridSpotKey([1, 3]), + image: 'inventory-ears.png', + }, + + handcuffs: { + displayName: 'handcuffs', + gridSpot: getGridSpotKey([1, 4]), + }, + + legcuffs: { + displayName: 'legcuffs', + gridSpot: getGridSpotKey([1, 5]), + }, + + w_uniform: { + displayName: 'uniform', + gridSpot: getGridSpotKey([2, 1]), + image: 'inventory-uniform.png', + }, + + wear_suit: { + displayName: 'suit', + gridSpot: getGridSpotKey([2, 2]), + image: 'inventory-suit.png', + }, + + gloves: { + displayName: 'gloves', + gridSpot: getGridSpotKey([2, 3]), + image: 'inventory-gloves.png', + }, + + r_hand: { + displayName: 'right hand', + gridSpot: getGridSpotKey([2, 4]), + image: 'inventory-hand_r.png', + additionalComponent: R, + }, + + l_hand: { + displayName: 'left hand', + gridSpot: getGridSpotKey([2, 5]), + image: 'inventory-hand_l.png', + additionalComponent: L, + }, + + shoes: { + displayName: 'shoes', + gridSpot: getGridSpotKey([3, 2]), + image: 'inventory-shoes.png', + }, + + j_store: { + displayName: 'suit storage item', + gridSpot: getGridSpotKey([4, 0]), + image: 'inventory-suit_storage.png', + }, + + id: { + displayName: 'ID', + gridSpot: getGridSpotKey([4, 1]), + image: 'inventory-id.png', + }, + + belt: { + displayName: 'belt', + gridSpot: getGridSpotKey([4, 2]), + image: 'inventory-belt.png', + }, + + back: { + displayName: 'backpack', + gridSpot: getGridSpotKey([4, 3]), + image: 'inventory-back.png', + }, + + l_store: { + displayName: 'left pocket', + gridSpot: getGridSpotKey([4, 4]), + image: 'inventory-pocket.png', + }, + + r_store: { + displayName: 'right pocket', + gridSpot: getGridSpotKey([4, 5]), + image: 'inventory-pocket.png', + }, +}; + +enum ObscuringLevel { + Completely = 1, + Hidden = 2, +} + +type Interactable = { + interacting: BooleanLike; +}; + +/** + * Some possible options: + * + * null - No interactions, no item, but is an available slot + * { interacting: 1 } - No item, but we're interacting with it + * { icon: icon, name: name } - An item with no alternate actions + * that we're not interacting with. + * { icon, name, interacting: 1 } - An item with no alternate actions + * that we're interacting with. + */ +type StripMenuItem = + | null + | Interactable + | (( + | { + icon: string; + name: string; + alternate?: string; + } + | { + obscured: ObscuringLevel; + } + ) & + Partial); + +type StripMenuData = { + items: Record; + name: string; +}; + +export const StripMenu = (props, context) => { + const { act, data } = useBackend(context); + + const gridSpots = new Map(); + for (const key of Object.keys(data.items)) { + gridSpots.set(SLOTS[key].gridSpot, key); + } + + return ( + + + + {range(0, ROWS).map((row) => ( + + + {range(0, COLUMNS).map((column) => { + const key = getGridSpotKey([row, column]); + const keyAtSpot = gridSpots.get(key); + + if (!keyAtSpot) { + return ( + + ); + } + + const item = data.items[keyAtSpot]; + const slot = SLOTS[keyAtSpot]; + + let alternateAction: AlternateAction | undefined; + + let content; + let tooltip; + + if (item === null) { + tooltip = slot.displayName; + } else if ('name' in item) { + alternateAction = undefined; + + content = ( + + ); + + tooltip = item.name; + } else if ('obscured' in item) { + content = ( + + ); + + tooltip = `obscured ${slot.displayName}`; + } + + return ( + + + + + {alternateAction !== undefined && ( + + )} + + + ); + })} + + + ))} + + + + ); +}; From 41eab207e4d312543e3c0f486331ad3543c471d4 Mon Sep 17 00:00:00 2001 From: ihatethisengine Date: Thu, 7 Mar 2024 21:52:49 +0300 Subject: [PATCH 02/17] 1 --- code/datums/elements/strippable.dm | 7 ++-- .../living/carbon/human/human_stripping.dm | 39 ------------------- 2 files changed, 3 insertions(+), 43 deletions(-) diff --git a/code/datums/elements/strippable.dm b/code/datums/elements/strippable.dm index 782c33e99d8c..726e7d529f09 100644 --- a/code/datums/elements/strippable.dm +++ b/code/datums/elements/strippable.dm @@ -127,7 +127,6 @@ "[user] tries to remove your [item]." ) - to_chat(user, "You try to remove [source]'s [item]...") // source.log_message("[key_name(source)] is being stripped of [item] by [key_name(src)]", LOG_ATTACK, color="red") // user.log_message("[key_name(source)] is being stripped of [item] by [key_name(src)]", LOG_ATTACK, color="red", log_globally=FALSE) item.add_fingerprint(src) @@ -177,7 +176,7 @@ return FALSE if (!equipping.mob_can_equip( source, - item_slot + key )) to_chat(user, "\The [equipping] doesn't fit in that place!") return FALSE @@ -197,7 +196,7 @@ if (!equipping.mob_can_equip( source, - item_slot + key )) return FALSE @@ -211,7 +210,7 @@ return FALSE var/mob/mob_source = source - mob_source.equip_to_slot(equipping, item_slot) + mob_source.equip_to_slot_if_possible(equipping, key) /datum/strippable_item/mob_item_slot/get_obscuring(atom/source) return FALSE diff --git a/code/modules/mob/living/carbon/human/human_stripping.dm b/code/modules/mob/living/carbon/human/human_stripping.dm index 98b6402c2c4a..077e532e6573 100644 --- a/code/modules/mob/living/carbon/human/human_stripping.dm +++ b/code/modules/mob/living/carbon/human/human_stripping.dm @@ -84,52 +84,13 @@ GLOBAL_LIST_INIT(strippable_human_items, create_strippable_list(list( key = STRIPPABLE_ITEM_BELT item_slot = SLOT_WAIST -/datum/strippable_item/mob_item_slot/pocket - /// Which pocket we're referencing. Used for visible text. - var/pocket_side - -/datum/strippable_item/mob_item_slot/pocket/get_obscuring(atom/source) - return FALSE - -/datum/strippable_item/mob_item_slot/pocket/get_equip_delay(obj/item/equipping) - return POCKET_EQUIP_DELAY - -/datum/strippable_item/mob_item_slot/pocket/start_equip(atom/source, obj/item/equipping, mob/user) - . = ..() - if (!.) - warn_owner(source) - -/datum/strippable_item/mob_item_slot/pocket/start_unequip(atom/source, mob/user) - var/obj/item/item = get_item(source) - if (isnull(item)) - return FALSE - - to_chat(user, "You try to empty [source]'s [pocket_side] pocket.") - - // var/log_message = "[key_name(source)] is being pickpocketed of [item] by [key_name(user)] ([pocket_side])" - // source.log_message(log_message, LOG_ATTACK, color="red") - // user.log_message(log_message, LOG_ATTACK, color="red", log_globally=FALSE) - item.add_fingerprint(src) - - var/result = start_unequip_mob(item, source, user, POCKET_STRIP_DELAY) - - if (!result) - warn_owner(source) - - return result - -/datum/strippable_item/mob_item_slot/pocket/proc/warn_owner(atom/owner) - to_chat(owner, "You feel your [pocket_side] pocket being fumbled with!") - /datum/strippable_item/mob_item_slot/pocket/left key = STRIPPABLE_ITEM_LPOCKET item_slot = SLOT_STORE - pocket_side = "left" /datum/strippable_item/mob_item_slot/pocket/right key = STRIPPABLE_ITEM_RPOCKET item_slot = SLOT_STORE - pocket_side = "right" // /datum/strippable_item/mob_item_slot/handcuffs // key = STRIPPABLE_ITEM_HANDCUFFS From 0c7a0b2cc78eb3f89ae097a73937e4c3c92c9b2c Mon Sep 17 00:00:00 2001 From: ihatethisengine Date: Fri, 8 Mar 2024 22:07:36 +0300 Subject: [PATCH 03/17] some progress --- code/datums/elements/strippable.dm | 8 ++- code/game/objects/items/weapons/twohanded.dm | 4 +- .../living/carbon/human/human_stripping.dm | 62 +++++++++++++++---- tgui/packages/tgui/interfaces/StripMenu.tsx | 17 ++++- 4 files changed, 73 insertions(+), 18 deletions(-) diff --git a/code/datums/elements/strippable.dm b/code/datums/elements/strippable.dm index 726e7d529f09..63be5a48f50f 100644 --- a/code/datums/elements/strippable.dm +++ b/code/datums/elements/strippable.dm @@ -180,7 +180,9 @@ )) to_chat(user, "\The [equipping] doesn't fit in that place!") return FALSE - + if(equipping.flags_item & WIELDED) + ai_announcement("tmega") + equipping.unwield(user) return TRUE /datum/strippable_item/mob_item_slot/start_equip(atom/source, obj/item/equipping, mob/user) @@ -191,7 +193,7 @@ if (!ismob(source)) return FALSE - if (!do_after(user, source, 2 SECONDS + get_equip_delay(equipping))) + if (!do_after(user, 2 SECONDS + get_equip_delay(equipping), INTERRUPT_ALL, BUSY_ICON_FRIENDLY, source, INTERRUPT_MOVED, BUSY_ICON_FRIENDLY)) return FALSE if (!equipping.mob_can_equip( @@ -238,7 +240,7 @@ /// A utility function for `/datum/strippable_item`s to start unequipping an item from a mob. /proc/start_unequip_mob(obj/item/item, mob/source, mob/user, strip_delay) - if (!do_after(user, 2 SECONDS + item.time_to_unequip, BUSY_ICON_HOSTILE, INTERRUPT_ALL, source, INTERRUPT_ALL)) + if (!do_after(user, 2 SECONDS + item.time_to_equip, INTERRUPT_ALL, BUSY_ICON_HOSTILE, source, INTERRUPT_MOVED, BUSY_ICON_HOSTILE)) return FALSE return TRUE diff --git a/code/game/objects/items/weapons/twohanded.dm b/code/game/objects/items/weapons/twohanded.dm index be7571fa84a1..a72f35434b4a 100644 --- a/code/game/objects/items/weapons/twohanded.dm +++ b/code/game/objects/items/weapons/twohanded.dm @@ -46,7 +46,9 @@ /obj/item/proc/unwield(mob/user) if( (flags_item|TWOHANDED|WIELDED) != flags_item) + ai_announcement("FALSE") return FALSE//Have to be actually a twohander and wielded. + ai_announcement("TRUE") flags_item ^= WIELDED SEND_SIGNAL(src, COMSIG_ITEM_UNWIELD, user) name = copytext(name,1,-10) @@ -101,7 +103,7 @@ w_class = SIZE_HUGE icon_state = "offhand" name = "offhand" - flags_item = DELONDROP|TWOHANDED|WIELDED + flags_item = DELONDROP|TWOHANDED|WIELDED|CANTSTRIP /obj/item/weapon/twohanded/offhand/unwield(mob/user) if(flags_item & WIELDED) diff --git a/code/modules/mob/living/carbon/human/human_stripping.dm b/code/modules/mob/living/carbon/human/human_stripping.dm index 077e532e6573..48d0fec89d4e 100644 --- a/code/modules/mob/living/carbon/human/human_stripping.dm +++ b/code/modules/mob/living/carbon/human/human_stripping.dm @@ -1,6 +1,3 @@ -#define INTERNALS_TOGGLE_DELAY (4 SECONDS) -#define POCKET_EQUIP_DELAY (1 SECONDS) - GLOBAL_LIST_INIT(strippable_human_items, create_strippable_list(list( /datum/strippable_item/mob_item_slot/head, /datum/strippable_item/mob_item_slot/back, @@ -17,8 +14,10 @@ GLOBAL_LIST_INIT(strippable_human_items, create_strippable_list(list( /datum/strippable_item/mob_item_slot/belt, /datum/strippable_item/mob_item_slot/pocket/left, /datum/strippable_item/mob_item_slot/pocket/right, - /datum/strippable_item/hand/left, - /datum/strippable_item/hand/right, + /datum/strippable_item/mob_item_slot/hand/left, + /datum/strippable_item/mob_item_slot/hand/right, + /datum/strippable_item/mob_item_slot/handcuffs, + /datum/strippable_item/mob_item_slot/legcuffs, ))) /mob/living/carbon/human/proc/should_strip(mob/user) @@ -59,6 +58,44 @@ GLOBAL_LIST_INIT(strippable_human_items, create_strippable_list(list( key = STRIPPABLE_ITEM_JUMPSUIT item_slot = SLOT_ICLOTHING +/datum/strippable_item/mob_item_slot/jumpsuit/get_alternate_action(atom/source, mob/user) + var/obj/item/clothing/under/uniform = get_item(source) + if (!istype(uniform)) + return null + return uniform?.accessories ? "remove_accessory" : null + +/datum/strippable_item/mob_item_slot/jumpsuit/alternate_action(atom/source, mob/user) + if(!ishuman(source)) + return + var/mob/living/carbon/human/sourcemob = source + if(!user.action_busy && !user.is_mob_incapacitated() && source.Adjacent(user)) + if(MODE_HAS_TOGGLEABLE_FLAG(MODE_NO_STRIPDRAG_ENEMY) && (sourcemob.stat == DEAD || sourcemob.health < HEALTH_THRESHOLD_CRIT) && !sourcemob.get_target_lock(user.faction_group)) + to_chat(user, SPAN_WARNING("You can't strip a crit or dead member of another faction!")) + return + if(sourcemob.w_uniform && istype(sourcemob.w_uniform, /obj/item/clothing)) + var/obj/item/clothing/under/U = sourcemob.w_uniform + if(!LAZYLEN(U.accessories)) + return FALSE + var/obj/item/clothing/accessory/A = LAZYACCESS(U.accessories, 1) + if(LAZYLEN(U.accessories) > 1) + A = tgui_input_list(user, "Select an accessory to remove from [U]", "Remove accessory", U.accessories) + if(!istype(A)) + return + sourcemob.attack_log += text("\[[time_stamp()]\] Has had their accessory ([A]) removed by [key_name(user)]") + user.attack_log += text("\[[time_stamp()]\] Attempted to remove [key_name(sourcemob)]'s' accessory ([A])") + if(istype(A, /obj/item/clothing/accessory/holobadge) || istype(A, /obj/item/clothing/accessory/medal)) + sourcemob.visible_message(SPAN_DANGER("[user] tears off \the [A] from [sourcemob]'s [U]!"), null, null, 5) + if(U == sourcemob.w_uniform) + U.remove_accessory(user, A) + else + if(HAS_TRAIT(sourcemob, TRAIT_UNSTRIPPABLE) && !sourcemob.is_mob_incapacitated()) //Can't strip the unstrippable! + to_chat(user, SPAN_DANGER("[sourcemob] has an unbreakable grip on their equipment!")) + return + sourcemob.visible_message(SPAN_DANGER("[user] is trying to take off \a [A] from [source]'s [U]!"), null, null, 5) + if(do_after(user, sourcemob.get_strip_delay(user, sourcemob), INTERRUPT_ALL, BUSY_ICON_GENERIC, sourcemob, INTERRUPT_MOVED, BUSY_ICON_GENERIC)) + if(U == sourcemob.w_uniform) + U.remove_accessory(user, A) + /datum/strippable_item/mob_item_slot/suit key = STRIPPABLE_ITEM_SUIT item_slot = SLOT_OCLOTHING @@ -92,15 +129,14 @@ GLOBAL_LIST_INIT(strippable_human_items, create_strippable_list(list( key = STRIPPABLE_ITEM_RPOCKET item_slot = SLOT_STORE -// /datum/strippable_item/mob_item_slot/handcuffs -// key = STRIPPABLE_ITEM_HANDCUFFS -// item_slot = SLOT_HANDCUFFED - -/datum/strippable_item/hand/left +/datum/strippable_item/mob_item_slot/hand/left key = STRIPPABLE_ITEM_LHAND -/datum/strippable_item/hand/right +/datum/strippable_item/mob_item_slot/hand/right key = STRIPPABLE_ITEM_RHAND -#undef INTERNALS_TOGGLE_DELAY -#undef POCKET_EQUIP_DELAY +/datum/strippable_item/mob_item_slot/handcuffs + key = STRIPPABLE_ITEM_HANDCUFFS + +/datum/strippable_item/mob_item_slot/legcuffs + key = STRIPPABLE_ITEM_LEGCUFFS diff --git a/tgui/packages/tgui/interfaces/StripMenu.tsx b/tgui/packages/tgui/interfaces/StripMenu.tsx index 531a0587001c..2ceaaa71e70f 100644 --- a/tgui/packages/tgui/interfaces/StripMenu.tsx +++ b/tgui/packages/tgui/interfaces/StripMenu.tsx @@ -46,6 +46,16 @@ const ALTERNATE_ACTIONS: Record = { text: 'Knot', }, + remove_accessory: { + icon: 'tshirt', + text: 'Remove accessory', + }, + + retrieve_tag: { + icon: 'tg-air-tank', + text: 'Retrieve info tag', + }, + untie: { icon: 'shoe-prints', text: 'Untie', @@ -79,6 +89,7 @@ const SLOTS: Record< gridSpot: GridSpotKey; image?: string; additionalComponent?: JSX.Element; + hideEmpty?: boolean; } > = { glasses: { @@ -114,11 +125,13 @@ const SLOTS: Record< handcuffs: { displayName: 'handcuffs', gridSpot: getGridSpotKey([1, 4]), + hideEmpty: true, }, legcuffs: { displayName: 'legcuffs', gridSpot: getGridSpotKey([1, 5]), + hideEmpty: true, }, w_uniform: { @@ -240,6 +253,8 @@ export const StripMenu = (props, context) => { const gridSpots = new Map(); for (const key of Object.keys(data.items)) { + const item = data.items[key]; + if (item === null && SLOTS[key].hideEmpty) continue; gridSpots.set(SLOTS[key].gridSpot, key); } @@ -277,7 +292,7 @@ export const StripMenu = (props, context) => { if (item === null) { tooltip = slot.displayName; } else if ('name' in item) { - alternateAction = undefined; + alternateAction = ALTERNATE_ACTIONS[item.alternate]; content = ( Date: Sat, 9 Mar 2024 18:15:11 +0300 Subject: [PATCH 04/17] almost there --- code/datums/elements/strippable.dm | 39 ++++- .../living/carbon/human/human_stripping.dm | 164 +++++++++++++++--- tgui/packages/tgui/interfaces/StripMenu.tsx | 34 +--- 3 files changed, 180 insertions(+), 57 deletions(-) diff --git a/code/datums/elements/strippable.dm b/code/datums/elements/strippable.dm index 63be5a48f50f..aace649cded3 100644 --- a/code/datums/elements/strippable.dm +++ b/code/datums/elements/strippable.dm @@ -41,6 +41,9 @@ if (!istype(overmob)) return + if (!overmob.Adjacent(source)) + return + if (!overmob.client) return @@ -109,6 +112,11 @@ if (isnull(item)) return FALSE + if (user.action_busy && !skillcheck(user, SKILL_POLICE, SKILL_POLICE_SKILLED)) { + to_chat(user, "You can't do this right now.") + return FALSE + } + if (ismob(source)) if ((item.flags_inventory & CANTSTRIP) || (item.flags_item & NODROP)) return FALSE @@ -174,6 +182,10 @@ if (!ismob(source)) return FALSE + if (user.action_busy) { + to_chat(user, "You can't do this right now.") + return FALSE + } if (!equipping.mob_can_equip( source, key @@ -193,11 +205,19 @@ if (!ismob(source)) return FALSE - if (!do_after(user, 2 SECONDS + get_equip_delay(equipping), INTERRUPT_ALL, BUSY_ICON_FRIENDLY, source, INTERRUPT_MOVED, BUSY_ICON_FRIENDLY)) + var/time_to_strip = HUMAN_STRIP_DELAY + equipping.time_to_equip + var/mob/sourcemob = source + + if (ishuman(sourcemob) && ishuman(user)) + var/mob/living/carbon/human/sourcehuman = sourcemob + var/mob/living/carbon/human/userhuman = user + time_to_strip = userhuman.get_strip_delay(userhuman, sourcehuman) + equipping.time_to_equip + + if (!do_after(user, time_to_strip, INTERRUPT_ALL, BUSY_ICON_FRIENDLY, source, INTERRUPT_MOVED, BUSY_ICON_FRIENDLY)) return FALSE if (!equipping.mob_can_equip( - source, + sourcemob, key )) return FALSE @@ -211,8 +231,8 @@ if (!ismob(source)) return FALSE - var/mob/mob_source = source - mob_source.equip_to_slot_if_possible(equipping, key) + var/mob/sourcemob = source + sourcemob.equip_to_slot_if_possible(equipping, key) /datum/strippable_item/mob_item_slot/get_obscuring(atom/source) return FALSE @@ -239,14 +259,19 @@ return equipping.time_to_equip /// A utility function for `/datum/strippable_item`s to start unequipping an item from a mob. -/proc/start_unequip_mob(obj/item/item, mob/source, mob/user, strip_delay) - if (!do_after(user, 2 SECONDS + item.time_to_equip, INTERRUPT_ALL, BUSY_ICON_HOSTILE, source, INTERRUPT_MOVED, BUSY_ICON_HOSTILE)) +/datum/strippable_item/mob_item_slot/proc/start_unequip_mob(obj/item/item, mob/living/carbon/human/source, mob/living/carbon/human/user) + var/time_to_strip = HUMAN_STRIP_DELAY + item.time_to_equip + + if (istype(source) && istype(user)) + time_to_strip = user.get_strip_delay(user, source) + item.time_to_equip + + if (!do_after(user, time_to_strip, INTERRUPT_ALL, BUSY_ICON_HOSTILE, source, INTERRUPT_MOVED, BUSY_ICON_HOSTILE)) return FALSE return TRUE /// A utility function for `/datum/strippable_item`s to finish unequipping an item from a mob. -/proc/finish_unequip_mob(obj/item/item, mob/source, mob/user) +/datum/strippable_item/mob_item_slot/proc/finish_unequip_mob(obj/item/item, mob/source, mob/user) if (!source.drop_inv_item_on_ground(item)) return FALSE diff --git a/code/modules/mob/living/carbon/human/human_stripping.dm b/code/modules/mob/living/carbon/human/human_stripping.dm index 48d0fec89d4e..d4774f722733 100644 --- a/code/modules/mob/living/carbon/human/human_stripping.dm +++ b/code/modules/mob/living/carbon/human/human_stripping.dm @@ -42,6 +42,55 @@ GLOBAL_LIST_INIT(strippable_human_items, create_strippable_list(list( key = STRIPPABLE_ITEM_MASK item_slot = SLOT_FACE +/datum/strippable_item/mob_item_slot/mask/get_alternate_action(atom/source, mob/user) + var/obj/item/clothing/mask = get_item(source) + if (!istype(mask)) + return null + if (!ishuman(source)) + return null + var/mob/living/carbon/human/sourcehuman = source + if (istype(sourcehuman.s_store, /obj/item/tank)) + return "toggle_internals" + if (istype(sourcehuman.back, /obj/item/tank)) + return "toggle_internals" + if (istype(sourcehuman.belt, /obj/item/tank)) + return "toggle_internals" + return null + +/datum/strippable_item/mob_item_slot/mask/alternate_action(atom/source, mob/user) + if(!ishuman(source)) + return + var/mob/living/carbon/human/sourcehuman = source + if(user.action_busy || user.is_mob_incapacitated() || !source.Adjacent(user)) + return + if(MODE_HAS_TOGGLEABLE_FLAG(MODE_NO_STRIPDRAG_ENEMY) && (sourcehuman.stat == DEAD || sourcehuman.health < HEALTH_THRESHOLD_CRIT) && !sourcehuman.get_target_lock(user.faction_group)) + to_chat(user, SPAN_WARNING("You can't toggle internals of a crit or dead member of another faction!")) + return + + sourcehuman.attack_log += text("\[[time_stamp()]\] Has had their internals toggled by [key_name(user)]") + user.attack_log += text("\[[time_stamp()]\] Attempted to toggle [key_name(src)]'s' internals") + if(sourcehuman.internal) + user.visible_message(SPAN_DANGER("[user] is trying to disable [sourcehuman]'s internals"), null, null, 3) + else + user.visible_message(SPAN_DANGER("[user] is trying to enable [sourcehuman]'s internals."), null, null, 3) + + if(do_after(user, POCKET_STRIP_DELAY, INTERRUPT_ALL, BUSY_ICON_GENERIC, sourcehuman, INTERRUPT_MOVED, BUSY_ICON_GENERIC)) + if(sourcehuman.internal) + sourcehuman.internal.add_fingerprint(user) + sourcehuman.internal = null + sourcehuman.visible_message("[sourcehuman] is no longer running on internals.", null, null, 1) + else + if(istype(sourcehuman.wear_mask, /obj/item/clothing/mask)) + if(istype(sourcehuman.back, /obj/item/tank)) + sourcehuman.internal = sourcehuman.back + else if(istype(sourcehuman.s_store, /obj/item/tank)) + sourcehuman.internal = sourcehuman.s_store + else if(istype(sourcehuman.belt, /obj/item/tank)) + sourcehuman.internal = sourcehuman.belt + if(sourcehuman.internal) + sourcehuman.visible_message(SPAN_NOTICE("[sourcehuman] is now running on internals."), null, null, 1) + sourcehuman.internal.add_fingerprint(user) + /datum/strippable_item/mob_item_slot/eyes key = STRIPPABLE_ITEM_EYES item_slot = SLOT_EYES @@ -68,38 +117,62 @@ GLOBAL_LIST_INIT(strippable_human_items, create_strippable_list(list( if(!ishuman(source)) return var/mob/living/carbon/human/sourcemob = source - if(!user.action_busy && !user.is_mob_incapacitated() && source.Adjacent(user)) - if(MODE_HAS_TOGGLEABLE_FLAG(MODE_NO_STRIPDRAG_ENEMY) && (sourcemob.stat == DEAD || sourcemob.health < HEALTH_THRESHOLD_CRIT) && !sourcemob.get_target_lock(user.faction_group)) - to_chat(user, SPAN_WARNING("You can't strip a crit or dead member of another faction!")) + if(user.action_busy || user.is_mob_incapacitated() || !source.Adjacent(user)) + return + if(MODE_HAS_TOGGLEABLE_FLAG(MODE_NO_STRIPDRAG_ENEMY) && (sourcemob.stat == DEAD || sourcemob.health < HEALTH_THRESHOLD_CRIT) && !sourcemob.get_target_lock(user.faction_group)) + to_chat(user, SPAN_WARNING("You can't strip a crit or dead member of another faction!")) + return + if(sourcemob.w_uniform && istype(sourcemob.w_uniform, /obj/item/clothing)) + var/obj/item/clothing/under/U = sourcemob.w_uniform + if(!LAZYLEN(U.accessories)) + return FALSE + var/obj/item/clothing/accessory/A = LAZYACCESS(U.accessories, 1) + if(LAZYLEN(U.accessories) > 1) + A = tgui_input_list(user, "Select an accessory to remove from [U]", "Remove accessory", U.accessories) + if(!istype(A)) return - if(sourcemob.w_uniform && istype(sourcemob.w_uniform, /obj/item/clothing)) - var/obj/item/clothing/under/U = sourcemob.w_uniform - if(!LAZYLEN(U.accessories)) - return FALSE - var/obj/item/clothing/accessory/A = LAZYACCESS(U.accessories, 1) - if(LAZYLEN(U.accessories) > 1) - A = tgui_input_list(user, "Select an accessory to remove from [U]", "Remove accessory", U.accessories) - if(!istype(A)) + sourcemob.attack_log += text("\[[time_stamp()]\] Has had their accessory ([A]) removed by [key_name(user)]") + user.attack_log += text("\[[time_stamp()]\] Attempted to remove [key_name(sourcemob)]'s' accessory ([A])") + if(istype(A, /obj/item/clothing/accessory/holobadge) || istype(A, /obj/item/clothing/accessory/medal)) + sourcemob.visible_message(SPAN_DANGER("[user] tears off \the [A] from [sourcemob]'s [U]!"), null, null, 5) + if(U == sourcemob.w_uniform) + U.remove_accessory(user, A) + else + if(HAS_TRAIT(sourcemob, TRAIT_UNSTRIPPABLE) && !sourcemob.is_mob_incapacitated()) //Can't strip the unstrippable! + to_chat(user, SPAN_DANGER("[sourcemob] has an unbreakable grip on their equipment!")) return - sourcemob.attack_log += text("\[[time_stamp()]\] Has had their accessory ([A]) removed by [key_name(user)]") - user.attack_log += text("\[[time_stamp()]\] Attempted to remove [key_name(sourcemob)]'s' accessory ([A])") - if(istype(A, /obj/item/clothing/accessory/holobadge) || istype(A, /obj/item/clothing/accessory/medal)) - sourcemob.visible_message(SPAN_DANGER("[user] tears off \the [A] from [sourcemob]'s [U]!"), null, null, 5) + sourcemob.visible_message(SPAN_DANGER("[user] is trying to take off \a [A] from [source]'s [U]!"), null, null, 5) + if(do_after(user, sourcemob.get_strip_delay(user, sourcemob), INTERRUPT_ALL, BUSY_ICON_GENERIC, sourcemob, INTERRUPT_MOVED, BUSY_ICON_GENERIC)) if(U == sourcemob.w_uniform) U.remove_accessory(user, A) - else - if(HAS_TRAIT(sourcemob, TRAIT_UNSTRIPPABLE) && !sourcemob.is_mob_incapacitated()) //Can't strip the unstrippable! - to_chat(user, SPAN_DANGER("[sourcemob] has an unbreakable grip on their equipment!")) - return - sourcemob.visible_message(SPAN_DANGER("[user] is trying to take off \a [A] from [source]'s [U]!"), null, null, 5) - if(do_after(user, sourcemob.get_strip_delay(user, sourcemob), INTERRUPT_ALL, BUSY_ICON_GENERIC, sourcemob, INTERRUPT_MOVED, BUSY_ICON_GENERIC)) - if(U == sourcemob.w_uniform) - U.remove_accessory(user, A) /datum/strippable_item/mob_item_slot/suit key = STRIPPABLE_ITEM_SUIT item_slot = SLOT_OCLOTHING +/datum/strippable_item/mob_item_slot/suit/get_alternate_action(atom/source, mob/user) + if(!ishuman(source)) + return + var/mob/living/carbon/human/sourcemob = source + for(var/bodypart in list("l_leg","r_leg","l_arm","r_arm","r_hand","l_hand","r_foot","l_foot","chest","head","groin")) + var/obj/limb/limb = sourcemob.get_limb(bodypart) + if(limb && (limb.status & LIMB_SPLINTED)) + return "remove_splints" + return null + +/datum/strippable_item/mob_item_slot/suit/alternate_action(atom/source, mob/user) + if(!ishuman(source)) + return + var/mob/living/carbon/human/sourcemob = source + if(user.action_busy || user.is_mob_incapacitated() || !source.Adjacent(user)) + return + if(MODE_HAS_TOGGLEABLE_FLAG(MODE_NO_STRIPDRAG_ENEMY) && (sourcemob.stat == DEAD || sourcemob.health < HEALTH_THRESHOLD_CRIT) && !sourcemob.get_target_lock(user.faction_group)) + to_chat(user, SPAN_WARNING("You can't remove splints of a crit or dead member of another faction!")) + return + sourcemob.attack_log += text("\[[time_stamp()]\] Has had their splints removed by [key_name(user)]") + user.attack_log += text("\[[time_stamp()]\] Attempted to remove [key_name(sourcemob)]'s' splints ") + sourcemob.remove_splints(user) + /datum/strippable_item/mob_item_slot/gloves key = STRIPPABLE_ITEM_GLOVES item_slot = SLOT_HANDS @@ -117,6 +190,51 @@ GLOBAL_LIST_INIT(strippable_human_items, create_strippable_list(list( key = STRIPPABLE_ITEM_ID item_slot = SLOT_ID +/datum/strippable_item/mob_item_slot/id/get_alternate_action(atom/source, mob/user) + var/obj/item/card/id/dogtag/tag = get_item(source) + if(!ishuman(source)) + return null + var/mob/living/carbon/human/sourcemob = source + if (!istype(tag)) + return null + if (!sourcemob.undefibbable && !skillcheck(user, SKILL_POLICE, SKILL_POLICE_SKILLED)) + return null + return tag.dogtag_taken ? null : "retrieve_tag" + +/datum/strippable_item/mob_item_slot/id/alternate_action(atom/source, mob/user) + ai_announcement("please?2") + if(!ishuman(source)) + return + var/mob/living/carbon/human/sourcemob = source + if(user.action_busy || user.is_mob_incapacitated() || !source.Adjacent(user)) + return + if(MODE_HAS_TOGGLEABLE_FLAG(MODE_NO_STRIPDRAG_ENEMY) && (sourcemob.stat == DEAD || sourcemob.health < HEALTH_THRESHOLD_CRIT) && !sourcemob.get_target_lock(user.faction_group)) + to_chat(user, SPAN_WARNING("You can't strip a crit or dead member of another faction!")) + return + ai_announcement("please?3") + if(!istype(sourcemob.wear_id, /obj/item/card/id/dogtag)) + return + if (!sourcemob.undefibbable && !skillcheck(user, SKILL_POLICE, SKILL_POLICE_SKILLED)) + return + ai_announcement("please?4") + var/obj/item/card/id/dogtag/tag = sourcemob.wear_id + if(!tag.dogtag_taken) + if(sourcemob.stat == DEAD) + to_chat(usr, SPAN_NOTICE("You take [sourcemob]'s information tag, leaving the ID tag")) + tag.dogtag_taken = TRUE + tag.icon_state = "dogtag_taken" + var/obj/item/dogtag/newtag = new(sourcemob.loc) + newtag.fallen_names = list(tag.registered_name) + newtag.fallen_assgns = list(tag.assignment) + newtag.fallen_blood_types = list(tag.blood_type) + user.put_in_hands(newtag) + else + to_chat(user, SPAN_WARNING("You can't take a dogtag's information tag while its owner is alive.")) + else + to_chat(user, SPAN_WARNING("Someone's already taken [sourcemob]'s information tag.")) + return + + /datum/strippable_item/mob_item_slot/belt key = STRIPPABLE_ITEM_BELT item_slot = SLOT_WAIST diff --git a/tgui/packages/tgui/interfaces/StripMenu.tsx b/tgui/packages/tgui/interfaces/StripMenu.tsx index 2ceaaa71e70f..26baaaa64fb6 100644 --- a/tgui/packages/tgui/interfaces/StripMenu.tsx +++ b/tgui/packages/tgui/interfaces/StripMenu.tsx @@ -41,9 +41,9 @@ type AlternateAction = { }; const ALTERNATE_ACTIONS: Record = { - knot: { - icon: 'shoe-prints', - text: 'Knot', + remove_splints: { + icon: 'crutch', + text: 'Remove splints', }, remove_accessory: { @@ -52,33 +52,13 @@ const ALTERNATE_ACTIONS: Record = { }, retrieve_tag: { - icon: 'tg-air-tank', + icon: 'tags', text: 'Retrieve info tag', }, - untie: { - icon: 'shoe-prints', - text: 'Untie', - }, - - unknot: { - icon: 'shoe-prints', - text: 'Unknot', - }, - - enable_internals: { - icon: 'tg-air-tank', - text: 'Enable internals', - }, - - disable_internals: { - icon: 'tg-air-tank-slash', - text: 'Disable internals', - }, - - adjust_jumpsuit: { - icon: 'tshirt', - text: 'Adjust jumpsuit', + toggle_internals: { + icon: 'mask-face', + text: 'Toggle internals', }, }; From 467bd9496e5f8fd21f4359e402067f0e9be8e690 Mon Sep 17 00:00:00 2001 From: ihatethisengine Date: Sun, 10 Mar 2024 07:21:18 +0300 Subject: [PATCH 05/17] fff --- code/datums/elements/strippable.dm | 4 +--- .../mob/living/carbon/human/human_stripping.dm | 12 ++++-------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/code/datums/elements/strippable.dm b/code/datums/elements/strippable.dm index aace649cded3..02c9d5a8cf5a 100644 --- a/code/datums/elements/strippable.dm +++ b/code/datums/elements/strippable.dm @@ -321,8 +321,6 @@ var/list/items = list() - //ai_announcement("[strippable.items.len]") - for (var/strippable_key in strippable.items) var/datum/strippable_item/item_data = strippable.items[strippable_key] @@ -347,7 +345,7 @@ LAZYINITLIST(result) - result["icon"] = icon2base64(icon(item.icon, item.icon_state)) + result["icon"] = icon2base64(icon(item.icon, item.icon_state, frame = 1)) result["name"] = item.name result["alternate"] = item_data.get_alternate_action(owner, user) diff --git a/code/modules/mob/living/carbon/human/human_stripping.dm b/code/modules/mob/living/carbon/human/human_stripping.dm index d4774f722733..2aecd907891a 100644 --- a/code/modules/mob/living/carbon/human/human_stripping.dm +++ b/code/modules/mob/living/carbon/human/human_stripping.dm @@ -21,14 +21,10 @@ GLOBAL_LIST_INIT(strippable_human_items, create_strippable_list(list( ))) /mob/living/carbon/human/proc/should_strip(mob/user) - // if (user.pulling != src || user.grab_state != GRAB_AGGRESSIVE) - // return TRUE + if (user.pulling != src || user.grab_level != GRAB_AGGRESSIVE) + return TRUE - // if (ishuman(user)) - // var/mob/living/carbon/human/human_user = user - // //return !human_user.can_be_firemanned(src) - - return TRUE + return user.a_intent != INTENT_GRAB /datum/strippable_item/mob_item_slot/head key = STRIPPABLE_ITEM_HEAD @@ -197,7 +193,7 @@ GLOBAL_LIST_INIT(strippable_human_items, create_strippable_list(list( var/mob/living/carbon/human/sourcemob = source if (!istype(tag)) return null - if (!sourcemob.undefibbable && !skillcheck(user, SKILL_POLICE, SKILL_POLICE_SKILLED)) + if (!sourcemob.undefibbable && (!skillcheck(user, SKILL_POLICE, SKILL_POLICE_SKILLED) || sourcemob.stat != DEAD)) return null return tag.dogtag_taken ? null : "retrieve_tag" From 3c568f9d8b38d049fb0a30a7857a2737bc471f4a Mon Sep 17 00:00:00 2001 From: ihatethisengine Date: Sun, 10 Mar 2024 18:13:54 +0300 Subject: [PATCH 06/17] final? --- code/_onclick/click_hold.dm | 1 - code/datums/elements/strippable.dm | 54 ++++---- code/game/objects/items/weapons/twohanded.dm | 2 - .../mob/living/carbon/carbon_stripping.dm | 129 ------------------ .../living/carbon/human/human_stripping.dm | 22 ++- 5 files changed, 40 insertions(+), 168 deletions(-) delete mode 100644 code/modules/mob/living/carbon/carbon_stripping.dm diff --git a/code/_onclick/click_hold.dm b/code/_onclick/click_hold.dm index bc5852cfdf40..3b5e1718d72a 100644 --- a/code/_onclick/click_hold.dm +++ b/code/_onclick/click_hold.dm @@ -102,4 +102,3 @@ if(over_object) SEND_SIGNAL(over_object, COMSIG_ATOM_DROP_ON, src_location, src) - ai_announcement("[over_object] [src_location] test") diff --git a/code/datums/elements/strippable.dm b/code/datums/elements/strippable.dm index 02c9d5a8cf5a..a7b518c8725f 100644 --- a/code/datums/elements/strippable.dm +++ b/code/datums/elements/strippable.dm @@ -37,6 +37,9 @@ if (user == source) return + if (over == source) + return + var/mob/overmob = over if (!istype(overmob)) return @@ -50,7 +53,7 @@ if (overmob.client != user) return - if (!isnull(should_strip_proc_path) && !call(source, should_strip_proc_path)(user)) + if (!isnull(should_strip_proc_path) && !call(source, should_strip_proc_path)(overmob)) return var/datum/strip_menu/strip_menu @@ -77,8 +80,8 @@ /// This should be used for checking if an item CAN be equipped. /// It should not perform the equipping itself. /datum/strippable_item/proc/try_equip(atom/source, obj/item/equipping, mob/user) - if ((equipping.flags_item & NODROP)) - to_chat(user, "You can't put [equipping] on [source], it's stuck to your hand!") + if ((equipping.flags_item & NODROP) || (equipping.flags_item & ITEM_ABSTRACT)) + to_chat(user, SPAN_WARNING("You can't put [equipping] on [source], it's stuck to your hand!")) return FALSE return TRUE @@ -86,14 +89,14 @@ /// Returns TRUE/FALSE depending on if it is allowed. /datum/strippable_item/proc/start_equip(atom/source, obj/item/equipping, mob/user) source.visible_message( - "[user] tries to put [equipping] on [source].", - "[user] tries to put [equipping] on you." + SPAN_NOTICE("[user] tries to put [equipping] on [source]."), + SPAN_NOTICE("[user] tries to put [equipping] on you.") ) - - // var/log = "[key_name(source)] is having [equipping] put on them by [key_name(user)]" - // source.log_message(log, LOG_ATTACK, color="red") - // user.log_message(log, LOG_ATTACK, color="red", log_globally=FALSE) + if (ismob(source)) + var/mob/sourcemob = source + sourcemob.attack_log += text("\[[time_stamp()]\] [key_name(sourcemob)] is having [equipping] put on them by [key_name(user)]") + user.attack_log += text("\[[time_stamp()]\] [key_name(user)] is putting [equipping] on [key_name(sourcemob)]") return TRUE @@ -112,13 +115,17 @@ if (isnull(item)) return FALSE - if (user.action_busy && !skillcheck(user, SKILL_POLICE, SKILL_POLICE_SKILLED)) { - to_chat(user, "You can't do this right now.") + if (user.action_busy && !skillcheck(user, SKILL_POLICE, SKILL_POLICE_SKILLED)) + to_chat(user, SPAN_WARNING("You can't do this right now.")) return FALSE - } - if (ismob(source)) - if ((item.flags_inventory & CANTSTRIP) || (item.flags_item & NODROP)) + if ((item.flags_inventory & CANTSTRIP) || (item.flags_item & NODROP) || (item.flags_item & ITEM_ABSTRACT)) + return FALSE + + if (ishuman(source)) + var/mob/living/carbon/human/sourcehuman = source + if(MODE_HAS_TOGGLEABLE_FLAG(MODE_NO_STRIPDRAG_ENEMY) && (sourcehuman.stat == DEAD || sourcehuman.health < HEALTH_THRESHOLD_CRIT) && !sourcehuman.get_target_lock(user.faction_group)) + to_chat(user, SPAN_WARNING("You can't strip items of a crit or dead member of another faction!")) return FALSE return TRUE @@ -131,12 +138,15 @@ return FALSE source.visible_message( - "[user] tries to remove [source]'s [item].", - "[user] tries to remove your [item]." + SPAN_WARNING("[user] tries to remove [source]'s [item]."), + SPAN_DANGER("[user] tries to remove your [item].") ) - // source.log_message("[key_name(source)] is being stripped of [item] by [key_name(src)]", LOG_ATTACK, color="red") - // user.log_message("[key_name(source)] is being stripped of [item] by [key_name(src)]", LOG_ATTACK, color="red", log_globally=FALSE) + if (ismob(source)) + var/mob/sourcemob = source + sourcemob.attack_log += text("\[[time_stamp()]\] [key_name(sourcemob)] is being stripped of [item] by [key_name(user)]") + user.attack_log += text("\[[time_stamp()]\] [key_name(user)] is stripping [key_name(sourcemob)] of [item]") + item.add_fingerprint(src) return TRUE @@ -182,18 +192,16 @@ if (!ismob(source)) return FALSE - if (user.action_busy) { - to_chat(user, "You can't do this right now.") + if (user.action_busy) + to_chat(user, SPAN_WARNING("You can't do this right now.")) return FALSE - } if (!equipping.mob_can_equip( source, key )) - to_chat(user, "\The [equipping] doesn't fit in that place!") + to_chat(user, SPAN_WARNING("\The [equipping] doesn't fit in that place!")) return FALSE if(equipping.flags_item & WIELDED) - ai_announcement("tmega") equipping.unwield(user) return TRUE diff --git a/code/game/objects/items/weapons/twohanded.dm b/code/game/objects/items/weapons/twohanded.dm index a72f35434b4a..36e0ea702a95 100644 --- a/code/game/objects/items/weapons/twohanded.dm +++ b/code/game/objects/items/weapons/twohanded.dm @@ -46,9 +46,7 @@ /obj/item/proc/unwield(mob/user) if( (flags_item|TWOHANDED|WIELDED) != flags_item) - ai_announcement("FALSE") return FALSE//Have to be actually a twohander and wielded. - ai_announcement("TRUE") flags_item ^= WIELDED SEND_SIGNAL(src, COMSIG_ITEM_UNWIELD, user) name = copytext(name,1,-10) diff --git a/code/modules/mob/living/carbon/carbon_stripping.dm b/code/modules/mob/living/carbon/carbon_stripping.dm deleted file mode 100644 index c5969d57c60b..000000000000 --- a/code/modules/mob/living/carbon/carbon_stripping.dm +++ /dev/null @@ -1,129 +0,0 @@ -/datum/strippable_item/mob_item_slot/head - key = STRIPPABLE_ITEM_HEAD - item_slot = ITEM_SLOT_HEAD - -/datum/strippable_item/mob_item_slot/back - key = STRIPPABLE_ITEM_BACK - item_slot = ITEM_SLOT_BACK - -/datum/strippable_item/mob_item_slot/mask - key = STRIPPABLE_ITEM_MASK - item_slot = ITEM_SLOT_MASK - -/datum/strippable_item/mob_item_slot/neck - key = STRIPPABLE_ITEM_NECK - item_slot = ITEM_SLOT_NECK - -/datum/strippable_item/mob_item_slot/handcuffs - key = STRIPPABLE_ITEM_HANDCUFFS - item_slot = ITEM_SLOT_HANDCUFFED - -/datum/strippable_item/mob_item_slot/handcuffs/should_show(atom/source, mob/user) - if (!iscarbon(source)) - return FALSE - - var/mob/living/carbon/carbon_source = source - return !isnull(carbon_source.handcuffed) - -// You shouldn't be able to equip things to handcuff slots. -/datum/strippable_item/mob_item_slot/handcuffs/try_equip(atom/source, obj/item/equipping, mob/user) - return FALSE - -/datum/strippable_item/mob_item_slot/legcuffs - key = STRIPPABLE_ITEM_LEGCUFFS - item_slot = ITEM_SLOT_LEGCUFFED - -/datum/strippable_item/mob_item_slot/legcuffs/should_show(atom/source, mob/user) - if (!iscarbon(source)) - return FALSE - - var/mob/living/carbon/carbon_source = source - return !isnull(carbon_source.legcuffed) - -// You shouldn't be able to equip things to legcuff slots. -/datum/strippable_item/mob_item_slot/legcuffs/try_equip(atom/source, obj/item/equipping, mob/user) - return FALSE - -/// A strippable item for a hand -/datum/strippable_item/hand - // Putting dangerous clothing in our hand is fine. - warn_dangerous_clothing = FALSE - - /// Which hand? - var/hand_index - -/datum/strippable_item/hand/get_item(atom/source) - if (!ismob(source)) - return null - - var/mob/mob_source = source - return mob_source.get_item_for_held_index(hand_index) - -/datum/strippable_item/hand/try_equip(atom/source, obj/item/equipping, mob/user) - . = ..() - if (!.) - return FALSE - - if (!ismob(source)) - return FALSE - - var/mob/mob_source = source - - if (!mob_source.can_put_in_hand(equipping, hand_index)) - to_chat(src, "\The [equipping] doesn't fit in that place!") - return FALSE - - return TRUE - -/datum/strippable_item/hand/start_equip(atom/source, obj/item/equipping, mob/user) - . = ..() - if (!.) - return - - if (!ismob(source)) - return FALSE - - var/mob/mob_source = source - - if (!do_mob(user, source, equipping.equip_delay_other)) - return FALSE - - if (!mob_source.can_put_in_hand(equipping, hand_index)) - return FALSE - - if (!user.temporarilyRemoveItemFromInventory(equipping)) - return FALSE - - return TRUE - -/datum/strippable_item/hand/finish_equip(atom/source, obj/item/equipping, mob/user) - if (!iscarbon(source)) - return FALSE - - var/mob/mob_source = source - mob_source.put_in_hand(equipping, hand_index) - -/datum/strippable_item/hand/start_unequip(atom/source, mob/user) - . = ..() - if (!.) - return - - return start_unequip_mob(get_item(source), source, user) - -/datum/strippable_item/hand/finish_unequip(atom/source, mob/user) - var/obj/item/item = get_item(source) - if (isnull(item)) - return FALSE - - if (!ismob(source)) - return FALSE - - return finish_unequip_mob(item, source, user) - -/datum/strippable_item/hand/left - key = STRIPPABLE_ITEM_LHAND - hand_index = 1 - -/datum/strippable_item/hand/right - key = STRIPPABLE_ITEM_RHAND - hand_index = 2 diff --git a/code/modules/mob/living/carbon/human/human_stripping.dm b/code/modules/mob/living/carbon/human/human_stripping.dm index 2aecd907891a..9e97da264db8 100644 --- a/code/modules/mob/living/carbon/human/human_stripping.dm +++ b/code/modules/mob/living/carbon/human/human_stripping.dm @@ -16,15 +16,14 @@ GLOBAL_LIST_INIT(strippable_human_items, create_strippable_list(list( /datum/strippable_item/mob_item_slot/pocket/right, /datum/strippable_item/mob_item_slot/hand/left, /datum/strippable_item/mob_item_slot/hand/right, - /datum/strippable_item/mob_item_slot/handcuffs, - /datum/strippable_item/mob_item_slot/legcuffs, + /datum/strippable_item/mob_item_slot/cuffs/handcuffs, + /datum/strippable_item/mob_item_slot/cuffs/legcuffs, ))) /mob/living/carbon/human/proc/should_strip(mob/user) - if (user.pulling != src || user.grab_level != GRAB_AGGRESSIVE) - return TRUE - - return user.a_intent != INTENT_GRAB + if (user.pulling == src && user.grab_level == GRAB_AGGRESSIVE && (user.a_intent & INTENT_GRAB)) + return FALSE //to not interfere with fireman carry + return TRUE /datum/strippable_item/mob_item_slot/head key = STRIPPABLE_ITEM_HEAD @@ -198,7 +197,6 @@ GLOBAL_LIST_INIT(strippable_human_items, create_strippable_list(list( return tag.dogtag_taken ? null : "retrieve_tag" /datum/strippable_item/mob_item_slot/id/alternate_action(atom/source, mob/user) - ai_announcement("please?2") if(!ishuman(source)) return var/mob/living/carbon/human/sourcemob = source @@ -207,12 +205,10 @@ GLOBAL_LIST_INIT(strippable_human_items, create_strippable_list(list( if(MODE_HAS_TOGGLEABLE_FLAG(MODE_NO_STRIPDRAG_ENEMY) && (sourcemob.stat == DEAD || sourcemob.health < HEALTH_THRESHOLD_CRIT) && !sourcemob.get_target_lock(user.faction_group)) to_chat(user, SPAN_WARNING("You can't strip a crit or dead member of another faction!")) return - ai_announcement("please?3") if(!istype(sourcemob.wear_id, /obj/item/card/id/dogtag)) return if (!sourcemob.undefibbable && !skillcheck(user, SKILL_POLICE, SKILL_POLICE_SKILLED)) return - ai_announcement("please?4") var/obj/item/card/id/dogtag/tag = sourcemob.wear_id if(!tag.dogtag_taken) if(sourcemob.stat == DEAD) @@ -249,8 +245,8 @@ GLOBAL_LIST_INIT(strippable_human_items, create_strippable_list(list( /datum/strippable_item/mob_item_slot/hand/right key = STRIPPABLE_ITEM_RHAND -/datum/strippable_item/mob_item_slot/handcuffs - key = STRIPPABLE_ITEM_HANDCUFFS +/datum/strippable_item/mob_item_slot/cuffs/handcuffs + key = STRIPPABLE_ITEM_HANDCUFFS -/datum/strippable_item/mob_item_slot/legcuffs - key = STRIPPABLE_ITEM_LEGCUFFS +/datum/strippable_item/mob_item_slot/cuffs/legcuffs + key = STRIPPABLE_ITEM_LEGCUFFS From 7eded776580f114a30ed80c55008bfaa3dff178a Mon Sep 17 00:00:00 2001 From: ihatethisengine Date: Sun, 10 Mar 2024 18:37:01 +0300 Subject: [PATCH 07/17] rip --- code/datums/elements/strippable.dm | 13 +- code/modules/mob/living/carbon/carbon.dm | 17 --- code/modules/mob/living/carbon/human/human.dm | 112 ------------------ .../mob/living/carbon/human/inventory.dm | 64 ---------- .../mob/living/carbon/xenomorph/XenoProcs.dm | 3 - .../mob/living/simple_animal/parrot.dm | 17 --- code/modules/mob/mob.dm | 34 ------ 7 files changed, 12 insertions(+), 248 deletions(-) diff --git a/code/datums/elements/strippable.dm b/code/datums/elements/strippable.dm index a7b518c8725f..235deadb581f 100644 --- a/code/datums/elements/strippable.dm +++ b/code/datums/elements/strippable.dm @@ -80,9 +80,16 @@ /// This should be used for checking if an item CAN be equipped. /// It should not perform the equipping itself. /datum/strippable_item/proc/try_equip(atom/source, obj/item/equipping, mob/user) - if ((equipping.flags_item & NODROP) || (equipping.flags_item & ITEM_ABSTRACT)) + if ((equipping.flags_item & ITEM_ABSTRACT)) + return FALSE + if ((equipping.flags_item & NODROP)) to_chat(user, SPAN_WARNING("You can't put [equipping] on [source], it's stuck to your hand!")) return FALSE + if (ishuman(source)) + var/mob/living/carbon/human/sourcehuman = source + if(HAS_TRAIT(sourcehuman, TRAIT_UNSTRIPPABLE) && !sourcehuman.is_mob_incapacitated()) + to_chat(src, SPAN_DANGER("[sourcehuman] is too strong to force [equipping] onto them!")) + return return TRUE /// Start the equipping process. This is the proc you should yield in. @@ -128,6 +135,10 @@ to_chat(user, SPAN_WARNING("You can't strip items of a crit or dead member of another faction!")) return FALSE + if(HAS_TRAIT(sourcehuman, TRAIT_UNSTRIPPABLE) && !sourcehuman.is_mob_incapacitated()) + to_chat(src, SPAN_DANGER("[sourcehuman] has an unbreakable grip on their equipment!")) + return + return TRUE /// Start the unequipping process. This is the proc you should yield in. diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 08daa5348022..2a197dadc2b1 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -399,23 +399,6 @@ bodytemperature = max(bodytemperature, BODYTEMP_HEAT_DAMAGE_LIMIT+10) recalculate_move_delay = TRUE - -/mob/living/carbon/show_inv(mob/living/carbon/user as mob) - user.set_interaction(src) - var/dat = {" -
[name]
-

-
Head(Mask): [(wear_mask ? wear_mask : "Nothing")] -
Left Hand: [(l_hand ? l_hand : "Nothing")] -
Right Hand: [(r_hand ? r_hand : "Nothing")] -
Back: [(back ? back : "Nothing")] [((istype(wear_mask, /obj/item/clothing/mask) && istype(back, /obj/item/tank) && !( internal )) ? " Set Internal" : "")] -
[(handcuffed ? "Handcuffed" : "Not Handcuffed")] -
[(internal ? "Remove Internal" : "")] -
Refresh -
Close -
"} - show_browser(user, dat, name, "mob[name]") - /** * Called by [/mob/dead/observer/proc/do_observe] when a carbon mob is observed by a ghost with [/datum/preferences/var/auto_observe] enabled. * diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 3f7b3955e878..3e2aa5477fe3 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -272,45 +272,6 @@ -/mob/living/carbon/human/show_inv(mob/living/user) - var/obj/item/clothing/under/suit = null - if(istype(w_uniform, /obj/item/clothing/under)) - suit = w_uniform - - user.set_interaction(src) - var/dat = {" -
[name]
-

-
(Exo)Suit: [(wear_suit ? wear_suit : "Nothing")] -
Suit Storage: [(s_store ? s_store : "Nothing")] [((istype(wear_mask, /obj/item/clothing/mask) && istype(s_store, /obj/item/tank) && !( internal )) ? " Set Internal" : "")] -
Back: [(back ? back : "Nothing")] [((istype(wear_mask, /obj/item/clothing/mask) && istype(back, /obj/item/tank) && !( internal )) ? " Set Internal" : "")] -
Head(Mask): [(wear_mask ? wear_mask : "Nothing")] -
Left Hand: [(l_hand ? l_hand : "Nothing")] -
Right Hand: [(r_hand ? r_hand : "Nothing")] -
Gloves: [(gloves ? gloves : "Nothing")] -
Eyes: [(glasses ? glasses : "Nothing")] -
Left Ear: [(wear_l_ear ? wear_l_ear : "Nothing")] -
Right Ear: [(wear_r_ear ? wear_r_ear : "Nothing")] -
Head: [(head ? head : "Nothing")] -
Shoes: [(shoes ? shoes : "Nothing")] -
Belt: [(belt ? belt : "Nothing")] [((istype(wear_mask, /obj/item/clothing/mask) && istype(belt, /obj/item/tank) && !internal) ? " Set Internal" : "")] -
Uniform: [(w_uniform ? w_uniform : "Nothing")] [(suit) ? ((suit.has_sensor == UNIFORM_HAS_SENSORS) ? " Sensors" : "") : null] -
ID: [(wear_id ? wear_id : "Nothing")] -
Left Pocket: [(l_store ? l_store : "Nothing")] -
Right Pocket: [(r_store ? r_store : "Nothing")] -
- [handcuffed ? "
Handcuffed" : ""] - [legcuffed ? "
Legcuffed" : ""] - [suit && LAZYLEN(suit.accessories) ? "
Remove Accessory" : ""] - [internal ? "
Remove Internal" : ""] - [istype(wear_id, /obj/item/card/id/dogtag) ? "
Retrieve Info Tag" : ""] -
Remove Splints -
-
Refresh -
Close -
"} - show_browser(user, dat, name, "mob[name]") - /** * Handles any storage containers that the human is looking inside when auto-observed. */ @@ -426,9 +387,6 @@ /mob/living/carbon/human/Topic(href, href_list) - if(href_list["refresh"]) - if(interactee&&(in_range(src, usr))) - show_inv(interactee) if(href_list["mach_close"]) var/t1 = text("window=[]", href_list["mach_close"]) @@ -473,76 +431,6 @@ what = usr.get_active_hand() usr.stripPanelEquip(what,src,slot) - if(href_list["internal"]) - - if(!usr.action_busy && !usr.is_mob_incapacitated() && Adjacent(usr)) - attack_log += text("\[[time_stamp()]\] Has had their internals toggled by [key_name(usr)]") - usr.attack_log += text("\[[time_stamp()]\] Attempted to toggle [key_name(src)]'s' internals") - if(internal) - usr.visible_message(SPAN_DANGER("[usr] is trying to disable [src]'s internals"), null, null, 3) - else - usr.visible_message(SPAN_DANGER("[usr] is trying to enable [src]'s internals."), null, null, 3) - - if(do_after(usr, POCKET_STRIP_DELAY, INTERRUPT_ALL, BUSY_ICON_GENERIC, src, INTERRUPT_MOVED, BUSY_ICON_GENERIC)) - if(internal) - internal.add_fingerprint(usr) - internal = null - visible_message("[src] is no longer running on internals.", null, null, 1) - else - if(istype(wear_mask, /obj/item/clothing/mask)) - if(istype(back, /obj/item/tank)) - internal = back - else if(istype(s_store, /obj/item/tank)) - internal = s_store - else if(istype(belt, /obj/item/tank)) - internal = belt - if(internal) - visible_message(SPAN_NOTICE("[src] is now running on internals."), null, null, 1) - internal.add_fingerprint(usr) - - // Update strip window - if(usr.interactee == src && Adjacent(usr)) - show_inv(usr) - - - if(href_list["splints"]) - if(!usr.action_busy && !usr.is_mob_incapacitated() && Adjacent(usr)) - if(MODE_HAS_TOGGLEABLE_FLAG(MODE_NO_STRIPDRAG_ENEMY) && (stat == DEAD || health < HEALTH_THRESHOLD_CRIT) && !get_target_lock(usr.faction_group)) - to_chat(usr, SPAN_WARNING("You can't strip a crit or dead member of another faction!")) - return - attack_log += text("\[[time_stamp()]\] Has had their splints removed by [key_name(usr)]") - usr.attack_log += text("\[[time_stamp()]\] Attempted to remove [key_name(src)]'s' splints ") - remove_splints(usr) - - if(href_list["tie"]) - if(!usr.action_busy && !usr.is_mob_incapacitated() && Adjacent(usr)) - if(MODE_HAS_TOGGLEABLE_FLAG(MODE_NO_STRIPDRAG_ENEMY) && (stat == DEAD || health < HEALTH_THRESHOLD_CRIT) && !get_target_lock(usr.faction_group)) - to_chat(usr, SPAN_WARNING("You can't strip a crit or dead member of another faction!")) - return - if(w_uniform && istype(w_uniform, /obj/item/clothing)) - var/obj/item/clothing/under/U = w_uniform - if(!LAZYLEN(U.accessories)) - return FALSE - var/obj/item/clothing/accessory/A = LAZYACCESS(U.accessories, 1) - if(LAZYLEN(U.accessories) > 1) - A = tgui_input_list(usr, "Select an accessory to remove from [U]", "Remove accessory", U.accessories) - if(!istype(A)) - return - attack_log += text("\[[time_stamp()]\] Has had their accessory ([A]) removed by [key_name(usr)]") - usr.attack_log += text("\[[time_stamp()]\] Attempted to remove [key_name(src)]'s' accessory ([A])") - if(istype(A, /obj/item/clothing/accessory/holobadge) || istype(A, /obj/item/clothing/accessory/medal)) - visible_message(SPAN_DANGER("[usr] tears off \the [A] from [src]'s [U]!"), null, null, 5) - if(U == w_uniform) - U.remove_accessory(usr, A) - else - if(HAS_TRAIT(src, TRAIT_UNSTRIPPABLE) && !is_mob_incapacitated()) //Can't strip the unstrippable! - to_chat(usr, SPAN_DANGER("[src] has an unbreakable grip on their equipment!")) - return - visible_message(SPAN_DANGER("[usr] is trying to take off \a [A] from [src]'s [U]!"), null, null, 5) - if(do_after(usr, get_strip_delay(usr, src), INTERRUPT_ALL, BUSY_ICON_GENERIC, src, INTERRUPT_MOVED, BUSY_ICON_GENERIC)) - if(U == w_uniform) - U.remove_accessory(usr, A) - if(href_list["sensor"]) if(!usr.action_busy && !usr.is_mob_incapacitated() && Adjacent(usr)) if(MODE_HAS_TOGGLEABLE_FLAG(MODE_NO_STRIPDRAG_ENEMY) && (stat == DEAD || health < HEALTH_THRESHOLD_CRIT) && !get_target_lock(usr.faction_group)) diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm index 3d372376d1e7..3f419333d218 100644 --- a/code/modules/mob/living/carbon/human/inventory.dm +++ b/code/modules/mob/living/carbon/human/inventory.dm @@ -504,70 +504,6 @@ /// Final result is overall delay * speed multiplier return target_delay * user_speed -/mob/living/carbon/human/stripPanelUnequip(obj/item/interact_item, mob/target_mob, slot_to_process) - if(HAS_TRAIT(target_mob, TRAIT_UNSTRIPPABLE) && !target_mob.is_mob_incapacitated()) //Can't strip the unstrippable! - to_chat(src, SPAN_DANGER("[target_mob] has an unbreakable grip on their equipment!")) - return - if(interact_item.flags_item & ITEM_ABSTRACT) - return - if(interact_item.flags_item & NODROP) - to_chat(src, SPAN_WARNING("You can't remove \the [interact_item.name], it appears to be stuck!")) - return - if(interact_item.flags_inventory & CANTSTRIP) - to_chat(src, SPAN_WARNING("You're having difficulty removing \the [interact_item.name].")) - return - target_mob.attack_log += "\[[time_stamp()]\] Has had their [interact_item.name] ([slot_to_process]) attempted to be removed by [key_name(src)]" - attack_log += "\[[time_stamp()]\] Attempted to remove [key_name(target_mob)]'s [interact_item.name] ([slot_to_process])" - log_interact(src, target_mob, "[key_name(src)] tried to remove [key_name(target_mob)]'s [interact_item.name] ([slot_to_process]).") - - src.visible_message(SPAN_DANGER("[src] tries to remove [target_mob]'s [interact_item.name]."), \ - SPAN_DANGER("You are trying to remove [target_mob]'s [interact_item.name]."), null, 5) - interact_item.add_fingerprint(src) - if(do_after(src, get_strip_delay(src, target_mob), INTERRUPT_ALL, BUSY_ICON_GENERIC, target_mob, INTERRUPT_MOVED, BUSY_ICON_GENERIC)) - if(interact_item && Adjacent(target_mob) && interact_item == target_mob.get_item_by_slot(slot_to_process)) - target_mob.drop_inv_item_on_ground(interact_item) - log_interact(src, target_mob, "[key_name(src)] removed [key_name(target_mob)]'s [interact_item.name] ([slot_to_process]) successfully.") - - if(target_mob) - if(interactee == target_mob && Adjacent(target_mob)) - target_mob.show_inv(src) - - -/mob/living/carbon/human/stripPanelEquip(obj/item/interact_item, mob/target_mob, slot_to_process) - if(HAS_TRAIT(target_mob, TRAIT_UNSTRIPPABLE) && !target_mob.is_mob_incapacitated()) - to_chat(src, SPAN_DANGER("[target_mob] is too strong to force [interact_item.name] onto them!")) - return - if(interact_item && !(interact_item.flags_item & ITEM_ABSTRACT)) - if(interact_item.flags_item & NODROP) - to_chat(src, SPAN_WARNING("You can't put \the [interact_item.name] on [target_mob], it's stuck to your hand!")) - return - if(interact_item.flags_inventory & CANTSTRIP) - to_chat(src, SPAN_WARNING("You're having difficulty putting \the [interact_item.name] on [target_mob].")) - return - if(interact_item.flags_item & WIELDED) - interact_item.unwield(src) - if(!interact_item.mob_can_equip(target_mob, slot_to_process, TRUE)) - to_chat(src, SPAN_WARNING("You can't put \the [interact_item.name] on [target_mob]!")) - return - visible_message(SPAN_NOTICE("[src] tries to put \the [interact_item.name] on [target_mob]."), null, null, 5) - log_interact(src, target_mob, "[key_name(src)] attempted to put [interact_item.name] on [key_name(target_mob)]'s ([slot_to_process]).") - if(do_after(src, get_strip_delay(src, target_mob), INTERRUPT_ALL, BUSY_ICON_GENERIC, target_mob, INTERRUPT_MOVED, BUSY_ICON_GENERIC)) - if(interact_item == get_active_hand() && !target_mob.get_item_by_slot(slot_to_process) && Adjacent(target_mob)) - if(interact_item.flags_item & WIELDED) //to prevent re-wielding it during the do_after - interact_item.unwield(src) - if(interact_item.mob_can_equip(target_mob, slot_to_process, TRUE))//Placing an item on the mob - drop_inv_item_on_ground(interact_item) - if(interact_item && !QDELETED(interact_item)) //Might be self-deleted? - target_mob.equip_to_slot_if_possible(interact_item, slot_to_process, 1, 0, 1, 1) - log_interact(src, target_mob, "[key_name(src)] put [interact_item.name] on [key_name(target_mob)]'s ([slot_to_process]) successfully.") - if(ishuman(target_mob) && target_mob.stat == DEAD) - var/mob/living/carbon/human/human_target = target_mob - human_target.disable_lights() // take that powergamers -spookydonut - - if(target_mob) - if(interactee == target_mob && Adjacent(target_mob)) - target_mob.show_inv(src) - /mob/living/carbon/human/drop_inv_item_on_ground(obj/item/I, nomoveupdate, force) remember_dropped_object(I) return ..() diff --git a/code/modules/mob/living/carbon/xenomorph/XenoProcs.dm b/code/modules/mob/living/carbon/xenomorph/XenoProcs.dm index 46b6c857d481..023c0a672621 100644 --- a/code/modules/mob/living/carbon/xenomorph/XenoProcs.dm +++ b/code/modules/mob/living/carbon/xenomorph/XenoProcs.dm @@ -262,9 +262,6 @@ move_delay = . -/mob/living/carbon/xenomorph/show_inv(mob/user) - return - /mob/living/carbon/xenomorph/proc/pounced_mob(mob/living/L) // This should only be called back by a mob that has pounce, so no need to check var/datum/action/xeno_action/activable/pounce/pounceAction = get_xeno_action_by_type(src, /datum/action/xeno_action/activable/pounce) diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index dcab9f70cbb5..14f220b3a77f 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -107,23 +107,6 @@ walk(src,0) . = ..() -/* - * Inventory - */ -/mob/living/simple_animal/parrot/show_inv(mob/user as mob) - user.set_interaction(src) - if(user.stat) return - - var/dat = "
Inventory of [name]

" - if(ears) - dat += "
Headset: [ears] (Remove)" - else - dat += "
Headset: Nothing" - - user << browse(dat, text("window=mob[];size=325x500", name)) - onclose(user, "mob[real_name]") - return - /mob/living/simple_animal/parrot/Topic(href, href_list) //Can the usr physically do this? diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 92943282437a..06e32bbcff32 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -370,25 +370,6 @@ SIGNAL_HANDLER reset_view(null) -/mob/proc/show_inv(mob/user) - user.set_interaction(src) - var/dat = {" -


[name] -

-
Head(Mask): [(wear_mask ? wear_mask : "Nothing")] -
Left Hand: [(l_hand ? l_hand : "Nothing")] -
Right Hand: [(r_hand ? r_hand : "Nothing")] -
Back: [(back ? back : "Nothing")] [((istype(wear_mask, /obj/item/clothing/mask) && istype(back, /obj/item/tank) && !( internal )) ? text(" Set Internal", src) : "")] -
[(internal ? text("Remove Internal") : "")] -
Empty Pockets -
Refresh -
Close -
"} - show_browser(user, dat, name, "mob[name]") - return - - - /mob/proc/point_to_atom(atom/A, turf/T) //Squad Leaders and above have reduced cooldown and get a bigger arrow if(check_improved_pointing()) @@ -446,21 +427,6 @@ update_flavor_text() return - -/mob/MouseDrop(mob/M) - ..() - if(M != usr) return - if(usr == src) return - if(!Adjacent(usr)) return - if(!ishuman(M) && !ismonkey(M)) return - if(!ishuman(src) && !ismonkey(src)) return - if(M.is_mob_incapacitated()) - return - if(M.pulling == src && (M.a_intent & INTENT_GRAB) && M.grab_level == GRAB_AGGRESSIVE) - return - - //show_inv(M) - /mob/proc/swap_hand() hand = !hand SEND_SIGNAL(src, COMSIG_MOB_SWAPPED_HAND) From 046f471641b1fccb941f549ec24f609a3c69f2e5 Mon Sep 17 00:00:00 2001 From: ihatethisengine Date: Sun, 10 Mar 2024 19:23:40 +0300 Subject: [PATCH 08/17] fix --- code/datums/elements/strippable.dm | 2 +- code/modules/mob/living/carbon/human/human.dm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/datums/elements/strippable.dm b/code/datums/elements/strippable.dm index 235deadb581f..d76ccae06ac1 100644 --- a/code/datums/elements/strippable.dm +++ b/code/datums/elements/strippable.dm @@ -19,7 +19,7 @@ if (!isatom(target)) return ELEMENT_INCOMPATIBLE - RegisterSignal(target, COMSIG_ATOM_DROP_ON, .proc/mouse_drop_onto) + RegisterSignal(target, COMSIG_ATOM_DROP_ON, PROC_REF(mouse_drop_onto)) src.items = items src.should_strip_proc_path = should_strip_proc_path diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 3e2aa5477fe3..e592e7ea1896 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -13,7 +13,7 @@ create_reagents(1000) if(!real_name || !name) change_real_name(src, "unknown") - AddElement(/datum/element/strippable, GLOB.strippable_human_items, /mob/living/carbon/human/.proc/should_strip) + AddElement(/datum/element/strippable, GLOB.strippable_human_items, TYPE_PROC_REF(/mob/living/carbon/human, should_strip)) . = ..() prev_gender = gender // Debug for plural genders From 52af408542b44c4feb749b9d98490dd92061f5a0 Mon Sep 17 00:00:00 2001 From: ihatethisengine Date: Sun, 10 Mar 2024 20:00:19 +0300 Subject: [PATCH 09/17] tgui --- tgui/packages/tgui/interfaces/StripMenu.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tgui/packages/tgui/interfaces/StripMenu.tsx b/tgui/packages/tgui/interfaces/StripMenu.tsx index 26baaaa64fb6..66dd934bc92a 100644 --- a/tgui/packages/tgui/interfaces/StripMenu.tsx +++ b/tgui/packages/tgui/interfaces/StripMenu.tsx @@ -215,7 +215,7 @@ type StripMenuItem = | { icon: string; name: string; - alternate?: string; + alternate: string; } | { obscured: ObscuringLevel; @@ -229,7 +229,7 @@ type StripMenuData = { }; export const StripMenu = (props, context) => { - const { act, data } = useBackend(context); + const { act, data } = useBackend(); const gridSpots = new Map(); for (const key of Object.keys(data.items)) { From 951ee114f66ec4f49ee81694ba04067aea714926 Mon Sep 17 00:00:00 2001 From: ihatethisengine Date: Sun, 10 Mar 2024 21:02:50 +0300 Subject: [PATCH 10/17] some cleanup --- code/datums/elements/strippable.dm | 6 ++-- .../living/carbon/human/human_stripping.dm | 30 +++++++++---------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/code/datums/elements/strippable.dm b/code/datums/elements/strippable.dm index d76ccae06ac1..d80a52f24b4a 100644 --- a/code/datums/elements/strippable.dm +++ b/code/datums/elements/strippable.dm @@ -294,8 +294,10 @@ if (!source.drop_inv_item_on_ground(item)) return FALSE - // source.log_message("[key_name(source)] has been stripped of [item] by [key_name(user)]", LOG_ATTACK, color="red") - // user.log_message("[key_name(source)] has been stripped of [item] by [key_name(user)]", LOG_ATTACK, color="red", log_globally=FALSE) + if (ismob(source)) + var/mob/sourcemob = source + sourcemob.attack_log += text("\[[time_stamp()]\] [key_name(sourcemob)] has been stripped of [item] by [key_name(user)]") + user.attack_log += text("\[[time_stamp()]\] [key_name(user)] has been stripped of [key_name(sourcemob)] of [item]") // Updates speed in case stripped speed affecting item source.recalculate_move_delay = TRUE diff --git a/code/modules/mob/living/carbon/human/human_stripping.dm b/code/modules/mob/living/carbon/human/human_stripping.dm index 9e97da264db8..6999adf94f4b 100644 --- a/code/modules/mob/living/carbon/human/human_stripping.dm +++ b/code/modules/mob/living/carbon/human/human_stripping.dm @@ -118,28 +118,28 @@ GLOBAL_LIST_INIT(strippable_human_items, create_strippable_list(list( to_chat(user, SPAN_WARNING("You can't strip a crit or dead member of another faction!")) return if(sourcemob.w_uniform && istype(sourcemob.w_uniform, /obj/item/clothing)) - var/obj/item/clothing/under/U = sourcemob.w_uniform - if(!LAZYLEN(U.accessories)) + var/obj/item/clothing/under/uniform = sourcemob.w_uniform + if(!LAZYLEN(uniform.accessories)) return FALSE - var/obj/item/clothing/accessory/A = LAZYACCESS(U.accessories, 1) - if(LAZYLEN(U.accessories) > 1) - A = tgui_input_list(user, "Select an accessory to remove from [U]", "Remove accessory", U.accessories) - if(!istype(A)) + var/obj/item/clothing/accessory/accessory = LAZYACCESS(uniform.accessories, 1) + if(LAZYLEN(uniform.accessories) > 1) + accessory = tgui_input_list(user, "Select an accessory to remove from [uniform]", "Remove accessory", uniform.accessories) + if(!istype(accessory)) return - sourcemob.attack_log += text("\[[time_stamp()]\] Has had their accessory ([A]) removed by [key_name(user)]") - user.attack_log += text("\[[time_stamp()]\] Attempted to remove [key_name(sourcemob)]'s' accessory ([A])") - if(istype(A, /obj/item/clothing/accessory/holobadge) || istype(A, /obj/item/clothing/accessory/medal)) - sourcemob.visible_message(SPAN_DANGER("[user] tears off \the [A] from [sourcemob]'s [U]!"), null, null, 5) - if(U == sourcemob.w_uniform) - U.remove_accessory(user, A) + sourcemob.attack_log += text("\[[time_stamp()]\] Has had their accessory ([accessory]) removed by [key_name(user)]") + user.attack_log += text("\[[time_stamp()]\] Attempted to remove [key_name(sourcemob)]'s' accessory ([accessory])") + if(istype(accessory, /obj/item/clothing/accessory/holobadge) || istype(accessory, /obj/item/clothing/accessory/medal)) + sourcemob.visible_message(SPAN_DANGER("[user] tears off \the [accessory] from [sourcemob]'s [uniform]!"), null, null, 5) + if(uniform == sourcemob.w_uniform) + uniform.remove_accessory(user, accessory) else if(HAS_TRAIT(sourcemob, TRAIT_UNSTRIPPABLE) && !sourcemob.is_mob_incapacitated()) //Can't strip the unstrippable! to_chat(user, SPAN_DANGER("[sourcemob] has an unbreakable grip on their equipment!")) return - sourcemob.visible_message(SPAN_DANGER("[user] is trying to take off \a [A] from [source]'s [U]!"), null, null, 5) + sourcemob.visible_message(SPAN_DANGER("[user] is trying to take off \a [accessory] from [source]'s [uniform]!"), null, null, 5) if(do_after(user, sourcemob.get_strip_delay(user, sourcemob), INTERRUPT_ALL, BUSY_ICON_GENERIC, sourcemob, INTERRUPT_MOVED, BUSY_ICON_GENERIC)) - if(U == sourcemob.w_uniform) - U.remove_accessory(user, A) + if(uniform == sourcemob.w_uniform) + uniform.remove_accessory(user, accessory) /datum/strippable_item/mob_item_slot/suit key = STRIPPABLE_ITEM_SUIT From 0683be66f40ccf814efade8b2f61e543c5c26ff7 Mon Sep 17 00:00:00 2001 From: ihatethisengine Date: Tue, 12 Mar 2024 22:14:31 +0300 Subject: [PATCH 11/17] split and type --- code/datums/elements/strippable.dm | 90 +++++++++++++-------- tgui/packages/tgui/interfaces/StripMenu.tsx | 33 ++++---- 2 files changed, 74 insertions(+), 49 deletions(-) diff --git a/code/datums/elements/strippable.dm b/code/datums/elements/strippable.dm index d80a52f24b4a..9b5632492739 100644 --- a/code/datums/elements/strippable.dm +++ b/code/datums/elements/strippable.dm @@ -393,7 +393,7 @@ var/mob/user = usr switch (action) - if ("use") + if ("equip") var/key = params["key"] var/datum/strippable_item/strippable_item = strippable.items[key] @@ -407,55 +407,75 @@ return var/item = strippable_item.get_item(owner) - if (isnull(item)) - var/obj/item/held_item = user.get_held_item() - if (isnull(held_item)) - return + if (!isnull(item)) + return - if (strippable_item.try_equip(owner, held_item, user)) - LAZYORASSOCLIST(interactions, user, key) + var/obj/item/held_item = user.get_held_item() + if (isnull(held_item)) + return + + if (!strippable_item.try_equip(owner, held_item, user)) + return - // Yielding call - var/should_finish = strippable_item.start_equip(owner, held_item, user) + LAZYORASSOCLIST(interactions, user, key) + + // Yielding call + var/should_finish = strippable_item.start_equip(owner, held_item, user) + + LAZYREMOVEASSOC(interactions, user, key) + + if (!should_finish) + return + + if (QDELETED(src) || QDELETED(owner)) + return + + // They equipped an item in the meantime + if (!isnull(strippable_item.get_item(owner))) + return + + if (!user.Adjacent(owner)) + return - LAZYREMOVEASSOC(interactions, user, key) + strippable_item.finish_equip(owner, held_item, user) + if ("strip") + var/key = params["key"] + var/datum/strippable_item/strippable_item = strippable.items[key] - if (!should_finish) - return + if (isnull(strippable_item)) + return - if (QDELETED(src) || QDELETED(owner)) - return + if (!strippable_item.should_show(owner, user)) + return - // They equipped an item in the meantime - if (!isnull(strippable_item.get_item(owner))) - return + if (strippable_item.get_obscuring(owner) == STRIPPABLE_OBSCURING_COMPLETELY) + return - if (!user.Adjacent(owner)) - return + var/item = strippable_item.get_item(owner) + if (isnull(item)) + return - strippable_item.finish_equip(owner, held_item, user) - else if (strippable_item.try_unequip(owner, user)) - LAZYORASSOCLIST(interactions, user, key) + LAZYORASSOCLIST(interactions, user, key) - var/should_unequip = strippable_item.start_unequip(owner, user) + var/should_unequip = strippable_item.start_unequip(owner, user) - LAZYREMOVEASSOC(interactions, user, key) + LAZYREMOVEASSOC(interactions, user, key) - // Yielding call - if (!should_unequip) - return + // Yielding call + if (!should_unequip) + return - if (QDELETED(src) || QDELETED(owner)) - return + if (QDELETED(src) || QDELETED(owner)) + return - // They changed the item in the meantime - if (strippable_item.get_item(owner) != item) - return + // They changed the item in the meantime + if (strippable_item.get_item(owner) != item) + return - if (!user.Adjacent(owner)) - return + if (!user.Adjacent(owner)) + return - strippable_item.finish_unequip(owner, user) + strippable_item.finish_unequip(owner, user) if ("alt") var/key = params["key"] var/datum/strippable_item/strippable_item = strippable.items[key] diff --git a/tgui/packages/tgui/interfaces/StripMenu.tsx b/tgui/packages/tgui/interfaces/StripMenu.tsx index 66dd934bc92a..f7ee684a9670 100644 --- a/tgui/packages/tgui/interfaces/StripMenu.tsx +++ b/tgui/packages/tgui/interfaces/StripMenu.tsx @@ -62,16 +62,15 @@ const ALTERNATE_ACTIONS: Record = { }, }; -const SLOTS: Record< - string, - { - displayName: string; - gridSpot: GridSpotKey; - image?: string; - additionalComponent?: JSX.Element; - hideEmpty?: boolean; - } -> = { +type Slot = { + displayName: string; + gridSpot: GridSpotKey; + image?: string; + additionalComponent?: JSX.Element; + hideEmpty?: boolean; +}; + +const SLOTS: Record = { glasses: { displayName: 'glasses', gridSpot: getGridSpotKey([0, 1]), @@ -224,7 +223,7 @@ type StripMenuItem = Partial); type StripMenuData = { - items: Record; + items: Record; name: string; }; @@ -325,9 +324,15 @@ export const StripMenu = (props, context) => { }}>