Merge pull request #12189 from Ghommie/Ghommie-cit735

Skill modifier datums.
This commit is contained in:
kevinz000
2020-05-14 04:46:24 -07:00
committed by GitHub
32 changed files with 431 additions and 196 deletions

View File

@@ -185,27 +185,6 @@
#define COMSIG_MOB_SPELL_CAN_CAST "mob_spell_can_cast" //from base of /obj/effect/proc_holder/spell/can_cast(): (spell)
#define COMSIG_MOB_ACTION_SKILL_MOD "mob_action_skill_mod" //from base of /datum/skill_holder/action_skills_mod() : (proc args list, list/mod_values)
#define ACTION_SKILL_MOD_SKILL 1 //redundancy.
#define ACTION_SKILL_MOD_VALUE 2
#define ACTION_SKILL_MOD_THRESHOLD 3
#define ACTION_SKILL_MOD_IS_MULTI 4
//other mod values, kept separate from the args
#define MOD_VALUES_SKILL_MOD 1
#define COMSIG_MOB_ITEM_ACTION_SKILLS_MOD "mob_item_action_skills_mod" //from base of /datum/skill_holder/item_action_skills_mod() : (proc args list, mod_values)
#define ITEM_SKILLS_MOD_ITEM 1 //redundancy
#define ITEM_SKILLS_MOD_VALUE 2
#define ITEM_SKILLS_MOD_FLAGS 3
#define ITEM_SKILLS_MOD_BAD_FLAGS 4
#define ITEM_SKILLS_MOD_IS_MULTI 5
//other mod values, kept separate from the args
#define MOD_VALUES_ITEM_SKILLS_SUM 1
#define MOD_VALUES_ITEM_SKILLS_DIV 2
#define MOD_VALUES_ITEM_SKILLS_CHECKED 3
#define COMSIG_MOB_SKILL_GET_AFFINITY "mob_skill_get_affinity" //from base of /datum/skill_holder/get_skill_affinity(): (skill_path, list/return_value)
// /mob/living signals
#define COMSIG_LIVING_REGENERATE_LIMBS "living_regenerate_limbs" //from base of /mob/living/regenerate_limbs(): (noheal, excluded_limbs)
#define COMSIG_LIVING_RESIST "living_resist" //from base of mob/living/resist() (/mob/living)

View File

@@ -20,9 +20,7 @@
// Standard values for job starting skill affinities
#define STARTING_SKILL_AFFINITY_SURGERY_MEDICAL 1.2
#define STARTING_SKILL_AFFINITY_WIRING_ENGI_ROBO 1.2
#define STARTING_SKILL_AFFINITY_DEF_JOB 1.2
// Standard values for skill gain (this is multiplied by affinity)
@@ -75,3 +73,26 @@
#define DORF_SKILL_COMPETENT 3
#define DORF_SKILL_EXPERT 8
#define DORF_SKILL_MASTER 12
/// Skill modifier defines and flags.
#define MODIFIER_SKILL_VALUE (1<<0)
#define MODIFIER_SKILL_AFFINITY (1<<1)
#define MODIFIER_SKILL_LEVEL (1<<2)
///makes the skill modifier a multiplier, not an addendum.
#define MODIFIER_SKILL_MULT (1<<3)
///Sets the skill to the defined value if lower than that. Highly reccomended you don't use it with MODIFIER_SKILL_MULT.
#define MODIFIER_SKILL_VIRTUE (1<<4)
///Does the opposite of the above. combining both effectively results in the skill being locked to the specified value.
#define MODIFIER_SKILL_HANDICAP (1<<5)
///Makes it untransferred by mind.transfer_to()
#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)
#define MODIFIER_TARGET_VALUE "value"
#define MODIFIER_TARGET_LEVEL "level"
#define MODIFIER_TARGET_AFFINITY "affinity"
///Ascending priority defines.
#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

View File

@@ -6,3 +6,43 @@
//More experience value getter macros
#define GET_STANDARD_LVL(lvl) XP_LEVEL(STD_XP_LVL_UP, STD_XP_LVL_MULTI, lvl)
#define GET_DORF_LVL(lvl) DORF_XP_LEVEL(DORF_XP_LVL_UP, DORF_XP_LVL_MULTI, lvl)
#define IS_SKILL_VALUE_GREATER(path, existing, new_value) GLOB.skill_datums[path].is_value_greater(existing, new_value)
#define SANITIZE_SKILL_VALUE(path, value) GLOB.skill_datums[path].sanitize_value(value)
///Doesn't automatically round the value.
#define SANITIZE_SKILL_LEVEL(path, lvl) clamp(lvl, 0, GLOB.skill_datums[path].max_levels)
/// Simple generic identifier macro.
#define GET_SKILL_MOD_ID(path, id) (id ? "[path]&[id]" : path)
/**
* A simple universal comsig for body bound skill modifiers.
* Technically they are still bound to the mind, but other signal procs will take care of adding and removing the modifier
* from/to new/old minds.
*/
#define ADD_SKILL_MODIFIER_BODY(path, id, body, prototype) \
prototype = GLOB.skill_modifiers[GET_SKILL_MOD_ID(path, id)] || new path(id, TRUE);\
if(body.mind){\
body.mind.add_skill_modifier(prototype.identifier)\
} else {\
prototype.RegisterSignal(body, COMSIG_MOB_ON_NEW_MIND, /datum/skill_modifier.proc/on_mob_new_mind, TRUE)\
}
/// Same as above but to remove the skill modifier.
#define REMOVE_SKILL_MODIFIER_BODY(path, id, body) \
if(GLOB.skill_modifiers[GET_SKILL_MOD_ID(path, id)]){\
if(body.mind){\
body.mind.remove_skill_modifier(GET_SKILL_MOD_ID(path, id))\
} else {\
GLOB.skill_modifiers[GET_SKILL_MOD_ID(path, id)].UnregisterSignal(body, COMSIG_MOB_ON_NEW_MIND)\
}\
}
///Macro used when adding generic singleton skill modifiers.
#define ADD_SINGLETON_SKILL_MODIFIER(mind, path, id) \
if(!GLOB.skill_modifiers[GET_SKILL_MOD_ID(path, id)]){\
new path(id, TRUE)\
};\
mind.add_skill_modifier(GET_SKILL_MOD_ID(path, id))

View File

@@ -37,6 +37,7 @@
* TYPECONT: The typepath of the contents of the list
* COMPARE: The object to compare against, usualy the same as INPUT
* COMPARISON: The variable on the objects to compare
* COMPTYPE: How the current bin item to compare against COMPARE is fetched. By key or value.
*/
#define BINARY_INSERT(INPUT, LIST, TYPECONT, COMPARE, COMPARISON, COMPTYPE) \
do {\

View File

@@ -118,11 +118,11 @@
bad_flag |= SKILL_COMBAT_MODE //blacklist combat skills.
if(I.used_skills && user.mind)
if(totitemdamage)
totitemdamage = user.mind.skill_holder.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_flag)
for(var/skill in I.used_skills)
if(!(I.used_skills[skill] & SKILL_TRAIN_ATTACK_OBJ))
continue
user.mind.skill_holder.auto_gain_experience(skill, I.skill_gain)
user.mind.auto_gain_experience(skill, I.skill_gain)
if(totitemdamage)
visible_message("<span class='danger'>[user] has hit [src] with [I]!</span>", null, null, COMBAT_MESSAGE_RANGE)
//only witnesses close by and the victim see a hit message.
@@ -163,11 +163,11 @@
if(!user.mind || !I.used_skills)
return
if(.)
. = user.mind.skill_holder.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_flag)
for(var/skill in I.used_skills)
if(!(I.used_skills[skill] & SKILL_TRAIN_ATTACK_MOB))
continue
user.mind.skill_holder.auto_gain_experience(skill, I.skill_gain)
user.mind.auto_gain_experience(skill, I.skill_gain)
/mob/living/carbon/pre_attacked_by(obj/item/I, mob/living/user)
. = ..()
@@ -216,7 +216,7 @@
. *= STAM_COST_NO_COMBAT_MULT
bad_flag |= SKILL_COMBAT_MODE
if(used_skills && user.mind)
. = user.mind.skill_holder.item_action_skills_mod(src, ., skill_difficulty, flags, bad_flag, FALSE)
. = user.mind.item_action_skills_mod(src, ., skill_difficulty, flags, bad_flag, FALSE)
/// How long this staggers for. 0 and negatives supported.
/obj/item/proc/melee_stagger_duration(force_override)

View File

@@ -1,4 +0,0 @@
PROCESSING_SUBSYSTEM_DEF(mood)
name = "Mood"
flags = SS_NO_INIT | SS_BACKGROUND
priority = 20

View File

@@ -1,3 +1,4 @@
#define ECSTATIC_SANITY_PEN -1
#define SLIGHT_INSANITY_PEN 1
#define MINOR_INSANITY_PEN 5
#define MAJOR_INSANITY_PEN 10
@@ -13,29 +14,39 @@
var/list/datum/mood_event/mood_events = list()
var/insanity_effect = 0 //is the owner being punished for low mood? If so, how much?
var/obj/screen/mood/screen_obj
var/datum/skill_modifier/bad_mood/malus
var/datum/skill_modifier/great_mood/bonus
var/static/malus_id = 0
var/static/bonus_id = 0
/datum/component/mood/Initialize()
if(!isliving(parent))
return COMPONENT_INCOMPATIBLE
START_PROCESSING(SSmood, src)
var/mob/living/owner = parent
if(owner.stat != DEAD)
START_PROCESSING(SSdcs, src)
RegisterSignal(parent, COMSIG_ADD_MOOD_EVENT, .proc/add_event)
RegisterSignal(parent, COMSIG_CLEAR_MOOD_EVENT, .proc/clear_event)
RegisterSignal(parent, COMSIG_MODIFY_SANITY, .proc/modify_sanity)
RegisterSignal(parent, COMSIG_LIVING_REVIVE, .proc/on_revive)
RegisterSignal(parent, COMSIG_MOB_HUD_CREATED, .proc/modify_hud)
var/mob/living/owner = parent
RegisterSignal(parent, COMSIG_MOB_DEATH, .proc/stop_processing)
if(owner.hud_used)
modify_hud()
var/datum/hud/hud = owner.hud_used
hud.show_hud(hud.hud_version)
/datum/component/mood/Destroy()
STOP_PROCESSING(SSmood, src)
STOP_PROCESSING(SSdcs, src)
unmodify_hud()
return ..()
/datum/component/mood/proc/stop_processing()
STOP_PROCESSING(SSdcs, src)
/datum/component/mood/proc/print_mood(mob/user)
var/msg = "<span class='info'>*---------*\n<EM>Your current mood</EM>\n"
msg += "<span class='notice'>My mental status: </span>" //Long term
@@ -127,7 +138,7 @@
else
screen_obj.icon_state = "mood[mood_level]"
/datum/component/mood/process() //Called on SSmood process
/datum/component/mood/process() //Called on SSdcs process
if(QDELETED(parent)) // workaround to an obnoxious sneaky periodical runtime.
qdel(src)
return
@@ -167,7 +178,6 @@
else if(sanity > maximum && amount > sanity - 0.5)
amount = sanity - 0.5
var/old_sanity = sanity
// Disturbed stops you from getting any more sane
if(HAS_TRAIT(master, TRAIT_UNSTABLE))
sanity = min(amount,sanity)
@@ -196,27 +206,33 @@
master.remove_movespeed_modifier(MOVESPEED_ID_SANITY)
sanity_level = 2
if(SANITY_GREAT+1 to INFINITY)
setInsanityEffect(0)
setInsanityEffect(ECSTATIC_SANITY_PEN) //It's not a penalty but w/e
master.remove_movespeed_modifier(MOVESPEED_ID_SANITY)
sanity_level = 1
if(old_sanity > 1 && sanity == 1)
RegisterSignal(master, COMSIG_MOB_SKILL_GET_AFFINITY, .proc/on_get_skill_affinity)
else if(old_sanity == 1 && sanity > 1)
UnregisterSignal(master, COMSIG_MOB_SKILL_GET_AFFINITY)
//update_mood_icon()
/datum/component/mood/proc/setInsanityEffect(newval)//More code so that the previous proc works
if(newval == insanity_effect)
return
//var/mob/living/master = parent
//master.crit_threshold = (master.crit_threshold - insanity_effect) + newval
if(!insanity_effect && newval)
RegisterSignal(parent, COMSIG_MOB_ACTION_SKILL_MOD, .proc/on_mob_action_skill_mod)
RegisterSignal(parent, COMSIG_MOB_ITEM_ACTION_SKILLS_MOD, .proc/on_item_action_skills_mod)
else if(insanity_effect && !newval)
UnregisterSignal(parent, list(COMSIG_MOB_ACTION_SKILL_MOD, COMSIG_MOB_ITEM_ACTION_SKILLS_MOD))
var/mob/living/L = parent
var/apply_malus = newval >= SLIGHT_INSANITY_PEN
var/apply_bonus = !apply_malus && newval <= ECSTATIC_SANITY_PEN
if(apply_malus)
if(!malus)
ADD_SKILL_MODIFIER_BODY(/datum/skill_modifier/bad_mood, malus_id++, L, malus)
var/debuff = 1 - (SANITY_DISTURBED - sanity) * MOOD_INSANITY_MALUS
malus.value_mod = malus.level_mod = debuff
else if(malus)
QDEL_NULL(malus)
if(apply_bonus)
if(!bonus)
ADD_SKILL_MODIFIER_BODY(/datum/skill_modifier/great_mood, bonus_id++, L, bonus)
else if(bonus)
QDEL_NULL(bonus)
insanity_effect = newval
/datum/component/mood/proc/modify_sanity(datum/source, amount, minimum = SANITY_INSANE, maximum = SANITY_AMAZING)
@@ -294,46 +310,15 @@
if(0 to NUTRITION_LEVEL_STARVING)
add_event(null, "nutrition", /datum/mood_event/starving)
///Called when parent is ahealed.
///Called when parent is revived.
/datum/component/mood/proc/on_revive(datum/source, full_heal)
START_PROCESSING(SSdcs, src)
if(!full_heal)
return
remove_temp_moods()
setSanity(initial(sanity))
/datum/component/mood/proc/on_mob_action_skill_mod(mob/source, list/skill_args, list/mod_values)
var/datum/skill/S = GLOB.skill_datums[skill_args[ACTION_SKILL_MOD_SKILL]]
if(!(S.skill_flags & SKILL_USE_MOOD))
return
var/debuff = 1 - (SANITY_DISTURBED - sanity) * MOOD_INSANITY_MALUS
mod_values[MOD_VALUES_SKILL_MOD] *= skill_args[ACTION_SKILL_MOD_IS_MULTI] ? debuff : 1/debuff
/datum/component/mood/proc/on_item_action_skills_mod(mob/source, list/skill_args, list/mod_values)
if(skill_args[ITEM_SKILLS_MOD_BAD_FLAGS] & SKILL_USE_MOOD)
return
var/divisor = mod_values[MOD_VALUES_ITEM_SKILLS_DIV]
if(!divisor)
return
var/obj/item/I = skill_args[ITEM_SKILLS_MOD_ITEM]
var/list/L = mod_values[MOD_VALUES_ITEM_SKILLS_CHECKED]
var/skills_len = length(L)
var/affected_skills = skills_len
for(var/k in L)
var/datum/skill/S = k
var/our_flags = I.used_skills[S.type]|S.skill_flags
if(!(our_flags & SKILL_USE_MOOD))
affected_skills--
if(!affected_skills)
return
var/debuff = 1 - (SANITY_DISTURBED - sanity) * MOOD_INSANITY_MALUS * (affected_skills/skills_len)
mod_values[MOD_VALUES_ITEM_SKILLS_SUM] *= skill_args[ITEM_SKILLS_MOD_IS_MULTI] ? debuff : 1/debuff
/datum/component/mood/proc/on_get_skill_affinity(mob/source, skill_path, list/return_value)
var/datum/skill/S = GLOB.skill_datums[skill_path]
if(!S || !(S.skill_flags & SKILL_TRAIN_MOOD))
return
return_value[1] *= SKILL_AFFINITY_MOOD_BONUS
#undef ECSTATIC_SANITY_PEN
#undef SLIGHT_INSANITY_PEN
#undef MINOR_INSANITY_PEN
#undef MAJOR_INSANITY_PEN

View File

@@ -67,7 +67,7 @@
var/datum/skill_holder/skill_holder
/datum/mind/New(var/key)
skill_holder = new(src)
skill_holder = new()
src.key = key
soulOwner = src
martial_art = default_martial_art

View File

@@ -12,5 +12,5 @@
to_chat(usr, "<span class='warning'>How do you check the skills of [(usr == src)? "yourself when you are" : "something"] without the capability for skills? (PROBABLY A BUG, PRESS F1.)</span>")
return
var/datum/browser/B = new(usr, "skilldisplay_[REF(src)]", "Skills of [src]")
B.set_content(mind.skill_holder.html_readout())
B.set_content(mind.skill_html_readout())
B.open()

View File

@@ -9,19 +9,10 @@ GLOBAL_LIST_INIT_TYPED(skill_datums, /datum/skill, init_skill_datums())
S = new path
.[S.type] = S
/proc/sanitize_skill_value(path, value)
var/datum/skill/S = GLOB.skill_datums[path]
// don't check, if we runtime let it happen.
return S.sanitize_value(value)
/proc/is_skill_value_greater(path, existing, new_value)
var/datum/skill/S = GLOB.skill_datums[path]
// don't check, if we runtime let it happen.
return S.is_value_greater(existing, new_value)
/**
* Skill datums
*/
/datum/skill
/// Our name
var/name
@@ -33,11 +24,13 @@ GLOBAL_LIST_INIT_TYPED(skill_datums, /datum/skill, init_skill_datums())
var/progression_type
/// Abstract type
var/abstract_type = /datum/skill
/// skill threshold used in generic skill modifiers calculations.
/// 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.
/// In which way this skil can affect or be affected through actions and skill modifiers.
var/skill_flags = SKILL_USE_MOOD|SKILL_TRAIN_MOOD
/**
@@ -114,9 +107,9 @@ GLOBAL_LIST_INIT_TYPED(skill_datums, /datum/skill, init_skill_datums())
/datum/skill/level
abstract_type = /datum/skill/level
progression_type = SKILL_PROGRESSION_LEVEL
max_levels = STD_MAX_LVL
var/standard_xp_lvl_up = STD_XP_LVL_UP //the standard required to level up. def: 100
var/xp_lvl_multiplier = STD_XP_LVL_MULTI //standard required level up exp multiplier. def: 2 (100, 200, 400, 800 etc.)
var/max_levels = STD_MAX_LVL
var/level_up_method = STANDARD_LEVEL_UP //how levels are calculated.
var/list/levels = list() //level thresholds, if associative, these will be preceded by tiers such as "novice" or "trained"
var/associative = FALSE //See above.
@@ -177,15 +170,15 @@ GLOBAL_LIST_INIT_TYPED(skill_datums, /datum/skill, init_skill_datums())
var/current_lvl_xp_sum = 0
if(level)
current_lvl_xp_sum = associative ? levels[levels[level]] : levels[level]
var/next_index = max(max_levels, level+1)
var/next_index = min(max_levels, level+1)
var/next_lvl_xp = associative ? levels[levels[next_index]] : levels[next_index]
if(next_lvl_xp > current_lvl_xp_sum)
next_lvl_xp -= current_lvl_xp_sum
return "[associative ? current_lvl : "Lvl. [current_lvl]"] ([value - current_lvl_xp_sum]/[next_lvl_xp])[level == max_levels ? " \[MAX!\]" : ""]"
/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)

View File

@@ -1,8 +1,8 @@
/**
* Skill holder datums
* All procs are tied to the mind, since they are always expected to have a skill holder anyway.
*/
/datum/skill_holder
var/datum/mind/owner
/// 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.
@@ -10,66 +10,87 @@
/// 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
/datum/skill_holder/New(datum/mind/M)
. = ..()
owner = M
/// 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
/**
* Grabs the value of a skill.
*/
/datum/skill_holder/proc/get_skill_value(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(!skills)
return null
return skills[skill]
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/skill_holder/proc/get_skill_level(skill)
/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_levels)
return 0
return skill_levels[skill]
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/skill_holder/proc/get_skill_affinity(skill)
/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_affinities, skill)
var/affinity = LAZYACCESS(skill_holder.skill_affinities, skill)
if(!isnull(affinity))
. = affinity
var/list/wrapped = list(.)
SEND_SIGNAL(owner.current, COMSIG_MOB_SKILL_GET_AFFINITY, skill, wrapped)
. = wrapped[1]
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/skill_holder/proc/set_skill_value(skill, value, silent = FALSE)
/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(skills)
S.set_skill_value(src, value, owner, silent)
LAZYINITLIST(skill_holder.skills)
S.set_skill_value(skill_holder, value, src, silent)
return TRUE
return FALSE
/**
* Boosts a skill to a value if not aobve
*/
/datum/skill_holder/proc/boost_skill_value_to(skill, value, silent = FALSE)
var/current = get_skill_value(skill)
if(!is_skill_value_greater(skill, current, value))
/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
@@ -78,20 +99,20 @@
* Automatic skill increase, multiplied by skill affinity if existing.
* Only works if skill is numerical.
*/
/datum/skill_holder/proc/auto_gain_experience(skill, value, maximum, silent = FALSE)
/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)
var/current = get_skill_value(skill, FALSE)
var/affinity = get_skill_affinity(skill)
var/target_value = current + (value * affinity)
if(maximum)
target_value = max(target_value, maximum)
target_value = min(target_value, maximum)
if(target_value == maximum) //no more experience to gain, early return.
return
boost_skill_value_to(skill, target_value, silent)
boost_skill_value_to(skill, target_value, silent, current)
/**
* Generic value modifier proc that uses one skill.
@@ -100,22 +121,17 @@
* * 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/skill_holder/proc/action_skills_mod(skill, value, threshold, modifier_is_multiplier = TRUE)
/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
switch(S.progression_type)
if(SKILL_PROGRESSION_LEVEL)
mod = LAZYACCESS(skill_levels, S.type)
mod = get_skill_level(S.type)
else
mod = LAZYACCESS(skills, S.type)
mod = get_skill_value(S.type)
mod = (1+(mod-S.competency_thresholds[threshold])*S.competency_mults[threshold])
var/list/comsig_values = list(mod)
SEND_SIGNAL(owner.current, COMSIG_MOB_ACTION_SKILL_MOD, args, comsig_values)
mod = comsig_values[MOD_VALUES_SKILL_MOD]
. = modifier_is_multiplier ? value*mod : value/mod
/**
@@ -128,11 +144,10 @@
* * bad_flags : the opposite of the above, skills that must not be present to impact the value.
* * modifier_is_multiplier : wheter the modifier is a multiplier or a divisor.
*/
/datum/skill_holder/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, flags = NONE, bad_flags = NONE, modifier_is_multiplier = TRUE)
. = value
var/sum = 0
var/divisor = 0
var/list/checked_skills
for(var/k in I.used_skills)
var/datum/skill/S = GLOB.skill_datums[k]
if(!S)
@@ -143,17 +158,10 @@
var/mod
switch(S.progression_type)
if(SKILL_PROGRESSION_LEVEL)
mod = LAZYACCESS(skill_levels, S.type)
mod = get_skill_level(S.type)
else
mod = LAZYACCESS(skills, S.type)
mod = get_skill_value(S.type)
sum += 1+(mod - S.competency_thresholds[I.skill_difficulty])*S.competency_mults[I.skill_difficulty]
LAZYADD(checked_skills, S)
var/list/comsig_values = list(sum, divisor, checked_skills)
SEND_SIGNAL(owner.current, COMSIG_MOB_ITEM_ACTION_SKILLS_MOD, args, comsig_values)
sum = comsig_values[MOD_VALUES_ITEM_SKILLS_SUM]
divisor = comsig_values[MOD_VALUES_ITEM_SKILLS_DIV]
if(divisor)
. = modifier_is_multiplier ? value*(sum/divisor) : value/(sum/divisor)
@@ -161,11 +169,13 @@
* Generates a HTML readout of our skills.
* Port to tgui-next when?
*/
/datum/skill_holder/proc/html_readout()
/datum/mind/proc/skill_html_readout()
var/list/out = list("<center><h1>Skills</h1></center><hr>")
out += "<table style=\"width:100%\"><tr><th><b>Skill</b><th><b>Value</b></tr>"
for(var/path in skills)
for(var/path in skill_holder.skills|skill_holder.skill_value_mods|skill_holder.skill_level_mods)
var/datum/skill/S = GLOB.skill_datums[path]
out += "<tr><td><font color='[S.name_color]'>[S.name]</font></td><td>[S.standard_render_value(skills[path], LAZYACCESS(skill_levels, path) || 0)]</td></tr>"
var/skill_value = get_skill_value(path)
var/skill_level = get_skill_level(path, round = TRUE)
out += "<tr><td><font color='[S.name_color]'>[S.name]</font></td><td>[S.standard_render_value(skill_value, skill_level)]</td></tr>"
out += "</table>"
return out.Join("")

View File

@@ -0,0 +1,184 @@
GLOBAL_LIST_EMPTY_TYPED(skill_modifiers, /datum/skill_modifier)
GLOBAL_LIST_EMPTY(potential_skills_per_mod)
GLOBAL_LIST_EMPTY(potential_mods_per_skill)
/**
* Base skill modifier datum, used to modify a player skills without directly touching their values, levels and affinity
* and cause lots of edge cases. These are fairly simple overall... make a subtype though, don't use this one.
*/
/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
/// 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.
var/affinity_mod = 1
/// skill value modifier, see above.
var/value_mod = 1
/// skill level modifier, see above.
var/level_mod = 1
/// Priority of this skill modifier compared to other ones.
var/priority = MODIFIER_SKILL_PRIORITY_DEF
/datum/skill_modifier/New(id, register = FALSE)
identifier = GET_SKILL_MOD_ID(type, id)
if(id)
var/former_id = identifier
var/dupe = 0
while(GLOB.skill_modifiers[identifier])
identifier = "[former_id][++dupe]"
if(register)
register()
/datum/skill_modifier/proc/register()
if(GLOB.skill_modifiers[identifier])
CRASH("Skill modifier identifier \"[identifier]\" already taken.")
GLOB.skill_modifiers[identifier] = src
if(ispath(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]"]
if(!L)
L = list()
for(var/path in GLOB.skill_datums)
if(GLOB.skill_datums[path].skill_flags & target_skills)
L += path
GLOB.potential_skills_per_mod["[target_skills]"] = L
for(var/path in L)
var/list/mod_L = GLOB.potential_mods_per_skill[path]
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
/datum/skill_modifier/Destroy()
for(var/A in GLOB.potential_skills_per_mod["[target_skills]"])
LAZYREMOVEASSOC(GLOB.potential_mods_per_skill, A, identifier)
GLOB.potential_skills_per_mod -= "[target_skills]"
GLOB.skill_modifiers -= src
return ..()
#define ADD_MOD_STEP(L, P, O, G) \
var/__L = L[P];\
if(!__L){\
L[P] = list(id)\
} else {\
L[P] = GLOB.potential_mods_per_skill[P] & (__L + id)\
}\
if(M.modifier_flags & MODIFIER_SKILL_ORIGIN_DIFF){\
LAZYADDASSOC(O, id, "[P]" = G)\
}
/datum/mind/proc/add_skill_modifier(id)
if(LAZYACCESS(skill_holder.all_current_skill_modifiers, id))
return
var/datum/skill_modifier/M = GLOB.skill_modifiers[id]
if(!M)
CRASH("Invalid add_skill_modifier id: [id].")
if(M.modifier_flags & MODIFIER_SKILL_BODYBOUND && !current)
CRASH("Body-bound skill modifier [M] was tried to be added to a mob-less mind.")
if(M.modifier_flags & MODIFIER_SKILL_VALUE)
LAZYINITLIST(skill_holder.skill_value_mods)
if(M.modifier_flags & MODIFIER_SKILL_AFFINITY)
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]"])
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)
ADD_MOD_STEP(skill_holder.skill_affinity_mods, path, skill_holder.original_affinities, get_skill_affinity(path, FALSE))
if(M.modifier_flags & MODIFIER_SKILL_LEVEL)
ADD_MOD_STEP(skill_holder.skill_level_mods, path, skill_holder.original_levels, get_skill_level(path, FALSE))
LAZYSET(skill_holder.all_current_skill_modifiers, id, TRUE)
if(M.modifier_flags & MODIFIER_SKILL_BODYBOUND)
M.RegisterSignal(src, COMSIG_MIND_TRANSFER, /datum/skill_modifier.proc/on_mind_transfer)
M.RegisterSignal(current, COMSIG_MOB_ON_NEW_MIND, /datum/skill_modifier.proc/on_mob_new_mind, TRUE)
RegisterSignal(M, COMSIG_PARENT_QDELETING, .proc/on_skill_modifier_deletion)
#undef ADD_MOD_STEP
#define REMOVE_MOD_STEP(L, P, O)\
LAZYREMOVEASSOC(L, P, id);\
if(M.modifier_flags & MODIFIER_SKILL_ORIGIN_DIFF){\
LAZYREMOVEASSOC(O, id, "[P]")\
}
/datum/mind/proc/remove_skill_modifier(id, mind_transfer = FALSE)
if(!LAZYACCESS(skill_holder.all_current_skill_modifiers, id))
return
var/datum/skill_modifier/M = GLOB.skill_modifiers[id]
if(!M)
CRASH("Invalid remove_skill_modifier id: [id].")
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]"])
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)
REMOVE_MOD_STEP(skill_holder.skill_affinity_mods, path, skill_holder.original_affinities)
if(M.modifier_flags & MODIFIER_SKILL_LEVEL)
REMOVE_MOD_STEP(skill_holder.skill_level_mods, path, skill_holder.original_levels)
LAZYREMOVE(skill_holder.all_current_skill_modifiers, id)
if(!mind_transfer && M.modifier_flags & MODIFIER_SKILL_BODYBOUND)
M.UnregisterSignal(src, COMSIG_MIND_TRANSFER)
M.UnregisterSignal(current, list(COMSIG_MOB_ON_NEW_MIND))
UnregisterSignal(M, COMSIG_PARENT_QDELETING)
#undef REMOVE_MOD_STEP
/datum/mind/proc/on_skill_modifier_deletion(datum/skill_modifier/source)
remove_skill_modifier(source.identifier)
/datum/skill_modifier/proc/apply_modifier(value, skillpath, datum/skill_holder/H, method = MODIFIER_TARGET_VALUE)
. = value
var/mod = value_mod
switch(method)
if(MODIFIER_TARGET_LEVEL)
mod = level_mod
if(MODIFIER_TARGET_AFFINITY)
mod = affinity_mod
var/diff = 0
if(modifier_flags & (MODIFIER_SKILL_VIRTUE|MODIFIER_SKILL_HANDICAP))
if(modifier_flags & MODIFIER_SKILL_VIRTUE)
. = max(., mod)
if(modifier_flags & MODIFIER_SKILL_HANDICAP)
. = min(., mod)
diff = . - mod
else if(modifier_flags & MODIFIER_SKILL_MULT)
. *= mod
else
. += mod
if(modifier_flags & MODIFIER_SKILL_ORIGIN_DIFF)
var/list/to_access = H.original_values
switch(method)
if(MODIFIER_TARGET_LEVEL)
to_access = H.original_levels
if(MODIFIER_TARGET_AFFINITY)
to_access = H.original_affinities
. += value - diff - LAZYACCESS(to_access[identifier], "[skillpath]")
///Body bound modifier signal procs.
/datum/skill_modifier/proc/on_mind_transfer(datum/mind/source, mob/new_character, mob/old_character)
source.remove_skill_modifier(identifier, TRUE)
UnregisterSignal(source, COMSIG_MIND_TRANSFER)
/datum/skill_modifier/proc/on_mob_new_mind(mob/source)
source.mind.add_skill_modifier(identifier)
RegisterSignal(source.mind, COMSIG_MIND_TRANSFER, /datum/skill_modifier.proc/on_mind_transfer)

View File

@@ -0,0 +1,35 @@
/// Jobbie skill modifiers.
/datum/skill_modifier/job
modifier_flags = MODIFIER_SKILL_VALUE|MODIFIER_SKILL_VIRTUE|MODIFIER_SKILL_ORIGIN_DIFF
priority = MODIFIER_SKILL_PRIORITY_MAX
/datum/skill_modifier/job/surgery
target_skills = /datum/skill/numerical/surgery
value_mod = STARTING_SKILL_SURGERY_MEDICAL
/datum/skill_modifier/job/affinity
modifier_flags = MODIFIER_SKILL_AFFINITY|MODIFIER_SKILL_VIRTUE
affinity_mod = STARTING_SKILL_AFFINITY_DEF_JOB
/datum/skill_modifier/job/affinity/surgery
target_skills = /datum/skill/numerical/surgery
/datum/skill_modifier/job/affinity/wiring
target_skills = /datum/skill/level/job/wiring
/// Level skill modifiers below.
/datum/skill_modifier/job/level
modifier_flags = MODIFIER_SKILL_VALUE|MODIFIER_SKILL_LEVEL|MODIFIER_SKILL_VIRTUE|MODIFIER_SKILL_ORIGIN_DIFF
level_mod = JOB_SKILL_TRAINED
/datum/skill_modifier/job/level/New(id)
if(level_mod)
value_mod = GET_STANDARD_LVL(level_mod)
..()
/datum/skill_modifier/job/level/wiring
target_skills = /datum/skill/level/job/wiring
/datum/skill_modifier/job/level/wiring/basic
level_mod = JOB_SKILL_BASIC

View File

@@ -0,0 +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
/datum/skill_modifier/great_mood
modifier_flags = MODIFIER_SKILL_AFFINITY|MODIFIER_SKILL_MULT|MODIFIER_SKILL_BODYBOUND
target_skills = SKILL_TRAIN_MOOD
affinity_mod = 1.2

View File

@@ -138,7 +138,7 @@
if(current_users[user])
return FALSE
if(req_skill && user?.mind)
var/level_diff = req_skill - user.mind.skill_holder.get_skill_level(/datum/skill/level/job/wiring)
var/level_diff = req_skill - user.mind.get_skill_level(/datum/skill/level/job/wiring, round = TRUE)
if(level_diff > 0)
LAZYSET(current_users, user, TRUE)
to_chat(user, "<span class='notice'>You begin cutting [holder]'s [color] wire...</span>")
@@ -167,7 +167,7 @@
if(current_users[user])
return FALSE
if(req_skill && user?.mind)
var/level_diff = req_skill - user.mind.skill_holder.get_skill_level(/datum/skill/level/job/wiring)
var/level_diff = req_skill - user.mind.get_skill_level(/datum/skill/level/job/wiring, round = TRUE)
if(level_diff > 0)
LAZYSET(current_users, user, TRUE)
to_chat(user, "<span class='notice'>You begin pulsing [holder]'s [color] wire...</span>")
@@ -255,7 +255,7 @@
var/reveal_wires = FALSE
// Admin ghost can see a purpose of each wire.
if(IsAdminGhost(user) || user.mind.skill_holder.get_skill_level(/datum/skill/level/job/wiring) >= req_knowledge)
if(IsAdminGhost(user) || user.mind.get_skill_level(/datum/skill/level/job/wiring) >= req_knowledge)
reveal_wires = TRUE
// Same for anyone with an abductor multitool.

View File

@@ -802,7 +802,7 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
if(delay)
if(user.mind && used_skills)
delay = user.mind.skill_holder.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, NONE, 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)
@@ -832,7 +832,7 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
for(var/skill in used_skills)
if(!(used_skills[skill] & SKILL_TRAINING_TOOL))
continue
user.mind.skill_holder.auto_gain_experience(skill, skill_gain*skill_gain_mult, GET_STANDARD_LVL(max_level))
user.mind.auto_gain_experience(skill, skill_gain*skill_gain_mult, GET_STANDARD_LVL(max_level))
return TRUE

View File

@@ -1025,8 +1025,6 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
v = SSnpcpool
if("SSmobs")
v = SSmobs
if("SSmood")
v = SSmood
if("SSquirks")
v = SSquirks
if("SSwet_floors")

View File

@@ -69,10 +69,8 @@
// How much threat this job is worth in dynamic. Is subtracted if the player's not an antag, added if they are.
var/threat = 0
/// Starting skill levels.
var/list/starting_skills
/// Skill affinities to set
var/list/skill_affinities
/// Starting skill modifiers.
var/list/starting_modifiers
//Only override this proc
//H is usually a human unless an /equip override transformed it
@@ -180,15 +178,10 @@
to_chat(M, "<b>Prefix your message with :h to speak on your department's radio. To see other prefixes, look closely at your headset.</b>")
/datum/job/proc/standard_assign_skills(datum/mind/M)
if(!starting_skills)
if(!starting_modifiers)
return
for(var/skill in starting_skills)
M.skill_holder.boost_skill_value_to(skill, starting_skills[skill], TRUE) //silent
// do wipe affinities though
M.skill_holder.skill_affinities = list()
for(var/skill in skill_affinities)
M.skill_holder.skill_affinities[skill] = skill_affinities[skill]
UNSETEMPTY(M.skill_holder.skill_affinities) //if we didn't set any.
for(var/mod in starting_modifiers)
ADD_SINGLETON_SKILL_MODIFIER(M, mod, null)
/datum/outfit/job
name = "Standard Gear"

View File

@@ -17,7 +17,7 @@
var/do_special_check = TRUE
threat = 5
starting_skills = list(/datum/skill/level/job/wiring = GET_STANDARD_LVL(JOB_SKILL_BASIC))
starting_modifiers = list(/datum/skill_modifier/job/level/wiring/basic)
/datum/job/ai/equip(mob/living/carbon/human/H, visualsOnly, announce, latejoin, datum/outfit/outfit_override, client/preference_source = null)
if(visualsOnly)

View File

@@ -20,8 +20,7 @@
paycheck = PAYCHECK_MEDIUM
paycheck_department = ACCOUNT_ENG
starting_skills = list(/datum/skill/level/job/wiring = GET_STANDARD_LVL(JOB_SKILL_BASIC))
skill_affinities = list(/datum/skill/level/job/wiring = STARTING_SKILL_AFFINITY_WIRING_ENGI_ROBO)
starting_modifiers = list(/datum/skill_modifier/job/level/wiring/basic, /datum/skill_modifier/job/affinity/wiring)
display_order = JOB_DISPLAY_ORDER_ATMOSPHERIC_TECHNICIAN
threat = 0.5

View File

@@ -21,8 +21,7 @@
display_order = JOB_DISPLAY_ORDER_CHEMIST
threat = 1.5
starting_skills = list(/datum/skill/numerical/surgery = STARTING_SKILL_SURGERY_MEDICAL)
skill_affinities = list(/datum/skill/numerical/surgery = STARTING_SKILL_AFFINITY_SURGERY_MEDICAL)
starting_modifiers = list(/datum/skill_modifier/job/surgery, /datum/skill_modifier/job/affinity/surgery)
/datum/outfit/job/chemist
name = "Chemist"

View File

@@ -29,8 +29,7 @@
paycheck = PAYCHECK_COMMAND
paycheck_department = ACCOUNT_ENG
starting_skills = list(/datum/skill/level/job/wiring = GET_STANDARD_LVL(JOB_SKILL_TRAINED))
skill_affinities = list(/datum/skill/level/job/wiring = STARTING_SKILL_AFFINITY_WIRING_ENGI_ROBO)
starting_modifiers = list(/datum/skill_modifier/job/level/wiring, /datum/skill_modifier/job/affinity/wiring)
display_order = JOB_DISPLAY_ORDER_CHIEF_ENGINEER
blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/brainproblems, /datum/quirk/paraplegic, /datum/quirk/insanity)

View File

@@ -31,8 +31,7 @@
blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/brainproblems, /datum/quirk/insanity)
threat = 2
starting_skills = list(/datum/skill/numerical/surgery = STARTING_SKILL_SURGERY_MEDICAL)
skill_affinities = list(/datum/skill/numerical/surgery = STARTING_SKILL_AFFINITY_SURGERY_MEDICAL)
starting_modifiers = list(/datum/skill_modifier/job/surgery, /datum/skill_modifier/job/affinity/surgery)
/datum/outfit/job/cmo
name = "Chief Medical Officer"

View File

@@ -12,7 +12,7 @@
exp_requirements = 120
exp_type = EXP_TYPE_CREW
starting_skills = list(/datum/skill/level/job/wiring = GET_STANDARD_LVL(JOB_SKILL_BASIC))
starting_modifiers = list(/datum/skill_modifier/job/level/wiring/basic)
display_order = JOB_DISPLAY_ORDER_CYBORG

View File

@@ -21,7 +21,7 @@
display_order = JOB_DISPLAY_ORDER_GENETICIST
threat = 1.5
starting_skills = list(/datum/skill/numerical/surgery = STARTING_SKILL_SURGERY_MEDICAL)
starting_modifiers = list(/datum/skill_modifier/job/surgery, /datum/skill_modifier/job/affinity/surgery)
/datum/outfit/job/geneticist
name = "Geneticist"

View File

@@ -19,8 +19,7 @@
display_order = JOB_DISPLAY_ORDER_MEDICAL_DOCTOR
threat = 0.5
starting_skills = list(/datum/skill/numerical/surgery = STARTING_SKILL_SURGERY_MEDICAL)
skill_affinities = list(/datum/skill/numerical/surgery = STARTING_SKILL_AFFINITY_SURGERY_MEDICAL)
starting_modifiers = list(/datum/skill_modifier/job/surgery, /datum/skill_modifier/job/affinity/surgery)
/datum/outfit/job/doctor
name = "Medical Doctor"

View File

@@ -21,8 +21,7 @@
threat = 0.5
starting_skills = list(/datum/skill/numerical/surgery = STARTING_SKILL_SURGERY_MEDICAL)
skill_affinities = list(/datum/skill/numerical/surgery = STARTING_SKILL_AFFINITY_SURGERY_MEDICAL)
starting_modifiers = list(/datum/skill_modifier/job/surgery, /datum/skill_modifier/job/affinity/surgery)
/datum/outfit/job/paramedic
name = "Paramedic"

View File

@@ -18,9 +18,7 @@
paycheck = PAYCHECK_MEDIUM
paycheck_department = ACCOUNT_SCI
starting_skills = list(/datum/skill/level/job/wiring = GET_STANDARD_LVL(JOB_SKILL_TRAINED))
skill_affinities = list(/datum/skill/level/job/wiring = STARTING_SKILL_AFFINITY_WIRING_ENGI_ROBO)
starting_modifiers = list(/datum/skill_modifier/job/level/wiring, /datum/skill_modifier/job/affinity/wiring)
display_order = JOB_DISPLAY_ORDER_ROBOTICIST
threat = 1

View File

@@ -20,8 +20,7 @@
paycheck = PAYCHECK_MEDIUM
paycheck_department = ACCOUNT_ENG
starting_skills = list(/datum/skill/level/job/wiring = GET_STANDARD_LVL(JOB_SKILL_TRAINED))
skill_affinities = list(/datum/skill/level/job/wiring = STARTING_SKILL_AFFINITY_WIRING_ENGI_ROBO)
starting_modifiers = list(/datum/skill_modifier/job/level/wiring, /datum/skill_modifier/job/affinity/wiring)
display_order = JOB_DISPLAY_ORDER_STATION_ENGINEER

View File

@@ -22,8 +22,7 @@
threat = 1.5
starting_skills = list(/datum/skill/numerical/surgery = STARTING_SKILL_SURGERY_MEDICAL)
skill_affinities = list(/datum/skill/numerical/surgery = STARTING_SKILL_AFFINITY_SURGERY_MEDICAL)
starting_modifiers = list(/datum/skill_modifier/job/surgery, /datum/skill_modifier/job/affinity/surgery)
/datum/outfit/job/virologist
name = "Virologist"

View File

@@ -59,7 +59,7 @@
if(tool)
speed_mod = tool.toolspeed
if(user.mind)
speed_mod = user.mind.skill_holder.action_skills_mod(/datum/skill/numerical/surgery, speed_mod, THRESHOLD_COMPETENT, FALSE)
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))
var/prob_chance = 100
if(implement_type) //this means it isn't a require hand or any item step.
@@ -68,7 +68,7 @@
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?.skill_holder.auto_gain_experience(/datum/skill/numerical/surgery, SKILL_GAIN_SURGERY_PER_STEP)
user.mind?.auto_gain_experience(/datum/skill/numerical/surgery, SKILL_GAIN_SURGERY_PER_STEP)
advance = TRUE
else
if(failure(user, target, target_zone, tool, surgery))

View File

@@ -291,7 +291,6 @@
#include "code\controllers\subsystem\minimaps.dm"
#include "code\controllers\subsystem\minor_mapping.dm"
#include "code\controllers\subsystem\mobs.dm"
#include "code\controllers\subsystem\moods.dm"
#include "code\controllers\subsystem\nightshift.dm"
#include "code\controllers\subsystem\npcpool.dm"
#include "code\controllers\subsystem\overlays.dm"
@@ -585,8 +584,11 @@
#include "code\datums\skills\_check_skills.dm"
#include "code\datums\skills\_skill.dm"
#include "code\datums\skills\_skill_holder.dm"
#include "code\datums\skills\_skill_modifier.dm"
#include "code\datums\skills\engineering.dm"
#include "code\datums\skills\medical.dm"
#include "code\datums\skills\modifiers\job.dm"
#include "code\datums\skills\modifiers\mood.dm"
#include "code\datums\status_effects\buffs.dm"
#include "code\datums\status_effects\debuffs.dm"
#include "code\datums\status_effects\gas.dm"