diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 288604434e34..1aaf3714182e 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -127,6 +127,8 @@ #define TRAIT_INTENT_EYES "t_intent_eyes" /// Masked synthetic biology. Basic medHUDs will percieve the mob as human. (Infiltrator Synths) #define TRAIT_INFILTRATOR_SYNTH "t_infiltrator_synth" +/// Makes it impossible to strip the inventory of this mob. +#define TRAIT_UNSTRIPPABLE "t_unstrippable" // HIVE TRAITS /// If the Hive is a Xenonid Hive @@ -251,6 +253,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_FOREIGN_BIO" = TRAIT_FOREIGN_BIO, "TRAIT_INTENT_EYES" = TRAIT_INTENT_EYES, "TRAIT_INFILTRATOR_SYNTH" = TRAIT_INFILTRATOR_SYNTH, + "TRAIT_UNSTRIPPABLE" = TRAIT_UNSTRIPPABLE, "TRAIT_NESTED" = TRAIT_NESTED, "TRAIT_CRAWLER" = TRAIT_CRAWLER, "TRAIT_SIMPLE_DESC" = TRAIT_SIMPLE_DESC, diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index d01416b2443f..4ed9795b1fc0 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -509,8 +509,11 @@ 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, HUMAN_STRIP_DELAY, INTERRUPT_ALL, BUSY_ICON_GENERIC, src, INTERRUPT_MOVED, BUSY_ICON_GENERIC)) + 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) @@ -529,7 +532,7 @@ else var/oldsens = U.has_sensor visible_message(SPAN_DANGER("[usr] is trying to modify [src]'s sensors!"), null, null, 4) - if(do_after(usr, HUMAN_STRIP_DELAY, INTERRUPT_ALL, BUSY_ICON_GENERIC, src, INTERRUPT_MOVED, BUSY_ICON_GENERIC)) + if(do_after(usr, get_strip_delay(usr, src), INTERRUPT_ALL, BUSY_ICON_GENERIC, src, INTERRUPT_MOVED, BUSY_ICON_GENERIC)) if(U == w_uniform) if(U.has_sensor >= UNIFORM_FORCED_SENSORS) to_chat(usr, "The controls are locked.") diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm index 835e03c8e042..45fb65771069 100644 --- a/code/modules/mob/living/carbon/human/inventory.dm +++ b/code/modules/mob/living/carbon/human/inventory.dm @@ -486,63 +486,82 @@ return WEAR_LEGCUFFS return ..() - - -/mob/living/carbon/human/stripPanelUnequip(obj/item/I, mob/M, slot_to_process) - if(I.flags_item & ITEM_ABSTRACT) +/mob/living/carbon/human/proc/get_strip_delay(mob/living/carbon/human/user, mob/living/carbon/human/target) + /// Default delay + var/target_delay = HUMAN_STRIP_DELAY + /// Multiplier for how quickly the user can strip things. + var/user_speed = user.get_skill_duration_multiplier(SKILL_CQC) + /// The total skill level of CQC & Police + var/target_skills = (target.skills.get_skill_level(SKILL_CQC) + target.skills.get_skill_level(SKILL_POLICE)) + + /// Delay then gets + 0.5s per skill level, so long as not dead or cuffed. + if(!(target.stat || target.handcuffed)) + target_delay += (target_skills * 5) + + /// 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(I.flags_item & NODROP) - to_chat(src, SPAN_WARNING("You can't remove \the [I.name], it appears to be stuck!")) + if(interact_item.flags_inventory & CANTSTRIP) + to_chat(src, SPAN_WARNING("You're having difficulty removing \the [interact_item.name].")) return - if(I.flags_inventory & CANTSTRIP) - to_chat(src, SPAN_WARNING("You're having difficulty removing \the [I.name].")) + 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 - M.attack_log += "\[[time_stamp()]\] Has had their [I.name] ([slot_to_process]) attempted to be removed by [key_name(src)]" - attack_log += "\[[time_stamp()]\] Attempted to remove [key_name(M)]'s [I.name] ([slot_to_process])" - log_interact(src, M, "[key_name(src)] tried to remove [key_name(M)]'s [I.name] ([slot_to_process]).") - - src.visible_message(SPAN_DANGER("[src] tries to remove [M]'s [I.name]."), \ - SPAN_DANGER("You are trying to remove [M]'s [I.name]."), null, 5) - I.add_fingerprint(src) - if(do_after(src, HUMAN_STRIP_DELAY * src.get_skill_duration_multiplier(SKILL_CQC), INTERRUPT_ALL, BUSY_ICON_GENERIC, M, INTERRUPT_MOVED, BUSY_ICON_GENERIC)) - if(I && Adjacent(M) && I == M.get_item_by_slot(slot_to_process)) - M.drop_inv_item_on_ground(I) - log_interact(src, M, "[key_name(src)] removed [key_name(M)]'s [I.name] ([slot_to_process]) successfully.") - - if(M) - if(interactee == M && Adjacent(M)) - M.show_inv(src) - - -/mob/living/carbon/human/stripPanelEquip(obj/item/I, mob/M, slot_to_process) - if(I && !(I.flags_item & ITEM_ABSTRACT)) - if(I.flags_item & NODROP) - to_chat(src, SPAN_WARNING("You can't put \the [I.name] on [M], it's stuck to your hand!")) + 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(I.flags_inventory & CANTSTRIP) - to_chat(src, SPAN_WARNING("You're having difficulty putting \the [I.name] on [M].")) + 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(I.flags_item & WIELDED) - I.unwield(src) - if(!I.mob_can_equip(M, slot_to_process, TRUE)) - to_chat(src, SPAN_WARNING("You can't put \the [I.name] on [M]!")) + 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 [I.name] on [M]."), null, null, 5) - if(do_after(src, HUMAN_STRIP_DELAY * src.get_skill_duration_multiplier(SKILL_CQC), INTERRUPT_ALL, BUSY_ICON_GENERIC, M, INTERRUPT_MOVED, BUSY_ICON_GENERIC)) - if(I == get_active_hand() && !M.get_item_by_slot(slot_to_process) && Adjacent(M)) - if(I.flags_item & WIELDED) //to prevent re-wielding it during the do_after - I.unwield(src) - if(I.mob_can_equip(M, slot_to_process, TRUE))//Placing an item on the mob - drop_inv_item_on_ground(I) - if(I && !QDELETED(I)) //Might be self-deleted? - M.equip_to_slot_if_possible(I, slot_to_process, 1, 0, 1, 1) - if(ishuman(M) && M.stat == DEAD) - var/mob/living/carbon/human/H = M - H.disable_lights() // take that powergamers -spookydonut - - if(M) - if(interactee == M && Adjacent(M)) - M.show_inv(src) + visible_message(SPAN_NOTICE("[src] tries to put \the [interact_item.name] on [target_mob]."), null, null, 5) + 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) + 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) diff --git a/code/modules/mob/living/carbon/human/species/working_joe/_species.dm b/code/modules/mob/living/carbon/human/species/working_joe/_species.dm index 139b339fbfd7..292c302f9317 100644 --- a/code/modules/mob/living/carbon/human/species/working_joe/_species.dm +++ b/code/modules/mob/living/carbon/human/species/working_joe/_species.dm @@ -4,7 +4,7 @@ death_message = "violently gargles fluid and seizes up, the glow in their eyes dimming..." uses_ethnicity = FALSE burn_mod = 0.65 // made for hazardous environments, withstanding temperatures up to 1210 degrees - mob_inherent_traits = list(TRAIT_SUPER_STRONG, TRAIT_INTENT_EYES, TRAIT_EMOTE_CD_EXEMPT, TRAIT_CANNOT_EAT) + mob_inherent_traits = list(TRAIT_SUPER_STRONG, TRAIT_INTENT_EYES, TRAIT_EMOTE_CD_EXEMPT, TRAIT_CANNOT_EAT, TRAIT_UNSTRIPPABLE) slowdown = 0.45 hair_color = "#000000"