Files
Matt Atlas 94d92803b4 Replaces our lighting system with CM's. (#21465)
Depends on #21458.

Ports https://github.com/cmss13-devs/cmss13/pull/4229, with the original
authors as:

- https://github.com/tgstation/TerraGov-Marine-Corps/pull/1964 for the
lighting controller (A-lexa)
- https://github.com/tgstation/TerraGov-Marine-Corps/pull/4747 and
https://github.com/tgstation/TerraGov-Marine-Corps/pull/7263 for the
lighting (TiviPlus)
- https://github.com/tgstation/tgstation/pull/54520 for the dir lighting
component
- https://github.com/tgstation/tgstation/pull/75018 for the out of
bounds fix in lighting
- https://github.com/tgstation/TerraGov-Marine-Corps/pull/6678 for the
emissives (TiviPlus)

The main driving reason behind this is that current lighting consumes
way too much processing power, especially for things like odysseys/away
sites where a billion light sources are processing/moving at once and
the game slows down to a crawl. Hopefully this improves the situation by
a good margin, but we will need some testmerging to confirm that.
<img width="1349" height="1349" alt="image"
src="https://github.com/user-attachments/assets/1059ba2b-c0c5-495a-9c76-2d75d0c42bf2"
/>
<img width="1349" height="1349" alt="image"
src="https://github.com/user-attachments/assets/9704b0f6-4cf6-4dfd-a6cb-5702ad07d677"
/>


- [x] Resolve todos
- [x] Look into open space fuckery (border objects)

---------

Co-authored-by: Matt Atlas <liermattia@gmail.com>
Co-authored-by: JohnWildkins <john.wildkins@gmail.com>
2025-11-04 21:27:42 +00:00

173 lines
5.9 KiB
Plaintext

///Lighting mask sprite radius in tiles
#define LIGHTING_MASK_RADIUS 4
///Lighting mask sprite diameter in pixels
#define LIGHTING_MASK_SPRITE_SIZE LIGHTING_MASK_RADIUS * 64
/atom/movable/lighting_mask
name = ""
icon = LIGHTING_ICON_BIG
icon_state = "light_normalized"
anchored = TRUE
plane = LIGHTING_PLANE
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
layer = LIGHTING_SECONDARY_LAYER
invisibility = INVISIBILITY_LIGHTING
blend_mode = BLEND_ADD
appearance_flags = KEEP_TOGETHER|RESET_TRANSFORM
///The current angle the item is pointing at
var/current_angle = 0
///The radius of illumination of the mask
var/radius = 0
///The atom that we are attached to, does not need hard del protection as we are deleted with it
var/atom/attached_atom
///Reference to the holder /obj/effect
var/obj/effect/lighting_mask_holder/mask_holder
///Prevents us from registering for update twice before SSlighting init
var/awaiting_update = FALSE
///Set to TRUE if you want the light to rotate with the owner
var/is_directional = FALSE
///Turfs that are being affected by this mask, this is for the sake of luminosity
var/list/turf/affecting_turfs
///list of mutable appearance shadows
var/list/mutable_appearance/shadows
var/times_calculated = 0
//Please dont change these
var/calculated_position_x
var/calculated_position_y
/atom/movable/lighting_mask/Initialize(mapload, ...)
. = ..()
add_filter("pixel_smoother", 3, gauss_blur_filter(2))
add_filter("shadow_alpha_masking", 4, alpha_mask_filter(render_source = SHADOW_RENDER_TARGET, flags = MASK_INVERSE))
/atom/movable/lighting_mask/Destroy()
//Make sure we werent destroyed in init
SSlighting.mask_queue -= src
//Remove from affecting turfs
if(affecting_turfs)
for(var/turf/thing as anything in affecting_turfs)
var/area/A = thing.loc
LAZYREMOVE(thing.hybrid_lights_affecting, src)
if(!A.base_lighting_alpha)
thing.luminosity -= 1
affecting_turfs = null
//Cut the shadows. Since they are overlays they will be deleted when cut from overlays.
LAZYCLEARLIST(shadows)
mask_holder = null
attached_atom = null
return ..()
///Sets the radius of the mask, and updates everything that needs to be updated
/atom/movable/lighting_mask/proc/set_radius(new_radius, transform_time = 0)
//Update our matrix
var/matrix/new_size_matrix = get_matrix(new_radius)
apply_matrix(new_size_matrix, transform_time)
radius = new_radius
//then recalculate and redraw
queue_mask_update()
///if you want the matrix to grow or shrink, you can do that using this proc when applyng it
/atom/movable/lighting_mask/proc/apply_matrix(matrix/to_apply, transform_time = 0)
if(transform_time)
animate(src, transform = to_apply, time = transform_time)
else
transform = to_apply
///Creates a matrix for the lighting mak to use
/atom/movable/lighting_mask/proc/get_matrix(radius = 1)
var/matrix/new_size_matrix = new()
//Scale
// - Scale to the appropriate radius
new_size_matrix.Scale(radius / LIGHTING_MASK_RADIUS)
//Translate
// - Center the overlay image
// - Ok so apparently translate is affected by the scale we already did huh.
// ^ Future me here, its because it works as translate then scale since its backwards.
// ^ ^ Future future me here, it totally shouldnt since the translation component of a matrix is independent to the scale component.
new_size_matrix.Translate(-128 + 16)
//Adjust for pixel offsets
var/invert_offsets = attached_atom.dir & (NORTH | EAST)
var/left_or_right = attached_atom.dir & (EAST | WEST)
var/offset_x = (left_or_right ? attached_atom.light_pixel_y : attached_atom.light_pixel_x) * (invert_offsets ? -1 : 1)
var/offset_y = (left_or_right ? attached_atom.light_pixel_x : attached_atom.light_pixel_y) * (invert_offsets ? -1 : 1)
new_size_matrix.Translate(offset_x, offset_y)
if(is_directional)
//Rotate
// - Rotate (Directional lights)
new_size_matrix.Turn(current_angle)
return new_size_matrix
///Rotates the light source to angle degrees.
/atom/movable/lighting_mask/proc/rotate(angle = 0)
//Converting our transform is pretty simple.
var/matrix/rotated_matrix = matrix()
rotated_matrix.Turn(angle - current_angle)
rotated_matrix *= transform
//Overlays cannot be edited while applied, meaning their transform cannot be changed.
//Disconnect the shadows from the overlay, apply the transform and then reapply them as an overlay.
//Oh also since the matrix is really weird standard rotation matrices wont work here.
overlays.Cut()
//Disconnect from parent matrix, become a global position
for(var/mutable_appearance/shadow as anything in shadows) //Mutable appearances are children of icon
shadow.transform *= transform
shadow.transform /= rotated_matrix
//Apply our matrix
transform = rotated_matrix
overlays += shadows
//Now we are facing this direction
current_angle = angle
///Setter proc for colors
/atom/movable/lighting_mask/proc/set_color(colour = "#ffffff")
color = colour
///Setter proc for the intensity of the mask
/atom/movable/lighting_mask/proc/set_intensity(intensity = 1)
if(intensity >= 0)
alpha = ALPHA_TO_INTENSITY(intensity)
blend_mode = BLEND_ADD
else
alpha = ALPHA_TO_INTENSITY(-intensity)
blend_mode = BLEND_SUBTRACT
///The holder atom turned, spins the mask if it's needed
/atom/movable/lighting_mask/proc/rotate_mask_on_holder_turn(new_direction)
SIGNAL_HANDLER
rotate(dir2angle(new_direction) - 180)
///Flickering lighting mask
/atom/movable/lighting_mask/flicker
icon_state = "light_flicker"
///Conical Light mask
/atom/movable/lighting_mask/conical
icon_state = "light_conical"
is_directional = TRUE
///Rotating Light mask
/atom/movable/lighting_mask/rotating
icon_state = "light_rotating-1"
/atom/movable/lighting_mask/rotating/Initialize(mapload, ...)
. = ..()
icon_state = "light_rotating-[rand(1, 3)]"
///rotating light mask, but only pointing in one direction
/atom/movable/lighting_mask/rotating_conical
icon_state = "light_conical_rotating"
/atom/movable/lighting_mask/ex_act(severity, target)
return
#undef LIGHTING_MASK_SPRITE_SIZE
#undef LIGHTING_MASK_RADIUS