Fixes mobs being unable to hear from inside machines or objects. (#14823)

This commit is contained in:
Matt Atlas
2022-09-16 15:29:31 +02:00
committed by GitHub
parent bf9dca275d
commit 640682ffaf
18 changed files with 168 additions and 120 deletions
@@ -14,6 +14,7 @@
anchored = 1 // don't get pushed around
simulated = FALSE
virtual_mob = null // Hear no evil, speak no evil
var/last_ready_name // This has to be saved because the client is nulled prior to Logout()
+101 -11
View File
@@ -1,13 +1,103 @@
/proc/mobs_in_range(var/atom/movable/AM)
. = list()
for(var/mob/abstract/observer/virtual/v_mob in range(world.view, get_turf(AM)))
if(ismob(v_mob.host))
. += v_mob.host
/*
* These calls could easily be setup to be a bunch of call()() with relevant procs and predicates but performance is a concern.
* BYOND is also a bit inflexible, as some predicates are of the sort /proc/name(host), others host.proc_name(), and some even do host.proc_name(parameter).
* Nothing that cannot be worked around, but it'd be a little messy. I miss C# lambdas...
*/
/proc/clients_in_range(var/atom/movable/AM)
// Procs are arranged by "in range/hearers/viewers()" usage, as opposed to virtual mob hear/see abilities.
// Most of these procs can technically take any movable atom but unless they have a virtual mob the returned objects may not be the expected ones
#define ACQUIRE_VIRTUAL_OR_TURF(A) A = (isvirtualmob(A) ? A : (((istype(A) && A.virtual_mob) ? A.virtual_mob : get_turf(A)))) ; if(!A) return
#define ACQUIRE_VIRTUAL_OR_RETURN(A) A = (isvirtualmob(A) ? A : (((istype(A) && A.virtual_mob) ? A.virtual_mob : null))) ; if(!A) return
/****************
* Range Helpers *
****************/
/proc/clients_in_range(var/atom/movable/center_vmob)
. = list()
for(var/mob/abstract/observer/virtual/v_mob in range(world.view, get_turf(AM)))
if(ismob(v_mob.host))
var/mob/M = v_mob.host
if(M.client)
. |= M.client
ACQUIRE_VIRTUAL_OR_TURF(center_vmob)
for(var/mob/abstract/observer/virtual/v_mob in range(world.view, center_vmob))
var/client/C = v_mob.get_client()
if(C)
. |= C
/proc/hearers_in_range(var/atom/movable/center_vmob, var/hearing_range = world.view)
. = list()
ACQUIRE_VIRTUAL_OR_TURF(center_vmob)
for(var/mob/abstract/observer/virtual/v_mob in range(hearing_range, center_vmob))
if(v_mob.abilities & VIRTUAL_ABILITY_HEAR)
. |= v_mob.host
/proc/viewers_in_range(var/atom/movable/center_vmob)
. = list()
ACQUIRE_VIRTUAL_OR_TURF(center_vmob)
for(var/mob/abstract/observer/virtual/v_mob in range(world.view, center_vmob))
if(v_mob.abilities & VIRTUAL_ABILITY_SEE)
. |= v_mob.host
/***************
* Hear Helpers *
***************/
// A mob hears another mob if they have direct line of sight, ignoring turf luminosity.
// If there is an opaque object beteween the mobs then they cannot hear each other, even if their respective turfs can be seen.
// Thus, unlike viewing hearing is communicative. I.e. if Mob A can hear Mob B then Mob B can also hear Mob A.
// Gets the hosts of all the virtual mobs that can hear the given movable atom (or rather, it's virtual mob or turf in that existence order)
/proc/all_hearers(var/atom/movable/heard_vmob, var/range = world.view)
. = list()
ACQUIRE_VIRTUAL_OR_TURF(heard_vmob)
for(var/mob/abstract/observer/virtual/v_mob in hearers(range, heard_vmob))
if(v_mob.abilities & VIRTUAL_ABILITY_HEAR)
. |= v_mob.host
/***************
* View Helpers *
***************/
// A mob can see another mob if:
// * Within visual range, with the following differences for (N)PCs.
// * PCs: Target is within client.view range, with center originating from either the mob or client.eye depending on client.eye_perspective.
// * NPCs: Target is within world.view range, with center always originating from the mob.
// * Either of the following is true:
// * The target mob is in direct line of sight and not standing on a turf with luminosity = 0 unless the viewing mob is close enough for see_in_dark to also be in range
// * The viewing mob has the SEE_MOBS sight flag.
// Gets the hosts of all virtual mobs that can see the given atom movable as well as its turf
/proc/all_viewers(var/mob/abstract/observer/virtual/viewed_atom)
. = list()
viewed_atom = istype(viewed_atom) ? viewed_atom.host : viewed_atom
var/turf/T = get_turf(viewed_atom)
if(!T)
return
for(var/mob/abstract/observer/virtual/seeing_v_mob in viewers(world.view, viewed_atom))
if(!(seeing_v_mob.abilities & VIRTUAL_ABILITY_SEE))
continue
var/atom/movable/host = seeing_v_mob.host
if(host.virtual_can_see_turf(T))
. |= host
// This proc returns all hosts of virtual mobs in the given atom's view range (using its turf), ignoring invisibility, VIRUAL_ABILITY_SEE, and most other restrictions.
// In most cases you actually want the all_* procs above. This helper was designed with LOOC in mind.
/proc/hosts_in_view_range(var/atom/movable/viewing_atom, var/range = world.view)
. = list()
ACQUIRE_VIRTUAL_OR_TURF(viewing_atom)
// As per http://www.byond.com/docs/ref/info.html#/proc/view by using a non-mob/client this automatically skips the vast majority of sight checks
for(var/mob/abstract/observer/virtual/v_mob in viewers(range, get_turf(viewing_atom.loc)))
. |= v_mob.host
/*
Misc. helper
*/
// Eye mobs technically see everything always, the owner just has an overlay applied, thus this helper
/atom/movable/proc/virtual_can_see_turf(var/turf/T)
return TRUE // We assume objects have already been filtered using viewers() or similar proc
#undef ACQUIRE_VIRTUAL_OR_TURF
#undef ACQUIRE_VIRTUAL_OR_RETURN
@@ -1,6 +1,7 @@
/mob/living/carbon/human/dummy
real_name = "Test Dummy"
status_flags = GODMODE|CANPUSH
virtual_mob = null
/mob/living/carbon/human/dummy/mannequin
mob_thinks = FALSE
+1
View File
@@ -4,6 +4,7 @@
animate_movement = 2
flags = PROXMOVE
sight = DEFAULT_SIGHT
virtual_mob = /mob/abstract/observer/virtual/mob
var/datum/mind/mind
var/stat = 0 //Whether a mob is alive or dead. TODO: Move this to living - Nodrak
+12
View File
@@ -1290,3 +1290,15 @@ proc/is_blind(A)
/mob/proc/get_talk_bubble()
return 'icons/mob/talk.dmi'
/datum/proc/get_client()
return null
/client/get_client()
return src
/mob/get_client()
return client
/mob/abstract/observer/virtual/get_client()
return host.get_client()