diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm
index 9c9fa30559a..7b07f950888 100644
--- a/code/__DEFINES/is_helpers.dm
+++ b/code/__DEFINES/is_helpers.dm
@@ -114,6 +114,8 @@ GLOBAL_LIST_INIT(turfs_without_ground, typecacheof(list(
#define isrogueobserver(A) (istype(A, /mob/dead/observer/rogue))
+#define isscryeye(A) (istype(A, /mob/scry_eye))
+
#define isdead(A) (istype(A, /mob/dead))
#define isnewplayer(A) (istype(A, /mob/dead/new_player))
diff --git a/code/__DEFINES/traits/definitions.dm b/code/__DEFINES/traits/definitions.dm
index e8388daee89..d16530567f3 100644
--- a/code/__DEFINES/traits/definitions.dm
+++ b/code/__DEFINES/traits/definitions.dm
@@ -361,9 +361,6 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// Given to a mob that can throw to make them not able to throw
#define TRAIT_NO_THROWING "no_throwing"
-
-/// Hides the SSD indicator. Used with scrying.
-#define TRAIT_NOSSDINDICATOR "nossdindicator"
/// Instant grabs on someone else.
#define TRAIT_NOSTRUGGLE "nostruggle"
/// Black-bagged. More snowflaking.
diff --git a/code/_onclick/hud/ghost.dm b/code/_onclick/hud/ghost.dm
index b62ca22297b..a8f47f3cce6 100644
--- a/code/_onclick/hud/ghost.dm
+++ b/code/_onclick/hud/ghost.dm
@@ -67,9 +67,6 @@
if(!istype(ghost))
return
- if(istype(ghost, /mob/dead/observer/rogue/arcaneeye))
- return
-
if(ghost.isinhell)
return
diff --git a/code/_onclick/observer.dm b/code/_onclick/observer.dm
index 6fe1270ba13..6a5858332cd 100644
--- a/code/_onclick/observer.dm
+++ b/code/_onclick/observer.dm
@@ -18,9 +18,6 @@
/mob/dead/observer/profane/DblClickOn(atom/clicked_atom, params) // Souls trapped by the dagger should not be jumping around.
return
-/mob/dead/observer/rogue/arcaneeye/DblClickOn(atom/clicked_atom, params)
- return
-
/mob/dead/observer/ClickOn(atom/clicked_atom, params)
var/list/modifiers = params2list(params)
diff --git a/code/datums/components/orbiter.dm b/code/datums/components/orbiter.dm
index 00732b29525..01e1efd4f9c 100644
--- a/code/datums/components/orbiter.dm
+++ b/code/datums/components/orbiter.dm
@@ -102,8 +102,7 @@
orbiter.glide_size = movable_parent.glide_size
orbiter.abstract_move(get_turf(parent))
- if(!istype(orbiter, /mob/dead/observer/screye))
- to_chat(orbiter, span_notice("Now orbiting [parent]."))
+ to_chat(orbiter, span_notice("Now orbiting [parent]."))
/datum/component/orbiter/proc/end_orbit(atom/movable/orbiter, refreshing=FALSE)
if(!orbiter_list[orbiter])
diff --git a/code/datums/components/scrying.dm b/code/datums/components/scrying.dm
new file mode 100644
index 00000000000..6c7f341ef7f
--- /dev/null
+++ b/code/datums/components/scrying.dm
@@ -0,0 +1,277 @@
+/datum/component/scrying
+ var/name = "scrying component"
+
+ var/text_cooldown_fail = "I look into NAME_HERE but only see inky smoke. Maybe I should wait."
+
+ var/vision_duration = 8 SECONDS
+ var/cooldown_duration = 30 SECONDS
+
+ /// Whether or not the user of the scrying device needs to personally know the identity of their target.
+ var/needs_to_know = TRUE
+ /// Whether or not the target needs to be alive
+ var/needs_to_live = TRUE
+
+ var/mob/scry_eye/scrying_eye
+ var/mob/living/carbon/held_user
+
+ var/interact_method = "attack_self"
+ COOLDOWN_DECLARE(scry_cooldown)
+
+/datum/component/scrying/Initialize()
+ . = ..()
+ if(!isobj(parent))
+ return COMPONENT_INCOMPATIBLE
+ text_cooldown_fail = replacetext(text_cooldown_fail, "NAME_HERE", "\the [name]")
+
+/datum/component/scrying/RegisterWithParent()
+ switch(interact_method)
+ if("attack_self")
+ RegisterSignal(parent, COMSIG_ITEM_ATTACK_SELF, PROC_REF(activate))
+ if("attack_hand")
+ RegisterSignal(parent, COMSIG_ATOM_ATTACK_HAND, PROC_REF(activate))
+
+/datum/component/scrying/UnregisterFromParent()
+ switch(interact_method)
+ if("attack_self")
+ UnregisterSignal(parent, COMSIG_ITEM_ATTACK_SELF)
+ if("attack_hand")
+ UnregisterSignal(parent, COMSIG_ATOM_ATTACK_HAND)
+
+/datum/component/scrying/Destroy(force)
+ QDEL_NULL(scrying_eye)
+ held_user = null
+ return ..()
+
+/datum/component/scrying/proc/activate(datum/source, mob/user)
+ if(!pass_extra_checks(user))
+ return FALSE
+
+ if(!COOLDOWN_FINISHED(src, scry_cooldown))
+ to_chat(user, span_warning(text_cooldown_fail))
+ return FALSE
+
+ var/search_name = tgui_input_text(user, "Who are you looking for?", name)
+ if(!search_name)
+ return FALSE
+
+ //check is applied twice to prevent someone from bypassing the cooldown
+ if(!COOLDOWN_FINISHED(src, scry_cooldown))
+ to_chat(user, span_warning(text_cooldown_fail))
+ return FALSE
+
+ if(!user.mind || (needs_to_know && !user.mind.do_i_know(name = search_name)))
+ to_chat(user, span_warning("I don't know anyone by that name."))
+ return FALSE
+
+ var/mob/living/carbon/human/found_target
+ for(var/mob/living/carbon/human/human_target as anything in GLOB.human_list)
+ if(lowertext(human_target.real_name) == lowertext(search_name))
+ var/turf/target_turf = get_turf(human_target)
+ if(!target_turf)
+ continue
+ found_target = human_target
+ break
+
+ if(!found_target)
+ return
+
+ if(HAS_TRAIT(found_target, TRAIT_ANTISCRYING))
+ to_chat(user, span_warning("I peer into \the [name], but an impenetrable fog shrouds [search_name]."))
+ to_chat(found_target, span_warning("My magical shrouding reacted to something."))
+ return
+
+ if(needs_to_live && found_target.stat)
+ to_chat(user, span_warning("I peer into \the [name], but can't find [search_name]."))
+ return FALSE
+
+ held_user = user
+ create_eye()
+ if(!scrying_eye)
+ remove_eye(TRUE)
+ return
+
+ log_game("SCRYING: [user.real_name] ([user.ckey]) has used the [name] to scry [found_target.real_name] ([found_target.ckey])")
+
+ var/real_cooldown = cooldown_duration + vision_duration
+ COOLDOWN_START(src, scry_cooldown, real_cooldown)
+ user.visible_message(span_danger("[user] stares into \the [name], [user.p_their()] eyes rolling back into [user.p_their()] head."), span_warning("My eyes roll into the back of my head as I'm lost in the depths of the orb."))
+ scrying_eye.orbit(found_target)
+ var/target_perception = GET_MOB_ATTRIBUTE_VALUE(found_target, STAT_PERCEPTION)
+ if(target_perception >= 15)
+ if(found_target.mind)
+ if(found_target.mind.do_i_know(name = user.real_name))
+ to_chat(found_target, span_warning("I can clearly see the face of [user.real_name] staring at me!"))
+ to_chat(user, span_warning("[found_target.real_name] stares back at me!"))
+ return TRUE
+ to_chat(found_target, span_warning("I can clearly see the face of an unknown [user.gender == FEMALE ? "woman" : "man"] staring at me!"))
+ return TRUE
+ if(target_perception >= 11)
+ to_chat(found_target, span_warning("I feel a pair of unknown eyes on me."))
+ return TRUE
+
+/datum/component/scrying/proc/create_eye()
+ if(!held_user)
+ return FALSE
+ scrying_eye = new
+ scrying_eye.user_mob = held_user
+ held_user.reset_perspective(scrying_eye)
+ held_user.Immobilize(vision_duration)
+ held_user.overlay_fullscreen("scrying", /atom/movable/screen/backhudl/obscured)
+ addtimer(CALLBACK(src, PROC_REF(remove_eye)), vision_duration)
+
+/datum/component/scrying/proc/remove_eye(early = FALSE)
+ if(!held_user)
+ return FALSE
+ held_user.reset_perspective(held_user)
+ held_user.clear_fullscreen("scrying")
+ if(early)
+ held_user.SetImmobilized(2 SECONDS)
+ QDEL_NULL(scrying_eye)
+ held_user = null
+
+
+/datum/component/scrying/proc/pass_extra_checks(mob/living/user)
+ return TRUE
+
+/datum/component/scrying/orb
+ name = "Scrying Orb"
+
+/datum/component/scrying/orb/pass_extra_checks(mob/living/user)
+ if(GET_MOB_SKILL_VALUE_OLD(user, /datum/attribute/skill/magic/arcane) < 1)
+ to_chat(user, span_warning("I do not know what to do with this..."))
+ return FALSE
+ return TRUE
+
+/datum/component/scrying/eye
+ name = "Accursed Eye"
+ cooldown_duration = 5 MINUTES
+
+/datum/component/scrying/vampire
+ name = "Night's Eye"
+ needs_to_know = FALSE
+ needs_to_live = FALSE
+ vision_duration = 12 SECONDS
+ cooldown_duration = 3 SECONDS
+
+/datum/component/scrying/vampire/pass_extra_checks(mob/living/user)
+ if(!user?.mind.has_antag_datum(/datum/antagonist/vampire/lord))
+ to_chat(user, span_warning("I don't have the power to use this!"))
+ return FALSE
+ return TRUE
+
+/datum/component/scrying/telescope
+ name = "NOC Device"
+ text_cooldown_fail = "I peer into the sky but cannot focus the lens on the face of Noc. Maybe I should wait."
+ interact_method = "attack_hand"
+
+/datum/component/scrying/telescope/pass_extra_checks(mob/living/user)
+ var/mob/living/carbon/human/human_user = user
+ if(!ishuman(human_user) || !HAS_TRAIT(human_user, TRAIT_VIRGIN))
+ to_chat(human_user, span_warning("Noc looks angry with me..."))
+ return FALSE
+ return TRUE
+
+/datum/component/scrying/mirror
+ name = "Black Mirror"
+ vision_duration = 6 SECONDS
+ needs_to_know = FALSE
+ needs_to_live = FALSE
+ var/obj/item/inqarticles/bmirror/parent_mirror
+ var/mob/stored_target
+ var/atom/movable/screen/alert/blackmirror/effect
+
+/datum/component/scrying/mirror/Initialize(obj/item/scrying/parent)
+ . = ..()
+ parent_mirror = parent
+ if(!istype(parent_mirror))
+ return INITIALIZE_HINT_QDEL
+
+/datum/component/scrying/mirror/Destroy(force)
+ parent_mirror = null
+ stored_target = null
+ QDEL_NULL(effect)
+ . = ..()
+
+/datum/component/scrying/mirror/activate(mob/living/user)
+ if(!pass_extra_checks(user))
+ message_admins("SCRY DEBUG: EXTRA CHECKS FAIL")
+ return FALSE
+
+ if(!COOLDOWN_FINISHED(src, scry_cooldown))
+ to_chat(user, span_warning(text_cooldown_fail))
+ return FALSE
+
+ var/search_name = stripped_input(user, "Who are you looking for?", name)
+ if(!search_name)
+ return FALSE
+
+ if(!user.mind)
+ to_chat(user, span_warning("I don't know of anyone by that name."))
+ return FALSE
+
+ //check is applied twice to prevent someone from bypassing the cooldown
+ if(!COOLDOWN_FINISHED(src, scry_cooldown))
+ to_chat(user, span_warning(text_cooldown_fail))
+ return FALSE
+
+ for(var/mob/living/carbon/human/human_target in GLOB.human_list)
+ if(human_target.real_name == search_name)
+ var/turf/target_turf = get_turf(human_target)
+ if(!target_turf)
+ continue
+ stored_target = human_target
+ break
+
+ held_user = user
+ if(HAS_TRAIT(stored_target, TRAIT_ANTISCRYING))
+ to_chat(user, span_warning("I peer into \the [name], but an impenetrable fog shrouds [search_name]."))
+ to_chat(stored_target, span_warning("My magical shrouding reacted to something."))
+ held_user = null
+ return
+
+ create_eye()
+ if(!scrying_eye)
+ remove_eye(TRUE)
+ return
+
+ log_game("SCRYING: [user.real_name] ([user.ckey]) has used the [name] to leer at [stored_target.real_name] ([stored_target.ckey])")
+
+ var/real_cooldown = cooldown_duration + vision_duration
+ COOLDOWN_START(src, scry_cooldown, real_cooldown)
+ user.visible_message(span_danger("[user] stares into \the [name], [user.p_their()] eyes rolling back into [user.p_their()] head."), span_warning("My eyes roll into the back of my head as I'm lost in the depths of the orb."))
+ apply_black_eye()
+ return TRUE
+
+/datum/component/scrying/mirror/create_eye()
+ if(!held_user)
+ return FALSE
+ scrying_eye = new
+ scrying_eye.user_mob = held_user
+ held_user.reset_perspective(scrying_eye)
+ held_user.Immobilize(vision_duration)
+ held_user.overlay_fullscreen("scrying", /atom/movable/screen/backhudl/obscured)
+ playsound(held_user, 'sound/items/blackmirror_use.ogg', 100, FALSE)
+ addtimer(CALLBACK(src, PROC_REF(remove_eye)), vision_duration)
+
+/datum/component/scrying/mirror/remove_eye(early = FALSE)
+ if(!held_user)
+ return FALSE
+ held_user.reset_perspective(held_user)
+ held_user.clear_fullscreen("scrying")
+ playsound(held_user, 'sound/items/blackeye.ogg', 100, FALSE)
+ if(early)
+ held_user.SetImmobilized(2 SECONDS)
+ QDEL_NULL(scrying_eye)
+ held_user = null
+ if(stored_target)
+ stored_target.clear_alert("blackmirror", TRUE)
+ stored_target.playsound_local(src, 'sound/items/blackeye.ogg', 40, FALSE)
+ stored_target = null
+ parent_mirror.donefixating()
+ effect = null
+
+/datum/component/scrying/mirror/proc/apply_black_eye()
+ scrying_eye.orbit(stored_target)
+ effect = stored_target.throw_alert("blackmirror", /atom/movable/screen/alert/blackmirror, override = TRUE)
+ effect.source = parent_mirror
+ playsound(stored_target, 'sound/items/blackeye_warn.ogg', 100, FALSE)
diff --git a/code/game/objects/items/inquisition_relics.dm b/code/game/objects/items/inquisition_relics.dm
index 33c32bb81f6..90949a4a365 100644
--- a/code/game/objects/items/inquisition_relics.dm
+++ b/code/game/objects/items/inquisition_relics.dm
@@ -1179,24 +1179,16 @@
var/usesleft = 3
var/active = FALSE
var/broken = FALSE
- /// Target name
- var/datum/weakref/fixation
- /// One with the bleed in the mirror
- var/datum/weakref/feeder
- var/atom/movable/screen/alert/blackmirror/effect
var/datum/looping_sound/blackmirror/soundloop
/obj/item/inqarticles/bmirror/Initialize()
. = ..()
soundloop = new(src, FALSE)
+ AddComponent(/datum/component/scrying/mirror)
/obj/item/inqarticles/bmirror/Destroy()
if(soundloop)
QDEL_NULL(soundloop)
- if(effect)
- QDEL_NULL(effect)
- fixation = null
- feeder = null
return ..()
/obj/item/inqarticles/bmirror/examine(mob/user)
@@ -1211,13 +1203,6 @@
active = FALSE
fedblood = FALSE
openstate = "bloody"
- feeder = null
- var/mob/living/fixated = fixation?.resolve()
- if(fixated)
- fixated.clear_alert("blackmirror", TRUE)
- fixated.playsound_local(src, 'sound/items/blackeye.ogg', 40, FALSE)
- effect = null
- fixation = null
usesleft--
soundloop.stop()
visible_message(span_info("[src] clouds itself with a chilling fog."))
@@ -1264,69 +1249,9 @@
to_chat(user, span_warning("It looks like it needs blood to work properly."))
return
- if(!active)
- var/mob/living/carbon/human/target = fixation?.resolve()
- var/input
- if(!target)
- input = "FIXATION" //skips through the tgui alert if target isn't set
- else
- input = tgui_alert(user, "THE MIRROR IS FIXATED ON [uppertext(target.real_name)]. WILL YOU REVEAL YOUR GAZE?", "THE PRICE IS PAID", list("STALK BLOOD", "FIXATION"))
- if(!input || QDELETED(user) || QDELETED(src))
- return
- if(input == "FIXATION")
- var/name = html_decode(browser_input_text(user, "WHO DO YOU SEEK?", "THE PRICE IS PAID"))
- if(!name)
- return
- for(var/mob/living/carbon/human/HL as anything in GLOB.player_list)
- if(LOWER_TEXT(HL.real_name) == LOWER_TEXT(name))
- fixation = WEAKREF(HL)
- target = HL
- playsound(src, 'sound/items/blackmirror_no.ogg', 100, FALSE)
- to_chat(user, span_warning("[src] makes a grating sound."))
- return
- to_chat(user, span_warning("The mirror makes no sound... It could not locate a person of such name."))
- return
- active = TRUE
- openstate = "active"
- update_appearance(UPDATE_ICON_STATE)
- soundloop.start()
-
- effect = target.throw_alert("blackmirror", /atom/movable/screen/alert/blackmirror, override = TRUE)
- effect.source = src
-
- target.playsound_local(target, 'sound/items/blackeye_warn.ogg', 100, FALSE)
-
- playsound(src, 'sound/items/blackmirror_active.ogg', 100, FALSE)
- addtimer(CALLBACK(src, PROC_REF(donefixating)), 2 MINUTES, TIMER_UNIQUE)
-
- message_admins("SCRYING: [user.real_name] ([user.ckey]) has fixated on [target.real_name] ([target.ckey]) via black mirror.")
- log_game("SCRYING: [user.real_name] ([user.ckey]) has fixated on [target.real_name] ([target.ckey]) via black mirror.")
- return
-
- var/datum/weakref/lookat = fixation ? fixation : feeder
- var/mob/living/target = lookat?.resolve()
- if(!target)
- to_chat(user, span_notice("The mirror remains clear..."))
- return
-
- playsound(src, 'sound/items/blackmirror_use.ogg', 100, FALSE)
-
- if(target.real_name == user.real_name) //prevents bugging the timer through looking at yourself
- to_chat(user, span_danger("I see my reflection in the mirror... It is quite distorted, but what am I trying to achieve?"))
- return
-
- ADD_TRAIT(user, TRAIT_NOSSDINDICATOR, "blackmirror")
-
- var/mob/dead/observer/screye/blackmirror/S = user.scry_ghost()
- if(!S)
- return
- S.ManualFollow(target)
- S.add_client_colour(/datum/client_colour/nocshaded)
- user.visible_message(span_warning("[user] stares into [src], their eyes glazing over..."))
-
- addtimer(CALLBACK(S, TYPE_PROC_REF(/mob/dead/observer, reenter_corpse)), 4 SECONDS)
- addtimer(CALLBACK(user, GLOBAL_PROC_REF(playsound), user, 'sound/items/blackeye.ogg', 100, FALSE), 4 SECONDS)
- addtimer(TRAIT_CALLBACK_REMOVE(user, TRAIT_NOSSDINDICATOR, "blackmirror"), 4 SECONDS)
+ //add_client_colour(/datum/client_colour/nocshaded)
+ var/datum/component/scrying/mirror/scry_comp = GetComponent(/datum/component/scrying/mirror)
+ scry_comp.activate(user)
/obj/item/inqarticles/bmirror/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
if(!isliving(interacting_with))
@@ -1338,7 +1263,7 @@
to_chat(user, span_warning("I need to open it first."))
return ITEM_INTERACT_BLOCKING
- if(feeder)
+ if(fedblood)
to_chat(user, span_warning("It's already been fed."))
return ITEM_INTERACT_BLOCKING
@@ -1365,7 +1290,6 @@
attacked.flash_fullscreen("redflash3")
attacked.adjustBruteLoss(40, damage_type = BCLASS_PIERCE, can_crit = FALSE)
attacked.adjust_bloodpool(-240)
- feeder = WEAKREF(attacked)
openstate = "bloody"
fedblood = TRUE
update_appearance(UPDATE_ICON_STATE)
@@ -1391,31 +1315,17 @@
/obj/item/inqarticles/bmirror/attack_hand_secondary(mob/user, list/modifiers)
. = ..()
openorshut(user)
+
/obj/item/inqarticles/bmirror/proc/openorshut(mob/user)
if(active)
to_chat(user, span_warning("I cannot close the mirror while it's active."))
return
- var/mob/living/fixated = fixation?.resolve()
+ opened = !opened
if(opened)
- if(fixated)
- fixated.clear_alert("blackmirror", TRUE)
- fixated.playsound_local(src, 'sound/items/blackeye.ogg', 40, FALSE)
- else if(effect)
- QDEL_NULL(effect)
+ playsound(src, 'sound/items/blackmirror_open.ogg', 100, FALSE)
+ else
playsound(src, 'sound/items/blackmirror_shut.ogg', 100, FALSE)
- opened = FALSE
- update_appearance(UPDATE_ICON_STATE)
- return
-
- playsound(src, 'sound/items/blackmirror_open.ogg', 100, FALSE)
-
- if(fixated)
- fixated.playsound_local(src, 'sound/items/blackeye_warn.ogg', 100, FALSE)
- effect = fixated.throw_alert("blackmirror", /atom/movable/screen/alert/blackmirror, override = TRUE)
- effect.source = src
-
- opened = TRUE
update_appearance(UPDATE_ICON_STATE)
/obj/item/inqarticles/bmirror/update_icon_state()
@@ -1431,35 +1341,39 @@
desc = "LOOK AT ME. I SEE YOU."
icon_state = "blackeye"
var/obj/item/inqarticles/bmirror/source
-
-/atom/movable/screen/alert/blackmirror/Destroy()
- source = null
- return ..()
+ var/mob/scry_eye/looking_eye
/atom/movable/screen/alert/blackmirror/Click()
- var/mob/living/L = usr
- if(!istype(L))
- return
- var/mob/living/target = null
- var/input = tgui_alert(L, "YOU FEEL UNFAMILIAR GAZE. WILL YOU STARE BACK AT ABYSS?", "PRESENCE WATCHING OVER", list("TRACE BLOOD", "LOOK BACK"))
- if(input == "TRACE BLOOD")
- target = source.feeder?.resolve()
- else if(input == "LOOK BACK")
- target = source
- playsound(L, 'sound/items/blackmirror_use.ogg', 100, FALSE)
- ADD_TRAIT(L, TRAIT_NOSSDINDICATOR, "blackmirror")
- if(!target)
+ var/mob/living/user = usr
+ if(!istype(user))
return
- var/mob/dead/observer/screye/blackmirror/S = L.scry_ghost()
- if(!S)
+ var/input = tgui_alert(user, "YOU FEEL AN UNFAMILIAR GAZE. WILL YOU STARE BACK AT THE ABYSS?", "PRESENCE WATCHING OVER", list("NO", "LOOK BACK"))
+ if(input != "LOOK BACK")
return
- S.ManualFollow(target)
- S.add_client_colour(/datum/client_colour/nocshaded)
- L.visible_message(span_warning("[L] looks inward as their eyes glaze over..."))
- addtimer(CALLBACK(S, TYPE_PROC_REF(/mob/dead/observer, reenter_corpse)), 4 SECONDS)
- addtimer(CALLBACK(L, GLOBAL_PROC_REF(playsound), L, 'sound/items/blackeye.ogg', 100, FALSE), 4 SECONDS)
- addtimer(TRAIT_CALLBACK_REMOVE(L, TRAIT_NOSSDINDICATOR, "blackmirror"), 4 SECONDS)
+ looking_eye = new
+ looking_eye.user_mob = user
+ looking_eye.orbit(source)
+ user.reset_perspective(looking_eye)
+ user.Immobilize(4 SECONDS)
+ user.overlay_fullscreen("scrying", /atom/movable/screen/backhudl/obscured)
+ playsound(user, 'sound/items/blackmirror_use.ogg', 100, FALSE)
+ addtimer(CALLBACK(src, PROC_REF(remove_eye)), 4 SECONDS)
+
+ user.visible_message(span_warning("[user] looks inward as their eyes glaze over..."))
+
+/atom/movable/screen/alert/blackmirror/Destroy()
+ remove_eye()
+ source = null
+ return ..()
+
+/atom/movable/screen/alert/blackmirror/proc/remove_eye()
+ if(mob_viewer)
+ mob_viewer.reset_perspective(mob_viewer)
+ mob_viewer.clear_fullscreen("scrying")
+ playsound(mob_viewer, 'sound/items/blackeye.ogg', 100, FALSE)
+ QDEL_NULL(looking_eye)
+ mob_viewer = null
// FINISH THIS AT YOUR LEISURE. I'M JUST LEAVING IT HERE UNIMPLEMENTED. IT'S INTENDED TO WORK AS A COMBINATION OF THE NOC FAR-SIGHT AND THE NOCSHADES. HAVE FUN! - YISCHE
/obj/item/inqarticles/spyglass
diff --git a/code/game/objects/items/magic.dm b/code/game/objects/items/magic.dm
deleted file mode 100644
index e06218234cf..00000000000
--- a/code/game/objects/items/magic.dm
+++ /dev/null
@@ -1,147 +0,0 @@
-/////////////////////////////////////////Scrying///////////////////
-
-/obj/item/scrying
- name = "scrying orb"
- desc = "On its glass depths, you can scry on many unsuspecting beings..."
- icon = 'icons/roguetown/items/misc.dmi'
- icon_state ="scrying"
- throw_speed = 3
- throw_range = 7
- throwforce = 15
- damtype = BURN
- force = 15
- hitsound = 'sound/blank.ogg'
- sellprice = 30
- dropshrink = 0.6
-
- grid_height = 32
- grid_width = 32
- item_weight = 400 GRAMS
-
- var/mob/current_owner
- var/last_scry
- var/cooldown = 30 SECONDS
-
-/obj/item/scrying/eye
- name = "accursed eye"
- desc = "It is pulsating."
- icon = 'icons/roguetown/items/misc.dmi'
- icon_state ="scryeye"
- cooldown = 5 MINUTES
- item_weight = 200 GRAMS
-
-/obj/item/scrying/attack_self(mob/user, list/modifiers)
- . = ..()
- if(world.time < last_scry + cooldown)
- to_chat(user, span_warning("I look into [src] but only see inky smoke. Maybe I should wait."))
- return
- var/input = SANITIZE_HEAR_MESSAGE(html_decode(tgui_input_text(user, "Who are you looking for?", "Scrying Orb")))
-
- if(!input)
- return
- if(!user.key)
- return
- if(!user.mind || !user.mind.do_i_know(name=input))
- to_chat(user, span_warning("I don't know anyone by that name."))
- return
- //check is applied twice to prevent someone from bypassing the cooldown
- if(world.time < last_scry + cooldown)
- to_chat(user, span_warning("I look into [src] but only see inky smoke. Maybe I should wait."))
- return
- for(var/mob/living/carbon/human/HL in GLOB.human_list)
- if(HL.real_name == input)
- var/turf/T = get_turf(HL)
- if(!T)
- continue
- if(HAS_TRAIT(HL, TRAIT_ANTISCRYING))
- to_chat(user, span_warning("I peer into [src], but an impenetrable fog shrouds [HL.real_name]."))
- to_chat(HL, span_warning("My magical shrouding reacted to something."))
- return
- log_game("SCRYING: [user.real_name] ([user.ckey]) has used the scrying orb to leer at [HL.real_name] ([HL.ckey])")
- ADD_TRAIT(user, TRAIT_NOSSDINDICATOR, "scryingorb")
- var/mob/dead/observer/screye/S = user.scry_ghost()
- if(!S)
- return
- S.ManualFollow(HL)
- last_scry = world.time
- user.visible_message(span_danger("[user] stares into [src], [p_their()] eyes rolling back into [p_their()] head."))
- addtimer(CALLBACK(S, TYPE_PROC_REF(/mob/dead/observer, reenter_corpse)), 8 SECONDS)
- if(!HL.stat)
- if(GET_MOB_ATTRIBUTE_VALUE(HL, STAT_PERCEPTION) >= 15)
- if(HL.mind)
- if(HL.mind.do_i_know(name=user.real_name))
- to_chat(HL, span_warning("I can clearly see the face of [user.real_name] staring at me!"))
- to_chat(user, span_warning("[HL.real_name] stares back at me!"))
- return
- to_chat(HL, span_warning("I can clearly see the face of an unknown [user.gender == FEMALE ? "woman" : "man"] staring at me!"))
- return
- if(GET_MOB_ATTRIBUTE_VALUE(HL, STAT_PERCEPTION) >= 11)
- to_chat(HL, span_warning("I feel a pair of unknown eyes on me."))
- REMOVE_TRAIT(user, TRAIT_NOSSDINDICATOR, "scryingorb")
- return
- to_chat(user, span_warning("I peer into [src], but can't find [input]."))
- return
-
-//23.08.2025
-//crystallball and nocdevice are depreciated?
-
-/////////////////////////////////////////Crystal ball ghsot vision///////////////////
-
-/obj/item/crystalball/attack_self(mob/user, list/modifiers)
- user.visible_message("[user] stares into [src], their eyes rolling back into their head.")
- user.ghostize(1)
-
-/* .................. NOC Device (Fixed scrying ball) ................... */
-/obj/structure/nocdevice
- name = "NOC Device"
- desc = "An intricate lunar observation machine, that allows its user to study the face of Noc in the sky, reflecting the true whereabouts of hidden beings..."
- icon = 'icons/roguetown/misc/96x96.dmi'
- icon_state = "nocdevice"
- layer = 4.2
- var/last_scry
-
-/obj/structure/nocdevice/attack_hand(mob/user)
- . = ..()
- var/mob/living/carbon/human/H = user
- if(HAS_TRAIT(H, TRAIT_VIRGIN))
- if(world.time < last_scry + 30 SECONDS)
- to_chat(user, "I peer into the sky but cannot focus the lens on the face of Noc. Maybe I should wait.")
- return
- var/input = stripped_input(user, "Who are you looking for?", "Scrying Orb")
- if(!input)
- return
- if(!user.key)
- return
- if(world.time < last_scry + 30 SECONDS)
- to_chat(user, "I peer into the sky but cannot focus the lens on the face of Noc. Maybe I should wait.")
- return
- if(!user.mind || !user.mind.do_i_know(name=input))
- to_chat(user, "I don't know anyone by that name.")
- return
- for(var/mob/living/carbon/human/HL in GLOB.human_list)
- if(HL.real_name == input)
- var/turf/T = get_turf(HL)
- if(!T)
- continue
- var/mob/dead/observer/screye/S = user.scry_ghost()
- if(!S)
- return
- S.ManualFollow(HL)
- last_scry = world.time
- user.visible_message("[user] stares into [src], [p_their()] squinting and concentrating...")
- addtimer(CALLBACK(S, TYPE_PROC_REF(/mob/dead/observer, reenter_corpse)), 8 SECONDS)
- if(!HL.stat)
- if(GET_MOB_ATTRIBUTE_VALUE(HL, STAT_PERCEPTION) >= 15)
- if(HL.mind)
- if(HL.mind.do_i_know(name=user.real_name))
- to_chat(HL, "I can clearly see the face of [user.real_name] staring at me!.")
- return
- to_chat(HL, "I can clearly see the face of an unknown [user.gender == FEMALE ? "woman" : "man"] staring at me!")
- return
- if(GET_MOB_ATTRIBUTE_VALUE(HL, STAT_PERCEPTION) >= 11)
- to_chat(HL, "I feel a pair of unknown eyes on me.")
- return
- to_chat(user, "I peer into the viewpiece, but Noc does not reveal where [input] is.")
- return
- else
- to_chat(user, "Noc looks angry with me...")
diff --git a/code/game/objects/items/scrying.dm b/code/game/objects/items/scrying.dm
new file mode 100644
index 00000000000..4fc04aed943
--- /dev/null
+++ b/code/game/objects/items/scrying.dm
@@ -0,0 +1,180 @@
+/obj/item/scrying
+ name = "scrying orb"
+ desc = "On its glass depths, you can scry on many unsuspecting beings..."
+ icon = 'icons/roguetown/items/misc.dmi'
+ icon_state ="scrying"
+ throw_speed = 3
+ throw_range = 7
+ throwforce = 15
+ damtype = BURN
+ force = 15
+ hitsound = 'sound/blank.ogg'
+ sellprice = 30
+ dropshrink = 0.6
+
+ grid_height = 32
+ grid_width = 32
+
+ var/scry_comp_path = /datum/component/scrying/orb
+
+/obj/item/scrying/Initialize(mapload)
+ . = ..()
+ AddComponent(scry_comp_path)
+
+/obj/item/scrying/eye
+ name = "accursed eye"
+ desc = "It is pulsating."
+ icon = 'icons/roguetown/items/misc.dmi'
+ icon_state ="scryeye"
+
+ scry_comp_path = /datum/component/scrying/eye
+
+
+/* .................. NOC Device (Fixed scrying ball) ................... */
+/obj/structure/nocdevice
+ name = "NOC Device"
+ desc = "An intricate lunar observation machine, that allows its user to study the face of Noc in the sky, reflecting the true whereabouts of hidden beings..."
+ icon = 'icons/roguetown/misc/96x96.dmi'
+ icon_state = "nocdevice"
+ layer = 4.2
+
+/obj/structure/nocdevice/Initialize()
+ . = ..()
+ AddComponent(/datum/component/scrying/telescope)
+
+/* .................. THE EYE ................... */
+/mob/scry_eye
+ sight = SEE_TURFS | SEE_MOBS | SEE_OBJS
+ see_in_dark = 100
+ hud_type = /datum/hud/obscured
+ invisibility = INVISIBILITY_GHOST
+ see_invisible = SEE_INVISIBLE_LIVING
+ var/mob/living/user_mob
+ var/moving_eye = FALSE
+
+/mob/scry_eye/blackmirror
+
+/mob/scry_eye/Move(n, direct)
+ if(!moving_eye)
+ return
+ ..()
+
+/mob/scry_eye/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, list/message_mods = list(), original_message)
+ if(!user_mob)
+ qdel(src)
+ return
+ user_mob.Hear(message, speaker, message_language, raw_message, radio_freq, spans, message_mods, original_message)
+ return
+
+
+
+
+
+
+/* VAMPIRE EYE */
+/mob/scry_eye/eye_of_night
+ sight = 0
+ see_in_dark = 2
+ invisibility = INVISIBILITY_GHOST
+ see_invisible = SEE_INVISIBLE_GHOST
+
+ var/mob/living/carbon/human/vampirelord = null
+ icon_state = "arcaneeye"
+ hud_type = /datum/hud/eye
+ moving_eye = TRUE
+
+/mob/scry_eye/eye_of_night/proc/scry_tele()
+ set category = "RoleUnique.Arcane Eye"
+ set name = "Teleport"
+ set desc= "Teleport to a location"
+ set hidden = 0
+
+ if(!isscryeye(usr))
+ to_chat(usr, span_warning("You're not an Eye!"))
+ return
+ var/list/filtered = list()
+ for(var/area/A as anything in get_sorted_areas())
+ if(A.area_flags & (HIDDEN_AREA|NO_TELEPORT))
+ continue
+ filtered += A
+ var/area/thearea = input("Area to jump to", "VANDERLIN") as null|anything in filtered
+
+ if(!thearea)
+ return
+
+ var/list/L = list()
+ for(var/turf/T in get_area_turfs(thearea.type))
+ L+=T
+
+ if(!L || !L.len)
+ to_chat(usr, span_warning("No area available."))
+ return
+
+ usr.forceMove(pick(L))
+
+/mob/scry_eye/eye_of_night/Initialize()
+ . = ..()
+ var/list/verbs = list(
+ /mob/scry_eye/eye_of_night/proc/scry_tele,
+ /mob/scry_eye/eye_of_night/proc/cancel_scry,
+ /mob/scry_eye/eye_of_night/proc/eye_down,
+ /mob/scry_eye/eye_of_night/proc/eye_up,
+ /mob/scry_eye/eye_of_night/proc/vampire_telepathy
+ )
+ add_verb(src, verbs)
+ name = "Arcane Eye"
+ grant_all_languages()
+
+/mob/scry_eye/eye_of_night/proc/cancel_scry()
+ set category = "RoleUnique.Arcane Eye"
+ set name = "Cancel Eye"
+ set desc= "Return to Body"
+
+ if(vampirelord)
+ vampirelord.ckey = ckey
+ qdel(src)
+ else
+ to_chat(src, "My body has been destroyed! I'm trapped!")
+
+/mob/scry_eye/eye_of_night/Crossed(mob/living/L)
+ if(istype(L, /mob/living/carbon/human))
+ var/mob/living/carbon/human/cross_human = L
+ var/holyskill = GET_MOB_SKILL_VALUE_OLD(cross_human, /datum/attribute/skill/magic/holy)
+ var/magicskill = GET_MOB_SKILL_VALUE_OLD(cross_human, /datum/attribute/skill/magic/arcane)
+ if(magicskill >= 2)
+ to_chat(cross_human, "An ancient and unusual magic looms in the air around you.")
+ return
+ if(holyskill >= 2)
+ to_chat(cross_human, "An ancient and unholy magic looms in the air around you.")
+ return
+ if(prob(20))
+ to_chat(cross_human, "You feel like someone is watching you, or something.")
+ return
+
+/mob/scry_eye/eye_of_night/proc/vampire_telepathy()
+ set name = "Telepathy"
+ set category = "RoleUnique.Arcane Eye"
+
+ var/msg = input("Send a message.", "Command") as text|null
+ if(!msg)
+ return
+ for(var/datum/mind/V in SSmapping.retainer.vampires)
+ to_chat(V, span_boldnotice("A message from [src.real_name]:[msg]"))
+ for(var/datum/mind/D in SSmapping.retainer.death_knights)
+ to_chat(D, span_boldnotice("A message from [src.real_name]:[msg]"))
+ for(var/mob/scry_eye/eye_of_night/A in GLOB.mob_list)
+ to_chat(A, span_boldnotice("A message from [src.real_name]:[msg]"))
+
+/mob/scry_eye/eye_of_night/proc/eye_up()
+ set category = "RoleUnique.Arcane Eye"
+ set name = "Move Up"
+
+ if(zMove(UP, TRUE))
+ to_chat(src, span_notice("I move upwards."))
+
+/mob/scry_eye/eye_of_night/proc/eye_down()
+ set category = "RoleUnique.Arcane Eye"
+ set name = "Move Down"
+
+ if(zMove(DOWN, TRUE))
+ to_chat(src, span_notice("I move down."))
diff --git a/code/modules/antagonists/villain/vampire/objects/scrying.dm b/code/modules/antagonists/villain/vampire/objects/scrying.dm
index 1bc9235c8df..c8958319600 100644
--- a/code/modules/antagonists/villain/vampire/objects/scrying.dm
+++ b/code/modules/antagonists/villain/vampire/objects/scrying.dm
@@ -2,136 +2,18 @@
name = "Eye of Night"
icon_state = "scrying"
-/obj/structure/vampire/scryingorb/attack_hand(mob/living/carbon/human/user)
- if(user?.mind.has_antag_datum(/datum/antagonist/vampire/lord))
- user.visible_message("[user]'s eyes turn dark red, as they channel the [src]", "I begin to channel my consciousness into a Predator's Eye.")
- if(do_after(user, 6 SECONDS, src))
- user.scry(can_reenter_corpse = 1, force_respawn = FALSE)
- else
- to_chat(user, span_warning("I don't have the power to use this!"))
-
-/mob/dead/observer/rogue/arcaneeye
- name = "Arcane Eye"
- icon_state = "arcaneeye"
- sight = 0
- see_in_dark = 2
- invisibility = INVISIBILITY_GHOST
- see_invisible = SEE_INVISIBLE_GHOST
-
- draw_icon = FALSE
- hud_type = /datum/hud/eye
-
- var/mob/living/carbon/human/vampirelord = null
-
-/mob/dead/observer/rogue/arcaneeye/Initialize(mapload)
+/obj/structure/vampire/scryingorb/Initialize()
. = ..()
- set_invisibility(GLOB.observer_default_invisibility)
- add_movespeed_modifier(MOVESPEED_ID_GHOST, override = TRUE, multiplicative_slowdown = 3)
- var/list/verbs = list(
- /mob/dead/observer/rogue/arcaneeye/proc/scry_tele,
- /mob/dead/observer/rogue/arcaneeye/proc/cancel_scry,
- /mob/dead/observer/rogue/arcaneeye/proc/eye_down,
- /mob/dead/observer/rogue/arcaneeye/proc/eye_up,
- /mob/dead/observer/rogue/arcaneeye/proc/vampire_telepathy
- )
- add_verb(src, verbs)
-
-/mob/dead/observer/rogue/arcaneeye/Destroy()
- vampirelord = null
- return ..()
-
-/mob/dead/observer/rogue/arcaneeye/Crossed(mob/living/L)
- if(istype(L, /mob/living/carbon/human))
- var/mob/living/carbon/human/V = L
- var/holyskill = GET_MOB_SKILL_VALUE_OLD(V, /datum/attribute/skill/magic/holy)
- var/magicskill = GET_MOB_SKILL_VALUE_OLD(V, /datum/attribute/skill/magic/arcane)
- if(magicskill >= 2)
- to_chat(V, "An ancient and unusual magic looms in the air around you.")
- return
- if(holyskill >= 2)
- to_chat(V, "An ancient and unholy magic looms in the air around you.")
- return
- if(prob(20))
- to_chat(V, "You feel like someone is watching you, or something.")
- return
-
-/mob/dead/observer/rogue/arcaneeye/proc/cancel_scry()
- set category = "RoleUnique.Arcane Eye"
- set name = "Cancel Eye"
- set desc= "Return to Body"
-
- if(vampirelord)
- vampirelord.ckey = ckey
- qdel(src)
- else
- to_chat(src, "My body has been destroyed! I'm trapped!")
-
-/mob/dead/observer/rogue/arcaneeye/proc/vampire_telepathy()
- set name = "Telepathy"
- set category = "RoleUnique.Arcane Eye"
-
- var/msg = input("Send a message.", "Command") as text|null
- if(!msg)
- return
- for(var/datum/mind/V in SSmapping.retainer.vampires)
- to_chat(V, span_boldnotice("A message from [src.real_name]:[msg]"))
- for(var/datum/mind/D in SSmapping.retainer.death_knights)
- to_chat(D, span_boldnotice("A message from [src.real_name]:[msg]"))
- for(var/mob/dead/observer/rogue/arcaneeye/A in GLOB.mob_list)
- to_chat(A, span_boldnotice("A message from [src.real_name]:[msg]"))
-
-/mob/dead/observer/rogue/arcaneeye/proc/eye_up()
- set category = "RoleUnique.Arcane Eye"
- set name = "Move Up"
-
- if(zMove(UP, z_move_flags = ZMOVE_FEEDBACK))
- to_chat(src, span_notice("I move upwards."))
-
-/mob/dead/observer/rogue/arcaneeye/proc/eye_down()
- set category = "RoleUnique.Arcane Eye"
- set name = "Move Down"
-
- if(zMove(DOWN, z_move_flags = ZMOVE_FEEDBACK))
- to_chat(src, span_notice("I move downwards."))
-
-/mob/dead/observer/rogue/arcaneeye/proc/scry_tele()
- set category = "RoleUnique.Arcane Eye"
- set name = "Teleport"
- set desc= "Teleport to a location"
-
- if(!isobserver(src))
- to_chat(src, span_warning("You're not an Eye!"))
- return
-
- var/list/filtered = list()
- for(var/area/A as anything in get_sorted_areas())
- if(A.area_flags & (HIDDEN_AREA|NO_TELEPORT))
- continue
- filtered += A
-
- var/area/thearea = browser_input_list(src, "Area to jump to", "VANDERLIN", filtered)
-
- if(!thearea)
- return
-
- var/list/L = list()
- for(var/turf/T as anything in get_area_turfs(thearea.type))
- L += T
-
- if(!length(L))
- to_chat(src, span_warning("No area available."))
- return
-
- forceMove(pick(L))
+ AddComponent(/datum/component/scrying/vampire) //Temporary alternative
-/mob/proc/scry(can_reenter_corpse = 1, force_respawn = FALSE, drawskip)
+/*
+/mob/proc/enter_night_eye()
stop_sound_channel(CHANNEL_HEARTBEAT) //Stop heartbeat sounds because You Are A Ghost Now
- var/mob/dead/observer/rogue/arcaneeye/eye = new(src) // Transfer safety to observer spawning proc.
+ var/mob/scry_eye/eye_of_night/eye = new(src) // Transfer safety to observer spawning proc.
SStgui.on_transfer(src, eye) // Transfer NanoUIs.
- eye.can_reenter_corpse = can_reenter_corpse
eye.vampirelord = src
- eye.ghostize_time = world.time
eye.key = key
qdel(eye.language_holder)
eye.language_holder = language_holder.copy(eye)
return eye
+*/
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index 6f4cf3b1eb6..fb0dca4bd2d 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -96,21 +96,6 @@ GLOBAL_LIST_INIT(ghost_verbs, list(
draw_icon = FALSE
alpha = 100
-/mob/dead/observer/screye
- sight = 0
- see_in_dark = 0
- hud_type = /datum/hud/obscured
- can_reenter_corpse = FALSE
- invisibility = INVISIBILITY_GHOST
- see_invisible = SEE_INVISIBLE_GHOST
-
-/mob/dead/observer/screye/blackmirror
- sight = SEE_TURFS | SEE_MOBS | SEE_OBJS
- see_in_dark = 100
-
-/mob/dead/observer/screye/Move(n, direct)
- return
-
/mob/dead/observer/profane // Ghost type for souls trapped by the profane dagger. They can't move, but can talk to the dagger's wielder and other trapped souls.
sight = 0
invisibility = INVISIBILITY_GHOST
@@ -192,10 +177,8 @@ GLOBAL_LIST_INIT(ghost_verbs, list(
. = ..()
- if(!istype(src, /mob/dead/observer/rogue/arcaneeye))
- if(!istype(src, /mob/dead/observer/screye))
- add_verb(src, GLOB.ghost_verbs)
- to_chat(src, span_danger("Click the SKULL on the left of your HUD to respawn."))
+ add_verb(src, GLOB.ghost_verbs)
+ to_chat(src, span_danger("Click the SKULL on the left of your HUD to respawn."))
if(grant_all_languages)
grant_all_languages()
@@ -293,24 +276,7 @@ Works together with spawning an observer, noted above.
SEND_SIGNAL(src, COMSIG_MOB_GHOSTIZED)
return ghost
-/mob/proc/scry_ghost()
- if(key)
- stop_sound_channel(CHANNEL_HEARTBEAT) //Stop heartbeat sounds because You Are A Ghost Now
- var/mob/dead/observer/screye/ghost = new(src) // Transfer safety to observer spawning proc.
- ghost.ghostize_time = world.time
- SStgui.on_transfer(src, ghost) // Transfer NanoUIs.
- ghost.can_reenter_corpse = TRUE
- ghost.key = key
- RegisterSignal(ghost, COMSIG_MOB_LOGOUT, PROC_REF(break_scry))
- return ghost
-
-/mob/proc/break_scry()
- return
-/mob/dead/observer/break_scry()
- client.view_size.setDefault(client.view_size.getScreenSize())
- mind.current_ghost = null
- mind.current.ckey = ckey(key)
/*
This is the proc mobs get to turn into a ghost. Forked from ghostize due to compatibility issues.
@@ -787,8 +753,6 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
O.updateghostimages()
/mob/dead/observer/proc/horde_respawn()
- if(istype(src, /mob/dead/observer/rogue/arcaneeye))
- return
var/bt = world.time
SEND_SOUND(src, sound('sound/misc/notice (2).ogg'))
if(tgui_alert(src, "You have been summoned to destroy Vanderlin!", "Join the Horde", list("Yes", "No")) == "Yes")
diff --git a/code/modules/mob/dead/observer/observer_say.dm b/code/modules/mob/dead/observer/observer_say.dm
index a672cf7a15c..f9e2df93d9b 100644
--- a/code/modules/mob/dead/observer/observer_say.dm
+++ b/code/modules/mob/dead/observer/observer_say.dm
@@ -29,18 +29,6 @@
. = say_dead(message)
-/mob/dead/observer/rogue/arcaneeye/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
- return
-
-/mob/dead/observer/rogue/arcaneeye/say_dead(message)
- return
-
-/mob/dead/observer/screye/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
- return
-
-/mob/dead/observer/screye/say_dead(message)
- return
-
/mob/dead/observer/profane/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
if (!message)
return
diff --git a/vanderlin.dme b/vanderlin.dme
index 7e22f5f940b..7b5fa06b23c 100644
--- a/vanderlin.dme
+++ b/vanderlin.dme
@@ -1148,6 +1148,7 @@
#include "code\datums\components\rope.dm"
#include "code\datums\components\rotation.dm"
#include "code\datums\components\rotting.dm"
+#include "code\datums\components\scrying.dm"
#include "code\datums\components\shrapnel.dm"
#include "code\datums\components\slippery.dm"
#include "code\datums\components\slowing_field.dm"
@@ -2064,7 +2065,6 @@
#include "code\game\objects\items\literary.dm"
#include "code\game\objects\items\locks.dm"
#include "code\game\objects\items\mageitems.dm"
-#include "code\game\objects\items\magic.dm"
#include "code\game\objects\items\natural.dm"
#include "code\game\objects\items\needle.dm"
#include "code\game\objects\items\ore.dm"
@@ -2078,6 +2078,7 @@
#include "code\game\objects\items\quicksilver.dm"
#include "code\game\objects\items\ropechainbola.dm"
#include "code\game\objects\items\scrolls.dm"
+#include "code\game\objects\items\scrying.dm"
#include "code\game\objects\items\servant_bell.dm"
#include "code\game\objects\items\signal_horn.dm"
#include "code\game\objects\items\soap.dm"