diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index 23f5a5c51f7..9a10f4b6230 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -113,6 +113,8 @@ GLOBAL_LIST_INIT(turfs_without_ground, typecacheof(list( #define isrogueobserver(A) (istype(A, /mob/dead/observer/rogue)) +#define is_scryeye(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 e1d9a1e0f70..85a19ffed28 100644 --- a/code/__DEFINES/traits/definitions.dm +++ b/code/__DEFINES/traits/definitions.dm @@ -311,8 +311,6 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_ANTIMAGIC_NO_SELFBLOCK "anti_magic_no_selfblock" /// makes your footsteps completely silent #define TRAIT_SILENT_FOOTSTEPS "silent_footsteps" -/// 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 92d5e97767c..fcf8a0abade 100644 --- a/code/_onclick/hud/ghost.dm +++ b/code/_onclick/hud/ghost.dm @@ -24,8 +24,6 @@ return if(ghost.client) if(ghost.client.holder) - if(istype(ghost, /mob/dead/observer/rogue/arcaneeye)) - return if(istype(ghost, /mob/dead/observer/profane)) // Souls trapped by a dagger can return to lobby if they want if(alert("Return to the lobby?", "", "Yes", "No") == "Yes") ghost.returntolobby() 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/game/objects/items/inquisition_relics.dm b/code/game/objects/items/inquisition_relics.dm index 30bce46fce7..6338d94e633 100644 --- a/code/game/objects/items/inquisition_relics.dm +++ b/code/game/objects/items/inquisition_relics.dm @@ -1128,24 +1128,20 @@ 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 + var/datum/scrying_component/mirror/scry_comp /obj/item/inqarticles/bmirror/Initialize() . = ..() soundloop = new(src, FALSE) + scry_comp = new(src) /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) @@ -1160,13 +1156,7 @@ 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.")) @@ -1213,67 +1203,9 @@ to_chat(user, span_warning("It looks like it needs blood to work properly.")) return - if(!active) - var/input = browser_alert(user, "WHAT DO YOU SEEK?", "THE PRICE IS PAID", list("BLOOD", "FIXATION")) - if(!input || QDELETED(user) || QDELETED(src)) - return - - var/mob/living/carbon/human/target - - if(input == "FIXATION") - var/name = 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(HL.real_name == 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 - else if(input == "BLOOD") - target = feeder?.resolve() - - if(!target) - 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) - - 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...")) + //add_client_colour(/datum/client_colour/nocshaded) - 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) + scry_comp.activate(user) /obj/item/inqarticles/bmirror/attack(mob/living/carbon/human/attacked, mob/living/carbon/human/user, list/modifiers) if(!istype(attacked) || !istype(user)) @@ -1283,7 +1215,7 @@ to_chat(user, span_warning("I need to open it first.")) return - if(feeder) + if(fedblood) to_chat(user, span_warning("It's already been fed.")) return @@ -1309,7 +1241,6 @@ attacked.adjustBruteLoss(40) attacked.blood_volume = max(attacked.blood_volume - 240, 0) attacked.handle_blood() - feeder = WEAKREF(attacked) openstate = "bloody" fedblood = TRUE update_appearance(UPDATE_ICON_STATE) @@ -1339,6 +1270,14 @@ to_chat(user, span_warning("I cannot close the mirror while it's active.")) return + opened = !opened + if(opened) + playsound(src, 'sound/items/blackmirror_open.ogg', 100, FALSE) + else + playsound(src, 'sound/items/blackmirror_shut.ogg', 100, FALSE) + update_appearance(UPDATE_ICON_STATE) + +/* var/mob/living/fixated = fixation?.resolve() if(opened) if(fixated) @@ -1356,10 +1295,10 @@ 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() . = ..() @@ -1373,37 +1312,6 @@ name = "BLACK EYE" 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 ..() - -/atom/movable/screen/alert/blackmirror/Click() - var/mob/living/L = usr - if(!istype(L)) - return - - var/datum/weakref/lookat = null - if(alert(L, "KEEP LOOKING, WHAT WILL YOU FIND?", "BLACK EYED GAZE", "BLOOD", "MIRROR") != "BLOOD") - lookat = source - else - lookat = source.feeder - playsound(L, 'sound/items/blackmirror_use.ogg', 100, FALSE) - ADD_TRAIT(L, TRAIT_NOSSDINDICATOR, "blackmirror") - var/mob/living/target = lookat?.resolve() - if(!target) - return - var/mob/dead/observer/screye/blackmirror/S = L.scry_ghost() - if(!S) - 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) // 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 ec76e5797f6..00000000000 --- a/code/game/objects/items/magic.dm +++ /dev/null @@ -1,145 +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 - - 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 - -/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 = stripped_input(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 [input].")) - 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(HL.STAPER >= 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(HL.STAPER >= 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" - plane = -1 - layer = 4.2 - var/last_scry - -/obj/structure/nocdevice/attack_hand(mob/user) - . = ..() - var/mob/living/carbon/human/H = user - if(H.virginity) - 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(HL.STAPER >= 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(HL.STAPER >= 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..923b30fa85f --- /dev/null +++ b/code/game/objects/items/scrying.dm @@ -0,0 +1,433 @@ +/datum/scrying_component + 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 + COOLDOWN_DECLARE(scry_cooldown) + +/datum/scrying_component/New(obj/item/scrying/parent) + . = ..() + text_cooldown_fail = replacetext(text_cooldown_fail, "NAME_HERE", "\the [name]") + if(!parent) + return + RegisterSignal(parent, COMSIG_PARENT_QDELETING, PROC_REF(handle_parent_del), parent) + +/datum/scrying_component/proc/handle_parent_del(var/obj/item/scrying/parent) + SIGNAL_HANDLER + parent?.scry_comp = null + qdel(src) + +/datum/scrying_component/proc/activate(mob/living/user) + if(!pass_extra_checks()) + 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 || (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 + + //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 + + var/mob/living/carbon/human/found_target + 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 + found_target = human_target + break + + held_user = user + 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.")) + held_user = null + return + + create_eye() + if(!scrying_eye) + remove_eye(TRUE) + return + + if(needs_to_live && found_target.stat) + to_chat(user, span_warning("I peer into \the [name], but can't find [search_name].")) + remove_eye(TRUE) + return FALSE + + log_game("SCRYING: [user.real_name] ([user.ckey]) has used the [name] to leer at [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) + if(found_target.STAPER >= 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(found_target.STAPER >= 11) + to_chat(found_target, span_warning("I feel a pair of unknown eyes on me.")) + return TRUE + +/datum/scrying_component/proc/create_eye() + if(!held_user) + return FALSE + scrying_eye = new + scrying_eye.component = src + held_user.reset_perspective(scrying_eye) + held_user.Immobilize(vision_duration) + held_user.overlay_fullscreen("scrying", /atom/movable/screen/backhudl/obs) + addtimer(CALLBACK(src, PROC_REF(remove_eye)), vision_duration) + +/datum/scrying_component/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/scrying_component/proc/pass_extra_checks(mob/living/user) + return TRUE + +/datum/scrying_component/orb + name = "Scrying Orb" + +/datum/scrying_component/eye + name = "Accursed Eye" + cooldown_duration = 5 MINUTES + +/datum/scrying_component/vampire + name = "Night's Eye" + needs_to_know = FALSE + needs_to_live = FALSE + +/datum/scrying_component/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." + +/datum/scrying_component/telescope/pass_extra_checks(mob/living/user) + var/mob/living/carbon/human/human_user = user + if(!ishuman(human_user) || !human_user.virginity) + to_chat(human_user, span_notice("Noc looks angry with me...")) + return FALSE + return TRUE + +/datum/scrying_component/mirror + name = "Black Mirror" + vision_duration = 4 SECONDS + needs_to_know = FALSE + needs_to_live = FALSE + var/obj/item/inqarticles/bmirror/parent_mirror + var/mob/stored_target + +/datum/scrying_component/mirror/New(obj/item/scrying/parent) + . = ..() + parent_mirror = parent + if(!istype(parent_mirror)) + UnregisterSignal(parent, COMSIG_PARENT_QDELETING) + qdel(src) + +/datum/scrying_component/mirror/activate(mob/living/user) + if(!pass_extra_checks()) + 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/scrying_component/mirror/create_eye() + if(!held_user) + return FALSE + scrying_eye = new + scrying_eye.component = src + held_user.reset_perspective(scrying_eye) + held_user.Immobilize(vision_duration) + held_user.overlay_fullscreen("scrying", /atom/movable/screen/backhudl/obs) + playsound(held_user, 'sound/items/blackmirror_use.ogg', 100, FALSE) + addtimer(CALLBACK(src, PROC_REF(remove_eye)), vision_duration) + +/datum/scrying_component/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() + +/datum/scrying_component/mirror/proc/apply_black_eye() + scrying_eye.orbit(stored_target) + parent_mirror.effect = stored_target.throw_alert("blackmirror", /atom/movable/screen/alert/blackmirror, override = TRUE) + playsound(stored_target, 'sound/items/blackeye_warn.ogg', 100, FALSE) + + +/////////////////////////////////////////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 + + var/datum/scrying_component/scry_comp + var/scry_comp_path = /datum/scrying_component/orb + +/obj/item/scrying/Initialize(mapload) + . = ..() + scry_comp = new scry_comp_path(src) + +/obj/item/scrying/eye + name = "accursed eye" + desc = "It is pulsating." + icon = 'icons/roguetown/items/misc.dmi' + icon_state ="scryeye" + + scry_comp_path = /datum/scrying_component/eye + +/obj/item/scrying/attack_self(mob/user, list/modifiers) + . = ..() + scry_comp.activate(user) + +//23.08.2025 +//nocdevice depreciated? + +/* .................. 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" + plane = -1 + layer = 4.2 + var/datum/scrying_component/telescope/scry_comp + +/obj/structure/nocdevice/Initialize() + . = ..() + scry_comp = new(src) + +/obj/structure/nocdevice/attack_hand(mob/user) + . = ..() + scry_comp.activate(user) + +/* .................. THE EYE ................... */ +/mob/scry_eye + sight = SEE_TURFS | SEE_MOBS | SEE_OBJS + see_in_dark = 100 + hud_type = /datum/hud/obs + invisibility = INVISIBILITY_GHOST + see_invisible = SEE_INVISIBLE_LIVING + var/datum/scrying_component/component + 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(!component) + qdel(src) + return + component.held_user.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(!is_scryeye(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/V = L + var/holyskill = V.get_skill_level(/datum/skill/magic/holy) + var/magicskill = V.get_skill_level(/datum/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/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 dcf76366c51..eee24b86c5f 100644 --- a/code/modules/antagonists/villain/vampire/objects/scrying.dm +++ b/code/modules/antagonists/villain/vampire/objects/scrying.dm @@ -1,157 +1,27 @@ /obj/structure/vampire/scryingorb // Method of spying on the town name = "Eye of Night" icon_state = "scrying" + var/datum/scrying_component/vampire/scry_comp //Temporary alternative + +/obj/structure/vampire/scryingorb/Initialize() + . = ..() + scry_comp = new(src) /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) + //user.scry(can_reenter_corpse = 1, force_respawn = FALSE) + scry_comp.activate(user) else to_chat(user, span_warning("I don't have the power to use this!")) -/mob/dead/observer/rogue/arcaneeye - sight = 0 - see_in_dark = 2 - invisibility = INVISIBILITY_GHOST - see_invisible = SEE_INVISIBLE_GHOST - - misting = 0 - var/mob/living/carbon/human/vampirelord = null - icon_state = "arcaneeye" - draw_icon = FALSE - hud_type = /datum/hud/eye - -/mob/dead/observer/rogue/arcaneeye/proc/scry_tele() - set category = "RoleUnique.Arcane Eye" - set name = "Teleport" - set desc= "Teleport to a location" - set hidden = 0 - - if(!isobserver(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/dead/observer/rogue/arcaneeye/Initialize() - . = ..() - set_invisibility(GLOB.observer_default_invisibility) - 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) - name = "Arcane Eye" - grant_all_languages() - -/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/Crossed(mob/living/L) - if(istype(L, /mob/living/carbon/human)) - var/mob/living/carbon/human/V = L - var/holyskill = V.get_skill_level(/datum/skill/magic/holy) - var/magicskill = V.get_skill_level(/datum/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/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, TRUE)) - 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, TRUE)) - to_chat(src, span_notice("I move down.")) - -/mob/dead/observer/rogue/arcaneeye/Move(NewLoc, direct) - if(world.time < next_gmove) - return - next_gmove = world.time + 3 - - if(updatedir) - setDir(direct)//only update dir if we actually need it, so overlays won't spin on base sprites that don't have directions of their own - var/oldloc = loc - - if(NewLoc) - var/NewLocTurf = get_turf(NewLoc) - if(istype(NewLocTurf, /turf/closed/mineral/bedrock)) // prevent going out of bounds. - return - forceMove(NewLoc) - else - forceMove(get_turf(src)) //Get out of closets and such as a ghost - if((direct & NORTH) && y < world.maxy) - y++ - else if((direct & SOUTH) && y > 1) - y-- - if((direct & EAST) && x < world.maxx) - x++ - else if((direct & WEST) && x > 1) - x-- - Moved(oldloc, direct) /mob/proc/scry(can_reenter_corpse = 1, force_respawn = FALSE, drawskip) 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 return eye diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index c5993fa4c31..32208c0d467 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -107,22 +107,6 @@ GLOBAL_LIST_INIT(ghost_verbs, list( return . = ..() -/mob/dead/observer/screye -// see_invisible = SEE_INVISIBLE_LIVING - sight = 0 - see_in_dark = 0 - hud_type = /datum/hud/obs - 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 @@ -212,10 +196,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.")) grant_all_languages() @@ -327,16 +309,6 @@ Works together with spawning an observer, noted above. // return return ..() -/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 - return ghost - /* This is the proc mobs get to turn into a ghost. Forked from ghostize due to compatibility issues. */ @@ -757,8 +729,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(alert(src, "You have been summoned to destroy Vanderlin!", "Join the Horde", "Yes", "No") == "Yes") diff --git a/code/modules/mob/dead/observer/observer_say.dm b/code/modules/mob/dead/observer/observer_say.dm index cce7351e5e0..57a2baf5964 100644 --- a/code/modules/mob/dead/observer/observer_say.dm +++ b/code/modules/mob/dead/observer/observer_say.dm @@ -37,12 +37,6 @@ /mob/dead/observer/rogue/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 0f79769aeb8..289e029bfdd 100644 --- a/vanderlin.dme +++ b/vanderlin.dme @@ -1744,7 +1744,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" @@ -1757,6 +1756,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"