diff --git a/code/__defines/dcs/signals/signals_object.dm b/code/__defines/dcs/signals/signals_object.dm index dfd22635af..267679390a 100644 --- a/code/__defines/dcs/signals/signals_object.dm +++ b/code/__defines/dcs/signals/signals_object.dm @@ -191,7 +191,7 @@ /// Return to prevent the default behavior (attack_selfing) from occurring. #define COMPONENT_ITEM_ACTION_SLOT_INVALID (1<<0) -///from base of mob/living/carbon/attacked_by(): (mob/living/carbon/target, mob/living/user, hit_zone) +///from base of mob/living/carbon/attacked_by(): (mob/living/carbon/target, mob/living/user, hit_zone) //We use hit_with_weapon here #define COMSIG_ITEM_ATTACK_ZONE "item_attack_zone" ///from base of obj/item/hit_reaction(): (owner, hitby, attack_text, final_block_chance, damage, attack_type, damage_type) #define COMSIG_ITEM_HIT_REACT "item_hit_react" diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index 1310542de2..78cea635d5 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -107,6 +107,7 @@ avoid code duplication. This includes items that may sometimes act as a standard //I would prefer to rename this attack_as_weapon(), but that would involve touching hundreds of files. /obj/item/proc/attack(mob/living/M, mob/living/user, target_zone, attack_modifier) + SEND_SIGNAL(src, COMSIG_ITEM_ATTACK, M, user, target_zone, attack_modifier) //Just that we TRIED to use it. if((!force || (flags & NOBLUDGEON)) && !(flags & ALLOW_ATTACK_ANIMATIONS)) return ITEM_INTERACT_FAILURE if(M == user && user.a_intent != I_HURT) diff --git a/code/datums/components/infective.dm b/code/datums/components/infective.dm index 019d8bd7ca..8d398b135a 100644 --- a/code/datums/components/infective.dm +++ b/code/datums/components/infective.dm @@ -56,8 +56,6 @@ RegisterSignal(parent, COMSIG_MOVABLE_IMPACT_ZONE, PROC_REF(try_infect_impact_zone)) RegisterSignal(parent, COMSIG_ATOM_EXTRAPOLATOR_ACT, PROC_REF(extrapolation)) - if(isitem(parent)) - RegisterSignal(parent, COMSIG_ITEM_ATTACK, PROC_REF(try_infect_attack)) if(isitem(parent)) RegisterSignal(parent, COMSIG_ITEM_ATTACK_ZONE, PROC_REF(try_infect_attack_zone)) RegisterSignal(parent, COMSIG_ITEM_ATTACK, PROC_REF(try_infect_attack)) diff --git a/code/datums/diseases/gbs.dm b/code/datums/diseases/gbs.dm index b024aa919b..fc7eea180c 100644 --- a/code/datums/diseases/gbs.dm +++ b/code/datums/diseases/gbs.dm @@ -11,7 +11,7 @@ agent = "Gravitokinetic Bipotential SADS+" viable_mobtypes = list(/mob/living/carbon/human) danger = DISEASE_PANDEMIC - disease_flags = CAN_NOT_POPULATE + disease_flags = CURABLE | CAN_NOT_POPULATE /datum/disease/gbs/stage_act() if(!..()) diff --git a/code/datums/diseases/horror.dm b/code/datums/diseases/horror.dm index cbea836b55..2ea75fc537 100644 --- a/code/datums/diseases/horror.dm +++ b/code/datums/diseases/horror.dm @@ -15,6 +15,7 @@ danger = DISEASE_BIOHAZARD disease_flags = CAN_NOT_POPULATE stage_prob = 1 + discovery_threshold = 0 //Doesn't show up on medical HUDs /datum/disease/fleshy_spread/stage_act() if(!..()) @@ -50,25 +51,27 @@ 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)) + if(drain_modifier && prob(0.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() + //You waited WAY too long to get this cured. You're permanently infected now. This means you're now 'infectious' + if(!(virus_modifiers & DORMANT)) + 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.")) + virus_modifiers |= DORMANT //Become dormant. This keeps antibodies from being taken from us. + return else return diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm index d642d60ab4..a61a0915a1 100644 --- a/code/game/objects/items/stacks/medical.dm +++ b/code/game/objects/items/stacks/medical.dm @@ -38,11 +38,11 @@ return ITEM_INTERACT_FAILURE if(affecting.organ_tag == BP_HEAD) - if(H.head && istype(H.head,/obj/item/clothing/head/helmet/space)) + if(H.head && (istype(H.head,/obj/item/clothing/head/helmet/space) && !istype(H.wear_suit, /obj/item/clothing/head/helmet/space/changeling))) //Changeling helmets are an extension of them. balloon_alert(user, "you can't apply [src] through [H.head]!") return ITEM_INTERACT_FAILURE else - if(H.wear_suit && istype(H.wear_suit,/obj/item/clothing/suit/space)) + if(H.wear_suit && (istype(H.wear_suit,/obj/item/clothing/suit/space) && !istype(H.wear_suit, /obj/item/clothing/suit/space/changeling))) //Changeling helmets are an extension of them. balloon_alert(user, "you can't apply [src] through [H.wear_suit]!") return ITEM_INTERACT_FAILURE diff --git a/code/modules/mob/_modifiers/horror.dm b/code/modules/mob/_modifiers/horror.dm deleted file mode 100644 index 8033051873..0000000000 --- a/code/modules/mob/_modifiers/horror.dm +++ /dev/null @@ -1,713 +0,0 @@ -// These are modifiers used for various spooky areas that are meant to be SCARY and THREATENING. -// Outside of extreme circumstances, these should not be used. -// For the primary effect, if someone is not in one of the below 'redspace_areas' Then they can not have the modifier -// applied to them. This acts as a failsafe from it from accidentally being used outside of events. -// If you DO want to use this for an event, make the event area a child of /redgate or add it to the below areas list. -// These have some extremely spooky effects and players should know about it beforehand. - - -// REDSPACE AREAS -// This list needs expansion... Currently, we have very few proper redspace areas. -// Tossing /area/redgate in here as well. Entering one of these areas (unless coded to do such) doesn't apply -// the modifier, but if you're in one of these areas, you'll keep the modifier until you leave. -GLOBAL_LIST_INIT(redspace_areas, list( - /area/redspace_abduction, - /area/redgate, - /area/survivalpod/redspace // Redspace shelters effectively pull a bit of redspace into realspace, so -)) - -/datum/modifier/redspace_drain - name = "redspace warp" - desc = "Your body is being slowly sapped of it's lifeforce, being used to fuel this hellish nightmare of a place." - - on_created_text = span_cult("You feel your body slowly being drained and warped") - on_expired_text = span_notice("Your body feels more normal.") - - stacks = MODIFIER_STACK_EXTEND - - //mob_overlay_state = "redspace_aura" //Let's be secretive~ - var/mob/living/carbon/human/unfortunate_soul //The human target of our modifier. - -/datum/modifier/redspace_drain/can_apply(mob/living/L, suppress_output = TRUE) - if(ishuman(L) && !L.isSynthetic() && L.lastarea && is_type_in_list(L.lastarea, GLOB.redspace_areas)) - return TRUE - return FALSE - -/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() - if(QDELETED(holder)) - unfortunate_soul = null - return //Don't do anything if we got QDEL'd, such as if we were gibbed - if(unfortunate_soul.stat == DEAD) //Only care if we're dead. - handle_corpse() - var/obj/effect/landmark/drop_point - drop_point = pick(GLOB.latejoin) //Can be changed to whatever exit list you want. By default, uses GLOB.latejoin - if(drop_point) - unfortunate_soul.forceMove(get_turf(drop_point)) - unfortunate_soul.maxHealth = max(50, unfortunate_soul.maxHealth) //If they died, send them back with 50 maxHealth or their current maxHealth. Whatever's higher. We're evil, but not mean. - else - message_admins("Redspace Drain expired, but no drop point was found, leaving [unfortunate_soul] in limbo. This is a bug. Please report it with this info: redspace_drain/on_expire") - unfortunate_soul = null - -/datum/modifier/redspace_drain/proc/handle_corpse() - return //Specialty stuff to do to a corpse other than teleport them. - -/datum/modifier/redspace_drain/check_if_valid() //We don't call parent. This doesn't wear off without set conditions. - if(holder.stat == DEAD) - expire(silent = TRUE) - else if(holder.lastarea && !is_type_in_list(holder.lastarea, GLOB.redspace_areas)) - expire(silent = TRUE) - -/datum/modifier/redspace_drain/tick() - if(isbelly(holder.loc)) //If you're eaten, let's hold off on doing anything spooky. - return - - //The dangerous health effects. - unfortunate_soul.nutrition = max(0, unfortunate_soul.nutrition - 5) //Your nutrition is being sapped faster than usual. - if(unfortunate_soul.life_tick % 100 == 0)// Once every 100 ticks, we mutate some organs. - choose_organs() - become_drippy() - to_chat(unfortunate_soul, span_cult("You feel as if your organs are crawling around within your body.")) - - if(unfortunate_soul.life_tick % 5 == 0) //Once every 5 ticks, we chip away at them. - unfortunate_soul.drip(1) //Blood trail. - unfortunate_soul.take_overall_damage(1) //Small bit of damage - if(unfortunate_soul.bloodstr.get_reagent_amount(REAGENT_ID_NUMBENZYME) < 2) //We lose all feeling in our body. We can't tell how injured we are. - unfortunate_soul.bloodstr.add_reagent(REAGENT_ID_NUMBENZYME,1) - if(unfortunate_soul.life_tick % 20 == 0) //Once every 20 ticks, we permanetly cripple them. - unfortunate_soul.maxHealth = max(10, unfortunate_soul.maxHealth - 1) //Max health is reduced by 1, but never below 10. This is PERMANENT for the rest of the round or until resleeving. - - - //The mental effects. - unfortunate_soul.fear = min(100, unfortunate_soul.fear + 2) //Fear is increased by 1, but never above 100. You're in a scary place. - if(unfortunate_soul.life_tick % 20 == 0) - var/obj/item/organ/O = pick(unfortunate_soul.internal_organs) - if(!O) //If you don't have any internal organs, you know what? No spooky messages for you, freak. - var/spooky_message = pick("Join us...", "Stay with us...", "Stay forever...", "Don't leave us...", \ - "Don't go...", "We can be as one...", "Become one with us...", \ - "You can feel your [O] squirming inside of you, trying to get out...", "Your [O] is trying to escape...", \ - "Your [O] itches.", "Your [O] is crawling around inside of you.") - to_chat(unfortunate_soul, span_cult(spooky_message)) - unfortunate_soul.make_dizzy(5) - 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(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) - if("eyes") - var/obj/item/organ/internal/eyes/E = unfortunate_soul.internal_organs_by_name[O_EYES] - if(E) - replace_eyes(E) - if("heart") - var/obj/item/organ/internal/heart/H = unfortunate_soul.internal_organs_by_name[O_HEART] - if(H) - replace_heart(H) - if("lungs") - var/obj/item/organ/internal/lungs/L = unfortunate_soul.internal_organs_by_name[O_LUNGS] - if(L) - replace_lungs(L) - if("liver") - var/obj/item/organ/internal/liver/L = unfortunate_soul.internal_organs_by_name[O_LIVER] - if(L) - replace_liver(L) - if("kidneys") - var/obj/item/organ/internal/kidneys/K = unfortunate_soul.internal_organs_by_name[O_KIDNEYS] - if(K) - replace_kidneys(K) - if("appendix") - var/obj/item/organ/internal/appendix/A = unfortunate_soul.internal_organs_by_name[O_APPENDIX] - if(A) - replace_appendix(A) - if("voicebox") - var/obj/item/organ/internal/voicebox/V = unfortunate_soul.internal_organs_by_name[O_VOICE] - if(V) - replace_voicebox(V) - if("spleen") - var/obj/item/organ/internal/spleen/S = unfortunate_soul.internal_organs_by_name[O_SPLEEN] - if(S) - replace_spleen(S) - if("stomach") - var/obj/item/organ/internal/stomach/S = unfortunate_soul.internal_organs_by_name[O_STOMACH] - if(S) - replace_stomach(S) - if("intestine") - var/obj/item/organ/internal/intestine/E = unfortunate_soul.internal_organs_by_name[O_INTESTINE] - if(E) - replace_intestine(E) - -/datum/modifier/redspace_drain/proc/replace_eyes(obj/item/organ/internal/O) - if(istype(O, /obj/item/organ/internal/eyes/horror)) - return - var/organ_spot = O.parent_organ - var/obj/item/organ/internal/eyes/new_organ = new /obj/item/organ/internal/eyes/horror() - O.removed(unfortunate_soul) - qdel(O) - new_organ.replaced(unfortunate_soul,unfortunate_soul.get_organ(organ_spot)) - var/random_name = pick("pulsating", "quivering", "throbbing", "crawling", "oozing", "melting", "gushing", "dripping", "twitching", "slimy", "gooey") - new_organ.name = "[random_name] [initial(new_organ.name)]" - -/datum/modifier/redspace_drain/proc/replace_heart(obj/item/organ/internal/O) - if(istype(O, /obj/item/organ/internal/heart/horror)) - return - var/organ_spot = O.parent_organ - var/obj/item/organ/internal/heart/new_organ = new /obj/item/organ/internal/heart/horror() - O.removed(unfortunate_soul) - qdel(O) - new_organ.replaced(unfortunate_soul,unfortunate_soul.get_organ(organ_spot)) - var/random_name = pick("pulsating", "quivering", "throbbing", "crawling", "oozing", "melting", "gushing", "dripping", "twitching", "slimy", "gooey") - new_organ.name = "[random_name] [initial(new_organ.name)]" - -/datum/modifier/redspace_drain/proc/replace_lungs(obj/item/organ/internal/O) - if(istype(O, /obj/item/organ/internal/lungs/horror)) - return - var/organ_spot = O.parent_organ - var/obj/item/organ/internal/lungs/new_organ = new /obj/item/organ/internal/lungs/horror() - O.removed(unfortunate_soul) - qdel(O) - new_organ.replaced(unfortunate_soul,unfortunate_soul.get_organ(organ_spot)) - var/random_name = pick("pulsating", "quivering", "throbbing", "crawling", "oozing", "melting", "gushing", "dripping", "twitching", "slimy", "gooey") - new_organ.name = "[random_name] [initial(new_organ.name)]" - -/datum/modifier/redspace_drain/proc/replace_liver(obj/item/organ/internal/O) - if(istype(O, /obj/item/organ/internal/liver/horror)) - return - var/organ_spot = O.parent_organ - var/obj/item/organ/internal/liver/new_organ = new /obj/item/organ/internal/liver/horror() - O.removed(unfortunate_soul) - qdel(O) - new_organ.replaced(unfortunate_soul,unfortunate_soul.get_organ(organ_spot)) - var/random_name = pick("pulsating", "quivering", "throbbing", "crawling", "oozing", "melting", "gushing", "dripping", "twitching", "slimy", "gooey") - new_organ.name = "[random_name] [initial(new_organ.name)]" - -/datum/modifier/redspace_drain/proc/replace_kidneys(obj/item/organ/internal/O) - if(istype(O, /obj/item/organ/internal/kidneys/horror)) - return - var/organ_spot = O.parent_organ - var/obj/item/organ/internal/kidneys/new_organ = new /obj/item/organ/internal/kidneys/horror() - O.removed(unfortunate_soul) - qdel(O) - new_organ.replaced(unfortunate_soul,unfortunate_soul.get_organ(organ_spot)) - var/random_name = pick("pulsating", "quivering", "throbbing", "crawling", "oozing", "melting", "gushing", "dripping", "twitching", "slimy", "gooey") - new_organ.name = "[random_name] [initial(new_organ.name)]" - -/datum/modifier/redspace_drain/proc/replace_appendix(obj/item/organ/internal/O) - if(istype(O, /obj/item/organ/internal/appendix/horror)) - return - var/organ_spot = O.parent_organ - var/obj/item/organ/internal/appendix/new_organ = new /obj/item/organ/internal/appendix/horror() - O.removed(unfortunate_soul) - qdel(O) - new_organ.replaced(unfortunate_soul,unfortunate_soul.get_organ(organ_spot)) - var/random_name = pick("pulsating", "quivering", "throbbing", "crawling", "oozing", "melting", "gushing", "dripping", "twitching", "slimy", "gooey") - new_organ.name = "[random_name] [initial(new_organ.name)]" - -/datum/modifier/redspace_drain/proc/replace_voicebox(obj/item/organ/internal/O) - if(istype(O, /obj/item/organ/internal/voicebox/horror)) - return - var/organ_spot = O.parent_organ - var/obj/item/organ/internal/voicebox/new_organ = new /obj/item/organ/internal/voicebox/horror() - O.removed(unfortunate_soul) - qdel(O) - new_organ.replaced(unfortunate_soul,unfortunate_soul.get_organ(organ_spot)) - var/random_name = pick("pulsating", "quivering", "throbbing", "crawling", "oozing", "melting", "gushing", "dripping", "twitching", "slimy", "gooey") - new_organ.name = "[random_name] [initial(new_organ.name)]" - -/datum/modifier/redspace_drain/proc/replace_spleen(obj/item/organ/internal/O) - if(istype(O, /obj/item/organ/internal/spleen/horror)) - return - var/organ_spot = O.parent_organ - var/obj/item/organ/internal/spleen/new_organ = new /obj/item/organ/internal/spleen/horror() - O.removed(unfortunate_soul) - qdel(O) - new_organ.replaced(unfortunate_soul,unfortunate_soul.get_organ(organ_spot)) - var/random_name = pick("pulsating", "quivering", "throbbing", "crawling", "oozing", "melting", "gushing", "dripping", "twitching", "slimy", "gooey") - new_organ.name = "[random_name] [initial(new_organ.name)]" - -/datum/modifier/redspace_drain/proc/replace_stomach(obj/item/organ/internal/O) - if(istype(O, /obj/item/organ/internal/stomach/horror)) - return - var/organ_spot = O.parent_organ - var/obj/item/organ/internal/stomach/new_organ = new /obj/item/organ/internal/stomach/horror() - O.removed(unfortunate_soul) - qdel(O) - new_organ.replaced(unfortunate_soul,unfortunate_soul.get_organ(organ_spot)) - var/random_name = pick("pulsating", "quivering", "throbbing", "crawling", "oozing", "melting", "gushing", "dripping", "twitching", "slimy", "gooey") - new_organ.name = "[random_name] [initial(new_organ.name)]" - -/datum/modifier/redspace_drain/proc/replace_intestine(obj/item/organ/internal/O) - if(istype(O, /obj/item/organ/internal/intestine/horror)) - return - var/organ_spot = O.parent_organ - var/obj/item/organ/internal/intestine/new_organ = new /obj/item/organ/internal/intestine/horror() - O.removed(unfortunate_soul) - qdel(O) - new_organ.replaced(unfortunate_soul,unfortunate_soul.get_organ(organ_spot)) - 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) - if(O) - var/random_name = pick("pulsating", "quivering", "throbbing", "crawling", "oozing", "melting", "gushing", "dripping", "twitching", "slimy", "gooey") - O.name = "[random_name] [initial(O.name)]" - O.desc = "A twisted, warped version of a [initial(O.name)] covered in thick, red, pulsating tendrils." - O.take_damage(3) - O.color = "#760b0b" - O.add_autopsy_data("ANOMALOUS FLESH GROWTH", 3) - O.decays = FALSE - O.meat_type = /obj/item/reagent_containers/food/snacks/meat/worm //It turns into 'weird meat' with the desc of 'A chunk of pulsating meat' - O.can_reject = FALSE -*/ - -/datum/modifier/redspace_drain/proc/become_drippy() - if(!(unfortunate_soul.species.flags & NO_DNA)) //Doing it as such in case drippy is ever made NOT a trait gene. - var/datum/gene/trait/drippy_trait = get_gene_from_trait(/datum/trait/neutral/drippy) - unfortunate_soul.dna.SetSEState(drippy_trait.block, TRUE) - domutcheck(unfortunate_soul, null, GENE_ALWAYS_ACTIVATE) - unfortunate_soul.UpdateAppearance() - -/datum/modifier/redsight - name = "redsight" - desc = "You can see into the unknown." - client_color = "#ce6161" - - on_created_text = span_alien("You feel as though you can see the horrors of reality!") - on_expired_text = span_notice("Your sight returns to what it once was.") - stacks = MODIFIER_STACK_EXTEND - -/datum/modifier/redsight/on_applied() - holder.see_invisible = 60 - holder.see_invisible_default = 60 - holder.vis_enabled += VIS_GHOSTS - holder.recalculate_vis() - -/datum/modifier/redsight/on_expire() - holder.see_invisible_default = initial(holder.see_invisible_default) - holder.see_invisible = holder.see_invisible_default - holder.vis_enabled -= VIS_GHOSTS - holder.recalculate_vis() - -/datum/modifier/redsight/can_apply(mob/living/L) - if(L.stat) - to_chat(L, span_warning("You can't be unconscious or dead to see the unknown.")) - return FALSE - var/obj/item/organ/internal/eyes/E = L.internal_organs_by_name[O_EYES] - if(E && istype(E, /obj/item/organ/internal/eyes/horror)) - return ..() - return FALSE - -/datum/modifier/redsight/check_if_valid() //We don't call parent. This doesn't wear off without set conditions. - //Dead? - if(holder.stat == DEAD) - expire(silent = TRUE) - //We got eyes and they're special eyes? - var/obj/item/organ/internal/eyes/E = holder.internal_organs_by_name[O_EYES] - if(!E) - expire(silent = TRUE) - else if(!istype(E, /obj/item/organ/internal/eyes/horror)) - expire(silent = TRUE) - -/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(armor_type, helmet_type, boot_type, 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/_modifiers/horror/_horror.dm b/code/modules/mob/_modifiers/horror/_horror.dm new file mode 100644 index 0000000000..5aee8f45e7 --- /dev/null +++ b/code/modules/mob/_modifiers/horror/_horror.dm @@ -0,0 +1,17 @@ +// These are modifiers used for various spooky areas that are meant to be SCARY and THREATENING. +// Outside of extreme circumstances, these should not be used. +// For the primary effect, if someone is not in one of the below 'redspace_areas' Then they can not have the modifier +// applied to them. This acts as a failsafe from it from accidentally being used outside of events. +// If you DO want to use this for an event, make the event area a child of /redgate or add it to the below areas list. +// These have some extremely spooky effects and players should know about it beforehand. + + +// REDSPACE AREAS +// This list needs expansion... Currently, we have very few proper redspace areas. +// Tossing /area/redgate in here as well. Entering one of these areas (unless coded to do such) doesn't apply +// the modifier, but if you're in one of these areas, you'll keep the modifier until you leave. +GLOBAL_LIST_INIT(redspace_areas, list( + /area/redspace_abduction, + /area/redgate, + /area/survivalpod/redspace // Redspace shelters effectively pull a bit of redspace into realspace, so +)) diff --git a/code/modules/mob/_modifiers/horror/horror_helpers.dm b/code/modules/mob/_modifiers/horror/horror_helpers.dm new file mode 100644 index 0000000000..9e54798511 --- /dev/null +++ b/code/modules/mob/_modifiers/horror/horror_helpers.dm @@ -0,0 +1,109 @@ +/datum/modifier/redspace_drain/proc/replace_eyes(obj/item/organ/internal/O) + if(istype(O, /obj/item/organ/internal/eyes/horror)) + return + var/organ_spot = O.parent_organ + var/obj/item/organ/internal/eyes/new_organ = new /obj/item/organ/internal/eyes/horror() + O.removed(unfortunate_soul) + qdel(O) + new_organ.replaced(unfortunate_soul,unfortunate_soul.get_organ(organ_spot)) + var/random_name = pick("pulsating", "quivering", "throbbing", "crawling", "oozing", "melting", "gushing", "dripping", "twitching", "slimy", "gooey") + new_organ.name = "[random_name] [initial(new_organ.name)]" + +/datum/modifier/redspace_drain/proc/replace_heart(obj/item/organ/internal/O) + if(istype(O, /obj/item/organ/internal/heart/horror)) + return + var/organ_spot = O.parent_organ + var/obj/item/organ/internal/heart/new_organ = new /obj/item/organ/internal/heart/horror() + O.removed(unfortunate_soul) + qdel(O) + new_organ.replaced(unfortunate_soul,unfortunate_soul.get_organ(organ_spot)) + var/random_name = pick("pulsating", "quivering", "throbbing", "crawling", "oozing", "melting", "gushing", "dripping", "twitching", "slimy", "gooey") + new_organ.name = "[random_name] [initial(new_organ.name)]" + +/datum/modifier/redspace_drain/proc/replace_lungs(obj/item/organ/internal/O) + if(istype(O, /obj/item/organ/internal/lungs/horror)) + return + var/organ_spot = O.parent_organ + var/obj/item/organ/internal/lungs/new_organ = new /obj/item/organ/internal/lungs/horror() + O.removed(unfortunate_soul) + qdel(O) + new_organ.replaced(unfortunate_soul,unfortunate_soul.get_organ(organ_spot)) + var/random_name = pick("pulsating", "quivering", "throbbing", "crawling", "oozing", "melting", "gushing", "dripping", "twitching", "slimy", "gooey") + new_organ.name = "[random_name] [initial(new_organ.name)]" + +/datum/modifier/redspace_drain/proc/replace_liver(obj/item/organ/internal/O) + if(istype(O, /obj/item/organ/internal/liver/horror)) + return + var/organ_spot = O.parent_organ + var/obj/item/organ/internal/liver/new_organ = new /obj/item/organ/internal/liver/horror() + O.removed(unfortunate_soul) + qdel(O) + new_organ.replaced(unfortunate_soul,unfortunate_soul.get_organ(organ_spot)) + var/random_name = pick("pulsating", "quivering", "throbbing", "crawling", "oozing", "melting", "gushing", "dripping", "twitching", "slimy", "gooey") + new_organ.name = "[random_name] [initial(new_organ.name)]" + +/datum/modifier/redspace_drain/proc/replace_kidneys(obj/item/organ/internal/O) + if(istype(O, /obj/item/organ/internal/kidneys/horror)) + return + var/organ_spot = O.parent_organ + var/obj/item/organ/internal/kidneys/new_organ = new /obj/item/organ/internal/kidneys/horror() + O.removed(unfortunate_soul) + qdel(O) + new_organ.replaced(unfortunate_soul,unfortunate_soul.get_organ(organ_spot)) + var/random_name = pick("pulsating", "quivering", "throbbing", "crawling", "oozing", "melting", "gushing", "dripping", "twitching", "slimy", "gooey") + new_organ.name = "[random_name] [initial(new_organ.name)]" + +/datum/modifier/redspace_drain/proc/replace_appendix(obj/item/organ/internal/O) + if(istype(O, /obj/item/organ/internal/appendix/horror)) + return + var/organ_spot = O.parent_organ + var/obj/item/organ/internal/appendix/new_organ = new /obj/item/organ/internal/appendix/horror() + O.removed(unfortunate_soul) + qdel(O) + new_organ.replaced(unfortunate_soul,unfortunate_soul.get_organ(organ_spot)) + var/random_name = pick("pulsating", "quivering", "throbbing", "crawling", "oozing", "melting", "gushing", "dripping", "twitching", "slimy", "gooey") + new_organ.name = "[random_name] [initial(new_organ.name)]" + +/datum/modifier/redspace_drain/proc/replace_voicebox(obj/item/organ/internal/O) + if(istype(O, /obj/item/organ/internal/voicebox/horror)) + return + var/organ_spot = O.parent_organ + var/obj/item/organ/internal/voicebox/new_organ = new /obj/item/organ/internal/voicebox/horror() + O.removed(unfortunate_soul) + qdel(O) + new_organ.replaced(unfortunate_soul,unfortunate_soul.get_organ(organ_spot)) + var/random_name = pick("pulsating", "quivering", "throbbing", "crawling", "oozing", "melting", "gushing", "dripping", "twitching", "slimy", "gooey") + new_organ.name = "[random_name] [initial(new_organ.name)]" + +/datum/modifier/redspace_drain/proc/replace_spleen(obj/item/organ/internal/O) + if(istype(O, /obj/item/organ/internal/spleen/horror)) + return + var/organ_spot = O.parent_organ + var/obj/item/organ/internal/spleen/new_organ = new /obj/item/organ/internal/spleen/horror() + O.removed(unfortunate_soul) + qdel(O) + new_organ.replaced(unfortunate_soul,unfortunate_soul.get_organ(organ_spot)) + var/random_name = pick("pulsating", "quivering", "throbbing", "crawling", "oozing", "melting", "gushing", "dripping", "twitching", "slimy", "gooey") + new_organ.name = "[random_name] [initial(new_organ.name)]" + +/datum/modifier/redspace_drain/proc/replace_stomach(obj/item/organ/internal/O) + if(istype(O, /obj/item/organ/internal/stomach/horror)) + return + var/organ_spot = O.parent_organ + var/obj/item/organ/internal/stomach/new_organ = new /obj/item/organ/internal/stomach/horror() + O.removed(unfortunate_soul) + qdel(O) + new_organ.replaced(unfortunate_soul,unfortunate_soul.get_organ(organ_spot)) + var/random_name = pick("pulsating", "quivering", "throbbing", "crawling", "oozing", "melting", "gushing", "dripping", "twitching", "slimy", "gooey") + new_organ.name = "[random_name] [initial(new_organ.name)]" + +/datum/modifier/redspace_drain/proc/replace_intestine(obj/item/organ/internal/O) + if(istype(O, /obj/item/organ/internal/intestine/horror)) + return + var/organ_spot = O.parent_organ + var/obj/item/organ/internal/intestine/new_organ = new /obj/item/organ/internal/intestine/horror() + O.removed(unfortunate_soul) + qdel(O) + new_organ.replaced(unfortunate_soul,unfortunate_soul.get_organ(organ_spot)) + var/random_name = pick("pulsating", "quivering", "throbbing", "crawling", "oozing", "melting", "gushing", "dripping", "twitching", "slimy", "gooey") + new_organ.name = "[random_name] [initial(new_organ.name)]" diff --git a/code/modules/mob/_modifiers/horror/redsight.dm b/code/modules/mob/_modifiers/horror/redsight.dm new file mode 100644 index 0000000000..ac3f4a6cac --- /dev/null +++ b/code/modules/mob/_modifiers/horror/redsight.dm @@ -0,0 +1,38 @@ +/datum/modifier/redsight + name = "redsight" + desc = "You can see into the unknown." + client_color = "#ce6161" + + on_created_text = span_alien("You feel as though you can see the horrors of reality!") + on_expired_text = span_notice("Your sight returns to what it once was.") + stacks = MODIFIER_STACK_EXTEND + +/datum/modifier/redsight/on_applied() + holder.see_invisible = 60 + holder.see_invisible_default = 60 + holder.vis_enabled += VIS_GHOSTS + holder.recalculate_vis() + +/datum/modifier/redsight/on_expire() + holder.see_invisible_default = initial(holder.see_invisible_default) + holder.see_invisible = holder.see_invisible_default + holder.vis_enabled -= VIS_GHOSTS + holder.recalculate_vis() + +/datum/modifier/redsight/can_apply(mob/living/L) + if(L.stat) + to_chat(L, span_warning("You can't be unconscious or dead to see the unknown.")) + return FALSE + var/obj/item/organ/internal/eyes/E = L.internal_organs_by_name[O_EYES] + if(E && istype(E, /obj/item/organ/internal/eyes/horror)) + return ..() + return FALSE + +/datum/modifier/redsight/check_if_valid() //We don't call parent. This doesn't wear off without set conditions. + //Dead? + if(holder.stat == DEAD) + expire(silent = TRUE) + //We got eyes and they're special eyes? + var/obj/item/organ/internal/eyes/E = holder.internal_organs_by_name[O_EYES] + if(!E || !istype(E, /obj/item/organ/internal/eyes/horror)) + expire(silent = TRUE) diff --git a/code/modules/mob/_modifiers/horror/redspace_corruption.dm b/code/modules/mob/_modifiers/horror/redspace_corruption.dm new file mode 100644 index 0000000000..0ed426d101 --- /dev/null +++ b/code/modules/mob/_modifiers/horror/redspace_corruption.dm @@ -0,0 +1,365 @@ +///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 = 0.5 + + ///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 = 1 MINUTE + + ///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 + exit_battle_stance() + 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() + 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! + continue + if(is_changeling(target)) + continue + if(target.HasDisease(/datum/disease/fleshy_spread)) + 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, 3) + 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 + return + +/datum/modifier/redspace_corruption/proc/handle_death() + + //Cooldown? + + if(armor_deployed) + 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) + var/datum/disease/fleshy_spread/flesh_disease = new /datum/disease/fleshy_spread + blade.AddComponent(/datum/component/infective, \ + flesh_disease, \ + weak = FALSE) + 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(armor_type, helmet_type, boot_type, glove_type) + if(!armor_type) + armor_type = /obj/item/clothing/suit/space/changeling/armored + if(!helmet_type) + helmet_type = /obj/item/clothing/head/helmet/space/changeling/armored + if(!boot_type) + boot_type = /obj/item/clothing/shoes/magboots/changeling/armored + if(!glove_type) + glove_type = /obj/item/clothing/gloves/combat/changeling + + 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()) + 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() + armor_deployed = FALSE + unfortunate_soul.drop_l_hand() + unfortunate_soul.drop_r_hand() diff --git a/code/modules/mob/_modifiers/horror/redspace_drain.dm b/code/modules/mob/_modifiers/horror/redspace_drain.dm new file mode 100644 index 0000000000..d8d5586a08 --- /dev/null +++ b/code/modules/mob/_modifiers/horror/redspace_drain.dm @@ -0,0 +1,182 @@ +/datum/modifier/redspace_drain + name = "redspace warp" + desc = "Your body is being slowly sapped of it's lifeforce, being used to fuel this hellish nightmare of a place." + + on_created_text = span_cult("You feel your body slowly being drained and warped") + on_expired_text = span_notice("Your body feels more normal.") + + stacks = MODIFIER_STACK_EXTEND + + //mob_overlay_state = "redspace_aura" //Let's be secretive~ + var/mob/living/carbon/human/unfortunate_soul //The human target of our modifier. + ///How long we have had the redspace modifier on us. + var/current_time = 0 + +/datum/modifier/redspace_drain/can_apply(mob/living/L, suppress_output = TRUE) + if(ishuman(L) && !L.isSynthetic() && L.lastarea && is_type_in_list(L.lastarea, GLOB.redspace_areas)) + return TRUE + return FALSE + +/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() + if(QDELETED(holder)) + unfortunate_soul = null + return //Don't do anything if we got QDEL'd, such as if we were gibbed + if(unfortunate_soul.stat == DEAD) //Only care if we're dead. + handle_corpse() + var/obj/effect/landmark/drop_point + drop_point = pick(GLOB.latejoin) //Can be changed to whatever exit list you want. By default, uses GLOB.latejoin + if(drop_point) + unfortunate_soul.forceMove(get_turf(drop_point)) + unfortunate_soul.maxHealth = max(50, unfortunate_soul.maxHealth) //If they died, send them back with 50 maxHealth or their current maxHealth. Whatever's higher. We're evil, but not mean. + else + message_admins("Redspace Drain expired, but no drop point was found, leaving [unfortunate_soul] in limbo. This is a bug. Please report it with this info: redspace_drain/on_expire") + unfortunate_soul = null + +/datum/modifier/redspace_drain/proc/handle_corpse() + return //Specialty stuff to do to a corpse other than teleport them. + +/datum/modifier/redspace_drain/check_if_valid() //We don't call parent. This doesn't wear off without set conditions. + if(holder.stat == DEAD) + expire(silent = TRUE) + else if(holder.lastarea && !is_type_in_list(holder.lastarea, GLOB.redspace_areas)) + expire(silent = TRUE) + +/datum/modifier/redspace_drain/tick() + if(isbelly(holder.loc)) //If you're eaten, let's hold off on doing anything spooky. + return + current_time++ + + if(current_time % 5 == 0) //Once every 5 ticks, we chip away at them. + unfortunate_soul.drip(1) //Blood trail. + unfortunate_soul.take_overall_damage(1) //Small bit of damage + if(unfortunate_soul.bloodstr.get_reagent_amount(REAGENT_ID_NUMBENZYME) < 2) //We lose all feeling in our body. We can't tell how injured we are. + unfortunate_soul.bloodstr.add_reagent(REAGENT_ID_NUMBENZYME,1) + + if(current_time % 20 == 0) //Once every 20 ticks, we permanetly cripple them. + unfortunate_soul.maxHealth = max(10, unfortunate_soul.maxHealth - 1) //Max health is reduced by 1, but never below 10. This is PERMANENT for the rest of the round or until resleeving. + + //The dangerous health effects. + if(current_time % 100 == 0)// Once every 100 ticks, we mutate some organs. + choose_organs() + become_drippy() + to_chat(unfortunate_soul, span_cult("You feel as if your organs are crawling around within your body.")) + + //The mental effects. + unfortunate_soul.fear = min(100, unfortunate_soul.fear + 2) //Fear is increased by 1, but never above 100. You're in a scary place. + if(current_time % 20 == 0) + var/obj/item/organ/O = pick(unfortunate_soul.internal_organs) + if(!O) //If you don't have any internal organs, you know what? No spooky messages for you, freak. + var/spooky_message = pick("Join us...", "Stay with us...", "Stay forever...", "Don't leave us...", \ + "Don't go...", "We can be as one...", "Become one with us...", \ + "You can feel your [O] squirming inside of you, trying to get out...", "Your [O] is trying to escape...", \ + "Your [O] itches.", "Your [O] is crawling around inside of you.") + to_chat(unfortunate_soul, span_cult(spooky_message)) + unfortunate_soul.make_dizzy(5) + 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(organs_to_replace) + if(!organs_to_replace) + organs_to_replace = rand(1,2) + 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) + if("eyes") + var/obj/item/organ/internal/eyes/E = unfortunate_soul.internal_organs_by_name[O_EYES] + if(E) + replace_eyes(E) + if("heart") + var/obj/item/organ/internal/heart/H = unfortunate_soul.internal_organs_by_name[O_HEART] + if(H) + replace_heart(H) + if("lungs") + var/obj/item/organ/internal/lungs/L = unfortunate_soul.internal_organs_by_name[O_LUNGS] + if(L) + replace_lungs(L) + if("liver") + var/obj/item/organ/internal/liver/L = unfortunate_soul.internal_organs_by_name[O_LIVER] + if(L) + replace_liver(L) + if("kidneys") + var/obj/item/organ/internal/kidneys/K = unfortunate_soul.internal_organs_by_name[O_KIDNEYS] + if(K) + replace_kidneys(K) + if("appendix") + var/obj/item/organ/internal/appendix/A = unfortunate_soul.internal_organs_by_name[O_APPENDIX] + if(A) + replace_appendix(A) + if("voicebox") + var/obj/item/organ/internal/voicebox/V = unfortunate_soul.internal_organs_by_name[O_VOICE] + if(V) + replace_voicebox(V) + if("spleen") + var/obj/item/organ/internal/spleen/S = unfortunate_soul.internal_organs_by_name[O_SPLEEN] + if(S) + replace_spleen(S) + if("stomach") + var/obj/item/organ/internal/stomach/S = unfortunate_soul.internal_organs_by_name[O_STOMACH] + if(S) + replace_stomach(S) + if("intestine") + var/obj/item/organ/internal/intestine/E = unfortunate_soul.internal_organs_by_name[O_INTESTINE] + if(E) + replace_intestine(E) + +///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) + if(O) + var/random_name = pick("pulsating", "quivering", "throbbing", "crawling", "oozing", "melting", "gushing", "dripping", "twitching", "slimy", "gooey") + O.name = "[random_name] [initial(O.name)]" + O.desc = "A twisted, warped version of a [initial(O.name)] covered in thick, red, pulsating tendrils." + O.take_damage(3) + O.color = "#760b0b" + O.add_autopsy_data("ANOMALOUS FLESH GROWTH", 3) + O.decays = FALSE + O.meat_type = /obj/item/reagent_containers/food/snacks/meat/worm //It turns into 'weird meat' with the desc of 'A chunk of pulsating meat' + O.can_reject = FALSE +*/ + +/datum/modifier/redspace_drain/proc/become_drippy() + if(!(unfortunate_soul.species.flags & NO_DNA)) //Doing it as such in case drippy is ever made NOT a trait gene. + var/datum/gene/trait/drippy_trait = get_gene_from_trait(/datum/trait/neutral/drippy) + unfortunate_soul.dna.SetSEState(drippy_trait.block, TRUE) + domutcheck(unfortunate_soul, null, GENE_ALWAYS_ACTIVATE) + unfortunate_soul.UpdateAppearance() diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index b44c97d666..ebf0ef73e0 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -234,6 +234,7 @@ emp_act return hit_zone /mob/living/carbon/human/hit_with_weapon(obj/item/I, mob/living/user, effective_force, hit_zone, hide_attack_message) + SEND_SIGNAL(I, COMSIG_ITEM_ATTACK_ZONE, I, user, hit_zone, effective_force) var/obj/item/organ/external/affecting = get_organ(hit_zone) if(!affecting) return //should be prevented by attacked_with_item() but for sanity. diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 05d1af5975..5f8727ef82 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -214,6 +214,7 @@ //Called when the mob is hit with an item in combat. Returns the blocked result /mob/living/proc/hit_with_weapon(obj/item/I, mob/living/user, effective_force, hit_zone, hide_attack_message) + SEND_SIGNAL(I, COMSIG_ITEM_ATTACK_ZONE, I, user, hit_zone, effective_force) if(!hide_attack_message) visible_message(span_danger("[src] has been [LAZYLEN(I.attack_verb) ? pick(I.attack_verb) : "attacked"] with [I.name] by [user]!")) diff --git a/vorestation.dme b/vorestation.dme index 07d080483b..194c8d6232 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -3527,7 +3527,6 @@ #include "code\modules\mob\_modifiers\cloning.dm" #include "code\modules\mob\_modifiers\crusher_mark.dm" #include "code\modules\mob\_modifiers\feysight.dm" -#include "code\modules\mob\_modifiers\horror.dm" #include "code\modules\mob\_modifiers\medical.dm" #include "code\modules\mob\_modifiers\modifiers.dm" #include "code\modules\mob\_modifiers\modifiers_misc.dm" @@ -3535,6 +3534,11 @@ #include "code\modules\mob\_modifiers\traits.dm" #include "code\modules\mob\_modifiers\traits_phobias.dm" #include "code\modules\mob\_modifiers\unholy.dm" +#include "code\modules\mob\_modifiers\horror\_horror.dm" +#include "code\modules\mob\_modifiers\horror\horror_helpers.dm" +#include "code\modules\mob\_modifiers\horror\redsight.dm" +#include "code\modules\mob\_modifiers\horror\redspace_corruption.dm" +#include "code\modules\mob\_modifiers\horror\redspace_drain.dm" #include "code\modules\mob\dead\corpse.dm" #include "code\modules\mob\dead\corpse_vox.dm" #include "code\modules\mob\dead\corpse_vr.dm"