mirror of
https://github.com/SPLURT-Station/S.P.L.U.R.T-Station-13.git
synced 2025-12-10 09:54:52 +00:00
Merge pull request #12189 from Ghommie/Ghommie-cit735
Skill modifier datums.
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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 {\
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
PROCESSING_SUBSYSTEM_DEF(mood)
|
||||
name = "Mood"
|
||||
flags = SS_NO_INIT | SS_BACKGROUND
|
||||
priority = 20
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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("")
|
||||
|
||||
184
code/datums/skills/_skill_modifier.dm
Normal file
184
code/datums/skills/_skill_modifier.dm
Normal 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)
|
||||
35
code/datums/skills/modifiers/job.dm
Normal file
35
code/datums/skills/modifiers/job.dm
Normal 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
|
||||
8
code/datums/skills/modifiers/mood.dm
Normal file
8
code/datums/skills/modifiers/mood.dm
Normal 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
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user