From cd2dda91eec762b73cab44c415fe70c79ca1c730 Mon Sep 17 00:00:00 2001 From: jimmyl <70376633+mc-oofert@users.noreply.github.com> Date: Mon, 1 Jul 2024 17:50:19 +0200 Subject: [PATCH] Facehuggers dont sleep people + muzzle code exorcism (#83721) ## About The Pull Request Facehuggers dont make people fall asleep, instead making them unable if on (already was in), and muffling speech Xenomorphs still have decent tools to stun people in melee thats stuff for another pr muzzle is now an element and also just makes you talk like word word word -> mmmmf mmmf mf COMPLETELY evaporates BLOCKS_SPEECH flag ## Why It's Good For The Game Being instantly sent to sleep by a facehugger thrown the hunter that materialized from a vent then getting dragged to their goon cave where you spend 2 minutes to get gibbed so someone else but you can play antag is just extremely unfun This should allow you to survive if youre quick enough after getting hugged instead of being forced to get gibbed, allowing cooler xenomorph combat (you could try fistfighting the dude or something idk) --- .../signals/signals_mob/signals_mob_main.dm | 2 +- code/__DEFINES/obj_flags.dm | 14 +++--- code/_globalvars/bitfields.dm | 1 - code/datums/components/knockoff.dm | 4 ++ code/datums/elements/muffles_speech.dm | 48 +++++++++++++++++++ code/datums/emotes.dm | 10 +--- code/modules/clothing/masks/muzzle.dm | 9 ++-- .../living/carbon/alien/adult/alien_powers.dm | 3 +- code/modules/mob/living/carbon/alien/alien.dm | 1 + .../living/carbon/alien/special/facehugger.dm | 44 ++++++++++++++--- code/modules/mob/living/carbon/carbon.dm | 7 --- code/modules/mob/living/carbon/emote.dm | 2 - code/modules/mob/living/living_defense.dm | 2 +- code/modules/mob/mob.dm | 4 -- code/modules/mob/mob_say.dm | 3 -- tgstation.dme | 1 + 16 files changed, 109 insertions(+), 46 deletions(-) create mode 100644 code/datums/elements/muffles_speech.dm 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"