Files
Bubberstation/code/modules/assembly/infrared.dm
tonty 3f0b4abb8d Replaces world.icon_size (and some magic numbers) with defines (#86819)
## About The Pull Request

All usages of world.icon_size in code have been replaced with new
`ICONSIZE_X`, `ICONSIZE_Y` and `ICONSIZE_ALL` defines depending on
context

Replaces some "32" magic numbers with the defines

A few bits of code have been modified to split up x/y math as well

## Why It's Good For The Game

Magic number bad, code more readable, code more flexible and I'm told
there's an access cost to doing world.icon_size so minor performance
gains

## Changelog

🆑 tonty
code: made some code relating to the world's icon size more readable
/🆑

---------

Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com>
2024-09-29 13:28:32 +00:00

311 lines
9.1 KiB
Plaintext

/obj/item/assembly/infra
name = "infrared emitter"
desc = "Emits a visible or invisible beam and is triggered when the beam is interrupted."
icon_state = "infrared"
base_icon_state = "infrared"
custom_materials = list(
/datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT,
/datum/material/glass = SMALL_MATERIAL_AMOUNT * 5,
)
is_position_sensitive = TRUE
drop_sound = 'sound/items/handling/component_drop.ogg'
pickup_sound = 'sound/items/handling/component_pickup.ogg'
set_dir_on_move = FALSE
/// Whether the beam is beaming
var/on = FALSE
/// Whether the beam is visible
var/visible = FALSE
/// The length the beam can go
var/max_beam_length = 8
/// The radius of which people can hear triggers
var/hearing_range = 3
/// Pass flags the beam uses to determine what it can pass through
var/beam_pass_flags = PASSTABLE|PASSGLASS|PASSGRILLE
/// The current active beam datum
VAR_FINAL/datum/beam/active_beam
/// A reference to the turf at the END of our active beam
VAR_FINAL/turf/buffer_turf
/obj/item/assembly/infra/Initialize(mapload)
. = ..()
AddComponent(/datum/component/simple_rotation)
/obj/item/assembly/infra/Destroy()
QDEL_NULL(active_beam)
buffer_turf = null
return ..()
/obj/item/assembly/infra/examine(mob/user)
. = ..()
. += span_notice("The infrared trigger is [on ? "on" : "off"].")
/// Checks if the passed movable can block the beam.
/obj/item/assembly/infra/proc/atom_blocks_beam(atom/movable/beam_atom)
if(isnull(beam_atom))
return FALSE
if(beam_atom == src || beam_atom == holder)
return FALSE
// Blocks beams from triggering themselves, important to avoid infinite loops
if(istype(beam_atom, /obj/effect/ebeam))
return FALSE
// Anti-revenant / anti-ghost guard
if(beam_atom.invisibility)
return FALSE
// In general non-dense items should not block beams, but make special cases for things being thrown
if(!beam_atom.density && !beam_atom.throwing)
return FALSE
// The actually important check. Ensures stuff like mobs trip it but stuff like laser projectiles don't
if(beam_atom.pass_flags_self & beam_pass_flags)
return FALSE
if(isitem(beam_atom))
var/obj/item/beam_item = beam_atom
if(beam_item.item_flags & ABSTRACT)
return FALSE
return TRUE
/// Checks if the passed turf (or something on it) can block the beam.
/obj/item/assembly/infra/proc/turf_blocks_beam(turf/beam_turf)
if(beam_turf.density)
return TRUE
for(var/atom/movable/blocker as anything in beam_turf)
if(atom_blocks_beam(blocker))
return TRUE
return FALSE
/// Used to refresh the beam in whatever context.
/obj/item/assembly/infra/proc/make_beam()
SHOULD_NOT_SLEEP(TRUE)
if(!isnull(buffer_turf))
UnregisterSignal(buffer_turf, list(COMSIG_ATOM_EXITED, COMSIG_TURF_CHANGE))
buffer_turf = null
QDEL_NULL(active_beam)
if(!on || !secured)
return
var/atom/start_loc = holder || src
var/turf/start_turf = start_loc.loc
if(!istype(start_turf))
return
// One extra turf is added to max length to get an extra buffer
var/list/turf/potential_turfs = get_line(start_turf, get_ranged_target_turf(start_turf, dir, max_beam_length + 1))
if(!length(potential_turfs))
return
var/list/turf/final_turfs = list()
for(var/turf/target_turf as anything in potential_turfs)
if(target_turf != start_turf && turf_blocks_beam(target_turf))
break
final_turfs += target_turf
if(!length(final_turfs))
return
var/turf/last_turf = final_turfs[length(final_turfs)]
buffer_turf = get_step(last_turf, dir)
active_beam = start_loc.Beam(
BeamTarget = last_turf,
beam_type = /obj/effect/ebeam/reacting/infrared,
icon = 'icons/effects/beam.dmi',
icon_state = "infrared",
emissive = TRUE,
override_target_pixel_x = pixel_x,
override_target_pixel_y = pixel_y,
)
RegisterSignal(active_beam, COMSIG_BEAM_ENTERED, PROC_REF(beam_entered))
RegisterSignal(active_beam, COMSIG_BEAM_TURFS_CHANGED, PROC_REF(beam_turfs_changed))
update_visible()
// Buffer can be null (if we're at map edge for an example) but this fine
if(!isnull(buffer_turf))
// We need to check the state of the turf at the end of the beam, to determine when we need to re-grow (if blocked)
RegisterSignal(buffer_turf, COMSIG_ATOM_EXITED, PROC_REF(buffer_exited))
RegisterSignal(buffer_turf, COMSIG_TURF_CHANGE, PROC_REF(buffer_changed))
/obj/item/assembly/infra/proc/beam_entered(datum/beam/source, obj/effect/ebeam/hit, atom/movable/entered)
SIGNAL_HANDLER
// First doesn't count
if(hit == active_beam.elements[1])
return
if(!atom_blocks_beam(entered))
return
beam_trigger(hit, entered)
/obj/item/assembly/infra/proc/beam_turfs_changed(datum/beam/source, list/datum/callback/post_change_callbacks)
SIGNAL_HANDLER
// If the turfs changed it's possible something is now blocking it, remake when done
post_change_callbacks += CALLBACK(src, PROC_REF(make_beam))
/obj/item/assembly/infra/proc/buffer_exited(turf/source, atom/movable/exited, ...)
SIGNAL_HANDLER
if(!atom_blocks_beam(exited))
return
make_beam()
/obj/item/assembly/infra/proc/buffer_changed(turf/source, path, list/new_baseturfs, flags, list/datum/callback/post_change_callbacks)
SIGNAL_HANDLER
post_change_callbacks += CALLBACK(src, PROC_REF(make_beam))
/obj/item/assembly/infra/proc/beam_trigger(obj/effect/ebeam/hit, atom/movable/entered)
make_beam()
if(!COOLDOWN_FINISHED(src, next_activate))
return
pulse()
audible_message(
message = span_infoplain("[icon2html(src, hearers(holder || src))] *beep* *beep* *beep*"),
hearing_distance = hearing_range,
)
playsound(src, 'sound/machines/beep/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE, extrarange = hearing_range - SOUND_RANGE + 1, falloff_distance = hearing_range)
COOLDOWN_START(src, next_activate, 3 SECONDS)
/obj/item/assembly/infra/activate()
. = ..()
if(!.)
return
toggle_on()
/obj/item/assembly/infra/toggle_secure()
. = ..()
make_beam()
/// Toggles the beam on or off.
/obj/item/assembly/infra/proc/toggle_on()
on = !on
make_beam()
update_appearance()
/// Toggles the visibility of the beam.
/obj/item/assembly/infra/proc/toggle_visible()
visible = !visible
update_visible()
update_appearance()
/// Updates the visibility of the beam (if active).
/obj/item/assembly/infra/proc/update_visible()
if(visible)
for(var/obj/effect/ebeam/beam as anything in active_beam?.elements)
beam.RemoveInvisibility(REF(src))
else
for(var/obj/effect/ebeam/beam as anything in active_beam?.elements)
beam.SetInvisibility(INVISIBILITY_ABSTRACT, REF(src))
/obj/item/assembly/infra/vv_edit_var(var_name, var_value)
. = ..()
if(!.)
return
switch(var_name)
if(NAMEOF(src, visible))
update_visible()
update_appearance()
if(NAMEOF(src, on), NAMEOF(src, max_beam_length), NAMEOF(src, beam_pass_flags))
make_beam()
update_appearance()
/obj/item/assembly/infra/update_appearance(updates)
. = ..()
holder?.update_appearance(updates)
/obj/item/assembly/infra/update_overlays()
. = ..()
attached_overlays = list()
if(on)
attached_overlays += "[base_icon_state]_on"
. += attached_overlays
/obj/item/assembly/infra/dropped()
. = ..()
if(holder)
holder_movement() //sync the dir of the device as well if it's contained in a TTV or an assembly holder
else
make_beam()
/obj/item/assembly/infra/on_attach()
. = ..()
make_beam()
holder.set_dir_on_move = set_dir_on_move
/obj/item/assembly/infra/on_detach()
holder.set_dir_on_move = initial(holder.set_dir_on_move)
. = ..()
if(!.)
return
make_beam()
/obj/item/assembly/infra/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change)
. = ..()
if(loc == old_loc)
return
make_beam()
if(!visible || forced || !movement_dir || !Adjacent(old_loc))
return
// Because the new beam is made in the new loc, it "jumps" from one turf to another
// We can do an animate to pretend we're gliding between turfs rather than making a whole new beam
var/x_move = 0
var/y_move = 0
if(movement_dir & NORTH)
y_move = -ICON_SIZE_Y
else if(movement_dir & SOUTH)
y_move = ICON_SIZE_Y
if(movement_dir & WEST)
x_move = ICON_SIZE_X
else if(movement_dir & EAST)
x_move = -ICON_SIZE_X
var/fake_glide_time = round(ICON_SIZE_ALL / glide_size * world.tick_lag, world.tick_lag)
for(var/obj/effect/ebeam/beam as anything in active_beam?.elements)
var/matrix/base_transform = matrix(beam.transform)
beam.transform = beam.transform.Translate(x_move, y_move)
animate(beam, transform = base_transform, time = fake_glide_time)
/obj/item/assembly/infra/setDir(newdir)
var/prev_dir = dir
. = ..()
if(dir == prev_dir)
return
make_beam()
/obj/item/assembly/infra/ui_status(mob/user, datum/ui_state/state)
return is_secured(user) ? ..() : UI_CLOSE
/obj/item/assembly/infra/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "InfraredEmitter", name)
ui.open()
/obj/item/assembly/infra/ui_data(mob/user)
var/list/data = list()
data["on"] = on
data["visible"] = visible
return data
/obj/item/assembly/infra/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return .
switch(action)
if("power")
toggle_on()
return TRUE
if("visibility")
toggle_visible()
return TRUE
// Beam subtype for the infrared emitter
/obj/effect/ebeam/reacting/infrared
name = "infrared beam"
alpha = 175