From 4d369ae1dbdd0e7ad798c27e58d2d7ea33ec87e1 Mon Sep 17 00:00:00 2001
From: ChesterTheCheesy <71487903+ChesterTheCheesy@users.noreply.github.com>
Date: Wed, 2 Feb 2022 19:44:08 +0100
Subject: [PATCH] Eldritch horror - the final PR (#13033)
* Update is_helpers.dm
* Update role_preferences.dm
* Update traits.dm
* Update names.dm
* Update traumas.dm
* Update miscellaneous.dm
* Update scanners.dm
* Update sql_ban_system.dm
* Add files via upload
* Update headcrab.dm
* Update panacea.dm
* Create e
* Delete e
* adds horror
* Add files via upload
* Update suicide.dm
* Add files via upload
* Update brain_item.dm
* Update death.dm
* Update human.dm
* Update transform_procs.dm
* Update gun.dm
* Update organ_manipulation.dm
* Update uplink_items.dm
* Add files via upload
* Add files via upload
* Add files via upload
* Add files via upload
* Add files via upload
* Add files via upload
* Add files via upload
* Create horror.txt
* Add files via upload
* Update yogstation.dme
* adds horror-relatated tips
* fixes a typo
* Update code/_onclick/hud/horror.dm
Co-authored-by: TheGamerdk <5618080+TheGamerdk@users.noreply.github.com>
* Update code/game/objects/effects/temporary_visuals/miscellaneous.dm
Co-authored-by: TheGamerdk <5618080+TheGamerdk@users.noreply.github.com>
* Update code/game/objects/effects/temporary_visuals/miscellaneous.dm
Co-authored-by: TheGamerdk <5618080+TheGamerdk@users.noreply.github.com>
* Update code/modules/antagonists/horror/horror.dm
Co-authored-by: TheGamerdk <5618080+TheGamerdk@users.noreply.github.com>
* Update code/modules/antagonists/horror/horror.dm
Co-authored-by: TheGamerdk <5618080+TheGamerdk@users.noreply.github.com>
* Update code/modules/antagonists/horror/horror.dm
Co-authored-by: TheGamerdk <5618080+TheGamerdk@users.noreply.github.com>
* Update code/modules/antagonists/horror/horror.dm
Co-authored-by: TheGamerdk <5618080+TheGamerdk@users.noreply.github.com>
* Update code/modules/antagonists/horror/horror.dm
Co-authored-by: TheGamerdk <5618080+TheGamerdk@users.noreply.github.com>
* Update code/modules/antagonists/horror/horror.dm
Co-authored-by: TheGamerdk <5618080+TheGamerdk@users.noreply.github.com>
* Update code/modules/antagonists/horror/horror.dm
Co-authored-by: TheGamerdk <5618080+TheGamerdk@users.noreply.github.com>
* Update scanners.dm
* Update code/modules/antagonists/changeling/powers/headcrab.dm
Co-authored-by: TheGamerdk <5618080+TheGamerdk@users.noreply.github.com>
* i hope this won't break again
* actually makes horror unable to force lings to suicide
* fixes duplicate definition
* Not needed, changeling code already checks for that
* Cleans up a lot of code
* thank you theos for absolutely nerfing the crap out of horror tentacle
* cleans up a lot of bibby's suggestions and fixes a few bugs
* not sure if i changed anything in this file, but fuck it
* lets fix that merge conflict
* i sure hope github doesn't go nuclear
* oops made horror unable to work
* i hate logic gates
* sorry for commit spam but this thing literally broke horror and i wanna fix it as soon as possibles
* HAHA STUPID DE MORGAN! (tested works as intended)
* abominations summoned by curator horn should not attack horror anymore
* adds missing "
* cannot use lube ability whilst dead anymore
* Update horror_abilities_and_upgrades.dm
* flailing lube stops when you're killed while doing it
* fixes ordering so you can't get stuck in wrong animation at the last spin
* Horror can no longer squeeze through welded airlocks
* Infesting people is now assigned to Alt+Click
* makes tentacle arm no longer affected by range multipliers
Co-authored-by: TheGamerdk <5618080+TheGamerdk@users.noreply.github.com>
---
code/__DEFINES/is_helpers.dm | 2 +
code/__DEFINES/role_preferences.dm | 2 +
code/__DEFINES/traits.dm | 1 +
code/_globalvars/lists/names.dm | 1 +
code/_onclick/hud/horror.dm | 18 +
code/controllers/subsystem/traumas.dm | 6 +-
.../temporary_visuals/miscellaneous.dm | 5 +
code/game/objects/items/devices/scanners.dm | 3 +
code/modules/admin/sql_ban_system.dm | 4 +-
.../antagonists/changeling/powers/headcrab.dm | 3 +-
.../antagonists/changeling/powers/panacea.dm | 7 +
code/modules/antagonists/horror/horror.dm | 848 ++++++++++++++++++
.../horror/horror_abilities_and_upgrades.dm | 461 ++++++++++
.../antagonists/horror/horror_chemicals.dm | 96 ++
.../antagonists/horror/horror_datums.dm | 333 +++++++
.../modules/antagonists/horror/horror_html.dm | 102 +++
.../antagonists/horror/horror_mutate.dm | 97 ++
code/modules/client/verbs/suicide.dm | 3 +
code/modules/events/horror.dm | 34 +
code/modules/mob/living/brain/brain_item.dm | 4 +
code/modules/mob/living/carbon/death.dm | 5 +-
code/modules/mob/living/carbon/human/human.dm | 4 +
code/modules/mob/transform_procs.dm | 3 +
code/modules/projectiles/gun.dm | 4 +
code/modules/surgery/organ_manipulation.dm | 12 +
code/modules/uplink/uplink_items.dm | 11 +
icons/effects/beam.dmi | Bin 114407 -> 115618 bytes
icons/effects/effects.dmi | Bin 901936 -> 902657 bytes
icons/mob/actions/actions_horror.dmi | Bin 0 -> 17343 bytes
icons/mob/animal.dmi | Bin 235830 -> 256133 bytes
icons/mob/inhands/antag/horror_lefthand.dmi | Bin 0 -> 402 bytes
icons/mob/inhands/antag/horror_righthand.dmi | Bin 0 -> 405 bytes
icons/mob/screen_gen.dmi | Bin 116146 -> 117012 bytes
icons/obj/items_and_weapons.dmi | Bin 95651 -> 96453 bytes
strings/names/horror.txt | 11 +
strings/tips.txt | 9 +-
tgui/packages/tgui/interfaces/HorrorMutate.js | 58 ++
yogstation.dme | 8 +
38 files changed, 2147 insertions(+), 8 deletions(-)
create mode 100644 code/_onclick/hud/horror.dm
create mode 100644 code/modules/antagonists/horror/horror.dm
create mode 100644 code/modules/antagonists/horror/horror_abilities_and_upgrades.dm
create mode 100644 code/modules/antagonists/horror/horror_chemicals.dm
create mode 100644 code/modules/antagonists/horror/horror_datums.dm
create mode 100644 code/modules/antagonists/horror/horror_html.dm
create mode 100644 code/modules/antagonists/horror/horror_mutate.dm
create mode 100644 code/modules/events/horror.dm
create mode 100644 icons/mob/actions/actions_horror.dmi
create mode 100644 icons/mob/inhands/antag/horror_lefthand.dmi
create mode 100644 icons/mob/inhands/antag/horror_righthand.dmi
create mode 100644 strings/names/horror.txt
create mode 100644 tgui/packages/tgui/interfaces/HorrorMutate.js
diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm
index 9206945cb24b..ad37bb80e11d 100644
--- a/code/__DEFINES/is_helpers.dm
+++ b/code/__DEFINES/is_helpers.dm
@@ -117,6 +117,8 @@ GLOBAL_LIST_INIT(turfs_without_ground, typecacheof(list(
#define isrevenant(A) (istype(A, /mob/living/simple_animal/revenant))
+#define ishorror(A) (istype(A, /mob/living/simple_animal/horror))
+
#define isbot(A) (istype(A, /mob/living/simple_animal/bot))
#define isshade(A) (istype(A, /mob/living/simple_animal/shade))
diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm
index 00dc098a3d34..604486e4b9bd 100644
--- a/code/__DEFINES/role_preferences.dm
+++ b/code/__DEFINES/role_preferences.dm
@@ -44,6 +44,7 @@
#define ROLE_GANG "gangster" // Yogs
#define ROLE_DARKSPAWN "darkspawn" // Yogs
#define ROLE_HOLOPARASITE "Holoparasite" // Yogs
+#define ROLE_HORROR "Eldritch Horror" // Yogs
#define ROLE_INFILTRATOR "Infiltrator" // Yogs
#define ROLE_ZOMBIE "Zombie"
@@ -63,6 +64,7 @@ GLOBAL_LIST_INIT(special_roles, list(
ROLE_MALF,
ROLE_REV = /datum/game_mode/revolution,
ROLE_ALIEN,
+ ROLE_HORROR,
ROLE_PAI,
ROLE_CULTIST = /datum/game_mode/cult,
ROLE_BLOB,
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index c49127608823..47d1d613ed65 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -319,6 +319,7 @@
#define GUARDIAN_TRAIT "guardian_trait"
#define RANDOM_BLACKOUTS "random_blackouts"
#define MADE_UNCLONEABLE "made-uncloneable"
+#define HORROR_TRAIT "horror"
///Traits given by station traits
#define STATION_TRAIT_BANANIUM_SHIPMENTS "station_trait_bananium_shipments"
diff --git a/code/_globalvars/lists/names.dm b/code/_globalvars/lists/names.dm
index e36e464685e0..32029882bf1d 100644
--- a/code/_globalvars/lists/names.dm
+++ b/code/_globalvars/lists/names.dm
@@ -20,6 +20,7 @@ GLOBAL_LIST_INIT(plasmaman_names, world.file2list("strings/names/plasmaman.txt")
GLOBAL_LIST_INIT(ethereal_names, world.file2list("strings/names/ethereal.txt"))
GLOBAL_LIST_INIT(posibrain_names, world.file2list("strings/names/posibrain.txt"))
GLOBAL_LIST_INIT(nightmare_names, world.file2list("strings/names/nightmare.txt"))
+GLOBAL_LIST_INIT(horror_names, world.file2list("strings/names/horror.txt"))
GLOBAL_LIST_INIT(megacarp_first_names, world.file2list("strings/names/megacarp1.txt"))
GLOBAL_LIST_INIT(megacarp_last_names, world.file2list("strings/names/megacarp2.txt"))
diff --git a/code/_onclick/hud/horror.dm b/code/_onclick/hud/horror.dm
new file mode 100644
index 000000000000..876dc00be9ca
--- /dev/null
+++ b/code/_onclick/hud/horror.dm
@@ -0,0 +1,18 @@
+/obj/screen/horror_chemicals
+ name = "chemicals"
+ icon_state = "horror_counter"
+ screen_loc = ui_lingchemdisplay
+
+/datum/hud/chemical_counter
+ ui_style = 'icons/mob/screen_midnight.dmi'
+ var/obj/screen/horror_chemicals/chemical_counter
+
+/datum/hud/chemical_counter/New(mob/owner)
+ . = ..()
+ chemical_counter = new /obj/screen/horror_chemicals
+ infodisplay += chemical_counter
+
+/datum/hud/chemical_counter/Destroy()
+ . = ..()
+ QDEL_NULL(chemical_counter)
+
diff --git a/code/controllers/subsystem/traumas.dm b/code/controllers/subsystem/traumas.dm
index e7f6bdec80f6..7c9026e1c628 100644
--- a/code/controllers/subsystem/traumas.dm
+++ b/code/controllers/subsystem/traumas.dm
@@ -46,7 +46,7 @@ SUBSYSTEM_DEF(traumas)
"doctors" = typecacheof(list(/mob/living/simple_animal/bot/medbot)),
"the supernatural" = typecacheof(list(/mob/living/simple_animal/hostile/construct,
/mob/living/simple_animal/hostile/clockwork, /mob/living/simple_animal/drone/cogscarab,
- /mob/living/simple_animal/revenant, /mob/living/simple_animal/shade)),
+ /mob/living/simple_animal/revenant, /mob/living/simple_animal/shade, /mob/living/simple_animal/horror)),
"aliens" = typecacheof(list(/mob/living/carbon/alien, /mob/living/simple_animal/slime)),
"conspiracies" = typecacheof(list(/mob/living/simple_animal/bot/secbot, /mob/living/simple_animal/bot/ed209, /mob/living/simple_animal/drone,
/mob/living/simple_animal/pet/penguin)),
@@ -115,7 +115,7 @@ SUBSYSTEM_DEF(traumas)
/obj/item/clothing/suit/cultrobes, /obj/item/clothing/suit/space/hardsuit/cult,
/obj/item/clothing/suit/hooded/cultrobes, /obj/item/clothing/head/hooded/cult_hoodie, /obj/effect/rune,
/obj/item/stack/sheet/runed_metal, /obj/machinery/door/airlock/cult, /obj/singularity/narsie,
- /obj/item/soulstone,
+ /obj/item/soulstone, /obj/item/horrortentacle,
/obj/structure/destructible/clockwork, /obj/item/clockwork, /obj/item/clothing/suit/armor/clockwork,
/obj/item/clothing/glasses/judicial_visor, /obj/effect/clockwork/sigil/, /obj/item/stack/tile/brass,
/obj/machinery/door/airlock/clockwork,
@@ -142,7 +142,7 @@ SUBSYSTEM_DEF(traumas)
"anime" = typecacheof(list(/obj/item/clothing/under/schoolgirl, /obj/item/katana, /obj/item/reagent_containers/food/snacks/sashimi, /obj/item/reagent_containers/food/snacks/chawanmushi,
/obj/item/reagent_containers/food/drinks/bottle/sake, /obj/item/throwing_star, /obj/item/clothing/head/kitty/genuine, /obj/item/clothing/suit/space/space_ninja,
- /obj/item/clothing/mask/gas/space_ninja, /obj/item/clothing/shoes/space_ninja, /obj/item/clothing/gloves/space_ninja, /obj/item/twohanded/vibro_weapon,
+ /obj/item/clothing/mask/gas/space_ninja, /obj/item/clothing/shoes/space_ninja, /obj/item/clothing/gloves/space_ninja, /obj/item/twohanded/vibro_weapon, /obj/item/horrortentacle,
/obj/item/nullrod/scythe/vibro, /obj/item/energy_katana, /obj/item/toy/katana, /obj/item/nullrod/claymore/katana, /obj/structure/window/paperframe, /obj/structure/mineral_door/paperframe))
)
diff --git a/code/game/objects/effects/temporary_visuals/miscellaneous.dm b/code/game/objects/effects/temporary_visuals/miscellaneous.dm
index 12315ff83940..430ea149cb61 100644
--- a/code/game/objects/effects/temporary_visuals/miscellaneous.dm
+++ b/code/game/objects/effects/temporary_visuals/miscellaneous.dm
@@ -483,3 +483,8 @@
layer = FLY_LAYER
duration = 0.48 SECONDS
mouse_opacity = 0
+
+/obj/effect/temp_visual/summon
+ randomdir = FALSE
+ duration = 2 SECONDS
+ icon_state = "summon"
diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index a2372e14efdc..f2058fbaa721 100644
--- a/code/game/objects/items/devices/scanners.dm
+++ b/code/game/objects/items/devices/scanners.dm
@@ -200,6 +200,9 @@ GENE SCANNER
to_chat(user, "\t[span_info("Subject is allergic to the chemical [C.allergies].")]")
if(advanced)
to_chat(user, "\t[span_info("Brain Activity Level: [(200 - M.getOrganLoss(ORGAN_SLOT_BRAIN))/2]%.")]")
+ if(M.has_horror_inside())
+ to_chat(user, "\t[span_alert("Detected parasitic organism residing in the cranial area.")]")
+ to_chat(user, "\t[span_alert("Recommended course of action: organ manipulation surgery performed on head..")]")
if (M.radiation)
to_chat(user, "\t[span_alert("Subject is irradiated.")]")
diff --git a/code/modules/admin/sql_ban_system.dm b/code/modules/admin/sql_ban_system.dm
index e460a43f3fa4..617ec1323a7e 100644
--- a/code/modules/admin/sql_ban_system.dm
+++ b/code/modules/admin/sql_ban_system.dm
@@ -266,10 +266,10 @@
break_counter++
output += ""
var/list/long_job_lists = list("Civilian" = GLOB.original_civilian_positions,
- "Ghost and Other Roles" = list(ROLE_BRAINWASHED, ROLE_DEATHSQUAD, ROLE_DRONE, ROLE_FUGITIVE, ROLE_HOLOPARASITE, ROLE_LAVALAND, ROLE_MIND_TRANSFER, ROLE_POSIBRAIN, ROLE_SENTIENCE),
+ "Ghost and Other Roles" = list(ROLE_BRAINWASHED, ROLE_DEATHSQUAD, ROLE_DRONE, ROLE_FUGITIVE, ROLE_HOLOPARASITE, ROLE_HORROR, ROLE_LAVALAND, ROLE_MIND_TRANSFER, ROLE_POSIBRAIN, ROLE_SENTIENCE),
"Antagonist Positions" = list(ROLE_ABDUCTOR, ROLE_ALIEN, ROLE_BLOB,
ROLE_BROTHER, ROLE_CHANGELING, ROLE_CULTIST,
- ROLE_DEVIL, ROLE_FUGITIVE, ROLE_HOLOPARASITE, ROLE_INTERNAL_AFFAIRS, ROLE_MALF,
+ ROLE_DEVIL, ROLE_FUGITIVE, ROLE_HOLOPARASITE, ROLE_HORROR, ROLE_INTERNAL_AFFAIRS, ROLE_MALF,
ROLE_MONKEY, ROLE_NINJA, ROLE_OPERATIVE,
ROLE_REV, ROLE_REVENANT,
ROLE_REV_HEAD, ROLE_SERVANT_OF_RATVAR, ROLE_SYNDICATE,
diff --git a/code/modules/antagonists/changeling/powers/headcrab.dm b/code/modules/antagonists/changeling/powers/headcrab.dm
index ee2130a52e7c..6c9e8701a78a 100644
--- a/code/modules/antagonists/changeling/powers/headcrab.dm
+++ b/code/modules/antagonists/changeling/powers/headcrab.dm
@@ -7,7 +7,6 @@
dna_cost = 1
req_human = 1
ignores_fakedeath = TRUE
- req_stat = DEAD
/datum/action/changeling/headcrab/sting_action(mob/user)
set waitfor = FALSE
@@ -33,6 +32,8 @@
to_chat(S, span_userdanger("Your sensors are disabled by a shower of blood!"))
S.Paralyze(60)
var/turf = get_turf(user)
+ var/mob/living/simple_animal/horror/H = user.has_horror_inside()
+ H?.leave_victim()
user.gib()
. = TRUE
sleep(5) // So it's not killed in explosion
diff --git a/code/modules/antagonists/changeling/powers/panacea.dm b/code/modules/antagonists/changeling/powers/panacea.dm
index 0bc43b1f33bb..234b195b24d2 100644
--- a/code/modules/antagonists/changeling/powers/panacea.dm
+++ b/code/modules/antagonists/changeling/powers/panacea.dm
@@ -10,6 +10,13 @@
//Heals the things that the other regenerative abilities don't.
/datum/action/changeling/panacea/sting_action(mob/user)
to_chat(user, span_notice("We cleanse impurities from our form."))
+ var/mob/living/simple_animal/horror/H = user.has_horror_inside()
+ if(H)
+ H.leave_victim()
+ if(iscarbon(user))
+ var/mob/living/carbon/C = user
+ C.vomit(0, toxic = TRUE)
+ to_chat(user, span_notice("A parasite exits our form."))
..()
var/list/bad_organs = list(
user.getorgan(/obj/item/organ/body_egg),
diff --git a/code/modules/antagonists/horror/horror.dm b/code/modules/antagonists/horror/horror.dm
new file mode 100644
index 000000000000..e0653bcbb8e6
--- /dev/null
+++ b/code/modules/antagonists/horror/horror.dm
@@ -0,0 +1,848 @@
+/mob/living/simple_animal/horror
+ name = "eldritch horror"
+ desc = "Your eyes can barely comprehend what they're looking at."
+ icon_state = "horror"
+ icon_living = "horror"
+ icon_dead = "horror_dead"
+ icon_gib = "horror_gib"
+ health = 50
+ maxHealth = 50
+ melee_damage_lower = 10
+ melee_damage_upper = 10
+ see_in_dark = 5
+ stop_automated_movement = TRUE
+ attacktext = "bites"
+ speak_emote = list("gurgles")
+ attack_sound = 'sound/weapons/bite.ogg'
+ pass_flags = PASSTABLE | PASSMOB
+ mob_size = MOB_SIZE_SMALL
+ faction = list("neutral","silicon","creature","heretics","abomination")
+ ventcrawler = VENTCRAWLER_ALWAYS
+ initial_language_holder = /datum/language_holder/universal
+ hud_type = /datum/hud/chemical_counter
+
+ atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
+ minbodytemp = 0
+ maxbodytemp = 1500
+ unsuitable_atmos_damage = 0.5
+
+ var/playstyle_string = span_bold(span_big("You are an eldritch horror,") + " an evermutating parasitic abomination. Seek human souls to consume. \
+ Crawl into people's heads and steal their essence. Use it to mutate yourself, giving you access to more power and abilities. \
+ You operate on chemicals that get built up while you spend time in someone's head. You are weak when outside, play carefully. \
+ You can attack airlocks to squeeze yourself through them. " + span_danger("Alt+Click on people to infest them."))
+
+ var/mob/living/carbon/victim
+ var/datum/mind/target
+ var/mob/living/captive_brain/host_brain
+ var/available_points = 4
+ var/consumed_souls = 0
+
+ //An associative list (associated by ability typepaths) containing the abilities the horror has
+ var/list/horrorabilities = list()
+ //same (associated by their ID), but for permanent upgrades
+ var/list/horrorupgrades = list()
+ //list storing what items we have to un-glue when stopping mind control
+ var/list/clothing = list()
+
+ var/bonding = FALSE
+ var/controlling = FALSE
+ var/chemicals = 10
+ var/chem_regen_rate = 2
+ var/used_freeze
+ var/used_target
+ var/horror_chems = list(/datum/horror_chem/epinephrine,/datum/horror_chem/mannitol,/datum/horror_chem/bicaridine,/datum/horror_chem/kelotane,/datum/horror_chem/charcoal)
+
+ var/leaving = FALSE
+ var/hiding = FALSE
+ var/invisible = FALSE
+ var/datum/action/innate/horror/talk_to_horror/talk_to_horror_action = new
+
+/mob/living/simple_animal/horror/Initialize(mapload, gen=1)
+ ..()
+ real_name = "[pick(GLOB.horror_names)]"
+
+ //default abilities
+ add_ability(/datum/action/innate/horror/mutate)
+ add_ability(/datum/action/innate/horror/seek_soul)
+ add_ability(/datum/action/innate/horror/consume_soul)
+ add_ability(/datum/action/innate/horror/talk_to_host)
+ add_ability(/datum/action/innate/horror/freeze_victim)
+ add_ability(/datum/action/innate/horror/toggle_hide)
+ add_ability(/datum/action/innate/horror/talk_to_brain)
+ add_ability(/datum/action/innate/horror/take_control)
+ add_ability(/datum/action/innate/horror/leave_body)
+ add_ability(/datum/action/innate/horror/make_chems)
+ add_ability(/datum/action/innate/horror/give_back_control)
+ RefreshAbilities()
+
+ var/datum/atom_hud/hud = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED]
+ hud.add_hud_to(src)
+ update_horror_hud()
+
+
+/mob/living/simple_animal/horror/Destroy()
+ host_brain = null
+ victim = null
+ return ..()
+
+/mob/living/simple_animal/horror/AltClickOn(atom/A)
+ if(iscarbon(A))
+ var/mob/living/carbon/C = A
+ if(!C || QDELETED(src) || !Adjacent(C) || victim || !can_use_ability())
+ return
+ if(victim)
+ to_chat(src, span_warning("You are already within a host."))
+ return
+
+ to_chat(src, span_warning("You slither your tentacles up [C] and begin probing at their ear canal..."))
+
+ if(!do_mob(src, C, 3 SECONDS))
+ to_chat(src, span_warning("As [C] moves away, you are dislodged and fall to the ground."))
+ return
+
+ if(!C || QDELETED(src))
+ return
+ if(C.has_horror_inside())
+ to_chat(src, span_warning("[C] is already infested!"))
+ return
+ Infect(C)
+ return
+ ..()
+
+/mob/living/simple_animal/horror/proc/has_chemicals(amt)
+ return chemicals >= amt
+
+/mob/living/simple_animal/horror/proc/use_chemicals(amt)
+ if(!has_chemicals(amt))
+ return FALSE
+ chemicals -= amt
+ update_horror_hud()
+ return TRUE
+
+/mob/living/simple_animal/horror/proc/regenerate_chemicals(amt)
+ chemicals += amt
+ chemicals = min(250, chemicals)
+ update_horror_hud()
+
+/mob/living/simple_animal/horror/proc/update_horror_hud()
+ if(!src || !hud_used)
+ return
+ var/datum/hud/chemical_counter/H = hud_used
+ var/obj/screen/counter = H.chemical_counter
+ counter.maptext = "
[chemicals]
"
+
+/mob/living/simple_animal/horror/proc/can_use_ability()
+ if(stat != CONSCIOUS)
+ to_chat(src, "You cannot do that in your current state.")
+ return FALSE
+ return TRUE
+
+/mob/living/simple_animal/horror/proc/SearchTarget()
+ if(target)
+ if(world.time - used_target < 3 MINUTES)
+ to_chat(src, span_warning("You cannot use that ability again so soon."))
+ return
+ if(alert("You already have a target ([target.name]). Would you like to change that target?","Swap targets?","Yes","No") != "Yes")
+ return
+
+ 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.hasSoul && (mind.enslaved_to != M.current))
+ possible_targets[M] = M
+
+ var/list/selected_targets = list()
+ var/list/icons = list()
+ while(selected_targets.len != 4)
+ if(possible_targets.len <= 0)
+ break
+ var/datum/mind/M = pick(possible_targets)
+ selected_targets[M] = M
+ possible_targets -= M
+
+ var/mob/living/carbon/human/H = M.current
+ icons[M] = H
+
+ used_target = world.time
+
+ var/entry_name = show_radial_menu(src, (victim ? src.loc : src), icons, tooltips = TRUE)
+ target = selected_targets[entry_name]
+
+ //you didn't select your target? let me do that for you, my friend
+ if(selected_targets.len > 0 && !target)
+ target = pick(selected_targets)
+
+ if(target)
+ to_chat(src, span_warning("You caught their scent. Go and consume [target.current.real_name], the [target.assigned_role]'s soul!"))
+ apply_status_effect(/datum/status_effect/agent_pinpointer/horror)
+ for(var/datum/status_effect/agent_pinpointer/horror/status in status_effects)
+ status.scan_target = target.current
+ else
+ //refund cooldown
+ used_target = 0
+ to_chat(src, span_warning("Failed to select a target!"))
+
+/mob/living/simple_animal/horror/proc/ConsumeSoul()
+ if(!can_use_ability())
+ return
+
+ if(!victim.mind.hasSoul)
+ to_chat(src, "This host doesn't have a soul!")
+ return
+
+ if(victim == mind.enslaved_to)
+ to_chat(src, span_userdanger("No, not yet... We still need them..."))
+ return
+
+ if(victim.mind != target)
+ to_chat(src, "This soul isn't your target, you can't consume it!")
+ return
+
+ to_chat(src, "You begin consuming [victim.name]'s soul!")
+ if(do_after(src, 20 SECONDS, target = victim, stayStill = FALSE))
+ consume()
+
+/mob/living/simple_animal/horror/proc/consume()
+ if(!can_use_ability() || !victim || !victim.mind.hasSoul || victim.mind != target)
+ return
+ consumed_souls++
+ available_points++
+ to_chat(src, span_userdanger("You succeed in consuming [victim.name]'s soul!"))
+ to_chat(victim, span_userdanger("You suddenly feel weak and hollow inside..."))
+ victim.health -= 20
+ victim.maxHealth -= 20
+ victim.mind.hasSoul = FALSE
+ target = null
+ remove_status_effect(/datum/status_effect/agent_pinpointer/horror)
+ playsound(src, 'sound/effects/curseattack.ogg', 150)
+ playsound(src, 'sound/effects/ghost.ogg', 50)
+
+/mob/living/simple_animal/horror/proc/Communicate()
+ if(!can_use_ability())
+ return
+ if(!victim)
+ to_chat(src, "You do not have a host to communicate with!")
+ return
+
+ var/input = stripped_input(src, "Please enter a message to tell your host.", "Horror", null)
+ if(!input)
+ return
+
+ if(src && !QDELETED(src) && !QDELETED(victim))
+ if(victim)
+ to_chat(victim, span_changeling("[real_name] slurs: [input]"))
+ for(var/M in GLOB.dead_mob_list)
+ if(isobserver(M))
+ var/rendered = span_changeling("Horror Communication from [real_name] : [input]")
+ var/link = FOLLOW_LINK(M, src)
+ to_chat(M, "[link] [rendered]")
+ to_chat(src, span_changeling("[real_name] slurs: [input]"))
+ add_verb(victim, /mob/living/proc/horror_comm)
+ talk_to_horror_action.Grant(victim)
+
+/mob/living/proc/horror_comm()
+ set name = "Converse with Horror"
+ set category = "Horror"
+ set desc = "Communicate mentally with the thing in your head."
+
+ var/mob/living/simple_animal/horror/B = has_horror_inside()
+ if(B)
+ var/input = stripped_input(src, "Please enter a message to tell the horror.", "Message", "")
+ if(!input)
+ return
+
+ to_chat(B, span_changeling("[real_name] says: [input]"))
+
+ for(var/M in GLOB.dead_mob_list)
+ if(isobserver(M))
+ var/rendered = span_changeling("Horror Communication from [real_name] : [input]")
+ var/link = FOLLOW_LINK(M, src)
+ to_chat(M, "[link] [rendered]")
+ to_chat(src, span_changeling("[real_name] says: [input]"))
+
+/mob/living/proc/trapped_mind_comm()
+ var/mob/living/simple_animal/horror/B = has_horror_inside()
+ if(!B || !B.host_brain)
+ return
+ var/mob/living/captive_brain/CB = B.host_brain
+ var/input = stripped_input(src, "Please enter a message to tell the trapped mind.", "Message", null)
+ if(!input)
+ return
+
+ to_chat(CB, span_changeling("[B.real_name] says: [input]"))
+
+ for(var/M in GLOB.dead_mob_list)
+ if(isobserver(M))
+ var/rendered = span_changeling("Horror Communication from [B.real_name] : [input]")
+ var/link = FOLLOW_LINK(M, src)
+ to_chat(M, "[link] [rendered]")
+ to_chat(src, span_changeling("[B.real_name] says: [input]"))
+
+/mob/living/simple_animal/horror/Life()
+ ..()
+ if(has_upgrade("regen"))
+ heal_overall_damage(5)
+
+ if(invisible) //don't regenerate chemicals when invisible
+ if(use_chemicals(5))
+ alpha = max(alpha - 100, 1)
+ else
+ to_chat(src, span_warning("You ran out of chemicals to support your invisibility."))
+ invisible = FALSE
+ Update_Invisibility_Button()
+ else
+ if(has_upgrade("nohost_regen"))
+ regenerate_chemicals(chem_regen_rate)
+ else if(victim)
+ if(victim.stat == DEAD)
+ regenerate_chemicals(1)
+ else
+ regenerate_chemicals(chem_regen_rate)
+ alpha = min(255, alpha + 50)
+
+ if(victim)
+ if(stat != DEAD && victim.stat != DEAD)
+ heal_overall_damage(1)
+
+/mob/living/simple_animal/horror/say(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
+ if(victim)
+ to_chat(src, span_warning("You cannot speak out loud while inside a host!"))
+ return
+ return ..()
+
+/mob/living/simple_animal/horror/emote(act, m_type = null, message = null, intentional = FALSE)
+ if(victim)
+ to_chat(src, span_warning("You cannot emote while inside a host!"))
+ return
+ return ..()
+
+/mob/living/simple_animal/horror/UnarmedAttack(atom/A)
+ if(istype(A, /obj/machinery/door/airlock))
+ var/obj/machinery/door/airlock/door = A
+ if(door.welded)
+ to_chat(src, span_danger("The door is welded shut!"))
+ return
+ visible_message(span_warning("[src] slips their tentacles into the airlock and starts prying it open!"), span_warning("You start moving onto the airlock."))
+ playsound(A, 'sound/misc/splort.ogg', 50, 1)
+ if(do_after(src, 5 SECONDS, target = A))
+ if(door.welded)
+ to_chat(src, span_danger("The door is welded shut!"))
+ return
+ visible_message(span_warning("[src] forces themselves through the airlock!"), span_warning("You force yourself through the airlock"))
+ forceMove(get_turf(A))
+ playsound(A, 'sound/machines/airlock_alien_prying.ogg', 50, 1)
+ return
+
+ if(isliving(A))
+ if(victim || A == src.mind.enslaved_to)
+ healthscan(usr, A)
+ chemscan(usr, A)
+ else
+ alpha = 255
+ if(hiding)
+ var/datum/action/innate/horror/H = has_ability(/datum/action/innate/horror/toggle_hide)
+ H.Activate()
+ if(invisible)
+ var/datum/action/innate/horror/H = has_ability(/datum/action/innate/horror/chameleon)
+ H.Activate()
+ Update_Invisibility_Button()
+ ..()
+
+/mob/living/simple_animal/horror/ex_act()
+ if(victim)
+ return
+
+ ..()
+
+/mob/living/simple_animal/horror/proc/Infect(mob/living/carbon/C)
+ if(!C)
+ return
+ var/obj/item/bodypart/head/head = C.get_bodypart(BODY_ZONE_HEAD)
+ if(!head)
+ to_chat(src, span_warning("[C] doesn't have a head!"))
+ return
+ var/hasbrain = locate(/obj/item/organ/brain) in C.internal_organs
+
+ if(!hasbrain)
+ to_chat(src, span_warning("[C] doesn't have a brain!"))
+ return
+
+ if(C.has_horror_inside())
+ to_chat(src, span_warning("[C] is already infested!"))
+ return
+
+ //can only infect non-ssd alive people / corpses with ghosts attached / current target
+ if((C.stat == DEAD || !C.key) && (C.stat != DEAD || !C.get_ghost()) && (!target || C != target.current))
+ to_chat(src, span_warning("[C]'s mind seems unresponsive. Try someone else!"))
+ return
+
+ if(hiding)
+ var/datum/action/innate/horror/H = has_ability(/datum/action/innate/horror/toggle_hide)
+ H.Activate()
+ invisible = FALSE
+ Update_Invisibility_Button()
+
+ victim = C
+ forceMove(victim)
+ RefreshAbilities()
+ log_game("[src]/([src.ckey]) has infested [victim]/([victim.ckey]")
+
+/mob/living/simple_animal/horror/proc/secrete_chemicals()
+ if(!can_use_ability())
+ return
+ if(!victim)
+ to_chat(src, span_warning("You are not inside a host body."))
+ return
+
+ var/content = "Chemicals: [chemicals]
"
+ content += ""
+
+ 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
+
+
+
+
+
+
+
+
+
+ [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 ccd92d34bd88..21394e8255f0 100644
--- a/code/modules/uplink/uplink_items.dm
+++ b/code/modules/uplink/uplink_items.dm
@@ -2010,6 +2010,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 0f7daf276d07e686585778d466d6fad4fb5f0ec8..fbe6182716b74ae994728bc53303ab3f51722251 100644
GIT binary patch
delta 65325
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