diff --git a/SQL/paradise_schema.sql b/SQL/paradise_schema.sql
index 9bfc6fa90b0..0b450df9848 100644
--- a/SQL/paradise_schema.sql
+++ b/SQL/paradise_schema.sql
@@ -274,6 +274,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,
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 c0cc8c86d6b..2489e6cb325 100644
--- a/SQL/paradise_schema_prefixed.sql
+++ b/SQL/paradise_schema_prefixed.sql
@@ -273,6 +273,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,
PRIMARY KEY (`id`),
UNIQUE KEY `ckey` (`ckey`)
) ENGINE=InnoDB AUTO_INCREMENT=32446 DEFAULT CHARSET=latin1;
diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm
index d39cc600221..05200eea7dd 100644
--- a/code/__DEFINES/preferences.dm
+++ b/code/__DEFINES/preferences.dm
@@ -27,3 +27,10 @@
#define DONATOR_PUBLIC 32768
#define TOGGLES_DEFAULT (CHAT_OOC|CHAT_DEAD|CHAT_GHOSTEARS|CHAT_GHOSTSIGHT|CHAT_PRAYER|CHAT_RADIO|CHAT_ATTACKLOGS|CHAT_LOOC|MEMBER_PUBLIC|DONATOR_PUBLIC)
+
+// Playtime tracking system, see jobs_exp.dm
+#define EXP_TYPE_LIVING "Living"
+#define EXP_TYPE_CREW "Crew"
+#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 9ac4a0eb4d6..2b5f80d7249 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -127,6 +127,10 @@
var/use_age_restriction_for_jobs = 0 //Do jobs use account age restrictions? --requires database
var/use_age_restriction_for_antags = 0 //Do antags use account age restrictions? --requires database
+ var/use_exp_tracking = 0
+ var/use_exp_restrictions = 0
+ var/use_exp_restrictions_admin_bypass = 0
+
var/simultaneous_pm_warning_timeout = 100
var/assistant_maint = 0 //Do assistants get maint access?
@@ -240,6 +244,15 @@
if("use_age_restriction_for_antags")
config.use_age_restriction_for_antags = 1
+ if("use_exp_tracking")
+ config.use_exp_tracking = 1
+
+ if("use_exp_restrictions")
+ config.use_exp_restrictions = 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/engineering.dm b/code/game/jobs/job/engineering.dm
index a8846f2134a..0384276a5e6 100644
--- a/code/game/jobs/job/engineering.dm
+++ b/code/game/jobs/job/engineering.dm
@@ -17,7 +17,8 @@
access_heads, access_construction, access_sec_doors,
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 = EXP_TYPE_CREW
equip(var/mob/living/carbon/human/H)
if(!H) return 0
@@ -51,7 +52,6 @@
minimal_access = list(access_eva, access_engine, access_engine_equip, access_tech_storage, access_maint_tunnels, access_external_airlocks, access_construction, access_mineral_storeroom)
alt_titles = list("Maintenance Technician","Engine Technician","Electrician")
minimal_player_age = 7
-
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)
@@ -83,7 +83,6 @@
minimal_access = list(access_eva, access_atmospherics, access_maint_tunnels, access_external_airlocks, access_emergency_storage, access_construction, access_mineral_storeroom)
alt_titles = list("Atmospheric Technician")
minimal_player_age = 7
-
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/job.dm b/code/game/jobs/job/job.dm
index 104ed27bc05..52a33e0f0fb 100644
--- a/code/game/jobs/job/job.dm
+++ b/code/game/jobs/job/job.dm
@@ -41,6 +41,9 @@
//If you have use_age_restriction_for_jobs config option enabled and the database set up, this option will add a requirement for players to be at least minimal_player_age days old. (meaning they first signed in at least that many days before.)
var/minimal_player_age = 0
+ var/exp_requirements = 0
+ var/exp_type = ""
+
var/admin_only = 0
var/spawn_ert = 0
diff --git a/code/game/jobs/job/medical.dm b/code/game/jobs/job/medical.dm
index 1baceb6a57e..64b34a4fa79 100644
--- a/code/game/jobs/job/medical.dm
+++ b/code/game/jobs/job/medical.dm
@@ -15,6 +15,8 @@
access_chemistry, access_virology, access_cmo, access_surgery, access_RC_announce,
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 = EXP_TYPE_CREW
equip(var/mob/living/carbon/human/H)
if(!H) return 0
@@ -109,7 +111,6 @@
alt_titles = list("Pharmacist","Pharmacologist")
minimal_player_age = 7
-
equip(var/mob/living/carbon/human/H)
if(!H) return 0
switch(H.backbag)
diff --git a/code/game/jobs/job/science.dm b/code/game/jobs/job/science.dm
index f03f5b4c0a1..7ea2eb5464f 100644
--- a/code/game/jobs/job/science.dm
+++ b/code/game/jobs/job/science.dm
@@ -17,6 +17,8 @@
access_research, access_robotics, access_xenobiology, access_ai_upload,
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 = EXP_TYPE_CREW
// All science-y guys get bonuses for maxing out their tech.
required_objectives=list(
diff --git a/code/game/jobs/job/security.dm b/code/game/jobs/job/security.dm
index 47532f8d081..19539a83f4e 100644
--- a/code/game/jobs/job/security.dm
+++ b/code/game/jobs/job/security.dm
@@ -17,6 +17,8 @@
access_research, access_engine, access_mining, access_medical, access_construction, access_mailsorting,
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 = EXP_TYPE_CREW
equip(var/mob/living/carbon/human/H)
if(!H) return 0
@@ -56,6 +58,8 @@
access = list(access_security, access_sec_doors, access_brig, access_armory, access_court, access_maint_tunnels, access_morgue, access_weapons)
minimal_access = list(access_security, access_sec_doors, access_brig, access_armory, access_court, access_maint_tunnels, access_weapons)
minimal_player_age = 21
+ exp_requirements = 300
+ exp_type = EXP_TYPE_CREW
equip(var/mob/living/carbon/human/H)
if(!H) return 0
@@ -98,6 +102,8 @@
minimal_access = list(access_sec_doors, access_forensics_lockers, access_morgue, access_maint_tunnels, access_court, access_weapons)
alt_titles = list("Forensic Technician")
minimal_player_age = 14
+ exp_requirements = 300
+ 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)
@@ -148,6 +154,8 @@
access = list(access_security, access_sec_doors, access_brig, access_court, access_maint_tunnels, access_morgue, access_weapons)
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 = 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 7bb2b006f87..eedb50a8412 100644
--- a/code/game/jobs/job/silicon.dm
+++ b/code/game/jobs/job/silicon.dm
@@ -8,6 +8,8 @@
supervisors = "your laws"
req_admin_notify = 1
minimal_player_age = 30
+ exp_requirements = 600
+ exp_type = EXP_TYPE_CREW
equip(var/mob/living/carbon/human/H)
if(!H) return 0
@@ -15,7 +17,7 @@
/datum/job/ai/is_position_available()
return (empty_playable_ai_cores.len != 0)
-
+
/datum/job/cyborg
title = "Cyborg"
@@ -26,6 +28,8 @@
supervisors = "your laws and the AI" //Nodrak
selection_color = "#ddffdd"
minimal_player_age = 21
+ exp_requirements = 300
+ 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 66f0ddb2c65..586fd27d639 100644
--- a/code/game/jobs/job/supervisor.dm
+++ b/code/game/jobs/job/supervisor.dm
@@ -12,6 +12,8 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 1)
access = list() //See get_access()
minimal_access = list() //See get_access()
minimal_player_age = 30
+ exp_requirements = 600
+ 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/heads/captain/alt(H), slot_l_ear)
@@ -56,6 +58,8 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 1)
idtype = /obj/item/weapon/card/id/silver
req_admin_notify = 1
minimal_player_age = 21
+ exp_requirements = 600
+ exp_type = EXP_TYPE_CREW
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,
diff --git a/code/game/jobs/job_controller.dm b/code/game/jobs/job_controller.dm
index 9073f56ceb3..93d048ce96e 100644
--- a/code/game/jobs/job_controller.dm
+++ b/code/game/jobs/job_controller.dm
@@ -123,6 +123,9 @@ var/global/datum/controller/occupations/job_master
if(job.admin_only) // No admin positions either.
continue
+ if(job.available_in_playtime(player.client))
+ continue
+
if(jobban_isbanned(player, job.title))
Debug("GRJ isbanned failed, Player: [player], Job: [job.title]")
continue
diff --git a/code/game/jobs/job_exp.dm b/code/game/jobs/job_exp.dm
new file mode 100644
index 00000000000..520b1182e49
--- /dev/null
+++ b/code/game/jobs/job_exp.dm
@@ -0,0 +1,190 @@
+// 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"
+ if(!check_rights(R_ADMIN))
+ return
+ var/msg = "
Playtime ReportPlaytime:
"
+ src << browse(msg, "window=Player_playtime_check")
+
+
+/datum/admins/proc/cmd_show_exp_panel(var/client/C)
+ if(!C)
+ to_chat(usr, "ERROR: Client 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[C.ckey];size=550x615")
+
+
+// Procs
+
+
+/datum/job/proc/available_in_playtime(client/C)
+ if(!C)
+ return 0
+ if(!exp_requirements || !exp_type)
+ return 0
+ if(!config.use_exp_restrictions)
+ return 0
+ 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])
+ if(isexempt)
+ return 0
+ var/my_exp = text2num(play_records[get_exp_req_type()])
+ var/job_requirement = text2num(get_exp_req_amount())
+ if(my_exp >= job_requirement)
+ return 0
+ else
+ return (job_requirement - my_exp)
+
+/datum/job/proc/get_exp_req_amount()
+ return exp_requirements
+
+/datum/job/proc/get_exp_req_type()
+ return exp_type
+
+/mob/proc/get_exp_report()
+ if(client)
+ return client.get_exp_report()
+ else
+ return "[src] has no client."
+
+/client/proc/get_exp_report()
+ if(!config.use_exp_tracking)
+ return "Tracking is disabled in the server configuration file."
+ var/list/play_records = params2list(prefs.exp)
+ if(!play_records.len)
+ return "[key] has no records."
+ var/return_text = ""
+ var/list/exp_data = list()
+ for(var/category in exp_jobsmap)
+ if(text2num(play_records[category]))
+ exp_data[category] = text2num(play_records[category])
+ else
+ exp_data[category] = 0
+ for(var/dep in exp_data)
+ if(exp_data[dep] > 0)
+ if(dep == EXP_TYPE_EXEMPT)
+ return_text += "- Exempt (all jobs auto-unlocked)
"
+ 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()
+ for(var/datum/job/job in job_master.occupations)
+ if(job.exp_requirements && job.exp_type)
+ if(!job.available_in_playtime(mob.client))
+ jobs_unlocked += job.title
+ else
+ var/xp_req = job.get_exp_req_amount()
+ jobs_locked += "[job.title] [get_exp_format(text2num(play_records[job.get_exp_req_type()]))] / [get_exp_format(xp_req)] as [job.get_exp_req_type()])"
+ if(jobs_unlocked.len)
+ return_text += "
Jobs Unlocked:- "
+ return_text += jobs_unlocked.Join("
- ")
+ return_text += "
"
+ if(jobs_locked.len)
+ return_text += "
Jobs Not Unlocked:- "
+ return_text += jobs_locked.Join("
- ")
+ return_text += "
"
+ return return_text
+
+
+/client/proc/get_exp_living()
+ var/list/play_records = params2list(prefs.exp)
+ var/exp_living = text2num(play_records[EXP_TYPE_LIVING])
+ return get_exp_format(exp_living)
+
+/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"
+
+/proc/update_exp(var/mins, var/ann = 0)
+ if(!establish_db_connection())
+ return -1
+ spawn(0)
+ for(var/client/L in clients)
+ if(L.inactivity >= (10 MINUTES))
+ continue
+ spawn(0)
+ L.update_exp_client(mins, ann)
+ sleep(10)
+
+/client/proc/update_exp_client(var/minutes, var/announce_changes = 0)
+ if(!src ||!ckey)
+ return
+ var/DBQuery/exp_read = dbcon.NewQuery("SELECT exp FROM [format_table_name("player")] WHERE ckey='[ckey]'")
+ if(!exp_read.Execute())
+ var/err = exp_read.ErrorMsg()
+ log_game("SQL ERROR during exp_update_client read. Error : \[[err]\]\n")
+ message_admins("SQL ERROR during exp_update_client read. Error : \[[err]\]\n")
+ return
+ 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(mob.stat == CONSCIOUS && mob.mind.assigned_role)
+ play_records[EXP_TYPE_LIVING] += minutes
+ if(announce_changes)
+ to_chat(mob,"You got: [minutes] Living EXP!")
+ for(var/category in exp_jobsmap)
+ if(exp_jobsmap[category]["titles"])
+ if(mob.mind.assigned_role in exp_jobsmap[category]["titles"])
+ play_records[category] += minutes
+ if(announce_changes)
+ to_chat(mob,"You got: [minutes] [category] EXP!")
+ if(mob.mind.special_role)
+ play_records[EXP_TYPE_SPECIAL] += minutes
+ if(announce_changes)
+ to_chat(mob,"You got: [minutes] Special EXP!")
+ else if(isobserver(mob))
+ play_records[EXP_TYPE_GHOST] += minutes
+ if(announce_changes)
+ to_chat(mob,"You got: [minutes] Ghost EXP!")
+ else
+ return
+ var/new_exp = list2params(play_records)
+ 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='[ckey]'")
+ if(!update_query.Execute())
+ var/err = update_query.ErrorMsg()
+ log_game("SQL ERROR during exp_update_client write. Error : \[[err]\]\n")
+ message_admins("SQL ERROR during exp_update_client write. Error : \[[err]\]\n")
+ return
+
+/hook/roundstart/proc/exptimer()
+ if(!config.sql_enabled || !config.use_exp_tracking)
+ return 1
+ spawn(0)
+ while(TRUE)
+ sleep(5 MINUTES)
+ update_exp(5,0)
+ return 1
diff --git a/code/game/jobs/jobs.dm b/code/game/jobs/jobs.dm
index 6af69ebed9d..73f5cc2e5b0 100644
--- a/code/game/jobs/jobs.dm
+++ b/code/game/jobs/jobs.dm
@@ -183,3 +183,10 @@ var/list/whitelisted_positions = list(
return titles
+var/global/list/exp_jobsmap = list(
+ 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 | list("AI","Cyborg") | whitelisted_positions), // crew positions
+ EXP_TYPE_SPECIAL = list(), // antags, ERT, etc
+ EXP_TYPE_GHOST = list(), // dead people, observers
+ EXP_TYPE_EXEMPT = list() // special grandfather setting
+)
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index 72de785456e..88a0c7708f0 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -41,7 +41,8 @@ var/global/nologevent = 0
body += "Options panel for [M]"
if(M.client)
body += " played by [M.client] "
- body += "\[[M.client.holder ? M.client.holder.rank : "Player"]\]"
+ body += "\[[M.client.holder ? M.client.holder.rank : "Player"]\] "
+ body += "\[" + M.client.get_exp_living() + "\]"
if(istype(M, /mob/new_player))
body += " Hasn't Entered Game "
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index ba46a995ffd..f3cf5c920db 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -75,6 +75,7 @@ var/list/admin_verbs_admin = list(
/client/proc/debug_variables,
/client/proc/show_snpc_verbs,
/client/proc/reset_all_tcs, /*resets all telecomms scripts*/
+ /client/proc/cmd_admin_check_player_exp, /* shows players by playtime */
/client/proc/toggle_mentor_chat
)
var/list/admin_verbs_ban = list(
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index 3b8e33d04f8..aeb00df710a 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -2009,6 +2009,15 @@
fax_panel(usr)
+ else if(href_list["getplaytimewindow"])
+ if(!check_rights(R_ADMIN))
+ return
+ var/mob/M = locateUID(href_list["getplaytimewindow"])
+ if(!M)
+ to_chat(usr, "ERROR: Mob not found.")
+ return
+ cmd_show_exp_panel(M.client)
+
else if(href_list["jumpto"])
if(!check_rights(R_ADMIN)) return
diff --git a/code/modules/client/preference/preferences.dm b/code/modules/client/preference/preferences.dm
index c049852e3be..8f82ab7274f 100644
--- a/code/modules/client/preference/preferences.dm
+++ b/code/modules/client/preference/preferences.dm
@@ -82,6 +82,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts
//game-preferences
var/lastchangelog = "" //Saved changlog filesize to detect if there was a change
+ var/exp
var/ooccolor = "#b82e00"
var/be_special = list() //Special role selection
var/UI_style = "Midnight"
@@ -593,6 +594,10 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts
if(jobban_isbanned(user, rank))
HTML += "[rank] \[BANNED] | "
continue
+ var/available_in_playtime = job.available_in_playtime(user.client)
+ if(available_in_playtime)
+ HTML += "[rank] \[ " + get_exp_format(available_in_playtime) + " as " + job.get_exp_req_type() + " | "
+ continue
if(!job.player_old_enough(user.client))
var/available_in_days = job.available_in_days(user.client)
HTML += "[rank] \[IN [(available_in_days)] DAYS] | "
diff --git a/code/modules/client/preference/preferences_mysql.dm b/code/modules/client/preference/preferences_mysql.dm
index 1425f714183..581daf222d3 100644
--- a/code/modules/client/preference/preferences_mysql.dm
+++ b/code/modules/client/preference/preferences_mysql.dm
@@ -13,7 +13,8 @@
volume,
nanoui_fancy,
show_ghostitem_attack,
- lastchangelog
+ lastchangelog,
+ exp
FROM [format_table_name("player")]
WHERE ckey='[C.ckey]'"}
)
@@ -40,6 +41,7 @@
nanoui_fancy = text2num(query.item[11])
show_ghostitem_attack = text2num(query.item[12])
lastchangelog = query.item[13]
+ exp = query.item[14]
//Sanitize
ooccolor = sanitize_hexcolor(ooccolor, initial(ooccolor))
@@ -54,6 +56,7 @@
nanoui_fancy = sanitize_integer(nanoui_fancy, 0, 1, initial(nanoui_fancy))
show_ghostitem_attack = sanitize_integer(show_ghostitem_attack, 0, 1, initial(show_ghostitem_attack))
lastchangelog = sanitize_text(lastchangelog, initial(lastchangelog))
+ exp = sanitize_text(exp, initial(exp))
return 1
/datum/preferences/proc/save_preferences(client/C)
diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm
index 0e354b4c4a6..fdc1ace289c 100644
--- a/code/modules/mob/living/silicon/robot/drone/drone.dm
+++ b/code/modules/mob/living/silicon/robot/drone/drone.dm
@@ -285,6 +285,7 @@
if(player.mob && player.mob.mind)
player.mob.mind.transfer_to(src)
+ player.mob.mind.assigned_role = "Drone"
lawupdate = 0
to_chat(src, "Systems rebooted. Loading base pattern maintenance protocol... loaded.")
diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm
index f8a73364264..3c5e3ac0130 100644
--- a/code/modules/mob/new_player/new_player.dm
+++ b/code/modules/mob/new_player/new_player.dm
@@ -244,8 +244,10 @@
if(!job.is_position_available()) return 0
if(jobban_isbanned(src,rank)) return 0
if(!is_job_whitelisted(src, rank)) return 0
- if(!job.player_old_enough(src.client)) return 0
+ if(!job.player_old_enough(client)) return 0
if(job.admin_only && !(check_rights(R_EVENT, 0))) return 0
+ if(job.available_in_playtime(client))
+ return 0
if(config.assistantlimit)
if(job.title == "Civilian")
diff --git a/config/example/config.txt b/config/example/config.txt
index 31bde9c5e4f..7c1b8d6051b 100644
--- a/config/example/config.txt
+++ b/config/example/config.txt
@@ -17,6 +17,13 @@ JOBS_HAVE_MINIMAL_ACCESS
## you have noone older than 0 days, since noone has been logged yet. Only turn this on once you have had the database up for 30 days.
#USE_AGE_RESTRICTION_FOR_JOBS
+##Unhash this to track player playtime in the database. Requires database to be enabled.
+#USE_EXP_TRACKING
+##Unhash this to enable playtime requirements for jobs that have them defined.
+#USE_EXP_RESTRICTIONS
+##Allows admins to bypass job playtime requirements.
+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
#USE_AGE_RESTRICTION_FOR_ANTAGS
diff --git a/paradise.dme b/paradise.dme
index 1f9b3430ba4..d27aa499fa0 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -465,6 +465,7 @@
#include "code\game\gamemodes\wizard\wizard.dm"
#include "code\game\jobs\access.dm"
#include "code\game\jobs\job_controller.dm"
+#include "code\game\jobs\job_exp.dm"
#include "code\game\jobs\job_objective.dm"
#include "code\game\jobs\jobs.dm"
#include "code\game\jobs\whitelist.dm"