diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm
index 8ac165cec6a..24c8235c83a 100644
--- a/code/__DEFINES/preferences.dm
+++ b/code/__DEFINES/preferences.dm
@@ -27,3 +27,19 @@
#define CHAT_NO_ADMINLOGS 16384
#define TOGGLES_DEFAULT (CHAT_OOC|CHAT_DEAD|CHAT_GHOSTEARS|CHAT_GHOSTSIGHT|CHAT_PRAYER|CHAT_RADIO|CHAT_ATTACKLOGS|CHAT_LOOC|MEMBER_PUBLIC)
+
+// Playtime tracking system, see jobs_exp.dm
+#define EXP_TYPE_LIVING "Living"
+#define EXP_TYPE_CREW "Crew"
+#define EXP_TYPE_COMMAND "Command"
+#define EXP_TYPE_ENGINEERING "Engineering"
+#define EXP_TYPE_MEDICAL "Medical"
+#define EXP_TYPE_SCIENCE "Science"
+#define EXP_TYPE_SUPPLY "Supply"
+#define EXP_TYPE_SECURITY "Security"
+#define EXP_TYPE_SILICON "Silicon"
+#define EXP_TYPE_SERVICE "Service"
+#define EXP_TYPE_WHITELIST "Whitelist"
+#define EXP_TYPE_SPECIAL "Special"
+#define EXP_TYPE_GHOST "Ghost"
+#define EXP_TYPE_EXEMPT "Exempt"
\ No newline at end of file
diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm
index c3779ab095d..67939f227b5 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -131,6 +131,7 @@
var/use_exp_restrictions_heads = 0
var/use_exp_restrictions_heads_hours = 0
var/use_exp_restrictions_other = 0
+ var/use_exp_restrictions_admin_bypass = 0
var/simultaneous_pm_warning_timeout = 100
@@ -256,6 +257,9 @@
if("use_exp_restrictions_other")
config.use_exp_restrictions_other = 1
+ if("use_exp_restrictions_admin_bypass")
+ config.use_exp_restrictions_admin_bypass = 1
+
if("jobs_have_minimal_access")
config.jobs_have_minimal_access = 1
diff --git a/code/game/jobs/job_exp.dm b/code/game/jobs/job_exp.dm
index 7d312b43319..dfeb9520502 100644
--- a/code/game/jobs/job_exp.dm
+++ b/code/game/jobs/job_exp.dm
@@ -1,27 +1,5 @@
-
-// 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
-
-
-
-
// Admin Verbs
-
/client/proc/cmd_admin_check_player_exp() //Allows admins to determine who the newer players are.
set category = "Admin"
set name = "Check Player Playtime"
@@ -29,20 +7,21 @@
return
var/msg = "
Playtime ReportPlaytime:
"
src << browse(msg, "window=Player_playtime_check")
/datum/admins/proc/cmd_show_exp_panel(var/client/C)
- if (!C)
+ if(!C)
+ to_chat(usr, "ERROR: Mob not found.")
return
if(!check_rights(R_ADMIN))
return
var/body = "Playtime for [C.key]
Playtime:"
body += C.get_exp_report()
body += ""
- usr << browse(body, "window=playerplaytime;size=550x615")
+ usr << browse(body, "window=playerplaytime[C.ckey];size=550x615")
// Procs
@@ -55,7 +34,7 @@
return 0
if(!job_is_xp_locked(src.title))
return 0
- if(check_rights(R_ADMIN, 0, C.mob))
+ if(config.use_exp_restrictions_admin_bypass && check_rights(R_ADMIN, 0, C.mob))
return 0
var/list/play_records = params2list(C.prefs.exp)
var/isexempt = text2num(play_records[EXP_TYPE_EXEMPT])
@@ -107,12 +86,13 @@
if(exp_data[dep] > 0)
if(dep == EXP_TYPE_EXEMPT)
return_text += "Exempt (all jobs auto-unlocked)"
- else if(exp_data[EXP_TYPE_CREW] > 0)
- var/my_pc = num2text(round(exp_data[dep]/exp_data[EXP_TYPE_CREW]*100))
+ else if(exp_data[EXP_TYPE_LIVING] > 0)
+ var/my_pc = num2text(round(exp_data[dep]/exp_data[EXP_TYPE_LIVING]*100))
return_text += "[dep] [get_exp_format(exp_data[dep])] ([my_pc]%)"
else
return_text += "[dep] [get_exp_format(exp_data[dep])] "
-
+ if(config.use_exp_restrictions_admin_bypass && check_rights(R_ADMIN, 0, mob))
+ return_text += "Admin (all jobs auto-unlocked)"
return_text += ""
var/list/jobs_locked = list()
var/list/jobs_unlocked = list()
@@ -140,9 +120,9 @@
return return_text
-/client/proc/get_exp_general()
+/client/proc/get_exp_living()
var/list/play_records = params2list(prefs.exp)
- var/exp_gen = text2num(play_records[EXP_TYPE_CREW])
+ var/exp_gen = text2num(play_records[EXP_TYPE_LIVING])
return get_exp_format(exp_gen)
/proc/get_exp_format(var/expnum)
@@ -153,53 +133,60 @@
else
return "0h"
-/proc/update_exp(var/minutes, var/announce_changes = 0)
+/proc/update_exp(var/mins, var/ann = 0)
if(!establish_db_connection())
return -1
spawn(0)
- for(var/client/C in clients)
- if(C.inactivity >= (10 MINUTES))
+ for(var/client/L in clients)
+ if(L.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/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[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()
+ spawn(0)
+ update_exp_client(L, mins, ann)
sleep(10)
+/proc/update_exp_client(var/client/C, var/minutes, var/announce_changes = 0)
+ if(!C ||!C.ckey)
+ return
+ 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()
+ var/hasread = 0
+ while(exp_read.NextRow())
+ read_records = params2list(exp_read.item[1])
+ hasread = 1
+ if(!hasread)
+ return
+ 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[EXP_TYPE_LIVING] += minutes
+ if(announce_changes)
+ to_chat(C.mob,"You got: [minutes] Living 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()
/hook/roundstart/proc/exptimer()
if(!config.sql_enabled || !config.use_exp_tracking)
diff --git a/code/game/jobs/jobs.dm b/code/game/jobs/jobs.dm
index 0c8f6a5d47d..0604ec0f428 100644
--- a/code/game/jobs/jobs.dm
+++ b/code/game/jobs/jobs.dm
@@ -184,16 +184,18 @@ 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_LIVING = list(), // all living mobs
+ EXP_TYPE_CREW = list(titles = command_positions + engineering_positions + medical_positions + science_positions + support_positions + supply_positions + security_positions + civilian_positions + whitelisted_positions), // crew positions
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_SECURITY = list(titles = security_positions),
+ EXP_TYPE_SILICON = list(titles = nonhuman_positions),
+ EXP_TYPE_SERVICE = list(titles = service_positions),
+ EXP_TYPE_WHITELIST = list(titles = whitelisted_positions), // karma-locked jobs
+ EXP_TYPE_SPECIAL = list(), // antags, ERT, etc
+ EXP_TYPE_GHOST = list(), // dead people, observers
EXP_TYPE_EXEMPT = list() // special grandfather setting
)
\ No newline at end of file
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index f0faa025854..be3fe24259c 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -43,7 +43,7 @@ var/global/nologevent = 0
if(M.client)
body += " played by [M.client] "
body += "\[[M.client.holder ? M.client.holder.rank : "Player"]\] "
- body += "\[" + M.client.get_exp_general() + "\]"
+ body += "\[" + M.client.get_exp_living() + "\]"
if(istype(M, /mob/new_player))
body += " Hasn't Entered Game "
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index 85c826a5594..71a2c81011e 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -2017,9 +2017,11 @@
fax_panel(usr)
else if(href_list["getplaytimewindow"])
- if(!check_rights(R_ADMIN)) return
+ if(!check_rights(R_ADMIN))
+ return
var/mob/M = locate(href_list["getplaytimewindow"])
if(!M)
+ to_chat(usr, "ERROR: Mob not found.")
return
cmd_show_exp_panel(M.client)
diff --git a/config/example/config.txt b/config/example/config.txt
index 350c59b079d..9a0fe069c77 100644
--- a/config/example/config.txt
+++ b/config/example/config.txt
@@ -20,10 +20,12 @@ JOBS_HAVE_MINIMAL_ACCESS
## USE_EXP_TRACKING enables playtime/exp tracking. REQUIRES DATABASE TO BE ENABLED.
## USE_EXP_RESTRICTIONS_HEADS enables playtime/exp requirements for head jobs. If USE_EXP_RESTRICTIONS_HEADS_HOURS is enabled also, it uses that value for their time requirements. If not, it goes by the values defined on their job definitions in code.
## USE_EXP_RESTRICTIONS_OTHER enables playtime/exp requirements for some dangerous non-head jobs, like engineer, scientist, security officer, etc. The requirements for these jobs are always taken from code.
+## USE_EXP_RESTRICTIONS_ADMIN_BYPASS lets admins always bypass the restrictions. Useful to turn off for testing.
#USE_EXP_TRACKING
#USE_EXP_RESTRICTIONS_HEADS
#USE_EXP_RESTRICTIONS_HEADS_HOURS 15
#USE_EXP_RESTRICTIONS_OTHER
+#USE_EXP_RESTRICTIONS_ADMIN_BYPASS
## Unhash this entry to have certain antagonist roles require your account to be at least a certain number of days old to select. You can configure the exact age requirement for different antag roles by editing special_role_times in /code/modules/client/preferences.dm.
## See USE_AGE_RESTRICTION_FOR_JOBS for more information