diff --git a/code/__DEFINES/dcs/signals/signals_global.dm b/code/__DEFINES/dcs/signals/signals_global.dm
index 81c37f60afa..f706e6b9fe9 100644
--- a/code/__DEFINES/dcs/signals/signals_global.dm
+++ b/code/__DEFINES/dcs/signals/signals_global.dm
@@ -88,6 +88,14 @@
/// global signal when a global nullrod type is picked
#define COMSIG_GLOB_NULLROD_PICKED "!nullrod_picked"
+/// global signal when someone prays via prayer verb.
+#define COMSIG_GLOB_SEND_PRAYER "!send_prayer"
+ #define ARG_PRAYING_MOB 1
+ #define ARG_PRAYER_MSG 2
+ #define ARG_PRAYER_TYPE 3
+ #define ARG_PRAYER_SYMBOL 4
+ #define ARG_PRAYED_DEITIES 5
+
/// Global signal when light debugging is canceled
#define COMSIG_LIGHT_DEBUG_DISABLED "!light_debug_disabled"
diff --git a/code/__DEFINES/religion.dm b/code/__DEFINES/religion.dm
index d3fb3d36e41..694968e0356 100644
--- a/code/__DEFINES/religion.dm
+++ b/code/__DEFINES/religion.dm
@@ -68,3 +68,20 @@
#define RITE_ALLOW_MULTIPLE_PERFORMS (1<<1)
///The rite can only be fully performed once, so we'll completely remove it from the rite list afterwards.
#define RITE_ONE_TIME_USE (1<<2)
+
+///Default prayer type for the pray verb
+#define DEFAULT_PRAYER "PRAYER"
+///Cult prayer type
+#define CULT_PRAYER "CULTIST PRAYER"
+//Heretic prayer type
+#define HERETIC_PRAYER "HERETICAL PRAYER"
+///Chaplain prayer type
+#define CHAPLAIN_PRAYER "CHAPLAIN PRAYER"
+///Spiritual prayer type
+#define SPIRITUAL_PRAYER "SPIRITUAL PRAYER"
+///Evil prayer type
+#define EVIL_PRAYER "EVIL PRAYER"
+///Standard prayer type that santa sees
+#define SANTA_PRAYER "PRAYER"
+///what type of prayer santa sees when someone's bad
+#define SANTA_NAUGHTY_PRAYER "PRAYER (NAUGHTY)"
diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm
index 59b12502d1c..ffd89e2cfdb 100644
--- a/code/__DEFINES/traits/declarations.dm
+++ b/code/__DEFINES/traits/declarations.dm
@@ -381,6 +381,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_SUCCUMB_OVERRIDE "succumb_override"
/// Can hear observers
#define TRAIT_SIXTHSENSE "sixth_sense"
+/// For mobs / minds with the listening_prayers trait, this stops them from hearing prayers
+#define TRAIT_DONT_HEAR_PRAYERS "dont_hear_prayers"
#define TRAIT_FEARLESS "fearless"
/// Ignores darkness for hearing
#define TRAIT_HEAR_THROUGH_DARKNESS "hear_through_darkness"
diff --git a/code/_globalvars/religion.dm b/code/_globalvars/religion.dm
index 063ff0e8642..c2ea1a69d35 100644
--- a/code/_globalvars/religion.dm
+++ b/code/_globalvars/religion.dm
@@ -15,6 +15,39 @@ GLOBAL_LIST_EMPTY(chaplain_altars)
GLOBAL_VAR(holy_weapon_type)
GLOBAL_VAR(holy_armor_type)
+GLOBAL_LIST_INIT(prayer_type_to_font_color, list(
+ DEFAULT_PRAYER = "purple",
+ CULT_PRAYER = "black",
+ HERETIC_PRAYER = "green",
+ CHAPLAIN_PRAYER = "yellow",
+ SPIRITUAL_PRAYER = "blue",
+ EVIL_PRAYER = "red",
+ SANTA_PRAYER = "purple",
+ SANTA_NAUGHTY_PRAYER = "red",
+))
+
+GLOBAL_LIST_INIT(prayer_type_to_icon_state, list(
+ DEFAULT_PRAYER = "bible",
+ CULT_PRAYER = "tome",
+ HERETIC_PRAYER = "necronomicon",
+ CHAPLAIN_PRAYER = "kingyellow",
+ SPIRITUAL_PRAYER = "holylight",
+ EVIL_PRAYER = "burning",
+ SANTA_PRAYER = "bible", //here just in case, we use present boxes for the icon
+ SANTA_NAUGHTY_PRAYER = "burning",
+))
+
+GLOBAL_LIST_INIT(prayer_type_to_message_box, list(
+ DEFAULT_PRAYER = "",
+ CULT_PRAYER = "red_box",
+ HERETIC_PRAYER = "green_box",
+ CHAPLAIN_PRAYER = "blue_box",
+ SPIRITUAL_PRAYER = "",
+ EVIL_PRAYER = "red_box",
+ SANTA_PRAYER = "blue_box",
+ SANTA_NAUGHTY_PRAYER = "red_box",
+))
+
/// Sets a new religious sect used by all chaplains int he round
/proc/set_new_religious_sect(path, reset_existing = FALSE)
if(!ispath(path, /datum/religion_sect))
diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm
index 61a0255dfaa..1ddfcd9dc14 100644
--- a/code/_globalvars/traits/_traits.dm
+++ b/code/_globalvars/traits/_traits.dm
@@ -265,6 +265,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_DISK_VERIFIER" = TRAIT_DISK_VERIFIER,
"TRAIT_DISPLAY_JOB_IN_BINARY" = TRAIT_DISPLAY_JOB_IN_BINARY,
"TRAIT_DISSECTED" = TRAIT_DISSECTED,
+ "TRAIT_DONT_HEAR_PRAYERS" = TRAIT_DONT_HEAR_PRAYERS,
"TRAIT_DONT_WRITE_MEMORY" = TRAIT_DONT_WRITE_MEMORY,
"TRAIT_DOUBLE_TAP" = TRAIT_DOUBLE_TAP,
"TRAIT_DREAMING" = TRAIT_DREAMING,
diff --git a/code/datums/components/listen_prayers.dm b/code/datums/components/listen_prayers.dm
new file mode 100644
index 00000000000..d22724eb666
--- /dev/null
+++ b/code/datums/components/listen_prayers.dm
@@ -0,0 +1,89 @@
+/**
+ * A component that allows the attached mind to listen prayers from other mobs.
+ * Unlike admins, they cannot smite the person for it, also the prayers are anonymous unless the person says who he's in the message.
+ */
+/datum/component/listen_prayers
+ ///A callback called before the prayer is heard. May alter the message or prevent it from being heard if it returns FALSE
+ var/datum/callback/pre_prayer_callback
+ ///This will be affixed to the "deities" that have heard this message.
+ var/deity_name
+ ///The action that the owner can use to ignore the prayers
+ var/datum/action/innate/listen_prayers/toggle
+ ///The description we give to the instance of the listen_prayers toggle.
+ var/toggle_desc
+
+/datum/component/listen_prayers/Initialize(datum/callback/pre_prayer_callback, deity_name, toggle_desc)
+ if (!istype(parent, /datum/mind))
+ return COMPONENT_INCOMPATIBLE
+ src.pre_prayer_callback = pre_prayer_callback
+ src.deity_name = deity_name
+ src.toggle_desc = toggle_desc
+
+/datum/component/listen_prayers/Destroy()
+ pre_prayer_callback = null
+ return ..()
+
+/datum/component/listen_prayers/RegisterWithParent()
+ RegisterSignal(SSdcs, COMSIG_GLOB_SEND_PRAYER, PROC_REF(on_sent_prayer))
+ var/datum/mind/mind = parent
+
+ var/datum/action/innate/listen_prayers/toggle = new(mind)
+ if(toggle_desc)
+ toggle.desc = toggle_desc
+ if(mind.current)
+ toggle.Grant(mind.current)
+
+/datum/component/listen_prayers/UnregisterFromParent()
+ UnregisterSignal(SSdcs, COMSIG_GLOB_SEND_PRAYER)
+ QDEL_NULL(toggle)
+
+/datum/component/listen_prayers/proc/on_sent_prayer(source, mob/praying, message, prayer_type, symbol, list/deities_that_listened)
+ SIGNAL_HANDLER
+ var/datum/mind/mind = parent
+ if(!mind.current || mind.current.stat >= UNCONSCIOUS || !mind.current.client) //You can't hear prayers if unconscious or disconnected
+ return
+ if(!isliving(praying) || praying.stat == DEAD)
+ return FALSE //I don't see any reason in hell to why dead people should be allowed into this. This isn't a knockoff TRAIT_SIXTHSENSE.
+ if(praying == mind.current) //Ignore prayers coming from ourselves.
+ return
+ if(mind.current.client in GLOB.admins) //This is redundant if we're adminning
+ return
+ if(HAS_MIND_TRAIT(mind.current, TRAIT_DONT_HEAR_PRAYERS))
+ return
+ var/list/arguments = args.Copy(2)
+ if(pre_prayer_callback && !pre_prayer_callback.Invoke(args))
+ return
+ prayer_type = arguments[ARG_PRAYER_TYPE]
+ symbol = arguments[ARG_PRAYER_SYMBOL]
+ message = "[icon2html(arguments[ARG_PRAYER_SYMBOL], mind.current.client)][prayer_type]: [span_notice(arguments[ARG_PRAYER_MSG])]"
+ to_chat(mind.current, custom_boxed_message(GLOB.prayer_type_to_message_box[prayer_type], message))
+ SEND_SOUND(mind.current, sound('sound/effects/pray.ogg'))
+ if(deity_name)
+ deities_that_listened += deity_name
+
+///An action to stop hearing prayers on command. Doesn't do a whole lot without the associated component
+/datum/action/innate/listen_prayers
+ name = "Listen Prayers"
+ button_icon = 'icons/hud/actions.dmi'
+ button_icon_state = "pray"
+ desc = "Allows you to eavesdrop on prayers from the world around you."
+ active = TRUE
+
+/datum/action/innate/listen_prayers/Destroy()
+ REMOVE_TRAIT(owner, TRAIT_DONT_HEAR_PRAYERS, ACTION_TRAIT)
+ return ..()
+
+/datum/action/innate/listen_prayers/is_action_active(atom/movable/screen/movable/action_button/current_button)
+ return !HAS_TRAIT_FROM(owner, TRAIT_DONT_HEAR_PRAYERS, ACTION_TRAIT)
+
+/datum/action/innate/listen_prayers/Activate()
+ active = TRUE
+ REMOVE_TRAIT(owner, TRAIT_DONT_HEAR_PRAYERS, ACTION_TRAIT)
+ to_chat(owner, span_green("You are ready to listen to prayers once again."))
+ build_all_button_icons(UPDATE_BUTTON_BACKGROUND)
+
+/datum/action/innate/listen_prayers/Deactivate()
+ active = FALSE
+ ADD_TRAIT(owner, TRAIT_DONT_HEAR_PRAYERS, ACTION_TRAIT)
+ to_chat(owner, span_green("You stop listening to prayers."))
+ build_all_button_icons(UPDATE_BUTTON_BACKGROUND)
diff --git a/code/modules/admin/verbs/pray.dm b/code/modules/admin/verbs/pray.dm
index aefd988aa74..c0955dec70b 100644
--- a/code/modules/admin/verbs/pray.dm
+++ b/code/modules/admin/verbs/pray.dm
@@ -16,31 +16,33 @@
if(src.client.handle_spam_prevention(message, MUTE_PRAY))
return
- var/mutable_appearance/cross = mutable_appearance('icons/obj/storage/book.dmi', "bible")
- var/font_color = "purple"
- var/prayer_type = "PRAYER"
- var/deity
+
+ var/prayer_type = DEFAULT_PRAYER
+ var/list/deities = list()
if(src.job == JOB_CHAPLAIN)
- cross.icon_state = "kingyellow"
- font_color = "blue"
- prayer_type = "CHAPLAIN PRAYER"
+ prayer_type = CHAPLAIN_PRAYER
if(GLOB.deity)
- deity = GLOB.deity
+ deities += GLOB.deity
else if(IS_CULTIST(src))
- cross.icon_state = "tome"
- font_color = "red"
- prayer_type = "CULTIST PRAYER"
- deity = "Nar'Sie"
- else if(isliving(src))
- var/mob/living/L = src
- if(HAS_TRAIT(L, TRAIT_SPIRITUAL))
- cross.icon_state = "holylight"
- font_color = "blue"
- prayer_type = "SPIRITUAL PRAYER"
+ prayer_type = CULT_PRAYER
+ deities += "Nar'Sie"
+ else if(IS_HERETIC_OR_MONSTER(src))
+ prayer_type = HERETIC_PRAYER
+ deities += "the Mansus"
+ else if(HAS_TRAIT(src, TRAIT_SPIRITUAL))
+ prayer_type = SPIRITUAL_PRAYER
+ else if(HAS_TRAIT(src, TRAIT_EVIL))
+ prayer_type = EVIL_PRAYER
+
+ var/mutable_appearance/cross = mutable_appearance('icons/obj/storage/book.dmi', GLOB.prayer_type_to_icon_state[prayer_type])
+
+ SEND_GLOBAL_SIGNAL(COMSIG_GLOB_SEND_PRAYER, src, message, prayer_type, cross, deities)
+
var/msg_tmp = message
GLOB.requests.pray(src.client, message, src.job == JOB_CHAPLAIN)
- message = span_adminnotice("[icon2html(cross, GLOB.admins)][prayer_type][deity ? " (to [deity])" : ""]: [ADMIN_FULLMONTY(src)] [ADMIN_SC(src)]: [span_linkify(message)]")
+ message = span_adminnotice("[icon2html(cross, GLOB.admins)][prayer_type][length(deities) ? " (to [english_list(deities)])" : ""]: [ADMIN_FULLMONTY(src)] [ADMIN_SC(src)]: [span_linkify(message)]")
+ message = custom_boxed_message(GLOB.prayer_type_to_message_box[prayer_type], message)
for(var/client/C in GLOB.admins)
if(get_chat_toggles(C) & CHAT_PRAYER)
to_chat(C, message, type = MESSAGE_TYPE_PRAYER, confidential = TRUE)
diff --git a/code/modules/antagonists/santa/santa.dm b/code/modules/antagonists/santa/santa.dm
index f3e1cbd02b9..8f6765604e0 100644
--- a/code/modules/antagonists/santa/santa.dm
+++ b/code/modules/antagonists/santa/santa.dm
@@ -4,6 +4,7 @@
show_name_in_check_antagonists = TRUE
show_to_ghosts = TRUE
suicide_cry = "FOR CHRISTMAS!!"
+ var/datum/component/listen_prayers/santa_prayers
/datum/antagonist/santa/on_gain()
. = ..()
@@ -12,6 +13,30 @@
owner.add_traits(list(TRAIT_CANNOT_OPEN_PRESENTS, TRAIT_PRESENT_VISION), TRAIT_SANTA)
+ santa_prayers = owner.AddComponent(/datum/component/listen_prayers, CALLBACK(src, PROC_REF(check_if_santa_prayer)), "Santa Claus", "Allows you to listen for prayers that mention you or Christmas.")
+
+/datum/antagonist/santa/proc/check_if_santa_prayer(list/arguments)
+ SIGNAL_HANDLER
+ var/message = arguments[ARG_PRAYER_MSG]
+ var/mob/boy_girl = arguments[ARG_PRAYING_MOB]
+ var/regex/santa_regex = regex("(santa|claus|christmas|xmas)", "i")
+ if(!santa_regex.Find(message) && (!prob(60) || !findtext(message, "satan")))
+ return FALSE //The message doesn't mention us (or satan, cuz the names are so similar, accidents may happen)
+ var/is_good_boy_girl = !boy_girl.is_antag() && !HAS_TRAIT(boy_girl, TRAIT_EVIL)
+ arguments[ARG_PRAYER_TYPE] = is_good_boy_girl ? SANTA_PRAYER : SANTA_NAUGHTY_PRAYER
+ arguments[ARG_PRAYER_SYMBOL] = icon('icons/obj/storage/wrapping.dmi', "giftdeliverypackage4")
+ return TRUE
+
+/datum/antagonist/santa/on_removal()
+ if(!owner)
+ return ..()
+ owner.remove_traits(list(TRAIT_CANNOT_OPEN_PRESENTS, TRAIT_PRESENT_VISION), TRAIT_SANTA)
+ QDEL_NULL(santa_prayers)
+ if(owner.current)
+ var/datum/action/cooldown/spell/teleport/area_teleport/wizard/santa/teleport = locate() in owner.current.actions
+ qdel(teleport)
+ return ..()
+
/datum/antagonist/santa/greet()
. = ..()
to_chat(owner, span_bolddanger("Your objective is to bring joy to the people on this station. You have a magical bag, which generates presents as long as you have it! You can examine the presents to take a peek inside, to make sure that you give the right gift to the right person."))
diff --git a/code/modules/clothing/head/tinfoilhat.dm b/code/modules/clothing/head/tinfoilhat.dm
index 18cd6fd931a..5d5a6cc25bc 100644
--- a/code/modules/clothing/head/tinfoilhat.dm
+++ b/code/modules/clothing/head/tinfoilhat.dm
@@ -6,6 +6,7 @@
armor_type = /datum/armor/costume_foilhat
equip_delay_other = 14 SECONDS
clothing_flags = ANTI_TINFOIL_MANEUVER
+ clothing_traits = list(TRAIT_DONT_HEAR_PRAYERS) //stops you from hearing prayers as well, yes
var/datum/brain_trauma/mild/phobia/conspiracies/paranoia
var/warped = FALSE
interaction_flags_mouse_drop = NEED_HANDS
diff --git a/icons/hud/actions.dmi b/icons/hud/actions.dmi
index e8385331596..d95481b1ef3 100644
Binary files a/icons/hud/actions.dmi and b/icons/hud/actions.dmi differ
diff --git a/strings/sillytips.txt b/strings/sillytips.txt
index fdf740f07a6..71616ba148c 100644
--- a/strings/sillytips.txt
+++ b/strings/sillytips.txt
@@ -40,3 +40,4 @@ When a round ends nearly everything about it is lost forever, leave your salt be
You can light a match with the heel of your boot. ...What's that? Friction matches went out of style in the 19th century? That's nonsense!
You can win a pulse rifle from the arcade machine. Honest.
Your sprite represents your hitbox, so that afro makes you easier to kill. The sacrifices we make for style.
+Santa Claus will listen to your prayers if you call him.
diff --git a/tgstation.dme b/tgstation.dme
index 7dd74e24b78..afa90adb47e 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1260,6 +1260,7 @@
#include "code\datums\components\limb_applicable.dm"
#include "code\datums\components\ling_decoy_brain.dm"
#include "code\datums\components\listen_and_repeat.dm"
+#include "code\datums\components\listen_prayers.dm"
#include "code\datums\components\lock_on_cursor.dm"
#include "code\datums\components\lockable_storage.dm"
#include "code\datums\components\magnet.dm"