From c0152869f542e8cebf77a325144b76b73fbabfa7 Mon Sep 17 00:00:00 2001
From: Zonespace <41448081+Zonespace27@users.noreply.github.com>
Date: Sat, 17 Aug 2024 00:31:20 -0700
Subject: [PATCH] Datumizes Specialist Sets (#6933)
# About the pull request
Converts specialist sets into datums instead of list/string hell,
improves a fair bit of surrounding code
Fixes a few things:
- If a heavy sniper went to cryo, it would reopen the sniper spot
instead
- Demo/Scout had a chance to not have the skill to use their c4 if they
got a specialist set from their vendor instead of a spec kit
# Explain why it's good for the game
Our old system sucked, this sucks significantly less
# Testing Photographs and Procedure
Screenshots & Videos
Tested:
- Vending spec sets
- Cryoing as spec
- Using spec kits
- Cryoing as spec kit spec
# Changelog
:cl:
fix: Fixed heavy sniper spec opening up the wrong specialist slot on
admin cryo
/:cl:
---------
Co-authored-by: kiVts <48099872+kiVts@users.noreply.github.com>
---
code/__DEFINES/traits.dm | 14 +-
code/_globalvars/global_lists.dm | 22 +--
code/game/jobs/job/job.dm | 4 +
code/game/jobs/job/marine/squad/specialist.dm | 5 +
code/game/machinery/cryopod.dm | 20 +--
code/game/machinery/vending/cm_vending.dm | 42 ++---
.../squad_prep/squad_specialist.dm | 2 +-
.../admin/player_panel/actions/physical.dm | 19 +--
.../modules/cm_marines/equipment/kit_boxes.dm | 57 ++-----
code/modules/cm_marines/specialist.dm | 148 ++++++++++++++++++
10 files changed, 197 insertions(+), 136 deletions(-)
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index e6b9c4c4b9ee..4d5fac9ba17b 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -232,9 +232,15 @@
/// If the mob is able to use the vulture rifle or spotting scope
#define TRAIT_VULTURE_USER "t_vulture_user"
/// If the mob is currently loading a tutorial
-#define TRAIT_IN_TUTORIAL "t_IN_TUTORIAL"
+#define TRAIT_IN_TUTORIAL "t_in_tutorial"
/// If the mob is cloaked in any form
#define TRAIT_CLOAKED "t_cloaked"
+/// If the mob claimed a specialist set from a vendor
+#define TRAIT_SPEC_VENDOR "t_spec_vendor"
+/// If the mob claimed a specialist set from a kit
+#define TRAIT_SPEC_KIT "t_spec_kit"
+/// What spec set the mob has claimed, if any
+#define TRAIT_SPEC(spec_type) "t_spec_[spec_type]"
/// If the mob won't drop items held in face slot when downed
#define TRAIT_IRON_TEETH "t_iron_teeth"
@@ -315,6 +321,8 @@ GLOBAL_LIST_INIT(mob_traits, list(
TRAIT_ABILITY_BURROWED,
TRAIT_VULTURE_USER,
TRAIT_IN_TUTORIAL,
+ TRAIT_SPEC_KIT,
+ TRAIT_SPEC_VENDOR,
))
/*
@@ -354,9 +362,9 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_CANNOT_EAT" = TRAIT_CANNOT_EAT,
"TRAIT_VULTURE_USER" = TRAIT_VULTURE_USER,
"TRAIT_CLOAKED" = TRAIT_CLOAKED,
+ "TRAIT_SPEC_KIT" = TRAIT_SPEC_KIT,
+ "TRAIT_SPEC_VENDOR" = TRAIT_SPEC_VENDOR,
),
-// /mob/living/carbon/human = list(
-// ),
/mob/living/carbon/xenomorph = list(
"TRAIT_ABILITY_NO_PLASMA_TRANSFER" = TRAIT_ABILITY_NO_PLASMA_TRANSFER,
"TRAIT_ABILITY_OVIPOSITOR" = TRAIT_ABILITY_OVIPOSITOR,
diff --git a/code/_globalvars/global_lists.dm b/code/_globalvars/global_lists.dm
index d2165fecc9b1..dadf5d597255 100644
--- a/code/_globalvars/global_lists.dm
+++ b/code/_globalvars/global_lists.dm
@@ -544,26 +544,8 @@ GLOBAL_REFERENCE_LIST_INDEXED(all_skills, /datum/skill, skill_name)
// Timelock
GLOBAL_LIST_EMPTY(timelocks)
-
-//the global list of specialist kits that haven't been claimed yet.
-GLOBAL_LIST_INIT(available_specialist_sets, list(
- "Scout Set",
- "Sniper Set",
- "Anti-materiel Sniper Set",
- "Demolitionist Set",
- "Heavy Grenadier Set",
- "Pyro Set"
- ))
-
-//Similar thing, but used in /obj/item/spec_kit
-GLOBAL_LIST_INIT(available_specialist_kit_boxes, list(
- "Pyro" = 2,
- "Grenadier" = 2,
- "Sniper" = 2,
- "Scout" = 2,
- "Demo" = 2,
- "Anti-materiel Sniper" = 2,
- ))
+GLOBAL_LIST_EMPTY_TYPED(specialist_set_name_dict, /datum/specialist_set)
+GLOBAL_LIST_INIT_TYPED(specialist_set_datums, /datum/specialist_set, setup_specialist_sets())
/proc/init_global_referenced_datums()
init_keybindings()
diff --git a/code/game/jobs/job/job.dm b/code/game/jobs/job/job.dm
index 640866db8ca2..0323d2a02733 100644
--- a/code/game/jobs/job/job.dm
+++ b/code/game/jobs/job/job.dm
@@ -318,3 +318,7 @@
if(user.client.check_whitelist_status(flags_whitelist))
return TRUE
+
+/// Called when the job owner enters deep cryogenic storage
+/datum/job/proc/on_cryo(mob/living/carbon/human/cryoing)
+ return
diff --git a/code/game/jobs/job/marine/squad/specialist.dm b/code/game/jobs/job/marine/squad/specialist.dm
index e69241cdc70b..38f7a38cbedc 100644
--- a/code/game/jobs/job/marine/squad/specialist.dm
+++ b/code/game/jobs/job/marine/squad/specialist.dm
@@ -23,6 +23,11 @@
total_positions_so_far = positions
return positions
+/datum/job/marine/specialist/on_cryo(mob/living/carbon/human/cryoing)
+ var/specialist_set = get_specialist_set(cryoing)
+ if(isnull(specialist_set))
+ return
+ GLOB.specialist_set_datums[specialist_set].refund_set(cryoing)
/datum/job/marine/specialist/whiskey
title = JOB_WO_SQUAD_SPECIALIST
diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm
index 658e2aa150f3..45a57eb96392 100644
--- a/code/game/machinery/cryopod.dm
+++ b/code/game/machinery/cryopod.dm
@@ -324,28 +324,10 @@ GLOBAL_LIST_INIT(frozen_items, list(SQUAD_MARINE_1 = list(), SQUAD_MARINE_2 = li
var/datum/job/job = GET_MAPPED_ROLE(occupant.job)
if(ishuman(occupant))
var/mob/living/carbon/human/H = occupant
+ job.on_cryo(H)
if(H.assigned_squad)
var/datum/squad/S = H.assigned_squad
S.forget_marine_in_squad(H)
- if(istype(job, /datum/job/marine/specialist))
- //we make the set this specialist took if any available again
- if(H.skills)
- var/set_name
- switch(H.skills.get_skill_level(SKILL_SPEC_WEAPONS))
- if(SKILL_SPEC_ROCKET)
- set_name = "Demolitionist Set"
- if(SKILL_SPEC_GRENADIER)
- set_name = "Heavy Grenadier Set"
- if(SKILL_SPEC_PYRO)
- set_name = "Pyro Set"
- if(SKILL_SPEC_SCOUT)
- set_name = "Scout Set"
- if(SKILL_SPEC_SNIPER)
- set_name = "Sniper Set"
- GLOB.available_specialist_sets += "Anti-materiel Sniper Set"
-
- if(set_name && !GLOB.available_specialist_sets.Find(set_name))
- GLOB.available_specialist_sets += set_name
//Cryoing someone out removes someone from the Marines, blocking further larva spawns until accounted for
SSticker.mode.latejoin_update(job, -1)
diff --git a/code/game/machinery/vending/cm_vending.dm b/code/game/machinery/vending/cm_vending.dm
index 2d15d4c37da8..5746a29aa1ae 100644
--- a/code/game/machinery/vending/cm_vending.dm
+++ b/code/game/machinery/vending/cm_vending.dm
@@ -565,48 +565,28 @@ GLOBAL_LIST_EMPTY(vending_products)
to_chat(user, SPAN_WARNING("Only specialists can take specialist sets."))
vend_fail()
return FALSE
+
else if(!user.skills || user.skills.get_skill_level(SKILL_SPEC_WEAPONS) != SKILL_SPEC_TRAINED)
to_chat(user, SPAN_WARNING("You already have a specialization."))
vend_fail()
return FALSE
+
var/p_name = itemspec[1]
- if(!GLOB.available_specialist_sets.Find(p_name))
+ if(!(p_name in GLOB.specialist_set_name_dict))
+ return
+
+ if(GLOB.specialist_set_name_dict[p_name].get_available_vendor_num() <= 0)
to_chat(user, SPAN_WARNING("That set is already taken."))
vend_fail()
return FALSE
+
var/obj/item/card/id/card = human_user.get_idcard()
- if(!card?.check_biometrics(user))
+ if(!istype(card) || !card.check_biometrics(user))
to_chat(user, SPAN_WARNING("You must be wearing your [SPAN_INFO("dog tags")] to select a specialization!"))
return FALSE
- var/specialist_assignment
- switch(p_name)
- if("Scout Set")
- user.skills.set_skill(SKILL_SPEC_WEAPONS, SKILL_SPEC_SCOUT)
- specialist_assignment = "Scout"
- if("Sniper Set")
- user.skills.set_skill(SKILL_SPEC_WEAPONS, SKILL_SPEC_SNIPER)
- specialist_assignment = "Sniper"
- GLOB.available_specialist_sets -= "Anti-materiel Sniper Set"
- if("Anti-materiel Sniper Set")
- user.skills.set_skill(SKILL_SPEC_WEAPONS, SKILL_SPEC_SNIPER)
- specialist_assignment = "Heavy Sniper"
- GLOB.available_specialist_sets -= "Sniper Set"
- if("Demolitionist Set")
- user.skills.set_skill(SKILL_SPEC_WEAPONS, SKILL_SPEC_ROCKET)
- specialist_assignment = "Demo"
- if("Heavy Grenadier Set")
- user.skills.set_skill(SKILL_SPEC_WEAPONS, SKILL_SPEC_GRENADIER)
- specialist_assignment = "Grenadier"
- if("Pyro Set")
- user.skills.set_skill(SKILL_SPEC_WEAPONS, SKILL_SPEC_PYRO)
- specialist_assignment = "Pyro"
- else
- to_chat(user, SPAN_WARNING("Something bad occurred with [src], tell a Dev."))
- vend_fail()
- return FALSE
- card.set_assignment((human_user.assigned_squad ? (human_user.assigned_squad.name + " ") : "") + JOB_SQUAD_SPECIALIST + " ([specialist_assignment])")
- GLOB.data_core.manifest_modify(user.real_name, WEAKREF(user), card.assignment)
- GLOB.available_specialist_sets -= p_name
+
+ GLOB.specialist_set_name_dict[p_name].redeem_set(human_user)
+
else if(vendor_role.Find(JOB_SYNTH))
if(user.job != JOB_SYNTH)
to_chat(user, SPAN_WARNING("Only USCM Synthetics may vend experimental tool tokens."))
diff --git a/code/game/machinery/vending/vendor_types/squad_prep/squad_specialist.dm b/code/game/machinery/vending/vendor_types/squad_prep/squad_specialist.dm
index a21207a6645c..527bb89b6af2 100644
--- a/code/game/machinery/vending/vendor_types/squad_prep/squad_specialist.dm
+++ b/code/game/machinery/vending/vendor_types/squad_prep/squad_specialist.dm
@@ -7,7 +7,7 @@ GLOBAL_LIST_INIT(cm_vending_gear_spec, list(
list("Pyro Set", 0, /obj/item/storage/box/spec/pyro, MARINE_CAN_BUY_ESSENTIALS, VENDOR_ITEM_REGULAR),
list("Scout Set", 0, /obj/item/storage/box/spec/scout, MARINE_CAN_BUY_ESSENTIALS, VENDOR_ITEM_REGULAR),
list("Sniper Set", 0, /obj/item/storage/box/spec/sniper, MARINE_CAN_BUY_ESSENTIALS, VENDOR_ITEM_RECOMMENDED),
- list("Anti-materiel Sniper Set", 0, /obj/item/storage/box/spec/sniper/anti_materiel, MARINE_CAN_BUY_ESSENTIALS, VENDOR_ITEM_RECOMMENDED),
+ list("Anti-Materiel Sniper Set", 0, /obj/item/storage/box/spec/sniper/anti_materiel, MARINE_CAN_BUY_ESSENTIALS, VENDOR_ITEM_RECOMMENDED),
list("EXTRA SCOUT AMMUNITION", 0, null, null, null),
list("A19 High Velocity Impact Magazine (10x24mm)", 40, /obj/item/ammo_magazine/rifle/m4ra/custom/impact, null, VENDOR_ITEM_REGULAR),
diff --git a/code/modules/admin/player_panel/actions/physical.dm b/code/modules/admin/player_panel/actions/physical.dm
index 21e6fed4f825..21f5504455d2 100644
--- a/code/modules/admin/player_panel/actions/physical.dm
+++ b/code/modules/admin/player_panel/actions/physical.dm
@@ -73,26 +73,9 @@
var/datum/job/job = GET_MAPPED_ROLE(target.job)
if(ishuman(target))
var/mob/living/carbon/human/H = target
+ job.on_cryo(H)
if(H.assigned_squad)
var/datum/squad/S = H.assigned_squad
- if(H.job == JOB_SQUAD_SPECIALIST)
- //we make the set this specialist took if any available again
- if(H.skills)
- var/set_name
- switch(H.skills.get_skill_level(SKILL_SPEC_WEAPONS))
- if(SKILL_SPEC_ROCKET)
- set_name = "Demolitionist Set"
- if(SKILL_SPEC_GRENADIER)
- set_name = "Heavy Grenadier Set"
- if(SKILL_SPEC_PYRO)
- set_name = "Pyro Set"
- if(SKILL_SPEC_SCOUT)
- set_name = "Scout Set"
- if(SKILL_SPEC_SNIPER)
- set_name = "Sniper Set"
-
- if(set_name && !GLOB.available_specialist_sets.Find(set_name))
- GLOB.available_specialist_sets += set_name
S.forget_marine_in_squad(H)
message_admins("[key_name_admin(user)] sent [key_name_admin(target)] ([H.job]) to cryogenics.")
diff --git a/code/modules/cm_marines/equipment/kit_boxes.dm b/code/modules/cm_marines/equipment/kit_boxes.dm
index 83b20636fc5e..d61fc6e8a4c0 100644
--- a/code/modules/cm_marines/equipment/kit_boxes.dm
+++ b/code/modules/cm_marines/equipment/kit_boxes.dm
@@ -249,57 +249,26 @@
return TRUE
/obj/item/spec_kit/proc/select_and_spawn(mob/living/carbon/human/user)
- var/selection = tgui_input_list(user, "Pick your specialist equipment type.", "Specialist Kit Selection", GLOB.available_specialist_kit_boxes, 10 SECONDS)
+ var/list/available_specialist_kits = list()
+ for(var/path in GLOB.specialist_set_datums)
+ var/datum/specialist_set/specset = GLOB.specialist_set_datums[path]
+ if(specset.get_available_kit_num() >= 1)
+ available_specialist_kits += specset.get_name()
+
+ var/selection = tgui_input_list(user, "Pick your specialist equipment type.", "Specialist Kit Selection", available_specialist_kits, 10 SECONDS)
if(!selection || QDELETED(src))
return FALSE
- if(!GLOB.available_specialist_kit_boxes[selection] || GLOB.available_specialist_kit_boxes[selection] <= 0)
+ if(!skillcheckexplicit(user, SKILL_SPEC_WEAPONS, SKILL_SPEC_TRAINED) && !skillcheckexplicit(user, SKILL_SPEC_WEAPONS, SKILL_SPEC_ALL))
+ to_chat(user, SPAN_WARNING("You already unwrapped your [name], give this one to someone else!"))
+ return FALSE
+ if(!GLOB.specialist_set_name_dict[selection] || (GLOB.specialist_set_name_dict[selection].get_available_kit_num() <= 0))
to_chat(user, SPAN_WARNING("No more kits of this type may be chosen!"))
return FALSE
var/obj/item/card/id/card = user.get_idcard()
if(!card || card.registered_ref != WEAKREF(user))
to_chat(user, SPAN_WARNING("You must be wearing your [SPAN_INFO("ID card")] or [SPAN_INFO("dog tags")] to select a specialization!"))
- return
- var/turf/T = get_turf(loc)
- var/obj/item/storage/box/spec/spec_box
- var/specialist_assignment
- switch(selection)
- if("Pyro")
- spec_box = new /obj/item/storage/box/spec/pyro(T)
- specialist_assignment = "Pyro"
- user.skills.set_skill(SKILL_SPEC_WEAPONS, SKILL_SPEC_PYRO)
- if("Grenadier")
- spec_box = new /obj/item/storage/box/spec/heavy_grenadier(T)
- specialist_assignment = "Grenadier"
- user.skills.set_skill(SKILL_SPEC_WEAPONS, SKILL_SPEC_GRENADIER)
- if("Sniper")
- spec_box = new /obj/item/storage/box/spec/sniper(T)
- specialist_assignment = "Sniper"
- user.skills.set_skill(SKILL_SPEC_WEAPONS, SKILL_SPEC_SNIPER)
- if("Anti-materiel Sniper")
- spec_box = new /obj/item/storage/box/spec/sniper/anti_materiel(T)
- specialist_assignment = "Heavy Sniper"
- user.skills.set_skill(SKILL_SPEC_WEAPONS, SKILL_SPEC_SNIPER)
- if("Scout")
- spec_box = new /obj/item/storage/box/spec/scout(T)
- specialist_assignment = "Scout"
- user.skills.set_skill(SKILL_SPEC_WEAPONS, SKILL_SPEC_SCOUT)
- //this is to be able to use C4s that are coming with the kit
- if(!skillcheck(user, SKILL_ENGINEER, SKILL_ENGINEER_NOVICE))
- user.skills.set_skill(SKILL_ENGINEER, SKILL_ENGINEER_NOVICE)
- if("Demo")
- spec_box = new /obj/item/storage/box/spec/demolitionist(T)
- specialist_assignment = "Demo"
- user.skills.set_skill(SKILL_SPEC_WEAPONS, SKILL_SPEC_ROCKET)
- //this is to be able to use C4s that are coming with the kit
- if(!skillcheck(user, SKILL_ENGINEER, SKILL_ENGINEER_NOVICE))
- user.skills.set_skill(SKILL_ENGINEER, SKILL_ENGINEER_NOVICE)
- if(specialist_assignment)
- user.put_in_hands(spec_box)
- card.set_assignment((user.assigned_squad && squad_assignment_update ? (user.assigned_squad.name + " ") : "") + card.assignment + " ([specialist_assignment])")
- GLOB.data_core.manifest_modify(user.real_name, WEAKREF(user), card.assignment)
- GLOB.available_specialist_kit_boxes[selection]--
- return TRUE
- return FALSE
+ return FALSE
+ return GLOB.specialist_set_name_dict[selection].redeem_set(user, TRUE)
//******************************************PFC Kits****************************************************************/
diff --git a/code/modules/cm_marines/specialist.dm b/code/modules/cm_marines/specialist.dm
index 2fc1fdec10db..9fbb240a1178 100644
--- a/code/modules/cm_marines/specialist.dm
+++ b/code/modules/cm_marines/specialist.dm
@@ -38,3 +38,151 @@
for (var/datum/action/item_action/specialist/SA in H.actions)
if (SA.ability_primacy == SPEC_PRIMARY_ACTION_2)
SA.handle_spec_macro()
+
+/// Get a specialist set datum typepath given a mob, returns null if they aren't a spec or haven't chosen a kit.
+/proc/get_specialist_set(mob/living/spec)
+ for(var/datum/specialist_set/path as anything in GLOB.specialist_set_datums)
+ if(HAS_TRAIT(spec, TRAIT_SPEC(path::trait_to_give)))
+ return path
+
+/proc/setup_specialist_sets()
+ var/list/set_list = list()
+ for(var/datum/specialist_set/path as anything in subtypesof(/datum/specialist_set))
+ var/datum/specialist_set/object = new path
+ set_list[path] = object
+ GLOB.specialist_set_name_dict[object.get_name()] = object
+ return set_list
+
+/datum/specialist_set
+ /// Human-readable name for the specialist set
+ VAR_PROTECTED/name = "" as text
+ /// What is the role title that should go on ID cards
+ VAR_PROTECTED/role_name = "" as text
+ /// How many more of this spec set can be picked from spec vendors
+ VAR_PRIVATE/available_vendor_num = 1 as num
+ /// How many more of this spec set can be picked from /obj/item/spec_kit
+ VAR_PRIVATE/available_kit_num = 2 as num
+ /// What skill tier to give the person redeeming the set
+ VAR_PROTECTED/skill_to_give = SKILL_SPEC_DEFAULT as num
+ /// What trait to give the person redeeming the set
+ VAR_PROTECTED/trait_to_give
+ /// What typepath to spawn for the redeemer if from a kit
+ VAR_PROTECTED/kit_typepath
+ /// List of typepaths that are incompatible with this set, meaning it'll subtract 1 from their vendor/kit num as well
+ VAR_PROTECTED/list/incompatible_sets = list()
+
+/datum/specialist_set/New()
+ . = ..()
+ incompatible_sets += type
+
+/datum/specialist_set/proc/redeem_set(mob/living/carbon/human/redeemer, kit = FALSE)
+ SHOULD_CALL_PARENT(TRUE)
+ if(!redeemer)
+ return FALSE
+
+ if(kit && (available_kit_num <= 0))
+ to_chat(redeemer, SPAN_WARNING("No more kits of this type may be chosen."))
+ return FALSE
+ else if(!kit && (available_vendor_num <= 0))
+ to_chat(redeemer, SPAN_WARNING("That set is already taken."))
+ return FALSE
+
+ if(skill_to_give != SKILL_SPEC_DEFAULT)
+ redeemer.skills?.set_skill(SKILL_SPEC_WEAPONS, skill_to_give)
+
+ if(kit)
+ redeemer.put_in_any_hand_if_possible(new kit_typepath, FALSE)
+ for(var/path in incompatible_sets)
+ GLOB.specialist_set_datums[path].available_kit_num--
+ ADD_TRAIT(redeemer, TRAIT_SPEC_KIT, TRAIT_SOURCE_INHERENT)
+ else
+ for(var/path in incompatible_sets)
+ GLOB.specialist_set_datums[path].available_vendor_num--
+ ADD_TRAIT(redeemer, TRAIT_SPEC_VENDOR, TRAIT_SOURCE_INHERENT)
+ ADD_TRAIT(redeemer, TRAIT_SPEC(trait_to_give), TRAIT_SOURCE_INHERENT)
+ var/obj/item/card/id/idcard = redeemer.get_idcard()
+ if(idcard)
+ idcard.set_assignment((redeemer.assigned_squad ? (redeemer.assigned_squad.name + " ") : "") + JOB_SQUAD_SPECIALIST + " ([role_name])")
+ GLOB.data_core.manifest_modify(redeemer.real_name, WEAKREF(redeemer), idcard.assignment)
+ return TRUE
+
+/datum/specialist_set/proc/refund_set(mob/living/carbon/human/refunder)
+ SHOULD_CALL_PARENT(TRUE)
+ if(!refunder)
+ return
+
+ if(HAS_TRAIT(refunder, TRAIT_SPEC_KIT))
+ for(var/path in incompatible_sets)
+ GLOB.specialist_set_datums[path].available_kit_num++
+ else if(HAS_TRAIT(refunder, TRAIT_SPEC_VENDOR))
+ for(var/path in incompatible_sets)
+ GLOB.specialist_set_datums[path].available_vendor_num++
+
+/datum/specialist_set/proc/get_name() as text
+ return name
+
+/datum/specialist_set/proc/get_available_kit_num() as num
+ return available_kit_num
+
+/datum/specialist_set/proc/get_available_vendor_num() as num
+ return available_vendor_num
+
+/datum/specialist_set/sadar
+ name = "Demolitionist Set"
+ role_name = "Demo"
+ skill_to_give = SKILL_SPEC_ROCKET
+ kit_typepath = /obj/item/storage/box/spec/demolitionist
+
+/datum/specialist_set/sadar/redeem_set(mob/living/redeemer, kit)
+ . = ..()
+ if(!.)
+ return .
+
+ if(!skillcheck(redeemer, SKILL_ENGINEER, SKILL_ENGINEER_TRAINED))
+ redeemer.skills?.set_skill(SKILL_ENGINEER, SKILL_ENGINEER_TRAINED)
+ return TRUE
+
+/datum/specialist_set/scout
+ name = "Scout Set"
+ role_name = "Scout"
+ skill_to_give = SKILL_SPEC_SCOUT
+ kit_typepath = /obj/item/storage/box/spec/scout
+
+/datum/specialist_set/scout/redeem_set(mob/living/redeemer, kit)
+ . = ..()
+ if(!.)
+ return .
+
+ if(!skillcheck(redeemer, SKILL_ENGINEER, SKILL_ENGINEER_TRAINED))
+ redeemer.skills?.set_skill(SKILL_ENGINEER, SKILL_ENGINEER_TRAINED)
+ return TRUE
+
+/datum/specialist_set/sniper
+ name = "Sniper Set"
+ role_name = "Sniper"
+ skill_to_give = SKILL_SPEC_SNIPER
+ kit_typepath = /obj/item/storage/box/spec/sniper
+ incompatible_sets = list(
+ /datum/specialist_set/anti_mat_sniper,
+ )
+
+/datum/specialist_set/anti_mat_sniper
+ name = "Anti-Materiel Sniper Set"
+ role_name = "Heavy Sniper"
+ skill_to_give = SKILL_SPEC_SNIPER
+ kit_typepath = /obj/item/storage/box/spec/sniper/anti_materiel
+ incompatible_sets = list(
+ /datum/specialist_set/sniper,
+ )
+
+/datum/specialist_set/grenadier
+ name = "Heavy Grenadier Set"
+ role_name = "Grenadier"
+ skill_to_give = SKILL_SPEC_GRENADIER
+ kit_typepath = /obj/item/storage/box/spec/heavy_grenadier
+
+/datum/specialist_set/pyro
+ name = "Pyro Set"
+ role_name = "Pyro"
+ skill_to_give = SKILL_SPEC_PYRO
+ kit_typepath = /obj/item/storage/box/spec/pyro