diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm
index 1e8ceb9ad00..5fdd953ae17 100644
--- a/code/_onclick/hud/alert.dm
+++ b/code/_onclick/hud/alert.dm
@@ -257,6 +257,34 @@ so as to remain in compliance with the most up-to-date laws."
icon_state = "low_mech_integrity"
+//GHOSTS
+//TODO: expand this system to replace the pollCandidates Yes/No messages
+/obj/screen/alert/notify_cloning
+ name = "Revival"
+ desc = "Someone is trying to revive you. Re-enter your corpse if you want to be revived!"
+ icon_state = "ghost_frame"
+ timeout = 300
+
+/obj/screen/alert/notify_cloning/Click()
+ if(!usr || !usr.client) return
+ var/mob/dead/observer/G = usr
+ G.reenter_corpse()
+
+/obj/screen/alert/notify_jump
+ name = "Body created"
+ desc = "A body was created. You can enter it."
+ icon_state = "ghost_frame"
+ timeout = 300
+ var/jump_target = null
+
+/obj/screen/alert/notify_jump/Click()
+ if(!usr || !usr.client) return
+ if(!jump_target) return
+ var/mob/dead/observer/G = usr
+ var/turf/T = get_turf(jump_target)
+ if(T && isturf(T))
+ G.loc = T
+
//OBJECT-BASED
/obj/screen/alert/buckled
diff --git a/code/_onclick/hud/ghost.dm b/code/_onclick/hud/ghost.dm
index 7c0c07275ff..ca78c21a1e9 100644
--- a/code/_onclick/hud/ghost.dm
+++ b/code/_onclick/hud/ghost.dm
@@ -1,5 +1,3 @@
-//TODO: add sec/med huds to ghosts
-//TODO: pop-up buttons for cloning,defib,drones,holoparasites,slimes,etcetc
/obj/screen/ghost
icon = 'icons/mob/screen_ghost.dmi'
diff --git a/code/game/gamemodes/shadowling/shadowling_abilities.dm b/code/game/gamemodes/shadowling/shadowling_abilities.dm
index fe10e866b2c..b97e228e7dd 100644
--- a/code/game/gamemodes/shadowling/shadowling_abilities.dm
+++ b/code/game/gamemodes/shadowling/shadowling_abilities.dm
@@ -583,7 +583,7 @@ datum/reagent/shadowling_blindness_smoke //Reagent used for above spell
return
usr.visible_message("[usr] kneels over [thrallToRevive], placing their hands on \his chest.", \
"You crouch over the body of your thrall and begin gathering energy...")
- thrallToRevive.notify_ghost_cloning("Your masters are resuscitating you! Re-enter your corpse if you wish to be brought to life.")
+ thrallToRevive.notify_ghost_cloning("Your masters are resuscitating you! Re-enter your corpse if you wish to be brought to life.", source = thrallToRevive)
if(!do_mob(usr, thrallToRevive, 30))
usr << "Your concentration snaps. The flow of energy ebbs."
charge_counter = charge_max
diff --git a/code/game/machinery/dna_scanner.dm b/code/game/machinery/dna_scanner.dm
index ac18b3fe445..8f5e8f6c8b1 100644
--- a/code/game/machinery/dna_scanner.dm
+++ b/code/game/machinery/dna_scanner.dm
@@ -109,7 +109,7 @@
|| locate(/obj/machinery/computer/cloning, get_step(src, EAST)) \
|| locate(/obj/machinery/computer/cloning, get_step(src, WEST)))
- occupant.notify_ghost_cloning("Your corpse has been placed into a cloning scanner. Re-enter your corpse if you want to be cloned!")
+ occupant.notify_ghost_cloning("Your corpse has been placed into a cloning scanner. Re-enter your corpse if you want to be cloned!", source = src)
return 1
/obj/machinery/dna_scannernew/open_machine()
diff --git a/code/game/objects/items/weapons/defib.dm b/code/game/objects/items/weapons/defib.dm
index a58e46a89b6..f81fc5dd559 100644
--- a/code/game/objects/items/weapons/defib.dm
+++ b/code/game/objects/items/weapons/defib.dm
@@ -450,7 +450,7 @@
busy = 0
update_icon()
return
- H.notify_ghost_cloning("Your heart is being defibrillated. Re-enter your corpse if you want to be revived!")
+ H.notify_ghost_cloning("Your heart is being defibrillated. Re-enter your corpse if you want to be revived!", source = src)
user.visible_message("[user] begins to place [src] on [M.name]'s chest.", "You begin to place [src] on [M.name]'s chest...")
busy = 1
diff --git a/code/game/objects/structures/spirit_board.dm b/code/game/objects/structures/spirit_board.dm
index ce2f47c59a2..8e97a5b40dc 100644
--- a/code/game/objects/structures/spirit_board.dm
+++ b/code/game/objects/structures/spirit_board.dm
@@ -30,7 +30,7 @@
if(virgin)
virgin = 0
- notify_ghosts("Someone has begun playing with a [src.name] in [get_area(src)]!")
+ notify_ghosts("Someone has begun playing with a [src.name] in [get_area(src)]!", source = src)
planchette = input("Choose the letter.", "Seance!") in list("A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z")
add_logs(M, src, "picked a letter on", " which was \"[planchette]\".")
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index a0cbb5bbb47..3df906627e0 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -165,9 +165,17 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
mind.current.key = key
return 1
-/mob/dead/observer/proc/notify_cloning(var/message, var/sound)
+/mob/dead/observer/proc/notify_cloning(var/message, var/sound, var/atom/source)
if(message)
src << "[message]"
+ if(source)
+ var/obj/screen/alert/A = throw_alert("\ref[source]_notify_cloning", /obj/screen/alert/notify_cloning)
+ if(A)
+ A.desc = message
+ var/old_layer = source.layer
+ source.layer = FLOAT_LAYER
+ A.overlays += source
+ source.layer = old_layer
src << "(Click to re-enter)"
if(sound)
src << sound(sound)
diff --git a/code/modules/mob/living/carbon/brain/MMI.dm b/code/modules/mob/living/carbon/brain/MMI.dm
index c762338f038..227b4ea3b43 100644
--- a/code/modules/mob/living/carbon/brain/MMI.dm
+++ b/code/modules/mob/living/carbon/brain/MMI.dm
@@ -41,7 +41,7 @@
return
var/mob/living/carbon/brain/B = newbrain.brainmob
if(!B.key)
- B.notify_ghost_cloning("Someone has put your brain in a MMI!")
+ B.notify_ghost_cloning("Someone has put your brain in a MMI!", source = src)
visible_message("[user] sticks \a [newbrain] into \the [src].")
brainmob = newbrain.brainmob
diff --git a/code/modules/mob/living/carbon/brain/posibrain.dm b/code/modules/mob/living/carbon/brain/posibrain.dm
index 5796e870ac8..c8f9a5b6067 100644
--- a/code/modules/mob/living/carbon/brain/posibrain.dm
+++ b/code/modules/mob/living/carbon/brain/posibrain.dm
@@ -22,7 +22,7 @@ var/global/posibrain_notif_cooldown = 0
/obj/item/device/mmi/posibrain/proc/ping_ghosts(msg)
if(!posibrain_notif_cooldown)
- notify_ghosts("Positronic brain [msg] in [get_area(src)]! (Click to enter)", 'sound/effects/ghost2.ogg')
+ notify_ghosts("Positronic brain [msg] in [get_area(src)]! (Click to enter)", 'sound/effects/ghost2.ogg', source = src)
posibrain_notif_cooldown = 1
spawn(askDelay) //Global one minute cooldown to avoid spam.
posibrain_notif_cooldown = 0
diff --git a/code/modules/mob/living/simple_animal/bot_swarm/swarmer.dm b/code/modules/mob/living/simple_animal/bot_swarm/swarmer.dm
index 460eb6a2bf2..5667cbc61be 100644
--- a/code/modules/mob/living/simple_animal/bot_swarm/swarmer.dm
+++ b/code/modules/mob/living/simple_animal/bot_swarm/swarmer.dm
@@ -6,7 +6,7 @@
icon_state = "swarmer_unactivated"
/obj/item/unactivated_swarmer/New()
- notify_ghosts("An unactivated swarmer has been created in [get_area(src)]! (Click to enter)")
+ notify_ghosts("An unactivated swarmer has been created in [get_area(src)]! (Click to enter)", source = src)
..()
/obj/item/unactivated_swarmer/Topic(href, href_list)
diff --git a/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm b/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm
index 92fb1c7ed00..3cc67299182 100644
--- a/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm
+++ b/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm
@@ -15,6 +15,12 @@
origin_tech = "programming=2;biotech=4"
var/drone_type = /mob/living/simple_animal/drone //Type of drone that will be spawned
+/obj/item/drone_shell/New()
+ ..()
+ var/area/A = get_area(src)
+ if(A)
+ notify_ghosts("A drone shell has been created in \the [A.name].", source = src)
+
/obj/item/drone_shell/attack_ghost(mob/user)
if(jobban_isbanned(user,"drone"))
return
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index ca0948b56c5..53b41e7988b 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -949,10 +949,10 @@ var/list/slot_equipment_priority = list( \
return G
break
-/mob/proc/notify_ghost_cloning(var/message = "Someone is trying to revive you. Re-enter your corpse if you want to be revived!", var/sound = 'sound/effects/genetics.ogg')
+/mob/proc/notify_ghost_cloning(var/message = "Someone is trying to revive you. Re-enter your corpse if you want to be revived!", var/sound = 'sound/effects/genetics.ogg', var/atom/source = null)
var/mob/dead/observer/ghost = get_ghost()
if(ghost)
- ghost.notify_cloning(message, sound)
+ ghost.notify_cloning(message, sound, source)
return ghost
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index f7cc8017d76..c8415e95e99 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -460,12 +460,25 @@ It's fairly easy to fix if dealing with single letters but not so much with comp
/mob/proc/reagent_check(datum/reagent/R) // utilized in the species code
return 1
-/proc/notify_ghosts(var/message, var/ghost_sound = null) //Easy notification of ghosts.
+/proc/notify_ghosts(var/message, var/ghost_sound = null, var/atom/source = null, var/image/alert_overlay = null) //Easy notification of ghosts.
for(var/mob/dead/observer/O in player_list)
if(O.client)
O << "[message]"
if(ghost_sound)
O << sound(ghost_sound)
+ if(source)
+ var/obj/screen/alert/notify_jump/A = O.throw_alert("\ref[source]_notify_jump", /obj/screen/alert/notify_jump)
+ if(A)
+ A.desc = message
+ A.jump_target = get_turf(source)
+ if(!alert_overlay)
+ var/old_layer = source.layer
+ source.layer = FLOAT_LAYER
+ A.overlays += source
+ source.layer = old_layer
+ else
+ alert_overlay.layer = FLOAT_LAYER
+ A.overlays += alert_overlay
/proc/item_heal_robotic(mob/living/carbon/human/H, mob/user, brute, burn)
var/obj/item/organ/limb/affecting = H.get_organ(check_zone(user.zone_sel.selecting))
diff --git a/code/modules/power/singularity/narsie.dm b/code/modules/power/singularity/narsie.dm
index 38f637d82fc..c64e244f0bf 100644
--- a/code/modules/power/singularity/narsie.dm
+++ b/code/modules/power/singularity/narsie.dm
@@ -29,7 +29,8 @@
var/area/A = get_area(src)
if(A)
- notify_ghosts("Nar-Sie has risen in \the [A.name]. Reach out to the Geometer to be given a new shell for your soul.")
+ var/image/alert_overlay = image('icons/mob/mob.dmi', "harvester")
+ notify_ghosts("Nar-Sie has risen in \the [A.name]. Reach out to the Geometer to be given a new shell for your soul.", source = src, alert_overlay = alert_overlay)
narsie_spawn_animation()
diff --git a/code/modules/reagents/Chemistry-Reagents/Medicine-Reagents.dm b/code/modules/reagents/Chemistry-Reagents/Medicine-Reagents.dm
index 6253c02ff83..7f45e8c9f7c 100644
--- a/code/modules/reagents/Chemistry-Reagents/Medicine-Reagents.dm
+++ b/code/modules/reagents/Chemistry-Reagents/Medicine-Reagents.dm
@@ -693,7 +693,7 @@
if(!M.suiciding && !(M.disabilities & NOCLONE))
if(!M)
return
- if(M.notify_ghost_cloning())
+ if(M.notify_ghost_cloning(source = M))
spawn (100) //so the ghost has time to re-enter
return
else
diff --git a/code/modules/reagents/Chemistry-Recipes/Slime_extracts.dm b/code/modules/reagents/Chemistry-Recipes/Slime_extracts.dm
index 6426985bc60..edd4550571d 100644
--- a/code/modules/reagents/Chemistry-Recipes/Slime_extracts.dm
+++ b/code/modules/reagents/Chemistry-Recipes/Slime_extracts.dm
@@ -501,7 +501,7 @@
feedback_add_details("slime_cores_used","[type]")
var/obj/effect/golemrune/Z = new /obj/effect/golemrune
Z.loc = get_turf(holder.my_atom)
- notify_ghosts("Golem rune created in [get_area(Z)].", 'sound/effects/ghost2.ogg')
+ notify_ghosts("Golem rune created in [get_area(Z)].", 'sound/effects/ghost2.ogg', source = Z)
//Bluespace
/datum/chemical_reaction/slimefloor2
diff --git a/html/changelogs/Tkdrg-GhostHUD.yml b/html/changelogs/Tkdrg-GhostHUD.yml
new file mode 100644
index 00000000000..d7d297210ee
--- /dev/null
+++ b/html/changelogs/Tkdrg-GhostHUD.yml
@@ -0,0 +1,23 @@
+# Valid Prefixes:
+# bugfix
+# wip (For works in progress)
+# tweak
+# soundadd
+# sounddel
+# rscadd (general adding of nice things)
+# rscdel (general deleting of nice things)
+# imageadd
+# imagedel
+# spellcheck (typo fixes)
+# experiment
+# tgs (TG-ported fixes?)
+#################################
+
+author: Tkdrg
+
+delete-after: True
+
+changes:
+ - rscadd: "Added a Ghost On-screen HUD. It can be toggled using the \"Toggle Ghost HUD\" verb. Thank you Razharas for the sprites!"
+ - rscadd: "Ghosts can now use the \"Toggle med/sec HUD\" verb to see the basic secHUD (jobs only) or the medHUD of humans."
+ - rscadd: "Ghost will now get clickable icon alerts from events such as being put in a cloner, drones being created, Nar-sie being summoned, among others. The older chat messages were kept. These alerts can not be toggled."
diff --git a/icons/mob/screen_alert.dmi b/icons/mob/screen_alert.dmi
index 1b707cda5e0..88fc0e85d5c 100644
Binary files a/icons/mob/screen_alert.dmi and b/icons/mob/screen_alert.dmi differ
diff --git a/icons/mob/screen_ghost.dmi b/icons/mob/screen_ghost.dmi
index 8c32d9b5246..39975761afe 100644
Binary files a/icons/mob/screen_ghost.dmi and b/icons/mob/screen_ghost.dmi differ