diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm
index 7a51eb5defb..695ab933aeb 100644
--- a/code/__defines/mobs.dm
+++ b/code/__defines/mobs.dm
@@ -113,6 +113,10 @@
#define COMPANY_ALIGNMENTS list(COMPANY_LOYAL,COMPANY_SUPPORTATIVE,COMPANY_NEUTRAL,COMPANY_SKEPTICAL,COMPANY_OPPOSED)
+// Defines the argument used for get_mobs_and_objs_in_view_fast
+#define GHOSTS_ALL_HEAR 1
+#define ONLY_GHOSTS_IN_VIEW 0
+
// Defines mob sizes, used by lockers and to determine what is considered a small sized mob, etc.
#define MOB_LARGE 16
diff --git a/code/_helpers/game.dm b/code/_helpers/game.dm
index 1210ba9f3e8..85548fe6352 100644
--- a/code/_helpers/game.dm
+++ b/code/_helpers/game.dm
@@ -245,6 +245,41 @@
. += M
return .
+/proc/get_mobs_and_objs_in_view_fast(var/turf/T, var/range, var/list/mobs, var/list/objs, var/checkghosts = GHOSTS_ALL_HEAR)
+
+ var/list/hear = list()
+ DVIEW(hear, range, T, INVISIBILITY_MAXIMUM)
+ var/list/hearturfs = list()
+
+ for(var/am in hear)
+ var/atom/movable/AM = am
+ if(ismob(AM))
+ mobs += AM
+ hearturfs += AM.locs[1]
+ else if(isobj(AM))
+ objs += AM
+ hearturfs += AM.locs[1]
+
+
+ for(var/m in player_list)
+ var/mob/M = m
+ if(checkghosts == GHOSTS_ALL_HEAR && M.stat == DEAD && (M.client && M.client.prefs.toggles & CHAT_GHOSTEARS))
+ mobs |= M
+ continue
+ if(M.loc && M.locs[1] in hearturfs)
+ mobs |= M
+
+
+
+ for(var/o in listening_objects)
+ var/obj/O = o
+ if(O && O.loc && O.locs[1] in hearturfs)
+ objs |= O
+
+
+
+
+
#define SIGN(X) ((X<0)?-1:1)
proc
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 132587422cb..44751295b56 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -458,22 +458,22 @@ its easier to just keep the beam vertical.
// Use for objects performing visible actions
// message is output to anyone who can see, e.g. "The [src] does something!"
// blind_message (optional) is what blind people will hear e.g. "You hear something!"
-/atom/proc/visible_message(var/message, var/blind_message)
- var/list/messageturfs = list()//List of turfs we broadcast to.
- var/list/messagemobs = list()//List of living mobs nearby who can hear it, and distant ghosts who've chosen to hear it
- for (var/turf in view(world.view, get_turf(src)))
- messageturfs += turf
+/atom/proc/visible_message(var/message, var/blind_message, var/range = world.view)
+ var/turf/T = get_turf(src)
+ var/list/mobs = list()
+ var/list/objs = list()
+ get_mobs_and_objs_in_view_fast(T,range, mobs, objs, ONLY_GHOSTS_IN_VIEW)
- for(var/A in player_list)
- var/mob/M = A
- if (!M.client || istype(M, /mob/new_player))
- continue
- if(get_turf(M) in messageturfs)
- messagemobs += M
+ for(var/o in objs)
+ var/obj/O = o
+ O.show_message(message,1,blind_message,2)
- for(var/A in messagemobs)
- var/mob/M = A
- M.show_message(message, 1, blind_message, 2)
+ for(var/m in mobs)
+ var/mob/M = m
+ if(M.see_invisible >= invisibility)
+ M.show_message(message,1,blind_message,2)
+ else if(blind_message)
+ M.show_message(blind_message, 2)
// Show a message to all mobs and objects in earshot of this atom
// Use for objects performing audible actions
@@ -485,18 +485,18 @@ its easier to just keep the beam vertical.
var/range = world.view
if(hearing_distance)
range = hearing_distance
- var/list/hear = get_mobs_or_objects_in_view(range,src)
+ var/turf/T = get_turf(src)
+ var/list/mobs = list()
+ var/list/objs = list()
+ get_mobs_and_objs_in_view_fast(T,range, mobs, objs, ONLY_GHOSTS_IN_VIEW)
+
+ for(var/m in mobs)
+ var/mob/M = m
+ M.show_message(message,2,deaf_message,1)
+ for(var/o in objs)
+ var/obj/O = o
+ O.show_message(message,2,deaf_message,1)
- for(var/I in hear)
- if(isobj(I))
- spawn(0)
- if(I) //It's possible that it could be deleted in the meantime.
- var/obj/O = I
- O.show_message( message, 2, deaf_message, 1)
- else if(ismob(I))
- var/mob/M = I
- M.show_message( message, 2, deaf_message, 1)
-
/atom/proc/change_area(var/area/oldarea, var/area/newarea)
change_area_name(oldarea.name, newarea.name)
diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm
index 24faa63f439..008e9baef29 100644
--- a/code/game/objects/items/devices/radio/radio.dm
+++ b/code/game/objects/items/devices/radio/radio.dm
@@ -63,9 +63,11 @@ var/global/list/default_medbay_channels = list(
..()
wires = new(src)
internal_channels = default_internal_channels.Copy()
+ listening_objects += src
/obj/item/device/radio/Destroy()
qdel(wires)
+ listening_objects -= src
wires = null
if(radio_controller)
radio_controller.remove_object(src, frequency)
diff --git a/code/game/objects/items/devices/spy_bug.dm b/code/game/objects/items/devices/spy_bug.dm
index 063e7a85cea..7e591b4a679 100644
--- a/code/game/objects/items/devices/spy_bug.dm
+++ b/code/game/objects/items/devices/spy_bug.dm
@@ -23,6 +23,11 @@
..()
radio = new(src)
camera = new(src)
+ listening_objects += src
+
+/obj/item/device/spy_bug/Destroy()
+ listening_objects -= src
+ return ..()
/obj/item/device/spy_bug/examine(mob/user)
. = ..(user, 0)
@@ -62,6 +67,11 @@
/obj/item/device/spy_monitor/New()
radio = new(src)
+ listening_objects += src
+
+/obj/item/device/spy_monitor/Destroy()
+ listening_objects -= src
+ return ..()
/obj/item/device/spy_monitor/examine(mob/user)
. = ..(user, 1)
diff --git a/code/game/objects/items/devices/taperecorder.dm b/code/game/objects/items/devices/taperecorder.dm
index e59d3e993b5..3ed02fe7d3a 100644
--- a/code/game/objects/items/devices/taperecorder.dm
+++ b/code/game/objects/items/devices/taperecorder.dm
@@ -20,6 +20,14 @@
throw_speed = 4
throw_range = 20
+/obj/item/device/taperecorder/New()
+ ..()
+ listening_objects += src
+
+/obj/item/device/taperecorder/Destroy()
+ listening_objects -= src
+ return ..()
+
/obj/item/device/taperecorder/hear_talk(mob/living/M as mob, msg, var/verb="says", datum/language/speaking=null)
if(recording)
timestamp += timerecorded
diff --git a/code/game/objects/items/weapons/implants/implant.dm b/code/game/objects/items/weapons/implants/implant.dm
index 3f262f84e17..ecd9a8a610f 100644
--- a/code/game/objects/items/weapons/implants/implant.dm
+++ b/code/game/objects/items/weapons/implants/implant.dm
@@ -50,7 +50,7 @@
Destroy()
if(part)
part.implants.Remove(src)
- ..()
+ return ..()
/obj/item/weapon/implant/tracking
name = "tracking implant"
@@ -162,6 +162,7 @@ Implant Specifics:
"}
if (malfunction == MALFUNCTION_PERMANENT)
return
+
var/need_gib = null
if(istype(imp_in, /mob/))
var/mob/T = imp_in
@@ -252,6 +253,15 @@ Implant Specifics:
"}
explosion(get_turf(imp_in), -1, -1, 2, 3)
qdel(src)
+/obj/item/weapon/implant/explosive/New()
+ ..()
+ listening_objects += src
+
+/obj/item/weapon/implant/explosive/Destroy()
+ listening_objects -= src
+ return ..()
+
+
/obj/item/weapon/implant/chem
name = "chemical implant"
desc = "Injects things."
diff --git a/code/global.dm b/code/global.dm
index 1ef56d9bf1b..0be6248f484 100644
--- a/code/global.dm
+++ b/code/global.dm
@@ -17,6 +17,8 @@ var/global/list/sec_hud_users = list() // List of all entities using
var/global/list/hud_icon_reference = list()
var/global/list/janitorial_supplies = list() // List of all the janitorial supplies on the map that the PDA cart may be tracking.
+var/global/list/listening_objects = list() // List of objects that need to be able to hear, used to avoid recursive searching through contents.
+
var/global/list/global_mutations = list() // List of hidden mutation things.
diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm
index 021216f2181..c29731c677f 100644
--- a/code/modules/assembly/holder.dm
+++ b/code/modules/assembly/holder.dm
@@ -211,6 +211,15 @@
return 1
+/obj/item/device/assembly_holder/New()
+ ..()
+ listening_objects += src
+
+/obj/item/device/assembly_holder/Destroy()
+ listening_objects -= src
+ return ..()
+
+
/obj/item/device/assembly_holder/hear_talk(mob/living/M as mob, msg, verb, datum/language/speaking)
if(a_right)
a_right.hear_talk(M,msg,verb,speaking)
diff --git a/code/modules/assembly/voice.dm b/code/modules/assembly/voice.dm
index 7e8c89cc796..eac18f4afd1 100644
--- a/code/modules/assembly/voice.dm
+++ b/code/modules/assembly/voice.dm
@@ -7,6 +7,14 @@
var/listening = 0
var/recorded //the activation message
+/obj/item/device/assembly/voice/New()
+ ..()
+ listening_objects += src
+
+/obj/item/device/assembly/voice/Destroy()
+ listening_objects -= src
+ return ..()
+
/obj/item/device/assembly/voice/hear_talk(mob/living/M as mob, msg)
if(listening)
recorded = msg
@@ -33,4 +41,4 @@
/obj/item/device/assembly/voice/toggle_secure()
. = ..()
- listening = 0
\ No newline at end of file
+ listening = 0
diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm
index e2883537c81..98858cef311 100644
--- a/code/modules/mob/living/say.dm
+++ b/code/modules/mob/living/say.dm
@@ -225,10 +225,9 @@ proc/get_radio_key_from_channel(var/channel)
if (speech_sound)
sound_vol *= 0.5
- var/turf/T = get_turf(src)
-
var/list/listening = list()
var/list/listening_obj = list()
+ var/turf/T = get_turf(src)
if(T)
//make sure the air can transmit speech - speaker's side
@@ -241,26 +240,8 @@ proc/get_radio_key_from_channel(var/channel)
italics = 1
sound_vol *= 0.5 //muffle the sound a bit, so it's like we're actually talking through contact
- var/list/hear = hear(message_range,T)
- var/list/hearturfs = list()
+ get_mobs_and_objs_in_view_fast(T, message_range, listening, listening_obj)
- for(var/I in hear)
- if(ismob(I))
- var/mob/M = I
- listening += M
- hearturfs += M.locs[1]
- else if(isobj(I))
- var/obj/O = I
- hearturfs += O.locs[1]
- listening_obj |= O
-
-
- for(var/mob/M in player_list)
- if(src.client && M.stat == DEAD && M.client && (M.client.prefs.toggles & CHAT_GHOSTEARS))
- listening |= M
- continue
- if(M.loc && M.locs[1] in hearturfs)
- listening |= M
var/speech_bubble_test = say_test(message)
var/image/speech_bubble = image('icons/mob/talk.dmi',src,"h[speech_bubble_test]")
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 179fc231f79..ff44620e461 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -158,21 +158,26 @@
var/range = world.view
if(hearing_distance)
range = hearing_distance
- var/list/hear = get_mobs_or_objects_in_view(range,src)
- for(var/I in hear)
- if(isobj(I))
- spawn(0)
- if(I) //It's possible that it could be deleted in the meantime.
- var/obj/O = I
- O.show_message( message, 2, deaf_message, 1)
- else if(ismob(I))
- var/mob/M = I
- var/msg = message
- if(self_message && M==src)
- msg = self_message
- M.show_message( msg, 2, deaf_message, 1)
+ var/turf/T = get_turf(src)
+ var/list/mobs = list()
+ var/list/objs = list()
+ get_mobs_and_objs_in_view_fast(T, range, mobs, objs)
+
+
+ for(var/m in mobs)
+ var/mob/M = m
+ if(self_message && M==src)
+ M.show_message(self_message,2,deaf_message,1)
+ continue
+
+ M.show_message(message,2,deaf_message,1)
+
+
+ for(var/o in objs)
+ var/obj/O = o
+ O.show_message(message,2,deaf_message,1)
/mob/proc/findname(msg)
for(var/mob/M in mob_list)
diff --git a/code/modules/projectiles/guns/energy/lawgiver.dm b/code/modules/projectiles/guns/energy/lawgiver.dm
index d8d3d91e797..a4c578e8de3 100644
--- a/code/modules/projectiles/guns/energy/lawgiver.dm
+++ b/code/modules/projectiles/guns/energy/lawgiver.dm
@@ -65,12 +65,20 @@
)
)
+/obj/item/weapon/gun/energy/lawgiver/New()
+ ..()
+ listening_objects += src
+
+/obj/item/weapon/gun/energy/lawgiver/Destroy()
+ listening_objects -= src
+ return ..()
+
/obj/item/weapon/gun/energy/lawgiver/proc/play_message()
while (message_enabled && !message_disable) //Shut down command issued. Inform user that boardcasting has been stopped
usr.audible_message("[usr]'s [src.name] broadcasts: [message]","")
playsound(get_turf(src), 'sound/voice/halt.ogg', 100, 1, vary = 0)
sleep(message_delay)
- usr << "\red Broadcasting Message disabled"
+ usr << "Broadcasting Message disabled"
message_enabled = 0
message_disable = 0
@@ -80,7 +88,7 @@
else
src.dna = user.dna.unique_enzymes
src.owner_name = user.real_name
- user << "\blue You feel your palm heat up as the gun reads your DNA profile."
+ user << "You feel your palm heat up as the gun reads your DNA profile."
desc += "
Linked to: [user.real_name]"
return
@@ -93,7 +101,7 @@
var/obj/item/organ/external/RA = H.get_organ("r_arm")
var/active_hand = H.hand
playsound(user, 'sound/weapons/lawgiver_idfail.ogg', 40, 1)
- user << "\red You hear a soft beep from the gun and 'ID FAIL' flashes across the screen."
+ user << "You hear a soft beep from the gun and 'ID FAIL' flashes across the screen."
user << "You feel a tiny prick in your hand!"
user.drop_item()
//Blow up Unauthorized Users Hand
@@ -132,50 +140,50 @@
sel_mode = 1
projectile_type=/obj/item/projectile/bullet/pistol
fire_sound='sound/weapons/Gunshot_smg.ogg'
- usr << "\red [src.name] is now set to single shot mode."
+ usr << "[src.name] is now set to single shot mode."
else if(findtext(msg,"rapidfire"))
sel_mode = 2
projectile_type=/obj/item/projectile/bullet/pistol
fire_sound='sound/weapons/Gunshot_smg.ogg'
- usr << "\red [src.name] is now set to rapid fire mode."
+ usr << "[src.name] is now set to rapid fire mode."
else if(findtext(msg,"highex") || findtext(msg,"grenade"))
sel_mode = 3
projectile_type=/obj/item/projectile/bullet/gyro/law
fire_sound='sound/effects/Explosion1.ogg'
- usr << "\red [src.name] is now set to high explosive mode."
+ usr << "[src.name] is now set to high explosive mode."
else if(findtext(msg,"stun"))
sel_mode = 4
projectile_type=/obj/item/projectile/beam/stun
fire_sound='sound/weapons/Taser.ogg'
- usr << "\red [src.name] is now set to stun mode."
+ usr << "[src.name] is now set to stun mode."
else if(findtext(msg,"hotshot") || findtext(msg,"incendiary"))
sel_mode = 5
projectile_type=/obj/item/projectile/bullet/shotgun/incendiary
fire_sound='sound/weapons/Gunshot.ogg'
- usr << "\red [src.name] is now set to incendiary mode."
+ usr << "[src.name] is now set to incendiary mode."
else if(findtext(msg,"armorpiercing") || findtext(msg,"execution"))
sel_mode = 6
projectile_type=/obj/item/projectile/bullet/rifle/a556
fire_sound='sound/weapons/Gunshot.ogg'
- usr << "\red [src.name] is now set to armorpiercing mode."
+ usr << "[src.name] is now set to armorpiercing mode."
else if(findtext(msg,"pellets"))
sel_mode = 7
projectile_type=/obj/item/projectile/bullet/pellet/shotgun
fire_sound='sound/weapons/Gunshot.ogg'
- usr << "\red [src.name] is now set to pellet mode."
+ usr << "[src.name] is now set to pellet mode."
/* Other Stuff */
else if(findtext(msg,"reset") && (findtext(msg,"user") || findtext(msg,"dna")))
dna = null
desc = default_desc
- usr << "\red [src.name]´s owner has been reset. Do not attempt to fire [src.name] without rebinding a new owner"
+ usr << "[src.name]´s owner has been reset. Do not attempt to fire [src.name] without rebinding a new owner."
else if((findtext(msg,"disable") || findtext(msg,"deactivate")) && findtext(msg,"crowdcontrol"))
message_disable = 1
- usr << "\red [src.name]´s crowdcontrol deactivation sequence started"
+ usr << "[src.name]´s crowdcontrol deactivation sequence started."
else if((findtext(msg,"enable") || findtext(msg,"activate")) && findtext(msg,"crowdcontrol"))
if(message_enabled) //Check if a message is already broadcasting -> abort
- usr << "\red [src.name] is already broadcasting a message."
+ usr << "[src.name] is already broadcasting a message."
return
- usr << "\red [src.name]´s crowdcontrol activation sequence started"
+ usr << "[src.name]´s crowdcontrol activation sequence started."
message = "Citizens stay calm. Stand back from the crime scene. Interference with the crime scene carries an automatic brig sentence."
message_enabled = 1
message_disable = 0
diff --git a/code/modules/research/xenoarchaeology/finds/finds_special.dm b/code/modules/research/xenoarchaeology/finds/finds_special.dm
index af73f541d60..359ba673603 100644
--- a/code/modules/research/xenoarchaeology/finds/finds_special.dm
+++ b/code/modules/research/xenoarchaeology/finds/finds_special.dm
@@ -10,6 +10,7 @@
processing_objects.Add(src)
spawning_id = pick("blood","holywater","lube","stoxin","ethanol","ice","glycerol","fuel","cleaner")
+
/obj/item/weapon/reagent_containers/glass/replenishing/process()
reagents.add_reagent(spawning_id, 0.3)
@@ -23,6 +24,12 @@
/obj/item/clothing/mask/gas/poltergeist/New()
processing_objects.Add(src)
+ listening_objects += src
+
+/obj/item/clothing/mask/gas/poltergeist/Destroy()
+ processing_objects.Remove(src)
+ listening_objects -= src
+ return ..()
/obj/item/clothing/mask/gas/poltergeist/process()
if(heard_talk.len && istype(src.loc, /mob/living) && prob(10))
@@ -57,6 +64,12 @@
/obj/item/weapon/vampiric/New()
..()
processing_objects.Add(src)
+ listening_objects += src
+
+/obj/item/weapon/vampiric/Destroy()
+ processing_objects.Remove(src)
+ listening_objects -= src
+ return ..()
/obj/item/weapon/vampiric/process()
//see if we've identified anyone nearby
@@ -146,6 +159,10 @@
processing_objects.Add(src)
loc_last_process = src.loc
+/obj/effect/decal/cleanable/blood/splatter/animated/Destroy()
+ processing_objects.Remove(src)
+ return ..()
+
/obj/effect/decal/cleanable/blood/splatter/animated/process()
if(target_turf && src.loc != target_turf)
step_towards(src,target_turf)
@@ -175,6 +192,10 @@
/obj/effect/shadow_wight/New()
processing_objects.Add(src)
+/obj/effect/shadow_wight/Destroy()
+ processing_objects.Remove(src)
+ return ..()
+
/obj/effect/shadow_wight/process()
if(src.loc)
src.loc = get_turf(pick(orange(1,src)))
@@ -200,4 +221,4 @@
processing_objects.Remove(src)
/obj/effect/shadow_wight/Bump(var/atom/obstacle)
- obstacle << "\red You feel a chill run down your spine!"
+ obstacle << "You feel a chill run down your spine!"
diff --git a/html/changelogs/Ccomp5950-saycodefix.yml b/html/changelogs/Ccomp5950-saycodefix.yml
new file mode 100644
index 00000000000..ac92d82e3a5
--- /dev/null
+++ b/html/changelogs/Ccomp5950-saycodefix.yml
@@ -0,0 +1,36 @@
+################################
+# Example Changelog File
+#
+# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb.
+#
+# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.)
+# When it is, any changes listed below will disappear.
+#
+# Valid Prefixes:
+# bugfix
+# wip (For works in progress)
+# tweak
+# soundadd
+# sounddel
+# rscadd (general adding of nice things)
+# rscdel (general deleting of nice things)
+# imageadd
+# imagedel
+# maptweak
+# spellcheck (typo fixes)
+# experiment
+#################################
+
+# Your name.
+author: Ccomp5950
+
+# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
+delete-after: True
+
+# Any changes you've made. See valid prefix list above.
+# INDENT WITH TWO SPACES. NOT TABS. SPACES.
+# SCREW THIS UP AND IT WON'T WORK.
+# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries.
+# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog.
+changes:
+ - bugfix: "Objects in bags and other containers (including your hands and pocket) will now hear speach again. This impacts radios, explosive implants, and the universal recorder."