diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm index 90af29c67c0..4d368ccc670 100644 --- a/code/__DEFINES/layers.dm +++ b/code/__DEFINES/layers.dm @@ -39,7 +39,7 @@ #define POINT_PLANE 5 //---------- LIGHTING ------------- -/// Normal 1 per turf dynamic lighting underlays +/// Normal 1 per turf dynamic lighting objects #define LIGHTING_PLANE 10 /// Lighting objects that are "free floating" diff --git a/code/controllers/subsystem/lighting.dm b/code/controllers/subsystem/lighting.dm index 4717e7dcf8c..d35a58b6ab6 100644 --- a/code/controllers/subsystem/lighting.dm +++ b/code/controllers/subsystem/lighting.dm @@ -4,7 +4,7 @@ SUBSYSTEM_DEF(lighting) /datum/controller/subsystem/atoms, /datum/controller/subsystem/mapping, ) - wait = 2 + wait = 1 ss_flags = SS_TICKER var/static/list/sources_queue = list() // List of lighting sources queued for update. var/static/list/corners_queue = list() // List of lighting corners queued for update. @@ -38,7 +38,7 @@ SUBSYSTEM_DEF(lighting) for(var/turf/area_turf as anything in zlevel_turfs) if(area_turf.space_lit) continue - new /datum/lighting_object(area_turf) + new /atom/movable/lighting_object(null, area_turf) CHECK_TICK CHECK_TICK @@ -53,6 +53,7 @@ SUBSYSTEM_DEF(lighting) // UPDATE SOURCE QUEUE var/i = 0 + // something something cache locally for sonic speed var/list/queue = current_sources while(i < length(queue)) //we don't use for loop here because i cannot be changed during an iteration i += 1 @@ -110,7 +111,7 @@ SUBSYSTEM_DEF(lighting) while(i < length(queue)) //we don't use for loop here because i cannot be changed during an iteration i += 1 - var/datum/lighting_object/O = queue[i] + var/atom/movable/lighting_object/O = queue[i] if(QDELETED(O)) continue O.update() @@ -132,3 +133,13 @@ SUBSYSTEM_DEF(lighting) /datum/controller/subsystem/lighting/Recover() initialized = SSlighting.initialized ..() + +/// Takes a list of turfs in, and sets up static lighting for them as needed. +/// Exactly what it says on the tin. +/datum/controller/subsystem/lighting/proc/setup_static_lighting_if_needed(list/turfs) + for(var/turf/unlit as anything in turfs) + if(unlit.space_lit) + continue + var/area/loc_area = unlit.loc + if(loc_area.static_lighting) + unlit.lighting_build_overlay() diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm index b3bcc301ea6..8d98ff05bd5 100644 --- a/code/controllers/subsystem/mapping.dm +++ b/code/controllers/subsystem/mapping.dm @@ -744,11 +744,6 @@ ADMIN_VERB(load_away_mission, R_FUN, "Load Away Mission", "Load a specific away if(contain_turfs) build_area_turfs(z_value, filled_with_space) - // And finally, misc global generation - - // We'll have to update this if offsets change, because we load lowest z to highest z - generate_lighting_appearance_by_z(z_value) - /datum/controller/subsystem/mapping/proc/build_area_turfs(z_level, space_guaranteed) // If we know this is filled with default tiles, we can use the default area // Faster @@ -785,11 +780,6 @@ ADMIN_VERB(load_away_mission, R_FUN, "Load Away Mission", "Load a specific away z_level_to_lowest_plane_offset[level_to_update.z_value] = plane_offset z_level_to_stack[level_to_update.z_value] = z_stack - // This can be affected by offsets, so we need to update it - // PAIN - for(var/i in 1 to length(z_list)) - generate_lighting_appearance_by_z(i) - var/old_max = max_plane_offset max_plane_offset = max(max_plane_offset, plane_offset) if(max_plane_offset == old_max) @@ -875,11 +865,6 @@ ADMIN_VERB(load_away_mission, R_FUN, "Load Away Mission", "Load a specific away CRASH("Attempted to lazy load a template key that does not exist: '[template_key]'") return target.lazy_load() -/proc/generate_lighting_appearance_by_z(z_level) - if(length(GLOB.default_lighting_underlays_by_z) < z_level) - GLOB.default_lighting_underlays_by_z.len = z_level - GLOB.default_lighting_underlays_by_z[z_level] = mutable_appearance(LIGHTING_ICON, "transparent", z_level * 0.01, null, LIGHTING_PLANE, 255, RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM, offset_const = GET_Z_PLANE_OFFSET(z_level)) - /// Returns true if the map we're playing on is on a planet /datum/controller/subsystem/mapping/proc/is_planetary() return current_map.planetary diff --git a/code/datums/lazy_template.dm b/code/datums/lazy_template.dm index 3faefc0cc78..9fe85cbcbcd 100644 --- a/code/datums/lazy_template.dm +++ b/code/datums/lazy_template.dm @@ -104,6 +104,7 @@ loaded_atom_movables |= thing SSatoms.InitializeAtoms(loaded_areas + loaded_atom_movables + loaded_turfs) + SSlighting.setup_static_lighting_if_needed(loaded_turfs) SSmachines.setup_template_powernets(loaded_cables) SSair.setup_template_machinery(loaded_atmospherics) diff --git a/code/game/turfs/change_turf.dm b/code/game/turfs/change_turf.dm index fa55c13fe40..33afb7c7b29 100644 --- a/code/game/turfs/change_turf.dm +++ b/code/game/turfs/change_turf.dm @@ -158,8 +158,12 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list( if(SSlighting.initialized) // Space tiles should never have lighting objects if(!space_lit) + if(old_lighting_object) + lighting_object = old_lighting_object + vis_contents += lighting_object // Should have a lighting object if we never had one - lighting_object = old_lighting_object || new /datum/lighting_object(src) + else + new /atom/movable/lighting_object(null, src) else if (old_lighting_object) qdel(old_lighting_object, force = TRUE) diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index c6debdb5ce9..b6a0b8c3867 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -67,7 +67,7 @@ GLOBAL_LIST_EMPTY(station_turfs) var/tmp/lighting_corners_initialised = FALSE ///Our lighting object. - var/tmp/datum/lighting_object/lighting_object + var/tmp/atom/movable/lighting_object/lighting_object ///Lighting Corner datums. var/tmp/datum/lighting_corner/lighting_corner_NE var/tmp/datum/lighting_corner/lighting_corner_SE diff --git a/code/modules/admin/view_variables/reference_tracking.dm b/code/modules/admin/view_variables/reference_tracking.dm index 5f3afd380f7..cccb97f6c9b 100644 --- a/code/modules/admin/view_variables/reference_tracking.dm +++ b/code/modules/admin/view_variables/reference_tracking.dm @@ -19,6 +19,7 @@ GLOBAL_ALIST_EMPTY(reftracker_skip_typecache_b) /icon, /matrix, /regex, + /atom/movable/lighting_object, // only contains turf and MA refs /atom/movable/mirage_holder, /atom/movable/render_step/emissive_blocker, /datum/armor, @@ -31,7 +32,6 @@ GLOBAL_ALIST_EMPTY(reftracker_skip_typecache_b) /datum/greyscale_layer, /datum/icon_transformer, /datum/instrument_key, - /datum/lighting_object, // only contains turf and MA refs /datum/movespeed_modifier, /datum/painting, /datum/paper_input, diff --git a/code/modules/lighting/lighting_corner.dm b/code/modules/lighting/lighting_corner.dm index 4c35a8f2bb3..1323f45f1e6 100644 --- a/code/modules/lighting/lighting_corner.dm +++ b/code/modules/lighting/lighting_corner.dm @@ -141,7 +141,7 @@ return #endif - var/datum/lighting_object/lighting_object = master_NE?.lighting_object + var/atom/movable/lighting_object/lighting_object = master_NE?.lighting_object if (lighting_object && !lighting_object.needs_update) lighting_object.needs_update = TRUE SSlighting.objects_queue += lighting_object @@ -163,10 +163,6 @@ self_destruct_if_idle() - -/datum/lighting_corner/dummy/New() - return - /datum/lighting_corner/Destroy(force) if (!force) return QDEL_HINT_LETMELIVE diff --git a/code/modules/lighting/lighting_object.dm b/code/modules/lighting/lighting_object.dm index c5ce42e45ed..85bcd6c0c31 100644 --- a/code/modules/lighting/lighting_object.dm +++ b/code/modules/lighting/lighting_object.dm @@ -1,32 +1,50 @@ -/datum/lighting_object - ///the underlay we are currently applying to our turf to apply light - var/mutable_appearance/current_underlay - +/atom/movable/lighting_object + name = "" + anchored = TRUE + plane = LIGHTING_PLANE + icon = LIGHTING_ICON + icon_state = null + color = null //we manually set color in init instead + appearance_flags = RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + invisibility = INVISIBILITY_LIGHTING + move_resist = INFINITY ///whether we are already in the SSlighting.objects_queue list var/needs_update = FALSE ///the turf that our light is applied to var/turf/affected_turf -// Global list of lighting underlays, indexed by z level -GLOBAL_LIST_EMPTY(default_lighting_underlays_by_z) - -/datum/lighting_object/New(turf/source) - if(!isturf(source)) - qdel(src, force=TRUE) - stack_trace("a lighting object was assigned to [source], a non turf! ") - return +/atom/movable/lighting_object/Initialize(mapload, turf/affected_turf) + if(!isnull(loc)) + if(isturf(loc)) + affected_turf = loc + moveToNullspace() + stack_trace("a lighting object was improperly initialized - they should have a null loc, with the affected turf being the second argument") + else + qdel(src, force = TRUE) + CRASH("a lighting object tried to be spawned for a non-turf!") + if(!isturf(affected_turf)) + qdel(src, force = TRUE) + CRASH("a lighting object was assigned to [affected_turf], a non turf!") . = ..() - current_underlay = new(GLOB.default_lighting_underlays_by_z[source.z]) + verbs.Cut() - affected_turf = source + src.affected_turf = affected_turf + layer = affected_turf.z * 0.01 + if(SSmapping.max_plane_offset) + // generates the offset lighting plane to use. NOTE: this assumes the turf lighting + // plane is ALWAYS offsettable which is technically dependent on a plane master var. + // checking for that is slower and this is hot enough that this is a worthwhile risk to take + plane = LIGHTING_PLANE - (PLANE_RANGE * GET_Z_PLANE_OFFSET(affected_turf.z)) if (affected_turf.lighting_object) qdel(affected_turf.lighting_object, force = TRUE) stack_trace("a lighting object was assigned to a turf that already had a lighting object!") affected_turf.lighting_object = src + affected_turf.vis_contents += src // Default to fullbright, so things can "see" if they use view() before we update affected_turf.luminosity = 1 @@ -38,18 +56,20 @@ GLOBAL_LIST_EMPTY(default_lighting_underlays_by_z) needs_update = TRUE SSlighting.objects_queue += src -/datum/lighting_object/Destroy(force) +/atom/movable/lighting_object/Destroy(force) if (!force) return QDEL_HINT_LETMELIVE SSlighting.objects_queue -= src if (isturf(affected_turf)) + affected_turf.vis_contents -= src affected_turf.lighting_object = null affected_turf.luminosity = 1 - affected_turf.underlays -= current_underlay affected_turf = null return ..() -/datum/lighting_object/proc/update() +/atom/movable/lighting_object/proc/update() + var/turf/affected_turf = src.affected_turf + // To the future coder who sees this and thinks // "Why didn't he just use a loop?" // Well my man, it's because the loop performed like shit. @@ -60,8 +80,6 @@ GLOBAL_LIST_EMPTY(default_lighting_underlays_by_z) var/static/datum/lighting_corner/dummy/dummy_lighting_corner = new - var/turf/affected_turf = src.affected_turf - #ifdef VISUALIZE_LIGHT_UPDATES affected_turf.add_atom_colour(COLOR_BLUE_LIGHT, ADMIN_COLOUR_PRIORITY) animate(affected_turf, 10, color = null) @@ -84,20 +102,12 @@ GLOBAL_LIST_EMPTY(default_lighting_underlays_by_z) var/set_luminosity = max > 1e-6 #endif - var/mutable_appearance/current_underlay = src.current_underlay - affected_turf.underlays -= current_underlay - if(red_corner.cache_r & green_corner.cache_r & blue_corner.cache_r & alpha_corner.cache_r && \ - (red_corner.cache_g + green_corner.cache_g + blue_corner.cache_g + alpha_corner.cache_g + \ - red_corner.cache_b + green_corner.cache_b + blue_corner.cache_b + alpha_corner.cache_b == 8)) - //anything that passes the first case is very likely to pass the second, and addition is a little faster in this case - current_underlay.icon_state = "lighting_transparent" - current_underlay.color = null - else if(!set_luminosity) - current_underlay.icon_state = "lighting_dark" - current_underlay.color = null + if(!set_luminosity) + icon_state = "lighting_dark" + color = null else - current_underlay.icon_state = null - current_underlay.color = list( + icon_state = null + color = list( red_corner.cache_r, red_corner.cache_g, red_corner.cache_b, 00, green_corner.cache_r, green_corner.cache_g, green_corner.cache_b, 00, blue_corner.cache_r, blue_corner.cache_g, blue_corner.cache_b, 00, @@ -105,7 +115,34 @@ GLOBAL_LIST_EMPTY(default_lighting_underlays_by_z) 00, 00, 00, 01 ) - // Of note. Most of the cost in this proc is here, I think because color matrix'd underlays DO NOT cache well, which is what adding to underlays does - // We use underlays because objects on each tile would fuck with maptick. if that ever changes, use an object for this instead - affected_turf.underlays += current_underlay - affected_turf.luminosity = set_luminosity + luminosity = set_luminosity + +// Variety of overrides so the overlays don't get affected by weird things. + +/atom/movable/lighting_object/ex_act(severity) + return FALSE + +/atom/movable/lighting_object/singularity_act() + return + +/atom/movable/lighting_object/singularity_pull() + return + +/atom/movable/lighting_object/blob_act() + return + +/atom/movable/lighting_object/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents = TRUE) + SHOULD_CALL_PARENT(FALSE) + return + +/atom/movable/lighting_object/wash(clean_types) + SHOULD_CALL_PARENT(FALSE) // lighting objects are dirty, confirmed + return + +// Override here to prevent things accidentally moving around overlays. +/atom/movable/lighting_object/forceMove(atom/destination, no_tp = FALSE, harderforce = FALSE) + if(harderforce) + return ..() + +/atom/movable/lighting_object/ref_search_details() + return "[text_ref(src)] (turf: [affected_turf ? "[affected_turf.type] @ [AREACOORD(affected_turf)]" : "null"] needs_update: [needs_update ? "true" : "false"])" diff --git a/code/modules/lighting/lighting_turf.dm b/code/modules/lighting/lighting_turf.dm index 949d9b59b8f..f3feed46bcf 100644 --- a/code/modules/lighting/lighting_turf.dm +++ b/code/modules/lighting/lighting_turf.dm @@ -14,7 +14,7 @@ if (lighting_object) qdel(lighting_object, force=TRUE) //Shitty fix for lighting objects persisting after death - new /datum/lighting_object(src) + new /atom/movable/lighting_object(null, src) // Used to get a scaled lumcount. /turf/proc/get_lumcount(minlum = 0, maxlum = 1) diff --git a/code/modules/mapping/map_template.dm b/code/modules/mapping/map_template.dm index 0f6ff85eed9..a0a309849dc 100644 --- a/code/modules/mapping/map_template.dm +++ b/code/modules/mapping/map_template.dm @@ -90,14 +90,7 @@ return SSatoms.InitializeAtoms(areas + turfs + movables, returns_created_atoms ? created_atoms : null) - - for(var/turf/unlit as anything in turfs) - if(unlit.space_lit) - continue - var/area/loc_area = unlit.loc - if(!loc_area.static_lighting) - continue - unlit.lighting_build_overlay() + SSlighting.setup_static_lighting_if_needed(turfs) // NOTE, now that Initialize and LateInitialize run correctly, do we really // need these two below? diff --git a/code/modules/photography/camera/camera_image_capturing.dm b/code/modules/photography/camera/camera_image_capturing.dm index 7a4cc5857a0..21ad4a51d4b 100644 --- a/code/modules/photography/camera/camera_image_capturing.dm +++ b/code/modules/photography/camera/camera_image_capturing.dm @@ -41,7 +41,7 @@ atoms += new /obj/effect/appearance_clone(newT, T.loc) if(T.lighting_object) var/obj/effect/appearance_clone/lighting_overlay = new(newT) - lighting_overlay.appearance = T.lighting_object.current_underlay + lighting_overlay.appearance = T.lighting_object.appearance lighting_overlay.underlays += backdrop lighting_overlay.blend_mode = BLEND_MULTIPLY lighting += lighting_overlay @@ -61,7 +61,7 @@ atoms += T if(T.lighting_object) var/obj/effect/appearance_clone/lighting_overlay = new(T) - lighting_overlay.appearance = T.lighting_object.current_underlay + lighting_overlay.appearance = T.lighting_object.appearance lighting_overlay.underlays += backdrop lighting_overlay.blend_mode = BLEND_MULTIPLY lighting += lighting_overlay diff --git a/code/modules/shuttle/mobile_port/shuttle_move_callbacks.dm b/code/modules/shuttle/mobile_port/shuttle_move_callbacks.dm index 2eee98a0d6f..245d1cb3485 100644 --- a/code/modules/shuttle/mobile_port/shuttle_move_callbacks.dm +++ b/code/modules/shuttle/mobile_port/shuttle_move_callbacks.dm @@ -78,6 +78,13 @@ All ShuttleMove procs go here if(rotation) shuttleRotate(rotation, params = ALL) //see shuttle_rotate.dm + + // if we have a lighting object that needs to be updated + if(lighting_object?.needs_update) + lighting_object.update() + lighting_object.needs_update = FALSE + SSlighting.objects_queue -= lighting_object + SEND_SIGNAL(src, COMSIG_TURF_AFTER_SHUTTLE_MOVE, oldT) return TRUE @@ -402,6 +409,9 @@ All ShuttleMove procs go here /************************************Misc move procs************************************/ +/atom/movable/lighting_object/onShuttleMove() + return FALSE + /obj/docking_port/mobile/hypotheticalShuttleMove(rotation, move_mode, obj/docking_port/mobile/moving_dock) . = ..() if(moving_dock == src) diff --git a/code/modules/unit_tests/unit_test.dm b/code/modules/unit_tests/unit_test.dm index 3814df5ef38..74f41bf1088 100644 --- a/code/modules/unit_tests/unit_test.dm +++ b/code/modules/unit_tests/unit_test.dm @@ -279,6 +279,9 @@ GLOBAL_VAR_INIT(focused_tests, focused_tests()) /obj/effect/decal/cleanable/blood/trail, //Should not exist outside of ethereals /obj/item/stock_parts/power_store/cell/ethereal, + // Abstract type, controlled by turfs + // Literally errors on creation/deletion + /atom/movable/lighting_object, ) // Everything that follows is a typesof() check. diff --git a/icons/effects/lighting_object.dmi b/icons/effects/lighting_object.dmi index d2e50395872..f2d5960d91b 100644 Binary files a/icons/effects/lighting_object.dmi and b/icons/effects/lighting_object.dmi differ