"
+
+ var/html = get_html_template(content)
+
+ usr << browse(html, "window=ViewHorror\ref[src]Chems;size=600x800")
+
+/mob/living/simple_animal/horror/proc/hide()
+ if(victim)
+ to_chat(src, span_warning("You cannot do this while you're inside a host."))
+ return
+
+ if(stat != CONSCIOUS)
+ return
+
+ if(!hiding)
+ layer = LATTICE_LAYER
+ visible_message(span_name("[src] scurries to the ground!"), \
+ span_noticealien("You are now hiding."))
+ hiding = TRUE
+ else
+ layer = MOB_LAYER
+ visible_message("[src] slowly peaks up from the ground...", \
+ span_noticealien("You stop hiding."))
+ hiding = FALSE
+
+/mob/living/simple_animal/horror/proc/go_invisible()
+ if(victim)
+ to_chat(src, span_warning("You cannot do this while you're inside a host."))
+ return
+
+ if(!can_use_ability())
+ return
+
+ if(!has_chemicals(10))
+ to_chat(src, span_warning("You don't have enough chemicals to do that."))
+ return
+
+ if(!invisible)
+ to_chat(src, span_noticealien("You focus your chameleon skin to blend into the environment."))
+ invisible = TRUE
+ else
+ to_chat(src, span_noticealien("You stop your camouflage."))
+ invisible = FALSE
+
+/mob/living/simple_animal/horror/proc/freeze_victim()
+ if(world.time - used_freeze < 150)
+ to_chat(src, span_warning("You cannot use that ability again so soon."))
+ return
+
+ if(victim)
+ to_chat(src, span_warning("You cannot do that from within a host body."))
+ return
+
+ if(!can_use_ability())
+ return
+
+ var/list/choices = list()
+ for(var/mob/living/carbon/C in view(1,src))
+ if(C.stat == CONSCIOUS)
+ choices += C
+
+ if(!choices.len)
+ return
+
+ if(QDELETED(src) || stat != CONSCIOUS || victim || (world.time - used_freeze < 150))
+ return
+
+ layer = MOB_LAYER
+ for (var/mob/living/carbon/M in range(1, src))
+ if(!M || !Adjacent(M))
+ return
+
+ if(has_upgrade("paralysis"))
+ playsound(loc, "sound/effects/sparks4.ogg", 30, 1, -1)
+ M.Stun(50)
+ M.SetSleeping(70) //knocked out cold
+ M.electrocute_act(15, src, 1, FALSE, FALSE, FALSE, 1, FALSE)
+ else
+ to_chat(M, span_userdanger("You feel something wrapping around your leg, pulling you down!"))
+ playsound(loc, "sound/weapons/whipgrab.ogg", 30, 1, -1)
+ M.Immobilize(50)
+ M.Knockdown(70)
+ used_freeze = world.time
+
+/mob/living/simple_animal/horror/proc/is_leaving()
+ return leaving
+
+/mob/living/simple_animal/horror/proc/release_victim()
+ if(!victim)
+ to_chat(src, span_danger("You are not inside a host body."))
+ return
+
+ if(!can_use_ability())
+ return
+
+ if(leaving)
+ leaving = FALSE
+ to_chat(src, span_danger("You decide against leaving your host."))
+ return
+
+ to_chat(src, span_danger("You begin disconnecting from [victim]'s synapses and prodding at their internal ear canal."))
+
+ if(victim.stat != DEAD && !has_upgrade("invisible_exit"))
+ to_chat(victim, span_userdanger("An odd, uncomfortable pressure begins to build inside your skull, behind your ear..."))
+
+ leaving = TRUE
+ if(do_after(src, 100, target = victim, extra_checks = CALLBACK(src, .proc/is_leaving), stayStill = FALSE))
+ release_host()
+
+/mob/living/simple_animal/horror/proc/release_host()
+ if(!victim || QDELETED(victim) || QDELETED(src) || controlling)
+ return
+
+ if(!can_use_ability())
+ return
+
+ if(has_upgrade("invisible_exit"))
+ alpha = 60
+ if(has_ability(/datum/action/innate/horror/chameleon))
+ invisible = TRUE
+ Update_Invisibility_Button()
+ to_chat(src, span_danger("You silently wiggle out of [victim]'s ear and plop to the ground before vanishing via reflective solution that covers you."))
+ else
+ to_chat(src, span_danger("You wiggle out of [victim]'s ear and plop to the ground."))
+ if(victim.mind)
+ if(!has_upgrade("invisible_exit"))
+ to_chat(victim, span_danger("Something slimy wiggles out of your ear and plops to the ground!"))
+
+ leaving = FALSE
+
+ leave_victim()
+
+/mob/living/simple_animal/horror/proc/leave_victim()
+ if(!victim)
+ return
+
+ if(controlling)
+ detatch()
+
+ forceMove(get_turf(victim))
+
+ reset_perspective()
+ unset_machine()
+
+ victim.reset_perspective()
+ victim.unset_machine()
+
+ var/mob/living/V = victim
+ remove_verb(V, /mob/living/proc/horror_comm)
+ talk_to_horror_action.Remove(victim)
+
+ for(var/obj/item/horrortentacle/T in victim)
+ victim.visible_message(span_warning("[victim]'s tentacle transforms back!"), span_notice("Your tentacle disappears!"))
+ playsound(victim, 'sound/effects/blobattack.ogg', 30, 1)
+ qdel(T)
+ victim = null
+
+ RefreshAbilities()
+
+
+/mob/living/simple_animal/horror/proc/jumpstart()
+ if(!victim)
+ to_chat(src, span_warning("You need a host to be able to use this."))
+ return
+
+ if(!can_use_ability())
+ return
+
+ if(victim.stat != DEAD)
+ to_chat(src, span_warning("Your host is already alive!"))
+ return
+
+ if(!has_chemicals(250))
+ to_chat(src, span_warning("You need 250 chemicals to use this!"))
+ return
+
+ if(HAS_TRAIT_FROM(target, TRAIT_BADDNA, CHANGELING_DRAIN))
+ to_chat(src, span_warning("Their DNA is completely destroyed! You can't revive them"))
+ return
+
+ if(victim.stat == DEAD)
+ playsound(src, 'sound/machines/defib_charge.ogg', 50, 1, -1)
+ sleep(1 SECONDS)
+ victim.tod = null
+ victim.setToxLoss(0)
+ victim.setOxyLoss(0)
+ victim.setCloneLoss(0)
+ victim.SetUnconscious(0)
+ victim.SetStun(0)
+ victim.SetKnockdown(0)
+ victim.radiation = 0
+ victim.heal_overall_damage(victim.getBruteLoss(), victim.getFireLoss())
+ victim.reagents.clear_reagents()
+ if(HAS_TRAIT_FROM(victim, TRAIT_HUSK, BURN))
+ victim.cure_husk(BURN)
+ for(var/organ in victim.internal_organs)
+ var/obj/item/organ/O = organ
+ O.setOrganDamage(0)
+ victim.restore_blood()
+ victim.remove_all_embedded_objects()
+ victim.revive()
+ log_game("[src]/([src.ckey]) has revived [victim]/([victim.ckey]")
+ chemicals -= 250
+ to_chat(src, span_notice("You send a jolt of energy to your host, reviving them!"))
+ victim.grab_ghost(force = TRUE) //brings the host back, no eggscape
+ victim.adjustOxyLoss(30)
+ to_chat(victim, span_userdanger("You bolt upright, gasping for breath!"))
+ victim.electrocute_act(15, src, 1, FALSE, FALSE, FALSE, 1, FALSE)
+ playsound(src, 'sound/machines/defib_zap.ogg', 50, 1, -1)
+
+
+/mob/living/simple_animal/horror/proc/view_memory()
+ if(!victim)
+ to_chat(src, span_warning("You need a host to be able to use this."))
+ return
+
+ if(!can_use_ability())
+ return
+
+ if(victim.stat == DEAD)
+ to_chat(src, span_warning("Your host brain is unresponsive. They are dead!"))
+ return
+
+ if(prob(20))
+ to_chat(victim, span_danger("You suddenly feel your memory being tangled with..."))//chance to alert the victim
+
+ if(victim.mind)
+ var/datum/mind/suckedbrain = victim.mind
+ to_chat(src, span_boldnotice("You skim through [victim]'s memories...[suckedbrain.memory]"))
+ for(var/A in suckedbrain.antag_datums)
+ var/datum/antagonist/antag_types = A
+ var/list/all_objectives = antag_types.objectives.Copy()
+ if(antag_types.antag_memory)
+ to_chat(src, span_notice("[antag_types.antag_memory]"))
+ if(LAZYLEN(all_objectives))
+ to_chat(src, span_boldnotice("Objectives:"))
+ var/obj_count = 1
+ for(var/O in all_objectives)
+ var/datum/objective/objective = O
+ to_chat(src, span_notice("Objective #[obj_count++]: [objective.explanation_text]"))
+ var/list/datum/mind/other_owners = objective.get_owners() - suckedbrain
+ if(other_owners.len)
+ for(var/mind in other_owners)
+ var/datum/mind/M = mind
+ to_chat(src, span_notice("Conspirator: [M.name]"))
+
+ var/list/recent_speech = list()
+ var/list/say_log = list()
+ var/log_source = victim.logging
+ for(var/log_type in log_source)
+ var/nlog_type = text2num(log_type)
+ if(nlog_type & LOG_SAY)
+ var/list/reversed = log_source[log_type]
+ if(islist(reversed))
+ say_log = reverseRange(reversed.Copy())
+ break
+ if(LAZYLEN(say_log))
+ for(var/spoken_memory in say_log)
+ if(recent_speech.len >= 5)//up to 5 random lines of speech, favoring more recent speech
+ break
+ if(prob(50))
+ recent_speech[spoken_memory] = say_log[spoken_memory]
+ if(recent_speech.len)
+ to_chat(src, span_boldnotice("You catch some drifting memories of their past conversations..."))
+ for(var/spoken_memory in recent_speech)
+ to_chat(src, span_notice("[recent_speech[spoken_memory]]"))
+ var/mob/living/carbon/human/H = victim
+ var/datum/dna/the_dna = H.has_dna()
+ if(the_dna)
+ to_chat(src, span_boldnotice("You uncover that [H.p_their()] true identity is [the_dna.real_name]."))
+
+/mob/living/simple_animal/horror/proc/is_bonding()
+ return bonding
+
+/mob/living/simple_animal/horror/proc/bond_brain()
+ if(!victim)
+ to_chat(src, span_warning("You are not inside a host body."))
+ return
+
+ if(!can_use_ability())
+ return
+
+ if(victim.stat == DEAD)
+ to_chat(src, span_notice("This host lacks enough brain function to control."))
+ return
+
+ if(victim.has_trauma_type(/datum/brain_trauma/severe/split_personality))
+ to_chat(src, span_notice("This host's brain lobe separation makes it too complex for you to control."))
+ return
+
+ if(bonding)
+ bonding = FALSE
+ to_chat(src, span_danger("You stop attempting to take control of your host."))
+ return
+
+ to_chat(src, span_danger("You begin delicately adjusting your connection to the host brain..."))
+
+ if(QDELETED(src) || QDELETED(victim))
+ return
+
+ bonding = TRUE
+
+ var/delay = 20 SECONDS
+ if(has_upgrade("fast_control"))
+ delay -= 12 SECONDS
+ if(do_after(src, delay, target = victim, extra_checks = CALLBACK(src, .proc/is_bonding), stayStill = FALSE))
+ assume_control()
+
+/mob/living/simple_animal/horror/proc/assume_control()
+ if(!victim || !src || controlling || victim.stat == DEAD)
+ return
+ if(is_servant_of_ratvar(victim) || iscultist(victim))
+ to_chat(src, span_warning("[victim]'s mind seems to be blocked by some unknown force!"))
+ bonding = FALSE
+ return
+ if(HAS_TRAIT(victim, TRAIT_MINDSHIELD))
+ to_chat(src, span_warning("[victim]'s mind seems to be shielded from your influence!"))
+ bonding = FALSE
+ return
+ else
+ RegisterSignal(victim, COMSIG_MOB_APPLY_DAMAGE, .proc/hit_detatch)
+ log_game("[src]/([src.ckey]) assumed control of [victim]/([victim.ckey] with eldritch powers.")
+ to_chat(src, span_warning("You plunge your probosci deep into the cortex of the host brain, interfacing directly with their nervous system."))
+ to_chat(victim, span_userdanger("You feel a strange shifting sensation behind your eyes as an alien consciousness displaces yours."))
+
+ clothing = victim.get_equipped_items()
+ for(var/obj/item/I in clothing)
+ ADD_TRAIT(I, TRAIT_NODROP, HORROR_TRAIT)
+
+ qdel(host_brain)
+ host_brain = new(src)
+ host_brain.H = src
+ host_brain.name = "Trapped mind of [victim.real_name]"
+ victim.mind.transfer_to(host_brain)
+ if(victim.key)
+ host_brain.key = victim.key
+
+ to_chat(host_brain, "You are trapped in your own mind. You feel that there must be a way to resist!")
+
+ mind.transfer_to(victim)
+
+ bonding = FALSE
+ controlling = TRUE
+
+ remove_verb(victim, /mob/living/proc/horror_comm)
+ talk_to_horror_action.Remove(victim)
+ GrantControlActions()
+
+ victim.med_hud_set_status()
+ if(target)
+ victim.apply_status_effect(/datum/status_effect/agent_pinpointer/horror)
+ for(var/datum/status_effect/agent_pinpointer/horror/status in victim.status_effects)
+ status.scan_target = target.current
+
+/mob/living/carbon/proc/release_control()
+ var/mob/living/simple_animal/horror/B = has_horror_inside()
+ if(B && B.host_brain)
+ to_chat(src, span_danger("You withdraw your probosci, releasing control of [B.host_brain]"))
+ B.detatch()
+
+//Check for brain worms in head.
+/mob/proc/has_horror_inside()
+ for(var/I in contents)
+ if(ishorror(I))
+ return I
+
+
+/mob/living/simple_animal/horror/proc/hit_detatch()
+ if(victim.health <= 75)
+ detatch()
+ to_chat(src, span_warning("It appears that [victim]s brain detected danger, and hastily took over."))
+ to_chat(victim, span_danger("Your body is under attack, you unconciously forced your brain to immediately take over!"))
+
+/mob/living/simple_animal/horror/proc/detatch()
+ if(!victim || !controlling)
+ return
+
+ controlling = FALSE
+ UnregisterSignal(victim, COMSIG_MOB_APPLY_DAMAGE)
+ add_verb(victim, /mob/living/proc/horror_comm)
+ RemoveControlActions()
+ RefreshAbilities()
+ talk_to_horror_action.Grant(victim)
+
+ for(var/obj/item/I in clothing)
+ REMOVE_TRAIT(I, TRAIT_NODROP, HORROR_TRAIT)
+ clothing = list()
+
+ victim.med_hud_set_status()
+ victim.remove_status_effect(/datum/status_effect/agent_pinpointer/horror)
+
+ victim.mind.transfer_to(src)
+ if(host_brain)
+ host_brain.mind.transfer_to(victim)
+ if(host_brain.key)
+ victim.key = host_brain.key
+
+ log_game("[src]/([src.ckey]) released control of [victim]/([victim.ckey]")
+ qdel(host_brain)
+
+/mob/living/simple_animal/horror/proc/Update_Invisibility_Button()
+ var/datum/action/innate/horror/action = has_ability(/datum/action/innate/horror/chameleon)
+ if(action)
+ action.button_icon_state = "horror_sneak_[invisible ? "true" : "false"]"
+ action.UpdateButtonIcon()
+
+/mob/living/simple_animal/horror/proc/GrantHorrorActions()
+ for(var/datum/action/innate/horror/ability in horrorabilities)
+ if("horror" in ability.category)
+ ability.Grant(src)
+
+/mob/living/simple_animal/horror/proc/RemoveHorrorActions()
+ for(var/datum/action/innate/horror/ability in horrorabilities)
+ if("horror" in ability.category)
+ ability.Remove(src)
+
+/mob/living/simple_animal/horror/proc/GrantInfestActions()
+ for(var/datum/action/innate/horror/ability in horrorabilities)
+ if("infest" in ability.category)
+ ability.Grant(src)
+
+/mob/living/simple_animal/horror/proc/RemoveInfestActions()
+ for(var/datum/action/innate/horror/ability in horrorabilities)
+ if("infest" in ability.category)
+ ability.Remove(src)
+
+/mob/living/simple_animal/horror/proc/GrantControlActions()
+ for(var/datum/action/innate/horror/ability in horrorabilities)
+ if("control" in ability.category)
+ ability.Grant(victim)
+
+/mob/living/simple_animal/horror/proc/RemoveControlActions()
+ for(var/datum/action/innate/horror/ability in horrorabilities)
+ if("control" in ability.category)
+ ability.Remove(victim)
+
+/mob/living/simple_animal/horror/proc/RefreshAbilities() //control abilities technically don't belong to horror
+ if(victim)
+ RemoveHorrorActions()
+ GrantInfestActions()
+ else
+ RemoveInfestActions()
+ GrantHorrorActions()
\ No newline at end of file
diff --git a/code/modules/antagonists/horror/horror_abilities_and_upgrades.dm b/code/modules/antagonists/horror/horror_abilities_and_upgrades.dm
new file mode 100644
index 000000000000..e5512caf24af
--- /dev/null
+++ b/code/modules/antagonists/horror/horror_abilities_and_upgrades.dm
@@ -0,0 +1,461 @@
+//ABILITIES
+
+/datum/action/innate/horror
+ background_icon_state = "bg_ecult"
+ icon_icon = 'icons/mob/actions/actions_horror.dmi'
+ var/blacklisted = FALSE //If the ability can't be mutated
+ var/soul_price = 0 //How much souls the ability costs to buy; if this is 0, it isn't listed on the catalog
+ var/chemical_cost = 0 //How much chemicals the ability costs to use
+ var/mob/living/simple_animal/horror/B //Horror holding the ability
+ var/category //category for when the ability is active, "horror" is for creature, "infest" is during infestation, "controlling" is when a horror is controlling a body
+
+/datum/action/innate/horror/IsAvailable()
+ if(!B)
+ return
+ if(!B.has_chemicals(chemical_cost))
+ return
+ . = ..()
+
+/datum/action/innate/horror/mutate
+ name = "Mutate"
+ desc = "Use consumed souls to mutate your abilities."
+ button_icon_state = "mutate"
+ blacklisted = TRUE
+ category = list("horror")
+
+/datum/action/innate/horror/mutate/Activate()
+ to_chat(usr, span_velvet(span_bold("You focus on mutating your body...")))
+ B.ui_interact(usr)
+ return TRUE
+
+/datum/action/innate/horror/seek_soul
+ name = "Seek target soul"
+ desc = "Search for a soul weak enough for you to consume."
+ button_icon_state = "seek_soul"
+ blacklisted = TRUE
+ category = list("horror","infest")
+
+/datum/action/innate/horror/seek_soul/Activate()
+ B.SearchTarget()
+
+/datum/action/innate/horror/consume_soul
+ name = "Consume soul"
+ desc = "Consume your target's soul."
+ button_icon_state = "consume_soul"
+ blacklisted = TRUE
+ category = list("infest")
+
+/datum/action/innate/horror/consume_soul/Activate()
+ B.ConsumeSoul()
+
+/datum/action/innate/horror/talk_to_host
+ name = "Converse with Host"
+ desc = "Send a silent message to your host."
+ button_icon_state = "talk_to_host"
+ blacklisted = TRUE
+ category = list("infest")
+
+/datum/action/innate/horror/talk_to_host/Activate()
+ B.Communicate()
+
+/datum/action/innate/horror/toggle_hide
+ name = "Toggle Hide"
+ desc = "Become invisible to the common eye. Toggled on or off."
+ button_icon_state = "horror_hiding_false"
+ blacklisted = TRUE
+ category = list("horror")
+
+/datum/action/innate/horror/toggle_hide/Activate()
+ B.hide()
+ button_icon_state = "horror_hiding_[B.hiding ? "true" : "false"]"
+ UpdateButtonIcon()
+
+/datum/action/innate/horror/talk_to_horror
+ name = "Converse with Horror"
+ desc = "Communicate mentally with your horror."
+ button_icon_state = "talk_to_horror"
+ blacklisted = TRUE
+ var/mob/living/O
+
+/datum/action/innate/horror/talk_to_horror/IsAvailable()
+ if(owner.stat == DEAD)
+ return
+ return TRUE
+
+/datum/action/innate/horror/talk_to_horror/Activate()
+ var/mob/living/O = owner
+ O.horror_comm()
+
+/datum/action/innate/horror/talk_to_brain
+ name = "Converse with Trapped Mind"
+ desc = "Communicate mentally with the trapped mind of your host."
+ button_icon_state = "talk_to_trapped_mind"
+ blacklisted = TRUE
+ category = list("control")
+
+/datum/action/innate/horror/talk_to_brain/Activate()
+ B.victim.trapped_mind_comm()
+
+/datum/action/innate/horror/take_control
+ name = "Assume Control"
+ desc = "Fully connect to the brain of your host."
+ button_icon_state = "horror_brain"
+ blacklisted = TRUE
+ category = list("infest")
+
+/datum/action/innate/horror/take_control/Activate()
+ B.bond_brain()
+
+/datum/action/innate/horror/give_back_control
+ name = "Release Control"
+ desc = "Release control of your host's body."
+ button_icon_state = "horror_leave"
+ blacklisted = TRUE
+ category = list("control")
+
+/datum/action/innate/horror/give_back_control/Activate()
+ B.victim.release_control()
+
+/datum/action/innate/horror/leave_body
+ name = "Release Host"
+ desc = "Slither out of your host."
+ button_icon_state = "horror_leave"
+ blacklisted = TRUE
+ category = list("infest")
+
+/datum/action/innate/horror/leave_body/Activate()
+ B.release_victim()
+
+/datum/action/innate/horror/make_chems
+ name = "Secrete chemicals"
+ desc = "Push some chemicals into your host's bloodstream."
+ icon_icon = 'icons/obj/chemical.dmi'
+ button_icon_state = "minidispenser"
+ blacklisted = TRUE
+ category = list("infest")
+
+/datum/action/innate/horror/make_chems/Activate()
+ B.secrete_chemicals()
+
+/datum/action/innate/horror/freeze_victim
+ name = "Knockdown victim"
+ desc = "Use your tentacle to trip a victim, stunning for a short duration."
+ button_icon_state = "trip"
+ blacklisted = TRUE
+ category = list("horror")
+
+/datum/action/innate/horror/freeze_victim/Activate()
+ B.freeze_victim()
+ UpdateButtonIcon()
+ addtimer(CALLBACK(src, .proc/UpdateButtonIcon), 150)
+
+/datum/action/innate/horror/freeze_victim/IsAvailable()
+ if(world.time - B.used_freeze < 150)
+ return FALSE
+ else
+ return ..()
+
+//non-default abilities, can be mutated
+
+/datum/action/innate/horror/tentacle
+ name = "Grow Tentacle"
+ desc = "Makes your host grow a tentacle in their arm. Costs 50 chemicals to activate."
+ button_icon_state = "tentacle"
+ chemical_cost = 50
+ category = list("infest", "control")
+ soul_price = 2
+
+/datum/action/innate/horror/tentacle/IsAvailable()
+ if(!active && !B.has_chemicals(chemical_cost))
+ return
+ return ..()
+
+/datum/action/innate/horror/tentacle/New()
+ ..()
+ START_PROCESSING(SSfastprocess, src)
+
+/datum/action/innate/horror/tentacle/Destroy()
+ STOP_PROCESSING(SSfastprocess, src)
+ return ..()
+
+/datum/action/innate/horror/tentacle/process()
+ ..()
+ active = locate(/obj/item/horrortentacle) in B.victim
+ UpdateButtonIcon()
+
+
+/datum/action/innate/horror/tentacle/Activate()
+ B.use_chemicals(50)
+ B.victim.visible_message(span_warning("[B.victim]'s arm contorts into tentacles!"), span_notice("Your arm transforms into a giant tentacle. Examine it to see possible uses."))
+ playsound(B.victim, 'sound/effects/blobattack.ogg', 30, 1)
+ to_chat(B, span_warning("You transform [B.victim]'s arm into a tentacle!"))
+ var/obj/item/horrortentacle/T = new
+ B.victim.put_in_hands(T)
+ return TRUE
+
+/datum/action/innate/horror/tentacle/Deactivate()
+ B.victim.visible_message(span_warning("[B.victim]'s tentacle transforms back!"), span_notice("Your tentacle disappears!"))
+ playsound(B.victim, 'sound/effects/blobattack.ogg', 30, 1)
+ to_chat(B, span_warning("You transform [B.victim]'s arm back."))
+ for(var/obj/item/horrortentacle/T in B.victim)
+ qdel(T)
+ return TRUE
+
+/datum/action/innate/horror/transfer_host
+ name = "Transfer to another Host"
+ desc = "Move into another host directly. Grabbing makes the process faster."
+ button_icon_state = "transfer_host"
+ category = list("infest", "control")
+ soul_price = 1
+ var/transferring = FALSE
+
+/datum/action/innate/horror/transfer_host/proc/is_transferring(var/mob/living/carbon/C)
+ return transferring && C.Adjacent(B.victim)
+
+/datum/action/innate/horror/transfer_host/Activate()
+ if(transferring)
+ transferring = FALSE
+ to_chat(src, span_warning("You decide against leaving your host."))
+ return
+
+ var/list/choices = list()
+ for(var/mob/living/carbon/C in range(1,B.victim))
+ if(C!=B.victim && C.Adjacent(B.victim))
+ choices += C
+
+ if(!choices.len)
+ return
+ var/mob/living/carbon/C = choices.len > 1 ? input(owner,"Who do you wish to infest?") in null|choices : choices[1]
+ if(!C || !B)
+ return
+ if(!C.Adjacent(B.victim))
+ return
+ var/obj/item/bodypart/head/head = C.get_bodypart(BODY_ZONE_HEAD)
+ if(!head)
+ to_chat(owner, span_warning("[C] doesn't have a head!"))
+ return
+ var/hasbrain = FALSE
+ for(var/obj/item/organ/brain/X in C.internal_organs)
+ hasbrain = TRUE
+ break
+ if(!hasbrain)
+ to_chat(owner, span_warning("[C] doesn't have a brain!"))
+ return
+ if((!C.key || !C.mind) && C != B.target.current)
+ to_chat(owner, span_warning("[C]'s mind seems unresponsive. Try someone else!"))
+ return
+ if(C.has_horror_inside())
+ to_chat(owner, span_warning("[C] is already infested!"))
+ return
+
+ to_chat(owner, span_warning("You move your tentacles away from [B.victim] and begin to transfer to [C]..."))
+ var/delay = 20 SECONDS
+ var/silent
+ if(B.victim.pulling != C)
+ silent = TRUE
+ else
+ switch(B.victim.grab_state)
+ if(GRAB_PASSIVE)
+ delay = 10 SECONDS
+ if(GRAB_AGGRESSIVE)
+ delay = 5 SECONDS
+ if(GRAB_NECK)
+ delay = 3 SECONDS
+ else
+ delay = 1 SECONDS
+
+ transferring = TRUE
+ if(!do_after(B.victim, delay, target = C, extra_checks = CALLBACK(src, .proc/is_transferring, C), stayStill = FALSE))
+ to_chat(owner, span_warning("As [C] moves away, your transfer gets interrupted!"))
+ transferring = FALSE
+ return
+ transferring = FALSE
+ if(!C || !B || !C.Adjacent(B.victim))
+ return
+ B.leave_victim()
+ B.Infect(C)
+ if(!silent)
+ to_chat(C, span_warning("Something slimy wiggles into your ear!"))
+ playsound(B, 'sound/effects/blobattack.ogg', 30, 1)
+
+/datum/action/innate/horror/jumpstart_host
+ name = "Revive Host"
+ desc = "Bring your host back to life."
+ button_icon_state = "revive"
+ category = list("infest")
+ soul_price = 2
+
+/datum/action/innate/horror/jumpstart_host/Activate()
+ B.jumpstart()
+
+/datum/action/innate/horror/view_memory
+ name = "View Memory"
+ desc = "Read recent memory of the host you're inside of."
+ button_icon_state = "view_memory"
+ category = list("infest")
+ soul_price = 1
+
+/datum/action/innate/horror/view_memory/Activate()
+ B.view_memory()
+
+/datum/action/innate/horror/chameleon
+ name = "Chameleon Skin"
+ desc = "Adjust your skin color to blend into environment. Costs 5 chemicals per tick, also stopping chemical regeneration while active. Attacking stops the invisibility completely."
+ button_icon_state = "horror_sneak_false"
+ category = list("horror")
+ soul_price = 1
+
+/datum/action/innate/horror/chameleon/Activate()
+ B.go_invisible()
+ button_icon_state = "horror_sneak_[B.invisible ? "true" : "false"]"
+ UpdateButtonIcon()
+
+/datum/action/innate/horror/lube_spill
+ name = "Lube spill"
+ desc = "Makes you spin around and flail slippery lube around you. Costs 30 chemicals to activate."
+ button_icon_state = "lube_spill"
+ chemical_cost = 30
+ category = list("horror")
+ soul_price = 1
+ var/cooldown = 0
+
+/datum/action/innate/horror/lube_spill/IsAvailable()
+ if(cooldown > world.time || !B.has_chemicals(chemical_cost) || !B.can_use_ability())
+ return
+ return ..()
+
+/datum/action/innate/horror/lube_spill/Activate()
+ B.use_chemicals(30)
+ cooldown = world.time + 10 SECONDS
+ UpdateButtonIcon()
+ addtimer(CALLBACK(src, .proc/UpdateButtonIcon), 10 SECONDS)
+ B.visible_message(span_warning("[B] starts spinning and throwing some sort of substance!"), span_notice("Your start to spin and flail oily substance everywhere!"))
+ var/spins_remaining = 10
+ B.icon_state = "horror_spin"
+ while(spins_remaining > 0)
+ playsound(B, 'sound/effects/blobattack.ogg', rand(20, 30), rand(0.5, 2))
+ for(var/turf/open/t in range(1, B))
+ if(prob(60) && B.Adjacent(t))
+ t.MakeSlippery(TURF_WET_LUBE, 100)
+ sleep(5)
+ spins_remaining--
+ if(!B.can_use_ability())
+ return TRUE
+ B.icon_state = "horror"
+ return TRUE
+
+//UPGRADES
+/datum/horror_upgrade
+ var/name = "horror upgrade"
+ var/desc = "This is an upgrade."
+ var/id
+ var/soul_price = 0 //How much souls an upgrade costs to buy
+ var/mob/living/simple_animal/horror/B //Horror holding the upgrades
+
+/datum/horror_upgrade/proc/unlock()
+ if(!B)
+ return
+ apply_effects()
+ qdel(src)
+ return TRUE
+
+/datum/horror_upgrade/New(owner)
+ ..()
+ B = owner
+
+/datum/horror_upgrade/proc/apply_effects()
+ return
+
+//Upgrades the stun ability
+/datum/horror_upgrade/paralysis
+ name = "Electrocharged tentacle"
+ id = "paralysis"
+ desc = "Empowers your tentacle knockdown ability by giving it extra charge, knocking your victim down unconcious."
+ soul_price = 3
+
+/datum/horror_upgrade/paralysis/apply_effects()
+ var/datum/action/innate/horror/A = B.has_ability(/datum/action/innate/horror/freeze_victim)
+ if(A)
+ A.name = "Paralyze Victim"
+ A.desc = "Shock a victim with an electrically charged tentacle."
+ A.button_icon_state = "paralyze"
+ B.update_action_buttons()
+
+//Increases chemical regeneration rate by 2
+/datum/horror_upgrade/chemical_regen
+ name = "Efficient chemical glands"
+ id = "chem_regen"
+ desc = "Your chemical glands work more efficiently. Unlocking this increases your chemical regeneration."
+ soul_price = 2
+
+/datum/horror_upgrade/chemical_regen/apply_effects()
+ B.chem_regen_rate += 2
+
+//Lets horror regenerate chemicals outside of a host
+/datum/horror_upgrade/nohost_regen
+ name = "Independent chemical glands"
+ id = "nohost_regen"
+ desc = "Your chemical glands become less parasitic and let you regenerate chemicals on their own without need for a host."
+ soul_price = 2
+
+//Lets horror regenerate health
+/datum/horror_upgrade/regen
+ name = "Regenerative skin"
+ id = "regen"
+ desc = "Your skin adapts to sustained damage and slowly regenerates itself, healing your wounds over time."
+ soul_price = 1
+
+//Doubles horror's health pool
+/datum/horror_upgrade/hp_up
+ name = "Rhino skin" //Horror can....roll?
+ id = "hp_up"
+ desc = "Your skin becomes hard as rock, greatly increasing your maximum health - and odds of survival outside of a host."
+ soul_price = 2
+
+/datum/horror_upgrade/hp_up/apply_effects()
+ B.health = round(min(B.maxHealth,B.health * 2))
+ B.maxHealth = round(B.maxHealth * 2)
+
+//Makes horror almost invisible for a short time after leaving a host
+/datum/horror_upgrade/invisibility
+ name = "Reflective fluids"
+ id = "invisible_exit"
+ desc = "You build up reflective solution inside host's brain. Upon exiting a host, you're briefly covered in it, rendering you near invisible for a few seconds. This mutation also makes the host unable to notice you exiting it directly."
+ soul_price = 2
+
+//Increases melee damage to 20
+/datum/horror_upgrade/dmg_up
+ name = "Sharpened teeth"
+ id = "dmg_up"
+ desc = "Your teeth become sharp blades, this mutation increases your melee damage."
+ soul_price = 2
+
+/datum/horror_upgrade/dmg_up/apply_effects()
+ B.attacktext = "crushes"
+ B.attack_sound = 'sound/weapons/pierce_slow.ogg' //chunky
+ B.melee_damage_lower += 10
+ B.melee_damage_upper += 10
+
+//Expands the reagent selection horror can make
+/datum/horror_upgrade/upgraded_chems
+ name = "Advanced reagent synthesis"
+ id = "upgraded_chems"
+ desc = "Lets you synthetize adrenaline, salicyclic acid, oxandrolone, pentetic acid and rezadone into your host."
+ soul_price = 2
+
+/datum/horror_upgrade/upgraded_chems/apply_effects()
+ B.horror_chems += list(/datum/horror_chem/adrenaline,/datum/horror_chem/sal_acid,/datum/horror_chem/oxandrolone,/datum/horror_chem/pen_acid,/datum/horror_chem/rezadone)
+
+//faster mind control
+/datum/horror_upgrade/fast_control
+ name = "Precise probosci"
+ id = "fast_control"
+ desc = "Your probosci become more precise, allowing you to take control over your host's brain noticably faster."
+ soul_price = 2
+
+//makes it longer for host to snap out of mind control
+/datum/horror_upgrade/deep_control
+ name = "Insulated probosci"
+ id = "deep_control"
+ desc = "Your probosci become insulated, protecting them from neural shocks. This makes it harder for the host to regain control over their body."
+ soul_price = 2
\ No newline at end of file
diff --git a/code/modules/antagonists/horror/horror_chemicals.dm b/code/modules/antagonists/horror/horror_chemicals.dm
new file mode 100644
index 000000000000..d0c6bdc4e04b
--- /dev/null
+++ b/code/modules/antagonists/horror/horror_chemicals.dm
@@ -0,0 +1,96 @@
+/mob/living/simple_animal/horror/Topic(href, href_list, hsrc)
+ if(href_list["horror_use_chem"])
+ locate(href_list["src"])
+ if(!istype(src, /mob/living/simple_animal/horror))
+ return
+
+ var/topic_chem = href_list["horror_use_chem"]
+ var/datum/horror_chem/C
+
+ for(var/datum in typesof(/datum/horror_chem))
+ var/datum/horror_chem/test = new datum()
+ if(test.chemname == topic_chem)
+ C = test
+ break
+
+ if(!istype(C, /datum/horror_chem))
+ return
+
+ if(!C || !victim || controlling || !src || stat)
+ return
+
+ if(!istype(C, /datum/horror_chem))
+ return
+
+ if(chemicals < C.chemuse)
+ to_chat(src, span_boldnotice("You need [C.chemuse] chemicals stored to use this chemical!"))
+ return
+
+ to_chat(src, span_danger("You squirt a measure of [C.chemname] from your reservoirs into [victim]'s bloodstream."))
+ victim.reagents.add_reagent(C.R, C.quantity)
+ chemicals -= C.chemuse
+ log_game("[src]/([src.ckey]) has injected [C.chemname] into their host [victim]/([victim.ckey])")
+
+ src << output(chemicals, "ViewHorror\ref[src]Chems.browser:update_chemicals")
+
+ ..()
+
+/datum/horror_chem
+ var/chemname
+ var/chem_desc = "This is a chemical"
+ var/datum/reagent/R
+ var/chemuse = 30
+ var/quantity = 10
+
+/datum/horror_chem/epinephrine
+ chemname = "epinephrine"
+ R = /datum/reagent/medicine/epinephrine
+ chem_desc = "Stabilizes critical condition and slowly restores oxygen damage."
+
+/datum/horror_chem/mannitol
+ chemname = "mannitol"
+ R = /datum/reagent/medicine/mannitol
+ chem_desc = "Heals brain damage."
+
+/datum/horror_chem/bicaridine
+ chemname = "bicaridine"
+ R = /datum/reagent/medicine/bicaridine
+ chem_desc = "Heals brute damage."
+
+/datum/horror_chem/kelotane
+ chemname = "kelotane"
+ R = /datum/reagent/medicine/kelotane
+ chem_desc = "Heals burn damage."
+
+/datum/horror_chem/charcoal
+ chemname = "charcoal"
+ R = /datum/reagent/medicine/charcoal
+ chem_desc = "Slowly heals toxin damage, while also slowly removing any other chemicals."
+
+/datum/horror_chem/adrenaline
+ chemname = "adrenaline"
+ R = /datum/reagent/medicine/changelingadrenaline
+ chemuse = 100
+ chem_desc = "Stimulates the brain, shrugging off effect of stuns while regenerating stamina."
+
+/datum/horror_chem/rezadone
+ chemname = "rezadone"
+ R = /datum/reagent/medicine/rezadone
+ chemuse = 50
+ chem_desc = "Heals cellular damage."
+
+/datum/horror_chem/pen_acid
+ chemname = "pentetic acid"
+ R = /datum/reagent/medicine/pen_acid
+ chemuse = 50
+ chem_desc = "Reduces massive amounts of radiation and toxin damage while purging other chemicals from the body."
+
+/datum/horror_chem/sal_acid
+ chemname = "salicyclic acid"
+ R = /datum/reagent/medicine/sal_acid
+ chem_desc = "Stimulates the healing of severe bruises. Rapidly heals severe bruising and slowly heals minor ones."
+
+/datum/horror_chem/oxandrolone
+ chemname = "oxandrolone"
+ R = /datum/reagent/medicine/oxandrolone
+ chem_desc = "Stimulates the healing of severe burns. Rapidly heals severe burns and slowly heals minor ones."
\ No newline at end of file
diff --git a/code/modules/antagonists/horror/horror_datums.dm b/code/modules/antagonists/horror/horror_datums.dm
new file mode 100644
index 000000000000..3ab9138356a4
--- /dev/null
+++ b/code/modules/antagonists/horror/horror_datums.dm
@@ -0,0 +1,333 @@
+//ANTAG DATUMS
+/datum/antagonist/horror
+ name = "Horror"
+ show_in_antagpanel = TRUE
+ prevent_roundtype_conversion = FALSE
+ show_name_in_check_antagonists = TRUE
+ show_to_ghosts = TRUE
+ var/datum/mind/summoner
+
+/datum/antagonist/horror/on_gain()
+ . = ..()
+ give_objectives()
+ if(ishorror(owner.current) && owner.current.mind)
+ var/mob/living/simple_animal/horror/H = owner.current
+ H.update_horror_hud()
+
+/datum/antagonist/horror/proc/give_objectives()
+ if(summoner)
+ var/datum/objective/newobjective = new
+ newobjective.explanation_text = "Serve your summoner, [summoner.name]."
+ newobjective.owner = owner
+ newobjective.completed = TRUE
+ objectives += newobjective
+ else
+ //succ some souls
+ var/datum/objective/horrorascend/ascend = new
+ ascend.owner = owner
+ ascend.hor = owner.current
+ ascend.target_amount = rand(5, 8)
+ objectives += ascend
+ ascend.update_explanation_text()
+
+ //looking for antagonist we can assist
+ var/list/possible_targets = list()
+ for(var/datum/mind/M in SSticker.minds)
+ if(M.current && M.current.stat != DEAD)
+ if(ishuman(M.current))
+ if(M.special_role)
+ possible_targets += M
+
+ if(possible_targets.len)
+ var/datum/mind/M = pick(possible_targets)
+ var/datum/objective/protect/O = new
+ O.owner = owner
+ O.target = M
+ O.explanation_text = "Protect and assist \the [M.current.real_name], the [M.assigned_role]."
+ objectives += O
+
+
+ //don't die while you're at is
+ var/datum/objective/survive/survive = new
+ survive.owner = owner
+ objectives += survive
+
+/datum/objective/horrorascend
+ name = "consume souls"
+ var/mob/living/simple_animal/horror/hor
+
+/datum/objective/horrorascend/update_explanation_text()
+ . = ..()
+ explanation_text = "Consume [target_amount] souls."
+
+/datum/objective/horrorascend/check_completion()
+ if(hor && hor.consumed_souls >= target_amount)
+ return TRUE
+ return FALSE
+
+
+//SPAWNER
+/obj/item/horrorspawner
+ name = "suspicious pet carrier"
+ desc = "It contains some sort of creature inside. You can see tentacles sticking out of it."
+ icon = 'icons/obj/pet_carrier.dmi'
+ lefthand_file = 'icons/mob/inhands/items_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/items_righthand.dmi'
+ item_state = "pet_carrier"
+ icon_state = "pet_carrier_occupied"
+ var/used = FALSE
+ color = rgb(130, 105, 160)
+
+/obj/item/horrorspawner/attack_self(mob/living/user)
+ if(used)
+ to_chat(user, "The pet carrier appears unresponsive.")
+ return
+ used = TRUE
+ to_chat(user, "You're attempting to wake up the creature inside the box...")
+ sleep(5 SECONDS)
+ var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the eldritch horror in service of [user.real_name]?", ROLE_HORROR, null, FALSE, 100)
+ if(LAZYLEN(candidates))
+ var/mob/dead/observer/C = pick(candidates)
+ var/mob/living/simple_animal/horror/H = new /mob/living/simple_animal/horror(get_turf(src))
+ H.key = C.key
+ H.mind.enslave_mind_to_creator(user)
+ H.mind.add_antag_datum(C)
+ H.mind.memory += "You are " + span_purple(span_bold("[H.real_name]")) + ", an eldritch horror. Consume souls to evolve. "
+ var/datum/antagonist/horror/S = new
+ S.summoner = user.mind
+ S.antag_memory += "[user.mind] woke you from your eternal slumber. Aid them in their objectives as a token of gratitude. "
+ H.mind.add_antag_datum(S)
+ log_game("[key_name(user)] has summoned [key_name(H)], an eldritch horror.")
+ to_chat(user, span_bold("[H.real_name] has awoken into your service!"))
+ used = TRUE
+ icon_state = "pet_carrier_open"
+ sleep(5)
+ var/obj/item/horrorsummonhorn/horn = new /obj/item/horrorsummonhorn(get_turf(src))
+ horn.summoner = user.mind
+ horn.horror = H
+ to_chat(user, span_notice("A strange looking [horn] falls out of [src]!"))
+ else
+ to_chat(user, "The creatures looks at you with one of it's eyes before going back to slumber.")
+ used = FALSE
+ return
+
+//Summoning horn
+/obj/item/horrorsummonhorn
+ name = "old horn"
+ desc = "A very old horn. You feel an incredible urge to blow into it."
+ icon = 'icons/obj/items_and_weapons.dmi'
+ lefthand_file = 'icons/mob/inhands/items_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/items_righthand.dmi'
+ item_state = "horn"
+ icon_state = "horn"
+ var/datum/mind/summoner
+ var/mob/living/simple_animal/horror/horror
+ var/cooldown
+
+/obj/item/horrorsummonhorn/examine(mob/user)
+ . = ..()
+ if(user.mind == summoner)
+ to_chat(user, span_purple("Blowing into this horn will recall the horror back to you. Be wary, the horn is loud, and may attract unwanted attention."))
+
+/obj/item/horrorsummonhorn/attack_self(mob/living/user)
+ if(cooldown > world.time)
+ to_chat(user, span_notice("Take a breath before you blow [src] again."))
+ return
+ to_chat(user, span_notice("You take a deep breath and prepare to blow into [src]..."))
+ if(do_mob(user, src, 10 SECONDS))
+ if(cooldown > world.time)
+ return
+ cooldown = world.time + 10 SECONDS
+ to_chat(src, span_notice("You blow the horn..."))
+ playsound(loc, "sound/items/airhorn.ogg", 100, 1, 30)
+ var/turf/summonplace = get_turf(src)
+ sleep(5 SECONDS)
+ if(prob(20)) //yeah you're summoning an eldritch horror allright
+ new /obj/effect/temp_visual/summon(summonplace)
+ sleep(10)
+ var/type = pick(typesof(/mob/living/simple_animal/hostile/abomination))
+ var/mob/R = new type(summonplace)
+ playsound(summonplace, "sound/effects/phasein.ogg", 30)
+ summonplace.visible_message(span_danger("[R] emerges!"))
+ else
+ if(!horror || horror.stat == DEAD)
+ summonplace.visible_message(span_danger("But nothing responds to the call!"))
+ else
+ new /obj/effect/temp_visual/summon(summonplace)
+ sleep(10)
+ horror.leave_victim()
+ horror.forceMove(summonplace)
+ playsound(summonplace, "sound/effects/phasein.ogg", 30)
+ summonplace.visible_message(span_notice("[horror] appears out of nowhere!"))
+ if(user.mind != summoner)
+ sleep(2 SECONDS)
+ playsound(summonplace, "sound/effects/glassbr2.ogg", 30, 1)
+ to_chat(user, span_danger("[src] breaks!"))
+ qdel(src)
+/obj/item/horrorsummonhorn/suicide_act(mob/living/user) //"I am the prettiest unicorn that ever was!" ~Spy 2013
+ user.visible_message(span_suicide("[user] stabs [user.p_their()] forehead with [src]! It looks like [user.p_theyre()] trying to commit suicide!"))
+ return BRUTELOSS
+//Tentacle arm
+/obj/item/horrortentacle
+ name = "tentacle"
+ desc = "A long, slimy, arm-like appendage."
+ icon = 'icons/obj/items_and_weapons.dmi'
+ icon_state = "horrortentacle"
+ item_state = "tentacle"
+ lefthand_file = 'icons/mob/inhands/antag/horror_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/antag/horror_righthand.dmi'
+ resistance_flags = ACID_PROOF
+ force = 17
+ item_flags = ABSTRACT | DROPDEL
+ weapon_stats = list(SWING_SPEED = 1, ENCUMBRANCE = 0, ENCUMBRANCE_TIME = 0, REACH = 2, DAMAGE_LOW = 0, DAMAGE_HIGH = 0)
+ range_cooldown_mod = 0 //tentacle is designed to hit from range
+ hitsound = 'sound/weapons/whip.ogg'
+/obj/item/horrortentacle/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_NODROP, ABSTRACT_ITEM_TRAIT)
+/obj/item/horrortentacle/examine(mob/user)
+ . = ..()
+ to_chat(user, span_velvet(span_bold("Functions:")))
+ to_chat(user, span_velvet("All attacks work up to 2 tiles away."))
+ to_chat(user, span_velvet("Help intent: Usual help function of an arm."))
+ to_chat(user, span_velvet("Disarm intent: Whips the tentacle, disarming your opponent."))
+ to_chat(user, span_velvet("Grab intent: Instant aggressive grab on an opponent. Can also throw them!"))
+ to_chat(user, span_velvet("Harm intent: Whips the tentacle, damaging your opponent."))
+ to_chat(user, span_velvet("Also functions to pry open unbolted airlocks."))
+/obj/item/horrortentacle/attack(atom/target, mob/living/user)
+ if(isliving(target))
+ user.Beam(target,"purpletentacle",time=5)
+ var/mob/living/L = target
+ switch(user.a_intent)
+ if(INTENT_HELP)
+ L.attack_hand(user)
+ return
+ if(INTENT_GRAB)
+ if(L != user)
+ L.grabbedby(user)
+ L.grippedby(user, instant = TRUE)
+ L.Knockdown(30)
+ return
+ if(INTENT_DISARM)
+ if(iscarbon(L))
+ var/mob/living/carbon/C = L
+ var/obj/item/I = C.get_active_held_item()
+ if(I)
+ if(C.dropItemToGround(I))
+ playsound(loc, "sound/weapons/whipgrab.ogg", 30)
+ target.visible_message(span_danger("[I] is whipped out of [C]'s hand by [user]!"),span_userdanger("A tentacle whips [I] out of your hand!"))
+ return
+ else
+ to_chat(user, span_danger("You can't seem to pry [I] off [C]'s hands!"))
+ return
+ else
+ C.attack_hand(user)
+ return
+ . = ..()
+/obj/item/horrortentacle/afterattack(atom/target, mob/user, proximity)
+ if(isliving(user.pulling) && user.pulling != target)
+ var/mob/living/H = user.pulling
+ user.visible_message(span_warning("[user] throws [H] with [user.p_their()] [src]!"), span_warning("You throw [H] with [src]."))
+ H.throw_at(target, 8, 2)
+ H.Knockdown(30)
+ return
+ if(!proximity)
+ return
+ if(istype(target, /obj/machinery/door/airlock))
+ var/obj/machinery/door/airlock/A = target
+ if((!A.requiresID() || A.allowed(user)) && A.hasPower())
+ return
+ if(A.locked)
+ to_chat(user, span_warning("The airlock's bolts prevent it from being forced!"))
+ return
+ if(A.hasPower())
+ user.visible_message(span_warning("[user] jams [src] into the airlock and starts prying it open!"), span_warning("You start forcing the airlock open."),
+ span_italics("You hear a metal screeching sound."))
+ playsound(A, 'sound/machines/airlock_alien_prying.ogg', 150, 1)
+ if(!do_after(user, 10 SECONDS, target = A))
+ return
+ user.visible_message(span_warning("[user] forces the airlock to open with [user.p_their()] [src]!"), span_warning("You force the airlock to open."),
+ span_italics("You hear a metal screeching sound."))
+ A.open(2)
+ return
+ . = ..()
+/obj/item/horrortentacle/suicide_act(mob/user) //this will never be called, since horror stops suicide, but might as well if they get tentacle through other means
+ user.visible_message(span_suicide("[src] coils itself around [user] tightly gripping [user.p_their()] neck! It looks like [user.p_theyre()] trying to commit suicide!"))
+ return (OXYLOSS)
+//Pinpointer
+/obj/screen/alert/status_effect/agent_pinpointer/horror
+ name = "Soul locator"
+ desc = "Find your target soul."
+/datum/status_effect/agent_pinpointer/horror
+ id = "horror_pinpointer"
+ minimum_range = 0
+ range_fuzz_factor = 0
+ tick_interval = 20
+ alert_type = /obj/screen/alert/status_effect/agent_pinpointer/horror
+/datum/status_effect/agent_pinpointer/horror/scan_for_target()
+ return
+//TRAPPED MIND - when horror takes control over your body, you become a mute trapped mind
+/mob/living/captive_brain
+ name = "host brain"
+ real_name = "host brain"
+ var/datum/action/innate/resist_control/R
+ var/mob/living/simple_animal/horror/H
+
+/mob/living/captive_brain/Initialize(mapload, gen=1)
+ ..()
+ R = new
+ R.Grant(src)
+
+/mob/living/captive_brain/say(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
+ if(client)
+ if(client.prefs.muted & MUTE_IC)
+ to_chat(src, span_danger("You cannot speak in IC (muted)."))
+ return
+ if(client.handle_spam_prevention(message,MUTE_IC))
+ return
+ if(ishorror(loc))
+ message = sanitize(message)
+ if(!message)
+ return
+ if(stat == 2)
+ return say_dead(message)
+ to_chat(src, span_alien(span_italics("You whisper silently, \"[message]\"")))
+ to_chat(H.victim, span_alien(span_italics("[src] whispers, \"[message]\"")))
+ for(var/M in GLOB.dead_mob_list)
+ if(isobserver(M))
+ var/rendered = span_changeling("[src] transfers: \"[message]\"")
+ var/link = FOLLOW_LINK(M, H.victim)
+ to_chat(M, "[link] [rendered]")
+
+/mob/living/captive_brain/emote(act, m_type = null, message = null, intentional = FALSE)
+ return
+
+/datum/action/innate/resist_control
+ name = "Resist control"
+ desc = "Try to take back control over your brain. A strong nerve impulse should do it."
+ background_icon_state = "bg_ecult"
+ icon_icon = 'icons/mob/actions/actions_horror.dmi'
+ button_icon_state = "resist_control"
+
+/datum/action/innate/resist_control/Activate()
+ var/mob/living/captive_brain/B = owner
+ if(B)
+ B.try_resist()
+
+/mob/living/captive_brain/resist()
+ try_resist()
+
+/mob/living/captive_brain/proc/try_resist()
+ var/delay = rand(20 SECONDS,30 SECONDS)
+ if(H.horrorupgrades["deep_control"])
+ delay += rand(20 SECONDS,30 SECONDS)
+ to_chat(src, span_danger("You begin doggedly resisting the parasite's control."))
+ to_chat(H.victim, span_danger("You feel the captive mind of [src] begin to resist your control."))
+ addtimer(CALLBACK(src, .proc/return_control), delay)
+
+/mob/living/captive_brain/proc/return_control()
+ if(!H || !H.controlling)
+ return
+ to_chat(src, span_userdanger("With an immense exertion of will, you regain control of your body!"))
+ to_chat(H.victim, span_danger("You feel control of the host brain ripped from your grasp, and retract your probosci before the wild neural impulses can damage you."))
+ H.detatch()
diff --git a/code/modules/antagonists/horror/horror_html.dm b/code/modules/antagonists/horror/horror_html.dm
new file mode 100644
index 000000000000..4f70c856805d
--- /dev/null
+++ b/code/modules/antagonists/horror/horror_html.dm
@@ -0,0 +1,102 @@
+/mob/living/simple_animal/horror/proc/get_html_template(content)
+ var/html = {"
+
+
+ Horror Chemicals
+
+
+
+
+
+
+
+
Horror Chemicals
+
+ [content]
+
"}
+ return html
\ No newline at end of file
diff --git a/code/modules/antagonists/horror/horror_mutate.dm b/code/modules/antagonists/horror/horror_mutate.dm
new file mode 100644
index 000000000000..2eaa8e93af92
--- /dev/null
+++ b/code/modules/antagonists/horror/horror_mutate.dm
@@ -0,0 +1,97 @@
+// Horror mutation menu
+// Totally not a copypaste of darkspawn menu, not a copypaste of cellular emporium, i swear. Edit: now looks like guardianbuilder too
+
+/mob/living/simple_animal/horror/proc/has_ability(typepath)
+ for(var/datum/action/innate/horror/ability in horrorabilities)
+ if(istype(ability, typepath))
+ return ability
+ return
+
+/mob/living/simple_animal/horror/proc/add_ability(typepath)
+ if(has_ability(typepath))
+ return
+ var/datum/action/innate/horror/action = new typepath
+ action.B = src
+ horrorabilities += action
+ RefreshAbilities()
+ to_chat(src, span_velvet("You have mutated the [action.name]."))
+ available_points = max(0, available_points - action.soul_price)
+ return TRUE
+
+/mob/living/simple_animal/horror/proc/has_upgrade(id)
+ return horrorupgrades[id]
+
+/mob/living/simple_animal/horror/proc/add_upgrade(id)
+ if(has_upgrade(id))
+ return
+ for(var/V in subtypesof(/datum/horror_upgrade))
+ var/datum/horror_upgrade/_U = V
+ if(initial(_U.id) == id)
+ var/datum/horror_upgrade/U = new _U(src)
+ horrorupgrades[id] = TRUE
+ to_chat(src, "You have adapted the \"[U.name]\" upgrade.")
+ available_points = max(0, available_points - U.soul_price)
+ U.unlock()
+
+//mutation menu, 100% ripoff of psiweb, pls don't sue
+
+/mob/living/simple_animal/horror/ui_state(mob/user)
+ return GLOB.always_state
+
+/mob/living/simple_animal/horror/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "HorrorMutate", "Horror Mutation")
+ ui.open()
+
+/mob/living/simple_animal/horror/ui_data(mob/user)
+ var/list/data = list()
+
+ data["available_points"] = "[available_points] | [consumed_souls] consumed souls total"
+
+ var/list/abilities = list()
+ var/list/upgrades = list()
+
+ for(var/path in subtypesof(/datum/action/innate/horror))
+ var/datum/action/innate/horror/ability = path
+
+ if(initial(ability.blacklisted))
+ continue
+
+ var/list/AL = list()
+ AL["name"] = initial(ability.name)
+ AL["typepath"] = path
+ AL["desc"] = initial(ability.desc)
+ AL["soul_cost"] = initial(ability.soul_price)
+ AL["owned"] = has_ability(path)
+ AL["can_purchase"] = !AL["owned"] && available_points >= initial(ability.soul_price)
+
+ abilities += list(AL)
+
+ data["abilities"] = abilities
+
+ for(var/path in subtypesof(/datum/horror_upgrade))
+ var/datum/horror_upgrade/upgrade = path
+
+ var/list/DE = list()
+ DE["name"] = initial(upgrade.name)
+ DE["id"] = initial(upgrade.id)
+ DE["desc"] = initial(upgrade.desc)
+ DE["soul_cost"] = initial(upgrade.soul_price)
+ DE["owned"] = has_upgrade(initial(upgrade.id))
+ DE["can_purchase"] = !DE["owned"] && available_points >= initial(upgrade.soul_price)
+
+ upgrades += list(DE)
+
+ data["upgrades"] = upgrades
+
+ return data
+
+/mob/living/simple_animal/horror/ui_act(action, params)
+ if(..())
+ return
+ switch(action)
+ if("unlock")
+ add_ability(params["typepath"])
+ if("upgrade")
+ add_upgrade(params["id"])
\ No newline at end of file
diff --git a/code/modules/client/verbs/suicide.dm b/code/modules/client/verbs/suicide.dm
index 8ed47046ed96..f26416fe6098 100644
--- a/code/modules/client/verbs/suicide.dm
+++ b/code/modules/client/verbs/suicide.dm
@@ -274,4 +274,7 @@
if(!(mobility_flags & MOBILITY_USE)) //just while I finish up the new 'fun' suiciding verb. This is to prevent metagaming via suicide
to_chat(src, "You can't commit suicide whilst immobile! ((You can type Ghost instead however.))")
return
+ if(has_horror_inside())
+ to_chat(src, "Something inside your head stops your action!")
+ return
return TRUE
diff --git a/code/modules/events/horror.dm b/code/modules/events/horror.dm
new file mode 100644
index 000000000000..dd42420f8477
--- /dev/null
+++ b/code/modules/events/horror.dm
@@ -0,0 +1,34 @@
+/datum/round_event_control/horror
+ name = "Spawn Eldritch Horror"
+ typepath = /datum/round_event/ghost_role/horror
+ max_occurrences = 2
+ min_players = 15
+ earliest_start = 20 MINUTES
+
+/datum/round_event/ghost_role/horror
+ minimum_required = 1
+ role_name = "horror"
+ fakeable = FALSE
+
+/datum/round_event/ghost_role/horror/spawn_role()
+ var/list/candidates = get_candidates(ROLE_HORROR, null, ROLE_HORROR)
+ if(!candidates.len)
+ return NOT_ENOUGH_PLAYERS
+
+ var/mob/dead/selected = pick_n_take(candidates)
+
+ var/datum/mind/player_mind = new /datum/mind(selected.key)
+ player_mind.active = 1
+ if(!GLOB.generic_event_spawns)
+ return MAP_ERROR
+ var/mob/living/simple_animal/horror/S = new /mob/living/simple_animal/horror(get_turf(pick(GLOB.generic_event_spawns)))
+ player_mind.transfer_to(S)
+ player_mind.assigned_role = "Eldritch Horror"
+ player_mind.special_role = "Eldritch Horror"
+ player_mind.add_antag_datum(/datum/antagonist/horror)
+ to_chat(S, S.playstyle_string)
+ SEND_SOUND(S, sound('sound/hallucinations/growl2.ogg'))
+ message_admins("[ADMIN_LOOKUPFLW(S)] has been made into an eldritch horror by an event.")
+ log_game("[key_name(S)] was spawned as an eldritch horror by an event.")
+ spawned_mobs += S
+ return SUCCESSFUL_SPAWN
\ No newline at end of file
diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm
index a62bc5b2cc3d..afb0dba21fcc 100644
--- a/code/modules/mob/living/brain/brain_item.dm
+++ b/code/modules/mob/living/brain/brain_item.dm
@@ -56,6 +56,10 @@
/obj/item/organ/brain/Remove(mob/living/carbon/C, special = 0, no_id_transfer = FALSE)
..()
+ if(!special)
+ if(C.has_horror_inside())
+ var/mob/living/simple_animal/horror/B = C.has_horror_inside()
+ B.leave_victim()
if(C.mind && C.mind.has_antag_datum(/datum/antagonist/changeling))
var/datum/antagonist/changeling/bruh = C.mind.has_antag_datum(/datum/antagonist/changeling)
for(var/d in bruh.purchasedpowers)
diff --git a/code/modules/mob/living/carbon/death.dm b/code/modules/mob/living/carbon/death.dm
index 9f26b38f384d..72df44048298 100644
--- a/code/modules/mob/living/carbon/death.dm
+++ b/code/modules/mob/living/carbon/death.dm
@@ -25,7 +25,10 @@
stomach_contents.Remove(M)
//yogs end
M.forceMove(Tsec)
- visible_message(span_danger("[M] bursts out of [src]!"))
+ if(ishorror(M)) //eldritch horror(aka. borer) check, they die along with their host to prevent mind controlled suicides
+ M.gib()
+ else
+ visible_message(span_danger("[M] bursts out of [src]!"))
..()
/mob/living/carbon/spill_organs(no_brain, no_organs, no_bodyparts)
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 790157d0ccf2..6aa6e38d7e75 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -73,6 +73,10 @@
. += ""
. += "Hivemind Vessels: [hivemind.hive_size] (+[hivemind.size_mod])"
. += "Psychic Link Duration: [(hivemind.track_bonus + TRACKER_DEFAULT_TIME)/10] seconds"
+ var/mob/living/simple_animal/horror/H = has_horror_inside()
+ if(H && H.controlling)
+ . += ""
+ . += "Horror chemicals: [H.chemicals]"
if(mind)
var/datum/antagonist/changeling/changeling = mind.has_antag_datum(/datum/antagonist/changeling)
diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm
index fe9fa8fb1d74..b42ed6b9e007 100644
--- a/code/modules/mob/transform_procs.dm
+++ b/code/modules/mob/transform_procs.dm
@@ -3,6 +3,9 @@
/mob/living/carbon/proc/monkeyize(tr_flags = (TR_KEEPITEMS | TR_KEEPVIRUS | TR_KEEPSTUNS | TR_KEEPREAGENTS | TR_DEFAULTMSG))
if (notransform || transformation_timer)
return
+ if(has_horror_inside())
+ to_chat(src, "You feel something strongly clinging to your humanity!")
+ return
//Handle items on mob
if(tr_flags & TR_KEEPITEMS)
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index 4165425f4056..ef88766149be 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -554,6 +554,10 @@
semicd = FALSE
+ if(user == target && user.has_horror_inside())
+ user.visible_message(span_warning("[user] decided not to shoot."), span_notice("Something inside your head stops your action!"))
+ return
+
target.visible_message(span_warning("[user] pulls the trigger!"), span_userdanger("[(user == target) ? "You pull" : "[user] pulls"] the trigger!"))
if(chambered && chambered.BB)
diff --git a/code/modules/surgery/organ_manipulation.dm b/code/modules/surgery/organ_manipulation.dm
index cac1afb711c3..42a2f3322d13 100644
--- a/code/modules/surgery/organ_manipulation.dm
+++ b/code/modules/surgery/organ_manipulation.dm
@@ -102,6 +102,11 @@
else if(implement_type in implements_extract)
current_type = "extract"
var/list/organs = target.getorganszone(target_zone)
+ var/mob/living/simple_animal/horror/H = target.has_horror_inside()
+ if(H)
+ user.visible_message("[user] begins to extract [H] from [target]'s [parse_zone(target_zone)].",
+ "You begin to extract [H] from [target]'s [parse_zone(target_zone)]...")
+ return TRUE
if(!organs.len)
to_chat(user, span_notice("There are no removable organs in [target]'s [parse_zone(target_zone)]!"))
return -1
@@ -143,6 +148,13 @@
"[user] inserts something into [target]'s [parse_zone(target_zone)]!")
else if(current_type == "extract")
+ var/mob/living/simple_animal/horror/H = target.has_horror_inside()
+ if(H && H.victim == target)
+ user.visible_message("[user] successfully extracts [H] from [target]'s [parse_zone(target_zone)]!",
+ "You successfully extract [H] from [target]'s [parse_zone(target_zone)].")
+ log_combat(user, target, "surgically removed [H] from", addition="INTENT: [uppertext(user.a_intent)]")
+ H.leave_victim()
+ return FALSE
if(I && I.owner == target)
display_results(user, target, span_notice("You successfully extract [I] from [target]'s [parse_zone(target_zone)]."),
"[user] successfully extracts [I] from [target]'s [parse_zone(target_zone)]!",
diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm
index 010909ccac4b..d59ce0fec0a4 100644
--- a/code/modules/uplink/uplink_items.dm
+++ b/code/modules/uplink/uplink_items.dm
@@ -2000,6 +2000,17 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
cost = 20
restricted_roles = list("Chaplain")
surplus = 5 //Very low chance to get it in a surplus crate even without being the chaplain
+
+/datum/uplink_item/role_restricted/horror
+ name = "Horror-in-a-box"
+ desc = "When dissecting the head of a dead Nanotrasen scientist, our surgeons noticed an incredibly peculiar creature inside and managed to extract it into safe containment. \
+ Either a failed experiment or otherworldly monster, this creature has been trained to aid whoever wakes it up. If you aren't afraid of it entering your head, it can prove a useful ally. \
+ We take no responsibility for your newfound madness and accept no refunds."
+ item = /obj/item/horrorspawner
+ cost = 16
+ surplus = 0
+ restricted_roles = list("Curator")
+ player_minimum = 20
/datum/uplink_item/role_restricted/explosive_hot_potato
name = "Exploding Hot Potato"
diff --git a/icons/effects/beam.dmi b/icons/effects/beam.dmi
index 0f7daf276d07..fbe6182716b7 100644
Binary files a/icons/effects/beam.dmi and b/icons/effects/beam.dmi differ
diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi
index 1a3d0b762527..59a5ad1a79a3 100644
Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ
diff --git a/icons/mob/actions/actions_horror.dmi b/icons/mob/actions/actions_horror.dmi
new file mode 100644
index 000000000000..3d0286a0e5e9
Binary files /dev/null and b/icons/mob/actions/actions_horror.dmi differ
diff --git a/icons/mob/animal.dmi b/icons/mob/animal.dmi
index eec8a5886a33..9489e06ca118 100644
Binary files a/icons/mob/animal.dmi and b/icons/mob/animal.dmi differ
diff --git a/icons/mob/inhands/antag/horror_lefthand.dmi b/icons/mob/inhands/antag/horror_lefthand.dmi
new file mode 100644
index 000000000000..c28c81ab31ba
Binary files /dev/null and b/icons/mob/inhands/antag/horror_lefthand.dmi differ
diff --git a/icons/mob/inhands/antag/horror_righthand.dmi b/icons/mob/inhands/antag/horror_righthand.dmi
new file mode 100644
index 000000000000..10d2c64115a5
Binary files /dev/null and b/icons/mob/inhands/antag/horror_righthand.dmi differ
diff --git a/icons/mob/screen_gen.dmi b/icons/mob/screen_gen.dmi
index 6d8925ce03d3..8538a0b9c613 100644
Binary files a/icons/mob/screen_gen.dmi and b/icons/mob/screen_gen.dmi differ
diff --git a/icons/obj/items_and_weapons.dmi b/icons/obj/items_and_weapons.dmi
index 6e57981c9197..1ba59e8fcbf6 100644
Binary files a/icons/obj/items_and_weapons.dmi and b/icons/obj/items_and_weapons.dmi differ
diff --git a/strings/names/horror.txt b/strings/names/horror.txt
new file mode 100644
index 000000000000..90b16322588d
--- /dev/null
+++ b/strings/names/horror.txt
@@ -0,0 +1,11 @@
+F'tan
+Hoe'lef
+Y'gge
+Uhluhtc
+Ret'sehc
+Geansrev
+Fnurgeetav
+Beq'pewks
+Man'lei
+Gvyrm-itei
+Se pge'ti
diff --git a/strings/tips.txt b/strings/tips.txt
index 2db26c09458a..9a6f5c96ba9e 100644
--- a/strings/tips.txt
+++ b/strings/tips.txt
@@ -236,6 +236,13 @@ As a Revenant, the illness inflicted on humans by Blight can be easily cured by
As a Swarmer, you can deconstruct more things than you think. Try deconstructing light switches, buttons, air alarms and more. Experiment!
As a Swarmer, you can teleport fellow swarmers away if you think they are in danger.
As a Morph, you can talk while disguised, but your words have a chance of being slurred, giving you away!
+As an Eldritch Horror, improvise! You have many abilities you can mutate, most of them applying to diverse situations. Experiment!
+As an Eldritch Horror, don't forget that you're not exempt from server rules while mind-controlling someone. Their actions become your actions.
+You can remove an Eldritch Horror from someone's head via surgery. Decapitation also works!
+You can detect an Eldritch Horror inside someone's head with an advanced health analyzer.
+If you see an Eldritch Horror trying to enter someone's head, you can quickly shove it away to interrupt the process!
+Eldritch Horrors can enter any creature that has a brain organ. Yes, that includes xenomorphs.
+Contrary to popular belief, Eldritch Horrors are not "cute".
As a Drone, you can ping other drones to alert them of areas in the station in need of repair.
As a Ghost, you can see the inside of a container on the ground by clicking on it.
As a Ghost, you can double click on just about anything to follow it. Or just warp around!
@@ -269,4 +276,4 @@ Anything you can light a cigarette with, you can use to cauterize a bleeding wou
Moderate and Severe cuts will slowly clot by themselves and don't require immediate attention on their own. Critical cuts will actively get worse with time, and are an immediate threat to the patient's life.
Laying down will help slow down bloodloss. Death will halt it entirely.
If you're bleeding, you can apply pressure to the limb by grabbing yourself while targeting the bleeding limb. This will slow you down and take up a hand, but it'll slow down how fast you lose blood. Note this won't help the bleeding clot any faster.
-The TOME OF HERBAL KNOWLEDGE in the ashwalker's den provides useful information on surviving the lavaland wastes.
+The TOME OF HERBAL KNOWLEDGE in the ashwalker's den provides useful information on surviving the lavaland wastes.
\ No newline at end of file
diff --git a/tgui/packages/tgui/interfaces/HorrorMutate.js b/tgui/packages/tgui/interfaces/HorrorMutate.js
new file mode 100644
index 000000000000..95bed928f155
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/HorrorMutate.js
@@ -0,0 +1,58 @@
+import { useBackend } from '../backend';
+import { Button, LabeledList, Section, Box } from '../components';
+import { Window } from '../layouts';
+
+export const HorrorMutate = (props, context) => {
+ const { act, data } = useBackend(context);
+ return (
+
+
+
+
+
+ {data.available_points}
+
+
+
+
+
+ {data.abilities.map(ability => (
+
+
+ {ability.desc}
+ Cost to unlock: {ability.soul_cost}
+
+ ))}
+
+
+
+
+ {data.upgrades.map(upgrade => (
+
+
+ {upgrade.desc}
+ Cost to unlock: {upgrade.soul_cost}
+
+ ))}
+
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/yogstation.dme b/yogstation.dme
index 4bed84d3f70e..34d83bee46ee 100644
--- a/yogstation.dme
+++ b/yogstation.dme
@@ -229,6 +229,7 @@
#include "code\_onclick\hud\generic_dextrous.dm"
#include "code\_onclick\hud\ghost.dm"
#include "code\_onclick\hud\guardian.dm"
+#include "code\_onclick\hud\horror.dm"
#include "code\_onclick\hud\hud.dm"
#include "code\_onclick\hud\human.dm"
#include "code\_onclick\hud\lavaland_elite.dm"
@@ -1504,6 +1505,12 @@
#include "code\modules\antagonists\highlander\highlander.dm"
#include "code\modules\antagonists\hivemind\hivemind.dm"
#include "code\modules\antagonists\hivemind\vessel.dm"
+#include "code\modules\antagonists\horror\horror.dm"
+#include "code\modules\antagonists\horror\horror_abilities_and_upgrades.dm"
+#include "code\modules\antagonists\horror\horror_chemicals.dm"
+#include "code\modules\antagonists\horror\horror_datums.dm"
+#include "code\modules\antagonists\horror\horror_html.dm"
+#include "code\modules\antagonists\horror\horror_mutate.dm"
#include "code\modules\antagonists\kudzu\kudzu.dm"
#include "code\modules\antagonists\magic_servant\servant.dm"
#include "code\modules\antagonists\malf\malf.dm"
@@ -1839,6 +1846,7 @@
#include "code\modules\events\grid_check.dm"
#include "code\modules\events\heart_attack.dm"
#include "code\modules\events\high_priority_bounty.dm"
+#include "code\modules\events\horror.dm"
#include "code\modules\events\immovable_rod.dm"
#include "code\modules\events\ion_storm.dm"
#include "code\modules\events\major_dust.dm"