From 17a73e2bac5deb78734fe9813cf11b82f95863b2 Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Sun, 26 Jul 2026 14:06:05 -0400 Subject: [PATCH] Psychic Damage Tweaks (#22878) Psychic damage apparently felt toothless, while also being pretty opaque to players on when it was effecting your skills. So this PR makes the effects of psychic damage harsher than the opposing bonuses from positive morale, while also adding new feedback messages to better indicate to a player what and when psychic damage penalties are occuring. I've also taken the time to fill out missing DMdocs comments for the morale component. --- .../components/morale/morale_component.dm | 100 +++++++++++++++--- .../hellfirejag-more-morale-tweaks.yml | 5 + 2 files changed, 93 insertions(+), 12 deletions(-) create mode 100644 html/changelogs/hellfirejag-more-morale-tweaks.yml diff --git a/code/datums/components/morale/morale_component.dm b/code/datums/components/morale/morale_component.dm index 19c34bfc8b3..264e9ac7fef 100644 --- a/code/datums/components/morale/morale_component.dm +++ b/code/datums/components/morale/morale_component.dm @@ -1,3 +1,13 @@ +/** + * This macro is absolutely required to handle recalculating the morale_ratio and updating UI effects, + * whenever any morale modifier is updated, added, or removed. + * + * Failing to use this macro whenever any private value changes causes the component + * to no longer provide mathematically correct results from its internal calculation. + * + * At the core of the morale component is a Hyperbolic Tangent function, which is calculated in advance so as to eliminate the need for + * expensive recalculating every time it is called to modify checks. + */ #define UPDATE_MORALE \ morale_ratio = ftanh(beta_value * (morale_points + phi_value)); \ morale_ui.icon_state = ((morale_ratio > -0.01 && morale_ratio < 0.01) ? "morale_hidden" : "morale" + "[round(morale_ratio * 4) + 5]"); \ @@ -103,7 +113,7 @@ * The maximum possible panic chance from negative morale point sums. * Since negative moodlets are unique to psychic damage, the effects of psychically induced panic are uniquely stronger than simply being unskilled. */ - var/panic_chance_ceiling = 10.0 + var/panic_chance_ceiling = 30.0 /// The maximum possible positive or negative contribution to surgery success chances from morale modifiers. var/surgery_success_contribution = 15.0 @@ -139,25 +149,39 @@ /// The maximum possible positive or negative (percentage as decimal) contribution to surgery speed from morale modifiers. var/surgery_speed_contribution = 0.1 + /** + * The increase in effective morale ratio for psychic damage. This makes it so that the psi-panic condition + * has a greater effect on penalizing skills than positive morale gives bonuses to them. + */ + var/psi_panic_effect_modifier = 3.0 + +/// Returns the current percentage effect of morale, expressed as a floating point number between -1.0 and 1.0 /datum/component/morale/proc/get_morale_ratio() return morale_ratio +/// Returns the current number of morale points (which is not linearly related to the effects of morale). /datum/component/morale/proc/get_morale_points() return morale_points +/// Adds (or removes) morale points to the component, then forces a recalculation of morale effects. /datum/component/morale/proc/add_morale_points(input) morale_points += input UPDATE_MORALE +/// Returns the "B" constant for the morale component's internal equation. /datum/component/morale/proc/get_beta_value() return beta_value +/// Sets the "B" constant for the morale component's internal equation, then forces a recalculation of morale effects. /datum/component/morale/proc/set_beta_value(input) beta_value = input + UPDATE_MORALE +/// Returns the "C" constant for the morale component's internal equation. /datum/component/morale/proc/get_phi_value() return phi_value +/// Sets the "C" constant for the morale component's internal equation, then forces a recalculation of morale effects. /datum/component/morale/proc/set_phi_value(input) phi_value = input UPDATE_MORALE @@ -229,6 +253,13 @@ UnregisterSignal(parent, COMSIG_PLANT_HARVESTER) return ..() +/** + * Morale components begin processing whenever they become the owner of any moodlets. + * The Morale component is solely responsible for handling the lifetime of its owned moodlets, + * since it must be responsible for maintaining the mathematical integrity of its internal equations. + * + * The component will also stop processing when it has no moodlets to handle, and start processing again once it gains any. + */ /datum/component/morale/process(seconds_per_tick) if (!length(moodlets)) return PROCESS_KILL @@ -260,15 +291,31 @@ Otherwise, morale effects are generally equivalent to "up to half a skill rank" for a large variety of numerical effects related to skills. */ +/datum/component/morale/proc/get_morale_hud(mob/living/carbon/human/user) + SIGNAL_HANDLER + if (QDELING(src)) + return // draw nothing. + + user.client?.screen |= morale_ui + +/// Slightly increases melee damage with positive morale, Psi-panic from negative morale reduces it. /datum/component/morale/proc/modify_hit_effect(owner, mob/living/target, obj/item/weapon, power, hit_zone) SIGNAL_HANDLER *power = *power * (1 + (melee_damage_contribution * morale_ratio)) +/// Increases gun accuracy with positive morale. Psi-panic from negative morale makes it much worse. /datum/component/morale/proc/handle_accuracy(mob/shooter, accuracy_decrease, dispersion_increase) SIGNAL_HANDLER - *accuracy_decrease = *accuracy_decrease - firearm_accuracy_contribution * morale_ratio - *dispersion_increase = *dispersion_increase - firearm_dispersion_contribution * morale_ratio + var/effective_morale = morale_ratio + if (effective_morale < 0) + effective_morale *= psi_panic_effect_modifier + if (prob(25)) // Don't spam it for every shot + to_chat(shooter, SPAN_WARNING("The pressure on your mind makes it hard to aim properly...")) + *accuracy_decrease = *accuracy_decrease - firearm_accuracy_contribution * effective_morale + *dispersion_increase = *dispersion_increase - firearm_dispersion_contribution * effective_morale + +/// Psi-panic from negative morale can cause a shooter to fail to disengage a gun's safety. This includes the automatic disengage when firing on harm intent. /datum/component/morale/proc/safety_fumble(mob/shooter, obj/item/gun/shoota, cancelled) SIGNAL_HANDLER // Up to 50% chance to fumble a safety when in a psionically-induced panic. @@ -280,30 +327,35 @@ SPAN_DANGER("\The [shooter] fumbles with \the [shoota]'s safety in a blind panic!"), SPAN_DANGER("You fumble with \the [shoota]'s safety in a blind panic!")) +/// Positive morale slightly improves unarmed hit/block chances. Psi-panic from negative morale reduces it instead. /datum/component/morale/proc/handle_harm_attack(mob/attacker, mob/defender, attacker_skill_level, miss_chance, rand_damage, block_chance) SIGNAL_HANDLER *attacker_skill_level = *attacker_skill_level + (unarmed_rank_contribution * morale_ratio) *miss_chance = *miss_chance - unarmed_chance_contribution * morale_ratio *block_chance = *block_chance - unarmed_chance_contribution * morale_ratio +/// Positive morale slightly improves unarmed hit/block chances. Psi-panic from negative morale reduces it instead. /datum/component/morale/proc/handle_harm_defend(mob/defender, mob/attacker, defender_skill_level, miss_chance, rand_damage, block_chance) SIGNAL_HANDLER *defender_skill_level = *defender_skill_level - (unarmed_rank_contribution * morale_ratio) *miss_chance = *miss_chance + unarmed_chance_contribution * morale_ratio *block_chance = *block_chance + unarmed_chance_contribution * morale_ratio +/// Positive morale slightly improves unarmed hit/block chances. Psi-panic from negative morale reduces it instead. /datum/component/morale/proc/handle_disarm_attack(mob/attacker, mob/defender, attacker_skill_level, disarm_cost, push_chance, disarm_chance) SIGNAL_HANDLER *attacker_skill_level = *attacker_skill_level + (unarmed_rank_contribution * morale_ratio) *push_chance = *push_chance - unarmed_chance_contribution * morale_ratio *disarm_chance = *disarm_chance - unarmed_chance_contribution * morale_ratio +/// Positive morale slightly improves unarmed hit/block chances. Psi-panic from negative morale reduces it instead. /datum/component/morale/proc/handle_disarm_defend(mob/defender, mob/attacker, defender_skill_level, disarm_cost, push_chance, disarm_chance) SIGNAL_HANDLER *defender_skill_level = *defender_skill_level + (unarmed_rank_contribution * morale_ratio) *push_chance = *push_chance + unarmed_chance_contribution * morale_ratio *disarm_chance = *disarm_chance + unarmed_chance_contribution * morale_ratio +/// Positive morale improves mech handling characteristics, negative morale both worsens them, and gives a chance to scramble movement inputs. /datum/component/morale/proc/handle_user_move(mob/living/user, direction, delay_modifier) SIGNAL_HANDLER if (parent != user) @@ -318,8 +370,15 @@ if (direction == NORTH) return - *delay_modifier = *delay_modifier - mech_move_delay_contribution * morale_ratio + var/effective_morale = morale_ratio + if (effective_morale < 0) + effective_morale *= psi_panic_effect_modifier + if (prob(5)) // Don't spam it on every move + to_chat(user, SPAN_WARNING("The pressure on your mind makes it hard to focus on piloting the mech...")) + *delay_modifier = *delay_modifier - mech_move_delay_contribution * effective_morale + +/// Positive morale improves mech handling characteristics, negative morale both worsens them, and gives a chance to scramble movement inputs. /datum/component/morale/proc/handle_user_strafe(mob/living/user, direction, delay_modifier) SIGNAL_HANDLER if (parent != user) @@ -331,8 +390,14 @@ SPAN_DANGER("You fumble with your mech's controls in a blind panic!")) *direction = angle2dir(dir2angle(direction) + 180) - *delay_modifier = *delay_modifier - mech_move_delay_contribution * morale_ratio + var/effective_morale = morale_ratio + if (effective_morale < 0) + effective_morale *= psi_panic_effect_modifier + if (prob(5)) // Don't spam it on every move + to_chat(user, SPAN_WARNING("The pressure on your mind makes it hard to focus on piloting the mech...")) + *delay_modifier = *delay_modifier - mech_move_delay_contribution * effective_morale +/// The psi-panic condition from psychic damage makes it more difficult to power up a mech. No effects on this from positive morale. /datum/component/morale/proc/handle_mech_toggle_power(mob/user, cancelled, delay) SIGNAL_HANDLER if (parent != user || cancelled || morale_points >= 0) @@ -346,24 +411,35 @@ *delay = *delay - (5 * morale_ratio) SECONDS to_chat(user, SPAN_WARNING("The pressure on your mind causes you to stumble in searching for the power switch...")) +/// Positive morale makes surgery easier/faster. Psi-panic from megative morale makes it much harder. /datum/component/morale/proc/handle_surgery_modifiers(mob/living/user, mob/living/carbon/target, success_rate, duration) SIGNAL_HANDLER + var/effective_morale = morale_ratio + if (effective_morale < 0) + effective_morale *= psi_panic_effect_modifier + to_chat(user, SPAN_WARNING("The pressure on your mind makes it hard to focus on the surgery...")) + *success_rate = *success_rate + surgery_success_contribution * morale_ratio *duration = *duration * (1 - surgery_speed_contribution * morale_ratio) -/datum/component/morale/proc/get_morale_hud(mob/living/carbon/human/user) - SIGNAL_HANDLER - if (QDELING(src)) - return // draw nothing. - - user.client?.screen |= morale_ui - +/// Positive morale makes item crafting faster. Psi-panic from negative morale makes it much slower. /datum/component/morale/proc/handle_crafting_speed(mob/user, doafter_time) SIGNAL_HANDLER + var/effective_morale = morale_ratio + if (effective_morale < 0) + effective_morale *= psi_panic_effect_modifier + to_chat(user, SPAN_WARNING("The pressure on your mind makes it hard to focus on constructing...")) + *doafter_time = *doafter_time * (1 - (crafting_speed_contribution * morale_ratio)) +/// Positive morale improves plant harvesting yield/speed. Psi-panic from negative morale makes it much worse. /datum/component/morale/proc/modify_yield(owner, datum/seed/plant, total_yield, cancelled, doafter) SIGNAL_HANDLER + var/effective_morale = morale_ratio + if (effective_morale < 0) + effective_morale *= psi_panic_effect_modifier + to_chat(owner, SPAN_WARNING("The pressure on your mind makes it hard to focus on harvesting the plants...")) + *total_yield = *total_yield + plant_yield_contribution * morale_ratio *doafter = *doafter * (1 - (harvest_speed_contribution * morale_ratio)) diff --git a/html/changelogs/hellfirejag-more-morale-tweaks.yml b/html/changelogs/hellfirejag-more-morale-tweaks.yml new file mode 100644 index 00000000000..1393e4c1e1e --- /dev/null +++ b/html/changelogs/hellfirejag-more-morale-tweaks.yml @@ -0,0 +1,5 @@ +author: Hellfirejag +delete-after: True +changes: + - rscadd: "Added feedback messages for when psychic damage is penalizing a user's skills." + - balance: "Increased the penalties to skills from psychic damage by a factor of 3."