diff --git a/SQL/paradise_schema.sql b/SQL/paradise_schema.sql
index dd54aea2e92..9278be59503 100644
--- a/SQL/paradise_schema.sql
+++ b/SQL/paradise_schema.sql
@@ -250,6 +250,7 @@ CREATE TABLE `player` (
`nanoui_fancy` smallint(4) DEFAULT '1',
`show_ghostitem_attack` smallint(4) DEFAULT '1',
`lastchangelog` varchar(32) NOT NULL,
+ `exp` mediumtext DEFAULT '',
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 5cedd40abfc..f8f5a68866b 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -127,6 +127,9 @@
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 = 1
+ var/use_exp_restrictions = 0
+
var/simultaneous_pm_warning_timeout = 100
var/assistant_maint = 0 //Do assistants get maint access?
@@ -239,6 +242,12 @@
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("jobs_have_minimal_access")
config.jobs_have_minimal_access = 1
diff --git a/code/game/gamemodes/gameticker.dm b/code/game/gamemodes/gameticker.dm
index 7d6ea9b4e7d..aef3f54af66 100644
--- a/code/game/gamemodes/gameticker.dm
+++ b/code/game/gamemodes/gameticker.dm
@@ -470,6 +470,7 @@ var/round_start_time = 0
if(findtext("[handler]","auto_declare_completion_"))
call(mode, handler)()
+ update_exp(round(ROUND_TIME / 60))
scoreboard()
karmareminder()
diff --git a/code/game/jobs/job/engineering.dm b/code/game/jobs/job/engineering.dm
index 73e3e5717d3..c13ccc93e85 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 = 240
+ exp_type = "eng"
equip(var/mob/living/carbon/human/H)
if(!H) return 0
@@ -54,7 +55,8 @@
minimal_access = list(access_eva, access_engine, access_engine_equip, access_tech_storage, access_maint_tunnels, access_external_airlocks, access_construction)
alt_titles = list("Maintenance Technician","Engine Technician","Electrician")
minimal_player_age = 7
-
+ exp_requirements = 120
+ exp_type = "all"
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)
@@ -89,7 +91,8 @@
minimal_access = list(access_eva, access_atmospherics, access_maint_tunnels, access_external_airlocks, access_emergency_storage, access_construction)
alt_titles = list("Atmospheric Technician")
minimal_player_age = 7
-
+ exp_requirements = 120
+ exp_type = "all"
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 b8e7f44ecb4..209127d9abe 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 a064f0247f5..4c0d54e07d6 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 = 240
+ exp_type = "med"
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 eb23d9a2427..46c99f48d5e 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 = 240
+ exp_type = "sci"
// All science-y guys get bonuses for maxing out their tech.
required_objectives=list(
@@ -55,6 +57,8 @@
minimal_access = list(access_tox, access_tox_storage, access_research, access_xenobiology, access_xenoarch, access_maint_tunnels, access_mineral_storeroom)
alt_titles = list("Xenoarcheologist", "Anomalist", "Plasma Researcher", "Xenobiologist", "Chemical Researcher")
minimal_player_age = 3
+ exp_requirements = 240
+ exp_type = "all"
// All science-y guys get bonuses for maxing out their tech.
required_objectives=list(
@@ -91,6 +95,8 @@
minimal_access = list(access_robotics, access_tech_storage, access_morgue, access_research, access_maint_tunnels, access_mineral_storeroom) //As a job that handles so many corpses, it makes sense for them to have morgue access.
alt_titles = list("Biomechanical Engineer","Mechatronic Engineer")
minimal_player_age = 3
+ exp_requirements = 120
+ exp_type = "all"
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 fdf754df57f..e1b889ed75a 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 = 240
+ exp_type = "sec"
equip(var/mob/living/carbon/human/H)
if(!H) return 0
@@ -60,6 +62,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 = 120
+ exp_type = "sec"
equip(var/mob/living/carbon/human/H)
if(!H) return 0
@@ -106,6 +110,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 = 120
+ exp_type = "all"
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)
@@ -162,6 +168,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 = 120
+ exp_type = "all"
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..a44040b5703 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 = 240
+ exp_type = "sil"
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 = 120
+ exp_type = "all"
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 7768cf11870..980846edc53 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 = 240
+ exp_type = "com"
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)
@@ -59,6 +61,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 = 240
+ exp_type = "sup"
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 2409bda62ba..3a556f6d2e5 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(!has_exp_for_job(player, job.title))
+ 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..66685c85d3f
--- /dev/null
+++ b/code/game/jobs/job_exp.dm
@@ -0,0 +1,154 @@
+
+/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(!config.use_exp_restrictions)
+ return 1
+ if(!M.client)
+ return 0
+ //if(check_rights(R_ADMIN, 0, M))
+ // return 1
+ var/list/play_records = params2list(M.client.prefs.exp)
+ var/my_exp = text2num(play_records[job.exp_type])
+ if(my_exp >= text2num(job.exp_requirements))
+ //return_text += "
[C.mob] has [my_exp] of the [job.exp_requirements] [job.exp_type] EXP required for [job.title]."
+ return 1
+ else
+ return 0
+
+/client/verb/check_exp()
+ set name = "EXP Check"
+ set desc = "Shows your EXP status"
+ set category = "EXP"
+ if(!establish_db_connection())
+ return 0
+ var/exp_text = get_exp_text(src)
+ to_chat(src,exp_text)
+
+/client/proc/get_exp_text(var/client/C)
+ var/return_text = ""
+ var/list/play_records = params2list(C.prefs.exp)
+ var/exp_all = text2num(play_records["all"])
+ var/exp_com = play_records["com"]
+ var/exp_sec = play_records["sec"]
+ var/exp_sci = play_records["sci"]
+ var/exp_eng = play_records["eng"]
+ var/exp_med = play_records["med"]
+ var/exp_sup = play_records["sup"]
+ var/exp_sil = play_records["sil"]
+ var/exp_ant = play_records["ant"]
+ var/exp_gho = play_records["gho"]
+ if(play_records.len)
+ return_text += "General EXP: [exp_all]"
+ if(exp_com > 0)
+ return_text += "
Command EXP: [exp_com]"
+ if(exp_sec > 0)
+ return_text += "
Security EXP: [exp_sec]"
+ if(exp_sci > 0)
+ return_text += "
Science EXP: [exp_sci]"
+ if(exp_eng > 0)
+ return_text += "
Engineering EXP: [exp_eng]"
+ if(exp_med > 0)
+ return_text += "
Medical EXP: [exp_med]"
+ if(exp_sup > 0)
+ return_text += "
Support EXP: [exp_sup]"
+ if(exp_sil > 0)
+ return_text += "
Silicon EXP: [exp_sil]"
+ if(exp_ant > 0)
+ return_text += "
Antag EXP: [exp_ant]"
+ if(exp_gho > 0)
+ return_text += "
Ghost EXP: [exp_gho]"
+ return_text += "
Jobs"
+ for(var/datum/job/job in job_master.occupations)
+ if(job.exp_requirements && job.exp_type)
+ if(has_exp_for_job(C.mob, job.title))
+ return_text += " | [job.title] "
+ else
+ return_text += " | [job.title] "
+ return(return_text)
+ else
+ return("[C.mob] has no EXP records.")
+
+/client/verb/add_exp()
+ set name = "EXP ADD"
+ set desc = "DEBUG"
+ set category = "EXP"
+ update_exp(25)
+
+/client/proc/cmd_show_exp_panel(var/mob/living/M in mob_list)
+ set category = "EXP"
+ set name = "Show EXP Panel"
+ set desc=""
+ if(!check_rights(R_ADMIN))
+ return
+ if(M.client)
+ var/body = "