+ Tasks
+
+ Assassinate Burnard Silkwind, the roboticist.
+ Additional Details
+
+ This mission is part of your assignment and must be
+ completed. No additional reward will be provided outside of the
+ terms that have been defined within your contract of employment.
+
+
+
+
+
+
+
+
+
+ No Reward
+
+
+
+
+
+
+
+
diff --git a/tgui/packages/tgui/styles/components/Uplink.scss b/tgui/packages/tgui/styles/components/Uplink.scss
index 5d0505c7100c8..feef11f78596c 100644
--- a/tgui/packages/tgui/styles/components/Uplink.scss
+++ b/tgui/packages/tgui/styles/components/Uplink.scss
@@ -1,5 +1,6 @@
@use '../base.scss';
+@use '../colors.scss';
.directives {
height: calc(100% - 50px);
@@ -55,5 +56,82 @@
top: 0;
height: 100%;
/** Aspect ratio container **/
- padding-right: 50%;
+ padding-right: calc(50%);
+}
+
+.directive_info {
+ position: absolute;
+ left: 0;
+ top: 0;
+ bottom: 0;
+ right: calc(50% + 10px);
+}
+
+.directive_section {
+ position: relative;
+ height: 100%;
+}
+
+.directive_prize {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ background-color: #00000060;
+ display: flex;
+ flex-direction: row;
+ vertical-align: middle;
+ padding: 5px;
+ border-top: 2px solid colors.$primary;
+ border-bottom: 2px solid colors.$primary;
+ border-radius: 4px;
+}
+
+.directive_prize_info {
+ display: flex;
+ flex-direction: row;
+ min-width: 30%;
+ vertical-align: middle;
+ height: 40px;
+ text-align: center;
+ align-items: center;
+ padding: 10px;
+}
+
+.bell_ring {
+ transform-origin: top;
+ animation: ring 1.5s infinite;
+}
+
+@keyframes ring {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 10% {
+ transform: rotate(23deg);
+ }
+ 20% {
+ transform: rotate(-18deg);
+ }
+ 30% {
+ transform: rotate(15deg);
+ }
+ 40% {
+ transform: rotate(-12deg);
+ }
+ 50% {
+ transform: rotate(12deg);
+ }
+ 60% {
+ transform: rotate(-10deg);
+ }
+ 70% {
+ transform: rotate(10deg);
+ }
+ 80% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(0deg);
+ }
}
diff --git a/tgui/packages/tgui/styles/themes/syndicate.scss b/tgui/packages/tgui/styles/themes/syndicate.scss
index 7caf7ab0169de..97009ffbee980 100644
--- a/tgui/packages/tgui/styles/themes/syndicate.scss
+++ b/tgui/packages/tgui/styles/themes/syndicate.scss
@@ -36,6 +36,7 @@
@include meta.load-css('../components/NumberInput.scss', $with: ('border-color': #87ce87));
@include meta.load-css('../components/ProgressBar.scss', $with: ('background-color': rgba(0, 0, 0, 0.5)));
@include meta.load-css('../components/Section.scss');
+ @include meta.load-css('../components/Uplink.scss');
@include meta.load-css('../components/Tooltip.scss', $with: ('background-color': #4a0202));
// Layouts
From ba85b19536b7a451b93c288dadb27e4b55eeaa48 Mon Sep 17 00:00:00 2001
From: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com>
Date: Wed, 13 Mar 2024 20:52:09 +0000
Subject: [PATCH 08/62] Updates directives to work with uplinks instead of
antag datums
---
.../subsystem/priority_directives.dm | 15 ++++++++++-----
.../traitor/directives/priority_directive.dm | 8 ++++----
.../secure_deaddrop/secure_deaddrop.dm | 18 ++++++++++--------
3 files changed, 24 insertions(+), 17 deletions(-)
diff --git a/code/controllers/subsystem/priority_directives.dm b/code/controllers/subsystem/priority_directives.dm
index 85abe871e27a2..061f0504b603c 100644
--- a/code/controllers/subsystem/priority_directives.dm
+++ b/code/controllers/subsystem/priority_directives.dm
@@ -1,7 +1,7 @@
SUBSYSTEM_DEF(directives)
name = "Priority Directives"
wait = 10 SECONDS
- var/datum/priority_directive/directive = null
+ var/datum/priority_directive/active_directive = null
var/next_directive_time
var/list/directives = list()
@@ -12,12 +12,12 @@ SUBSYSTEM_DEF(directives)
directives += new directive_type()
/datum/controller/subsystem/directives/fire(resumed)
- if (directive)
+ if (active_directive)
// Are we completed or ended?
- if (directive.is_completed() || world.time > directive.end_at)
- directive.finish()
+ if (active_directive.is_completed() || world.time > active_directive.end_at)
+ active_directive.finish()
return
- // Check if we are ready to spawn our next directive
+ // Check if we are ready to spawn our next active_directive
if (world.time < next_directive_time)
return
// Find all the antags
@@ -46,3 +46,8 @@ SUBSYSTEM_DEF(directives)
var/datum/priority_directive/selected = pick(valid_directives)
selected.generate(antag_datums, player_minds)
next_directive_time = INFINITY
+ active_directive = selected
+
+/datum/controller/subsystem/directives/proc/get_uplink_data(datum/component/uplink/uplink)
+ var/data = list()
+ return data
diff --git a/code/modules/antagonists/traitor/directives/priority_directive.dm b/code/modules/antagonists/traitor/directives/priority_directive.dm
index 0c864f0dfa79f..402642658d2c1 100644
--- a/code/modules/antagonists/traitor/directives/priority_directive.dm
+++ b/code/modules/antagonists/traitor/directives/priority_directive.dm
@@ -11,10 +11,10 @@
/datum/priority_directive/proc/check()
SHOULD_NOT_OVERRIDE(TRUE)
-/datum/priority_directive/proc/allocate_teams(list/antag_datums, list/player_minds)
+/datum/priority_directive/proc/allocate_teams(list/uplinks, list/player_minds)
/// Return the reward type and amount
-/datum/priority_directive/proc/generate(list/antag_datums, list/player_minds)
+/datum/priority_directive/proc/generate(list/uplinks, list/player_minds)
/// Check if we have finished early. We always complete after a set time period.
/datum/priority_directive/proc/is_completed()
@@ -23,14 +23,14 @@
/datum/priority_directive/proc/finish()
/// Activate the directive, requires a list of traitor datums and security minsd
-/datum/priority_directive/proc/activate(list/antag_datums, list/player_minds)
+/datum/priority_directive/proc/activate(list/uplinks, list/player_minds)
SHOULD_NOT_OVERRIDE(TRUE)
/// Advertise this directive to security objectives consoles
/datum/priority_directive/proc/advertise_security()
SHOULD_NOT_OVERRIDE(TRUE)
-/datum/priority_directive/proc/add_antagonist_team(list/antag_datums)
+/datum/priority_directive/proc/add_antagonist_team(list/uplinks)
SHOULD_NOT_OVERRIDE(TRUE)
/// Reject this directive, prevent it from firing
diff --git a/code/modules/antagonists/traitor/directives/secure_deaddrop/secure_deaddrop.dm b/code/modules/antagonists/traitor/directives/secure_deaddrop/secure_deaddrop.dm
index 86d973e93f388..1394e6450891a 100644
--- a/code/modules/antagonists/traitor/directives/secure_deaddrop/secure_deaddrop.dm
+++ b/code/modules/antagonists/traitor/directives/secure_deaddrop/secure_deaddrop.dm
@@ -5,29 +5,31 @@
box which will automatically unlock after a set period of time."
var/obj/item/storage/deaddrop_box/target
-/datum/priority_directive/deaddrop/allocate_teams(list/antag_datums, list/player_minds)
+/datum/priority_directive/deaddrop/allocate_teams(list/uplinks, list/player_minds)
if (length(antag_datums) <= 1)
reject()
return
- for (var/datum/antagonist/antag in antag_datums)
+ for (var/datum/component/uplink/antag in antag_datums)
// Create individual teams
add_antagonist_team(antag)
-/datum/priority_directive/deaddrop/generate(list/antag_datums, list/player_minds)
+/datum/priority_directive/deaddrop/generate(list/uplinks, list/player_minds)
// Spawn the deaddrop package
target = new()
var/tc_count = tc_curve(get_independent_difficulty())
new /obj/item/stack/sheet/telecrystal(target, tc_count)
// Put the deaddrop somewhere
- var/list/antag_minds = list()
- for (var/datum/antagonist/antag in antag_datums)
- antag_minds += antag.owner
- new /datum/component/stash(antag_minds, target)
+ var/turf/selected = get_random_station_turf()
+ while (!istype(selected, /turf/open/floor/plasteel))
+ selected = get_random_station_turf()
+ var/secret_bag = new /obj/item/storage/backpack/satchel/flat(selected)
+ SEND_SIGNAL(secret_bag, COMSIG_OBJ_HIDE, selected.underfloor_accessibility < UNDERFLOOR_VISIBLE)
+ new /obj/item/storage/deaddrop_box(secret_bag)
// Return the reward generated
return list(
/obj/item/stack/sheet/telecrystal = tc_count,
)
-/datum/priority_directive/deaddrop/finish(list/antag_datums, list/player_minds)
+/datum/priority_directive/deaddrop/finish(list/uplinks, list/player_minds)
. = ..()
target.unlock()
From d82a58dd6b0008a30665a48290f0061887973c8d Mon Sep 17 00:00:00 2001
From: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com>
Date: Thu, 14 Mar 2024 08:16:05 +0000
Subject: [PATCH 09/62] A bit of morning integration coding
---
.../subsystem/priority_directives.dm | 23 ++++++++
code/datums/components/uplink.dm | 20 ++++---
code/datums/mind.dm | 4 +-
code/game/gamemodes/nuclear/nuclear.dm | 2 +-
.../objects/items/implants/implantuplink.dm | 4 +-
.../antagonists/_common/antag_datum.dm | 4 ++
code/modules/antagonists/brother/brother.dm | 1 +
.../antagonists/incursion/incursion.dm | 1 +
code/modules/antagonists/nukeop/nukeop.dm | 1 +
.../traitor/backstory/traitor_backstory_ui.dm | 8 +--
.../backstory/traitor_datum_backstory.dm | 4 +-
.../antagonists/traitor/datum_traitor.dm | 17 +++---
.../traitor/directives/priority_directive.dm | 5 +-
.../secure_deaddrop/secure_deaddrop.dm | 9 +--
code/modules/clothing/outfits/vr.dm | 2 +-
code/modules/uplink/uplink_devices.dm | 20 +++----
tgui/packages/tgui/interfaces/Uplink.js | 59 ++++++++++---------
17 files changed, 113 insertions(+), 71 deletions(-)
diff --git a/code/controllers/subsystem/priority_directives.dm b/code/controllers/subsystem/priority_directives.dm
index 061f0504b603c..474eb8887441f 100644
--- a/code/controllers/subsystem/priority_directives.dm
+++ b/code/controllers/subsystem/priority_directives.dm
@@ -50,4 +50,27 @@ SUBSYSTEM_DEF(directives)
/datum/controller/subsystem/directives/proc/get_uplink_data(datum/component/uplink/uplink)
var/data = list()
+ data["time"] = world.time
+ // The uplink can only detect syndicate assigned objectives of the owner
+ if (uplink.owner)
+ var/list/known_objectives = list()
+ for (var/datum/antagonist/antagonist_type in uplink.owner.antag_datums)
+ // The syndicate uplink is only aware of syndicate-given objectives.
+ if (antagonist_type.faction != FACTION_SYNDICATE)
+ continue
+ for (var/datum/objective/objective in antagonist_type.objectives)
+ known_objectives += list(list(
+ "name" = objective.name,
+ "tasks" = list(objective.explanation_text),
+ ))
+ // Add the priority directive
+ if (active_directive)
+ known_objectives += list(list(
+ "name" = active_directive.name,
+ "tasks" = list(active_directive.objective_explanation),
+ "time" = active_directive.end_at,
+ "details" = active_directive.details,
+ "reward" = active_directive.tc_reward
+ ))
+ data["objectives"] = known_objectives
return data
diff --git a/code/datums/components/uplink.dm b/code/datums/components/uplink.dm
index 88ed9e3048d4d..4413a91e22622 100644
--- a/code/datums/components/uplink.dm
+++ b/code/datums/components/uplink.dm
@@ -16,7 +16,8 @@
var/allow_restricted = TRUE
var/telecrystals
var/selected_cat
- var/owner = null
+ // Antag datum of the owner
+ var/datum/mind/owner = null
var/uplink_flag
var/datum/uplink_purchase_log/purchase_log
var/list/uplink_items
@@ -31,7 +32,7 @@
var/list/previous_attempts
-/datum/component/uplink/Initialize(_owner, _lockable = TRUE, _enabled = FALSE, uplink_flag = UPLINK_TRAITORS, starting_tc = TELECRYSTALS_DEFAULT)
+/datum/component/uplink/Initialize(datum/mind/_owner, _lockable = TRUE, _enabled = FALSE, uplink_flag = UPLINK_TRAITORS, starting_tc = TELECRYSTALS_DEFAULT)
if(!isitem(parent))
return COMPONENT_INCOMPATIBLE
@@ -53,10 +54,10 @@
if(_owner)
owner = _owner
LAZYINITLIST(GLOB.uplink_purchase_logs_by_key)
- if(GLOB.uplink_purchase_logs_by_key[owner])
- purchase_log = GLOB.uplink_purchase_logs_by_key[owner]
+ if(GLOB.uplink_purchase_logs_by_key[owner.key])
+ purchase_log = GLOB.uplink_purchase_logs_by_key[owner.key]
else
- purchase_log = new(owner, src)
+ purchase_log = new(owner.key, src)
lockable = _lockable
active = _enabled
src.uplink_flag = uplink_flag
@@ -167,6 +168,7 @@
data["telecrystals"] = telecrystals
data["lockable"] = lockable
data["compactMode"] = compact_mode
+ data["objectives"] = SSdirectives.get_uplink_data(src)
return data
/datum/component/uplink/ui_static_data(mob/user)
@@ -271,13 +273,13 @@
SIGNAL_HANDLER
var/mob/user = arguments[2]
- owner = user?.key
+ owner = user?.mind
if(owner && !purchase_log)
LAZYINITLIST(GLOB.uplink_purchase_logs_by_key)
- if(GLOB.uplink_purchase_logs_by_key[owner])
- purchase_log = GLOB.uplink_purchase_logs_by_key[owner]
+ if(GLOB.uplink_purchase_logs_by_key[owner.key])
+ purchase_log = GLOB.uplink_purchase_logs_by_key[owner.key]
else
- purchase_log = new(owner, src)
+ purchase_log = new(owner.key, src)
/datum/component/uplink/proc/old_implant(datum/source, list/arguments, obj/item/implant/new_implant)
SIGNAL_HANDLER
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index 2858aed2ac5f6..5943b37f872b3 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -353,7 +353,7 @@
if (!implant)
. = uplink_loc
- var/datum/component/uplink/U = uplink_loc.AddComponent(/datum/component/uplink, traitor_mob.key, TRUE, FALSE, gamemode, telecrystals)
+ var/datum/component/uplink/U = uplink_loc.AddComponent(/datum/component/uplink, traitor_mob?.mind, TRUE, FALSE, gamemode, telecrystals)
if(src.has_antag_datum(/datum/antagonist/incursion))
U.uplink_flag = UPLINK_INCURSION
if(src.has_antag_datum(/datum/antagonist/traitor/excommunicate))
@@ -375,7 +375,7 @@
else
traitor_mob.mind.store_memory(U.unlock_note)
else
- var/obj/item/implant/uplink/starting/I = new(traitor_mob)
+ var/obj/item/implant/uplink/starting/I = new(traitor_mob, traitor_mob)
I.implant(traitor_mob, null, silent = TRUE)
var/datum/component/uplink/U = I.GetComponent(/datum/component/uplink)
if(!silent)
diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm
index d637e46021bb4..34f92120e4b10 100644
--- a/code/game/gamemodes/nuclear/nuclear.dm
+++ b/code/game/gamemodes/nuclear/nuclear.dm
@@ -153,7 +153,7 @@
R.use_command = TRUE
if(ispath(uplink_type, /obj/item/uplink/nuclear) || tc) // /obj/item/uplink/nuclear understands 0 tc
- var/obj/item/U = new uplink_type(H, H.key, tc)
+ var/obj/item/U = new uplink_type(H, H, tc)
H.equip_to_slot_or_del(U, ITEM_SLOT_BACKPACK)
var/obj/item/implant/explosive/E = new/obj/item/implant/explosive(H)
diff --git a/code/game/objects/items/implants/implantuplink.dm b/code/game/objects/items/implants/implantuplink.dm
index f80782c77efc6..e975ef86665bf 100644
--- a/code/game/objects/items/implants/implantuplink.dm
+++ b/code/game/objects/items/implants/implantuplink.dm
@@ -9,9 +9,9 @@
/// The uplink flags of the implant uplink inside, only checked during initialisation so modifying it after initialisation will do nothing
var/uplink_flag = UPLINK_TRAITORS
-/obj/item/implant/uplink/Initialize(mapload, owner, uplink_flag)
+/obj/item/implant/uplink/Initialize(mapload, mob/owner, uplink_flag)
. = ..()
- AddComponent(/datum/component/uplink, _owner = owner, _lockable = TRUE, _enabled = FALSE, uplink_flag = uplink_flag, starting_tc = starting_tc)
+ AddComponent(/datum/component/uplink, _owner = owner?.mind, _lockable = TRUE, _enabled = FALSE, uplink_flag = uplink_flag, starting_tc = starting_tc)
RegisterSignal(src, COMSIG_COMPONENT_REMOVING, PROC_REF(_component_removal))
/**
diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm
index e4ab8352c804d..4652888fd608a 100644
--- a/code/modules/antagonists/_common/antag_datum.dm
+++ b/code/modules/antagonists/_common/antag_datum.dm
@@ -22,6 +22,10 @@ GLOBAL_LIST(admin_antag_list)
var/antag_memory = ""//These will be removed with antag datum
var/antag_moodlet //typepath of moodlet that the mob will gain with their status
var/ui_name = "AntagInfoGeneric"
+ /// What faction does the antag belong to, used to determine if faction specific items
+ /// such as uplinks can detect this datum's objectives for the cases where a syndicate
+ /// gets new objectives due to conversion.
+ var/faction = null
var/can_elimination_hijack = ELIMINATION_NEUTRAL //If these antags are alone when a shuttle elimination happens.
/// If above 0, this is the multiplier for the speed at which we hijack the shuttle. Do not directly read, use hijack_speed().
diff --git a/code/modules/antagonists/brother/brother.dm b/code/modules/antagonists/brother/brother.dm
index 538669d3e02ab..011ac6d2a69d2 100644
--- a/code/modules/antagonists/brother/brother.dm
+++ b/code/modules/antagonists/brother/brother.dm
@@ -2,6 +2,7 @@
name = "Brother"
antagpanel_category = "Brother"
banning_key = ROLE_BROTHER
+ faction = FACTION_SYNDICATE
required_living_playtime = 4
ui_name = "AntagInfoBrother"
hijack_speed = 0.5
diff --git a/code/modules/antagonists/incursion/incursion.dm b/code/modules/antagonists/incursion/incursion.dm
index 306168009d6c4..ce57e92b0fbf7 100644
--- a/code/modules/antagonists/incursion/incursion.dm
+++ b/code/modules/antagonists/incursion/incursion.dm
@@ -2,6 +2,7 @@
name = "Syndicate Incursion Member"
antagpanel_category = "Incursion"
banning_key = ROLE_INCURSION
+ faction = FACTION_SYNDICATE
required_living_playtime = 4
ui_name = "AntagInfoIncursion"
var/datum/team/incursion/team
diff --git a/code/modules/antagonists/nukeop/nukeop.dm b/code/modules/antagonists/nukeop/nukeop.dm
index b7dcbdbed6116..80b44d0706837 100644
--- a/code/modules/antagonists/nukeop/nukeop.dm
+++ b/code/modules/antagonists/nukeop/nukeop.dm
@@ -8,6 +8,7 @@
show_to_ghosts = TRUE
hijack_speed = 2 //If you can't take out the station, take the shuttle instead.
ui_name = "AntagInfoNukeOp"
+ faction = FACTION_SYNDICATE
var/datum/team/nuclear/nuke_team
var/always_new_team = FALSE //If not assigned a team by default ops will try to join existing ones, set this to TRUE to always create new team.
var/send_to_spawnpoint = TRUE //Should the user be moved to default spawnpoint.
diff --git a/code/modules/antagonists/traitor/backstory/traitor_backstory_ui.dm b/code/modules/antagonists/traitor/backstory/traitor_backstory_ui.dm
index c591b959fb63d..40253af776d18 100644
--- a/code/modules/antagonists/traitor/backstory/traitor_backstory_ui.dm
+++ b/code/modules/antagonists/traitor/backstory/traitor_backstory_ui.dm
@@ -33,8 +33,8 @@
data["recommended_backstories"] = recommended_backstories
if(istype(backstory))
data["backstory"] = "[backstory.type]"
- if(istype(faction))
- data["faction"] = faction.key
+ if(istype(traitor_faction))
+ data["faction"] = traitor_faction.key
data["employer"] = employer
var/datum/component/uplink/uplink = uplink_ref?.resolve()
@@ -90,13 +90,13 @@
var/datum/traitor_faction/selected_faction = GLOB.traitor_factions_to_datum[params["faction"]]
if(!istype(selected_faction) || !istype(selected_backstory))
return TRUE
- if(istype(faction) && faction.key != selected_faction.key) // bad!
+ if(istype(traitor_faction) && traitor_faction.key != selected_faction.key) // bad!
return TRUE
if(!(selected_faction.key in selected_backstory.allowed_factions))
return TRUE
if(!("[selected_backstory.type]" in allowed_backstories))
return TRUE
- if(!istype(faction))
+ if(!istype(traitor_faction))
set_faction(selected_faction)
set_backstory(selected_backstory)
return TRUE
diff --git a/code/modules/antagonists/traitor/backstory/traitor_datum_backstory.dm b/code/modules/antagonists/traitor/backstory/traitor_datum_backstory.dm
index 6a1f9a925e734..840c87245edcd 100644
--- a/code/modules/antagonists/traitor/backstory/traitor_datum_backstory.dm
+++ b/code/modules/antagonists/traitor/backstory/traitor_datum_backstory.dm
@@ -10,7 +10,7 @@
/// The actual backstory for this traitor. Can be null.
var/datum/traitor_backstory/backstory
/// The actual faction for this traitor. Can be null.
- var/datum/traitor_faction/faction
+ var/datum/traitor_faction/traitor_faction
/datum/antagonist/traitor/proc/setup_backstories(murderbone, hijack)
if(murderbone || hijack)
@@ -38,7 +38,7 @@
if(!istype(new_faction))
return
var/no_faction = isnull(faction)
- faction = new_faction
+ traitor_faction = new_faction
employer = new_faction.employer_name
if(no_faction)
if(new_faction.give_codewords)
diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm
index 2edf39ceb2c55..4ac79d941ea7d 100644
--- a/code/modules/antagonists/traitor/datum_traitor.dm
+++ b/code/modules/antagonists/traitor/datum_traitor.dm
@@ -8,6 +8,7 @@
banning_key = ROLE_TRAITOR
required_living_playtime = 4
antag_moodlet = /datum/mood_event/focused
+ faction = FACTION_SYNDICATE
hijack_speed = 0.5 //10 seconds per hijack stage by default
var/special_role = ROLE_TRAITOR
/// Shown when giving uplinks and codewords to the player
@@ -112,7 +113,7 @@
var/mob/living/silicon/ai/A = M
A.hack_software = TRUE
// Give codewords to the new mob on mind transfer.
- if(mob_override && istype(faction) && faction.give_codewords)
+ if(mob_override && istype(traitor_faction) && traitor_faction.give_codewords)
give_codewords(mob_override)
/datum/antagonist/traitor/remove_innate_effects(mob/living/mob_override)
@@ -122,18 +123,18 @@
if(istype(A) && traitor_kind == TRAITOR_AI)
A.hack_software = FALSE
// Remove codewords from the old mob on mind transfer.
- if(mob_override && istype(faction) && faction.give_codewords)
+ if(mob_override && istype(traitor_faction) && traitor_faction.give_codewords)
remove_codewords(mob_override)
/// Enables displaying codewords to this traitor.
/datum/antagonist/traitor/proc/give_codewords(mob/living/mob_override)
- if((!mob_override && !owner.current) || !istype(faction))
+ if((!mob_override && !owner.current) || !istype(traitor_faction))
return
has_codewords = TRUE
RegisterSignal(mob_override || owner.current, COMSIG_MOVABLE_HEAR, PROC_REF(handle_hearing))
/datum/antagonist/traitor/proc/remove_codewords(mob/living/mob_override)
- if((!mob_override && !owner.current) || !istype(faction))
+ if((!mob_override && !owner.current) || !istype(traitor_faction))
return
has_codewords = FALSE
UnregisterSignal(mob_override || owner.current, COMSIG_MOVABLE_HEAR, PROC_REF(handle_hearing))
@@ -187,8 +188,8 @@
/datum/antagonist/traitor/antag_panel_data()
// Traitor Backstory
var/backstory_text = "Traitor Backstory: "
- if(istype(faction))
- backstory_text += "Faction:\[ [faction.name][faction.description] \] "
+ if(istype(traitor_faction))
+ backstory_text += "Faction:\[ [traitor_faction.name][traitor_faction.description] \] "
else
backstory_text += "No faction selected! "
if(istype(backstory))
@@ -234,8 +235,8 @@
result += objectives_text
var/backstory_text = " "
- if(istype(faction))
- backstory_text += "Faction:\[ [faction.name][faction.description] \] "
+ if(istype(traitor_faction))
+ backstory_text += "Faction:\[ [traitor_faction.name][traitor_faction.description] \] "
if(istype(backstory))
backstory_text += "Backstory:\[ [backstory.name][backstory.description] \] "
else
diff --git a/code/modules/antagonists/traitor/directives/priority_directive.dm b/code/modules/antagonists/traitor/directives/priority_directive.dm
index 402642658d2c1..fb935f5e77880 100644
--- a/code/modules/antagonists/traitor/directives/priority_directive.dm
+++ b/code/modules/antagonists/traitor/directives/priority_directive.dm
@@ -1,8 +1,10 @@
/// This can only be running once at a time, do not run in parallel
/datum/priority_directive
var/name
- var/desc
+ var/objective_explanation
+ var/details
var/end_at
+ var/tc_reward = 0
var/rejected = FALSE
/datum/priority_directive/New()
@@ -25,6 +27,7 @@
/// Activate the directive, requires a list of traitor datums and security minsd
/datum/priority_directive/proc/activate(list/uplinks, list/player_minds)
SHOULD_NOT_OVERRIDE(TRUE)
+ end_at = world.time + 10 MINUTES
/// Advertise this directive to security objectives consoles
/datum/priority_directive/proc/advertise_security()
diff --git a/code/modules/antagonists/traitor/directives/secure_deaddrop/secure_deaddrop.dm b/code/modules/antagonists/traitor/directives/secure_deaddrop/secure_deaddrop.dm
index 1394e6450891a..bde9f154bbbb7 100644
--- a/code/modules/antagonists/traitor/directives/secure_deaddrop/secure_deaddrop.dm
+++ b/code/modules/antagonists/traitor/directives/secure_deaddrop/secure_deaddrop.dm
@@ -1,15 +1,16 @@
/datum/priority_directive/deaddrop
name = "Secure Deaddrop"
- desc = "We have identified a deaddrop that has been placed by a rival spy agency and have maintained an accurate track on the box. \
+ objective_explanation = "Secure a trackable lockbox which will unlock after 10 minutes."
+ details = "We have identified a deaddrop that has been placed by a rival spy agency and have maintained an accurate track on the box. \
You have the option to track and secure the valuable items before anyone else gets to them. The items are stored in a trackable \
box which will automatically unlock after a set period of time."
var/obj/item/storage/deaddrop_box/target
/datum/priority_directive/deaddrop/allocate_teams(list/uplinks, list/player_minds)
- if (length(antag_datums) <= 1)
+ if (length(uplinks) <= 1)
reject()
return
- for (var/datum/component/uplink/antag in antag_datums)
+ for (var/datum/component/uplink/antag in uplinks)
// Create individual teams
add_antagonist_team(antag)
@@ -22,7 +23,7 @@
var/turf/selected = get_random_station_turf()
while (!istype(selected, /turf/open/floor/plasteel))
selected = get_random_station_turf()
- var/secret_bag = new /obj/item/storage/backpack/satchel/flat(selected)
+ var/datum/secret_bag = new /obj/item/storage/backpack/satchel/flat(selected)
SEND_SIGNAL(secret_bag, COMSIG_OBJ_HIDE, selected.underfloor_accessibility < UNDERFLOOR_VISIBLE)
new /obj/item/storage/deaddrop_box(secret_bag)
// Return the reward generated
diff --git a/code/modules/clothing/outfits/vr.dm b/code/modules/clothing/outfits/vr.dm
index 4eba242b85c82..847360af5c4cb 100644
--- a/code/modules/clothing/outfits/vr.dm
+++ b/code/modules/clothing/outfits/vr.dm
@@ -27,7 +27,7 @@
/datum/outfit/vr/syndicate/post_equip(mob/living/carbon/human/H)
. = ..()
- var/obj/item/uplink/U = new /obj/item/uplink/nuclear_restricted(H, H.key, 80)
+ var/obj/item/uplink/U = new /obj/item/uplink/nuclear_restricted(H, H, 80)
H.equip_to_slot_or_del(U, ITEM_SLOT_BACKPACK)
var/obj/item/implant/weapons_auth/W = new/obj/item/implant/weapons_auth(H)
W.implant(H)
diff --git a/code/modules/uplink/uplink_devices.dm b/code/modules/uplink/uplink_devices.dm
index 84eb6ff8a9ede..6e4ebd8dbbb51 100644
--- a/code/modules/uplink/uplink_devices.dm
+++ b/code/modules/uplink/uplink_devices.dm
@@ -21,14 +21,14 @@
var/uplink_flag = UPLINK_TRAITORS
-/obj/item/uplink/Initialize(mapload, owner, tc_amount = 20)
+/obj/item/uplink/Initialize(mapload, mob/owner, tc_amount = 20)
. = ..()
- AddComponent(/datum/component/uplink, owner, FALSE, TRUE, uplink_flag, tc_amount)
+ AddComponent(/datum/component/uplink, owner?.mind, FALSE, TRUE, uplink_flag, tc_amount)
/obj/item/uplink/debug
name = "debug uplink"
-/obj/item/uplink/debug/Initialize(mapload, owner, tc_amount = 9000)
+/obj/item/uplink/debug/Initialize(mapload, mob/owner, tc_amount = 9000)
. = ..()
var/datum/component/uplink/hidden_uplink = GetComponent(/datum/component/uplink)
hidden_uplink.name = "debug uplink"
@@ -41,7 +41,7 @@
name = "debug nuclear uplink"
uplink_flag = UPLINK_NUKE_OPS
-/obj/item/uplink/nuclear/debug/Initialize(mapload, owner, tc_amount = 9000)
+/obj/item/uplink/nuclear/debug/Initialize(mapload, mob/owner, tc_amount = 9000)
. = ..()
var/datum/component/uplink/hidden_uplink = GetComponent(/datum/component/uplink)
hidden_uplink.name = "debug nuclear uplink"
@@ -62,17 +62,17 @@
name = "dusty radio"
desc = "A dusty looking radio."
-/obj/item/uplink/old/Initialize(mapload, owner, tc_amount = 10)
+/obj/item/uplink/old/Initialize(mapload, mob/owner, tc_amount = 10)
. = ..()
- var/datum/component/uplink/hidden_uplink = GetComponent(/datum/component/uplink)
+ var/datum/component/uplink/hidden_uplink = GetComponent(/datum/component/uplink, owner?.mind)
hidden_uplink.name = "dusty radio"
// Multitool uplink
-/obj/item/multitool/uplink/Initialize(mapload, owner, tc_amount = 20)
+/obj/item/multitool/uplink/Initialize(mapload, mob/owner, tc_amount = 20)
. = ..()
- AddComponent(/datum/component/uplink, owner, FALSE, TRUE, UPLINK_TRAITORS, tc_amount)
+ AddComponent(/datum/component/uplink, owner?.mind, FALSE, TRUE, UPLINK_TRAITORS, tc_amount)
// Pen uplink
-/obj/item/pen/uplink/Initialize(mapload, owner, tc_amount = 20)
+/obj/item/pen/uplink/Initialize(mapload, mob/owner, tc_amount = 20)
. = ..()
- AddComponent(/datum/component/uplink, owner, TRUE, FALSE, UPLINK_TRAITORS, tc_amount)
+ AddComponent(/datum/component/uplink, owner?.mind, TRUE, FALSE, UPLINK_TRAITORS, tc_amount)
diff --git a/tgui/packages/tgui/interfaces/Uplink.js b/tgui/packages/tgui/interfaces/Uplink.js
index 42fcaecf08c8e..5b8f770238d85 100644
--- a/tgui/packages/tgui/interfaces/Uplink.js
+++ b/tgui/packages/tgui/interfaces/Uplink.js
@@ -1,4 +1,4 @@
-import { createSearch, decodeHtmlEntities } from 'common/string';
+import { capitalize, createSearch, decodeHtmlEntities } from 'common/string';
import { useBackend, useLocalState } from '../backend';
import { Stack, Box, Button, Flex, Input, Section, Table, Tabs, NoticeBox, Grid, Divider, Icon, Tooltip } from '../components';
import { formatMoney } from '../format';
@@ -44,6 +44,12 @@ export const Uplink = (props, context) => {
const Directives = (props, context) => {
const [selected, setSelected] = useLocalState(context, "sel_obj", 0);
+ const { act, data } = useBackend(context);
+ const {
+ time,
+ objectives = [],
+ } = data.objectives;
+ const selectedObjective = objectives[selected];
return (
@@ -51,26 +57,19 @@ const Directives = (props, context) => {
- {
- setSelected(0);
- }} />
- {
- setSelected(1);
- }} />
- {
- setSelected(2);
- }} />
- {
- setSelected(3);
- }} />
+ {objectives.map((objective, index) => (
+ {
+ setSelected(index);
+ }}
+ objective_info={{
+ name: objective.name,
+ reward: objective.reward || 0,
+ time_left: objective.time ? objective.time - time : null,
+ }} />
+ ))}
@@ -95,13 +94,19 @@ const Directives = (props, context) => {
Tasks
-
- Assassinate Burnard Silkwind, the roboticist.
+ {selectedObjective.tasks.map(task => (
+
+
+ {task}
+
+ ))}
Additional Details
- This mission is part of your assignment and must be
- completed. No additional reward will be provided outside of the
- terms that have been defined within your contract of employment.
+ {selectedObjective.details || (
+ "This mission is part of your assignment and must be\
+ completed. No additional reward will be provided outside of the\
+ terms that have been defined within your contract of employment."
+ )}
@@ -155,7 +160,7 @@ const ObjectiveCard = (props, context) => {
className={"objective_card " + (selected && "selected")}
onClick={onClick}>
- {name}
+ {capitalize(name)}
From 3b0afb9d6050abc903e35d42e0a79b295ca4274e Mon Sep 17 00:00:00 2001
From: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com>
Date: Thu, 14 Mar 2024 21:01:51 +0000
Subject: [PATCH 10/62] Steal objectives can now be tracked
---
beestation.dme | 1 +
.../subsystem/priority_directives.dm | 9 ++++++
code/datums/components/trackable.dm | 24 +++++++++++++++
code/game/gamemodes/objective_items.dm | 6 ++++
code/game/gamemodes/objectives/_objective.dm | 5 ++++
.../gamemodes/objectives/basic/assassinate.dm | 3 ++
.../objectives/basic/changeling/absorb.dm | 3 ++
.../gamemodes/objectives/basic/debrain.dm | 3 ++
.../gamemodes/objectives/basic/destroy_ai.dm | 3 ++
.../game/gamemodes/objectives/basic/maroon.dm | 3 ++
.../gamemodes/objectives/basic/protect.dm | 6 +++-
.../objectives/basic/protect_object.dm | 3 ++
code/game/gamemodes/objectives/basic/steal.dm | 11 +++++++
.../gamemodes/objectives/open/explosion.dm | 1 +
.../telecomms/machines/message_server.dm | 5 ++++
code/game/objects/items/blueprints.dm | 5 +++-
code/game/objects/items/documents.dm | 4 +++
code/game/objects/items/tanks/jetpack.dm | 4 +++
code/game/objects/items/teleportation.dm | 4 +++
code/game/objects/items/theft_tools.dm | 8 +++++
.../game/objects/structures/tank_dispenser.dm | 5 ++++
.../nukeop/equipment/nuclearbomb.dm | 1 +
.../backstory/traitor_datum_backstory.dm | 2 +-
code/modules/clothing/shoes/magboots.dm | 4 +++
code/modules/clothing/suits/armor.dm | 4 +++
.../modules/clothing/suits/reactive_armour.dm | 4 +++
code/modules/clothing/under/accessories.dm | 4 +++
code/modules/mob/living/silicon/ai/ai.dm | 1 +
.../mob/living/simple_animal/slime/life.dm | 4 +++
code/modules/power/supermatter/supermatter.dm | 4 +++
.../projectiles/guns/energy/energy_gun.dm | 4 +++
code/modules/projectiles/guns/energy/laser.dm | 4 +++
.../reagents/reagent_containers/hypospray.dm | 4 +++
.../steal_objectives_trackability.dm | 17 +++++++++++
tgui/packages/tgui/interfaces/Uplink.js | 30 +++++++++++++++----
35 files changed, 194 insertions(+), 9 deletions(-)
create mode 100644 code/datums/components/trackable.dm
create mode 100644 code/modules/unit_tests/steal_objectives_trackability.dm
diff --git a/beestation.dme b/beestation.dme
index 2eea030c5fa18..acdb91e976246 100644
--- a/beestation.dme
+++ b/beestation.dme
@@ -684,6 +684,7 @@
#include "code\datums\components\team_monitor.dm"
#include "code\datums\components\tether.dm"
#include "code\datums\components\thermite.dm"
+#include "code\datums\components\trackable.dm"
#include "code\datums\components\twohanded.dm"
#include "code\datums\components\udder.dm"
#include "code\datums\components\uplink.dm"
diff --git a/code/controllers/subsystem/priority_directives.dm b/code/controllers/subsystem/priority_directives.dm
index 474eb8887441f..640bde1554bdb 100644
--- a/code/controllers/subsystem/priority_directives.dm
+++ b/code/controllers/subsystem/priority_directives.dm
@@ -51,6 +51,11 @@ SUBSYSTEM_DEF(directives)
/datum/controller/subsystem/directives/proc/get_uplink_data(datum/component/uplink/uplink)
var/data = list()
data["time"] = world.time
+ var/atom/uplink_owner = uplink.parent
+ if (istype(uplink_owner))
+ data["pos_x"] = uplink_owner?.x
+ data["pos_y"] = uplink_owner?.y
+ data["pos_z"] = uplink_owner?.z
// The uplink can only detect syndicate assigned objectives of the owner
if (uplink.owner)
var/list/known_objectives = list()
@@ -59,9 +64,13 @@ SUBSYSTEM_DEF(directives)
if (antagonist_type.faction != FACTION_SYNDICATE)
continue
for (var/datum/objective/objective in antagonist_type.objectives)
+ var/atom/tracking_target = objective.get_tracking_target(uplink_owner)
known_objectives += list(list(
"name" = objective.name,
"tasks" = list(objective.explanation_text),
+ "track_x" = tracking_target?.x,
+ "track_y" = tracking_target?.y,
+ "track_z" = tracking_target?.z,
))
// Add the priority directive
if (active_directive)
diff --git a/code/datums/components/trackable.dm b/code/datums/components/trackable.dm
new file mode 100644
index 0000000000000..92e9a7fcb0110
--- /dev/null
+++ b/code/datums/components/trackable.dm
@@ -0,0 +1,24 @@
+GLOBAL_LIST_EMPTY(tracks_by_type)
+
+/datum/component/trackable/Initialize(...)
+ var/atom/object = parent
+ if (!istype(object))
+ return COMPONENT_INCOMPATIBLE
+ if (!GLOB.tracks_by_type[object.type])
+ GLOB.tracks_by_type[object.type] = list(object)
+ else
+ GLOB.tracks_by_type[object.type] += object
+
+/datum/component/trackable/Destroy(force, silent)
+ var/atom/object = parent
+ if (istype(object))
+ GLOB.tracks_by_type[object.type] -= object
+ return ..()
+
+/proc/get_trackables_by_type(typepath, locate_subtypes)
+ if (locate_subtypes)
+ var/list/results = list()
+ for (var/type in typesof(typepath))
+ results += GLOB.tracks_by_type[type]
+ return results
+ return GLOB.tracks_by_type[typepath]
diff --git a/code/game/gamemodes/objective_items.dm b/code/game/gamemodes/objective_items.dm
index 1b80a82c1e2fa..00f3458acb4a0 100644
--- a/code/game/gamemodes/objective_items.dm
+++ b/code/game/gamemodes/objective_items.dm
@@ -10,6 +10,8 @@
var/list/special_equipment = list()
/// Require that the target item is spawned at roundstart by closets.
var/require_item_spawns_at_roundstart = TRUE
+ /// If we want a different item other than the target to be the track target
+ var/special_track_type
/datum/objective_item/proc/check_special_completion() //for objectives with special checks (is that slime extract unused? does that intellicard have an ai in it? etcetc)
return 1
@@ -125,6 +127,7 @@
name = "a sliver of a supermatter crystal. Be sure to use the proper safety equipment when extracting the sliver!"
targetitem = /obj/item/nuke_core/supermatter_sliver
difficulty = 15
+ special_track_type = /obj/machinery/power/supermatter_crystal
/datum/objective_item/steal/supermatter/New()
special_equipment += /obj/item/storage/box/syndie_kit/supermatter
@@ -139,6 +142,7 @@
targetitem = /obj/item/tank
difficulty = 3
excludefromjob = list(JOB_NAME_CHIEFENGINEER,JOB_NAME_RESEARCHDIRECTOR,JOB_NAME_STATIONENGINEER,JOB_NAME_SCIENTIST,JOB_NAME_ATMOSPHERICTECHNICIAN)
+ special_track_type = /obj/structure/tank_dispenser
/datum/objective_item/steal/plasma/check_special_completion(obj/item/tank/T)
var/target_amount = text2num(name)
@@ -151,6 +155,7 @@
targetitem = /obj/item/aicard
difficulty = 20 //beyond the impossible
requiredjob = list(JOB_NAME_AI)
+ special_track_type = /mob/living/silicon/ai
/datum/objective_item/steal/functionalai/check_special_completion(obj/item/aicard/C)
for(var/mob/living/silicon/ai/A in C)
@@ -181,6 +186,7 @@
difficulty = 3
excludefromjob = list(JOB_NAME_RESEARCHDIRECTOR,JOB_NAME_SCIENTIST)
requiredjob = list(JOB_NAME_RESEARCHDIRECTOR, JOB_NAME_SCIENTIST)
+ special_track_type = /mob/living/simple_animal/slime
/datum/objective_item/steal/slime/check_special_completion(obj/item/slime_extract/E)
if(E.Uses > 0)
diff --git a/code/game/gamemodes/objectives/_objective.dm b/code/game/gamemodes/objectives/_objective.dm
index 9df471eb5526f..653907689792f 100644
--- a/code/game/gamemodes/objectives/_objective.dm
+++ b/code/game/gamemodes/objectives/_objective.dm
@@ -302,3 +302,8 @@ GLOBAL_LIST(admin_objective_list) //Prefilled admin assignable objective list
for(var/datum/mind/own as() in get_owners())
to_chat(own.current, " You get the feeling your target is no longer within reach. Time for Plan [pick("A","B","C","D","X","Y","Z")]. Objectives updated!")
own.announce_objectives()
+
+/// Get the tracking target for this objective
+/datum/objective/proc/get_tracking_target(atom/source)
+ RETURN_TYPE(/atom)
+ return null
diff --git a/code/game/gamemodes/objectives/basic/assassinate.dm b/code/game/gamemodes/objectives/basic/assassinate.dm
index 1fa29331fbf7d..965b1f56b1b41 100644
--- a/code/game/gamemodes/objectives/basic/assassinate.dm
+++ b/code/game/gamemodes/objectives/basic/assassinate.dm
@@ -20,6 +20,9 @@
/datum/objective/assassinate/admin_edit(mob/admin)
admin_simple_target_pick(admin)
+/datum/objective/assassinate/get_tracking_target(atom/source)
+ return target?.current
+
/datum/objective/assassinate/incursion
name = "eliminate"
diff --git a/code/game/gamemodes/objectives/basic/changeling/absorb.dm b/code/game/gamemodes/objectives/basic/changeling/absorb.dm
index dacbea207a169..90b3b4ac5008a 100644
--- a/code/game/gamemodes/objectives/basic/changeling/absorb.dm
+++ b/code/game/gamemodes/objectives/basic/changeling/absorb.dm
@@ -46,6 +46,9 @@
var/span = check_completion() ? "grentext" : "redtext"
return "[explanation_text] [absorbedcount] lifeform\s absorbed!"
+/datum/objective/absorb/get_tracking_target(atom/source)
+ return target?.current
+
/datum/objective/absorb_most
name = "absorb most"
explanation_text = "Extract more compatible genomes than any other Changeling."
diff --git a/code/game/gamemodes/objectives/basic/debrain.dm b/code/game/gamemodes/objectives/basic/debrain.dm
index 1e9dcf9f18abd..26fff7a617fad 100644
--- a/code/game/gamemodes/objectives/basic/debrain.dm
+++ b/code/game/gamemodes/objectives/basic/debrain.dm
@@ -31,3 +31,6 @@
/datum/objective/debrain/admin_edit(mob/admin)
admin_simple_target_pick(admin)
+
+/datum/objective/debrain/get_tracking_target(atom/source)
+ return target?.current
diff --git a/code/game/gamemodes/objectives/basic/destroy_ai.dm b/code/game/gamemodes/objectives/basic/destroy_ai.dm
index feaf4ac62e7a2..8f4397945a8e6 100644
--- a/code/game/gamemodes/objectives/basic/destroy_ai.dm
+++ b/code/game/gamemodes/objectives/basic/destroy_ai.dm
@@ -38,5 +38,8 @@
to_chat(admin, "No active AIs with minds")
update_explanation_text()
+/datum/objective/destroy/get_tracking_target(atom/source)
+ return target?.current
+
/datum/objective/destroy/internal
var/stolen = FALSE //Have we already eliminated this target?
diff --git a/code/game/gamemodes/objectives/basic/maroon.dm b/code/game/gamemodes/objectives/basic/maroon.dm
index 712dca6c76833..e87c289a5cb0c 100644
--- a/code/game/gamemodes/objectives/basic/maroon.dm
+++ b/code/game/gamemodes/objectives/basic/maroon.dm
@@ -18,3 +18,6 @@
/datum/objective/maroon/admin_edit(mob/admin)
admin_simple_target_pick(admin)
+
+/datum/objective/maroon/get_tracking_target(atom/source)
+ return target?.current
diff --git a/code/game/gamemodes/objectives/basic/protect.dm b/code/game/gamemodes/objectives/basic/protect.dm
index 74ad524178246..ff33da4d08db7 100644
--- a/code/game/gamemodes/objectives/basic/protect.dm
+++ b/code/game/gamemodes/objectives/basic/protect.dm
@@ -1,4 +1,5 @@
-/datum/objective/protect//The opposite of killing a dude.
+//The opposite of killing a dude.
+/datum/objective/protect
name = "protect"
var/target_role_type = FALSE
var/human_check = TRUE
@@ -30,6 +31,9 @@
/datum/objective/protect/admin_edit(mob/admin)
admin_simple_target_pick(admin)
+/datum/objective/protect/get_tracking_target(atom/source)
+ return target?.current
+
/datum/objective/protect/nonhuman
name = "protect nonhuman"
human_check = FALSE
diff --git a/code/game/gamemodes/objectives/basic/protect_object.dm b/code/game/gamemodes/objectives/basic/protect_object.dm
index 736c820c156f5..35a98c5c9926e 100644
--- a/code/game/gamemodes/objectives/basic/protect_object.dm
+++ b/code/game/gamemodes/objectives/basic/protect_object.dm
@@ -15,3 +15,6 @@
/datum/objective/protect_object/check_completion()
return !QDELETED(protect_target) || ..()
+
+/datum/objective/protect_object/get_tracking_target(atom/source)
+ return protect_target
diff --git a/code/game/gamemodes/objectives/basic/steal.dm b/code/game/gamemodes/objectives/basic/steal.dm
index b6ccf9b4e3dd0..0e920cada2fee 100644
--- a/code/game/gamemodes/objectives/basic/steal.dm
+++ b/code/game/gamemodes/objectives/basic/steal.dm
@@ -8,6 +8,17 @@ GLOBAL_LIST_EMPTY(possible_items)
/datum/objective/steal/get_target()
return steal_target
+/datum/objective/steal/get_tracking_target(atom/source)
+ var/closest = INFINITY
+ var/atom/tracked = null
+ for (var/atom/target in get_trackables_by_type(steal_target, TRUE))
+ var/dist = get_dist(target, source)
+ if (dist > closest)
+ continue
+ closest = dist
+ tracked = target
+ return tracked
+
/datum/objective/steal/New()
..()
if(!GLOB.possible_items.len)//Only need to fill the list when it's needed.
diff --git a/code/game/gamemodes/objectives/open/explosion.dm b/code/game/gamemodes/objectives/open/explosion.dm
index 5f5cfe04fa983..b083c59e04c8c 100644
--- a/code/game/gamemodes/objectives/open/explosion.dm
+++ b/code/game/gamemodes/objectives/open/explosion.dm
@@ -21,6 +21,7 @@
var/devistation = 0
var/heavy = 0
var/light = 0
+ var/turf/generic_track_location
/datum/objective/open/explosion/New(text)
. = ..()
diff --git a/code/game/machinery/telecomms/machines/message_server.dm b/code/game/machinery/telecomms/machines/message_server.dm
index 5f7003e346d4d..65b63e09eef3b 100644
--- a/code/game/machinery/telecomms/machines/message_server.dm
+++ b/code/game/machinery/telecomms/machines/message_server.dm
@@ -56,6 +56,7 @@
else
icon_state = "blackbox_b"
return ..()
+
/obj/item/blackbox
name = "the blackbox"
desc = "A strange relic, capable of recording data on extradimensional vertices. It lives inside the blackbox recorder for safe keeping."
@@ -66,6 +67,10 @@
w_class = WEIGHT_CLASS_BULKY
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
+/obj/item/blackbox/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/trackable)
+
#define MESSAGE_SERVER_FUNCTIONING_MESSAGE "This is an automated message. The messaging system is functioning correctly."
// The message server itself.
diff --git a/code/game/objects/items/blueprints.dm b/code/game/objects/items/blueprints.dm
index de93897096974..edfdab19a07c1 100644
--- a/code/game/objects/items/blueprints.dm
+++ b/code/game/objects/items/blueprints.dm
@@ -51,11 +51,14 @@
var/legend = FALSE //Viewing the wire legend
investigate_flags = ADMIN_INVESTIGATE_TARGET
+/obj/item/areaeditor/blueprints/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/trackable)
+
/obj/item/areaeditor/blueprints/Destroy()
clear_viewer()
return ..()
-
/obj/item/areaeditor/blueprints/attack_self(mob/user)
. = ..()
if(!legend)
diff --git a/code/game/objects/items/documents.dm b/code/game/objects/items/documents.dm
index 9976848bdfc46..8ec6acc044bbc 100644
--- a/code/game/objects/items/documents.dm
+++ b/code/game/objects/items/documents.dm
@@ -12,6 +12,10 @@
pressure_resistance = 2
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
+/obj/item/documents/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/trackable)
+
/obj/item/documents/nanotrasen
desc = "\"Top Secret\" Nanotrasen documents, filled with complex diagrams and lists of names, dates and coordinates."
icon_state = "docs_verified"
diff --git a/code/game/objects/items/tanks/jetpack.dm b/code/game/objects/items/tanks/jetpack.dm
index 36b06e40ff658..aa40367241eb1 100644
--- a/code/game/objects/items/tanks/jetpack.dm
+++ b/code/game/objects/items/tanks/jetpack.dm
@@ -196,6 +196,10 @@
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF //steal objective items are hard to destroy.
investigate_flags = ADMIN_INVESTIGATE_TARGET
+/obj/item/tank/jetpack/oxygen/captain/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/trackable)
+
/obj/item/tank/jetpack/oxygen/security
name = "security jetpack (oxygen)"
desc = "A tank of compressed oxygen for use as propulsion in zero-gravity areas by security forces."
diff --git a/code/game/objects/items/teleportation.dm b/code/game/objects/items/teleportation.dm
index 9f67d17f582b5..2fe27d2051fce 100644
--- a/code/game/objects/items/teleportation.dm
+++ b/code/game/objects/items/teleportation.dm
@@ -121,6 +121,10 @@
. = ..()
active_portal_pairs = list()
+/obj/item/hand_tele/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/trackable)
+
/obj/item/hand_tele/pre_attack(atom/target, mob/user, params)
if(try_dispel_portal(target, user))
return FALSE
diff --git a/code/game/objects/items/theft_tools.dm b/code/game/objects/items/theft_tools.dm
index e92289f30efa1..1b78b4ad07d81 100644
--- a/code/game/objects/items/theft_tools.dm
+++ b/code/game/objects/items/theft_tools.dm
@@ -20,6 +20,10 @@
. = ..()
START_PROCESSING(SSobj, src)
+/obj/item/nuke_core/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/trackable)
+
/obj/item/nuke_core/Destroy()
STOP_PROCESSING(SSobj, src)
return ..()
@@ -127,6 +131,10 @@
item_state = "supermattersliver"
pulseicon = "supermatter_sliver_pulse"
+/obj/item/nuke_core/supermatter_sliver/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/trackable)
+
/obj/item/nuke_core/supermatter_sliver/attack_tk() // no TK dusting memes
return FALSE
diff --git a/code/game/objects/structures/tank_dispenser.dm b/code/game/objects/structures/tank_dispenser.dm
index eb3e049d16d30..03a7ce467429b 100644
--- a/code/game/objects/structures/tank_dispenser.dm
+++ b/code/game/objects/structures/tank_dispenser.dm
@@ -11,6 +11,11 @@
var/oxygentanks = TANK_DISPENSER_CAPACITY
var/plasmatanks = TANK_DISPENSER_CAPACITY
+/obj/structure/tank_dispenser/ComponentInitialize()
+ . = ..()
+ if (plasmatanks > 0)
+ AddComponent(/datum/component/trackable)
+
/obj/structure/tank_dispenser/oxygen
plasmatanks = 0
diff --git a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
index 9f79aaf5d78c7..713ca09d590e1 100644
--- a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
+++ b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
@@ -677,6 +677,7 @@ This is here to make the tiles around the station mininuke change when it's arme
if(!fake)
//Global teamfinder signal trackable on the synd frequency.
AddComponent(/datum/component/tracking_beacon, "synd", null, null, TRUE, "#ebeca1", TRUE, TRUE, "#818157")
+ AddComponent(/datum/component/trackable)
/obj/item/disk/nuclear/process()
++process_tick
diff --git a/code/modules/antagonists/traitor/backstory/traitor_datum_backstory.dm b/code/modules/antagonists/traitor/backstory/traitor_datum_backstory.dm
index 840c87245edcd..61137689a96b3 100644
--- a/code/modules/antagonists/traitor/backstory/traitor_datum_backstory.dm
+++ b/code/modules/antagonists/traitor/backstory/traitor_datum_backstory.dm
@@ -37,7 +37,7 @@
/datum/antagonist/traitor/proc/set_faction(datum/traitor_faction/new_faction)
if(!istype(new_faction))
return
- var/no_faction = isnull(faction)
+ var/no_faction = isnull(traitor_faction)
traitor_faction = new_faction
employer = new_faction.employer_name
if(no_faction)
diff --git a/code/modules/clothing/shoes/magboots.dm b/code/modules/clothing/shoes/magboots.dm
index d2475cf7ac92a..573359fa4b5ba 100644
--- a/code/modules/clothing/shoes/magboots.dm
+++ b/code/modules/clothing/shoes/magboots.dm
@@ -52,6 +52,10 @@
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
investigate_flags = ADMIN_INVESTIGATE_TARGET
+/obj/item/clothing/shoes/magboots/advance/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/trackable)
+
/obj/item/clothing/shoes/magboots/syndie
desc = "Reverse-engineered magnetic boots that have a heavy magnetic pull. Property of Gorlex Marauders."
name = "blood-red magboots"
diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm
index f19bba0ef3643..f1e656daf4c67 100644
--- a/code/modules/clothing/suits/armor.dm
+++ b/code/modules/clothing/suits/armor.dm
@@ -178,6 +178,10 @@
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
var/hit_reflect_chance = 40
+/obj/item/clothing/suit/armor/laserproof/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/trackable)
+
/obj/item/clothing/suit/armor/laserproof/IsReflect(def_zone)
if(!(def_zone in list(BODY_ZONE_CHEST, BODY_ZONE_PRECISE_GROIN))) //If not shot where ablative is covering you, you don't get the reflection bonus!
return 0
diff --git a/code/modules/clothing/suits/reactive_armour.dm b/code/modules/clothing/suits/reactive_armour.dm
index 2586acfefa579..380b71f6b6189 100644
--- a/code/modules/clothing/suits/reactive_armour.dm
+++ b/code/modules/clothing/suits/reactive_armour.dm
@@ -134,6 +134,10 @@
var/tele_range = 6
var/rad_amount= 15
+/obj/item/clothing/suit/armor/reactive/teleport/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/trackable)
+
/obj/item/clothing/suit/armor/reactive/teleport/reactive_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", damage = 0, attack_type = MELEE_ATTACK)
owner.visible_message("The reactive teleport system flings [owner] clear of [attack_text], shutting itself off in the process!")
playsound(get_turf(owner),'sound/magic/blink.ogg', 100, 1)
diff --git a/code/modules/clothing/under/accessories.dm b/code/modules/clothing/under/accessories.dm
index 22b024c42f02b..cdb4c9d9dc3c1 100755
--- a/code/modules/clothing/under/accessories.dm
+++ b/code/modules/clothing/under/accessories.dm
@@ -219,6 +219,10 @@
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
investigate_flags = ADMIN_INVESTIGATE_TARGET
+/obj/item/clothing/accessory/medal/gold/captain/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/trackable)
+
/obj/item/clothing/accessory/medal/gold/heroism
name = "medal of exceptional heroism"
desc = "An extremely rare golden medal awarded only by CentCom. To receive such a medal is the highest honor and as such, very few exist. This medal is almost never awarded to anybody but commanders."
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index 8e7290fc2ea32..723f4108731b8 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -110,6 +110,7 @@
/mob/living/silicon/ai/Initialize(mapload, datum/ai_laws/L, mob/target_ai)
default_access_list = get_all_accesses()
. = ..()
+ AddComponent(/datum/component/trackable)
add_sensors()
if(!target_ai) //If there is no player/brain inside.
new/obj/structure/AIcore/deactivated(loc) //New empty terminal.
diff --git a/code/modules/mob/living/simple_animal/slime/life.dm b/code/modules/mob/living/simple_animal/slime/life.dm
index ec700c523c358..1445746f730fb 100644
--- a/code/modules/mob/living/simple_animal/slime/life.dm
+++ b/code/modules/mob/living/simple_animal/slime/life.dm
@@ -6,6 +6,10 @@
var/attack_cooldown = 0
var/attack_cooldown_time = 20 //How long, in deciseconds, the cooldown of attacks is
+/mob/living/simple_animal/slime/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/trackable)
+
/mob/living/simple_animal/slime/Life()
set invisibility = 0
if(notransform)
diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm
index f9a76a0002bfb..b00f889c4b60c 100644
--- a/code/modules/power/supermatter/supermatter.dm
+++ b/code/modules/power/supermatter/supermatter.dm
@@ -170,6 +170,10 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
/obj/boh_tear
))
+/obj/machinery/power/supermatter_crystal/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/trackable)
+
/obj/machinery/power/supermatter_crystal/Destroy()
investigate_log("has been destroyed.", INVESTIGATE_ENGINES)
SSair.stop_processing_machine(src)
diff --git a/code/modules/projectiles/guns/energy/energy_gun.dm b/code/modules/projectiles/guns/energy/energy_gun.dm
index 3bbfc202bb1ba..3d9c8113f4b62 100644
--- a/code/modules/projectiles/guns/energy/energy_gun.dm
+++ b/code/modules/projectiles/guns/energy/energy_gun.dm
@@ -78,6 +78,10 @@
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
investigate_flags = ADMIN_INVESTIGATE_TARGET
+/obj/item/gun/energy/e_gun/hos/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/trackable)
+
/obj/item/gun/energy/e_gun/dragnet
name = "\improper DRAGnet"
desc = "The \"Dynamic Rapid-Apprehension of the Guilty\" net is a revolution in law enforcement technology."
diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm
index 92cc334fbdaea..535e0d6225269 100644
--- a/code/modules/projectiles/guns/energy/laser.dm
+++ b/code/modules/projectiles/guns/energy/laser.dm
@@ -45,6 +45,10 @@
weapon_weight = WEAPON_LIGHT
investigate_flags = ADMIN_INVESTIGATE_TARGET
+/obj/item/gun/energy/laser/captain/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/trackable)
+
/obj/item/gun/energy/laser/captain/scattershot
name = "scatter shot laser rifle"
icon_state = "lasercannon"
diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm
index 4d9cfde8bd1ac..22dfb185345c4 100644
--- a/code/modules/reagents/reagent_containers/hypospray.dm
+++ b/code/modules/reagents/reagent_containers/hypospray.dm
@@ -74,6 +74,10 @@
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
investigate_flags = ADMIN_INVESTIGATE_TARGET
+/obj/item/reagent_containers/hypospray/CMO/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/trackable)
+
/obj/item/reagent_containers/hypospray/combat
name = "combat stimulant injector"
desc = "A modified air-needle autoinjector, used by support operatives to quickly heal injuries in combat."
diff --git a/code/modules/unit_tests/steal_objectives_trackability.dm b/code/modules/unit_tests/steal_objectives_trackability.dm
new file mode 100644
index 0000000000000..e8252fca76e98
--- /dev/null
+++ b/code/modules/unit_tests/steal_objectives_trackability.dm
@@ -0,0 +1,17 @@
+/datum/unit_test/trackable/Run()
+ var/list/fails = list()
+ for (var/datum/objective_item/steal/item as() in subtypesof(/datum/objective_item/steal))
+ if (ispath(item, /datum/objective_item/special) || ispath(item, /datum/objective_item/stack))
+ continue
+ var/item_target = initial(item.special_track_type) || initial(item.targetitem)
+ // Accepted ignore: AIs get deleted during init but will be trackable
+ if (ispath(item_target, /mob/living/silicon/ai))
+ continue
+ var/atom/created = new item_target(run_loc_floor_bottom_left)
+ if (!created.GetComponent(/datum/component/trackable))
+ fails += "[item_target] is not trackable but is the target of a steal objective. Add the following code:\n[item_target]/ComponentInitialize()\n\t. = ..()\n\tAddComponent(/datum/component/trackable)"
+ qdel(created)
+ for (var/atom/a in run_loc_floor_bottom_left)
+ qdel(a)
+ if (length(fails))
+ Fail(jointext(fails, "\n"))
diff --git a/tgui/packages/tgui/interfaces/Uplink.js b/tgui/packages/tgui/interfaces/Uplink.js
index 5b8f770238d85..a44133db15f41 100644
--- a/tgui/packages/tgui/interfaces/Uplink.js
+++ b/tgui/packages/tgui/interfaces/Uplink.js
@@ -46,10 +46,21 @@ const Directives = (props, context) => {
const [selected, setSelected] = useLocalState(context, "sel_obj", 0);
const { act, data } = useBackend(context);
const {
+ pos_x = 0,
+ pos_y = 0,
+ pos_z = 0,
time,
objectives = [],
} = data.objectives;
const selectedObjective = objectives[selected];
+ const {
+ track_x,
+ track_y,
+ track_z,
+ } = selectedObjective || {};
+ const dx = track_x - pos_x;
+ const dy = track_y - pos_y;
+ const angle = (360 / (Math.PI * 2)) * (Math.atan2(dx, dy));
return (
@@ -76,14 +87,21 @@ const Directives = (props, context) => {
+ );
+};
+
const Directives = (props, context) => {
const [selected, setSelected] = useLocalState(context, 'sel_obj', 0);
const { act, data } = useBackend(context);
diff --git a/tgui/packages/tgui/styles/interfaces/Uplink.scss b/tgui/packages/tgui/styles/interfaces/Uplink.scss
index 642362a5c81ed..a2dc5288bdd68 100644
--- a/tgui/packages/tgui/styles/interfaces/Uplink.scss
+++ b/tgui/packages/tgui/styles/interfaces/Uplink.scss
@@ -18,7 +18,7 @@
left: 0;
top: 0;
bottom: 0;
- width: 70%;
+ width: 60%;
margin: 10px;
}
@@ -27,7 +27,7 @@
right: 0;
top: 0;
bottom: 0;
- width: 30%;
+ width: 40%;
margin: 10px;
}
@@ -43,24 +43,44 @@
}
.RankCard {
+ position: relative;
width: 100%;
padding-bottom: 20px;
color: #d4d4d4;
}
+.RankCardHighlight {
+ border: 1px solid rgb(255, 209, 70);
+ box-shadow: black 3px 3px;
+}
+
.RankCardMain {
- width: calc(100% - 200px);
+ width: calc(100% - 164px);
height: 90px;
background: linear-gradient(0deg, #b457385a, #8D3E3E5a);
padding: 10px;
border-radius: 13px;
}
+.RankCardProgression {
+ position: absolute;
+ right: 10px;
+ top: calc(45px - 20px);
+ width: 144px;
+ height: 40px;
+ padding: 10px;
+ border-radius: 13px;
+ text-align: center;
+ vertical-align: middle;
+ padding-top: 5px;
+}
+
.RankCardTitle {
position: relative;
width: 100%;
font-size: 1.3em;
margin-bottom: 6px;
+ height: 1.3em;
}
.RankCardName {
From f92d4d7b05da0c0ce569c72935c3d5547dd88f3f Mon Sep 17 00:00:00 2001
From: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com>
Date: Sun, 31 Mar 2024 14:31:43 +0100
Subject: [PATCH 47/62] Backend reputation and a host of balancing changes to
the uplink
---
code/__DEFINES/antagonists.dm | 26 +++++
code/datums/components/uplink.dm | 18 +++-
code/modules/uplink/uplink_items.dm | 98 +++++++++++++------
config/dbconfig.txt | 10 +-
tgui/packages/tgui/interfaces/Uplink.js | 13 ++-
.../tgui/styles/interfaces/Uplink.scss | 39 ++++++--
6 files changed, 157 insertions(+), 47 deletions(-)
diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm
index b6a4875b346b7..224b13fe9241e 100644
--- a/code/__DEFINES/antagonists.dm
+++ b/code/__DEFINES/antagonists.dm
@@ -120,6 +120,32 @@
/// because they have nothing else that supports an implant.
#define UPLINK_IMPLANT_TELECRYSTAL_COST 1
+/// Traitor reputation levels
+
+/// Ex-communicated
+#define REPUTATION_EXCOMMUNICATED 0
+/// Blood brother level: Untrusted
+#define REPUTATION_LOW 100
+/// Standard traitor level
+#define REPUTATION_STANDARD 200
+/// Good reputation, additional gear
+#define REPUTATION_GOOD 400
+/// Excellent reputation, more murderboney stuff
+#define REPUTATION_EXCELLENT 600
+/// Elite reputation, access to rare and unique items.
+/// Nuclear operative level
+#define REPUTATION_ELITE 800
+/// Access to anything your heart could ever desire
+#define REPUTATION_MAX 1000
+
+/// How much reputation is gained per completed directive
+#define REPUTATION_GAIN_PER_DIRECTIVE 200
+
+/// How much reputation you lose for failing a solo directive
+#define REPUTATION_LOSS_SOLO_DIRECTIVE 50
+/// How much reputation you lose for failing a team-directive
+#define REPUTATION_LOSS_TEAM_DIRECTIVE 100
+
///Checks if given mob is a hive host
#define IS_HIVEHOST(mob) (mob.mind?.has_antag_datum(/datum/antagonist/hivemind))
///Checks if given mob is an awakened vessel
diff --git a/code/datums/components/uplink.dm b/code/datums/components/uplink.dm
index 19db740a0e05f..bca06f1635ce4 100644
--- a/code/datums/components/uplink.dm
+++ b/code/datums/components/uplink.dm
@@ -5,9 +5,9 @@ GLOBAL_LIST_EMPTY(uplinks)
/**
* Uplinks
*
- * All /obj/item(s) have a hidden_uplink var. By default it's null. Give the item one with 'new(src') (it must be in it's contents). Then add 'uses.'
- * Use whatever conditionals you want to check that the user has an uplink, and then call interact() on their uplink.
- * You might also want the uplink menu to open if active. Check if the uplink is 'active' and then interact() with it.
+ * All /obj/item(s) can be given an uplink. Give the item one with AddComponent(/datum/component/uplink, mind)
+ * Use whatever conditionals you want to check that the user has an uplink
+ * This component will handle UI interactions.
**/
/datum/component/uplink
dupe_mode = COMPONENT_DUPE_UNIQUE
@@ -31,10 +31,19 @@ GLOBAL_LIST_EMPTY(uplinks)
var/compact_mode = FALSE
var/debug = FALSE
var/non_traitor_allowed = TRUE
+ // Tied to uplink rather than mind since generally traitors only have 1 uplink
+ // and tying it to anything else is difficult due to how much uses an uplink
+ var/reputation = 200
var/list/previous_attempts
-/datum/component/uplink/Initialize(datum/mind/_owner, _lockable = TRUE, _enabled = FALSE, uplink_flag = UPLINK_TRAITORS, starting_tc = TELECRYSTALS_DEFAULT)
+/datum/component/uplink/Initialize(datum/mind/_owner,
+ _lockable = TRUE
+ _enabled = FALSE,
+ uplink_flag = UPLINK_TRAITORS,
+ starting_tc = TELECRYSTALS_DEFAULT,
+ _reputation = 200,
+ )
if(!isitem(parent))
return COMPONENT_INCOMPATIBLE
@@ -62,6 +71,7 @@ GLOBAL_LIST_EMPTY(uplinks)
purchase_log = new(owner.key, src)
lockable = _lockable
active = _enabled
+ reputation = _reputation
src.uplink_flag = uplink_flag
update_items()
telecrystals = starting_tc
diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm
index 63da060a91b5f..99ed6026833c7 100644
--- a/code/modules/uplink/uplink_items.dm
+++ b/code/modules/uplink/uplink_items.dm
@@ -138,6 +138,9 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
var/spawn_amount = 1 //How many times we should run the spawn
var/additional_uplink_entry = null //Bonus items you gain if you purchase it
var/is_bonus = FALSE // entry in 'additional_uplink_entry' will have this as TRUE. Used for logging detail
+ /// How much reputation is required to purchase this item.
+ /// Things that can be used for indiscriminant murder should require more work to get.
+ var/reputation_required = REPUTATION_EXCOMMUNICATED
/datum/uplink_item/New()
. = ..()
@@ -269,7 +272,8 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
standard contractor gear to help with your mission - comes supplied with the tablet, specialised space suit, chameleon jumpsuit and mask, \
agent card, specialised contractor baton, and three randomly selected low cost items. Can include otherwise unobtainable items."
item = /obj/item/storage/box/syndie_kit/contract_kit
- cost = 20
+ cost = 15
+ reputation_required = REPUTATION_GOOD
player_minimum = 15
purchasable_from = ~(UPLINK_INCURSION | UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
@@ -279,7 +283,8 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
These items are collectively worth more than 20 telecrystals, but you do not know which specialization \
you will receive. May contain discontinued and/or exotic items."
item = /obj/item/storage/box/syndie_kit/bundle_A
- cost = 20
+ cost = 15
+ reputation_required = REPUTATION_STANDARD
purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
/datum/uplink_item/bundles_TC/bundle_B
@@ -287,7 +292,8 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
desc = "Syndicate Bundles, also known as Syndi-Kits, are specialized groups of items that arrive in a plain box. \
In Syndi-kit Special, you will receive items used by famous syndicate agents of the past. Collectively worth more than 20 telecrystals, the syndicate loves a good throwback."
item = /obj/item/storage/box/syndie_kit/bundle_B
- cost = 20
+ cost = 15
+ reputation_required = REPUTATION_STANDARD
purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
/datum/uplink_item/bundles_TC/surplus
@@ -295,7 +301,8 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
desc = "A dusty crate from the back of the Syndicate warehouse. Rumored to contain a valuable assortment of items, \
but you never know. Contents are sorted to always be worth 50 TC."
item = /obj/structure/closet/crate
- cost = 20
+ cost = 18
+ reputation_required = REPUTATION_GOOD
player_minimum = 20
purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
var/starting_crate_value = 50
@@ -305,7 +312,8 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
name = "Super Surplus Crate"
desc = "A dusty SUPER-SIZED from the back of the Syndicate warehouse. Rumored to contain a valuable assortment of items, \
but you never know. Contents are sorted to always be worth 125 TC."
- cost = 40
+ cost = 25
+ reputation_required = REPUTATION_EXCELLENT
player_minimum = 30
starting_crate_value = 125
purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
@@ -343,6 +351,8 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
purchasable_from = ~(UPLINK_INCURSION | UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
uplink_contents = (UPLINK_TRAITORS | UPLINK_NUKE_OPS)
player_minimum = 30
+ cost = 15
+ reputation_required = REPUTATION_EXCELLENT
/datum/uplink_item/bundles_TC/surplus/random/purchase(mob/user, datum/component/uplink/U)
var/index = rand(1, 20)
@@ -414,7 +424,7 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
/datum/uplink_item/bundles_TC/crate
name = "Bulk Hardsuit Bundle"
desc = "A crate containing 4 valueable syndicate hardsuits."
- cost = 18
+ cost = 8
purchasable_from = UPLINK_INCURSION
item = /obj/effect/gibspawner/generic
var/list/contents = list(
@@ -437,7 +447,7 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
name = "Syndicate Medical Bundle"
desc = "Contains an assortment of syndicate medical equipment for you and your team.\
Comes with a variety of first-aid kits, pill bottles, a compact defibrillator and 4 stimpacks."
- cost = 12
+ cost = 8
contents = list(
/obj/item/storage/firstaid/tactical = 2, //8 TC
/obj/item/storage/firstaid/brute = 2,
@@ -454,7 +464,10 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
desc = "Every syndicate team needs their own shuttle. It's a shame you weren't supplied with one, but thats not a problem\
if you can spare some TC! The all new shuttle creation kit (produced by the syndicate) contains everything you need\
to get flying! All syndicate agents are advised to ignore the Nanotrasen labels on products. Space proof suits not included."
- cost = 15 //There are multiple uses for the RCD and plasma canister, but both are easilly accessible for items that cost less than all of their TC.
+ reputation_required = REPUTATION_STANDARD
+ // There are multiple uses for the RCD and plasma canister, but both are easilly accessible for items that cost less than all of their TC.
+ // Should be cheaper than the shuttle capsule
+ cost = 5
contents = list(
/obj/machinery/portable_atmospherics/canister/plasma = 1,
/obj/item/construction/rcd/combat = 1,
@@ -612,13 +625,15 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
desc = "These gloves let the user punch people very fast. Does not improve weapon attack speed or the meaty fists of a hulk."
item = /obj/item/clothing/gloves/rapid
cost = 8
+ reputation_required = REPUTATION_STANDARD
/datum/uplink_item/dangerous/holoparasite
name = "Holoparasites"
desc = "Though capable of near sorcerous feats via use of hardlight holograms and nanomachines, they require an \
organic host as a home base and source of fuel. Holoparasites come in various types and share damage with their host."
item = /obj/item/holoparasite_creator/tech
- cost = 18
+ cost = 15
+ reputation_required = REPUTATION_EXCELLENT
surplus = 10
purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
player_minimum = 25
@@ -700,6 +715,7 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
item = /obj/item/gun/ballistic/revolver
player_minimum = 25
cost = 12
+ reputation_required = REPUTATION_GOOD
surplus = 50
purchasable_from = ~UPLINK_CLOWN_OPS
@@ -802,7 +818,8 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
desc = "This scroll contains the secrets of an ancient martial arts technique. You will master unarmed combat, \
deflecting all ranged weapon fire, but you also refuse to use dishonorable ranged weaponry."
item = /obj/item/book/granter/martial/carp
- cost = 16
+ cost = 15
+ reputation_required = REPUTATION_EXCELLENT
player_minimum = 20
surplus = 10
purchasable_from = ~(UPLINK_INCURSION | UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
@@ -817,6 +834,7 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
item = /obj/item/gun/energy/kinetic_accelerator/crossbow/radbow
cost = 8
surplus = 50
+ reputation_required = REPUTATION_GOOD
/datum/uplink_item/stealthy_weapons/crossbow
name = "Miniature Energy Crossbow"
@@ -827,7 +845,8 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
slur as if inebriated. It can produce an infinite number \
of bolts, but takes a small amount of time to automatically recharge after each shot."
item = /obj/item/gun/energy/kinetic_accelerator/crossbow
- cost = 12
+ cost = 10
+ reputation_required = REPUTATION_GOOD
surplus = 50
purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
@@ -845,7 +864,7 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
name = "Poison Kit"
desc = "An assortment of deadly chemicals packed into a compact box. Comes with a syringe for more precise application."
item = /obj/item/storage/box/syndie_kit/chemical
- cost = 7
+ cost = 6
surplus = 50
/datum/uplink_item/stealthy_weapons/romerol_kit
@@ -854,7 +873,8 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
On death, these nodules take control of the dead body, causing limited revivification, \
along with slurred speech, aggression, and the ability to infect others with this agent."
item = /obj/item/storage/box/syndie_kit/romerol
- cost = 20
+ cost = 15
+ reputation_required = REPUTATION_ELITE
cant_discount = TRUE
murderbone_type = TRUE
surplus = 0
@@ -1253,7 +1273,8 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
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
- cost = 8 //20% discount!
+ cost = 6 //40 % discount
+ reputation_required = REPUTATION_GOOD
cant_discount = TRUE
/datum/uplink_item/explosives/x4bag
@@ -1262,7 +1283,8 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
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."
item = /obj/item/storage/backpack/duffelbag/syndie/x4
- cost = 4 //
+ cost = 4
+ reputation_required = REPUTATION_GOOD
cant_discount = TRUE
/datum/uplink_item/explosives/clown_bomb_clownops
@@ -1284,6 +1306,7 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
The concussive effect from the explosion will knock the recipient out for a short period, and deafen them for longer."
item = /obj/item/computer_hardware/hard_drive/role/virus/syndicate
cost = 6
+ reputation_required = REPUTATION_GOOD
restricted = TRUE
/datum/uplink_item/explosives/emp
@@ -1331,6 +1354,7 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
This variant has been fitted with high yield X4 charges for a larger explosion."
item = /obj/item/deployablemine/traitor/bigboom
cost = 10
+ reputation_required = REPUTATION_GOOD
/datum/uplink_item/explosives/pizza_bomb
name = "Pizza Bomb"
@@ -1356,6 +1380,7 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
The bomb core can be pried out and manually detonated with other explosives."
item = /obj/item/sbeacondrop/bomb
cost = 12
+ reputation_required = REPUTATION_GOOD
/datum/uplink_item/explosives/syndicate_detonator
name = "Syndicate Detonator"
@@ -1374,6 +1399,7 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
item = /obj/item/grenade/syndieminibomb
cost = 4
purchasable_from = ~UPLINK_CLOWN_OPS
+ reputation_required = REPUTATION_STANDARD
/datum/uplink_item/explosives/tearstache
name = "Teachstache Grenade"
@@ -1623,6 +1649,7 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
item = /obj/item/clothing/suit/space/hardsuit/syndi
cost = 7
purchasable_from = ~UPLINK_NUKE_OPS //you can't buy it in nuke, because the elite hardsuit costs the same while being better
+ reputation_required = REPUTATION_STANDARD
/datum/uplink_item/suits/hardsuit/spawn_item(spawn_path, mob/user, datum/component/uplink/U)
var/obj/item/clothing/suit/space/hardsuit/suit = ..()
@@ -1701,7 +1728,8 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
an in-built shuttle interdictor and a single canister of plasma to fuel your adventures! \
This innovative shuttle can seat up to 4 passengers, willing or not! Shuttle must be deployed in space or on lavaland, space suits not included."
item = /obj/item/survivalcapsule/shuttle/traitor
- cost = 8
+ cost = 7
+ reputation_required = REPUTATION_GOOD
purchasable_from = (UPLINK_INCURSION | UPLINK_TRAITORS)
/datum/uplink_item/device_tools/magboots
@@ -1748,6 +1776,9 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
in electronic devices, subverts intended functions, and easily breaks security mechanisms."
item = /obj/item/card/emag
cost = 6
+ // I don't want people using this to break into captain's office at roundstart, at least
+ // wait until 1 directive appears
+ reputation_required = REPUTATION_GOOD
/datum/uplink_item/device_tools/fakenucleardisk
name = "Decoy Nuclear Authentication Disk"
@@ -1774,7 +1805,7 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
You will receive the unlock code upon activating the virus, and the new uplink may be charged with \
telecrystals normally."
item = /obj/item/computer_hardware/hard_drive/role/virus/frame
- cost = 4
+ cost = 3
restricted = TRUE
/datum/uplink_item/device_tools/failsafe
@@ -1826,6 +1857,7 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
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."
item = /obj/item/assembly/flash/hypnotic
cost = 7
+ reputation_required = REPUTATION_GOOD
/datum/uplink_item/device_tools/medgun
name = "Medbeam Gun"
@@ -1843,6 +1875,7 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
sends you a small beacon that will teleport the larger beacon to your location upon activation."
item = /obj/item/sbeacondrop
cost = 10
+ reputation_required = REPUTATION_ELITE
/datum/uplink_item/device_tools/powersink
name = "Power Sink"
@@ -2207,14 +2240,16 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
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!"
item = /obj/vehicle/sealed/car/clowncar
- cost = 20
+ cost = 14
+ reputation_required = REPUTATION_EXCELLENT
restricted_roles = list(JOB_NAME_CLOWN)
purchasable_from = ~UPLINK_INCURSION
/datum/uplink_item/role_restricted/taeclowndo_shoes
name = "Tae-clown-do Shoes"
desc = "A pair of shoes for the most elite agents of the honkmotherland. They grant the mastery of taeclowndo with some honk-fu moves as long as they're worn."
- cost = 12
+ cost = 10
+ reputation_required = REPUTATION_GOOD
item = /obj/item/clothing/shoes/clown_shoes/taeclowndo
restricted_roles = list(JOB_NAME_CLOWN)
@@ -2249,7 +2284,7 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
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. \
Be warned, that spirits are often capricious or just little assholes. To use, simply speak your question aloud, then begin shaking."
item = /obj/item/toy/eightball/haunted
- cost = 2
+ cost = 1
restricted_roles = list(JOB_NAME_CURATOR)
limited_stock = 1 //please don't spam deadchat
@@ -2264,7 +2299,7 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
name = "Prison Cube"
desc = "A very strange artifact recovered from a volcanic planet that is useful for keeping people locked away, but not very useful for keeping their disappearance unknown"
item = /obj/item/prisoncube
- cost = 6
+ cost = 5
restricted_roles = list(JOB_NAME_CURATOR)
/datum/uplink_item/role_restricted/his_grace
@@ -2274,7 +2309,8 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
However, if left alone for long enough, He will fall back to slumber. \
To activate His Grace, simply unlatch Him."
item = /obj/item/his_grace
- cost = 20
+ cost = 14
+ reputation_required = REPUTATION_ELITE
restricted_roles = list(JOB_NAME_CHAPLAIN)
murderbone_type = TRUE
surplus = 0
@@ -2283,7 +2319,8 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
name = "Cult Construct Kit"
desc = "Recovered from an abandoned Nar'sie cult lair two construct shells and a stash of empty soulstones was found. These were purified to prevent occult contamination and have been put in a belt so they may be used as an accessible source of disposable minions. The construct shells have been packaged into two beacons for rapid and portable deployment."
item = /obj/item/storage/box/syndie_kit/cultconstructkit
- cost = 20
+ cost = 14
+ reputation_required = REPUTATION_ELITE
restricted_roles = list(JOB_NAME_CHAPLAIN)
/datum/uplink_item/role_restricted/shadowmutationtoxin
@@ -2298,7 +2335,8 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
desc = "A bottle of cursed blood, full of angry spirits which will burn all the heretics with the fires of hell. \
At least, that's what the label says"
item = /obj/item/reagent_containers/glass/bottle/fluspanish
- cost = 12
+ cost = 10
+ reputation_required = REPUTATION_EXCELLENT
restricted_roles = list(JOB_NAME_CHAPLAIN, JOB_NAME_VIROLOGIST)
/datum/uplink_item/role_restricted/retrovirus
@@ -2306,7 +2344,8 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
desc = "A bottle of contagious DNA bugs, which will manually rearrange the DNA of hosts. \
At least, that's what the label says."
item = /obj/item/reagent_containers/glass/bottle/retrovirus
- cost = 12
+ cost = 10
+ reputation_required = REPUTATION_EXCELLENT
restricted_roles = list(JOB_NAME_VIROLOGIST, JOB_NAME_GENETICIST)
/datum/uplink_item/role_restricted/anxiety
@@ -2339,12 +2378,13 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
desc = "Fishsticks prepared through ritualistic means in honor of the god Carp-sie, capable of binding a holocarp \
to act as a servant and guardian to their host."
item = /obj/item/holoparasite_creator/carp
- cost = 18
+ cost = 16
surplus = 5
purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS)
player_minimum = 25
restricted = TRUE
restricted_roles = list(JOB_NAME_COOK, JOB_NAME_CHAPLAIN)
+ reputation_required = REPUTATION_EXCELLENT
/datum/uplink_item/role_restricted/ez_clean_bundle
name = "EZ Clean Grenade Bundle"
@@ -2430,7 +2470,7 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
name = "Reverse Revolver"
desc = "A revolver that always fires at its user. \"Accidentally\" drop your weapon, then watch as the greedy corporate pigs blow their own brains all over the wall. \
The revolver itself is actually real. Only clumsy people, and clowns, can fire it normally. Comes in a box of hugs. Honk."
- cost = 13
+ cost = 8
item = /obj/item/storage/box/hug/reverse_revolver
restricted_roles = list(JOB_NAME_CLOWN)
@@ -2485,7 +2525,9 @@ GLOBAL_LIST_INIT(illegal_tech_blacklist, typecacheof(list(
desc = "For showing that you are THE BOSS: A useless red balloon with the Syndicate logo on it. \
Can blow the deepest of covers."
item = /obj/item/toy/syndicateballoon
- cost = 20
+ cost = 8
+ // Only for the best
+ reputation_required = REPUTATION_ELITE
cant_discount = TRUE
illegal_tech = FALSE
surplus = 0
diff --git a/config/dbconfig.txt b/config/dbconfig.txt
index ffcbe885689dd..35443519b1c3e 100644
--- a/config/dbconfig.txt
+++ b/config/dbconfig.txt
@@ -3,17 +3,17 @@
## administration, and the in game library.
## Should SQL be enabled? Uncomment to enable
-#SQL_ENABLED
+SQL_ENABLED
## Server the MySQL database can be found at.
## Examples: localhost, 200.135.5.43, www.mysqldb.com, etc.
-ADDRESS localhost
+ADDRESS 127.0.0.1
## MySQL server port (default is 3306).
PORT 3306
## Database for all SQL functions, not just feedback.
-FEEDBACK_DATABASE ss13beedb
+FEEDBACK_DATABASE beestation13
## Prefix to be added to the name of every table, older databases will require this be set to erro_
## Note, this does not change the table names in the database, you will have to do that yourself.
@@ -24,7 +24,7 @@ FEEDBACK_DATABASE ss13beedb
FEEDBACK_TABLEPREFIX SS13_
## Username/Login used to access the database.
-FEEDBACK_LOGIN ss13dbuser
+FEEDBACK_LOGIN beestation13
## Password used to access the database.
FEEDBACK_PASSWORD password1
@@ -45,4 +45,4 @@ BSQL_THREAD_LIMIT 50
MAX_CONCURRENT_QUERIES 25
## Disable advanced feedback tracking, saving a substantial amount of database space.
-LIMITED_FEEDBACK
+#LIMITED_FEEDBACK
diff --git a/tgui/packages/tgui/interfaces/Uplink.js b/tgui/packages/tgui/interfaces/Uplink.js
index ec160f4f9d6b3..4cf274afdf802 100644
--- a/tgui/packages/tgui/interfaces/Uplink.js
+++ b/tgui/packages/tgui/interfaces/Uplink.js
@@ -104,7 +104,18 @@ const HomePage = (props, context) => {
-
+ Current Reputation
+
+ 200 Reputation
+
+ Uplink Services
+
+ Shop Now
+
+ Special Directives
+
+ None Available
+
diff --git a/tgui/packages/tgui/styles/interfaces/Uplink.scss b/tgui/packages/tgui/styles/interfaces/Uplink.scss
index a2dc5288bdd68..85e8785a7587c 100644
--- a/tgui/packages/tgui/styles/interfaces/Uplink.scss
+++ b/tgui/packages/tgui/styles/interfaces/Uplink.scss
@@ -22,15 +22,6 @@
margin: 10px;
}
-.HomeRight {
- position: absolute;
- right: 0;
- top: 0;
- bottom: 0;
- width: 40%;
- margin: 10px;
-}
-
.HomeTitle {
width: 100%;
font-size: x-large;
@@ -92,6 +83,36 @@
font-weight: bold;
}
+.HomeRight {
+ position: absolute;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ width: 40%;
+ margin: 10px;
+}
+
+.HomeButton {
+ width: 176px;
+ height: 40px;
+ padding: 10px;
+ margin-left: calc(100% - 176px);
+ border-radius: 13px;
+ text-align: center;
+ vertical-align: middle;
+ padding-top: 12px;
+ background-color: red;
+ margin-bottom: 40px;
+ box-shadow: black 3px 3px;
+ margin-top: 2px;
+
+ &:hover {
+ margin-left: calc(100% - 174px);
+ margin-bottom: 40px;
+ box-shadow: black 1px 1px;
+ }
+}
+
.objective_card {
position: relative;
width: 180px;
From b06f5bc42414415f399720e051e5aa14164fac82 Mon Sep 17 00:00:00 2001
From: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com>
Date: Sun, 31 Mar 2024 20:02:40 +0100
Subject: [PATCH 48/62] Adds in ranks and reputation
---
code/datums/components/uplink.dm | 8 +-
tgui/packages/tgui/interfaces/Uplink.js | 93 +++++++++++++++----
.../tgui/styles/interfaces/Uplink.scss | 65 +++++++++++--
3 files changed, 135 insertions(+), 31 deletions(-)
diff --git a/code/datums/components/uplink.dm b/code/datums/components/uplink.dm
index bca06f1635ce4..46e7290ced9e7 100644
--- a/code/datums/components/uplink.dm
+++ b/code/datums/components/uplink.dm
@@ -38,7 +38,7 @@ GLOBAL_LIST_EMPTY(uplinks)
var/list/previous_attempts
/datum/component/uplink/Initialize(datum/mind/_owner,
- _lockable = TRUE
+ _lockable = TRUE,
_enabled = FALSE,
uplink_flag = UPLINK_TRAITORS,
starting_tc = TELECRYSTALS_DEFAULT,
@@ -181,6 +181,7 @@ GLOBAL_LIST_EMPTY(uplinks)
data["lockable"] = lockable
data["compactMode"] = compact_mode
data["objectives"] = SSdirectives.get_uplink_data(src)
+ data["reputation"] = reputation
return data
/datum/component/uplink/ui_static_data(mob/user)
@@ -219,7 +220,8 @@ GLOBAL_LIST_EMPTY(uplinks)
"cost" = I.cost,
"desc" = I.desc,
"is_illegal" = I.illegal_tech,
- "are_contents_illegal" = I.contents_are_illegal_tech
+ "are_contents_illegal" = I.contents_are_illegal_tech,
+ "reputation" = I.reputation_required
))
data["categories"] += list(cat)
return data
@@ -266,6 +268,8 @@ GLOBAL_LIST_EMPTY(uplinks)
if(telecrystals < U.cost || U.limited_stock == 0)
return
+ if (reputation < U.reputation_required)
+ return
telecrystals -= U.cost
U.purchase(user, src)
diff --git a/tgui/packages/tgui/interfaces/Uplink.js b/tgui/packages/tgui/interfaces/Uplink.js
index 4cf274afdf802..629020e1c7180 100644
--- a/tgui/packages/tgui/interfaces/Uplink.js
+++ b/tgui/packages/tgui/interfaces/Uplink.js
@@ -6,15 +6,38 @@ import { Window } from '../layouts';
import '../styles/interfaces/Uplink.scss';
import { NtosRadarMap } from './NtosRadar';
import { Color } from 'common/color';
+import { classes } from 'common/react';
const MAX_SEARCH_RESULTS = 25;
+const reputationLevels = {
+ 0: { name: "Ex-Communicate", description: "A traitor to the cause, betraying their brothers to seek personal gain. Reaching this level will result in halted services and a termination order after 5 minutes." },
+ 100: { name: "Blood Servant", description: "An operative with a reason to act, but without the will to fulfill their greater purpose." },
+ 200: { name: "Field Agent", description: "An operative acting on the ground, with access to some standard equipment that can be used to complete their mission." },
+ 400: { name: "Specialist", description: "An operative with specialized skills and knowledge, granted with additional resources and services for completing critical missions." },
+ 600: { name: "Operative", description: "An experienced operative who has demonstrated competence and effectiveness in completing various missions for the Syndicate." },
+ 800: { name: "Director", description: "A high-ranking official responsible for overseeing and coordinating operations with their team, ensuring the success of its objectives." },
+ 1000: { name: "Archon", description: "A high ranking and secretive authority, possessing unparalleled knowledge, influence, and control over operations and resources." },
+};
+
+
export const Uplink = (props, context) => {
const { data } = useBackend(context);
- const { telecrystals } = data;
+ const { telecrystals, reputation } = data;
const [tab, setTab] = useSharedState(context, 'tab_id', 2);
+ let currentLevel = null;
+
+ // Find the highest reputation level that is less than the current reputation
+ for (const level in reputationLevels) {
+ if (level <= reputation) {
+ currentLevel = reputationLevels[level].name;
+ } else {
+ break;
+ }
+ }
+
return (
@@ -43,9 +66,8 @@ export const Uplink = (props, context) => {
-
- Neutral Reputation
+
+ {currentLevel ? currentLevel : "Neutral Reputation"} ({reputation})
{tab === 0
@@ -306,7 +328,7 @@ const ObjectiveCard = (props, context) => {
export const GenericUplink = (props, context) => {
const { currencyAmount = 0, currencySymbol = 'cr' } = props;
const { act, data } = useBackend(context);
- const { compactMode, lockable, categories = [] } = data;
+ const { compactMode, lockable, categories = [], reputation } = data;
const [searchText, setSearchText] = useLocalState(context, 'searchText', '');
const [selectedCategory, setSelectedCategory] = useLocalState(context, 'category', categories[0]?.name);
const testSearch = createSearch(searchText, (item) => {
@@ -362,6 +384,7 @@ export const GenericUplink = (props, context) => {
{searchText.length === 0 ? 'No items in this category.' : 'No results found.'}
)}
0 || compactMode}
currencyAmount={currencyAmount}
currencySymbol={currencySymbol}
@@ -374,16 +397,16 @@ export const GenericUplink = (props, context) => {
};
const ItemList = (props, context) => {
- const { compactMode, currencyAmount, currencySymbol } = props;
+ const { reputation, compactMode, currencyAmount, currencySymbol } = props;
const { act } = useBackend(context);
const [hoveredItem, setHoveredItem] = useLocalState(context, 'hoveredItem', {});
- const hoveredCost = (hoveredItem && hoveredItem.cost) || 0;
+ const hoveredCost = (hoveredItem && hoveredItem.cost && reputation >= hoveredItem.reputation) || 0;
// Append extra hover data to items
const items = props.items.map((item) => {
const notSameItem = hoveredItem && hoveredItem.name !== item.name;
const notEnoughHovered = currencyAmount - hoveredCost < item.cost;
const disabledDueToHovered = notSameItem && notEnoughHovered;
- const disabled = currencyAmount < item.cost || disabledDueToHovered;
+ const disabled = reputation < item.reputation || currencyAmount < item.cost || disabledDueToHovered;
return {
...item,
disabled,
@@ -422,6 +445,20 @@ const ItemList = (props, context) => {
{items.map((item) => (
{decodeHtmlEntities(item.name)}
+ {item.reputation ? (
+
+ = item.reputation ? "green" : "red"}>
+ {item.reputation} reputation
+
+
+ ) : }
+ {currencyAmount < item.cost ? (
+
+
+ Insufficient Funds
+
+
+ ) : }
{
title={GetTooltipMessage(item.name, item.is_illegal, item.are_contents_illegal)}
level={2}
buttons={
- setHoveredItem(item)}
- onmouseout={() => setHoveredItem({})}
- onClick={() =>
- act('buy', {
- name: item.name,
- })
- }
- />
+
@@ -362,19 +381,30 @@ const ObjectiveCard = (props, context) => {
name: 'Assassination',
reward: 0,
time_left: null,
+ rep_gain: null,
+ rep_loss: null,
},
selected = 0,
onClick,
} = props;
- const { name, reward, time_left } = objective_info;
+ const { name, reward, time_left, rep_gain, rep_loss } = objective_info;
return (
-
+ {capitalize(name)}
- {reward === 0 ? 'Assignment' : reward + ' TC Reward'}
+
+ {(rep_gain || rep_loss) && (
+
+ -{rep_loss}/+{rep_gain} Reputation
+
+ )}
+ {reward === 0 ? 'Assignment' : reward + ' TC Reward'}
+
{time_left === null
diff --git a/tgui/packages/tgui/styles/interfaces/Uplink.scss b/tgui/packages/tgui/styles/interfaces/Uplink.scss
index 256ebda291613..0d688d5e3d805 100644
--- a/tgui/packages/tgui/styles/interfaces/Uplink.scss
+++ b/tgui/packages/tgui/styles/interfaces/Uplink.scss
@@ -138,11 +138,12 @@
right: 5px;
box-shadow: 0 0 14px 8px #00000069;
background-color: #00000069;
+ text-align: right;
}
.time_limit {
position: absolute;
- top: 40%;
+ top: 35%;
right: 0;
left: 0;
font-size: x-large;
From b21b4a4740cf53da96658cedc91abd765d9659f5 Mon Sep 17 00:00:00 2001
From: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com>
Date: Thu, 11 Apr 2024 09:24:41 +0100
Subject: [PATCH 56/62] Revert DB config
---
config/dbconfig.txt | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/config/dbconfig.txt b/config/dbconfig.txt
index 35443519b1c3e..ffcbe885689dd 100644
--- a/config/dbconfig.txt
+++ b/config/dbconfig.txt
@@ -3,17 +3,17 @@
## administration, and the in game library.
## Should SQL be enabled? Uncomment to enable
-SQL_ENABLED
+#SQL_ENABLED
## Server the MySQL database can be found at.
## Examples: localhost, 200.135.5.43, www.mysqldb.com, etc.
-ADDRESS 127.0.0.1
+ADDRESS localhost
## MySQL server port (default is 3306).
PORT 3306
## Database for all SQL functions, not just feedback.
-FEEDBACK_DATABASE beestation13
+FEEDBACK_DATABASE ss13beedb
## Prefix to be added to the name of every table, older databases will require this be set to erro_
## Note, this does not change the table names in the database, you will have to do that yourself.
@@ -24,7 +24,7 @@ FEEDBACK_DATABASE beestation13
FEEDBACK_TABLEPREFIX SS13_
## Username/Login used to access the database.
-FEEDBACK_LOGIN beestation13
+FEEDBACK_LOGIN ss13dbuser
## Password used to access the database.
FEEDBACK_PASSWORD password1
@@ -45,4 +45,4 @@ BSQL_THREAD_LIMIT 50
MAX_CONCURRENT_QUERIES 25
## Disable advanced feedback tracking, saving a substantial amount of database space.
-#LIMITED_FEEDBACK
+LIMITED_FEEDBACK
From 932cbbd0531d7f49ccf8c1ef947ae353a3bbf1b1 Mon Sep 17 00:00:00 2001
From: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com>
Date: Thu, 11 Apr 2024 09:30:12 +0100
Subject: [PATCH 57/62] Runs TGUI uglify
---
tgui/packages/tgui/interfaces/Uplink.js | 192 +++++++++---------
.../tgui/styles/interfaces/Uplink.scss | 10 +-
2 files changed, 102 insertions(+), 100 deletions(-)
diff --git a/tgui/packages/tgui/interfaces/Uplink.js b/tgui/packages/tgui/interfaces/Uplink.js
index 0e7ab328c3bda..945a6381e94c6 100644
--- a/tgui/packages/tgui/interfaces/Uplink.js
+++ b/tgui/packages/tgui/interfaces/Uplink.js
@@ -12,44 +12,50 @@ const MAX_SEARCH_RESULTS = 25;
const reputationLevels = {
0: {
- name: "Ex-Communicate",
- description: "A traitor to the cause, betraying their brothers to seek personal gain. Reaching this level will result in halted services and a termination order after 5 minutes.",
+ name: 'Ex-Communicate',
+ description:
+ 'A traitor to the cause, betraying their brothers to seek personal gain. Reaching this level will result in halted services and a termination order after 5 minutes.',
min_reputation: null,
max_reputation: 99,
},
100: {
- name: "Blood Servant",
- description: "An operative with a reason to act, but without the will to fulfill their greater purpose.",
+ name: 'Blood Servant',
+ description: 'An operative with a reason to act, but without the will to fulfill their greater purpose.',
min_reputation: 100,
max_reputation: 199,
},
200: {
- name: "Field Agent",
- description: "An operative acting on the ground, with access to some standard equipment that can be used to complete their mission.",
+ name: 'Field Agent',
+ description:
+ 'An operative acting on the ground, with access to some standard equipment that can be used to complete their mission.',
min_reputation: 200,
max_reputation: 399,
},
400: {
- name: "Specialist",
- description: "An operative with specialized skills and knowledge, granted with additional resources and services for completing critical missions.",
+ name: 'Specialist',
+ description:
+ 'An operative with specialized skills and knowledge, granted with additional resources and services for completing critical missions.',
min_reputation: 400,
max_reputation: 599,
},
600: {
- name: "Operative",
- description: "An experienced operative who has demonstrated competence and effectiveness in completing various missions for the Syndicate.",
+ name: 'Operative',
+ description:
+ 'An experienced operative who has demonstrated competence and effectiveness in completing various missions for the Syndicate.',
min_reputation: 600,
max_reputation: 799,
},
800: {
- name: "Director",
- description: "A high-ranking official responsible for overseeing and coordinating operations with their team, ensuring the success of its objectives.",
+ name: 'Director',
+ description:
+ 'A high-ranking official responsible for overseeing and coordinating operations with their team, ensuring the success of its objectives.',
min_reputation: 800,
max_reputation: 999,
},
1000: {
- name: "Archon",
- description: "A high ranking and secretive authority, possessing unparalleled knowledge, influence, and control over operations and resources.",
+ name: 'Archon',
+ description:
+ 'A high ranking and secretive authority, possessing unparalleled knowledge, influence, and control over operations and resources.',
min_reputation: 1000,
max_reputation: null,
},
@@ -63,7 +69,7 @@ const GetLevel = (reputation, index_change = 0) => {
for (const level in reputationLevels) {
if (level <= reputation || currentLevel === null) {
currentLevel = reputationLevels[level];
- index ++;
+ index++;
} else {
break;
}
@@ -71,18 +77,15 @@ const GetLevel = (reputation, index_change = 0) => {
// Adjust the index based on the change provided
index += index_change;
- if (index < 0)
- {
+ if (index < 0) {
return {
- name: "",
- description: "There are no lower levels within the Syndicate database.",
+ name: '',
+ description: 'There are no lower levels within the Syndicate database.',
};
- }
- else if (index >= Object.keys(reputationLevels).length)
- {
+ } else if (index >= Object.keys(reputationLevels).length) {
return {
- name: "",
- description: "You are at the highest rank an agent can reach within the Syndicate.",
+ name: '',
+ description: 'You are at the highest rank an agent can reach within the Syndicate.',
};
}
@@ -130,15 +133,21 @@ export const Uplink = (props, context) => {
-
- {currentLevel ? currentLevel : "Neutral Reputation"} ({reputation})
+
+ {currentLevel ? currentLevel : 'Neutral Reputation'} ({reputation})
- {tab === 0
- ?
- : tab === 1
- ?
- : }
+ {tab === 0 ? (
+
+ ) : tab === 1 ? (
+
+ ) : (
+
+ )}
);
@@ -155,9 +164,7 @@ const HomePage = (props, context) => {