diff --git a/code/__DEFINES/atom_hud.dm b/code/__DEFINES/atom_hud.dm
index 2decc918f2..dcdf5aa268 100644
--- a/code/__DEFINES/atom_hud.dm
+++ b/code/__DEFINES/atom_hud.dm
@@ -17,8 +17,9 @@
#define DIAG_TRACK_HUD "13"// Mech tracking beacon
#define DIAG_AIRLOCK_HUD "14"//Airlock shock overlay
#define DIAG_PATH_HUD "15"//Bot path indicators
+#define GLAND_HUD "16"//Gland indicators for abductors
//for antag huds. these are used at the /mob level
-#define ANTAG_HUD "16"
+#define ANTAG_HUD "17"
//by default everything in the hud_list of an atom is an image
//a value in hud_list with one of these will change that behavior
@@ -32,21 +33,22 @@
#define DATA_HUD_MEDICAL_ADVANCED 4
#define DATA_HUD_DIAGNOSTIC_BASIC 5
#define DATA_HUD_DIAGNOSTIC_ADVANCED 6
+#define DATA_HUD_ABDUCTOR 7
//antag HUD defines
-#define ANTAG_HUD_CULT 7
-#define ANTAG_HUD_REV 8
-#define ANTAG_HUD_OPS 9
-#define ANTAG_HUD_WIZ 10
-#define ANTAG_HUD_SHADOW 11
-#define ANTAG_HUD_TRAITOR 12
-#define ANTAG_HUD_NINJA 13
-#define ANTAG_HUD_CHANGELING 14
-#define ANTAG_HUD_ABDUCTOR 15
-#define ANTAG_HUD_DEVIL 16
-#define ANTAG_HUD_SINTOUCHED 17
-#define ANTAG_HUD_SOULLESS 18
-#define ANTAG_HUD_CLOCKWORK 19
-#define ANTAG_HUD_BROTHER 20
+#define ANTAG_HUD_CULT 8
+#define ANTAG_HUD_REV 9
+#define ANTAG_HUD_OPS 10
+#define ANTAG_HUD_WIZ 11
+#define ANTAG_HUD_SHADOW 12
+#define ANTAG_HUD_TRAITOR 13
+#define ANTAG_HUD_NINJA 14
+#define ANTAG_HUD_CHANGELING 15
+#define ANTAG_HUD_ABDUCTOR 16
+#define ANTAG_HUD_DEVIL 17
+#define ANTAG_HUD_SINTOUCHED 18
+#define ANTAG_HUD_SOULLESS 19
+#define ANTAG_HUD_CLOCKWORK 20
+#define ANTAG_HUD_BROTHER 21
// Notification action types
#define NOTIFY_JUMP "jump"
diff --git a/code/datums/hud.dm b/code/datums/hud.dm
index 837e7a6a95..fc6c09be14 100644
--- a/code/datums/hud.dm
+++ b/code/datums/hud.dm
@@ -10,6 +10,7 @@ GLOBAL_LIST_INIT(huds, list(
DATA_HUD_MEDICAL_ADVANCED = new/datum/atom_hud/data/human/medical/advanced(),
DATA_HUD_DIAGNOSTIC_BASIC = new/datum/atom_hud/data/diagnostic/basic(),
DATA_HUD_DIAGNOSTIC_ADVANCED = new/datum/atom_hud/data/diagnostic/advanced(),
+ DATA_HUD_ABDUCTOR = new/datum/atom_hud/abductor(),
ANTAG_HUD_CULT = new/datum/atom_hud/antag(),
ANTAG_HUD_REV = new/datum/atom_hud/antag(),
ANTAG_HUD_OPS = new/datum/atom_hud/antag(),
diff --git a/code/game/data_huds.dm b/code/game/data_huds.dm
index 1afd2953a0..74ac93c023 100644
--- a/code/game/data_huds.dm
+++ b/code/game/data_huds.dm
@@ -60,6 +60,9 @@
/datum/atom_hud/data/bot_path
hud_icons = list(DIAG_PATH_HUD)
+/datum/atom_hud/abductor
+ hud_icons = list(GLAND_HUD)
+
/* MED/SEC/DIAG HUD HOOKS */
/*
diff --git a/code/game/gamemodes/miniantags/abduction/abduction_gear.dm b/code/game/gamemodes/miniantags/abduction/abduction_gear.dm
index 9128847e16..3e7e5f01ef 100644
--- a/code/game/gamemodes/miniantags/abduction/abduction_gear.dm
+++ b/code/game/gamemodes/miniantags/abduction/abduction_gear.dm
@@ -2,6 +2,8 @@
#define VEST_COMBAT 2
#define GIZMO_SCAN 1
#define GIZMO_MARK 2
+#define MIND_DEVICE_MESSAGE 1
+#define MIND_DEVICE_CONTROL 2
//AGENT VEST
/obj/item/clothing/suit/armor/abductor/vest
@@ -278,6 +280,84 @@
if(!istype(I, /obj/item/device/radio/headset))
r.broadcasting = 0 //goddamned headset hacks
+/obj/item/device/abductor/mind_device
+ name = "mental interface device"
+ desc = "A dual-mode tool for directly communicating with sentient brains. It can be used to send a direct message to a target, \
+ or to send a command to a test subject with a charged gland."
+ icon_state = "mind_device_message"
+ item_state = "silencer"
+ lefthand_file = 'icons/mob/inhands/antag/abductor_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/antag/abductor_righthand.dmi'
+ var/mode = MIND_DEVICE_MESSAGE
+
+/obj/item/device/abductor/mind_device/attack_self(mob/user)
+ if(!ScientistCheck(user))
+ return
+
+ if(mode == MIND_DEVICE_MESSAGE)
+ mode = MIND_DEVICE_CONTROL
+ icon_state = "mind_device_control"
+ else
+ mode = MIND_DEVICE_MESSAGE
+ icon_state = "mind_device_message"
+ to_chat(user, "You switch the device to [mode==MIND_DEVICE_MESSAGE? "TRANSMISSION": "COMMAND"] MODE")
+
+/obj/item/device/abductor/mind_device/afterattack(atom/target, mob/living/user, flag, params)
+ if(!ScientistCheck(user))
+ return
+
+ switch(mode)
+ if(MIND_DEVICE_CONTROL)
+ mind_control(target, user)
+ if(MIND_DEVICE_MESSAGE)
+ mind_message(target, user)
+
+/obj/item/device/abductor/mind_device/proc/mind_control(atom/target, mob/living/user)
+ if(iscarbon(target))
+ var/mob/living/carbon/C = target
+ var/obj/item/organ/heart/gland/G = C.getorganslot("heart")
+ if(!istype(G))
+ to_chat(user, "Your target does not have an experimental gland!")
+ return
+ if(!G.mind_control_uses)
+ to_chat(user, "Your target's gland is spent!")
+ return
+ if(G.active_mind_control)
+ to_chat(user, "Your target is already under a mind-controlling influence!")
+ return
+
+ var/command = stripped_input(user, "Enter the command for your target to follow.\
+ Uses Left: [G.mind_control_uses], Duration: [G.mind_control_duration / 10] seconds","Enter command")
+
+ if(!command)
+ return
+
+ if(QDELETED(user) || user.get_active_held_item() != src || loc != user)
+ return
+
+ if(QDELETED(G))
+ return
+
+ G.mind_control(command, user)
+ to_chat(user, "You send the command to your target.")
+
+/obj/item/device/abductor/mind_device/proc/mind_message(atom/target, mob/living/user)
+ if(isliving(target))
+ var/mob/living/L = target
+ if(L.stat == DEAD)
+ to_chat(user, "Your target is dead!")
+ return
+ var/message = stripped_input(user, "Write a message to send to your target's brain.","Enter message")
+ if(!message)
+ return
+ if(QDELETED(L) || L.stat == DEAD)
+ return
+
+ to_chat(L, "You hear a voice in your head saying: [message]")
+ to_chat(user, "You send the message to your target.")
+ log_talk(user,"[key_name(user)] sent an abductor mind message to [L]/[L.ckey]: '[message]'", LOGSAY)
+
+
/obj/item/device/firing_pin/abductor
name = "alien firing pin"
icon_state = "firing_pin_ayy"
diff --git a/code/game/gamemodes/miniantags/abduction/gland.dm b/code/game/gamemodes/miniantags/abduction/gland.dm
index 22edb79c80..508f4e9f13 100644
--- a/code/game/gamemodes/miniantags/abduction/gland.dm
+++ b/code/game/gamemodes/miniantags/abduction/gland.dm
@@ -12,28 +12,68 @@
var/human_only = 0
var/active = 0
+ var/mind_control_uses = 1
+ var/mind_control_duration = 1800
+ var/active_mind_control = FALSE
+
/obj/item/organ/heart/gland/proc/ownerCheck()
if(ishuman(owner))
- return 1
+ return TRUE
if(!human_only && iscarbon(owner))
- return 1
- return 0
+ return TRUE
+ return FALSE
/obj/item/organ/heart/gland/proc/Start()
active = 1
next_activation = world.time + rand(cooldown_low,cooldown_high)
+/obj/item/organ/heart/gland/proc/update_gland_hud()
+ if(!owner)
+ return
+ var/image/holder = owner.hud_list[GLAND_HUD]
+ var/icon/I = icon(owner.icon, owner.icon_state, owner.dir)
+ holder.pixel_y = I.Height() - world.icon_size
+ if(active_mind_control)
+ holder.icon_state = "hudgland_active"
+ else if(mind_control_uses)
+ holder.icon_state = "hudgland_ready"
+ else
+ holder.icon_state = "hudgland_spent"
-/obj/item/organ/heart/gland/Remove(var/mob/living/carbon/M, special = 0)
+/obj/item/organ/heart/gland/proc/mind_control(command, mob/living/user)
+ if(!ownerCheck() || !mind_control_uses || active_mind_control)
+ return
+ mind_control_uses--
+ to_chat(owner, "You suddenly feel an irresistible compulsion to follow an order...")
+ to_chat(owner, "[command]")
+ active_mind_control = TRUE
+ log_admin("[key_name(user)] sent an abductor mind control message to [key_name(owner)]: [command]")
+ update_gland_hud()
+
+ addtimer(CALLBACK(src, .proc/clear_mind_control), mind_control_duration)
+
+/obj/item/organ/heart/gland/proc/clear_mind_control()
+ if(!ownerCheck() || !active_mind_control)
+ return
+ to_chat(owner, "You feel the compulsion fade, and you completely forget about your previous orders.")
+ active_mind_control = FALSE
+
+/obj/item/organ/heart/gland/Remove(mob/living/carbon/M, special = 0)
active = 0
if(initial(uses) == 1)
uses = initial(uses)
+ var/datum/atom_hud/abductor/hud = GLOB.huds[DATA_HUD_ABDUCTOR]
+ hud.remove_from_hud(owner)
+ clear_mind_control()
..()
-/obj/item/organ/heart/gland/Insert(var/mob/living/carbon/M, special = 0)
+/obj/item/organ/heart/gland/Insert(mob/living/carbon/M, special = 0)
..()
if(special != 2 && uses) // Special 2 means abductor surgery
Start()
+ var/datum/atom_hud/abductor/hud = GLOB.huds[DATA_HUD_ABDUCTOR]
+ hud.add_to_hud(owner)
+ update_gland_hud()
/obj/item/organ/heart/gland/on_life()
if(!beating)
@@ -59,6 +99,8 @@
cooldown_high = 400
uses = -1
icon_state = "health"
+ mind_control_uses = 3
+ mind_control_duration = 3000
/obj/item/organ/heart/gland/heals/activate()
to_chat(owner, "You feel curiously revitalized.")
@@ -71,6 +113,8 @@
cooldown_high = 1200
uses = -1
icon_state = "slime"
+ mind_control_uses = 1
+ mind_control_duration = 2400
/obj/item/organ/heart/gland/slime/activate()
to_chat(owner, "You feel nauseous!")
@@ -86,6 +130,8 @@
cooldown_high = 300
uses = -1
icon_state = "mindshock"
+ mind_control_uses = 1
+ mind_control_duration = 6000
/obj/item/organ/heart/gland/mindshock/activate()
to_chat(owner, "You get a headache.")
@@ -103,6 +149,8 @@
uses = -1
human_only = 1
icon_state = "species"
+ mind_control_uses = 5
+ mind_control_duration = 300
/obj/item/organ/heart/gland/pop/activate()
to_chat(owner, "You feel unlike yourself.")
@@ -114,6 +162,8 @@
cooldown_high = 2400
uses = 1
icon_state = "vent"
+ mind_control_uses = 4
+ mind_control_duration = 1800
/obj/item/organ/heart/gland/ventcrawling/activate()
to_chat(owner, "You feel very stretchy.")
@@ -125,6 +175,8 @@
cooldown_high = 2400
uses = 1
icon_state = "viral"
+ mind_control_uses = 1
+ mind_control_duration = 1800
/obj/item/organ/heart/gland/viral/activate()
to_chat(owner, "You feel sick.")
@@ -141,6 +193,8 @@
cooldown_high = 1600
uses = 10
icon_state = "emp"
+ mind_control_uses = 1
+ mind_control_duration = 1800
/obj/item/organ/heart/gland/emp/activate()
to_chat(owner, "You feel a spike of pain in your head.")
@@ -151,6 +205,8 @@
cooldown_high = 900
uses = 10
icon_state = "spider"
+ mind_control_uses = 2
+ mind_control_duration = 2400
/obj/item/organ/heart/gland/spiderman/activate()
to_chat(owner, "You feel something crawling in your skin.")
@@ -164,6 +220,8 @@
icon_state = "egg"
lefthand_file = 'icons/mob/inhands/misc/food_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/food_righthand.dmi'
+ mind_control_uses = 2
+ mind_control_duration = 1800
/obj/item/organ/heart/gland/egg/activate()
to_chat(owner, "You lay an egg!")
@@ -175,6 +233,8 @@
cooldown_low = 200
cooldown_high = 400
uses = -1
+ mind_control_uses = 1
+ mind_control_duration = 450
/obj/item/organ/heart/gland/bloody/activate()
owner.blood_volume -= 20
@@ -192,6 +252,8 @@
cooldown_high = 600
human_only = 1
uses = 1
+ mind_control_uses = 1
+ mind_control_duration = 600
/obj/item/organ/heart/gland/bodysnatch/activate()
to_chat(owner, "You feel something moving around inside you...")
@@ -233,6 +295,8 @@
cooldown_low = 1200
cooldown_high = 1800
uses = -1
+ mind_control_uses = 1
+ mind_control_duration = 800
/obj/item/organ/heart/gland/plasma/activate()
to_chat(owner, "You feel bloated.")
diff --git a/code/game/gamemodes/miniantags/abduction/machinery/console.dm b/code/game/gamemodes/miniantags/abduction/machinery/console.dm
index f243f161c3..0b13c359eb 100644
--- a/code/game/gamemodes/miniantags/abduction/machinery/console.dm
+++ b/code/game/gamemodes/miniantags/abduction/machinery/console.dm
@@ -48,6 +48,7 @@
dat += "Agent Vest
"
dat += "Radio Silencer
"
dat += "Science Tool
"
+ dat += "Mental Interface Device
"
else
dat += "NO EXPERIMENT MACHINE DETECTED
"
@@ -109,6 +110,8 @@
Dispense(/obj/item/device/abductor/gizmo)
if("vest")
Dispense(/obj/item/clothing/suit/armor/abductor/vest)
+ if("mind_device")
+ Dispense(/obj/item/device/abductor/mind_device,cost=2)
updateUsrDialog()
/obj/machinery/abductor/console/proc/TeleporterRetrieve()
diff --git a/code/modules/goonchat/browserassets/css/browserOutput.css b/code/modules/goonchat/browserassets/css/browserOutput.css
index 824c86c69d..3403253c07 100644
--- a/code/modules/goonchat/browserassets/css/browserOutput.css
+++ b/code/modules/goonchat/browserassets/css/browserOutput.css
@@ -390,6 +390,7 @@ h1.alert, h2.alert {color: #000000;}
.memo {color: #638500; text-align: center;}
.memoedit {text-align: center; font-size: 16px;}
.abductor {color: #800080; font-style: italic;}
+.mind_control {color: #A00D6F; font-size: 3; font-weight: bold; font-style: italic;}
.slime {color: #00CED1;}
.drone {color: #848482;}
.monkey {color: #975032;}
diff --git a/code/modules/mob/living/carbon/carbon_defines.dm b/code/modules/mob/living/carbon/carbon_defines.dm
index 4e409391da..d601fde492 100644
--- a/code/modules/mob/living/carbon/carbon_defines.dm
+++ b/code/modules/mob/living/carbon/carbon_defines.dm
@@ -2,6 +2,7 @@
gender = MALE
pressure_resistance = 15
possible_a_intents = list(INTENT_HELP, INTENT_HARM)
+ hud_possible = list(HEALTH_HUD,STATUS_HUD,ANTAG_HUD,GLAND_HUD)
var/list/stomach_contents = list()
var/list/internal_organs = list() //List of /obj/item/organ in the mob. They don't go in the contents for some reason I don't want to know.
var/list/internal_organs_slot= list() //Same as above, but stores "slot ID" - "organ" pairs for easy access.
diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm
index 8e72b26419..716373061d 100644
--- a/code/modules/mob/living/carbon/human/human_defines.dm
+++ b/code/modules/mob/living/carbon/human/human_defines.dm
@@ -1,5 +1,5 @@
/mob/living/carbon/human
- hud_possible = list(HEALTH_HUD,STATUS_HUD,ID_HUD,WANTED_HUD,IMPLOYAL_HUD,IMPCHEM_HUD,IMPTRACK_HUD,ANTAG_HUD)
+ hud_possible = list(HEALTH_HUD,STATUS_HUD,ID_HUD,WANTED_HUD,IMPLOYAL_HUD,IMPCHEM_HUD,IMPTRACK_HUD,ANTAG_HUD,GLAND_HUD)
possible_a_intents = list(INTENT_HELP, INTENT_DISARM, INTENT_GRAB, INTENT_HARM)
pressure_resistance = 25
//Hair colour and style
diff --git a/code/modules/mob/living/carbon/human/species_types/abductors.dm b/code/modules/mob/living/carbon/human/species_types/abductors.dm
index cf03d12130..cd273ec98e 100644
--- a/code/modules/mob/living/carbon/human/species_types/abductors.dm
+++ b/code/modules/mob/living/carbon/human/species_types/abductors.dm
@@ -9,3 +9,13 @@
/datum/species/abductor/copy_properties_from(datum/species/abductor/old_species)
scientist = old_species.scientist
+
+/datum/species/abductor/on_species_gain(mob/living/carbon/C, datum/species/old_species)
+ . = ..()
+ var/datum/atom_hud/abductor_hud = GLOB.huds[DATA_HUD_ABDUCTOR]
+ abductor_hud.add_hud_to(C)
+
+/datum/species/abductor/on_species_loss(mob/living/carbon/C)
+ . = ..()
+ var/datum/atom_hud/abductor_hud = GLOB.huds[DATA_HUD_ABDUCTOR]
+ abductor_hud.remove_hud_from(C)
diff --git a/icons/mob/hud.dmi b/icons/mob/hud.dmi
index 70988dbef5..5e33807ec7 100644
Binary files a/icons/mob/hud.dmi and b/icons/mob/hud.dmi differ
diff --git a/icons/obj/abductor.dmi b/icons/obj/abductor.dmi
index 38edd8dc7d..5602b03f87 100644
Binary files a/icons/obj/abductor.dmi and b/icons/obj/abductor.dmi differ
diff --git a/interface/stylesheet.dm b/interface/stylesheet.dm
index dd23e1807a..e1505d6088 100644
--- a/interface/stylesheet.dm
+++ b/interface/stylesheet.dm
@@ -160,6 +160,7 @@ h1.alert, h2.alert {color: #000000;}
.memo {color: #638500; text-align: center;}
.memoedit {text-align: center; font-size: 2;}
.abductor {color: #800080; font-style: italic;}
+.mind_control {color: #A00D6F; font-size: 3; font-weight: bold; font-style: italic;}
.slime {color: #00CED1;}
.drone {color: #848482;}
.monkey {color: #975032;}