From 68621359136cb79947d8b5a82d10415b2b1cc0ba Mon Sep 17 00:00:00 2001 From: kyunkyunkyun <120701975+kyunkyunkyun@users.noreply.github.com> Date: Tue, 9 Sep 2025 00:25:37 +0500 Subject: [PATCH] Ports some old TG lighting + opacity refactors (#29724) * lighting ports * missed these * fix * tweak optical scanners * Update code/datums/elements/light_blocking.dm Co-authored-by: Contrabang <91113370+Contrabang@users.noreply.github.com> Signed-off-by: kyunkyunkyun <120701975+kyunkyunkyun@users.noreply.github.com> --------- Signed-off-by: Burzah <116982774+Burzah@users.noreply.github.com> Signed-off-by: kyunkyunkyun <120701975+kyunkyunkyun@users.noreply.github.com> Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com> Co-authored-by: Contrabang <91113370+Contrabang@users.noreply.github.com> --- code/__DEFINES/dcs/atom_signals.dm | 2 + code/__DEFINES/flags.dm | 3 + code/__DEFINES/turfs.dm | 2 + code/__HELPERS/game.dm | 4 +- code/__HELPERS/radiation_helpers.dm | 3 - code/__HELPERS/unsorted.dm | 12 +- code/controllers/subsystem/SSlighting.dm | 5 +- code/datums/elements/light_blocking.dm | 32 +++ code/datums/spells/mimic.dm | 1 - code/game/atoms.dm | 4 - code/game/atoms_movable.dm | 10 +- code/game/machinery/doors/airlock.dm | 4 +- code/game/machinery/doors/airlock_types.dm | 4 +- code/game/machinery/doors/door.dm | 4 +- code/game/machinery/shieldgen.dm | 3 - code/game/mecha/mecha.dm | 1 - code/game/mecha/working/ripley.dm | 1 - code/game/objects/structures/aliens.dm | 3 +- code/game/objects/structures/curtains.dm | 2 +- code/game/objects/structures/flora.dm | 4 +- code/game/objects/structures/mineral_doors.dm | 2 +- code/game/turfs/simulated/walls_mineral.dm | 55 ++---- code/game/turfs/space/space_turf.dm | 2 +- code/game/turfs/turf.dm | 68 +++---- code/modules/clothing/glasses/glasses.dm | 2 +- code/modules/clothing/head/helmet.dm | 2 +- code/modules/events/spacevine.dm | 6 +- code/modules/lighting/lighting_atom.dm | 33 ++-- code/modules/lighting/lighting_corner.dm | 162 +++++++++------ code/modules/lighting/lighting_object.dm | 168 ++++++---------- code/modules/lighting/lighting_setup.dm | 2 +- code/modules/lighting/lighting_source.dm | 187 ++++++++---------- code/modules/lighting/lighting_turf.dm | 129 ++++++------ .../living/simple_animal/hostile/hostile.dm | 2 +- .../living/simple_animal/hostile/mushroom.dm | 2 +- code/modules/paperwork/photography.dm | 23 +-- code/modules/power/generators/turbine.dm | 2 - code/modules/shuttle/on_move.dm | 4 - code/modules/shuttle/shuttle.dm | 6 - icons/effects/lighting_object.dmi | Bin 1256 -> 2092 bytes paradise.dme | 1 + 41 files changed, 456 insertions(+), 506 deletions(-) create mode 100644 code/datums/elements/light_blocking.dm diff --git a/code/__DEFINES/dcs/atom_signals.dm b/code/__DEFINES/dcs/atom_signals.dm index 54934917ece..e388110c76a 100644 --- a/code/__DEFINES/dcs/atom_signals.dm +++ b/code/__DEFINES/dcs/atom_signals.dm @@ -69,6 +69,8 @@ #define COMSIG_ATOM_SING_PULL "atom_sing_pull" ///from base of atom/set_light(): (l_range, l_power, l_color) #define COMSIG_ATOM_SET_LIGHT "atom_set_light" +/// from base of atom/set_opacity(): (new_opacity) +#define COMSIG_ATOM_SET_OPACITY "atom_set_opacity" ///from base of atom/setDir(): (old_dir, new_dir) #define COMSIG_ATOM_DIR_CHANGE "atom_dir_change" ///from [/datum/controller/subsystem/processing/dcs/proc/rotate_decals]: (list/datum/element/decal/rotating) diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm index 60c0c6fda0f..f26be195ad8 100644 --- a/code/__DEFINES/flags.dm +++ b/code/__DEFINES/flags.dm @@ -1,6 +1,9 @@ #define ALL ~0 //For convenience. #define NONE 0 +/// All the cardinal direction bitflags. +#define ALL_CARDINALS (NORTH | SOUTH | EAST | WEST) + //FLAGS BITMASK #define STOPSPRESSUREDMAGE (1<<0) //This flag is used on the flags variable for SUIT and HEAD items which stop pressure damage. Note that the flag 1 was previous used as ONBACK, so it is possible for some code to use (flags & 1) when checking if something can be put on your back. Replace this code with (inv_flags & ITEM_SLOT_BACK) if you see it anywhere To successfully stop you taking all pressure damage you must have both a suit and head item with this flag. #define NODROP (1<<1) // This flag makes it so that an item literally cannot be removed at all, or at least that's how it should be. Only deleted. diff --git a/code/__DEFINES/turfs.dm b/code/__DEFINES/turfs.dm index f3149bb42aa..a6f4353a714 100644 --- a/code/__DEFINES/turfs.dm +++ b/code/__DEFINES/turfs.dm @@ -20,3 +20,5 @@ CORNER_BLOCK_OFFSET(corner, width + 2, 1, -1, height) + \ CORNER_BLOCK_OFFSET(corner, 1, height, -1, 0) + \ CORNER_BLOCK_OFFSET(corner, 1, height, width, 0)) + +#define IS_OPAQUE_TURF(turf) (turf.directional_opacity == ALL_CARDINALS) diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index fbb60db265a..1b9be7b3c5b 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -268,7 +268,7 @@ Y1+=s while(Y1!=Y2) T=locate(X1,Y1,Z) - if(T.opacity) + if(IS_OPAQUE_TURF(T)) return 0 Y1+=s else @@ -284,7 +284,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 diff --git a/code/__HELPERS/radiation_helpers.dm b/code/__HELPERS/radiation_helpers.dm index b5ee6832832..017d29a78cd 100644 --- a/code/__HELPERS/radiation_helpers.dm +++ b/code/__HELPERS/radiation_helpers.dm @@ -9,7 +9,6 @@ /mob/dead, /obj/effect, /obj/docking_port, - /atom/movable/lighting_object, /obj/item/projectile, /atom/movable/emissive_blocker, )) @@ -55,7 +54,6 @@ /mob/dead, /obj/effect, /obj/docking_port, - /atom/movable/lighting_object, /obj/item/projectile, /atom/movable/emissive_blocker, )) @@ -111,7 +109,6 @@ /mob/dead, /obj/effect, /obj/docking_port, - /atom/movable/lighting_object, /obj/item/projectile, /atom/movable/emissive_blocker, )) diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 336ddc6e160..151fdc5a924 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -644,17 +644,13 @@ Returns 1 if the chain up to the area contains the given typepath 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 //Returns: all the areas in the world /proc/return_areas() diff --git a/code/controllers/subsystem/SSlighting.dm b/code/controllers/subsystem/SSlighting.dm index 1628f46b396..139c1e3701a 100644 --- a/code/controllers/subsystem/SSlighting.dm +++ b/code/controllers/subsystem/SSlighting.dm @@ -60,8 +60,9 @@ SUBSYSTEM_DEF(lighting) for(i in 1 to length(queue)) var/datum/lighting_corner/C = queue[i] - C.update_objects() C.needs_update = FALSE + C.update_objects() + if(init_tick_checks) CHECK_TICK else if(MC_TICK_CHECK) @@ -76,7 +77,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/datums/elements/light_blocking.dm b/code/datums/elements/light_blocking.dm new file mode 100644 index 00000000000..0746c37b757 --- /dev/null +++ b/code/datums/elements/light_blocking.dm @@ -0,0 +1,32 @@ +/** + * 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_ON_HOST_DESTROY + +/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)) + var/turf/turf_loc = movable_target.loc + turf_loc.add_opacity_source(target) + +/datum/element/light_blocking/Detach(atom/movable/target) + . = ..() + UnregisterSignal(target, COMSIG_MOVABLE_MOVED) + var/atom/movable/movable_target = target + if(isturf(movable_target.loc)) + var/turf/turf_loc = movable_target.loc + 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 = FALSE) + if(isturf(old_loc)) + var/turf/old_turf = old_loc + old_turf.remove_opacity_source(source) + if(isturf(source.loc)) + var/turf/new_turf = source.loc + new_turf.add_opacity_source(source) diff --git a/code/datums/spells/mimic.dm b/code/datums/spells/mimic.dm index 96b7f8081cc..05751a7ec20 100644 --- a/code/datums/spells/mimic.dm +++ b/code/datums/spells/mimic.dm @@ -22,7 +22,6 @@ /obj/singularity, /obj/effect, /mob/living/simple_animal/hostile/megafauna, - /atom/movable/lighting_object, /obj/machinery/dna_vault, /obj/machinery/power/bluespace_tap, /obj/machinery/barsign, diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 3bfcffd266c..d435c5bfc1e 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -190,10 +190,6 @@ if(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 guranteed to be on afterwards anyways. - if(loc) SEND_SIGNAL(loc, COMSIG_ATOM_INITIALIZED_ON, src) // Used for poolcontroller / pool to improve performance greatly. However it also open up path to other usage of observer pattern on turfs. diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 5890ac77d51..2f8fe74348d 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -102,6 +102,8 @@ render_target = ref(src) em_block = new(src, render_target) add_overlay(list(em_block)) + if(opacity) + AddElement(/datum/element/light_blocking) /atom/movable/proc/update_emissive_block() if(!em_block && !QDELETED(src)) @@ -110,7 +112,6 @@ add_overlay(list(em_block)) /atom/movable/Destroy() - var/turf/T = loc unbuckle_all_mobs(force = TRUE) QDEL_NULL(em_block) @@ -131,11 +132,8 @@ qdel(move_packet) move_packet = null - 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() + if(opacity) + RemoveElement(/datum/element/light_blocking) //Returns an atom's power cell, if it has one. Overload for individual items. /atom/movable/proc/get_cell() diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index 3f7a56fb144..42509fd4730 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -1266,7 +1266,7 @@ GLOBAL_LIST_EMPTY(airlock_emissive_underlays) recalculate_atmos_connectivity() update_icon(AIRLOCK_OPENING, 1) sleep(1) - set_opacity(0) + set_opacity(FALSE) if(width > 1) set_fillers_opacity(0) update_freelook_sight() @@ -1320,7 +1320,7 @@ GLOBAL_LIST_EMPTY(airlock_emissive_underlays) if(!safe) crush() if((visible && !glass) || polarized_on) - set_opacity(1) + set_opacity(TRUE) if(width > 1) set_fillers_opacity(1) update_freelook_sight() diff --git a/code/game/machinery/doors/airlock_types.dm b/code/game/machinery/doors/airlock_types.dm index 78e35cda0a2..3719fa35d8f 100644 --- a/code/game/machinery/doors/airlock_types.dm +++ b/code/game/machinery/doors/airlock_types.dm @@ -573,7 +573,7 @@ /obj/machinery/door/airlock/cult/cult_conceal() icon = stealth_icon overlays_file = stealth_overlays - opacity = stealth_opacity + set_opacity(stealth_opacity) glass = stealth_glass airlock_material = stealth_airlock_material name = "airlock" @@ -584,7 +584,7 @@ /obj/machinery/door/airlock/cult/cult_reveal() icon = GET_CULT_DATA(airlock_runed_icon_file, initial(icon)) overlays_file = GET_CULT_DATA(airlock_runed_overlays_file, initial(overlays_file)) - opacity = initial(opacity) + set_opacity(initial(opacity)) glass = initial(glass) airlock_material = initial(airlock_material) name = initial(name) diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 1c8044b4c8d..1b991677215 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -385,7 +385,7 @@ operating = DOOR_OPENING recalculate_atmos_connectivity() do_animate("opening") - set_opacity(0) + set_opacity(FALSE) if(width > 1) set_fillers_opacity(0) sleep(5) @@ -395,7 +395,7 @@ sleep(5) layer = initial(layer) update_icon() - set_opacity(0) + set_opacity(FALSE) if(width > 1) set_fillers_opacity(0) operating = NONE diff --git a/code/game/machinery/shieldgen.dm b/code/game/machinery/shieldgen.dm index 9a3558c9ad7..2ed5256816d 100644 --- a/code/game/machinery/shieldgen.dm +++ b/code/game/machinery/shieldgen.dm @@ -11,13 +11,10 @@ /obj/machinery/shield/Initialize(mapload) . = ..() - dir = pick(NORTH, SOUTH, EAST, WEST) recalculate_atmos_connectivity() GLOB.tesla_containment += src /obj/machinery/shield/Destroy() - opacity = FALSE - density = FALSE recalculate_atmos_connectivity() GLOB.tesla_containment -= src return ..() diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 2fe203246db..36324869f8a 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -6,7 +6,6 @@ icon = 'icons/mecha/mecha.dmi' density = TRUE //Dense. To raise the heat. - opacity = TRUE ///opaque. Menacing. anchored = TRUE //no pulling around. resistance_flags = FIRE_PROOF layer = MOB_LAYER //icon draw layer diff --git a/code/game/mecha/working/ripley.dm b/code/game/mecha/working/ripley.dm index 63c41f73dde..bd44b6c9a1f 100644 --- a/code/game/mecha/working/ripley.dm +++ b/code/game/mecha/working/ripley.dm @@ -169,7 +169,6 @@ initial_icon = "deathripley" step_in = 3 slow_pressure_step_in = 3 - opacity=0 max_temperature = 65000 max_integrity = 300 armor = list(MELEE = 40, BULLET = 40, LASER = 40, ENERGY = 0, BOMB = 70, RAD = 0, FIRE = 100, ACID = 100) diff --git a/code/game/objects/structures/aliens.dm b/code/game/objects/structures/aliens.dm index c07c42989d4..b479106a8b3 100644 --- a/code/game/objects/structures/aliens.dm +++ b/code/game/objects/structures/aliens.dm @@ -102,7 +102,6 @@ */ /obj/structure/alien/resin/door name = "resin door" - icon = 'icons/obj/smooth_structures/alien/resin_door.dmi' icon_state = "resin" base_icon_state = "resin" @@ -187,7 +186,7 @@ playsound(loc, close_sound, 50, 1) flick("[initial_state]closing", src) density = !density - opacity = !opacity + set_opacity(!opacity) state_open = !state_open addtimer(CALLBACK(src, PROC_REF(operate_update), bumped_open), 1 SECONDS) diff --git a/code/game/objects/structures/curtains.dm b/code/game/objects/structures/curtains.dm index ffedc3fed4b..8bfb3fc0126 100644 --- a/code/game/objects/structures/curtains.dm +++ b/code/game/objects/structures/curtains.dm @@ -135,7 +135,7 @@ /obj/structure/curtain/update_overlays() . = ..() if(!assembled) - opacity = FALSE + set_opacity(FALSE) return if(opacity) diff --git a/code/game/objects/structures/flora.dm b/code/game/objects/structures/flora.dm index 6c34a2c0ad1..0c735c95982 100644 --- a/code/game/objects/structures/flora.dm +++ b/code/game/objects/structures/flora.dm @@ -433,7 +433,7 @@ . = ..() if(icon_state == "random-alien") icon_state = "alien-[rand(1,8)]" - + /obj/item/kirbyplants/large/alien/alien1 icon_state = "alien-1" /obj/item/kirbyplants/large/alien/alien3 @@ -519,7 +519,7 @@ /obj/structure/bush/Initialize(mapload) . = ..() if(prob(20)) - opacity = TRUE + set_opacity(TRUE) /* /obj/structure/bush/Bumped(M as mob) diff --git a/code/game/objects/structures/mineral_doors.dm b/code/game/objects/structures/mineral_doors.dm index 5ae9bdab683..64602411892 100644 --- a/code/game/objects/structures/mineral_doors.dm +++ b/code/game/objects/structures/mineral_doors.dm @@ -101,7 +101,7 @@ /obj/structure/mineral_door/proc/operate_update() density = !density - opacity = !opacity + set_opacity(!opacity) state_open = !state_open recalculate_atmos_connectivity() update_icon(UPDATE_ICON_STATE) diff --git a/code/game/turfs/simulated/walls_mineral.dm b/code/game/turfs/simulated/walls_mineral.dm index fde37f33e58..a4253b4efbf 100644 --- a/code/game/turfs/simulated/walls_mineral.dm +++ b/code/game/turfs/simulated/walls_mineral.dm @@ -214,6 +214,10 @@ smoothing_groups = list(SMOOTH_GROUP_TITANIUM_WALLS, SMOOTH_GROUP_WINDOW_FULLTILE_SHUTTLE) canSmoothWith = list(SMOOTH_GROUP_TITANIUM_WALLS, SMOOTH_GROUP_AIRLOCK, SMOOTH_GROUP_SHUTTLE_PARTS, SMOOTH_GROUP_WINDOW_FULLTILE_SHUTTLE) +/turf/simulated/wall/mineral/titanium/copyTurf(turf/T) + . = ..() + T.transform = transform + /turf/simulated/wall/mineral/titanium/nodiagonal icon_state = "map-shuttle_nd" smoothing_flags = SMOOTH_BITMASK @@ -225,28 +229,8 @@ icon_state = "map-overspace" fixed_underlay = list("space" = TRUE) -//sub-type to be used for interior shuttle walls -//won't get an underlay of the destination turf on shuttle move -/turf/simulated/wall/mineral/titanium/interior/copyTurf(turf/T) - if(T.type != type) - T.ChangeTurf(type) - if(length(underlays)) - T.underlays = underlays - if(T.icon_state != icon_state) - T.icon_state = icon_state - if(T.icon != icon) - T.icon = icon - if(color) - T.atom_colours = atom_colours.Copy() - T.update_atom_colour() - if(T.dir != dir) - T.setDir(dir) - T.transform = transform - return T - -/turf/simulated/wall/mineral/titanium/copyTurf(turf/T) - . = ..() - T.transform = transform +/// Used when we don't want to smooth with outer walls +/turf/simulated/wall/mineral/titanium/interior /turf/simulated/wall/mineral/titanium/survival name = "pod wall" @@ -276,6 +260,10 @@ smoothing_groups = list(SMOOTH_GROUP_PLASTITANIUM_WALLS) canSmoothWith = list(SMOOTH_GROUP_PLASTITANIUM_WALLS, SMOOTH_GROUP_AIRLOCK, SMOOTH_GROUP_SHUTTLE_PARTS) +/turf/simulated/wall/mineral/plastitanium/copyTurf(turf/T) + . = ..() + T.transform = transform + /turf/simulated/wall/mineral/plastitanium/nodiagonal icon_state = "map-shuttle_nd" smoothing_flags = SMOOTH_BITMASK @@ -289,24 +277,5 @@ icon_state = "map-overspace" fixed_underlay = list("space" = TRUE) -//have to copypaste this code -/turf/simulated/wall/mineral/plastitanium/interior/copyTurf(turf/T) - if(T.type != type) - T.ChangeTurf(type) - if(length(underlays)) - T.underlays = underlays - if(T.icon_state != icon_state) - T.icon_state = icon_state - if(T.icon != icon) - T.icon = icon - if(color) - T.atom_colours = atom_colours.Copy() - T.update_atom_colour() - if(T.dir != dir) - T.setDir(dir) - T.transform = transform - return T - -/turf/simulated/wall/mineral/plastitanium/copyTurf(turf/T) - . = ..() - T.transform = transform +/// Used when we don't want to smooth with outer walls +/turf/simulated/wall/mineral/plastitanium/interior diff --git a/code/game/turfs/space/space_turf.dm b/code/game/turfs/space/space_turf.dm index 05b801ef4c2..647bbbd6d58 100644 --- a/code/game/turfs/space/space_turf.dm +++ b/code/game/turfs/space/space_turf.dm @@ -45,7 +45,7 @@ update_light() if(opacity) - has_opaque_atom = TRUE + directional_opacity = ALL_CARDINALS return INITIALIZE_HINT_NORMAL diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index db85c94865b..d28b16731bb 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -67,14 +67,18 @@ /// Is the lighting on this turf inited var/tmp/lighting_corners_initialised = FALSE - /// List of light sources affecting this turf. - var/tmp/list/datum/light_source/affecting_lights /// The lighting Object affecting us - var/tmp/atom/movable/lighting_object/lighting_object - /// A list of our lighting corners. - var/tmp/list/datum/lighting_corner/corners - /// Not to be confused with opacity, this will be TRUE if there's any opaque atom on the tile. - var/tmp/has_opaque_atom = FALSE + 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 /// The general behavior of atmos on this tile. var/atmos_mode = ATMOS_MODE_SEALED @@ -128,11 +132,11 @@ if(!IS_DYNAMIC_LIGHTING(src) && IS_DYNAMIC_LIGHTING(A)) add_overlay(/obj/effect/fullbright) - if(light_power && light_range) + if(light_range && light_power) update_light() if(opacity) - has_opaque_atom = TRUE + directional_opacity = ALL_CARDINALS initialize_milla() @@ -238,11 +242,6 @@ if(!O.lastarea) O.lastarea = get_area(O.loc) - // If an opaque movable atom moves around we need to potentially update visibility. - if(A.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() - if((!(A) || !(src in A.locs))) return @@ -315,13 +314,14 @@ return src set_light(0) - var/old_opacity = opacity - var/old_dynamic_lighting = dynamic_lighting - var/old_affecting_lights = affecting_lights var/old_lighting_object = lighting_object var/old_blueprint_data = blueprint_data var/old_obscured = obscured - var/old_corners = corners + var/old_lighting_corner_NE = lighting_corner_NE + 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 BeforeChange() SEND_SIGNAL(src, COMSIG_TURF_CHANGE, path, defer_change, keep_icon, ignore_air, copy_existing_baseturf) @@ -352,24 +352,26 @@ W.blueprint_data = old_blueprint_data W.pressure_overlay = old_pressure_overlay - recalc_atom_opacity() + lighting_corner_NE = old_lighting_corner_NE + lighting_corner_SE = old_lighting_corner_SE + lighting_corner_SW = old_lighting_corner_SW + lighting_corner_NW = old_lighting_corner_NW + + if(!W.dynamic_lighting) + W.lighting_build_overlay() + else + W.lighting_clear_overlay() if(SSlighting.initialized) - recalc_atom_opacity() - lighting_object = old_lighting_object - affecting_lights = old_affecting_lights - corners = old_corners - if(old_opacity != opacity || dynamic_lighting != old_dynamic_lighting) - reconsider_lights() + W.lighting_object = old_lighting_object + directional_opacity = old_directional_opacity + recalculate_directional_opacity() - if(dynamic_lighting != old_dynamic_lighting) - if(IS_DYNAMIC_LIGHTING(src)) - lighting_build_overlay() - else - lighting_clear_overlay() + if(lighting_object && !lighting_object.needs_update) + lighting_object.update() - for(var/turf/space/S in RANGE_TURFS(1, src)) //RANGE_TURFS is in code\__HELPERS\game.dm - S.update_starlight() + for(var/turf/space/space_tile in RANGE_TURFS(1, src)) + space_tile.update_starlight() obscured = old_obscured @@ -616,7 +618,7 @@ add_blueprints(AM) /turf/proc/empty(turf_type = /turf/space) - // Remove all atoms except observers, landmarks, docking ports, and (un)`simulated` atoms (lighting overlays) + // Remove all atoms except observers, landmarks, docking ports var/turf/T0 = src for(var/X in T0.GetAllContents()) var/atom/A = X diff --git a/code/modules/clothing/glasses/glasses.dm b/code/modules/clothing/glasses/glasses.dm index ce8312ebe2a..cf5d59154c4 100644 --- a/code/modules/clothing/glasses/glasses.dm +++ b/code/modules/clothing/glasses/glasses.dm @@ -250,7 +250,7 @@ icon_state = "material" item_state = "glasses" origin_tech = "magnets=3;engineering=3" - vision_flags = SEE_OBJS + vision_flags = SEE_OBJS | SEE_TURFS sprite_sheets = list( "Vox" = 'icons/mob/clothing/species/vox/eyes.dmi', diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm index 408f91402fa..3d113d02a92 100644 --- a/code/modules/clothing/head/helmet.dm +++ b/code/modules/clothing/head/helmet.dm @@ -69,7 +69,7 @@ /obj/item/clothing/head/helmet/material name = "material visor helmet" desc = "A helmet with a built-in material scanning visor." - vision_flags = SEE_OBJS + vision_flags = SEE_OBJS | SEE_TURFS /obj/item/clothing/head/helmet/night name = "night-vision helmet" diff --git a/code/modules/events/spacevine.dm b/code/modules/events/spacevine.dm index 4801fd332b2..50fd19bdadc 100644 --- a/code/modules/events/spacevine.dm +++ b/code/modules/events/spacevine.dm @@ -273,7 +273,7 @@ quality = SPACEVINE_MUTATION_POSITIVE /datum/spacevine_mutation/transparency/on_grow(obj/structure/spacevine/holder) - holder.set_opacity(0) + holder.set_opacity(FALSE) holder.alpha = 125 /datum/spacevine_mutation/thorns @@ -446,7 +446,7 @@ qdel(master) master = null mutations.Cut() - set_opacity(0) + set_opacity(FALSE) if(has_buckled_mobs()) unbuckle_all_mobs(force = TRUE) return ..() @@ -643,7 +643,7 @@ if(!energy) icon_state = pick("Med1", "Med2", "Med3") energy = 1 - set_opacity(1) + set_opacity(TRUE) else icon_state = pick("Hvy1", "Hvy2", "Hvy3") energy = 2 diff --git a/code/modules/lighting/lighting_atom.dm b/code/modules/lighting/lighting_atom.dm index b3578178ab3..b815704206c 100644 --- a/code/modules/lighting/lighting_atom.dm +++ b/code/modules/lighting/lighting_atom.dm @@ -48,25 +48,36 @@ /atom/proc/extinguish_light(force = FALSE) return -// Should always be used to change the opacity of an atom. -// It notifies (potentially) affected light sources so they can update (if needed). +/** + * 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) - 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/proc/flash_lighting_fx(_range = FLASH_LIGHT_RANGE, _power = FLASH_LIGHT_POWER, _color = LIGHT_COLOR_WHITE, _duration = FLASH_LIGHT_DURATION, _reset_lighting = TRUE) return diff --git a/code/modules/lighting/lighting_corner.dm b/code/modules/lighting/lighting_corner.dm index 57302b5b168..96fec530e81 100644 --- a/code/modules/lighting/lighting_corner.dm +++ b/code/modules/lighting/lighting_corner.dm @@ -2,34 +2,37 @@ // 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. -// This list is what the code that assigns corners listens to, the order in this list is the order in which corners are added to the /turf/corners list. -GLOBAL_LIST_INIT(LIGHTING_CORNER_DIAGONAL, list(NORTHEAST, SOUTHEAST, SOUTHWEST, NORTHWEST)) - /datum/lighting_corner - var/list/turf/masters - var/list/datum/light_source/affecting // Light sources affecting us. - var/active = FALSE // TRUE if one of our masters has dynamic lighting. + /// Light sources affecting us. + var/list/datum/light_source/affecting - var/x = 0 - var/y = 0 - var/z = 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 + + /// 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) . = ..() - masters = list() - masters[new_turf] = turn(diagonal, 180) - z = new_turf.z + save_master(new_turf, turn(diagonal, 180)) var/vertical = diagonal & ~(diagonal - 1) // The horizontal directions (4 and 8) are bigger than the vertical ones (1 and 2), so we can reliably say the lsb is the horizontal direction. var/horizontal = diagonal & ~vertical // Now that we know the horizontal one we can get the vertical one. @@ -40,50 +43,49 @@ GLOBAL_LIST_INIT(LIGHTING_CORNER_DIAGONAL, list(NORTHEAST, SOUTHEAST, SOUTHWEST, // 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/i + 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. - if(!T.corners) - T.corners = list(null, null, null, null) - - masters[T] = diagonal - i = GLOB.LIGHTING_CORNER_DIAGONAL.Find(turn(diagonal, 180)) - T.corners[i] = src + 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. - if(!T.corners) - T.corners = list(null, null, null, null) - - masters[T] = ((T.x > x) ? EAST : WEST) | ((T.y > y) ? NORTH : SOUTH) // Get the dir based on coordinates. - i = GLOB.LIGHTING_CORNER_DIAGONAL.Find(turn(masters[T], 180)) - T.corners[i] = src + 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) - if(!T.corners) - T.corners = list(null, null, null, null) + 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. - masters[T] = ((T.x > x) ? EAST : WEST) | ((T.y > y) ? NORTH : SOUTH) // Get the dir based on coordinates. - i = GLOB.LIGHTING_CORNER_DIAGONAL.Find(turn(masters[T], 180)) - T.corners[i] = src +/datum/lighting_corner/proc/save_master(turf/master, dir) + switch(dir) + if(NORTHEAST) + master_NE = master + master.lighting_corner_SW = src + if(SOUTHEAST) + master_SE = master + master.lighting_corner_NW = src + if(SOUTHWEST) + master_SW = master + master.lighting_corner_NE = src + if(NORTHWEST) + master_NW = master + master.lighting_corner_SE = src - update_active() +/datum/lighting_corner/proc/self_destruct_if_idle() + if(!LAZYLEN(affecting)) + qdel(src, force = TRUE) -/datum/lighting_corner/proc/update_active() - active = FALSE - var/turf/T - var/thing - for(thing in masters) - T = thing - if(T.lighting_object) - active = TRUE - return +/datum/lighting_corner/proc/vis_update() + for(var/datum/light_source/light_source as anything in affecting) + light_source.vis_update() + +/datum/lighting_corner/proc/full_update() + for(var/datum/light_source/light_source as anything in affecting) + light_source.recalc_corner(src) // God that was a mess, now to do the rest of the corner code! Hooray! /datum/lighting_corner/proc/update_lumcount(delta_r, delta_g, delta_b) @@ -104,13 +106,13 @@ GLOBAL_LIST_INIT(LIGHTING_CORNER_DIAGONAL, list(NORTHEAST, SOUTHEAST, SOUTHWEST, 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 @@ -121,13 +123,30 @@ GLOBAL_LIST_INIT(LIGHTING_CORNER_DIAGONAL, list(NORTHEAST, SOUTHEAST, SOUTHWEST, cache_g = round(lum_g * ., LIGHTING_ROUND_VALUE) cache_b = round(lum_b * ., LIGHTING_ROUND_VALUE) #endif - cache_mx = round(mx, LIGHTING_ROUND_VALUE) - for(var/TT in masters) - var/turf/T = TT - if(T.lighting_object && !T.lighting_object.needs_update) - T.lighting_object.needs_update = TRUE - SSlighting.objects_queue += T.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 + + lighting_object = master_SE?.lighting_object + if(lighting_object && !lighting_object.needs_update) + lighting_object.needs_update = TRUE + SSlighting.objects_queue += lighting_object + + lighting_object = master_SW?.lighting_object + if(lighting_object && !lighting_object.needs_update) + lighting_object.needs_update = TRUE + SSlighting.objects_queue += lighting_object + + lighting_object = master_NW?.lighting_object + if(lighting_object && !lighting_object.needs_update) + lighting_object.needs_update = TRUE + SSlighting.objects_queue += lighting_object + + self_destruct_if_idle() /datum/lighting_corner/dummy/New() @@ -138,6 +157,23 @@ GLOBAL_LIST_INIT(LIGHTING_CORNER_DIAGONAL, list(NORTHEAST, SOUTHEAST, SOUTHWEST, if(!force) return QDEL_HINT_LETMELIVE - stack_trace("Attempted qdel of a lighting corner.") + for(var/datum/light_source/light_source as anything in affecting) + LAZYREMOVE(light_source.effect_str, src) + affecting = null + + if(master_NE) + master_NE.lighting_corner_SW = null + master_NE.lighting_corners_initialised = FALSE + if(master_SE) + master_SE.lighting_corner_NW = null + master_SE.lighting_corners_initialised = FALSE + if(master_SW) + master_SW.lighting_corner_NE = null + master_SW.lighting_corners_initialised = FALSE + if(master_NW) + master_NW.lighting_corner_SE = null + master_NW.lighting_corners_initialised = FALSE + if(needs_update) + SSlighting.corners_queue -= src return ..() diff --git a/code/modules/lighting/lighting_object.dm b/code/modules/lighting/lighting_object.dm index df62b5c342e..265c47ca96d 100644 --- a/code/modules/lighting/lighting_object.dm +++ b/code/modules/lighting/lighting_object.dm @@ -1,66 +1,45 @@ -/atom/movable/lighting_object - name = "" - - anchored = TRUE - - icon = LIGHTING_ICON - icon_state = "transparent" - color = null //we manually set color in init instead - plane = LIGHTING_PLANE - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - layer = LIGHTING_LAYER - invisibility = INVISIBILITY_LIGHTING - simulated = FALSE - +/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 + /// the turf that our light is applied to + var/turf/affected_turf -/atom/movable/lighting_object/Initialize(mapload) +/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 . = ..() - verbs.Cut() - //We avoid setting this in the base as if we do then the parent atom handling will add_atom_color it and that - //is totally unsuitable for this object, as we are always changing it's colour manually - color = LIGHTING_BASE_MATRIX + current_underlay = mutable_appearance(LIGHTING_ICON, "transparent", -LIGHTING_LAYER, LIGHTING_PLANE, 255, RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM) - myturf = loc - if(myturf.lighting_object) - qdel(myturf.lighting_object, force = TRUE) - myturf.lighting_object = src - myturf.luminosity = 0 + 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!") - for(var/turf/space/S in RANGE_TURFS(1, src)) //RANGE_TURFS is in code\__HELPERS\game.dm - S.update_starlight() + affected_turf.lighting_object = src + affected_turf.luminosity = 0 + + for(var/turf/space/space_tile in RANGE_TURFS(1, affected_turf)) + space_tile.update_starlight() needs_update = TRUE SSlighting.objects_queue += src -/atom/movable/lighting_object/Destroy(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 - - return ..() - - 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?" // Well my man, it's because the loop performed like shit. @@ -69,37 +48,30 @@ // 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/list/corners = myturf.corners - var/datum/lighting_corner/cr = dummy_lighting_corner - var/datum/lighting_corner/cg = dummy_lighting_corner - var/datum/lighting_corner/cb = dummy_lighting_corner - var/datum/lighting_corner/ca = dummy_lighting_corner - if(corners) //done this way for speed - cr = corners[3] || dummy_lighting_corner - cg = corners[2] || dummy_lighting_corner - cb = corners[4] || dummy_lighting_corner - ca = corners[1] || 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 @@ -110,42 +82,26 @@ #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 = "lighting_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 = "lighting_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, ar, ag, ab, 00, 00, 00, 00, 01 ) + affected_turf.underlays += current_underlay - 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(obj/structure/blob/B) - return - -/atom/movable/lighting_object/on_changed_z_level(turf/old_turf, turf/new_turf, notify_contents = FALSE) - return ..() - -// Override here to prevent things accidentally moving around overlays. -/atom/movable/lighting_object/forceMove(atom/destination, no_tp = FALSE, 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 9923006815f..21828e46840 100644 --- a/code/modules/lighting/lighting_setup.dm +++ b/code/modules/lighting/lighting_setup.dm @@ -7,6 +7,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 9302b1fc56b..8741e1bf9cf 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. + /// 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 - 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 turf under the above. + 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,12 +28,14 @@ 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. - var/list/turf/affecting_turfs + /// 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 queued for an update. + var/needs_update = LIGHTING_NO_UPDATE /datum/light_source/New(atom/owner, atom/top) @@ -110,55 +119,44 @@ // The braces and semicolons are there to be able to do this on a single line. #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); \ - . *= light_power; \ - var/OLD = effect_str[C]; \ - effect_str[C] = .; \ - C.update_lumcount \ - ( \ - (. * lum_r) - (OLD * applied_lum_r), \ - (. * lum_g) - (OLD * applied_lum_g), \ - (. * lum_b) - (OLD * applied_lum_b) \ +#define APPLY_CORNER(C) \ + . = LUM_FALLOFF(C, pixel_turf); \ + . *= light_power; \ + var/OLD = effect_str[C]; \ + \ + C.update_lumcount \ + ( \ + (. * lum_r) - (OLD * applied_lum_r), \ + (. * lum_g) - (OLD * applied_lum_g), \ + (. * lum_b) - (OLD * applied_lum_b) \ + ); \ + +#define REMOVE_CORNER(C) \ + . = -effect_str[C]; \ + C.update_lumcount \ + ( \ + . * applied_lum_r, \ + . * applied_lum_g, \ + . * applied_lum_b \ ); -#define REMOVE_CORNER(C) \ - . = -effect_str[C]; \ - C.update_lumcount \ - ( \ - . * applied_lum_r, \ - . * applied_lum_g, \ - . * 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 - var/thing - for(thing in affecting_turfs) - var/turf/T = thing - LAZYREMOVE(T.affecting_lights, src) - - affecting_turfs = null - - var/datum/lighting_corner/C - for(thing in effect_str) - C = thing - REMOVE_CORNER(C) - - LAZYREMOVE(C.affecting, src) + for(var/datum/lighting_corner/corner as anything in effect_str) + REMOVE_CORNER(corner) + LAZYREMOVE(corner.affecting, src) effect_str = null -/datum/light_source/proc/recalc_corner(datum/lighting_corner/C) +/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) - UNSETEMPTY(effect_str) + APPLY_CORNER(corner) + effect_str[corner] = . /datum/light_source/proc/update_corners() var/update = FALSE @@ -194,9 +192,9 @@ pixel_turf = get_turf_pixel(top_atom) update = TRUE else - var/P = get_turf_pixel(top_atom) - if(P != pixel_turf) - pixel_turf = P + var/pixel_loc = get_turf_pixel(top_atom) + if(pixel_loc != pixel_turf) + pixel_turf = pixel_loc update = TRUE if(!isturf(source_turf)) @@ -224,77 +222,56 @@ return //nothing's changed var/list/datum/lighting_corner/corners = list() - var/list/turf/turfs = list() - var/thing - var/datum/lighting_corner/C - var/turf/T + var/list/turf/turfs = list() if(source_turf) var/oldlum = source_turf.luminosity source_turf.luminosity = CEILING(light_range, 1) - for(T in view(CEILING(light_range, 1), source_turf)) - if((!IS_DYNAMIC_LIGHTING(T) && !T.light_sources)) - continue - if(!T.has_opaque_atom) + for(var/turf/T in view(CEILING(light_range, 1), source_turf)) + if(!IS_OPAQUE_TURF(T)) if(!T.lighting_corners_initialised) T.generate_missing_corners() - for(thing in T.corners) - C = thing - corners[C] = 0 + corners[T.lighting_corner_NE] = 0 + corners[T.lighting_corner_SE] = 0 + corners[T.lighting_corner_SW] = 0 + corners[T.lighting_corner_NW] = 0 turfs += T source_turf.luminosity = oldlum - LAZYINITLIST(affecting_turfs) - var/list/L = turfs - affecting_turfs // New turfs, add us to the affecting lights of them. - affecting_turfs += L - for(thing in L) - T = thing - LAZYADD(T.affecting_lights, src) - - L = affecting_turfs - turfs // Now-gone turfs, remove us from the affecting lights. - affecting_turfs -= L - for(thing in L) - T = thing - LAZYREMOVE(T.affecting_lights, src) + var/list/datum/lighting_corner/new_corners = (corners - effect_str) LAZYINITLIST(effect_str) if(needs_update == LIGHTING_VIS_UPDATE) - for(thing in corners - effect_str) // New corners - C = thing - LAZYADD(C.affecting, src) - if(!C.active) - effect_str[C] = 0 - continue - APPLY_CORNER(C) + for(var/datum/lighting_corner/corner as anything in new_corners) + APPLY_CORNER(corner) + if(. != 0) + LAZYADD(corner.affecting, src) + effect_str[corner] = . else - L = corners - effect_str - for(thing in L) // New corners - C = thing - LAZYADD(C.affecting, src) - if(!C.active) - effect_str[C] = 0 - continue - APPLY_CORNER(C) + for(var/datum/lighting_corner/corner as anything in new_corners) + APPLY_CORNER(corner) + if(. != 0) + LAZYADD(corner.affecting, src) + effect_str[corner] = . - for(thing in corners - L) // Existing corners - C = thing - if(!C.active) - effect_str[C] = 0 - continue - APPLY_CORNER(C) + for(var/datum/lighting_corner/corner as anything in corners - new_corners) // Existing corners + APPLY_CORNER(corner) + if(. != 0) + effect_str[corner] = . + else + LAZYREMOVE(corner.affecting, src) + effect_str -= corner - L = effect_str - corners - for(thing in L) // Old, now gone, corners. - C = thing - REMOVE_CORNER(C) - LAZYREMOVE(C.affecting, src) - effect_str -= L + var/list/datum/lighting_corner/gone_corners = effect_str - corners + for(var/datum/lighting_corner/corner as anything in gone_corners) + REMOVE_CORNER(corner) + LAZYREMOVE(corner.affecting, src) + effect_str -= gone_corners applied_lum_r = lum_r applied_lum_g = lum_g applied_lum_b = lum_b UNSETEMPTY(effect_str) - UNSETEMPTY(affecting_turfs) #undef EFFECT_UPDATE #undef LUM_FALLOFF diff --git a/code/modules/lighting/lighting_turf.dm b/code/modules/lighting/lighting_turf.dm index 6237548bfc3..76b3c78bfae 100644 --- a/code/modules/lighting/lighting_turf.dm +++ b/code/modules/lighting/lighting_turf.dm @@ -1,49 +1,20 @@ // Causes any affecting light sources to be queued for a visibility update, for example a door got opened. /turf/proc/reconsider_lights() - var/datum/light_source/L - var/thing - for(thing in affecting_lights) - L = thing - L.vis_update() + lighting_corner_NE?.vis_update() + lighting_corner_SE?.vis_update() + lighting_corner_SW?.vis_update() + lighting_corner_NW?.vis_update() /turf/proc/lighting_clear_overlay() if(lighting_object) - qdel(lighting_object, TRUE) - - var/datum/lighting_corner/C - var/thing - for(thing in corners) - if(!thing) - continue - C = thing - C.update_active() + 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) - return - - if(!lighting_corners_initialised) - generate_missing_corners() - - new/atom/movable/lighting_object(src) - - var/thing - var/datum/lighting_corner/C - var/datum/light_source/S - for(thing in corners) - if(!thing) - continue - C = thing - if(!C.active) // We would activate the corner, calculate the lighting for it. - for(thing in C.affecting) - S = thing - S.recalc_corner(C) - C.active = TRUE + new /datum/lighting_object(src) // Used to get a scaled lumcount. /turf/proc/get_lumcount(minlum = 0, maxlum = 1) @@ -51,13 +22,19 @@ return 1 var/totallums = 0 - var/thing - var/datum/lighting_corner/L - for(thing in corners) - if(!thing) - continue - L = thing - totallums += L.lum_r + L.lum_b + L.lum_g + var/datum/lighting_corner/corner + corner = lighting_corner_NE + if(corner) + totallums += corner.lum_r + corner.lum_b + corner.lum_g + corner = lighting_corner_SE + if(corner) + totallums += corner.lum_r + corner.lum_b + corner.lum_g + corner = lighting_corner_SW + if(corner) + totallums += corner.lum_r + corner.lum_b + corner.lum_g + corner = lighting_corner_NW + if(corner) + totallums += corner.lum_r + corner.lum_b + corner.lum_g totallums /= 12 // 4 corners, each with 3 channels, get the average. @@ -73,23 +50,39 @@ if(!lighting_object) return FALSE - return !lighting_object.luminosity + return !luminosity -// 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 +/// 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() -/turf/Exited(atom/movable/Obj, direction) - . = ..() +/// 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() - if(Obj && Obj.opacity) - recalc_atom_opacity() // Make sure to do this before reconsider_lights(), incase we're on instant updates. - reconsider_lights() +/// 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 in opacity_sources) + if(opacity_source.flags & ON_BORDER) + 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) @@ -102,14 +95,16 @@ machine.reregister_machine() /turf/proc/generate_missing_corners() - if(!IS_DYNAMIC_LIGHTING(src) && !light_sources) - return + if(!lighting_corner_NE) + lighting_corner_NE = new /datum/lighting_corner(src, NORTH | EAST) + + if(!lighting_corner_SE) + lighting_corner_SE = new /datum/lighting_corner(src, SOUTH | EAST) + + if(!lighting_corner_SW) + lighting_corner_SW = new /datum/lighting_corner(src, SOUTH | WEST) + + if(!lighting_corner_NW) + lighting_corner_NW = new /datum/lighting_corner(src, NORTH | WEST) + lighting_corners_initialised = TRUE - if(!corners) - corners = list(null, null, null, null) - - for(var/i = 1 to 4) - if(corners[i]) // Already have a corner on this direction. - continue - - corners[i] = new/datum/lighting_corner(src, GLOB.LIGHTING_CORNER_DIAGONAL[i]) diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm index da97915a8a7..3a424121441 100644 --- a/code/modules/mob/living/simple_animal/hostile/hostile.dm +++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm @@ -188,7 +188,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/mushroom.dm b/code/modules/mob/living/simple_animal/hostile/mushroom.dm index ce5dc0764cc..806a8197038 100644 --- a/code/modules/mob/living/simple_animal/hostile/mushroom.dm +++ b/code/modules/mob/living/simple_animal/hostile/mushroom.dm @@ -59,7 +59,7 @@ health = maxHealth /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/paperwork/photography.dm b/code/modules/paperwork/photography.dm index 0816d699bde..59783962a6b 100644 --- a/code/modules/paperwork/photography.dm +++ b/code/modules/paperwork/photography.dm @@ -284,15 +284,13 @@ GLOBAL_LIST_INIT(SpookyGhosts, list("ghost","shade","shade2","ghost-narsie","hor res.Blend("#000", ICON_OVERLAY) var/atoms[] = list() - for(var/turf/the_turf in turfs) + var/list/datum/lighting_object/lighting_objects = list() + for(var/turf/the_turf as anything in turfs) // Add ourselves to the list of stuff to draw atoms.Add(the_turf) + lighting_objects += the_turf.lighting_object // As well as anything that isn't invisible. - for(var/atom/A in the_turf) - if(istype(A, /atom/movable/lighting_object)) //Add lighting to make image look nice - atoms.Add(A) - continue - + for(var/atom/A as anything in the_turf) // AI can't see unconcealed runes or cult portals if(A.invisibility == INVISIBILITY_RUNES && see_cult) atoms.Add(A) @@ -324,11 +322,8 @@ GLOBAL_LIST_INIT(SpookyGhosts, list("ghost","shade","shade2","ghost-narsie","hor // Sort the atoms into their layers var/list/sorted = sort_atoms(atoms) var/center_offset = (size-1)/2 * 32 + 1 - for(var/i; i <= length(sorted); i++) + for(var/i in 1 to length(sorted)) var/atom/A = sorted[i] - if(istype(A, /atom/movable/lighting_object)) - continue //Lighting objects render last, need to be above all atoms and turfs displayed - if(A) var/icon/img = getFlatIcon(A)//build_composite_icon(A) if(istype(A, /obj/item/areaeditor/blueprints/ce)) @@ -363,10 +358,10 @@ GLOBAL_LIST_INIT(SpookyGhosts, list("ghost","shade","shade2","ghost-narsie","hor res.Blend(getFlatIcon(the_turf.loc), blendMode2iconMode(the_turf.blend_mode),xoff,yoff) // Render lighting objects to make picture look nice. - for(var/atom/movable/lighting_object/light in sorted) - var/xoff = (light.x - center.x) * 32 + center_offset - var/yoff = (light.y - center.y) * 32 + center_offset - res.Blend(getFlatIcon(light), blendMode2iconMode(BLEND_MULTIPLY), light.pixel_x + xoff, light.pixel_y + yoff) + for(var/datum/lighting_object/light as anything in lighting_objects) + var/xoff = (light.affected_turf.x - center.x) * 32 + center_offset + var/yoff = (light.affected_turf.y - center.y) * 32 + center_offset + res.Blend(getFlatIcon(light.current_underlay), blendMode2iconMode(BLEND_MULTIPLY), xoff, yoff) return res diff --git a/code/modules/power/generators/turbine.dm b/code/modules/power/generators/turbine.dm index fe292686cdc..954fcb61f80 100644 --- a/code/modules/power/generators/turbine.dm +++ b/code/modules/power/generators/turbine.dm @@ -267,7 +267,6 @@ /mob/camera, /obj/effect, /obj/docking_port, - /atom/movable/lighting_object, )) if(!compressor_ignored_things[entered.type] && !entered.anchored) to_suck_in += entered @@ -291,7 +290,6 @@ /mob/camera, /obj/effect, /obj/docking_port, - /atom/movable/lighting_object, )) var/list/act_list = list() diff --git a/code/modules/shuttle/on_move.dm b/code/modules/shuttle/on_move.dm index 999ae6a8cf1..b89795742f8 100644 --- a/code/modules/shuttle/on_move.dm +++ b/code/modules/shuttle/on_move.dm @@ -11,10 +11,6 @@ abstract_move(T1) return 1 -/atom/movable/lighting_object/onShuttleMove(turf/oldT, turf/T1, rotation, mob/calling_mob) - // lighting objects should not be moved from their parent turfs - return 0 - /obj/effect/landmark/shuttle_import/onShuttleMove() // Used for marking where to preview/load shuttles return 0 diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm index b3850636ee7..b09294a6d8d 100644 --- a/code/modules/shuttle/shuttle.dm +++ b/code/modules/shuttle/shuttle.dm @@ -1122,13 +1122,7 @@ /turf/proc/copyTurf(turf/T) if(T.type != type) - var/obj/O - if(length(underlays)) //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) T.ChangeTurf(type, keep_icon = FALSE) - if(length(underlays)) - T.underlays = O.underlays if(T.icon_state != icon_state) T.icon_state = icon_state if(T.icon != icon) diff --git a/icons/effects/lighting_object.dmi b/icons/effects/lighting_object.dmi index 03bfa20b44602eba92aa94883365e6699e9c5fae..d2e503958723fba64ad288e8819ffbcdcb87afc0 100644 GIT binary patch literal 2092 zcmc(g`#;ltAIHD5cCck=Q-r1_r`3gWm^Q}|x+GRkR}M3mV=Z$?4x>4QmWOgXBqJ%x z6&KUt#*J)p=pcvWCMI83S-OqXB8s`^dfflR{ln+|{^j-ld|r?D^ZkB&E_=8;E1`Cy z002;Ob#bK0GhKe{kZ^gNHH*c_6F2#kZ@gn@Tu5vrD?T#%8~`MiUwYYu>N3F{ow53E zm^KkmpYUp*;XkTUVT!tXyUdGuSDrk{r0T~|mMZq#`y|=b@^*yi`u=ejL$vU`-6|@* z=ZoE4-2tc0VY7$~r?G;|2b6XaZq#KtNAcAySReh1&OE8jnz(6C8)fd6xtTs&xV~t5 z5OZ$j8{`paiG(%y^aB7Qz}3UHdT@W|x%LIuU;a@}F zGeH7KSR&j(A4W?;zbPDrgp}Ta64HTVLr$_o6|~ z-brw>w+}qsdpd3dxB5rxO7J(+u>!F=sD5<$SKv&>Js^<28{km8%)oc+(+VI{sw8C& zhGgQ`+~RLIBly>?vWF*e9#dM8-j+tkVZOmeZX?di_R#Xia)-r%N*IXV#TG--Hc{Y5 z;QOJUCMn#IQi3+3oY0SY7{NWs83;*40mw{_;&jDy+7hCRvXIxjOzh%b)n7fyZ7u7- z-Rbuft8-Pc6bH|H9FMs7q6PY?Dr6jgk1$=-lWll~7s9d%~Bv`T%ZVejo#% zfAcSw9%P?O)4tmdSofozI4uQ(oBPzUXS$heAG$G&zuc>5d#2xt9sgDo?r~i-s)4G~w4`oUY$)%m1ZT;`NsJ zz}XRBOejq?K@j_3jg%Q!wWP>;ChRvi%}*!orYdaFg0xwZQ@4b}ZQwSj2E?FBtx7Zd z?f)5_DD-2IL93ZER+GIO4trjM6(UQ5w+va2PmNnMYDraU0R(5K74;aQ@|+^x?&qa& zBH`sdq`4uD#@jdK?=t3tcvrH86AH!JP{xb3_6!%Bn%y32AOFVHGIdX!{~dE%iLb`W zWf9*4&V^IeE+G5jGS)co>stjMfx+CL&9;vw{NzkJx1K$h){X%T|NFo-?HP7g=qq4O7Z1UElu zZoTh1TsF2(ty`%z0kr( ztqccZ8N$-c!t8AA%l`o#L_Z+sWSYbHIMWPIu6j>xQq%LYrTN3rT0^Z-#K^h?Z)=PF zmTH!}c!fMP4-C8&lPbS#b>?sWnoJvY!;wX!z#E1g_PUiOLn-kMikl02}gNnO?0Bsg7@ahKq`} zh`Od9-Y7nT|0D3kGL0ngE*v#_qicStjK(W!+jqf9a(`UztR+ZWMWo8s;S>I*sd7!x zYAy(}mrz~zV$-5< z0f`xjB;$@vMjEO`m6Z7%_vHEUtjL0PuuY@Qc(ous=dyM;w4L0!0S?y79*1_xe!zk- z(j5hNN9w~y2kSh@HYM<^9=E6DCZLBJL&c*CPr4)OdX3 z4g`x4hjqin=ejYmmVZIM1~2(n>g*9*hcwRy!2o*5VzEuap$Ujq0I+|coUEhip%n4z ztfq;DjxuY}PGUz9l+sxUaZeu!yr{wQN&6{8>pYQet4v8wUHHE7Vwkg1+`5s{D=Sv! z40d8|1ygJO)_6Kz(`y&(dCmH)6au7`9O08n1`*&U%(u-}w%EkBp+APyg{-j!QBd!` zuWOu9czwQHS*PTy{qiH!Ta$l8F0Z$spFdEk?8_AV$JYkWpWuxCUkfLiJ8iaHv89`6 exe;*ji}C9rbAz$~yAk>K54bwHJ2p7bGyV%Xp49&U literal 1256 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=4OJl#B`&GO$wiq3C7Jno3=9=> zg2M`mO22;zF8KKMiI%sn*10q1gExd4Tr__0Nawtd=E(pc$L!IgGahGs&TBLlba@MT z2Z`p0PMNa9BG{qwrs5n?LXGhbdMMw5LYC5v$${wMntYD$;O@*34VFh1{ z13|_!F;vgdpWU$dpdkYTi?XMSV@SoVw=)$>L_7uBR+n$KmS*RZIWdQsoiF9wQyGf_ zg<`$u2O7`poO9X|%x+?qj43$Sno_>m-u7?4fBy2cNeueyea=_^UG-}HE6=OyX2zHO zSp1&c_IP5Ud+c=jBR}2#-CJ#zu{r4In@>_^w_G=C*`1FZ2klQLO?|%VLC8C!!lOS@ zTJj^zSPOjlI1iWyN1Ig!nMb##Nd~81p2@d7d&l<{tM_-;)*memW-x!Ez_cR3Tx8Y~ z(c_GJ?=8HzE&KSM_|;;LKkkU%F<~w6{w8~4M%>1SWqW5GjnTh+ImzyF=sV-Q*-Q}= z`_J(kwMZAQu<-fRzSGVw`0$>4)_wmb>{{m-`5wYy@`S+gnVzo_b z4gU-8iyUCSS;)%>uOe={&Z*Y>ksmBvzQ{LmcQF#F4@xfNa4#~vp}i0 zDw${8V>7>~dnwO2De!({7FUArJr;(Anzt-$a_>jquaG$ReA4^1s~!fdTE;m2uPjqV zw`nj#e~LBJirIXl!J&)yZ*Jb*?;AEc0P}bINwQUEqSp#U8Jp3%z@JV|v!+TF><}4%fb!#dKq` zHA{e^X`CYCO7^D$Z>1g<#P4!n_VRa?G~=D4^E4a!H$A$M@a<;MZ|7f+-_2rc`&Y86 zmZ|A*{*M%{g!sJDdJ*~9CFjMy-edh*RVllDV?NgdhrACnnx30{RB^~?-FWTNN4HB~ zHg;XRVt40Aqh^Ei&a!ikV!G|=aaRis_$nUC)q1qOwg9GgmBlwoVsd_6a-TQdMs97$ zJcen7DO?P7D-VBsI_-y6Rt?WVpC0`gdXHDD9}8y;U=U$obs&j~`TJGY&+deVI8VcW QV9~|k>FVdQ&MBb@0An;l`Tzg` diff --git a/paradise.dme b/paradise.dme index 807eebcaaa7..3e10ac344e4 100644 --- a/paradise.dme +++ b/paradise.dme @@ -656,6 +656,7 @@ #include "code\datums\elements\earhealing.dm" #include "code\datums\elements\high_value_item.dm" #include "code\datums\elements\hostile_machine.dm" +#include "code\datums\elements\light_blocking.dm" #include "code\datums\elements\pet_bonus.dm" #include "code\datums\elements\rad_insulation.dm" #include "code\datums\elements\relay_attackers.dm"