diff --git a/code/__DEFINES/admin.dm b/code/__DEFINES/admin.dm index e65b3e60ebd..a9b559d5477 100644 --- a/code/__DEFINES/admin.dm +++ b/code/__DEFINES/admin.dm @@ -48,6 +48,7 @@ #define ADMIN_VV(atom) "(VV)" #define ADMIN_SM(user) "(SM)" #define ADMIN_TP(user) "(TP)" +#define ADMIN_SP(user) "(TP)" #define ADMIN_KICK(user) "(KICK)" #define ADMIN_CENTCOM_REPLY(user) "(RPLY)" #define ADMIN_SYNDICATE_REPLY(user) "(RPLY)" @@ -56,7 +57,7 @@ #define ADMIN_LOOKUP(user) "[key_name_admin(user)][ADMIN_QUE(user)]" #define ADMIN_LOOKUPFLW(user) "[key_name_admin(user)][ADMIN_QUE(user)] [ADMIN_FLW(user)]" #define ADMIN_SET_SD_CODE "(SETCODE)" -#define ADMIN_FULLMONTY_NONAME(user) "[ADMIN_QUE(user)] [ADMIN_PP(user)] [ADMIN_VV(user)] [ADMIN_SM(user)] [ADMIN_FLW(user)] [ADMIN_TP(user)] [ADMIN_INDIVIDUALLOG(user)] [ADMIN_SMITE(user)]" +#define ADMIN_FULLMONTY_NONAME(user) "[ADMIN_QUE(user)] [ADMIN_PP(user)] [ADMIN_VV(user)] [ADMIN_SM(user)] [ADMIN_FLW(user)] [ADMIN_SP(user)] [ADMIN_TP(user)] [ADMIN_INDIVIDUALLOG(user)] [ADMIN_SMITE(user)]" #define ADMIN_FULLMONTY(user) "[key_name_admin(user)] [ADMIN_FULLMONTY_NONAME(user)]" #define ADMIN_JMP(src) "(JMP)" #define COORD(src) "[src ? "([src.x],[src.y],[src.z])" : "nonexistent location"]" diff --git a/code/__DEFINES/skills.dm b/code/__DEFINES/skills.dm index b765ba2315c..b5302aa000e 100644 --- a/code/__DEFINES/skills.dm +++ b/code/__DEFINES/skills.dm @@ -1,27 +1,24 @@ // Skill levels -#define SKILL_LEVEL_NONE 0 -#define SKILL_LEVEL_NOVICE 1 -#define SKILL_LEVEL_APPRENTICE 2 -#define SKILL_LEVEL_JOURNEYMAN 3 -#define SKILL_LEVEL_EXPERT 4 -#define SKILL_LEVEL_MASTER 5 -#define SKILL_LEVEL_LEGENDARY 6 +#define SKILL_LEVEL_NONE 1 +#define SKILL_LEVEL_NOVICE 2 +#define SKILL_LEVEL_APPRENTICE 3 +#define SKILL_LEVEL_JOURNEYMAN 4 +#define SKILL_LEVEL_EXPERT 5 +#define SKILL_LEVEL_MASTER 6 +#define SKILL_LEVEL_LEGENDARY 7 -//Skill experience thresholds -#define SKILL_EXP_NOVICE 100 -#define SKILL_EXP_APPRENTICE 250 -#define SKILL_EXP_JOURNEYMAN 500 -#define SKILL_EXP_EXPERT 900 -#define SKILL_EXP_MASTER 1500 -#define SKILL_EXP_LEGENDARY 2500 +#define SKILL_LVL 1 +#define SKILL_EXP 2 + +//Allows us to get EXP from level, or level from EXP +#define SKILL_EXP_LIST list(0, 100, 250, 500, 900, 1500, 2500) //Skill modifier types #define SKILL_SPEED_MODIFIER "skill_speed_modifier"//ideally added/subtracted in speed calculations to make you do stuff faster #define SKILL_PROBS_MODIFIER "skill_probability_modifier"//ideally added/subtracted where beneficial in prob(x) calls #define SKILL_RANDS_MODIFIER "skill_randomness_modifier"//ideally added/subtracted where beneficial in rand(x,y) calls - // Gets the reference for the skill type that was given #define GetSkillRef(A) (SSskills.all_skills[A]) diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index eb564638d2a..6cb507fa722 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -822,11 +822,12 @@ GLOBAL_LIST_INIT(binary, list("0","1")) catch return -/proc/num2loadingbar(percent as num, var/numSquares = 20) - var/loadstring = "" - for (var/i in 1 to numSquares) - if (i <= numSquares - percent*numSquares) - loadstring += "▮" - else - loadstring += "▯" - return "\[" + loadstring + "]" +/proc/num2loadingbar(percent as num, var/numSquares = 20, var/reverse = FALSE) + var/loadstring = "" + for (var/i in 1 to numSquares) + var/limit = reverse ? numSquares - percent*numSquares : percent*numSquares + if (i <= limit) + loadstring += "█" + else + loadstring += "░" + return "\[" + loadstring + "]" diff --git a/code/controllers/subsystem/skills.dm b/code/controllers/subsystem/skills.dm index 5e67d1adb55..c7256a42b32 100644 --- a/code/controllers/subsystem/skills.dm +++ b/code/controllers/subsystem/skills.dm @@ -8,9 +8,8 @@ SUBSYSTEM_DEF(skills) init_order = INIT_ORDER_SKILLS ///Dictionary of skill.type || skill ref var/list/all_skills = list() - ///Static assoc list of levels (ints) - strings - var/list/level_names = list("Novice", "Apprentice", "Journeyman", "Expert", "Master", "Legendary")//This list is already in the right order, due to indexing - + ///List of level names with index corresponding to skill level + var/list/level_names = list("None", "Novice", "Apprentice", "Journeyman", "Expert", "Master", "Legendary") //List of skill level names. Note that indexes can be accessed like so: level_names[SKILL_LEVEL_NOVICE] /datum/controller/subsystem/skills/Initialize(timeofday) InitializeSkills() @@ -18,6 +17,6 @@ SUBSYSTEM_DEF(skills) ///Ran on initialize, populates the skills dictionary /datum/controller/subsystem/skills/proc/InitializeSkills(timeofday) - for(var/type in subtypesof(/datum/skill)) + for(var/type in GLOB.skill_types) var/datum/skill/ref = new type all_skills[type] = ref diff --git a/code/datums/mind.dm b/code/datums/mind.dm index fda13db0152..3f35025cb89 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -67,15 +67,16 @@ var/list/learned_recipes //List of learned recipe TYPES. - ///Assoc list of skills - level + ///List of skills the user has recieved a reward for. Should not be used to keep track of currently known skills. Lazy list because it shouldnt be filled often + var/list/skills_rewarded + ///Assoc list of skills. Use SKILL_LVL to access level, and SKILL_EXP to access skill's exp. var/list/known_skills = list() - ///Assoc list of skills - exp - var/list/skill_experience = list() /datum/mind/New(key) src.key = key soulOwner = src martial_art = default_martial_art + init_known_skills() /datum/mind/Destroy() SSticker.minds -= src @@ -123,62 +124,83 @@ new_character.key = key //now transfer the key to link the client to our new body current.update_atom_languages() +/datum/mind/proc/init_known_skills() + for (var/type in GLOB.skill_types) + known_skills[type] = list(SKILL_LEVEL_NONE, 0) + +///Return the amount of EXP needed to go to the next level. Returns 0 if max level +/datum/mind/proc/exp_needed_to_level_up(skill) + var/lvl = update_skill_level(skill) + if (lvl >= length(SKILL_EXP_LIST)) //If we're already past the last exp threshold + return 0 + return SKILL_EXP_LIST[lvl+1] - known_skills[skill][SKILL_EXP] ///Adjust experience of a specific skill -/datum/mind/proc/adjust_experience(skill, amt, silent = FALSE) +/datum/mind/proc/adjust_experience(skill, amt, silent = FALSE, force_old_level = 0) var/datum/skill/S = GetSkillRef(skill) - skill_experience[S] = max(0, skill_experience[S] + amt) //Prevent going below 0 - var/old_level = known_skills[S] - switch(skill_experience[S]) - if(SKILL_EXP_LEGENDARY to INFINITY) - known_skills[S] = SKILL_LEVEL_LEGENDARY - if(SKILL_EXP_MASTER to SKILL_EXP_LEGENDARY) - known_skills[S] = SKILL_LEVEL_MASTER - if(SKILL_EXP_EXPERT to SKILL_EXP_MASTER) - known_skills[S] = SKILL_LEVEL_EXPERT - if(SKILL_EXP_JOURNEYMAN to SKILL_EXP_EXPERT) - known_skills[S] = SKILL_LEVEL_JOURNEYMAN - if(SKILL_EXP_APPRENTICE to SKILL_EXP_JOURNEYMAN) - known_skills[S] = SKILL_LEVEL_APPRENTICE - if(SKILL_EXP_NOVICE to SKILL_EXP_APPRENTICE) - known_skills[S] = SKILL_LEVEL_NOVICE - if(0 to SKILL_EXP_NOVICE) - known_skills[S] = SKILL_LEVEL_NONE - if(isnull(old_level) || known_skills[S] == old_level) - return //same level or we just started earning xp towards the first level. + var/old_level = force_old_level ? force_old_level : known_skills[skill][SKILL_LVL] //Get current level of the S skill + known_skills[skill][SKILL_EXP] = max(0, known_skills[skill][SKILL_EXP] + amt) //Update exp. Prevent going below 0 + known_skills[skill][SKILL_LVL] = update_skill_level(skill)//Check what the current skill level is based on that skill's exp if(silent) return - if(known_skills[S] >= old_level) - S.level_gained(src, known_skills[S], old_level) - else - S.level_lost(src, known_skills[S], old_level) + if(known_skills[skill][SKILL_LVL] > old_level) + S.level_gained(src, known_skills[skill][SKILL_LVL], old_level) + else if(known_skills[skill][SKILL_LVL] < old_level) + S.level_lost(src, known_skills[skill][SKILL_LVL], old_level) + +///Set experience of a specific skill to a number +/datum/mind/proc/set_experience(skill, amt, silent = FALSE) + var/old_level = known_skills[skill][SKILL_EXP] + known_skills[skill][SKILL_EXP] = amt + adjust_experience(skill, 0, silent, old_level) //Make a call to adjust_experience to handle updating level + +///Set level of a specific skill +/datum/mind/proc/set_level(skill, newlevel, silent = FALSE) + var/oldlevel = get_skill_level(skill) + var/difference = SKILL_EXP_LIST[newlevel] - SKILL_EXP_LIST[oldlevel] + adjust_experience(skill, difference, silent) + +///Check what the current skill level is based on that skill's exp +/datum/mind/proc/update_skill_level(skill) + var/i = 0 + for (var/exp in SKILL_EXP_LIST) + i ++ + if (known_skills[skill][SKILL_EXP] >= SKILL_EXP_LIST[i]) + continue + return i - 1 //Return level based on the last exp requirement that we were greater than + return i //If we had greater EXP than even the last exp threshold, we return the last level ///Gets the skill's singleton and returns the result of its get_skill_modifier /datum/mind/proc/get_skill_modifier(skill, modifier) var/datum/skill/S = GetSkillRef(skill) - return S.get_skill_modifier(modifier, known_skills[S] || SKILL_LEVEL_NONE) + return S.get_skill_modifier(modifier, known_skills[skill][SKILL_LVL]) +///Gets the player's current level number from the relevant skill /datum/mind/proc/get_skill_level(skill) - var/datum/skill/S = GetSkillRef(skill) - return known_skills[S] || SKILL_LEVEL_NONE + return known_skills[skill][SKILL_LVL] + +///Gets the player's current exp from the relevant skill +/datum/mind/proc/get_skill_exp(skill) + return known_skills[skill][SKILL_EXP] + +/datum/mind/proc/get_skill_level_name(skill) + var/level = get_skill_level(skill) + return SSskills.level_names[level] /datum/mind/proc/print_levels(user) var/list/shown_skills = list() for(var/i in known_skills) - if(known_skills[i]) //Do we actually have a level in this? + if(known_skills[i][SKILL_LVL] > SKILL_LEVEL_NONE) //Do we actually have a level in this? shown_skills += i if(!length(shown_skills)) to_chat(user, "You don't seem to have any particularly outstanding skills.") return - var/msg = "" - msg += "*---------*\nYour skills\n" + var/msg = "*---------*\nYour skills\n" for(var/i in shown_skills) - var/datum/skill/S = i - msg += "[i] - [SSskills.level_names[known_skills[S]]]\n" + msg += "[i] - [get_skill_level_name(i)]\n" msg += "" to_chat(user, msg) - /datum/mind/proc/set_death_time() last_death = world.time diff --git a/code/datums/skills/_skill.dm b/code/datums/skills/_skill.dm index ca38b812c11..b593a2b418e 100644 --- a/code/datums/skills/_skill.dm +++ b/code/datums/skills/_skill.dm @@ -1,10 +1,42 @@ +GLOBAL_LIST_INIT(skill_types, subtypesof(/datum/skill)) + /datum/skill - var/name = "Skill" + var/name = "Skilling" + var/title = "Skiller" var/desc = "the art of doing things" + ///Dictionary of modifier type - list of modifiers (indexed by level). 7 entries in each list for all 7 skill levels. var/modifiers = list(SKILL_SPEED_MODIFIER = list(1, 1, 1, 1, 1, 1, 1)) //Dictionary of modifier type - list of modifiers (indexed by level). 7 entries in each list for all 7 skill levels. + ///List Path pointing to the skill cape reward that will appear when a user finishes leveling up a skill + var/skill_cape_path + ///List associating different messages that appear on level up with different levels + var/list/levelUpMessages = list() + ///List associating different messages that appear on level up with different levels + var/list/levelDownMessages = list() /datum/skill/proc/get_skill_modifier(modifier, level) - return modifiers[modifier][level+1] //+1 because lists start at 1 + return modifiers[modifier][level] //Levels range from 1 (None) to 7 (Legendary) +/** + * new: sets up some lists. + * + *Can't happen in the datum's definition because these lists are not constant expressions + */ +/datum/skill/New() + . = ..() + levelUpMessages = list("What the hell is [name]? Tell an admin if you see this message.", //This first index shouldn't ever really be used + "I'm starting to figure out what [name] really is!", + "I'm getting a little better at [name]!", + "I'm getting much better at [name]!", + "I feel like I've become quite proficient at [name]!", + "After lots of practice, I've begun to truly understand the intricies \ + and surprising depth behind [name]. I now condsider myself a master [title].", + "Through incredible determination and effort, I've reached the peak of my [name] abiltities. I'm finally able to consider myself a legendary [title]!" ) + levelDownMessages = list("I have somehow completely lost all understanding of [name]. Please tell an admin if you see this.", + "I'm starting to forget what [name] really even is. I need more practice...", + "I'm getting a little worse at [name]. I'll need to keep practicing to get better at it...", + "I'm getting a little worse at [name]...", + "I'm losing my [name] expertise ....", + "I feel like I'm losing my mastery of [name].", + "I feel as though my legendary [name] skills have deteriorated. I'll need more intense training to recover my lost skills." ) /** * level_gained: Gives skill levelup messages to the user @@ -16,10 +48,34 @@ * * old_level - Similar to the above, but the level you had before levelling up. */ /datum/skill/proc/level_gained(var/datum/mind/mind, new_level, old_level)//just for announcements (doesn't go off if the xp gain is silent) - to_chat(mind.current, "I feel like I've become more proficient at [name]!") - + to_chat(mind.current, levelUpMessages[new_level]) //new_level will be a value from 1 to 6, so we get appropriate message from the 6-element levelUpMessages list /** * level_lost: See level_gained, same idea but fires on skill level-down */ /datum/skill/proc/level_lost(var/datum/mind/mind, new_level, old_level) - to_chat(mind.current, "I feel like I've become worse at [name]!") + to_chat(mind.current, levelDownMessages[old_level]) //old_level will be a value from 1 to 6, so we get appropriate message from the 6-element levelUpMessages list + +/** + * try_skill_reward: Checks to see if a user is eligable for a tangible reward for reaching a certain skill level + * + * Currently gives the user a special cloak when they reach a legendary level at any given skill + * Arguments: + * * mind - The mind that you'll want to send messages and rewards to + * * new_level - The current level of the user. Used to check if it meets the requirements for a reward + */ +/datum/skill/proc/try_skill_reward(var/datum/mind/mind, new_level) + if (new_level != SKILL_LEVEL_LEGENDARY) + return + if (!ispath(skill_cape_path)) + to_chat(mind.current, "My legendary [name] skill is quite impressive, though it seems the Professional [title] Association doesn't have any status symbols to commemorate my abilities with. I should let Centcom know of this travesty, maybe they can do something about it.") + return + if (LAZYFIND(mind.skills_rewarded, src.type)) + to_chat(mind.current, "It seems the Professional [title] Association won't send me another status symbol.") + return + var/obj/structure/closet/supplypod/bluespacepod/pod = new() + pod.landingDelay = 150 + pod.explosionSize = list(0,0,0,0) + to_chat(mind.current, "My legendary skill has attracted the attention of the Professional [title] Association. It seems they are sending me a status symbol to commemorate my abilities.") + var/turf/T = get_turf(mind.current) + new /obj/effect/DPtarget(T, pod , new skill_cape_path(T)) + LAZYADD(mind.skills_rewarded, src.type) diff --git a/code/datums/skills/cleaning.dm b/code/datums/skills/cleaning.dm index 6255c8c27f8..98f173c22f7 100644 --- a/code/datums/skills/cleaning.dm +++ b/code/datums/skills/cleaning.dm @@ -1,4 +1,6 @@ /datum/skill/cleaning - name = "Scrubbing" + name = "Cleaning" + title = "Cleaner" desc = "It’s not who I am underneath, but what I mop up that defines me." modifiers = list(SKILL_SPEED_MODIFIER = list(1, 0.9, 0.8, 0.7, 0.6, 0.5, 0.36)) //speed also touches probability in using up a soap's charge + skill_cape_path = /obj/item/clothing/neck/cloak/skill_reward/cleaning diff --git a/code/datums/skills/gaming.dm b/code/datums/skills/gaming.dm index 5a8e098fda0..b737b7ee172 100644 --- a/code/datums/skills/gaming.dm +++ b/code/datums/skills/gaming.dm @@ -1,15 +1,13 @@ /datum/skill/gaming name = "Gaming" + title = "Gamer" desc = "My proficiency as a gamer. This helps me beat bosses with ease, powergame in Orion Trail, and makes me wanna slam some gamer fuel." modifiers = list(SKILL_PROBS_MODIFIER = list(0, 5, 10, 15, 15, 20, 25), SKILL_RANDS_MODIFIER = list(0, 1, 2, 3, 4, 5, 7)) + skill_cape_path = /obj/item/clothing/neck/cloak/skill_reward/gaming -/datum/skill/gaming/level_gained(var/datum/mind/mind, new_level, old_level) - switch(new_level) - if(SKILL_LEVEL_JOURNEYMAN)//gives slight extra perks outside roll modifiers - to_chat(mind.current, "I'm starting to pick up the meta of these arcade games. \ - If I were to minmax the optimal strat and accentuate my playstyle around well-refined tech...") - if(SKILL_LEVEL_LEGENDARY)//leads to a mysterious power... - to_chat(mind.current, "Maybe gamerfuel actually would help me play better...") - else - to_chat(mind.current, "I'm getting better at these arcade games!") +/datum/skill/gaming/New() + . = ..() + levelUpMessages[1] = "I'm starting to get a hang of the controls of these games..." + levelUpMessages[4] = "I'm starting to pick up the meta of these arcade games. If I were to minmax the optimal strat and accentuate my playstyle around well-refined tech..." + levelUpMessages[6] = "Through incredible determination and effort, I've reached the peak of my [name] abilities. I wonder how I can become any more powerful... Maybe gamer fuel would actually help me play better..?" diff --git a/code/datums/skills/medical.dm b/code/datums/skills/medical.dm index 1f4c7bda060..f9bd5d541e6 100644 --- a/code/datums/skills/medical.dm +++ b/code/datums/skills/medical.dm @@ -1,4 +1,6 @@ -/datum/skill/medical - name = "Medical" +/datum/skill/healing + name = "Healing" + title = "Healer" desc = "From Bandaids to biopsies, this improves your ability to get people back up both in the field and on the operating table." modifiers = list(SKILL_SPEED_MODIFIER = list(1, 0.95, 0.9, 0.85, 0.75, 0.6, 0.5)) + skill_cape_path = /obj/item/clothing/neck/cloak/skill_reward/healing diff --git a/code/datums/skills/mining.dm b/code/datums/skills/mining.dm index 43f8eee4607..e1b698a3d87 100644 --- a/code/datums/skills/mining.dm +++ b/code/datums/skills/mining.dm @@ -1,4 +1,6 @@ /datum/skill/mining name = "Mining" + title = "Miner" desc = "A dwarf's biggest skill, after drinking." modifiers = list(SKILL_SPEED_MODIFIER = list(1, 0.95, 0.9, 0.85, 0.75, 0.6, 0.5),SKILL_PROBS_MODIFIER=list(10, 15, 20, 25, 30, 35, 40)) + skill_cape_path = /obj/item/clothing/neck/cloak/skill_reward/mining diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm index 98c27f3fd22..e9aa15f5ef1 100644 --- a/code/game/objects/items/devices/PDA/PDA.dm +++ b/code/game/objects/items/devices/PDA/PDA.dm @@ -251,6 +251,7 @@ GLOBAL_LIST_EMPTY(PDAs) dat += "