Skip to content

Commit

Permalink
skill update part 2 (#22956)
Browse files Browse the repository at this point in the history
* skill issue

* more stuff

* not so much
  • Loading branch information
SapphicOverload authored Jan 19, 2025
1 parent 1d8e4c9 commit 827e14b
Show file tree
Hide file tree
Showing 19 changed files with 206 additions and 46 deletions.
3 changes: 3 additions & 0 deletions code/__DEFINES/skills.dm
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,6 @@

/// Experience required to increase your skills by one level. Increases exponentially the higher your level already is.
#define EXPERIENCE_PER_LEVEL 500

/// Calculates how much experience is required to reach a given level.
#define EXP_REQ_CALC(level) (EXPERIENCE_PER_LEVEL * (((2**(level + 1)) / 2) - 1))
4 changes: 2 additions & 2 deletions code/_onclick/hud/screen_objects.dm
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@
data["skills"] = skill_data
data["skill_points"] = user.mind.skill_points
data["allocated_points"] = allocated_points
data["exceptional_skill"] = HAS_MIND_TRAIT(user, TRAIT_EXCEPTIONAL_SKILL)
data["skill_cap"] = EXP_MASTER + HAS_MIND_TRAIT(user, TRAIT_EXCEPTIONAL_SKILL)
return data

/atom/movable/screen/skill_menu/ui_static_data(mob/user)
Expand Down Expand Up @@ -199,7 +199,7 @@
return TRUE
if(allocated_points + params["amount"] < 0)
return TRUE
if(allocated_skills[params["skill"]] + params["amount"] + user.get_skill(params["skill"]) > (4 + HAS_MIND_TRAIT(user, TRAIT_EXCEPTIONAL_SKILL)))
if(allocated_skills[params["skill"]] + params["amount"] + user.get_skill(params["skill"]) > (EXP_MASTER + HAS_MIND_TRAIT(user, TRAIT_EXCEPTIONAL_SKILL)))
return TRUE
if(allocated_skills[params["skill"]] + params["amount"] < 0)
return TRUE
Expand Down
66 changes: 50 additions & 16 deletions code/datums/mind.dm
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
SKILL_FITNESS = EXP_NONE,
)

/// Progress towards increasing their skill level
/// Progress towards increasing their skill level.
var/list/exp_progress = list(
SKILL_PHYSIOLOGY = 0,
SKILL_MECHANICAL = 0,
Expand All @@ -68,6 +68,9 @@
SKILL_FITNESS = 0,
)

/// One-time experience gains that have already been acquired.
var/list/exp_sources = list()

/// Free skill points to allocate
var/skill_points = 0

Expand Down Expand Up @@ -118,6 +121,7 @@
key = _key
soulOwner = src
martial_art = default_martial_art
RegisterSignal(src, SIGNAL_ADDTRAIT(TRAIT_EXCEPTIONAL_SKILL), PROC_REF(update_skills))

/datum/mind/Destroy()
SSticker.minds -= src
Expand Down Expand Up @@ -804,25 +808,47 @@
return FALSE
return (mind.skills[skill] >= amount)

/// Adds progress towards increasing skill level. Returns TRUE if it improved the skill.
/mob/proc/add_exp(skill, amount)
/// Adds progress towards increasing skill level. Returns TRUE if it added progress. Adding a source prevents gaining exp from that source again.
/mob/proc/add_exp(skill, amount, source)
if(!mind)
return FALSE
if(mind.skill_points > 0)
if(!amount)
return FALSE
var/exp_required = EXPERIENCE_PER_LEVEL * (2**mind.skills[skill]) // exp required scales exponentially
if(mind.exp_progress[skill] + amount >= exp_required)
var/levels_gained = round(log(2, 1 + (mind.exp_progress[skill] + amount) / exp_required)) // in case you gained so much you go up more than one level
var/levels_allocated = hud_used?.skill_menu ? hud_used.skill_menu.allocated_skills[skill] : 0
if(levels_allocated > 0) // adjust any already allocated skills to prevent shenanigans (you know who you are)
hud_used.skill_menu.allocated_points -= min(levels_gained, levels_allocated)
hud_used.skill_menu.allocated_skills[skill] -= min(levels_gained, levels_allocated)
mind.exp_progress[skill] += amount - exp_required * (2**(levels_gained - 1))
adjust_skill(skill, levels_gained)
to_chat(src, span_boldnotice("Your [skill] skill is now level [get_skill(skill)]!"))
return TRUE
if(source && (source in mind.exp_sources))
return FALSE
mind.exp_sources.Add(source)
mind.exp_progress[skill] += amount
return FALSE
var/levels_gained = check_exp(skill)
if(levels_gained) // remove an equal amount of unallocated skill points to prevent exploits
mind.skill_points = max(mind.skill_points - levels_gained, 0)
return TRUE

/// Levels up a skill if it has enough experience to do so.
/mob/proc/check_exp(skill)
if(!mind)
return FALSE
var/current_level = get_skill(skill)
var/exp_required = EXPERIENCE_PER_LEVEL * (2**current_level) // exp required scales exponentially
if(mind.exp_progress[skill] < exp_required)
return FALSE
var/skill_cap = EXP_MASTER + HAS_MIND_TRAIT(src, TRAIT_EXCEPTIONAL_SKILL)
var/levels_gained = min(round(log(2, 1 + (mind.exp_progress[skill] / exp_required))), max(skill_cap - current_level)) // in case you gained so much you go up more than one level
if(levels_gained < 1)
return FALSE
var/levels_allocated = hud_used?.skill_menu ? hud_used.skill_menu.allocated_skills[skill] : 0
if(levels_allocated > 0) // adjust any already allocated skills to prevent shenanigans (you know who you are)
hud_used.skill_menu.allocated_points -= min(levels_gained, levels_allocated)
hud_used.skill_menu.allocated_skills[skill] -= min(levels_gained, levels_allocated)
mind.exp_progress[skill] -= exp_required * (((2**round(levels_gained + 1)) / 2) - 1)
adjust_skill(skill, levels_gained, max_skill = skill_cap)
to_chat(src, span_boldnotice("Your [skill] skill is now level [get_skill(skill)]!"))
return levels_gained

/// Returns whether experience has been gained from a given source.
/mob/proc/has_exp(source)
if(!mind)
return FALSE
return (source in mind.exp_sources) ? TRUE : FALSE

/// Adds skill points to be allocated at will.
/mob/proc/add_skill_points(amount)
Expand All @@ -831,6 +857,14 @@
mind.skill_points += amount
throw_alert("skill points", /atom/movable/screen/alert/skill_up)

/// Called when [TRAIT_EXCEPTIONAL_SKILL] is added to the mob.
/datum/mind/proc/update_skills(datum/source)
SIGNAL_HANDLER
if(!current)
return
for(var/skill in skills)
current.check_exp(skill)

/datum/mind/proc/has_martialart(string)
if(martial_art && martial_art.id == string)
return martial_art
Expand Down
8 changes: 6 additions & 2 deletions code/datums/wires/_wires.dm
Original file line number Diff line number Diff line change
Expand Up @@ -184,13 +184,15 @@
return TRUE
return FALSE

/datum/wires/proc/cut(wire)
/datum/wires/proc/cut(wire, mob/user)
if(is_cut(wire))
cut_wires -= wire
on_cut(wire, mend = TRUE)
else
cut_wires += wire
on_cut(wire, mend = FALSE)
if(user)
user.add_exp(SKILL_TECHNICAL, 50, "[wire]_[type]")

/datum/wires/proc/cut_color(color)
cut(get_wire(color))
Expand All @@ -202,10 +204,12 @@
for(var/wire in wires)
cut(wire)

/datum/wires/proc/pulse(wire, user)
/datum/wires/proc/pulse(wire, mob/user)
if(is_cut(wire))
return
on_pulse(wire, user)
if(user)
user.add_exp(SKILL_TECHNICAL, 50, "[wire]_[type]")

/datum/wires/proc/pulse_color(color, mob/living/user)
pulse(get_wire(color), user)
Expand Down
6 changes: 3 additions & 3 deletions code/game/mecha/mecha.dm
Original file line number Diff line number Diff line change
Expand Up @@ -1380,6 +1380,6 @@ GLOBAL_VAR_INIT(year_integer, text2num(year)) // = 2013???
if(!pilot)
pilot = occupant
var/effective_skill = pilot.get_skill(SKILL_TECHNICAL)
if(effective_skill < EXP_MASTER && HAS_TRAIT(pilot, TRAIT_SKILLED_PILOT))
effective_skill += EXP_LOW // mech pilot suit adds extra pilot skill, up to EXP_MASTER
return (12 - effective_skill) / 10
if(HAS_TRAIT(pilot, TRAIT_SKILLED_PILOT) || HAS_MIND_TRAIT(pilot, TRAIT_SKILLED_PILOT))
effective_skill += EXP_LOW
return 1 + (2 - effective_skill) * 0.075
8 changes: 5 additions & 3 deletions code/game/objects/items/granters/_granters.dm
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
unique = 1
/// Flavor messages displayed to mobs reading the granter
var/list/remarks = list()
/// Whether to display the remarks in order.
var/ordered_remarks = FALSE
/// Controls how long a mob must keep the book in his hand to actually successfully learn
var/pages_to_mastery = 3
/// Sanity, whether it's currently being read
Expand Down Expand Up @@ -40,7 +42,7 @@
on_reading_start(user)
reading = TRUE
for(var/i in 1 to pages_to_mastery)
if(!turn_page(user))
if(!turn_page(user, i))
on_reading_stopped()
reading = FALSE
return
Expand All @@ -64,13 +66,13 @@
to_chat(user, span_notice("You finish reading [name]!"))

/// The actual "turning over of the page" flavor bit that happens while someone is reading the granter.
/obj/item/book/granter/proc/turn_page(mob/living/user)
/obj/item/book/granter/proc/turn_page(mob/living/user, current_page = 1)
playsound(user, pick(book_sounds), 30, TRUE)

if(!do_after(user, 5 SECONDS, src))
return FALSE

to_chat(user, span_notice("[length(remarks) ? pick_n_take(remarks) : "You keep reading..."]"))
to_chat(user, span_notice("[ordered_remarks ? "[remarks[current_page]]" : (length(remarks) ? pick_n_take(remarks) : "You keep reading...")]"))
return TRUE

/// Effects that occur whenever the book is read when it has no uses left.
Expand Down
1 change: 1 addition & 0 deletions code/game/objects/items/granters/martial_arts/racial.dm
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
icon = 'icons/obj/module.dmi'
icon_state = "cyborg_upgrade"
remarks = list("MANKIND IS DEAD.", "BLOOD IS FUEL.", "HELL IS FULL.")
ordered_remarks = TRUE

/obj/item/book/granter/martial/ultra_violence/on_reading_start(mob/user)
to_chat(user, span_notice("You plug \the [src] in and begin loading PRG$[martial_name]."))
Expand Down
2 changes: 1 addition & 1 deletion code/game/objects/items/granters/mech_piloting.dm
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@

/obj/item/book/granter/mechpiloting/on_reading_finished(mob/user)
. = ..()
user.adjust_skill(SKILL_TECHNICAL, EXP_MID, max_skill = EXP_GENIUS)
ADD_TRAIT(user.mind, TRAIT_SKILLED_PILOT, type)
77 changes: 77 additions & 0 deletions code/game/objects/items/granters/skills.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/// Grants experience to the reader.
/obj/item/book/granter/skill
name = "skill guide"
desc = "A guide to getting good, whatever that means."
remarks = list(
"Skill issue...?",
)
/// Experience gains from reading this book.
var/list/exp_gain = list(
SKILL_PHYSIOLOGY = EXPERIENCE_PER_LEVEL,
SKILL_MECHANICAL = EXPERIENCE_PER_LEVEL,
SKILL_TECHNICAL = EXPERIENCE_PER_LEVEL,
SKILL_SCIENCE = EXPERIENCE_PER_LEVEL,
SKILL_PHYSIOLOGY = EXPERIENCE_PER_LEVEL,
)

/obj/item/book/granter/skill/can_learn(mob/living/user)
return !user.has_exp("[type]_[exp_gain[1]]")

/obj/item/book/granter/skill/on_reading_finished(mob/living/user)
. = ..()
if(!user.mind)
CRASH("[user.type] somehow read [type] without a mind!")
for(var/skill in exp_gain)
user.add_exp(skill, exp_gain[skill], "[type]_[skill]")

/obj/item/book/granter/skill/physiology
name = "\improper Guide to First Aid"
desc = "This book teaches basic first aid information."
remarks = list(
"Dying is bad..?",
"Suture or cauterize open wounds to prevent bleeding out...",
"Apply ointment or regenerative mesh to sterilize infected burns...",
"Move critical patients on rolling beds or over your shoulder..."
)
exp_gain = list(
SKILL_PHYSIOLOGY = EXP_REQ_CALC(EXP_HIGH),
)

/obj/item/book/granter/skill/mechanics
name = "Nuclear Engineering for Dummies"
desc = "A step-by-step guide to operating a nuclear reactor."
remarks = list(
"Wear radiation protection during maintenance...",
"Adjust control rods to moderate the temperature...",
"High temperatures generate more power...",
"Don't press AZ-5..?",
)
exp_gain = list(
SKILL_MECHANICAL = EXP_REQ_CALC(EXP_HIGH),
)

/obj/item/book/granter/skill/technical
name = "Hacking 101"
desc = "Contains detailed information on airlock maintenance."
remarks = list(
"Wear insulated gloves for protection...",
"Pulse wires twice to avoid changing settings...",
"Pry open unpowered doors with a crowbar...",
"Bolt an open door to prevent it closing behind you...",
)
exp_gain = list(
SKILL_TECHNICAL = EXP_REQ_CALC(EXP_HIGH),
)

/obj/item/book/granter/skill/science
name = "Statistical Mechanics and Thermodynamics"
desc = "Perhaps it will be wise to approach this subject cautiously."
remarks = list(
"Ludwig Boltzmann, who spent much of his life studying statistical mechanics, died in 1906, by his own hand...",
"Paul Ehrenfest, carrying on the work, died similarly in 1933...",
"Now it is our turn to study statistical mechanics...",
)
ordered_remarks = TRUE
exp_gain = list(
SKILL_SCIENCE = EXP_REQ_CALC(EXP_HIGH),
)
2 changes: 1 addition & 1 deletion code/game/objects/items/storage/bags.dm
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@
if(usr.incapacitated())
return
for(var/obj/item/O in contents)
seedify(O, 1)
seedify(O, 1, null, usr)

// -----------------------------
// Stack Snatcher
Expand Down
7 changes: 6 additions & 1 deletion code/modules/hydroponics/gene_modder.dm
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,9 @@
/obj/machinery/plantgenes/Topic(href, list/href_list)
if(..())
return
usr.set_machine(src)

var/mob/user = usr
user.set_machine(src)

if(href_list["eject_seed"] && !operation)
if (seed)
Expand Down Expand Up @@ -338,6 +340,9 @@
gene.value = max(gene.value, min_wrate)
else if(istype(G, /datum/plant_gene/core/weed_chance))
gene.value = max(gene.value, min_wchance)
if(G.extract_value && user.add_exp(SKILL_SCIENCE, G.extract_value, G.type))
user.playsound_local(get_turf(src), 'sound/machines/ping.ogg', 25, TRUE)
balloon_alert(user, "new trait catalogued: [lowertext(G.name)]")
disk.update_appearance()
qdel(seed)
seed = null
Expand Down
5 changes: 5 additions & 0 deletions code/modules/hydroponics/plant_genes.dm
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/datum/plant_gene
var/name
/// Skill gain from extracting this gene. Can only be gained once.
var/extract_value = 0

/datum/plant_gene/proc/get_name() // Used for manipulator display and gene disk name.
return name
Expand Down Expand Up @@ -138,6 +140,7 @@
var/rate = 0.05
var/examine_line = ""
var/trait_id // must be set and equal for any two traits of the same type
extract_value = 150

/datum/plant_gene/trait/Copy()
var/datum/plant_gene/trait/G = ..()
Expand Down Expand Up @@ -433,6 +436,7 @@

/datum/plant_gene/trait/fire_resistance // Lavaland
name = "Fire Resistance"
extract_value = 500

/datum/plant_gene/trait/fire_resistance/apply_vars(obj/item/seeds/S)
if(!(S.resistance_flags & FIRE_PROOF))
Expand All @@ -454,3 +458,4 @@

/datum/plant_gene/trait/plant_type/alien_properties
name ="?????"
extract_value = 500
12 changes: 8 additions & 4 deletions code/modules/hydroponics/seed_extractor.dm
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
else
t_max = rand(1,4)

var/seedloc = O.loc
var/atom/seedloc = O.loc
if(extractor)
seedloc = extractor.loc

Expand All @@ -37,7 +37,6 @@
t_prod.forceMove(seedloc)
t_amount++
qdel(O)
return seeds

else if(istype(O, /obj/item/grown))
var/obj/item/grown/F = O
Expand All @@ -46,13 +45,18 @@
return
while(t_amount < t_max)
var/obj/item/seeds/t_prod = F.seed.Copy()
seeds.Add(t_prod)
t_prod.forceMove(seedloc)
t_amount++
qdel(O)
return 1

return 0
if(user && seeds.len)
var/obj/item/seeds/seed = seeds[1] // all seeds are duplicates, pick the first one in the list
if(user.add_exp(SKILL_SCIENCE, seed.rarity * 10, seed.type))
user.playsound_local(get_turf(seedloc), 'sound/machines/ping.ogg', 25, TRUE)
seedloc.balloon_alert(user, "rare plant catalogued: [initial(seed.product.name)]")

return (seeds.len ? seeds : FALSE)

/obj/machinery/seed_extractor
name = "seed extractor"
Expand Down
Loading

0 comments on commit 827e14b

Please sign in to comment.