<commit_before>/// 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_MOUSEDROP_ONTO, .proc/mouse_drop_onto)

	src.items = items
	src.should_strip_proc_path = should_strip_proc_path

/datum/element/strippable/Detach(datum/source)
	. = ..()

	UnregisterSignal(source, COMSIG_MOUSEDROP_ONTO)

	if (!isnull(strip_menus))
		qdel(strip_menus[source])
		strip_menus -= source

/datum/element/strippable/proc/mouse_drop_onto(datum/source, atom/over, mob/user)
	SIGNAL_HANDLER

	if (user == source)
		return

	if (over != user)
		return

	// Cyborgs buckle people by dragging them onto them, unless in combat mode.
	if (iscyborg(user))
		var/mob/living/silicon/robot/cyborg_user = user
		if (!cyborg_user.combat_mode)
			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, /datum/.proc/ui_interact, user)

/// 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(SEND_SIGNAL(user, COMSIG_TRY_STRIP, source, equipping) & COMPONENT_CANT_STRIP)
		return FALSE

	if (HAS_TRAIT(equipping, TRAIT_NODROP))
		to_chat(user, span_warning("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)
	if (warn_dangerous_clothing && isclothing(source))
		var/obj/item/clothing/clothing = source
		if(clothing.clothing_flags & DANGEROUS_OBJECT)
			source.visible_message(
				span_danger("[user] tries to put [equipping] on [source]."),
				span_userdanger("[user] tries to put [equipping] on you."),
				ignored_mobs = user,
			)
		else
			source.visible_message(
				span_notice("[user] tries to put [equipping] on [source]."),
				span_notice("[user] tries to put [equipping] on you."),
				ignored_mobs = user,
			)

		if(ishuman(source))
			var/mob/living/carbon/human/victim_human = source
			if(victim_human.key && !victim_human.client) // AKA braindead
				if(victim_human.stat <= SOFT_CRIT && LAZYLEN(victim_human.afk_thefts) <= AFK_THEFT_MAX_MESSAGES)
					var/list/new_entry = list(list(user.name, "tried equipping you with [equipping]", world.time))
					LAZYADD(victim_human.afk_thefts, new_entry)

	to_chat(user, span_notice("You try to put [equipping] on [source]..."))

	var/log = "[key_name(source)] is having [equipping] put on them by [key_name(user)]"
	user.log_message(log, LOG_ATTACK, color="red")
	source.log_message(log, LOG_VICTIM, 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(SEND_SIGNAL(user, COMSIG_TRY_STRIP, source, item) & COMPONENT_CANT_STRIP)
			return FALSE
		var/mob/mob_source = source
		if (!item.canStrip(user, mob_source))
			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(
		span_warning("[user] tries to remove [source]'s [item]."),
		span_userdanger("[user] tries to remove your [item]."),
		ignored_mobs = user,
	)

	to_chat(user, span_danger("You try to remove [source]'s [item]..."))
	user.log_message("[key_name(source)] is being stripped of [item] by [key_name(user)]", LOG_ATTACK, color="red")
	source.log_message("[key_name(source)] is being stripped of [item] by [key_name(user)]", LOG_VICTIM, color="red", log_globally=FALSE)
	item.add_fingerprint(src)

	if(ishuman(source))
		var/mob/living/carbon/human/victim_human = source
		if(victim_human.key && !victim_human.client) // AKA braindead
			if(victim_human.stat <= SOFT_CRIT && LAZYLEN(victim_human.afk_thefts) <= AFK_THEFT_MAX_MESSAGES)
				var/list/new_entry = list(list(user.name, "tried unequipping your [item.name]", world.time))
				LAZYADD(victim_human.afk_thefts, new_entry)

	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)<commit_msg>Fixes errant "the" in visible messages for item stripping  (#62049)<commit_after>/// 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_MOUSEDROP_ONTO, .proc/mouse_drop_onto)

	src.items = items
	src.should_strip_proc_path = should_strip_proc_path

/datum/element/strippable/Detach(datum/source)
	. = ..()

	UnregisterSignal(source, COMSIG_MOUSEDROP_ONTO)

	if (!isnull(strip_menus))
		qdel(strip_menus[source])
		strip_menus -= source

/datum/element/strippable/proc/mouse_drop_onto(datum/source, atom/over, mob/user)
	SIGNAL_HANDLER

	if (user == source)
		return

	if (over != user)
		return

	// Cyborgs buckle people by dragging them onto them, unless in combat mode.
	if (iscyborg(user))
		var/mob/living/silicon/robot/cyborg_user = user
		if (!cyborg_user.combat_mode)
			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, /datum/.proc/ui_interact, user)

/// 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(SEND_SIGNAL(user, COMSIG_TRY_STRIP, source, equipping) & COMPONENT_CANT_STRIP)
		return FALSE

	if (HAS_TRAIT(equipping, TRAIT_NODROP))
		to_chat(user, span_warning("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)
	if (warn_dangerous_clothing && isclothing(source))
		var/obj/item/clothing/clothing = source
		if(clothing.clothing_flags & DANGEROUS_OBJECT)
			source.visible_message(
				span_danger("[user] tries to put [equipping] on [source]."),
				span_userdanger("[user] tries to put [equipping] on you."),
				ignored_mobs = user,
			)
		else
			source.visible_message(
				span_notice("[user] tries to put [equipping] on [source]."),
				span_notice("[user] tries to put [equipping] on you."),
				ignored_mobs = user,
			)

		if(ishuman(source))
			var/mob/living/carbon/human/victim_human = source
			if(victim_human.key && !victim_human.client) // AKA braindead
				if(victim_human.stat <= SOFT_CRIT && LAZYLEN(victim_human.afk_thefts) <= AFK_THEFT_MAX_MESSAGES)
					var/list/new_entry = list(list(user.name, "tried equipping you with [equipping]", world.time))
					LAZYADD(victim_human.afk_thefts, new_entry)

	to_chat(user, span_notice("You try to put [equipping] on [source]..."))

	var/log = "[key_name(source)] is having [equipping] put on them by [key_name(user)]"
	user.log_message(log, LOG_ATTACK, color="red")
	source.log_message(log, LOG_VICTIM, 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(SEND_SIGNAL(user, COMSIG_TRY_STRIP, source, item) & COMPONENT_CANT_STRIP)
			return FALSE
		var/mob/mob_source = source
		if (!item.canStrip(user, mob_source))
			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(
		span_warning("[user] tries to remove [source]'s [item.name]."),
		span_userdanger("[user] tries to remove your [item.name]]."),
		ignored_mobs = user,
	)

	to_chat(user, span_danger("You try to remove [source]'s [item]..."))
	user.log_message("[key_name(source)] is being stripped of [item] by [key_name(user)]", LOG_ATTACK, color="red")
	source.log_message("[key_name(source)] is being stripped of [item] by [key_name(user)]", LOG_VICTIM, color="red", log_globally=FALSE)
	item.add_fingerprint(src)

	if(ishuman(source))
		var/mob/living/carbon/human/victim_human = source
		if(victim_human.key && !victim_human.client) // AKA braindead
			if(victim_human.stat <= SOFT_CRIT && LAZYLEN(victim_human.afk_thefts) <= AFK_THEFT_MAX_MESSAGES)
				var/list/new_entry = list(list(user.name, "tried unequipping your [item.name]", world.time))
				LAZYADD(victim_human.afk_thefts, new_entry)

	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)
{'fraction_non_alphanumeric': 0.11660635303578609, 'fraction_numerical': 0.0005629272215520708, 'mean_word_length': 4.46157224418094, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5981837', 'n_tokens_mistral': 4699, 'n_tokens_neox': 4398, 'n_words': 1108}