diff --git a/beestation.dme b/beestation.dme index 82267ef4f53b7..07858700ccdf9 100644 --- a/beestation.dme +++ b/beestation.dme @@ -737,6 +737,8 @@ #include "code\datums\elements\embed.dm" #include "code\datums\elements\firestacker.dm" #include "code\datums\elements\forced_gravity.dm" +#include "code\datums\elements\light_blocking.dm" +#include "code\datums\elements\undertile.dm" #include "code\datums\elements\item_scaling.dm" #include "code\datums\elements\mechanical_repair.dm" #include "code\datums\elements\openspace_item_click_handler.dm" diff --git a/code/__DEFINES/dcs/signals/signals_atom.dm b/code/__DEFINES/dcs/signals/signals_atom.dm index faafe73374d91..8314fb15a8785 100644 --- a/code/__DEFINES/dcs/signals/signals_atom.dm +++ b/code/__DEFINES/dcs/signals/signals_atom.dm @@ -91,8 +91,6 @@ ///from obj/machinery/bsa/full/proc/fire(): () #define COMSIG_ATOM_BSA_BEAM "atom_bsa_beam_pass" #define COMSIG_ATOM_BLOCKS_BSA_BEAM 1 -///! from base of atom/set_light(): (l_range, l_power, l_color) -#define COMSIG_ATOM_SET_LIGHT "atom_set_light" ///! from base of atom/setDir(): (old_dir, new_dir) #define COMSIG_ATOM_DIR_CHANGE "atom_dir_change" ///! from base of atom/handle_atom_del(): (atom/deleted) @@ -118,6 +116,8 @@ #define COMSIG_ATOM_ORBIT_BEGIN "atom_orbit_begin" /// called when an atom stops orbiting another atom: (atom) #define COMSIG_ATOM_ORBIT_STOP "atom_orbit_stop" +///from base of atom/set_opacity(): (new_opacity) +#define COMSIG_ATOM_SET_OPACITY "atom_set_opacity" ///////////////// /* Attack signals. They should share the returned flags, to standardize the attack chain. */ diff --git a/code/__DEFINES/dcs/signals/signals_lighting.dm b/code/__DEFINES/dcs/signals/signals_lighting.dm index f728e2503073d..3801441a4d0b8 100644 --- a/code/__DEFINES/dcs/signals/signals_lighting.dm +++ b/code/__DEFINES/dcs/signals/signals_lighting.dm @@ -3,6 +3,10 @@ // All signals send the source datum of the signal as the first argument //Lightning +///! from base of atom/set_light(): (l_range, l_power, l_color) +#define COMSIG_ATOM_SET_LIGHT "atom_set_light" + /// Blocks [/atom/proc/set_light], [/atom/proc/set_light_power], [/atom/proc/set_light_range], [/atom/proc/set_light_color], [/atom/proc/set_light_on], and [/atom/proc/set_light_flags]. + #define COMPONENT_BLOCK_LIGHT_UPDATE (1<<0) ///Called right before the atom changes the value of light_range to a different one, from base atom/set_light_range(): (new_range) #define COMSIG_ATOM_SET_LIGHT_RANGE "atom_set_light_range" ///Called right before the atom changes the value of light_power to a different one, from base atom/set_light_power(): (new_power) @@ -22,3 +26,13 @@ ///Called when the movable tries to toggle its dynamic light LIGHTING_ON status, from base atom/movable/lighting_overlay_toggle_on(): (new_state) #define COMSIG_MOVABLE_LIGHT_OVERLAY_TOGGLE_ON "movable_light_overlay_toggle_on" +///Called right after the atom changes the value of light_power to a different one, from base of [/atom/proc/set_light_power]: (old_power) +#define COMSIG_ATOM_UPDATE_LIGHT_POWER "atom_update_light_power" +///Called right after the atom changes the value of light_range to a different one, from base of [/atom/proc/set_light_range]: (old_range) +#define COMSIG_ATOM_UPDATE_LIGHT_RANGE "atom_update_light_range" +///Called right after the atom changes the value of light_color to a different one, from base of [/atom/proc/set_light_color]: (old_color) +#define COMSIG_ATOM_UPDATE_LIGHT_COLOR "atom_update_light_color" +///Called right after the atom changes the value of light_on to a different one, from base of [/atom/proc/set_light_on]: (old_value) +#define COMSIG_ATOM_UPDATE_LIGHT_ON "atom_update_light_on" +///Called right after the atom changes the value of light_flags to a different one, from base of [/atom/proc/set_light_flags]: (old_flags) +#define COMSIG_ATOM_UPDATE_LIGHT_FLAGS "atom_update_light_flags" diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm index 8dbf84eefacdb..2bc9b90125b26 100644 --- a/code/__DEFINES/flags.dm +++ b/code/__DEFINES/flags.dm @@ -17,6 +17,10 @@ /// Currently covers (1<<0) to (1<<22) GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304)) +/* Directions */ +///All the cardinal direction bitflags. +#define ALL_CARDINALS (NORTH|SOUTH|EAST|WEST) + // for /datum/var/datum_flags #define DF_USE_TAG (1<<0) #define DF_VAR_EDITED (1<<1) diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm index e29f405ecfa70..c752b81405825 100644 --- a/code/__DEFINES/layers.dm +++ b/code/__DEFINES/layers.dm @@ -101,7 +101,7 @@ #define DATA_HUD_PLANE 15 //---------- LIGHTING ------------- -///Normal 1 per turf dynamic lighting objects +///Normal 1 per turf dynamic lighting underlays #define LIGHTING_PLANE 100 ///Lighting objects that are "free floating" diff --git a/code/__DEFINES/turfs.dm b/code/__DEFINES/turfs.dm index c317e9b1ec4f2..742d9b8e1ac85 100644 --- a/code/__DEFINES/turfs.dm +++ b/code/__DEFINES/turfs.dm @@ -5,6 +5,8 @@ #define CHANGETURF_INHERIT_AIR 16 //! Inherit air from previous turf. Implies CHANGETURF_IGNORE_AIR #define CHANGETURF_RECALC_ADJACENT 32 //! Immediately recalc adjacent atmos turfs instead of queuing. +#define IS_OPAQUE_TURF(turf) (turf.directional_opacity == ALL_CARDINALS) + //supposedly the fastest way to do this according to https://gist.github.com/Giacom/be635398926bb463b42a ///Returns a list of turf in a square #define RANGE_TURFS(RADIUS, CENTER) \ diff --git a/code/__HELPERS/atmos.dm b/code/__HELPERS/atmos.dm index 02b6ef30a01cf..652ad960d96ca 100644 --- a/code/__HELPERS/atmos.dm +++ b/code/__HELPERS/atmos.dm @@ -55,17 +55,13 @@ current = get_step_towards(current, target_turf) while(current != target_turf) if(steps > length) - return 0 - if(current.opacity) - return 0 - for(var/thing in current) - var/atom/A = thing - if(A.opacity) - return 0 + return FALSE + if (IS_OPAQUE_TURF(current)) + return FALSE current = get_step_towards(current, target_turf) steps++ - return 1 + return TRUE ///Get the cardinal direction between two atoms /proc/get_cardinal_dir(atom/start, atom/end) diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index f0f81c7e4c316..cf64f5d1f46e3 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -271,7 +271,7 @@ Y1+=s while(Y1!=Y2) T=locate(X1,Y1,Z) - if(T.opacity) + if(IS_OPAQUE_TURF(T)) return 0 Y1+=s else @@ -287,7 +287,7 @@ else X1+=signX //Line exits tile horizontally T=locate(X1,Y1,Z) - if(T.opacity) + if(IS_OPAQUE_TURF(T)) return 0 return 1 #undef SIGNV diff --git a/code/__HELPERS/radiation.dm b/code/__HELPERS/radiation.dm index be13aeb279fe2..9af22d5ed38e3 100644 --- a/code/__HELPERS/radiation.dm +++ b/code/__HELPERS/radiation.dm @@ -8,7 +8,6 @@ /mob/living/simple_animal/revenant, /obj/effect, /obj/docking_port, - /atom/movable/lighting_object, /obj/item/projectile, /obj/structure/chisel_message, /mob/living/simple_animal/eminence diff --git a/code/_onclick/hud/rendering/plane_master.dm b/code/_onclick/hud/rendering/plane_master.dm index dd3dc8d3aabbc..b6ca05b8b8ca9 100644 --- a/code/_onclick/hud/rendering/plane_master.dm +++ b/code/_onclick/hud/rendering/plane_master.dm @@ -37,6 +37,7 @@ appearance_flags = PLANE_MASTER blend_mode = BLEND_MULTIPLY alpha = 255 + /atom/movable/screen/plane_master/openspace name = "open space plane master" plane = OPENSPACE_PLANE @@ -44,6 +45,7 @@ /atom/movable/screen/plane_master/openspace/Initialize(mapload) . = ..() + //add_filter("multiz_lighting_mask", 1, alpha_mask_filter(render_source = LIGHTING_RENDER_TARGET, flags = MASK_INVERSE)) -Bacon: Not ported since I think this has something to do with plane cube add_filter("first_stage_openspace", 1, drop_shadow_filter(color = "#04080FAA", size = -10)) add_filter("second_stage_openspace", 2, drop_shadow_filter(color = "#04080FAA", size = -15)) add_filter("third_stage_openspace", 3, drop_shadow_filter(color = "#04080FAA", size = -20)) diff --git a/code/controllers/subsystem/lighting.dm b/code/controllers/subsystem/lighting.dm index 6984e82410e20..c6615ffb1c114 100644 --- a/code/controllers/subsystem/lighting.dm +++ b/code/controllers/subsystem/lighting.dm @@ -78,7 +78,7 @@ SUBSYSTEM_DEF(lighting) queue = objects_queue for (i in 1 to length(queue)) - var/atom/movable/lighting_object/O = queue[i] + var/datum/lighting_object/O = queue[i] if (QDELETED(O)) continue diff --git a/code/controllers/subsystem/zclear.dm b/code/controllers/subsystem/zclear.dm index 9af77b84a1105..0ced911bca723 100644 --- a/code/controllers/subsystem/zclear.dm +++ b/code/controllers/subsystem/zclear.dm @@ -35,7 +35,7 @@ SUBSYSTEM_DEF(zclear) /datum/controller/subsystem/zclear/New() . = ..() - ignored_atoms = typecacheof(list(/mob/dead, /mob/camera, /mob/dview, /atom/movable/lighting_object, /obj/effect/abstract/mirage_holder)) + ignored_atoms = typecacheof(list(/mob/dead, /mob/camera, /mob/dview, /obj/effect/abstract/mirage_holder)) /datum/controller/subsystem/zclear/Recover() if(!islist(autowipe)) autowipe = list() diff --git a/code/datums/elements/light_blocking.dm b/code/datums/elements/light_blocking.dm new file mode 100644 index 0000000000000..2d710d30bf8e5 --- /dev/null +++ b/code/datums/elements/light_blocking.dm @@ -0,0 +1,42 @@ +/** + * Attached to movable atoms with opacity. Listens to them move and updates their old and new turf loc's opacity accordingly. + */ +/datum/element/light_blocking + element_flags = ELEMENT_DETACH + + +/datum/element/light_blocking/Attach(datum/target) + . = ..() + if(!ismovable(target)) + return ELEMENT_INCOMPATIBLE + RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(on_target_move)) + var/atom/movable/movable_target = target + if(!isturf(movable_target.loc)) + return + for(var/turf/turf_loc as anything in movable_target.locs) + turf_loc.add_opacity_source(target) + + +/datum/element/light_blocking/Detach(datum/target) + . = ..() + UnregisterSignal(target, list(COMSIG_MOVABLE_MOVED)) + var/atom/movable/movable_target = target + if(!isturf(movable_target.loc)) + return + for(var/turf/turf_loc as anything in movable_target.locs) + turf_loc.remove_opacity_source(target) + + +///Updates old and new turf loc opacities. +/datum/element/light_blocking/proc/on_target_move(atom/movable/source, atom/old_loc, dir, forced, list/old_locs) + SIGNAL_HANDLER + if(isturf(old_loc)) + if(old_locs) + for(var/turf/old_turf as anything in old_locs) + old_turf.remove_opacity_source(source) + else + var/turf/old_turf = old_loc + old_turf.remove_opacity_source(source) + if(isturf(source.loc)) + for(var/turf/new_turf as anything in source.locs) + new_turf.add_opacity_source(source) diff --git a/code/datums/martial/wrestling.dm b/code/datums/martial/wrestling.dm index d315b2f38ec1f..caa0250d7358b 100644 --- a/code/datums/martial/wrestling.dm +++ b/code/datums/martial/wrestling.dm @@ -364,15 +364,15 @@ var/turf/ST = null var/falling = 0 - for (var/obj/O in oview(1, A)) - if(O.density) - if (O == D) - continue - if (O.opacity) - continue - else + for (var/turf/T in oview(1, A)) + if (IS_OPAQUE_TURF(T)) + continue + for (var/obj/O in T) + if(O.density) + if (O == D) + continue surface = O - ST = get_turf(O) + ST = T break if (surface && (ST && isturf(ST))) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 4225026387d6a..13a502913ddbf 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -77,12 +77,23 @@ var/list/custom_materials ///Bitfield for how the atom handles materials. var/material_flags = NONE + ///Light systems, both shouldn't be active at the same time. var/light_system = STATIC_LIGHT + ///Range of the light in tiles. Zero means no light. + var/light_range = 0 + ///Intensity of the light. The stronger, the less shadows you will see on the lit area. + var/light_power = 1 + ///Hexadecimal RGB string representing the colour of the light. White by default. + var/light_color = COLOR_WHITE ///Boolean variable for toggleable lights. Has no effect without the proper light_system, light_range and light_power values. var/light_on = TRUE ///Bitflags to determine lighting-related atom properties. var/light_flags = NONE + ///Our light source. Don't fuck with this directly unless you have a good reason! + var/tmp/datum/light_source/light + ///Any light sources that are "inside" of us, for example, if src here was a mob that's carrying a flashlight, that flashlight's light source would be part of this list. + var/tmp/list/light_sources var/flags_ricochet = NONE ///When a projectile tries to ricochet off this atom, the projectile ricochet chance is multiplied by this @@ -209,10 +220,6 @@ if (light_system == STATIC_LIGHT && light_power && light_range) update_light() - if (opacity && isturf(loc)) - var/turf/T = loc - T.has_opaque_atom = TRUE // No need to recalculate it in this case, it's guaranteed to be on afterwards anyways. - if(custom_materials && custom_materials.len) var/temp_list = list() for(var/i in custom_materials) diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index a06497cd9ffd9..86c0b10e81f99 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -72,6 +72,8 @@ if(EMISSIVE_BLOCK_UNIQUE) render_target = ref(src) em_block = new(src, render_target) + if(opacity) + AddElement(/datum/element/light_blocking) QDEL_NULL(em_block) @@ -485,6 +487,10 @@ CanAtmosPass = ATMOS_PASS_YES air_update_turf(TRUE) loc.handle_atom_del(src) + + if(opacity) + RemoveElement(/datum/element/light_blocking) + for(var/atom/movable/AM in contents) qdel(AM) moveToNullspace() diff --git a/code/game/turfs/change_turf.dm b/code/game/turfs/change_turf.dm index df7f357d4aba2..bf8b76b887783 100644 --- a/code/game/turfs/change_turf.dm +++ b/code/game/turfs/change_turf.dm @@ -6,7 +6,7 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list( /turf/proc/empty(turf_type=/turf/open/space, baseturf_type, list/ignore_typecache, flags) // Remove all atoms except observers, landmarks, docking ports - var/static/list/ignored_atoms = typecacheof(list(/mob/dead, /obj/effect/landmark, /obj/docking_port, /atom/movable/lighting_object)) + var/static/list/ignored_atoms = typecacheof(list(/mob/dead, /obj/effect/landmark, /obj/docking_port)) var/list/allowed_contents = typecache_filter_list_reverse(GetAllContentsIgnoring(ignore_typecache), ignored_atoms) allowed_contents -= src for(var/i in 1 to allowed_contents.len) @@ -22,10 +22,11 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list( var/obj/O if(underlays.len) //we have underlays, which implies some sort of transparency, so we want to a snapshot of the previous turf as an underlay O = new() - O.underlays.Add(T) + O.underlays += T T.ChangeTurf(type) if(underlays.len) - T.underlays = O.underlays + T.underlays.Cut() + T.underlays += O.underlays if(T.icon_state != icon_state) T.icon_state = icon_state if(T.icon != icon) @@ -85,6 +86,7 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list( var/old_lighting_corner_SE = lighting_corner_SE var/old_lighting_corner_SW = lighting_corner_SW var/old_lighting_corner_NW = lighting_corner_NW + var/old_directional_opacity = directional_opacity var/old_exl = explosion_level var/old_exi = explosion_id @@ -133,9 +135,11 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list( lighting_corner_NW = old_lighting_corner_NW if(SSlighting.initialized) - recalc_atom_opacity() lighting_object = old_lighting_object + directional_opacity = old_directional_opacity + recalculate_directional_opacity() + if (old_opacity != opacity || dynamic_lighting != old_dynamic_lighting) reconsider_lights() @@ -144,9 +148,11 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list( lighting_build_overlay() else lighting_clear_overlay() + else if(lighting_object && !lighting_object.needs_update) + lighting_object.update() - for(var/turf/open/space/S in RANGE_TURFS(1, src)) //RANGE_TURFS is in code\__HELPERS\game.dm - S.update_starlight() + for(var/turf/open/space/space_tile in RANGE_TURFS(1, src)) //RANGE_TURFS is in code\__HELPERS\game.dm + space_tile.update_starlight() return W diff --git a/code/game/turfs/open/openspace.dm b/code/game/turfs/open/openspace.dm index 6fbe7ec8a2715..e8a001a8f7a21 100644 --- a/code/game/turfs/open/openspace.dm +++ b/code/game/turfs/open/openspace.dm @@ -15,7 +15,7 @@ GLOBAL_DATUM_INIT(openspace_backdrop_one_for_all, /atom/movable/openspace_backdr /turf/open/openspace name = "open space" desc = "Watch your step!" - icon_state = "transparent" + icon_state = "invisible" baseturfs = /turf/open/openspace CanAtmosPassVertical = ATMOS_PASS_YES allow_z_travel = TRUE diff --git a/code/game/turfs/open/space/space.dm b/code/game/turfs/open/space/space.dm index e55e239484a3c..3bd712a64a652 100644 --- a/code/game/turfs/open/space/space.dm +++ b/code/game/turfs/open/space/space.dm @@ -60,7 +60,7 @@ update_light() if (opacity) - has_opaque_atom = TRUE + directional_opacity = ALL_CARDINALS ComponentInitialize() diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index caf04ca25f60e..93c206233aed2 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -46,6 +46,23 @@ GLOBAL_LIST_EMPTY(created_baseturf_lists) ///Lumcount added by sources other than lighting datum objects, such as the overlay lighting component. var/dynamic_lumcount = 0 + var/dynamic_lighting = TRUE + + var/tmp/lighting_corners_initialised = FALSE + + ///Our lighting object. + var/tmp/datum/lighting_object/lighting_object + ///Lighting Corner datums. + var/tmp/datum/lighting_corner/lighting_corner_NE + var/tmp/datum/lighting_corner/lighting_corner_SE + var/tmp/datum/lighting_corner/lighting_corner_SW + var/tmp/datum/lighting_corner/lighting_corner_NW + + ///Which directions does this turf block the vision of, taking into account both the turf's opacity and the movable opacity_sources. + var/directional_opacity = NONE + ///Lazylist of movable atoms providing opacity sources. + var/list/atom/movable/opacity_sources + /// Should we used the smooth tiled dirt decal or not var/tiled_dirt = FALSE @@ -111,7 +128,7 @@ GLOBAL_LIST_EMPTY(created_baseturf_lists) SEND_SIGNAL(T, COMSIG_TURF_MULTIZ_NEW, src, UP) if (opacity) - has_opaque_atom = TRUE + directional_opacity = ALL_CARDINALS ComponentInitialize() if(isopenturf(src)) @@ -419,13 +436,6 @@ GLOBAL_LIST_EMPTY(created_baseturf_lists) return (mover.movement_type & PHASING) return TRUE -/turf/Entered(atom/movable/arrived, direction) - ..() - // If an opaque movable atom moves around we need to potentially update visibility. - if (arrived.opacity) - has_opaque_atom = TRUE // Make sure to do this before reconsider_lights(), incase we're on instant updates. Guaranteed to be on in this case. - reconsider_lights() - /turf/open/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs) ..() //melting diff --git a/code/modules/cargo/centcom_podlauncher.dm b/code/modules/cargo/centcom_podlauncher.dm index 51e84e8c61ac2..237aa94d51824 100644 --- a/code/modules/cargo/centcom_podlauncher.dm +++ b/code/modules/cargo/centcom_podlauncher.dm @@ -27,7 +27,7 @@ //Variables declared to change how items in the launch bay are picked and launched. (Almost) all of these are changed in the ui_act proc //Some effect groups are choices, while other are booleans. This is because some effects can stack, while others dont (ex: you can stack explosion and quiet, but you cant stack ordered launch and random launch) /datum/centcom_podlauncher - var/static/list/ignored_atoms = typecacheof(list(null, /mob/dead, /obj/effect/landmark, /obj/docking_port, /atom/movable/lighting_object, /obj/effect/particle_effect/sparks, /obj/effect/pod_landingzone, /obj/effect/hallucination/simple/supplypod_selector, /obj/effect/hallucination/simple/dropoff_location)) + var/static/list/ignored_atoms = typecacheof(list(null, /mob/dead, /obj/effect/landmark, /obj/docking_port, /obj/effect/particle_effect/sparks, /obj/effect/pod_landingzone, /obj/effect/hallucination/simple/supplypod_selector, /obj/effect/hallucination/simple/dropoff_location)) var/turf/oldTurf //Keeps track of where the user was at if they use the "teleport to centcom" button, so they can go back var/client/owner_client //client of whoever is using this datum var/area/centcom/supplypod/loading/bay //What bay we're using to launch shit from. diff --git a/code/modules/elevator/elevator_segment.dm b/code/modules/elevator/elevator_segment.dm index 9a64246d4424e..445288f20153d 100644 --- a/code/modules/elevator/elevator_segment.dm +++ b/code/modules/elevator/elevator_segment.dm @@ -18,7 +18,7 @@ /obj/structure/elevator_segment/Initialize(mapload) music_files = list('sound/effects/turbolift/elevatormusic.ogg' = 45, 'sound/effects/turbolift/elevator_loop.ogg' = 25) - move_blacklist = typecacheof(list(/atom/movable/lighting_object, /obj/structure/cable, /obj/structure/disposalpipe, /obj/machinery/atmospherics/pipe)) + move_blacklist = typecacheof(list(/obj/structure/cable, /obj/structure/disposalpipe, /obj/machinery/atmospherics/pipe)) var/turf/T = get_turf(src) //Technical vanity diff --git a/code/modules/lighting/lighting_area.dm b/code/modules/lighting/lighting_area.dm index 43de9bdf0406f..497a7e857ed8c 100644 --- a/code/modules/lighting/lighting_area.dm +++ b/code/modules/lighting/lighting_area.dm @@ -1,8 +1,8 @@ /area - luminosity = TRUE + luminosity = TRUE var/dynamic_lighting = DYNAMIC_LIGHTING_ENABLED -/area/proc/set_dynamic_lighting(var/new_dynamic_lighting = DYNAMIC_LIGHTING_ENABLED) +/area/proc/set_dynamic_lighting(new_dynamic_lighting = DYNAMIC_LIGHTING_ENABLED) if (new_dynamic_lighting == dynamic_lighting) return FALSE @@ -11,22 +11,13 @@ if (IS_DYNAMIC_LIGHTING(src)) cut_overlay(/obj/effect/fullbright) blend_mode = BLEND_DEFAULT - if(lighting_overlay) - cut_overlay(lighting_overlay) - if(lighting_overlay_opacity && lighting_overlay_colour) - lighting_overlay = new /obj/effect/fullbright - lighting_overlay.color = lighting_overlay_colour - lighting_overlay.alpha = lighting_overlay_opacity - add_overlay(lighting_overlay) for (var/turf/T in src) if (IS_DYNAMIC_LIGHTING(T)) T.lighting_build_overlay() else - if(lighting_overlay) - cut_overlay(lighting_overlay) add_overlay(/obj/effect/fullbright) - blend_mode = BLEND_DEFAULT + blend_mode = BLEND_MULTIPLY for (var/turf/T in src) if (T.lighting_object) T.lighting_clear_overlay() @@ -38,18 +29,4 @@ if(NAMEOF(src, dynamic_lighting)) set_dynamic_lighting(var_value) return TRUE - if("lighting_overlay_colour") - ..() - if(lighting_overlay) - cut_overlay(lighting_overlay) - lighting_overlay.color = var_value - add_overlay(lighting_overlay) - return TRUE - if("lighting_overlay_opacity") - ..() - if(lighting_overlay) - cut_overlay(lighting_overlay) - lighting_overlay.alpha = var_value - add_overlay(lighting_overlay) - return TRUE return ..() diff --git a/code/modules/lighting/lighting_atom.dm b/code/modules/lighting/lighting_atom.dm index ceefc7ca2c887..4beac5b1dd264 100644 --- a/code/modules/lighting/lighting_atom.dm +++ b/code/modules/lighting/lighting_atom.dm @@ -1,28 +1,25 @@ -/atom - var/light_power = 1 // Intensity of the light. - var/light_range = 0 // Range in tiles of the light. - var/light_color // Hexadecimal RGB string representing the colour of the light. - - var/tmp/datum/light_source/light // Our light source. Don't fuck with this directly unless you have a good reason! - var/tmp/list/light_sources // Any light sources that are "inside" of us, for example, if src here was a mob that's carrying a flashlight, that flashlight's light source would be part of this list. - // The proc you should always use to set the light of this atom. // Nonesensical value for l_color default, so we can detect if it gets set to null. #define NONSENSICAL_VALUE -99999 -/atom/proc/set_light(var/l_range, var/l_power, var/l_color = NONSENSICAL_VALUE) +/atom/proc/set_light(l_range, l_power, l_color = NONSENSICAL_VALUE, l_on) if(l_range > 0 && l_range < MINIMUM_USEFUL_LIGHT_RANGE) - l_range = MINIMUM_USEFUL_LIGHT_RANGE //Brings the range up to 1.4, which is just barely brighter than the soft lighting that surrounds players. - if (l_power != null) - light_power = l_power + l_range = MINIMUM_USEFUL_LIGHT_RANGE //Brings the range up to 1.4, which is just barely brighter than the soft lighting that surrounds players. + + if(SEND_SIGNAL(src, COMSIG_ATOM_SET_LIGHT, l_range, l_power, l_color, l_on) & COMPONENT_BLOCK_LIGHT_UPDATE) + return + + if(!isnull(l_power)) + set_light_power(l_power) - if (l_range != null) - light_range = l_range + if(!isnull(l_range)) + set_light_range(l_range) - if (l_color != NONSENSICAL_VALUE) - light_color = l_color + if(l_color != NONSENSICAL_VALUE) + set_light_color(l_color) - SEND_SIGNAL(src, COMSIG_ATOM_SET_LIGHT, l_range, l_power, l_color) + if(!isnull(l_on)) + set_light_on(l_on) update_light() @@ -38,7 +35,7 @@ if(light_system != STATIC_LIGHT) CRASH("update_light() for [src] with following light_system value: [light_system]") - if (!light_power || !light_range) // We won't emit light anyways, destroy the light source. + if (!light_power || !light_range || !light_on) // We won't emit light anyways, destroy the light source. QDEL_NULL(light) else if (!ismovable(loc)) // We choose what atom should be the top atom of the light here. @@ -51,72 +48,43 @@ else light = new/datum/light_source(src, .) -// If we have opacity, make sure to tell (potentially) affected light sources. -/atom/movable/Destroy() - var/turf/T = loc - . = ..() - if (opacity && istype(T)) - var/old_has_opaque_atom = T.has_opaque_atom - T.recalc_atom_opacity() - if (old_has_opaque_atom != T.has_opaque_atom) - T.reconsider_lights() - -// Should always be used to change the opacity of an atom. -// It notifies (potentially) affected light sources so they can update (if needed). -/atom/proc/set_opacity(var/new_opacity) + +/** + * Updates the atom's opacity value. + * + * This exists to act as a hook for associated behavior. + * It notifies (potentially) affected light sources so they can update (if needed). + */ +/atom/proc/set_opacity(new_opacity) if (new_opacity == opacity) return - + SEND_SIGNAL(src, COMSIG_ATOM_SET_OPACITY, new_opacity) + . = opacity opacity = new_opacity - var/turf/T = loc - if (!isturf(T)) + + +/atom/movable/set_opacity(new_opacity) + . = ..() + if(isnull(.) || !isturf(loc)) return - if (new_opacity == TRUE) - T.has_opaque_atom = TRUE - T.reconsider_lights() + if(opacity) + AddElement(/datum/element/light_blocking) else - var/old_has_opaque_atom = T.has_opaque_atom - T.recalc_atom_opacity() - if (old_has_opaque_atom != T.has_opaque_atom) - T.reconsider_lights() + RemoveElement(/datum/element/light_blocking) + + +/turf/set_opacity(new_opacity) + . = ..() + if(isnull(.)) + return + recalculate_directional_opacity() /atom/movable/Moved(atom/OldLoc, Dir) . = ..() - var/datum/light_source/L - var/thing - for (thing in light_sources) // Cycle through the light sources on this atom and tell them to update. - L = thing - L.source_atom.update_light() - -/atom/vv_edit_var(var_name, var_value) - switch (var_name) - if (NAMEOF(src, light_range)) - if(light_system == STATIC_LIGHT) - set_light(l_range = var_value) - else - set_light_range(var_value) - datum_flags |= DF_VAR_EDITED - return TRUE - - if (NAMEOF(src, light_power)) - if(light_system == STATIC_LIGHT) - set_light(l_power = var_value) - else - set_light_power(var_value) - datum_flags |= DF_VAR_EDITED - return TRUE - - if (NAMEOF(src, light_color)) - if(light_system == STATIC_LIGHT) - set_light(l_color = var_value) - else - set_light_color(var_value) - datum_flags |= DF_VAR_EDITED - return TRUE - - return ..() + for (var/datum/light_source/light as anything in light_sources) // Cycle through the light sources on this atom and tell them to update. + light.source_atom.update_light() /atom/proc/flash_lighting_fx(_range = FLASH_LIGHT_RANGE, _power = FLASH_LIGHT_POWER, _color = COLOR_WHITE, _duration = FLASH_LIGHT_DURATION) @@ -143,42 +111,52 @@ var/obj/effect/dummy/lighting_obj/moblight/mob_light_obj = new (src, _range, _power, _color, _duration) return mob_light_obj - -/atom/proc/set_light_range(new_range) - if(new_range == light_range) - return - SEND_SIGNAL(src, COMSIG_ATOM_SET_LIGHT_RANGE, new_range) - . = light_range - light_range = new_range - - +/// Setter for the light power of this atom. /atom/proc/set_light_power(new_power) if(new_power == light_power) return - SEND_SIGNAL(src, COMSIG_ATOM_SET_LIGHT_POWER, new_power) + if(SEND_SIGNAL(src, COMSIG_ATOM_SET_LIGHT_POWER, new_power) & COMPONENT_BLOCK_LIGHT_UPDATE) + return . = light_power light_power = new_power + SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_LIGHT_POWER, .) +/// Setter for the light range of this atom. +/atom/proc/set_light_range(new_range) + if(new_range == light_range) + return + if(SEND_SIGNAL(src, COMSIG_ATOM_SET_LIGHT_RANGE, new_range) & COMPONENT_BLOCK_LIGHT_UPDATE) + return + . = light_range + light_range = new_range + SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_LIGHT_RANGE, .) +/// Setter for the light color of this atom. /atom/proc/set_light_color(new_color) if(new_color == light_color) return - SEND_SIGNAL(src, COMSIG_ATOM_SET_LIGHT_COLOR, new_color) + if(SEND_SIGNAL(src, COMSIG_ATOM_SET_LIGHT_COLOR, new_color) & COMPONENT_BLOCK_LIGHT_UPDATE) + return . = light_color light_color = new_color + SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_LIGHT_COLOR, .) - +/// Setter for whether or not this atom's light is on. /atom/proc/set_light_on(new_value) if(new_value == light_on) return - SEND_SIGNAL(src, COMSIG_ATOM_SET_LIGHT_ON, new_value) + if(SEND_SIGNAL(src, COMSIG_ATOM_SET_LIGHT_ON, new_value) & COMPONENT_BLOCK_LIGHT_UPDATE) + return . = light_on light_on = new_value + SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_LIGHT_ON, .) - +/// Setter for the light flags of this atom. /atom/proc/set_light_flags(new_value) if(new_value == light_flags) return - SEND_SIGNAL(src, COMSIG_ATOM_SET_LIGHT_FLAGS, new_value) + if(SEND_SIGNAL(src, COMSIG_ATOM_SET_LIGHT_FLAGS, new_value) & COMPONENT_BLOCK_LIGHT_UPDATE) + return . = light_flags light_flags = new_value + SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_LIGHT_FLAGS, .) diff --git a/code/modules/lighting/lighting_corner.dm b/code/modules/lighting/lighting_corner.dm index 6ccda2b63bba7..65e5a32dfc610 100644 --- a/code/modules/lighting/lighting_corner.dm +++ b/code/modules/lighting/lighting_corner.dm @@ -2,30 +2,34 @@ // And corners get shared between multiple turfs (unless you're on the corners of the map, then 1 corner doesn't). // For the record: these should never ever ever be deleted, even if the turf doesn't have dynamic lighting. - /datum/lighting_corner var/list/datum/light_source/affecting // Light sources affecting us. - var/x = 0 - var/y = 0 + var/x = 0 + var/y = 0 var/turf/master_NE var/turf/master_SE var/turf/master_SW var/turf/master_NW + //"raw" color values, changed by update_lumcount() var/lum_r = 0 var/lum_g = 0 var/lum_b = 0 - var/needs_update = FALSE + //true color values, guaranteed to be between 0 and 1 + var/cache_r = LIGHTING_SOFT_THRESHOLD + var/cache_g = LIGHTING_SOFT_THRESHOLD + var/cache_b = LIGHTING_SOFT_THRESHOLD - var/cache_r = LIGHTING_SOFT_THRESHOLD - var/cache_g = LIGHTING_SOFT_THRESHOLD - var/cache_b = LIGHTING_SOFT_THRESHOLD - var/cache_mx = 0 + ///the maximum of lum_r, lum_g, and lum_b. if this is > 1 then the three cached color values are divided by this + var/largest_color_luminosity = 0 -/datum/lighting_corner/New(var/turf/new_turf, var/diagonal) + ///whether we are to be added to SSlighting's corners_queue list for an update + var/needs_update = FALSE + +/datum/lighting_corner/New(turf/new_turf, diagonal) . = ..() save_master(new_turf, turn(diagonal, 180)) @@ -38,22 +42,22 @@ // My initial plan was to make this loop through a list of all the dirs (horizontal, vertical, diagonal). // Issue being that the only way I could think of doing it was very messy, slow and honestly overengineered. // So we'll have this hardcode instead. - var/turf/T + var/turf/new_master_turf // Diagonal one is easy. - T = get_step(new_turf, diagonal) - if (T) // In case we're on the map's border. - save_master(T, diagonal) + new_master_turf = get_step(new_turf, diagonal) + if (new_master_turf) // In case we're on the map's border. + save_master(new_master_turf, diagonal) // Now the horizontal one. - T = get_step(new_turf, horizontal) - if (T) // Ditto. - save_master(T, ((T.x > x) ? EAST : WEST) | ((T.y > y) ? NORTH : SOUTH)) // Get the dir based on coordinates. + new_master_turf = get_step(new_turf, horizontal) + if (new_master_turf) // Ditto. + save_master(new_master_turf, ((new_master_turf.x > x) ? EAST : WEST) | ((new_master_turf.y > y) ? NORTH : SOUTH)) // Get the dir based on coordinates. // And finally the vertical one. - T = get_step(new_turf, vertical) - if (T) - save_master(T, ((T.x > x) ? EAST : WEST) | ((T.y > y) ? NORTH : SOUTH)) // Get the dir based on coordinates. + new_master_turf = get_step(new_turf, vertical) + if (new_master_turf) + save_master(new_master_turf, ((new_master_turf.x > x) ? EAST : WEST) | ((new_master_turf.y > y) ? NORTH : SOUTH)) // Get the dir based on coordinates. /datum/lighting_corner/proc/save_master(turf/master, dir) switch (dir) @@ -100,13 +104,13 @@ var/lum_r = src.lum_r var/lum_g = src.lum_g var/lum_b = src.lum_b - var/mx = max(lum_r, lum_g, lum_b) // Scale it so one of them is the strongest lum, if it is above 1. + var/largest_color_luminosity = max(lum_r, lum_g, lum_b) // Scale it so one of them is the strongest lum, if it is above 1. . = 1 // factor - if (mx > 1) - . = 1 / mx + if (largest_color_luminosity > 1) + . = 1 / largest_color_luminosity #if LIGHTING_SOFT_THRESHOLD != 0 - else if (mx < LIGHTING_SOFT_THRESHOLD) + else if (largest_color_luminosity < LIGHTING_SOFT_THRESHOLD) . = 0 // 0 means soft lighting. cache_r = round(lum_r * ., LIGHTING_ROUND_VALUE) || LIGHTING_SOFT_THRESHOLD @@ -117,9 +121,10 @@ cache_g = round(lum_g * ., LIGHTING_ROUND_VALUE) cache_b = round(lum_b * ., LIGHTING_ROUND_VALUE) #endif - cache_mx = round(mx, LIGHTING_ROUND_VALUE) - var/atom/movable/lighting_object/lighting_object = master_NE?.lighting_object + src.largest_color_luminosity = round(largest_color_luminosity, LIGHTING_ROUND_VALUE) + + var/datum/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 diff --git a/code/modules/lighting/lighting_object.dm b/code/modules/lighting/lighting_object.dm index 8546e3d43dd2d..d4dd2d9d0e162 100644 --- a/code/modules/lighting/lighting_object.dm +++ b/code/modules/lighting/lighting_object.dm @@ -1,61 +1,48 @@ -/atom/movable/lighting_object - name = "" - - anchored = TRUE - - icon = LIGHTING_ICON - icon_state = "transparent" - color = LIGHTING_BASE_MATRIX - plane = LIGHTING_PLANE - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - invisibility = INVISIBILITY_LIGHTING +/datum/lighting_object + ///the underlay we are currently applying to our turf to apply light + var/mutable_appearance/current_underlay + ///whether we are already in the SSlighting.objects_queue list var/needs_update = FALSE - var/turf/myturf -/atom/movable/lighting_object/Initialize(mapload) + ///the turf that our light is applied to + var/turf/affected_turf + +/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 . = ..() - remove_verb(verbs) - atom_colours.Cut() - myturf = loc - if (myturf.lighting_object) - qdel(myturf.lighting_object, force = TRUE) - myturf.lighting_object = src - myturf.luminosity = 0 + current_underlay = mutable_appearance(LIGHTING_ICON, "transparent", source.z, LIGHTING_PLANE, 255, RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM) - for(var/turf/open/space/S in RANGE_TURFS(1, src)) //RANGE_TURFS is in code\__HELPERS\game.dm - S.update_starlight() + affected_turf = source + 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!") - needs_update = TRUE - SSlighting.objects_queue += src + affected_turf.lighting_object = src + affected_turf.luminosity = 0 -/atom/movable/lighting_object/Destroy(var/force) - if (force) - SSlighting.objects_queue -= src - if (loc != myturf) - var/turf/oldturf = get_turf(myturf) - var/turf/newturf = get_turf(loc) - stack_trace("A lighting object was qdeleted with a different loc then it is suppose to have ([COORD(oldturf)] -> [COORD(newturf)])") - if (isturf(myturf)) - myturf.lighting_object = null - myturf.luminosity = 1 - myturf = null + for(var/turf/open/space/space_tile in RANGE_TURFS(1, affected_turf)) + space_tile.update_starlight() - return ..() + needs_update = TRUE + SSlighting.objects_queue += src - else +/datum/lighting_object/Destroy(force) + if (!force) return QDEL_HINT_LETMELIVE + SSlighting.objects_queue -= src + if (isturf(affected_turf)) + affected_turf.lighting_object = null + affected_turf.luminosity = 1 + affected_turf.underlays -= current_underlay + affected_turf = null + return ..() -/atom/movable/lighting_object/proc/update() - if (loc != myturf) - if (loc) - var/turf/oldturf = get_turf(myturf) - var/turf/newturf = get_turf(loc) - warning("A lighting object realised it's loc had changed in update() ([myturf]\[[myturf ? myturf.type : "null"]]([COORD(oldturf)]) -> [loc]\[[ loc ? loc.type : "null"]]([COORD(newturf)]))!") - - qdel(src, TRUE) - return +/datum/lighting_object/proc/update() // To the future coder who sees this and thinks // "Why didn't he just use a loop?" @@ -65,50 +52,54 @@ // Oh it's also shorter line wise. // Including with these comments. - // See LIGHTING_CORNER_DIAGONAL in lighting_corner.dm for why these values are what they are. var/static/datum/lighting_corner/dummy/dummy_lighting_corner = new - var/datum/lighting_corner/cr = myturf.lighting_corner_SW || dummy_lighting_corner - var/datum/lighting_corner/cg = myturf.lighting_corner_SE || dummy_lighting_corner - var/datum/lighting_corner/cb = myturf.lighting_corner_NW || dummy_lighting_corner - var/datum/lighting_corner/ca = myturf.lighting_corner_NE || dummy_lighting_corner + var/datum/lighting_corner/red_corner = affected_turf.lighting_corner_SW || dummy_lighting_corner + var/datum/lighting_corner/green_corner = affected_turf.lighting_corner_SE || dummy_lighting_corner + var/datum/lighting_corner/blue_corner = affected_turf.lighting_corner_NW || dummy_lighting_corner + var/datum/lighting_corner/alpha_corner = affected_turf.lighting_corner_NE || dummy_lighting_corner - var/max = max(cr.cache_mx, cg.cache_mx, cb.cache_mx, ca.cache_mx) + var/max = max(red_corner.largest_color_luminosity, green_corner.largest_color_luminosity, blue_corner.largest_color_luminosity, alpha_corner.largest_color_luminosity) - var/rr = cr.cache_r - var/rg = cr.cache_g - var/rb = cr.cache_b + var/rr = red_corner.cache_r + var/rg = red_corner.cache_g + var/rb = red_corner.cache_b - var/gr = cg.cache_r - var/gg = cg.cache_g - var/gb = cg.cache_b + var/gr = green_corner.cache_r + var/gg = green_corner.cache_g + var/gb = green_corner.cache_b - var/br = cb.cache_r - var/bg = cb.cache_g - var/bb = cb.cache_b + var/br = blue_corner.cache_r + var/bg = blue_corner.cache_g + var/bb = blue_corner.cache_b - var/ar = ca.cache_r - var/ag = ca.cache_g - var/ab = ca.cache_b + var/ar = alpha_corner.cache_r + var/ag = alpha_corner.cache_g + var/ab = alpha_corner.cache_b #if LIGHTING_SOFT_THRESHOLD != 0 var/set_luminosity = max > LIGHTING_SOFT_THRESHOLD #else - // Because of floating points�?, it won't even be a flat 0. + // Because of floating points™?, it won't even be a flat 0. // This number is mostly arbitrary. var/set_luminosity = max > 1e-6 #endif if((rr & gr & br & ar) && (rg + gg + bg + ag + rb + gb + bb + ab == 8)) - //anything that passes the first case is very likely to pass the second, and addition is a little faster in this case - icon_state = "transparent" - color = null + //anything that passes the first case is very likely to pass the second, and addition is a little faster in this case + affected_turf.underlays -= current_underlay + current_underlay.icon_state = "transparent" + current_underlay.color = null + affected_turf.underlays += current_underlay else if(!set_luminosity) - icon_state = "dark" - color = null + affected_turf.underlays -= current_underlay + current_underlay.icon_state = "dark" + current_underlay.color = null + affected_turf.underlays += current_underlay else - icon_state = null - color = list( + affected_turf.underlays -= current_underlay + current_underlay.icon_state = null + current_underlay.color = list( rr, rg, rb, 00, gr, gg, gb, 00, br, bg, bb, 00, @@ -116,26 +107,6 @@ 00, 00, 00, 01 ) - luminosity = set_luminosity - -// Variety of overrides so the overlays don't get affected by weird things. - -/atom/movable/lighting_object/ex_act(severity) - return 0 - -/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/onTransitZ() - return + affected_turf.underlays += current_underlay -// Override here to prevent things accidentally moving around overlays. -/atom/movable/lighting_object/forceMove(atom/destination, var/no_tp=FALSE, var/harderforce = FALSE) - if(harderforce) - . = ..() + affected_turf.luminosity = set_luminosity diff --git a/code/modules/lighting/lighting_setup.dm b/code/modules/lighting/lighting_setup.dm index 6cd25efe2b231..d320f0b28c92d 100644 --- a/code/modules/lighting/lighting_setup.dm +++ b/code/modules/lighting/lighting_setup.dm @@ -1,5 +1,5 @@ /proc/create_all_lighting_objects() - for(var/area/A in GLOB.sortedAreas) + for(var/area/A in world) if(!IS_DYNAMIC_LIGHTING(A)) continue @@ -8,6 +8,6 @@ if(!IS_DYNAMIC_LIGHTING(T)) continue - new/atom/movable/lighting_object(T) + new/datum/lighting_object(T) CHECK_TICK CHECK_TICK diff --git a/code/modules/lighting/lighting_source.dm b/code/modules/lighting/lighting_source.dm index 0e5716a68a1c3..dc993c92ad0ce 100644 --- a/code/modules/lighting/lighting_source.dm +++ b/code/modules/lighting/lighting_source.dm @@ -2,14 +2,21 @@ // These are the main datums that emit light. /datum/light_source - var/atom/top_atom // The atom we're emitting light from (for example a mob if we're from a flashlight that's being held). - var/atom/source_atom // The atom that we belong to. - - var/turf/source_turf // The turf under the above. - var/turf/pixel_turf // The turf the top_atom appears to over. - var/light_power // Intensity of the emitter light. - var/light_range // The range of the emitted light. - var/light_color // The colour of the light, string, decomposed by parse_light_color() + ///The atom we're emitting light from (for example a mob if we're from a flashlight that's being held). + var/atom/top_atom + ///The atom that we belong to. + var/atom/source_atom + + ///The turf under the source atom. + var/turf/source_turf + ///The turf the top_atom appears to over. + var/turf/pixel_turf + ///Intensity of the emitter light. + var/light_power + /// The range of the emitted light. + var/light_range + /// The colour of the light, string, decomposed by parse_light_color() + var/light_color // Variables for keeping track of the colour. var/lum_r @@ -21,19 +28,17 @@ var/tmp/applied_lum_g var/tmp/applied_lum_b - var/list/datum/lighting_corner/effect_str // List used to store how much we're affecting corners. + /// List used to store how much we're affecting corners. + var/list/datum/lighting_corner/effect_str - var/applied = FALSE // Whether we have applied our light yet or not. + /// Whether we have applied our light yet or not. + var/applied = FALSE - var/needs_update = LIGHTING_NO_UPDATE // Whether we are queued for an update. + /// whether we are to be added to SSlighting's sources_queue list for an update + var/needs_update = LIGHTING_NO_UPDATE -// Thanks to Lohikar for flinging this tiny bit of code at me, increasing my brain cell count from 1 to 2 in the process. -// This macro will only offset up to 1 tile, but anything with a greater offset is an outlier and probably should handle its own lighting offsets. -// Anything pixelshifted 16px or more will be considered on the next tile. -#define GET_APPROXIMATE_PIXEL_DIR(PX, PY) ((!(PX) ? 0 : ((PX >= 16 ? EAST : (PX <= -16 ? WEST : 0)))) | (!PY ? 0 : (PY >= 16 ? NORTH : (PY <= -16 ? SOUTH : 0)))) -#define UPDATE_APPROXIMATE_PIXEL_TURF var/_mask = GET_APPROXIMATE_PIXEL_DIR(top_atom.pixel_x, top_atom.pixel_y); pixel_turf = _mask ? (get_step(source_turf, _mask) || source_turf) : source_turf -/datum/light_source/New(var/atom/owner, var/atom/top) +/datum/light_source/New(atom/owner, atom/top) source_atom = owner // Set our new owner. LAZYADD(source_atom.light_sources, src) top_atom = top @@ -41,7 +46,7 @@ LAZYADD(top_atom.light_sources, src) source_turf = top_atom - UPDATE_APPROXIMATE_PIXEL_TURF + pixel_turf = get_turf_pixel(top_atom) || source_turf light_power = source_atom.light_power light_range = source_atom.light_range @@ -74,13 +79,13 @@ // Actually that'd be great if you could! #define EFFECT_UPDATE(level) \ if (needs_update == LIGHTING_NO_UPDATE) \ - SSlighting.sources_queue += src; \ + SSlighting.sources_queue += src; \ if (needs_update < level) \ needs_update = level; \ // This proc will cause the light source to update the top atom, and add itself to the update queue. -/datum/light_source/proc/update(var/atom/new_top_atom) +/datum/light_source/proc/update(atom/new_top_atom) // This top atom is different. if (new_top_atom && new_top_atom != top_atom) if(top_atom != source_atom && top_atom.light_sources) // Remove ourselves from the light sources of that top atom. @@ -106,19 +111,7 @@ // If you're wondering what's with the backslashes, the backslashes cause BYOND to not automatically end the line. // As such this all gets counted as a single line. // The braces and semicolons are there to be able to do this on a single line. - -//Original lighting falloff calculation. This looks the best out of the three. However, this is also the most expensive. -//#define LUM_FALLOFF(C, T) (1 - CLAMP01(sqrt((C.x - T.x) ** 2 + (C.y - T.y) ** 2 + LIGHTING_HEIGHT) / max(1, light_range))) - -//Cubic lighting falloff. This has the *exact* same range as the original lighting falloff calculation, down to the exact decimal, but it looks a little unnatural due to the harsher falloff and how it's generally brighter across the board. -//#define LUM_FALLOFF(C, T) (1 - CLAMP01((((C.x - T.x) * (C.x - T.x)) + ((C.y - T.y) * (C.y - T.y)) + LIGHTING_HEIGHT) / max(1, light_range*light_range))) - -//Linear lighting falloff. This resembles the original lighting falloff calculation the best, but results in lights having a slightly larger range, which is most noticeable with large light sources. This also results in lights being diamond-shaped, fuck. This looks the darkest out of the three due to how lights are brighter closer to the source compared to the original falloff algorithm. This falloff method also does not at all take into account lighting height, as it acts as a flat reduction to light range with this method. -//#define LUM_FALLOFF(C, T) (1 - CLAMP01(((abs(C.x - T.x) + abs(C.y - T.y))) / max(1, light_range+1))) - -//Linear lighting falloff but with an octagonal shape in place of a diamond shape. Lummox JR please add pointer support. -#define GET_LUM_DIST(DISTX, DISTY) (DISTX + DISTY + abs(DISTX - DISTY)*0.4) -#define LUM_FALLOFF(C, T) (1 - CLAMP01(max(GET_LUM_DIST(abs(C.x - T.x), abs(C.y - T.y)),LIGHTING_HEIGHT) / max(1, light_range+1))) +#define LUM_FALLOFF(C, T) (1 - CLAMP01(sqrt((C.x - T.x) ** 2 + (C.y - T.y) ** 2 + LIGHTING_HEIGHT) / max(1, light_range))) #define APPLY_CORNER(C) \ . = LUM_FALLOFF(C, pixel_turf); \ @@ -141,22 +134,23 @@ . * applied_lum_b \ ); -// This is the define used to calculate falloff. - +/// This is the define used to calculate falloff. /datum/light_source/proc/remove_lum() applied = FALSE for (var/datum/lighting_corner/corner as anything in effect_str) REMOVE_CORNER(corner) LAZYREMOVE(corner.affecting, src) -/datum/light_source/proc/recalc_corner(var/datum/lighting_corner/C) + effect_str = null + +/datum/light_source/proc/recalc_corner(datum/lighting_corner/corner) LAZYINITLIST(effect_str) - if (effect_str[C]) // Already have one. - REMOVE_CORNER(C) - effect_str[C] = 0 + if (effect_str[corner]) // Already have one. + REMOVE_CORNER(corner) + effect_str[corner] = 0 - APPLY_CORNER(C) - effect_str[C] = . + APPLY_CORNER(corner) + effect_str[corner] = . /datum/light_source/proc/update_corners() @@ -186,12 +180,17 @@ if (isturf(top_atom)) if (source_turf != top_atom) source_turf = top_atom - UPDATE_APPROXIMATE_PIXEL_TURF + pixel_turf = source_turf update = TRUE else if (top_atom.loc != source_turf) source_turf = top_atom.loc - UPDATE_APPROXIMATE_PIXEL_TURF + pixel_turf = get_turf_pixel(top_atom) update = TRUE + else + var/pixel_loc = get_turf_pixel(top_atom) + if (pixel_loc != pixel_turf) + pixel_turf = pixel_loc + update = TRUE if (!isturf(source_turf)) if (applied) @@ -216,13 +215,13 @@ return //nothing's changed var/list/datum/lighting_corner/corners = list() - var/list/turf/turfs = list() + var/list/turf/turfs = list() if (source_turf) var/oldlum = source_turf.luminosity source_turf.luminosity = CEILING(light_range, 1) for(var/turf/T in view(CEILING(light_range, 1), source_turf)) - if(!T.has_opaque_atom) + if(!IS_OPAQUE_TURF(T)) if (!T.lighting_corners_initialised) T.generate_missing_corners() corners[T.lighting_corner_NE] = 0 @@ -269,6 +268,5 @@ #undef EFFECT_UPDATE #undef LUM_FALLOFF -#undef GET_LUM_DIST #undef REMOVE_CORNER #undef APPLY_CORNER diff --git a/code/modules/lighting/lighting_turf.dm b/code/modules/lighting/lighting_turf.dm index e24641d0172eb..ebe4b1b6bb3a7 100644 --- a/code/modules/lighting/lighting_turf.dm +++ b/code/modules/lighting/lighting_turf.dm @@ -1,18 +1,3 @@ -/turf - var/dynamic_lighting = TRUE - luminosity = 1 - - var/tmp/lighting_corners_initialised = FALSE - - var/tmp/atom/movable/lighting_object/lighting_object // Our lighting object. - ///Lighting Corner datums. - var/tmp/datum/lighting_corner/lighting_corner_NE - var/tmp/datum/lighting_corner/lighting_corner_SE - var/tmp/datum/lighting_corner/lighting_corner_SW - var/tmp/datum/lighting_corner/lighting_corner_NW - - var/tmp/has_opaque_atom = FALSE // Not to be confused with opacity, this will be TRUE if there's any opaque atom on the tile. - // Causes any affecting light sources to be queued for a visibility update, for example a door got opened. /turf/proc/reconsider_lights() lighting_corner_NE?.vis_update() @@ -22,21 +7,21 @@ /turf/proc/lighting_clear_overlay() if (lighting_object) - qdel(lighting_object, TRUE) + qdel(lighting_object, force=TRUE) // Builds a lighting object for us, but only if our area is dynamic. /turf/proc/lighting_build_overlay() if (lighting_object) - qdel(lighting_object,force=TRUE) //Shitty fix for lighting objects persisting after death + qdel(lighting_object, force=TRUE) //Shitty fix for lighting objects persisting after death - var/area/A = loc - if (!IS_DYNAMIC_LIGHTING(A) && !light_sources) + var/area/our_area = loc + if (!IS_DYNAMIC_LIGHTING(our_area) && !light_sources) return - new/atom/movable/lighting_object(src) + new/datum/lighting_object(src) // Used to get a scaled lumcount. -/turf/proc/get_lumcount(var/minlum = 0, var/maxlum = 1) +/turf/proc/get_lumcount(minlum = 0, maxlum = 1) if (!lighting_object) return 1 @@ -72,25 +57,45 @@ if (!lighting_object) return FALSE - return !lighting_object.luminosity + return !(luminosity || dynamic_lumcount) -// Can't think of a good name, this proc will recalculate the has_opaque_atom variable. -/turf/proc/recalc_atom_opacity() - has_opaque_atom = opacity - if (!has_opaque_atom) - for (var/atom/A in src.contents) // Loop through every movable atom on our tile PLUS ourselves (we matter too...) - if (A.opacity) - has_opaque_atom = TRUE - break -/turf/Exited(atom/movable/Obj, atom/newloc) - . = ..() +///Proc to add movable sources of opacity on the turf and let it handle lighting code. +/turf/proc/add_opacity_source(atom/movable/new_source) + LAZYADD(opacity_sources, new_source) + if(opacity) + return + recalculate_directional_opacity() - if (Obj?.opacity) - recalc_atom_opacity() // Make sure to do this before reconsider_lights(), incase we're on instant updates. - reconsider_lights() -/turf/proc/change_area(var/area/old_area, var/area/new_area) +///Proc to remove movable sources of opacity on the turf and let it handle lighting code. +/turf/proc/remove_opacity_source(atom/movable/old_source) + LAZYREMOVE(opacity_sources, old_source) + if(opacity) //Still opaque, no need to worry on updating. + return + recalculate_directional_opacity() + + +///Calculate on which directions this turfs block view. +/turf/proc/recalculate_directional_opacity() + . = directional_opacity + if(opacity) + directional_opacity = ALL_CARDINALS + if(. != directional_opacity) + reconsider_lights() + return + directional_opacity = NONE + for(var/atom/movable/opacity_source as anything in opacity_sources) + if(opacity_source.flags_1 & ON_BORDER_1) + directional_opacity |= opacity_source.dir + else //If fulltile and opaque, then the whole tile blocks view, no need to continue checking. + directional_opacity = ALL_CARDINALS + break + if(. != directional_opacity && (. == ALL_CARDINALS || directional_opacity == ALL_CARDINALS)) + reconsider_lights() //The lighting system only cares whether the tile is fully concealed from all directions or not. + + +/turf/proc/change_area(area/old_area, area/new_area) if(SSlighting.initialized) if (new_area.dynamic_lighting != old_area.dynamic_lighting) if (new_area.dynamic_lighting) diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm index f267e0c61c97f..8d16f220770ed 100644 --- a/code/modules/mob/living/simple_animal/hostile/hostile.dm +++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm @@ -189,7 +189,7 @@ // Please do not add one-off mob AIs here, but override this function for your mob /mob/living/simple_animal/hostile/CanAttack(atom/the_target)//Can we actually attack a possible target? - if(isturf(the_target) || !the_target || the_target.type == /atom/movable/lighting_object) // bail out on invalids + if(isturf(the_target) || !the_target) // bail out on invalids return FALSE if(ismob(the_target)) //Target is in godmode, ignore it. diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/gutlunch.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/gutlunch.dm index d618d4077d292..f995955b51346 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/gutlunch.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/gutlunch.dm @@ -47,7 +47,7 @@ . = ..() /mob/living/simple_animal/hostile/asteroid/gutlunch/CanAttack(atom/the_target) // Gutlunch-specific version of CanAttack to handle stupid stat_exclusive = true crap so we don't have to do it for literally every single simple_animal/hostile except the two that spawn in lavaland - if(isturf(the_target) || !the_target || the_target.type == /atom/movable/lighting_object) // bail out on invalids + if(isturf(the_target) || !the_target) // bail out on invalids return FALSE if(see_invisible < the_target.invisibility)//Target's invisible to us, forget it diff --git a/code/modules/mob/living/simple_animal/hostile/mushroom.dm b/code/modules/mob/living/simple_animal/hostile/mushroom.dm index 9ff339f9b0e74..4a21ad7ac5978 100644 --- a/code/modules/mob/living/simple_animal/hostile/mushroom.dm +++ b/code/modules/mob/living/simple_animal/hostile/mushroom.dm @@ -60,7 +60,7 @@ . = ..() /mob/living/simple_animal/hostile/mushroom/CanAttack(atom/the_target) // Mushroom-specific version of CanAttack to handle stupid attack_same = 2 crap so we don't have to do it for literally every single simple_animal/hostile because this shit never gets spawned - if(!the_target || isturf(the_target) || istype(the_target, /atom/movable/lighting_object)) + if(!the_target || isturf(the_target)) return FALSE if(see_invisible < the_target.invisibility)//Target's invisible to us, forget it diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 662a6a1fb67c4..643cd294198ed 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -213,7 +213,7 @@ else if(T != loc && T != src) //if src is inside something and not a turf. if(!allow_inside_usr || loc != usr) msg = blind_message - else if(T.lighting_object && T.lighting_object.invisibility <= M.see_invisible && T.is_softly_lit() && !in_range(T,M)) //if it is too dark. + else if(M.lighting_alpha > LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE && T.is_softly_lit() && !in_range(T,M)) //if it is too dark. msg = blind_message if(!msg) continue diff --git a/code/modules/shuttle/on_move.dm b/code/modules/shuttle/on_move.dm index cdd182c873900..eabbff7dffcfd 100644 --- a/code/modules/shuttle/on_move.dm +++ b/code/modules/shuttle/on_move.dm @@ -387,9 +387,6 @@ All ShuttleMove procs go here /************************************Misc move procs************************************/ -/atom/movable/lighting_object/onShuttleMove() - return FALSE - /obj/docking_port/mobile/beforeShuttleMove(turf/newT, rotation, move_mode, obj/docking_port/mobile/moving_dock) . = ..() if(moving_dock == src) diff --git a/code/modules/surgery/organs/eyes.dm b/code/modules/surgery/organs/eyes.dm index e71e1d46b4859..415c4e7ca7b20 100644 --- a/code/modules/surgery/organs/eyes.dm +++ b/code/modules/surgery/organs/eyes.dm @@ -322,7 +322,7 @@ on_mob.forceMove(scanning) for(var/i in 1 to light_beam_distance) scanning = get_step(scanning, scandir) - if(scanning.opacity || scanning.has_opaque_atom) + if(scanning.opacity || (scandir & scanning.directional_opacity)) stop = TRUE var/obj/effect/abstract/eye_lighting/L = LAZYACCESS(eye_lighting, i) if(stop)