diff --git a/code/__DEFINES/~skyrat_defines/lewd_defines.dm b/code/__DEFINES/~skyrat_defines/lewd_defines.dm
index 90711768d36..93d54149799 100644
--- a/code/__DEFINES/~skyrat_defines/lewd_defines.dm
+++ b/code/__DEFINES/~skyrat_defines/lewd_defines.dm
@@ -78,3 +78,20 @@
/area/centcom/holding/cafe/ruin/xenonest,\
/area/centcom/holding/cafe/beach,\
)
+
+/// Applied to a mob wearing gloves that should be passable for hand surgery (e.g. ball mittens).
+#define TRAIT_GLOVE_SURGERY_PASSTHROUGH "glove_surgery_passthrough"
+/// Trait source for ball_mittens_fumble component.
+#define MITTENS_FUMBLE_TRAIT "ball_mittens_fumble"
+
+
+/// Fired on a mob in attempt_pickup after mobility checks. Handler can set pickup_mods["delay"] or return COMPONENT_BLOCK_ITEM_PICKUP.
+#define COMSIG_LIVING_ITEM_ATTEMPT_PICKUP "living_item_attempt_pickup"
+#define COMPONENT_BLOCK_ITEM_PICKUP (1<<0)
+
+/// Fired on a mob when they interact with machinery, before interact() runs. Return COMPONENT_BLOCK_MACHINERY_INTERACT to block.
+#define COMSIG_MOB_MACHINERY_INTERACT "mob_machinery_interact"
+#define COMPONENT_BLOCK_MACHINERY_INTERACT (1<<1)
+
+/// Fired on a mob when attempt_pickup fails due to fail_chance in pickup_mods. (obj/item/item)
+#define COMSIG_LIVING_ITEM_PICKUP_FAILED "living_item_pickup_failed"
diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm
index 70cb1811034..b6e46ff6976 100644
--- a/code/game/machinery/_machinery.dm
+++ b/code/game/machinery/_machinery.dm
@@ -704,6 +704,10 @@
//Return a non FALSE value to interrupt attack_hand propagation to subtypes.
/obj/machinery/interact(mob/user)
+ // BUBBER EDIT ADDITION - allow components on the user to intercept or delay machinery interaction
+ if(SEND_SIGNAL(user, COMSIG_MOB_MACHINERY_INTERACT, src) & COMPONENT_BLOCK_MACHINERY_INTERACT)
+ return FALSE
+ // BUBBER EDIT ADDITION END
update_last_used(user)
return ..()
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index bfcf2ab4e59..301bfcc57d2 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -420,6 +420,8 @@
lefthand_file = SSgreyscale.GetColoredIconByType(greyscale_config_inhand_left, greyscale_colors)
if(greyscale_config_inhand_right)
righthand_file = SSgreyscale.GetColoredIconByType(greyscale_config_inhand_right, greyscale_colors)
+ // BUBBER EDIT ADDITION - Fire COMSIG_ATOM_UPDATED_ICON after all GAGS icons update so update_icon_updates_onmob can refresh worn overlays
+ SEND_SIGNAL(src, COMSIG_ATOM_UPDATED_ICON)
/obj/item/verb/move_to_top()
set name = "Move To Top"
@@ -574,6 +576,18 @@
if(!(user.mobility_flags & MOBILITY_PICKUP))
return
+ // BUBBER EDIT ADDITION - allow components on the user to inject a pickup delay or fail chance (e.g. ball mittens fumble)
+ var/list/pickup_mods = list("delay" = 0, "fail_chance" = 0)
+ if(SEND_SIGNAL(user, COMSIG_LIVING_ITEM_ATTEMPT_PICKUP, src, pickup_mods) & COMPONENT_BLOCK_ITEM_PICKUP)
+ return
+ if(pickup_mods["delay"])
+ if(!do_after(user, pickup_mods["delay"], src, timed_action_flags = IGNORE_HELD_ITEM))
+ return
+ if(pickup_mods["fail_chance"] && prob(pickup_mods["fail_chance"]))
+ SEND_SIGNAL(user, COMSIG_LIVING_ITEM_PICKUP_FAILED, src)
+ return
+ // BUBBER EDIT ADDITION END
+
if(!skip_grav)
//Heavy gravity makes picking up things very slow.
var/grav = user.has_gravity()
diff --git a/code/modules/mob/living/carbon/inventory.dm b/code/modules/mob/living/carbon/inventory.dm
index fcf292496f2..e5ebe9cb4fc 100644
--- a/code/modules/mob/living/carbon/inventory.dm
+++ b/code/modules/mob/living/carbon/inventory.dm
@@ -443,6 +443,10 @@
return covered_flags
/mob/living/carbon/is_location_accessible(location, exluded_equipment_slots = NONE)
+ // BUBBER EDIT ADDITION - ball mittens surgery passthrough
+ if(HAS_TRAIT(src, TRAIT_GLOVE_SURGERY_PASSTHROUGH))
+ exluded_equipment_slots |= ITEM_SLOT_GLOVES
+ // BUBBER EDIT ADDITION END
switch(location)
// Snowflake checks for these precise zones
if(BODY_ZONE_PRECISE_EYES)
diff --git a/code/modules/surgery/operations/_operation.dm b/code/modules/surgery/operations/_operation.dm
index 26d647f2472..12a3fe54988 100644
--- a/code/modules/surgery/operations/_operation.dm
+++ b/code/modules/surgery/operations/_operation.dm
@@ -470,7 +470,7 @@ GLOBAL_DATUM_INIT(operations, /datum/operation_holder, new)
/datum/surgery_operation/proc/check_availability(mob/living/patient, atom/movable/operating_on, mob/living/surgeon, tool, operated_zone)
SHOULD_NOT_OVERRIDE(TRUE)
SHOULD_NOT_SLEEP(TRUE)
- SHOULD_BE_PURE(TRUE)
+ // BUBBER EDIT REMOVAL - SHOULD_BE_PURE(TRUE) removed to allow clothing items to override is_location_accessible
if(isnull(operating_on))
return FALSE
diff --git a/icons/map_icons/clothing/_clothing.dmi b/icons/map_icons/clothing/_clothing.dmi
index 8dd4f7952e0..45438b87349 100644
Binary files a/icons/map_icons/clothing/_clothing.dmi and b/icons/map_icons/clothing/_clothing.dmi differ
diff --git a/modular_skyrat/modules/GAGS/greyscale_configs.dm b/modular_skyrat/modules/GAGS/greyscale_configs.dm
index 8e47adef44d..59bf969d9f3 100644
--- a/modular_skyrat/modules/GAGS/greyscale_configs.dm
+++ b/modular_skyrat/modules/GAGS/greyscale_configs.dm
@@ -1403,6 +1403,18 @@ TREK
name = "Catgloves Worn"
icon_file = 'modular_skyrat/modules/GAGS/icons/catglove_worn.dmi'
+
+/datum/greyscale_config/magpaws
+ name = "Magpaws"
+ icon_file = 'modular_skyrat/modules/GAGS/icons/magpaws.dmi'
+ json_config = 'modular_skyrat/modules/GAGS/json_configs/gloves/magpaws.json'
+
+/datum/greyscale_config/magpaws/worn
+ name = "Magpaws Worn"
+ icon_file = 'modular_skyrat/modules/GAGS/icons/magpaws.dmi'
+ json_config = 'modular_skyrat/modules/GAGS/json_configs/gloves/magpaws_worn.json'
+
+
// MISC SHOES
/datum/greyscale_config/heels
diff --git a/modular_skyrat/modules/GAGS/icons/catglove.dmi b/modular_skyrat/modules/GAGS/icons/catglove.dmi
index 1a0ad7234ea..fe8de214915 100644
Binary files a/modular_skyrat/modules/GAGS/icons/catglove.dmi and b/modular_skyrat/modules/GAGS/icons/catglove.dmi differ
diff --git a/modular_skyrat/modules/GAGS/icons/catglove_worn.dmi b/modular_skyrat/modules/GAGS/icons/catglove_worn.dmi
index 2b5d8708c2e..c7c0cec7b06 100644
Binary files a/modular_skyrat/modules/GAGS/icons/catglove_worn.dmi and b/modular_skyrat/modules/GAGS/icons/catglove_worn.dmi differ
diff --git a/modular_skyrat/modules/GAGS/icons/credit.md b/modular_skyrat/modules/GAGS/icons/credit.md
new file mode 100644
index 00000000000..9b9846c2139
--- /dev/null
+++ b/modular_skyrat/modules/GAGS/icons/credit.md
@@ -0,0 +1 @@
+catglove.cmi and catglove_worn.dmi by Online! (Discord) 🩵
\ No newline at end of file
diff --git a/modular_skyrat/modules/GAGS/icons/magpaws.dmi b/modular_skyrat/modules/GAGS/icons/magpaws.dmi
new file mode 100644
index 00000000000..13ed07e5f19
Binary files /dev/null and b/modular_skyrat/modules/GAGS/icons/magpaws.dmi differ
diff --git a/modular_skyrat/modules/GAGS/json_configs/gloves/catgloves.json b/modular_skyrat/modules/GAGS/json_configs/gloves/catgloves.json
index 217e9e5ba85..5b0b49f47fa 100644
--- a/modular_skyrat/modules/GAGS/json_configs/gloves/catgloves.json
+++ b/modular_skyrat/modules/GAGS/json_configs/gloves/catgloves.json
@@ -4,13 +4,25 @@
"type": "icon_state",
"icon_state": "catglove",
"blend_mode": "overlay",
- "color_ids": [ 1 ]
+ "color_ids": [
+ 1
+ ]
},
{
"type": "icon_state",
"icon_state": "catglove_paws",
"blend_mode": "overlay",
- "color_ids": [ 2 ]
+ "color_ids": [
+ 2
+ ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "catgloves_tag",
+ "blend_mode": "overlay",
+ "color_ids": [
+ 3
+ ]
}
]
}
diff --git a/modular_skyrat/modules/GAGS/json_configs/gloves/magpaws.json b/modular_skyrat/modules/GAGS/json_configs/gloves/magpaws.json
new file mode 100644
index 00000000000..422e9f01b2c
--- /dev/null
+++ b/modular_skyrat/modules/GAGS/json_configs/gloves/magpaws.json
@@ -0,0 +1,28 @@
+{
+ "magpaws": [
+ {
+ "type": "icon_state",
+ "icon_state": "GAGSpawnsMainOBJ",
+ "blend_mode": "overlay",
+ "color_ids": [
+ 1
+ ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "GAGgumdropsOBJ",
+ "blend_mode": "overlay",
+ "color_ids": [
+ 2
+ ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "GAGsymbolOBJ",
+ "blend_mode": "overlay",
+ "color_ids": [
+ 3
+ ]
+ }
+ ]
+}
diff --git a/modular_skyrat/modules/GAGS/json_configs/gloves/magpaws_worn.json b/modular_skyrat/modules/GAGS/json_configs/gloves/magpaws_worn.json
new file mode 100644
index 00000000000..7ca94ea5af8
--- /dev/null
+++ b/modular_skyrat/modules/GAGS/json_configs/gloves/magpaws_worn.json
@@ -0,0 +1,28 @@
+{
+ "magpaws_worn": [
+ {
+ "type": "icon_state",
+ "icon_state": "GAGspawsMainMOB",
+ "blend_mode": "overlay",
+ "color_ids": [
+ 1
+ ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "GAGpawscolorMainMOB",
+ "blend_mode": "overlay",
+ "color_ids": [
+ 2
+ ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "GaGsymbolMainMOB",
+ "blend_mode": "overlay",
+ "color_ids": [
+ 3
+ ]
+ }
+ ]
+}
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/clothing_pref_check.dm b/modular_skyrat/modules/modular_items/lewd_items/code/clothing_pref_check.dm
index ab865536af4..e005031601e 100644
--- a/modular_skyrat/modules/modular_items/lewd_items/code/clothing_pref_check.dm
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/clothing_pref_check.dm
@@ -13,7 +13,7 @@ GLOBAL_LIST_INIT(pref_checked_clothes, list(
/obj/item/clothing/suit/straight_jacket/kinky_sleepbag,
/obj/item/clothing/suit/straight_jacket/latex_straight_jacket,
/obj/item/clothing/gloves/ball_mittens,
- /obj/item/clothing/gloves/ball_mittens_reinforced,
+ /obj/item/clothing/gloves/ball_mittens/loadout_paw,
/obj/item/clothing/suit/straight_jacket/shackles,
/obj/item/clothing/suit/straight_jacket/shackles/reinforced,
/obj/item/clothing/gloves/shibari_hands,
@@ -23,7 +23,10 @@ GLOBAL_LIST_INIT(pref_checked_clothes, list(
/obj/item/clothing/mob_can_equip(mob/living/user, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE, ignore_equipped = FALSE, indirect_action)
if((slot_flags & slot) && (src.type in GLOB.pref_checked_clothes))
- if(!(user.client?.prefs?.read_preference(/datum/preference/toggle/erp/sex_toy)))
- return FALSE
+ // indirect_action = TRUE means this is an outfit/loadout equip, not a player action.
+ // Allow it through - the player selected this item in their loadout deliberately.
+ // Also allow when there is no client yet (round start character transfer not complete).
+ if(!indirect_action && user.client)
+ if(!(user.client?.prefs?.read_preference(/datum/preference/toggle/erp/sex_toy)))
+ return FALSE
return ..()
-
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/lewd_gloves.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/lewd_gloves.dm
index f5b247b60fc..97ca033d638 100644
--- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/lewd_gloves.dm
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/lewd_gloves.dm
@@ -1,40 +1,558 @@
-//normal ball mittens
+
+/datum/component/ball_mittens_fumble
+ dupe_mode = COMPONENT_DUPE_UNIQUE
+ var/struggle_delay_min = 0.8 SECONDS
+ var/interact_delay = 1.5 SECONDS
+ var/max_item_size = WEIGHT_CLASS_HUGE
+ var/cuff_resist_multiplier = 3
+ var/gun_spread_penalty = 12
+ var/is_interacting = FALSE
+ var/obj/item/tracked_cuffs = null
+
+/datum/component/ball_mittens_fumble/Initialize()
+ . = ..()
+ if(!istype(parent, /obj/item/clothing/gloves/ball_mittens))
+ return COMPONENT_INCOMPATIBLE
+ RegisterSignal(parent, COMSIG_ITEM_PRE_UNEQUIP, PROC_REF(on_pre_unequip))
+
+/datum/component/ball_mittens_fumble/proc/register_wearer(mob/living/wearer)
+ var/obj/item/clothing/gloves/ball_mittens/mittens = parent
+ if(mittens.is_paw_skin && isliving(wearer))
+ wearer.add_mood_event("paw_mittens", /datum/mood_event/wearing_paw_mittens)
+ if(!mittens.spawn_flavor_shown && mittens.loadout_created)
+ mittens.spawn_flavor_shown = TRUE
+ INVOKE_ASYNC(mittens, TYPE_PROC_REF(/obj/item/clothing/gloves/ball_mittens, deferred_spawn_flavor), wearer)
+ ADD_TRAIT(wearer, TRAIT_GLOVE_SURGERY_PASSTHROUGH, MITTENS_FUMBLE_TRAIT)
+ RegisterSignal(wearer, COMSIG_LIVING_ITEM_ATTEMPT_PICKUP, PROC_REF(on_attempt_pickup))
+ RegisterSignal(wearer, COMSIG_LIVING_ITEM_PICKUP_FAILED, PROC_REF(on_pickup_failed))
+ RegisterSignal(wearer, COMSIG_MOB_MACHINERY_INTERACT, PROC_REF(on_machinery_interact))
+ RegisterSignal(wearer, COMSIG_MOB_CLICKON, PROC_REF(on_clickon))
+ RegisterSignal(wearer, COMSIG_MOB_REMOVING_CUFFS, PROC_REF(on_removing_cuffs))
+ RegisterSignal(wearer, COMSIG_MOB_FIRED_GUN, PROC_REF(on_fired_gun))
+ RegisterSignal(wearer, COMSIG_TRY_STRIP, PROC_REF(on_try_strip))
+ RegisterSignal(wearer, COMSIG_MOUSEDROPPED_ONTO, PROC_REF(on_mousedrop_receive))
+ RegisterSignal(wearer, COMSIG_LIVING_UNARMED_ATTACK, PROC_REF(on_unarmed_attack))
+
+/datum/component/ball_mittens_fumble/proc/unregister_wearer(mob/living/wearer)
+ if(!isliving(wearer))
+ return
+ wearer.clear_mood_event("paw_mittens")
+ REMOVE_TRAIT(wearer, TRAIT_GLOVE_SURGERY_PASSTHROUGH, MITTENS_FUMBLE_TRAIT)
+ UnregisterSignal(wearer, list(
+ COMSIG_LIVING_ITEM_ATTEMPT_PICKUP,
+ COMSIG_LIVING_ITEM_PICKUP_FAILED,
+ COMSIG_MOB_MACHINERY_INTERACT,
+ COMSIG_MOB_CLICKON,
+ COMSIG_MOB_REMOVING_CUFFS,
+ COMSIG_MOB_FIRED_GUN,
+ COMSIG_TRY_STRIP,
+ COMSIG_LIVING_UNARMED_ATTACK,
+ COMSIG_MOUSEDROPPED_ONTO,
+ ))
+
+/datum/component/ball_mittens_fumble/proc/on_pre_unequip(datum/source, force)
+ SIGNAL_HANDLER
+ if(force)
+ return
+ var/obj/item/clothing/gloves/ball_mittens/mittens = parent
+ var/mob/living/wearer = mittens.loc
+ if(!isliving(wearer))
+ return
+ to_chat(wearer, span_purple("You struggle furiously with [mittens], but you're not even sure if these can come off."))
+ return COMPONENT_ITEM_BLOCK_UNEQUIP
+
+/datum/component/ball_mittens_fumble/proc/get_hand_descriptor(mob/living/wearer)
+ if(!iscarbon(wearer))
+ return "chunky mitts"
+ var/mob/living/carbon/human/human_wearer = wearer
+ var/obj/item/clothing/gloves/ball_mittens/mittens = human_wearer.gloves
+ if(istype(mittens) && mittens.is_paw_skin)
+ return "paws"
+ return "chunky mitts"
+
+/// Intercepts item pickups via attempt_pickup. Enforces the one-item limit and injects a short pickup delay.
+/datum/component/ball_mittens_fumble/proc/on_attempt_pickup(mob/living/wearer, obj/item/item, list/pickup_mods)
+ SIGNAL_HANDLER
+ if(!isgun(item) && item.w_class >= max_item_size)
+ pickup_mods["delay"] = struggle_delay_min * (item.w_class - max_item_size + 2)
+ pickup_mods["fail_chance"] = min(75, (item.w_class - max_item_size) * 25)
+ to_chat(wearer, span_warning("You wrestle with [item], trying to get it between your [get_hand_descriptor(wearer)]..."))
+ return
+ var/hand_desc = get_hand_descriptor(wearer)
+ var/player_msgs = list(
+ "You awkwardly wedge the [item] between your [hand_desc]...",
+ "You clumsily pin the [item] between your [hand_desc]...",
+ "You struggle to secure the [item] in your [hand_desc] before finally succeeding.",
+ "You carefully balance the [item] between your unwieldy [hand_desc]..."
+ )
+ to_chat(wearer, span_purple(pick(player_msgs)))
+ var/public_msg
+ if(isgun(item))
+ var/gun_msgs = list(
+ "[wearer] takes hold of the [item] in defiance of common sense.",
+ "[wearer] awkwardly presses the [item] between [wearer.p_their()] [hand_desc].",
+ "[wearer] picks up the [item], awkwardly pressing [wearer.p_their()] pads against the trigger guard!"
+ )
+ public_msg = pick(gun_msgs)
+ else
+ var/general_msgs = list(
+ "[wearer] awkwardly secures the [item] in [wearer.p_their()] [hand_desc].",
+ "[wearer] carefully grips the [item] with [wearer.p_their()] [hand_desc].",
+ "[wearer] almost drops [item] before managing to press it between [wearer.p_their()] [hand_desc].",
+ "[wearer] carefully balances the [item] between [wearer.p_their()] [hand_desc]."
+ )
+ public_msg = pick(general_msgs)
+ wearer.visible_message(span_warning(public_msg))
+ pickup_mods["delay"] = struggle_delay_min
+
+/datum/component/ball_mittens_fumble/proc/on_pickup_failed(mob/living/wearer, obj/item/item)
+ SIGNAL_HANDLER
+ var/hand_desc = get_hand_descriptor(wearer)
+ to_chat(wearer, span_warning("[item] slips out of your [hand_desc]!"))
+ playsound(wearer, 'modular_skyrat/modules/modular_items/lewd_items/sounds/latex.ogg', 30, TRUE)
+
+/// Handles machinery interaction delay via COMSIG_MOB_MACHINERY_INTERACT.
+/datum/component/ball_mittens_fumble/proc/on_machinery_interact(mob/living/wearer, obj/machinery/machine)
+ SIGNAL_HANDLER
+ if(is_interacting)
+ return
+ // Simple toggle/single-action machines trivial to operate with a paw - bypass delay
+ var/static/list/paw_passthrough = typecacheof(list(
+ /obj/machinery/button,
+ /obj/machinery/photobooth,
+ /obj/machinery/light_switch,
+ /obj/machinery/shower,
+ /obj/machinery/firealarm,
+ /obj/machinery/conveyor,
+ /obj/machinery/conveyor_switch,
+ /obj/machinery/grill,
+ /obj/machinery/oven,
+ /obj/machinery/food_cart,
+ ))
+ if(is_type_in_typecache(machine, paw_passthrough))
+ return
+ INVOKE_ASYNC(src, PROC_REF(fumble_interact), wearer, machine)
+ return COMPONENT_BLOCK_MACHINERY_INTERACT
+
+/datum/component/ball_mittens_fumble/proc/on_clickon(mob/living/wearer, atom/target, list/modifiers)
+ SIGNAL_HANDLER
+ if(wearer.throw_mode)
+ var/obj/item/held = wearer.get_active_held_item()
+ if(!held)
+ wearer.throw_mode_off(THROW_MODE_TOGGLE)
+ return COMSIG_MOB_CANCEL_CLICKON
+ if(prob(5))
+ to_chat(wearer, span_purple("You try to grip the [held] with your [get_hand_descriptor(wearer)] to throw it, but it clatters noisily to the floor instead."))
+ playsound(wearer, 'modular_skyrat/modules/modular_items/lewd_items/sounds/latex.ogg', 30, TRUE)
+ wearer.dropItemToGround(held)
+ wearer.throw_mode_off(THROW_MODE_TOGGLE)
+ return COMSIG_MOB_CANCEL_CLICKON
+ if(modifiers && (LAZYACCESS(modifiers, SHIFT_CLICK) || LAZYACCESS(modifiers, ALT_CLICK) || LAZYACCESS(modifiers, RIGHT_CLICK) || LAZYACCESS(modifiers, CTRL_CLICK)))
+ return
+ if(get_dist(wearer, target) > 1)
+ return
+ if(isclothing(target) && isliving(target.loc) && !wearer.get_active_held_item() && !wearer.combat_mode)
+ var/obj/item/clothing/cloth = target
+ var/mob/living/living_loc = target.loc
+ var/in_hand = (target in wearer.held_items)
+ var/truly_worn = (cloth.slot_flags && living_loc.get_item_by_slot(cloth.slot_flags) == cloth)
+ if(in_hand || truly_worn)
+ if(istype(cloth, /obj/item/clothing/gloves/ball_mittens))
+ INVOKE_ASYNC(cloth, TYPE_PROC_REF(/obj/item/clothing/gloves/ball_mittens, doStrip), wearer, wearer)
+ return COMSIG_MOB_CANCEL_CLICKON
+ INVOKE_ASYNC(src, PROC_REF(clothing_struggle), wearer, target)
+ return COMSIG_MOB_CANCEL_CLICKON
+
+/datum/component/ball_mittens_fumble/proc/clothing_struggle(mob/living/wearer, obj/item/clothing/cloth)
+ var/delay = 12 SECONDS
+ var/hand_desc = get_hand_descriptor(wearer)
+ to_chat(wearer, span_purple("You struggle to remove [cloth]. It's extremely difficult with your [hand_desc]... (This will take around [DisplayTimeText(delay)] and you need to stand still.)"))
+ if(!do_after(wearer, delay, cloth, timed_action_flags = IGNORE_HELD_ITEM))
+ to_chat(wearer, span_warning("You give up on [cloth]."))
+ return
+ if(QDELETED(src) || QDELETED(wearer) || QDELETED(cloth))
+ return
+ wearer.dropItemToGround(cloth)
+
+/datum/component/ball_mittens_fumble/proc/on_unarmed_attack(mob/living/wearer, atom/attack_target, proximity_flag, list/modifiers)
+ SIGNAL_HANDLER
+ if(!proximity_flag || !isliving(attack_target))
+ return
+ playsound(wearer, pick('sound/items/toy_squeak/toysqueak1.ogg', 'sound/items/toy_squeak/toysqueak2.ogg', 'sound/items/toy_squeak/toysqueak3.ogg'), 40, TRUE)
+
+/datum/component/ball_mittens_fumble/proc/on_mousedrop_receive(mob/living/wearer, atom/from, mob/user, params)
+ SIGNAL_HANDLER
+ if(user != wearer || !isitem(from))
+ return
+ var/obj/item/dragged = from
+ var/slot = wearer.get_slot_by_item(dragged)
+ if(!(slot & (ITEM_SLOT_BELT|ITEM_SLOT_BACK)))
+ return
+ INVOKE_ASYNC(src, PROC_REF(delayed_drag_unequip), wearer, dragged)
+ return COMPONENT_CANCEL_MOUSEDROPPED_ONTO
+
+/datum/component/ball_mittens_fumble/proc/delayed_drag_unequip(mob/living/wearer, obj/item/item)
+ var/hand_desc = get_hand_descriptor(wearer)
+ to_chat(wearer, span_purple("You carefully work at your [item] with your [hand_desc]..."))
+ if(!do_after(wearer, struggle_delay_min, item, timed_action_flags = IGNORE_HELD_ITEM))
+ to_chat(wearer, span_warning("You give up on [item]."))
+ return
+ if(QDELETED(src) || QDELETED(wearer) || QDELETED(item))
+ return
+ if(!wearer.get_slot_by_item(item))
+ return
+ wearer.put_in_hands(item)
+
+/datum/component/ball_mittens_fumble/proc/on_try_strip(mob/living/wearer, atom/target, obj/item/item)
+ SIGNAL_HANDLER
+ // COMSIG_TRY_STRIP and COMSIG_BEING_STRIPPED share the same signal string "try_strip".
+ // When item IS on the wearer: signal fired via COMSIG_BEING_STRIPPED - someone acting on the wearer - allow.
+ // When item is NOT on the wearer but in target's held_items: equip action onto the wearer - allow.
+ // When item is NOT on the wearer and NOT in target's hands: wearer is stripping from target - delay.
+ if(wearer.get_slot_by_item(item))
+ return
+ if(isliving(target))
+ var/mob/living/living_target = target
+ if(item in living_target.held_items)
+ return // Item in target's hands = equip action, not a strip
+ if(!isliving(target) || !isitem(item))
+ return COMPONENT_CANT_STRIP
+ to_chat(wearer, span_purple("You fumble awkwardly at [target]'s gear with your [get_hand_descriptor(wearer)], trying to find a grip..."))
+ INVOKE_ASYNC(src, PROC_REF(delayed_strip), wearer, target, item)
+ return COMPONENT_CANT_STRIP
+
+/datum/component/ball_mittens_fumble/proc/delayed_strip(mob/living/wearer, mob/living/target, obj/item/item)
+ if(!do_after(wearer, item.strip_delay * 2.5, target, timed_action_flags = IGNORE_HELD_ITEM))
+ to_chat(wearer, span_warning("You give up trying to remove [item] from [target]."))
+ return
+ if(QDELETED(src) || QDELETED(wearer) || QDELETED(target) || QDELETED(item))
+ return
+ item.doStrip(wearer, target)
+
+/datum/component/ball_mittens_fumble/proc/on_removing_cuffs(mob/living/wearer, obj/item/cuffs)
+ SIGNAL_HANDLER
+ if(!cuffs)
+ return
+ if(cuffs == tracked_cuffs)
+ return
+ tracked_cuffs = cuffs
+ var/original = cuffs.breakouttime
+ cuffs.breakouttime = original * cuff_resist_multiplier
+ addtimer(CALLBACK(src, PROC_REF(restore_breakouttime), cuffs, original), 1, TIMER_UNIQUE | TIMER_OVERRIDE)
+
+/datum/component/ball_mittens_fumble/proc/restore_breakouttime(obj/item/cuffs, original_time)
+ if(QDELETED(cuffs))
+ tracked_cuffs = null
+ return
+ cuffs.breakouttime = original_time
+ tracked_cuffs = null
+
+/datum/component/ball_mittens_fumble/proc/on_fired_gun(mob/living/wearer, obj/item/gun/gun, atom/target, params, zone_override, list/bonus_spread_values)
+ SIGNAL_HANDLER
+ if(!isgun(gun) || !islist(bonus_spread_values))
+ return
+ bonus_spread_values[MAX_BONUS_SPREAD_INDEX] += gun_spread_penalty
+
+/datum/component/ball_mittens_fumble/proc/fumble_interact(mob/living/wearer, obj/machinery/machine)
+ is_interacting = TRUE
+ var/hand_desc = get_hand_descriptor(wearer)
+ var/msg = istype(machine, /obj/machinery/vending) ? \
+ "You awkwardly mash your [hand_desc] against [machine]'s keypad..." : \
+ "You awkwardly paw at [machine] with your [hand_desc]..."
+ wearer.face_atom(machine)
+ to_chat(wearer, span_purple(msg))
+ wearer.visible_message(span_warning("[wearer] awkwardly paws at [machine] with [wearer.p_their()] [hand_desc], visibly struggling to use it."))
+ if(!do_after(wearer, interact_delay, machine))
+ to_chat(wearer, span_warning("You give up on [machine]."))
+ is_interacting = FALSE
+ return
+ if(QDELETED(src) || QDELETED(wearer) || QDELETED(machine) || get_dist(wearer, machine) > 1)
+ is_interacting = FALSE
+ return
+ machine.interact(wearer)
+ is_interacting = FALSE
+
+// ============================================================
+
+/datum/atom_skin/ball_mittens_skin
+ abstract_type = /datum/atom_skin/ball_mittens_skin
+ greyscale_item_path = /obj/item/clothing/gloves/ball_mittens/loadout_paw
+
+/datum/atom_skin/ball_mittens_skin/default
+ preview_name = "Ball Mittens"
+ reset_missing = TRUE
+
+/datum/atom_skin/ball_mittens_skin/cat_paws
+ preview_name = "Cat Paws"
+ new_name = "latex paw mittens"
+ new_desc = "A pair of inflatable latex mittens shaped like rounded paws. Helpless AND humiliating."
+ change_worn_icon_state = FALSE
+ greyscale_item_path = null
+ new_icon = 'icons/map_icons/clothing/_clothing.dmi'
+ new_icon_state = "/obj/item/clothing/gloves/ball_mittens/loadout_paw"
+ new_worn_icon = 'modular_skyrat/modules/GAGS/icons/catglove_worn.dmi'
+
+/datum/atom_skin/ball_mittens_skin/cat_paws/get_preview_icon(atom/for_atom)
+ return image(icon = 'icons/map_icons/clothing/_clothing.dmi', icon_state = "/obj/item/clothing/gloves/ball_mittens/loadout_paw")
+
+/datum/atom_skin/ball_mittens_skin/cat_paws/apply(atom/apply_to, mob/user)
+ var/obj/item/clothing/gloves/ball_mittens/mittens = apply_to
+ if(istype(mittens))
+ mittens.greyscale_config = /datum/greyscale_config/catgloves
+ mittens.greyscale_config_worn = /datum/greyscale_config/catgloves/worn
+ if(!mittens.greyscale_colors || mittens.greyscale_colors == "")
+ mittens.greyscale_colors = "#242329#7B48A6#15B1BF"
+ mittens.flags_1 |= IS_PLAYER_COLORABLE_1
+ . = ..()
+ if(istype(mittens))
+ mittens.is_paw_skin = TRUE
+ mittens.worn_icon = 'modular_skyrat/modules/GAGS/icons/catglove_worn.dmi'
+ mittens.icon_state = "catgloves"
+ mittens.worn_icon_state = "catgloves"
+ mittens.set_greyscale(mittens.greyscale_colors, /datum/greyscale_config/catgloves)
+
+/datum/atom_skin/ball_mittens_skin/cat_paws/clear_skin(atom/clear_from)
+ var/obj/item/clothing/gloves/ball_mittens/mittens = clear_from
+ if(istype(mittens))
+ mittens.is_paw_skin = FALSE
+ mittens.greyscale_config = null
+ mittens.greyscale_config_worn = null
+ mittens.flags_1 &= ~IS_PLAYER_COLORABLE_1
+ mittens.icon_state = initial(mittens.icon_state)
+ mittens.worn_icon_state = initial(mittens.worn_icon_state)
+ mittens.worn_icon = initial(mittens.worn_icon)
+ . = ..()
+ if(istype(mittens))
+ mittens.update_appearance()
+
+// ============================================================
+
/obj/item/clothing/gloves/ball_mittens
name = "ball mittens"
- desc = "A nice, comfortable pair of inflatable ball gloves."
+ desc = "A pair of inflatable latex mittens. Adorable and comfortable, but completely useless for anything requiring fingers. Getting these off yourself is a serious ordeal — you'll probably want help."
icon_state = "ballmittens"
- inhand_icon_state = null
+ inhand_icon_state = "" // Explicitly empty - null can cause BYOND to fall back to the main icon
icon = 'modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_clothing/lewd_gloves.dmi'
worn_icon = 'modular_skyrat/modules/modular_items/lewd_items/icons/mob/lewd_clothing/lewd_gloves.dmi'
- breakouttime = 1 SECONDS
+ strip_delay = 8 SECONDS
+ resistance_flags = FIRE_PROOF
+ armor_type = /datum/armor/ball_mittens
+ equip_sound = 'modular_zubbers/sound/lewd/rubber1.ogg'
+ drop_sound = 'modular_zubbers/sound/lewd/rubber2.ogg'
+ pickup_sound = 'modular_zubbers/sound/lewd/rubber3.ogg'
+ var/is_paw_skin = FALSE
+ var/spawn_flavor_shown = FALSE
+ var/loadout_created = FALSE
+ var/lights_on = FALSE
-//That part allows reinforcing this item with handcuffs
-/obj/item/clothing/gloves/ball_mittens/attackby(obj/item/attacking_item, mob/user, params)
+/obj/item/clothing/gloves/ball_mittens/Initialize(mapload)
. = ..()
- if(.)
+ AddElement(/datum/element/update_icon_updates_onmob)
+
+/obj/item/clothing/gloves/ball_mittens/equipped(mob/user, slot)
+ . = ..()
+ if(slot != ITEM_SLOT_GLOVES)
return
- if(!istype(attacking_item, /obj/item/restraints/handcuffs))
+ if(is_paw_skin)
+ to_chat(user, span_purple("The [src] seal around your hands. You pull at them and find it completely impossible to remove them..."))
+ else
+ to_chat(user, span_purple("Your hands sink into [src]. Soft, round, and not particularly good at anything. As soon as you put them on, you hear them self inflate. Oh shit..."))
+ RegisterSignal(src, COMSIG_OBJ_RESKIN, PROC_REF(on_reskin))
+ var/datum/component/ball_mittens_fumble/comp = AddComponent(/datum/component/ball_mittens_fumble)
+ comp.register_wearer(user)
+
+/obj/item/clothing/gloves/ball_mittens/dropped(mob/user)
+ . = ..()
+ UnregisterSignal(src, COMSIG_OBJ_RESKIN)
+ var/datum/component/ball_mittens_fumble/comp = GetComponent(/datum/component/ball_mittens_fumble)
+ comp?.unregister_wearer(user)
+ qdel(comp)
+
+/obj/item/clothing/gloves/ball_mittens/proc/on_reskin(datum/source, skin_name)
+ SIGNAL_HANDLER
+ is_paw_skin = (skin_name == "Cat Paws")
+ var/mob/living/wearer = loc
+ if(!isliving(wearer))
return
- var/obj/item/clothing/gloves/ball_mittens_reinforced/reinforced_muffs = new
- remove_item_from_storage(user)
- user.put_in_hands(reinforced_muffs)
- to_chat(user, span_notice("You reinforced the belts on [src] with [attacking_item]."))
- qdel(attacking_item)
- qdel(src)
+ if(is_paw_skin)
+ wearer.add_mood_event("paw_mittens", /datum/mood_event/wearing_paw_mittens)
+ to_chat(wearer, span_purple("The nanite-infused rubber shifts your hands into soft, rounded paw shapes."))
+ else
+ wearer.clear_mood_event("paw_mittens")
+ to_chat(wearer, span_purple("The mittens ease back into their round ball shape. Still completely useless."))
+
+/// Opens the GAGS recolor menu for spray can use on paw mittens.
+/// gags_recolorable.open_ui() uses initial() on greyscale_config which returns null
+/// for runtime-assigned configs, so we open the menu with the live config directly.
+/obj/item/clothing/gloves/ball_mittens/proc/paw_spray_interact(mob/living/user, obj/item/tool, list/modifiers)
+ if(!istype(tool, /obj/item/toy/crayon/spraycan))
+ return NONE
+ var/obj/item/toy/crayon/spraycan/can = tool
+ if(can.is_capped)
+ user.balloon_alert(user, "take the cap off first!")
+ return ITEM_INTERACT_BLOCKING
+ if(can.check_empty())
+ user.balloon_alert(user, "empty!")
+ return ITEM_INTERACT_BLOCKING
+ if(!user.client)
+ return ITEM_INTERACT_BLOCKING
+ var/list/allowed = list("[/datum/greyscale_config/catgloves]", "[/datum/greyscale_config/catgloves/worn]")
+ var/datum/greyscale_modify_menu/spray_paint/menu = new(
+ src, user.client, allowed,
+ CALLBACK(src, PROC_REF(apply_spray_colors), can),
+ "catgloves",
+ /datum/greyscale_config/catgloves,
+ greyscale_colors,
+ can,
+ )
+ menu.ui_interact(user)
+ return ITEM_INTERACT_SUCCESS
+
+/obj/item/clothing/gloves/ball_mittens/proc/apply_spray_colors(obj/item/toy/crayon/spraycan/can, datum/greyscale_modify_menu/menu)
+ if(QDELETED(src) || QDELETED(can) || QDELETED(menu))
+ return
+ if(can.is_capped || can.check_empty())
+ menu.ui_close()
+ return
+ can.use_charges()
+ if(can.pre_noise)
+ audible_message(span_hear("You hear spraying."))
+ playsound(loc, 'sound/effects/spray.ogg', 50, TRUE, 5)
+ set_greyscale(menu.split_colors)
+
+/obj/item/clothing/gloves/ball_mittens/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(is_paw_skin && istype(tool, /obj/item/toy/crayon/spraycan))
+ return paw_spray_interact(user, tool, modifiers)
+ return ..()
+
+/obj/item/clothing/gloves/ball_mittens/update_overlays()
+ . = ..()
+ if(lights_on && is_paw_skin)
+ . += emissive_appearance(icon, icon_state, src, alpha = 100)
+
+/obj/item/clothing/gloves/ball_mittens/worn_overlays(mutable_appearance/standing, isinhands, icon_file)
+ . = ..()
+ if(lights_on && is_paw_skin)
+ . += emissive_appearance(standing.icon, standing.icon_state, src, alpha = 100)
+
+/obj/item/clothing/gloves/ball_mittens/examine(mob/user)
+ . = ..()
+ if(is_paw_skin)
+ . += span_notice("There's a paw-friendly switch on the cuff. It's currently [lights_on ? "ON" : "OFF"]. \[Toggle\]")
+
+/obj/item/clothing/gloves/ball_mittens/Topic(href, href_list)
+ . = ..()
+ if(href_list["toggle_lights"])
+ if(!usr || !ishuman(usr))
+ return
+ var/mob/living/carbon/human/toggler = usr
+ if(toggler.gloves != src)
+ return
+ lights_on = !lights_on
+ playsound(src, 'sound/machines/click.ogg', 30, FALSE)
+ to_chat(usr, span_notice("You turn the accent lighting [lights_on ? "on" : "off"]."))
+ update_appearance()
+
+/obj/item/clothing/gloves/ball_mittens/proc/deferred_spawn_flavor(mob/user)
+ if(user.client)
+ show_spawn_flavor(user)
+ return
+ RegisterSignal(user, COMSIG_MOB_LOGIN, PROC_REF(on_client_login_flavor))
+
+/obj/item/clothing/gloves/ball_mittens/proc/on_client_login_flavor(mob/user)
+ SIGNAL_HANDLER
+ UnregisterSignal(user, COMSIG_MOB_LOGIN)
+ show_spawn_flavor(user)
+
+/datum/armor/ball_mittens
+ bio = 1
+
+/obj/item/clothing/gloves/ball_mittens/proc/show_spawn_flavor(mob/user)
+ if(!user?.client)
+ return
+ if(is_paw_skin)
+ to_chat(user, span_purple("You look down at your paws. Round and soft and utterly useless. You blush as you think about how difficult this is going to make your day at work."))
+ else
+ to_chat(user, span_purple("You look down at [src]. Why did I come to work like this?"))
+
+/obj/item/clothing/gloves/ball_mittens/attackby(obj/item/item, mob/living/user, params)
+ if(!istype(item, /obj/item/clothing/gloves/color/yellow))
+ return ..()
+ if(siemens_coefficient == 0)
+ to_chat(user, span_warning("[src] are already insulated."))
+ return
+ user.visible_message(
+ span_notice("[user] holds [item] up to [src] with a look of intense concentration, then discards it with a sigh."),
+ span_notice("You press [item] against [src], attempting to combine them. Halfway through you realize latex is, in fact, already an insulator. You throw away the insulated gloves.")
+ )
+ siemens_coefficient = 0
+ name = "insulated [name]"
+ if(desc == initial(desc))
+ desc = "A pair of inflatable latex mittens. Someone has helpfully applied insulated gloves to them, only to realise too late that latex was already an insulator."
+ qdel(item)
+ update_appearance()
return TRUE
-//ball_mittens reinforced
-/obj/item/clothing/gloves/ball_mittens_reinforced //We getting this item by using handcuffs on normal ball mittens
- name = "reinforced ball mittens"
- desc = "Do not put these on, it's REALLY hard to take them off! But they look so comfortable..."
- icon_state = "ballmittens"
- inhand_icon_state = null
- icon = 'modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_clothing/lewd_gloves.dmi'
- worn_icon = 'modular_skyrat/modules/modular_items/lewd_items/icons/mob/lewd_clothing/lewd_gloves.dmi'
- clothing_flags = DANGEROUS_OBJECT
- breakouttime = 100 SECONDS //do not touch this, i beg you.
+/obj/item/clothing/gloves/ball_mittens/doStrip(mob/stripper, mob/owner)
+ if(stripper != owner)
+ if(!owner.dropItemToGround(src, force = TRUE))
+ return FALSE
+ if(HAS_TRAIT(stripper, TRAIT_STICKY_FINGERS))
+ stripper.put_in_hands(src)
+ return TRUE
+ var/delay = 1 MINUTES
+ to_chat(owner, span_purple("You attempt to remove [src]... (This will take around [DisplayTimeText(delay)] and you need to stand still.)"))
+ to_chat(owner, span_purple("You struggle furiously with [src], but you're not even sure if these can come off."))
+ playsound(owner, pick('modular_zubbers/sound/lewd/rubber1.ogg', 'modular_zubbers/sound/lewd/rubber2.ogg', 'modular_zubbers/sound/lewd/rubber3.ogg'), 40, TRUE)
+ if(!do_after(owner, delay, src, timed_action_flags = IGNORE_HELD_ITEM))
+ to_chat(owner, span_purple("You give up trying to escape [src]. Maybe having [src] isn't so bad..."))
+ return FALSE
+ if(QDELETED(src) || !isliving(loc))
+ return FALSE
+ if(!owner.dropItemToGround(src, force = TRUE))
+ return FALSE
+ if(HAS_TRAIT(stripper, TRAIT_STICKY_FINGERS))
+ stripper.put_in_hands(src)
+ return TRUE
+
+// ============================================================
+// Loadout subtype for paw mittens.
+// ============================================================
+
+/obj/item/clothing/gloves/ball_mittens/loadout_paw
+ name = "latex paw mittens"
+ desc = "A pair of inflatable latex mittens shaped like rounded paws. Helpless AND humiliating."
+ greyscale_config = /datum/greyscale_config/catgloves
+ greyscale_config_worn = /datum/greyscale_config/catgloves/worn
+ greyscale_colors = "#242329#7B48A6#15B1BF"
+ flags_1 = IS_PLAYER_COLORABLE_1
+ worn_icon = 'modular_skyrat/modules/GAGS/icons/catglove_worn.dmi'
+ worn_icon_state = "catgloves"
+ icon_state = "BasePaws"
+ post_init_icon_state = "catgloves"
+ icon_preview = 'icons/map_icons/clothing/_clothing.dmi'
+ icon_state_preview = "/obj/item/clothing/gloves/ball_mittens/loadout_paw"
+ is_paw_skin = TRUE
+ inhand_icon_state = "greyscale_gloves"
+ alternate_worn_layer = SHOES_LAYER // Ensures paws render above mech_suit which claims GLOVES_LAYER
+
+/obj/item/clothing/gloves/ball_mittens/loadout_paw/Initialize(mapload)
+ . = ..()
+ loadout_created = TRUE
+ for(var/datum/component/reskinable_item/reskin_comp in GetComponents(/datum/component/reskinable_item))
+ qdel(reskin_comp)
+
+/obj/item/clothing/gloves/ball_mittens/loadout_paw/equipped(mob/user, slot)
+ . = ..()
+ if(slot != ITEM_SLOT_GLOVES)
+ return
+ update_greyscale()
+
+// ============================================================
+
+/datum/mood_event/wearing_paw_mittens
+ description = span_purple("So-ft... Ro-und... Use-less... Lo-ve it!\n")
-//latex gloves
/obj/item/clothing/gloves/latex_gloves
name = "latex gloves"
desc = "Awesome looking gloves that are satisfying to the touch."
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_helpers/human.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_helpers/human.dm
index a7253d276ef..0d842d7f221 100644
--- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_helpers/human.dm
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_helpers/human.dm
@@ -357,15 +357,6 @@
* MISC LOGIC
*/
-// Handles breaking out of gloves that restrain people.
-/mob/living/carbon/human/resist_restraints()
- if(gloves?.breakouttime)
- changeNext_move(CLICK_CD_BREAKOUT)
- last_special = world.time + CLICK_CD_BREAKOUT
- cuff_resist(gloves)
- else
- ..()
-
/// Checks if the human is wearing a condom, and also hasn't broken it.
/mob/living/carbon/human/proc/is_wearing_condom()
if(!penis || !istype(penis, /obj/item/clothing/sextoy/condom))
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/_erp_disabled_item_enforcement.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/_erp_disabled_item_enforcement.dm
index 0b5155e6cd3..fd68b0c3118 100644
--- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/_erp_disabled_item_enforcement.dm
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/_erp_disabled_item_enforcement.dm
@@ -77,8 +77,9 @@
. = ..()
if(CONFIG_GET(flag/disable_lewd_items))
return INITIALIZE_HINT_QDEL
+ AddComponent(/datum/component/reskinable_item, /datum/atom_skin/ball_mittens_skin, FALSE)
-/obj/item/clothing/gloves/ball_mittens_reinforced/Initialize(mapload)
+/obj/item/clothing/gloves/ball_mittens/loadout_paw/Initialize(mapload)
. = ..()
if(CONFIG_GET(flag/disable_lewd_items))
return INITIALIZE_HINT_QDEL
@@ -237,8 +238,3 @@
. = ..()
if(CONFIG_GET(flag/disable_lewd_items))
return INITIALIZE_HINT_QDEL
-
-/obj/item/toy/plush/aeri/Initialize(mapload)
- . = ..()
- if(CONFIG_GET(flag/disable_lewd_items))
- return INITIALIZE_HINT_QDEL
diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_machinery/lustwish.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_machinery/lustwish.dm
index bf075cf3e9e..34ec651693e 100644
--- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_machinery/lustwish.dm
+++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_machinery/lustwish.dm
@@ -124,6 +124,7 @@
//hands
/obj/item/clothing/gloves/ball_mittens = 8,
+ /obj/item/clothing/gloves/ball_mittens/loadout_paw = 5,
),
),
list(
diff --git a/modular_zubbers/code/_globalvars/lists/erp_items.dm b/modular_zubbers/code/_globalvars/lists/erp_items.dm
index 20a9a4be496..cd9795ad789 100644
--- a/modular_zubbers/code/_globalvars/lists/erp_items.dm
+++ b/modular_zubbers/code/_globalvars/lists/erp_items.dm
@@ -6,7 +6,7 @@ GLOBAL_LIST_INIT(erp_items, list(
/obj/item/clothing/glasses/blindfold/kinky,
/obj/item/clothing/glasses/hypno,
/obj/item/clothing/gloves/ball_mittens,
- /obj/item/clothing/gloves/ball_mittens_reinforced,
+ /obj/item/clothing/gloves/ball_mittens/loadout_paw,
/obj/item/clothing/head/domina_cap,
/obj/item/clothing/mask/ballgag,
/obj/item/clothing/mask/gas/bdsm_mask,
diff --git a/modular_zubbers/code/modules/clothing/gloves/misc.dm b/modular_zubbers/code/modules/clothing/gloves/misc.dm
index 7eb04b29d49..215d6ccd81f 100644
--- a/modular_zubbers/code/modules/clothing/gloves/misc.dm
+++ b/modular_zubbers/code/modules/clothing/gloves/misc.dm
@@ -15,17 +15,86 @@
//Cat Gloves seemingly by Taomayo of MonkeStation
/obj/item/clothing/gloves/cat
- desc = "hewwo everynyaan!!"
+ desc = "\"High Tech\" paw shaped gloves perfect for cosplay enthusiasts, streamers and general weirdos. hewwo everynyaan!!"
name = "cat gloves"
icon = 'icons/map_icons/clothing/_clothing.dmi'
icon_state = "/obj/item/clothing/gloves/cat"
post_init_icon_state = "catgloves"
flags_1 = IS_PLAYER_COLORABLE_1
- greyscale_colors = "#ffffff#FFC0CB"
+ greyscale_colors = "#ffffff#FFC0CB#B0EAF6"
greyscale_config_worn = /datum/greyscale_config/catgloves/worn
greyscale_config = /datum/greyscale_config/catgloves
greyscale_config_inhand_left = null
greyscale_config_inhand_right = null
+ resistance_flags = FIRE_PROOF
+ var/lights_on = FALSE
+
+/obj/item/clothing/gloves/cat/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/update_icon_updates_onmob)
+
+/obj/item/clothing/gloves/cat/equipped(mob/user, slot)
+ . = ..()
+ if(slot == ITEM_SLOT_GLOVES)
+ ADD_TRAIT(user, TRAIT_GLOVE_SURGERY_PASSTHROUGH, "cat_gloves")
+
+/obj/item/clothing/gloves/cat/dropped(mob/user)
+ . = ..()
+ REMOVE_TRAIT(user, TRAIT_GLOVE_SURGERY_PASSTHROUGH, "cat_gloves")
+
+/obj/item/clothing/gloves/cat/update_overlays()
+ . = ..()
+ if(lights_on)
+ . += emissive_appearance(icon, icon_state, src, alpha = 100)
+
+/obj/item/clothing/gloves/cat/worn_overlays(mutable_appearance/standing, isinhands, icon_file)
+ . = ..()
+ if(lights_on)
+ . += emissive_appearance(standing.icon, standing.icon_state, src, alpha = 100)
+
+/obj/item/clothing/gloves/cat/examine(mob/user)
+ . = ..()
+ . += span_notice("There's a small switch on the wrist. It's currently [lights_on ? "ON" : "OFF"]. \[Toggle\]")
+
+/obj/item/clothing/gloves/cat/Topic(href, href_list)
+ . = ..()
+ if(href_list["toggle_lights"])
+ if(!usr || !ishuman(usr))
+ return
+ var/mob/living/carbon/human/toggler = usr
+ if(toggler.gloves != src)
+ return
+ lights_on = !lights_on
+ playsound(src, 'sound/machines/click.ogg', 30, FALSE)
+ to_chat(usr, span_notice("You turn the accent lighting [lights_on ? "on" : "off"]."))
+ update_appearance()
+
+/obj/item/clothing/gloves/cat/attackby(obj/item/item, mob/living/user, params)
+ if(!istype(item, /obj/item/clothing/gloves/color/yellow))
+ return ..()
+ if(siemens_coefficient == 0)
+ to_chat(user, span_warning("[src] are already insulated."))
+ return
+ user.visible_message(
+ span_notice("[user] awkwardly crams [item] inside [src]."),
+ span_notice("You press [item] against [src], after some time you succeed at inserting [item] inside.")
+ )
+ siemens_coefficient = 0
+ name = "insulated [name]"
+ if(desc == initial(desc))
+ desc = "A pair of cat gloves with a pair of insulated gloves awkwardly crammed inside them. Somehow this works."
+ qdel(item)
+ update_appearance()
+ return TRUE
+
+/obj/item/clothing/gloves/cat/equipped(mob/user, slot)
+ . = ..()
+ if(slot == ITEM_SLOT_GLOVES)
+ ADD_TRAIT(user, TRAIT_GLOVE_SURGERY_PASSTHROUGH, "cat_gloves")
+
+/obj/item/clothing/gloves/cat/dropped(mob/user)
+ . = ..()
+ REMOVE_TRAIT(user, TRAIT_GLOVE_SURGERY_PASSTHROUGH, "cat_gloves")
//Metrocop Gloves by ... Dun dun dun, HL13 station.
diff --git a/modular_zubbers/code/modules/loadout/categories/gloves.dm b/modular_zubbers/code/modules/loadout/categories/gloves.dm
index 1c4e067803d..5e7534286dd 100644
--- a/modular_zubbers/code/modules/loadout/categories/gloves.dm
+++ b/modular_zubbers/code/modules/loadout/categories/gloves.dm
@@ -157,6 +157,14 @@
name = "Charcoal Fingerless Gloves"
item_path = /obj/item/clothing/gloves/skyy
+// Uses a loadout-only dummy type that holds the catgloves GAGS config at type level
+// so the loadout color picker works, then swaps itself for real ball mittens in paw mode on spawn.
+// i know this is probably dumb but I couldn't figure out a better way to do it :(
+/datum/loadout_item/gloves/ball_mittens_paw
+ name = "Latex Paw Mittens"
+ item_path = /obj/item/clothing/gloves/ball_mittens/loadout_paw
+ erp_item = TRUE
+
/*
* RINGS
*/
diff --git a/modular_zubbers/code/modules/loadout/categories/toys.dm b/modular_zubbers/code/modules/loadout/categories/toys.dm
index 59b3907e29d..21073b5d61e 100644
--- a/modular_zubbers/code/modules/loadout/categories/toys.dm
+++ b/modular_zubbers/code/modules/loadout/categories/toys.dm
@@ -209,6 +209,11 @@
name = "Strapon"
item_path = /obj/item/clothing/strapon
+/datum/loadout_item/toys/lewd/ball_mittens
+ name = "Ball Mittens"
+ item_path = /obj/item/clothing/gloves/ball_mittens
+ erp_item = TRUE
+
/datum/loadout_item/toys/lewd/aeri
name = "Interdimensional Terrorist Plushie"
item_path = /obj/item/toy/plush/aeri