diff --git a/code/__defines/species_languages.dm b/code/__defines/species_languages.dm index 32919c0fe9..589b360bea 100644 --- a/code/__defines/species_languages.dm +++ b/code/__defines/species_languages.dm @@ -49,6 +49,7 @@ #define LANGUAGE_XENOLINGUA "Xenomorph" #define LANGUAGE_HIVEMIND "Hivemind" +#define LANGUAGE_REDSPACE "Carnem amplecti" // Language flags. #define WHITELISTED 1 // Language is available if the speaker is whitelisted. diff --git a/code/__defines/traits/declarations.dm b/code/__defines/traits/declarations.dm index 674ba25e31..19fb86cd51 100644 --- a/code/__defines/traits/declarations.dm +++ b/code/__defines/traits/declarations.dm @@ -47,3 +47,5 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define UNIQUE_MINDSTRUCTURE "unique_mindstructure" // Owner is immune to hallucinations #define TRAIT_MADNESS_IMMUNE "supermatter_madness_immune" +/// Owner is corrupted via redspace. Used downstream. SHOULD NOT BE USED LIGHTLY. HORROR THEMES. +#define TRAIT_REDSPACE_CORRUPTED "redspace_corrupted" diff --git a/code/__defines/traits/sources.dm b/code/__defines/traits/sources.dm index d7d65ff601..27c2b83725 100644 --- a/code/__defines/traits/sources.dm +++ b/code/__defines/traits/sources.dm @@ -7,3 +7,5 @@ #define CURRENTLY_TYPING_TRAIT "currently_typing" /// Trait given by performing an action (proc, verb, etc. Something that the USER can control) #define ACTION_TRAIT "action" +/// Trait that is given by unholy effects or corruption. Can be dispelled by a chaplain with a nullrod. +#define UNHOLY_TRAIT "unholy" diff --git a/code/datums/diseases/horror.dm b/code/datums/diseases/horror.dm new file mode 100644 index 0000000000..0e38f0c23f --- /dev/null +++ b/code/datums/diseases/horror.dm @@ -0,0 +1,84 @@ +/datum/disease/fleshy_spread + name = "Vastantes Carnes" + medical_name = "Idiopathic Cellular Infestation" + form = "Unknown Disease" + max_stages = 5 + spread_text = "UNKNOWN BIOLOGICAL AGENT" + spread_flags = DISEASE_SPREAD_BLOOD + virus_modifiers = BYPASSES_IMMUNITY | SPREAD_DEAD + cure_text = REAGENT_ID_HOLYWATER + cures = list(REAGENT_ID_HOLYWATER) + agent = "Infectious Cellular Growth" + viable_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/human/monkey) + desc = "Unknown infection only known to be present in specific regions of space. \ + The host's cells are slowly rewritten and replaced. Unknown effects if left untreated." + danger = DISEASE_BIOHAZARD + disease_flags = CAN_NOT_POPULATE + stage_prob = 1 + +/datum/disease/fleshy_spread/stage_act() + if(!..()) + return FALSE + var/mob/living/carbon/human/infected = affected_mob + switch(stage) + if(2) + if(prob(2)) + to_chat(infected, span_danger(pick("You feel a strange tingling under your skin", "Your flesh feels tingly", "You feel as if your blood vessels are crawling", "You see a tendril slithering across your eye."))) + if(infected.bodytemperature < BODYTEMP_HEAT_DAMAGE_LIMIT) + fever(infected) + else if(prob(1)) + infected.visible_message(span_cult("The skin of [infected] shifts, as if something is moving within.")) + else if(prob(1)) + to_chat(infected, span_danger("You feel as though something is trying to displace your consciousness")) + infected.AdjustConfused(5) + + if(3) + if(prob(2)) + to_chat(infected, span_danger(pick("You feel warmth spreading throughout your body.", "You feel as if your body is burning up.", "Your skin feels like it's trying to pull away from your body."))) + if(infected.bodytemperature < BODYTEMP_HEAT_DAMAGE_LIMIT) + fever(infected) + else if(prob(1)) + infected.visible_message(span_cult("The flesh of [infected] bulges and shifts unnaturally, a weeping sore appearing on [infected.p_their()] skin.")) + infected.adjustBruteLossByPart(2, BP_HEAD, "Fleshy Rupture") + infected.drip(1) + else if(prob(1)) + to_chat(infected, span_danger("You hear whispers speaking to you in the back of your mind, your throat closing up.")) + infected.emote("gasp") + infected.AdjustConfused(10) + infected.silent = max(10, infected.silent) + if(4) + if(!infected.has_modifier_of_type(/datum/modifier/redspace_drain)) + infected.add_modifier(/datum/modifier/redspace_drain/lesser) + var/datum/modifier/redspace_drain/drain_modifier = infected.get_modifier_of_type(/datum/modifier/redspace_drain/lesser) + if(drain_modifier && prob(5)) + drain_modifier.choose_organs(1) + if(5) + //You waited WAY too long to get this cured. You're permanently infected now. Technically, this means you're now 'infectious' + if(infected.mind?.assigned_role == JOB_CHAPLAIN) + to_chat(infected, span_cult("An alien presence attempts to prod at your mind, but your faith shields you from its full effects, purging the corruption.")) + cure() + return + if(infected.species.flags & NO_SLEEVE) + to_chat(infected, span_cult("An alien presence attempts to prod at your mind, but at the final hour the effects of the corruption subsides.")) + cure() + return + if(!infected.has_modifier_of_type(/datum/modifier/redspace_corruption)) + infected.add_modifier(/datum/modifier/redspace_corruption) + to_chat(infected, span_cult("You feel something latch into your mind, something melding with every fiber of your being.")) + to_chat(infected, span_cult("Every one of your cells scream out in agony as they're individually overtaken by a foreign entity.")) + to_chat(infected, span_cult("Your body feels foreign, like you're an invader in another's body.")) + to_chat(infected, span_cult("In the back of your mind, you can hear a voice reaching out to you, pleading for you to come back. To come and never leave.")) + cure() + + else + return + +/datum/disease/fleshy_spread/cure() + var/mob/living/carbon/human/infected = affected_mob + if(infected.has_modifier_of_type(/datum/modifier/redspace_drain/lesser)) + infected.remove_modifiers_of_type(/datum/modifier/redspace_drain/lesser) + ..() + +/datum/disease/fleshy_spread/proc/fever(var/mob/living/M) + M.bodytemperature = min(M.bodytemperature + (2 * stage), BODYTEMP_HEAT_DAMAGE_LIMIT - 1) + return TRUE diff --git a/code/datums/mind.dm b/code/datums/mind.dm index f6c6c77b52..b8437e8822 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -504,6 +504,12 @@ return G break +///Proc that FORCIBLY grabs a client no matter where they are and returns their currently inhabited mob. +/datum/mind/proc/forcibly_grab_client() + for(var/mob/mob_to_grab in GLOB.player_list) + if(mob_to_grab.ckey == loaded_from_ckey) + return mob_to_grab + /datum/mind/proc/grab_ghost(force) var/mob/observer/dead/G = get_ghost(even_if_they_cant_reenter = force) . = G diff --git a/code/game/objects/items/weapons/weaponry.dm b/code/game/objects/items/weapons/weaponry.dm index 071b0d6d65..600670eff6 100644 --- a/code/game/objects/items/weapons/weaponry.dm +++ b/code/game/objects/items/weapons/weaponry.dm @@ -12,32 +12,51 @@ drop_sound = 'sound/items/drop/sword.ogg' pickup_sound = 'sound/items/pickup/sword.ogg' -/obj/item/nullrod/attack(mob/M as mob, mob/living/user as mob) //Paste from old-code to decult with a null rod. +/obj/item/nullrod/attack(mob/M as mob, mob/living/user as mob) add_attack_logs(user,M,"Hit with [src] (nullrod)") user.setClickCooldown(user.get_attack_speed(src)) user.do_attack_animation(M) - if (!user.IsAdvancedToolUser()) + if(!user.IsAdvancedToolUser()) to_chat(user, span_danger("You don't have the dexterity to do this!")) return - if ((CLUMSY in user.mutations) && prob(50)) + if((CLUMSY in user.mutations) && prob(50)) to_chat(user, span_danger("The rod slips out of your hand and hits your head.")) user.take_organ_damage(10) user.Paralyse(20) return - if (M.stat !=2) + if(M.stat != DEAD) + if(user?.mind?.assigned_role != JOB_CHAPLAIN) + to_chat(user, span_danger("You feel that only a chaplain can wield the null rod's true power!")) + ..() + return + + if(ishuman(M)) + var/mob/living/carbon/human/infected = M + if(infected.has_modifier_of_type(/datum/modifier/redspace_corruption)) + infected.remove_modifiers_of_type(/datum/modifier/redspace_corruption) + to_chat(user, "You wave [src] over [infected]'s head, and feel a dark presence leave [M.p_their()] body.") + to_chat(infected, "[user] waves [src] over your head and you feel a dark presence leave your body.") + + if(infected.HasDisease(/datum/disease/fleshy_spread)) + for(var/datum/disease/fleshy_spread/disease in infected.GetViruses()) + disease.cure() + break + to_chat(user, "You wave [src] over [infected]'s head, curing them of their infection.") + to_chat(infected, "[user] waves [src] over your head, curing you of your infection.") + + //Chaplain null rod removes ALL unholy traits. + REMOVE_TRAITS_IN(M, UNHOLY_TRAIT) + if(cult && (M.mind in cult.current_antagonists) && prob(33)) to_chat(M, span_danger("The power of [src] clears your mind of the cult's influence!")) to_chat(user, span_danger("You wave [src] over [M]'s head and see their eyes become clear, their mind returning to normal.")) cult.remove_antagonist(M.mind) M.visible_message(span_danger("\The [user] waves \the [src] over \the [M]'s head.")) - else if(prob(10)) - to_chat(user, span_danger("The rod slips in your hand.")) - ..() else to_chat(user, span_danger("The rod appears to do nothing.")) M.visible_message(span_danger("\The [user] waves \the [src] over \the [M]'s head.")) diff --git a/code/modules/client/verbs/typing.dm b/code/modules/client/verbs/typing.dm index 50704f6c93..0523f03d24 100644 --- a/code/modules/client/verbs/typing.dm +++ b/code/modules/client/verbs/typing.dm @@ -36,6 +36,8 @@ if(channel == "Whis" || channel == "Subtle" || channel == "whisper" || channel == "subtle") if(!prefs?.read_preference(/datum/preference/toggle/show_typing_indicator_subtle)) return FALSE + if(mob.status_flags & FAKEDEATH) + return FALSE ADD_TRAIT(mob, TRAIT_THINKING_IN_CHARACTER, CURRENTLY_TYPING_TRAIT) mob.create_thinking_indicator() diff --git a/code/modules/mob/_modifiers/horror.dm b/code/modules/mob/_modifiers/horror.dm index 04dbd6ace2..b1609149ee 100644 --- a/code/modules/mob/_modifiers/horror.dm +++ b/code/modules/mob/_modifiers/horror.dm @@ -36,6 +36,9 @@ var/static/list/redspace_areas = list ( /datum/modifier/redspace_drain/on_applied() unfortunate_soul = holder to_chat(unfortunate_soul, span_cult("You feel as if your lifeforce is slowly being rended from your body.")) + if(!unfortunate_soul.HasDisease(/datum/disease/fleshy_spread)) + var/datum/disease/fleshy_spread/flesh_disease = new /datum/disease/fleshy_spread() + unfortunate_soul.ForceContractDisease(flesh_disease, BP_TORSO) return /datum/modifier/redspace_drain/on_expire() @@ -96,8 +99,11 @@ var/static/list/redspace_areas = list ( unfortunate_soul.stuttering = min(100, unfortunate_soul.stuttering + 10) //Stuttering is increased by 1, but never above 100. You're in a scary place. return -/datum/modifier/redspace_drain/proc/choose_organs() - var/organs_to_replace = rand(2,3) +/datum/modifier/redspace_drain/proc/choose_organs(organs_to_replace) + if(!organs_to_replace) + organs_to_replace = rand(2,3) + if(organs_to_replace <= 0) //Sanity + return for(var/i = 0 to organs_to_replace) var/organ_choice = pick("eyes", "heart", "lungs", "liver", "kidneys", "appendix", "voicebox", "spleen", "stomach", "intestine") switch(organ_choice) @@ -252,6 +258,33 @@ var/static/list/redspace_areas = list ( var/random_name = pick("pulsating", "quivering", "throbbing", "crawling", "oozing", "melting", "gushing", "dripping", "twitching", "slimy", "gooey") new_organ.name = "[random_name] [initial(new_organ.name)]" +///Variant redspace drain ONLY used for the virus. +/datum/modifier/redspace_drain/lesser + name = "redspace infection" + desc = "Your body is warping..." + + on_created_text = null + on_expired_text = null + +/datum/modifier/redspace_drain/lesser/can_apply(mob/living/L, suppress_output = TRUE) + if(ishuman(L) && !L.isSynthetic()) + return TRUE + return FALSE + +/datum/modifier/redspace_drain/lesser/on_applied() + unfortunate_soul = holder + return + +/datum/modifier/redspace_drain/lesser/on_expire() + unfortunate_soul = null + return + +/datum/modifier/redspace_drain/lesser/check_if_valid() + if(expire_at && expire_at < world.time) + src.expire() + +/datum/modifier/redspace_drain/lesser/tick() + return /* /datum/modifier/redspace_drain/proc/replace_organ() //Old version of doing this WITHOUT the custom organs. Preserved as an alternative version / reference var/obj/item/organ/O = pick(unfortunate_soul.internal_organs) @@ -317,3 +350,364 @@ var/static/list/redspace_areas = list ( /datum/modifier/redsight/tick() ..() + +///The PERMANENT debuff that redspace warp leaves you with. +/datum/modifier/redspace_corruption + name = "redspace corruption" + desc = "Your body has been permanently twisted." + + on_created_text = null + on_expired_text = null + + stacks = MODIFIER_STACK_EXTEND + + ///Time since we last revived + var/time_since_revival = 0 + + ///Cooldown on how often we can revive. + var/revival_cooldown = 60 SECONDS + + ///When did we last do a 'heal tick' ? + var/heal_tick = 0 + + ///How often do we do a 'heal tick' ? + var/heal_tick_cooldown = 5 SECONDS + + ///What chance is there per tick that we unsheath an armblade and face someone? + var/blade_chance = 1 + + ///What is the chance that we inject a paralyze a nearby crewmember that is standing too close to us? + var/injection_chance = 3 + + ///If we have our flesh armor deployed or not. + var/armor_deployed = FALSE + + ///At what time did we deploy our armor? + var/armor_deployed_time = 0 + + ///How long can we upkeep our armor? + var/armor_duration = 5 MINUTES + + ///What is our hivemind name? + var/speech_name = "The Unseen Horror" + + + var/mob/living/carbon/human/unfortunate_soul //The human target of our modifier. + +/datum/modifier/redspace_corruption/can_apply(mob/living/L, suppress_output = TRUE) + if(ishuman(L) && !L.isSynthetic()) + if(L.mind?.assigned_role == JOB_CHAPLAIN) + return FALSE + return TRUE + return FALSE + +/datum/modifier/redspace_corruption/on_applied() + unfortunate_soul = holder + ADD_TRAIT(unfortunate_soul, TRAIT_REDSPACE_CORRUPTED, UNHOLY_TRAIT) + ADD_TRAIT(unfortunate_soul, UNIQUE_MINDSTRUCTURE, UNHOLY_TRAIT) + speech_name = pick("Lost Soul", "Rescued One", "The Embraced", "The Chosen", "The Unseen Horror", "Obedient Servant", "Willing Follower") + + //SHUNT ALL THE IMPORTANT ORGANS TO THE CHEST! + var/obj/item/organ/internal/brain/brain = unfortunate_soul.internal_organs_by_name[O_BRAIN] + var/obj/item/organ/internal/eyes/eyes = unfortunate_soul.internal_organs_by_name[O_EYES] + var/obj/item/organ/external/chest/torso = unfortunate_soul.get_organ(BP_TORSO) + if(unfortunate_soul.should_have_organ(O_BRAIN)) + brain.parent_organ = BP_TORSO //Move the brain to the torso. + torso.internal_organs |= brain + if(unfortunate_soul.should_have_organ(O_EYES)) + eyes.parent_organ = BP_TORSO //Move the eyes to the torso. + torso.internal_organs |= eyes + for(var/obj/item/organ/external/head/ex_organ in unfortunate_soul.organs) + ex_organ.cannot_break = TRUE + ex_organ.dislocated = -1 + ex_organ.nonsolid = TRUE + ex_organ.spread_dam = TRUE + ex_organ.max_damage = 5 //VERY fragile, now. + ex_organ.vital = FALSE + ex_organ.encased = FALSE + ex_organ.cannot_gib = FALSE + if(brain) + ex_organ.internal_organs -= brain //Remove the brain from the head. + if(eyes) + ex_organ.internal_organs -= eyes + +/datum/modifier/redspace_corruption/on_expire() + REMOVE_TRAIT(unfortunate_soul, TRAIT_REDSPACE_CORRUPTED, UNHOLY_TRAIT) + REMOVE_TRAIT(unfortunate_soul, UNIQUE_MINDSTRUCTURE, UNHOLY_TRAIT) + unfortunate_soul = null + +/datum/modifier/redspace_corruption/tick() + //Handles resurrection and healing if dead. + var/bellied = FALSE + if(isbelly(unfortunate_soul.loc)) + bellied = TRUE + var/mob/living/carbon/human/predator = unfortunate_soul.loc.loc + if(istype(predator) && !predator.HasDisease(/datum/disease/fleshy_spread)) + var/datum/disease/fleshy_spread/flesh_disease = new /datum/disease/fleshy_spread() + predator.ForceContractDisease(flesh_disease, BP_TORSO) + + if(unfortunate_soul.stat == DEAD) + handle_death() + return + + if(!armor_deployed && (unfortunate_soul.stunned || unfortunate_soul.weakened || unfortunate_soul.paralysis || unfortunate_soul.health < unfortunate_soul.getMaxHealth() * 0.75)) + if(assume_battle_stance()) + unfortunate_soul.adjustHalLoss(-200) //WAKE UP SAMURI + unfortunate_soul.reagents.add_reagent(REAGENT_ID_ADRENALINE, 5) + unfortunate_soul.reagents.add_reagent(REAGENT_ID_EPINEPHRINE, 5) + unfortunate_soul.reagents.add_reagent(REAGENT_ID_NUMBENZYME, 1) + to_chat(unfortunate_soul, span_large(span_bolddanger("Your body surges with adrenaline, and every cell ignites with a primal fight or flight. Your instincts are screaming through every fiber of your body: Escape the danger. Kill the threats. Protect your body. SURVIVE."))) + return + + else if(unfortunate_soul.stat == UNCONSCIOUS) + return + + if(bellied) + return + + if(armor_deployed && ((armor_deployed_time + armor_duration) < world.time)) //Time ran out. + + //Are we still in panic mode? + if(unfortunate_soul.stunned || unfortunate_soul.weakened || unfortunate_soul.paralysis || (unfortunate_soul.health < unfortunate_soul.maxHealth * 0.75)) + return + else + equip_flesh_armor(/obj/item/clothing/suit/space/changeling/armored, /obj/item/clothing/head/helmet/space/changeling/armored, /obj/item/clothing/shoes/magboots/changeling/armored, /obj/item/clothing/gloves/combat/changeling) + armor_deployed = FALSE + return + //Stuff that happens when we're ALIVE. + if(prob(blade_chance)) + //If we have an open hand and we have people near us, unsheath an armblade and face them. + //This plays a BIG SCARY MESSAGE in chat and will make people panic. + if(!unfortunate_soul.hands_are_full()) + if(attempt_armblade()) + return + if(prob(injection_chance)) + var/list_of_humans = list() + var/prick_message + for(var/mob/living/carbon/human/target in oview(1, unfortunate_soul.loc)) + if(target.has_modifier_of_type(/datum/modifier/redspace_corruption)) //No cyclic injections! + if(!prick_message) + to_chat(unfortunate_soul, span_warning("Your body stealthily injects [target] but they seem unaffected.")) + to_chat(target, span_bolddanger("You feel a tiny prick.")) + prick_message = TRUE + continue + if(is_changeling(target)) + if(!prick_message) + to_chat(unfortunate_soul, span_warning("Your body stealthily injects [target] but they seem unaffected.")) + to_chat(target, span_bolddanger("You feel a tiny prick.")) + prick_message = TRUE + continue + list_of_humans += target + if(LAZYLEN(list_of_humans)) + var/mob/living/carbon/human/prey = pick(list_of_humans) + if(!prey) + return + to_chat(prey, span_bolddanger("You feel a tiny prick.")) + prey.reagents.add_reagent(REAGENT_ID_ZOMBIEPOWDER, 5) + if(!prey.HasDisease(/datum/disease/fleshy_spread)) + var/datum/disease/fleshy_spread/flesh_disease = new /datum/disease/fleshy_spread() + prey.ForceContractDisease(flesh_disease) + to_chat(unfortunate_soul, span_warning("[prey] walks too close to you, your body instinctually stinging them")) + + return + +/datum/modifier/redspace_corruption/proc/handle_death() + + //Cooldown? + + if(armor_deployed && (armor_deployed_time + (armor_duration * 2)) < world.time) //Takes longer for armor to undeploy when dead. + exit_battle_stance() + + if(heal_tick + heal_tick_cooldown > world.time) + return + + var/obj/item/organ/internal/brain/brain = unfortunate_soul.internal_organs_by_name[O_BRAIN] + if(unfortunate_soul.should_have_organ(O_BRAIN)) + if(!brain) //Removed the brain? Can't do anything. + return + + var/obj/item/organ/internal/heart/heart = unfortunate_soul.internal_organs_by_name[O_HEART] + if(!heart) + return + + + var/blood_volume = unfortunate_soul.vessel.get_reagent_amount(REAGENT_ID_BLOOD) + var/lethal_blood = FALSE + if(unfortunate_soul.reagents.get_reagent_amount(REAGENT_ID_MYELAMINE) < 5) + unfortunate_soul.reagents.add_reagent(REAGENT_ID_MYELAMINE, 5) //Helps stabilize them. + if(!heart || heart.is_broken()) + blood_volume *= 0.3 + else if(heart.is_bruised()) + blood_volume *= 0.7 + else if(heart.damage > 1) + blood_volume *= 0.8 + if(blood_volume < unfortunate_soul.species.blood_volume*unfortunate_soul.species.blood_level_fatal) + unfortunate_soul.reagents.add_reagent(REAGENT_ID_SYNTHBLOOD, 25) //Will get processed below. + lethal_blood = TRUE + + //Circulate chems. + for(var/i in 1 to 5) + unfortunate_soul.handle_chemicals_in_body() + unfortunate_soul.handle_organs() + + //Slowly come back from the dead. + unfortunate_soul.heal_overall_damage(2, 2) + unfortunate_soul.adjustToxLoss(-5) + unfortunate_soul.adjustOxyLoss(-5) + unfortunate_soul.adjustBrainLoss(-0.5) + + //Handle our organs. We might not heal entirely before we come back, but that's fine. If we die again, we come back again. + for(var/obj/item/organ/I in unfortunate_soul.internal_organs) + I.process() + I.germ_level = max(0, I.germ_level - 25) + I.damage = max(0, I.damage - 2.5) + if(I.status & ORGAN_DEAD && (I.damage < I.is_broken()) && (I.germ_level < INFECTION_LEVEL_ONE)) //If we have any dead organs, try to revive them. + I.status = 0 + + for(var/obj/item/organ/external/limb in unfortunate_soul.bad_external_organs) + limb.germ_level = max(0, limb.germ_level - 25) + if(limb.status & ORGAN_DEAD && (limb.damage < limb.is_broken()) && (limb.germ_level < INFECTION_LEVEL_ONE)) //If we have any dead organs, try to revive them. + limb.status = 0 + + heal_tick = world.time + + //Big checks to see if there's a reason we CAN'T revive. + if(time_since_revival + revival_cooldown > world.time) //On cooldown. + return + if(lethal_blood) //Blood volume is low enough we'd immediately die upon revival. + return + if(unfortunate_soul.health <= -(unfortunate_soul.getMaxHealth() * 0.33)) //Too injured to revive. We want to be a bit JUST before hardcrit. + return + if(unfortunate_soul.check_vital_organs()) //Missing a vital organ. + return + if(unfortunate_soul.teleop) //Aghosted or the sort. + return + if(!unfortunate_soul.mind) //Mind is gone. + return + + + //This won't get EVERYTHING, but if we end up reviving just to die shortly afterwards to heal up further, that's fine. + + //Time to revive! This FORCIBLY grabs their mind and puts it back in. + revive() + return + +/datum/modifier/redspace_corruption/proc/revive() + //Force us back into the body. + unfortunate_soul.grab_ghost(TRUE) + + //Defib stuff here. + GLOB.dead_mob_list.Remove(unfortunate_soul) + if((unfortunate_soul in GLOB.living_mob_list) || (unfortunate_soul in GLOB.dead_mob_list)) + WARNING("Mob [unfortunate_soul] was revived but already in the living or dead list still!") + GLOB.living_mob_list += unfortunate_soul + unfortunate_soul.timeofdeath = 0 + unfortunate_soul.set_stat(UNCONSCIOUS) //Life() can bring them back to consciousness if it needs to. + unfortunate_soul.failed_last_breath = 0 //So mobs that died of oxyloss don't revive and have perpetual out of breath. + unfortunate_soul.reload_fullscreen() + + //Awaken! + unfortunate_soul.emote("gasp") + unfortunate_soul.Weaken(rand(10,25)) + unfortunate_soul.updatehealth() + time_since_revival = world.time + +//Returns TRUE If we succeeded. FALSE if we failed. +/datum/modifier/redspace_corruption/proc/attempt_armblade() + var/list_of_humans = list() + for(var/mob/living/carbon/human/target in oview(4, unfortunate_soul.loc)) + if(target.has_modifier_of_type(/datum/modifier/redspace_corruption)) //Flesh knows flesh. + continue + if(is_changeling(target)) + continue + list_of_humans += target + if(LAZYLEN(list_of_humans)) + var/mob/person_to_stare_at_with_our_special_eyes = pick(list_of_humans) + if(!person_to_stare_at_with_our_special_eyes) + return FALSE + deploy_armblade() + unfortunate_soul.face_atom(person_to_stare_at_with_our_special_eyes) + if(get_dist(unfortunate_soul, person_to_stare_at_with_our_special_eyes.loc) > 1) + step_towards(unfortunate_soul, person_to_stare_at_with_our_special_eyes) + if(get_dist(unfortunate_soul, person_to_stare_at_with_our_special_eyes.loc) > 1) + step_towards(unfortunate_soul, person_to_stare_at_with_our_special_eyes) + return TRUE + return FALSE + +/datum/modifier/redspace_corruption/proc/deploy_armblade() + var/obj/item/melee/changeling/arm_blade/blade = new /obj/item/melee/changeling/arm_blade(unfortunate_soul) + if(!unfortunate_soul.put_in_hands(blade)) + qdel(blade) //failed, sad. + +//shamelessly stolen from changeling/armor.dm +/datum/modifier/redspace_corruption/proc/equip_flesh_armor(var/armor_type, var/helmet_type, var/boot_type, var/glove_type) + + var/mob/living/carbon/human/M = unfortunate_soul + + //First, check if we're already wearing the armor, and if so, take it off. + if(istype(M.wear_suit, armor_type) || istype(M.head, helmet_type) || istype(M.shoes, boot_type) || istype(M.gloves, glove_type)) + M.visible_message(span_warning("[M] casts off their [M.wear_suit.name]!"), + span_warning("We cast off our [M.wear_suit.name]"), + span_warningplain("You hear the organic matter ripping and tearing!")) + if(istype(M.wear_suit, armor_type)) + qdel(M.wear_suit) + if(istype(M.head, helmet_type)) + qdel(M.head) + if(istype(M.shoes, boot_type)) + qdel(M.shoes) + if(istype(M.gloves, glove_type)) + qdel(M.gloves) + M.update_inv_wear_suit() + M.update_inv_head() + M.update_hair() + M.update_inv_shoes() + M.update_inv_gloves() + return TRUE + + var/obj/item/clothing/suit/A = new armor_type(M) + if(M.wear_suit) + M.unEquip(M.wear_suit, TRUE) + M.equip_to_slot_or_del(A, slot_wear_suit) + + var/obj/item/clothing/suit/H = new helmet_type(M) + if(M.head) + M.unEquip(M.head, TRUE) + M.equip_to_slot_or_del(H, slot_head) + + var/obj/item/clothing/shoes/B = new boot_type(M) + if(M.shoes) + M.unEquip(M.shoes, TRUE) + M.equip_to_slot_or_del(B, slot_shoes) + + var/obj/item/clothing/gloves/G = new glove_type(M) + if(M.gloves) + M.unEquip(M.gloves, TRUE) + M.equip_to_slot_or_del(G, slot_gloves) + + playsound(M, 'sound/effects/blobattack.ogg', 30, 1) + M.update_inv_wear_suit() + M.update_inv_head() + M.update_hair() + M.update_inv_shoes() + M.update_inv_gloves() + return TRUE + +///Equips armor and melee weapon. If we succeed, returns TRUE. FALSE if we fail. +/datum/modifier/redspace_corruption/proc/assume_battle_stance() + if(equip_flesh_armor(/obj/item/clothing/suit/space/changeling/armored,/obj/item/clothing/head/helmet/space/changeling/armored,/obj/item/clothing/shoes/magboots/changeling/armored, /obj/item/clothing/gloves/combat/changeling)) + to_chat(unfortunate_soul, span_warning("Your flesh shifts and hardens into a protective armor!")) + armor_deployed = TRUE + armor_deployed_time = world.time + unfortunate_soul.drop_l_hand() + unfortunate_soul.drop_r_hand() + deploy_armblade() + deploy_armblade() + return TRUE + return FALSE + +/datum/modifier/redspace_corruption/proc/exit_battle_stance() + if(armor_deployed) + equip_flesh_armor(/obj/item/clothing/suit/space/changeling/armored, /obj/item/clothing/head/helmet/space/changeling/armored, /obj/item/clothing/shoes/magboots/changeling/armored, /obj/item/clothing/gloves/combat/changeling) + armor_deployed = FALSE + unfortunate_soul.drop_l_hand() + unfortunate_soul.drop_r_hand() diff --git a/code/modules/mob/language/horror.dm b/code/modules/mob/language/horror.dm new file mode 100644 index 0000000000..d0ef69c60f --- /dev/null +++ b/code/modules/mob/language/horror.dm @@ -0,0 +1,45 @@ +/datum/language/redspace + name = LANGUAGE_REDSPACE + desc = "An incurable corruption spread to one's mind and body. It's all you can speak, now. Why would you want to speak anything else?" + speech_verb = "wails" + colour = "cult" + key = "F" + flags = RESTRICTED | NONVERBAL | HIVEMIND | NO_STUTTER + //copy and past cultish because i'm lazy...Nobody but people with it should be able to hear you say it anyway. + syllables = list("ire","ego","nahlizet","certum","veri","jatkaa","mgar","balaq", "karazet", "geeri", \ + "orkan", "allaq", "sas'so", "c'arta", "forbici", "tarem", "n'ath", "reth", "sh'yro", "eth", "d'raggathnor", \ + "mah'weyh", "pleggh", "at", "e'ntrath", "tok-lyr", "rqa'nap", "g'lt-ulotf", "ta'gh", "fara'qha", "fel", "d'amar det", \ + "yu'gular", "faras", "desdae", "havas", "mithum", "javara", "umathar", "uf'kal", "thenar", "rash'tla", \ + "sektath", "mal'zua", "zasan", "therium", "viortia", "kla'atu", "barada", "nikt'o", "fwe'sh", "mah", "erl", "nyag", "r'ya", \ + "gal'h'rfikk", "harfrandid", "mud'gib", "il", "fuu", "ma'jin", "dedo", "ol'btoh", "n'ath", "reth", "sh'yro", "eth", \ + "d'rekkathnor", "khari'd", "gual'te", "nikka", "nikt'o", "barada", "kla'atu", "barhah", "hra" ,"zar'garis", "spiri", "malum") + +//Special, spooky effects when you speak. +/datum/language/redspace/broadcast(var/mob/living/speaker,var/message,var/speaker_mask) + if(prob(10)) + speaker.hallucination += 5 + + if(prob(5)) + speaker.visible_message(span_danger("[speaker] suddenly " + pick("writhes", "twitches", "shudders", "quivers", "contorts unnaturally"))) + else if(ishuman(speaker)) + var/mob/living/carbon/human/human_speaker = speaker + if(prob(1)) + human_speaker.drip(1) + var/turf/T = get_turf(human_speaker) + if(T) + var/obj/effect/spider/spiderling/non_growing/horror/spider = new /obj/effect/spider/spiderling/non_growing/horror(get_turf(human_speaker)) + spider.faction = human_speaker.faction + spider.color = "[human_speaker.species.blood_color]" + spider.name = "writhing tendril mass" + spider.desc = "A small, writhing mass of flesh and tendrils." + var/obj/item/organ/external/head/head = human_speaker.get_organ(BP_HEAD) + if(head) + speaker.visible_message(span_danger("[speaker] opens [speaker.p_their()] mouth to speak and a writing mass of tendrils crawls out.")) + else + speaker.visible_message(span_danger("[speaker] acts as if [speaker.p_theyre()] attempting to speak, only for a writing mass of tendrils to crawl out of [speaker.p_their()] neck hole.")) + var/datum/modifier/redspace_corruption/corruption = speaker.get_modifier_of_type(/datum/modifier/redspace_corruption) + if(corruption) + speaker_mask = corruption.speech_name + else + speaker_mask = speaker.real_name + ..(speaker, message, speaker_mask) diff --git a/code/modules/organs/blood.dm b/code/modules/organs/blood.dm index 4b78bcc5fa..c2e89b972b 100644 --- a/code/modules/organs/blood.dm +++ b/code/modules/organs/blood.dm @@ -49,7 +49,7 @@ BLOOD_VOLUME_SURVIVE = 40 if(isSynthetic()) B.data["species"] = "synthetic" - B.data["changeling"] = (!isnull(mind) && is_changeling(mind)) || species?.ambulant_blood + B.data["changeling"] = (!isnull(mind) && is_changeling(mind)) || species?.ambulant_blood || HAS_TRAIT(src, TRAIT_REDSPACE_CORRUPTED) B.color = B.data["blood_colour"] B.name = B.data["blood_name"] @@ -295,7 +295,7 @@ BLOOD_VOLUME_SURVIVE = 40 B.data["resistances"] |= GetResistances() B.data["blood_DNA"] = copytext(src.dna.unique_enzymes,1,0) B.data["blood_type"] = copytext(src.dna.b_type,1,0) - B.data["changeling"] = (!isnull(mind) && is_changeling(mind)) || species?.ambulant_blood + B.data["changeling"] = (!isnull(mind) && is_changeling(mind)) || species?.ambulant_blood || HAS_TRAIT(src, TRAIT_REDSPACE_CORRUPTED) // Putting this here due to return shenanigans. if(ishuman(src)) @@ -372,7 +372,7 @@ BLOOD_VOLUME_SURVIVE = 40 if(!our) log_runtime("Failed to re-initialize blood datums on [src]!") return - if(is_changeling(src)) //Changelings don't reject blood! + if((is_changeling(src) || HAS_TRAIT(src, TRAIT_REDSPACE_CORRUPTED))) //Changelings don't reject blood! vessel.add_reagent(REAGENT_ID_BLOOD, amount, injected.data) vessel.update_total() else if(blood_incompatible(injected.data["blood_type"],our.data["blood_type"],injected.data["species"],our.data["species"]) ) diff --git a/code/modules/organs/internal/horror.dm b/code/modules/organs/internal/horror.dm index 0d479ac7ba..6e6495b653 100644 --- a/code/modules/organs/internal/horror.dm +++ b/code/modules/organs/internal/horror.dm @@ -223,18 +223,19 @@ decays = FALSE can_reject = FALSE meat_type = /obj/item/reagent_containers/food/snacks/meat/worm - will_assist_languages = list(LANGUAGE_DAEMON) + will_assist_languages = list(LANGUAGE_REDSPACE) var/speak_chance = 25 //25% chance to speak Daemon every 10 ticks - var/datum/language/daemon //Storage for demon language so we don't have to constantly set it. + var/datum/language/redspace + mute = TRUE //You can only speak the hivemind language~ /obj/item/organ/internal/voicebox/horror/Initialize(mapload) . = ..() - daemon = GLOB.all_languages[LANGUAGE_DAEMON] + redspace = GLOB.all_languages[LANGUAGE_REDSPACE] /obj/item/organ/internal/voicebox/horror/replaced(var/mob/living/carbon/human/target,var/obj/item/organ/external/affected) ..() - target.add_language(LANGUAGE_DAEMON) //Learn Daemon - target.default_language = daemon //Begin speaking Daemon. + target.add_language(LANGUAGE_REDSPACE) + target.default_language = redspace /obj/item/organ/internal/voicebox/horror/process() ..() @@ -243,6 +244,5 @@ if(is_bruised()) //They heal theirselves. damage -= 1 if(owner.life_tick % 10 == 0 && prob(speak_chance)) - owner.default_language = daemon //Swap back to Daemon. if(prob(5)) //1/20 on a 1/4 chance. 1/80 chance every 10 ticks. - owner.say(pick("Join us", "Become one with us", "Join the Flesh", "Come to us", "[owner.real_name] is just a vessel.", "You can be just like us.")) + owner.say(pick("; Accept our gift.", "; Become one with us.", "; Join our embrace.", "; Come to us.", "; We welcome all that can hear.", "; You can be just like us.")) diff --git a/code/modules/reagents/reagents/other.dm b/code/modules/reagents/reagents/other.dm index 957da0f21f..e884321e64 100644 --- a/code/modules/reagents/reagents/other.dm +++ b/code/modules/reagents/reagents/other.dm @@ -395,12 +395,28 @@ supply_conversion_value = REFINERYEXPORT_VALUE_NO industrial_use = REFINERYEXPORT_REASON_RAW coolant_modifier = 1 // It's water + var/failed_message = FALSE /datum/reagent/water/holywater/affect_ingest(var/mob/living/carbon/M, var/alien, var/removed) ..() if(ishuman(M)) // Any location if(M.mind && cult.is_antagonist(M.mind) && prob(10)) cult.remove_antagonist(M.mind) + if(prob(2)) //Get an ACTUAL chaplain for your stuff + if(M.has_modifier_of_type(/datum/modifier/redspace_corruption)) + M.remove_modifiers_of_type(/datum/modifier/redspace_corruption) + to_chat(M, "You feel calmer.") + + if(M.HasDisease(/datum/disease/fleshy_spread)) + for(var/datum/disease/fleshy_spread/disease in M.GetViruses()) + disease.cure() + break + to_chat(M, "Your fever subsides..") + if(volume < max_dose * 0.1 && !failed_message) + if(M.has_modifier_of_type(/datum/modifier/redspace_corruption) || M.HasDisease(/datum/disease/fleshy_spread)) + to_chat(M, span_notice("The power of the holy water courses through you, but seems to have failed to cure your ailments. Perhaps a larger dose is needed?")) + failed_message = TRUE + /datum/reagent/water/holywater/touch_turf(var/turf/T) ..() diff --git a/vorestation.dme b/vorestation.dme index b8c6a3d9d5..250fffb294 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -834,6 +834,7 @@ #include "code\datums\diseases\flu.dm" #include "code\datums\diseases\food_poisoning.dm" #include "code\datums\diseases\gbs.dm" +#include "code\datums\diseases\horror.dm" #include "code\datums\diseases\lycancoughy.dm" #include "code\datums\diseases\magnitis.dm" #include "code\datums\diseases\roanoke.dm" @@ -3492,6 +3493,7 @@ #include "code\modules\mob\freelook\mask\update_triggers.dm" #include "code\modules\mob\language\animal.dm" #include "code\modules\mob\language\generic.dm" +#include "code\modules\mob\language\horror.dm" #include "code\modules\mob\language\language.dm" #include "code\modules\mob\language\outsider.dm" #include "code\modules\mob\language\snowflake.dm"