diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm
index c08ae986c6d..353f807a682 100644
--- a/code/game/machinery/camera/camera.dm
+++ b/code/game/machinery/camera/camera.dm
@@ -198,7 +198,7 @@
if(istype(O, /mob/living/silicon/ai))
var/mob/living/silicon/ai/AI = O
if(U.name == "Unknown") AI << "[U] holds \a [itemname] up to one of your cameras ..."
- else AI << "[U] holds \a [itemname] up to one of your cameras ..."
+ else AI << "[U] holds \a [itemname] up to one of your cameras ..."
AI.last_paper_seen = "
[itemname][info]"
else if (O.client && O.client.eye == src)
O << "[U] holds \a [itemname] up to one of the cameras ..."
diff --git a/code/game/machinery/camera/tracking.dm b/code/game/machinery/camera/tracking.dm
index 28523b3c761..1c423bf1da0 100644
--- a/code/game/machinery/camera/tracking.dm
+++ b/code/game/machinery/camera/tracking.dm
@@ -129,8 +129,9 @@
if(istype(M, /mob/living/carbon/human))
human = 1
var/mob/living/carbon/human/H = M
- //Cameras can't track people wearing an agent card or a ninja hood.
- if(H.wear_id && istype(H.wear_id.GetID(), /obj/item/weapon/card/id/syndicate))
+ //Cameras can't track people wearing an untrackable card
+ var/obj/item/weapon/card/id/id = H.wear_id
+ if(istype(id) && id.is_untrackable())
continue
if(istype(H.head, /obj/item/clothing/head))
var/obj/item/clothing/head/hat = H.head
@@ -189,25 +190,9 @@
while (U.cameraFollow == target)
if (U.cameraFollow == null)
return
- if (istype(target, /mob/living/carbon/human))
- var/mob/living/carbon/human/H = target
- if(H.wear_id && istype(H.wear_id.GetID(), /obj/item/weapon/card/id/syndicate))
- U.ai_cancel_tracking(1)
- return
- if(H.digitalcamo)
- U.ai_cancel_tracking(1)
- return
- if(istype(H.head, /obj/item/clothing/head))
- var/obj/item/clothing/head/hat = H.head
- if (hat.blockTracking)
- U.ai_cancel_tracking(1)
- return
- if(istype(target.loc,/obj/effect/dummy))
- U.ai_cancel_tracking()
- return
if (!trackable(target))
- U << "Target is not near any active cameras."
+ U << "Target is not on or near any active cameras on the station."
sleep(100)
continue
@@ -229,7 +214,21 @@
/proc/trackable(atom/movable/M)
var/turf/T = get_turf(M)
- if(T && (T.z in config.contact_levels) && (hassensorlevel(M, SUIT_SENSOR_TRACKING) || M in aibots))
+ if (istype(M, /mob/living/carbon/human))
+ var/mob/living/carbon/human/H = M
+ var/obj/item/weapon/card/id/id = H.wear_id
+ if(istype(id) && id.is_untrackable())
+ return 0
+ if(H.digitalcamo)
+ return 0
+ if(istype(H.head, /obj/item/clothing/head))
+ var/obj/item/clothing/head/hat = H.head
+ if (hat.blockTracking)
+ return 0
+ if(istype(M.loc,/obj/effect/dummy))
+ return 0
+
+ if((T && (T.z in config.contact_levels)) && ((hassensorlevel(M, SUIT_SENSOR_TRACKING) || M in aibots)))
return 1
return near_camera(M)
diff --git a/code/game/objects/items/weapons/cards_ids.dm b/code/game/objects/items/weapons/cards_ids.dm
index b092d31e6d9..c698dc70c10 100644
--- a/code/game/objects/items/weapons/cards_ids.dm
+++ b/code/game/objects/items/weapons/cards_ids.dm
@@ -88,6 +88,7 @@
var/access = list()
var/registered_name = "Unknown" // The name registered_name on the card
slot_flags = SLOT_ID
+ var/untrackable // Can not be tracked by AI's
var/blood_type = "\[UNSET\]"
var/dna_hash = "\[UNSET\]"
@@ -172,6 +173,9 @@
/obj/item/weapon/card/id/GetID()
return src
+
+/obj/item/weapon/card/id/proc/is_untrackable()
+ return untrackable
/obj/item/weapon/card/id/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
..()
@@ -213,6 +217,7 @@
var/list/initial_access = list(access_maint_tunnels, access_syndicate, access_external_airlocks)
origin_tech = "syndicate=3"
var/registered_user = null
+ untrackable = 1
/obj/item/weapon/card/id/syndicate/New()
access = initial_access.Copy()
@@ -429,6 +434,7 @@
registered_name = "Syndicate"
icon_state = "syndie"
assignment = "Syndicate Overlord"
+ untrackable = 1
access = list(access_syndicate, access_external_airlocks)
/obj/item/weapon/card/id/captains_spare
@@ -450,6 +456,7 @@
item_state = "gold_id"
registered_name = "Admin"
assignment = "Testing Shit"
+ untrackable = 1
/obj/item/weapon/card/id/admin/New()
access = get_absolutely_all_accesses()
diff --git a/code/modules/mob/hear_say.dm b/code/modules/mob/hear_say.dm
index edee5035f2d..f99db44de5a 100644
--- a/code/modules/mob/hear_say.dm
+++ b/code/modules/mob/hear_say.dm
@@ -95,7 +95,7 @@
if(!(language && (language.flags & INNATE))) // skip understanding checks for INNATE languages
if(!say_understands(speaker,language))
- if(istype(speaker,/mob/living/simple_animal))
+ if(isanimal(speaker))
var/mob/living/simple_animal/S = speaker
if(S.speak && S.speak.len)
message = pick(S.speak)
@@ -111,7 +111,8 @@
message = stars(message)
var/speaker_name = "unknown"
- if(speaker) speaker_name = speaker.name
+ if(speaker)
+ speaker_name = speaker.name
if(vname)
speaker_name = vname
@@ -120,18 +121,17 @@
var/changed_voice
- if(istype(src, /mob/living/silicon/ai) && !hard_to_hear)
+ if(isAI(src) && !hard_to_hear)
var/jobname // the mob's "job"
var/mob/living/carbon/human/impersonating //The crewmember being impersonated, if any.
if (ishuman(speaker))
var/mob/living/carbon/human/H = speaker
- if((H.wear_id && istype(H.wear_id,/obj/item/weapon/card/id/syndicate)) && (H.wear_mask && istype(H.wear_mask,/obj/item/clothing/mask/gas/voice)))
-
+ var/obj/item/weapon/card/id/id = H.wear_id
+ if((istype(id) && id.is_untrackable()) && H.HasVoiceChanger())
changed_voice = 1
var/mob/living/carbon/human/I = locate(speaker_name)
-
if(I)
impersonating = I
jobname = impersonating.get_assignment()
@@ -141,28 +141,28 @@
jobname = H.get_assignment()
else if (iscarbon(speaker)) // Nonhuman carbon mob
- jobname = "No id"
+ jobname = "No ID"
else if (isAI(speaker))
jobname = "AI"
else if (isrobot(speaker))
jobname = "Cyborg"
- else if (istype(speaker, /mob/living/silicon/pai))
+ else if (ispAI(speaker))
jobname = "Personal AI"
else
jobname = "Unknown"
if(changed_voice)
if(impersonating)
- track = "[speaker_name] ([jobname])"
+ track = "[speaker_name] ([jobname])"
else
track = "[speaker_name] ([jobname])"
else
- if(istype(follow_target, /obj/machinery/bot) && isAI(src))
- track = "[speaker_name] ([jobname])"
+ if(istype(follow_target, /obj/machinery/bot))
+ track = "[speaker_name] ([jobname])"
else
- track = "[speaker_name] ([jobname])"
+ track = "[speaker_name] ([jobname])"
- if(istype(src, /mob/dead/observer))
+ if(isobserver(src))
if(speaker && (speaker_name != speaker.real_name) && !isAI(speaker)) //Announce computer and various stuff that broadcasts doesn't use it's real name but AI's can't pretend to be other mobs.
speaker_name = "[speaker.real_name] ([speaker_name])"
track = "[speaker_name] ([ghost_follow_link(follow_target, ghost=src)])"
diff --git a/code/modules/mob/language.dm b/code/modules/mob/language.dm
index b11540db9f4..a6d44fa768b 100644
--- a/code/modules/mob/language.dm
+++ b/code/modules/mob/language.dm
@@ -468,7 +468,7 @@
if(drone_only && !istype(S,/mob/living/silicon/robot/drone))
continue
else if(istype(S , /mob/living/silicon/ai))
- message_start = "[name], [speaker.name]"
+ message_start = "[name], [speaker.name]"
else if (!S.binarycheck())
continue
diff --git a/code/modules/mob/living/carbon/human/say.dm b/code/modules/mob/living/carbon/human/say.dm
index 538a1dbbf50..75167bef614 100644
--- a/code/modules/mob/living/carbon/human/say.dm
+++ b/code/modules/mob/living/carbon/human/say.dm
@@ -207,16 +207,22 @@
// return 0
return ..()
-
-/mob/living/carbon/human/GetVoice()
-
- var/voice_sub
+
+/mob/living/carbon/human/proc/HasVoiceChanger()
for(var/obj/item/gear in list(wear_mask,wear_suit,head))
if(!gear)
continue
var/obj/item/voice_changer/changer = locate() in gear
if(changer && changer.active && changer.voice)
- voice_sub = changer.voice
+ return changer
+ return 0
+
+/mob/living/carbon/human/GetVoice()
+
+ var/voice_sub
+ var/has_changer = HasVoiceChanger()
+ if(has_changer)
+ voice_sub = has_changer
if(voice_sub)
return voice_sub
if(mind && mind.changeling && mind.changeling.mimicing)
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index c89552719bb..52dbb0a7af1 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -502,18 +502,18 @@ var/list/ai_verbs_default = list(
if (href_list["track"])
var/mob/target = locate(href_list["track"]) in mob_list
-
- if(target && (!istype(target, /mob/living/carbon/human) || html_decode(href_list["trackname"]) == target:get_face_name()))
+ if(target && trackable(target))
ai_actual_track(target)
else
- src << "\red System error. Cannot locate [html_decode(href_list["trackname"])]."
+ src << "Target is not on or near any active cameras on the station."
return
if (href_list["trackbot"])
var/obj/machinery/bot/target = locate(href_list["trackbot"]) in aibots
- var/mob/living/silicon/ai/A = locate(href_list["track2"]) in mob_list
- if(A && target)
- A.ai_actual_track(target)
+ if(target && trackable(target))
+ ai_actual_track(target)
+ else
+ src << "Target is not on or near any active cameras on the station."
return
if (href_list["callbot"]) //Command a bot to move to a selected location.
@@ -925,10 +925,8 @@ var/list/ai_verbs_default = list(
spawn(0)
if(istype(target, /mob/living/carbon/human))
var/mob/living/carbon/human/H = target
- if(H.wear_id && istype(H.wear_id.GetID(), /obj/item/weapon/card/id/syndicate))
- src << "Unable to locate an airlock"
- return
- if(H.wear_id && istype(H.wear_id.GetID(), /obj/item/weapon/card/id/syndicate))
+ var/obj/item/weapon/card/id/id = H.wear_id
+ if(istype(id) && id.is_untrackable())
src << "Unable to locate an airlock"
return
if(H.digitalcamo)