Files
S.P.L.U.R.T-Station-13/code/datums/skills/_skill_holder.dm
lolman360 de2d80696e dix
2020-08-16 22:04:28 +10:00

197 lines
8.2 KiB
Plaintext

/**
* Skill holder datums
* All procs are tied to the mind, since they are always expected to have a skill holder anyway.
*/
/datum/skill_holder
/// Our list of skills and values. Lazylist. Associative. Keys are datum typepaths to the skill.
var/list/skills
/// Same as [skills] but affinities, which are multiplied to increase amount when gaining skills.
var/list/skill_affinities
/// Let's say we want to get a specific skill "level" without looping through a proc everytime.
/// Only supported by skills with tiers or levels.
var/list/skill_levels
/// current skill modifiers lists, per value, affinity, level.
var/list/skill_value_mods
var/list/skill_affinity_mods
var/list/skill_level_mods
/// List of all current skill modifiers, so we don't add the same ones twice.
var/list/all_current_skill_modifiers
/// List of original values stored at the time a modifier with the MODIFIER_SKILL_ORIGIN_DIFF enabled was added.
var/list/original_values
var/list/original_affinities
var/list/original_levels
/// The mind datum this skill is associated with, only used for the check_skills UI
var/datum/mind/owner
/// For UI updates.
var/need_static_data_update = TRUE
/// Whether modifiers and final skill values or only base values are displayed.
var/see_skill_mods = TRUE
/// The current selected skill category.
var/selected_category
/datum/skill_holder/New(owner)
..()
src.owner = owner
/**
* Grabs the value of a skill.
*/
/datum/mind/proc/get_skill_value(skill, apply_modifiers = TRUE)
if(!ispath(skill))
CRASH("Invalid get_skill_value call. Use typepaths.") //until a time when we somehow need text ids for dynamic skills, I'm enforcing this.
if(!skill_holder.skills)
. = 0
else
. = skill_holder.skills[skill] || 0
if(apply_modifiers && skill_holder.skill_value_mods)
var/L = LAZYACCESS(skill_holder.skill_value_mods, skill)
for(var/k in L)
var/datum/skill_modifier/M = GLOB.skill_modifiers[k]
. = M.apply_modifier(., skill, skill_holder, MODIFIER_TARGET_VALUE)
/**
* Grabs the level of a skill. Only supported by skills with tiers or levels.
*/
/datum/mind/proc/get_skill_level(skill, apply_modifiers = TRUE, round = FALSE)
if(!ispath(skill, /datum/skill))
CRASH("Invalid get_skill_value call. Use skill typepaths.") //until a time when we somehow need text ids for dynamic skills, I'm enforcing this.
if(!skill_holder.skill_levels)
. = 0
else
. = skill_holder.skill_levels[skill] || 0
if(apply_modifiers && skill_holder.skill_level_mods)
var/L = LAZYACCESS(skill_holder.skill_level_mods, skill)
for(var/k in L)
var/datum/skill_modifier/M = GLOB.skill_modifiers[k]
. = M.apply_modifier(., skill, skill_holder, MODIFIER_TARGET_LEVEL)
. = SANITIZE_SKILL_LEVEL(skill, round ? round(., 1) : .)
/**
* Grabs our affinity for a skill. !!This is a multiplier!!
*/
/datum/mind/proc/get_skill_affinity(skill, apply_modifiers = TRUE)
. = 1
if(!ispath(skill, /datum/skill))
CRASH("Invalid get_skill_affinity call. Use skill typepaths.") //until a time when we somehow need text ids for dynamic skills, I'm enforcing this.
var/affinity = LAZYACCESS(skill_holder.skill_affinities, skill)
if(!isnull(affinity))
. = affinity
if(apply_modifiers && skill_holder.skill_affinity_mods)
var/L = LAZYACCESS(skill_holder.skill_affinity_mods, skill)
for(var/k in L)
var/datum/skill_modifier/M = GLOB.skill_modifiers[k]
. = M.apply_modifier(., skill, skill_holder, MODIFIER_TARGET_AFFINITY)
/**
* Sets the value of a skill.
*/
/datum/mind/proc/set_skill_value(skill, value, silent = FALSE)
if(!ispath(skill, /datum/skill))
CRASH("Invalid set_skill_value call. Use skill typepaths.") //until a time when we somehow need text ids for dynamic skills, I'm enforcing this.
var/datum/skill/S = GLOB.skill_datums[skill]
value = S.sanitize_value(value)
if(!isnull(value))
LAZYINITLIST(skill_holder.skills)
S.set_skill_value(skill_holder, value, src, silent)
skill_holder.need_static_data_update = TRUE
return TRUE
return FALSE
/**
* Boosts a skill to a value if not aobve
*/
/datum/mind/proc/boost_skill_value_to(skill, value, silent = FALSE, current)
current = current || get_skill_value(skill, FALSE)
if(!IS_SKILL_VALUE_GREATER(skill, current, value))
return FALSE
set_skill_value(skill, value, silent)
return TRUE
/**
* Automatic skill increase, multiplied by skill affinity if existing.
* Only works if skill is numerical or levelled..
*/
/datum/mind/proc/auto_gain_experience(skill, value, maximum, silent = FALSE)
if(!ispath(skill, /datum/skill))
CRASH("Invalid set_skill_value call. Use skill typepaths.")
var/datum/skill/S = GLOB.skill_datums[skill]
if(S.progression_type != SKILL_PROGRESSION_NUMERICAL && S.progression_type != SKILL_PROGRESSION_LEVEL)
CRASH("You cannot auto increment a non numerical(experience skill!")
var/current = get_skill_value(skill, FALSE)
var/affinity = get_skill_affinity(skill)
var/target_value = round(current + (value * affinity), S.skill_gain_quantisation)
if(maximum && target_value >= maximum) //no more experience to gain, early return.
return
boost_skill_value_to(skill, target_value, silent, current)
/**
* Generic value modifier proc that uses one skill.
* Args:
* * value : the value to modify, may be a delay, damage, probability.
* * threshold : The difficulty of the action, in short. Refer to __DEFINES/skills/defines.dm for the defines.
* * modifier_is_multiplier : wheter the modifier is a multiplier or a divisor.
*/
/datum/mind/proc/action_skill_mod(skill, value, threshold, modifier_is_multiplier = TRUE)
var/datum/skill/S = GLOB.skill_datums[skill]
if(!S)
return value
var/mod = S.base_multiplier
switch(S.progression_type)
if(SKILL_PROGRESSION_LEVEL)
var/datum/skill/level/L = S
var/skill_lvl = get_skill_level(L.type)
mod += skill_lvl/(L.max_levels+max(L.competency_thresholds[threshold]-skill_lvl, 0))*L.competency_multiplier
if(SKILL_PROGRESSION_NUMERICAL)
var/datum/skill/numerical/N = S
var/skill_val = get_skill_value(N.type)
mod += skill_val/(N.max_value+max(N.competency_thresholds[threshold]-skill_val, 0))*N.competency_multiplier
else
var/comp_threshold = S.competency_thresholds[threshold]
mod += (comp_threshold ? (get_skill_value(S.type) / comp_threshold) : get_skill_value(S.type))*S.competency_multiplier
. = modifier_is_multiplier ? value*mod : value/mod
/**
* Generic value modifier proc that uses several skills, intended for items.
* Args:
* * item/I : the item used in this action. its used_skills list variable contains the skills exercised with it.
* * value : the value to modify, may be a delay, damage, probability.
* * traits : the required traits each skill (either in I.used_skills or the skill datum skill_traits) must have to influence
* * the value.
* * bad_traits : the opposite of the above.
* * modifier_is_multiplier : wheter the modifier is a multiplier or a divisor.
*/
/datum/mind/proc/item_action_skills_mod(obj/item/I, value, traits, bad_traits, modifier_is_multiplier = TRUE)
. = value
var/sum = 0
var/divisor = 0
var/one_trait = istext(traits)
var/one_bad_trait = istext(bad_traits)
for(var/k in I.used_skills)
var/datum/skill/S = GLOB.skill_datums[k]
if(!S)
continue
var/our_traits = S.skill_traits
our_traits |= I.used_skills[k]
if(traits && !(one_trait ? (traits in our_traits) : length(our_traits & traits)))
continue
if(bad_traits && (one_bad_trait ? (bad_traits in our_traits) : length(our_traits & bad_traits)))
continue
var/mod = S.base_multiplier
switch(S.progression_type)
if(SKILL_PROGRESSION_LEVEL)
var/datum/skill/level/L = S
var/skill_lvl = get_skill_level(L.type)
mod += skill_lvl/(L.max_levels+max(L.competency_thresholds[I.skill_difficulty]-skill_lvl, 0))*L.competency_multiplier
if(SKILL_PROGRESSION_NUMERICAL)
var/datum/skill/numerical/N = S
var/skill_val = get_skill_value(N.type)
mod += skill_val/(N.max_value+max(N.competency_thresholds[I.skill_difficulty]-skill_val, 0))*N.competency_multiplier
else
var/comp_threshold = S.competency_thresholds[I.skill_difficulty]
mod += (comp_threshold ? (get_skill_value(S.type) / comp_threshold) : get_skill_value(S.type))*S.competency_multiplier
sum += mod
divisor++
if(divisor)
. = modifier_is_multiplier ? value*(sum/divisor) : value/(sum/divisor)