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"