diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index c45b41afa84..4cbeab8f995 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -57,6 +57,9 @@
#define TRAIT_PARALYSIS_R_LEG "para-r-leg"
#define TRAIT_NOMOBSWAP "no-mob-swap"
#define TRAIT_XRAY_VISION "xray_vision"
+#define TRAIT_ABDUCTOR_TRAINING "abductor-training"
+#define TRAIT_ABDUCTOR_SCIENTIST_TRAINING "abductor-scientist-training"
+#define TRAIT_SURGEON "surgeon"
//non-mob traits
#define TRAIT_PARALYSIS "paralysis" //Used for limb-based paralysis, where replacing the limb will fix it
@@ -102,3 +105,4 @@
#define EYES_COVERED "eyes_covered"
#define CULT_EYES "cult_eyes"
#define SCRYING_ORB "scrying-orb"
+#define ABDUCTOR_ANTAGONIST "abductor-antagonist"
diff --git a/code/datums/traits.dm b/code/datums/traits.dm
index c105865b11e..15715c3c888 100644
--- a/code/datums/traits.dm
+++ b/code/datums/traits.dm
@@ -1,16 +1,16 @@
/datum/proc/add_trait(trait, source)
LAZYINITLIST(status_traits)
-
+
if(!status_traits[trait])
status_traits[trait] = list(source)
else
status_traits[trait] |= list(source)
-
+
/datum/proc/remove_trait(trait, list/sources, force)
if(!status_traits)
return //nothing to remove
-
+
if(!status_traits[trait])
return
@@ -33,11 +33,11 @@
if(!LAZYLEN(status_traits[trait]))
status_traits -= trait
-
+
/datum/proc/has_trait(trait, list/sources)
if(!status_traits)
return FALSE //well of course it doesn't have the trait
-
+
if(!status_traits[trait])
return FALSE
@@ -51,11 +51,11 @@
return TRUE
else if(LAZYLEN(status_traits[trait]))
return TRUE
-
+
/datum/proc/remove_all_traits(remove_species_traits = FALSE, remove_organ_traits = FALSE, remove_quirks = FALSE)
if(!status_traits)
return //nothing to remove
-
+
var/list/blacklisted_sources = list()
if(!remove_species_traits)
blacklisted_sources += SPECIES_TRAIT
@@ -72,4 +72,4 @@
break
if(!skip)
remove_trait(kebab, null, TRUE)
- CHECK_TICK
\ No newline at end of file
+ CHECK_TICK
diff --git a/code/modules/antagonists/abductor/abductor.dm b/code/modules/antagonists/abductor/abductor.dm
index 3e5a3c257c4..fa41b65f97b 100644
--- a/code/modules/antagonists/abductor/abductor.dm
+++ b/code/modules/antagonists/abductor/abductor.dm
@@ -44,12 +44,14 @@
owner.assigned_role = "[name] [sub_role]"
objectives += team.objectives
finalize_abductor()
+ owner.add_trait(TRAIT_ABDUCTOR_TRAINING, ABDUCTOR_ANTAGONIST)
return ..()
/datum/antagonist/abductor/on_removal()
if(owner.current)
to_chat(owner.current,"You are no longer the [owner.special_role]!")
owner.special_role = null
+ owner.remove_trait(TRAIT_ABDUCTOR_TRAINING, ABDUCTOR_ANTAGONIST)
return ..()
/datum/antagonist/abductor/greet()
@@ -73,11 +75,15 @@
update_abductor_icons_added(owner,"abductor")
-/datum/antagonist/abductor/scientist/finalize_abductor()
- ..()
- var/mob/living/carbon/human/H = owner.current
- var/datum/species/abductor/A = H.dna.species
- A.scientist = TRUE
+/datum/antagonist/abductor/scientist/on_gain()
+ owner.add_trait(TRAIT_ABDUCTOR_SCIENTIST_TRAINING, ABDUCTOR_ANTAGONIST)
+ owner.add_trait(TRAIT_SURGEON, ABDUCTOR_ANTAGONIST)
+ . = ..()
+
+/datum/antagonist/abductor/scientist/on_removal()
+ owner.remove_trait(TRAIT_ABDUCTOR_SCIENTIST_TRAINING, ABDUCTOR_ANTAGONIST)
+ owner.remove_trait(TRAIT_SURGEON, ABDUCTOR_ANTAGONIST)
+ . = ..()
/datum/antagonist/abductor/admin_add(datum/mind/new_owner,mob/admin)
var/list/current_teams = list()
@@ -211,4 +217,4 @@
/datum/antagonist/proc/update_abductor_icons_removed(datum/mind/alien_mind)
var/datum/atom_hud/antag/hud = GLOB.huds[ANTAG_HUD_ABDUCTOR]
hud.leave_hud(alien_mind.current)
- set_antag_hud(alien_mind.current, null)
\ No newline at end of file
+ set_antag_hud(alien_mind.current, null)
diff --git a/code/modules/antagonists/abductor/equipment/abduction_gear.dm b/code/modules/antagonists/abductor/equipment/abduction_gear.dm
index 5249bc04db3..ec968aafed2 100644
--- a/code/modules/antagonists/abductor/equipment/abduction_gear.dm
+++ b/code/modules/antagonists/abductor/equipment/abduction_gear.dm
@@ -134,22 +134,24 @@
lefthand_file = 'icons/mob/inhands/antag/abductor_lefthand.dmi'
righthand_file = 'icons/mob/inhands/antag/abductor_righthand.dmi'
-/obj/item/abductor/proc/AbductorCheck(user)
- if(isabductor(user))
+/obj/item/abductor/proc/AbductorCheck(mob/user)
+ if(user.has_trait(TRAIT_ABDUCTOR_TRAINING))
return TRUE
to_chat(user, "You can't figure how this works!")
return FALSE
-/obj/item/abductor/proc/ScientistCheck(user)
- if(!AbductorCheck(user))
- return FALSE
+/obj/item/abductor/proc/ScientistCheck(mob/user)
+ var/training = user.has_trait(TRAIT_ABDUCTOR_TRAINING)
+ var/sci_training = user.has_trait(TRAIT_ABDUCTOR_SCIENTIST_TRAINING)
- var/mob/living/carbon/human/H = user
- var/datum/species/abductor/S = H.dna.species
- if(S.scientist)
- return TRUE
- to_chat(user, "You're not trained to use this!")
- return FALSE
+ if(training && !sci_training)
+ to_chat(user, "You're not trained to use this!")
+ . = FALSE
+ else if(!training && !sci_training)
+ to_chat(user, "You can't figure how this works!")
+ . = FALSE
+ else
+ . = TRUE
/obj/item/abductor/gizmo
name = "science tool"
@@ -675,7 +677,7 @@ Congratulations! You are now trained for invasive xenobiology research!"}
desc = "Abduct with style - spiky style. Prevents digital tracking."
icon_state = "alienhelmet"
item_state = "alienhelmet"
- blockTracking = 1
+ blockTracking = TRUE
flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR
// Operating Table / Beds / Lockers
diff --git a/code/modules/antagonists/abductor/equipment/gland.dm b/code/modules/antagonists/abductor/equipment/gland.dm
index 969590402c0..58a8ec92f3b 100644
--- a/code/modules/antagonists/abductor/equipment/gland.dm
+++ b/code/modules/antagonists/abductor/equipment/gland.dm
@@ -5,6 +5,7 @@
icon_state = "gland"
status = ORGAN_ROBOTIC
beating = TRUE
+ var/true_name = "baseline placebo referencer"
var/cooldown_low = 300
var/cooldown_high = 300
var/next_activation = 0
@@ -16,6 +17,11 @@
var/mind_control_duration = 1800
var/active_mind_control = FALSE
+/obj/item/organ/heart/gland/examine(mob/user)
+ . = ..()
+ if(user.has_trait(TRAIT_ABDUCTOR_SCIENTIST_TRAINING) || isobserver(user))
+ to_chat(user, "It is \a [true_name].")
+
/obj/item/organ/heart/gland/proc/ownerCheck()
if(ishuman(owner))
return TRUE
@@ -95,6 +101,7 @@
return
/obj/item/organ/heart/gland/heals
+ true_name = "coherency harmonizer"
cooldown_low = 200
cooldown_high = 400
uses = -1
@@ -109,6 +116,7 @@
owner.adjustOxyLoss(-20)
/obj/item/organ/heart/gland/slime
+ true_name = "gastric animation galvanizer"
cooldown_low = 600
cooldown_high = 1200
uses = -1
@@ -130,6 +138,7 @@
Slime.Leader = owner
/obj/item/organ/heart/gland/mindshock
+ true_name = "neural crosstalk uninhibitor"
cooldown_low = 400
cooldown_high = 700
uses = -1
@@ -156,6 +165,7 @@
H.hallucination += 60
/obj/item/organ/heart/gland/pop
+ true_name = "anthropmorphic translocator"
cooldown_low = 900
cooldown_high = 1800
uses = -1
@@ -171,6 +181,7 @@
owner.set_species(species)
/obj/item/organ/heart/gland/ventcrawling
+ true_name = "pliant cartilage enabler"
cooldown_low = 1800
cooldown_high = 2400
uses = 1
@@ -183,6 +194,7 @@
owner.ventcrawler = VENTCRAWLER_ALWAYS
/obj/item/organ/heart/gland/viral
+ true_name = "contamination incubator"
cooldown_low = 1800
cooldown_high = 2400
uses = 1
@@ -217,6 +229,7 @@
return A
/obj/item/organ/heart/gland/trauma
+ true_name = "white matter randomiser"
cooldown_low = 800
cooldown_high = 1200
uses = 5
@@ -235,6 +248,7 @@
owner.gain_trauma_type(BRAIN_TRAUMA_MILD, rand(TRAUMA_RESILIENCE_BASIC, TRAUMA_RESILIENCE_LOBOTOMY))
/obj/item/organ/heart/gland/spiderman
+ true_name = "araneae cloister accelerator"
cooldown_low = 450
cooldown_high = 900
uses = -1
@@ -249,6 +263,7 @@
S.directive = "Protect your nest inside [owner.real_name]."
/obj/item/organ/heart/gland/egg
+ true_name = "roe/enzymatic synthesizer"
cooldown_low = 300
cooldown_high = 400
uses = -1
@@ -264,6 +279,7 @@
new /obj/item/reagent_containers/food/snacks/egg/gland(T)
/obj/item/organ/heart/gland/electric
+ true_name = "electron accumulator/discharger"
cooldown_low = 800
cooldown_high = 1200
uses = -1
@@ -289,6 +305,7 @@
playsound(get_turf(owner), 'sound/magic/lightningshock.ogg', 50, 1)
/obj/item/organ/heart/gland/chem
+ true_name = "intrinsic pharma-provider"
cooldown_low = 50
cooldown_high = 50
uses = -1
@@ -315,6 +332,7 @@
..()
/obj/item/organ/heart/gland/plasma
+ true_name = "effluvium sanguine-synonym emitter"
cooldown_low = 1200
cooldown_high = 1800
uses = -1
diff --git a/code/modules/antagonists/abductor/machinery/camera.dm b/code/modules/antagonists/abductor/machinery/camera.dm
index 1d18cbe7b56..022234b6c87 100644
--- a/code/modules/antagonists/abductor/machinery/camera.dm
+++ b/code/modules/antagonists/abductor/machinery/camera.dm
@@ -55,8 +55,7 @@
actions += set_droppoint_action
/obj/machinery/computer/camera_advanced/abductor/proc/IsScientist(mob/living/carbon/human/H)
- var/datum/species/abductor/S = H.dna.species
- return S.scientist
+ return H.has_trait(TRAIT_ABDUCTOR_SCIENTIST_TRAINING)
/datum/action/innate/teleport_in
name = "Send To"
diff --git a/code/modules/antagonists/abductor/machinery/console.dm b/code/modules/antagonists/abductor/machinery/console.dm
index b3e52ed0a6c..30eec3d5464 100644
--- a/code/modules/antagonists/abductor/machinery/console.dm
+++ b/code/modules/antagonists/abductor/machinery/console.dm
@@ -28,7 +28,7 @@
. = ..()
if(.)
return
- if(!isabductor(user))
+ if(!user.has_trait(TRAIT_ABDUCTOR_TRAINING))
to_chat(user, "You start mashing alien buttons at random!")
if(do_after(user,100, target = src))
TeleporterSend()
diff --git a/code/modules/antagonists/abductor/machinery/pad.dm b/code/modules/antagonists/abductor/machinery/pad.dm
index 1cb95fbf052..ab636f7d0e8 100644
--- a/code/modules/antagonists/abductor/machinery/pad.dm
+++ b/code/modules/antagonists/abductor/machinery/pad.dm
@@ -53,4 +53,4 @@
. = ..()
var/datum/effect_system/spark_spread/S = new
S.set_up(10,0,loc)
- S.start()
\ No newline at end of file
+ S.start()
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 5d3ca54d12c..9c5c6d5e2fd 100644
--- a/code/modules/mob/living/carbon/human/species_types/abductors.dm
+++ b/code/modules/mob/living/carbon/human/species_types/abductors.dm
@@ -6,10 +6,6 @@
species_traits = list(NOBLOOD,NOEYES)
inherent_traits = list(TRAIT_VIRUSIMMUNE,TRAIT_NOGUNS,TRAIT_NOHUNGER,TRAIT_NOBREATH)
mutanttongue = /obj/item/organ/tongue/abductor
- var/scientist = FALSE // vars to not pollute spieces list with castes
-
-/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)
. = ..()
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index e2bf5fd0538..85e22844fb3 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -512,4 +512,12 @@ It's fairly easy to fix if dealing with single letters but not so much with comp
. = ""
if(has_trait(TRAIT_DISSECTED))
- . += "This body has been dissected and analyzed. It is no longer worth experimenting on.
"
\ No newline at end of file
+ . += "This body has been dissected and analyzed. It is no longer worth experimenting on.
"
+
+/mob/has_trait(trait, list/sources, check_mind=TRUE)
+ . = ..(trait, sources)
+ if(.)
+ return
+
+ if(check_mind && istype(mind))
+ return mind.has_trait(trait, sources)
diff --git a/code/modules/surgery/surgery.dm b/code/modules/surgery/surgery.dm
index db724ebd655..ea8ab736d6f 100644
--- a/code/modules/surgery/surgery.dm
+++ b/code/modules/surgery/surgery.dm
@@ -88,13 +88,10 @@
/datum/surgery/advanced/can_start(mob/user, mob/living/carbon/target)
if(!..())
return FALSE
- //Abductor scientists need no instructions
- if(isabductor(user))
- var/mob/living/carbon/human/H = user
- var/datum/species/abductor/S = H.dna.species
- if(S.scientist)
- return TRUE
-
+ // True surgeons (like abductor scientists) need no instructions
+ if(user.has_trait(TRAIT_SURGEON))
+ return TRUE
+
if(iscyborg(user))
var/mob/living/silicon/robot/R = user
var/obj/item/surgical_processor/SP = locate() in R.module.modules
@@ -102,7 +99,7 @@
return FALSE
if(type in SP.advanced_surgeries)
return TRUE
-
+
var/turf/T = get_turf(target)
var/obj/structure/table/optable/table = locate(/obj/structure/table/optable, T)
if(!table || !table.computer)