From 0e6ef7d12f2603b64bf71f086fa20b01014b7cc9 Mon Sep 17 00:00:00 2001
From: SkyratBot <59378654+SkyratBot@users.noreply.github.com>
Date: Thu, 30 Mar 2023 00:10:18 +0200
Subject: [PATCH] [MIRROR] Reduces progression cost of a lot of traitor items.
Groups assassinate/behead/eyesnatching objectives together so that they're
treated the same under the traitor no-dupe rules. [MDB IGNORE] (#20112)
* Reduces progression cost of a lot of traitor items. Groups assassinate/behead/eyesnatching objectives together so that they're treated the same under the traitor no-dupe rules.
* ok
---------
Co-authored-by: Watermelon914 <37270891+Watermelon914@users.noreply.github.com>
Co-authored-by: Gandalf <9026500+Gandalf2k15@users.noreply.github.com>
---
code/datums/components/uplink.dm | 2 +
.../antagonists/traitor/objective_category.dm | 2 +-
.../objectives/abstract/target_player.dm | 18 +++
.../traitor/objectives/assassination.dm | 115 ++++++++---------
.../traitor/objectives/destroy_heirloom.dm | 2 +
.../traitor/objectives/eyesnatching.dm | 116 ++++++++----------
.../traitor/objectives/kidnapping.dm | 97 ++++++---------
.../antagonists/traitor/uplink_handler.dm | 12 +-
code/modules/asset_cache/assets/uplink.dm | 1 +
code/modules/uplink/uplink_items.dm | 9 +-
.../modules/uplink/uplink_items/ammunition.dm | 8 --
code/modules/uplink/uplink_items/badass.dm | 4 +-
code/modules/uplink/uplink_items/dangerous.dm | 9 --
.../uplink/uplink_items/device_tools.dm | 13 --
code/modules/uplink/uplink_items/explosive.dm | 19 +--
code/modules/uplink/uplink_items/implant.dm | 3 -
code/modules/uplink/uplink_items/job.dm | 74 +++++------
code/modules/uplink/uplink_items/stealthy.dm | 4 -
.../uplink/uplink_items/stealthy_tools.dm | 9 +-
code/modules/uplink/uplink_items/suits.dm | 13 +-
tgstation.dme | 1 +
.../packages/tgui/interfaces/Uplink/index.tsx | 67 +++++++---
22 files changed, 291 insertions(+), 307 deletions(-)
create mode 100644 code/modules/antagonists/traitor/objectives/abstract/target_player.dm
diff --git a/code/datums/components/uplink.dm b/code/datums/components/uplink.dm
index 51fe967d95a..64799014c62 100644
--- a/code/datums/components/uplink.dm
+++ b/code/datums/components/uplink.dm
@@ -235,6 +235,8 @@
data["extra_purchasable"] = extra_purchasable
data["extra_purchasable_stock"] = extra_purchasable_stock
data["current_stock"] = remaining_stock
+ data["shop_locked"] = uplink_handler.shop_locked
+ data["purchased_items"] = length(uplink_handler.purchase_log.purchase_log)
return data
/datum/component/uplink/ui_static_data(mob/user)
diff --git a/code/modules/antagonists/traitor/objective_category.dm b/code/modules/antagonists/traitor/objective_category.dm
index dcd41cd8518..5484d293798 100644
--- a/code/modules/antagonists/traitor/objective_category.dm
+++ b/code/modules/antagonists/traitor/objective_category.dm
@@ -48,7 +48,7 @@
var/list/result = filter_invalid_objective_list(value, progression_points)
if(!length(result))
continue
- filtered_objectives[value] = objectives[value]
+ filtered_objectives[result] = objectives[value]
else
if(!objective_valid(value, progression_points))
continue
diff --git a/code/modules/antagonists/traitor/objectives/abstract/target_player.dm b/code/modules/antagonists/traitor/objectives/abstract/target_player.dm
new file mode 100644
index 00000000000..76471eb244a
--- /dev/null
+++ b/code/modules/antagonists/traitor/objectives/abstract/target_player.dm
@@ -0,0 +1,18 @@
+/**
+ * The point of this datum is to act as a means to group target player objectives
+ * Not all 'target player' objectives have to be under this subtype, it's only used if you don't want duplicates among the current
+ * children types under this type.
+ */
+/datum/traitor_objective/target_player
+ abstract_type = /datum/traitor_objective/target_player
+
+ progression_minimum = 30 MINUTES
+
+ // The code below is for limiting how often you can get this objective. You will get this objective at a maximum of maximum_objectives_in_period every objective_period
+ /// The objective period at which we consider if it is an 'objective'. Set to 0 to accept all objectives.
+ var/objective_period = 15 MINUTES
+ /// The maximum number of objectives we can get within this period.
+ var/maximum_objectives_in_period = 4
+
+ /// The target that we need to target.
+ var/mob/living/target
diff --git a/code/modules/antagonists/traitor/objectives/assassination.dm b/code/modules/antagonists/traitor/objectives/assassination.dm
index d99ddc55710..8e3320d0e96 100644
--- a/code/modules/antagonists/traitor/objectives/assassination.dm
+++ b/code/modules/antagonists/traitor/objectives/assassination.dm
@@ -1,47 +1,50 @@
-/datum/traitor_objective_category/assassinate
- name = "Assassination"
+/datum/traitor_objective_category/assassinate_kidnap
+ name = "Assassination/Kidnap"
objectives = list(
- //starter assassinations, basically just require you to kill someone
list(
- /datum/traitor_objective/assassinate/calling_card = 1,
- /datum/traitor_objective/assassinate/behead = 1,
+ list(
+ /datum/traitor_objective/target_player/assassinate/calling_card = 1,
+ /datum/traitor_objective/target_player/assassinate/behead = 1,
+ ) = 1,
+ list(
+ /datum/traitor_objective/target_player/assassinate/calling_card/heads_of_staff = 1,
+ /datum/traitor_objective/target_player/assassinate/behead/heads_of_staff = 1,
+ ) = 1,
) = 1,
- //above but for heads
list(
- /datum/traitor_objective/assassinate/calling_card/heads_of_staff = 1,
- /datum/traitor_objective/assassinate/behead/heads_of_staff = 1,
+ list(
+ /datum/traitor_objective/target_player/kidnapping/common = 20,
+ /datum/traitor_objective/target_player/kidnapping/common/assistant = 1,
+ ) = 4,
+ /datum/traitor_objective/target_player/kidnapping/uncommon = 3,
+ /datum/traitor_objective/target_player/kidnapping/rare = 2,
+ /datum/traitor_objective/target_player/kidnapping/captain = 1
) = 1,
)
-/datum/traitor_objective/assassinate
+/datum/traitor_objective/target_player/assassinate
name = "Assassinate %TARGET% the %JOB TITLE%"
description = "Simply kill your target to accomplish this objective."
- abstract_type = /datum/traitor_objective/assassinate
+ abstract_type = /datum/traitor_objective/target_player/assassinate
progression_minimum = 30 MINUTES
- // The code below is for limiting how often you can get this objective. You will get this objective at a maximum of maximum_objectives_in_period every objective_period
- /// The objective period at which we consider if it is an 'objective'. Set to 0 to accept all objectives.
- var/objective_period = 15 MINUTES
- /// The maximum number of objectives we can get within this period.
- var/maximum_objectives_in_period = 3
-
/**
* Makes the objective only set heads as targets when true, and block them from being targets when false.
* This also blocks the objective from generating UNTIL the un-heads_of_staff version (WHICH SHOULD BE A DIRECT PARENT) is completed.
* example: calling card objective, you kill someone, you unlock the chance to roll a head of staff target version of calling card.
*/
var/heads_of_staff = FALSE
- ///target we need to kill
- var/mob/living/kill_target
-/datum/traitor_objective/assassinate/supported_configuration_changes()
+ duplicate_type = /datum/traitor_objective/target_player
+
+/datum/traitor_objective/target_player/assassinate/supported_configuration_changes()
. = ..()
. += NAMEOF(src, objective_period)
. += NAMEOF(src, maximum_objectives_in_period)
-/datum/traitor_objective/assassinate/calling_card
+/datum/traitor_objective/target_player/assassinate/calling_card
name = "Assassinate %TARGET% the %JOB TITLE%, and plant a calling card"
description = "Kill your target and plant a calling card in the pockets of your victim. If your calling card gets destroyed before you are able to plant it, this objective will fail."
progression_reward = 2 MINUTES
@@ -49,13 +52,13 @@
var/obj/item/paper/calling_card/card
-/datum/traitor_objective/assassinate/calling_card/heads_of_staff
+/datum/traitor_objective/target_player/assassinate/calling_card/heads_of_staff
progression_reward = 4 MINUTES
telecrystal_reward = list(2, 3)
heads_of_staff = TRUE
-/datum/traitor_objective/assassinate/behead
+/datum/traitor_objective/target_player/assassinate/behead
name = "Behead %TARGET%, the %JOB TITLE%"
description = "Behead and hold %TARGET%'s head to succeed this objective. If the head gets destroyed before you can do this, you will fail this objective."
progression_reward = 2 MINUTES
@@ -66,20 +69,20 @@
///the head that needs to be picked up
var/obj/item/bodypart/head/behead_goal
-/datum/traitor_objective/assassinate/behead/heads_of_staff
+/datum/traitor_objective/target_player/assassinate/behead/heads_of_staff
progression_reward = 4 MINUTES
telecrystal_reward = list(2, 3)
heads_of_staff = TRUE
-/datum/traitor_objective/assassinate/calling_card/generate_ui_buttons(mob/user)
+/datum/traitor_objective/target_player/assassinate/calling_card/generate_ui_buttons(mob/user)
var/list/buttons = list()
if(!card)
buttons += add_ui_button("", "Pressing this will materialize a calling card, which you must plant to succeed.", "paper-plane", "summon_card")
return buttons
-/datum/traitor_objective/assassinate/calling_card/ui_perform_action(mob/living/user, action)
+/datum/traitor_objective/target_player/assassinate/calling_card/ui_perform_action(mob/living/user, action)
. = ..()
switch(action)
if("summon_card")
@@ -94,9 +97,9 @@
fail_signals = list(COMSIG_PARENT_QDELETING), \
penalty = TRUE)
-/datum/traitor_objective/assassinate/calling_card/proc/on_card_planted(datum/source, mob/living/equipper, slot)
+/datum/traitor_objective/target_player/assassinate/calling_card/proc/on_card_planted(datum/source, mob/living/equipper, slot)
SIGNAL_HANDLER
- if(equipper != kill_target)
+ if(equipper != target)
return //your target please
if(equipper.stat != DEAD)
return //kill them please
@@ -104,45 +107,45 @@
return //in their pockets please
succeed_objective()
-/datum/traitor_objective/assassinate/calling_card/generate_objective(datum/mind/generating_for, list/possible_duplicates)
+/datum/traitor_objective/target_player/assassinate/calling_card/generate_objective(datum/mind/generating_for, list/possible_duplicates)
. = ..()
if(!.) //didn't generate
return FALSE
- RegisterSignal(kill_target, COMSIG_PARENT_QDELETING, PROC_REF(on_target_qdeleted))
+ RegisterSignal(target, COMSIG_PARENT_QDELETING, PROC_REF(on_target_qdeleted))
-/datum/traitor_objective/assassinate/calling_card/ungenerate_objective()
- UnregisterSignal(kill_target, COMSIG_PARENT_QDELETING)
+/datum/traitor_objective/target_player/assassinate/calling_card/ungenerate_objective()
+ UnregisterSignal(target, COMSIG_PARENT_QDELETING)
. = ..() //unsets kill target
if(card)
UnregisterSignal(card, COMSIG_ITEM_EQUIPPED)
card = null
-/datum/traitor_objective/assassinate/calling_card/on_target_qdeleted()
+/datum/traitor_objective/target_player/assassinate/calling_card/on_target_qdeleted()
//you cannot plant anything on someone who is gone gone, so even if this happens after you're still liable to fail
fail_objective(penalty_cost = telecrystal_penalty)
-/datum/traitor_objective/assassinate/behead/special_target_filter(list/possible_targets)
+/datum/traitor_objective/target_player/assassinate/behead/special_target_filter(list/possible_targets)
for(var/datum/mind/possible_target as anything in possible_targets)
var/mob/living/carbon/possible_current = possible_target.current
var/obj/item/bodypart/head/behead_goal = possible_current.get_bodypart(BODY_ZONE_HEAD)
if(!behead_goal)
possible_targets -= possible_target //cannot be beheaded without a head
-/datum/traitor_objective/assassinate/behead/generate_objective(datum/mind/generating_for, list/possible_duplicates)
+/datum/traitor_objective/target_player/assassinate/behead/generate_objective(datum/mind/generating_for, list/possible_duplicates)
. = ..()
if(!.) //didn't generate
return FALSE
AddComponent(/datum/component/traitor_objective_register, behead_goal, fail_signals = list(COMSIG_PARENT_QDELETING))
- RegisterSignal(kill_target, COMSIG_CARBON_REMOVE_LIMB, PROC_REF(on_target_dismembered))
+ RegisterSignal(target, COMSIG_CARBON_REMOVE_LIMB, PROC_REF(on_target_dismembered))
-/datum/traitor_objective/assassinate/behead/ungenerate_objective()
- UnregisterSignal(kill_target, COMSIG_CARBON_REMOVE_LIMB)
- . = ..() //this unsets kill_target
+/datum/traitor_objective/target_player/assassinate/behead/ungenerate_objective()
+ UnregisterSignal(target, COMSIG_CARBON_REMOVE_LIMB)
+ . = ..() //this unsets target
if(behead_goal)
UnregisterSignal(behead_goal, COMSIG_ITEM_PICKUP)
behead_goal = null
-/datum/traitor_objective/assassinate/behead/proc/on_head_pickup(datum/source, mob/taker)
+/datum/traitor_objective/target_player/assassinate/behead/proc/on_head_pickup(datum/source, mob/taker)
SIGNAL_HANDLER
if(objective_state == OBJECTIVE_STATE_INACTIVE) //just in case- this shouldn't happen?
fail_objective()
@@ -151,7 +154,7 @@
taker.visible_message(span_notice("[taker] holds [behead_goal] into the air for a moment."), span_boldnotice("You lift [behead_goal] into the air for a moment."))
succeed_objective()
-/datum/traitor_objective/assassinate/behead/proc/on_target_dismembered(datum/source, obj/item/bodypart/head/lost_head, special)
+/datum/traitor_objective/target_player/assassinate/behead/proc/on_target_dismembered(datum/source, obj/item/bodypart/head/lost_head, special)
SIGNAL_HANDLER
if(!istype(lost_head))
return
@@ -162,15 +165,15 @@
behead_goal = lost_head
RegisterSignal(behead_goal, COMSIG_ITEM_PICKUP, PROC_REF(on_head_pickup))
-/datum/traitor_objective/assassinate/New(datum/uplink_handler/handler)
+/datum/traitor_objective/target_player/assassinate/New(datum/uplink_handler/handler)
. = ..()
AddComponent(/datum/component/traitor_objective_limit_per_time, \
- /datum/traitor_objective/assassinate, \
+ /datum/traitor_objective/target_player, \
time_period = objective_period, \
maximum_objectives = maximum_objectives_in_period \
)
-/datum/traitor_objective/assassinate/generate_objective(datum/mind/generating_for, list/possible_duplicates)
+/datum/traitor_objective/target_player/assassinate/generate_objective(datum/mind/generating_for, list/possible_duplicates)
var/list/already_targeting = list() //List of minds we're already targeting. The possible_duplicates is a list of objectives, so let's not mix things
for(var/datum/objective/task as anything in handler.primary_objectives)
@@ -211,8 +214,8 @@
if((possible_target.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND))
continue
possible_targets += possible_target
- for(var/datum/traitor_objective/assassinate/objective as anything in possible_duplicates)
- possible_targets -= objective.kill_target
+ for(var/datum/traitor_objective/target_player/objective as anything in possible_duplicates)
+ possible_targets -= objective.target
if(try_target_late_joiners)
var/list/all_possible_targets = possible_targets.Copy()
for(var/datum/mind/possible_target as anything in all_possible_targets)
@@ -224,28 +227,28 @@
if(!possible_targets.len)
return FALSE //MISSION FAILED, WE'LL GET EM NEXT TIME
- var/datum/mind/kill_target_mind = pick(possible_targets)
- kill_target = kill_target_mind.current
- replace_in_name("%TARGET%", kill_target.real_name)
- replace_in_name("%JOB TITLE%", kill_target_mind.assigned_role.title)
- RegisterSignal(kill_target, COMSIG_LIVING_DEATH, PROC_REF(on_target_death))
+ var/datum/mind/target_mind = pick(possible_targets)
+ target = target_mind.current
+ replace_in_name("%TARGET%", target.real_name)
+ replace_in_name("%JOB TITLE%", target_mind.assigned_role.title)
+ RegisterSignal(target, COMSIG_LIVING_DEATH, PROC_REF(on_target_death))
return TRUE
-/datum/traitor_objective/assassinate/ungenerate_objective()
- UnregisterSignal(kill_target, COMSIG_LIVING_DEATH)
- kill_target = null
+/datum/traitor_objective/target_player/assassinate/ungenerate_objective()
+ UnregisterSignal(target, COMSIG_LIVING_DEATH)
+ target = null
///proc for checking for special states that invalidate a target
-/datum/traitor_objective/assassinate/proc/special_target_filter(list/possible_targets)
+/datum/traitor_objective/target_player/assassinate/proc/special_target_filter(list/possible_targets)
return
-/datum/traitor_objective/assassinate/proc/on_target_qdeleted()
+/datum/traitor_objective/target_player/assassinate/proc/on_target_qdeleted()
SIGNAL_HANDLER
if(objective_state == OBJECTIVE_STATE_INACTIVE)
//don't take an objective target of someone who is already obliterated
fail_objective()
-/datum/traitor_objective/assassinate/proc/on_target_death()
+/datum/traitor_objective/target_player/assassinate/proc/on_target_death()
SIGNAL_HANDLER
if(objective_state == OBJECTIVE_STATE_INACTIVE)
//don't take an objective target of someone who is already dead
diff --git a/code/modules/antagonists/traitor/objectives/destroy_heirloom.dm b/code/modules/antagonists/traitor/objectives/destroy_heirloom.dm
index 756607d421f..eb6064becda 100644
--- a/code/modules/antagonists/traitor/objectives/destroy_heirloom.dm
+++ b/code/modules/antagonists/traitor/objectives/destroy_heirloom.dm
@@ -30,6 +30,8 @@
/// The maximum number of objectives that can be taken in this period.
var/maximum_objectives_in_period = 2
+ duplicate_type = /datum/traitor_objective/destroy_heirloom
+
/datum/traitor_objective/destroy_heirloom/common
/// 30 minutes in, syndicate won't care about common heirlooms anymore
progression_minimum = 0 MINUTES
diff --git a/code/modules/antagonists/traitor/objectives/eyesnatching.dm b/code/modules/antagonists/traitor/objectives/eyesnatching.dm
index e9325206c17..b637064139d 100644
--- a/code/modules/antagonists/traitor/objectives/eyesnatching.dm
+++ b/code/modules/antagonists/traitor/objectives/eyesnatching.dm
@@ -1,12 +1,12 @@
/datum/traitor_objective_category/eyesnatching
name = "Eyesnatching"
objectives = list(
- /datum/traitor_objective/eyesnatching = 1,
- /datum/traitor_objective/eyesnatching/heads = 1,
+ /datum/traitor_objective/target_player/eyesnatching = 1,
+ /datum/traitor_objective/target_player/eyesnatching/heads = 1,
)
weight = OBJECTIVE_WEIGHT_UNLIKELY
-/datum/traitor_objective/eyesnatching
+/datum/traitor_objective/target_player/eyesnatching
name = "Steal the eyes of %TARGET% the %JOB TITLE%"
description = "%TARGET% messed with the wrong people. Steal their eyes to teach them a lesson. You will be provided an experimental eyesnatcher device to aid you in your mission."
@@ -15,41 +15,33 @@
progression_reward = list(4 MINUTES, 8 MINUTES)
telecrystal_reward = list(1, 2)
- // The code below is for limiting how often you can get this objective. You will get this objective at a maximum of maximum_objectives_in_period every objective_period
- /// The objective period at which we consider if it is an 'objective'. Set to 0 to accept all objectives.
- var/objective_period = 15 MINUTES
- /// The maximum number of objectives we can get within this period.
- var/maximum_objectives_in_period = 3
-
- /// Who we're stealing eyes from
- var/mob/living/victim
/// If we're targeting heads of staff or not
var/heads_of_staff = FALSE
/// Have we already spawned an eyesnatcher
var/spawned_eyesnatcher = FALSE
- duplicate_type = /datum/traitor_objective/eyesnatching
+ duplicate_type = /datum/traitor_objective/target_player
-/datum/traitor_objective/eyesnatching/supported_configuration_changes()
+/datum/traitor_objective/target_player/eyesnatching/supported_configuration_changes()
. = ..()
. += NAMEOF(src, objective_period)
. += NAMEOF(src, maximum_objectives_in_period)
-/datum/traitor_objective/eyesnatching/New(datum/uplink_handler/handler)
+/datum/traitor_objective/target_player/eyesnatching/New(datum/uplink_handler/handler)
. = ..()
AddComponent(/datum/component/traitor_objective_limit_per_time, \
- /datum/traitor_objective/eyesnatching, \
+ /datum/traitor_objective/target_player, \
time_period = objective_period, \
maximum_objectives = maximum_objectives_in_period \
)
-/datum/traitor_objective/eyesnatching/heads
+/datum/traitor_objective/target_player/eyesnatching/heads
progression_reward = list(6 MINUTES, 12 MINUTES)
telecrystal_reward = list(2, 3)
heads_of_staff = TRUE
-/datum/traitor_objective/eyesnatching/generate_objective(datum/mind/generating_for, list/possible_duplicates)
+/datum/traitor_objective/target_player/eyesnatching/generate_objective(datum/mind/generating_for, list/possible_duplicates)
var/list/already_targeting = list() //List of minds we're already targeting. The possible_duplicates is a list of objectives, so let's not mix things
for(var/datum/objective/task as anything in handler.primary_objectives)
@@ -94,8 +86,8 @@
possible_targets += possible_target
- for(var/datum/traitor_objective/eyesnatching/objective as anything in possible_duplicates)
- possible_targets -= objective.victim?.mind
+ for(var/datum/traitor_objective/target_player/objective as anything in possible_duplicates)
+ possible_targets -= objective.target?.mind
if(try_target_late_joiners)
var/list/all_possible_targets = possible_targets.Copy()
@@ -109,16 +101,16 @@
if(!possible_targets.len)
return FALSE //MISSION FAILED, WE'LL GET EM NEXT TIME
- var/datum/mind/victim_mind = pick(possible_targets)
- victim = victim_mind.current
+ var/datum/mind/target_mind = pick(possible_targets)
+ target = target_mind.current
- replace_in_name("%TARGET%", victim_mind.name)
- replace_in_name("%JOB TITLE%", victim_mind.assigned_role.title)
- RegisterSignal(victim, COMSIG_CARBON_LOSE_ORGAN, PROC_REF(check_eye_removal))
- AddComponent(/datum/component/traitor_objective_register, victim, fail_signals = list(COMSIG_PARENT_QDELETING))
+ replace_in_name("%TARGET%", target_mind.name)
+ replace_in_name("%JOB TITLE%", target_mind.assigned_role.title)
+ RegisterSignal(target, COMSIG_CARBON_LOSE_ORGAN, PROC_REF(check_eye_removal))
+ AddComponent(/datum/component/traitor_objective_register, target, fail_signals = list(COMSIG_PARENT_QDELETING))
return TRUE
-/datum/traitor_objective/eyesnatching/proc/check_eye_removal(datum/source, obj/item/organ/internal/eyes/removed)
+/datum/traitor_objective/target_player/eyesnatching/proc/check_eye_removal(datum/source, obj/item/organ/internal/eyes/removed)
SIGNAL_HANDLER
if(!istype(removed))
@@ -126,13 +118,13 @@
succeed_objective()
-/datum/traitor_objective/eyesnatching/generate_ui_buttons(mob/user)
+/datum/traitor_objective/target_player/eyesnatching/generate_ui_buttons(mob/user)
var/list/buttons = list()
if(!spawned_eyesnatcher)
buttons += add_ui_button("", "Pressing this will materialize an eyesnatcher, which can be used on incapacitaded or restrained targets to forcefully remove their eyes.", "syringe", "eyesnatcher")
return buttons
-/datum/traitor_objective/eyesnatching/ui_perform_action(mob/living/user, action)
+/datum/traitor_objective/target_player/eyesnatching/ui_perform_action(mob/living/user, action)
. = ..()
switch(action)
if("eyesnatcher")
@@ -163,59 +155,59 @@
. = ..()
icon_state = "[base_icon_state][used ? "-used" : ""]"
-/obj/item/eyesnatcher/attack(mob/living/carbon/human/victim, mob/living/user, params)
- if(used || !istype(victim) || !victim.Adjacent(user)) //Works only once, no TK use
+/obj/item/eyesnatcher/attack(mob/living/carbon/human/target, mob/living/user, params)
+ if(used || !istype(target) || !target.Adjacent(user)) //Works only once, no TK use
return ..()
- var/obj/item/organ/internal/eyes/eyeballies = victim.getorganslot(ORGAN_SLOT_EYES)
- var/obj/item/bodypart/head/head = victim.get_bodypart(BODY_ZONE_HEAD)
+ var/obj/item/organ/internal/eyes/eyeballies = target.getorganslot(ORGAN_SLOT_EYES)
+ var/obj/item/bodypart/head/head = target.get_bodypart(BODY_ZONE_HEAD)
- if(!head || !eyeballies || victim.is_eyes_covered())
+ if(!head || !eyeballies || target.is_eyes_covered())
return ..()
- user.do_attack_animation(victim, used_item = src)
- victim.visible_message(
- span_warning("[user] presses [src] against [victim]'s skull!"),
+ user.do_attack_animation(target, used_item = src)
+ target.visible_message(
+ span_warning("[user] presses [src] against [target]'s skull!"),
span_userdanger("[user] presses [src] against your skull!"))
- if(!do_after(user, 5 SECONDS, target = victim, extra_checks = CALLBACK(src, PROC_REF(eyeballs_exist), eyeballies, head, victim)))
+ if(!do_after(user, 5 SECONDS, target = target, extra_checks = CALLBACK(src, PROC_REF(eyeballs_exist), eyeballies, head, target)))
return
- to_chat(victim, span_userdanger("You feel something forcing its way into your skull!"))
+ to_chat(target, span_userdanger("You feel something forcing its way into your skull!"))
balloon_alert(user, "applying pressure...")
- if(!do_after(user, 5 SECONDS, target = victim, extra_checks = CALLBACK(src, PROC_REF(eyeballs_exist), eyeballies, head, victim)))
+ if(!do_after(user, 5 SECONDS, target = target, extra_checks = CALLBACK(src, PROC_REF(eyeballs_exist), eyeballies, head, target)))
return
var/datum/wound/blunt/severe/severe_wound_type = /datum/wound/blunt/severe
var/datum/wound/blunt/critical/critical_wound_type = /datum/wound/blunt/critical
- victim.apply_damage(20, BRUTE, BODY_ZONE_HEAD, wound_bonus = rand(initial(severe_wound_type.threshold_minimum), initial(critical_wound_type.threshold_minimum) + 10))
- victim.visible_message(
- span_danger("[src] pierces through [victim]'s skull, horribly mutilating their eyes!"),
+ target.apply_damage(20, BRUTE, BODY_ZONE_HEAD, wound_bonus = rand(initial(severe_wound_type.threshold_minimum), initial(critical_wound_type.threshold_minimum) + 10))
+ target.visible_message(
+ span_danger("[src] pierces through [target]'s skull, horribly mutilating their eyes!"),
span_userdanger("Something penetrates your skull, horribly mutilating your eyes! Holy fuck!"),
span_hear("You hear a sickening sound of metal piercing flesh!")
)
eyeballies.applyOrganDamage(eyeballies.maxHealth)
- victim.emote("scream")
- playsound(victim, "sound/effects/wounds/crackandbleed.ogg", 100)
- log_combat(user, victim, "cracked the skull of (eye snatching)", src)
+ target.emote("scream")
+ playsound(target, "sound/effects/wounds/crackandbleed.ogg", 100)
+ log_combat(user, target, "cracked the skull of (eye snatching)", src)
- if(!do_after(user, 5 SECONDS, target = victim, extra_checks = CALLBACK(src, PROC_REF(eyeballs_exist), eyeballies, head, victim)))
+ if(!do_after(user, 5 SECONDS, target = target, extra_checks = CALLBACK(src, PROC_REF(eyeballs_exist), eyeballies, head, target)))
return
- if(!victim.is_blind())
- to_chat(victim, span_userdanger("You suddenly go blind!"))
+ if(!target.is_blind())
+ to_chat(target, span_userdanger("You suddenly go blind!"))
if(prob(1))
- to_chat(victim, span_notice("At least you got a new pirate-y look out of it..."))
- var/obj/item/clothing/glasses/eyepatch/new_patch = new(victim.loc)
- victim.equip_to_slot_if_possible(new_patch, ITEM_SLOT_EYES, disable_warning = TRUE)
+ to_chat(target, span_notice("At least you got a new pirate-y look out of it..."))
+ var/obj/item/clothing/glasses/eyepatch/new_patch = new(target.loc)
+ target.equip_to_slot_if_possible(new_patch, ITEM_SLOT_EYES, disable_warning = TRUE)
- to_chat(user, span_notice("You successfully extract [victim]'s eyeballs."))
- playsound(victim, 'sound/surgery/retractor2.ogg', 100, TRUE)
- playsound(victim, 'sound/effects/pop.ogg', 100, TRAIT_MUTE)
- eyeballies.Remove(victim)
- eyeballies.forceMove(get_turf(victim))
- victim.emote("scream")
+ to_chat(user, span_notice("You successfully extract [target]'s eyeballs."))
+ playsound(target, 'sound/surgery/retractor2.ogg', 100, TRUE)
+ playsound(target, 'sound/effects/pop.ogg', 100, TRAIT_MUTE)
+ eyeballies.Remove(target)
+ eyeballies.forceMove(get_turf(target))
+ target.emote("scream")
if(prob(20))
- victim.emote("cry")
+ target.emote("cry")
used = TRUE
update_appearance(UPDATE_ICON)
@@ -224,17 +216,17 @@
if(used)
. += span_notice("It has been used up.")
-/obj/item/eyesnatcher/proc/eyeballs_exist(obj/item/organ/internal/eyes/eyeballies, obj/item/bodypart/head/head, mob/living/carbon/human/victim)
+/obj/item/eyesnatcher/proc/eyeballs_exist(obj/item/organ/internal/eyes/eyeballies, obj/item/bodypart/head/head, mob/living/carbon/human/target)
if(!eyeballies || QDELETED(eyeballies))
return FALSE
if(!head || QDELETED(head))
return FALSE
- if(eyeballies.owner != victim)
+ if(eyeballies.owner != target)
return FALSE
- var/obj/item/organ/internal/eyes/eyes = victim.getorganslot(ORGAN_SLOT_EYES)
+ var/obj/item/organ/internal/eyes/eyes = target.getorganslot(ORGAN_SLOT_EYES)
//got different eyes or doesn't own the head... somehow
- if(head.owner != victim || eyes != eyeballies)
+ if(head.owner != target || eyes != eyeballies)
return FALSE
return TRUE
diff --git a/code/modules/antagonists/traitor/objectives/kidnapping.dm b/code/modules/antagonists/traitor/objectives/kidnapping.dm
index 5dc2ec634ae..bdbbb416f3b 100644
--- a/code/modules/antagonists/traitor/objectives/kidnapping.dm
+++ b/code/modules/antagonists/traitor/objectives/kidnapping.dm
@@ -1,54 +1,37 @@
-/datum/traitor_objective_category/kidnapping
- name = "Kidnap Personnel"
- objectives = list( //Similar weights to destroy heirloom objectives
- list(
- /datum/traitor_objective/kidnapping/common = 20,
- /datum/traitor_objective/kidnapping/common/assistant = 1,
- ) = 4,
- /datum/traitor_objective/kidnapping/uncommon = 3,
- /datum/traitor_objective/kidnapping/rare = 2,
- /datum/traitor_objective/kidnapping/captain = 1
- )
-
-/datum/traitor_objective/kidnapping
+/datum/traitor_objective/target_player/kidnapping
name = "Kidnap %TARGET% the %JOB TITLE% and deliver them to %AREA%"
description = "%TARGET% holds extremely important information regarding secret NT projects - and you'll need to kidnap and deliver them to %AREA%, where our transport pod will be waiting. \
You'll get additional reward if %TARGET% is delivered alive."
- abstract_type = /datum/traitor_objective/kidnapping
-
- /// The period of time until you can take another objective after taking 3 objectives.
- var/objective_period = 15 MINUTES
- /// The maximum number of objectives we can get within this period.
- var/maximum_objectives_in_period = 3
+ abstract_type = /datum/traitor_objective/target_player/kidnapping
/// The jobs that this objective is targetting.
var/list/target_jobs
- /// The person we need to kidnap
- var/mob/living/victim
- /// Area that the victim needs to be delivered to
+ /// Area that the target needs to be delivered to
var/area/dropoff_area
/// Have we called the pod yet?
var/pod_called = FALSE
/// How much TC do we get from sending the target alive
var/alive_bonus = 0
- /// All stripped victims belongings
- var/list/victim_belongings = list()
+ /// All stripped targets belongings
+ var/list/target_belongings = list()
-/datum/traitor_objective/kidnapping/supported_configuration_changes()
+ duplicate_type = /datum/traitor_objective/target_player
+
+/datum/traitor_objective/target_player/kidnapping/supported_configuration_changes()
. = ..()
. += NAMEOF(src, objective_period)
. += NAMEOF(src, maximum_objectives_in_period)
-/datum/traitor_objective/kidnapping/New(datum/uplink_handler/handler)
+/datum/traitor_objective/target_player/kidnapping/New(datum/uplink_handler/handler)
. = ..()
AddComponent(/datum/component/traitor_objective_limit_per_time, \
- /datum/traitor_objective/kidnapping, \
+ /datum/traitor_objective/target_player, \
time_period = objective_period, \
maximum_objectives = maximum_objectives_in_period \
)
-/datum/traitor_objective/kidnapping/common
+/datum/traitor_objective/target_player/kidnapping/common
progression_minimum = 0 MINUTES
progression_maximum = 30 MINUTES
progression_reward = list(2 MINUTES, 4 MINUTES)
@@ -80,14 +63,14 @@
)
alive_bonus = 2
-/datum/traitor_objective/kidnapping/common/assistant
+/datum/traitor_objective/target_player/kidnapping/common/assistant
progression_minimum = 0 MINUTES
progression_maximum = 15 MINUTES
target_jobs = list(
/datum/job/assistant
)
-/datum/traitor_objective/kidnapping/uncommon //Hard to fish out victims
+/datum/traitor_objective/target_player/kidnapping/uncommon //Hard to fish out targets
progression_minimum = 0 MINUTES
progression_maximum = 45 MINUTES
progression_reward = list(4 MINUTES, 8 MINUTES)
@@ -103,7 +86,7 @@
)
alive_bonus = 3
-/datum/traitor_objective/kidnapping/rare
+/datum/traitor_objective/target_player/kidnapping/rare
progression_minimum = 15 MINUTES
progression_maximum = 60 MINUTES
progression_reward = list(8 MINUTES, 12 MINUTES)
@@ -122,7 +105,7 @@
)
alive_bonus = 4
-/datum/traitor_objective/kidnapping/captain
+/datum/traitor_objective/target_player/kidnapping/captain
progression_minimum = 30 MINUTES
progression_reward = list(12 MINUTES, 16 MINUTES)
telecrystal_reward = list(2, 3)
@@ -132,7 +115,7 @@
)
alive_bonus = 5
-/datum/traitor_objective/kidnapping/generate_objective(datum/mind/generating_for, list/possible_duplicates)
+/datum/traitor_objective/target_player/kidnapping/generate_objective(datum/mind/generating_for, list/possible_duplicates)
var/list/already_targeting = list() //List of minds we're already targeting. The possible_duplicates is a list of objectives, so let's not mix things
for(var/datum/objective/task as anything in handler.primary_objectives)
@@ -165,17 +148,17 @@
possible_targets += possible_target
- for(var/datum/traitor_objective/kidnapping/objective as anything in possible_duplicates)
- if(!objective.victim) //the old objective was already completed.
+ for(var/datum/traitor_objective/target_player/objective as anything in possible_duplicates)
+ if(!objective.target) //the old objective was already completed.
continue
- possible_targets -= objective.victim.mind
+ possible_targets -= objective.target.mind
if(!length(possible_targets))
return FALSE
var/datum/mind/target_mind = pick(possible_targets)
- victim = target_mind.current
- AddComponent(/datum/component/traitor_objective_register, victim, fail_signals = list(COMSIG_PARENT_QDELETING))
+ target = target_mind.current
+ AddComponent(/datum/component/traitor_objective_register, target, fail_signals = list(COMSIG_PARENT_QDELETING))
var/list/possible_areas = GLOB.the_station_areas.Copy()
for(var/area/possible_area as anything in possible_areas)
if(ispath(possible_area, /area/station/hallway) || ispath(possible_area, /area/station/security) || initial(possible_area.outdoors))
@@ -187,51 +170,51 @@
replace_in_name("%AREA%", initial(dropoff_area.name))
return TRUE
-/datum/traitor_objective/kidnapping/ungenerate_objective()
- victim = null
+/datum/traitor_objective/target_player/kidnapping/ungenerate_objective()
+ target = null
dropoff_area = null
-/datum/traitor_objective/kidnapping/on_objective_taken(mob/user)
+/datum/traitor_objective/target_player/kidnapping/on_objective_taken(mob/user)
. = ..()
INVOKE_ASYNC(src, PROC_REF(generate_holding_area))
-/datum/traitor_objective/kidnapping/proc/generate_holding_area()
+/datum/traitor_objective/target_player/kidnapping/proc/generate_holding_area()
// Let's load in the holding facility ahead of time
// even if they fail the objective it's better to get done now rather than later
SSmapping.lazy_load_template(LAZY_TEMPLATE_KEY_NINJA_HOLDING_FACILITY)
-/datum/traitor_objective/kidnapping/generate_ui_buttons(mob/user)
+/datum/traitor_objective/target_player/kidnapping/generate_ui_buttons(mob/user)
var/list/buttons = list()
if(!pod_called)
buttons += add_ui_button("Call Extraction Pod", "Pressing this will call down an extraction pod.", "rocket", "call_pod")
return buttons
-/datum/traitor_objective/kidnapping/ui_perform_action(mob/living/user, action)
+/datum/traitor_objective/target_player/kidnapping/ui_perform_action(mob/living/user, action)
. = ..()
switch(action)
if("call_pod")
if(pod_called)
return
var/area/user_area = get_area(user)
- var/area/victim_area = get_area(victim)
+ var/area/target_area = get_area(target)
if(user_area.type != dropoff_area)
to_chat(user, span_warning("You must be in [initial(dropoff_area.name)] to call the extraction pod."))
return
- if(victim_area.type != dropoff_area)
- to_chat(user, span_warning("[victim.real_name] must be in [initial(dropoff_area.name)] for you to call the extraction pod."))
+ if(target_area.type != dropoff_area)
+ to_chat(user, span_warning("[target.real_name] must be in [initial(dropoff_area.name)] for you to call the extraction pod."))
return
call_pod(user)
-/datum/traitor_objective/kidnapping/proc/call_pod(mob/living/user)
+/datum/traitor_objective/target_player/kidnapping/proc/call_pod(mob/living/user)
pod_called = TRUE
var/obj/structure/closet/supplypod/extractionpod/new_pod = new()
RegisterSignal(new_pod, COMSIG_ATOM_ENTERED, PROC_REF(enter_check))
new /obj/effect/pod_landingzone(get_turf(user), new_pod)
-/datum/traitor_objective/kidnapping/proc/enter_check(obj/structure/closet/supplypod/extractionpod/source, entered_atom)
+/datum/traitor_objective/target_player/kidnapping/proc/enter_check(obj/structure/closet/supplypod/extractionpod/source, entered_atom)
if(!istype(source))
CRASH("Kidnapping objective's enter_check called with source being not an extraction pod: [source ? source.type : "N/A"]")
@@ -250,7 +233,7 @@
var/unequipped = sent_mob.transferItemToLoc(belonging)
if (!unequipped)
continue
- victim_belongings.Add(belonging)
+ target_belongings.Add(belonging)
var/datum/bank_account/cargo_account = SSeconomy.get_dep_account(ACCOUNT_CAR)
@@ -259,9 +242,9 @@
priority_announce("One of your crew was captured by a rival organisation - we've needed to pay their ransom to bring them back. As is policy we've taken a portion of the station's funds to offset the overall cost.", "Nanotrasen Asset Protection", has_important_message = TRUE)
- addtimer(CALLBACK(src, PROC_REF(handle_victim), sent_mob), 1.5 SECONDS)
+ addtimer(CALLBACK(src, PROC_REF(handle_target), sent_mob), 1.5 SECONDS)
- if(sent_mob != victim)
+ if(sent_mob != target)
fail_objective(penalty_cost = telecrystal_penalty)
source.startExitSequence(source)
return
@@ -272,8 +255,8 @@
succeed_objective()
source.startExitSequence(source)
-/datum/traitor_objective/kidnapping/proc/handle_victim(mob/living/carbon/human/sent_mob)
- addtimer(CALLBACK(src, PROC_REF(return_victim), sent_mob), 3 MINUTES)
+/datum/traitor_objective/target_player/kidnapping/proc/handle_target(mob/living/carbon/human/sent_mob)
+ addtimer(CALLBACK(src, PROC_REF(return_target), sent_mob), 3 MINUTES)
if(sent_mob.stat == DEAD)
return
@@ -286,7 +269,7 @@
we thank you for providing them. Your value is expended, and you will be ransomed back to your station. We always get paid, \
so it's only a matter of time before we ship you back...\"")))
-/datum/traitor_objective/kidnapping/proc/return_victim(mob/living/carbon/human/sent_mob)
+/datum/traitor_objective/target_player/kidnapping/proc/return_target(mob/living/carbon/human/sent_mob)
if(!sent_mob || QDELETED(sent_mob)) //suicided and qdeleted themselves
return
@@ -319,7 +302,7 @@
continue
sent_mob.dropItemToGround(belonging) // No souvenirs, except shoes and t-shirts
- for(var/obj/item/belonging in victim_belongings)
+ for(var/obj/item/belonging in target_belongings)
belonging.forceMove(return_pod)
sent_mob.forceMove(return_pod)
@@ -332,7 +315,7 @@
new /obj/effect/pod_landingzone(pick(possible_turfs), return_pod)
/// Returns a list of things that the provided mob has which we would rather that they do not have
-/datum/traitor_objective/kidnapping/proc/gather_belongings(mob/living/carbon/human/kidnapee)
+/datum/traitor_objective/target_player/kidnapping/proc/gather_belongings(mob/living/carbon/human/kidnapee)
var/list/belongings = kidnapee.get_all_gear()
for (var/obj/item/implant/storage/internal_bag in kidnapee.implants)
belongings += internal_bag.contents
diff --git a/code/modules/antagonists/traitor/uplink_handler.dm b/code/modules/antagonists/traitor/uplink_handler.dm
index 800844a70dc..9b2b7dfb706 100644
--- a/code/modules/antagonists/traitor/uplink_handler.dm
+++ b/code/modules/antagonists/traitor/uplink_handler.dm
@@ -44,8 +44,10 @@
var/assigned_role
/// The species this uplink handler is associated to.
var/assigned_species
- /// Whether this is in debug mode or not. If in debug mode, allows all purchases
+ /// Whether this is in debug mode or not. If in debug mode, allows all purchases. Bypasses the shop lock.
var/debug_mode = FALSE
+ /// Whether the shop is locked or not. If set to true, nothing can be purchased.
+ var/shop_locked = FALSE
/datum/uplink_handler/New()
. = ..()
@@ -76,6 +78,14 @@
if(debug_mode)
return TRUE
+ if(shop_locked)
+ return FALSE
+
+ if(to_purchase.lock_other_purchases)
+ // Can't purchase an uplink item that locks other purchases if you've already purchased something
+ if(length(purchase_log.purchase_log) > 0)
+ return FALSE
+
if(!check_if_restricted(to_purchase))
return FALSE
diff --git a/code/modules/asset_cache/assets/uplink.dm b/code/modules/asset_cache/assets/uplink.dm
index 7d1b6d950b9..e283b86c042 100644
--- a/code/modules/asset_cache/assets/uplink.dm
+++ b/code/modules/asset_cache/assets/uplink.dm
@@ -33,6 +33,7 @@
"restricted_species" = item.restricted_species,
"progression_minimum" = item.progression_minimum,
"cost_override_string" = item.cost_override_string,
+ "lock_other_purchases" = item.lock_other_purchases
))
}
SStraitor.uplink_items += item
diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm
index 4d344efa16e..8d1607fd119 100644
--- a/code/modules/uplink/uplink_items.dm
+++ b/code/modules/uplink/uplink_items.dm
@@ -66,8 +66,13 @@
var/restricted = FALSE
/// Can this item be deconstructed to unlock certain techweb research nodes?
var/illegal_tech = TRUE
- // String to be shown instead of the price, e.g for the Random item.
+ /// String to be shown instead of the price, e.g for the Random item.
var/cost_override_string = ""
+ /// Whether this item locks all other items from being purchased. Used by syndicate balloon and a few other purchases.
+ /// Can't be purchased if you've already bought other things
+ /// Uses the purchase log, so items purchased that are not visible in the purchase log will not count towards this.
+ /// However, they won't be purchasable afterwards.
+ var/lock_other_purchases = FALSE
/datum/uplink_item/New()
. = ..()
@@ -108,6 +113,8 @@
log_uplink("[key_name(user)] purchased [src] for [cost] telecrystals from [source]'s uplink")
if(purchase_log_vis && uplink_handler.purchase_log)
uplink_handler.purchase_log.LogPurchase(A, src, cost)
+ if(lock_other_purchases)
+ uplink_handler.shop_locked = TRUE
/// Spawns an item in the world
/datum/uplink_item/proc/spawn_item(spawn_path, mob/user, datum/uplink_handler/uplink_handler, atom/movable/source)
diff --git a/code/modules/uplink/uplink_items/ammunition.dm b/code/modules/uplink/uplink_items/ammunition.dm
index 6a70d60c90c..4ab97445251 100644
--- a/code/modules/uplink/uplink_items/ammunition.dm
+++ b/code/modules/uplink/uplink_items/ammunition.dm
@@ -1,5 +1,3 @@
-// File ordered by progression
-
/datum/uplink_category/ammo
name = "Ammunition"
weight = 7
@@ -8,8 +6,6 @@
category = /datum/uplink_category/ammo
surplus = 40
-// No progression cost
-
/datum/uplink_item/ammo/toydarts
name = "Box of Riot Darts"
desc = "A box of 40 Donksoft riot darts, for reloading any compatible foam dart magazine. Don't forget to share!"
@@ -19,8 +15,6 @@
illegal_tech = FALSE
purchasable_from = ~UPLINK_NUKE_OPS
-// Low progression cost
-
/datum/uplink_item/ammo/pistol
name = "9mm Handgun Magazine"
desc = "An additional 8-round 9mm magazine, compatible with the Makarov pistol."
@@ -30,8 +24,6 @@
purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
illegal_tech = FALSE
-// Medium progression cost
-
/datum/uplink_item/ammo/pistolap
name = "9mm Armour Piercing Magazine"
desc = "An additional 8-round 9mm magazine, compatible with the Makarov pistol. \
diff --git a/code/modules/uplink/uplink_items/badass.dm b/code/modules/uplink/uplink_items/badass.dm
index 6c48c51226b..1fb7fab4964 100644
--- a/code/modules/uplink/uplink_items/badass.dm
+++ b/code/modules/uplink/uplink_items/badass.dm
@@ -12,6 +12,7 @@
Can blow the deepest of covers."
item = /obj/item/toy/balloon/syndicate
cost = 20
+ lock_other_purchases = TRUE
cant_discount = TRUE
illegal_tech = FALSE
@@ -32,8 +33,6 @@
cost = 2
illegal_tech = FALSE
-// Low progression
-
/datum/uplink_item/badass/syndiecash
name = "Syndicate Briefcase Full of Cash"
desc = "A secure briefcase containing 5000 space credits. Useful for bribing personnel, or purchasing goods \
@@ -45,7 +44,6 @@
restricted = TRUE
illegal_tech = FALSE
-// Ultra high progression
/datum/uplink_item/badass/costumes/clown
name = "Clown Costume"
desc = "Nothing is more terrifying than clowns with fully automatic weaponry."
diff --git a/code/modules/uplink/uplink_items/dangerous.dm b/code/modules/uplink/uplink_items/dangerous.dm
index 4f295904bfb..0c14c302b5e 100644
--- a/code/modules/uplink/uplink_items/dangerous.dm
+++ b/code/modules/uplink/uplink_items/dangerous.dm
@@ -1,5 +1,3 @@
-// File organised based on progression
-
//All bundles and telecrystals
/datum/uplink_category/dangerous
name = "Conspicuous Weapons"
@@ -8,8 +6,6 @@
/datum/uplink_item/dangerous
category = /datum/uplink_category/dangerous
-// No progression cost
-
/datum/uplink_item/dangerous/foampistol
name = "Toy Pistol with Riot Darts"
desc = "An innocent-looking toy pistol designed to fire foam darts. Comes loaded with riot-grade \
@@ -19,8 +15,6 @@
surplus = 10
purchasable_from = ~UPLINK_NUKE_OPS
-// Low progression cost
-
/datum/uplink_item/dangerous/pistol
name = "Makarov Pistol"
desc = "A small, easily concealable handgun that uses 9mm auto rounds in 8-round magazines and is compatible \
@@ -66,9 +60,6 @@
item = /obj/item/clothing/gloves/rapid
cost = 8
-
-// Medium progression cost
-
/datum/uplink_item/dangerous/doublesword
name = "Double-Bladed Energy Sword"
desc = "The double-bladed energy sword does slightly more damage than a standard energy sword and will deflect \
diff --git a/code/modules/uplink/uplink_items/device_tools.dm b/code/modules/uplink/uplink_items/device_tools.dm
index 5e8e22427d1..4ed280885c2 100644
--- a/code/modules/uplink/uplink_items/device_tools.dm
+++ b/code/modules/uplink/uplink_items/device_tools.dm
@@ -1,5 +1,3 @@
-// File ordered based on progression
-
/datum/uplink_category/device_tools
name = "Misc. Gadgets"
weight = 3
@@ -7,8 +5,6 @@
/datum/uplink_item/device_tools
category = /datum/uplink_category/device_tools
-// No progression cost
-
/datum/uplink_item/device_tools/soap
name = "Syndicate Soap"
desc = "A sinister-looking surfactant used to clean blood stains to hide murders and prevent DNA analysis. \
@@ -192,13 +188,10 @@
surplus = 75
restricted = TRUE
-// Low progression cost
-
/datum/uplink_item/device_tools/emag
name = "Cryptographic Sequencer"
desc = "The cryptographic sequencer, electromagnetic card, or emag, is a small card that unlocks hidden functions \
in electronic devices, subverts intended functions, and easily breaks security mechanisms. Cannot be used to open airlocks."
- progression_minimum = 20 MINUTES
item = /obj/item/card/emag
cost = 4
@@ -206,7 +199,6 @@
name = "Stimpack"
desc = "Stimpacks, the tool of many great heroes, make you nearly immune to stuns and knockdowns for about \
5 minutes after injection."
- progression_minimum = 20 MINUTES
item = /obj/item/reagent_containers/hypospray/medipen/stimulants
cost = 5
surplus = 90
@@ -215,12 +207,9 @@
name = "Super Pointy Tape"
desc = "An all-purpose super pointy tape roll. The tape is built with hundreds of tiny metal needles, the roll comes with in 5 pieces. When added to items the \
item that was taped will embed when thrown at people. Taping people's mouthes with it will hurt them if pulled off by someone else."
- progression_minimum = 10 MINUTES
item = /obj/item/stack/sticky_tape/pointy/super
cost = 1
-// Medium progression cost
-
/datum/uplink_item/device_tools/hacked_module
name = "Hacked AI Law Upload Module"
desc = "When used with an upload console, this module allows you to upload priority laws to an artificial intelligence. \
@@ -232,14 +221,12 @@
/datum/uplink_item/device_tools/hypnotic_flash
name = "Hypnotic Flash"
desc = "A modified flash able to hypnotize targets. If the target is not in a mentally vulnerable state, it will only confuse and pacify them temporarily."
- progression_minimum = 30 MINUTES
item = /obj/item/assembly/flash/hypnotic
cost = 7
/datum/uplink_item/device_tools/hypnotic_grenade
name = "Hypnotic Grenade"
desc = "A modified flashbang grenade able to hypnotize targets. The sound portion of the flashbang causes hallucinations, and will allow the flash to induce a hypnotic trance to viewers."
- progression_minimum = 30 MINUTES
item = /obj/item/grenade/hypnotic
cost = 12
diff --git a/code/modules/uplink/uplink_items/explosive.dm b/code/modules/uplink/uplink_items/explosive.dm
index bd28454122c..32a5ad49d57 100644
--- a/code/modules/uplink/uplink_items/explosive.dm
+++ b/code/modules/uplink/uplink_items/explosive.dm
@@ -1,5 +1,3 @@
-// File ordered based on progression.
-
/datum/uplink_category/explosives
name = "Explosives"
weight = 6
@@ -7,22 +5,18 @@
/datum/uplink_item/explosives
category = /datum/uplink_category/explosives
-// Low progression cost
/datum/uplink_item/explosives/soap_clusterbang
name = "Slipocalypse Clusterbang"
- progression_minimum = 10 MINUTES
desc = "A traditional clusterbang grenade with a payload consisting entirely of Syndicate soap. Useful in any scenario!"
item = /obj/item/grenade/clusterbuster/soap
cost = 3
-// Medium progression cost
-
/datum/uplink_item/explosives/c4
name = "Composition C-4"
desc = "C-4 is plastic explosive of the common variety Composition C. You can use it to breach walls, sabotage equipment, or connect \
an assembly to it in order to alter the way it detonates. It can be attached to almost all objects and has a modifiable timer with a \
minimum setting of 10 seconds."
- progression_minimum = 10 MINUTES
+ progression_minimum = 5 MINUTES
item = /obj/item/grenade/c4
cost = 1
@@ -30,7 +24,7 @@
name = "Bag of C-4 explosives"
desc = "Because sometimes quantity is quality. Contains 10 C-4 plastic explosives."
item = /obj/item/storage/backpack/duffelbag/syndie/c4
- progression_minimum = 20 MINUTES
+ progression_minimum = 10 MINUTES
cost = 8 //20% discount!
cant_discount = TRUE
@@ -39,7 +33,7 @@
desc = "Contains 3 X-4 shaped plastic explosives. Similar to C4, but with a stronger blast that is directional instead of circular. \
X-4 can be placed on a solid surface, such as a wall or window, and it will blast through the wall, injuring anything on the opposite side, while being safer to the user. \
For when you want a controlled explosion that leaves a wider, deeper, hole."
- progression_minimum = 30 MINUTES
+ progression_minimum = 20 MINUTES
item = /obj/item/storage/backpack/duffelbag/syndie/x4
cost = 4
cant_discount = TRUE
@@ -49,7 +43,7 @@
desc = "When inserted into a tablet, this cartridge gives you four opportunities to \
detonate tablets of crewmembers who have their message feature enabled. \
The concussive effect from the explosion will knock the recipient out for a short period, and deafen them for longer."
- progression_minimum = 30 MINUTES
+ progression_minimum = 20 MINUTES
item = /obj/item/computer_disk/virus/detomatix
cost = 6
restricted = TRUE
@@ -58,7 +52,6 @@
name = "EMP Grenades and Implanter Kit"
desc = "A box that contains five EMP grenades and an EMP implant with three uses. Useful to disrupt communications, \
security's energy weapons and silicon lifeforms when you're in a tight spot."
- progression_minimum = 30 MINUTES
item = /obj/item/storage/box/syndie_kit/emp
cost = 2
@@ -98,8 +91,6 @@
if(HAS_TRAIT(SSstation, STATION_TRAIT_CYBERNETIC_REVOLUTION))
cost *= 2
-// High progression cost
-
/datum/uplink_item/explosives/syndicate_bomb
name = "Syndicate Bomb"
desc = "The Syndicate bomb is a fearsome device capable of massive destruction. It has an adjustable timer, \
@@ -108,7 +99,7 @@
transported to you that will teleport the actual bomb to it upon activation. Note that this bomb can \
be defused, and some crew may attempt to do so. \
The bomb core can be pried out and manually detonated with other explosives."
- progression_minimum = 40 MINUTES
+ progression_minimum = 30 MINUTES
item = /obj/item/sbeacondrop/bomb
cost = 11
diff --git a/code/modules/uplink/uplink_items/implant.dm b/code/modules/uplink/uplink_items/implant.dm
index fa616bc4e4f..34fc9eedb0f 100644
--- a/code/modules/uplink/uplink_items/implant.dm
+++ b/code/modules/uplink/uplink_items/implant.dm
@@ -1,5 +1,3 @@
-// File ordered based on progression
-
/datum/uplink_category/implants
name = "Implants"
weight = 2
@@ -9,7 +7,6 @@
category = /datum/uplink_category/implants
surplus = 50
-// No progression cost
/datum/uplink_item/implants/freedom
name = "Freedom Implant"
desc = "An implant injected into the body and later activated at the user's will. It will attempt to free the \
diff --git a/code/modules/uplink/uplink_items/job.dm b/code/modules/uplink/uplink_items/job.dm
index b5088f9c9db..fe66edd0fea 100644
--- a/code/modules/uplink/uplink_items/job.dm
+++ b/code/modules/uplink/uplink_items/job.dm
@@ -1,5 +1,3 @@
-// File organised based on progression
-
/datum/uplink_category/role_restricted
name = "Role-Restricted"
weight = 1
@@ -8,7 +6,6 @@
category = /datum/uplink_category/role_restricted
purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
-// No progression cost
/datum/uplink_item/role_restricted/haunted_magic_eightball
name = "Haunted Magic Eightball"
desc = "Most magic eightballs are toys with dice inside. Although identical in appearance to the harmless toys, this occult device reaches into the spirit world to find its answers. \
@@ -53,12 +50,9 @@
restricted_roles = list(JOB_ASSISTANT)
surplus = 0
-// Low progression cost
-
/datum/uplink_item/role_restricted/clownpin
name = "Ultra Hilarious Firing Pin"
desc = "A firing pin that, when inserted into a gun, makes that gun only usable by clowns and clumsy people and makes that gun honk whenever anyone tries to fire it."
- progression_minimum = 5 MINUTES
cost = 4
item = /obj/item/firing_pin/clown/ultra
restricted_roles = list(JOB_CLOWN)
@@ -68,7 +62,6 @@
/datum/uplink_item/role_restricted/clownsuperpin
name = "Super Ultra Hilarious Firing Pin"
desc = "Like the ultra hilarious firing pin, except the gun you insert this pin into explodes when someone who isn't clumsy or a clown tries to fire it."
- progression_minimum = 5 MINUTES
cost = 7
item = /obj/item/firing_pin/clown/ultra/selfdestruct
restricted_roles = list(JOB_CLOWN)
@@ -78,7 +71,6 @@
/datum/uplink_item/role_restricted/syndimmi
name = "Syndicate Brand MMI"
desc = "An MMI modified to give cyborgs laws to serve the Syndicate without having their interface damaged by Cryptographic Sequencers, this will not unlock their hidden modules."
- progression_minimum = 10 MINUTES
item = /obj/item/mmi/syndie
cost = 2
restricted_roles = list(JOB_ROBOTICIST, JOB_RESEARCH_DIRECTOR, JOB_SCIENTIST, JOB_MEDICAL_DOCTOR, JOB_CHIEF_MEDICAL_OFFICER)
@@ -88,7 +80,6 @@
name = "Exploding Hot Potato"
desc = "A potato rigged with explosives. On activation, a special mechanism is activated that prevents it from being dropped. \
The only way to get rid of it if you are holding it is to attack someone else with it, causing it to latch to that person instead."
- progression_minimum = 10 MINUTES
item = /obj/item/hot_potato/syndicate
cost = 4
restricted_roles = list(JOB_COOK, JOB_BOTANIST, JOB_CLOWN, JOB_MIME)
@@ -97,7 +88,7 @@
name = "Combat Bakery Kit"
desc = "A kit of clandestine baked weapons. Contains a baguette which a skilled mime could use as a sword, \
a pair of throwing croissants, and the recipe to make more on demand. Once the job is done, eat the evidence."
- progression_minimum = 20 MINUTES
+ progression_minimum = 15 MINUTES
item = /obj/item/storage/box/syndie_kit/combat_baking
cost = 7
restricted_roles = list(JOB_COOK, JOB_MIME)
@@ -106,7 +97,6 @@
name = "EZ Clean Grenade Bundle"
desc = "A box with three cleaner grenades using the trademark Waffle Co. formula. Serves as a cleaner and causes acid damage to anyone standing nearby. \
The acid only affects carbon-based creatures."
- progression_minimum = 10 MINUTES
item = /obj/item/storage/box/syndie_kit/ez_clean
cost = 6
surplus = 20
@@ -117,7 +107,6 @@
desc = "An ingenious execution device worn on (or forced onto) the head. Arming it starts a 1-minute kitchen timer mounted on the bear trap. When it goes off, the trap's jaws will \
violently open, instantly killing anyone wearing it by tearing their jaws in half. To arm, attack someone with it while they're not wearing headgear, and you will force it onto their \
head after three seconds uninterrupted."
- progression_minimum = 10 MINUTES
cost = 5
item = /obj/item/reverse_bear_trap
restricted_roles = list(JOB_CLOWN)
@@ -125,7 +114,6 @@
/datum/uplink_item/role_restricted/modified_syringe_gun
name = "Modified Syringe Gun"
desc = "A syringe gun that fires DNA injectors instead of normal syringes."
- progression_minimum = 15 MINUTES
item = /obj/item/gun/syringe/dna
cost = 14
restricted_roles = list(JOB_GENETICIST, JOB_RESEARCH_DIRECTOR)
@@ -133,7 +121,6 @@
/datum/uplink_item/role_restricted/meathook
name = "Butcher's Meat Hook"
desc = "A brutal cleaver on a long chain, it allows you to pull people to your location."
- progression_minimum = 15 MINUTES
item = /obj/item/gun/magic/hook
cost = 11
restricted_roles = list(JOB_COOK)
@@ -141,7 +128,6 @@
/datum/uplink_item/role_restricted/turretbox
name = "Disposable Sentry Gun"
desc = "A disposable sentry gun deployment system cleverly disguised as a toolbox, apply wrench for functionality."
- progression_minimum = 15 MINUTES
item = /obj/item/storage/toolbox/emergency/turret
cost = 11
restricted_roles = list(JOB_STATION_ENGINEER)
@@ -150,7 +136,7 @@
name = "Magillitis Serum Autoinjector"
desc = "A single-use autoinjector which contains an experimental serum that causes rapid muscular growth in Hominidae. \
Side-affects may include hypertrichosis, violent outbursts, and an unending affinity for bananas."
- progression_minimum = 20 MINUTES
+ progression_minimum = 10 MINUTES
item = /obj/item/reagent_containers/hypospray/medipen/magillitis
cost = 15
restricted_roles = list(JOB_GENETICIST, JOB_RESEARCH_DIRECTOR)
@@ -159,18 +145,15 @@
name = "Box of Gorilla Cubes"
desc = "A box with three Waffle Co. brand gorilla cubes. Eat big to get big. \
Caution: Product may rehydrate when exposed to water."
- progression_minimum = 20 MINUTES
+ progression_minimum = 15 MINUTES
item = /obj/item/storage/box/gorillacubes
cost = 6
restricted_roles = list(JOB_GENETICIST, JOB_RESEARCH_DIRECTOR)
-// Medium progression cost
-
/datum/uplink_item/role_restricted/brainwash_disk
name = "Brainwashing Surgery Program"
desc = "A disk containing the procedure to perform a brainwashing surgery, allowing you to implant an objective onto a target. \
Insert into an Operating Console to enable the procedure."
- progression_minimum = 25 MINUTES
item = /obj/item/disk/surgery/brainwashing
restricted_roles = list(JOB_MEDICAL_DOCTOR, JOB_CHIEF_MEDICAL_OFFICER, JOB_ROBOTICIST)
cost = 5
@@ -213,7 +196,7 @@
name = "Guide to Advanced Mimery Series"
desc = "The classical two part series on how to further hone your mime skills. Upon studying the series, the user should be able to make 3x1 invisible walls, and shoot bullets out of their fingers. \
Obviously only works for Mimes."
- progression_minimum = 30 MINUTES
+ progression_minimum = 20 MINUTES
cost = 12
item = /obj/item/storage/box/syndie_kit/mimery
restricted_roles = list(JOB_MIME)
@@ -222,7 +205,7 @@
/datum/uplink_item/role_restricted/laser_arm
name = "Laser Arm Implant"
desc = "An implant that grants you a recharging laser gun inside your arm. Weak to EMPs. Comes with a syndicate autosurgeon for immediate self-application."
- progression_minimum = 30 MINUTES
+ progression_minimum = 20 MINUTES
cost = 10
item = /obj/item/autosurgeon/syndicate/laser_arm
restricted_roles = list(JOB_ROBOTICIST, JOB_RESEARCH_DIRECTOR)
@@ -231,7 +214,7 @@
/datum/uplink_item/role_restricted/chemical_gun
name = "Reagent Dartgun"
desc = "A heavily modified syringe gun which is capable of synthesizing its own chemical darts using input reagents. Can hold 90u of reagents."
- progression_minimum = 30 MINUTES
+ progression_minimum = 20 MINUTES
item = /obj/item/gun/chem
cost = 12
restricted_roles = list(JOB_CHEMIST, JOB_CHIEF_MEDICAL_OFFICER, JOB_BOTANIST)
@@ -239,7 +222,7 @@
/datum/uplink_item/role_restricted/pie_cannon
name = "Banana Cream Pie Cannon"
desc = "A special pie cannon for a special clown, this gadget can hold up to 20 pies and automatically fabricates one every two seconds!"
- progression_minimum = 30 MINUTES
+ progression_minimum = 10 MINUTES
cost = 10
item = /obj/item/pneumatic_cannon/pie/selfcharge
restricted_roles = list(JOB_CLOWN)
@@ -251,7 +234,7 @@
movement. The bomb is bulky and cannot be moved; upon ordering this item, a smaller beacon will be \
transported to you that will teleport the actual bomb to it upon activation. Note that this bomb can \
be defused, and some crew may attempt to do so."
- progression_minimum = 30 MINUTES
+ progression_minimum = 15 MINUTES
item = /obj/item/sbeacondrop/clownbomb
cost = 15
restricted_roles = list(JOB_CLOWN)
@@ -269,12 +252,30 @@
someone saves them or they manage to crawl out. Be sure not to ram into any walls or vending machines, as the springloaded seats \
are very sensitive. Now with our included lube defense mechanism which will protect you against any angry shitcurity! \
Premium features can be unlocked with a cryptographic sequencer!"
- progression_minimum = 30 MINUTES
+ // It has a low progression cost because it's the sort of item that only works well early in the round
+ // Plus, it costs all your TC, and it's not an instant kill tool.
+ progression_minimum = 5 MINUTES
item = /obj/vehicle/sealed/car/clowncar
cost = 20
restricted_roles = list(JOB_CLOWN)
surplus = 10
+/datum/uplink_item/role_restricted/his_grace
+ name = "His Grace"
+ desc = "An incredibly dangerous weapon recovered from a station overcome by the grey tide. Once activated, He will thirst for blood and must be used to kill to sate that thirst. \
+ His Grace grants gradual regeneration and complete stun immunity to His wielder, but be wary: if He gets too hungry, He will become impossible to drop and eventually kill you if not fed. \
+ However, if left alone for long enough, He will fall back to slumber. \
+ To activate His Grace, simply unlatch Him."
+ // It has a low progression cost because it's the sort of item that only works well early in the round
+ // Plus, it costs all your TC and will lock your uplink.
+ progression_minimum = 5 MINUTES
+ lock_other_purchases = TRUE
+ cant_discount = TRUE
+ item = /obj/item/his_grace
+ cost = 20
+ surplus = 0
+ restricted_roles = list(JOB_CHAPLAIN)
+
/datum/uplink_item/role_restricted/concealed_weapon_bay
name = "Concealed Weapon Bay"
desc = "A modification for non-combat exosuits that allows them to equip one piece of equipment designed for combat units. \
@@ -288,30 +289,13 @@
surplus = 15
/* //SKYRAT REMOVAL START
-/datum/uplink_item/role_restricted/his_grace
- name = "His Grace"
- desc = "An incredibly dangerous weapon recovered from a station overcome by the grey tide. Once activated, He will thirst for blood and must be used to kill to sate that thirst. \
- His Grace grants gradual regeneration and complete stun immunity to His wielder, but be wary: if He gets too hungry, He will become impossible to drop and eventually kill you if not fed. \
- However, if left alone for long enough, He will fall back to slumber. \
- To activate His Grace, simply unlatch Him."
- progression_minimum = 30 MINUTES
- item = /obj/item/his_grace
- cost = 20
- restricted_roles = list(JOB_CHAPLAIN)
- surplus = 5
-*/ //SKYRAT REMOVAL END
-
-// High progression cost
-
-//SKYRAT EDIT REMOVAL BEGIN
-/*
/datum/uplink_item/role_restricted/spider_injector
name = "Australicus Slime Mutator"
desc = "Crikey mate, it's been a wild travel from the Australicus sector but we've managed to get \
some special spider extract from the giant spiders down there. Use this injector on a gold slime core \
to create a few of the same type of spiders we found on the planets over there. They're a bit tame until you \
also give them a bit of sentience though."
- progression_minimum = 40 MINUTES
+ progression_minimum = 30 MINUTES
item = /obj/item/reagent_containers/syringe/spider_extract
cost = 10
restricted_roles = list(JOB_RESEARCH_DIRECTOR, JOB_SCIENTIST, JOB_ROBOTICIST)
@@ -325,7 +309,7 @@
withstand extreme pressure and temperatures, and has a mechanical trigger for triggering the transfer valve. Essentially, it turns the explosive force of a bomb into a narrow-angle \
blast wave \"projectile\". Aspiring scientists may find this highly useful, as forcing the pressure shockwave into a narrow angle seems to be able to bypass whatever quirk of physics \
disallows explosive ranges above a certain distance, allowing for the device to use the theoretical yield of a transfer valve bomb, instead of the factual yield. It's simple design makes it easy to conceal."
- progression_minimum = 45 MINUTES
+ progression_minimum = 30 MINUTES
item = /obj/item/gun/blastcannon
cost = 14 //High cost because of the potential for extreme damage in the hands of a skilled scientist.
restricted_roles = list(JOB_RESEARCH_DIRECTOR, JOB_SCIENTIST)
diff --git a/code/modules/uplink/uplink_items/stealthy.dm b/code/modules/uplink/uplink_items/stealthy.dm
index 6ee8b5fc0ca..54c9bbe9adc 100644
--- a/code/modules/uplink/uplink_items/stealthy.dm
+++ b/code/modules/uplink/uplink_items/stealthy.dm
@@ -1,4 +1,3 @@
-// File organised based on progression
/datum/uplink_category/stealthy
name = "Stealthy Weapons"
weight = 8
@@ -6,7 +5,6 @@
/datum/uplink_item/stealthy_weapons
category = /datum/uplink_category/stealthy
-// No progression cost
/datum/uplink_item/stealthy_weapons/dart_pistol
name = "Dart Pistol"
@@ -61,7 +59,6 @@
cost = 4
purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
-// Low progression cost
/datum/uplink_item/stealthy_weapons/origami_kit
name = "Boxed Origami Kit"
@@ -73,7 +70,6 @@
surplus = 0
purchasable_from = ~UPLINK_NUKE_OPS //clown ops intentionally left in, because that seems like some s-tier shenanigans.
-// Medium progression cost
/datum/uplink_item/stealthy_weapons/martialarts
name = "Martial Arts Scroll"
diff --git a/code/modules/uplink/uplink_items/stealthy_tools.dm b/code/modules/uplink/uplink_items/stealthy_tools.dm
index 2d96fadd622..48001edb955 100644
--- a/code/modules/uplink/uplink_items/stealthy_tools.dm
+++ b/code/modules/uplink/uplink_items/stealthy_tools.dm
@@ -1,5 +1,3 @@
-// File ordered based on progression
-
/datum/uplink_category/stealthy_tools
name = "Stealth Gadgets"
weight = 4
@@ -7,7 +5,6 @@
/datum/uplink_item/stealthy_tools
category = /datum/uplink_category/stealthy_tools
-// No progression cost
/datum/uplink_item/stealthy_tools/agent_card
name = "Agent Identification Card"
@@ -95,14 +92,12 @@
surplus = 30
illegal_tech = FALSE
-// High progression cost
-
/datum/uplink_item/stealthy_tools/telecomm_blackout
name = "Disable Telecomms"
desc = "When purchased, a virus will be uploaded to the telecommunication processing servers to temporarily disable themselves."
item = /obj/effect/gibspawner/generic
surplus = 0
- progression_minimum = 30 MINUTES
+ progression_minimum = 15 MINUTES
limited_stock = 1
cost = 4
restricted = TRUE
@@ -118,7 +113,7 @@
desc = "When purchased, a virus will be uploaded to the engineering processing servers to force a routine power grid check, forcing all APCs on the station to be temporarily disabled."
item = /obj/effect/gibspawner/generic
surplus = 0
- progression_minimum = 40 MINUTES
+ progression_minimum = 20 MINUTES
limited_stock = 1
cost = 6
restricted = TRUE
diff --git a/code/modules/uplink/uplink_items/suits.dm b/code/modules/uplink/uplink_items/suits.dm
index 1a7394f367a..b264e088530 100644
--- a/code/modules/uplink/uplink_items/suits.dm
+++ b/code/modules/uplink/uplink_items/suits.dm
@@ -33,7 +33,6 @@
item = /obj/item/storage/box/syndie_kit/space
cost = 4
-// Low progression cost
/datum/uplink_item/suits/modsuit
name = "Syndicate MODsuit"
@@ -41,37 +40,37 @@
item = /obj/item/mod/control/pre_equipped/traitor
cost = 8
purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS) //you can't buy it in nuke, because the elite modsuit costs the same while being better
- progression_minimum = 20 MINUTES
/datum/uplink_item/suits/thermal
name = "MODsuit Thermal Visor Module"
desc = "A visor for a MODsuit. Lets you see living beings through walls."
item = /obj/item/mod/module/visor/thermal
- progression_minimum = 20 MINUTES
cost = 3
/datum/uplink_item/suits/night
name = "MODsuit Night Visor Module"
desc = "A visor for a MODsuit. Lets you see clearer in the dark."
item = /obj/item/mod/module/visor/night
- progression_minimum = 20 MINUTES
cost = 2
/datum/uplink_item/suits/chameleon
name = "MODsuit Chameleon Module"
desc = "A MODsuit module that lets the suit disguise itself as other objects."
item = /obj/item/mod/module/chameleon
- progression_minimum = 20 MINUTES
cost = 2
/datum/uplink_item/suits/plate_compression
name = "MODsuit Plate Compression Module"
desc = "A MODsuit module that lets the suit compress into a smaller size. Not compatible with storage modules or the Infiltrator MODsuit."
item = /obj/item/mod/module/plate_compression
- progression_minimum = 20 MINUTES
cost = 2
-// Very high progression cost
+
+/datum/uplink_item/suits/noslip
+ name = "MODsuit Anti-Slip Module"
+ desc = "A MODsuit module preventing the user from slipping on water."
+ item = /obj/item/mod/module/noslip
+ cost = 2
/datum/uplink_item/suits/modsuit/elite_traitor
name = "Elite Syndicate MODsuit"
diff --git a/tgstation.dme b/tgstation.dme
index 5531cc418f6..8c76fd771c5 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -2711,6 +2711,7 @@
#include "code\modules\antagonists\traitor\objectives\sabotage_machinery.dm"
#include "code\modules\antagonists\traitor\objectives\sleeper_protocol.dm"
#include "code\modules\antagonists\traitor\objectives\steal.dm"
+#include "code\modules\antagonists\traitor\objectives\abstract\target_player.dm"
#include "code\modules\antagonists\traitor\objectives\final_objective\battlecruiser.dm"
#include "code\modules\antagonists\traitor\objectives\final_objective\final_objective.dm"
#include "code\modules\antagonists\traitor\objectives\final_objective\infect_ai.dm"
diff --git a/tgui/packages/tgui/interfaces/Uplink/index.tsx b/tgui/packages/tgui/interfaces/Uplink/index.tsx
index 2ae997f15eb..ebd72fae1e3 100644
--- a/tgui/packages/tgui/interfaces/Uplink/index.tsx
+++ b/tgui/packages/tgui/interfaces/Uplink/index.tsx
@@ -5,7 +5,7 @@ import { Component, Fragment } from 'inferno';
import { fetchRetry } from '../../http';
import { resolveAsset } from '../../assets';
import { BooleanLike } from 'common/react';
-import { Box, Tabs, Button, Stack, Section, Tooltip } from '../../components';
+import { Box, Tabs, Button, Stack, Section, Tooltip, Dimmer } from '../../components';
import { PrimaryObjectiveMenu } from './PrimaryObjectiveMenu';
import { Objective, ObjectiveMenu } from './ObjectiveMenu';
import { calculateProgression, calculateReputationLevel, reputationDefault, reputationLevelsTooltip } from './calculateReputationLevel';
@@ -24,6 +24,7 @@ type UplinkItem = {
restricted_species: string;
progression_minimum: number;
cost_override_string: string;
+ lock_other_purchases: BooleanLike;
ref?: string;
};
@@ -56,6 +57,8 @@ type UplinkData = {
active_objectives: Objective[];
maximum_active_objectives: number;
maximum_potential_objectives: number;
+ purchased_items: number;
+ shop_locked: BooleanLike;
};
type UplinkState = {
@@ -171,6 +174,8 @@ export class Uplink extends Component<{}, UplinkState> {
extra_purchasable_stock,
current_stock,
lockable,
+ purchased_items,
+ shop_locked,
} = data;
const { allItems, allCategories, currentTab } = this.state as UplinkState;
@@ -200,7 +205,19 @@ export class Uplink extends Component<{}, UplinkState> {
id: item.id,
name: item.name,
category: item.category,
- desc: {item.desc},
+ desc: (
+
+ {item.desc}
+ {(item.lock_other_purchases && (
+
+ Taking this item will lock you from further purchasing from the
+ marketplace. Additionally, if you have already purchased an
+ item, you will not be able to purchase this.
+
+ )) ||
+ null}
+
+ ),
cost: (
{item.cost_override_string || `${item.cost} TC`}
@@ -216,7 +233,10 @@ export class Uplink extends Component<{}, UplinkState> {
)}
),
- disabled: !canBuy || (has_progression && !hasEnoughProgression),
+ disabled:
+ !canBuy ||
+ (has_progression && !hasEnoughProgression) ||
+ (item.lock_other_purchases && purchased_items > 0),
extraData: {
ref: item.ref,
},
@@ -395,19 +415,34 @@ export class Uplink extends Component<{}, UplinkState> {
handleRequestObjectives={() => act('regenerate_objectives')}
/>
)) || (
- {
- const extraDataItem = item as Item;
- if (!extraDataItem.extraData?.ref) {
- act('buy', { path: item.id });
- } else {
- act('buy', { ref: extraDataItem.extraData.ref });
- }
- }}
- />
+
+ {
+ const extraDataItem = item as Item;
+ if (!extraDataItem.extraData?.ref) {
+ act('buy', { path: item.id });
+ } else {
+ act('buy', { ref: extraDataItem.extraData.ref });
+ }
+ }}
+ />
+ {(shop_locked && (
+
+
+ SHOP LOCKED
+
+
+ )) ||
+ null}
+
)}