diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 7b669124a2d0..e92610d5bfa0 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -230,9 +230,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]" // -- ability traits -- /// Xenos with this trait cannot have plasma transfered to them @@ -311,6 +317,8 @@ GLOBAL_LIST_INIT(mob_traits, list( TRAIT_ABILITY_BURROWED, TRAIT_VULTURE_USER, TRAIT_IN_TUTORIAL, + TRAIT_SPEC_KIT, + TRAIT_SPEC_VENDOR, )) /* @@ -350,9 +358,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 c0fd361c6203..1bd928d34741 100644 --- a/code/_globalvars/global_lists.dm +++ b/code/_globalvars/global_lists.dm @@ -485,26 +485,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 1a04c3cafeb5..daaff5bd6592 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 1793b87c72ae..7c70fc94553a 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 6c55ce8c7946..ade250263e93 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/ID = human_user.wear_id if(!istype(ID) || !ID.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 - ID.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), ID.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 4d14b7b89ccd..0bd4500580eb 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 e0220d017d42..cf47fc4e2edd 100644 --- a/code/modules/cm_marines/equipment/kit_boxes.dm +++ b/code/modules/cm_marines/equipment/kit_boxes.dm @@ -248,59 +248,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) + 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) if(!selection || QDELETED(src)) return FALSE 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 - if(!GLOB.available_specialist_kit_boxes[selection] || GLOB.available_specialist_kit_boxes[selection] <= 0) + 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/ID = user.wear_id if(!istype(ID) || ID.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_TRAINED)) - user.skills.set_skill(SKILL_ENGINEER, SKILL_ENGINEER_TRAINED) - 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_TRAINED)) - user.skills.set_skill(SKILL_ENGINEER, SKILL_ENGINEER_TRAINED) - if(specialist_assignment) - user.put_in_hands(spec_box) - ID.set_assignment((user.assigned_squad && squad_assignment_update ? (user.assigned_squad.name + " ") : "") + ID.assignment + " ([specialist_assignment])") - GLOB.data_core.manifest_modify(user.real_name, WEAKREF(user), ID.assignment) - 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..2c8c46db2db6 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.wear_id + 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