diff --git a/SQL/paradise_schema.sql b/SQL/paradise_schema.sql
index 1e509dc8149..eb9c08af9ea 100644
--- a/SQL/paradise_schema.sql
+++ b/SQL/paradise_schema.sql
@@ -250,7 +250,7 @@ CREATE TABLE `player` (
`nanoui_fancy` smallint(4) DEFAULT '1',
`show_ghostitem_attack` smallint(4) DEFAULT '1',
`lastchangelog` varchar(32) NOT NULL DEFAULT '0',
- `exp` mediumtext NOT NULL DEFAULT '',
+ `exp` mediumtext,
PRIMARY KEY (`id`),
UNIQUE KEY `ckey` (`ckey`)
) ENGINE=InnoDB AUTO_INCREMENT=32446 DEFAULT CHARSET=latin1;
diff --git a/SQL/paradise_schema_prefixed.sql b/SQL/paradise_schema_prefixed.sql
index 20efd87c90a..802c3fd352b 100644
--- a/SQL/paradise_schema_prefixed.sql
+++ b/SQL/paradise_schema_prefixed.sql
@@ -250,7 +250,7 @@ CREATE TABLE `SS13_player` (
`nanoui_fancy` smallint(4) DEFAULT '1',
`show_ghostitem_attack` smallint(4) DEFAULT '1',
`lastchangelog` varchar(32) NOT NULL DEFAULT '0',
- `exp` mediumtext NOT NULL DEFAULT '',
+ `exp` mediumtext,
PRIMARY KEY (`id`),
UNIQUE KEY `ckey` (`ckey`)
) ENGINE=InnoDB AUTO_INCREMENT=32446 DEFAULT CHARSET=latin1;
diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm
index a72563541d5..c3779ab095d 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -251,7 +251,7 @@
config.use_exp_restrictions_heads = 1
if("use_exp_restrictions_heads_hours")
- config.use_exp_restrictions_heads_hours = value
+ config.use_exp_restrictions_heads_hours = text2num(value)
if("use_exp_restrictions_other")
config.use_exp_restrictions_other = 1
diff --git a/code/game/gamemodes/gameticker.dm b/code/game/gamemodes/gameticker.dm
index 26c037fe7f5..7d6ea9b4e7d 100644
--- a/code/game/gamemodes/gameticker.dm
+++ b/code/game/gamemodes/gameticker.dm
@@ -67,12 +67,6 @@ var/round_start_time = 0
initialtpass = 1
votetimer()
-/datum/controller/gameticker/proc/exptimer()
- spawn(0)
- while(TRUE)
- update_exp(5,0)
- sleep(5 MINUTES)
-
/datum/controller/gameticker/proc/setup()
//Create and announce mode
if(master_mode=="secret")
@@ -234,8 +228,6 @@ var/round_start_time = 0
if(config.sql_enabled)
spawn(3000)
statistic_cycle() // Polls population totals regularly and stores them in an SQL DB
- if(config.use_exp_tracking)
- exptimer()
votetimer()
diff --git a/code/game/jobs/job/engineering.dm b/code/game/jobs/job/engineering.dm
index 3e42c3daa32..dd88a6496ab 100644
--- a/code/game/jobs/job/engineering.dm
+++ b/code/game/jobs/job/engineering.dm
@@ -18,7 +18,7 @@
access_ce, access_RC_announce, access_keycard_auth, access_tcomsat, access_minisat, access_mechanic, access_mineral_storeroom)
minimal_player_age = 21
exp_requirements = 600
- exp_type = "Engineering"
+ exp_type = EXP_TYPE_ENGINEERING
equip(var/mob/living/carbon/human/H)
if(!H) return 0
@@ -56,7 +56,7 @@
alt_titles = list("Maintenance Technician","Engine Technician","Electrician")
minimal_player_age = 7
exp_requirements = 180
- exp_type = "Crew"
+ exp_type = EXP_TYPE_CREW
equip(var/mob/living/carbon/human/H)
if(!H) return 0
H.equip_or_collect(new /obj/item/device/radio/headset/headset_eng(H), slot_l_ear)
@@ -92,7 +92,7 @@
alt_titles = list("Atmospheric Technician")
minimal_player_age = 7
exp_requirements = 180
- exp_type = "Crew"
+ exp_type = EXP_TYPE_CREW
equip(var/mob/living/carbon/human/H)
if(!H) return 0
H.equip_or_collect(new /obj/item/device/radio/headset/headset_eng(H), slot_l_ear)
diff --git a/code/game/jobs/job/medical.dm b/code/game/jobs/job/medical.dm
index b27bc4586be..d623a79a3d1 100644
--- a/code/game/jobs/job/medical.dm
+++ b/code/game/jobs/job/medical.dm
@@ -16,7 +16,7 @@
access_keycard_auth, access_sec_doors, access_psychiatrist, access_maint_tunnels, access_paramedic, access_mineral_storeroom)
minimal_player_age = 21
exp_requirements = 600
- exp_type = "Medical"
+ exp_type = EXP_TYPE_MEDICAL
equip(var/mob/living/carbon/human/H)
if(!H) return 0
@@ -54,7 +54,7 @@
alt_titles = list("Surgeon","Nurse","Coroner")
minimal_player_age = 3
exp_requirements = 60
- exp_type = "Crew"
+ exp_type = EXP_TYPE_CREW
equip(var/mob/living/carbon/human/H)
if(!H) return 0
@@ -119,7 +119,7 @@
alt_titles = list("Pharmacist","Pharmacologist")
minimal_player_age = 7
exp_requirements = 180
- exp_type = "Crew"
+ exp_type = EXP_TYPE_CREW
equip(var/mob/living/carbon/human/H)
if(!H) return 0
@@ -152,7 +152,7 @@
minimal_access = list(access_medical, access_morgue, access_genetics, access_research, access_maint_tunnels)
minimal_player_age = 3
exp_requirements = 60
- exp_type = "Crew"
+ exp_type = EXP_TYPE_CREW
equip(var/mob/living/carbon/human/H)
if(!H) return 0
@@ -184,7 +184,7 @@
alt_titles = list("Pathologist","Microbiologist")
minimal_player_age = 7
exp_requirements = 180
- exp_type = "Crew"
+ exp_type = EXP_TYPE_CREW
equip(var/mob/living/carbon/human/H)
if(!H) return 0
diff --git a/code/game/jobs/job/science.dm b/code/game/jobs/job/science.dm
index 5f9c0b94103..090b6a2adef 100644
--- a/code/game/jobs/job/science.dm
+++ b/code/game/jobs/job/science.dm
@@ -18,7 +18,7 @@
access_RC_announce, access_keycard_auth, access_tcomsat, access_gateway, access_xenoarch, access_minisat, access_maint_tunnels, access_mineral_storeroom)
minimal_player_age = 21
exp_requirements = 600
- exp_type = "Science"
+ exp_type = EXP_TYPE_SCIENCE
// All science-y guys get bonuses for maxing out their tech.
required_objectives=list(
@@ -58,7 +58,7 @@
alt_titles = list("Xenoarcheologist", "Anomalist", "Plasma Researcher", "Xenobiologist", "Chemical Researcher")
minimal_player_age = 3
exp_requirements = 60
- exp_type = "Crew"
+ exp_type = EXP_TYPE_CREW
// All science-y guys get bonuses for maxing out their tech.
required_objectives=list(
@@ -96,7 +96,7 @@
alt_titles = list("Biomechanical Engineer","Mechatronic Engineer")
minimal_player_age = 3
exp_requirements = 60
- exp_type = "Crew"
+ exp_type = EXP_TYPE_CREW
required_objectives=list(
/datum/job_objective/make_cyborg,
diff --git a/code/game/jobs/job/security.dm b/code/game/jobs/job/security.dm
index d13c38d0548..22661bcb35b 100644
--- a/code/game/jobs/job/security.dm
+++ b/code/game/jobs/job/security.dm
@@ -18,7 +18,7 @@
access_heads, access_hos, access_RC_announce, access_keycard_auth, access_gateway, access_pilot, access_weapons)
minimal_player_age = 21
exp_requirements = 600
- exp_type = "Security"
+ exp_type = EXP_TYPE_SECURITY
equip(var/mob/living/carbon/human/H)
if(!H) return 0
@@ -111,7 +111,7 @@
alt_titles = list("Forensic Technician")
minimal_player_age = 14
exp_requirements = 300
- exp_type = "Crew"
+ exp_type = EXP_TYPE_CREW
equip(var/mob/living/carbon/human/H)
if(!H) return 0
H.equip_or_collect(new /obj/item/device/radio/headset/headset_sec/alt(H), slot_l_ear)
@@ -169,7 +169,7 @@
minimal_access = list(access_security, access_sec_doors, access_brig, access_court, access_maint_tunnels, access_weapons)
minimal_player_age = 14
exp_requirements = 300
- exp_type = "Crew"
+ exp_type = EXP_TYPE_CREW
equip(var/mob/living/carbon/human/H)
if(!H) return 0
H.equip_or_collect(new /obj/item/device/radio/headset/headset_sec/alt(H), slot_l_ear)
diff --git a/code/game/jobs/job/silicon.dm b/code/game/jobs/job/silicon.dm
index 41d6cb974e7..5461fb1d9b9 100644
--- a/code/game/jobs/job/silicon.dm
+++ b/code/game/jobs/job/silicon.dm
@@ -9,7 +9,7 @@
req_admin_notify = 1
minimal_player_age = 30
exp_requirements = 300
- exp_type = "Silicon"
+ exp_type = EXP_TYPE_SILICON
equip(var/mob/living/carbon/human/H)
if(!H) return 0
@@ -29,7 +29,7 @@
selection_color = "#ddffdd"
minimal_player_age = 21
exp_requirements = 300
- exp_type = "Crew"
+ exp_type = EXP_TYPE_CREW
alt_titles = list("Android", "Robot")
equip(var/mob/living/carbon/human/H)
diff --git a/code/game/jobs/job/supervisor.dm b/code/game/jobs/job/supervisor.dm
index 4ee2720162c..876706d114d 100644
--- a/code/game/jobs/job/supervisor.dm
+++ b/code/game/jobs/job/supervisor.dm
@@ -13,7 +13,7 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 1)
minimal_access = list() //See get_access()
minimal_player_age = 30
exp_requirements = 600
- exp_type = "Command"
+ exp_type = EXP_TYPE_COMMAND
equip(var/mob/living/carbon/human/H)
if(!H) return 0
H.equip_or_collect(new /obj/item/device/radio/headset/heads/captain/alt(H), slot_l_ear)
@@ -62,7 +62,7 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 1)
req_admin_notify = 1
minimal_player_age = 21
exp_requirements = 600
- exp_type = "Supply"
+ exp_type = EXP_TYPE_SUPPLY
access = list(access_security, access_sec_doors, access_brig, access_court, access_forensics_lockers,
access_medical, access_engine, access_change_ids, access_ai_upload, access_eva, access_heads,
access_all_personal_lockers, access_maint_tunnels, access_bar, access_janitor, access_construction, access_morgue,
@@ -243,7 +243,7 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 1)
alt_titles = list("Lawyer","Public Defender")
minimal_player_age = 30
exp_requirements = 300
- exp_type = "Security"
+ exp_type = EXP_TYPE_SECURITY
idtype = /obj/item/weapon/card/id/security
equip(var/mob/living/carbon/human/H)
diff --git a/code/game/jobs/job_controller.dm b/code/game/jobs/job_controller.dm
index 3a556f6d2e5..ba2e5be5d46 100644
--- a/code/game/jobs/job_controller.dm
+++ b/code/game/jobs/job_controller.dm
@@ -123,7 +123,7 @@ var/global/datum/controller/occupations/job_master
if(job.admin_only) // No admin positions either.
continue
- if(!has_exp_for_job(player, job.title))
+ if(job.available_in_playtime(player.client))
continue
if(jobban_isbanned(player, job.title))
diff --git a/code/game/jobs/job_exp.dm b/code/game/jobs/job_exp.dm
index 535da5303d4..caa59a57fa5 100644
--- a/code/game/jobs/job_exp.dm
+++ b/code/game/jobs/job_exp.dm
@@ -1,20 +1,22 @@
+// Defines
+
+#define EXP_TYPE_CREW "Crew"
+#define EXP_TYPE_COMMAND "Command"
+#define EXP_TYPE_ENGINEERING "Engineering"
+#define EXP_TYPE_SECURITY "Security"
+#define EXP_TYPE_SILICON "Silicon"
+#define EXP_TYPE_SERVICE "Service"
+#define EXP_TYPE_MEDICAL "Medical"
+#define EXP_TYPE_SCIENCE "Science"
+#define EXP_TYPE_SUPPLY "Supply"
+#define EXP_TYPE_SPECIAL "Special"
+#define EXP_TYPE_GHOST "Ghost"
+#define EXP_TYPE_EXEMPT "Exempt"
+
// Globals, Defines, Etc
-var/global/list/exp_jobsmap = list(
- "Crew" = list(), // all playtime from all jobs, together
- "Command" = list(titles = command_positions),
- "Engineering" = list(titles = engineering_positions),
- "Security" = list(titles = security_positions),
- "Silicon" = list(titles = nonhuman_positions),
- "Service" = list(titles = service_positions),
- "Medical" = list(titles = medical_positions),
- "Science" = list(titles = science_positions),
- "Supply" = list(titles = supply_positions),
- "Special" = list(), // special_role
- "Ghost" = list(), // dead/observer
- "Exempt" = list() // special grandfather setting
-)
+
// Admin Verbs
@@ -25,53 +27,51 @@ var/global/list/exp_jobsmap = list(
set name = "Check Player Playtime"
if(!check_rights(R_ADMIN))
return
- var/msg = "
Playtime Report
Playtime:
"
+ var/msg = "Playtime ReportPlaytime:
"
for(var/client/C in clients)
var/list/play_records = params2list(C.prefs.exp)
- var/gen_exp_time = text2num(play_records["Crew"])
+ var/gen_exp_time = text2num(play_records[EXP_TYPE_CREW])
gen_exp_time = num2text(round(gen_exp_time / 60)) + "h"
- msg += "
- [key_name_admin(C.mob)]: " + C.get_exp_general() + " "
- if(msg != "")
- src << browse(msg, "window=Player_playtime_check")
- log_admin("[key_name(usr)] checked player playtime")
- feedback_add_details("admin_verb","MCPP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ msg += "- - [key_name_admin(C.mob)]: " + C.get_exp_general() + "
"
+ msg += "
"
+ src << browse(msg, "window=Player_playtime_check")
/client/proc/cmd_show_exp_panel(var/client/C in clients)
if(!check_rights(R_ADMIN))
return
- var/body = "Playtime for [C.key]
Playtime:"
+ var/body = "Playtime for [C.key]
Playtime:"
body += C.get_exp_report()
- body += ""
+ body += ""
src << browse(body, "window=playerplaytime;size=550x615")
// Procs
-/proc/has_exp_for_job(mob/M, rank)
- var/datum/job/job = job_master.GetJob(rank)
- if(!job)
- return 1
- if(!job.exp_requirements || !job.exp_type)
- return 1
- if(!job_is_xp_locked(rank))
- return 1
- if(!M.client)
+
+/datum/job/proc/available_in_playtime(client/C)
+ if(!C)
return 0
- if(check_rights(R_ADMIN, 0, M))
+ if(!exp_requirements || !exp_type)
+ return 0
+ if(!job_is_xp_locked(src.title))
+ return 0
+ if(check_rights(R_ADMIN, 0, C.mob))
return 1
- var/list/play_records = params2list(M.client.prefs.exp)
- var/isexempt = text2num(play_records["Exempt"])
+ var/list/play_records = params2list(C.prefs.exp)
+ var/isexempt = text2num(play_records[EXP_TYPE_EXEMPT])
if(isexempt)
- return 1
- var/my_exp = text2num(play_records[job.exp_type])
- var/job_requirement = text2num(job.exp_requirements)
- if(config.use_exp_restrictions_heads_hours && ((rank in command_positions) || rank == "AI"))
- job_requirement = text2num(config.use_exp_restrictions_heads_hours) * 60
- if(my_exp >= job_requirement)
- return 1
- else
return 0
+ var/my_exp = text2num(play_records[exp_type])
+ var/job_requirement = text2num(exp_requirements)
+ if(config.use_exp_restrictions_heads_hours && ((title in command_positions) || title == "AI"))
+ job_requirement = config.use_exp_restrictions_heads_hours * 60
+ if(my_exp >= job_requirement)
+ return 0
+ else
+ return (job_requirement - my_exp)
+
+
/proc/job_is_xp_locked(jobtitle)
if(!config.use_exp_restrictions_heads && ((jobtitle in command_positions) || jobtitle == "AI"))
@@ -93,9 +93,9 @@ var/global/list/exp_jobsmap = list(
var/list/play_records = params2list(prefs.exp)
if(!play_records.len)
- return "[src] has no records."
+ return "[key] has no records."
- var/return_text = ""
+ var/return_text = ""
var/list/exp_data = list()
for(var/cat in exp_jobsmap)
@@ -106,43 +106,47 @@ var/global/list/exp_jobsmap = list(
for(var/dep in exp_data)
if(exp_data[dep] > 0)
- if(dep == "Exempt")
- return_text += "
- Exempt (all jobs auto-unlocked)"
- else
- return_text += "
- [dep] " + get_exp_format(exp_data[dep]) + " (" + num2text(round(exp_data[dep]/exp_data["Crew"]*100)) + "%)"
-
+ if(dep == EXP_TYPE_EXEMPT)
+ return_text += "- Exempt (all jobs auto-unlocked)
"
+ else if(exp_data[EXP_TYPE_CREW] > 0)
+ return_text += "- [dep] [get_exp_format(exp_data[dep])] (" + num2text(round(exp_data[dep]/exp_data[EXP_TYPE_CREW]*100)) + "%)
"
+ return_text += "
"
var/list/jobs_locked = list()
var/list/jobs_unlocked = list()
for(var/datum/job/job in job_master.occupations)
if(job.exp_requirements && job.exp_type)
if(!job_is_xp_locked(job.title))
continue
- else if(has_exp_for_job(mob, job.title))
- jobs_unlocked += "
- " + job.title
+ else if(!job.available_in_playtime(mob.client))
+ jobs_unlocked += job.title
else
var/xp_req = job.exp_requirements
if(config.use_exp_restrictions_heads_hours && ((job.title in command_positions) || job.title == "AI"))
- xp_req = text2num(config.use_exp_restrictions_heads_hours) * 60
- jobs_locked += "
- " + job.title + " (" + get_exp_format(text2num(play_records[job.exp_type])) + " / " + get_exp_format(xp_req) + " [job.exp_type] EXP)"
+ xp_req = config.use_exp_restrictions_heads_hours * 60
+ jobs_locked += job.title + " (" + get_exp_format(text2num(play_records[job.exp_type])) + " / " + get_exp_format(xp_req) + " [job.exp_type] EXP)"
if(jobs_unlocked.len)
- return_text += "
Jobs Unlocked: "
+ return_text += "
Jobs Unlocked: "
for(var/text in jobs_unlocked)
- return_text += text
+ return_text += "- [text]
"
+ return_text += "
"
if(jobs_locked.len)
- return_text += "
Jobs Not Unlocked: "
+ return_text += "
Jobs Not Unlocked:"
for(var/text in jobs_locked)
- return_text += text
+ return_text += "- [text]
"
+ return_text += "
"
return return_text
/client/proc/get_exp_general()
var/list/play_records = params2list(prefs.exp)
- var/exp_gen = text2num(play_records["Crew"])
+ var/exp_gen = text2num(play_records[EXP_TYPE_CREW])
return get_exp_format(exp_gen)
-/client/proc/get_exp_format(var/expnum)
- if(expnum > 0)
+/proc/get_exp_format(var/expnum)
+ if(expnum > 60)
return num2text(round(expnum / 60)) + "h"
+ else if(expnum > 0)
+ return num2text(expnum) + "m"
else
return "0h"
@@ -151,47 +155,53 @@ var/global/list/exp_jobsmap = list(
return -1
spawn(0)
for(var/client/C in clients)
- if(C.inactivity < (10 MINUTES))
+ if(C.inactivity >= (10 MINUTES))
+ continue
- var/DBQuery/exp_read = dbcon.NewQuery("SELECT exp FROM [format_table_name("player")] WHERE ckey='[C.ckey]'")
- exp_read.Execute()
- var/list/read_records = list()
- while(exp_read.NextRow())
- read_records = params2list(exp_read.item[1])
+ var/DBQuery/exp_read = dbcon.NewQuery("SELECT exp FROM [format_table_name("player")] WHERE ckey='[C.ckey]'")
+ exp_read.Execute()
+ var/list/read_records = list()
+ while(exp_read.NextRow())
+ read_records = params2list(exp_read.item[1])
- var/list/play_records = list()
- for(var/rtype in exp_jobsmap)
- if(text2num(read_records[rtype]))
- play_records[rtype] = text2num(read_records[rtype])
- else
- play_records[rtype] = 0
-
- if(C.mob.stat == CONSCIOUS && C.mob.mind.assigned_role)
- play_records["Crew"] += minutes
- if(announce_changes)
- to_chat(C.mob,"You got: [minutes] General EXP!")
-
- for(var/cat in exp_jobsmap)
- if(exp_jobsmap[cat]["titles"])
- if(C.mob.mind.assigned_role in exp_jobsmap[cat]["titles"])
- play_records[cat] += minutes
- if(announce_changes)
- to_chat(C.mob,"You got: [minutes] [cat] EXP!")
-
- if(C.mob.mind.special_role)
- play_records["Special"] += minutes
- if(announce_changes)
- to_chat(C.mob,"You got: [minutes] Special EXP!")
-
- else if(isobserver(C.mob))
- play_records["Ghost"] += minutes
- if(announce_changes)
- to_chat(C.mob,"You got: [minutes] Ghost EXP!")
+ var/list/play_records = list()
+ for(var/rtype in exp_jobsmap)
+ if(text2num(read_records[rtype]))
+ play_records[rtype] = text2num(read_records[rtype])
else
- return
- var/new_exp = list2params(play_records)
- C.prefs.exp = new_exp
- new_exp = sanitizeSQL(new_exp)
- var/DBQuery/update_query = dbcon.NewQuery("UPDATE [format_table_name("player")] SET exp = '[new_exp]' WHERE ckey='[C.ckey]'")
- update_query.Execute()
- sleep(10)
\ No newline at end of file
+ play_records[rtype] = 0
+ if(C.mob.stat == CONSCIOUS && C.mob.mind.assigned_role)
+ play_records[EXP_TYPE_CREW] += minutes
+ if(announce_changes)
+ to_chat(C.mob,"You got: [minutes] General EXP!")
+ for(var/cat in exp_jobsmap)
+ if(exp_jobsmap[cat]["titles"])
+ if(C.mob.mind.assigned_role in exp_jobsmap[cat]["titles"])
+ play_records[cat] += minutes
+ if(announce_changes)
+ to_chat(C.mob,"You got: [minutes] [cat] EXP!")
+ if(C.mob.mind.special_role)
+ play_records[EXP_TYPE_SPECIAL] += minutes
+ if(announce_changes)
+ to_chat(C.mob,"You got: [minutes] Special EXP!")
+ else if(isobserver(C.mob))
+ play_records[EXP_TYPE_GHOST] += minutes
+ if(announce_changes)
+ to_chat(C.mob,"You got: [minutes] Ghost EXP!")
+ else
+ return
+ var/new_exp = list2params(play_records)
+ C.prefs.exp = new_exp
+ new_exp = sanitizeSQL(new_exp)
+ var/DBQuery/update_query = dbcon.NewQuery("UPDATE [format_table_name("player")] SET exp = '[new_exp]' WHERE ckey='[C.ckey]'")
+ update_query.Execute()
+ sleep(10)
+
+
+/hook/roundstart/proc/exptimer()
+ if(!config.sql_enabled || !config.use_exp_tracking)
+ return 0
+ spawn(0)
+ while(TRUE)
+ sleep(5 MINUTES)
+ update_exp(5,0)
diff --git a/code/game/jobs/jobs.dm b/code/game/jobs/jobs.dm
index 6af69ebed9d..0c8f6a5d47d 100644
--- a/code/game/jobs/jobs.dm
+++ b/code/game/jobs/jobs.dm
@@ -183,3 +183,17 @@ var/list/whitelisted_positions = list(
return titles
+var/global/list/exp_jobsmap = list(
+ EXP_TYPE_CREW = list(), // all playtime from all jobs, together
+ EXP_TYPE_COMMAND = list(titles = command_positions),
+ EXP_TYPE_ENGINEERING = list(titles = engineering_positions),
+ EXP_TYPE_SECURITY = list(titles = security_positions),
+ EXP_TYPE_SILICON = list(titles = nonhuman_positions),
+ EXP_TYPE_SERVICE = list(titles = service_positions),
+ EXP_TYPE_MEDICAL = list(titles = medical_positions),
+ EXP_TYPE_SCIENCE = list(titles = science_positions),
+ EXP_TYPE_SUPPLY = list(titles = supply_positions),
+ EXP_TYPE_SPECIAL = list(), // special_role
+ EXP_TYPE_GHOST = list(), // dead/observer
+ EXP_TYPE_EXEMPT = list() // special grandfather setting
+)
\ No newline at end of file
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index a32da447050..4d965ab38f8 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -2018,8 +2018,9 @@
else if(href_list["getplaytimewindow"])
if(!check_rights(R_ADMIN)) return
-
var/mob/M = locate(href_list["getplaytimewindow"])
+ if(!M)
+ return
usr.client.cmd_show_exp_panel(M.client)
else if(href_list["jumpto"])
diff --git a/code/modules/client/preference/preferences.dm b/code/modules/client/preference/preferences.dm
index 50fd26b929c..8832e3fc3e4 100644
--- a/code/modules/client/preference/preferences.dm
+++ b/code/modules/client/preference/preferences.dm
@@ -552,8 +552,9 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts
if(jobban_isbanned(user, rank))
HTML += "[rank] \[BANNED] | "
continue
- if(!has_exp_for_job(user, job.title))
- HTML += "[rank] \[TIME] | "
+ var/available_in_playtime = job.available_in_playtime(user.client)
+ if(available_in_playtime)
+ HTML += "[rank] \[ " + get_exp_format(available_in_playtime) + " as [job.exp_type] ] | "
continue
if(!job.player_old_enough(user.client))
var/available_in_days = job.available_in_days(user.client)
diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm
index 6665bb54982..97e07a71fb0 100644
--- a/code/modules/mob/new_player/new_player.dm
+++ b/code/modules/mob/new_player/new_player.dm
@@ -246,7 +246,7 @@
if(!is_job_whitelisted(src, rank)) return 0
if(!job.player_old_enough(src.client)) return 0
if(job.admin_only && !(check_rights(R_EVENT, 0))) return 0
- if(!has_exp_for_job(src, rank))
+ if(job.available_in_playtime(src.client))
return 0
if(config.assistantlimit)