mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-27 23:27:34 +01:00
[MDB IGNORE] [IDB IGNORE] Upstream Sync - 04/17/2026 (#5453)
Upstream 04/17/2026 fixes https://github.com/Bubberstation/Bubberstation/issues/5549 --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: tgstation-ci[bot] <179393467+tgstation-ci[bot]@users.noreply.github.com> Co-authored-by: ArcaneMusic <41715314+ArcaneMusic@users.noreply.github.com> Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Co-authored-by: Rhials <28870487+Rhials@users.noreply.github.com> Co-authored-by: rageguy505 <54517726+rageguy505@users.noreply.github.com> Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com> Co-authored-by: John Willard <53777086+JohnFulpWillard@users.noreply.github.com> Co-authored-by: Aliceee2ch <160794176+Aliceee2ch@users.noreply.github.com> Co-authored-by: Time-Green <7501474+Time-Green@users.noreply.github.com> Co-authored-by: Tsar-Salat <62388554+Tsar-Salat@users.noreply.github.com> Co-authored-by: SmArtKar <44720187+SmArtKar@users.noreply.github.com> Co-authored-by: Maxipat <108554989+Maxipat112@users.noreply.github.com> Co-authored-by: SyncIt21 <110812394+SyncIt21@users.noreply.github.com> Co-authored-by: deltanedas <39013340+deltanedas@users.noreply.github.com> Co-authored-by: SimplyLogan <47579821+loganuk@users.noreply.github.com> Co-authored-by: loganuk <fakeemail123@aol.com> Co-authored-by: Leland Kemble <70413276+lelandkemble@users.noreply.github.com> Co-authored-by: FalloutFalcon <86381784+FalloutFalcon@users.noreply.github.com> Co-authored-by: Roxy <75404941+TealSeer@users.noreply.github.com> Co-authored-by: Lucy <lucy@absolucy.moe> Co-authored-by: siliconOpossum <138069572+siliconOpossum@users.noreply.github.com> Co-authored-by: Isratosh <Isratosh@hotmail.com> Co-authored-by: TheRyeGuyWhoWillNowDie <70169560+TheRyeGuyWhoWillNowDie@users.noreply.github.com> Co-authored-by: Neocloudy <88008002+Neocloudy@users.noreply.github.com> Co-authored-by: Alexander V. <volas@ya.ru> Co-authored-by: ElGitificador <168473461+ElGitificador@users.noreply.github.com> Co-authored-by: Twaticus <46540570+Twaticus@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Bloop <13398309+vinylspiders@users.noreply.github.com> Co-authored-by: Cameron Lennox <killer65311@gmail.com> Co-authored-by: Tim <timothymtorres@gmail.com> Co-authored-by: Iamgoofball <iamgoofball@gmail.com> Co-authored-by: Layzu666 <121319428+Layzu666@users.noreply.github.com> Co-authored-by: Arturlang <24881678+Arturlang@users.noreply.github.com> Co-authored-by: _0Steven <42909981+00-Steven@users.noreply.github.com> Co-authored-by: mrmanlikesbt <99309552+mrmanlikesbt@users.noreply.github.com> Co-authored-by: Ben10Omintrix <138636438+Ben10Omintrix@users.noreply.github.com> Co-authored-by: John F. Kennedy <54908920+MacaroniCritter@users.noreply.github.com> Co-authored-by: Cursor <102828457+theselfish@users.noreply.github.com> Co-authored-by: Josh <josh.adam.powell@gmail.com> Co-authored-by: Josh Powell <josh.powell@softwire.com> Co-authored-by: Yobrocharlie <Charliemiller5617@gmail.com> Co-authored-by: Hardly3D <66234359+Hardly3D@users.noreply.github.com> Co-authored-by: shayoki <96078776+shayoki@users.noreply.github.com> Co-authored-by: LT3 <83487515+lessthnthree@users.noreply.github.com>
This commit is contained in:
co-authored by
tgstation-ci[bot] <179393467+tgstation-ci[bot]@users.noreply.github.com>
ArcaneMusic
MrMelbert
Rhials
rageguy505
LemonInTheDark
John Willard
Aliceee2ch
Time-Green
Tsar-Salat
SmArtKar
Maxipat
SyncIt21
deltanedas
SimplyLogan
loganuk
Leland Kemble
FalloutFalcon
Roxy
Lucy
siliconOpossum
Isratosh
TheRyeGuyWhoWillNowDie
Neocloudy
Alexander V.
ElGitificador
Twaticus
dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bloop
Cameron Lennox
Tim
Iamgoofball
Layzu666
Arturlang
_0Steven
mrmanlikesbt
Ben10Omintrix
John F. Kennedy
Cursor
Josh
Josh Powell
Yobrocharlie
Hardly3D
shayoki
LT3
parent
d1f6ed3137
commit
21b4095dfd
@@ -1,213 +1,276 @@
|
||||
/datum/component/echolocation
|
||||
/// Radius of our view.
|
||||
var/echo_range = 4
|
||||
/// Time between echolocations. IMPORTANT!! The effective time in local and the effective time in live are very different. The second is noticeably slower,
|
||||
var/cooldown_time = 1 SECONDS
|
||||
/// Radius of our echolocation.
|
||||
var/echo_range = 5
|
||||
/// Time for the image to start fading out.
|
||||
var/image_expiry_time = 0.7 SECONDS
|
||||
/// Time for the image to fade in.
|
||||
var/fade_in_time = 0.2 SECONDS
|
||||
/// Time for the image to fade out and delete itself.
|
||||
var/fade_out_time = 0.3 SECONDS
|
||||
/// Are images static? If yes, spawns them on the turf and makes them not change location. Otherwise they change location and pixel shift with the original.
|
||||
var/images_are_static = TRUE
|
||||
/// Does this echolocation cause us to go blind?
|
||||
var/blinding = TRUE
|
||||
/// With mobs that have this echo group in their echolocation receiver trait, we share echo images.
|
||||
var/echo_group = null
|
||||
/// This trait blocks us from receiving echolocation.
|
||||
var/blocking_trait
|
||||
/// Ref of the client color we give to the echolocator.
|
||||
var/client_colour
|
||||
/// Associative list of receivers to lists of atoms they are rendering (those atoms are associated to data of the image and time they were rendered at).
|
||||
var/list/receivers = list()
|
||||
/// All the saved appearances, keyed by icon-icon_state.
|
||||
var/static/list/saved_appearances = list()
|
||||
/// Typecache of all the allowed paths to render.
|
||||
var/static/list/allowed_paths
|
||||
/// Typecache of turfs that are dangerous, to give them a special icon.
|
||||
var/static/list/danger_turfs
|
||||
/// A matrix that turns everything except #ffffff into pure blackness, used for our images (the outlines are #ffffff).
|
||||
var/static/list/black_white_matrix = list(85, 85, 85, 0, 85, 85, 85, 0, 85, 85, 85, 0, 0, 0, 0, 1, -254, -254, -254, 0)
|
||||
/// A matrix that turns everything into pure white.
|
||||
var/static/list/white_matrix = list(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 0, 0, 1, 0, 0, 0, 0)
|
||||
/// Cooldown for the echolocation.
|
||||
COOLDOWN_DECLARE(cooldown_last)
|
||||
|
||||
/datum/component/echolocation/Initialize(echo_range, cooldown_time, image_expiry_time, fade_in_time, fade_out_time, images_are_static, blocking_trait, echo_group, echo_icon, color_path, blinding, use_echo = TRUE, show_own_outline = FALSE, personal_color = "#ffffff") // SKYRAT EDIT CHANGE - ORIGINAL: /datum/component/echolocation/Initialize(echo_range, cooldown_time, image_expiry_time, fade_in_time, fade_out_time, images_are_static, blocking_trait, echo_group, echo_icon = "echo", color_path, blinding)
|
||||
/// Assoc list of world.time to atom ref to active image
|
||||
VAR_PRIVATE/list/active_images = list()
|
||||
/// All the saved appearances, keyed by icon-icon_state.
|
||||
VAR_PRIVATE/static/list/saved_appearances = list()
|
||||
|
||||
/// Typepache of atom types that will be highlighted with an image on ABOVE_GAME_PLANE.
|
||||
var/list/highlighted_paths
|
||||
/// Typepache of atom types that will have an image generated on WALL_PLANE,
|
||||
/// so they stick out from the floor but they don't obstruct game objects.
|
||||
var/list/background_paths
|
||||
/// Typecache of overlay icon states to the typecache of types that get that overlay.
|
||||
var/list/overlay_states
|
||||
|
||||
/// Combined typecache of all (UNCHANGING) types we want to echolocate, for faster checking.
|
||||
VAR_PRIVATE/list/combined_filter
|
||||
|
||||
/// The focus action for adjusting echolocation settings.
|
||||
var/datum/action/echolocation_focus/focus
|
||||
|
||||
/// A matrix that turns everything except #ffffff into pure blackness, used for our images (the outlines are #ffffff).
|
||||
VAR_PRIVATE/list/black_white_matrix
|
||||
/// List of planes we apply our filters to.
|
||||
VAR_PRIVATE/list/planes
|
||||
|
||||
/// Check every few seconds if we lost our hearing, disable sight_bypass if so (turning us really blind).
|
||||
COOLDOWN_DECLARE(deafness_check)
|
||||
|
||||
/datum/component/echolocation/Initialize(
|
||||
echo_range = src.echo_range,
|
||||
image_expiry_time = src.image_expiry_time,
|
||||
fade_in_time = src.fade_in_time,
|
||||
fade_out_time = src.fade_out_time,
|
||||
echo_icon,
|
||||
use_echo = TRUE, // BUBBER EDIT - ADDITION
|
||||
)
|
||||
. = ..()
|
||||
var/mob/living/echolocator = parent
|
||||
if(!istype(echolocator))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
if(!danger_turfs)
|
||||
danger_turfs = typecacheof(list(/turf/open/space, /turf/open/openspace, /turf/open/chasm, /turf/open/lava, /turf/open/floor/fakespace, /turf/open/floor/fakepit, /turf/closed/wall/space))
|
||||
if(!allowed_paths)
|
||||
allowed_paths = typecacheof(list(/turf/closed, /obj, /mob/living)) + danger_turfs - typecacheof(/obj/effect/decal)
|
||||
if(!isnull(echo_range))
|
||||
src.echo_range = echo_range
|
||||
if(!isnull(cooldown_time))
|
||||
src.cooldown_time = cooldown_time
|
||||
if(!isnull(image_expiry_time))
|
||||
src.image_expiry_time = image_expiry_time
|
||||
if(!isnull(fade_in_time))
|
||||
src.fade_in_time = fade_in_time
|
||||
if(!isnull(fade_out_time))
|
||||
src.fade_out_time = fade_out_time
|
||||
if(!isnull(blinding))
|
||||
src.blinding = blinding
|
||||
if(!isnull(images_are_static))
|
||||
src.images_are_static = images_are_static
|
||||
if(!isnull(blocking_trait))
|
||||
src.blocking_trait = blocking_trait
|
||||
// SKYRAT ADDITION START: echolocation
|
||||
src.show_own_outline = show_own_outline
|
||||
src.echo_color = personal_color
|
||||
// SKYRAT EDIT ADDITION END
|
||||
src.echo_group = echo_group || REF(src)
|
||||
if(ispath(color_path))
|
||||
client_colour = echolocator.add_client_colour(color_path, src.echo_group)
|
||||
echolocator.add_traits(list(TRAIT_ECHOLOCATION_RECEIVER, TRAIT_TRUE_NIGHT_VISION), src.echo_group) //so they see all the tiles they echolocated, even if they are in the dark
|
||||
if(blinding)
|
||||
echolocator.become_blind(ECHOLOCATION_TRAIT)
|
||||
// SKYRAT EDIT ADDITION START
|
||||
if (use_echo) // add constructor toggle to not use the eye overlay
|
||||
echolocator.overlay_fullscreen("echo", /atom/movable/screen/fullscreen/echo, echo_icon)
|
||||
// SKYRAT EDIT ADDITION END
|
||||
|
||||
overlay_states = list(
|
||||
"danger" = typecacheof(
|
||||
list(
|
||||
/turf/closed/wall/space,
|
||||
/turf/open/chasm,
|
||||
/turf/open/floor/fakepit,
|
||||
/turf/open/floor/fakespace,
|
||||
/turf/open/lava,
|
||||
/turf/open/openspace,
|
||||
/turf/open/space,
|
||||
),
|
||||
),
|
||||
"door" = typecacheof(
|
||||
list(
|
||||
/obj/machinery/door,
|
||||
/obj/structure/mineral_door,
|
||||
),
|
||||
)
|
||||
)
|
||||
highlighted_paths = list()
|
||||
background_paths = typecacheof(list(/obj/structure/bed, /obj/structure/table))
|
||||
|
||||
combined_filter = list()
|
||||
for(var/state, typecache in overlay_states)
|
||||
combined_filter |= typecache
|
||||
combined_filter |= background_paths
|
||||
|
||||
black_white_matrix = list(85, 85, 85, 0, 85, 85, 85, 0, 85, 85, 85, 0, 0, 0, 0, 1, -254, -254, -254, 0)
|
||||
planes = list(ABOVE_GAME_PLANE, FLOOR_PLANE, GAME_PLANE, WALL_PLANE)
|
||||
|
||||
focus = new(src)
|
||||
focus.Grant(parent)
|
||||
|
||||
src.echo_range = echo_range
|
||||
src.image_expiry_time = image_expiry_time
|
||||
src.fade_in_time = fade_in_time
|
||||
src.fade_out_time = fade_out_time
|
||||
|
||||
ADD_TRAIT(echolocator, TRAIT_ECHOLOCATOR, ECHOLOCATION_TRAIT)
|
||||
deafness_check()
|
||||
RegisterSignals(echolocator, list(SIGNAL_ADDTRAIT(TRAIT_DEAF), SIGNAL_REMOVETRAIT(TRAIT_DEAF)), PROC_REF(deafness_check))
|
||||
echolocator.become_blind(ECHOLOCATION_TRAIT)
|
||||
// echolocator.overlay_fullscreen(ECHOLOCATION_TRAIT, /atom/movable/screen/fullscreen/echo, echo_icon) // BUBBER EDIT - REMOVAL
|
||||
// BUBBER EDIT - ADDITION - START
|
||||
if (use_echo)
|
||||
echolocator.overlay_fullscreen(ECHOLOCATION_TRAIT, /atom/movable/screen/fullscreen/echo, echo_icon)
|
||||
// BUBBER EDIT - ADDITION - END
|
||||
echolocator.apply_status_effect(/datum/status_effect/grouped/see_no_names, ECHOLOCATION_TRAIT)
|
||||
START_PROCESSING(SSfastprocess, src)
|
||||
|
||||
if(echolocator.hud_used)
|
||||
hud_created()
|
||||
RegisterSignal(echolocator, COMSIG_MOB_HUD_CREATED, PROC_REF(hud_created))
|
||||
echolocator.update_sight()
|
||||
|
||||
/datum/component/echolocation/Destroy(force)
|
||||
STOP_PROCESSING(SSfastprocess, src)
|
||||
var/mob/living/echolocator = parent
|
||||
QDEL_NULL(client_colour)
|
||||
echolocator.remove_traits(list(TRAIT_ECHOLOCATION_RECEIVER, TRAIT_TRUE_NIGHT_VISION), echo_group)
|
||||
if(blinding)
|
||||
echolocator.cure_blind(ECHOLOCATION_TRAIT)
|
||||
echolocator.clear_fullscreen("echo")
|
||||
for(var/mob/living/echolocate_receiver as anything in receivers)
|
||||
if(!echolocate_receiver.client)
|
||||
continue
|
||||
for(var/atom/rendered_atom as anything in receivers[echolocate_receiver])
|
||||
echolocate_receiver.client.images -= receivers[echolocate_receiver][rendered_atom]["image"]
|
||||
receivers -= list(echolocate_receiver)
|
||||
|
||||
REMOVE_TRAIT(echolocator, TRAIT_ECHOLOCATOR, ECHOLOCATION_TRAIT)
|
||||
REMOVE_TRAIT(echolocator, TRAIT_SIGHT_BYPASS, ECHOLOCATION_TRAIT)
|
||||
UnregisterSignal(echolocator, list(SIGNAL_ADDTRAIT(TRAIT_DEAF), SIGNAL_REMOVETRAIT(TRAIT_DEAF)))
|
||||
echolocator.cure_blind(ECHOLOCATION_TRAIT)
|
||||
echolocator.clear_fullscreen(ECHOLOCATION_TRAIT)
|
||||
echolocator.remove_status_effect(/datum/status_effect/grouped/see_no_names, ECHOLOCATION_TRAIT)
|
||||
QDEL_NULL(focus)
|
||||
|
||||
for(var/tplane in planes)
|
||||
for(var/atom/movable/screen/plane_master/game_plane as anything in echolocator.hud_used?.get_true_plane_masters(tplane))
|
||||
game_plane.remove_filter("[ECHOLOCATION_TRAIT]_color")
|
||||
game_plane.remove_filter("[ECHOLOCATION_TRAIT]_outline")
|
||||
|
||||
UnregisterSignal(echolocator, COMSIG_MOB_HUD_CREATED)
|
||||
echolocator.update_sight()
|
||||
|
||||
for(var/time, image_list in active_images)
|
||||
for(var/atom_ref, echo_image in image_list)
|
||||
echolocator.client?.images -= echo_image
|
||||
|
||||
return ..()
|
||||
|
||||
/// Add or remove SIGHT_BYPASS depending on if we are deaf or not
|
||||
/datum/component/echolocation/proc/deafness_check(...)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(HAS_TRAIT(parent, TRAIT_DEAF))
|
||||
REMOVE_TRAIT(parent, TRAIT_SIGHT_BYPASS, ECHOLOCATION_TRAIT)
|
||||
else
|
||||
ADD_TRAIT(parent, TRAIT_SIGHT_BYPASS, ECHOLOCATION_TRAIT)
|
||||
|
||||
/// If the mob had no hud when they gained echolocation we need to apply the effect now
|
||||
/datum/component/echolocation/proc/hud_created(...)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
var/mob/living/echolocator = parent
|
||||
for(var/tplane in planes)
|
||||
for(var/atom/movable/screen/plane_master/game_plane as anything in echolocator.hud_used.get_true_plane_masters(tplane))
|
||||
game_plane.add_filter("[ECHOLOCATION_TRAIT]_color", 1, color_matrix_filter(black_white_matrix))
|
||||
game_plane.add_filter("[ECHOLOCATION_TRAIT]_outline", 1, outline_filter(size = 1, color = COLOR_WHITE))
|
||||
|
||||
/datum/component/echolocation/process()
|
||||
var/mob/living/echolocator = parent
|
||||
if(echolocator.stat == DEAD)
|
||||
return
|
||||
COOLDOWN_START(src, deafness_check, 0. SECONDS)
|
||||
echolocate()
|
||||
|
||||
/datum/component/echolocation/proc/echolocate()
|
||||
if(stall || !COOLDOWN_FINISHED(src, cooldown_last)) // SKYRAT EDIT CHANGE - ORIGINAL: if(!COOLDOWN_FINISHED(src, cooldown_last))
|
||||
return
|
||||
COOLDOWN_START(src, cooldown_last, cooldown_time)
|
||||
var/mob/living/echolocator = parent
|
||||
var/real_echo_range = echo_range
|
||||
if(HAS_TRAIT(echolocator, TRAIT_ECHOLOCATION_EXTRA_RANGE))
|
||||
real_echo_range += 2
|
||||
var/list/filtered = list()
|
||||
if(blinding)
|
||||
for(var/atom/seen_atom as anything in dview(real_echo_range, get_turf(echolocator.client?.eye || echolocator), invis_flags = echolocator.see_invisible))
|
||||
if(!seen_atom.alpha)
|
||||
continue
|
||||
if(allowed_paths[seen_atom.type])
|
||||
filtered += seen_atom
|
||||
else
|
||||
for(var/atom/possible_atom as anything in range(real_echo_range, get_turf(echolocator.client?.eye || echolocator)))
|
||||
if(!possible_atom.alpha || possible_atom.invisibility > echolocator.see_invisible)
|
||||
continue
|
||||
if(allowed_paths[possible_atom.type])
|
||||
filtered += possible_atom
|
||||
|
||||
for(var/atom/seen_atom as anything in dview(echo_range, get_turf(echolocator.client?.eye || echolocator), invis_flags = echolocator.see_invisible))
|
||||
if(!seen_atom.alpha)
|
||||
continue
|
||||
if(!is_type_in_typecache(seen_atom, combined_filter) && !is_type_in_typecache(seen_atom, highlighted_paths))
|
||||
continue
|
||||
filtered += seen_atom
|
||||
|
||||
if(!length(filtered))
|
||||
return
|
||||
|
||||
var/list/known_refs = list()
|
||||
for(var/time, image_list in active_images)
|
||||
for(var/atom_ref, echo_image in image_list)
|
||||
known_refs[atom_ref] = time
|
||||
|
||||
var/current_time = "[world.time]"
|
||||
for(var/mob/living/viewer in filtered)
|
||||
if(blocking_trait && HAS_TRAIT(viewer, blocking_trait))
|
||||
continue
|
||||
if(HAS_TRAIT_FROM(viewer, TRAIT_ECHOLOCATION_RECEIVER, echo_group) && isnull(receivers[viewer]))
|
||||
receivers[viewer] = list()
|
||||
active_images[current_time] = list()
|
||||
|
||||
for(var/atom/filtered_atom as anything in filtered)
|
||||
show_image(saved_appearances["[filtered_atom.icon]-[filtered_atom.icon_state]"] || generate_appearance(filtered_atom), filtered_atom, current_time)
|
||||
// if we are already showing an image for this atom, just update its time so it sticks around longer
|
||||
var/atom_ref = REF(filtered_atom)
|
||||
if(known_refs[atom_ref])
|
||||
var/old_time = known_refs[atom_ref]
|
||||
var/image/old_image = active_images[old_time][atom_ref]
|
||||
active_images[old_time] -= atom_ref
|
||||
active_images[current_time][atom_ref] = old_image
|
||||
if(!length(active_images[old_time]))
|
||||
active_images -= old_time
|
||||
// if they are mid fade, cancel it
|
||||
animate(old_image, time = 0, alpha = 255)
|
||||
continue
|
||||
|
||||
|
||||
var/image/final_image
|
||||
|
||||
// check for special overlays first
|
||||
for(var/overlay_state, overlay_typecache in overlay_states)
|
||||
if(!is_type_in_typecache(filtered_atom, overlay_typecache))
|
||||
continue
|
||||
|
||||
var/image/special_overlay = saved_appearances[overlay_state]
|
||||
if(!special_overlay)
|
||||
special_overlay = image('icons/effects/echolocate.dmi', null, overlay_state)
|
||||
special_overlay.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
saved_appearances[overlay_state] = special_overlay
|
||||
|
||||
final_image = image(special_overlay)
|
||||
break
|
||||
|
||||
// or generate a new image for this atom
|
||||
if(isnull(final_image))
|
||||
var/image/found_appearance = saved_appearances["[filtered_atom.icon]-[filtered_atom.icon_state]"]
|
||||
if(isnull(found_appearance))
|
||||
found_appearance = new(filtered_atom)
|
||||
if(filtered_atom.icon && filtered_atom.icon_state)
|
||||
saved_appearances["[filtered_atom.icon]-[filtered_atom.icon_state]"] = found_appearance
|
||||
found_appearance.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
|
||||
final_image = image(found_appearance)
|
||||
final_image.dir = filtered_atom.dir
|
||||
|
||||
if(isturf(filtered_atom))
|
||||
// We don't want to mess with you, you can stay on floor or wall plane or whatever
|
||||
SET_PLANE(final_image, filtered_atom.plane, filtered_atom)
|
||||
else if(is_type_in_typecache(filtered_atom, background_paths) || PLANE_TO_TRUE(final_image.plane) == FLOOR_PLANE)
|
||||
// I am being evil here and using wall plane due to being in-between of game plane and floor plane
|
||||
// Why? Because we need background/floor objects to have their own layering, otherwise the effect is blended in wrong
|
||||
// These objects will scarcely interact with real walls so it's... fine
|
||||
final_image.layer = ABOVE_NORMAL_TURF_LAYER
|
||||
SET_PLANE(final_image, WALL_PLANE, filtered_atom)
|
||||
else
|
||||
// Must be on above game plane to separate the effects from game objects
|
||||
SET_PLANE(final_image, ABOVE_GAME_PLANE, filtered_atom)
|
||||
|
||||
// Setting loc so we should disregard pixel offsets
|
||||
final_image.pixel_w = 0
|
||||
final_image.pixel_x = 0
|
||||
final_image.pixel_y = 0
|
||||
final_image.pixel_z = 0
|
||||
final_image.loc = filtered_atom
|
||||
// fade in
|
||||
if(fade_in_time > 0)
|
||||
final_image.alpha = 0
|
||||
animate(final_image, alpha = 255, time = fade_in_time)
|
||||
|
||||
active_images[current_time] ||= list()
|
||||
active_images[current_time][atom_ref] = final_image
|
||||
|
||||
echolocator.client?.images += final_image
|
||||
|
||||
addtimer(CALLBACK(src, PROC_REF(fade_images), current_time), image_expiry_time)
|
||||
|
||||
/datum/component/echolocation/proc/show_image(image/input_appearance, atom/input, current_time)
|
||||
var/image/final_image = image(input_appearance)
|
||||
final_image.layer += EFFECTS_LAYER
|
||||
final_image.plane = FULLSCREEN_PLANE
|
||||
final_image.loc = images_are_static ? get_turf(input) : input
|
||||
final_image.dir = input.dir
|
||||
final_image.alpha = 0
|
||||
if(images_are_static)
|
||||
final_image.pixel_x = input.pixel_x
|
||||
final_image.pixel_y = input.pixel_y
|
||||
// SKYRAT ADDITION START: echolocation (show outlines on self)
|
||||
if(HAS_TRAIT_FROM(input, TRAIT_ECHOLOCATION_RECEIVER, echo_group)) //mark other echolocation with full white, except ourselves
|
||||
var/datum/component/echolocation/located_component = input.GetComponent(/datum/component/echolocation)
|
||||
var/mob/living/echolocator = parent
|
||||
if(located_component)
|
||||
final_image.color = located_component.echo_color
|
||||
else if(input != echolocator)
|
||||
final_image.color = white_matrix
|
||||
// SKYRAT EDIT ADDITION END
|
||||
var/list/fade_ins = list(final_image)
|
||||
for(var/mob/living/echolocate_receiver as anything in receivers)
|
||||
if(echolocate_receiver == input && !show_own_outline) // SKYRAT EDIT CHANGE - ORIGINAL: if(echolocate_receiver == input)
|
||||
continue
|
||||
if(receivers[echolocate_receiver][input])
|
||||
var/previous_image = receivers[echolocate_receiver][input]["image"]
|
||||
fade_ins |= previous_image
|
||||
receivers[echolocate_receiver][input] = list("image" = previous_image, "time" = current_time)
|
||||
else
|
||||
if(echolocate_receiver.client)
|
||||
echolocate_receiver.client.images += final_image
|
||||
receivers[echolocate_receiver][input] = list("image" = final_image, "time" = current_time)
|
||||
for(var/image_echo in fade_ins)
|
||||
animate(image_echo, alpha = 255, time = fade_in_time)
|
||||
/datum/component/echolocation/proc/fade_images(from_time)
|
||||
if(fade_out_time <= 0)
|
||||
cleanup_images(from_time)
|
||||
return
|
||||
|
||||
/datum/component/echolocation/proc/generate_appearance(atom/input)
|
||||
var/use_outline = TRUE
|
||||
var/mutable_appearance/copied_appearance = new /mutable_appearance()
|
||||
copied_appearance.appearance = input
|
||||
if(istype(input, /obj/machinery/door/airlock)) //i hate you
|
||||
copied_appearance.cut_overlays()
|
||||
copied_appearance.icon_state = "closed"
|
||||
else if(danger_turfs[input.type])
|
||||
copied_appearance.icon = 'icons/turf/floors.dmi'
|
||||
copied_appearance.icon_state = "danger"
|
||||
use_outline = FALSE
|
||||
copied_appearance.color = black_white_matrix
|
||||
if(use_outline)
|
||||
copied_appearance.filters += outline_filter(size = 1, color = COLOR_WHITE)
|
||||
if(!images_are_static)
|
||||
copied_appearance.pixel_x = 0
|
||||
copied_appearance.pixel_y = 0
|
||||
copied_appearance.transform = matrix()
|
||||
if(input.icon && input.icon_state)
|
||||
saved_appearances["[input.icon]-[input.icon_state]"] = copied_appearance
|
||||
return copied_appearance
|
||||
for(var/atom_ref, echo_image in active_images[from_time])
|
||||
animate(echo_image, alpha = 0, time = fade_out_time)
|
||||
|
||||
/datum/component/echolocation/proc/fade_images(from_when)
|
||||
var/fade_outs = list()
|
||||
for(var/mob/living/echolocate_receiver as anything in receivers)
|
||||
for(var/atom/rendered_atom as anything in receivers[echolocate_receiver])
|
||||
if(receivers[echolocate_receiver][rendered_atom]["time"] <= from_when)
|
||||
fade_outs |= receivers[echolocate_receiver][rendered_atom]["image"]
|
||||
for(var/image_echo in fade_outs)
|
||||
animate(image_echo, alpha = 0, time = fade_out_time)
|
||||
addtimer(CALLBACK(src, PROC_REF(delete_images), from_when), fade_out_time)
|
||||
addtimer(CALLBACK(src, PROC_REF(cleanup_images), from_time), fade_out_time + 0.5 SECONDS)
|
||||
|
||||
/datum/component/echolocation/proc/delete_images(from_when)
|
||||
for(var/mob/living/echolocate_receiver as anything in receivers)
|
||||
for(var/atom/rendered_atom as anything in receivers[echolocate_receiver])
|
||||
if(receivers[echolocate_receiver][rendered_atom]["time"] <= from_when && echolocate_receiver.client)
|
||||
echolocate_receiver.client.images -= receivers[echolocate_receiver][rendered_atom]["image"]
|
||||
receivers[echolocate_receiver] -= rendered_atom
|
||||
if(!length(receivers[echolocate_receiver]))
|
||||
receivers -= echolocate_receiver
|
||||
/datum/component/echolocation/proc/cleanup_images(from_time)
|
||||
var/mob/living/echolocator = parent
|
||||
for(var/atom_ref, echo_image in active_images[from_time])
|
||||
echolocator.client?.images -= echo_image
|
||||
active_images -= from_time
|
||||
|
||||
/atom/movable/screen/fullscreen/echo
|
||||
icon_state = "echo"
|
||||
layer = ECHO_LAYER
|
||||
layer = 30
|
||||
plane = PLANE_SPACE_PARALLAX
|
||||
show_when_dead = TRUE
|
||||
|
||||
/atom/movable/screen/fullscreen/echo/Initialize(mapload, datum/hud/hud_owner)
|
||||
@@ -217,3 +280,75 @@
|
||||
/atom/movable/screen/fullscreen/echo/Destroy()
|
||||
QDEL_NULL(particles)
|
||||
return ..()
|
||||
|
||||
|
||||
/datum/action/echolocation_focus
|
||||
name = "Echolocation Focus"
|
||||
desc = "Focus your echolocation to reveal more details or ignore certain objects."
|
||||
|
||||
/// Assoc list of option name to typecache list
|
||||
var/static/list/options
|
||||
/// List of currently selected option names
|
||||
var/list/selected_options
|
||||
|
||||
/datum/action/echolocation_focus/New(Target)
|
||||
. = ..()
|
||||
if(!options)
|
||||
var/list/all_floor_objects = list(/obj/machinery/atmospherics/components)
|
||||
for(var/obj/floor_type as anything in typesof(/obj/machinery, /obj/structure))
|
||||
if(initial(floor_type.plane) == FLOOR_PLANE)
|
||||
all_floor_objects += floor_type
|
||||
|
||||
options = list()
|
||||
options["Blood"] = typecacheof(/obj/effect/decal/cleanable/blood)
|
||||
options["Items"] = typecacheof(/obj/item)
|
||||
options["Floor Objects"] = typecacheof(all_floor_objects)
|
||||
|
||||
selected_options = list(options[1], options[2])
|
||||
update_echocomp()
|
||||
|
||||
/datum/action/echolocation_focus/proc/update_echocomp()
|
||||
var/datum/component/echolocation/echo_comp = target
|
||||
echo_comp.highlighted_paths.Cut()
|
||||
for(var/option_name in selected_options)
|
||||
echo_comp.highlighted_paths |= options[option_name]
|
||||
|
||||
/datum/action/echolocation_focus/Trigger(mob/clicker, trigger_flags)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
|
||||
ui_interact(clicker)
|
||||
|
||||
/datum/action/echolocation_focus/ui_status(mob/user, datum/ui_state/state)
|
||||
return IsAvailable() ? UI_INTERACTIVE : UI_CLOSE
|
||||
|
||||
/datum/action/echolocation_focus/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(isnull(ui))
|
||||
ui = new(user, src, "EcholocationFocus")
|
||||
ui.open()
|
||||
|
||||
/datum/action/echolocation_focus/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
|
||||
data["selected_options"] = selected_options
|
||||
data["all_options"] = assoc_to_keys(options)
|
||||
|
||||
return data
|
||||
|
||||
/datum/action/echolocation_focus/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
|
||||
if(action != "toggle")
|
||||
return
|
||||
|
||||
var/toggled_option = params["option"]
|
||||
if(toggled_option in selected_options)
|
||||
selected_options -= toggled_option
|
||||
else
|
||||
selected_options += toggled_option
|
||||
update_echocomp()
|
||||
return TRUE
|
||||
|
||||
Reference in New Issue
Block a user