From fe4f84703cab6339e4c7297944f0376a4788ea2f Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Wed, 27 May 2026 11:38:57 -0400 Subject: [PATCH] Morale Modifying Skills (#22508) This PR adds a new Everyday skill called Ministry, which is targeted both for the Chaplain role, as well as for characters with Theological training in general. It provides a character with a unique action called "Offer Blessing", appearing in the action bar on the top-left corner of the screen. When used on an adjacent character, it offers a series of RP prompts for the user to write a blessing of their choice to said character. If the blessing is accepted and received, the recipient gains a small Morale Modifier that scales with the user's Ministry skill ranks. The morale modifier is also increased if the two characters involved share a religion in common. image image image image image There is also now a similar skill for the combat category, called Leadership. image image I have fully tested this PR to its completion. ### Asset Licenses The following assets that **have not** been created by myself are included in this PR: | Path | Original Author | License | | icons/hud/action_buttons/skills.dmi | gabrielgabey (Discord) | CC-BY-SA-3.0 | --- aurorastation.dme | 1 + code/__DEFINES/components.dm | 1 + code/__DEFINES/dcs/signals.dm | 10 ++ code/_onclick/hud/action.dm | 5 + .../components/psionics/psiblock_drugs.dm | 31 ++++ .../combat/leadership_skill_component.dm | 163 +++++++++++++++++- .../service/ministry_skill_component.dm | 132 ++++++++++++++ code/datums/skills/combat/combat.dm | 28 ++- code/datums/skills/everyday/service.dm | 15 ++ code/modules/mob/login.dm | 2 + .../subtypes/augment/augments/mind_blanker.dm | 41 ++++- .../changelogs/hellfirejag-ministry-skill.yml | 5 + icons/hud/action_buttons/skills.dmi | Bin 0 -> 6111 bytes 13 files changed, 420 insertions(+), 14 deletions(-) create mode 100644 code/datums/components/skills/service/ministry_skill_component.dm create mode 100644 html/changelogs/hellfirejag-ministry-skill.yml create mode 100644 icons/hud/action_buttons/skills.dmi diff --git a/aurorastation.dme b/aurorastation.dme index 870caeb77df..89d7a4ec88a 100644 --- a/aurorastation.dme +++ b/aurorastation.dme @@ -547,6 +547,7 @@ #include "code\datums\components\skills\service\cooking_skill_component.dm" #include "code\datums\components\skills\service\entertaining_skill_component.dm" #include "code\datums\components\skills\service\gardening_skill_component.dm" +#include "code\datums\components\skills\service\ministry_skill_component.dm" #include "code\datums\components\synthetic_endoskeleton\synthetic_endoskeleton.dm" #include "code\datums\components\tools\tool_quality.dm" #include "code\datums\components\turf_click\turf_hand.dm" diff --git a/code/__DEFINES/components.dm b/code/__DEFINES/components.dm index bf4a98969b9..033a7025362 100644 --- a/code/__DEFINES/components.dm +++ b/code/__DEFINES/components.dm @@ -29,6 +29,7 @@ #define TOOL_COMPONENT /datum/component/tool_quality_container #define CAROUSING_SKILL_COMPONENT /datum/component/skill/carousing #define TENACITY_SKILL_COMPONENT /datum/component/skill/tenacity +#define MINISTRY_SKILL_COMPONENT /datum/component/skill/ministry /** * Trinary-Boolean helper that either returns null, or the skill level of a given skill component(which can be zero). diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 6573f2eec63..3db45dab5e9 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -41,6 +41,8 @@ #define COMSIG_MOB_ZONE_SEL_CHANGE "mob_zone_sel_change" ///from base of /mob/Login(): () #define COMSIG_MOB_LOGIN "mob_login" +/// Sent at the end of /mob/LateLogin() after the client is fully initialized. +#define COMSIG_MOB_AFTER_LOGIN "mob_after_login" ///from base of /mob/Logout(): () #define COMSIG_MOB_LOGOUT "mob_logout" /// from mob/get_status_tab_items(): (list/items) @@ -130,3 +132,11 @@ // Surgery Signals /// Signal raised against the surgeon attempting to perform a surgery to query their components for any rate mods. #define COMSIG_GET_SURGERY_SUCCESS_MODIFIERS "get_surgery_success_modifiers" + +/// Signal raised against a character attempting to give a ministry moodlet to someone. +#define COMSIG_GET_MINISTRY_MODIFIERS "get_ministry_modifiers" +/// Signal raised against the recipient of the Ministry skill action, allowing effects to interrupt the received moodlet. +#define COMSIG_RECEIVE_MINISTRY_MODIFIERS "receive_ministry_modifiers" + +/// Signal raised against a character attempting to deliver a speech. +#define COMSIG_GET_LEADERSHIP_MODIFIERS "get_leadership_modifiers" diff --git a/code/_onclick/hud/action.dm b/code/_onclick/hud/action.dm index 928e2889317..69e323792aa 100644 --- a/code/_onclick/hud/action.dm +++ b/code/_onclick/hud/action.dm @@ -3,6 +3,7 @@ #define AB_INNATE 3 #define AB_GENERIC 4 #define AB_ITEM_USE_ICON 5 +#define AB_CALL_SELF 6 #define AB_CHECK_RESTRAINED 1 #define AB_CHECK_STUNNED 2 @@ -90,6 +91,9 @@ if(AB_GENERIC) if(target && procname) call(target,procname)(usr) + if(AB_CALL_SELF) + if (procname) + call(src, procname)() return /datum/action/proc/Activate() @@ -337,6 +341,7 @@ #undef AB_INNATE #undef AB_GENERIC #undef AB_ITEM_USE_ICON +#undef AB_CALL_SELF #undef AB_CHECK_RESTRAINED #undef AB_CHECK_STUNNED diff --git a/code/datums/components/psionics/psiblock_drugs.dm b/code/datums/components/psionics/psiblock_drugs.dm index d8bfadcc99c..5d94738764b 100644 --- a/code/datums/components/psionics/psiblock_drugs.dm +++ b/code/datums/components/psionics/psiblock_drugs.dm @@ -26,6 +26,9 @@ RegisterSignal(parent, COMSIG_PSI_CHECK_SENSITIVITY, PROC_REF(modify_sensitivity), override = TRUE) RegisterSignal(parent, COMSIG_PSI_MIND_POWER, PROC_REF(cancel_power), override = TRUE) + RegisterSignal(parent, COMSIG_GET_MINISTRY_MODIFIERS, PROC_REF(modify_ministry_empathy), override = TRUE) + RegisterSignal(parent, COMSIG_RECEIVE_MINISTRY_MODIFIERS, PROC_REF(modify_ministry_receiving), override = TRUE) + RegisterSignal(parent, COMSIG_GET_LEADERSHIP_MODIFIERS, PROC_REF(modify_leadership_empathy), override = TRUE) /datum/component/timed_life/psiblock_drugs/Destroy() owner = null @@ -39,6 +42,9 @@ UnregisterSignal(parent, COMSIG_PSI_CHECK_SENSITIVITY) UnregisterSignal(parent, COMSIG_PSI_MIND_POWER) + UnregisterSignal(parent, COMSIG_GET_MINISTRY_MODIFIERS) + UnregisterSignal(parent, COMSIG_RECEIVE_MINISTRY_MODIFIERS) + UnregisterSignal(parent, COMSIG_GET_LEADERSHIP_MODIFIERS) return ..() /datum/component/timed_life/psiblock_drugs/proc/modify_sensitivity(parent, effective_sensitivity) @@ -51,6 +57,31 @@ if (prob(telepathy_cancel_probability)) *cancelled = TRUE +/datum/component/timed_life/psiblock_drugs/proc/modify_ministry_empathy(minister, ministree, moodlet_value) + SIGNAL_HANDLER + if (!(*moodlet_value)) + return + + to_chat(minister, SPAN_BAD("Why should you care how [ministree] feels?")) + *moodlet_value = 0 + +/datum/component/timed_life/psiblock_drugs/proc/modify_ministry_receiving(ministree, minister, moodlet_value) + SIGNAL_HANDLER + if (!(*moodlet_value)) + return + + to_chat(ministree, SPAN_BAD("You feel nothing from [minister]'s words.")) + *moodlet_value = 0 + +/datum/component/timed_life/psiblock_drugs/proc/modify_leadership_empathy(leader, moodlet_value) + SIGNAL_HANDLER + if (!(*moodlet_value)) + return + + to_chat(leader, SPAN_BAD("Why should you care about how others feel?")) + *moodlet_value = 0 + + /datum/component/timed_life/psiblock_drugs/process(seconds_per_tick) . = ..() diff --git a/code/datums/components/skills/combat/leadership_skill_component.dm b/code/datums/components/skills/combat/leadership_skill_component.dm index e995b06e520..6ec370d09c6 100644 --- a/code/datums/components/skills/combat/leadership_skill_component.dm +++ b/code/datums/components/skills/combat/leadership_skill_component.dm @@ -1,6 +1,165 @@ +#define GET_SPEECH_TYPE(owner) alert(owner.client, "What type of chat message would you like to use for your inspirational speech?", "Say Type", "Say", "Whisper", "Emote") +#define GET_SPEECH_TEXT(owner) tgui_input_text(owner, "Write what you wish to say for your speech.", "Deliver Speech") + +/datum/moodlet/leadership + moodlet_descriptor = SPAN_GOOD("Listened to an encouraging speech") + initial_descriptor = SPAN_GOOD("You have received a morale modifier from hearing words of encouragement.") + duration = 15 MINUTES + +/datum/action/leadership + name = "Deliver Speech" + action_type = 6 + procname = "queue_click" + button_icon = 'icons/hud/action_buttons/skills.dmi' + button_icon_state = "leadership" + +/datum/action/leadership/proc/queue_click() + if (!owner) + return + + to_chat(owner, SPAN_NOTICE("You prepare yourself to give a speech. Left click on yourself to target those who can hear you in an area. Left click on another person to instead give them directed words of encouragement. Targeting a single person will give a stronger morale modifier.")) + RegisterSignal(owner, COMSIG_MOB_CLICKON, PROC_REF(get_target), override = TRUE) + +/datum/action/leadership/proc/get_target(owner, atom/target, modifiers) + SIGNAL_HANDLER + // Both forms of deliver speech will immediately return control to the caller without blocking. + // Unfortunately StrongDMM is apparently incapable of reading that. + if (owner == target) + UNLINT(deliver_speech_area(owner)) + else + UNLINT(deliver_speech_target(owner, target)) + UnregisterSignal(owner, COMSIG_MOB_CLICKON) + return COMSIG_MOB_CANCEL_CLICKON + +/datum/action/leadership/proc/deliver_speech_area(mob/owner) + set waitfor = FALSE // Immediately return control to the caller. + if (!istype(owner)) + return + + owner.visible_message(SPAN_NOTICE("[owner] prepares to deliver a speech.")) + var/speech_text = GET_SPEECH_TEXT(owner) + if (!speech_text) + owner.visible_message(SPAN_NOTICE("[owner] has stopped speaking.")) + return + + owner.say(speech_text) + var/datum/component/skill/leadership/leadership_skill = owner.GetComponent(LEADERSHIP_SKILL_COMPONENT) + if (!leadership_skill) + return + + var/moodlet_value = leadership_skill.moodlet_value_per_rank * (leadership_skill.skill_level - 1) + SEND_SIGNAL(owner, COMSIG_GET_LEADERSHIP_MODIFIERS, &moodlet_value) + if (!moodlet_value) + return + + var/list/listening = get_hearers_in_view(world.view, owner) + if (!listening) + return + listening -= owner + + for (var/mob/player_mob in listening) + var/datum/component/morale/receiver_morale = player_mob.GetComponent(MORALE_COMPONENT) + if (!receiver_morale) + continue + + if (astype(receiver_morale.moodlets[/datum/moodlet/leadership], /datum/moodlet)?.get_morale_modifier() >= moodlet_value) + receiver_morale.load_moodlet(/datum/moodlet/leadership)?.refresh_moodlet() + continue // Don't overwrite stronger moodlets. + + var/datum/moodlet/speech = receiver_morale.load_moodlet(/datum/moodlet/leadership, moodlet_value) + speech.refresh_moodlet() + speech.moodlet_descriptor += " from [owner.name]." + +/datum/action/leadership/proc/deliver_speech_target(mob/owner, mob/target) + set waitfor = FALSE // Immediately return control to the caller. + if (!istype(owner) || !istype(target)) + return + + if (!target.client) + to_chat(owner, SPAN_NOTICE("[target] cannot hear your speech.")) + return + + owner.visible_message(SPAN_NOTICE("[owner] prepares to deliver a speech.")) + var/speech_type = GET_SPEECH_TYPE(owner) + var/speech_text = GET_SPEECH_TEXT(owner) + if (!speech_text) + owner.visible_message(SPAN_NOTICE("[owner] has stopped speaking.")) + return + + switch(speech_type) + if ("Say") + if (get_dist(owner, target) >= 7) + to_chat(owner, SPAN_NOTICE("You must be closer to [target] to give them words of encouragement")) + return + owner.say(speech_text) + if ("Whisper") + if (get_dist(owner, target) >= 2) + to_chat(owner, SPAN_NOTICE("You must be adjacent to [target] to whisper them words of encouragement")) + return + owner.whisper(speech_text) + if ("Emote") + if (get_dist(owner, target) >= 7) + to_chat(owner, SPAN_NOTICE("You must be closer to [target] to give them words of encouragement")) + owner.emote(speech_text) + + var/datum/component/skill/leadership/leadership_skill = owner.GetComponent(LEADERSHIP_SKILL_COMPONENT) + if (!leadership_skill) + return + + var/moodlet_value = leadership_skill.moodlet_value_per_rank * (leadership_skill.skill_level - 1) * leadership_skill.single_target_bonus_mod + SEND_SIGNAL(owner, COMSIG_GET_LEADERSHIP_MODIFIERS, &moodlet_value) + if (!moodlet_value) + return + + var/datum/component/morale/target_morale = target.GetComponent(MORALE_COMPONENT) + if (!target_morale) + return + + if (astype(target_morale.moodlets[/datum/moodlet/leadership], /datum/moodlet)?.get_morale_modifier() >= moodlet_value) + target_morale.load_moodlet(/datum/moodlet/leadership)?.refresh_moodlet() + return // Don't overwrite stronger moodlets. + + var/datum/moodlet/speech = target_morale.load_moodlet(/datum/moodlet/leadership, moodlet_value) + speech.refresh_moodlet() + speech.moodlet_descriptor += " from [owner.name]." + /** - * Component used for the Leadership Skill. This is not currently implemented, and will be handled in a separate PR to avoid scope creep. - * The way this will be intended to work is that having the component grants access to an "Inspire" action. Activating it will prompt the user to select a person, then prompt them to "Say something inspiring!". + * Component used for the Leadership Skill. + * The way this works is that having the component grants access to an "Inspire" action. Activating it will prompt the user to select a person, then prompt them to "Say something inspiring!". * If the target can "Hear" the inspirational speech, they gain a morale bonus which scales with the actor's Leadership Skill. */ /datum/component/skill/leadership + /// The action icon stored for this ability. + var/datum/action/leadership/leadership_action + + /// The value of the moodlet provided by this ability. + var/moodlet_value_per_rank = 10.0 / 3.0 + + /// Moodlet value multiplier for single target speeches. + var/single_target_bonus_mod = 1.5 + +/datum/component/skill/leadership/Initialize(level) + . = ..() + if (!parent) + return + + leadership_action = new /datum/action/leadership() + leadership_action.SetTarget(leadership_action) + leadership_action.Grant(parent) + + RegisterSignal(parent, COMSIG_MOB_AFTER_LOGIN, PROC_REF(setup_action_button), override = TRUE) + +/datum/component/skill/leadership/Destroy(force) + if (!parent) + return ..() + + UnregisterSignal(parent, COMSIG_MOB_AFTER_LOGIN) + leadership_action?.Remove(parent) + QDEL_NULL(leadership_action) + return ..() + +/datum/component/skill/leadership/proc/setup_action_button() + astype(parent, /mob)?.update_action_buttons() + +#undef GET_SPEECH_TYPE +#undef GET_SPEECH_TEXT diff --git a/code/datums/components/skills/service/ministry_skill_component.dm b/code/datums/components/skills/service/ministry_skill_component.dm new file mode 100644 index 00000000000..1aeea1b2729 --- /dev/null +++ b/code/datums/components/skills/service/ministry_skill_component.dm @@ -0,0 +1,132 @@ +/// Moodlet used for the Ministry skill. +/datum/moodlet/ministry_blessing + moodlet_descriptor = SPAN_GOOD("Received a blessing.") + initial_descriptor = SPAN_GOOD("You have received a morale modifier from hearing a blessing.") + +/// Action bar object used by the Ministry skill. +/datum/action/ministry + name = "Offer Blessing" + action_type = 6 + procname = "queue_click" + button_icon = 'icons/hud/action_buttons/skills.dmi' + button_icon_state = "ministry" + +/datum/action/ministry/proc/queue_click() + if (!owner) + return + + to_chat(owner, SPAN_NOTICE("You prepare yourself to offer a blessing to another. Left click on an adjacent character to offer them it.")) + RegisterSignal(owner, COMSIG_MOB_CLICKON, PROC_REF(get_target), override = TRUE) + +/datum/action/ministry/Destroy() + if (!owner) + return ..() + + UnregisterSignal(owner, COMSIG_MOB_CLICKON) + return ..() + +/datum/action/ministry/proc/get_target(owner, atom/target, modifiers) + SIGNAL_HANDLER + if (owner == target) + to_chat(owner, SPAN_NOTICE("You cannot offer a blessing to yourself.")) + return COMSIG_MOB_CANCEL_CLICKON + + if (!astype(target, /mob)?.client) + to_chat(owner, SPAN_NOTICE("[target] cannot receive a blessing.")) + return COMSIG_MOB_CANCEL_CLICKON + + if (get_dist(owner, target) >= 2) + to_chat(owner, SPAN_NOTICE("You must be adjacent to [target] to offer them a blessing.")) + return COMSIG_MOB_CANCEL_CLICKON + + // StrongDMM for whatever ungodly reason can't tell that this proc won't block the caller. + UNLINT(try_give_blessing(target)) + UnregisterSignal(owner, COMSIG_MOB_CLICKON) + return COMSIG_MOB_CANCEL_CLICKON + +/datum/action/ministry/proc/try_give_blessing(mob/target) + set waitfor = FALSE + switch(alert(target.client, "Would you like to accept a blessing from [owner]? You will need to remain close to them while they speak it", "Accept Blessing", "Yes", "No")) + if ("Yes") + var/blessing_type = alert(owner.client, "What type of chat message would you like to use for your blessing?", "Say Type", "Say", "Whisper", "Emote") + var/blessing_text = tgui_input_text(owner, "Write what you wish to say as a blessing for [target].", "Offer Blessing") + if (!blessing_text) + to_chat(owner, SPAN_NOTICE("You have stopped speaking.")) + to_chat(target, SPAN_NOTICE("[owner] has stopped speaking.")) + return + + if (get_dist(owner, target) >= 2) + to_chat(owner, SPAN_NOTICE("[target] is too far away to receive a blessing.")) + to_chat(target, SPAN_NOTICE("[owner] tried to offer a blessing, but you were too far away to receive it.")) + return + + switch(blessing_type) + if ("Say") + owner.say(blessing_text) + if ("Whisper") + owner.whisper(blessing_text) + if ("Emote") + owner.emote(blessing_text) + + var/datum/component/skill/ministry/ministry_skill = owner.GetComponent(MINISTRY_SKILL_COMPONENT) + if (!ministry_skill) + return + + var/datum/component/morale/target_morale = target.GetComponent(MORALE_COMPONENT) + if (!target_morale) + return + + var/moodlet_value = ministry_skill.moodlet_value_per_rank * (ministry_skill.skill_level - 1) + if (astype(owner, /mob/living/carbon/human)?.religion == astype(target, /mob/living/carbon/human)?.religion) + moodlet_value *= ministry_skill.same_religion_bonus_mod + + + SEND_SIGNAL(owner, COMSIG_GET_MINISTRY_MODIFIERS, target, &moodlet_value) + SEND_SIGNAL(target, COMSIG_RECEIVE_MINISTRY_MODIFIERS, owner, &moodlet_value) + if (!moodlet_value) + return + + if (astype(target_morale.moodlets[/datum/moodlet/ministry_blessing], /datum/moodlet)?.get_morale_modifier() >= moodlet_value) + target_morale.load_moodlet(/datum/moodlet/ministry_blessing)?.refresh_moodlet() + return // Don't overwrite stronger moodlets. + + var/datum/moodlet/blessing = target_morale.load_moodlet(/datum/moodlet/ministry_blessing, moodlet_value) + blessing.refresh_moodlet() + blessing.moodlet_descriptor += " from [owner.name]." + + if ("No") + to_chat(owner, SPAN_NOTICE("[target] has refused your blessing.")) + return + +/datum/component/skill/ministry + /// The action icon stored for this ability. + var/datum/action/ministry/ministry_action + + /// The value of the moodlet provided by this ability. + var/moodlet_value_per_rank = 10.0 / 3.0 + + /// The moodlet value multiplier for a target having the same religion as the performer. + var/same_religion_bonus_mod = 1.5 + +/datum/component/skill/ministry/Initialize(level) + . = ..() + if (!parent) + return + + ministry_action = new /datum/action/ministry() + ministry_action.SetTarget(ministry_action) + ministry_action.Grant(parent) + + RegisterSignal(parent, COMSIG_MOB_AFTER_LOGIN, PROC_REF(setup_action_button), override = TRUE) + +/datum/component/skill/ministry/Destroy(force) + if (!parent) + return ..() + + UnregisterSignal(parent, COMSIG_MOB_AFTER_LOGIN) + ministry_action?.Remove(parent) + QDEL_NULL(ministry_action) + return ..() + +/datum/component/skill/ministry/proc/setup_action_button() + astype(parent, /mob)?.update_action_buttons() diff --git a/code/datums/skills/combat/combat.dm b/code/datums/skills/combat/combat.dm index 128b3648cbc..9c8c2712880 100644 --- a/code/datums/skills/combat/combat.dm +++ b/code/datums/skills/combat/combat.dm @@ -69,14 +69,26 @@ + " - Firearms you shoot have a 30 degree spread-angle decrease, making them somewhat more accurate. This generally doesn't apply to weapons fired in semi-auto, but will make burst and automatic fire more manageable." \ ) -// Temporarily commented because this is a little too complicated to catch in the initial release. -// /singleton/skill/leadership -// name = "Leadership" -// description = "Leadership skill grants access to a unique 'Inspire' action, which lets you say something inspiring and give the target a positive moodlet." -// category = /singleton/skill_category/combat -// subcategory = SKILL_SUBCATEGORY_SUPPORT -// required = TRUE -// component_type = LEADERSHIP_SKILL_COMPONENT +/singleton/skill/leadership + name = "Leadership" + description = "Leadership represents a characters skill with inspiring others. Having ranks in this skill grants access to the \"Deliver Speech\" action. Which can be used to give a morale modifier either in an area, or to a single person. " \ + + "Additional ranks increase the morale modifier provided." + maximum_level = SKILL_LEVEL_PROFESSIONAL + category = /singleton/skill_category/combat + subcategory = SKILL_SUBCATEGORY_SUPPORT + component_type = LEADERSHIP_SKILL_COMPONENT + skill_level_descriptions = alist( + SKILL_LEVEL_UNFAMILIAR = "You have no skill with motivational speeches.", + SKILL_LEVEL_FAMILIAR = "You gain the \"Deliver Speech\" ability, which provides a small morale bonus.", + SKILL_LEVEL_TRAINED = "You gain the \"Deliver Speech\" ability, which provides a modest morale bonus.", + SKILL_LEVEL_PROFESSIONAL = "You gain the \"Deliver Speech\" ability, which provides a moderate morale bonus." + ) + skill_cost_map = alist( + SKILL_LEVEL_UNFAMILIAR = 0, + SKILL_LEVEL_FAMILIAR = 1, + SKILL_LEVEL_TRAINED = 2, + SKILL_LEVEL_PROFESSIONAL = 4 + ) /singleton/skill/tenacity name = "Tenacity" diff --git a/code/datums/skills/everyday/service.dm b/code/datums/skills/everyday/service.dm index 632310d886e..d67d27518c4 100644 --- a/code/datums/skills/everyday/service.dm +++ b/code/datums/skills/everyday/service.dm @@ -57,3 +57,18 @@ SKILL_LEVEL_TRAINED = "Your liver is 10% more effective at filtering drugs and alcohol.", SKILL_LEVEL_PROFESSIONAL = "Your liver is 15% more effective at filtering drugs and alcohol." ) + +/singleton/skill/ministry + name = "Ministry" + description = "Represents a characters training in religious counseling. Having ranks in this skill unlocks the \"Offer Blessing\" ability, which offers a small morale modifier to an adjacent character. " \ + + "Additional ranks increase the morale modifier, which is empowered further if the recipient shares a religion with you." + maximum_level = SKILL_LEVEL_PROFESSIONAL + category = /singleton/skill_category/everyday + subcategory = SKILL_SUBCATEGORY_SERVICE + component_type = MINISTRY_SKILL_COMPONENT + skill_level_descriptions = alist( + SKILL_LEVEL_UNFAMILIAR = "You have no training in religious counseling.", + SKILL_LEVEL_FAMILIAR = "You gain the \"Offer Blessing\" ability, which provides a small morale bonus to a recipient.", + SKILL_LEVEL_TRAINED = "You gain the \"Offer Blessing\" ability, which provides a modest morale bonus to a recipient.", + SKILL_LEVEL_PROFESSIONAL = "You gain the \"Offer Blessing\" ability, which provides a moderate morale bonus to a recipient." + ) diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm index 7c622122eb4..1c03b4b14f7 100644 --- a/code/modules/mob/login.dm +++ b/code/modules/mob/login.dm @@ -119,3 +119,5 @@ for(var/atom/movable/screen/movable/spell_master/spell_master in spell_masters) spell_master.toggle_open(1) client.screen -= spell_master + + SEND_SIGNAL(src, COMSIG_MOB_AFTER_LOGIN) diff --git a/code/modules/organs/subtypes/augment/augments/mind_blanker.dm b/code/modules/organs/subtypes/augment/augments/mind_blanker.dm index de0a2c4dd14..d431215c6fb 100644 --- a/code/modules/organs/subtypes/augment/augments/mind_blanker.dm +++ b/code/modules/organs/subtypes/augment/augments/mind_blanker.dm @@ -14,6 +14,9 @@ RegisterSignal(owner, COMSIG_PSI_MIND_POWER, PROC_REF(cancel_power), override = TRUE) RegisterSignal(owner, COMSIG_PSI_CHECK_SENSITIVITY, PROC_REF(modify_sensitivity), override = TRUE) + RegisterSignal(owner, COMSIG_GET_MINISTRY_MODIFIERS, PROC_REF(modify_ministry_empathy), override = TRUE) + RegisterSignal(owner, COMSIG_RECEIVE_MINISTRY_MODIFIERS, PROC_REF(modify_ministry_receiving), override = TRUE) + RegisterSignal(owner, COMSIG_GET_LEADERSHIP_MODIFIERS, PROC_REF(modify_leadership_empathy), override = TRUE) /obj/item/organ/internal/augment/bioaug/mind_blanker/replaced() . = ..() @@ -22,6 +25,9 @@ RegisterSignal(owner, COMSIG_PSI_MIND_POWER, PROC_REF(cancel_power), override = TRUE) RegisterSignal(owner, COMSIG_PSI_CHECK_SENSITIVITY, PROC_REF(modify_sensitivity), override = TRUE) + RegisterSignal(owner, COMSIG_GET_MINISTRY_MODIFIERS, PROC_REF(modify_ministry_empathy), override = TRUE) + RegisterSignal(owner, COMSIG_RECEIVE_MINISTRY_MODIFIERS, PROC_REF(modify_ministry_receiving), override = TRUE) + RegisterSignal(owner, COMSIG_GET_LEADERSHIP_MODIFIERS, PROC_REF(modify_leadership_empathy), override = TRUE) /obj/item/organ/internal/augment/bioaug/mind_blanker/removed() if(!owner) @@ -29,9 +35,12 @@ UnregisterSignal(owner, COMSIG_PSI_MIND_POWER) UnregisterSignal(owner, COMSIG_PSI_CHECK_SENSITIVITY) + UnregisterSignal(owner, COMSIG_GET_MINISTRY_MODIFIERS) + UnregisterSignal(owner, COMSIG_RECEIVE_MINISTRY_MODIFIERS) + UnregisterSignal(owner, COMSIG_GET_LEADERSHIP_MODIFIERS) return ..() -/obj/item/organ/internal/augment/bioaug/mind_blanker/proc/cancel_power(var/implantee, var/caster, var/cancelled, var/cancel_return, var/wide_field) +/obj/item/organ/internal/augment/bioaug/mind_blanker/proc/cancel_power(implantee, caster, cancelled, cancel_return, wide_field) SIGNAL_HANDLER if(is_broken()) return @@ -42,13 +51,37 @@ to_chat(implantee, SPAN_DANGER("Your mind wriggles as it repulses an outside thought.")) -/obj/item/organ/internal/augment/bioaug/mind_blanker/proc/modify_sensitivity(var/implantee, var/effective_sensitivity) +/obj/item/organ/internal/augment/bioaug/mind_blanker/proc/modify_sensitivity(implantee, effective_sensitivity) SIGNAL_HANDLER if(is_broken()) return *effective_sensitivity += sensitivity_modifier +/obj/item/organ/internal/augment/bioaug/mind_blanker/proc/modify_ministry_empathy(minister, ministree, moodlet_value) + SIGNAL_HANDLER + if (!(*moodlet_value)) + return + + to_chat(minister, SPAN_BAD("Why should you care how [ministree] feels?")) + *moodlet_value = 0 + +/obj/item/organ/internal/augment/bioaug/mind_blanker/proc/modify_ministry_receiving(ministree, minister, moodlet_value) + SIGNAL_HANDLER + if (!(*moodlet_value)) + return + + to_chat(ministree, SPAN_BAD("You feel nothing from [minister]'s words.")) + *moodlet_value = 0 + +/obj/item/organ/internal/augment/bioaug/mind_blanker/proc/modify_leadership_empathy(leader, moodlet_value) + SIGNAL_HANDLER + if (!(*moodlet_value)) + return + + to_chat(leader, SPAN_BAD("Why should you care about how others feel?")) + *moodlet_value = 0 + /obj/item/organ/internal/augment/bioaug/mind_blanker_lethal name = "lethal mind blanker" desc = "A small, discrete organ attached near the base of the brainstem." \ @@ -82,7 +115,7 @@ UnregisterSignal(owner, COMSIG_PSI_MIND_POWER) UnregisterSignal(owner, COMSIG_PSI_CHECK_SENSITIVITY) -/obj/item/organ/internal/augment/bioaug/mind_blanker_lethal/proc/cancel_power_lethal(var/mob/living/carbon/human/implantee, var/caster, var/cancelled, var/cancel_return, var/wide_field) +/obj/item/organ/internal/augment/bioaug/mind_blanker_lethal/proc/cancel_power_lethal(mob/living/carbon/human/implantee, caster, cancelled, cancel_return, wide_field) SIGNAL_HANDLER if(is_broken()) return @@ -98,7 +131,7 @@ victim.confused += 20 *cancel_return = SPAN_DANGER("Agony lances through my brain as their mind clamps down upon me!") -/obj/item/organ/internal/augment/bioaug/mind_blanker_lethal/proc/modify_sensitivity(var/implantee, var/effective_sensitivity) +/obj/item/organ/internal/augment/bioaug/mind_blanker_lethal/proc/modify_sensitivity(implantee, effective_sensitivity) SIGNAL_HANDLER if(is_broken()) return diff --git a/html/changelogs/hellfirejag-ministry-skill.yml b/html/changelogs/hellfirejag-ministry-skill.yml new file mode 100644 index 00000000000..f27adbd9f03 --- /dev/null +++ b/html/changelogs/hellfirejag-ministry-skill.yml @@ -0,0 +1,5 @@ +author: Hellfirejag +delete-after: True +changes: + - rscadd: "Added a new skill called Ministry." + - rscadd: "Added a new skill called Leadership." diff --git a/icons/hud/action_buttons/skills.dmi b/icons/hud/action_buttons/skills.dmi new file mode 100644 index 0000000000000000000000000000000000000000..a897d9d22817efc4e2768b6edabc347f01908da0 GIT binary patch literal 6111 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 literal 0 HcmV?d00001