diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm index 39fa037a264..8be5b1fdb64 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm @@ -155,7 +155,7 @@ ///Mob is trying to open the wires of a target [/atom], from /datum/wires/interactable(): (atom/target) #define COMSIG_TRY_WIRES_INTERACT "try_wires_interact" #define COMPONENT_CANT_INTERACT_WIRES (1<<0) -///Mob is trying to emote, from /datum/emote/proc/run_emote(): (key, params, type_override, intentional) +///Mob is trying to emote, from /datum/emote/proc/run_emote(): (key, params, type_override, intentional, emote) #define COMSIG_MOB_PRE_EMOTED "mob_pre_emoted" #define COMPONENT_CANT_EMOTE (1<<0) #define COMSIG_MOB_EMOTED(emote_key) "mob_emoted_[emote_key]" diff --git a/code/__DEFINES/obj_flags.dm b/code/__DEFINES/obj_flags.dm index c050c73f231..9f575de9fbc 100644 --- a/code/__DEFINES/obj_flags.dm +++ b/code/__DEFINES/obj_flags.dm @@ -75,20 +75,18 @@ #define DANGEROUS_OBJECT (1<<10) /// Clothes that use large icons, for applying the proper overlays like blood #define LARGE_WORN_ICON (1<<11) -/// Clothes that block speech (i.e the muzzle). Can be applied to any clothing piece. -#define BLOCKS_SPEECH (1<<12) /// prevents from placing on plasmaman helmet or modsuit hat holder -#define STACKABLE_HELMET_EXEMPT (1<<13) +#define STACKABLE_HELMET_EXEMPT (1<<12) /// Prevents plasmamen from igniting when wearing this -#define PLASMAMAN_PREVENT_IGNITION (1<<14) +#define PLASMAMAN_PREVENT_IGNITION (1<<13) /// Usable as casting clothes by wizards (matters for suits, glasses and headwear) -#define CASTING_CLOTHES (1<<15) +#define CASTING_CLOTHES (1<<14) ///Moths can't eat the clothing that has this flag. -#define INEDIBLE_CLOTHING (1<<16) +#define INEDIBLE_CLOTHING (1<<15) /// Headgear/helmet allows internals -#define HEADINTERNALS (1<<17) +#define HEADINTERNALS (1<<16) /// Prevents masks from getting adjusted from enabling internals -#define INTERNALS_ADJUST_EXEMPT (1<<18) +#define INTERNALS_ADJUST_EXEMPT (1<<17) /// Integrity defines for clothing (not flags but close enough) #define CLOTHING_PRISTINE 0 // We have no damage on the clothing diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index f98178d537f..ea1b16d71ce 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -83,7 +83,6 @@ DEFINE_BITFIELD(car_traits, list( DEFINE_BITFIELD(clothing_flags, list( "ANTI_TINFOIL_MANEUVER" = ANTI_TINFOIL_MANEUVER, - "BLOCKS_SPEECH" = BLOCKS_SPEECH, "BLOCK_GAS_SMOKE_EFFECT" = BLOCK_GAS_SMOKE_EFFECT, "CASTING_CLOTHES" = CASTING_CLOTHES, "GAS_FILTERING" = GAS_FILTERING, diff --git a/code/datums/components/knockoff.dm b/code/datums/components/knockoff.dm index db72072789c..3fe007736a1 100644 --- a/code/datums/components/knockoff.dm +++ b/code/datums/components/knockoff.dm @@ -23,6 +23,10 @@ /datum/component/knockoff/RegisterWithParent() RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(on_equipped)) RegisterSignal(parent, COMSIG_ITEM_DROPPED, PROC_REF(on_dropped)) + var/atom/movable/atom_parent = parent + var/mob/living/as_mob = atom_parent.loc + if(ismob(as_mob)) + on_equipped(atom_parent, as_mob, as_mob.get_slot_by_item(atom_parent)) // lie a little /datum/component/knockoff/UnregisterFromParent() UnregisterSignal(parent, list(COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED)) diff --git a/code/datums/elements/muffles_speech.dm b/code/datums/elements/muffles_speech.dm new file mode 100644 index 00000000000..bbf41e3c4de --- /dev/null +++ b/code/datums/elements/muffles_speech.dm @@ -0,0 +1,48 @@ +/datum/element/muffles_speech + +/datum/element/muffles_speech/Attach(datum/target) + . = ..() + if(!isitem(target)) + return ELEMENT_INCOMPATIBLE + + RegisterSignal(target, COMSIG_ITEM_EQUIPPED, PROC_REF(equipped)) + RegisterSignal(target, COMSIG_ITEM_DROPPED, PROC_REF(dropped)) + +/datum/element/muffles_speech/Detach(datum/source) + . = ..() + UnregisterSignal(source, list(COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED)) + +/datum/element/muffles_speech/proc/equipped(obj/item/source, mob/user, slot) + SIGNAL_HANDLER + if(source.slot_flags & slot) + RegisterSignal(user, COMSIG_MOB_SAY, PROC_REF(muzzle_talk)) + RegisterSignal(user, COMSIG_MOB_PRE_EMOTED, PROC_REF(emote_override)) + +/datum/element/muffles_speech/proc/dropped(obj/item/source, mob/user) + SIGNAL_HANDLER + UnregisterSignal(user, list(COMSIG_MOB_PRE_EMOTED, COMSIG_MOB_SAY)) + +/datum/element/muffles_speech/proc/emote_override(mob/living/source, key, params, type_override, intentional, datum/emote/emote) + SIGNAL_HANDLER + if(!emote.hands_use_check && (emote.emote_type & EMOTE_AUDIBLE)) + source.audible_message("makes a [pick("strong ", "weak ", "")]noise.", audible_message_flags = EMOTE_MESSAGE|ALWAYS_SHOW_SELF_MESSAGE) + return COMPONENT_CANT_EMOTE + return NONE + +/datum/element/muffles_speech/proc/muzzle_talk(datum/source, list/speech_args) + SIGNAL_HANDLER + + var/spoken_message = speech_args[SPEECH_MESSAGE] + if(spoken_message) + var/list/words = splittext(spoken_message, " ") + var/yell_suffix = copytext(spoken_message, findtext(spoken_message, "!")) + spoken_message = "" + + for(var/ind = 1 to length(words)) + var/new_word = "" + for(var/i = 1 to length(words[ind]) + rand(-1,1)) + new_word += "m" + new_word += "f" + words[ind] = yell_suffix ? uppertext(new_word) : new_word + spoken_message = "[jointext(words, " ")][yell_suffix]" + speech_args[SPEECH_MESSAGE] = spoken_message diff --git a/code/datums/emotes.dm b/code/datums/emotes.dm index 36d405450af..7006d6d6daa 100644 --- a/code/datums/emotes.dm +++ b/code/datums/emotes.dm @@ -38,8 +38,6 @@ var/emote_type = EMOTE_VISIBLE /// Checks if the mob can use its hands before performing the emote. var/hands_use_check = FALSE - /// Will only work if the emote is EMOTE_AUDIBLE. - var/muzzle_ignore = FALSE /// Types that are allowed to use that emote. var/list/mob_type_allowed_typecache = /mob /// Types that are NOT allowed to use that emote. @@ -90,7 +88,7 @@ /datum/emote/proc/run_emote(mob/user, params, type_override, intentional = FALSE) if(!can_run_emote(user, TRUE, intentional)) return FALSE - if(SEND_SIGNAL(user, COMSIG_MOB_PRE_EMOTED, key, params, type_override, intentional) & COMPONENT_CANT_EMOTE) + if(SEND_SIGNAL(user, COMSIG_MOB_PRE_EMOTED, key, params, type_override, intentional, src) & COMPONENT_CANT_EMOTE) return TRUE // We don't return FALSE because the error output would be incorrect, provide your own if necessary. var/msg = select_message_type(user, message, intentional) if(params && message_param) @@ -259,8 +257,6 @@ return . var/mob/living/living_user = user - if(!muzzle_ignore && user.is_muzzled() && emote_type & EMOTE_AUDIBLE) - return "makes a [pick("strong ", "weak ", "")]noise." if(HAS_MIND_TRAIT(user, TRAIT_MIMING) && message_mime) . = message_mime if(isalienadult(user) && message_alien) @@ -338,9 +334,7 @@ * Returns a bool about whether or not the user should play a sound when performing the emote. */ /datum/emote/proc/should_play_sound(mob/user, intentional = FALSE) - if(emote_type & EMOTE_AUDIBLE && !muzzle_ignore) - if(user.is_muzzled()) - return FALSE + if(emote_type & EMOTE_AUDIBLE && !hands_use_check) if(HAS_TRAIT(user, TRAIT_MUTE)) return FALSE if(ishuman(user)) diff --git a/code/modules/clothing/masks/muzzle.dm b/code/modules/clothing/masks/muzzle.dm index 32f29a54a1f..6154e7762cb 100644 --- a/code/modules/clothing/masks/muzzle.dm +++ b/code/modules/clothing/masks/muzzle.dm @@ -5,11 +5,14 @@ inhand_icon_state = "blindfold" lefthand_file = 'icons/mob/inhands/clothing/glasses_lefthand.dmi' righthand_file = 'icons/mob/inhands/clothing/glasses_righthand.dmi' - clothing_flags = BLOCKS_SPEECH flags_cover = MASKCOVERSMOUTH w_class = WEIGHT_CLASS_SMALL equip_delay_other = 20 +/obj/item/clothing/mask/muzzle/Initialize(mapload) + . = ..() + AddElement(/datum/element/muffles_speech) + /obj/item/clothing/mask/muzzle/attack_paw(mob/user, list/modifiers) if(iscarbon(user)) var/mob/living/carbon/carbon_user = user @@ -26,7 +29,7 @@ lefthand_file = 'icons/mob/inhands/clothing/masks_lefthand.dmi' righthand_file = 'icons/mob/inhands/clothing/masks_righthand.dmi' body_parts_covered = NONE - clothing_flags = MASKINTERNALS | BLOCKS_SPEECH + clothing_flags = MASKINTERNALS armor_type = /datum/armor/muzzle_breath equip_delay_other = 25 // my sprite has 4 straps, a-la a head harness. takes a while to equip, longer than a muzzle @@ -37,7 +40,7 @@ worn_icon_state = "tape_piece_worn" inhand_icon_state = null w_class = WEIGHT_CLASS_TINY - clothing_flags = INEDIBLE_CLOTHING|BLOCKS_SPEECH + clothing_flags = INEDIBLE_CLOTHING equip_delay_other = 40 strip_delay = 40 greyscale_config = /datum/greyscale_config/tape_piece diff --git a/code/modules/mob/living/carbon/alien/adult/alien_powers.dm b/code/modules/mob/living/carbon/alien/adult/alien_powers.dm index f3cfbfda8a9..999ec07f41a 100644 --- a/code/modules/mob/living/carbon/alien/adult/alien_powers.dm +++ b/code/modules/mob/living/carbon/alien/adult/alien_powers.dm @@ -251,7 +251,8 @@ Doesn't work on other aliens/AI.*/ plasma_cost = 50 /datum/action/cooldown/alien/acid/neurotoxin/IsAvailable(feedback = FALSE) - if(owner.is_muzzled()) + var/mob/living/carbon/as_carbon = owner + if(istype(as_carbon) && as_carbon.is_mouth_covered(ITEM_SLOT_MASK)) return FALSE if(!isturf(owner.loc)) return FALSE diff --git a/code/modules/mob/living/carbon/alien/alien.dm b/code/modules/mob/living/carbon/alien/alien.dm index f8210c77368..a995eafbb3a 100644 --- a/code/modules/mob/living/carbon/alien/alien.dm +++ b/code/modules/mob/living/carbon/alien/alien.dm @@ -76,6 +76,7 @@ return pick (list("xltrails_1", "xltrails2")) else return pick (list("xttrails_1", "xttrails2")) + /*---------------------------------------- Proc: AddInfectionImages() Des: Gives the client of the alien an image on each infected mob. diff --git a/code/modules/mob/living/carbon/alien/special/facehugger.dm b/code/modules/mob/living/carbon/alien/special/facehugger.dm index d068610b28f..d1ec6d7e880 100644 --- a/code/modules/mob/living/carbon/alien/special/facehugger.dm +++ b/code/modules/mob/living/carbon/alien/special/facehugger.dm @@ -24,6 +24,7 @@ layer = MOB_LAYER max_integrity = 100 item_flags = XENOMORPH_HOLDABLE + slowdown = 2 var/stat = CONSCIOUS //UNCONSCIOUS is the idle state in this case var/sterile = FALSE @@ -39,6 +40,7 @@ ) AddElement(/datum/element/connect_loc, loc_connections) AddElement(/datum/element/atmos_sensitive, mapload) + AddElement(/datum/element/muffles_speech) RegisterSignal(src, COMSIG_LIVING_TRYING_TO_PULL, PROC_REF(react_to_mob)) @@ -122,7 +124,7 @@ /obj/item/clothing/mask/facehugger/proc/valid_to_attach(mob/living/hit_mob) // valid targets: carbons except aliens and devils - // facehugger state early exit checks + // facehugger state early exit checks (Note: Melbert does not want dead people to be huggable) if(stat != CONSCIOUS) return FALSE if(attached) @@ -171,9 +173,12 @@ log_combat(target, src, "was facehugged by") return TRUE // time for a smoke -/obj/item/clothing/mask/facehugger/proc/Attach(mob/living/M) - if(!valid_to_attach(M)) +/obj/item/clothing/mask/facehugger/proc/Attach(mob/living/victim) + if(!valid_to_attach(victim)) return + + if(victim.stat < UNCONSCIOUS) //sorry bro you gotta be awake + victim.say("AAAA!!") //triggers muffled speech and also visual feedback i guess // early returns and validity checks done: attach. attached++ //ensure we detach once we no longer need to be attached @@ -181,12 +186,12 @@ if(!sterile) - M.take_bodypart_damage(strength,0) //done here so that humans in helmets take damage - M.Unconscious(MAX_IMPREGNATION_TIME/0.3) //something like 25 ticks = 20 seconds with the default settings - + victim.take_bodypart_damage(strength,0) //done here so that humans in helmets take damage + if(real && !sterile) + victim.Knockdown(5 SECONDS) GoIdle() //so it doesn't jump the people that tear it off - addtimer(CALLBACK(src, PROC_REF(Impregnate), M), rand(MIN_IMPREGNATION_TIME, MAX_IMPREGNATION_TIME)) + addtimer(CALLBACK(src, PROC_REF(Impregnate), victim), rand(MIN_IMPREGNATION_TIME, MAX_IMPREGNATION_TIME)) /obj/item/clothing/mask/facehugger/proc/detach() attached = 0 @@ -249,6 +254,29 @@ visible_message(span_danger("[src] curls up into a ball!")) + // chest maybe because getting slammed in the chest would knock it off your face while dead + AddComponent(/datum/component/knockoff, knockoff_chance = 40, target_zones = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST), slots_knockoffable = slot_flags) + +/obj/item/clothing/mask/facehugger/allow_attack_hand_drop(mob/living/carbon/human/user) + if(!real || sterile || user.get_organ_by_type(/obj/item/organ/internal/body_egg/alien_embryo)) + return ..() + if(istype(user) && ishuman(loc) && stat != DEAD) + if(user == loc && user.get_item_by_slot(slot_flags) == src) + to_chat(user, span_userdanger("[src] is latched on too tight! Get help or wait for it to let go!")) + return FALSE + return ..() + +/obj/item/clothing/mask/facehugger/mouse_drop_dragged(atom/over, mob/user, src_location, over_location, params) + var/mob/living/carbon/human/wearer = loc + if(!istype(wearer) || user != wearer) + return + if(!real || sterile || user.get_organ_by_type(/obj/item/organ/internal/body_egg/alien_embryo)) + return ..() + if(wearer.get_item_by_slot(slot_flags) == src && stat != DEAD) + to_chat(user, span_userdanger("[src] is latched on too tight! Get help or wait for it to let go!")) + return + return ..() + /proc/CanHug(mob/living/M) if(!istype(M)) return FALSE @@ -268,6 +296,7 @@ name = "Lamarr" desc = "The Research Director's pet, a domesticated and debeaked xenomorph facehugger. Friendly, but may still try to couple with your head." sterile = TRUE + slowdown = 1.5 //lamarr is too fat after being fed in captivity to effectively slow people down or something /obj/item/clothing/mask/facehugger/dead icon_state = "facehugger_dead" @@ -287,6 +316,7 @@ real = FALSE sterile = TRUE tint = 3 //Makes it feel more authentic when it latches on + slowdown = 0 /obj/item/clothing/mask/facehugger/toy/Die() return diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 539dfd64ce3..98948e6a9af 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -228,13 +228,6 @@ . = ..() loc.handle_fall(src)//it's loc so it doesn't call the mob's handle_fall which does nothing -/mob/living/carbon/is_muzzled() - for (var/obj/item/clothing/clothing in get_equipped_items()) - if(clothing.clothing_flags & BLOCKS_SPEECH) - return TRUE - return FALSE - - /mob/living/carbon/resist_buckle() if(!HAS_TRAIT(src, TRAIT_RESTRAINED)) buckled.user_unbuckle_mob(src, src) diff --git a/code/modules/mob/living/carbon/emote.dm b/code/modules/mob/living/carbon/emote.dm index 85662220392..00699196ae9 100644 --- a/code/modules/mob/living/carbon/emote.dm +++ b/code/modules/mob/living/carbon/emote.dm @@ -20,7 +20,6 @@ key = "clap" key_third_person = "claps" message = "claps." - muzzle_ignore = TRUE hands_use_check = TRUE emote_type = EMOTE_AUDIBLE | EMOTE_VISIBLE audio_cooldown = 5 SECONDS @@ -185,7 +184,6 @@ message_param = "snaps their fingers at %t." emote_type = EMOTE_AUDIBLE | EMOTE_VISIBLE hands_use_check = TRUE - muzzle_ignore = TRUE /datum/emote/living/carbon/snap/get_sound(mob/living/user) if(ishuman(user)) diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 3ee43801a76..5a5995e2072 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -411,7 +411,7 @@ if(!user.get_bodypart(BODY_ZONE_HEAD)) return FALSE - if(user.is_muzzled() || user.is_mouth_covered(ITEM_SLOT_MASK)) + if(user.is_mouth_covered(ITEM_SLOT_MASK)) to_chat(user, span_warning("You can't bite with your mouth covered!")) return FALSE diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 3b02567d1c9..6ec820bc469 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -805,10 +805,6 @@ set category = null return -///Is the mob muzzled (default false) -/mob/proc/is_muzzled() - return FALSE - /// Adds this list to the output to the stat browser /mob/proc/get_status_tab_items() . = list("") //we want to offset unique stuff from standard stuff diff --git a/code/modules/mob/mob_say.dm b/code/modules/mob/mob_say.dm index 568aee66902..6b8c8f9aa6b 100644 --- a/code/modules/mob/mob_say.dm +++ b/code/modules/mob/mob_say.dm @@ -106,9 +106,6 @@ if(!allow_mimes && HAS_MIND_TRAIT(src, TRAIT_MIMING)) return FALSE - if(is_muzzled()) - return FALSE - return ..() ///Speak as a dead person (ghost etc) diff --git a/tgstation.dme b/tgstation.dme index f65ae96a779..4f6c96dd3d0 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1473,6 +1473,7 @@ #include "code\datums\elements\move_force_on_death.dm" #include "code\datums\elements\movement_turf_changer.dm" #include "code\datums\elements\movetype_handler.dm" +#include "code\datums\elements\muffles_speech.dm" #include "code\datums\elements\nerfed_pulling.dm" #include "code\datums\elements\noisy_movement.dm" #include "code\datums\elements\noticable_organ.dm"