diff --git a/code/game/objects/items/devices/flash.dm b/code/game/objects/items/devices/flash.dm
index 4a870a1bf7..8c2ba36e76 100644
--- a/code/game/objects/items/devices/flash.dm
+++ b/code/game/objects/items/devices/flash.dm
@@ -216,18 +216,34 @@
sleep(5)
qdel(animation)
+ var/datum/gender/TT = gender_datums[M.get_visible_gender()]
if(!flashfail)
flick("flash2", src)
if(!issilicon(M))
-
- user.visible_message("[user] blinds [M] with the flash!")
+ M.interact_message(user,
+ SPAN_DANGER("\The [user] blinds \the [M] with \the [src]!"),
+ SPAN_DANGER("\The [user] blinds you with \the [src]!"),
+ SPAN_DANGER("You blind \the [M] with \the [src]!"),
+ SPAN_DANGER("\The [user] blinds [TT.himself] with \the [src]!"),
+ SPAN_DANGER("You blind yourself with \the [src]! Why?")
+ )
else
-
- user.visible_message("[user] overloads [M]'s sensors with the flash!")
+ M.interact_message(user,
+ SPAN_DANGER("\The [user] overloads \the [M]'s sensors with \the [src]!"),
+ SPAN_DANGER("\The [user] overloads your sensors with \the [src]!"),
+ SPAN_DANGER("You overload \the [M]'s sensors with \the [src]!"),
+ SPAN_DANGER("\The [user] overloads [TT.his] own sensors with \the [src]!"),
+ SPAN_DANGER("You overload your own sensors with \the [src]! Why?")
+ )
M.Weaken(rand(5,10))
else
-
- user.visible_message("[user] fails to blind [M] with the flash!")
+ M.interact_message(user,
+ SPAN_DANGER("\The [user] fails to blind \the [M] with \the [src]!"),
+ SPAN_DANGER("\The [user] fails to blind you with \the [src]!"),
+ SPAN_DANGER("You fail to blind \the [M] with \the [src]!"),
+ SPAN_DANGER("\The [user] fails to blind [TT.himself] with \the [src]!"),
+ SPAN_DANGER("You fail to blind yourself with \the [src]!"),
+ )
return
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 3f100dbc7a..066e3ccf0e 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -101,6 +101,63 @@
/atom/proc/drain_power(var/drain_check,var/surge, var/amount = 0)
return -1
+/**
+ * Very flexible visible message analogue allowing for different messages to be shown to two different mobs as well as any nearby observers.
+ *
+ * All arguments except `other` and `message` are optional, but the proc is built around using at least a few of them!
+ * * `other` - The mob that's interacting with this one
+ * * `message` - Shown to all nearby observers, e.g. "X does something to Y!"
+ * * `self_message` - Shown to the source mob, e.g. "X does something to you!"
+ * * `other_message` - Shown to the other, e.g. "You do something to Y!"
+ * * `self_target_message` - Shown to nearby mobs if `src == other`, e.g. "X do something to themselves!"
+ * * `self_targeted_message` - As above, but to the source mob themselves, e.g. "You do something to yourself!"
+ * * `blind_message` - Shown to nearby blind mobs instead of `message`, e.g. "You hear something!"
+ * * `blind_self_message` - Shown to the source mob if they're blind, e.g. "You feel something done to you!"
+ */
+/mob/proc/interact_message(mob/other, message, self_message, other_message, self_target_message, self_targeted_message, blind_message, blind_self_message, range = world.view, list/exclude_objs = null, list/exclude_mobs = null)
+ var/turf/T = get_turf(src)
+ var/list/in_range = get_mobs_and_objs_in_view_fast(T, range, remote_ghosts = FALSE)
+ T = get_turf(other)
+ // We check for anything in range of either mob, not just one
+ in_range += get_mobs_and_objs_in_view_fast(T, range, remote_ghosts = FALSE)
+ var/list/mobs = uniquelist(in_range["mobs"])
+ var/list/objs = uniquelist(in_range["objs"])
+ mobs.Remove(exclude_mobs)
+ objs.Remove(exclude_objs)
+
+ for (var/V in objs)
+ var/obj/O = V
+ O.show_message(message, VISIBLE_MESSAGE, blind_message, AUDIBLE_MESSAGE)
+
+ for (var/V in mobs)
+ var/mob/M = V
+
+ if (self_targeted_message && other == src && M == src)
+ // Always shown even if blind/deaf, since the initiating mob will always know what they're doing to themselves
+ M.show_message(self_targeted_message)
+ continue
+
+ else if (self_message && M == src)
+ M.show_message(self_message, VISIBLE_MESSAGE, blind_self_message, AUDIBLE_MESSAGE)
+ continue
+
+ else if (M == other)
+ M.show_message(other_message, VISIBLE_MESSAGE, blind_message, AUDIBLE_MESSAGE)
+ continue
+
+ else if (!M.is_blind() && M.see_invisible >= src.invisibility)
+ M.show_message(other == src && self_target_message ? self_target_message : message, VISIBLE_MESSAGE, blind_message, AUDIBLE_MESSAGE)
+ continue
+
+ else if (blind_message)
+ M.show_message(blind_message, AUDIBLE_MESSAGE)
+ continue
+
+ if (shadow)
+ shadow.visible_message(message, self_message, blind_message, exclude_mobs, range)
+ else if (other.shadow)
+ other.shadow.visible_message(message, self_message, blind_message, exclude_mobs, range)
+
// Show a message to all mobs and objects in earshot of this one
// This would be for audible actions by the src mob
// message is the message output to anyone who can hear.