diff --git a/code/__DEFINES/skills/defines.dm b/code/__DEFINES/skills/defines.dm
index 60d2321927..47aaeeb1dc 100644
--- a/code/__DEFINES/skills/defines.dm
+++ b/code/__DEFINES/skills/defines.dm
@@ -8,12 +8,6 @@
/// Levels
#define SKILL_PROGRESSION_LEVEL 4
-
-/// Max value of skill for numerical skills
-#define SKILL_NUMERICAL_MAX 100
-/// Min value of skill for numerical skills
-#define SKILL_NUMERICAL_MIN 0
-
// Standard values for job starting skills
#define STARTING_SKILL_SURGERY_MEDICAL 35 //out of SKILL_NUMERICAL_MAX
@@ -26,6 +20,13 @@
#define DEF_SKILL_GAIN 1
#define SKILL_GAIN_SURGERY_PER_STEP 0.25
+#define STD_USE_TOOL_MULT 1
+#define EASY_USE_TOOL_MULT 0.75
+#define TRIVIAL_USE_TOOL_MULT 0.5
+#define BARE_USE_TOOL_MULT 0.25
+
+//multiplier of the difference of max_value and min_value. Mostly for balance purposes between numerical and level-based skills.
+#define STD_NUM_SKILL_ITEM_GAIN_MULTI 0.002
//An extra point for each few seconds of delay when using a tool. Before the multiplier.
#define SKILL_GAIN_DELAY_DIVISOR 3 SECONDS
diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm
index 106db7f40a..bef06a69e9 100644
--- a/code/_onclick/item_attack.dm
+++ b/code/_onclick/item_attack.dm
@@ -123,7 +123,7 @@
if(totitemdamage)
totitemdamage = user.mind.item_action_skills_mod(I, totitemdamage, I.skill_difficulty, SKILL_ATTACK_OBJ, bad_trait)
for(var/skill in I.used_skills)
- if(!(I.used_skills[skill] & SKILL_TRAIN_ATTACK_OBJ))
+ if(!(SKILL_TRAIN_ATTACK_OBJ in I.used_skills[skill]))
continue
user.mind.auto_gain_experience(skill, I.skill_gain)
@@ -192,9 +192,10 @@
if(.)
. = user.mind.item_action_skills_mod(I, ., I.skill_difficulty, SKILL_ATTACK_MOB, bad_trait)
for(var/skill in I.used_skills)
- if(!(I.used_skills[skill] & SKILL_TRAIN_ATTACK_MOB))
+ if(!(SKILL_TRAIN_ATTACK_MOB in I.used_skills[skill]))
continue
- user.mind.auto_gain_experience(skill, I.skill_gain)
+ var/datum/skill/S = GLOB.skill_datums[skill]
+ user.mind.auto_gain_experience(skill, I.skill_gain*S.item_skill_gain_multi)
// Proximity_flag is 1 if this afterattack was called on something adjacent, in your square, or on your person.
// Click parameters is the params string from byond Click() code, see that documentation.
diff --git a/code/datums/skills/_skill.dm b/code/datums/skills/_skill.dm
index e3f1d00ae6..eecf416b1b 100644
--- a/code/datums/skills/_skill.dm
+++ b/code/datums/skills/_skill.dm
@@ -33,6 +33,10 @@ GLOBAL_LIST_INIT_TYPED(skill_datums, /datum/skill, init_skill_datums())
var/base_multiplier = 1
/// Value added to the base multiplier depending on overall competency compared to maximum value/level.
var/competency_multiplier = 1
+ /// Experience gain multiplier gained from using items.
+ var/item_skill_gain_multi = 1
+ /// Skill gain quantisation
+ var/skill_gain_quantisation = 0.1
/// A list of ways this skill can affect or be affected through actions and skill modifiers.
var/list/skill_traits = list(SKILL_SANITY, SKILL_INTELLIGENCE)
/// Index of this skill in the UI
@@ -108,6 +112,10 @@ GLOBAL_LIST_INIT_TYPED(skill_datums, /datum/skill, init_skill_datums())
/// Min value of this skill
var/min_value = 0
+/datum/skill/numerical/New()
+ ..()
+ skill_gain_quantisation = item_skill_gain_multi = item_skill_gain_multi * (max_value - min_value) * STD_NUM_SKILL_ITEM_GAIN_MULTI
+
/datum/skill/numerical/sanitize_value(new_value)
return clamp(new_value, min_value, max_value)
diff --git a/code/datums/skills/_skill_holder.dm b/code/datums/skills/_skill_holder.dm
index 8357211463..73748417c3 100644
--- a/code/datums/skills/_skill_holder.dm
+++ b/code/datums/skills/_skill_holder.dm
@@ -91,10 +91,10 @@
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)
- skill_holder.need_static_data_update = TRUE
if(!isnull(value))
LAZYINITLIST(skill_holder.skills)
S.set_skill_value(skill_holder, value, src, silent)
+ skill_holder.need_static_data_update = TRUE
return TRUE
return FALSE
@@ -120,11 +120,9 @@
CRASH("You cannot auto increment a non numerical(experience skill!")
var/current = get_skill_value(skill, FALSE)
var/affinity = get_skill_affinity(skill)
- var/target_value = current + (value * affinity)
- if(maximum)
- target_value = min(target_value, maximum)
- if(target_value == maximum) //no more experience to gain, early return.
- return
+ var/target_value = round(current + (value * affinity), S.skill_gain_quantisation)
+ if(maximum && target_value >= maximum) //no more experience to gain, early return.
+ return
boost_skill_value_to(skill, target_value, silent, current)
/**
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index e998cf69bd..90e232938c 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -180,6 +180,11 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
if(damtype == "brute")
hitsound = "swing_hit"
+ if(used_skills)
+ for(var/path in used_skills)
+ var/datum/skill/S = GLOB.skill_datums[path]
+ LAZYADD(used_skills[path], S.skill_traits)
+
/obj/item/Destroy()
item_flags &= ~DROPDEL //prevent reqdels
if(ismob(loc))
@@ -848,7 +853,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
// Called when a mob tries to use the item as a tool.
// Handles most checks.
-/obj/item/proc/use_tool(atom/target, mob/living/user, delay, amount=0, volume=0, datum/callback/extra_checks, skill_gain_mult = 1, max_level = INFINITY)
+/obj/item/proc/use_tool(atom/target, mob/living/user, delay, amount=0, volume=0, datum/callback/extra_checks, skill_gain_mult = STD_USE_TOOL_MULT)
// No delay means there is no start message, and no reason to call tool_start_check before use_tool.
// Run the start check here so we wouldn't have to call it manually.
if(!delay && !tool_start_check(user, amount))
@@ -893,7 +898,8 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
for(var/skill in used_skills)
if(!(SKILL_TRAINING_TOOL in used_skills[skill]))
continue
- user.mind.auto_gain_experience(skill, gain*skill_gain_mult, GET_STANDARD_LVL(max_level))
+ var/datum/skill/S = GLOB.skill_datums[skill]
+ user.mind.auto_gain_experience(skill, gain*skill_gain_mult*S.item_skill_gain_multi)
return TRUE
diff --git a/code/game/objects/items/grenades/chem_grenade.dm b/code/game/objects/items/grenades/chem_grenade.dm
index 4ce0e811c3..f06dd634c6 100644
--- a/code/game/objects/items/grenades/chem_grenade.dm
+++ b/code/game/objects/items/grenades/chem_grenade.dm
@@ -97,7 +97,7 @@
to_chat(user, "You add [A] to the [initial(name)] assembly.")
else if(stage == EMPTY && istype(I, /obj/item/stack/cable_coil))
- if (I.use_tool(src, user, 0, 1, max_level = JOB_SKILL_BASIC))
+ if (I.use_tool(src, user, 0, 1, skill_gain_mult = TRIVIAL_USE_TOOL_MULT))
det_time = 50 // In case the cable_coil was removed and readded.
stage_change(WIRED)
to_chat(user, "You rig the [initial(name)] assembly.")
diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm
index 57af862b69..d4baea2487 100644
--- a/code/game/objects/items/stacks/sheets/glass.dm
+++ b/code/game/objects/items/stacks/sheets/glass.dm
@@ -69,7 +69,7 @@ GLOBAL_LIST_INIT(glass_recipes, list ( \
if (get_amount() < 1 || CC.get_amount() < 5)
to_chat(user, "You attach wire to the [name].")
var/obj/item/stack/light_w/new_tile = new(user.loc)
diff --git a/code/modules/hydroponics/plant_genes.dm b/code/modules/hydroponics/plant_genes.dm
index b18f4396d6..a666a36099 100644
--- a/code/modules/hydroponics/plant_genes.dm
+++ b/code/modules/hydroponics/plant_genes.dm
@@ -392,7 +392,7 @@
/datum/plant_gene/trait/battery/on_attackby(obj/item/reagent_containers/food/snacks/grown/G, obj/item/I, mob/user)
if(istype(I, /obj/item/stack/cable_coil))
- if(I.use_tool(src, user, 0, 5, max_level = JOB_SKILL_EXPERT))
+ if(I.use_tool(src, user, 0, 5, skill_gain_mult = TRIVIAL_USE_TOOL_MULT))
to_chat(user, "You add some cable to [G] and slide it inside the battery encasing.")
var/obj/item/stock_parts/cell/potato/pocell = new /obj/item/stock_parts/cell/potato(user.loc)
pocell.icon_state = G.icon_state
diff --git a/code/modules/mining/ores_coins.dm b/code/modules/mining/ores_coins.dm
index b3462773d8..8bc9cc4512 100644
--- a/code/modules/mining/ores_coins.dm
+++ b/code/modules/mining/ores_coins.dm
@@ -390,7 +390,7 @@ GLOBAL_LIST_INIT(sand_recipes, list(\
to_chat(user, "There already is a string attached to this coin!")
return
- if (W.use_tool(src, user, 0, 1, max_level = JOB_SKILL_BASIC))
+ if (W.use_tool(src, user, 0, 1, skill_gain_mult = BARE_USE_TOOL_MULT))
add_overlay("coin_string_overlay")
string_attached = 1
to_chat(user, "You attach a string to the coin.")
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 633135b3d2..51cff93ceb 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -315,7 +315,7 @@
if (getFireLoss() > 0 || getToxLoss() > 0)
if(src == user)
to_chat(user, "You start fixing yourself...")
- if(!W.use_tool(src, user, 50, 1, max_level = JOB_SKILL_TRAINED))
+ if(!W.use_tool(src, user, 50, 1, skill_gain_mult = TRIVIAL_USE_TOOL_MULT))
to_chat(user, "You need more cable to repair [src]!")
return
adjustFireLoss(-10)
diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm
index c18eebbb55..f911a6a4e4 100644
--- a/code/modules/power/lighting.dm
+++ b/code/modules/power/lighting.dm
@@ -118,7 +118,7 @@
return
if(istype(W, /obj/item/stack/cable_coil))
- if(W.use_tool(src, user, 0, 1, max_level = JOB_SKILL_TRAINED))
+ if(W.use_tool(src, user, 0, 1, skill_gain_mult = TRIVIAL_USE_TOOL_MULT))
icon_state = "[fixture_type]-construct-stage2"
stage = 2
user.visible_message("[user.name] adds wires to [src].", \
diff --git a/code/modules/projectiles/guns/ballistic/revolver.dm b/code/modules/projectiles/guns/ballistic/revolver.dm
index f34dbc6abc..b64b1c74ba 100644
--- a/code/modules/projectiles/guns/ballistic/revolver.dm
+++ b/code/modules/projectiles/guns/ballistic/revolver.dm
@@ -336,7 +336,7 @@
/obj/item/gun/ballistic/revolver/doublebarrel/improvised/attackby(obj/item/A, mob/user, params)
..()
if(istype(A, /obj/item/stack/cable_coil) && !sawn_off)
- if(A.use_tool(src, user, 0, 10, max_level = JOB_SKILL_BASIC))
+ if(A.use_tool(src, user, 0, 10, skill_gain_mult = EASY_USE_TOOL_MULT))
slot_flags = ITEM_SLOT_BACK
to_chat(user, "You tie the lengths of cable to the shotgun, making a sling.")
slung = TRUE
diff --git a/code/modules/projectiles/guns/ballistic/shotgun.dm b/code/modules/projectiles/guns/ballistic/shotgun.dm
index a4a4065959..873b129c8f 100644
--- a/code/modules/projectiles/guns/ballistic/shotgun.dm
+++ b/code/modules/projectiles/guns/ballistic/shotgun.dm
@@ -156,7 +156,7 @@
/obj/item/gun/ballistic/shotgun/boltaction/improvised/attackby(obj/item/A, mob/user, params)
..()
if(istype(A, /obj/item/stack/cable_coil) && !sawn_off)
- if(A.use_tool(src, user, 0, 10, max_level = JOB_SKILL_BASIC))
+ if(A.use_tool(src, user, 0, 10, skill_gain_mult = EASY_USE_TOOL_MULT))
slot_flags = ITEM_SLOT_BACK
to_chat(user, "You tie the lengths of cable to the rifle, making a sling.")
slung = TRUE