mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-07-13 17:07:53 +01:00
Resolve Skill
This commit is contained in:
@@ -1,3 +1,8 @@
|
||||
#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]"); \
|
||||
morale_ui.update_icon();
|
||||
|
||||
/// Screen space movable object for the morale component.
|
||||
/atom/movable/screen/morale
|
||||
name = "morale"
|
||||
@@ -14,13 +19,18 @@
|
||||
qdel(src) // whoops the attached mob doesn't have a morale component.
|
||||
return
|
||||
|
||||
if (!length(morale_comp.moodlets))
|
||||
// This section is Unlinted since we need to access a same-file PRIVATE var
|
||||
// and for whatever ungodly reason strongdmm doesn't allow same-file to touch VAR_PRIVATE
|
||||
var/morale_percent = UNLINT(morale_comp.morale_ratio * 100)
|
||||
if (!morale_percent)
|
||||
to_chat(usr, SPAN_NOTICE("You currently have no morale modifiers."))
|
||||
return
|
||||
|
||||
// This section is Unlinted since we need to access a same-file PRIVATE var
|
||||
// and for whatever ungodly reason strongdmm doesn't allow same-file to touch VAR_PRIVATE
|
||||
UNLINT(to_chat(usr, SPAN_NOTICE("Your current morale bonus is [morale_comp.morale_ratio * 100]%")))
|
||||
to_chat(usr, SPAN_NOTICE("Your current morale bonus is [morale_percent]%"))
|
||||
|
||||
if (!length(morale_comp.moodlets))
|
||||
return
|
||||
|
||||
to_chat(usr, SPAN_NOTICE("You have the following morale modifiers: "))
|
||||
for (var/datum/moodlet/moodlet as anything in morale_comp.moodlets)
|
||||
to_chat(usr, moodlet.get_moodlet_descriptor())
|
||||
@@ -54,17 +64,25 @@
|
||||
VAR_PRIVATE/morale_ratio = 0.0 // Positive and negative floating points are allowed.
|
||||
|
||||
/**
|
||||
* The "B" constant in the equation for y = Atanh(Bx + C).
|
||||
* The "B" constant in the equation for y = Atanh(B(x + C)).
|
||||
* This constant is not arbitrary, it was carefully selected such that the equation will give "75% of its effect" at 50 morale points, and "96% of its effect" at 100 morale.
|
||||
* This allows for there to be an effect of diminishing returns for chasing ever increasingly more morale points, while front-loading the bulk of the effects at a specific amount of moodlets.
|
||||
* Since the effects of morale are a "Logistic Curve", "100% of the morale effect" is only ever obtained at +INFINITY.
|
||||
* This also goes for the opposite direction, morale penalties max out only at -INFINITY points, but get to "75% of the penalty effect" at -50 points.
|
||||
*
|
||||
* The actual "Effects" of morale are to be per-signal, and are defined by the A value in y = Atanh(Bx + C)
|
||||
* The actual "Effects" of morale are to be per-signal, and are defined by the A value in y = Atanh(B(x + C))
|
||||
* This is private for a reason, if you need to change it, do so by using set_beta_value(), which will also make the component recalculate its morale ratio.
|
||||
*/
|
||||
VAR_PRIVATE/beta_value = 0.0195
|
||||
|
||||
/**
|
||||
* The "C" constant in the equation for y = Atanh(B(x + C))
|
||||
* This constant acts like a "permanent" moodlet with a morale bonus equal to what its set as.
|
||||
* Please set this extremely sparingly. You won't necessarily break anything by setting it, since the system will tolerate any number between +/- Infinity.
|
||||
* But making a habit of bypassing the morale system like this can easily trivialize it.
|
||||
*/
|
||||
VAR_PRIVATE/phi_value = 0.0
|
||||
|
||||
/**
|
||||
* UI element stored for the morale component,
|
||||
* which is attached to the screen of a client controlling a character with this component.
|
||||
@@ -129,20 +147,20 @@
|
||||
|
||||
/datum/component/morale/proc/add_morale_points(input)
|
||||
morale_points += input
|
||||
morale_ratio = ftanh(beta_value * morale_points)
|
||||
UPDATE_MORALE
|
||||
|
||||
// I get to do this freakishly compact state setting because I can
|
||||
// logically prove via VAR_PRIVATE that this is only ever set via a hyperbolic tangent
|
||||
// And that because a hyperbolic tangent will only ever return a value between -1 and 1,
|
||||
// the range of this equation becomes the set of integers between 1 and 9 inclusive.
|
||||
morale_ui.icon_state = ((morale_ratio > -0.01 && morale_ratio < 0.01) ? "morale_hidden" : "morale" + "[round(morale_ratio * 4) + 5]")
|
||||
morale_ui.update_icon()
|
||||
/datum/component/morale/proc/get_beta_value()
|
||||
return beta_value
|
||||
|
||||
/datum/component/morale/proc/set_beta_value(input)
|
||||
beta_value = input
|
||||
morale_ratio = ftanh(beta_value * morale_points)
|
||||
morale_ui.icon_state = ((morale_ratio > -0.01 && morale_ratio < 0.01) ? "morale_hidden" : "morale" + "[round(morale_ratio * 4) + 5]")
|
||||
morale_ui.update_icon()
|
||||
|
||||
/datum/component/morale/proc/get_phi_value()
|
||||
return phi_value
|
||||
|
||||
/datum/component/morale/proc/set_phi_value(input)
|
||||
phi_value = input
|
||||
UPDATE_MORALE
|
||||
|
||||
/**
|
||||
* Your one-stop-shop for making moodlets. This proc returns the pre-existing moodlet of a given type.
|
||||
@@ -184,6 +202,7 @@
|
||||
RegisterSignal(parent, COMSIG_GET_SURGERY_SUCCESS_MODIFIERS, PROC_REF(handle_surgery_modifiers), override = TRUE)
|
||||
RegisterSignal(parent, COMSIG_GET_CRAFTING_MODIFIERS, PROC_REF(handle_crafting_speed), override = TRUE)
|
||||
RegisterSignal(parent, COMSIG_PLANT_HARVESTER, PROC_REF(modify_yield), override = TRUE)
|
||||
UPDATE_MORALE
|
||||
|
||||
/datum/component/morale/Destroy()
|
||||
QDEL_LIST_FORCE(moodlets)
|
||||
@@ -228,7 +247,7 @@
|
||||
if (!list_trimmed)
|
||||
return
|
||||
|
||||
morale_ratio = ftanh(morale_points)
|
||||
UPDATE_MORALE
|
||||
|
||||
/*
|
||||
AND NOW THE GIANT WALL OF SIGNAL HANDLERS
|
||||
@@ -347,3 +366,5 @@
|
||||
SIGNAL_HANDLER
|
||||
*total_yield = *total_yield + plant_yield_contribution * morale_ratio
|
||||
*doafter = *doafter * (1 - (harvest_speed_contribution * morale_ratio))
|
||||
|
||||
#undef UPDATE_MORALE
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
* It will be called during the process of spawning a player character in.
|
||||
*/
|
||||
/singleton/skill/proc/on_spawn(mob/owner, skill_level)
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
// Note that this can be directly overridden for skills that wish to use the ECS hook to provide an effect other than the standard "adding a component".
|
||||
if (!owner || !component_type || (!required && skill_level == SKILL_LEVEL_UNFAMILIAR))
|
||||
return
|
||||
|
||||
|
||||
@@ -79,3 +79,32 @@
|
||||
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."
|
||||
)
|
||||
|
||||
/singleton/skill/resolve
|
||||
name = "Resolve"
|
||||
description = "Represents a character's mental resilience, emotional stability, and ability to remain steadfast under stress. " \
|
||||
+ "Characters with greater Resolve receive a permanent passive bonus to their effective morale value. This can provide small bonuses to a variety of interactions. " \
|
||||
+ "This morale bonus can also act as a pool of resistance towards psychic damage. " \
|
||||
+ "This skill has no effect species which does not interact with Morale."
|
||||
maximum_level = SKILL_LEVEL_PROFESSIONAL
|
||||
category = /singleton/skill_category/everyday
|
||||
subcategory = SKILL_SUBCATEGORY_PHYSICAL
|
||||
component_type = null // No component, this skill is flexing the ECS hook in a different way entirely.
|
||||
skill_level_descriptions = alist(
|
||||
SKILL_LEVEL_UNFAMILIAR = "You have no modifiers from Resolve.",
|
||||
SKILL_LEVEL_FAMILIAR = "You have a passive permanent bonus of +5 morale points.",
|
||||
SKILL_LEVEL_TRAINED = "You have a passive permanent bonus of +10 morale points.",
|
||||
SKILL_LEVEL_PROFESSIONAL = "You have a passive permanent bonus of +15 morale points."
|
||||
)
|
||||
|
||||
/singleton/skill/resolve/on_spawn(mob/owner, skill_level)
|
||||
var/mob/living/carbon/character = astype(owner)
|
||||
if (!character || skill_level == SKILL_LEVEL_UNFAMILIAR || !character.species || !character.species.has_morale)
|
||||
return
|
||||
|
||||
// Using LoadComponent here lets me defeat any possible race conditions coming out of character creation.
|
||||
// Uniquely among skills, this skill works by modifying a pre-existing component used by player characters, rather than adding its own.
|
||||
// If you're a contributor reading up on skills, consider this your tutorial on how to make a skill modify some statistic directly.
|
||||
var/datum/component/morale/morale_comp = character.LoadComponent(MORALE_COMPONENT)
|
||||
morale_comp.set_phi_value(morale_comp.get_phi_value() + (5 * skill_level))
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
author: Hellfirejag
|
||||
delete-after: True
|
||||
changes:
|
||||
- rscadd: "Added a new Resolve skill to the Everyday category. Resolve as a skill provides a small passive bonus to a characters Morale statistic."
|
||||
- bugfix: "Fixed Morale not properly updating its effects when a moodlet (such as Psychic Damage) expires."
|
||||
Reference in New Issue
Block a user