diff --git a/code/__DEFINES/jobs.dm b/code/__DEFINES/jobs.dm
index d6e456a9627..5f1ce8856e0 100644
--- a/code/__DEFINES/jobs.dm
+++ b/code/__DEFINES/jobs.dm
@@ -219,6 +219,7 @@
#define JOB_UNAVAILABLE_SLOTFULL 5
#define JOB_UNAVAILABLE_SPECIESLOCK 6
#define JOB_UNAVAILABLE_WHITELIST 7
+#define JOB_UNAVAILABLE_SPECIAL 8
#define DEFAULT_RELIGION "Christianity"
#define DEFAULT_DEITY "Space Jesus"
diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm
index 8636f9789c4..61a1a8d4b68 100644
--- a/code/controllers/subsystem/job.dm
+++ b/code/controllers/subsystem/job.dm
@@ -102,6 +102,8 @@ SUBSYSTEM_DEF(job)
var/datum/job/job = GetJob(rank)
if(!job)
return FALSE
+ if(job.special_stat_check(player.client?.prefs))
+ return FALSE
if(jobban_isbanned(player, rank) || QDELETED(player))
return FALSE
if(!job.player_old_enough(player.client))
@@ -124,6 +126,9 @@ SUBSYSTEM_DEF(job)
JobDebug("Running FOC, Job: [job], Level: [level], Flag: [flag]")
var/list/candidates = list()
for(var/mob/dead/new_player/player in unassigned)
+ if(job.special_stat_check(player.client?.prefs))
+ JobDebug("FOC special stat failed, player: [player]")
+ continue
if(jobban_isbanned(player, job.title) || QDELETED(player))
JobDebug("FOC isbanned failed, Player: [player]")
continue
@@ -163,6 +168,10 @@ SUBSYSTEM_DEF(job)
if((job.title in GLOB.faction_whitelist_positions) && (CONFIG_GET(flag/use_role_whitelist))) //If you want a whitelist position, get a whitelist and choose it.
continue
+ if(job.special_stat_check(player.client?.prefs))
+ JobDebug("GRJ special stat failed, player: [player]")
+ continue
+
if(jobban_isbanned(player, job.title) || QDELETED(player))
if(QDELETED(player))
JobDebug("GRJ isbanned failed, Player deleted")
@@ -344,6 +353,10 @@ SUBSYSTEM_DEF(job)
if(!job)
continue
+ if(job.special_stat_check(player.client?.prefs))
+ JobDebug("DO special stat failed, player: [player]")
+ continue
+
if(jobban_isbanned(player, job.title))
JobDebug("DO isbanned failed, Player: [player], Job:[job.title]")
continue
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 8ad2c0fff55..42fd5b75661 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -908,6 +908,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(jobban_isbanned(user, rank))
HTML += "[rank]
BANNED | "
continue
+ if(job.special_stat_check(src))
+ HTML += "[rank]\[SPECIAL [job.special_stat_check(src)]\] | "
+ continue
var/required_playtime_remaining = job.required_playtime_remaining(user.client)
if(required_playtime_remaining)
HTML += "[rank] \[ [get_exp_format(required_playtime_remaining)] as [job.get_exp_req_type()] \] | "
diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm
index d582c1dc816..90893f0dccb 100644
--- a/code/modules/jobs/job_types/_job.dm
+++ b/code/modules/jobs/job_types/_job.dm
@@ -100,6 +100,33 @@
/// the value should be something like "strings/names/cow.txt"
var/whitelist_path
+ var/list/min_required_special
+
+ /* //!Template for pasting into job defines, leave only needed fields:
+ min_required_special = list(
+ "special_s" = 0,
+ "special_p" = 0,
+ "special_e" = 0,
+ "special_c" = 0,
+ "special_i" = 0,
+ "special_a" = 0,
+ "special_l" = 0,
+ )
+ */
+
+ var/list/modify_special
+
+ /* //!Template for pasting into job defines, leave only needed fields:
+ modify_special = list(
+ "special_s" = 0,
+ "special_p" = 0,
+ "special_e" = 0,
+ "special_c" = 0,
+ "special_i" = 0,
+ "special_a" = 0,
+ "special_l" = 0,
+ )
+ */
/datum/job/proc/after_spawn(mob/living/spawner, mob/client_holder, latejoin = FALSE)
SHOULD_CALL_PARENT(TRUE)
@@ -170,6 +197,15 @@
//Equip the rest of the gear
H.dna.species.before_equip_job(src, H, visualsOnly)
+ if(islist(modify_special))
+ H.special_s = clamp(H.special_s + modify_special["special_s"], SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE)
+ H.special_p = clamp(H.special_p + modify_special["special_p"], SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE)
+ H.special_e = clamp(H.special_e + modify_special["special_e"], SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE)
+ H.special_c = clamp(H.special_c + modify_special["special_c"], SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE)
+ H.special_i = clamp(H.special_i + modify_special["special_i"], SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE)
+ H.special_a = clamp(H.special_a + modify_special["special_a"], SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE)
+ H.special_l = clamp(H.special_l + modify_special["special_l"], SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE)
+
var/datum/outfit/job/O = outfit_override || outfit
if(O)
H.equipOutfit(O, visualsOnly, preference_source) //mob doesn't have a client yet.
@@ -383,3 +419,27 @@
if(CONFIG_GET(flag/security_has_maint_access))
return list(ACCESS_MAINT_TUNNELS)
return list()
+
+/datum/job/proc/special_stat_check(datum/preferences/prefs)
+ var/output = ""
+ var/sum = prefs.special_s + prefs.special_p + prefs.special_e + prefs.special_c + prefs.special_i + prefs.special_a + prefs.special_l
+ if(sum > SPECIAL_MAX_POINT_SUM_CAP)
+ return "\[SPECIAL CAP\]"
+ if(islist(min_required_special))
+ if(min_required_special["special_s"] && min_required_special["special_s"] > prefs.special_s)
+ output += "S: [min_required_special["special_s"]], "
+ if(min_required_special["special_p"] && min_required_special["special_p"] > prefs.special_p)
+ output += "P: [min_required_special["special_p"]], "
+ if(min_required_special["special_e"] && min_required_special["special_e"] > prefs.special_e)
+ output += "E: [min_required_special["special_e"]], "
+ if(min_required_special["special_c"] && min_required_special["special_c"] > prefs.special_c)
+ output += "C: [min_required_special["special_c"]], "
+ if(min_required_special["special_i"] && min_required_special["special_i"] > prefs.special_i)
+ output += "I: [min_required_special["special_i"]], "
+ if(min_required_special["special_a"] && min_required_special["special_a"] > prefs.special_a)
+ output += "A: [min_required_special["special_a"]], "
+ if(min_required_special["special_l"] && min_required_special["special_l"] > prefs.special_l)
+ output += "L: [min_required_special["special_l"]], "
+ if(length(output))
+ output = copytext_char(output, 1, length(output) - 1)
+ return output
diff --git a/code/modules/jobs/job_types/bos.dm b/code/modules/jobs/job_types/bos.dm
index 054511ee464..d96e93c9aef 100644
--- a/code/modules/jobs/job_types/bos.dm
+++ b/code/modules/jobs/job_types/bos.dm
@@ -173,6 +173,10 @@ Paladin Commander
/datum/outfit/loadout/sentmini //Minigun
)
+ min_required_special = list(
+ "special_c" = 4,
+ )
+
access = list(ACCESS_BROTHERHOOD_COMMAND, ACCESS_ROBOTICS, ACCESS_BOS, ACCESS_ENGINE_EQUIP, ACCESS_ENGINE, ACCESS_HYDROPONICS, ACCESS_KITCHEN, ACCESS_BAR, ACCESS_SEC_DOORS, ACCESS_CHANGE_IDS)
minimal_access = list(ACCESS_BROTHERHOOD_COMMAND, ACCESS_ROBOTICS, ACCESS_BOS, ACCESS_ENGINE_EQUIP, ACCESS_ENGINE, ACCESS_HYDROPONICS, ACCESS_KITCHEN, ACCESS_BAR, ACCESS_SEC_DOORS, ACCESS_CHANGE_IDS)
@@ -328,6 +332,10 @@ Knight-Captain
access = list(ACCESS_BROTHERHOOD_COMMAND, ACCESS_ROBOTICS, ACCESS_BOS, ACCESS_ENGINE_EQUIP, ACCESS_ENGINE, ACCESS_HYDROPONICS, ACCESS_KITCHEN, ACCESS_BAR, ACCESS_SEC_DOORS, ACCESS_ARMORY, ACCESS_BRIG, ACCESS_CHANGE_IDS)
minimal_access = list(ACCESS_BROTHERHOOD_COMMAND, ACCESS_ROBOTICS, ACCESS_BOS, ACCESS_ENGINE_EQUIP, ACCESS_ENGINE, ACCESS_HYDROPONICS, ACCESS_KITCHEN, ACCESS_BAR, ACCESS_SEC_DOORS, ACCESS_ARMORY, ACCESS_BRIG, ACCESS_CHANGE_IDS)
+ min_required_special = list(
+ "special_c" = 4,
+ )
+
/datum/outfit/job/bos/f13knightcap/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
..()
if(visualsOnly)
@@ -462,6 +470,10 @@ Paladin
),
)
+ min_required_special = list(
+ "special_c" = 4,
+ )
+
/datum/outfit/job/bos/f13paladin/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
..()
if(visualsOnly)
diff --git a/code/modules/jobs/job_types/legion.dm b/code/modules/jobs/job_types/legion.dm
index e767ea12819..cf3cb19a03b 100644
--- a/code/modules/jobs/job_types/legion.dm
+++ b/code/modules/jobs/job_types/legion.dm
@@ -210,6 +210,10 @@ Weapons Lever shotgun, Grease gun, Repeater carbines, Revolvers, simple guns al
/datum/outfit/loadout/centurion, // 14mm Pistol, GOliath
)
+ min_required_special = list(
+ "special_c" = 4,
+ )
+
/datum/outfit/job/CaesarsLegion/Legionnaire/f13centurion/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
..()
if(visualsOnly)
@@ -300,6 +304,10 @@ Weapons Lever shotgun, Grease gun, Repeater carbines, Revolvers, simple guns al
/datum/outfit/loadout/decvetbrave, // trench shotgun, 44 revolver, ballistic fist
)
+ min_required_special = list(
+ "special_c" = 4,
+ )
+
/datum/outfit/job/CaesarsLegion/Legionnaire/f13decanvet/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
..()
@@ -405,6 +413,10 @@ Weapons Lever shotgun, Grease gun, Repeater carbines, Revolvers, simple guns al
),
)
+ min_required_special = list(
+ "special_c" = 4,
+ )
+
/datum/outfit/job/CaesarsLegion/Legionnaire/f13decan/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
..()
if(visualsOnly)
@@ -498,6 +510,10 @@ Weapons Lever shotgun, Grease gun, Repeater carbines, Revolvers, simple guns al
),
)
+ min_required_special = list(
+ "special_c" = 4,
+ )
+
/datum/outfit/job/CaesarsLegion/Legionnaire/f13decanrec/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
..()
if(visualsOnly)
diff --git a/code/modules/jobs/job_types/ncr.dm b/code/modules/jobs/job_types/ncr.dm
index 06726a0f621..a97205e771a 100644
--- a/code/modules/jobs/job_types/ncr.dm
+++ b/code/modules/jobs/job_types/ncr.dm
@@ -156,6 +156,10 @@ Weapons Service Rifle, Grease Gun, 9mm pistol, all good.
/datum/outfit/loadout/captainfrontline, // AR Service Rifle
)
+ min_required_special = list(
+ "special_c" = 4,
+ )
+
/datum/outfit/job/ncr/f13captain/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
..()
if(visualsOnly)
@@ -236,6 +240,10 @@ Weapons Service Rifle, Grease Gun, 9mm pistol, all good.
),
)
+ min_required_special = list(
+ "special_c" = 4,
+ )
+
/datum/outfit/job/ncr/f13lieutenant // AR Service Rifle, Binoculars, Bayonet, M1911 custom
name = "NCR Lieutenant"
jobtype = /datum/job/ncr/f13lieutenant
@@ -536,6 +544,10 @@ Weapons Service Rifle, Grease Gun, 9mm pistol, all good.
/datum/outfit/loadout/vrcqc // 2 x .45 Long colt revolvers
)
+ min_required_special = list(
+ "special_c" = 4,
+ )
+
/datum/outfit/job/ncr/f13vetranger/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
..()
if(visualsOnly)
diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm
index 2dd3b5be982..680a77677eb 100644
--- a/code/modules/mob/dead/new_player/new_player.dm
+++ b/code/modules/mob/dead/new_player/new_player.dm
@@ -405,6 +405,8 @@
return "Your species cannot play as a [jobtitle]."
if(JOB_UNAVAILABLE_WHITELIST)
return "[jobtitle] requires a whitelist."
+ if(JOB_UNAVAILABLE_SPECIAL)
+ return "[jobtitle] requires certain SPECIAL stats high enough."
return "Error: Unknown job availability."
/mob/dead/new_player/proc/IsJobUnavailable(rank, latejoin = FALSE)
@@ -422,6 +424,8 @@
return JOB_UNAVAILABLE_SLOTFULL
if(jobban_isbanned(src,rank))
return JOB_UNAVAILABLE_BANNED
+ if(job.special_stat_check(client?.prefs))
+ return JOB_UNAVAILABLE_SPECIAL
if(QDELETED(src))
return JOB_UNAVAILABLE_GENERIC
if(!job.player_old_enough(client))
@@ -711,4 +715,3 @@
// Add verb for re-opening the interview panel, and re-init the verbs for the stat panel
add_verb(src, /mob/dead/new_player/proc/open_interview)
-