Files
Alexis 21b4095dfd [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>
2026-05-16 00:56:00 +02:00

161 lines
7.3 KiB
Plaintext

/// Allows us to intercept overlay lighting's well, light overlays
/// Normally these are static, but by giving them a render source and copying their base appearance
/// Animating this datum's child objects allows us to do SO much fun stuff
/datum/light_middleman
/// Owning parent we're interceeding for
/// Could in theory be a turf but lies to areas means we have to pick something to type it as
var/atom/movable/parent
/// The holder we are currently displaying our light on
var/atom/movable/light_holder
/// Holds the primary light source
var/obj/effect/abstract/light_middleman/primary_intercept
/// Exists to hold the cone so children can modify it if they want
var/obj/effect/abstract/light_middleman/cone_intercept
/// Are we overriding the light already?
var/overriding = FALSE
/// Weakref to the object we are displaying our effects on
var/datum/weakref/holder_ref
/datum/light_middleman/New(atom/parent, unique_string)
. = ..()
if(!IS_OVERLAY_LIGHT_SYSTEM(parent.light_system))
stack_trace("Attempted to create a light middleman with a parent [parent.type] that does not use overlay lighting! This will not work.")
if(isturf(parent))
stack_trace("Warning, becuase overlay lights are basically never used on turfs, since they don't move,\
vis contents replacement has not yet been implemented for them (see changeturf for why this is needed)!")
src.parent = parent
primary_intercept = new()
cone_intercept = new()
var/static/uuid = 0
uuid = WRAP_UID(uuid + 1)
primary_intercept.render_target = "*[unique_string]_[uuid]_target"
cone_intercept.render_target = "[primary_intercept.render_target]_cone" // made to mirror how overlay lights work
/datum/light_middleman/Destroy(force)
stop_overriding_light()
QDEL_NULL(primary_intercept)
QDEL_NULL(cone_intercept)
parent = null
light_holder = null
return ..()
/datum/light_middleman/proc/being_overriding_light(unique_string)
if(overriding)
return
overriding = TRUE
// We register here because our later set render source will always trigger a refresh and thus let us capture appearances properly
// Assuming there's an overlay light on the other side
RegisterSignal(parent, COMSIG_ATOM_OVERLAY_LIGHT_APPLIED, PROC_REF(light_applied))
RegisterSignal(parent, COMSIG_ATOM_OVERLAY_LIGHT_REMOVED, PROC_REF(light_removed))
parent.set_light_render_source(primary_intercept.render_target)
/datum/light_middleman/proc/stop_overriding_light()
if(!overriding)
return
overriding = FALSE
UnregisterSignal(parent, COMSIG_ATOM_OVERLAY_LIGHT_APPLIED)
UnregisterSignal(parent, COMSIG_ATOM_OVERLAY_LIGHT_REMOVED)
var/atom/movable/old_holder = holder_ref?.resolve()
if(old_holder)
old_holder.vis_contents -= primary_intercept
old_holder.vis_contents -= cone_intercept
holder_ref = null
parent.set_light_render_source("")
/datum/light_middleman/proc/light_applied(datum/source, image/visible_mask, image/cone, atom/movable/light_holder)
SIGNAL_HANDLER
var/atom/movable/old_holder = holder_ref?.resolve()
// If we were somewhere before, clean us out
if(old_holder)
old_holder.vis_contents -= primary_intercept
old_holder.vis_contents -= cone_intercept
holder_ref = null
// how we make sure we're in the client's view
light_holder.vis_contents += primary_intercept
// Avoids unneeded effects clientside
if(IS_OVERLAY_CONE_LIGHT_SYSTEM(parent.light_system))
light_holder.vis_contents += cone_intercept
old_holder = WEAKREF(light_holder)
var/old_target = primary_intercept.render_target
var/old_cone_target = cone_intercept.render_target
// This will halt any animations we have ongoing so if you care about that you've gotta react to it properly
primary_intercept.appearance = visible_mask
cone_intercept.appearance = cone
// set ourselves up to render back onto the visible mask
primary_intercept.render_source = ""
primary_intercept.render_target = old_target
cone_intercept.render_source = ""
cone_intercept.render_target = old_cone_target
// Dir is important I'm told
primary_intercept.vis_flags |= VIS_INHERIT_DIR
cone_intercept.vis_flags |= VIS_INHERIT_DIR
// Will double apply, here we go gang
primary_intercept.transform = null
cone_intercept.transform = null
primary_intercept.color = null
cone_intercept.color = null
primary_intercept.alpha = 255
cone_intercept.alpha = 255
// Sometimes can be BLEND_SUBTRACT, we don't want that
primary_intercept.blend_mode = BLEND_ADD
cone_intercept.blend_mode = BLEND_ADD
/// Allows users to hook into a refresh so they can remake their modifications to our intercepts
SEND_SIGNAL(src, COMSIG_LIGHT_MIDDLEMAN_UPDATED)
/datum/light_middleman/proc/light_removed(datum/source, atom/movable/light_holder)
SIGNAL_HANDLER
light_holder.vis_contents -= primary_intercept
light_holder.vis_contents -= cone_intercept
holder_ref = null
/// Just... cause it's better then not having a bespoke type
/obj/effect/abstract/light_middleman
// Procs for reuse on multiple types
/proc/fire_flicker_middleman(datum/light_middleman/middleman)
var/obj/effect/abstract/main_light = middleman.primary_intercept
// Just in case a subtype is wildin
var/obj/effect/abstract/cone_light = middleman.cone_intercept
/// Applies a nice random flicker to flares and their subtypes which will I hope sell the fire effect better
var/list/random_times = list()
for(var/i in 1 to 17)
// Makes a nice upside down U distribution
var/random_down = LERP(-0.075 SECONDS, 0.075 SECONDS, ANCHORED_INVERSE_CAUCHY(0.55))
var/random_bottom = LERP(-0.05 SECONDS, 0.05 SECONDS, ANCHORED_INVERSE_CAUCHY(0.55))
var/random_up = LERP(-0.075 SECONDS, 0.075 SECONDS, ANCHORED_INVERSE_CAUCHY(0.55))
// We want a potentially quite long "top end" so the flicker can be an actual flicker instead of a heartbeat (that's the goal at least)
var/random_top = LERP(-0.225 SECONDS, 0.225 SECONDS, ANCHORED_INVERSE_CAUCHY(0.55))
random_times += list(list(
0.125 SECONDS + random_down,
0 SECONDS + random_bottom,
0.125 SECONDS + random_up,
0.275 SECONDS + random_top,
))
// Going to loop our alpha high and low semi quickly to mimik a flickering fire/flare
var/list/first_time = random_times[1]
animate(main_light, alpha = 235, time = first_time[1], easing = CUBIC_EASING|EASE_OUT, loop = -1)
animate(alpha = 235, time = first_time[2])
animate(alpha = 255, time = first_time[3], easing = CUBIC_EASING|EASE_OUT)
animate(alpha = 255, time = first_time[4])
for(var/list/time in random_times - first_time)
animate(alpha = 235, time = time[1], easing = CUBIC_EASING|EASE_OUT)
animate(alpha = 235, time = first_time[2])
animate(alpha = 255, time = first_time[3], easing = CUBIC_EASING|EASE_OUT)
animate(alpha = 255, time = first_time[4])
// I'd really love to do both these in the same loop but parallel animations are the devil from the bible
// I don't think any of these are directional but just in case
animate(cone_light, alpha = 235, time = first_time[1], easing = CUBIC_EASING|EASE_OUT, loop = -1)
animate(alpha = 235, time = first_time[2])
animate(alpha = 255, time = first_time[3], easing = CUBIC_EASING|EASE_OUT)
animate(alpha = 255, time = first_time[4])
for(var/list/time in random_times - first_time)
animate(alpha = 235, time = time[1], easing = CUBIC_EASING|EASE_OUT)
animate(alpha = 235, time = first_time[2])
animate(alpha = 255, time = first_time[3], easing = CUBIC_EASING|EASE_OUT)
animate(alpha = 255, time = first_time[4])