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

420 lines
14 KiB
Plaintext

/proc/debug_light_sources()
GLOB.light_debug_enabled = TRUE
var/list/sum = list()
var/total = 0
for(var/datum/light_source/source)
if(!source.source_atom)
continue
source.source_atom.debug_lights()
sum[source.source_atom.type] += 1
total += 1
sortTim(sum, associative = TRUE)
var/text = ""
for(var/type in sum)
text += "[type] = [sum[type]]\n"
text += "total iterated: [total]"
for(var/client/lad in GLOB.admins)
var/datum/action/spawn_light/let_there_be = new (lad.mob.mind || lad.mob)
let_there_be.Grant(lad.mob)
// I am sorry
SSdcs.RegisterSignal(SSdcs, COMSIG_GLOB_CLIENT_CONNECT, TYPE_PROC_REF(/datum/controller/subsystem/processing/dcs, on_client_connect))
message_admins(text)
/datum/controller/subsystem/processing/dcs/proc/on_client_connect(datum/source, client/new_lad)
SIGNAL_HANDLER
var/datum/action/spawn_light/let_there_be = new (new_lad.mob.mind || new_lad.mob)
let_there_be.Grant(new_lad.mob)
/proc/undebug_light_sources()
GLOB.light_debug_enabled = FALSE
for(var/datum/weakref/button_ref as anything in GLOB.light_debugged_atoms)
var/atom/button = button_ref.resolve()
if(!button)
GLOB.light_debugged_atoms -= button_ref
continue
button.undebug_lights()
SEND_GLOBAL_SIGNAL(COMSIG_LIGHT_DEBUG_DISABLED)
SSdcs.UnregisterSignal(SSdcs, COMSIG_GLOB_CLIENT_CONNECT)
GLOBAL_LIST_EMPTY(light_debugged_atoms)
/// Sets up this light source to be debugged, setting up in world buttons to control and move it
/// Also freezes it, so it can't change in future
/atom/proc/debug_lights()
if(isturf(src) || HAS_TRAIT(src, TRAIT_LIGHTING_DEBUGGED))
return
ADD_TRAIT(src, TRAIT_LIGHTING_DEBUGGED, LIGHT_DEBUG_TRAIT)
GLOB.light_debugged_atoms += WEAKREF(src)
add_filter("debug_light", 0, outline_filter(2, COLOR_CENTCOM_BLUE))
var/static/uid = 0
if(!render_target)
render_target = "light_debug_[uid]"
uid++
var/atom/movable/render_step/color/above_light = new(null, src, "#ffffff23")
SET_PLANE_EXPLICIT(above_light, ABOVE_LIGHTING_PLANE, src)
add_overlay(above_light)
QDEL_NULL(above_light)
// Freeze our light would you please
light_flags |= LIGHT_FROZEN
new /atom/movable/screen/light_button/toggle(src)
new /atom/movable/screen/light_button/edit(src)
new /atom/movable/screen/light_button/move(src)
/// Disables light debugging, so you can let a scene fall to what it visually should be, or just fix admin fuckups
/atom/proc/undebug_lights()
// I don't really want to undebug a light if it's off rn
// Loses control if we turn it back on again
if(isturf(src) || !HAS_TRAIT(src, TRAIT_LIGHTING_DEBUGGED) || !light)
return
REMOVE_TRAIT(src, TRAIT_LIGHTING_DEBUGGED, LIGHT_DEBUG_TRAIT)
GLOB.light_debugged_atoms -= WEAKREF(src)
remove_filter("debug_light")
// Removes the glow overlay via stupid, sorry
var/atom/movable/render_step/color/above_light = new(null, src, "#ffffff23")
SET_PLANE_EXPLICIT(above_light, ABOVE_LIGHTING_PLANE, src)
cut_overlay(above_light)
QDEL_NULL(above_light)
var/atom/movable/lie_to_areas = src
// Freeze our light would you please
light_flags &= ~LIGHT_FROZEN
for(var/atom/movable/screen/light_button/button in lie_to_areas.vis_contents)
qdel(button)
/atom/movable/screen/light_button
icon = 'icons/testing/lighting_debug.dmi'
plane = BALLOON_CHAT_PLANE // We hijack runechat because we can get multiz niceness without making a new PM
layer = ABOVE_ALL_MOB_LAYER
alpha = 100
var/datum/weakref/last_hovored_ref
/atom/movable/screen/light_button/Initialize(mapload)
. = ..()
attach_to(loc)
/atom/movable/screen/light_button/proc/attach_to(atom/new_owner)
if(loc)
UnregisterSignal(loc, COMSIG_QDELETING)
var/atom/movable/mislead_areas = loc
mislead_areas.vis_contents -= src
forceMove(new_owner)
layer = loc.layer
RegisterSignal(loc, COMSIG_QDELETING, PROC_REF(delete_self))
var/atom/movable/lie_to_areas = loc
lie_to_areas.vis_contents += src
/atom/movable/screen/light_button/proc/delete_self(datum/source)
SIGNAL_HANDLER
qdel(src)
// Entered and Exited won't fire while you're dragging something, because you're still "holding" it
// Very much byond logic, but I want nice for my highlighting, so we fake it with drag
// Copypasta from action buttons
/atom/movable/screen/light_button/MouseDrag(atom/over_object, src_location, over_location, src_control, over_control, params)
. = ..()
if(IS_WEAKREF_OF(over_object, last_hovored_ref))
return
var/atom/old_object
if(last_hovored_ref)
old_object = last_hovored_ref?.resolve()
else // If there's no current ref, we assume it was us. We also treat this as our "first go" location
old_object = src
if(old_object)
old_object.MouseExited(over_location, over_control, params)
last_hovored_ref = WEAKREF(over_object)
over_object.MouseEntered(over_location, over_control, params)
/atom/movable/screen/light_button/mouse_drop_dragged(atom/over, mob/user, src_location, over_location, params)
last_hovored_ref = null
/atom/movable/screen/light_button/MouseEntered(location, control, params)
. = ..()
animate(src, alpha = 255, time = 2)
/atom/movable/screen/light_button/MouseExited(location, control, params)
. = ..()
animate(src, alpha = initial(alpha), time = 2)
/atom/movable/screen/light_button/toggle
name = "Toggle Light"
desc = "Click to turn the light on/off"
icon_state = "light_enable"
/atom/movable/screen/light_button/toggle/attach_to(atom/new_owner)
if(loc)
UnregisterSignal(loc, COMSIG_ATOM_UPDATE_LIGHT_ON)
. = ..()
RegisterSignal(loc, COMSIG_ATOM_UPDATE_LIGHT_ON, PROC_REF(on_changed))
update_appearance()
/atom/movable/screen/light_button/toggle/Click(location, control, params)
. = ..()
if(!check_rights_for(usr.client, R_DEBUG))
return
var/atom/movable/parent = loc
parent.light_flags &= ~LIGHT_FROZEN
loc.set_light(l_on = !loc.light_on)
parent.light_flags |= LIGHT_FROZEN
/atom/movable/screen/light_button/toggle/proc/on_changed()
SIGNAL_HANDLER
update_appearance()
/atom/movable/screen/light_button/toggle/update_icon_state()
. = ..()
if(loc.light_on)
icon_state = "light_enable"
else
icon_state = "light_disable"
/atom/movable/screen/light_button/edit
name = "Edit Light"
desc = "Click to open an editing menu for the light"
icon_state = "light_focus"
/atom/movable/screen/light_button/edit/attach_to(atom/new_owner)
. = ..()
SStgui.try_update_ui(usr, src, null)
/atom/movable/screen/light_button/edit/Click(location, control, params)
. = ..()
ui_interact(usr)
/atom/movable/screen/light_button/edit/ui_state(mob/user)
return ADMIN_STATE(R_DEBUG)
/atom/movable/screen/light_button/edit/can_interact()
return TRUE
/atom/movable/screen/light_button/edit/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "LightController")
ui.open()
/atom/movable/screen/light_button/edit/ui_assets(mob/user)
return list(get_asset_datum(/datum/asset/spritesheet_batched/lights))
/atom/movable/screen/light_button/edit/ui_data()
var/list/data = list()
var/atom/parent = loc
var/list/light_info = list()
light_info["name"] = full_capitalize(parent.name)
light_info["on"] = parent.light_on
light_info["power"] = parent.light_power
light_info["range"] = parent.light_range
light_info["color"] = parent.light_color
light_info["angle"] = parent.light_angle
data["light_info"] = light_info
data["on"] = parent.light_on
data["direction"] = parent.dir
return data
/atom/movable/screen/light_button/edit/ui_static_data(mob/user)
. = ..()
var/list/data = list()
data["templates"] = list()
data["category_ids"] = list()
for(var/id in GLOB.light_types)
var/datum/light_template/template = GLOB.light_types[id]
var/list/insert = list()
var/list/light_info = list()
light_info["name"] = template.name
light_info["power"] = template.power
light_info["range"] = template.range
light_info["color"] = template.color
light_info["angle"] = template.angle
insert["light_info"] = light_info
insert["description"] = template.desc
insert["id"] = template.id
insert["category"] = template.category
if(!data["category_ids"][template.category])
data["category_ids"][template.category] = list()
data["category_ids"][template.category] += id
data["templates"][template.id] = insert
var/datum/light_template/first_template = GLOB.light_types[GLOB.light_types[1]]
data["default_id"] = first_template.id
data["default_category"] = first_template.category
return data
/atom/movable/screen/light_button/edit/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
var/atom/parent = loc
parent.light_flags &= ~LIGHT_FROZEN
switch(action)
if("set_on")
parent.set_light(l_on = params["value"])
if("change_color")
var/chosen_color = tgui_color_picker(ui.user, "Pick new color", "[parent]", parent.light_color)
if(chosen_color)
parent.set_light(l_color = chosen_color)
if("set_power")
parent.set_light(l_power = params["value"])
if("set_range")
parent.set_light(l_range = params["value"])
if("set_angle")
// We use dir instead of light dir because anything directional should have its lightdir tied
// And this way we can update the sprite too
parent.set_light(l_angle = params["value"])
if("set_dir")
parent.setDir(params["value"])
if("mirror_template")
var/datum/light_template/template = GLOB.light_types[params["id"]]
var/atom/new_light = template.create(parent.loc, parent.dir)
var/atom/movable/lies_to_children = parent
for(var/atom/movable/screen/light_button/button in lies_to_children.vis_contents)
button.attach_to(new_light)
qdel(parent)
if("isolate")
isolate_light(parent)
parent.light_flags |= LIGHT_FROZEN
return TRUE
/// Hides all the lights around a source temporarially, for the sake of figuring out how bad a light bleeds
/// (Except for turf lights, because they're a part of the "scene" and rarely modified)
/proc/isolate_light(atom/source, delay = 7 SECONDS)
var/list/datum/lighting_corner/interesting_corners = source.light?.effect_str
var/list/atom/sources = list()
for(var/datum/lighting_corner/corner as anything in interesting_corners)
for(var/datum/light_source/target_spotted as anything in corner.affecting)
if(isturf(target_spotted.source_atom))
continue
sources[target_spotted.source_atom] = TRUE
sources -= source // Please don't disable yourself
if(!length(sources))
return
// Now that we have all the lights (and a bit more), let's get rid of em
for(var/atom/light_source as anything in sources)
light_source.light_flags &= ~LIGHT_FROZEN
light_source.set_light(l_on = FALSE)
light_source.light_flags |= LIGHT_FROZEN
// Now we sleep until the lighting system has processed them
var/current_tick = SSlighting.times_fired
UNTIL(SSlighting.times_fired > current_tick || QDELETED(source) || !source.light)
if(QDELETED(source) || !source.light)
repopulate_lights(sources)
return
// And finally, wait the allotted time, and reawake em
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(repopulate_lights), sources), delay)
/proc/repopulate_lights(list/atom/sources)
for(var/atom/light_source as anything in sources)
light_source.light_flags &= ~LIGHT_FROZEN
light_source.set_light(l_on = TRUE)
light_source.light_flags |= LIGHT_FROZEN
/atom/movable/screen/light_button/move
name = "Move Light"
desc = "Drag to move the light around"
icon_state = "light_move"
mouse_drag_pointer = 'icons/effects/mouse_pointers/light_drag.dmi'
/atom/movable/screen/light_button/move/mouse_drop_dragged(atom/over_object)
if(!ismovable(loc))
return
var/atom/movable/movable_owner = loc
movable_owner.forceMove(get_turf(over_object))
/datum/action/spawn_light
name = "Spawn Light"
desc = "Create a light from a template"
button_icon = 'icons/mob/actions/actions_construction.dmi'
button_icon_state = "light_spawn"
/datum/action/spawn_light/New(Target)
. = ..()
RegisterSignal(SSdcs, COMSIG_LIGHT_DEBUG_DISABLED, PROC_REF(debug_disabled))
/datum/action/spawn_light/proc/debug_disabled()
SIGNAL_HANDLER
qdel(src)
/datum/action/spawn_light/Grant(mob/grant_to)
. = ..()
RegisterSignal(grant_to.client, COMSIG_CLIENT_MOB_LOGIN, PROC_REF(move_action), override = TRUE)
/datum/action/spawn_light/proc/move_action(client/source, mob/new_mob)
SIGNAL_HANDLER
Grant(new_mob)
/datum/action/spawn_light/Trigger(mob/clicker, trigger_flags)
. = ..()
if(!.)
return
ui_interact(usr)
/datum/action/spawn_light/ui_state(mob/user)
return ADMIN_STATE(R_DEBUG)
/datum/action/spawn_light/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "LightSpawn")
ui.open()
/datum/action/spawn_light/ui_assets(mob/user)
return list(get_asset_datum(/datum/asset/spritesheet_batched/lights))
/datum/action/spawn_light/ui_data()
var/list/data = list()
return data
/datum/action/spawn_light/ui_static_data(mob/user)
. = ..()
var/list/data = list()
data["templates"] = list()
data["category_ids"] = list()
for(var/id in GLOB.light_types)
var/datum/light_template/template = GLOB.light_types[id]
var/list/insert = list()
var/list/light_info = list()
light_info["name"] = template.name
light_info["power"] = template.power
light_info["range"] = template.range
light_info["color"] = template.color
light_info["angle"] = template.angle
insert["light_info"] = light_info
insert["description"] = template.desc
insert["id"] = template.id
insert["category"] = template.category
if(!data["category_ids"][template.category])
data["category_ids"][template.category] = list()
data["category_ids"][template.category] += id
data["templates"][template.id] = insert
var/datum/light_template/first_template = GLOB.light_types[GLOB.light_types[1]]
data["default_id"] = first_template.id
data["default_category"] = first_template.category
return data
/datum/action/spawn_light/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
switch(action)
if("spawn_template")
var/datum/light_template/template = GLOB.light_types[params["id"]]
template.create(get_turf(owner), params["dir"])
return TRUE