diff --git a/code/controllers/subsystem/blackbox.dm b/code/controllers/subsystem/blackbox.dm index a45b34fde50..9fda5d2d37a 100644 --- a/code/controllers/subsystem/blackbox.dm +++ b/code/controllers/subsystem/blackbox.dm @@ -33,7 +33,7 @@ SUBSYSTEM_DEF(blackbox) if(CONFIG_GET(flag/use_exp_tracking)) if((triggertime < 0) || (world.time > (triggertime +3000))) //subsystem fires once at roundstart then once every 10 minutes. a 5 min check skips the first fire. The <0 is midnight rollover check - update_exp(10,FALSE) + update_exp(10) /datum/controller/subsystem/blackbox/proc/CheckPlayerCount() set waitfor = FALSE diff --git a/code/modules/jobs/job_exp.dm b/code/modules/jobs/job_exp.dm index d1ffb238ae9..42a7863347a 100644 --- a/code/modules/jobs/job_exp.dm +++ b/code/modules/jobs/job_exp.dm @@ -70,13 +70,13 @@ GLOBAL_PROTECT(exp_to_update) else return "0h" -/datum/controller/subsystem/blackbox/proc/update_exp(mins, ann = FALSE) +/datum/controller/subsystem/blackbox/proc/update_exp(mins) if(!SSdbcore.Connect()) return -1 for(var/client/L in GLOB.clients) if(L.is_afk()) continue - L.update_exp_list(mins,ann) + L.update_exp_list(mins) /datum/controller/subsystem/blackbox/proc/update_exp_db() set waitfor = FALSE @@ -135,59 +135,48 @@ GLOBAL_PROTECT(exp_to_update) return -1 qdel(flag_update) - -/client/proc/update_exp_list(minutes, announce_changes = FALSE) +/** + * Tallies up the exp for the playtime tracking and adds it to the global update list. + * + * For a client mob of [/mob/dead/observer], it adds EXP_TYPE_GHOST. + * + * For a client mob of [/mob/living], it grabs the exp list from a mob proc call. + * Being dead but still in your body will tally time towards your /mob/living roles instead of ghost roles. + * If /mob/living returns an empty list, uses "Unknown" instead. + * + * For anything else, it doesn't update anything. + * + * Arguments: + * * minutes - The number of minutes to add to the playtime tally. + */ +/client/proc/update_exp_list(minutes) if(!CONFIG_GET(flag/use_exp_tracking)) return -1 if(!SSdbcore.Connect()) return -1 if (!isnum(minutes)) return -1 + var/list/play_records = list() - if(isliving(mob)) - if(mob.stat != DEAD) - var/rolefound = FALSE - play_records[EXP_TYPE_LIVING] += minutes - if(announce_changes) - to_chat(src,"You got: [minutes] Living EXP!") - if(mob.mind.assigned_role) - for(var/job in SSjob.name_occupations) - if(mob.mind.assigned_role == job) - rolefound = TRUE - play_records[job] += minutes - if(announce_changes) - to_chat(src,"You got: [minutes] [job] EXP!") - if(!rolefound) - for(var/role in GLOB.exp_specialmap[EXP_TYPE_SPECIAL]) - if(mob.mind.assigned_role == role) - rolefound = TRUE - play_records[role] += minutes - if(announce_changes) - to_chat(mob,"You got: [minutes] [role] EXP!") - if(mob.mind.special_role && !(mob.mind.datum_flags & DF_VAR_EDITED)) - var/trackedrole = mob.mind.special_role - play_records[trackedrole] += minutes - if(announce_changes) - to_chat(src,"You got: [minutes] [trackedrole] EXP!") - if(!rolefound) - play_records["Unknown"] += minutes + if(isobserver(mob)) + play_records[EXP_TYPE_GHOST] = minutes + else if(isliving(mob)) + var/mob/living/living_mob = mob + var/list/mob_exp_list = living_mob.get_exp_list(minutes) + if(!length(mob_exp_list)) + play_records["Unknown"] = minutes else - if(holder && !holder.deadmined) - play_records[EXP_TYPE_ADMIN] += minutes - if(announce_changes) - to_chat(src,"You got: [minutes] Admin EXP!") - else - play_records[EXP_TYPE_GHOST] += minutes - if(announce_changes) - to_chat(src,"You got: [minutes] Ghost EXP!") - else if(isobserver(mob)) - play_records[EXP_TYPE_GHOST] += minutes - if(announce_changes) - to_chat(src,"You got: [minutes] Ghost EXP!") - else if(minutes) //Let "refresh" checks go through + play_records |= mob_exp_list + + play_records[EXP_TYPE_LIVING] = minutes + // Lobby surfing? /mob/dead/new_player? Not worth any exp! + else return + if(holder && !holder.deadmined && holder.check_for_rights(R_BAN)) + play_records[EXP_TYPE_ADMIN] = minutes + for(var/jtype in play_records) var/jvalue = play_records[jtype] if (!jvalue) diff --git a/code/modules/jobs/job_report.dm b/code/modules/jobs/job_report.dm index b586e94ee66..22061f3154e 100644 --- a/code/modules/jobs/job_report.dm +++ b/code/modules/jobs/job_report.dm @@ -49,6 +49,7 @@ data["livingTime"] = play_records[EXP_TYPE_LIVING] data["ghostTime"] = play_records[EXP_TYPE_GHOST] + data["adminTime"] = play_records[EXP_TYPE_ADMIN] ? play_records[EXP_TYPE_ADMIN] : 0 data["isAdmin"] = check_rights(R_ADMIN, show_msg = FALSE) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index caecadab157..95d6f30d1ab 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -1018,6 +1018,12 @@ return FALSE return ..() +/mob/living/carbon/human/get_exp_list(minutes) + . = ..() + + if(mind.assigned_role in SSjob.name_occupations) + .[mind.assigned_role] = minutes + /mob/living/carbon/human/monkeybrain ai_controller = /datum/ai_controller/monkey diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 18312938439..e3a42c56eda 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -2001,3 +2001,20 @@ return FALSE return style.harm_act(src, target) return style.help_act(src, target) + +/** + * Returns an assoc list of assignments and minutes for updating a client's exp time in the databse. + * + * Arguments: + * * minutes - The number of minutes to allocate to each valid role. + */ +/mob/living/proc/get_exp_list(minutes) + var/list/exp_list = list() + + if(mind && mind.special_role && !(mind.datum_flags & DF_VAR_EDITED)) + exp_list[mind.special_role] = minutes + + if(mind.assigned_role in GLOB.exp_specialmap[EXP_TYPE_SPECIAL]) + exp_list[mind.assigned_role] = minutes + + return exp_list diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 799bd53046a..dc5e38a4867 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -1057,3 +1057,10 @@ /mob/living/silicon/on_handsblocked_end() return // AIs have no hands + +/mob/living/silicon/ai/get_exp_list(minutes) + . = ..() + + var/datum/job/ai/ai_job_ref = SSjob.GetJobType(/datum/job/ai) + + .[ai_job_ref.title] = minutes diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index a934c19e062..6a3f4f72a9e 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -1002,3 +1002,10 @@ aiPDA.owner = newname aiPDA.name = newname + " (" + aiPDA.ownjob + ")" // SKYRAT EDIT ADDITION END + +/mob/living/silicon/robot/get_exp_list(minutes) + . = ..() + + var/datum/job/cyborg/cyborg_job_ref = SSjob.GetJobType(/datum/job/cyborg) + + .[cyborg_job_ref.title] = minutes diff --git a/tgui/packages/tgui/interfaces/TrackedPlaytime.js b/tgui/packages/tgui/interfaces/TrackedPlaytime.js index e3caae59062..1ca9d04f92c 100644 --- a/tgui/packages/tgui/interfaces/TrackedPlaytime.js +++ b/tgui/packages/tgui/interfaces/TrackedPlaytime.js @@ -10,7 +10,14 @@ const sortByPlaytime = sortBy(([_, playtime]) => -playtime); const PlaytimeSection = props => { const { playtimes } = props; - const sortedPlaytimes = sortByPlaytime(Object.entries(playtimes)); + + const sortedPlaytimes = sortByPlaytime(Object.entries(playtimes)) + .filter(entry => entry[1]); + + if (!sortedPlaytimes.length) { + return "No recorded playtime hours for this section."; + } + const mostPlayed = sortedPlaytimes[0][1]; return (