diff --git a/_maps/map_files/generic/CentCom.dmm b/_maps/map_files/generic/CentCom.dmm
index c267f9fb7d..a8f9178a83 100644
--- a/_maps/map_files/generic/CentCom.dmm
+++ b/_maps/map_files/generic/CentCom.dmm
@@ -16794,6 +16794,10 @@
},
/turf/open/indestructible/boss/air,
/area/centcom/holding)
+"Mu" = (
+/obj/machinery/computer/robotics,
+/turf/open/indestructible/hotelwood,
+/area/syndicate_mothership)
"Mv" = (
/obj/structure/table/reinforced,
/obj/item/storage/fancy/donut_box,
@@ -17309,6 +17313,9 @@
},
/turf/open/floor/mineral/titanium/blue,
/area/centcom/holding)
+"OQ" = (
+/turf/open/indestructible/hotelwood,
+/area/syndicate_mothership)
"OS" = (
/obj/structure/mecha_wreckage/mauler,
/turf/open/floor/plasteel/dark,
@@ -17327,10 +17334,23 @@
},
/turf/open/floor/wood,
/area/syndicate_mothership)
+"OY" = (
+/obj/machinery/light{
+ dir = 8
+ },
+/turf/open/indestructible/hotelwood,
+/area/syndicate_mothership)
"OZ" = (
/obj/effect/turf_decal/delivery,
/turf/open/floor/plating,
/area/syndicate_mothership)
+"Pc" = (
+/turf/closed/indestructible{
+ icon = 'icons/turf/walls/wood_wall.dmi';
+ icon_state = "wood";
+ smooth = 1
+ },
+/area/syndicate_mothership)
"Pg" = (
/obj/machinery/light{
dir = 1
@@ -18557,6 +18577,10 @@
},
/turf/open/indestructible/hotelwood,
/area/centcom/holding)
+"WB" = (
+/obj/effect/landmark/start/slaver,
+/turf/open/floor/carpet,
+/area/syndicate_mothership)
"WC" = (
/obj/structure/table/reinforced,
/obj/item/pen,
@@ -18654,6 +18678,10 @@
/obj/item/vending_refill/snack,
/turf/open/indestructible/hotelwood,
/area/centcom/holding)
+"Xc" = (
+/obj/effect/landmark/start/slaver_leader,
+/turf/open/floor/carpet,
+/area/syndicate_mothership)
"Xd" = (
/obj/structure/flora/ausbushes/fernybush,
/obj/structure/flora/ausbushes/fullgrass,
@@ -18771,6 +18799,10 @@
"XN" = (
/turf/open/floor/carpet/red,
/area/centcom/holding)
+"XP" = (
+/obj/machinery/computer/telecrystals,
+/turf/open/indestructible/hotelwood,
+/area/syndicate_mothership)
"XT" = (
/obj/structure/table/wood,
/obj/item/reagent_containers/food/snacks/sashimi,
@@ -18837,6 +18869,10 @@
/obj/structure/dresser,
/turf/open/floor/plasteel/dark,
/area/syndicate_mothership)
+"Yj" = (
+/obj/machinery/computer/slavery,
+/turf/open/indestructible/hotelwood,
+/area/syndicate_mothership)
"Ym" = (
/obj/machinery/computer/arcade/orion_trail,
/turf/open/indestructible/hotelwood,
@@ -53000,24 +53036,24 @@ aa
aa
aa
aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
+Pc
+Pc
+Pc
+Pc
+Pc
+Pc
+Pc
+Pc
+Pc
+Pc
+Pc
+Pc
+Pc
+Pc
+Pc
+Pc
+Pc
+Pc
aa
aa
aa
@@ -53257,24 +53293,24 @@ aa
aa
aa
aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
+Pc
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+Pc
aa
aa
aa
@@ -53514,24 +53550,24 @@ aa
aa
aa
aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
+Pc
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+Pc
aa
aa
aa
@@ -53771,24 +53807,24 @@ aa
aa
aa
aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
+Pc
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OY
+OQ
+Pc
aa
aa
aa
@@ -54028,24 +54064,24 @@ aa
aa
aa
aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
+Pc
+OQ
+OQ
+OQ
+OY
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OY
+OQ
+OQ
+OQ
+OY
+OQ
+Pc
aa
aa
aa
@@ -54285,24 +54321,24 @@ aa
aa
aa
aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
+Pc
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+Pc
aa
aa
aa
@@ -54542,24 +54578,24 @@ aa
aa
aa
aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
+Pc
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+Pc
aa
aa
aa
@@ -54799,24 +54835,24 @@ aa
aa
aa
aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
+Pc
+OQ
+OQ
+OY
+OQ
+Mu
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+Pc
aa
aa
aa
@@ -55056,24 +55092,24 @@ aa
aa
aa
aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
+Pc
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OY
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+Pc
aa
aa
aa
@@ -55313,24 +55349,24 @@ aa
aa
aa
aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
+Pc
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+WB
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+Pc
aa
aa
aa
@@ -55570,24 +55606,24 @@ aa
aa
aa
aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
+Pc
+OQ
+OQ
+OQ
+OQ
+Yj
+OQ
+Xc
+OQ
+WB
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+Pc
aa
aa
aa
@@ -55827,24 +55863,24 @@ aa
aa
aa
aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
+Pc
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+WB
+OQ
+OY
+OQ
+OQ
+OQ
+OQ
+OQ
+Pc
aa
aa
aa
@@ -56084,24 +56120,24 @@ aa
aa
aa
aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
+Pc
+OQ
+OQ
+OY
+OQ
+OQ
+OQ
+OQ
+OQ
+WB
+OQ
+OQ
+OQ
+OQ
+OY
+OQ
+OQ
+Pc
aa
aa
aa
@@ -56341,24 +56377,24 @@ aa
aa
aa
aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
+Pc
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+WB
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+Pc
aa
aa
aa
@@ -56598,24 +56634,24 @@ aa
aa
aa
aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
+Pc
+OQ
+OQ
+OQ
+OQ
+OQ
+XP
+OY
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+Pc
aa
aa
aa
@@ -56855,24 +56891,24 @@ aa
aa
aa
aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
+Pc
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+Pc
aa
aa
aa
@@ -57112,24 +57148,24 @@ aa
aa
aa
aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
+Pc
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+Pc
aa
aa
aa
@@ -57369,24 +57405,24 @@ aa
aa
aa
aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
+Pc
+OQ
+OQ
+OY
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OY
+OQ
+OQ
+OQ
+Pc
aa
aa
aa
@@ -57626,24 +57662,24 @@ aa
aa
aa
aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
+Pc
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+Pc
aa
aa
aa
@@ -57883,24 +57919,24 @@ aa
aa
aa
aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
+Pc
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+OQ
+Pc
aa
aa
aa
@@ -58140,24 +58176,24 @@ aa
aa
aa
aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
-aa
+Pc
+Pc
+Pc
+Pc
+Pc
+Pc
+Pc
+Pc
+Pc
+Pc
+Pc
+Pc
+Pc
+Pc
+Pc
+Pc
+Pc
+Pc
aa
aa
aa
diff --git a/code/__DEFINES/access.dm b/code/__DEFINES/access.dm
index c2021a86f2..a7b690e210 100644
--- a/code/__DEFINES/access.dm
+++ b/code/__DEFINES/access.dm
@@ -83,6 +83,7 @@
//The Syndicate
#define ACCESS_SYNDICATE 150//General Syndicate Access. Includes Syndicate mechs and ruins.
#define ACCESS_SYNDICATE_LEADER 151//Nuke Op Leader Access
+#define ACCESS_SLAVER 152 //Slaver traders
//Away Missions or Ruins
/*For generic away-mission/ruin access. Why would normal crew have access to a long-abandoned derelict
diff --git a/code/__DEFINES/atom_hud.dm b/code/__DEFINES/atom_hud.dm
index 386473a3ae..d060944d70 100644
--- a/code/__DEFINES/atom_hud.dm
+++ b/code/__DEFINES/atom_hud.dm
@@ -60,7 +60,8 @@
#define ANTAG_HUD_BROTHER 23
#define ANTAG_HUD_BLOODSUCKER 24
#define ANTAG_HUD_FUGITIVE 25
-#define ANTAG_HUD_HERETIC 26
+#define ANTAG_HUD_HERETIC 26
+#define ANTAG_HUD_SLAVER 27
// Notification action types
#define NOTIFY_JUMP "jump"
diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm
index 19ccf5fdc1..7fd88c5fe1 100644
--- a/code/__DEFINES/role_preferences.dm
+++ b/code/__DEFINES/role_preferences.dm
@@ -9,6 +9,7 @@
#define ROLE_SYNDICATE "Syndicate"
#define ROLE_TRAITOR "traitor"
#define ROLE_OPERATIVE "operative"
+#define ROLE_SLAVER "slaver"
#define ROLE_CHANGELING "changeling"
#define ROLE_WIZARD "wizard"
#define ROLE_MALF "malf AI"
@@ -55,6 +56,7 @@ GLOBAL_LIST_INIT(special_roles, list(
ROLE_TRAITOR = /datum/game_mode/traitor,
ROLE_BROTHER = /datum/game_mode/traitor/bros,
ROLE_OPERATIVE = /datum/game_mode/nuclear,
+ ROLE_SLAVER,
ROLE_CHANGELING = /datum/game_mode/changeling,
ROLE_WIZARD = /datum/game_mode/wizard,
ROLE_MALF,
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index 811ad111c4..40abb10ff7 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -335,6 +335,7 @@
#define DOMAIN_TRAIT "domain"
#define NUKEOP_TRAIT "nuke-op"
#define CLOWNOP_TRAIT "clown-op"
+#define SLAVER_TRAIT "slaver"
#define MEGAFAUNA_TRAIT "megafauna"
#define DEATHSQUAD_TRAIT "deathsquad"
#define SLIMEPUDDLE_TRAIT "slimepuddle"
diff --git a/code/_globalvars/lists/mapping.dm b/code/_globalvars/lists/mapping.dm
index 1e3f8d25eb..c831edba85 100644
--- a/code/_globalvars/lists/mapping.dm
+++ b/code/_globalvars/lists/mapping.dm
@@ -21,6 +21,8 @@ GLOBAL_LIST_EMPTY(jobspawn_overrides) //These will take precedence over norm
GLOBAL_LIST_EMPTY(wizardstart)
GLOBAL_LIST_EMPTY(nukeop_start)
GLOBAL_LIST_EMPTY(nukeop_leader_start)
+GLOBAL_LIST_EMPTY(slaver_start)
+GLOBAL_LIST_EMPTY(slaver_leader_start)
GLOBAL_LIST_EMPTY(newplayer_start)
GLOBAL_LIST_EMPTY(prisonwarp) //prisoners go to these
GLOBAL_LIST_EMPTY(holdingfacility) //captured people go here
diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm
index 9abc37c7e4..662e122419 100644
--- a/code/_globalvars/lists/objects.dm
+++ b/code/_globalvars/lists/objects.dm
@@ -26,6 +26,7 @@ GLOBAL_LIST_EMPTY(rcd_list) //list of Rapid Construction Devices.
GLOBAL_LIST_EMPTY(apcs_list) //list of all Area Power Controller machines, separate from machines for powernet speeeeeeed.
GLOBAL_LIST_EMPTY(tracked_implants) //list of all current implants that are tracked to work out what sort of trek everyone is on. Sadly not on lavaworld not implemented...
GLOBAL_LIST_EMPTY(tracked_chem_implants) //list of implants the prisoner console can track and send inject commands too
+GLOBAL_LIST_EMPTY(tracked_slaves) //list of mobs with slaver collars equipped.
GLOBAL_LIST_EMPTY(poi_list) //list of points of interest for observe/follow
GLOBAL_LIST_EMPTY(pinpointer_list) //list of all pinpointers. Used to change stuff they are pointing to all at once.
GLOBAL_LIST_EMPTY(zombie_infection_list) // A list of all zombie_infection organs, for any mass "animation"
diff --git a/code/datums/hud.dm b/code/datums/hud.dm
index 1c23d5f2a0..fcf13cac4c 100644
--- a/code/datums/hud.dm
+++ b/code/datums/hud.dm
@@ -29,7 +29,8 @@ GLOBAL_LIST_INIT(huds, list(
ANTAG_HUD_BROTHER = new/datum/atom_hud/antag/hidden(),
ANTAG_HUD_BLOODSUCKER = new/datum/atom_hud/antag/bloodsucker(),
ANTAG_HUD_FUGITIVE = new/datum/atom_hud/antag(),
- ANTAG_HUD_HERETIC = new/datum/atom_hud/antag/hidden()
+ ANTAG_HUD_HERETIC = new/datum/atom_hud/antag/hidden(),
+ ANTAG_HUD_SLAVER = new/datum/atom_hud/antag(),
))
/datum/atom_hud
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index fe8a7db23d..33d640c4dd 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -234,6 +234,12 @@
remove_antag_datum(nuke.type)
special_role = null
+/datum/mind/proc/remove_slaver()
+ var/datum/antagonist/slaver/slaver = has_antag_datum(/datum/antagonist/slaver,TRUE)
+ if(slaver)
+ remove_antag_datum(slaver.type)
+ special_role = null
+
/datum/mind/proc/remove_wizard()
remove_antag_datum(/datum/antagonist/wizard)
special_role = null
@@ -263,6 +269,7 @@
remove_changeling()
remove_traitor()
remove_nukeop()
+ remove_slaver()
remove_wizard()
remove_cultist()
remove_rev()
@@ -367,6 +374,13 @@
N.nukeop_outfit = null
add_antag_datum(N,converter.nuke_team)
+ else if(is_slaver(creator))
+ var/datum/antagonist/slaver/converter = creator.mind.has_antag_datum(/datum/antagonist/slaver,TRUE)
+ var/datum/antagonist/slaver/S = new()
+ S.send_to_spawnpoint = FALSE
+ S.slaver_outfit = null
+ add_antag_datum(S,converter.slaver_team)
+
enslaved_to = creator
diff --git a/code/game/gamemodes/bloodsucker/bloodsucker.dm b/code/game/gamemodes/bloodsucker/bloodsucker.dm
index aa8edeaa72..6acedb0d52 100644
--- a/code/game/gamemodes/bloodsucker/bloodsucker.dm
+++ b/code/game/gamemodes/bloodsucker/bloodsucker.dm
@@ -7,7 +7,7 @@
// LISTS //
var/list/vassal_allowed_antags = list(/datum/antagonist/brother, /datum/antagonist/traitor, /datum/antagonist/traitor/internal_affairs, /datum/antagonist/survivalist, \
- /datum/antagonist/rev, /datum/antagonist/nukeop, /datum/antagonist/pirate, /datum/antagonist/cult, /datum/antagonist/abductee, /datum/antagonist/valentine, /datum/antagonist/heartbreaker,)
+ /datum/antagonist/rev, /datum/antagonist/nukeop, /datum/antagonist/slaver, /datum/antagonist/pirate, /datum/antagonist/cult, /datum/antagonist/abductee, /datum/antagonist/valentine, /datum/antagonist/heartbreaker,)
// The antags you're allowed to be if turning Vassal.
/proc/AmBloodsucker(mob/living/M, falseIfInDisguise = FALSE)
diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm
index 1c6c444fb4..193ab47ad1 100644
--- a/code/game/gamemodes/objective.dm
+++ b/code/game/gamemodes/objective.dm
@@ -557,6 +557,15 @@ If not set, defaults to check_completion instead. Set it. It's used by cryo.
return TRUE
return FALSE
+/datum/objective/slaver
+ name = "slaver"
+ explanation_text = "Capture some of the station crew. Bring them back to the hideout."
+
+/datum/objective/slaver/check_completion()
+ // if(SSticker && SSticker.mode && SSticker.mode.station_was_nuked)
+ // return TRUE
+ return FALSE
+
GLOBAL_LIST_EMPTY(possible_items)
/datum/objective/steal
name = "steal"
diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm
index 822302789f..df3fb00f61 100755
--- a/code/game/machinery/computer/communications.dm
+++ b/code/game/machinery/computer/communications.dm
@@ -376,6 +376,7 @@
data["authorizeName"] = authorize_name
data["canLogOut"] = !issilicon(user)
data["shuttleCanEvacOrFailReason"] = SSshuttle.canEvac(user, TRUE)
+ data["availableSlaves"] = TRUE // GLOB.tracked_slaves
if (authenticated_as_non_silicon_captain(user))
data["canRequestNuke"] = TRUE
diff --git a/code/game/machinery/computer/slavery.dm b/code/game/machinery/computer/slavery.dm
new file mode 100644
index 0000000000..c452063621
--- /dev/null
+++ b/code/game/machinery/computer/slavery.dm
@@ -0,0 +1,117 @@
+/obj/machinery/computer/slavery
+ name = "\improper slave management console"
+ desc = "Used to track and manage collared slaves. The limited range reaches only as far as the hideout perimeter."
+ icon_screen = "explosive"
+ icon_keyboard = "security_key"
+ clockwork = TRUE
+ resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
+ req_access = list(ACCESS_SLAVER)
+ light_color = LIGHT_COLOR_RED
+
+// /obj/machinery/computer/slavery/ui_interact(mob/user)
+// . = ..()
+// var/dat = ""
+// dat += "Refresh.
"
+// dat += "[storedcrystals] telecrystals are available for distribution.
"
+// dat += "
"
+
+// dat += "
Slaves
"
+// for(var/obj/item/electropack/shockcollar/slave/S in GLOB.tracked_slaves)
+// if(!isliving(loc))
+// continue
+// Tr = get_turf(T.imp_in)
+// if((Tr) && (Tr.z != src.z))
+// continue//Out of range
+
+// var/loc_display = "Unknown"
+// var/mob/living/M = T.imp_in
+// if(is_station_level(Tr.z) && !isspaceturf(M.loc))
+// var/turf/mob_loc = get_turf(M)
+// loc_display = mob_loc.loc
+
+// dat += "ID: [T.imp_in.name] | Location: [loc_display]
"
+// dat += "(Message Holder) |
"
+// dat += "********************************
"
+// dat += "
{Log Out}"
+
+/obj/machinery/computer/slavery/ui_interact(mob/user, datum/tgui/ui)
+ // priority_announce("Message test 123 123 Message test 123 123 Message test 123 123 Message test 123 123 Message test 123 123.", sender_override = "Hail from the [slaver_team.slaver_crew_name]")
+ // if(!GLOB.bounties_list.len)
+ // setup_bounties()
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "SlaveConsole", name)
+ ui.open()
+
+/obj/machinery/computer/slavery/ui_data(mob/user)
+ var/list/data = list()
+
+ var/turf/curr = get_turf(src)
+ data["currentCoords"] = "[curr.x], [curr.y], [curr.z]"
+
+ var/list/slaves = list()
+ data["slaves"] = list()
+
+ for(var/tracked_slave in GLOB.tracked_slaves)
+ var/obj/item/electropack/shockcollar/slave/C = tracked_slave
+ if (!isliving(C.loc))
+ continue;
+
+ var/mob/living/L = C.loc
+ var/turf/pos = get_turf(L)
+ if(!pos || pos.z != curr.z || C == L.get_item_by_slot(SLOT_NECK))
+ continue
+
+ var/list/slave = list()
+ slave["id"] = C.collarID
+ priority_announce("Getting collar ID first: [C.GetID()] end.", sender_override = "toggleBought")
+ slave["name"] = L.real_name
+ slave["bought"] = C.bought
+
+ slave["coords"] = "[pos.x], [pos.y], [pos.z]"
+ slave["dist"] = max(get_dist(curr, pos), 0) //Distance between the machine and slave turfs
+ slave["degrees"] = round(Get_Angle(curr, pos)) //0-360 degree directional bearing, for more precision.
+ slaves += list(slave) //Add this slave to the list of slaves
+ data["slaves"] = slaves
+ return data
+
+/obj/machinery/computer/slavery/ui_act(action, params)
+ if(..())
+ return
+
+ switch(action)
+ if ("makePriorityAnnouncement")
+ priority_announce("Announcement.", sender_override = "Beep Beep upgate")
+ // var/is_ai = issilicon(user)
+ // if(!SScommunications.can_announce(user, is_ai))
+ // to_chat(user, "Intercomms recharging. Please stand by.")
+ // return
+ // var/input = stripped_input(user, "Please choose a message to announce to the station crew.", "What?")
+ // if(!input || !user.canUseTopic(src, !issilicon(usr)))
+ // return
+ // if(!(user.can_speak())) //No more cheating, mime/random mute guy!
+ // input = "..."
+ // to_chat(user, "You find yourself unable to speak.")
+ // else
+ // input = user.treat_message(input) //Adds slurs and so on. Someone should make this use languages too.
+ // SScommunications.make_announcement(user, is_ai, input)
+ // deadchat_broadcast(" made a priority announcement from [get_area_name(usr, TRUE)].", "[user.real_name]", user)
+
+ if("purchaseSupplies")
+ priority_announce("Purchase Supplies.", sender_override = "Beep Beep upgate")
+
+ if("toggleBought")
+ var/boughtID = text2num(params["id"])
+ priority_announce("Collar ID is: [boughtID] end.", sender_override = "toggleBought")
+
+ for(var/tracked_slave in GLOB.tracked_slaves)
+ var/obj/item/electropack/shockcollar/slave/C = tracked_slave
+ if (C.collarID == boughtID)
+ if (!isliving(C.loc))
+ priority_announce("Not living location", sender_override = "toggleBought")
+ continue;
+
+ var/mob/living/L = C.loc
+ priority_announce("Slave ckey is: [L.ckey] end.", sender_override = "toggleBought")
+
+
diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm
index 8ff4017b8c..c87b1f1133 100644
--- a/code/game/objects/effects/landmarks.dm
+++ b/code/game/objects/effects/landmarks.dm
@@ -270,6 +270,30 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark)
GLOB.nukeop_leader_start += loc
return INITIALIZE_HINT_QDEL
+
+
+/obj/effect/landmark/start/slaver
+ name = "slaver"
+ icon = 'icons/effects/landmarks_static.dmi'
+ icon_state = "sslaver_spawn"
+
+/obj/effect/landmark/start/slaver/Initialize()
+ ..()
+ GLOB.slaver_start += loc
+ return INITIALIZE_HINT_QDEL
+
+/obj/effect/landmark/start/slaver_leader
+ name = "slaver leader"
+ icon = 'icons/effects/landmarks_static.dmi'
+ icon_state = "sslaver_leader_spawn"
+
+/obj/effect/landmark/start/slaver_leader/Initialize()
+ ..()
+ GLOB.slaver_leader_start += loc
+ return INITIALIZE_HINT_QDEL
+
+
+
// Must be immediate because players will
// join before SSatom initializes everything.
INITIALIZE_IMMEDIATE(/obj/effect/landmark/start/new_player)
diff --git a/code/game/objects/items/devices/radio/electropack.dm b/code/game/objects/items/devices/radio/electropack.dm
index c51edc8106..852316b02b 100644
--- a/code/game/objects/items/devices/radio/electropack.dm
+++ b/code/game/objects/items/devices/radio/electropack.dm
@@ -202,3 +202,21 @@
if(action == "power") // DO. NOT.
return FALSE
return ..()
+
+/obj/item/electropack/shockcollar/slave
+ name = "slave collar"
+ desc = "A reinforced metal collar. This one has a shock element and tracker installed."
+ var/static/lastID = 0;
+ var/collarID
+ var/bought = FALSE
+
+/obj/item/electropack/shockcollar/slave/Initialize()
+ . = ..()
+ collarID = ++lastID
+ GLOB.tracked_slaves += src
+ priority_announce("New pack", sender_override = "Beep Beep upgate")
+
+/obj/item/electropack/shockcollar/slave/Destroy()
+ . = ..()
+ GLOB.tracked_slaves -= src
+ priority_announce("pack deleted", sender_override = "Beep Beep upgate")
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index a8a7e51611..fd48d5015e 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -113,6 +113,14 @@
else
message_admins("[key_name_admin(usr)] tried to create a nuke team. Unfortunately, there were not enough candidates available.")
log_admin("[key_name(usr)] failed to create a nuke team.")
+ if("slaver")
+ message_admins("[key_name(usr)] is creating a slaver trader crew...")
+ if(src.makeSlaverTeam())
+ message_admins("[key_name(usr)] created a slave trader crew.")
+ log_admin("[key_name(usr)] created a slave trader crew.")
+ else
+ message_admins("[key_name_admin(usr)] tried to create a slave trader crew. Unfortunately, there were not enough candidates available.")
+ log_admin("[key_name(usr)] failed to create a slave trader crew.")
if("ninja")
message_admins("[key_name(usr)] spawned a ninja.")
log_admin("[key_name(usr)] spawned a ninja.")
diff --git a/code/modules/admin/verbs/one_click_antag.dm b/code/modules/admin/verbs/one_click_antag.dm
index 6c43d61cce..26c56fbeeb 100644
--- a/code/modules/admin/verbs/one_click_antag.dm
+++ b/code/modules/admin/verbs/one_click_antag.dm
@@ -19,6 +19,7 @@
Make Blob
Make Wizard (Requires Ghosts)
Make Nuke Team (Requires Ghosts)
+ Make Slave Trader Crew (Requires Ghosts)
Make CentCom Response Team (Requires Ghosts)
Make Abductor Team (Requires Ghosts)
Make Revenant (Requires Ghost)
@@ -254,6 +255,50 @@
return 0
+//Abductors
+///datum/admins/proc/makeAbductorTeam()
+// new /datum/round_event/ghost_role/abductor
+// return 1
+
+/datum/admins/proc/makeSlaverTeam()
+ var/list/mob/candidates = pollGhostCandidates("Do you wish to be considered for the slave trader crew entering the sector?", ROLE_SLAVER)
+ var/list/mob/chosen = list()
+ var/mob/theghost = null
+
+ if(candidates.len)
+ var/numslavers = 5
+ var/slavercount = 0
+
+ for(var/i = 0, iYou are a slave trader!")
+ owner.announce_objectives()
+
+/datum/antagonist/slaver/on_gain()
+ forge_objectives()
+ . = ..()
+ equip_slaver()
+ if(send_to_spawnpoint)
+ move_to_spawnpoint()
+
+/datum/antagonist/slaver/get_team()
+ return slaver_team
+
+/datum/antagonist/slaver/proc/forge_objectives()
+ objectives |= slaver_team.objectives
+
+/datum/antagonist/slaver/proc/move_to_spawnpoint()
+ owner.current.forceMove(GLOB.slaver_start[(0 % GLOB.slaver_start.len) + 1])
+
+/datum/antagonist/slaver/leader/move_to_spawnpoint()
+ owner.current.forceMove(pick(GLOB.slaver_leader_start))
+
+/datum/antagonist/slaver/admin_add(datum/mind/new_owner,mob/admin)
+ new_owner.assigned_role = ROLE_SLAVER
+ new_owner.add_antag_datum(src)
+ message_admins("[key_name_admin(admin)] has slaver'd [new_owner.current].")
+ log_admin("[key_name(admin)] has slaver'd [new_owner.current].")
+
+/datum/antagonist/slaver/get_admin_commands()
+ . = ..()
+ .["Send to base"] = CALLBACK(src,.proc/admin_send_to_base)
+
+/datum/antagonist/slaver/proc/admin_send_to_base(mob/admin)
+ owner.current.forceMove(pick(GLOB.slaver_start))
+
+/datum/antagonist/slaver/leader
+ name = "Slave Master"
+ slaver_outfit = /datum/outfit/slaver/leader
+
+/datum/antagonist/slaver/leader/greet()
+ owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/ops.ogg',100,0)
+ to_chat(owner, "You are the Slave Master for this collection job. You are responsible for organising your team and your ID is the only one who can open the launch bay doors.")
+ to_chat(owner, "If you feel you are not up to this task, give your ID to another slaver.")
+ owner.announce_objectives()
+ addtimer(CALLBACK(src, .proc/slavers_name_assign), 1)
+
+
+/datum/antagonist/slaver/leader/proc/slavers_name_assign()
+ slaver_team.slaver_crew_name = ask_name()
+ // priority_announce("Message test 123 123 Message test 123 123 Message test 123 123 Message test 123 123 Message test 123 123.", sender_override = "Hail from the [slaver_team.slaver_crew_name]")
+
+/datum/antagonist/slaver/leader/proc/ask_name()
+ var/defaultname = slaver_team.slaver_crew_name
+ var/newname = stripped_input(owner.current, "You are the slave master. Please choose a name for your crew.", "Crew name", defaultname)
+ if (!newname)
+ newname = defaultname
+ else
+ newname = reject_bad_name(newname)
+ if(!newname)
+ newname = defaultname
+
+ return capitalize(newname)
+
+/datum/team/slavers
+ var/slaver_crew_name = "Slave Traders"
+ var/core_objective = /datum/objective/slaver
+
+/datum/team/slavers/proc/update_objectives()
+ if(core_objective)
+ var/datum/objective/O = new core_objective
+ O.team = src
+ objectives += O
+
+/datum/team/pirate/roundend_report()
+ var/list/parts = list()
+
+ parts += ""
+
+ var/all_dead = TRUE
+ for(var/datum/mind/M in members)
+ if(considered_alive(M))
+ all_dead = FALSE
+ parts += printplayerlist(members)
+
+ parts += "Loot stolen: "
+ var/datum/objective/loot/L = locate() in objectives
+ parts += L.loot_listing()
+ parts += "Total loot value : [L.get_loot_value()]/[L.target_value] credits"
+
+ if(L.check_completion() && !all_dead)
+ parts += "The pirate crew was successful!"
+ else
+ parts += "The pirate crew has failed."
+
+ return "[parts.Join("
")]
"
+
+/datum/team/slavers/roundend_report()
+ var/list/parts = list()
+ parts += ""
+
+ var/text = "
"
+ var/purchases = ""
+ var/TC_uses = 0
+ LAZYINITLIST(GLOB.uplink_purchase_logs_by_key)
+ for(var/I in members)
+ var/datum/mind/syndicate = I
+ var/datum/uplink_purchase_log/H = GLOB.uplink_purchase_logs_by_key[syndicate.key]
+ if(H)
+ TC_uses += H.total_spent
+ purchases += H.generate_render(show_key = FALSE)
+ text += printplayerlist(members)
+ text += "
"
+ text += "(Syndicates used [TC_uses] TC) [purchases]"
+
+ parts += text
+
+ return "[parts.Join("
")]
"
+
+/datum/team/slavers/antag_listing_name()
+ if(slaver_crew_name)
+ return "[slaver_crew_name] Syndico"
+ else
+ return "Syndicaa"
+
+/datum/team/slavers/is_gamemode_hero()
+ return SSticker.mode.name == "slave hunters"
+
+/proc/is_slaver(mob/M)
+ return M && istype(M) && M.mind && M.mind.has_antag_datum(/datum/antagonist/slaver)
+
+// /datum/round_event_control/operative
+// name = "Slave Traders"
+// typepath = /datum/round_event/ghost_role/slavers
+// weight = 0 //Admin only
+// max_occurrences = 1
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index 5bfc6fe652..757d8847f1 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -327,6 +327,9 @@ It's fairly easy to fix if dealing with single letters but not so much with comp
if("nuclear")
if(M.mind.has_antag_datum(/datum/antagonist/nukeop,TRUE))
return 2
+ if("slaver")
+ if(M.mind.has_antag_datum(/datum/antagonist/slaver,TRUE))
+ return 2
if("changeling")
if(M.mind.has_antag_datum(/datum/antagonist/changeling,TRUE))
return 2
diff --git a/icons/mob/hud.dmi b/icons/mob/hud.dmi
index 1d2007fd63..db5371f95d 100644
Binary files a/icons/mob/hud.dmi and b/icons/mob/hud.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index 0be30ab1ed..b0a25d6f09 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -946,6 +946,7 @@
#include "code\game\machinery\computer\pod.dm"
#include "code\game\machinery\computer\robot.dm"
#include "code\game\machinery\computer\security.dm"
+#include "code\game\machinery\computer\slavery.dm"
#include "code\game\machinery\computer\station_alert.dm"
#include "code\game\machinery\computer\telecrystalconsoles.dm"
#include "code\game\machinery\computer\teleporter.dm"
@@ -1777,6 +1778,8 @@
#include "code\modules\antagonists\slaughter\slaughter.dm"
#include "code\modules\antagonists\slaughter\slaughter_antag.dm"
#include "code\modules\antagonists\slaughter\slaughterevent.dm"
+#include "code\modules\antagonists\slaver\slaver.dm"
+#include "code\modules\antagonists\slaver\equipment\slaver_outfits.dm"
#include "code\modules\antagonists\space_dragon\space_dragon.dm"
#include "code\modules\antagonists\space_ninja\space_ninja.dm"
#include "code\modules\antagonists\survivalist\survivalist.dm"
diff --git a/tgui/packages/tgui/interfaces/CommunicationsConsole.js b/tgui/packages/tgui/interfaces/CommunicationsConsole.js
index a9b07a8f36..6350c43d55 100644
--- a/tgui/packages/tgui/interfaces/CommunicationsConsole.js
+++ b/tgui/packages/tgui/interfaces/CommunicationsConsole.js
@@ -1,7 +1,7 @@
import { sortBy } from "common/collections";
import { capitalize } from "common/string";
import { useBackend, useLocalState } from "../backend";
-import { Blink, Box, Button, Dimmer, Flex, Icon, Input, Modal, Section, TextArea } from "../components";
+import { Blink, Box, Button, Dimmer, Flex, Stack, Icon, Input, Modal, Section, TextArea, LabeledList } from "../components";
import { Window } from "../layouts";
import { sanitizeText } from "../sanitize";
@@ -317,6 +317,7 @@ export const PageMain = (props, context) => {
shuttleCanEvacOrFailReason,
shuttleLastCalled,
shuttleRecallable,
+ availableSlaves,
} = data;
const [callingShuttle, setCallingShuttle] = useLocalState(
@@ -472,6 +473,56 @@ export const PageMain = (props, context) => {
+ {availableSlaves && (
+
+
+
+ John Smith
+
+
+
+
+
+
+
+
+ Tkyohamtka Retkhohoobaaashnn
+
+
+
+
+
+
+
+
+
+ Abbytha Kushanka
+
+
+
+
+
+
+ )}
+
{!!canMessageAssociates && messagingAssociates && map(parseFloat)(coords.split(', '));
+
+export const SlaveConsole = (props, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ currentCoords,
+ } = data;
+ const slaves = flow([
+ map((slave, index) => {
+ // Calculate distance to the target. BYOND distance is capped to 127,
+ // that's why we roll our own calculations here.
+ const dist = slave.dist && (
+ Math.round(vecLength(vecSubtract(
+ coordsToVec(currentCoords),
+ coordsToVec(slave.coords))))
+ );
+ return { ...slave, dist, index };
+ }),
+ sortBy(
+ slave => slave.name), // Sort alphabetically
+ ])(data.slaves || []);
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {slaves.map(slave => (
+
+
+
+ {slave.name}
+
+
+
+ {slave.degrees !== undefined && (
+
+ )}
+ {slave.dist !== undefined && (
+ slave.dist + 'm'
+ )}
+ act("makePriorityAnnouncement")} />
+
+
+
+ ))}
+
+
+
+
+
+ {slaves.map(slave => (
+
+
+
+ {slave.name}
+
+
+
+ {slave.degrees !== undefined && (
+
+ )}
+ {slave.dist !== undefined && (
+ slave.dist + 'm'
+ )}
+
+
+
+ act("makePriorityAnnouncement")} />
+
+
+
+ act('toggleBought', {
+ id: slave.id,
+ })} />
+
+
+
+ ))}
+
+
+
+
+
+ {slaves.map(slave => (
+
+
+
+ {slave.name}
+
+
+
+ {slave.degrees !== undefined && (
+
+ )}
+ {slave.dist !== undefined && (
+ slave.dist + 'm'
+ )}
+
+
+
+ act("makePriorityAnnouncement")} />
+
+
+
+ act('toggleBought', {
+ id: slave.id,
+ })} />
+
+
+
+ ))}
+
+
+
+
+ );
+};