diff --git a/_maps/map_files/austranderlin/austranderlin.dmm b/_maps/map_files/austranderlin/austranderlin.dmm index ff9ff8aac78..cede7785cc6 100644 --- a/_maps/map_files/austranderlin/austranderlin.dmm +++ b/_maps/map_files/austranderlin/austranderlin.dmm @@ -48816,7 +48816,7 @@ /turf/open/floor/carpet/green, /area/indoors/town/merc_guild) "vNk" = ( -/obj/structure/nocdevice, +/obj/structure/scrying/nocdevice, /obj/structure/carpet/blue, /turf/open/floor/cobblerock, /area/indoors/town/keep/magician) diff --git a/_maps/map_files/rosewood/rosewood.dmm b/_maps/map_files/rosewood/rosewood.dmm index d0115059075..2940b61a62c 100644 --- a/_maps/map_files/rosewood/rosewood.dmm +++ b/_maps/map_files/rosewood/rosewood.dmm @@ -29922,7 +29922,7 @@ /obj/structure/fluff/railing/border{ dir = 6 }, -/obj/structure/nocdevice{ +/obj/structure/scrying/nocdevice{ pixel_x = -5; pixel_y = 6 }, @@ -46110,7 +46110,7 @@ /turf/open/floor/snow/patchy, /area/outdoors/town) "wdz" = ( -/obj/structure/nocdevice{ +/obj/structure/scrying/nocdevice{ pixel_x = -3 }, /turf/open/floor/wood, diff --git a/_maps/map_files/rosewood/rosewood_marsh.dmm b/_maps/map_files/rosewood/rosewood_marsh.dmm index 943d3f2da33..ec02881c0bf 100644 --- a/_maps/map_files/rosewood/rosewood_marsh.dmm +++ b/_maps/map_files/rosewood/rosewood_marsh.dmm @@ -2965,7 +2965,7 @@ /obj/structure/table/wood/reinf_long{ dir = 4 }, -/obj/item/scrying{ +/obj/item/scrying/orb{ pixel_x = 8; pixel_y = 1 }, diff --git a/_maps/map_files/rosewood/rosewood_mountain.dmm b/_maps/map_files/rosewood/rosewood_mountain.dmm index 45e74a38f62..4959e12d6d8 100644 --- a/_maps/map_files/rosewood/rosewood_mountain.dmm +++ b/_maps/map_files/rosewood/rosewood_mountain.dmm @@ -5447,7 +5447,7 @@ /area/under/mountains/anvil/upper) "Rn" = ( /obj/structure/table/vtable/v2, -/obj/item/scrying, +/obj/item/scrying/orb, /turf/open/floor/carpet/royalblack, /area/under/mountains/anvil/dungeon) "Rp" = ( diff --git a/_maps/map_files/shared/CentCom.dmm b/_maps/map_files/shared/CentCom.dmm index 8f5ba3a5a56..87b5189ef78 100644 --- a/_maps/map_files/shared/CentCom.dmm +++ b/_maps/map_files/shared/CentCom.dmm @@ -278,7 +278,7 @@ name = "Staff of the Wizend Wizard" }, /obj/structure/table/wood/fancy/royalblue, -/obj/item/scrying, +/obj/item/scrying/orb, /turf/open/floor/carpet, /area/indoors) "aW" = ( diff --git a/_maps/map_files/vanderlin/vanderlin.dmm b/_maps/map_files/vanderlin/vanderlin.dmm index 333b06e40fa..d1d0e58320f 100644 --- a/_maps/map_files/vanderlin/vanderlin.dmm +++ b/_maps/map_files/vanderlin/vanderlin.dmm @@ -53106,7 +53106,7 @@ /turf/open/floor/dirt, /area/indoors/town/church) "vNk" = ( -/obj/structure/nocdevice, +/obj/structure/scrying/nocdevice, /obj/structure/carpet/blue, /turf/open/floor/cobblerock, /area/indoors/town/keep/magician) diff --git a/_maps/map_files/vanderlin/vanderlin_bog.dmm b/_maps/map_files/vanderlin/vanderlin_bog.dmm index f6961d6bd5b..d3b7b7ee4e4 100644 --- a/_maps/map_files/vanderlin/vanderlin_bog.dmm +++ b/_maps/map_files/vanderlin/vanderlin_bog.dmm @@ -11014,7 +11014,7 @@ /area/outdoors/bog/south/west) "jvo" = ( /obj/structure/table/wood/fancy/black, -/obj/item/scrying{ +/obj/item/scrying/orb{ pixel_x = -4; pixel_y = 10 }, diff --git a/_maps/map_files/vanderlin/vanderlin_mountain.dmm b/_maps/map_files/vanderlin/vanderlin_mountain.dmm index b3c6bf67610..afe7cf62958 100644 --- a/_maps/map_files/vanderlin/vanderlin_mountain.dmm +++ b/_maps/map_files/vanderlin/vanderlin_mountain.dmm @@ -13724,7 +13724,7 @@ /turf/open/floor/cobblerock, /area/indoors/mountains/anvil/upperkeep) "rOq" = ( -/obj/structure/nocdevice{ +/obj/structure/scrying/nocdevice{ pixel_y = 11; pixel_x = -7; name = " defunct Magical Artillery device" diff --git a/_maps/map_files/voyager/voyager.dmm b/_maps/map_files/voyager/voyager.dmm index e45ef61a0c2..df21b8b504c 100644 --- a/_maps/map_files/voyager/voyager.dmm +++ b/_maps/map_files/voyager/voyager.dmm @@ -2090,7 +2090,7 @@ /area/ship/middeck) "nT" = ( /obj/structure/closet/crate/drawer, -/obj/item/scrying, +/obj/item/scrying/orb, /obj/structure/fluff/wallclock, /turf/open/floor/ruinedwood/two, /area/ship/nobledeck) diff --git a/_maps/map_files/whitepalacepass/WhitePalacePass.dmm b/_maps/map_files/whitepalacepass/WhitePalacePass.dmm index cb46cb69a83..063c3c3bd6d 100644 --- a/_maps/map_files/whitepalacepass/WhitePalacePass.dmm +++ b/_maps/map_files/whitepalacepass/WhitePalacePass.dmm @@ -19146,7 +19146,7 @@ /turf/open/floor/cobble, /area/under/town/basement) "lHr" = ( -/obj/structure/nocdevice{ +/obj/structure/scrying/nocdevice{ pixel_x = -3 }, /turf/open/floor/wood, diff --git a/_maps/matthios_tomb/room/SteamCastle.dmm b/_maps/matthios_tomb/room/SteamCastle.dmm index db97af0b554..cc7c3e6641c 100644 --- a/_maps/matthios_tomb/room/SteamCastle.dmm +++ b/_maps/matthios_tomb/room/SteamCastle.dmm @@ -700,7 +700,7 @@ /obj/structure/fluff/railing/border{ dir = 5 }, -/obj/structure/nocdevice{ +/obj/structure/scrying/nocdevice{ pixel_x = -7; pixel_y = 0 }, diff --git a/_maps/templates/malums_anvil_dungeons/embercradle.dmm b/_maps/templates/malums_anvil_dungeons/embercradle.dmm index 4ff26079460..e9b44781e39 100644 --- a/_maps/templates/malums_anvil_dungeons/embercradle.dmm +++ b/_maps/templates/malums_anvil_dungeons/embercradle.dmm @@ -1267,7 +1267,7 @@ /area/under/mountains/anvil/dungeon) "ro" = ( /obj/machinery/artificer_table, -/obj/item/scrying, +/obj/item/scrying/orb, /obj/effect/decal/cleanable/dirt/dust, /turf/open/floor/carpet/stellar, /area/under/mountains/anvil/dungeon/lower) 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 6e7562caaf1..24243a40d16 100644 --- a/code/__DEFINES/traits/definitions.dm +++ b/code/__DEFINES/traits/definitions.dm @@ -354,9 +354,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 4cf38bf2101..dcf99736496 100644 --- a/code/datums/components/orbiter.dm +++ b/code/datums/components/orbiter.dm @@ -104,8 +104,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..9ea93170a3f --- /dev/null +++ b/code/datums/components/scrying.dm @@ -0,0 +1,261 @@ +/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 + + /// The perception stat required to see the face of your scry-er. + var/perception_face = 15 + /// The perception stat to sense being scryed. + var/perception_sense = 11 + + COOLDOWN_DECLARE(scry_cooldown) + +/datum/component/scrying/Initialize(new_view_duration, new_cooldown_duration, need_knowledge, need_alive, cooldown_text_override, new_face_perception, new_sense_perception) + . = ..() + if(!isitem(parent) && !isstructure(parent)) + return COMPONENT_INCOMPATIBLE + + if(new_view_duration) + vision_duration = new_view_duration + if(new_cooldown_duration) + cooldown_duration = new_cooldown_duration + if(!isnull(need_knowledge)) + needs_to_know = need_knowledge + if(!isnull(need_alive)) + needs_to_live = need_alive + if(cooldown_text_override) + text_cooldown_fail = cooldown_text_override + if(new_face_perception) + perception_face = new_face_perception + if(new_sense_perception) + perception_sense = new_sense_perception + + var/obj/parent_obj = parent + name = parent_obj.name + text_cooldown_fail = replacetext(text_cooldown_fail, "NAME_HERE", "\the [name]") + +/datum/component/scrying/RegisterWithParent() + if(isitem(parent)) + RegisterSignal(parent, COMSIG_ITEM_ATTACK_SELF, PROC_REF(activate)) + return + if(isstructure(parent)) + RegisterSignal(parent, COMSIG_ATOM_ATTACK_HAND, PROC_REF(activate)) + return + +/datum/component/scrying/UnregisterFromParent() + if(isitem(parent)) + UnregisterSignal(parent, COMSIG_ITEM_ATTACK_SELF) + return + if(isstructure(parent)) + UnregisterSignal(parent, COMSIG_ATOM_ATTACK_HAND) + return + +/datum/component/scrying/Destroy(force) + QDEL_NULL(scrying_eye) + held_user = null + return ..() + +/datum/component/scrying/proc/activate(datum/source, mob/living/user) + var/obj/parent_obj = parent + if(!parent_obj.pass_scrying_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, timeout = 10 SECONDS) + + //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(!search_name) + 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(LOWER_TEXT(human_target.real_name) == LOWER_TEXT(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 [name].")) + scrying_eye.orbit(found_target) + var/target_perception = GET_MOB_ATTRIBUTE_VALUE(found_target, STAT_PERCEPTION) + if(target_perception >= perception_face) + 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 >= perception_sense) + 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/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(new_view_duration, new_cooldown_duration, need_knowledge, need_alive, cooldown_text_override) + . = ..() + 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) + var/obj/parent_obj = parent + if(!parent_obj.pass_scrying_checks(user)) + 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 d5009338c33..fd6763fb8ec 100644 --- a/code/game/objects/items/inquisition_relics.dm +++ b/code/game/objects/items/inquisition_relics.dm @@ -1179,24 +1179,17 @@ 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) + enchant(/datum/enchantment/anti_theft) /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 +1204,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 +1250,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 +1264,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 +1291,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 +1316,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 +1342,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..7fd5a7f5c21 --- /dev/null +++ b/code/game/objects/items/scrying.dm @@ -0,0 +1,243 @@ +/obj/proc/pass_scrying_checks(mob/user) + return TRUE + +/obj/item/scrying + name = "scrying object" + desc = "You can see more than you ought with this..." + 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 + + abstract_type = /obj/item/scrying + +/obj/item/scrying/Initialize(mapload) + . = ..() + add_scry_comp() + +/obj/item/scrying/proc/add_scry_comp() + AddComponent(/datum/component/scrying) + +/obj/item/scrying/orb + name = "arcyne scrying orb" + desc = "Within its glass depths, you can scry on many unsuspecting beings..." + +/obj/item/scrying/orb/pass_scrying_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 + +/obj/item/scrying/orb/miracle + name = "divine scrying orb" + +/obj/item/scrying/orb/miracle/pass_scrying_checks(mob/living/user) + if(GET_MOB_SKILL_VALUE_OLD(user, /datum/attribute/skill/magic/holy) < 1) + to_chat(user, span_warning("I do not know what to do with this...")) + return FALSE + return TRUE + +/obj/item/scrying/eye + name = "accursed eye" + desc = "It is pulsating." + icon = 'icons/roguetown/items/misc.dmi' + icon_state ="scryeye" + +/obj/item/scrying/eye/add_scry_comp() + AddComponent(/datum/component/scrying, 8 SECONDS, 5 MINUTES) + +/obj/item/scrying/eye/bogwitch + name = "eye of the hunt" + +/obj/item/scrying/eye/bogwitch/add_scry_comp() + AddComponent(/datum/component/scrying, 8 SECONDS, 60 SECONDS) + +/obj/item/scrying/eye/bogwitch/pass_scrying_checks(mob/living/user) + if(!istype(user.patron, /datum/patron/alternate/great_hunt/proven)) + to_chat(user, span_warning("I do not know what to do with this...")) + return FALSE + return TRUE + +/obj/item/scrying/flame + name = "enchanted abyssal flame" + icon = 'icons/roguetown/items/natural.dmi' + icon_state = "abyssalflame" + desc = "A flickering, black flame contained in a crystal; the heart of an archfiend. Or, at least, what passes for one. It pulses with dense thrums of magick. It has been enchanted to see beyond sight." + resistance_flags = FIRE_PROOF + w_class = WEIGHT_CLASS_SMALL + sellprice = 50 + item_weight = 50 GRAMS + +/obj/item/scrying/flame/add_scry_comp() + AddComponent(/datum/component/scrying, 10 SECONDS, 50 SECONDS, TRUE, FALSE, "I look into NAME_HERE but only see dark fire. Maybe I should wait.", 17, 13) + +/obj/item/scrying/flame/pass_scrying_checks(mob/living/user) + if(GET_MOB_SKILL_VALUE_OLD(user, /datum/attribute/skill/magic/blood) < 1) + to_chat(user, span_warning("I do not know what to do with this...")) + return FALSE + return TRUE + +/* .................. NOC Device (Fixed scrying ball) ................... */ +/obj/structure/scrying/Initialize() + . = ..() + add_scry_comp() + +/obj/structure/scrying/proc/add_scry_comp() + AddComponent(/datum/component/scrying) + +/obj/structure/scrying/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/scrying/nocdevice/add_scry_comp() + AddComponent(/datum/component/scrying, 10 SECONDS, 45 SECONDS, TRUE, FALSE, "I peer into the sky but cannot focus the lens on the face of Noc. Maybe I should wait.") + +/obj/structure/scrying/nocdevice/pass_scrying_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 + +/* .................. 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/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 + name = "Arcane Eye" + 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) + 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..78da0bd39ed 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, 12 SECONDS, 3 SECONDS, FALSE, FALSE) //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/jobs/job_types/gallowband/bogwitch.dm b/code/modules/jobs/job_types/gallowband/bogwitch.dm index b5b3a538142..9f30dfe306b 100644 --- a/code/modules/jobs/job_types/gallowband/bogwitch.dm +++ b/code/modules/jobs/job_types/gallowband/bogwitch.dm @@ -119,5 +119,5 @@ gloves = /obj/item/clothing/gloves/leather neck = /obj/item/clothing/neck/psycross/great_hunt backpack_contents = list( - /obj/item/scrying = 1 + /obj/item/scrying/eye/bogwitch = 1 ) diff --git a/code/modules/jobs/job_types/nobility/magician.dm b/code/modules/jobs/job_types/nobility/magician.dm index a0cb6d127dc..1aebcf4e7e1 100644 --- a/code/modules/jobs/job_types/nobility/magician.dm +++ b/code/modules/jobs/job_types/nobility/magician.dm @@ -153,7 +153,7 @@ shoes = /obj/item/clothing/shoes/shortboots neck = /obj/item/clothing/neck/mana_star backpack_contents = list( - /obj/item/scrying = 1, + /obj/item/scrying/orb = 1, /obj/item/chalk = 1, /obj/item/reagent_containers/glass/bottle/killersice = 1, /obj/item/weapon/knife/dagger/silver/arcyne = 1, diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 5f005717bcd..f9b30f8d9ae 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 b1027b62a33..3287e13325a 100644 --- a/vanderlin.dme +++ b/vanderlin.dme @@ -1175,6 +1175,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\skill_bind.dm" #include "code\datums\components\slippery.dm" @@ -2124,7 +2125,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" @@ -2138,6 +2138,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"