diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 1d47ae409b3..056bc25be5d 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -129,6 +129,9 @@ #define TINT_BLIND 3 //Threshold of tint level to obscure vision fully #define EYE_SHINE_THRESHOLD 6 //dark_view threshold past which a humanoid's eyes will 'shine' in the dark. +#define EMOTE_VISUAL 1 //A mob emote is visual +#define EMOTE_SOUND 2 //A mob emote is sound + //Human sub-species #define isshadowling(A) (is_species(A, /datum/species/shadow/ling)) #define isshadowlinglesser(A) (is_species(A, /datum/species/shadow/ling/lesser)) diff --git a/code/modules/assembly/voice.dm b/code/modules/assembly/voice.dm index df7206a7914..8228e490a38 100644 --- a/code/modules/assembly/voice.dm +++ b/code/modules/assembly/voice.dm @@ -10,46 +10,67 @@ bomb_name = "voice-activated bomb" - describe() - if(recorded || listening) - return "A meter on [src] flickers with every nearby sound." - else - return "[src] is deactivated." +/obj/item/assembly/voice/describe() + if(recorded || listening) + return "A meter on [src] flickers with every nearby sound." + else + return "[src] is deactivated." - hear_talk(mob/living/M as mob, msg) - hear_input(M, msg, 0) +/obj/item/assembly/voice/hear_talk(mob/living/M as mob, msg) + hear_input(M, msg, 0) - hear_message(mob/living/M as mob, msg) +/obj/item/assembly/voice/hear_message(mob/living/M as mob, msg) hear_input(M, msg, 1) - proc/hear_input(mob/living/M as mob, msg, type) - if(!istype(M,/mob/living)) - return - if(listening) - recorded = msg - recorded_type = type - listening = 0 - var/turf/T = get_turf(src) //otherwise it won't work in hand - T.visible_message("[bicon(src)] beeps, \"Activation message is [type ? "the sound when one [recorded]" : "'[recorded]'."]\"") - else - if(findtext(msg, recorded) && type == recorded_type) - pulse(0) - var/turf/T = get_turf(src) //otherwise it won't work in hand - T.visible_message("[bicon(src)] beeps!") +/obj/item/assembly/voice/proc/hear_input(mob/living/M as mob, msg, type) + if(!istype(M,/mob/living)) + return + if(listening) + recorded = msg + recorded_type = type + listening = 0 + var/turf/T = get_turf(src) //otherwise it won't work in hand + T.visible_message("[bicon(src)] beeps, \"Activation message is [type ? "the sound when one [recorded]" : "'[recorded]'."]\"") + else if(findtext(msg, recorded) && type == recorded_type) + pulse(0) + var/turf/T = get_turf(src) //otherwise it won't work in hand + T.visible_message("[bicon(src)] beeps!") - activate() - return // previously this toggled listning when not in a holder, that's a little silly. It was only called in attack_self that way. +/obj/item/assembly/voice/activate() + return // previously this toggled listning when not in a holder, that's a little silly. It was only called in attack_self that way. - attack_self(mob/user) - if(!user || !secured) return 0 +/obj/item/assembly/voice/attack_self(mob/user) + if(!user || !secured) return 0 - listening = !listening - var/turf/T = get_turf(src) - T.visible_message("[bicon(src)] beeps, \"[listening ? "Now" : "No longer"] recording input.\"") - return 1 + listening = !listening + var/turf/T = get_turf(src) + T.visible_message("[bicon(src)] beeps, \"[listening ? "Now" : "No longer"] recording input.\"") + return 1 - toggle_secure() - . = ..() - listening = 0 \ No newline at end of file +/obj/item/assembly/voice/toggle_secure() + . = ..() + listening = 0 + +/obj/item/assembly/voice/noise + name = "noise sensor" + desc = "A simple noise sensor that triggers on vocalizations other then speech." + icon_state = "voice" + materials = list(MAT_METAL=100, MAT_GLASS=10) + origin_tech = "magnets=1;engineering=1" + bomb_name = "noise-activated bomb" + +/obj/item/assembly/voice/noise/attack_self(mob/user) + return + +/obj/item/assembly/voice/noise/describe() + return "[src] does not appear to have any controls." + +/obj/item/assembly/voice/noise/hear_talk(mob/living/M as mob, msg) + return + +/obj/item/assembly/voice/hear_message(mob/living/M as mob, msg) + pulse(0) + var/turf/T = get_turf(src) //otherwise it won't work in hand + T.visible_message("[bicon(src)] beeps!") \ No newline at end of file diff --git a/code/modules/clothing/masks/miscellaneous.dm b/code/modules/clothing/masks/miscellaneous.dm index 22f293aedfc..fba251f71f8 100644 --- a/code/modules/clothing/masks/miscellaneous.dm +++ b/code/modules/clothing/masks/miscellaneous.dm @@ -120,6 +120,78 @@ mute = MUZZLE_MUTE_NONE security_lock = TRUE locked = FALSE + materials = list(MAT_METAL=500, MAT_GLASS=50) + +/obj/item/clothing/mask/muzzle/safety/shock + name = "shock muzzle" + desc = "A muzzle designed to prevent biting. This one is fitted with a behavior correction system." + var/obj/item/assembly/trigger = null + origin_tech = "materials=1;engineering=1" + materials = list(MAT_METAL=500, MAT_GLASS=50) + +/obj/item/clothing/mask/muzzle/safety/shock/attackby(obj/item/W, mob/user, params) + if(isscrewdriver(W) && trigger) + to_chat(user, "You disassemble [src].") + trigger.forceMove(get_turf(user)) + trigger.master = null + trigger.holder = null + trigger = null + return TRUE + else if(istype(W, /obj/item/assembly/signaler) || istype(W, /obj/item/assembly/voice)) + if(istype(trigger, /obj/item/assembly/signaler) || istype(trigger, /obj/item/assembly/voice)) + to_chat(user, "Something is already attached to [src].") + return FALSE + if(!user.drop_item()) + to_chat(user, "You are unable to insert [W] into [src].") + return FALSE + trigger = W + trigger.forceMove(src) + trigger.master = src + trigger.holder = src + to_chat(user, "You attach the [W] to [src].") + return TRUE + else if(istype(W, /obj/item/assembly)) + to_chat(user, "That won't fit in [src]. Perhaps a signaler or voice analyzer would?") + return FALSE + + return ..() + + +/obj/item/clothing/mask/muzzle/safety/shock/proc/can_shock(obj/item/clothing/C) + if(istype(C)) + if(isliving(C.loc)) + return C.loc + else if(isliving(loc)) + return loc + return FALSE + +/obj/item/clothing/mask/muzzle/safety/shock/proc/process_activation(var/obj/D, var/normal = 1, var/special = 1) + visible_message("[bicon(src)] *beep* *beep*", "*beep* *beep*") + var/mob/M = can_shock(loc) + if(M) + to_chat(M, "You feel a sharp shock!") + var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread + s.set_up(3, 1, M) + s.start() + + M.Weaken(5) + M.Stuttering(1) + M.Jitter(20) + return + +/obj/item/clothing/mask/muzzle/safety/shock/HasProximity(atom/movable/AM as mob|obj) + if(trigger) + trigger.HasProximity(AM) + + +/obj/item/clothing/mask/muzzle/safety/shock/hear_talk(mob/living/M as mob, msg) + if(trigger) + trigger.hear_talk(M, msg) + +/obj/item/clothing/mask/muzzle/safety/shock/hear_message(mob/living/M as mob, msg) + if(trigger) + trigger.hear_message(M, msg) + /obj/item/clothing/mask/surgical diff --git a/code/modules/mob/emote.dm b/code/modules/mob/emote.dm index 623f5b6b353..fffda3a2158 100644 --- a/code/modules/mob/emote.dm +++ b/code/modules/mob/emote.dm @@ -24,15 +24,20 @@ return target // All mobs should have custom emote, really.. -/mob/proc/custom_emote(var/m_type=1,var/message = null) +/mob/proc/custom_emote(var/m_type=EMOTE_VISUAL,var/message = null) if(stat || !use_me && usr == src) if(usr) to_chat(usr, "You are unable to emote.") return - var/muzzled = istype(src.wear_mask, /obj/item/clothing/mask/muzzle) - if(m_type == 2 && muzzled) return + var/muzzled = is_muzzled() + if(muzzled) + var/obj/item/clothing/mask/muzzle/M = wear_mask + if(m_type == EMOTE_SOUND && M.mute >= MUZZLE_MUTE_MUFFLE) + return //Not all muzzles block sound + if(!can_speak()) + return var/input if(!message) @@ -63,7 +68,7 @@ // Type 1 (Visual) emotes are sent to anyone in view of the item - if(m_type & 1) + if(m_type & EMOTE_VISUAL) var/list/can_see = get_mobs_in_view(1,src) //Allows silicon & mmi mobs carried around to see the emotes of the person carrying them around. can_see |= viewers(src,null) for(var/mob/O in can_see) @@ -80,7 +85,7 @@ // Type 2 (Audible) emotes are sent to anyone in hear range // of the *LOCATION* -- this is important for pAIs to be heard - else if(m_type & 2) + else if(m_type & EMOTE_SOUND) for(var/mob/O in get_mobs_in_view(7,src)) if(O.status_flags & PASSEMOTES) diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm index c1eb8641820..69653dfe1e8 100644 --- a/code/modules/mob/living/carbon/human/emote.dm +++ b/code/modules/mob/living/carbon/human/emote.dm @@ -10,6 +10,10 @@ act = copytext(act, 1, t1) var/muzzled = is_muzzled() + if(muzzled) + var/obj/item/clothing/mask/muzzle/M = wear_mask + if(M.mute == MUZZLE_MUTE_NONE) + muzzled = 0 //Not all muzzles block sound if(!can_speak()) muzzled = 1 //var/m_type = 1 diff --git a/code/modules/mob/living/update_status.dm b/code/modules/mob/living/update_status.dm index f7498392c02..c7f9bdd0d96 100644 --- a/code/modules/mob/living/update_status.dm +++ b/code/modules/mob/living/update_status.dm @@ -49,7 +49,14 @@ // Whether the mob is capable of talking /mob/living/can_speak() - return !(silent || (disabilities & MUTE) || is_muzzled()) + if(!(silent || (disabilities & MUTE))) + if(is_muzzled()) + var/obj/item/clothing/mask/muzzle/M = wear_mask + if(M.mute >= MUZZLE_MUTE_MUFFLE) + return FALSE + return TRUE + else + return FALSE // Whether the mob is capable of standing or not /mob/living/proc/can_stand() diff --git a/code/modules/research/designs/autolathe_designs.dm b/code/modules/research/designs/autolathe_designs.dm index bba85a5ff00..79dd9fb53f9 100644 --- a/code/modules/research/designs/autolathe_designs.dm +++ b/code/modules/research/designs/autolathe_designs.dm @@ -551,6 +551,14 @@ build_path = /obj/item/assembly/voice category = list("initial", "Miscellaneous") +/datum/design/noise_analyser + name = "Noise Analyser" + id = "Noise_analyser" + build_type = AUTOLATHE + materials = list(MAT_METAL = 100, MAT_GLASS = 10) + build_path = /obj/item/assembly/voice/noise + category = list("initial", "Miscellaneous") + /datum/design/light_tube name = "Light Tube" id = "light_tube" diff --git a/code/modules/research/designs/misc_designs.dm b/code/modules/research/designs/misc_designs.dm index 8bdf6771df1..1036bb6e188 100644 --- a/code/modules/research/designs/misc_designs.dm +++ b/code/modules/research/designs/misc_designs.dm @@ -60,3 +60,23 @@ materials = list(MAT_METAL = 500, MAT_GLASS = 300) build_path = /obj/item/camera/digital category = list("Miscellaneous") + +/datum/design/safety_muzzle + name = "Safety Muzzle" + desc = "Produce a lockable muzzle keyed to security ID cards" + id = "safetymuzzle" + req_tech = list("materials" = 1) + build_type = PROTOLATHE + materials = list(MAT_METAL=500, MAT_GLASS=50) + build_path = /obj/item/clothing/mask/muzzle/safety + category = list("Miscellaneous") + +/datum/design/shock_muzzle + name = "Shock Muzzle" + desc = "Produce a modified safety muzzle that includes an electric shock pack and a slot for a trigger assembly." + id = "shockmuzzle" + req_tech = list("materials" = 1, "engineering" = 1) + build_type = PROTOLATHE + materials = list(MAT_METAL=500, MAT_GLASS=50) + build_path = /obj/item/clothing/mask/muzzle/safety/shock + category = list("Miscellaneous")