From 7f38e0714bf499b6d428be37444a92c5e0f8022b Mon Sep 17 00:00:00 2001 From: Ghom <42542238+Ghommie@users.noreply.github.com> Date: Thu, 21 May 2020 03:42:58 +0200 Subject: [PATCH] Skill calculation fixes, tweaks, antag and brain damage modifiers. (#12302) * Skill calculation fixes, tweaks, antag and brain damage modifiers. * Experience through patience. * Priority Fix etcetera. --- code/__DEFINES/skills/defines.dm | 41 +++++++++-------- code/_onclick/item_attack.dm | 20 ++++----- code/datums/skills/_skill.dm | 26 +++++------ code/datums/skills/_skill_holder.dm | 45 +++++++++++++------ code/datums/skills/_skill_modifier.dm | 40 ++++++++++++----- code/datums/skills/engineering.dm | 3 +- code/datums/skills/medical.dm | 2 +- code/datums/skills/modifiers/mood.dm | 4 +- code/datums/skills/modifiers/organs.dm | 13 ++++++ code/game/objects/items.dm | 10 +++-- .../antagonists/_common/antag_datum.dm | 26 +++++++---- code/modules/antagonists/abductor/abductor.dm | 2 + .../antagonists/clockcult/clockcult.dm | 1 + code/modules/antagonists/ert/ert.dm | 2 + code/modules/antagonists/nukeop/nukeop.dm | 1 + .../antagonists/traitor/datum_traitor.dm | 1 + code/modules/mob/living/brain/brain_item.dm | 45 ++++++++----------- code/modules/surgery/surgery_step.dm | 12 +++-- tgstation.dme | 1 + 19 files changed, 181 insertions(+), 114 deletions(-) create mode 100644 code/datums/skills/modifiers/organs.dm diff --git a/code/__DEFINES/skills/defines.dm b/code/__DEFINES/skills/defines.dm index 9127b4e3dd..659c0fe11b 100644 --- a/code/__DEFINES/skills/defines.dm +++ b/code/__DEFINES/skills/defines.dm @@ -27,25 +27,27 @@ #define DEF_SKILL_GAIN 1 #define SKILL_GAIN_SURGERY_PER_STEP 0.25 -#define SKILL_AFFINITY_MOOD_BONUS 1.25 +//An extra point for each few seconds of delay when using a tool. Before the multiplier. +#define SKILL_GAIN_DELAY_DIVISOR 3 SECONDS -///Items skill_flags and other defines -#define SKILL_USE_TOOL (1<<0) -#define SKILL_TRAINING_TOOL (1<<1) -#define SKILL_ATTACK_MOB (1<<2) -#define SKILL_TRAIN_ATTACK_MOB (1<<3) -#define SKILL_ATTACK_OBJ (1<<4) -#define SKILL_TRAIN_ATTACK_OBJ (1<<5) -#define SKILL_STAMINA_COST (1<<6) //Influences the stamina cost from weapon usage. -#define SKILL_THROW_STAM_COST (1<<7) -#define SKILL_COMBAT_MODE (1<<8) //The user must have combat mode on. -#define SKILL_USE_MOOD (1<<9) //Is the skill negatively affected by bad mood. -#define SKILL_TRAIN_MOOD (1<<10) //Is this skill training affected by good mood. +///Items skill_traits and other defines +#define SKILL_USE_TOOL "use_tool" +#define SKILL_TRAINING_TOOL "training_tool" +#define SKILL_ATTACK_MOB "attack_mob" +#define SKILL_TRAIN_ATTACK_MOB "train_attack_mob" +#define SKILL_ATTACK_OBJ "attack_obj" +#define SKILL_TRAIN_ATTACK_OBJ "train_attack_obj" +#define SKILL_STAMINA_COST "stamina_cost" //Influences the stamina cost from weapon usage. +#define SKILL_THROW_STAM_COST "throw_stam_cost" +#define SKILL_COMBAT_MODE "combat_mode" //The user must have combat mode on. +#define SKILL_SANITY "sanity" //Is the skill affected by (in)sanity. +#define SKILL_INTELLIGENCE "intelligence" //Is the skill affected by brain damage? -///competency_threshold index defines -#define THRESHOLD_COMPETENT 1 -#define THRESHOLD_EXPERT 2 -#define THRESHOLD_MASTER 3 +///competency_threshold defines +#define THRESHOLD_UNTRAINED "untrained" +#define THRESHOLD_COMPETENT "competent" +#define THRESHOLD_EXPERT "expert" +#define THRESHOLD_MASTER "master" /// Level/Experience skills defines. #define STD_XP_LVL_UP 100 @@ -88,11 +90,14 @@ #define MODIFIER_SKILL_BODYBOUND (1<<6) ///Adds the difference of the current value and the value stored at the time the modifier was added to the result. #define MODIFIER_SKILL_ORIGIN_DIFF (1<<7) +///Will this skill use competency thresholds instead of preset values +#define MODIFIER_USE_THRESHOLDS (1<<8) #define MODIFIER_TARGET_VALUE "value" #define MODIFIER_TARGET_LEVEL "level" #define MODIFIER_TARGET_AFFINITY "affinity" ///Ascending priority defines. +#define MODIFIER_SKILL_PRIORITY_LOW 100 #define MODIFIER_SKILL_PRIORITY_DEF 50 -#define MODIFIER_SKILL_PRIORITY_MAX 100 //max priority, meant for job/antag modifiers so they don't null out any debuff +#define MODIFIER_SKILL_PRIORITY_MAX 1 //max priority, meant for job/antag modifiers so they don't null out other (de)buffs \ No newline at end of file diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index 9e120db102..3ad0219391 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -112,13 +112,13 @@ /obj/attacked_by(obj/item/I, mob/living/user) var/totitemdamage = I.force - var/bad_flag = NONE + var/bad_trait if(!(user.combat_flags & COMBAT_FLAG_COMBAT_ACTIVE) && iscarbon(user)) totitemdamage *= 0.5 - bad_flag |= SKILL_COMBAT_MODE //blacklist combat skills. + bad_trait = SKILL_COMBAT_MODE //blacklist combat skills. if(I.used_skills && user.mind) if(totitemdamage) - totitemdamage = user.mind.item_action_skills_mod(I, totitemdamage, I.skill_difficulty, SKILL_ATTACK_OBJ, bad_flag) + totitemdamage = user.mind.item_action_skills_mod(I, totitemdamage, I.skill_difficulty, SKILL_ATTACK_OBJ, bad_trait) for(var/skill in I.used_skills) if(!(I.used_skills[skill] & SKILL_TRAIN_ATTACK_OBJ)) continue @@ -154,16 +154,16 @@ /mob/living/proc/pre_attacked_by(obj/item/I, mob/living/user) . = I.force - var/bad_flag = NONE + var/bad_trait if(!(user.combat_flags & COMBAT_FLAG_COMBAT_ACTIVE) && iscarbon(user)) . *= 0.5 - bad_flag |= SKILL_COMBAT_MODE //blacklist combat skills. + bad_trait = SKILL_COMBAT_MODE //blacklist combat skills. if(!CHECK_MOBILITY(user, MOBILITY_STAND)) . *= 0.5 if(!user.mind || !I.used_skills) return if(.) - . = user.mind.item_action_skills_mod(I, ., I.skill_difficulty, SKILL_ATTACK_MOB, bad_flag) + . = user.mind.item_action_skills_mod(I, ., I.skill_difficulty, SKILL_ATTACK_MOB, bad_trait) for(var/skill in I.used_skills) if(!(I.used_skills[skill] & SKILL_TRAIN_ATTACK_MOB)) continue @@ -207,16 +207,16 @@ return 1 /// How much stamina this takes to swing this is not for realism purposes hecc off. -/obj/item/proc/getweight(mob/living/user, multiplier = 1, flags = SKILL_STAMINA_COST) +/obj/item/proc/getweight(mob/living/user, multiplier = 1, trait = SKILL_STAMINA_COST) . = (total_mass || w_class * STAM_COST_W_CLASS_MULT) * multiplier if(!user) return - var/bad_flag = NONE + var/bad_trait if(iscarbon(user) && !(user.combat_flags & COMBAT_FLAG_COMBAT_ACTIVE)) . *= STAM_COST_NO_COMBAT_MULT - bad_flag |= SKILL_COMBAT_MODE + bad_trait = SKILL_COMBAT_MODE if(used_skills && user.mind) - . = user.mind.item_action_skills_mod(src, ., skill_difficulty, flags, bad_flag, FALSE) + . = user.mind.item_action_skills_mod(src, ., skill_difficulty, trait, bad_trait, FALSE) /// How long this staggers for. 0 and negatives supported. /obj/item/proc/melee_stagger_duration(force_override) diff --git a/code/datums/skills/_skill.dm b/code/datums/skills/_skill.dm index 41ef52e317..571a8274d8 100644 --- a/code/datums/skills/_skill.dm +++ b/code/datums/skills/_skill.dm @@ -20,18 +20,20 @@ GLOBAL_LIST_INIT_TYPED(skill_datums, /datum/skill, init_skill_datums()) var/desc /// Color of the name as shown in the html readout var/name_color = "#F0F0F0" // White on dark surface. - /// Our progression type + /// Our progression type. These are mostly used to skip typechecks overhead, don't go around messing with these. var/progression_type /// Abstract type var/abstract_type = /datum/skill /// List of max levels. Only used in level skills, but present here for helper macros. var/max_levels = INFINITY - /// skill threshold used in generic skill competency calculations. - var/list/competency_thresholds = list(0, 0, 0) - /// Multiplier of the difference of the holder skill value and the selected threshold. - var/list/competency_mults = list(0, 0, 0) - /// In which way this skil can affect or be affected through actions and skill modifiers. - var/skill_flags = SKILL_USE_MOOD|SKILL_TRAIN_MOOD + /// skill threshold used in generic skill competency operations. + var/list/competency_thresholds + /// Base multiplier used in skill competency operations. + var/base_multiplier = 1 + /// Value added to the base multiplier depending on overall competency compared to maximum value/level. + var/competency_multiplier = 1 + /// A list of ways this skill can affect or be affected through actions and skill modifiers. + var/list/skill_traits = list(SKILL_SANITY, SKILL_INTELLIGENCE) /** * Ensures what someone's setting as a value for this skill is valid. @@ -66,8 +68,7 @@ GLOBAL_LIST_INIT_TYPED(skill_datums, /datum/skill, init_skill_datums()) /datum/skill/binary abstract_type = /datum/skill/binary progression_type = SKILL_PROGRESSION_BINARY - competency_thresholds = list(FALSE, TRUE, TRUE) - competency_mults = list(0.5, 0.5, 0.5) + competency_thresholds = list(THRESHOLD_COMPETENT = FALSE, THRESHOLD_EXPERT = TRUE, THRESHOLD_MASTER = TRUE) /datum/skill/binary/sanitize_value(new_value) return new_value? TRUE : FALSE @@ -78,6 +79,7 @@ GLOBAL_LIST_INIT_TYPED(skill_datums, /datum/skill, init_skill_datums()) /datum/skill/numerical abstract_type = /datum/skill/numerical progression_type = SKILL_PROGRESSION_NUMERICAL + competency_thresholds = list(THRESHOLD_COMPETENT = 25, THRESHOLD_EXPERT = 50, THRESHOLD_MASTER = 75) /// Max value of this skill var/max_value = 100 /// Min value of this skill @@ -180,8 +182,7 @@ GLOBAL_LIST_INIT_TYPED(skill_datums, /datum/skill, init_skill_datums()) /datum/skill/level/job abstract_type = /datum/skill/level/job levels = list("Basic", "Trained", "Experienced", "Master") - competency_thresholds = list(JOB_SKILL_TRAINED, JOB_SKILL_EXPERT, JOB_SKILL_MASTER) - competency_mults = list(0.15, 0.1, 0.1) + competency_thresholds = list(THRESHOLD_COMPETENT = JOB_SKILL_TRAINED, THRESHOLD_EXPERT = JOB_SKILL_EXPERT, THRESHOLD_MASTER = JOB_SKILL_MASTER) associative = TRUE //quite the reference, no? @@ -195,7 +196,6 @@ GLOBAL_LIST_INIT_TYPED(skill_datums, /datum/skill, init_skill_datums()) "Proficient", "Talented", "Adept", "Expert", "Professional", "Accomplished", "Great", "Master", "High Master", "Grand Master", "Legendary") - competency_thresholds = list(DORF_SKILL_COMPETENT, DORF_SKILL_EXPERT, DORF_SKILL_MASTER) - competency_mults = list(0.15, 0.1, 0.08) + competency_thresholds = list(THRESHOLD_COMPETENT = DORF_SKILL_COMPETENT, THRESHOLD_EXPERT = DORF_SKILL_EXPERT, THRESHOLD_MASTER = DORF_SKILL_MASTER) associative = TRUE unskilled_tier = "Dabbling" diff --git a/code/datums/skills/_skill_holder.dm b/code/datums/skills/_skill_holder.dm index 500b49a1ff..554269091a 100644 --- a/code/datums/skills/_skill_holder.dm +++ b/code/datums/skills/_skill_holder.dm @@ -122,16 +122,22 @@ * * 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/mod var/datum/skill/S = GLOB.skill_datums[skill] if(!S) - return + return value + var/mod = S.base_multiplier switch(S.progression_type) if(SKILL_PROGRESSION_LEVEL) - mod = get_skill_level(S.type) + 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 - mod = get_skill_value(S.type) - mod = (1+(mod-S.competency_thresholds[threshold])*S.competency_mults[threshold]) + 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 /** @@ -139,12 +145,12 @@ * 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. - * * flags : the required flags that each skill (either in I.used_skills or the skill datum skill_flags) must have to influence + * * traits : the required traits each skill (either in I.used_skills or the skill datum skill_traits) must have to influence * * the value. - * * bad_flags : the opposite of the above, skills that must not be present to impact 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, flags = NONE, bad_flags = NONE, modifier_is_multiplier = TRUE) +/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 @@ -152,16 +158,27 @@ var/datum/skill/S = GLOB.skill_datums[k] if(!S) continue - var/our_flags = (I.used_skills[k]|S.skill_flags) - if((flags && !(our_flags & flags)) || (bad_flags && our_flags & bad_flags)) + var/our_traits = S.skill_traits + var/item_traits = I.used_skills[k] + if(item_traits) + our_traits |= item_traits + if((traits && !(our_traits[traits] || length(our_traits & traits))) || (bad_traits && (our_traits[bad_traits] || length(our_traits & bad_traits)))) continue - var/mod + var/mod = S.base_multiplier switch(S.progression_type) if(SKILL_PROGRESSION_LEVEL) - mod = get_skill_level(S.type) + 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 - mod = get_skill_value(S.type) - sum += 1+(mod - S.competency_thresholds[I.skill_difficulty])*S.competency_mults[I.skill_difficulty] + 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) diff --git a/code/datums/skills/_skill_modifier.dm b/code/datums/skills/_skill_modifier.dm index b0115194a7..a28cf3aebd 100644 --- a/code/datums/skills/_skill_modifier.dm +++ b/code/datums/skills/_skill_modifier.dm @@ -9,11 +9,13 @@ GLOBAL_LIST_EMPTY(potential_mods_per_skill) /datum/skill_modifier /// flags for this skill modifier. var/modifier_flags = NONE - /// target skills, can be a specific skill typepath or a set of skill flags. - var/target_skills = NONE + /// target skills, can be a specific skill typepath or a list of skill traits. + var/target_skills = /datum/skill + /// the GLOB.potential_skills_per_mod key generated on runtime. You shouldn't be var-editing it. + var/target_skills_key /// The identifier key this skill modifier is associated with. var/identifier - /// skill affinity modifier, can be a multiplier or addendum, depending on the modifier_flags flags. + /// skill affinity modifier, can be a multiplier or addendum, depending on the modifier_flags. var/affinity_mod = 1 /// skill value modifier, see above. var/value_mod = 1 @@ -38,21 +40,25 @@ GLOBAL_LIST_EMPTY(potential_mods_per_skill) CRASH("Skill modifier identifier \"[identifier]\" already taken.") GLOB.skill_modifiers[identifier] = src if(ispath(target_skills)) + target_skills_key = target_skills var/list/mod_L = GLOB.potential_mods_per_skill[target_skills] if(!mod_L) mod_L = GLOB.potential_mods_per_skill[target_skills] = list() else BINARY_INSERT(identifier, mod_L, datum/skill_modifier, src, priority, COMPARE_VALUE) mod_L[identifier] = src - GLOB.potential_skills_per_mod["[target_skills]"] = list(target_skills) - else //Should be a bitfield. - var/list/L = GLOB.potential_skills_per_mod["[target_skills]"] + GLOB.potential_skills_per_mod[target_skills_key] = list(target_skills) + else //Should be a list. + var/list/T = target_skills + T = sortTim(target_skills, /proc/cmp_text_asc) //Sort the list contents alphabetically. + target_skills_key = T.Join("-") + var/list/L = GLOB.potential_skills_per_mod[target_skills_key] if(!L) L = list() for(var/path in GLOB.skill_datums) - if(GLOB.skill_datums[path].skill_flags & target_skills) + if(GLOB.skill_datums[path].skill_traits & target_skills) L += path - GLOB.potential_skills_per_mod["[target_skills]"] = L + GLOB.potential_skills_per_mod[target_skills_key] = L for(var/path in L) var/list/mod_L = GLOB.potential_mods_per_skill[path] if(!mod_L) @@ -62,7 +68,7 @@ GLOBAL_LIST_EMPTY(potential_mods_per_skill) mod_L[identifier] = src /datum/skill_modifier/Destroy() - for(var/path in GLOB.potential_skills_per_mod["[target_skills]"]) + for(var/path in GLOB.potential_skills_per_mod[target_skills_key]) var/mod_L = GLOB.potential_mods_per_skill[path] mod_L -= identifier if(!length(mod_L)) @@ -96,7 +102,7 @@ GLOBAL_LIST_EMPTY(potential_mods_per_skill) LAZYINITLIST(skill_holder.skill_affinity_mods) if(M.modifier_flags & MODIFIER_SKILL_LEVEL) LAZYINITLIST(skill_holder.skill_level_mods) - for(var/path in GLOB.potential_skills_per_mod["[M.target_skills]"]) + for(var/path in GLOB.potential_skills_per_mod[M.target_skills_key]) if(M.modifier_flags & MODIFIER_SKILL_VALUE) ADD_MOD_STEP(skill_holder.skill_value_mods, path, skill_holder.original_values, get_skill_value(path, FALSE)) if(M.modifier_flags & MODIFIER_SKILL_AFFINITY) @@ -127,7 +133,7 @@ GLOBAL_LIST_EMPTY(potential_mods_per_skill) if(!skill_holder.skill_value_mods && !skill_holder.skill_affinity_mods && !skill_holder.skill_level_mods) return - for(var/path in GLOB.potential_skills_per_mod["[M.target_skills]"]) + for(var/path in GLOB.potential_skills_per_mod[M.target_skills_key]) if(M.modifier_flags & MODIFIER_SKILL_VALUE && skill_holder.skill_value_mods) REMOVE_MOD_STEP(skill_holder.skill_value_mods, path, skill_holder.original_values) if(M.modifier_flags & MODIFIER_SKILL_AFFINITY && skill_holder.skill_affinity_mods) @@ -155,6 +161,18 @@ GLOBAL_LIST_EMPTY(potential_mods_per_skill) if(MODIFIER_TARGET_AFFINITY) mod = affinity_mod + if(modifier_flags & MODIFIER_USE_THRESHOLDS && istext(mod)) + var/datum/skill/S = GLOB.skill_datums[skillpath] + if(method == MODIFIER_TARGET_VALUE && S.progression_type == SKILL_PROGRESSION_LEVEL) + var/datum/skill/level/L = S + switch(L.level_up_method) + if(STANDARD_LEVEL_UP) + mod = XP_LEVEL(L.standard_xp_lvl_up, L.xp_lvl_multiplier, S.competency_thresholds[mod]) + if(DWARFY_LEVEL_UP) + mod = DORF_XP_LEVEL(L.standard_xp_lvl_up, L.xp_lvl_multiplier, S.competency_thresholds[mod]) + else + mod = S.competency_thresholds[mod] + var/diff = 0 if(modifier_flags & (MODIFIER_SKILL_VIRTUE|MODIFIER_SKILL_HANDICAP)) if(modifier_flags & MODIFIER_SKILL_VIRTUE) diff --git a/code/datums/skills/engineering.dm b/code/datums/skills/engineering.dm index 59c9436e46..db7b33450c 100644 --- a/code/datums/skills/engineering.dm +++ b/code/datums/skills/engineering.dm @@ -2,5 +2,4 @@ name = "Wiring" desc = "How proficient and knowledged you are at wiring beyond laying cables on the floor." name_color = COLOR_PALE_ORANGE - competency_thresholds = list(JOB_SKILL_BASIC, JOB_SKILL_EXPERT, JOB_SKILL_MASTER) - skill_flags = SKILL_USE_MOOD|SKILL_TRAIN_MOOD|SKILL_USE_TOOL|SKILL_TRAINING_TOOL + skill_traits = list(SKILL_SANITY, SKILL_INTELLIGENCE, SKILL_USE_TOOL, SKILL_TRAINING_TOOL) diff --git a/code/datums/skills/medical.dm b/code/datums/skills/medical.dm index aaae1236a6..404c141157 100644 --- a/code/datums/skills/medical.dm +++ b/code/datums/skills/medical.dm @@ -2,4 +2,4 @@ name = "Surgery" desc = "How proficient you are at doing surgery." name_color = COLOR_PALE_BLUE_GRAY - competency_mults = list(0.025, 0.025, 0.025) // 60% surgery speed up at max value of 100. + competency_multiplier = 1.5 // 60% surgery speed up at max value of 100, considering the base multiplier. diff --git a/code/datums/skills/modifiers/mood.dm b/code/datums/skills/modifiers/mood.dm index 2ab74bdbbb..30f24afcc4 100644 --- a/code/datums/skills/modifiers/mood.dm +++ b/code/datums/skills/modifiers/mood.dm @@ -1,8 +1,8 @@ /datum/skill_modifier/bad_mood modifier_flags = MODIFIER_SKILL_VALUE|MODIFIER_SKILL_LEVEL|MODIFIER_SKILL_MULT|MODIFIER_SKILL_BODYBOUND - target_skills = SKILL_USE_MOOD + target_skills = list(SKILL_SANITY) /datum/skill_modifier/great_mood modifier_flags = MODIFIER_SKILL_AFFINITY|MODIFIER_SKILL_MULT|MODIFIER_SKILL_BODYBOUND - target_skills = SKILL_TRAIN_MOOD + target_skills = list(SKILL_SANITY) affinity_mod = 1.2 diff --git a/code/datums/skills/modifiers/organs.dm b/code/datums/skills/modifiers/organs.dm new file mode 100644 index 0000000000..13ebaf0658 --- /dev/null +++ b/code/datums/skills/modifiers/organs.dm @@ -0,0 +1,13 @@ +/datum/skill_modifier/brain_damage + target_skills = list(SKILL_INTELLIGENCE) + modifier_flags = MODIFIER_SKILL_VALUE|MODIFIER_SKILL_AFFINITY|MODIFIER_SKILL_LEVEL|MODIFIER_SKILL_MULT|MODIFIER_SKILL_BODYBOUND + value_mod = 0.85 + level_mod = 0.85 + affinity_mod = 0.85 + +/datum/skill_modifier/heavy_brain_damage + target_skills = list(SKILL_INTELLIGENCE) + modifier_flags = MODIFIER_SKILL_VALUE|MODIFIER_SKILL_AFFINITY|MODIFIER_SKILL_LEVEL|MODIFIER_SKILL_BODYBOUND|MODIFIER_SKILL_HANDICAP|MODIFIER_USE_THRESHOLDS + priority = MODIFIER_SKILL_PRIORITY_LOW + value_mod = THRESHOLD_COMPETENT + level_mod = THRESHOLD_COMPETENT diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index a8422dedc0..2a4b6d7df5 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -135,7 +135,7 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) ///Skills vars //list of skill PATHS exercised when using this item. An associated bitfield can be set to indicate additional ways the skill is used by this specific item. var/list/datum/skill/used_skills - var/skill_difficulty = THRESHOLD_COMPETENT //how difficult it's to use this item in general. + var/skill_difficulty = THRESHOLD_UNTRAINED //how difficult it's to use this item in general. var/skill_gain = DEF_SKILL_GAIN //base skill value gain from using this item. /obj/item/Initialize() @@ -805,7 +805,7 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) if(delay) if(user.mind && used_skills) - delay = user.mind.item_action_skills_mod(src, delay, skill_difficulty, SKILL_USE_TOOL, NONE, FALSE) + delay = user.mind.item_action_skills_mod(src, delay, skill_difficulty, SKILL_USE_TOOL, null, FALSE) // Create a callback with checks that would be called every tick by do_after. var/datum/callback/tool_check = CALLBACK(src, .proc/tool_check_callback, user, amount, extra_checks) @@ -831,11 +831,13 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) if(delay >= MIN_TOOL_SOUND_DELAY) play_tool_sound(target, volume) + if(user.mind && used_skills && skill_gain_mult) + var/gain = skill_gain + delay/SKILL_GAIN_DELAY_DIVISOR for(var/skill in used_skills) - if(!(used_skills[skill] & SKILL_TRAINING_TOOL)) + if(!(SKILL_TRAINING_TOOL in used_skills[skill])) continue - user.mind.auto_gain_experience(skill, skill_gain*skill_gain_mult, GET_STANDARD_LVL(max_level)) + user.mind.auto_gain_experience(skill, gain*skill_gain_mult, GET_STANDARD_LVL(max_level)) return TRUE diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm index 4a7abda53c..3184a169fc 100644 --- a/code/modules/antagonists/_common/antag_datum.dm +++ b/code/modules/antagonists/_common/antag_datum.dm @@ -24,6 +24,8 @@ GLOBAL_LIST_EMPTY(antagonists) var/list/blacklisted_quirks = list(/datum/quirk/nonviolent,/datum/quirk/mute) // Quirks that will be removed upon gaining this antag. Pacifist and mute are default. var/threat = 0 // Amount of threat this antag poses, for dynamic mode + var/list/skill_modifiers + /datum/antagonist/New() GLOB.antagonists += src typecache_datum_blacklist = typecacheof(typecache_datum_blacklist) @@ -68,15 +70,19 @@ GLOBAL_LIST_EMPTY(antagonists) //Proc called when the datum is given to a mind. /datum/antagonist/proc/on_gain() - if(owner && owner.current) - if(!silent) - greet() - apply_innate_effects() - give_antag_moodies() - remove_blacklisted_quirks() - if(is_banned(owner.current) && replace_banned) - replace_banned_player() - SEND_SIGNAL(owner.current, COMSIG_MOB_ANTAG_ON_GAIN, src) + if(!(owner?.current)) + return + if(!silent) + greet() + apply_innate_effects() + give_antag_moodies() + remove_blacklisted_quirks() + if(is_banned(owner.current) && replace_banned) + replace_banned_player() + if(skill_modifiers) + for(var/A in skill_modifiers) + ADD_SINGLETON_SKILL_MODIFIER(owner, A, type) + SEND_SIGNAL(owner.current, COMSIG_MOB_ANTAG_ON_GAIN, src) /datum/antagonist/proc/is_banned(mob/M) if(!M) @@ -99,6 +105,8 @@ GLOBAL_LIST_EMPTY(antagonists) clear_antag_moodies() if(owner) LAZYREMOVE(owner.antag_datums, src) + for(var/A in skill_modifiers) + owner.remove_skill_modifier(GET_SKILL_MOD_ID(A, type)) if(!silent && owner.current) farewell() var/datum/team/team = get_team() diff --git a/code/modules/antagonists/abductor/abductor.dm b/code/modules/antagonists/abductor/abductor.dm index 44116dbe36..9132288415 100644 --- a/code/modules/antagonists/abductor/abductor.dm +++ b/code/modules/antagonists/abductor/abductor.dm @@ -21,6 +21,7 @@ landmark_type = /obj/effect/landmark/abductor/agent greet_text = "Use your stealth technology and equipment to incapacitate humans for your scientist to retrieve." show_in_antagpanel = TRUE + skill_modifiers = list(/datum/skill_modifier/job/level/wiring) /datum/antagonist/abductor/scientist name = "Abductor Scientist" @@ -29,6 +30,7 @@ landmark_type = /obj/effect/landmark/abductor/scientist greet_text = "Use your experimental console and surgical equipment to monitor your agent and experiment upon abducted humans." show_in_antagpanel = TRUE + skill_modifiers = list(/datum/skill_modifier/job/affinity/surgery) /datum/antagonist/abductor/create_team(datum/team/abductor_team/new_team) if(!new_team) diff --git a/code/modules/antagonists/clockcult/clockcult.dm b/code/modules/antagonists/clockcult/clockcult.dm index a730b52cc1..b6ed7dfe65 100644 --- a/code/modules/antagonists/clockcult/clockcult.dm +++ b/code/modules/antagonists/clockcult/clockcult.dm @@ -5,6 +5,7 @@ antagpanel_category = "Clockcult" job_rank = ROLE_SERVANT_OF_RATVAR antag_moodlet = /datum/mood_event/cult + skill_modifiers = list(/datum/skill_modifier/job/level/wiring) var/datum/action/innate/hierophant/hierophant_network = new threat = 3 var/datum/team/clockcult/clock_team diff --git a/code/modules/antagonists/ert/ert.dm b/code/modules/antagonists/ert/ert.dm index bda1fbabe6..1d773627c7 100644 --- a/code/modules/antagonists/ert/ert.dm +++ b/code/modules/antagonists/ert/ert.dm @@ -51,6 +51,7 @@ /datum/antagonist/ert/engineer role = "Engineer" outfit = /datum/outfit/ert/engineer + skill_modifiers = list(/datum/skill_modifier/job/level/wiring) /datum/antagonist/ert/engineer/amber outfit = /datum/outfit/ert/engineer/alert @@ -61,6 +62,7 @@ /datum/antagonist/ert/medic role = "Medical Officer" outfit = /datum/outfit/ert/medic + skill_modifiers = list(/datum/skill_modifier/job/affinity/surgery) /datum/antagonist/ert/medic/amber outfit = /datum/outfit/ert/medic/alert diff --git a/code/modules/antagonists/nukeop/nukeop.dm b/code/modules/antagonists/nukeop/nukeop.dm index 7510ad6997..454cde6d72 100644 --- a/code/modules/antagonists/nukeop/nukeop.dm +++ b/code/modules/antagonists/nukeop/nukeop.dm @@ -5,6 +5,7 @@ job_rank = ROLE_OPERATIVE antag_moodlet = /datum/mood_event/focused threat = 10 + skill_modifiers = list(/datum/skill_modifier/job/level/wiring) var/datum/team/nuclear/nuke_team var/always_new_team = FALSE //If not assigned a team by default ops will try to join existing ones, set this to TRUE to always create new team. var/send_to_spawnpoint = TRUE //Should the user be moved to default spawnpoint. diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm index 1d30cdbf77..4422f2f174 100644 --- a/code/modules/antagonists/traitor/datum_traitor.dm +++ b/code/modules/antagonists/traitor/datum_traitor.dm @@ -4,6 +4,7 @@ antagpanel_category = "Traitor" job_rank = ROLE_TRAITOR antag_moodlet = /datum/mood_event/focused + skill_modifiers = list(/datum/skill_modifier/job/level/wiring/basic) var/special_role = ROLE_TRAITOR var/employer = "The Syndicate" var/give_objectives = TRUE diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm index 90621ecd3f..6d3bf648f6 100644 --- a/code/modules/mob/living/brain/brain_item.dm +++ b/code/modules/mob/living/brain/brain_item.dm @@ -51,6 +51,12 @@ var/datum/brain_trauma/BT = X BT.owner = owner BT.on_gain() + if(damage > BRAIN_DAMAGE_MILD) + var/datum/skill_modifier/S + ADD_SKILL_MODIFIER_BODY(/datum/skill_modifier/brain_damage, null, C, S) + if(damage > BRAIN_DAMAGE_SEVERE) + var/datum/skill_modifier/S + ADD_SKILL_MODIFIER_BODY(/datum/skill_modifier/heavy_brain_damage, null, C, S) //Update the body's icon so it doesnt appear debrained anymore C.update_hair() @@ -66,6 +72,8 @@ if((!QDELETED(src) || C) && !no_id_transfer) transfer_identity(C) if(C) + REMOVE_SKILL_MODIFIER_BODY(/datum/skill_modifier/brain_damage, null, C) + REMOVE_SKILL_MODIFIER_BODY(/datum/skill_modifier/heavy_brain_damage, null, C) C.update_hair() /obj/item/organ/brain/prepare_eat() @@ -219,31 +227,6 @@ Insert(C) else ..() -/* TO BE REMOVED, KEPT IN CASE OF BUGS -/obj/item/organ/brain/proc/get_brain_damage() - var/brain_damage_threshold = max_integrity * BRAIN_DAMAGE_INTEGRITY_MULTIPLIER - var/offset_integrity = obj_integrity - (max_integrity - brain_damage_threshold) - . = round((1 - (offset_integrity / brain_damage_threshold)) * BRAIN_DAMAGE_DEATH, DAMAGE_PRECISION) - -/obj/item/organ/brain/proc/adjust_brain_damage(amount, maximum) - var/adjusted_amount - if(amount >= 0 && maximum) - var/brainloss = get_brain_damage() - var/new_brainloss = clamp(brainloss + amount, 0, maximum) - if(brainloss > new_brainloss) //brainloss is over the cap already - return 0 - adjusted_amount = new_brainloss - brainloss - else - adjusted_amount = amount - - adjusted_amount = round(adjusted_amount * BRAIN_DAMAGE_INTEGRITY_MULTIPLIER, DAMAGE_PRECISION) - if(adjusted_amount) - if(adjusted_amount >= DAMAGE_PRECISION) - take_damage(adjusted_amount) - else if(adjusted_amount <= -DAMAGE_PRECISION) - obj_integrity = min(max_integrity, obj_integrity-adjusted_amount) - . = adjusted_amount -*/ /obj/item/organ/brain/applyOrganDamage(var/d, var/maximum = maxHealth) . = ..() @@ -261,18 +244,28 @@ . = ..() //if we're not more injured than before, return without gambling for a trauma if(damage <= prev_damage) + if(damage < prev_damage && owner) + if(prev_damage > BRAIN_DAMAGE_MILD && damage <= BRAIN_DAMAGE_MILD) + REMOVE_SKILL_MODIFIER_BODY(/datum/skill_modifier/brain_damage, null, owner) + if(prev_damage > BRAIN_DAMAGE_SEVERE && damage <= BRAIN_DAMAGE_SEVERE) + REMOVE_SKILL_MODIFIER_BODY(/datum/skill_modifier/heavy_brain_damage, null, owner) return damage_delta = damage - prev_damage if(damage > BRAIN_DAMAGE_MILD) if(prob(damage_delta * (1 + max(0, (damage - BRAIN_DAMAGE_MILD)/100)))) //Base chance is the hit damage; for every point of damage past the threshold the chance is increased by 1% //learn how to do your bloody math properly goddamnit gain_trauma_type(BRAIN_TRAUMA_MILD) + if(prev_damage <= BRAIN_DAMAGE_MILD && owner) + var/datum/skill_modifier/S + ADD_SKILL_MODIFIER_BODY(/datum/skill_modifier/brain_damage, null, owner, S) if(damage > BRAIN_DAMAGE_SEVERE) if(prob(damage_delta * (1 + max(0, (damage - BRAIN_DAMAGE_SEVERE)/100)))) //Base chance is the hit damage; for every point of damage past the threshold the chance is increased by 1% if(prob(20)) gain_trauma_type(BRAIN_TRAUMA_SPECIAL) else gain_trauma_type(BRAIN_TRAUMA_SEVERE) - + if(prev_damage <= BRAIN_DAMAGE_SEVERE && owner) + var/datum/skill_modifier/S + ADD_SKILL_MODIFIER_BODY(/datum/skill_modifier/heavy_brain_damage, null, owner, S) if (owner) if(owner.stat < UNCONSCIOUS) //conscious or soft-crit var/brain_message diff --git a/code/modules/surgery/surgery_step.dm b/code/modules/surgery/surgery_step.dm index 57661901ae..e287b38ca1 100644 --- a/code/modules/surgery/surgery_step.dm +++ b/code/modules/surgery/surgery_step.dm @@ -57,10 +57,11 @@ surgery.step_in_progress = FALSE return FALSE if(tool) - speed_mod = tool.toolspeed + speed_mod = tool.toolspeed //faster tools mean faster surgeries, but also less experience. if(user.mind) - speed_mod = user.mind.action_skill_mod(/datum/skill/numerical/surgery, speed_mod, THRESHOLD_COMPETENT, FALSE) - if(do_after(user, time * speed_mod, target = target)) + speed_mod = user.mind.action_skill_mod(/datum/skill/numerical/surgery, speed_mod, THRESHOLD_UNTRAINED, FALSE) + var/delay = time * speed_mod + if(do_after(user, delay, target = target)) var/prob_chance = 100 if(implement_type) //this means it isn't a require hand or any item step. prob_chance = implements[implement_type] @@ -68,7 +69,10 @@ if((prob(prob_chance) || (iscyborg(user) && !silicons_obey_prob)) && chem_check(target) && !try_to_fail) if(success(user, target, target_zone, tool, surgery)) - user.mind?.auto_gain_experience(/datum/skill/numerical/surgery, SKILL_GAIN_SURGERY_PER_STEP) + var/multi = (delay/SKILL_GAIN_DELAY_DIVISOR) + if(repeatable) + multi *= 0.5 //Spammable surgeries award less experience. + user.mind?.auto_gain_experience(/datum/skill/numerical/surgery, SKILL_GAIN_SURGERY_PER_STEP * multi) advance = TRUE else if(failure(user, target, target_zone, tool, surgery)) diff --git a/tgstation.dme b/tgstation.dme index 0d14ab1254..a675b2620a 100755 --- a/tgstation.dme +++ b/tgstation.dme @@ -595,6 +595,7 @@ #include "code\datums\skills\medical.dm" #include "code\datums\skills\modifiers\job.dm" #include "code\datums\skills\modifiers\mood.dm" +#include "code\datums\skills\modifiers\organs.dm" #include "code\datums\status_effects\buffs.dm" #include "code\datums\status_effects\debuffs.dm" #include "code\datums\status_effects\gas.dm"