GLOBAL_LIST_EMPTY(all_beam_points) // Creates and manages a beam attached to itself and another beam_point. // You can do cool things with these such as moving the beam_point to move the beam, turning them on and off on a timer, triggered by external input, and more. /obj/effect/map_effect/beam_point name = "beam point" icon_state = "beam_point" // General variables. var/list/my_beams = list() // Instances of beams. Deleting one will kill the beam. var/id = "A" // Two beam_points must share the same ID to be connected to each other. var/max_beams = 10 // How many concurrent beams to seperate beam_points to have at once. Set to zero to only act as targets for other beam_points. var/seek_range = 7 // How far to look for an end beam_point when not having a beam. Defaults to screen height/width. Make sure this is below beam_max_distance. // Controls how and when the beam is created. var/make_beams_on_init = FALSE var/use_timer = FALSE // Sadly not the /tg/ timers. var/list/on_duration = list(2 SECONDS, 2 SECONDS, 2 SECONDS) // How long the beam should stay on for, if use_timer is true. Alternates between each duration in the list. var/list/off_duration = list(3 SECONDS, 0.5 SECOND, 0.5 SECOND) // How long it should stay off for. List length is not needed to be the same as on_duration. var/timer_on_index = 1 // Index to use for on_duration list. var/timer_off_index = 1// Ditto, for off_duration list. var/initial_delay = 0 // How long to wait before first turning on the beam, to sync beam times or create a specific pattern. var/beam_creation_sound = null // Optional sound played when one or more beams are created. var/beam_destruction_sound = null // Optional sound played when a beam is destroyed. // Beam datum arguments. var/beam_icon = 'icons/effects/beam.dmi' // Icon file to use for beam visuals. var/beam_icon_state = "b_beam" // Icon state to use for visuals. var/beam_time = INFINITY // How long the beam lasts. By default it will last forever until destroyed. var/beam_max_distance = 10 // If the beam is farther than this, it will be destroyed. Make sure it's higher than seek_range. var/beam_type = /obj/effect/ebeam // The type of beam. Default has no special properties. Some others may do things like hurt things touching it. var/beam_sleep_time = 3 // How often the beam updates visually. Suggested to leave this alone, 3 is already fast. /obj/effect/map_effect/beam_point/Initialize() GLOB.all_beam_points += src if(make_beams_on_init) create_beams() if(use_timer) addtimer(CALLBACK(src, .proc/handle_beam_timer), initial_delay) return ..() /obj/effect/map_effect/beam_point/Destroy() destroy_all_beams() use_timer = FALSE GLOB.all_beam_points -= src return ..() // This is the top level proc to make the magic happen. /obj/effect/map_effect/beam_point/proc/create_beams() if(my_beams.len >= max_beams) return var/beams_to_fill = max_beams - my_beams.len for(var/i = 1 to beams_to_fill) var/obj/effect/map_effect/beam_point/point = seek_beam_point() if(!point) break // No more points could be found, no point checking repeatively. build_beam(point) // Finds a suitable beam point. /obj/effect/map_effect/beam_point/proc/seek_beam_point() for(var/obj/effect/map_effect/beam_point/point in GLOB.all_beam_points) if(id != point.id) continue // Not linked together by ID. if(has_active_beam(point)) continue // Already got one. if(point.z != src.z) continue // Not on same z-level. get_dist() ignores z-levels by design according to docs. if(get_dist(src, point) > seek_range) continue // Too far. return point // Checks if the two points have an active beam between them. // Used to make sure two points don't have more than one beam. /obj/effect/map_effect/beam_point/proc/has_active_beam(var/obj/effect/map_effect/beam_point/them) // First, check our beams. for(var/datum/beam/B in my_beams) if(B.target == them) return TRUE if(B.origin == them) // This shouldn't be needed unless the beam gets built backwards but why not. return TRUE // Now check theirs, to see if they have a beam on us. for(var/datum/beam/B in them.my_beams) if(B.target == src) return TRUE if(B.origin == src) // Same story as above. return TRUE return FALSE /obj/effect/map_effect/beam_point/proc/build_beam(var/atom/beam_target) if(!beam_target) log_debug("[src] ([src.type] \[[x],[y],[z]\]) failed to build its beam due to not having a target.") return FALSE var/datum/beam/new_beam = Beam(beam_target, beam_icon_state, beam_icon, beam_time, beam_max_distance, beam_type, beam_sleep_time) my_beams += new_beam if(beam_creation_sound) playsound(src, beam_creation_sound, 70, 1) return TRUE /obj/effect/map_effect/beam_point/proc/destroy_beam(var/datum/beam/B) if(!B) log_debug("[src] ([src.type] \[[x],[y],[z]\]) was asked to destroy a beam that does not exist.") return FALSE if(!(B in my_beams)) log_debug("[src] ([src.type] \[[x],[y],[z]\]) was asked to destroy a beam it did not own.") return FALSE my_beams -= B qdel(B) if(beam_destruction_sound) playsound(src, beam_destruction_sound, 70, 1) return TRUE /obj/effect/map_effect/beam_point/proc/destroy_all_beams() for(var/datum/beam/B in my_beams) destroy_beam(B) return TRUE // This code makes me sad. /obj/effect/map_effect/beam_point/proc/handle_beam_timer() if(!use_timer || QDELETED(src)) return if(my_beams.len) // Currently on. destroy_all_beams() color = "#FF0000" timer_off_index++ if(timer_off_index > off_duration.len) timer_off_index = 1 spawn(off_duration[timer_off_index]) .() else // Currently off. // If nobody's around, keep the beams off to avoid wasteful beam process(), if they have one. if(!always_run && !check_for_player_proximity(src, proximity_needed, ignore_ghosts, ignore_afk)) spawn(retry_delay) .() return create_beams() color = "#00FF00" timer_on_index++ if(timer_on_index > on_duration.len) timer_on_index = 1 spawn(on_duration[timer_on_index]) .() // Subtypes to use in maps and adminbuse. // Remember, beam_points ONLY connect to other beam_points with the same id variable. // Creates the beam when instantiated and stays on until told otherwise. /obj/effect/map_effect/beam_point/instant make_beams_on_init = TRUE /obj/effect/map_effect/beam_point/instant/electric beam_icon_state = "nzcrentrs_power" beam_type = /obj/effect/ebeam/reactive/electric beam_creation_sound = 'sound/effects/lightningshock.ogg' beam_destruction_sound = "sparks" // Turns on and off on a timer. /obj/effect/map_effect/beam_point/timer use_timer = TRUE // Shocks people who touch the beam while it's on. Flicks on and off on a specific pattern. /obj/effect/map_effect/beam_point/timer/electric beam_icon_state = "nzcrentrs_power" beam_type = /obj/effect/ebeam/reactive/electric beam_creation_sound = 'sound/effects/lightningshock.ogg' beam_destruction_sound = "sparks" seek_range = 3 // Is only a target for other beams to connect to. /obj/effect/map_effect/beam_point/end max_beams = 0 // Can only have one beam. /obj/effect/map_effect/beam_point/mono make_beams_on_init = TRUE max_beams = 1