diff --git a/baystation12.dme b/baystation12.dme index f8c41a625641e..5b8551a5f016b 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -20,6 +20,7 @@ #include "code\__datastructures\priority_queue.dm" #include "code\__datastructures\stack.dm" #include "code\__defines\__compile_options.dm" +#include "code\__defines\__dview.dm" #include "code\__defines\__initialization.dm" #include "code\__defines\__renderer.dm" #include "code\__defines\_excom.dm" @@ -188,6 +189,7 @@ #include "code\controllers\subsystems\air.dm" #include "code\controllers\subsystems\airflow.dm" #include "code\controllers\subsystems\alarm.dm" +#include "code\controllers\subsystems\ambient_lighting.dm" #include "code\controllers\subsystems\ao.dm" #include "code\controllers\subsystems\atoms.dm" #include "code\controllers\subsystems\chat.dm" @@ -1991,11 +1993,12 @@ #include "code\modules\library\manuals\medical.dm" #include "code\modules\library\manuals\nanotrasen.dm" #include "code\modules\library\manuals\union.dm" +#include "code\modules\lighting\_lighting_defs.dm" +#include "code\modules\lighting\darksight.dm" #include "code\modules\lighting\lighting_area.dm" #include "code\modules\lighting\lighting_atom.dm" #include "code\modules\lighting\lighting_corner.dm" #include "code\modules\lighting\lighting_overlay.dm" -#include "code\modules\lighting\lighting_planemaster.dm" #include "code\modules\lighting\lighting_setup.dm" #include "code\modules\lighting\lighting_source.dm" #include "code\modules\lighting\lighting_turf.dm" @@ -2070,6 +2073,7 @@ #include "code\modules\mining\machinery\mineral_unloader.dm" #include "code\modules\mob\animations.dm" #include "code\modules\mob\death.dm" +#include "code\modules\mob\dview.dm" #include "code\modules\mob\examinations.dm" #include "code\modules\mob\gender.dm" #include "code\modules\mob\hear_say.dm" diff --git a/code/__defines/MC.dm b/code/__defines/MC.dm index 8e6659624730f..edd52aa3fb13d 100644 --- a/code/__defines/MC.dm +++ b/code/__defines/MC.dm @@ -169,3 +169,9 @@ if(Datum.is_processing) {\ ****/ #define addtimer(args...) _addtimer(args, source ="[__FILE__]#[__LINE__]") + +/**** + * Helper for waits + ****/ + +#define UNTIL(X) while(!(X)) stoplag() diff --git a/code/__defines/__dview.dm b/code/__defines/__dview.dm new file mode 100644 index 0000000000000..52553ef99784a --- /dev/null +++ b/code/__defines/__dview.dm @@ -0,0 +1,14 @@ +//DVIEW defines + +#define FOR_DVIEW(type, range, center, invis_flags) \ + global.dview_mob.loc = center; \ + global.dview_mob.see_invisible = invis_flags; \ + for(type in view(range, dview_mob)) + +#define END_FOR_DVIEW dview_mob.loc = null + +#define DVIEW(output, range, center, invis_flags) \ + global.dview_mob.loc = center; \ + global.dview_mob.see_invisible = invis_flags; \ + output = view(range, dview_mob); \ + global.dview_mob.loc = null; diff --git a/code/__defines/_renderer.dm b/code/__defines/_renderer.dm index 90ff79719959c..c46d78b2060a0 100644 --- a/code/__defines/_renderer.dm +++ b/code/__defines/_renderer.dm @@ -190,13 +190,6 @@ GLOBAL_LIST_EMPTY(zmimic_renderers) plane = LIGHTING_PLANE appearance_flags = PLANE_MASTER | NO_CLIENT_COLOR relay_blend_mode = BLEND_MULTIPLY - color = list( - -1, 0, 0, 0, // R - 0, -1, 0, 0, // G - 0, 0, -1, 0, // B - 0, 0, 0, 0, // A - 1, 1, 1, 1 // Mapping - ) mouse_opacity = MOUSE_OPACITY_UNCLICKABLE diff --git a/code/__defines/lighting.dm b/code/__defines/lighting.dm index 5e35bad8636b1..8a5820f42579d 100644 --- a/code/__defines/lighting.dm +++ b/code/__defines/lighting.dm @@ -1,29 +1,42 @@ -#define FOR_DVIEW(type, range, center, invis_flags) \ - GLOB.dview_mob.loc = center; \ - GLOB.dview_mob.see_invisible = invis_flags; \ - for(type in view(range, GLOB.dview_mob)) +#define LIGHTING_INTERVAL 1 // Frequency, in 1/10ths of a second, of the lighting process. -#define END_FOR_DVIEW GLOB.dview_mob.loc = null +#define LIGHTING_HEIGHT 1 // height off the ground of light sources on the pseudo-z-axis, you should probably leave this alone +#define LIGHTING_Z_FACTOR 10 // Z diff is multiplied by this and LIGHTING_HEIGHT to get the final height of a light source. Affects how much darker A Z light gets with each level transitioned. +#define LIGHTING_ROUND_VALUE (1 / 200) //Value used to round lumcounts, values smaller than 1/255 don't matter (if they do, thanks sinking points), greater values will make lighting less precise, but in turn increase performance, VERY SLIGHTLY. #define LIGHTING_ICON 'icons/effects/lighting_overlay.dmi' // icon used for lighting shading effects -#define LIGHTING_ICON_STATE_DARK "dark" // Change between "soft_dark" and "dark" to swap soft darkvision +#define LIGHTING_BASE_ICON_STATE "matrix" // icon_state used for normal color-matrix based lighting overlays. +#define LIGHTING_STATION_ICON_STATE "tubedefault" // icon_state used for lighting overlays that are just displaying standard station lighting. +#define LIGHTING_DARKNESS_ICON_STATE "black" // icon_state used for lighting overlays with no luminosity. +#define LIGHTING_TRANSPARENT_ICON_STATE "blank" -#define LIGHTING_ROUND_VALUE (1 / 64) // Value used to round lumcounts, values smaller than 1/69 don't matter (if they do, thanks sinking points), greater values will make lighting less precise, but in turn increase performance, VERY SLIGHTLY. +#define LIGHTING_BLOCKED_FACTOR 0.5 // How much the range of a directional light will be reduced while facing a wall. -#define LIGHTING_SOFT_THRESHOLD 0 // If the max of the lighting lumcounts of each spectrum drops below this, disable luminosity on the lighting overlays. This also should be the transparancy of the "soft_dark" icon state. +// If defined, instant updates will be used whenever server load permits. Otherwise queued updates are always used. +#define USE_INTELLIGENT_LIGHTING_UPDATES + +/// Maximum light_range before forced to always queue instead of using sync updates. Setting this too high will cause server stutter with moving large lights. +#define LIGHTING_MAXIMUM_INSTANT_RANGE 8 + +// mostly identical to below, but doesn't make sure T is valid first. Should only be used by lighting code. +#define TURF_IS_DYNAMICALLY_LIT_UNSAFE(T) ((T:dynamic_lighting && T:loc:dynamic_lighting)) +#define TURF_IS_DYNAMICALLY_LIT(T) (isturf(T) && TURF_IS_DYNAMICALLY_LIT_UNSAFE(T)) + +// Note: this does not imply the above, a turf can have ambient light without being dynamically lit. +#define TURF_IS_AMBIENT_LIT_UNSAFE(T) (T:ambient_active) +#define TURF_IS_AMBIENT_LIT(T) (isturf(T) && TURF_IS_AMBIENT_LIT_UNSAFE(T)) -#define LIGHTING_MULT_FACTOR 0.9 // If I were you I'd leave this alone. #define LIGHTING_BASE_MATRIX \ - list \ - ( \ - LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, 0, \ - LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, 0, \ - LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, 0, \ - LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, LIGHTING_SOFT_THRESHOLD, 0, \ - 0, 0, 0, 1 \ - ) + list \ + ( \ + 1, 1, 1, 0, \ + 1, 1, 1, 0, \ + 1, 1, 1, 0, \ + 1, 1, 1, 0, \ + 0, 0, 0, 1 \ + ) \ // Helpers so we can (more easily) control the colour matrices. #define CL_MATRIX_RR 1 @@ -47,12 +60,25 @@ #define CL_MATRIX_CB 19 #define CL_MATRIX_CA 20 +// Higher numbers override lower. +#define LIGHTING_NO_UPDATE 0 +#define LIGHTING_VIS_UPDATE 1 +#define LIGHTING_CHECK_UPDATE 2 +#define LIGHTING_FORCE_UPDATE 3 + // Lightbulb statuses #define LIGHT_OK 0 // A light bulb is installed and functioning. #define LIGHT_EMPTY 1 // There is no light bulb installed. #define LIGHT_BROKEN 2 // The light bulb is broken/shattered. #define LIGHT_BURNED 3 // The light bulb is burned out. +// This color of overlay is very common - most of the station is this color when lit fully. +// Tube lights are a bluish-white, so we can't just assume 1-1-1 is full-illumination. +// -- If you want to change these, find them *by checking in-game*, just converting tubes' RGB color into floats will not work! +#define LIGHTING_DEFAULT_TUBE_R 0.96 +#define LIGHTING_DEFAULT_TUBE_G 1 +#define LIGHTING_DEFAULT_TUBE_B 1 + // Lighting color presets #define LIGHT_COLOUR_WHITE "#fefefe" // Clinical white light bulbs #define LIGHT_COLOUR_WARM "#fffee0" // Warm yellowish light bulbs @@ -84,3 +110,16 @@ #define AREA_LIGHTING_WARM "warm" #define AREA_LIGHTING_COOL "cool" #define AREA_LIGHTING_DEFAULT "default" // For light replacers, defaults to whatever the area is set to. For areas, uses the initial lighting value from the light bulb itself. + +// Some angle presets for directional lighting. +#define LIGHT_OMNI null +#define LIGHT_SEMI 180 +#define LIGHT_VERY_WIDE 135 +#define LIGHT_WIDE 90 +#define LIGHT_NARROW 45 + +#define DARKSIGHT_GRADIENT_SIZE 480 +// Max number of ambient groups, amount over this value will simply not be created +#define AMBIENT_GROUP_MAX_BITS 24 +// Ambient group used for exterior turfs not on planets - Could also replace Space turf legacy starlight implementation +#define SPACE_AMBIENT_GROUP 1 diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm index 0d86253502bdd..c8da27040f9d9 100644 --- a/code/__defines/misc.dm +++ b/code/__defines/misc.dm @@ -338,3 +338,5 @@ #define SANITY_CHECK_TOPIC_PHYSICALLY_INTERACT FLAG(6) #define SANITY_CHECK_DEFAULT (SANITY_CHECK_TOOL_IN_HAND | SANITY_CHECK_BOTH_ADJACENT) + +#define Z_ALL_TURFS(Z) block(locate(1, 1, Z), locate(world.maxx, world.maxy, Z)) diff --git a/code/__defines/species.dm b/code/__defines/species.dm index 58dbcc6e359cf..31b74723209b8 100644 --- a/code/__defines/species.dm +++ b/code/__defines/species.dm @@ -42,7 +42,7 @@ #define SKIN_THREAT FLAG(0) -// Darkvision Levels. Inverted - white is darkest, black is full vision -#define DARKTINT_NONE "#ffffff" -#define DARKTINT_MODERATE "#f9f9f5" -#define DARKTINT_GOOD "#ebebe6" +// Darkvision Levels. White is brightest, darker tints affect vision negatively +#define DARKTINT_GOOD "#ffffff" +#define DARKTINT_MODERATE "#f9f9f5" +#define DARKTINT_NONE "#ebebe6" diff --git a/code/__defines/subsystem-priority.dm b/code/__defines/subsystem-priority.dm index 9e3e1e818a456..add0b6b88c821 100644 --- a/code/__defines/subsystem-priority.dm +++ b/code/__defines/subsystem-priority.dm @@ -20,6 +20,7 @@ #define SS_PRIORITY_AIR 80 // ZAS processing. #define SS_PRIORITY_THROWING 75 // Throwing calculation and constant checks #define SS_PRIORITY_CHEMISTRY 60 // Multi-tick chemical reactions. +#define SS_PRIORITY_LIGHTING 50 // Queued lighting engine updates. #define SS_PRIORITY_SPACEDRIFT 45 // Drifting things #define SS_PRIORITY_CHAT 40 // Chat #define SS_PRIORITY_ALARM 20 // Alarm processing. diff --git a/code/__defines/subsystems.dm b/code/__defines/subsystems.dm index addaa201bbee6..3257785cc6fef 100644 --- a/code/__defines/subsystems.dm +++ b/code/__defines/subsystems.dm @@ -43,10 +43,11 @@ #define SS_INIT_SHUTTLE -5 #define SS_INIT_GOALS -5 #define SS_INIT_LIGHTING -6 -#define SS_INIT_ZCOPY -7 -#define SS_INIT_HOLOMAP -8 -#define SS_INIT_OVERLAYS -9 -#define SS_INIT_XENOARCH -10 +#define SS_INIT_AMBIENT_LIGHT -7 +#define SS_INIT_ZCOPY -8 +#define SS_INIT_HOLOMAP -9 +#define SS_INIT_OVERLAYS -10 +#define SS_INIT_XENOARCH -11 #define SS_INIT_BAY_LEGACY -12 #define SS_INIT_TICKER -20 #define SS_INIT_AI -21 diff --git a/code/_helpers/game.dm b/code/_helpers/game.dm index 739ca2d00e8f8..fd8df21c08558 100644 --- a/code/_helpers/game.dm +++ b/code/_helpers/game.dm @@ -262,7 +262,8 @@ /proc/get_mobs_and_objs_in_view_fast(turf/T, range, list/mobs, list/objs, checkghosts = null) - var/list/hear = dview(range,T,INVISIBILITY_MAXIMUM) + var/list/hear = list() + DVIEW(hear, range, T, INVISIBILITY_MAXIMUM) var/list/hearturfs = list() for(var/atom/movable/AM in hear) diff --git a/code/_helpers/time.dm b/code/_helpers/time.dm index 9b2fb78d22b49..7b539933c0584 100644 --- a/code/_helpers/time.dm +++ b/code/_helpers/time.dm @@ -207,3 +207,19 @@ var/global/round_start_time = 0 var/time_string = time2text(world.realtime, "MM-DD") var/time_list = splittext(time_string, "-") return list(text2num(time_list[1]), text2num(time_list[2])) + + +#define MIDNIGHT_ROLLOVER 864000 //number of deciseconds in a day + +var/global/midnight_rollovers = 0 +var/global/rollovercheck_last_timeofday = 0 +/proc/update_midnight_rollover() + if (world.timeofday < global.rollovercheck_last_timeofday) //TIME IS GOING BACKWARDS! + global.midnight_rollovers += 1 + global.rollovercheck_last_timeofday = world.timeofday + return global.midnight_rollovers + +//time of day but automatically adjusts to the server going into the next day within the same round. +//for when you need a reliable time number that doesn't depend on byond time. +#define REALTIMEOFDAY (world.timeofday + (MIDNIGHT_ROLLOVER * MIDNIGHT_ROLLOVER_CHECK)) +#define MIDNIGHT_ROLLOVER_CHECK ( global.rollovercheck_last_timeofday != world.timeofday ? update_midnight_rollover() : global.midnight_rollovers ) diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm index a642fae1cf57d..7a19e3db3d74d 100644 --- a/code/_helpers/unsorted.dm +++ b/code/_helpers/unsorted.dm @@ -1036,17 +1036,6 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new) . = view(range, GLOB.dview_mob) GLOB.dview_mob.loc = null -/mob/dview - invisibility = INVISIBILITY_ABSTRACT - density = FALSE - - anchored = TRUE - simulated = FALSE - - see_in_dark = 1e6 - - virtual_mob = null - /mob/dview/Destroy() SHOULD_CALL_PARENT(FALSE) return QDEL_HINT_LETMELIVE @@ -1062,7 +1051,7 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new) /atom/proc/get_light_and_color(atom/origin) if(origin) color = origin.color - set_light(origin.light_max_bright, origin.light_inner_range, origin.light_outer_range, origin.light_falloff_curve) + set_light(origin.light_range, origin.light_power) // call to generate a stack trace and print to runtime logs diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index 3100d3345740b..e11f37874629f 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -335,8 +335,8 @@ var/static/aooc_allowed = TRUE - /// Whether space turfs have ambient light or not - var/static/starlight = 0 + /// Whether space turfs and some exterior turfs have ambient light or not default, 0.5, values over 1 may overpower dynamic lights + var/static/starlight = 0.5 var/static/list/ert_species = list(SPECIES_HUMAN) diff --git a/code/controllers/subsystems/ambient_lighting.dm b/code/controllers/subsystems/ambient_lighting.dm new file mode 100644 index 0000000000000..a234899708402 --- /dev/null +++ b/code/controllers/subsystems/ambient_lighting.dm @@ -0,0 +1,244 @@ +SUBSYSTEM_DEF(ambient_lighting) //A simple SS that handles updating ambient lights of away sites and such places + name = "Ambient Lighting" + wait = 1 + priority = SS_PRIORITY_LIGHTING + init_order = SS_INIT_AMBIENT_LIGHT + runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT + + /// List of turfs queued for ambient light evaluation + var/list/queued = list() + + /// A bitmap of free ambience group indexes. + var/ambient_group_free_bitmap = ~0 + /// map of ambiient groups + var/list/ambient_groups[AMBIENT_GROUP_MAX_BITS] + +/datum/ambient_group + /// Index in SSambient_lighting map + var/global_index + var/list/member_turfs_by_z = list() + /// Color data, do NOT modify manually + var/apparent_r + var/apparent_g + var/apparent_b + /// Prevent modification of member turfs or colour while an operation is taking place + var/busy = FALSE + +/datum/ambient_group/New(ncolor, nmultiplier, nindex) + . = ..() + set_color(ncolor, nmultiplier) + global_index = nindex + +/datum/ambient_group/Destroy() + SSambient_lighting.ambient_groups[global_index] = null + SSambient_lighting.ambient_group_free_bitmap |= FLAG(global_index) + return ..() + +/datum/ambient_group/proc/set_color(color, multiplier) + var/list/new_parts = rgb2num(color) + //Calculate delta from current to desired location + var/dr = (new_parts[1] / 255) * multiplier - apparent_r + var/dg = (new_parts[2] / 255) * multiplier - apparent_g + var/db = (new_parts[3] / 255) * multiplier - apparent_b + + if (round(dr/4, LIGHTING_ROUND_VALUE) == 0 && round(dg/4, LIGHTING_ROUND_VALUE) == 0 && round(db/4, LIGHTING_ROUND_VALUE) == 0) + // no-op + return + + busy = TRUE + + // Doing it ordered by zlev should ensure that it looks vaguely coherent mid-update regardless of turf insertion order. + for (var/zlev in 1 to length(member_turfs_by_z)) + for (var/turf/T as anything in member_turfs_by_z[zlev]) + T.add_ambient_light_raw(dr, dg, db) + CHECK_TICK + + apparent_r += dr + apparent_g += dg + apparent_b += db + + busy = FALSE + +/** + * Adds group ambient light to a turf + * + * **Parameters**: + * - `T` turf - Turf to modify + * + */ +/datum/ambient_group/proc/set_ambient_light(turf/T) + set waitfor = FALSE + + UNTIL(!busy) + T.add_ambient_light_raw(apparent_r, apparent_g, apparent_b) + +/** + * Removes group ambient light from turf + * + * **Parameters**: + * - `T` turf - Turf to modify + * + */ +/datum/ambient_group/proc/remove_ambient_light(turf/T) + set waitfor = FALSE + + UNTIL(!busy) + T.add_ambient_light_raw(-apparent_r, -apparent_g, -apparent_b) + +/** + * Adds turf to ambient group, will set bitflags and set current ambient light + * + * **Parameters**: + * - `T` turf - Turf to add and track + * + */ +/datum/ambient_group/proc/add_turf(turf/T) + set waitfor = FALSE + + UNTIL(!busy) + //Already existing + if(T.ambient_bitflag & FLAG(global_index)) + return + + if (T.z > length(member_turfs_by_z)) + member_turfs_by_z.len = T.z + + LAZYADD(member_turfs_by_z[T.z], T) + T.ambient_bitflag |= FLAG(global_index) + set_ambient_light(T) + +/** + * Removes turf from ambient group if it is part of it. Removes group's ambient light and flag from turf + * + * **Parameters**: + * - `T` turf - Turf to remove + * + */ +/datum/ambient_group/proc/remove_turf(turf/T) + set waitfor = FALSE + + UNTIL(!busy) + if(!(T.ambient_bitflag & FLAG(global_index))) + return + + if (T.z > length(member_turfs_by_z)) + CRASH("Attempt to remove member turf with Z greater than local max -- this turf is not a member") + + remove_ambient_light(T) + T.ambient_bitflag &= ~FLAG(global_index) + member_turfs_by_z[T.z] -= T + +/** + * Find a valid index in the ambient group map for a new group + * + * Returns index or -1 if no indices are left + */ +/datum/controller/subsystem/ambient_lighting/proc/allocate_index() + if (ambient_group_free_bitmap == 0) + return -1 //Out of indices, no ambient light for you + + // Find the first free index in the bitmap. + var/index = 1 + while (!(ambient_group_free_bitmap & FLAG(index)) && index < AMBIENT_GROUP_MAX_BITS) + index += 1 + + ambient_group_free_bitmap &= ~FLAG(index) + + return index +/** + * Adds the space ambient group if it doesn't currently exist + * + */ +/datum/controller/subsystem/ambient_lighting/proc/add_space_ambient_group() + var/index = allocate_index() //It will always be 1, but we want to make sure bitmap is in a valid state + + ASSERT(index == SPACE_AMBIENT_GROUP) + + ambient_groups[index] = new /datum/ambient_group(SSskybox.background_color, config.starlight, index ) + +/** + * Removes turf from ambient group if it is part of it. Removes group's ambient light and flag from turf + * + * **Parameters**: + * - `color` color - Initial color + * - `multiplier` float - Initial multiplier of light strength + * + * Returns index or -1 if no indices are left + */ +/datum/controller/subsystem/ambient_lighting/proc/create_ambient_group(color, multiplier) + + if(ambient_groups[SPACE_AMBIENT_GROUP] == null) //Something (probably a planet) wants to add an ambient group, add space first + add_space_ambient_group() + + // Find the first free index in the bitmap. + var/index = allocate_index() + + if(index <= 0) + return index + + ambient_groups[index] = new /datum/ambient_group(color, multiplier, index) + + return index + +/** + * Removes turf from all ambient groups it is part of (if any) + * + * **Parameters**: + * - `target` turf - Turf to remove + */ +/datum/controller/subsystem/ambient_lighting/proc/clean_turf(turf/target) + if(target.ambient_bitflag != 0) + for(var/datum/ambient_group/A in ambient_groups) + if(target.ambient_bitflag & FLAG(A.global_index)) + A.remove_turf(target) + if(!target.ambient_bitflag) + return //Return early if flag is already clear + +/datum/controller/subsystem/ambient_lighting/Initialize(start_timeofday) + //Create space ambient group if nothing created it until now. + if(ambient_groups[SPACE_AMBIENT_GROUP] == null) + add_space_ambient_group() + + fire(FALSE, TRUE) + return ..() + +/// Go over turfs in queue, add them to space or planet ambient groups if valid, else remove them from all ambient groups +/datum/controller/subsystem/ambient_lighting/fire(resumed = FALSE, no_mc_tick = FALSE) + var/list/curr = queued + var/starlight_enabled = config.starlight + + var/needs_ambience + while (length(curr)) + var/turf/target = curr[length(curr)] + LIST_DEC(curr) + + if(target && target.is_outside()) + needs_ambience = TURF_IS_DYNAMICALLY_LIT_UNSAFE(target) + if (!needs_ambience) + for (var/turf/T in RANGE_TURFS(target, 1)) + if(TURF_IS_DYNAMICALLY_LIT_UNSAFE(T)) + needs_ambience = TRUE + break + + if (needs_ambience) + var/obj/effect/overmap/visitable/sector/exoplanet/E = map_sectors["[target.z]"] + if (istype(E)) + if(E.ambient_group_index > 0) + var/datum/ambient_group/A = ambient_groups[E.ambient_group_index] + A.add_turf(target) + else + if (starlight_enabled) //Assume we can light up exterior with space light generally + var/datum/ambient_group/A = ambient_groups[SPACE_AMBIENT_GROUP] + A.add_turf(target) + else if (TURF_IS_AMBIENT_LIT_UNSAFE(target)) + //Remove from all groups + if(target.ambient_bitflag != 0) + for(var/datum/ambient_group/A in ambient_groups) + A.remove_turf(target) + if(!target.ambient_bitflag) + break + + if (no_mc_tick) + CHECK_TICK + else if (MC_TICK_CHECK) + return diff --git a/code/controllers/subsystems/lighting.dm b/code/controllers/subsystems/lighting.dm index 775bb83b47ba5..971adfeac50b7 100644 --- a/code/controllers/subsystems/lighting.dm +++ b/code/controllers/subsystems/lighting.dm @@ -1,112 +1,140 @@ -var/global/lighting_overlays_initialised = FALSE - SUBSYSTEM_DEF(lighting) name = "Lighting" - wait = 1 + wait = LIGHTING_INTERVAL + priority = SS_PRIORITY_LIGHTING init_order = SS_INIT_LIGHTING + runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY - // Queues of update counts, waiting to be rolled into stats lists - var/list/stats_queues = list( - "Source" = list(), - "Corner" = list(), - "Overlay" = list() - ) - // Stats lists - var/list/stats_lists = list( - "Source" = list(), - "Corner" = list(), - "Overlay" = list() - ) - var/update_stats_every = 1 SECOND - var/next_stats_update = 0 - var/stat_updates_to_keep = 5 + var/total_lighting_overlays = 0 + var/total_lighting_sources = 0 + var/total_ambient_turfs = 0 + var/total_lighting_corners = 0 - var/list/light_queue = list() // lighting sources queued for update. + /// lighting sources queued for update. + var/list/light_queue = list() var/lq_idex = 1 - var/list/corner_queue = list() // lighting corners queued for update. + /// lighting corners queued for update. + var/list/corner_queue = list() var/cq_idex = 1 - var/list/overlay_queue = list() // lighting overlays queued for update. + /// lighting overlays queued for update. + var/list/overlay_queue = list() var/oq_idex = 1 + // - Performance and analytics data var/processed_lights = 0 var/processed_corners = 0 var/processed_overlays = 0 + var/total_ss_updates = 0 + var/total_instant_updates = 0 + +#ifdef USE_INTELLIGENT_LIGHTING_UPDATES + var/force_queued = TRUE + var/force_override = FALSE // For admins. +#endif + /datum/controller/subsystem/lighting/UpdateStat(time) - if (PreventUpdateStat(time)) - return ..() - ..({"\ - Queues: \ - Source [length(light_queue)] \ - Corner [length(corner_queue)] \ - Overlay [length(overlay_queue)]\n\ - Source Updates [length(stats_lists["Source"])]\n\ - Corner Updates [length(stats_lists["Corner"])]\n\ - Overlay Updates [length(stats_lists["Overlay"])]\ - "}) - - -/datum/controller/subsystem/lighting/Initialize(start_uptime) - InitializeTurfs() - lighting_overlays_initialised = TRUE + var/list/out = list( +#ifdef USE_INTELLIGENT_LIGHTING_UPDATES + "IUR: [total_ss_updates ? round(total_instant_updates/(total_instant_updates+total_ss_updates)*100, 0.1) : "NaN"]%\n", +#endif + "\tT:{L:[total_lighting_sources] C:[total_lighting_corners] O:[total_lighting_overlays] A:[total_ambient_turfs]}\n", + "\tP:{L:[length(light_queue) - (lq_idex - 1)]|C:[length(corner_queue) - (cq_idex - 1)]|O:[length(overlay_queue) - (oq_idex - 1)]}\n", + "\tL:{L:[processed_lights]|C:[processed_corners]|O:[processed_overlays]}\n" + ) + ..(out.Join()) + +#ifdef USE_INTELLIGENT_LIGHTING_UPDATES + +/hook/roundstart/proc/lighting_init_roundstart() + SSlighting.handle_roundstart() + return TRUE + +/datum/controller/subsystem/lighting/proc/handle_roundstart() + force_queued = FALSE + total_ss_updates = 0 + total_instant_updates = 0 + +#endif +/// Generate overlays for all Zlevels and then fire normally +/datum/controller/subsystem/lighting/Initialize(timeofday) + var/overlaycount = 0 + var/starttime = REALTIMEOFDAY + + // Generate overlays. + for (var/zlevel = 1 to world.maxz) + overlaycount += InitializeZlev(zlevel) + + admin_notice(SPAN_DANGER("Created [overlaycount] lighting overlays in [(REALTIMEOFDAY - starttime)/10] seconds."), R_DEBUG) + + starttime = REALTIMEOFDAY + // Tick once to clear most lights. fire(FALSE, TRUE) + admin_notice(SPAN_DANGER("Processed [processed_lights] light sources."), R_DEBUG) + admin_notice(SPAN_DANGER("Processed [processed_corners] light corners."), R_DEBUG) + admin_notice(SPAN_DANGER("Processed [processed_overlays] light overlays."), R_DEBUG) + admin_notice(SPAN_DANGER("Lighting pre-bake completed in [(REALTIMEOFDAY - starttime)/10] seconds."), R_DEBUG) + + log_ss("lighting", "NOv:[overlaycount] L:[processed_lights] C:[processed_corners] O:[processed_overlays]") + + ..() + +/** + * Go over turfs thay may be dynamically lit and add a lighting overlay if they don't have one. Then do the same for turfs that may be ambient lit. + * + * **Parameters**: + * - `zlev` int - z-level index + */ +/datum/controller/subsystem/lighting/proc/InitializeZlev(zlev) + for (var/thing in Z_ALL_TURFS(zlev)) + var/turf/T = thing + if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(T) && !T.lighting_overlay) // Can't assume that one hasn't already been created on bay/neb. + new /atom/movable/lighting_overlay(T) + . += 1 + if(TURF_IS_AMBIENT_LIT_UNSAFE(T)) + T.generate_missing_corners() // Forcibly generate corners. -// It's safe to pass a list of non-turfs to this list - it'll only check turfs. + CHECK_TICK + +/// Initialize a set of turfs (for example as part of loading a map template) It's safe to pass a list of non-turfs to this list - it'll only check turfs. /datum/controller/subsystem/lighting/proc/InitializeTurfs(list/targets) for (var/turf/T in (targets || world)) - if (T.dynamic_lighting && T.loc:dynamic_lighting) + if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(T)) T.lighting_build_overlay() // If this isn't here, BYOND will set-background us. CHECK_TICK +/** + * Go over light queue and update corners as needed + * Go over light corner queue and update overlays as needed + * Go over overlay queue and update as needed + */ /datum/controller/subsystem/lighting/fire(resumed = FALSE, no_mc_tick = FALSE) if (!resumed) - stats_queues["Source"] += processed_lights - stats_queues["Corner"] += processed_corners - stats_queues["Overlay"] += processed_overlays - processed_lights = 0 processed_corners = 0 processed_overlays = 0 - if(next_stats_update <= world.time) - next_stats_update = world.time + update_stats_every - for(var/stat_name in stats_queues) - var/stat_sum = 0 - var/list/stats_queue = stats_queues[stat_name] - for(var/count in stats_queue) - stat_sum += count - stats_queue.Cut() - - var/list/stats_list = stats_lists[stat_name] - stats_list.Insert(1, stat_sum) - if(length(stats_list) > stat_updates_to_keep) - stats_list.Cut(length(stats_list)) - MC_SPLIT_TICK_INIT(3) if (!no_mc_tick) MC_SPLIT_TICK - // Sources. - while (lq_idex <= length(light_queue)) - var/datum/light_source/L = light_queue[lq_idex] - lq_idex += 1 + var/list/curr_lights = light_queue + var/list/curr_corners = corner_queue + var/list/curr_overlays = overlay_queue - if(L.check() || L.destroyed || L.force_update) - L.remove_lum() - if(!L.destroyed) - L.apply_lum() + while (lq_idex <= length(curr_lights)) + var/datum/light_source/L = curr_lights[lq_idex++] - else if(L.vis_update) //We smartly update only tiles that became (in) visible to use. - L.smart_vis_update() + if (L.needs_update != LIGHTING_NO_UPDATE) + total_ss_updates += 1 + L.update_corners() - L.vis_update = FALSE - L.force_update = FALSE - L.needs_update = FALSE + L.needs_update = LIGHTING_NO_UPDATE - processed_lights += 1 + processed_lights++ if (no_mc_tick) CHECK_TICK @@ -114,22 +142,21 @@ SUBSYSTEM_DEF(lighting) break if (lq_idex > 1) - light_queue.Cut(1, lq_idex) + curr_lights.Cut(1, lq_idex) lq_idex = 1 if (!no_mc_tick) MC_SPLIT_TICK - // Corners. - while (cq_idex <= length(corner_queue)) - var/datum/lighting_corner/C = corner_queue[cq_idex] - cq_idex += 1 + while (cq_idex <= length(curr_corners)) + var/datum/lighting_corner/C = curr_corners[cq_idex++] - C.update_overlays() + if (C.needs_update) + C.update_overlays() - C.needs_update = FALSE + C.needs_update = FALSE - processed_corners += 1 + processed_corners++ if (no_mc_tick) CHECK_TICK @@ -137,27 +164,51 @@ SUBSYSTEM_DEF(lighting) break if (cq_idex > 1) - corner_queue.Cut(1, cq_idex) + curr_corners.Cut(1, cq_idex) cq_idex = 1 if (!no_mc_tick) MC_SPLIT_TICK - // Objects. - while (oq_idex <= length(overlay_queue)) - var/atom/movable/lighting_overlay/O = overlay_queue[oq_idex] - oq_idex += 1 + while (oq_idex <= length(curr_overlays)) + var/atom/movable/lighting_overlay/O = curr_overlays[oq_idex++] - O.update_overlay() - O.needs_update = 0 + if (!QDELETED(O) && O.needs_update) + O.update_overlay() + O.needs_update = FALSE - processed_overlays += 1 + processed_overlays++ if (no_mc_tick) CHECK_TICK else if (MC_TICK_CHECK) break + if (oq_idex > 1) + curr_overlays.Cut(1, oq_idex) + oq_idex = 1 + +/datum/controller/subsystem/lighting/Recover() + total_lighting_corners = SSlighting.total_lighting_corners + total_lighting_overlays = SSlighting.total_lighting_overlays + total_lighting_sources = SSlighting.total_lighting_sources + + light_queue = SSlighting.light_queue + corner_queue = SSlighting.corner_queue + overlay_queue = SSlighting.overlay_queue + + lq_idex = SSlighting.lq_idex + cq_idex = SSlighting.cq_idex + oq_idex = SSlighting.oq_idex + + if (lq_idex > 1) + light_queue.Cut(1, lq_idex) + lq_idex = 1 + + if (cq_idex > 1) + corner_queue.Cut(1, cq_idex) + cq_idex = 1 + if (oq_idex > 1) overlay_queue.Cut(1, oq_idex) oq_idex = 1 diff --git a/code/controllers/subsystems/mobs.dm b/code/controllers/subsystems/mobs.dm index 42fd7330a381a..7c6378b83a61e 100644 --- a/code/controllers/subsystems/mobs.dm +++ b/code/controllers/subsystems/mobs.dm @@ -61,8 +61,3 @@ if(MOB.is_processing == SSmobs) {\ else if (MOB.is_processing) {\ crash_with("Failed to stop processing mob. Being processed by [MOB.is_processing] instead.")\ } - - -/mob/dview/Initialize() - . = ..() - STOP_PROCESSING_MOB(src) diff --git a/code/controllers/subsystems/zcopy.dm b/code/controllers/subsystems/zcopy.dm index 6c8bc6a8fe523..a5f21140f0832 100644 --- a/code/controllers/subsystems/zcopy.dm +++ b/code/controllers/subsystems/zcopy.dm @@ -1,5 +1,6 @@ -#define SHADOWER_DARKENING_FACTOR 0.6 // The multiplication factor for openturf shadower darkness. Lighting will be multiplied by this. -#define SHADOWER_DARKENING_COLOR "#999999" // The above, but as an RGB string for lighting-less turfs. +#define SHADOWER_DARKENING_FACTOR 0.8 // The multiplication factor for openturf shadower darkness. Lighting will be multiplied by this. +#define SHADOWER_DARKENING_COLOR "#00000033" // The above, but as an RGB string for lighting-less turfs. +//Bay can't do multiplicative lighting for zmimic currently so we change alpha, this does mean full lit turfs need a different colour. TODO: Take another look at zmimic render setup SUBSYSTEM_DEF(zcopy) name = "Z-Copy" @@ -181,6 +182,9 @@ SUBSYSTEM_DEF(zcopy) T.z_generation += 1 T.z_queued -= 1 + if (T.above) + T.above.update_mimic() + if (no_mc_tick) CHECK_TICK else if (MC_TICK_CHECK) @@ -268,7 +272,7 @@ SUBSYSTEM_DEF(zcopy) var/atom/movable/openspace/turf_mimic/DC = T.below.mimic_above_copy DC.appearance = T.below DC.mouse_opacity = initial(DC.mouse_opacity) - DC.plane = OPENTURF_MAX_PLANE + DC.plane = OPENTURF_MAX_PLANE - turf_depth - 1 else if (T.below.mimic_above_copy) QDEL_NULL(T.below.mimic_above_copy) @@ -284,7 +288,7 @@ SUBSYSTEM_DEF(zcopy) // Special case: these are merged into the shadower to reduce memory usage. if (object.type == /atom/movable/lighting_overlay) - //T.shadower.copy_lighting(object) + T.shadower.copy_lighting(object) continue if (!object.bound_overlay) // Generate a new overlay if the atom doesn't already have one. @@ -297,6 +301,7 @@ SUBSYSTEM_DEF(zcopy) var/have_performed_fixup = FALSE switch (object.type) + // Layering for recursive mimic needs to be inherited. if (/atom/movable/openspace/mimic) var/atom/movable/openspace/mimic/OOO = object original_type = OOO.mimiced_type diff --git a/code/datums/observation/sight_set.dm b/code/datums/observation/sight_set.dm index 664ad33b88c80..c2db96c382d3c 100644 --- a/code/datums/observation/sight_set.dm +++ b/code/datums/observation/sight_set.dm @@ -18,8 +18,14 @@ GLOBAL_DATUM_INIT(sight_set_event, /singleton/observ/sight_set, new) * Sight Set Handling * *********************/ +/mob/var/see_black = FALSE + /mob/proc/set_sight(new_sight) var/old_sight = sight + if(see_black) + new_sight |= SEE_BLACKNESS // Avoids pixel bleed from atoms overlapping completely dark turfs, but conflicts with other flags. + else + new_sight &= ~SEE_BLACKNESS if(old_sight != new_sight) sight = new_sight GLOB.sight_set_event.raise_event(src, old_sight, new_sight) diff --git a/code/datums/proximity_trigger/proximity_trigger.dm b/code/datums/proximity_trigger/proximity_trigger.dm index af67ffe616097..a4ffa41bbf795 100644 --- a/code/datums/proximity_trigger/proximity_trigger.dm +++ b/code/datums/proximity_trigger/proximity_trigger.dm @@ -140,9 +140,10 @@ var/global/const/PROXIMITY_EXCLUDE_HOLDER_TURF = 1 // When acquiring turfs to mo if(!center) return - for(var/T in dview(range_, center)) - if(T in turfs_in_range) + FOR_DVIEW(var/T, range_, center, 0) + if (T in turfs_in_range) // This is awful, but I don't want to refactor this to be assoc. . += T + END_FOR_DVIEW /datum/proximity_trigger/proc/acquire_relevant_turfs() . = turf_selection.get_turfs(holder, range_) diff --git a/code/datums/security_state.dm b/code/datums/security_state.dm index 08669a6280070..65e5bc8ddc909 100644 --- a/code/datums/security_state.dm +++ b/code/datums/security_state.dm @@ -141,9 +141,8 @@ var/alarm_level = "off" // These values are primarily for station alarms and status displays, and which light colors and overlays to use - var/light_max_bright = 0.5 - var/light_inner_range = 0.1 - var/light_outer_range = 1 + var/light_range + var/light_power var/light_color_alarm var/light_color_status_display @@ -205,9 +204,8 @@ /singleton/security_level/default/code_green name = "code green" - light_max_bright = 0.25 - light_inner_range = 0.1 - light_outer_range = 1 + light_range = 2 + light_power = 1 light_color_alarm = COLOR_GREEN light_color_status_display = COLOR_GREEN @@ -222,9 +220,8 @@ name = "code blue" alarm_level = "on" - light_max_bright = 0.5 - light_inner_range = 0.1 - light_outer_range = 2 + light_range = 2 + light_power = 1 light_color_alarm = COLOR_BLUE light_color_status_display = COLOR_BLUE @@ -241,9 +238,8 @@ name = "code red" alarm_level = "on" - light_max_bright = 0.5 - light_inner_range = 0.1 - light_outer_range = 2 + light_range = 4 + light_power = 2 light_color_alarm = COLOR_RED light_color_status_display = COLOR_RED @@ -260,9 +256,8 @@ name = "code delta" alarm_level = "on" - light_max_bright = 0.75 - light_inner_range = 0.1 - light_outer_range = 3 + light_range = 4 + light_power = 2 light_color_alarm = COLOR_RED light_color_status_display = COLOR_NAVY_BLUE diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 5968e31fa6adc..12e0b991923bf 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -83,14 +83,14 @@ log_debug("Abstract atom [type] created!") return INITIALIZE_HINT_QDEL - if(light_max_bright && light_outer_range) + if(light_power && light_range) update_light() if(opacity) updateVisibility(src) var/turf/T = loc if(istype(T)) - T.RecalculateOpacity() + T.recalc_atom_opacity() if (health_max) health_current = health_max @@ -114,6 +114,7 @@ /atom/Destroy() QDEL_NULL(reagents) + QDEL_NULL(light) . = ..() /** @@ -422,6 +423,18 @@ return FALSE dir = new_dir GLOB.dir_set_event.raise_event(src, old_dir, dir) + + //Lighting + if(light_source_solo) + if(light_source_solo.light_angle) + light_source_solo.source_atom.update_light() + else if(light_source_multi) + var/datum/light_source/L + for(var/thing in light_source_multi) + L = thing + if(L.light_angle) + L.source_atom.update_light() + return TRUE /** diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 86cf810c8b69c..29c70a42c096c 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -217,8 +217,13 @@ updateVisibility(src) // lighting - if (light_sources) // Yes, I know you can for-null safely, but this is slightly faster. Hell knows why. - for (var/datum/light_source/L in light_sources) + if (light_source_solo) + light_source_solo.source_atom.update_light() + else if (light_source_multi) + var/datum/light_source/L + var/thing + for (thing in light_source_multi) + L = thing L.source_atom.update_light() /atom/movable/Move(...) @@ -233,8 +238,13 @@ updateVisibility(src) // lighting - if (light_sources) // Yes, I know you can for-null safely, this is slightly faster. Hell knows why. - for (var/datum/light_source/L in light_sources) + if (light_source_solo) + light_source_solo.source_atom.update_light() + else if (light_source_multi) + var/datum/light_source/L + var/thing + for (thing in light_source_multi) + L = thing L.source_atom.update_light() //called when src is thrown into hit_atom diff --git a/code/game/gamemodes/cult/cult_structures.dm b/code/game/gamemodes/cult/cult_structures.dm index 360a6bcd78296..cc96c3f22d01c 100644 --- a/code/game/gamemodes/cult/cult_structures.dm +++ b/code/game/gamemodes/cult/cult_structures.dm @@ -19,9 +19,8 @@ desc = "A floating crystal that hums with an unearthly energy." icon = 'icons/obj/structures/pylon.dmi' icon_state = "pylon" - light_max_bright = 0.5 - light_inner_range = 1 - light_outer_range = 13 + light_power = 0.5 + light_range = 13 light_color = "#3e0000" health_max = 20 health_min_damage = 4 @@ -73,7 +72,7 @@ var/spawnable = null /obj/effect/gateway/active - light_outer_range=5 + light_range=5 light_color="#ff0000" spawnable=list( /mob/living/simple_animal/hostile/scarybat, @@ -82,7 +81,6 @@ ) /obj/effect/gateway/active/cult - light_outer_range=5 light_color="#ff0000" spawnable=list( /mob/living/simple_animal/hostile/scarybat/cult, diff --git a/code/game/gamemodes/cult/narsie.dm b/code/game/gamemodes/cult/narsie.dm index 9a84d4566df41..fa2bfb1c41fe7 100644 --- a/code/game/gamemodes/cult/narsie.dm +++ b/code/game/gamemodes/cult/narsie.dm @@ -32,7 +32,7 @@ var/global/list/narsie_list = list() // Pixel stuff centers Narsie. pixel_x = -236 pixel_y = -256 - light_outer_range = 1 + light_range = 1 light_color = "#3e0000" current_size = 6 @@ -154,7 +154,7 @@ var/global/list/narsie_list = list() T.icon_state = "cult-narsie" T.set_opacity(0) T.set_density(0) - set_light(1) + set_light(1, 1) /obj/singularity/narsie/large/consume(atom/A) //Has its own consume proc because it doesn't need energy and I don't want BoHs to explode it. --NEO //NEW BEHAVIOUR diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm index b5bd691adfb74..3d0589477743e 100644 --- a/code/game/machinery/alarm.dm +++ b/code/game/machinery/alarm.dm @@ -370,7 +370,7 @@ else if(dir == EAST) pixel_x = -21 - set_light(0.25, 0.1, 1, 2, new_color) + set_light(2, 0.25, new_color) /obj/machinery/alarm/receive_signal(datum/signal/signal) if(!signal || signal.encryption) @@ -964,12 +964,12 @@ FIRE ALARM else if(!detecting) AddOverlays(get_cached_overlay("fire1")) - set_light(0.25, 0.1, 1, 2, COLOR_RED) + set_light(2, 0.25, COLOR_RED) else if(z in GLOB.using_map.contact_levels) var/singleton/security_state/security_state = GET_SINGLETON(GLOB.using_map.security_state) var/singleton/security_level/sl = security_state.current_security_level - set_light(sl.light_max_bright, sl.light_inner_range, sl.light_outer_range, 2, sl.light_color_alarm) + set_light(sl.light_power, sl.light_range, sl.light_color_alarm) AddOverlays(image(sl.icon, sl.overlay_alarm)) else AddOverlays(get_cached_overlay("fire0")) diff --git a/code/game/machinery/bluespace_drive.dm b/code/game/machinery/bluespace_drive.dm index 1e84f0d607d94..c95f810717ead 100644 --- a/code/game/machinery/bluespace_drive.dm +++ b/code/game/machinery/bluespace_drive.dm @@ -38,7 +38,7 @@ . = ..() drive_sound = GLOB.sound_player.PlayLoopingSound(src, "\ref[src]", 'sound/machines/BSD_idle.ogg', 50, 7) AddParticles(/particles/torus/bluespace) - set_light(1, 5, 15, 10, COLOR_CYAN) + set_light(15, 1, COLOR_CYAN) update_icon() diff --git a/code/game/machinery/computer/computer.dm b/code/game/machinery/computer/computer.dm index f3251db715e88..6ac304b64da2c 100644 --- a/code/game/machinery/computer/computer.dm +++ b/code/game/machinery/computer/computer.dm @@ -17,9 +17,8 @@ var/icon_keyboard = "generic_key" var/icon_screen = "generic" - var/light_max_bright_on = 0.2 - var/light_inner_range_on = 0.1 - var/light_outer_range_on = 2 + var/light_power_on = 1 + var/light_range_on = 2 var/overlay_layer atom_flags = ATOM_FLAG_NO_TEMP_CHANGE | ATOM_FLAG_CLIMBABLE clicksound = "keyboard" @@ -109,7 +108,7 @@ */ /obj/machinery/computer/proc/update_glow() if (operable()) - set_light(light_max_bright_on, light_inner_range_on, light_outer_range_on, 2, light_color) + set_light(light_range_on, light_power_on, light_color) return TRUE else set_light(0) diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index cabbad0bbacc4..864a515e82ec2 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -705,21 +705,21 @@ About the new airlock wires panel: if(AIRLOCK_CLOSED) if(lights && locked) new_overlays += overlay_image(bolts_file, plane = EFFECTS_ABOVE_LIGHTING_PLANE, layer = ABOVE_LIGHTING_LAYER) - set_light(0.25, 0.1, 1, 2, COLOR_RED_LIGHT) + set_light(2, 0.75, COLOR_RED_LIGHT) if(AIRLOCK_DENY) if(lights) new_overlays += overlay_image(deny_file, plane = EFFECTS_ABOVE_LIGHTING_PLANE, layer = ABOVE_LIGHTING_LAYER) - set_light(0.25, 0.1, 1, 2, COLOR_RED_LIGHT) + set_light(2, 0.75, COLOR_RED_LIGHT) if(AIRLOCK_EMAG) new_overlays += overlay_image(emag_file, plane = EFFECTS_ABOVE_LIGHTING_PLANE, layer = ABOVE_LIGHTING_LAYER) if(AIRLOCK_CLOSING) if(lights) new_overlays += overlay_image(lights_file, plane = EFFECTS_ABOVE_LIGHTING_PLANE, layer = ABOVE_LIGHTING_LAYER) - set_light(0.25, 0.1, 1, 2, COLOR_LIME) + set_light(2, 0.75, COLOR_LIME) if(AIRLOCK_OPENING) if(lights) new_overlays += overlay_image(lights_file, plane = EFFECTS_ABOVE_LIGHTING_PLANE, layer = ABOVE_LIGHTING_LAYER) - set_light(0.25, 0.1, 1, 2, COLOR_LIME) + set_light(2, 0.75, COLOR_LIME) if(MACHINE_IS_BROKEN(src)) new_overlays += overlay_image(sparks_broken_file, plane = EFFECTS_ABOVE_LIGHTING_PLANE, layer = ABOVE_LIGHTING_LAYER) else if (get_damage_percentage() >= 25) diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm index 7369ae505838d..d89a8e1b00d1a 100644 --- a/code/game/machinery/doors/firedoor.dm +++ b/code/game/machinery/doors/firedoor.dm @@ -521,7 +521,7 @@ weld_overlay = welded_file if(do_set_light) - set_light(0.25, 0.1, 1, 2, COLOR_SUN) + set_light(2, 0.25, COLOR_SUN) AddOverlays(panel_overlay) AddOverlays(weld_overlay) diff --git a/code/game/machinery/doors/simple.dm b/code/game/machinery/doors/simple.dm index d9064599205d8..196c9b9b19a65 100644 --- a/code/game/machinery/doors/simple.dm +++ b/code/game/machinery/doors/simple.dm @@ -29,7 +29,7 @@ if(locked) lock = new(src,locked) if(material.luminescence) - set_light(0.5, 1, material.luminescence, l_color = material.icon_colour) + set_light(material.luminescence, 0.5, l_color = material.icon_colour) if(material.opacity < 0.5) glass = TRUE diff --git a/code/game/machinery/floodlight.dm b/code/game/machinery/floodlight.dm index 62a481a074d26..ce38f148a0522 100644 --- a/code/game/machinery/floodlight.dm +++ b/code/game/machinery/floodlight.dm @@ -17,9 +17,8 @@ machine_desc = "A portable, battery-powered LED flood lamp used to illuminate large areas." //better laser, increased brightness & power consumption - var/l_max_bright = 0.8 //brightness of light when on, can be negative - var/l_inner_range = 0 //inner range of light when on, can be negative - var/l_outer_range = 4.5 //outer range of light when on, can be negative + var/l_power = 2.5 //brightness of light when on, can be negative + var/l_range = 7 //outer range of light when on, can be negative /obj/machinery/floodlight/on_update_icon() icon_state = "flood[panel_open ? "o" : ""][panel_open && get_cell() ? "b" : ""]0[use_power == POWER_USE_ACTIVE]" @@ -34,17 +33,17 @@ // If the cell is almost empty rarely "flicker" the light. Aesthetic only. if(prob(30)) - set_light(l_max_bright / 2, l_inner_range, l_outer_range) + set_light(l_range, l_power / 2, angle = LIGHT_WIDE) spawn(20) if(use_power) - set_light(l_max_bright, l_inner_range, l_outer_range) + set_light(l_range, l_power, angle = LIGHT_WIDE) // Returns 0 on failure and 1 on success /obj/machinery/floodlight/proc/turn_on(loud = 0) if(!is_powered()) return 0 - set_light(l_max_bright, l_inner_range, l_outer_range) + set_light(l_range, l_power / 2, angle = LIGHT_WIDE) update_use_power(POWER_USE_ACTIVE) use_power_oneoff(active_power_usage)//so we drain cell if they keep trying to use it update_icon() @@ -77,9 +76,8 @@ /obj/machinery/floodlight/RefreshParts()//if they're insane enough to modify a floodlight, let them ..() var/light_mod = clamp(total_component_rating_of_type(/obj/item/stock_parts/capacitor), 0, 10) - l_max_bright = light_mod? light_mod*0.01 + initial(l_max_bright) : initial(l_max_bright)/2 //gives us between 0.8-0.9 with capacitor, or 0.4 without one - l_inner_range = light_mod + initial(l_inner_range) - l_outer_range = light_mod*1.5 + initial(l_outer_range) + l_power = light_mod? light_mod*0.01 + initial(l_power) : initial(l_power)/2 //gives us between 0.8-0.9 with capacitor, or 0.4 without one + l_range = light_mod*1.5 + initial(l_range) change_power_consumption(initial(active_power_usage) * light_mod , POWER_USE_ACTIVE) if(use_power) - set_light(l_max_bright, l_inner_range, l_outer_range) + set_light(l_range, l_power, angle = LIGHT_WIDE) diff --git a/code/game/machinery/floor_light.dm b/code/game/machinery/floor_light.dm index e058d23f3e911..81939b750a7c6 100644 --- a/code/game/machinery/floor_light.dm +++ b/code/game/machinery/floor_light.dm @@ -14,9 +14,8 @@ var/global/list/floor_light_cache = list() matter = list(MATERIAL_STEEL = 250, MATERIAL_GLASS = 250) var/damaged - var/default_light_max_bright = 0.75 - var/default_light_inner_range = 1 - var/default_light_outer_range = 3 + var/default_light_power = 0.75 + var/default_light_range = 3 var/default_light_colour = "#ffffff" @@ -105,11 +104,11 @@ var/global/list/floor_light_cache = list() /obj/machinery/floor_light/proc/update_brightness() if((use_power == POWER_USE_ACTIVE) && operable()) - if(light_outer_range != default_light_outer_range || light_max_bright != default_light_max_bright || light_color != default_light_colour) - set_light(default_light_max_bright, default_light_inner_range, default_light_outer_range, l_color = default_light_colour) - change_power_consumption((light_outer_range + light_max_bright) * 20, POWER_USE_ACTIVE) + if(light_range != default_light_range || light_power != default_light_power || light_color != default_light_colour) + set_light(default_light_range, default_light_power, default_light_colour) + change_power_consumption((light_range + light_power) * 20, POWER_USE_ACTIVE) else - if(light_outer_range || light_max_bright) + if(light_range || light_power) set_light(0) /obj/machinery/floor_light/on_update_icon() diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index 8e5cc24e5b90c..5f4d088ed140c 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -356,9 +356,9 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ hologram.SetName("[A.name] (Hologram)") //If someone decides to right click. A.holo = src masters[A] = hologram - hologram.set_light(1, 0.1, 2) //hologram lighting + hologram.set_light(2, 0.1) //hologram lighting hologram.color = color //painted holopad gives coloured holograms - set_light(1, 0.1, 2) //pad lighting + set_light(2, 0.1) //pad lighting icon_state = "[base_icon]1" return 1 diff --git a/code/game/machinery/holosign.dm b/code/game/machinery/holosign.dm index 2a6c00e93ec22..a0920d6fab780 100644 --- a/code/game/machinery/holosign.dm +++ b/code/game/machinery/holosign.dm @@ -36,7 +36,7 @@ set_light(0) else icon_state = on_icon - set_light(0.5, 0.5, 1, l_color = COLOR_CYAN_BLUE) + set_light(1, 0.5, COLOR_CYAN_BLUE) /singleton/public_access/public_variable/holosign_on expected_type = /obj/machinery/holosign diff --git a/code/game/machinery/lightswitch.dm b/code/game/machinery/lightswitch.dm index c121ac2efc611..0e48e0a77e2a2 100644 --- a/code/game/machinery/lightswitch.dm +++ b/code/game/machinery/lightswitch.dm @@ -41,7 +41,7 @@ icon_state = "light[on]" overlay.icon_state = "light[on]-overlay" AddOverlays(overlay) - set_light(0.1, 0.1, 1, 2, on ? "#82ff4c" : "#f86060") + set_light(2, 0.25, on ? "#82ff4c" : "#f86060") /obj/machinery/light_switch/examine(mob/user, distance) . = ..() diff --git a/code/game/machinery/newscaster.dm b/code/game/machinery/newscaster.dm index 889addcde312d..62c5f3cb24e9a 100644 --- a/code/game/machinery/newscaster.dm +++ b/code/game/machinery/newscaster.dm @@ -153,7 +153,7 @@ var/global/list/obj/machinery/newscaster/allCasters = list() //Global list that var/hitstaken = 0 //Death at 3 hits from an item with force>=15 var/datum/feed_channel/viewing_channel = null var/datum/feed_network/connected_group - light_outer_range = 0 + light_range = 0 anchored = TRUE layer = ABOVE_WINDOW_LAYER diff --git a/code/game/machinery/requests_console.dm b/code/game/machinery/requests_console.dm index 7d0c25c78b187..36f789cc7fa83 100644 --- a/code/game/machinery/requests_console.dm +++ b/code/game/machinery/requests_console.dm @@ -50,7 +50,7 @@ var/global/list/obj/machinery/requests_console/allConsoles = list() var/message = ""; var/recipient = ""; //the department which will be receiving the message var/priority = -1 ; //Priority of the message being sent - light_outer_range = 0 + light_range = 0 var/datum/announcement/announcement = new /obj/machinery/requests_console/on_update_icon() diff --git a/code/game/machinery/rotating_alarm.dm b/code/game/machinery/rotating_alarm.dm index 4dc1650968955..5d9e7b20a067e 100644 --- a/code/game/machinery/rotating_alarm.dm +++ b/code/game/machinery/rotating_alarm.dm @@ -96,7 +96,7 @@ /obj/machinery/rotating_alarm/proc/set_on() vis_contents += spin_effect - set_light(1, 0.5, 2, 0.3, alarm_light_color) + set_light(2, 0.5, alarm_light_color) on = TRUE low_alarm = FALSE diff --git a/code/game/machinery/spaceheater.dm b/code/game/machinery/spaceheater.dm index 4c9e6641b6bc5..71107f83a46b4 100644 --- a/code/game/machinery/spaceheater.dm +++ b/code/game/machinery/spaceheater.dm @@ -14,6 +14,8 @@ atom_flags = ATOM_FLAG_NO_TEMP_CHANGE | ATOM_FLAG_CLIMBABLE clicksound = "switch" + light_power = 0.5 + /obj/machinery/space_heater/New() ..() @@ -26,10 +28,10 @@ set_light(0) else if(active > 0) icon_state = "sheater-heat" - set_light(0.7, 1, 2, 3, COLOR_SEDONA) + set_light(3, COLOR_SEDONA) else if(active < 0) icon_state = "sheater-cool" - set_light(0.7, 1, 2, 3, COLOR_DEEP_SKY_BLUE) + set_light(3, l_color = COLOR_DEEP_SKY_BLUE) else icon_state = "sheater-standby" set_light(0) diff --git a/code/game/machinery/status_display.dm b/code/game/machinery/status_display.dm index dd86c05886db2..c909f8d85fa88 100644 --- a/code/game/machinery/status_display.dm +++ b/code/game/machinery/status_display.dm @@ -191,7 +191,7 @@ var/image/alert = overlay_image(sl.icon, sl.overlay_status_display, plane = EFFECTS_ABOVE_LIGHTING_PLANE, layer = ABOVE_LIGHTING_LAYER) - set_light(sl.light_max_bright, sl.light_inner_range, sl.light_outer_range, 2, sl.light_color_alarm) + set_light(sl.light_range, sl.light_power, sl.light_color_alarm) AddOverlays(alert) /obj/machinery/status_display/proc/set_picture(state) @@ -200,13 +200,13 @@ picture_state = state picture = image('icons/obj/machines/status_display.dmi', icon_state=picture_state) AddOverlays(picture) - set_light(0.5, 0.1, 1, 2, COLOR_WHITE) + set_light(2, 0.5, COLOR_WHITE) /obj/machinery/status_display/proc/update_display(line1, line2) var/new_text = {"
[line1]
[line2]
"} if(maptext != new_text) maptext = new_text - set_light(0.5, 0.1, 1, 2, COLOR_WHITE) + set_light(2, 0.5, COLOR_WHITE) /obj/machinery/status_display/proc/get_shuttle_timer() var/timeleft = evacuation_controller.get_eta() diff --git a/code/game/machinery/supplybeacon.dm b/code/game/machinery/supplybeacon.dm index b4e90fdac91cc..806d04e3e4e3e 100644 --- a/code/game/machinery/supplybeacon.dm +++ b/code/game/machinery/supplybeacon.dm @@ -76,7 +76,7 @@ if(surplus() < 500) if(user) to_chat(user, SPAN_NOTICE("The connected wire doesn't have enough current.")) return - set_light(1, 0.5, 2, 2, "#00ccaa") + set_light(3, 3, "#00ccaa") icon_state = "beacon_active" update_use_power(POWER_USE_IDLE) admin_attacker_log(user, "has activated \a [src] at [get_area(src)]") diff --git a/code/game/machinery/teleporter/pad.dm b/code/game/machinery/teleporter/pad.dm index c8044cbe88ec9..27ea156fc52df 100644 --- a/code/game/machinery/teleporter/pad.dm +++ b/code/game/machinery/teleporter/pad.dm @@ -81,7 +81,7 @@ I.plane = EFFECTS_ABOVE_LIGHTING_PLANE I.layer = ABOVE_LIGHTING_LAYER AddOverlays(I) - set_light(0.4, 1.2, 4, 10) + set_light(4, 0.4) if (interference && prob(20)) visible_message(SPAN_WARNING("The teleporter sparks ominously!")) diff --git a/code/game/machinery/turret_control.dm b/code/game/machinery/turret_control.dm index f703976b19e7c..bdaeadccc00ad 100644 --- a/code/game/machinery/turret_control.dm +++ b/code/game/machinery/turret_control.dm @@ -199,13 +199,13 @@ else if (enabled) if (lethal) icon_state = "control_kill" - set_light(1, 0.5, 2, 2, "#990000") + set_light(1.5, 1,"#990000") else icon_state = "control_stun" - set_light(1, 0.5, 2, 2, "#ff9900") + set_light(1.5, 1,"#ff9900") else icon_state = "control_standby" - set_light(1, 0.5, 2, 2, "#003300") + set_light(1.5, 1,"#003300") /obj/machinery/turretid/emp_act(severity) if(enabled) diff --git a/code/game/machinery/vending/_vending.dm b/code/game/machinery/vending/_vending.dm index 77a8554ebda67..295352e7335aa 100644 --- a/code/game/machinery/vending/_vending.dm +++ b/code/game/machinery/vending/_vending.dm @@ -91,7 +91,6 @@ var/scan_id = TRUE var/obj/item/material/coin/coin var/light_max_bright_on = 0.2 - var/light_inner_range_on = 1 var/light_outer_range_on = 2 @@ -148,7 +147,7 @@ if (!is_powered() || MACHINE_IS_BROKEN(src)) set_light(0) else - set_light(light_max_bright_on, light_inner_range_on, light_outer_range_on, 2, light_color) + set_light(light_outer_range_on, light_max_bright_on, light_color) /obj/machinery/vending/on_update_icon() diff --git a/code/game/objects/auras/radiant_aura.dm b/code/game/objects/auras/radiant_aura.dm index fb62c8646758d..9b5a4a68a68b9 100644 --- a/code/game/objects/auras/radiant_aura.dm +++ b/code/game/objects/auras/radiant_aura.dm @@ -7,7 +7,7 @@ /obj/aura/radiant_aura/added_to(mob/living/L) ..() to_chat(L,SPAN_NOTICE("A bubble of light appears around you, exuding protection and warmth.")) - set_light(0.6, 1, 6, 2, "#e09d37") + set_light(6, 6, "#e09d37") /obj/aura/radiant_aura/removed() to_chat(user, SPAN_WARNING("Your protective aura dissipates, leaving you feeling cold and unsafe.")) diff --git a/code/game/objects/effects/decals/Cleanable/misc.dm b/code/game/objects/effects/decals/Cleanable/misc.dm index 33c4dc193b1c7..6e8b2043b2bba 100644 --- a/code/game/objects/effects/decals/Cleanable/misc.dm +++ b/code/game/objects/effects/decals/Cleanable/misc.dm @@ -44,7 +44,7 @@ name = "glowing goo" desc = "Jeez. I hope that's not for lunch." gender = PLURAL - light_outer_range = 1 + light_range = 1 icon = 'icons/effects/effects.dmi' icon_state = "greenglow" persistent = TRUE diff --git a/code/game/objects/effects/effect_system.dm b/code/game/objects/effects/effect_system.dm index b74dc451a7e9c..6ab22462b7b18 100644 --- a/code/game/objects/effects/effect_system.dm +++ b/code/game/objects/effects/effect_system.dm @@ -240,7 +240,7 @@ would spawn and follow the beaker, even if it is carried or thrown. icon_state = "sparks" /obj/effect/effect/smoke/illumination/New(newloc, lifetime=10, range=null, power=null, color=null) - set_light(power, 0.1, range, 2, color) + set_light(range, power, color) time_to_live=lifetime ..() diff --git a/code/game/objects/effects/fake_fire.dm b/code/game/objects/effects/fake_fire.dm index ad6f2ded48d57..4dfabc3bdd78a 100644 --- a/code/game/objects/effects/fake_fire.dm +++ b/code/game/objects/effects/fake_fire.dm @@ -11,7 +11,7 @@ /obj/effect/fake_fire/New() ..() - set_light(0.5, 1, 3, l_color = color) + set_light(3, 0.5, color) START_PROCESSING(SSobj,src) if(lifetime) QDEL_IN(src,lifetime) diff --git a/code/game/objects/effects/fire/fire.dm b/code/game/objects/effects/fire/fire.dm index da455a9545c3e..1e5511b4fc228 100644 --- a/code/game/objects/effects/fire/fire.dm +++ b/code/game/objects/effects/fire/fire.dm @@ -197,16 +197,16 @@ switch(current_fire_state) if(TURF_FIRE_STATE_SMALL) icon_state = "small" - set_light(0.5, 1, 1.5) + set_light(1.5, 0.5) if(TURF_FIRE_STATE_MEDIUM) icon_state = "medium" - set_light(0.5, 1, 2,) + set_light(2, 0.5) if(TURF_FIRE_STATE_LARGE) icon_state = "big" - set_light(0.5, 1.5, 2,) + set_light(2, 0.6) if(TURF_FIRE_STATE_MAX) icon_state = "max" - set_light(0.7, 1.6, 3) + set_light(3, 0.7) #undef TURF_FIRE_REQUIRED_TEMP #undef TURF_FIRE_TEMP_BASE diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm index 1ec7f3cb3394f..b87544ada4656 100644 --- a/code/game/objects/items/devices/flashlight.dm +++ b/code/game/objects/items/devices/flashlight.dm @@ -19,11 +19,13 @@ action_button_name = "Toggle Flashlight" var/on = FALSE var/activation_sound = 'sound/effects/flashlight.ogg' - var/flashlight_max_bright = 0.5 //brightness of light when on, must be no greater than 1. - var/flashlight_inner_range = 1 //inner range of light when on, can be negative - var/flashlight_outer_range = 3 //outer range of light when on, can be negative + var/flashlight_power = 1 //brightness of light when on + var/flashlight_range = 4 //outer range of light when on, can be negative + light_wedge = LIGHT_VERY_WIDE var/flashlight_flags = EMPTY_BITFIELD // FLASHLIGHT_ bitflags + var/spawn_dir // a way for mappers to force which way a flashlight faces upon spawning + /obj/item/device/flashlight/Initialize() . = ..() @@ -61,11 +63,34 @@ return 1 /obj/item/device/flashlight/proc/set_flashlight() + if(light_wedge) + set_dir(pick(NORTH, SOUTH, EAST, WEST)) + if(spawn_dir) + set_dir(spawn_dir) + if (on) - set_light(flashlight_max_bright, flashlight_inner_range, flashlight_outer_range, 2, light_color) + set_light(flashlight_range, flashlight_power, light_color) else set_light(0) +/obj/item/device/flashlight/examine(mob/user, distance) + . = ..() + if(light_wedge && isturf(loc)) + to_chat(user, FONT_SMALL(SPAN_NOTICE("\The [src] is facing [dir2text(dir)]."))) + +/obj/item/device/flashlight/dropped(mob/user) + . = ..() + if(light_wedge) + set_dir(user.dir) + update_light() + +/obj/item/device/flashlight/throw_at() + . = ..() + if(light_wedge) + var/new_dir = pick(NORTH, SOUTH, EAST, WEST) + set_dir(new_dir) + update_light() + /obj/item/device/flashlight/attack(mob/living/M as mob, mob/living/user as mob) . = FALSE if (istype(M) && on && user.zone_sel.selecting == BP_EYES) @@ -138,8 +163,8 @@ desc = "An energy efficient flashlight." icon_state = "biglight" item_state = "biglight" - flashlight_max_bright = 0.75 - flashlight_outer_range = 4 + flashlight_power = 3 + flashlight_range = 6 /obj/item/device/flashlight/flashdark name = "flashdark" @@ -147,9 +172,8 @@ icon_state = "flashdark" item_state = "flashdark" w_class = ITEM_SIZE_NORMAL - flashlight_max_bright = -1 - flashlight_outer_range = 4 - flashlight_inner_range = 1 + flashlight_power = -6 + flashlight_range = 6 flashlight_flags = FLASHLIGHT_CANNOT_BLIND /obj/item/device/flashlight/pen @@ -160,9 +184,8 @@ obj_flags = OBJ_FLAG_CONDUCTIBLE slot_flags = SLOT_EARS w_class = ITEM_SIZE_TINY - flashlight_max_bright = 0.25 - flashlight_inner_range = 0.1 - flashlight_outer_range = 2 + light_wedge = LIGHT_OMNI + flashlight_range = 2 /obj/item/device/flashlight/maglight name = "maglight" @@ -174,8 +197,8 @@ attack_verb = list ("smacked", "thwacked", "thunked") matter = list(MATERIAL_ALUMINIUM = 200, MATERIAL_GLASS = 50) hitsound = "swing_hit" - flashlight_max_bright = 0.5 - flashlight_outer_range = 5 + light_wedge = LIGHT_NARROW + flashlight_range = 8 /******************************Lantern*******************************/ /obj/item/device/flashlight/lantern @@ -189,7 +212,10 @@ obj_flags = OBJ_FLAG_CONDUCTIBLE slot_flags = SLOT_BELT matter = list(MATERIAL_STEEL = 200,MATERIAL_GLASS = 100) - flashlight_outer_range = 5 + flashlight_range = 3 + light_wedge = LIGHT_OMNI + light_color = COLOR_ORANGE + flashlight_power = 1 /obj/item/device/flashlight/lantern/on_update_icon() ..() @@ -207,9 +233,7 @@ item_state = "" obj_flags = OBJ_FLAG_CONDUCTIBLE w_class = ITEM_SIZE_TINY - flashlight_max_bright = 0.25 - flashlight_inner_range = 0.1 - flashlight_outer_range = 2 + flashlight_range = 2 flashlight_flags = FLASHLIGHT_CANNOT_BLIND @@ -222,9 +246,8 @@ item_state = "lamp" w_class = ITEM_SIZE_LARGE obj_flags = OBJ_FLAG_CONDUCTIBLE - flashlight_max_bright = 0.3 - flashlight_inner_range = 2 - flashlight_outer_range = 5 + flashlight_range = 5 + light_wedge = LIGHT_OMNI on = 1 @@ -260,9 +283,9 @@ activation_sound = 'sound/effects/flare.ogg' flashlight_flags = FLASHLIGHT_SINGLE_USE - flashlight_max_bright = 0.8 - flashlight_inner_range = 0.1 - flashlight_outer_range = 5 + flashlight_power = 3 + flashlight_range = 5 + light_wedge = LIGHT_OMNI /obj/item/device/flashlight/flare/Initialize() . = ..() @@ -336,9 +359,9 @@ produce_heat = 0 activation_sound = 'sound/effects/glowstick.ogg' - flashlight_max_bright = 0.6 - flashlight_inner_range = 0.1 - flashlight_outer_range = 3 + flashlight_range = 4 + flashlight_power = 1.5 + light_wedge = LIGHT_OMNI flashlight_flags = FLASHLIGHT_SINGLE_USE | FLASHLIGHT_CANNOT_BLIND @@ -407,9 +430,9 @@ on = TRUE //Bio-luminesence has one setting, on. flashlight_flags = FLASHLIGHT_ALWAYS_ON | FLASHLIGHT_CANNOT_BLIND - flashlight_max_bright = 1 - flashlight_inner_range = 0.1 - flashlight_outer_range = 5 + flashlight_power = 0.8 + flashlight_range = 5 + light_wedge = LIGHT_OMNI /obj/item/device/flashlight/slime/New() ..() @@ -426,9 +449,9 @@ w_class = ITEM_SIZE_LARGE obj_flags = OBJ_FLAG_CONDUCTIBLE | OBJ_FLAG_ROTATABLE - flashlight_max_bright = 0.8 - flashlight_inner_range = 1 - flashlight_outer_range = 5 + flashlight_power = 1 + flashlight_range = 7 + light_wedge = LIGHT_WIDE /obj/item/device/flashlight/lamp/floodlamp/green icon_state = "greenfloodlamp" @@ -440,7 +463,7 @@ icon_state = "lavalamp" on = 0 action_button_name = "Toggle lamp" - flashlight_outer_range = 3 //range of light when on + flashlight_range = 3 //range of light when on matter = list(MATERIAL_ALUMINIUM = 250, MATERIAL_GLASS = 200) flashlight_flags = FLASHLIGHT_CANNOT_BLIND diff --git a/code/game/objects/items/devices/oxycandle.dm b/code/game/objects/items/devices/oxycandle.dm index 72705d5b6373a..174df6eca7708 100644 --- a/code/game/objects/items/devices/oxycandle.dm +++ b/code/game/objects/items/devices/oxycandle.dm @@ -11,8 +11,8 @@ var/on = 0 var/activation_sound = 'sound/effects/flare.ogg' light_color = "#e58775" - light_outer_range = 2 - light_max_bright = 1 + light_range = 2 + light_power = 1 var/brightness_on = 1 // Moderate-low bright. action_button_name = null diff --git a/code/game/objects/items/devices/powersink.dm b/code/game/objects/items/devices/powersink.dm index ba96d722b9f4e..2afe8bf32716a 100644 --- a/code/game/objects/items/devices/powersink.dm +++ b/code/game/objects/items/devices/powersink.dm @@ -131,7 +131,7 @@ var/datum/powernet/PN = attached.powernet var/drained = 0 if(PN) - set_light(0.5, 0.1, 12) + set_light(12) PN.trigger_warning() // found a powernet, so drain up to max power from it drained = PN.draw_power(drain_rate) diff --git a/code/game/objects/items/devices/slide_projector.dm b/code/game/objects/items/devices/slide_projector.dm index ab69838fd8e1d..9191355377619 100644 --- a/code/game/objects/items/devices/slide_projector.dm +++ b/code/game/objects/items/devices/slide_projector.dm @@ -93,7 +93,7 @@ projection = new projection_type(target) projection.set_source(current_slide) GLOB.moved_event.register(src, src, .proc/check_projections) - set_light(0.1, 0.1, 1, 2, COLOR_WHITE) //Bit of light + set_light(1, 0.1, COLOR_WHITE) //Bit of light update_icon() /obj/item/storage/slide_projector/attack_self(mob/user) @@ -155,7 +155,7 @@ /obj/effect/projection/Initialize() . = ..() - set_light(0.1, 0.1, 1, 2, COLOR_WHITE) //Makes turning off the lights not invalidate projection + set_light(1, 0.1, COLOR_WHITE) //Makes turning off the lights not invalidate projection /obj/effect/projection/on_update_icon() filters = filter(type="drop_shadow", color = COLOR_WHITE, size = 4, offset = 1,x = 0, y = 0) diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index c68602b4db467..9b64e41714689 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -889,7 +889,7 @@ attack_verb = list("attacked", "whacked", "jabbed", "poked", "marshalled") /obj/item/marshalling_wand/Initialize() - set_light(0.6, 0.5, 2, 2, "#ff0000") + set_light(1.5, 1.5, "#ff0000") return ..() /obj/item/marshalling_wand/attack_self(mob/living/user as mob) diff --git a/code/game/objects/items/weapons/candle/candle.dm b/code/game/objects/items/weapons/candle/candle.dm index d7f1c22b76b41..4b83b86b5e982 100644 --- a/code/game/objects/items/weapons/candle/candle.dm +++ b/code/game/objects/items/weapons/candle/candle.dm @@ -11,10 +11,8 @@ var/wax var/last_lit var/icon_set = "candle" - var/candle_max_bright = 0.3 - var/candle_inner_range = 0.1 - var/candle_outer_range = 4 - var/candle_falloff = 2 + var/candle_range = CANDLE_LUM + var/candle_power /obj/item/flame/candle/Initialize() wax = rand(27 MINUTES, 33 MINUTES) / SSobj.wait // Enough for 27-33 minutes. 30 minutes on average, adjusted for subsystem tickrate. @@ -52,7 +50,7 @@ if(!lit) lit = 1 visible_message(SPAN_NOTICE("\The [user] lights the [name].")) - set_light(candle_max_bright, candle_inner_range, candle_outer_range, candle_falloff) + set_light(candle_range, candle_power) START_PROCESSING(SSobj, src) /obj/item/flame/candle/Process() diff --git a/code/game/objects/items/weapons/candle/incense.dm b/code/game/objects/items/weapons/candle/incense.dm index 6c7246377cbd1..75eb59076e8af 100644 --- a/code/game/objects/items/weapons/candle/incense.dm +++ b/code/game/objects/items/weapons/candle/incense.dm @@ -5,10 +5,7 @@ available_colours = null icon_set = "incense" - candle_max_bright = 0.1 - candle_inner_range = 0.1 - candle_outer_range = 1 - candle_falloff = 2 + candle_range = 1 scent_types = list(/singleton/scent_type/rose, /singleton/scent_type/citrus, diff --git a/code/game/objects/items/weapons/ecigs.dm b/code/game/objects/items/weapons/ecigs.dm index dd47f843c66ea..b44380dd2d7c1 100644 --- a/code/game/objects/items/weapons/ecigs.dm +++ b/code/game/objects/items/weapons/ecigs.dm @@ -131,7 +131,7 @@ if (active) item_state = icon_on icon_state = icon_on - set_light(0.6, 0.5, brightness_on) + set_light(brightness_on) else if (ec_cartridge) set_light(0) item_state = icon_off diff --git a/code/game/objects/items/weapons/flamethrower.dm b/code/game/objects/items/weapons/flamethrower.dm index e93d54d22dff2..f636b6dd3a8ae 100644 --- a/code/game/objects/items/weapons/flamethrower.dm +++ b/code/game/objects/items/weapons/flamethrower.dm @@ -140,7 +140,7 @@ playsound(loc, 'sound/items/welderdeactivate.ogg', 50, TRUE) STOP_PROCESSING(SSobj,src) if(lit) - set_light(0.7, 1, 2.5, l_color = COLOR_ORANGE) + set_light(2.5, 0.7, l_color = COLOR_ORANGE) else set_light(0) diff --git a/code/game/objects/items/weapons/grenades/anti_photon_grenade.dm b/code/game/objects/items/weapons/grenades/anti_photon_grenade.dm index d428534046f34..dc385553ee0d2 100644 --- a/code/game/objects/items/weapons/grenades/anti_photon_grenade.dm +++ b/code/game/objects/items/weapons/grenades/anti_photon_grenade.dm @@ -8,11 +8,11 @@ /obj/item/grenade/anti_photon/detonate(mob/living/user) playsound(src.loc, 'sound/effects/phasein.ogg', 50, 1, 5) - set_light(-1, 6, 10, 2, "#ffffff") + set_light(10, -10, "#ffffff") addtimer(new Callback(src, .proc/finish), rand(20 SECONDS, 29 SECONDS)) /obj/item/grenade/anti_photon/proc/finish() - set_light(1, 1, 10, 2, "#[num2hex(rand(64,255))][num2hex(rand(64,255))][num2hex(rand(64,255))]") + set_light(10, 10, "#[num2hex(rand(64,255))][num2hex(rand(64,255))][num2hex(rand(64,255))]") playsound(loc, 'sound/effects/bang.ogg', 50, 1, 5) sleep(1 SECOND) qdel(src) diff --git a/code/game/objects/items/weapons/grenades/light.dm b/code/game/objects/items/weapons/grenades/light.dm index f6c947468e1a1..08b8568ab62b4 100644 --- a/code/game/objects/items/weapons/grenades/light.dm +++ b/code/game/objects/items/weapons/grenades/light.dm @@ -12,5 +12,5 @@ playsound(src, 'sound/effects/snap.ogg', 80, 1) audible_message(SPAN_WARNING("\The [src] detonates with a sharp crack!")) - set_light(1, 1, 12, 2, light_colour) + set_light(12, 1, light_colour) QDEL_IN(src, lifetime) diff --git a/code/game/objects/items/weapons/lighter.dm b/code/game/objects/items/weapons/lighter.dm index ed78d113f49a1..8d2d56bc003c3 100644 --- a/code/game/objects/items/weapons/lighter.dm +++ b/code/game/objects/items/weapons/lighter.dm @@ -30,7 +30,7 @@ lit = 1 update_icon() light_effects(user) - set_light(0.6, 0.5, 2, l_color = COLOR_PALE_ORANGE) + set_light(2, l_color = COLOR_PALE_ORANGE) START_PROCESSING(SSobj, src) /obj/item/flame/lighter/proc/light_effects(mob/living/carbon/user) @@ -95,7 +95,7 @@ if(ismob(loc) && prob(10) && reagents.get_reagent_amount(/datum/reagent/fuel) < 1) to_chat(loc, SPAN_WARNING("\The [src]'s flame flickers.")) set_light(0) - addtimer(new Callback(src, .atom/proc/set_light, 0.6, 0.5, 2), 4) + addtimer(new Callback(src, .atom/proc/set_light, 2), 4) reagents.remove_reagent(/datum/reagent/fuel, 0.05) else extinguish() diff --git a/code/game/objects/items/weapons/melee/energy.dm b/code/game/objects/items/weapons/melee/energy.dm index 5c37dc6390848..c5e640901247a 100644 --- a/code/game/objects/items/weapons/melee/energy.dm +++ b/code/game/objects/items/weapons/melee/energy.dm @@ -56,7 +56,7 @@ if (user) playsound(user, 'sound/weapons/saberon.ogg', 50, 1) to_chat(user, SPAN_NOTICE("\The [src] is now energised.")) - set_light(0.8, 1, 2, 4, lighting_color) + set_light(2, 0.8, lighting_color) if (istype(user,/mob/living/carbon/human)) var/mob/living/carbon/human/H = user diff --git a/code/game/objects/items/weapons/shields.dm b/code/game/objects/items/weapons/shields.dm index 5e12c02dd694b..659b31bc11eaf 100644 --- a/code/game/objects/items/weapons/shields.dm +++ b/code/game/objects/items/weapons/shields.dm @@ -185,7 +185,7 @@ /obj/item/shield/energy/on_update_icon() icon_state = "eshield[active]" if (active) - set_light(0.6, 0.1, 2, 1, "#006aff") + set_light(1.5, 1.5, "#006aff") else set_light(0) diff --git a/code/game/objects/items/weapons/stunbaton.dm b/code/game/objects/items/weapons/stunbaton.dm index 9dd976773a32a..7e688821c1e8a 100644 --- a/code/game/objects/items/weapons/stunbaton.dm +++ b/code/game/objects/items/weapons/stunbaton.dm @@ -63,7 +63,7 @@ icon_state = "[initial(name)]" if(icon_state == "[initial(name)]_active") - set_light(0.4, 0.1, 1, 2, "#ff6a00") + set_light(1.5, 2, "#ff6a00") else set_light(0) @@ -241,7 +241,7 @@ /obj/item/melee/baton/robot/electrified_arm/on_update_icon() if(status) icon_state = "electrified_arm_active" - set_light(0.4, 0.1, 1, 2, "#006aff") + set_light(1.5, 2, "#006aff") else icon_state = "electrified_arm" set_light(0) diff --git a/code/game/objects/items/weapons/tools/weldingtool.dm b/code/game/objects/items/weapons/tools/weldingtool.dm index 48a66a1674c5e..4e3d3d0f39ef7 100644 --- a/code/game/objects/items/weapons/tools/weldingtool.dm +++ b/code/game/objects/items/weapons/tools/weldingtool.dm @@ -179,7 +179,7 @@ burn_fuel(amount) if(M) M.welding_eyecheck()//located in mob_helpers.dm - set_light(0.7, 2, 5, l_color = COLOR_LIGHT_CYAN) + set_light(5, 0.7, COLOR_LIGHT_CYAN) addtimer(new Callback(src, /atom/proc/update_icon), 5) return 1 @@ -222,7 +222,7 @@ AddOverlays(image('icons/obj/tools/welder.dmi', "welder_[tank.icon_state]")) if(welding) AddOverlays(image('icons/obj/tools/welder.dmi', "welder_on")) - set_light(0.6, 0.5, 2.5, l_color =COLOR_PALE_ORANGE) + set_light(2.5, 0.6, l_color =COLOR_PALE_ORANGE) else set_light(0) item_state = welding ? "welder1" : "welder" diff --git a/code/game/objects/structures/flora.dm b/code/game/objects/structures/flora.dm index 1c9484231b39c..919f6b743e8e1 100644 --- a/code/game/objects/structures/flora.dm +++ b/code/game/objects/structures/flora.dm @@ -244,7 +244,7 @@ /obj/structure/flora/pottedplant/unusual/Initialize() . = ..() - set_light(0.4, 0.1, 2, 2, "#007fff") + set_light(2, 0.4, "#007fff") /obj/structure/flora/pottedplant/orientaltree name = "potted oriental tree" @@ -298,7 +298,7 @@ /obj/structure/flora/pottedplant/subterranean/Initialize() . = ..() - set_light(0.4, 0.1, 2, 2, "#ff6633") + set_light(2, 0.4, "#ff6633") /obj/structure/flora/pottedplant/minitree name = "potted tree" diff --git a/code/game/objects/structures/fountain.dm b/code/game/objects/structures/fountain.dm index 9e6285f326c7a..69df5092771b0 100644 --- a/code/game/objects/structures/fountain.dm +++ b/code/game/objects/structures/fountain.dm @@ -19,7 +19,7 @@ /obj/structure/fountain/strange/Initialize() . = ..() light_color = get_random_colour(lower = 190) - set_light(0.6, 3, 5, 2, light_color) + set_light(5, 0.6, light_color) /obj/structure/fountain/strange/attack_hand(mob/living/user) diff --git a/code/game/objects/structures/seaweed.dm b/code/game/objects/structures/seaweed.dm index d3bc8722ffc64..8853a7b5ce01a 100644 --- a/code/game/objects/structures/seaweed.dm +++ b/code/game/objects/structures/seaweed.dm @@ -20,11 +20,11 @@ /obj/structure/flora/seaweed/glow/Initialize() . = ..() - set_light(0.6, 0.1, 4, 3, "#00fff4") + set_light(4, 0.6, "#00fff4") icon_state = "glowweed[rand(1,3)]" /obj/effect/decal/cleanable/lichen name = "lichen" desc = "Damp and mossy plant life." icon_state = "lichen" - icon = 'icons/obj/structures/plants.dmi' \ No newline at end of file + icon = 'icons/obj/structures/plants.dmi' diff --git a/code/game/turfs/flooring/flooring_premade.dm b/code/game/turfs/flooring/flooring_premade.dm index 2c8ce0cf64573..0a0c8c7c5ac51 100644 --- a/code/game/turfs/flooring/flooring_premade.dm +++ b/code/game/turfs/flooring/flooring_premade.dm @@ -10,8 +10,8 @@ icon = 'icons/turf/flooring/circuit.dmi' icon_state = "bcircuit" initial_flooring = /singleton/flooring/reinforced/circuit - light_outer_range = 2 - light_max_bright = 1 + light_range = 2 + light_power = 1 light_color = COLOR_BLUE /turf/simulated/floor/greengrid @@ -19,8 +19,8 @@ icon = 'icons/turf/flooring/circuit.dmi' icon_state = "gcircuit" initial_flooring = /singleton/flooring/reinforced/circuit/green - light_outer_range = 2 - light_max_bright = 3 + light_range = 2 + light_power = 3 light_color = COLOR_GREEN /turf/simulated/floor/redgrid @@ -28,8 +28,8 @@ icon = 'icons/turf/flooring/circuit.dmi' icon_state = "rcircuit" initial_flooring = /singleton/flooring/reinforced/circuit/red - light_outer_range = 2 - light_max_bright = 2 + light_range = 2 + light_power = 2 light_color = COLOR_RED /turf/simulated/floor/selfestructgrid @@ -37,8 +37,8 @@ icon = 'icons/turf/flooring/circuit.dmi' icon_state = "rcircuit_off" initial_flooring = /singleton/flooring/reinforced/circuit/selfdestruct - light_outer_range = 2 - light_max_bright = 2 + light_range = 2 + light_power = 2 light_color = COLOR_BLACK /turf/simulated/floor/wood diff --git a/code/game/turfs/simulated/wall_attacks.dm b/code/game/turfs/simulated/wall_attacks.dm index bfa606fd3f5b0..6a7401b9ea926 100644 --- a/code/game/turfs/simulated/wall_attacks.dm +++ b/code/game/turfs/simulated/wall_attacks.dm @@ -29,7 +29,7 @@ update_icon() update_air() sleep(15) - set_light(0.4, 0.1, 1) + set_light(1, 0.4) src.blocks_air = 1 set_opacity(1) for(var/turf/simulated/turf in loc) diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm index 885c13f82d01a..016e0d2e26ad0 100644 --- a/code/game/turfs/space/space.dm +++ b/code/game/turfs/space/space.dm @@ -11,6 +11,7 @@ turf_flags = TURF_DISALLOW_BLOB z_eventually_space = TRUE + var/starlit = FALSE /turf/space/Initialize() . = ..() @@ -32,6 +33,7 @@ return INITIALIZE_HINT_LATELOAD // oh no! we need to switch to being a different kind of turf! /turf/space/Destroy() + remove_starlight() // Cleanup cached z_eventually_space values above us. if (above) var/turf/T = src @@ -53,13 +55,26 @@ /turf/space/is_solid_structure() return locate(/obj/structure/lattice, src) || locate(/obj/structure/catwalk, src) //counts as solid structure if it has a lattice or catwalk +/turf/space/proc/remove_starlight() + if(starlit) + replace_ambient_light(SSskybox.background_color, null, config.starlight, 0) + starlit = FALSE + /turf/space/proc/update_starlight() if(!config.starlight) return - if(locate(/turf/simulated) in orange(src,1)) //Let's make sure not to break everything if people use a crazy setting. - set_light(min(0.1*config.starlight, 1), 1, 3, l_color = SSskybox.background_color) - else - set_light(0) + + //We only need starlight on turfs adjacent to dynamically lit turfs, for example space near bulkhead + for (var/turf/T in RANGE_TURFS(src, 1)) + if (!isloc(T.loc) || !TURF_IS_DYNAMICALLY_LIT_UNSAFE(T)) + continue + + add_ambient_light(SSskybox.background_color, config.starlight) + starlit = TRUE + return + + if(TURF_IS_AMBIENT_LIT_UNSAFE(src)) + remove_starlight() /turf/space/attackby(obj/item/C as obj, mob/user as mob) diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index ffd975cddea93..9aee9a3e69669 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -52,7 +52,14 @@ else luminosity = 1 - RecalculateOpacity() + if (light_power && light_range) + update_light() + + if (!mapload || (!istype(src, /turf/space) && is_outside())) + SSambient_lighting.queued += src + + if (opacity) + has_opaque_atom = TRUE if (mapload && permit_ao) queue_ao() @@ -431,3 +438,16 @@ var/global/const/enterloopsanity = 100 /turf/proc/IgniteTurf(power, fire_colour) return + +//Maybe we want to make this stateful at some point +/turf/proc/is_outside() + + //For the purposes of light, dense turfs should not be considered to be outside + if(density) + return FALSE + + var/area/A = get_area(src) + if(A.area_flags & AREA_FLAG_EXTERNAL) + return TRUE + + //TODO: CitRP has some concept of outside based on turfs above. We don't really have any use cases right now, revisit this function if this changes diff --git a/code/game/turfs/turf_changing.dm b/code/game/turfs/turf_changing.dm index 2cb0c880a4431..14226552f07a8 100644 --- a/code/game/turfs/turf_changing.dm +++ b/code/game/turfs/turf_changing.dm @@ -35,13 +35,14 @@ var/old_hotspot = hotspot var/old_turf_fire = null var/old_opacity = opacity - var/old_dynamic_lighting = dynamic_lighting + var/old_dynamic_lighting = TURF_IS_DYNAMICALLY_LIT_UNSAFE(src) var/old_affecting_lights = affecting_lights var/old_lighting_overlay = lighting_overlay var/old_corners = corners var/old_ao_neighbors = ao_neighbors var/old_above = above var/old_permit_ao = permit_ao + var/old_zflags = z_flags if(isspaceturf(N) || isopenspace(N)) QDEL_NULL(turf_fire) @@ -63,18 +64,16 @@ var/turf/simulated/S = src if(S.zone) S.zone.rebuild() + if(ambient_bitflag) //Should remove everything about current bitflag, let it be recalculated by SS later + SSambient_lighting.clean_turf(src) + // Run the Destroy() chain. qdel(src) - - var/old_opaque_counter = opaque_counter var/turf/simulated/W = new N(src) if (permit_ao) regenerate_ao() - W.opaque_counter = old_opaque_counter - W.RecalculateOpacity() - if (keep_air) W.air = old_air @@ -101,18 +100,28 @@ . = W W.ao_neighbors = old_ao_neighbors - if(lighting_overlays_initialised) + // lighting stuff + + if(SSlighting.initialized) + recalc_atom_opacity() lighting_overlay = old_lighting_overlay affecting_lights = old_affecting_lights corners = old_corners - if((old_opacity != opacity) || (dynamic_lighting != old_dynamic_lighting)) + if (old_opacity != opacity || dynamic_lighting != old_dynamic_lighting || force_lighting_update) reconsider_lights() - if(dynamic_lighting != old_dynamic_lighting) - if(dynamic_lighting) + updateVisibility(src) + + if (dynamic_lighting != old_dynamic_lighting) + if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(src)) lighting_build_overlay() else lighting_clear_overlay() + W.setup_local_ambient() + if(z_flags != old_zflags) + W.rebuild_zbleed() + // end of lighting stuff + for(var/turf/T in RANGE_TURFS(src, 1)) T.update_icon() diff --git a/code/modules/ZAS/Fire.dm b/code/modules/ZAS/Fire.dm index cfe59cf749212..3141430ade5fa 100644 --- a/code/modules/ZAS/Fire.dm +++ b/code/modules/ZAS/Fire.dm @@ -112,13 +112,13 @@ If it gains pressure too slowly, it may leak or just rupture instead of explodin if(firelevel > 6) icon_state = "3" - set_light(1, 2, 7) + set_light(7, 1) else if(firelevel > 2.5) icon_state = "2" - set_light(0.7, 2, 5) + set_light(5, 0.7) else icon_state = "1" - set_light(0.5, 1, 3) + set_light(3, 0.5) for(var/mob/living/L in loc) L.FireBurn(firelevel, air_contents.temperature, air_contents.return_pressure()) //Burn the mobs! @@ -168,7 +168,7 @@ If it gains pressure too slowly, it may leak or just rupture instead of explodin var/datum/gas_mixture/air_contents = loc.return_air() color = fire_color(air_contents.temperature) - set_light(0.5, 1, 3, l_color = color) + set_light(3, 0.5, l_color = color) firelevel = fl SSair.active_hotspots.Add(src) diff --git a/code/modules/admin/buildmode/light_maker.dm b/code/modules/admin/buildmode/light_maker.dm index 552624cb87838..6af18301838c7 100644 --- a/code/modules/admin/buildmode/light_maker.dm +++ b/code/modules/admin/buildmode/light_maker.dm @@ -2,8 +2,8 @@ name = "Light Maker" icon_state = "buildmode8" - var/light_outer_range = 3 - var/light_max_bright = 3 + var/light_range = 3 + var/light_power = 3 var/light_color = COLOR_WHITE /datum/build_mode/light_maker/Help() @@ -17,14 +17,14 @@ var/choice = alert("Change the new light range, power, or color?", "Light Maker", "Range", "Power", "Color", "Cancel") switch(choice) if("Range") - var/input = input("New light range.", name, light_outer_range) as null|num + var/input = input("New light range.", name, light_range) as null|num if(input) - light_outer_range = input + light_range = input if("Power") - var/input = input("New light power, from 0.1 to 1 in decimal increments.", name, light_max_bright) as null|num + var/input = input("New light power, from 0.1 to 1 in decimal increments.", name, light_power) as null|num if(input) input = clamp(input, 0.1, 1) - light_max_bright = input + light_power = input if("Color") var/input = input("New light color.", name, light_color) as null|color if(input) @@ -33,7 +33,7 @@ /datum/build_mode/light_maker/OnClick(atom/A, list/parameters) if(parameters["left"]) if(A) - A.set_light(light_max_bright, 0.1, light_outer_range, l_color = light_color) + A.set_light(light_range, light_power, l_color = light_color) if(parameters["right"]) if(A) A.set_light(0, 0, 0, l_color = COLOR_WHITE) diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index b3a50edc990f1..19de2e0d3aa29 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -482,7 +482,7 @@ var/obj/effect/overmap/visitable/sector/exoplanet/new_planet = new exoplanet_type(null, world.maxx, world.maxy) new_planet.features_budget = budget new_planet.themes = list(new theme) - new_planet.lightlevel = rand(5, 10)/10 + new_planet.sun_brightness_modifier = rand(0.1, 0.6) log_and_message_admins("is spawning [new_planet] at [new_planet.start_x],[new_planet.start_y], containing Z [english_list(new_planet.map_z)]") new_planet.build_level() diff --git a/code/modules/admin/view_variables/vv_set_handlers.dm b/code/modules/admin/view_variables/vv_set_handlers.dm index 57e0faedab68f..8e68ebeb00ba8 100644 --- a/code/modules/admin/view_variables/vv_set_handlers.dm +++ b/code/modules/admin/view_variables/vv_set_handlers.dm @@ -118,7 +118,7 @@ /singleton/vv_set_handler/light_handler handled_type = /atom - handled_vars = list("light_max_bright","light_inner_range","light_outer_range","light_falloff_curve") + handled_vars = list("light_power","light_range") /singleton/vv_set_handler/light_handler/handle_set_var(atom/A, variable, var_value, client) var_value = text2num(var_value) @@ -126,12 +126,10 @@ return // More sanity checks - var/new_max = variable == "light_max_bright" ? var_value : A.light_max_bright - var/new_inner = variable == "light_inner_range" ? var_value : A.light_inner_range - var/new_outer = variable == "light_outer_range" ? var_value : A.light_outer_range - var/new_falloff = variable == "light_falloff_curve" ? var_value : A.light_falloff_curve + var/new_max = variable == "light_power" ? var_value : A.light_power + var/new_range = variable == "light_range" ? var_value : A.light_range - A.set_light(new_max, new_inner, new_outer, new_falloff) + A.set_light(new_range, new_max) /singleton/vv_set_handler/health_value_handler handled_type = /atom diff --git a/code/modules/ai/ai_holder_targeting.dm b/code/modules/ai/ai_holder_targeting.dm index 1115e4dcae8e2..7fdf701698144 100644 --- a/code/modules/ai/ai_holder_targeting.dm +++ b/code/modules/ai/ai_holder_targeting.dm @@ -26,7 +26,7 @@ /// Step 1, find out what we can see. /datum/ai_holder/proc/list_targets() . = ohearers(vision_range, holder) - . -= GLOB.dview_mob // Not the dview mob! + . -= global.dview_mob // Not the dview mob! var/static/hostile_machines = typecacheof(list(/obj/machinery/porta_turret, /mob/living/exosuit, /obj/effect/blob)) diff --git a/code/modules/blob/blob.dm b/code/modules/blob/blob.dm index 268caaf4920e7..6a3e9977347e8 100644 --- a/code/modules/blob/blob.dm +++ b/code/modules/blob/blob.dm @@ -10,7 +10,7 @@ desc = "A pulsating mass of interwoven tendrils." icon = 'icons/mob/blob.dmi' icon_state = "blob" - light_outer_range = 2 + light_range = 2 light_color = BLOB_COLOR_PULS density = TRUE opacity = 1 diff --git a/code/modules/clothing/_clothing.dm b/code/modules/clothing/_clothing.dm index 6dfad07833ae1..3d2b1cab59232 100644 --- a/code/modules/clothing/_clothing.dm +++ b/code/modules/clothing/_clothing.dm @@ -456,7 +456,7 @@ BLIND // can't see anything /obj/item/clothing/head/proc/update_flashlight(mob/user = null) if(on && !light_applied) - set_light(brightness_on, 1, 3) + set_light(3, brightness_on, angle = LIGHT_WIDE) light_applied = 1 else if(!on && light_applied) set_light(0) diff --git a/code/modules/clothing/glasses/glasses.dm b/code/modules/clothing/glasses/glasses.dm index 549a244c00942..157030d0bc7d1 100644 --- a/code/modules/clothing/glasses/glasses.dm +++ b/code/modules/clothing/glasses/glasses.dm @@ -173,7 +173,6 @@ darkness_view = 5 action_button_name = "Toggle Goggles" toggleable = TRUE - see_invisible = SEE_INVISIBLE_NOLIGHTING siemens_coefficient = 0.6 electric = TRUE diff --git a/code/modules/clothing/spacesuits/rig/modules/ninja.dm b/code/modules/clothing/spacesuits/rig/modules/ninja.dm index fa8857967191f..5fb896c503db9 100644 --- a/code/modules/clothing/spacesuits/rig/modules/ninja.dm +++ b/code/modules/clothing/spacesuits/rig/modules/ninja.dm @@ -220,7 +220,7 @@ holder.visible_message(SPAN_DANGER("\The [src.holder] emits a shrill tone!"),SPAN_DANGER(" You hear a shrill tone!")) sleep(blink_solid_time) src.blink_mode = 0 - src.holder.set_light(0, 0, 0, 2, "#000000") + src.holder.set_light(2, 0, "#000000") explosion(get_turf(src), explosion_radius, explosion_max_power) if(holder && holder.wearer) @@ -244,17 +244,17 @@ if(0) return if(1) - src.holder.set_light(1, 1, 8.5, 2, "#ff0a00") + src.holder.set_light(8.5, 1, "#ff0a00") sleep(6) - src.holder.set_light(0, 0, 0, 2, "#000000") + src.holder.set_light(0) spawn(6) .() if(2) - src.holder.set_light(1, 1, 8.5, 2, "#ff0a00") + src.holder.set_light(8.5, 1, "#ff0a00") sleep(2) - src.holder.set_light(0, 0, 0, 2, "#000000") + src.holder.set_light(0) spawn(2) .() if(3) - src.holder.set_light(1, 1, 8.5, 2, "#ff0a00") + src.holder.set_light(8.5, 1, "#ff0a00") /obj/item/rig_module/grenade_launcher/ninja suit_overlay = null diff --git a/code/modules/clothing/spacesuits/spacesuits.dm b/code/modules/clothing/spacesuits/spacesuits.dm index efb52b798ca84..9eab5276cf639 100644 --- a/code/modules/clothing/spacesuits/spacesuits.dm +++ b/code/modules/clothing/spacesuits/spacesuits.dm @@ -33,7 +33,7 @@ action_button_name = "Toggle Helmet Light" light_overlay = "helmet_light" - brightness_on = 0.5 + brightness_on = 1 on = 0 var/tinted = null //Set to non-null for toggleable tint helmets diff --git a/code/modules/detectivework/tools/rag.dm b/code/modules/detectivework/tools/rag.dm index 56383bbcfbccf..a95facf8eaae4 100644 --- a/code/modules/detectivework/tools/rag.dm +++ b/code/modules/detectivework/tools/rag.dm @@ -238,7 +238,7 @@ return START_PROCESSING(SSobj, src) - set_light(0.5, 0.1, 2, 2, "#e38f46") + set_light(2, 0.5, "#e38f46") on_fire = 1 update_name() update_icon() diff --git a/code/modules/detectivework/tools/uvlight.dm b/code/modules/detectivework/tools/uvlight.dm index 1d828ffc8df6b..1b90195421e0c 100644 --- a/code/modules/detectivework/tools/uvlight.dm +++ b/code/modules/detectivework/tools/uvlight.dm @@ -21,7 +21,7 @@ /obj/item/device/uv_light/attack_self(mob/user) on = !on if(on) - set_light(0.5, 0.1, range, 2, "#007fff") + set_light(range, 0.5, "#007fff") START_PROCESSING(SSobj, src) icon_state = "uv_on" else diff --git a/code/modules/holodeck/HolodeckObjects.dm b/code/modules/holodeck/HolodeckObjects.dm index 43509ea0cd995..f4982e39cdfaa 100644 --- a/code/modules/holodeck/HolodeckObjects.dm +++ b/code/modules/holodeck/HolodeckObjects.dm @@ -419,10 +419,9 @@ /mob/living/simple_animal/hostile/carp/holodeck/on_update_icon() return - /mob/living/simple_animal/hostile/carp/holodeck/Initialize(mapload, ...) . = ..() - set_light(0.5, 0.1, 2) //hologram lighting + set_light(2, 0.5) //hologram lighting /mob/living/simple_animal/hostile/carp/holodeck/proc/set_safety(safe) diff --git a/code/modules/holomap/ship_holomap.dm b/code/modules/holomap/ship_holomap.dm index a55b4f1cf64a0..0fc732082952d 100644 --- a/code/modules/holomap/ship_holomap.dm +++ b/code/modules/holomap/ship_holomap.dm @@ -160,7 +160,7 @@ set_light(0) else icon_state = "station_map" - set_light(0.8, 0.1, 2, 2, "#1dbe17") + set_light(2, 0.8, "#1dbe17") // Put the little "map" overlay down where it looks nice if(small_station_map) diff --git a/code/modules/hydroponics/seed.dm b/code/modules/hydroponics/seed.dm index 70d4d9811f8bb..fdef5fce2ffb2 100644 --- a/code/modules/hydroponics/seed.dm +++ b/code/modules/hydroponics/seed.dm @@ -204,7 +204,7 @@ var/clr if(get_trait(TRAIT_BIOLUM_COLOUR)) clr = get_trait(TRAIT_BIOLUM_COLOUR) - splat.set_light(0.5, 0.1, 3, l_color = clr) + splat.set_light(3, 0.5, l_color = clr) var/flesh_colour = get_trait(TRAIT_FLESH_COLOUR) if(!flesh_colour) flesh_colour = get_trait(TRAIT_PRODUCT_COLOUR) if(flesh_colour) splat.color = get_trait(TRAIT_PRODUCT_COLOUR) @@ -787,7 +787,7 @@ var/clr if(get_trait(TRAIT_BIOLUM_COLOUR)) clr = get_trait(TRAIT_BIOLUM_COLOUR) - product.set_light(0.5, 0.1, 3, l_color = clr) + product.set_light(3, 0.5, l_color = clr) //Handle spawning in living, mobile products (like dionaea). if(istype(product,/mob/living)) diff --git a/code/modules/hydroponics/spreading/spreading.dm b/code/modules/hydroponics/spreading/spreading.dm index 540bf2a8e8e4b..55cad3d73e222 100644 --- a/code/modules/hydroponics/spreading/spreading.dm +++ b/code/modules/hydroponics/spreading/spreading.dm @@ -157,7 +157,7 @@ // Apply colour and light from seed datum. if(seed.get_trait(TRAIT_BIOLUM)) - set_light(0.5, 0.1, 3, l_color = seed.get_trait(TRAIT_BIOLUM_COLOUR)) + set_light(3, 0.5, l_color = seed.get_trait(TRAIT_BIOLUM_COLOUR)) else set_light(0) diff --git a/code/modules/hydroponics/trays/tray_update_icons.dm b/code/modules/hydroponics/trays/tray_update_icons.dm index 1344d374a5749..60fb1f53159ea 100644 --- a/code/modules/hydroponics/trays/tray_update_icons.dm +++ b/code/modules/hydroponics/trays/tray_update_icons.dm @@ -71,7 +71,7 @@ // Update bioluminescence. if(seed && seed.get_trait(TRAIT_BIOLUM)) - set_light(0.5, 0.1, 3, l_color = seed.get_trait(TRAIT_BIOLUM_COLOUR)) + set_light(3, 0.5, l_color = seed.get_trait(TRAIT_BIOLUM_COLOUR)) else set_light(0) diff --git a/code/modules/integrated_electronics/subtypes/output.dm b/code/modules/integrated_electronics/subtypes/output.dm index d8b1e3e639552..e4b03e969f841 100644 --- a/code/modules/integrated_electronics/subtypes/output.dm +++ b/code/modules/integrated_electronics/subtypes/output.dm @@ -83,7 +83,7 @@ /obj/item/integrated_circuit/output/light/proc/update_lighting() if(light_toggled) if(assembly) - assembly.set_light(light_brightness, 1, 4, 2, light_rgb) + assembly.set_light(4, light_brightness, light_rgb) else if(assembly) assembly.set_light(0) diff --git a/code/modules/lighting/__lighting_docs.dm b/code/modules/lighting/__lighting_docs.dm index 3816595f3c2d0..a742e1a98adb5 100644 --- a/code/modules/lighting/__lighting_docs.dm +++ b/code/modules/lighting/__lighting_docs.dm @@ -1,67 +1,46 @@ /* -BS12 object based lighting system -*/ + -- O7 lighting system -- -/* -Changes from tg DAL: - - Lighting is done using objects instead of subareas. - - Animated transitions. (newer tg DAL has this) - - Full colours with mixing. - - Support for lights on shuttles. + Documentation is present in most of the code files. + lighting_atom.dm -> procs/vars for tracking/managing lights attached to objects. + lighting_turf.dm -> procs/vars for managing lighting overlays bound to turfs, tracking lights affecting said turf, and getting information about the turf's light level. + lighting_corner.dm -> contains code for tracking per-corner lighting data. + lighting_source.dm -> contains actual light emitter datum & core lighting calculations. Directional lights and Z-lights are implemented here. + lighting_area.dm -> contains area vars/procs for managing an area's dynamic lighting state. - - Code: - - Instead of one flat luminosity var, light is represented by 3 atom vars: - - light_range; diameter in tiles of the light, used for calculating falloff, Cannot be 1. - - light_power; multiplier for the brightness of lights, - - light_color; hex string representing the RGB colour of the light. - - setLuminousity() is now set_light() and takes the three variables above. - - Variables can be left as null to not update them. - - set_opacity() is now set_opacity(). - - Areas have luminosity set to 1 permanently, no hard-lighting. - - Objects inside other objects can have lights and they properly affect the turf. (flashlights) - - area/master and area/list/related have been eviscerated since subareas aren't needed. */ /* -Relevant vars/procs: - -atom: (lighting_atom.dm) - - var/light_range; range in tiles of the light, used for calculating falloff - - var/light_power; multiplier for the brightness of lights - - var/light_color; hex string representing the RGB colour of the light - - - var/datum/light_source/light; light source datum for this atom, only present if light_range && light_power - - var/list/light_sources; light sources in contents that are shining through this object, including this object - - - proc/set_light(l_max_bright, l_inner_range, l_outer_range, l_falloff_curve, l_color): - - Sets light_range/power/color to non-null args and calls update_light() - - proc/set_opacity(new_opacity): - - Sets opacity to new_opacity. - - If opacity has changed, call turf.reconsider_lights() to fix light occlusion - - proc/update_light(): - - Updates the light var on this atom, deleting or creating as needed and calling .update() - - -turf: (lighting_turf.dm) - - var/list/affecting_lights; list of light sources that are shining onto this turf - - - proc/reconsider_lights(): - - Force all light sources shining onto this turf to update - - - proc/lighting_clear_overlays(): - - Delete (manual GC) all light overlays on this turf, used when changing turf to space - - proc/lighting_build_overlays(): - - Create lighting overlays for this turf - - -/atom/movable/lighting_overlay: (lighting_overlay.dm) - - var/lum_r, var/lum_g, var/lum_b; lumcounts of each colour - - var/needs_update; set on update_lumcount, checked by lighting process - - - var/xoffset, var/yoffset; (only present when using sub-tile overlays) fractional offset of this overlay in the tile - - - proc/update_lumcount(delta_r, delta_g, delta_b): - - Change the lumcount vars and queue the overlay for update - - proc/update_overlay() - - Called by the lighting process to update the color of the overlay -*/ \ No newline at end of file + Useful procs when using lights: + +/atom/proc/set_light(range, power, color, angle, no_update) + desc: Sets an atom's light emission. `set_light(FALSE)` will disable the light. + args: + range -> the range of the light. 1.4 is the lowest possible value here. + power -> the power (intensity) of the light. Generally should be 1 or lower. Optional. + color -> The hex string (#FFFFFF) color of the light. Optional. + angle -> The angle of the cone that the light should shine at (directional lighting). Behavior of lights over 180 degrees is undefined. Best to stick to using the LIGHT_ defines for this. Optional. + no_update -> if TRUE, the light will not be updated. Useful for when making several of these calls to the same object. Optional. + +/atom/proc/set_opacity(new_opacity) + desc: Sets an atom's opacity, updating affecting lights' visibility. + args: + new_opacity -> the new opacity value. + +/turf/proc/reconsider_lights() + desc: Cause all lights affecting this turf to recalculate visibility. + args: none + +/turf/proc/force_update_lights() + desc: Force all lights affecting this turf to regenerate. Slow, use reconsider_lights instead when possible. + args: none + +/turf/proc/get_avg_color() + desc: Gets the average color of this tile as a hexadecimal color string. Used by cameras. + +/turf/proc/get_lumcount(minlum = 0, maxlum = 1) + desc: Gets the brightness of this tile. If not dynamically lit, always returns 0.5, otherwise returns the average brightness of all 4 corners, scaled between minlum and maxlum. + args: + minlum -> the low-bound of the scalar. + maxlum -> the high-bound of the scalar. +*/ diff --git a/code/modules/lighting/_lighting_defs.dm b/code/modules/lighting/_lighting_defs.dm new file mode 100644 index 0000000000000..f43ef762a6b3e --- /dev/null +++ b/code/modules/lighting/_lighting_defs.dm @@ -0,0 +1,43 @@ +// This is the define used to calculate falloff. +#define LUM_FALLOFF(Cx,Cy,Tx,Ty,HEIGHT) (1 - CLAMP01(sqrt(((Cx) - (Tx)) ** 2 + ((Cy) - (Ty)) ** 2 + HEIGHT) / max(1, actual_range))) + +// Macro that applies light to a new corner. +// It is a macro in the interest of speed, yet not having to copy paste it. +// 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. + +#define APPLY_CORNER(C,now,Tx,Ty,hdiff) \ + . = LUM_FALLOFF(C.x, C.y, Tx, Ty, hdiff) * 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), \ + now \ + ); + +// I don't need to explain what this does, do I? +#define REMOVE_CORNER(C,now) \ + . = -effect_str[C]; \ + C.update_lumcount \ + ( \ + . * applied_lum_r, \ + . * applied_lum_g, \ + . * applied_lum_b, \ + now \ + ); + +// Converts two Z levels into a height value for LUM_FALLOFF or HEIGHT_FALLOFF. +#define CALCULATE_CORNER_HEIGHT(ZA,ZB) (((max(ZA,ZB) - min(ZA,ZB)) + 1) * LIGHTING_HEIGHT * LIGHTING_Z_FACTOR) + +#define APPLY_CORNER_BY_HEIGHT(now) \ + if (C.z != Sz) { \ + corner_height = CALCULATE_CORNER_HEIGHT(C.z, Sz); \ + } \ + else { \ + corner_height = LIGHTING_HEIGHT; \ + } \ + APPLY_CORNER(C, now, Sx, Sy, corner_height); diff --git a/code/modules/lighting/darksight.dm b/code/modules/lighting/darksight.dm new file mode 100644 index 0000000000000..a3fcd2747abeb --- /dev/null +++ b/code/modules/lighting/darksight.dm @@ -0,0 +1,25 @@ +/obj/darksight + plane = LIGHTING_PLANE + + icon = 'icons/mob/darksight.dmi' + + screen_loc = "CENTER-7,CENTER-7" + + blend_mode = BLEND_ADD + invisibility = INVISIBILITY_LIGHTING + alpha = 0 //By default make it transparent so mobs that don't update it also don't get it + +/obj/darksight/Initialize() + . = ..() + SetTransform((world.icon_size/DARKSIGHT_GRADIENT_SIZE) * 0.9) + +/obj/darksight/proc/sync(new_colour) + color = new_colour + +/mob + var/obj/darksight/darksight = null + + +/mob/proc/change_light_colour(new_colour) + if(darksight) + darksight.sync(new_colour) diff --git a/code/modules/lighting/lighting_area.dm b/code/modules/lighting/lighting_area.dm index 003d7bba6fef2..cdcd2c9c10a21 100644 --- a/code/modules/lighting/lighting_area.dm +++ b/code/modules/lighting/lighting_area.dm @@ -1,10 +1,27 @@ -/area/luminosity = TRUE -/// Boolean. Whether or not the area should process dynamic lighting. Affects the value of `luminosity` during init and the lighting effects on turfs in the area. +/area/luminosity = TRUE /area/var/dynamic_lighting = TRUE -/// String (One of `AREA_LIGHTING_*`). The light tone selection mode used for the area. See `code\__defines\lighting.dm` for possible values. -/area/var/lighting_tone = AREA_LIGHTING_DEFAULT +/area/var/lighting_tone = AREA_LIGHTING_DEFAULT -/area/New() - ..() - if(dynamic_lighting) +/area/Initialize() + . = ..() + + if (dynamic_lighting) luminosity = FALSE + +/area/proc/set_dynamic_lighting(new_dynamic_lighting = TRUE) + if (new_dynamic_lighting == dynamic_lighting) + return FALSE + + dynamic_lighting = new_dynamic_lighting + + if (new_dynamic_lighting) + for (var/turf/T in src) + if (T.dynamic_lighting) + T.lighting_build_overlay() + + else + for (var/turf/T in src) + if (T.lighting_overlay) + T.lighting_clear_overlay() + + return TRUE diff --git a/code/modules/lighting/lighting_atom.dm b/code/modules/lighting/lighting_atom.dm index 52b63428bea91..d33781ed60799 100644 --- a/code/modules/lighting/lighting_atom.dm +++ b/code/modules/lighting/lighting_atom.dm @@ -1,98 +1,117 @@ -/// Float. Intensity of the light within the full brightness range. Value between 0 and 1. Do not modify directly. See `set_light()`. -/atom/var/light_max_bright = 1.0 -/// Integer. Range, in tiles, the light is at full brightness. Do not modify directly. See `set_light()`. -/atom/var/light_inner_range = 1 -/// Integer. Range, in tiles, where the light becomes darkness. Do not modify directly. See `set_light()`. -/atom/var/light_outer_range = 0 -/// Integer. Adjusts curve for falloff gradient. Must be greater than 0. Do not modify directly. See `set_light()`. -/atom/var/light_falloff_curve = 2 -/// String (Hexadecimal color code). The color of the light. Do not modify directly. See `set_light()`. -/atom/var/light_color +#define MINIMUM_USEFUL_LIGHT_RANGE 1.4 -/// The light source datum handling rendering of the light defined in the `light_*` vars on this atom. See `set_light()` and `update_light()`. + /// Intensity of the light. +/atom/var/light_power = 1 +/// Range in tiles of the light. +/atom/var/light_range = 0 +/// Hexadecimal RGB string representing the colour of the light. +/atom/var/light_color +/// The angle that the light's emission should be restricted to. null for omnidirectional. +/atom/var/light_wedge +/** These two vars let you override the default light offset detection (pixel_x/y). + * Useful for objects like light fixtures that aren't visually in the middle of the turf, but aren't offset either. + */ +/atom/var/light_offset_x +/atom/var/light_offset_y +/// Our light source. Don't fuck with this directly unless you have a good reason! /atom/var/datum/light_source/light -/// LAZYLIST of all light sources contained within the atom and its contents. Used to propagate updates whenever somehting, i.e. position, changes. -/atom/var/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. +/atom/var/list/light_source_multi +/// Same as above - this is a shortcut to avoid allocating the above list if we can +/atom/var/datum/light_source/light_source_solo -// Nonsensical value for l_color default, so we can detect if it gets set to null. +/// Nonsensical value for l_color default, so we can detect if it gets set to null. #define NONSENSICAL_VALUE -99999 -#define DEFAULT_FALLOFF_CURVE (2) /** - * Sets the atom's light values and color. Calls `update_light()`. + * Sets the atom's light values and color. May call `update_light()`. * * **Parameters**: - * - `l_max_bright` float (0 to 1) - Intensity of the light within the full brightness range. Value between 0 and 1. Applied to `light_max_bright`. - * - `l_inner_range` integer - Range, in tiles, the light is at full brightness. Applied to `light_inner_range`. - * - `l_outer_range` integer - Range, in tiles, where the light becomes darkness. Do not modify directly. Applied to `light_outer_range`. - * - `l_falloff_curbe` integer (Default `NONSENSICAL_VALUE`) - Adjusts curve for falloff gradient. Must be greater than 0. Do not modify directly. Applied to `light_falloff_curve`. - * - `l_color` color (Default `NONSENSICAL_VALUE`) - The color of the light. Applied to `light_color`. + * - `l_range` integer - Range in tiles of the light (Must be above `MINIMUM_USEFUL_LIGHT_RANGE`), light brightness will decay to 0 at this range. Applied to `light_range`. WARNING: Values over 32 are bound to cause lag + * - `l_power` float - The power (intensity) of the light. Generally should be 1 or lower but may be higher. Applied to `light_power`. Optional + * - `l_color` color (Default `NONSENSICAL_VALUE`) - The color of the light. Applied to `light_color`. Optional + * - `angle` integer (Default `NONSENSICAL_VALUE`) - The angle of the cone that the light should shine at (directional lighting). Behavior of lights over 180 degrees is undefined. Best to stick to using the LIGHT_ defines for this. Optional. + * - `no_update` boolean (Default `FALSE`) -if TRUE, `update_light()` will not be called. Useful for when making several of these calls to the same object. Optional. * - * Returns boolean. Whether or not the light was actually changed. + * Returns null */ -/atom/proc/set_light(l_max_bright, l_inner_range, l_outer_range, l_falloff_curve = NONSENSICAL_VALUE, l_color = NONSENSICAL_VALUE) - . = 0 //make it less costly if nothing's changed - - if(l_max_bright != null && l_max_bright != light_max_bright) - light_max_bright = l_max_bright - . = 1 - if(l_outer_range != null && l_outer_range != light_outer_range) - light_outer_range = l_outer_range - . = 1 - if(l_inner_range != null && l_inner_range != light_inner_range) - if(light_inner_range >= light_outer_range) - light_inner_range = light_outer_range / 4 - else - light_inner_range = l_inner_range - . = 1 - if(l_falloff_curve != NONSENSICAL_VALUE) - if(!l_falloff_curve || l_falloff_curve <= 0) - light_falloff_curve = DEFAULT_FALLOFF_CURVE - if(l_falloff_curve != light_falloff_curve) - light_falloff_curve = l_falloff_curve - . = 1 - if(l_color != NONSENSICAL_VALUE && l_color != light_color) +/atom/proc/set_light(l_range, l_power, l_color = NONSENSICAL_VALUE, angle = NONSENSICAL_VALUE, no_update = FALSE) + if(l_range > 0 && l_range < MINIMUM_USEFUL_LIGHT_RANGE) + l_range = MINIMUM_USEFUL_LIGHT_RANGE //Brings the range up to 1.4 + if (l_power != null) + light_power = l_power + + if (l_range != null) + light_range = l_range + + if (l_color != NONSENSICAL_VALUE) light_color = l_color - . = 1 - if(.) update_light() + if (angle != NONSENSICAL_VALUE) + light_wedge = angle + + if (no_update) + return + + update_light() #undef NONSENSICAL_VALUE -#undef DEFAULT_FALLOFF_CURVE /** - * Updates the atom's light source datum. This is automatically called by `set_light()`. + * Will update the light (duh). + * + * Creates or destroys it if needed, makes it update values, makes sure it's got the correct source turf... */ /atom/proc/update_light() - set waitfor = FALSE - - if(!light_max_bright || !light_outer_range || light_max_bright > 1) - if(light) - light.destroy() - light = null - if(light_max_bright > 1) - light_max_bright = 1 - CRASH("Attempted to call update_light() on atom [src] \ref[src] with a light_max_bright value greater than one") + if (QDELING(src)) + return + + if (!light_power || !light_range) // We won't emit light anyways, destroy the light source. + QDEL_NULL(light) else - if(!istype(loc, /atom/movable)) + if (!istype(loc, /atom/movable)) // We choose what atom should be the top atom of the light here. . = src else . = loc - if(light) + if (light) light.update(.) else light = new /datum/light_source(src, .) -/atom/Destroy() - if(light) - light.destroy() - light = null - return ..() -/atom/set_opacity() +// Should always be used to change the opacity of an atom. +// It notifies (potentially) affected light sources so they can update (if needed). +/atom/set_opacity(new_opacity) + . = ..() + if (!.) + return + + opacity = new_opacity + var/turf/T = loc + if (!isturf(T)) + return + + if (new_opacity == TRUE) + T.has_opaque_atom = TRUE + T.reconsider_lights() + #ifdef AO_USE_LIGHTING_OPACITY + T.regenerate_ao() + #endif + 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() + +/atom/movable/forceMove() . = ..() - if(.) - var/turf/T = loc - if(istype(T)) - T.RecalculateOpacity() + + if (light_source_solo) + light_source_solo.source_atom.update_light() + else if (light_source_multi) + var/datum/light_source/L + var/thing + for (thing in light_source_multi) + L = thing + L.source_atom.update_light() diff --git a/code/modules/lighting/lighting_corner.dm b/code/modules/lighting/lighting_corner.dm index 225b9d4a560eb..bd2ace158a9c0 100644 --- a/code/modules/lighting/lighting_corner.dm +++ b/code/modules/lighting/lighting_corner.dm @@ -1,4 +1,3 @@ -var/global/total_lighting_corners = 0 var/global/datum/lighting_corner/dummy/dummy_lighting_corner = new // Because we can control each corner of every lighting overlay. // And corners get shared between multiple turfs (unless you're on the corners of the map, then 1 corner doesn't). @@ -7,35 +6,75 @@ var/global/datum/lighting_corner/dummy/dummy_lighting_corner = new // 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. var/global/list/LIGHTING_CORNER_DIAGONAL = list(NORTHEAST, SOUTHEAST, SOUTHWEST, NORTHWEST) +// This is the reverse of the above - the position in the array is a dir. Update this if the above changes. +var/global/list/REVERSE_LIGHTING_CORNER_DIAGONAL = list(0, 0, 0, 0, 3, 4, 0, 0, 2, 1) + /datum/lighting_corner - var/list/turf/masters = list() - var/list/datum/light_source/affecting = list() // Light sources affecting us. + // t1 through t4 are our masters, in no particular order. + // They are split into vars like this in the interest of reducing memory usage. + // tX is the turf itself, tXi is the index of this corner in that turf's corners list. + var/turf/t1 + var/t1i + var/turf/t2 + var/t2i + var/turf/t3 + var/t3i + var/turf/t4 + var/t4i + + var/list/datum/light_source/affecting // Light sources affecting us. var/active = FALSE // TRUE if one of our masters has dynamic lighting. - var/x = 0 - var/y = 0 - var/z = 0 + var/x = 0 + var/y = 0 + var/z = 0 + + // Our own intensity, from lights directly shining on us. + var/self_r = 0 + var/self_g = 0 + var/self_b = 0 + + // The intensity we're inheriting from the turf below us, if we're a Z-turf. + var/below_r = 0 + var/below_g = 0 + var/below_b = 0 + + // Ambient turf lighting that's not inherited from a light source. These are updated as absolute values. + var/ambient_r = 0 + var/ambient_g = 0 + var/ambient_b = 0 - var/lum_r = 0 - var/lum_g = 0 - var/lum_b = 0 + // The turf above us' ambient + var/above_ambient_r = 0 + var/above_ambient_g = 0 + var/above_ambient_b = 0 + + // The final intensity, all things considered. + var/apparent_r = 0 + var/apparent_g = 0 + var/apparent_b = 0 var/needs_update = FALSE - var/cache_r = LIGHTING_SOFT_THRESHOLD - var/cache_g = LIGHTING_SOFT_THRESHOLD - var/cache_b = LIGHTING_SOFT_THRESHOLD + var/cache_r = 0 + var/cache_g = 0 + var/cache_b = 0 var/cache_mx = 0 + /// Used for planet lighting. Probably needs a better system to prevent over-updating when not needed at some point. var/update_gen = 0 -/datum/lighting_corner/New(turf/new_turf, diagonal) - . = ..() +/datum/lighting_corner/New(turf/new_turf, diagonal, oi) + SSlighting.total_lighting_corners += 1 - total_lighting_corners++ + var/has_ambience = FALSE - masters[new_turf] = turn(diagonal, 180) + t1 = new_turf z = new_turf.z + t1i = oi + + if (TURF_IS_AMBIENT_LIT_UNSAFE(new_turf)) + has_ambience = TRUE 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. @@ -47,88 +86,320 @@ var/global/list/LIGHTING_CORNER_DIAGONAL = list(NORTHEAST, SOUTHEAST, SOUTHWEST, // 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 + // 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) + T.corners = new(4) - masters[T] = diagonal - i = LIGHTING_CORNER_DIAGONAL.Find(turn(diagonal, 180)) - T.corners[i] = src + t2 = T + t2i = REVERSE_LIGHTING_CORNER_DIAGONAL[diagonal] + T.corners[t2i] = src + if (TURF_IS_AMBIENT_LIT_UNSAFE(T)) + has_ambience = TRUE // Now the horizontal one. T = get_step(new_turf, horizontal) if (T) // Ditto. if (!T.corners) - T.corners = list(null, null, null, null) + T.corners = new(4) - masters[T] = ((T.x > x) ? EAST : WEST) | ((T.y > y) ? NORTH : SOUTH) // Get the dir based on coordinates. - i = LIGHTING_CORNER_DIAGONAL.Find(turn(masters[T], 180)) - T.corners[i] = src + t3 = T + t3i = REVERSE_LIGHTING_CORNER_DIAGONAL[((T.x > x) ? EAST : WEST) | ((T.y > y) ? NORTH : SOUTH)] // Get the dir based on coordinates. + T.corners[t3i] = src + if (TURF_IS_AMBIENT_LIT_UNSAFE(T)) + has_ambience = TRUE // And finally the vertical one. T = get_step(new_turf, vertical) if (T) if (!T.corners) - T.corners = list(null, null, null, null) + T.corners = new(4) - masters[T] = ((T.x > x) ? EAST : WEST) | ((T.y > y) ? NORTH : SOUTH) // Get the dir based on coordinates. - i = LIGHTING_CORNER_DIAGONAL.Find(turn(masters[T], 180)) - T.corners[i] = src + t4 = T + t4i = REVERSE_LIGHTING_CORNER_DIAGONAL[((T.x > x) ? EAST : WEST) | ((T.y > y) ? NORTH : SOUTH)] // Get the dir based on coordinates. + T.corners[t4i] = src + if (TURF_IS_AMBIENT_LIT_UNSAFE(T)) + has_ambience = TRUE update_active() + if (has_ambience) + init_ambient() + +#define OVERLAY_PRESENT(T) (T && T.lighting_overlay) /datum/lighting_corner/proc/update_active() active = FALSE - for (var/turf/T in masters) - if (T.lighting_overlay) - active = TRUE + + if (OVERLAY_PRESENT(t1) || OVERLAY_PRESENT(t2) || OVERLAY_PRESENT(t3) || OVERLAY_PRESENT(t4)) + active = TRUE + +#undef OVERLAY_PRESENT + +#define GET_ABOVE(T) (HasAbove(T:z) ? get_step(T, UP) : null) +#define GET_BELOW(T) (HasBelow(T:z) ? get_step(T, DOWN) : null) + +#define UPDATE_APPARENT(T, CH) T.apparent_##CH = T.self_##CH + T.below_##CH + T.ambient_##CH + T.above_ambient_##CH + +/datum/lighting_corner/proc/init_ambient() + var/sum_r = 0 + var/sum_g = 0 + var/sum_b = 0 + + var/turf/T + for (var/i in 1 to 4) + // this is ugly as fuck, but it's still more legible than doing this with a macro + switch (i) + if (1) T = t1 + if (2) T = t2 + if (3) T = t3 + if (4) T = t4 + + if (!T || !T.ambient_light) + continue + + var/list/parts = rgb2num(T.ambient_light) + + sum_r += (parts[1] / 255) * T.ambient_light_multiplier + sum_g += (parts[2] / 255) * T.ambient_light_multiplier + sum_b += (parts[3] / 255) * T.ambient_light_multiplier + + sum_r /= 4 + sum_g /= 4 + sum_b /= 4 + + update_ambient_lumcount(sum_r, sum_g, sum_b) // 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) - lum_r += delta_r - lum_g += delta_g - lum_b += delta_b +/datum/lighting_corner/proc/update_lumcount(delta_r, delta_g, delta_b, now = FALSE) + if (!(delta_r + delta_g + delta_b)) + return + + self_r += delta_r + self_g += delta_g + self_b += delta_b + + UPDATE_APPARENT(src, r) + UPDATE_APPARENT(src, g) + UPDATE_APPARENT(src, b) + + var/turf/T + var/Ti + // Grab the first master that's a Z-turf, if one exists. + if ((T = t1?.above) && (T.z_flags & ZM_ALLOW_LIGHTING)) + Ti = t1i + else if ((T = t2?.above) && (T.z_flags & ZM_ALLOW_LIGHTING)) + Ti = t2i + else if ((T = t3?.above) && (T.z_flags & ZM_ALLOW_LIGHTING)) + Ti = t3i + else if ((T = t4?.above) && (T.z_flags & ZM_ALLOW_LIGHTING)) + Ti = t4i + else // Nothing above us that cares about below light. + T = null + + if (TURF_IS_DYNAMICALLY_LIT(T)) + if (!T.corners || !T.corners[Ti]) + T.generate_missing_corners() + var/datum/lighting_corner/above = T.corners[Ti] + above.update_below_lumcount(delta_r, delta_g, delta_b, now) + + // This needs to be down here instead of the above if so the lum values are properly updated. + if (needs_update) + return - if (!needs_update) + if (now) + update_overlays(TRUE) + else needs_update = TRUE SSlighting.corner_queue += src -/datum/lighting_corner/proc/update_overlays() - // Cache these values a head of time so 4 individual lighting overlays don't all calculate them individually. - var/lum_r = src.lum_r > 0 ? LIGHTING_MULT_FACTOR * sqrt(src.lum_r) : src.lum_r - var/lum_g = src.lum_g > 0 ? LIGHTING_MULT_FACTOR * sqrt(src.lum_g) : src.lum_g - var/lum_b = src.lum_b > 0 ? LIGHTING_MULT_FACTOR * sqrt(src.lum_b) : src.lum_b +/datum/lighting_corner/proc/update_below_lumcount(delta_r, delta_g, delta_b, now = FALSE) + if (!(delta_r + delta_g + delta_b)) + return + + below_r += delta_r + below_g += delta_g + below_b += delta_b + + UPDATE_APPARENT(src, r) + UPDATE_APPARENT(src, g) + UPDATE_APPARENT(src, b) + + // This needs to be down here instead of the above if so the lum values are properly updated. + if (needs_update) + return + + if (!now) + needs_update = TRUE + SSlighting.corner_queue += src + else + update_overlays(TRUE) + +//So, a turf with 4 corners needs to reset all 4 of those to 0, then we need to take turf below and tell its corners to rebuild +// Probably better ways to do this +/datum/lighting_corner/proc/clear_below_lumcount() - var/mx = max(lum_r, lum_g, lum_b) // Scale it so 1 is the strongest lum, if it is above 1. + if(!(below_r || below_b || below_g)) + return + + below_r = 0 + below_g = 0 + below_b = 0 + + UPDATE_APPARENT(src, r) + UPDATE_APPARENT(src, g) + UPDATE_APPARENT(src, b) + + if (needs_update) + return + + needs_update = TRUE + SSlighting.corner_queue += src + +/datum/lighting_corner/proc/set_below_lumcount(_r, _g, _b) + + below_r = _r + below_g = _g + below_b = _b + + UPDATE_APPARENT(src, r) + UPDATE_APPARENT(src, g) + UPDATE_APPARENT(src, b) + + if (needs_update) + return + + needs_update = TRUE + SSlighting.corner_queue += src + +/datum/lighting_corner/proc/rebuild_above_below_lumcount() + //Destroy current state and rebuild it! + var/turf/T + var/Ti + // Grab the first master that's a Z-turf, if one exists. + if ((T = t1?.above) && (T.z_flags & ZM_ALLOW_LIGHTING)) + Ti = t1i + else if ((T = t2?.above) && (T.z_flags & ZM_ALLOW_LIGHTING)) + Ti = t2i + else if ((T = t3?.above) && (T.z_flags & ZM_ALLOW_LIGHTING)) + Ti = t3i + else if ((T = t4?.above) && (T.z_flags & ZM_ALLOW_LIGHTING)) + Ti = t4i + else // Nothing above us that cares about below light. + T = null + + if (TURF_IS_DYNAMICALLY_LIT(T)) + if (!T.corners || !T.corners[Ti]) + T.generate_missing_corners() + var/datum/lighting_corner/above = T.corners[Ti] + above.set_below_lumcount(self_r, self_g, self_b) + +/datum/lighting_corner/proc/update_ambient_lumcount(delta_r, delta_g, delta_b, skip_update = FALSE) + ambient_r += delta_r + ambient_g += delta_g + ambient_b += delta_b + + UPDATE_APPARENT(src, r) + UPDATE_APPARENT(src, g) + UPDATE_APPARENT(src, b) + + var/turf/T + var/Ti + + if (t1) + T = t1 + Ti = t1i + else if (t2) + T = t2 + Ti = t2i + else if (t3) + T = t3 + Ti = t3i + else if (t4) + T = t4 + Ti = t4i + else + // This should be impossible to reach -- how do we exist without at least one master turf? + CRASH("Corner has no masters!") + + var/datum/lighting_corner/below = src + + var/turf/lasT + + // We init before Z-Mimic, cannot rely on above/below. + while ((lasT = T) && (T = T.below || GET_BELOW(T)) && (lasT.z_flags & ZM_ALLOW_LIGHTING) && TURF_IS_DYNAMICALLY_LIT_UNSAFE(T)) + T.ambient_has_indirect = TRUE + + if (!T.corners || !T.corners[Ti]) + T.generate_missing_corners() + + ASSERT(length(T.corners)) + + below = T.corners[Ti] + below.above_ambient_r += delta_r + below.above_ambient_g += delta_g + below.above_ambient_b += delta_b + + UPDATE_APPARENT(below, r) + UPDATE_APPARENT(below, g) + UPDATE_APPARENT(below, b) + + if (!skip_update && !below.needs_update) + below.needs_update = TRUE + SSlighting.corner_queue += below + + if (needs_update || skip_update) + return + + // Always queue for this, not important enough to hit the synchronous path. + needs_update = TRUE + SSlighting.corner_queue += src + +#undef UPDATE_APPARENT + +/datum/lighting_corner/proc/update_overlays(now = FALSE) + var/lr = apparent_r + var/lg = apparent_g + var/lb = apparent_b + + // Cache these values a head of time so 4 individual lighting overlays don't all calculate them individually. + var/mx = max(lr, lg, lb) // Scale it so 1 is the strongest lum, if it is above 1. . = 1 // factor if (mx > 1) . = 1 / mx - #if LIGHTING_SOFT_THRESHOLD != 0 - else if (mx < LIGHTING_SOFT_THRESHOLD) - . = 0 // 0 means soft lighting. - - cache_r = round(lum_r * ., LIGHTING_ROUND_VALUE) || LIGHTING_SOFT_THRESHOLD - cache_g = round(lum_g * ., LIGHTING_ROUND_VALUE) || LIGHTING_SOFT_THRESHOLD - cache_b = round(lum_b * ., LIGHTING_ROUND_VALUE) || LIGHTING_SOFT_THRESHOLD - #else - cache_r = round(lum_r * ., LIGHTING_ROUND_VALUE) - cache_g = round(lum_g * ., LIGHTING_ROUND_VALUE) - cache_b = round(lum_b * ., LIGHTING_ROUND_VALUE) - #endif + cache_r = round(lr * ., LIGHTING_ROUND_VALUE) + cache_g = round(lg * ., LIGHTING_ROUND_VALUE) + cache_b = round(lb * ., LIGHTING_ROUND_VALUE) + cache_mx = round(mx, LIGHTING_ROUND_VALUE) - for (var/TT in masters) - var/turf/T = TT - if (T.lighting_overlay) - if (!T.lighting_overlay.needs_update) - T.lighting_overlay.needs_update = TRUE - SSlighting.overlay_queue += T.lighting_overlay + var/turf/T + for (var/i in 1 to 4) + // this is ugly as fuck, but it's still more legible than doing this with a macro + switch (i) + if (1) T = t1 + if (2) T = t2 + if (3) T = t3 + if (4) T = t4 + + var/atom/movable/lighting_overlay/Ov + if (T && (Ov = T.lighting_overlay)) + if (now) + Ov.update_overlay() + else if (!Ov.needs_update) + Ov.needs_update = TRUE + SSlighting.overlay_queue += Ov + +/datum/lighting_corner/Destroy(force = FALSE) + //PRINT_STACK_TRACE("Someone [force ? "force-" : ""]deleted a lighting corner.") + if (!force) + return QDEL_HINT_LETMELIVE + SSlighting.total_lighting_corners -= 1 + return ..() /datum/lighting_corner/dummy/New() return diff --git a/code/modules/lighting/lighting_overlay.dm b/code/modules/lighting/lighting_overlay.dm index ec09a9930ac94..55b3837145f77 100644 --- a/code/modules/lighting/lighting_overlay.dm +++ b/code/modules/lighting/lighting_overlay.dm @@ -1,71 +1,84 @@ -var/global/total_lighting_overlays = 0 /atom/movable/lighting_overlay - name = "" + name = "" + anchored = TRUE + icon = LIGHTING_ICON + icon_state = LIGHTING_BASE_ICON_STATE + color = LIGHTING_BASE_MATRIX mouse_opacity = 0 - simulated = FALSE - anchored = TRUE - icon = LIGHTING_ICON - plane = LIGHTING_PLANE - layer = LIGHTING_LAYER - invisibility = INVISIBILITY_LIGHTING - color = LIGHTING_BASE_MATRIX - icon_state = "light1" - blend_mode = BLEND_OVERLAY - - appearance_flags = DEFAULT_APPEARANCE_FLAGS - - var/lum_r = 0 - var/lum_g = 0 - var/lum_b = 0 + layer = LIGHTING_LAYER + plane = LIGHTING_PLANE + invisibility = INVISIBILITY_LIGHTING + simulated = FALSE + blend_mode = BLEND_OVERLAY var/needs_update = FALSE -/atom/movable/lighting_overlay/Initialize(mapload, no_update) - var/turf/turf = loc - if (!turf.dynamic_lighting) - atom_flags |= ATOM_FLAG_INITIALIZED - return INITIALIZE_HINT_QDEL + #if WORLD_ICON_SIZE != 32 + transform = matrix(WORLD_ICON_SIZE / 32, 0, (WORLD_ICON_SIZE - 32) / 2, 0, WORLD_ICON_SIZE / 32, (WORLD_ICON_SIZE - 32) / 2) + #endif + +/atom/movable/lighting_overlay/Initialize(mapload, update_now = FALSE) . = ..() - verbs.Cut() - total_lighting_overlays++ - turf.lighting_overlay = src - turf.luminosity = 0 - if (no_update) - return - update_overlay() + atom_flags |= ATOM_FLAG_INITIALIZED + SSlighting.total_lighting_overlays += 1 + + var/turf/T = loc // If this runtimes atleast we'll know what's creating overlays in things that aren't turfs. + T.lighting_overlay = src + T.luminosity = 0 + + if (T.corners && length(T.corners)) + for (var/datum/lighting_corner/C in T.corners) + C.active = TRUE + + if (update_now) + update_overlay() + needs_update = FALSE + else + needs_update = TRUE + SSlighting.overlay_queue += src + +/atom/movable/lighting_overlay/Destroy(force = FALSE) + if (!force) + return QDEL_HINT_LETMELIVE // STOP DELETING ME + SSlighting.total_lighting_overlays -= 1 + + var/turf/T = loc + if (istype(T)) + T.lighting_overlay = null + T.luminosity = 1 + + return ..() + +// This is a macro PURELY so that the if below is actually readable. +#define ALL_EQUAL ((rr == gr && gr == br && br == ar) && (rg == gg && gg == bg && bg == ag) && (rb == gb && gb == bb && bb == ab)) /atom/movable/lighting_overlay/proc/update_overlay() - set waitfor = FALSE var/turf/T = loc + if (!isturf(T)) // Erm... + if (loc) + warning("A lighting overlay realised its loc was NOT a turf (actual loc: [loc], [loc.type]) in update_overlay() and got deleted!") - if(!istype(T)) - if(loc) - log_debug("A lighting overlay realised its loc was NOT a turf (actual loc: [loc][loc ? ", " + loc.type : "null"]) in update_overlay() and got qdel'ed!") else - log_debug("A lighting overlay realised it was in nullspace in update_overlay() and got pooled!") - qdel(src) - return - if(!T.dynamic_lighting) - qdel(src) - return + warning("A lighting overlay realised it was in nullspace in update_overlay() and got deleted!") - // 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. - // And there's no way to improve it because - // without a loop you can make the list all at once which is the fastest you're gonna get. - // Oh it's also shorter line wise. - // Including with these comments. + qdel(src, TRUE) + return // See LIGHTING_CORNER_DIAGONAL in lighting_corner.dm for why these values are what they are. - // No I seriously cannot think of a more efficient method, fuck off Comic. - var/datum/lighting_corner/cr = T.corners[3] || dummy_lighting_corner - var/datum/lighting_corner/cg = T.corners[2] || dummy_lighting_corner - var/datum/lighting_corner/cb = T.corners[4] || dummy_lighting_corner - var/datum/lighting_corner/ca = T.corners[1] || dummy_lighting_corner + var/list/corners = T.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) + 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/max = max(cr.cache_mx, cg.cache_mx, cb.cache_mx, ca.cache_mx) + luminosity = max > 0 var/rr = cr.cache_r var/rg = cr.cache_g @@ -83,61 +96,70 @@ var/global/total_lighting_overlays = 0 var/ag = ca.cache_g var/ab = ca.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. - // 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" + if(rr + rg + rb + gr + gg + gb + br + bg + bb + ar + ag + ab >= 12) + icon_state = LIGHTING_TRANSPARENT_ICON_STATE color = null - else if(!set_luminosity) - icon_state = LIGHTING_ICON_STATE_DARK + else if (!luminosity) + icon_state = LIGHTING_DARKNESS_ICON_STATE + color = null + else if (rr == LIGHTING_DEFAULT_TUBE_R && rg == LIGHTING_DEFAULT_TUBE_G && rb == LIGHTING_DEFAULT_TUBE_B && ALL_EQUAL) + icon_state = LIGHTING_STATION_ICON_STATE color = null else - icon_state = null - color = list( - -rr, -rg, -rb, 00, - -gr, -gg, -gb, 00, - -br, -bg, -bb, 00, - -ar, -ag, -ab, 00, - 01, 01, 01, 01 - ) - - luminosity = set_luminosity - // if (T.above && T.above.shadower) - // T.above.shadower.copy_lighting(src) + icon_state = LIGHTING_BASE_ICON_STATE + if (islist(color)) + // Does this even save a list alloc? + var/list/c_list = color + c_list[CL_MATRIX_RR] = rr + c_list[CL_MATRIX_RG] = rg + c_list[CL_MATRIX_RB] = rb + c_list[CL_MATRIX_GR] = gr + c_list[CL_MATRIX_GG] = gg + c_list[CL_MATRIX_GB] = gb + c_list[CL_MATRIX_BR] = br + c_list[CL_MATRIX_BG] = bg + c_list[CL_MATRIX_BB] = bb + c_list[CL_MATRIX_AR] = ar + c_list[CL_MATRIX_AG] = ag + c_list[CL_MATRIX_AB] = ab + color = c_list + else + color = list( + rr, rg, rb, 0, + gr, gg, gb, 0, + br, bg, bb, 0, + ar, ag, ab, 0, + 0, 0, 0, 1 + ) + + // If there's a Z-turf above us, update its shadower. + if (T.above) + if (T.above.shadower) + T.above.shadower.copy_lighting(src) + else + T.above.update_mimic() + +#undef ALL_EQUAL // Variety of overrides so the overlays don't get affected by weird things. -/atom/movable/lighting_overlay/ex_act() + +/atom/movable/lighting_overlay/ex_act(severity) + SHOULD_CALL_PARENT(FALSE) return -/atom/movable/lighting_overlay/singularity_pull() +/atom/movable/lighting_overlay/singularity_act() return -/atom/movable/lighting_overlay/Destroy() - total_lighting_overlays-- - SSlighting.overlay_queue -= src +/atom/movable/lighting_overlay/singularity_pull() + return - var/turf/T = loc - if(istype(T)) - T.lighting_overlay = null +/atom/movable/lighting_overlay/singuloCanEat() + return FALSE - . = ..() +/atom/movable/lighting_overlay/can_fall() + return FALSE -/atom/movable/lighting_overlay/forceMove() - //should never move - //In theory... except when getting deleted :C +// Override here to prevent things accidentally moving around overlays. +/atom/movable/lighting_overlay/forceMove(atom/destination, harderforce = FALSE) if(QDELING(src)) - return ..() - return 0 - -/atom/movable/lighting_overlay/Move() - return 0 - -/atom/movable/lighting_overlay/throw_at() - return 0 + . = ..() diff --git a/code/modules/lighting/lighting_planemaster.dm b/code/modules/lighting/lighting_planemaster.dm deleted file mode 100644 index 7d82972cbfc42..0000000000000 --- a/code/modules/lighting/lighting_planemaster.dm +++ /dev/null @@ -1,25 +0,0 @@ -/obj/lighting_general - plane = LIGHTING_PLANE - screen_loc = "8,8" - - icon = LIGHTING_ICON - icon_state = LIGHTING_ICON_STATE_DARK - - color = "#ffffff" - - blend_mode = BLEND_MULTIPLY - -/obj/lighting_general/Initialize() - . = ..() - SetTransform(scale = world.view * 2.2) - -/obj/lighting_general/proc/sync(new_colour) - color = new_colour - -/mob - var/obj/lighting_general/l_general - - -/mob/proc/change_light_colour(new_colour) - if(l_general) - l_general.sync(new_colour) diff --git a/code/modules/lighting/lighting_source.dm b/code/modules/lighting/lighting_source.dm index 0f9cdeafa6d56..48fcd6b5ae284 100644 --- a/code/modules/lighting/lighting_source.dm +++ b/code/modules/lighting/lighting_source.dm @@ -1,17 +1,16 @@ -var/global/total_lighting_sources = 0 // This is where the fun begins. // 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/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/light_max_bright = 1 // intensity of the light within the full brightness range. Value between 0 and 1 - var/light_inner_range = 0 // range, in tiles, the light is at full brightness - var/light_outer_range = 0 // range, in tiles, where the light becomes darkness - var/light_falloff_curve // adjusts curve for falloff gradient - var/light_color // The colour of the light, string, decomposed by parse_light_color() + var/turf/source_turf // The turf under the above. + var/turf/pixel_turf // The turf the top_atom _appears_ to be on + 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() + var/light_angle // The light's emission angle, in degrees. // Variables for keeping track of the colour. var/lum_r @@ -23,310 +22,430 @@ var/global/total_lighting_sources = 0 var/applied_lum_g var/applied_lum_b + // Variables used to keep track of the atom's angle. + var/limit_a_x // The first test point's X coord for the cone. + var/limit_a_y // The first test point's Y coord for the cone. + var/limit_b_x // The second test point's X coord for the cone. + var/limit_b_y // The second test point's Y coord for the cone. + var/cached_origin_x // The last known X coord of the origin. + var/cached_origin_y // The last known Y coord of the origin. + var/old_direction // The last known direction of the origin. + var/test_x_offset // How much the X coord should be offset due to direction. + var/test_y_offset // How much the Y coord should be offset due to direction. + var/facing_opaque = FALSE + var/list/datum/lighting_corner/effect_str // List used to store how much we're affecting corners. var/list/turf/affecting_turfs var/applied = FALSE // Whether we have applied our light yet or not. - var/vis_update // Whether we should smartly recalculate visibility. and then only update tiles that became(in)visible to us. - var/needs_update // Whether we are queued for an update. - var/destroyed // Whether we are destroyed and need to stop emitting light. - var/force_update + var/needs_update = LIGHTING_NO_UPDATE + +// 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/px = top_atom.light_offset_x || top_atom.pixel_x; var/py = top_atom.light_offset_y || top_atom.pixel_y; var/_dir = GET_APPROXIMATE_PIXEL_DIR(px, py); pixel_turf = _dir ? (get_step(source_turf, _dir) || source_turf) : source_turf + +// These macros are for dealing with the multi/solo split. +#define ADD_SOURCE(TARGET) if (!TARGET.light_source_multi && !TARGET.light_source_solo) { TARGET.light_source_solo = src; } else if (TARGET.light_source_solo) { TARGET.light_source_multi = list(TARGET.light_source_solo, src); TARGET.light_source_solo = null; } else { TARGET.light_source_multi += src } +#define REMOVE_SOURCE(TARGET) if (TARGET.light_source_solo == src) { TARGET.light_source_solo = null } else if (TARGET.light_source_multi) { TARGET.light_source_multi -= src; if (length(TARGET.light_source_multi) == 1) { TARGET.light_source_solo = TARGET.light_source_multi[1]; TARGET.light_source_multi = null; } } /datum/light_source/New(atom/owner, atom/top) - total_lighting_sources++ + SSlighting.total_lighting_sources += 1 source_atom = owner // Set our new owner. - if(!source_atom.light_sources) - source_atom.light_sources = list() - source_atom.light_sources += src // Add us to the lights of our owner. - top_atom = top - if(top_atom != source_atom) - if(!top.light_sources) - top.light_sources = list() + ADD_SOURCE(source_atom) - top_atom.light_sources += src + top_atom = top + if (top_atom != source_atom) + ADD_SOURCE(top_atom) source_turf = top_atom - light_max_bright = source_atom.light_max_bright - light_inner_range = source_atom.light_inner_range - light_outer_range = source_atom.light_outer_range - light_falloff_curve = source_atom.light_falloff_curve + UPDATE_APPROXIMATE_PIXEL_TURF + light_power = source_atom.light_power + light_range = source_atom.light_range light_color = source_atom.light_color + light_angle = source_atom.light_wedge parse_light_color() - effect_str = list() - affecting_turfs = list() - update() - - return ..() - -/* lighting debugging verb -/mob/verb/self_light() - set name = "set self light" - set category = "Light" - var/v1 = input(usr, "Enter max bright", "max bright", 1) as num|null - var/v2 = input(usr, "Enter inner range", "inner range", 0.1) as num|null - var/v3 = input(usr, "Enter outer range", "outer range", 4) as num|null - var/v4 = input(usr, "Enter curve", "curve", 2) as num|null - set_light(v1, v2, v3, v4, "#0066ff") -*/ - // Kill ourselves. -/datum/light_source/proc/destroy() - total_lighting_sources-- - destroyed = TRUE - force_update() - if(source_atom && source_atom.light_sources) - source_atom.light_sources -= src - - if(top_atom && top_atom.light_sources) - top_atom.light_sources -= src - -// Call it dirty, I don't care. -// This is here so there's no performance loss on non-instant updates from the fact that the engine can also do instant updates. -// If you're wondering what's with the "BYOND" argument: BYOND won't let me have a() macro that has no arguments :|. -#define effect_update(BYOND) \ - if(!needs_update) \ - { \ - SSlighting.light_queue += src; \ - needs_update = TRUE; \ +/datum/light_source/Destroy(force) + SSlighting.total_lighting_sources -= 1 + + remove_lum() + if (source_atom) + REMOVE_SOURCE(source_atom) + + if (top_atom) + REMOVE_SOURCE(top_atom) + + . = ..() + if (!force) + return QDEL_HINT_IWILLGC + +#ifdef USE_INTELLIGENT_LIGHTING_UPDATES +// Picks either scheduled or instant updates based on current server load. +#define INTELLIGENT_UPDATE(level) \ + var/_should_update = needs_update == LIGHTING_NO_UPDATE; \ + if (needs_update < level) { \ + needs_update = level; \ + } \ + if (_should_update) { \ + if (world.tick_usage > (Master.current_ticklimit/2) || light_range > LIGHTING_MAXIMUM_INSTANT_RANGE || SSlighting.force_queued) { \ + SSlighting.light_queue += src; \ + } \ + else { \ + SSlighting.total_instant_updates += 1; \ + update_corners(TRUE); \ + needs_update = LIGHTING_NO_UPDATE; \ + } \ } +#else +#define INTELLIGENT_UPDATE(level) \ + if (needs_update == LIGHTING_NO_UPDATE) \ + SSlighting.light_queue += src; \ + if (needs_update < level) \ + needs_update = level; +#endif // This proc will cause the light source to update the top atom, and add itself to the update queue. /datum/light_source/proc/update(atom/new_top_atom) // This top atom is different. - if(new_top_atom && new_top_atom != top_atom) + if (new_top_atom && new_top_atom != top_atom) if(top_atom != source_atom) // Remove ourselves from the light sources of that top atom. - top_atom.light_sources -= src + REMOVE_SOURCE(top_atom) top_atom = new_top_atom - if(top_atom != source_atom) - if(!top_atom.light_sources) - top_atom.light_sources = list() + if (top_atom != source_atom) + ADD_SOURCE(top_atom) // Add ourselves to the light sources of our new top atom. - top_atom.light_sources += src // Add ourselves to the light sources of our new top atom. - - effect_update(null) + INTELLIGENT_UPDATE(LIGHTING_CHECK_UPDATE) // Will force an update without checking if it's actually needed. /datum/light_source/proc/force_update() - force_update = 1 - - effect_update(null) + INTELLIGENT_UPDATE(LIGHTING_FORCE_UPDATE) // Will cause the light source to recalculate turfs that were removed or added to visibility only. /datum/light_source/proc/vis_update() - vis_update = 1 + INTELLIGENT_UPDATE(LIGHTING_VIS_UPDATE) - effect_update(null) +// Decompile the hexadecimal colour into lumcounts of each perspective. +/datum/light_source/proc/parse_light_color() + if (light_color) + var/list/parts = rgb2num(light_color) + ASSERT(length(parts) == 3) + lum_r = parts[1] / 255 + lum_g = parts[2] / 255 + lum_b = parts[3] / 255 + else + lum_r = 1 + lum_g = 1 + lum_b = 1 -// Will check if we actually need to update, and update any variables that may need to be updated. -/datum/light_source/proc/check() - if(!source_atom || !light_outer_range || !light_max_bright) - destroy() - return 1 +#define POLAR_TO_CART_X(R,T) ((R) * cos(T)) +#define POLAR_TO_CART_Y(R,T) ((R) * sin(T)) +#define DETERMINANT(A_X,A_Y,B_X,B_Y) ((A_X)*(B_Y) - (A_Y)*(B_X)) +#define MINMAX(NUM) ((NUM) < 0 ? -round(-(NUM)) : round(NUM)) +#define ARBITRARY_NUMBER 10 + +/datum/light_source/proc/regenerate_angle(ndir) + old_direction = ndir + + var/turf/front = get_step(source_turf, old_direction) + facing_opaque = (front && front.has_opaque_atom) + + cached_origin_x = test_x_offset = source_turf.x + cached_origin_y = test_y_offset = source_turf.y + + if (facing_opaque) + return + + var/limit_a_t + var/limit_b_t + + var/angle = light_angle * 0.5 + switch (old_direction) + if (NORTH) + limit_a_t = angle + 90 + limit_b_t = -(angle) + 90 + test_y_offset += 1 + + if (SOUTH) + limit_a_t = (angle) - 90 + limit_b_t = -(angle) - 90 + test_y_offset -= 1 + + if (EAST) + limit_a_t = angle + limit_b_t = -(angle) + test_x_offset += 1 + + if (WEST) + limit_a_t = angle + 180 + limit_b_t = -(angle) - 180 + test_x_offset -= 1 + + // Convert our angle + range into a vector. + limit_a_x = POLAR_TO_CART_X(light_range + ARBITRARY_NUMBER, limit_a_t) + limit_a_x = MINMAX(limit_a_x) + limit_a_y = POLAR_TO_CART_Y(light_range + ARBITRARY_NUMBER, limit_a_t) + limit_a_y = MINMAX(limit_a_y) + limit_b_x = POLAR_TO_CART_X(light_range + ARBITRARY_NUMBER, limit_b_t) + limit_b_x = MINMAX(limit_b_x) + limit_b_y = POLAR_TO_CART_Y(light_range + ARBITRARY_NUMBER, limit_b_t) + limit_b_y = MINMAX(limit_b_y) + +#undef ARBITRARY_NUMBER +#undef POLAR_TO_CART_X +#undef POLAR_TO_CART_Y +#undef MINMAX + +/datum/light_source/proc/remove_lum(now = FALSE) + applied = FALSE - if(!top_atom) - top_atom = source_atom - . = 1 + var/thing + for (thing in affecting_turfs) + var/turf/T = thing + LAZYREMOVE(T.affecting_lights, src) - if(isturf(top_atom)) - if(source_turf != top_atom) - source_turf = top_atom - . = 1 - else if(top_atom.loc != source_turf) - source_turf = top_atom.loc - . = 1 + affecting_turfs = null - if(source_atom.light_max_bright != light_max_bright) - light_max_bright = source_atom.light_max_bright - . = 1 + for (thing in effect_str) + var/datum/lighting_corner/C = thing + REMOVE_CORNER(C,now) - if(source_atom.light_inner_range != light_inner_range) - light_inner_range = source_atom.light_inner_range - . = 1 + LAZYREMOVE(C.affecting, src) - if(source_atom.light_outer_range != light_outer_range) - light_outer_range = source_atom.light_outer_range - . = 1 + effect_str = null - if(source_atom.light_falloff_curve != light_falloff_curve) - light_falloff_curve = source_atom.light_falloff_curve - . = 1 +/datum/light_source/proc/recalc_corner(datum/lighting_corner/C, now = FALSE) + LAZYINITLIST(effect_str) + if (effect_str[C]) // Already have one. + REMOVE_CORNER(C,now) + effect_str[C] = 0 - if(light_max_bright && light_outer_range && !applied) - . = 1 + var/actual_range = light_range - if(source_atom.light_color != light_color) - light_color = source_atom.light_color - parse_light_color() - . = 1 + var/Sx = pixel_turf.x + var/Sy = pixel_turf.y + var/Sz = pixel_turf.z -// Decompile the hexadecimal colour into lumcounts of each perspective. -/datum/light_source/proc/parse_light_color() - if(light_color) - lum_r = GetRedPart (light_color) / 255 - lum_g = GetGreenPart(light_color) / 255 - lum_b = GetBluePart (light_color) / 255 - else - lum_r = 1 - lum_g = 1 - lum_b = 1 + var/height = C.z == Sz ? LIGHTING_HEIGHT : CALCULATE_CORNER_HEIGHT(C.z, Sz) + APPLY_CORNER(C, now, Sx, Sy, height) -// Macro that applies light to a new corner. -// It is a macro in the interest of speed, yet not having to copy paste it. -// 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. - -#define APPLY_CORNER(C) \ - . = LUM_FALLOFF(C, source_turf); \ - . *= (light_max_bright ** 2); \ - . *= light_max_bright < 0 ? -1:1;\ - effect_str[C] = .; \ - C.update_lumcount \ - ( \ - . * applied_lum_r, \ - . * applied_lum_g, \ - . * applied_lum_b \ - ); - -// I don't need to explain what this does, do I? -#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. -// Assuming a brightness of 1 at range 1, formula should be (brightness = 1 / distance^2) -// However, due to the weird range factor, brightness = (-(distance - full_dark_start) / (full_dark_start - full_light_end)) ^ light_max_bright - -#define LUM_FALLOFF(C, T)(CLAMP01(-((((C.x - T.x) ** 2 +(C.y - T.y) ** 2) ** 0.5 - light_outer_range) / max(light_outer_range - light_inner_range, 1))) ** light_falloff_curve) - - -/datum/light_source/proc/apply_lum() - var/static/update_gen = 1 - applied = 1 - - // Keep track of the last applied lum values so that the lighting can be reversed - applied_lum_r = lum_r - applied_lum_g = lum_g - applied_lum_b = lum_b + UNSETEMPTY(effect_str) - FOR_DVIEW(var/turf/T, light_outer_range, source_turf, INVISIBILITY_LIGHTING) - check_t: - if (!T) - continue - if(!T.lighting_corners_initialised) - T.generate_missing_corners() +/datum/light_source/proc/update_corners(now = FALSE) + var/update = FALSE - for(var/datum/lighting_corner/C in T.get_corners()) - if(C.update_gen == update_gen) - continue + if (QDELETED(source_atom)) + qdel(src) + return - C.update_gen = update_gen - C.affecting += src + if (source_atom.light_power != light_power) + light_power = source_atom.light_power + update = TRUE - if(!C.active) - effect_str[C] = 0 - continue + if (source_atom.light_range != light_range) + light_range = source_atom.light_range + update = TRUE - APPLY_CORNER(C) + if (!top_atom) + top_atom = source_atom + update = TRUE - LAZYADD(T.affecting_lights, src) - affecting_turfs += T + if (top_atom.loc != source_turf) + source_turf = top_atom.loc + UPDATE_APPROXIMATE_PIXEL_TURF + update = TRUE - if (T.z_flags & ZM_ALLOW_LIGHTING) - T = T.below - goto check_t + if (!light_range || !light_power) + qdel(src) + return - END_FOR_DVIEW + if (isturf(top_atom)) + if (source_turf != top_atom) + source_turf = top_atom + UPDATE_APPROXIMATE_PIXEL_TURF + update = TRUE + else if (top_atom.loc != source_turf) + source_turf = top_atom.loc + UPDATE_APPROXIMATE_PIXEL_TURF + update = TRUE - update_gen++ + if (!source_turf) + return // Somehow we've got a light in nullspace, no-op. -/datum/light_source/proc/remove_lum() - applied = FALSE + if (light_range && light_power && !applied) + update = TRUE - for(var/turf/T in affecting_turfs) - LAZYREMOVE(T.affecting_lights, src) + if (source_atom.light_color != light_color) + light_color = source_atom.light_color + parse_light_color() + update = TRUE + + else if (applied_lum_r != lum_r || applied_lum_g != lum_g || applied_lum_b != lum_b) + update = TRUE + + if (source_atom.light_wedge != light_angle) + light_angle = source_atom.light_wedge + update = TRUE + + if (light_angle) + var/ndir + if (istype(top_atom, /mob) && top_atom:facing_dir) + ndir = top_atom:facing_dir + else + ndir = top_atom.dir + + if (old_direction != ndir) // If our direction has changed, we need to regenerate all the angle info. + regenerate_angle(ndir) + update = TRUE + else // Check if it was just a x/y translation, and update our vars without an regenerate_angle() call if it is. + var/co_updated = FALSE + if (source_turf.x != cached_origin_x) + test_x_offset += source_turf.x - cached_origin_x + cached_origin_x = source_turf.x + + co_updated = TRUE + + if (source_turf.y != cached_origin_y) + test_y_offset += source_turf.y - cached_origin_y + cached_origin_y = source_turf.y + + co_updated = TRUE + + if (co_updated) + // We might be facing a wall now. + var/turf/front = get_step(source_turf, old_direction) + var/new_fo = (front && front.has_opaque_atom) + if (new_fo != facing_opaque) + facing_opaque = new_fo + regenerate_angle(ndir) + + update = TRUE + + if (update) + needs_update = LIGHTING_CHECK_UPDATE + else if (needs_update == LIGHTING_CHECK_UPDATE) + return // No change. - affecting_turfs.Cut() + var/list/datum/lighting_corner/corners = list() + var/list/turf/turfs = list() + var/thing + var/datum/lighting_corner/C + var/turf/T + var/list/Tcorners + var/Sx = pixel_turf.x // these are used by APPLY_CORNER_BY_HEIGHT + var/Sy = pixel_turf.y + var/Sz = pixel_turf.z + var/corner_height = LIGHTING_HEIGHT + var/actual_range = (light_angle && facing_opaque) ? light_range * LIGHTING_BLOCKED_FACTOR : light_range + var/test_x + var/test_y + + var/should_do_wedge = light_angle && !facing_opaque + + FOR_DVIEW(T, Ceilm(actual_range, 1), source_turf, 0) do + if (should_do_wedge) // Directional lighting coordinate filter. + test_x = T.x - test_x_offset + test_y = T.y - test_y_offset + + // If the signs of these are the same, then the point is within the cone. + if ((DETERMINANT(limit_a_x, limit_a_y, test_x, test_y) > 0) || DETERMINANT(test_x, test_y, limit_b_x, limit_b_y) > 0) + continue - for(var/datum/lighting_corner/C in effect_str) - REMOVE_CORNER(C) + if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(T) || T.light_source_solo || T.light_source_multi) + Tcorners = T.corners + if (!T.lighting_corners_initialised) + T.lighting_corners_initialised = TRUE - C.affecting -= src + if (!Tcorners) + T.corners = list(null, null, null, null) + Tcorners = T.corners - effect_str.Cut() + for (var/i = 1 to 4) + if (Tcorners[i]) + continue -/datum/light_source/proc/recalc_corner(datum/lighting_corner/C) - if(effect_str.Find(C)) // Already have one. - REMOVE_CORNER(C) + Tcorners[i] = new /datum/lighting_corner(T, LIGHTING_CORNER_DIAGONAL[i], i) - APPLY_CORNER(C) + if (!T.has_opaque_atom) + for (var/v in 1 to 4) + var/val = Tcorners[v] + if (val) + corners[val] = 0 -/datum/light_source/proc/smart_vis_update() - var/list/datum/lighting_corner/corners = list() - var/list/turf/turfs = list() - FOR_DVIEW(var/turf/T, light_outer_range, source_turf, 0) - if (!T) - continue - if(!T.lighting_corners_initialised) - T.generate_missing_corners() - corners |= T.get_corners() - turfs += T - - var/turf/simulated/open/O = T - if(istype(O) && O.below) - // Consider the turf below us as well. (Z-lights) - for(T = O.below; !isnull(T); T = update_the_turf(T,corners, turfs)); + turfs += T + + // Upwards lights are handled at the corner level, so only search down. + // This is a do-while associated with the FOR_DVIEW above. + while (T && (T.z_flags & ZM_ALLOW_LIGHTING) && (T = T.below)) END_FOR_DVIEW + LAZYINITLIST(affecting_turfs) + var/list/L = turfs - affecting_turfs // New turfs, add us to the affecting lights of them. affecting_turfs += L - for(var/turf/T in 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(var/turf/T in L) + for (thing in L) + T = thing LAZYREMOVE(T.affecting_lights, src) - for(var/datum/lighting_corner/C in corners - effect_str) // New corners - C.affecting += src - if(!C.active) - effect_str[C] = 0 - continue + LAZYINITLIST(effect_str) + if (needs_update == LIGHTING_VIS_UPDATE) + for (thing in corners - effect_str) + C = thing + LAZYADD(C.affecting, src) + if (!C.active) + effect_str[C] = 0 + continue + + APPLY_CORNER_BY_HEIGHT(now) + else + L = corners - effect_str + for (thing in L) + C = thing + LAZYADD(C.affecting, src) + if (!C.active) + effect_str[C] = 0 + continue + + APPLY_CORNER_BY_HEIGHT(now) + + for (thing in corners - L) + C = thing + if (!C.active) + effect_str[C] = 0 + continue - APPLY_CORNER(C) + APPLY_CORNER_BY_HEIGHT(now) - for(var/datum/lighting_corner/C in effect_str - corners) // Old, now gone, corners. - REMOVE_CORNER(C) - C.affecting -= src - effect_str -= C + L = effect_str - corners + for (thing in L) + C = thing + REMOVE_CORNER(C, now) + LAZYREMOVE(C.affecting, src) + effect_str -= L -/datum/light_source/proc/update_the_turf(turf/T, list/datum/lighting_corner/corners, list/turf/turfs) - if(!T.lighting_corners_initialised) - T.generate_missing_corners() - corners |= T.get_corners() - turfs += T + applied_lum_r = lum_r + applied_lum_g = lum_g + applied_lum_b = lum_b - var/turf/simulated/open/O = T - if(istype(O) && O.below) - return O.below - return null + UNSETEMPTY(effect_str) + UNSETEMPTY(affecting_turfs) -#undef effect_update -#undef LUM_FALLOFF -#undef REMOVE_CORNER -#undef APPLY_CORNER +#undef INTELLIGENT_UPDATE +#undef DETERMINANT +#undef GET_APPROXIMATE_PIXEL_DIR +#undef UPDATE_APPROXIMATE_PIXEL_TURF diff --git a/code/modules/lighting/lighting_turf.dm b/code/modules/lighting/lighting_turf.dm index 8071ca8d4796f..eea95935de39e 100644 --- a/code/modules/lighting/lighting_turf.dm +++ b/code/modules/lighting/lighting_turf.dm @@ -1,113 +1,273 @@ -/// Does the turf use dynamic lighting? -/turf/var/dynamic_lighting = TRUE -/turf/luminosity = 1 +/turf + var/dynamic_lighting = TRUE + var/ambient_light // If non-null, a hex RGB light color that should be applied to this turf. + var/ambient_light_multiplier = 0.3 // The power of the above is multiplied by this. Setting too high may drown out normal lights on the same turf. + luminosity = 1 -/turf/var/lighting_corners_initialised = FALSE + var/lighting_corners_initialised = FALSE -/// List of light sources affecting this turf. -/turf/var/list/datum/light_source/affecting_lights -/// Our lighting overlay. -/turf/var/atom/movable/lighting_overlay/lighting_overlay -/turf/var/list/datum/lighting_corner/corners -/turf/var/opaque_counter + var/list/datum/light_source/affecting_lights // List of light sources affecting this turf. + var/atom/movable/lighting_overlay/lighting_overlay // Our lighting overlay. + var/list/datum/lighting_corner/corners -/turf/set_opacity(new_opacity) + var/ambient_has_indirect = FALSE // If this is TRUE, an above turf's ambient light is affecting this turf. + + // Record-keeping, do not touch -- that means you, admins. + var/ambient_active = FALSE //! Do we have non-zero ambient light? Use [TURF_IS_AMBIENT_LIT] instead of reading this directly. + var/ambient_light_old_r = 0 + var/ambient_light_old_g = 0 + var/ambient_light_old_b = 0 + + var/ambient_bitflag = 0 + +//Done on init if mapload, done post copying corners if changeturf +/turf/proc/setup_local_ambient() + return + +/turf/Initialize(mapload, ...) . = ..() - if(opacity == new_opacity) - return FALSE + if(mapload) + setup_local_ambient() - opacity = new_opacity - return RecalculateOpacity() +/turf/proc/set_ambient_light(color, multiplier) + if (color == ambient_light && multiplier == ambient_light_multiplier) + return -/turf/proc/RecalculateOpacity() - var/old_opaque_counter = opaque_counter + ambient_light = color || ambient_light + ambient_light_multiplier = multiplier || ambient_light_multiplier + if (!ambient_light_multiplier) + ambient_light_multiplier = initial(ambient_light_multiplier) - opaque_counter = opacity - for(var/a in src) - var/atom/A = a - opaque_counter += A.opacity + update_ambient_light() - // If the counter changed and was or became 0 then lift event/reconsider lights - if(opaque_counter != old_opaque_counter && (!opaque_counter || !old_opaque_counter)) - GLOB.opacity_set_event.raise_event(src, !opaque_counter, !!opaque_counter) - reconsider_lights() - return TRUE - return FALSE +/turf/proc/replace_ambient_light(old_color, new_color, old_multiplier, new_multiplier = 0) + if (!TURF_IS_AMBIENT_LIT_UNSAFE(src)) + add_ambient_light(new_color, new_multiplier) + return + + ASSERT(old_multiplier) // omitting new_multiplier is allowed for removing light nondestructively + + old_color ||= COLOR_WHITE + new_color ||= COLOR_WHITE + + var/list/old_parts = rgb2num(old_color) + var/list/new_parts = rgb2num(new_color) + + var/dr = (new_parts[1] / 255) * new_multiplier - (old_parts[1] / 255) * old_multiplier + var/dg = (new_parts[2] / 255) * new_multiplier - (old_parts[2] / 255) * old_multiplier + var/db = (new_parts[3] / 255) * new_multiplier - (old_parts[3] / 255) * old_multiplier + + if (!dr && !dg && !db) + return + + add_ambient_light_raw(dr, dg, db) + +/turf/proc/add_ambient_light(color, multiplier, update = TRUE) + if (!color) + return + + multiplier ||= ambient_light_multiplier + + var/list/ambient_parts = rgb2num(color) + + var/ambient_r = (ambient_parts[1] / 255) * multiplier + var/ambient_g = (ambient_parts[2] / 255) * multiplier + var/ambient_b = (ambient_parts[3] / 255) * multiplier + + add_ambient_light_raw(ambient_r, ambient_g, ambient_b, update) + +/turf/proc/add_ambient_light_raw(lr, lg, lb, update = TRUE) + if (!lr && !lg && !lb) + if (!ambient_light_old_r || !ambient_light_old_g || !ambient_light_old_b) + ambient_active = FALSE + SSlighting.total_ambient_turfs -= 1 + return + + if (!ambient_active) + SSlighting.total_ambient_turfs += 1 + ambient_active = TRUE + + // There are four corners per (lit) turf, we don't want to apply our light 4 times -- compensate by dividing by 4. + lr /= 4 + lg /= 4 + lb /= 4 + + lr = round(lr, LIGHTING_ROUND_VALUE) + lg = round(lg, LIGHTING_ROUND_VALUE) + lb = round(lb, LIGHTING_ROUND_VALUE) + + ambient_light_old_r += lr + ambient_light_old_g += lg + ambient_light_old_b += lb + + if (!corners || !lighting_corners_initialised) + generate_missing_corners() + + // This list can contain nulls on things like space turfs -- they only have their neighbors' corners. + for (var/datum/lighting_corner/C in corners) + C.update_ambient_lumcount(lr, lg, lb, !update) + +/turf/proc/clear_ambient_light() + if (ambient_light == null) + return + + ambient_light = null + update_ambient_light() + +/turf/proc/update_ambient_light(no_corner_update = FALSE) + // These are deltas. + var/ambient_r = 0 + var/ambient_g = 0 + var/ambient_b = 0 + + if (ambient_light) + var/list/parts = rgb2num(ambient_light) + ambient_r = ((parts[1] / 255) * ambient_light_multiplier) - ambient_light_old_r + ambient_g = ((parts[2] / 255) * ambient_light_multiplier) - ambient_light_old_g + ambient_b = ((parts[3] / 255) * ambient_light_multiplier) - ambient_light_old_b + else + ambient_r = -ambient_light_old_r + ambient_g = -ambient_light_old_g + ambient_b = -ambient_light_old_b + + add_ambient_light_raw(ambient_r, ambient_g, ambient_b, !no_corner_update) // Causes any affecting light sources to be queued for a visibility update, for example a door got opened. /turf/proc/reconsider_lights() - for(var/datum/light_source/L in affecting_lights) + var/datum/light_source/L + for (var/thing in affecting_lights) + L = thing L.vis_update() +// Forces a lighting update. Reconsider lights is preferred when possible. +/turf/proc/force_update_lights() + var/datum/light_source/L + for (var/thing in affecting_lights) + L = thing + L.force_update() + /turf/proc/lighting_clear_overlay() - if(lighting_overlay) - qdel(lighting_overlay) + if (lighting_overlay) + if (lighting_overlay.loc != src) + stack_trace("Lighting overlay variable on turf [log_info_line(src)] is insane, lighting overlay actually located on [log_info_line(lighting_overlay.loc)]!") + + qdel(lighting_overlay, TRUE) + lighting_overlay = null - for(var/datum/lighting_corner/C in corners) + for (var/datum/lighting_corner/C in corners) C.update_active() // Builds a lighting overlay for us, but only if our area is dynamic. -/turf/proc/lighting_build_overlay() - if(lighting_overlay) - return +/turf/proc/lighting_build_overlay(now = FALSE) + if (lighting_overlay) + return //In Cit this wont happen, bay has a slightly different init so just returning is fine + //CRASH("Attempted to create lighting_overlay on tile that already had one.") - var/area/A = loc - if(A.dynamic_lighting && dynamic_lighting) - if(!lighting_corners_initialised) + if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(src)) + if (!lighting_corners_initialised || !corners) generate_missing_corners() - new /atom/movable/lighting_overlay(src) + new /atom/movable/lighting_overlay(src, now) - for(var/datum/lighting_corner/C in corners) - if(!C.active) // We would activate the corner, calculate the lighting for it. - for(var/L in C.affecting) + for (var/datum/lighting_corner/C in corners) + if (!C.active) // We would activate the corner, calculate the lighting for it. + for (var/L in C.affecting) var/datum/light_source/S = L - S.recalc_corner(C) + S.recalc_corner(C, TRUE) C.active = TRUE +// Returns the average color of this tile. Roughly corresponds to the color of a single old-style lighting overlay. +/turf/proc/get_avg_color() + if (!lighting_overlay) + return null + + var/lum_r + var/lum_g + var/lum_b + + for (var/datum/lighting_corner/L in corners) + lum_r += L.apparent_r + lum_g += L.apparent_g + lum_b += L.apparent_b + + lum_r = CLAMP01(lum_r / 4) * 255 + lum_g = CLAMP01(lum_g / 4) * 255 + lum_b = CLAMP01(lum_b / 4) * 255 + + return "#[num2hex(lum_r)][num2hex(lum_g)][num2hex(lum_b)]" + +#define SCALE(targ,min,max) (targ - min) / (max - min) + // Used to get a scaled lumcount. /turf/proc/get_lumcount(minlum = 0, maxlum = 1) - if(!lighting_overlay) - var/area/A = loc - if(A.dynamic_lighting && dynamic_lighting) - var/atom/movable/lighting_overlay/O = new /atom/movable/lighting_overlay(src) - lighting_overlay = O + if (!lighting_overlay) + return 0.5 var/totallums = 0 - for(var/datum/lighting_corner/L in corners) - totallums += max(L.lum_r, L.lum_g, L.lum_b) + for (var/datum/lighting_corner/L in corners) + totallums += L.apparent_r + L.apparent_b + L.apparent_g - totallums /= 4 // 4 corners, max channel selected, return the average + totallums /= 12 // 4 corners, each with 3 channels, get the average. - totallums =(totallums - minlum) /(maxlum - minlum) + totallums = SCALE(totallums, minlum, maxlum) return CLAMP01(totallums) -// If an opaque movable atom moves around we need to potentially update visibility. -/turf/Entered(atom/movable/AM, atom/OldLoc) - . = ..() - if(AM?.opacity) - RecalculateOpacity() +#undef SCALE + +// Can't think of a good name, this proc will recalculate the has_opaque_atom variable. +/turf/proc/recalc_atom_opacity() +#ifdef AO_USE_LIGHTING_OPACITY + var/old = has_opaque_atom +#endif + + has_opaque_atom = FALSE + if (opacity) + has_opaque_atom = TRUE + else + for (var/thing in src) // Loop through every movable atom on our tile + var/atom/movable/A = thing + if (A.opacity) + has_opaque_atom = TRUE + break // No need to continue if we find something opaque. + +#ifdef AO_USE_LIGHTING_OPACITY + if (old != has_opaque_atom) + regenerate_ao() +#endif -/turf/Exited(atom/movable/AM, atom/newloc) +/turf/Exited(atom/movable/Obj, atom/newloc) . = ..() - if(AM?.opacity) - RecalculateOpacity() -/turf/proc/get_corners() - if(opaque_counter) - return null // Since this proc gets used in a for loop, null won't be looped though. + if (!Obj) + return + + if (Obj.opacity) + recalc_atom_opacity() // Make sure to do this before reconsider_lights(), incase we're on instant updates. + reconsider_lights() - return corners +// This block isn't needed now, but it's here if supporting area dyn lighting changes is needed later. +// /turf/change_area(area/old_area, area/new_area) +// if (new_area.dynamic_lighting != old_area.dynamic_lighting) +// if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(src)) +// lighting_build_overlay() +// else +// lighting_clear_overlay() + +// This is inlined in lighting_source.dm. +// Update it too if you change this. /turf/proc/generate_missing_corners() + if (!TURF_IS_DYNAMICALLY_LIT_UNSAFE(src) && !light_source_solo && !light_source_multi && !(z_flags & ZM_ALLOW_LIGHTING) && !ambient_light && !ambient_has_indirect) + return + lighting_corners_initialised = TRUE - if(!corners) - corners = list(null, null, null, null) + if (!corners) + corners = new(4) - for(var/i = 1 to 4) - if(corners[i]) // Already have a corner on this direction. + for (var/i = 1 to 4) + if (corners[i]) // Already have a corner on this direction. continue - corners[i] = new /datum/lighting_corner(src, LIGHTING_CORNER_DIAGONAL[i]) + corners[i] = new/datum/lighting_corner(src, LIGHTING_CORNER_DIAGONAL[i], i) diff --git a/code/modules/mechs/equipment/engineering.dm b/code/modules/mechs/equipment/engineering.dm index e2649221cef61..2e71a23d79db0 100644 --- a/code/modules/mechs/equipment/engineering.dm +++ b/code/modules/mechs/equipment/engineering.dm @@ -73,7 +73,7 @@ /obj/effect/mech_shield/Initialize() . = ..() - set_light(0.8, 0.1, 1, 2, COLOR_SABER_BLUE) + set_light(1, 0.8, COLOR_SABER_BLUE) update_nearby_tiles(need_rebuild=1) /obj/effect/mech_shield/Destroy() diff --git a/code/modules/mechs/equipment/utility.dm b/code/modules/mechs/equipment/utility.dm index 13ac4c7b41e25..312c0eca24110 100644 --- a/code/modules/mechs/equipment/utility.dm +++ b/code/modules/mechs/equipment/utility.dm @@ -274,9 +274,8 @@ var/on = 0 - var/l_max_bright = 0.9 - var/l_inner_range = 1 - var/l_outer_range = 6 + var/l_power = 2 + var/l_range = 6 origin_tech = list(TECH_MATERIAL = 1, TECH_ENGINEERING = 1) /obj/item/mech_equipment/light/installed(mob/living/exosuit/_owner) @@ -304,7 +303,7 @@ /obj/item/mech_equipment/light/on_update_icon() if(on) icon_state = "[initial(icon_state)]-on" - set_light(l_max_bright, l_inner_range, l_outer_range) + set_light(l_range, l_power, angle = LIGHT_WIDE) else icon_state = "[initial(icon_state)]" set_light(0, 0) @@ -793,7 +792,7 @@ . = ..() if (active) icon_state = "mech_jet_on" - set_light(1, 1, 1, l_color = COLOR_LIGHT_CYAN) + set_light(1, 1, l_color = COLOR_LIGHT_CYAN) else icon_state = "mech_jet_off" set_light(0) diff --git a/code/modules/mining/mine_items.dm b/code/modules/mining/mine_items.dm index 4c0e2409c949a..1e4385d32b21c 100644 --- a/code/modules/mining/mine_items.dm +++ b/code/modules/mining/mine_items.dm @@ -241,7 +241,7 @@ addon.layer = ABOVE_LIGHTING_LAYER addon.plane = EFFECTS_ABOVE_LIGHTING_PLANE AddOverlays(addon) - set_light(0.5, 0.5, 3) + set_light(3, 0.5) else pixel_x = rand(-randpixel, randpixel) pixel_y = rand(-randpixel, randpixel) diff --git a/code/modules/mob/dview.dm b/code/modules/mob/dview.dm new file mode 100644 index 0000000000000..816c757082cce --- /dev/null +++ b/code/modules/mob/dview.dm @@ -0,0 +1,27 @@ +//DVIEW is a hack that uses a mob with darksight in order to find lists of viewable stuff while ignoring darkness +// Defines for dview are elsewhere. + +var/global/mob/dview/dview_mob = new + +/mob/dview + anchored = TRUE + density = FALSE + invisibility = INVISIBILITY_ABSTRACT + see_in_dark = 1e6 + simulated = FALSE + virtual_mob = null + +/mob/dview/Destroy(force = FALSE) + SHOULD_CALL_PARENT(FALSE) + if (!force) + return QDEL_HINT_LETMELIVE + + crash_with("Forced deletion of dview mob, this should not happen! : [log_info_line(src)]") + + dview_mob = new + return QDEL_HINT_QUEUE + +/mob/dview/Initialize() + . = ..() + // We don't want to be in any mob lists; we're a dummy not a mob. + STOP_PROCESSING_MOB(src) diff --git a/code/modules/mob/living/bot/bot.dm b/code/modules/mob/living/bot/bot.dm index 6893de9bdcee0..c1a8d5373fc1d 100644 --- a/code/modules/mob/living/bot/bot.dm +++ b/code/modules/mob/living/bot/bot.dm @@ -374,7 +374,7 @@ if(stat) return 0 on = 1 - set_light(0.5, 0.1, light_strength) + set_light(light_strength, 0.5) update_icons() resetTarget() patrol_path = list() diff --git a/code/modules/mob/living/carbon/alien/diona/nymph_life.dm b/code/modules/mob/living/carbon/alien/diona/nymph_life.dm index f33a947b6f3b1..0404aa3482044 100644 --- a/code/modules/mob/living/carbon/alien/diona/nymph_life.dm +++ b/code/modules/mob/living/carbon/alien/diona/nymph_life.dm @@ -17,7 +17,7 @@ else var/mult = clamp(radiation/200, 0.5, 1) if(last_glow != mult) - set_light(mult, 0.5, (5*mult), 2, "#55ff55") + set_light((5*mult), mult, "#55ff55") last_glow = mult set_nutrition(clamp(nutrition + floor(radiation/100) + light_amount, 0, 500)) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 2e97b3286c5b0..7c1d7309a6e3a 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -1114,6 +1114,40 @@ to_chat(src, SPAN_NOTICE("You look up.")) reset_view(z_eye) return + + var/turf/T= get_turf(src) + + if(T.is_outside())// They're outside and hopefully on a planet. + var/obj/effect/overmap/visitable/sector/exoplanet/E = map_sectors["[T.z]"] + if (!istype(E)) + to_chat(usr, SPAN_NOTICE("You see... things, it's hard to put into words what you're seeing specifically.")) + return + + //Weather hook here when it is a thing + + // Sun-related output. + //Calculate time of day + var/time_of_day = E.sun_last_process % E.daycycle + var/afternoon = time_of_day > (E.daycycle / 2) + var/star_name = GLOB.using_map.system_name + + var/sun_message = null + switch(E.sun_position) + if(0 to 0.4) // Night + sun_message = "It is night time, [star_name] is not visible." + if(0.4 to 0.5) // Twilight + sun_message = "The sky is in twilight, however [star_name] is not visible." + if(0.5 to 0.7) // Sunrise/set. + sun_message = "[star_name] is slowly [!afternoon ? "rising from" : "setting on"] the horizon." + if(0.7 to 0.9) // Morning/evening + sun_message = "[star_name]'s position implies it is currently [!afternoon ? "early" : "late"] in the day." + if(0.9 to 1.0) // Noon + sun_message = "It's high noon. [star_name] hangs directly above you." + + to_chat(usr, SPAN_NOTICE(sun_message)) + return + + to_chat(src, SPAN_NOTICE("You can see \the [above ? above : "ceiling"].")) else to_chat(src, SPAN_NOTICE("You can't look up right now.")) diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index b2dc3683bdaff..fe83411edc3a6 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -231,7 +231,7 @@ set_light(0) else if(species.appearance_flags & SPECIES_APPEARANCE_RADIATION_GLOWS) - set_light(0.3, 0.1, max(1,min(20,radiation/20)), 2, species.get_flesh_colour(src)) + set_light(max(1,min(20,radiation/20)), 0.3, species.get_flesh_colour(src)) // END DOGSHIT SNOWFLAKE var/obj/item/organ/internal/diona/nutrients/rad_organ = locate() in internal_organs @@ -885,7 +885,7 @@ //0.1% chance of playing a scary sound to someone who's in complete darkness if(isturf(loc) && rand(1,1000) == 1) var/turf/T = loc - if(T.get_lumcount() <= LIGHTING_SOFT_THRESHOLD) + if(T.get_lumcount() <= 0) playsound_local(src,pick(GLOB.scarySounds),50, 1, -1) var/area/A = get_area(src) diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index 8151bd3f7e2f2..a1e459d93a871 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -193,8 +193,6 @@ reset_view(null) /mob/living/proc/update_sight() - set_sight(0) - set_see_in_dark(0) if(stat == DEAD || eyeobj) update_dead_sight() else @@ -226,3 +224,52 @@ /mob/living/proc/handle_hud_icons_health() return + +//Adaptative darksight +//Ideally this would run instantly as mob updates are a bit too slow for this (noticeable when moving fast), but set_see_in_dark is called several timees. +//For the time being it's instant and called whenever see in dark changes. Replace with a single call at end of updates once code is not spaghetti +/mob/living/proc/handle_darksight() + if(!darksight) + return + + //For testing purposes + var/darksightedness = min(see_in_dark/world.view,1.0) //A ratio of how good your darksight is, from 'nada' to 'really darn good' + var/current = darksight.alpha/255 //Our current adjustedness + var/adjusted_diameter = (0.5 + (see_in_dark - 1)) * 2 + var/newScale = min((adjusted_diameter) * (world.icon_size/DARKSIGHT_GRADIENT_SIZE), 1)*0.9 //Scale the darksight gradient + + var/brightness = 0.0 //We'll assume it's superdark if we can't find something else. + + + //Currently we're going to assume that only thing that matter is your turf + //This is not necessarily correct. + //We may want to blind people inside exosuits and such. At some point we could try moving lumcount to atom, default to turf and then we can make those override + + var/turf/my_turf = get_turf(src) + if(isturf(my_turf)) + brightness = my_turf.get_lumcount() + + brightness = min((brightness + brightness*brightness), 1) //Increase apparent brightness so it's not that obvious. TODO: Make this a curve + + var/darkness = 1-brightness //Silly, I know, but 'alpha' and 'darkness' go the same direction on a number line + newScale *= darkness // you see further in the dark, in fully lit areas you don't get a bonus + var/adjust_to = min(darkness,darksightedness)//Capped by how darksighted they are + var/distance = abs(current-adjust_to) //Used for how long to animate for + var/negative = current > adjust_to //Unfortunately due to a visual issue this must be instant if we go down 1 level of darksight + + if((distance < 0.001) && (abs(darksight.transform.a - newScale) < 0.01)) return //We're already all set + + if(negative) + distance = 0 //Make it instant + + //TODO:. + //FIX VISION CODE! There is no correct place to update darksight as it keeps being reset and enabled several times a frame (even placing it on Life doesnt work because overrides set it in wrong function) + // Time = 0 means instant change, avoids some issues of animation resetting several times a frame + distance = 0 + + animate(darksight, alpha = (adjust_to*255), transform = matrix().Update(scale_x = newScale, scale_y = newScale), time = (distance*1 SECOND), flags = ANIMATION_LINEAR_TRANSFORM) + +//Need to update every time we set see in dark as it can be called at many different points and waiting for next frame causes visual artifacts +/mob/living/set_see_in_dark(new_see_in_dark) + . = ..() + handle_darksight() diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 85170b9e950a6..22927c7925d5f 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -256,7 +256,7 @@ /mob/living/proc/IgniteMob() if(fire_stacks > 0 && !on_fire) on_fire = 1 - set_light(0.6, 0.1, 4, l_color = COLOR_ORANGE) + set_light(4, 0.6, l_color = COLOR_ORANGE) update_fire() /mob/living/proc/ExtinguishMob() diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 88c994145126f..017f90e34b8d3 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -449,7 +449,7 @@ var/global/list/ai_verbs_default = list( camera = A ..() if(istype(A,/obj/machinery/camera)) - if(camera_light_on) A.set_light(0.5, 0.1, AI_CAMERA_LUMINOSITY) + if(camera_light_on) A.set_light(AI_CAMERA_LUMINOSITY, 0.5) else A.set_light(0) @@ -599,7 +599,7 @@ var/global/list/ai_verbs_default = list( src.camera.set_light(0) if(!camera.light_disabled) src.camera = camera - src.camera.set_light(0.5, 0.1, AI_CAMERA_LUMINOSITY) + src.camera.set_light(AI_CAMERA_LUMINOSITY, 0.5) else src.camera = null else if(isnull(camera)) @@ -609,7 +609,7 @@ var/global/list/ai_verbs_default = list( var/obj/machinery/camera/camera = near_range_camera(src.eyeobj) if(camera && !camera.light_disabled) src.camera = camera - src.camera.set_light(0.5, 0.1, AI_CAMERA_LUMINOSITY) + src.camera.set_light(AI_CAMERA_LUMINOSITY, 0.5) camera_light_on = world.timeofday + 1 * 20 // Update the light every 2 seconds. @@ -709,13 +709,13 @@ var/global/list/ai_verbs_default = list( icon = selected_sprite.icon if(stat == DEAD) icon_state = selected_sprite.dead_icon - set_light(0.7, 0.1, 1, 2, selected_sprite.dead_light) + set_light(1, 0.7, selected_sprite.dead_light) else if(!has_power()) icon_state = selected_sprite.nopower_icon - set_light(0.4, 0.1, 1, 2, selected_sprite.nopower_light) + set_light(1, 0.4, selected_sprite.nopower_light) else icon_state = selected_sprite.alive_icon - set_light(0.4, 0.1, 1, 2, selected_sprite.alive_light) + set_light(1, 0.4, selected_sprite.alive_light) // Pass lying down or getting up to our pet human, if we're in a rig. /mob/living/silicon/ai/lay_down() diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm index 7a5809f98aa20..7a976e2d8aac7 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -73,9 +73,8 @@ GLOBAL_LIST_INIT(possible_say_verbs, list( var/translator_on = 0 // keeps track of the translator module - var/flashlight_max_bright = 0.5 //brightness of light when on, must be no greater than 1. - var/flashlight_inner_range = 1 //inner range of light when on, can be negative - var/flashlight_outer_range = 3 //outer range of light when on, can be negative + var/flashlight_power = 0.5 //brightness of light when on, must be no greater than 1. + var/flashlight_range = 3 //outer range of light when on, can be negative var/light_on = FALSE hud_type = /datum/hud/pai @@ -325,7 +324,7 @@ GLOBAL_LIST_INIT(possible_say_verbs, list( /mob/living/silicon/pai/proc/toggle_integrated_light() if(!light_on) - set_light(flashlight_max_bright, flashlight_inner_range, flashlight_outer_range, 2) + set_light(flashlight_range, flashlight_power, 2) to_chat(src, SPAN_NOTICE("You enable your integrated light.")) light_on = TRUE else diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm index f1aeb74cd0945..1c5da33484c4a 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone.dm @@ -36,7 +36,7 @@ var/global/list/mob_hat_cache = list() lawupdate = FALSE density = TRUE req_access = list(access_engine, access_robotics) - integrated_light_max_bright = 0.5 + integrated_light_power = 0.5 local_transmit = 1 possession_candidate = 1 diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index a73a3eff911c9..3ba5e50d0ea5e 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -23,7 +23,7 @@ var/custom_sprite = FALSE var/crisis //Admin-settable for combat module use. var/crisis_override = FALSE - var/integrated_light_max_bright = 0.75 + var/integrated_light_power = 0.75 var/datum/wires/robot/wires var/module_category = ROBOT_MODULE_TYPE_GROUNDED var/dismantle_type = /obj/item/robot_parts/robot_suit @@ -408,9 +408,9 @@ /mob/living/silicon/robot/proc/update_robot_light() if(lights_on) if(intenselight) - set_light(1, 2, 6) + set_light(6, 1) else - set_light(0.75, 1, 4) + set_light(4, 0.75) else set_light(0) diff --git a/code/modules/mob/living/silicon/robot/robot_items.dm b/code/modules/mob/living/silicon/robot/robot_items.dm index 73d169f1e8afb..7482a8aae5647 100644 --- a/code/modules/mob/living/silicon/robot/robot_items.dm +++ b/code/modules/mob/living/silicon/robot/robot_items.dm @@ -114,7 +114,7 @@ /obj/item/party_light/on_update_icon() if (activated) icon_state = "partylight-on" - set_light(1, 1, 7) + set_light(7, 1) else icon_state = "partylight_off" set_light(0) diff --git a/code/modules/mob/living/simple_animal/constructs/constructs.dm b/code/modules/mob/living/simple_animal/constructs/constructs.dm index 7b8df30f8f80e..9f06592e52fc0 100644 --- a/code/modules/mob/living/simple_animal/constructs/constructs.dm +++ b/code/modules/mob/living/simple_animal/constructs/constructs.dm @@ -280,7 +280,7 @@ eye_glow.plane = EFFECTS_ABOVE_LIGHTING_PLANE eye_glow.layer = EYE_GLOW_LAYER AddOverlays(eye_glow) - set_light(-2, 0.1, 1.5, l_color = "#ffffff") + set_light(1.5, -2, l_color = "#ffffff") z_flags |= ZMM_MANGLE_PLANES ////////////////HUD////////////////////// diff --git a/code/modules/mob/living/simple_animal/hostile/bluespace.dm b/code/modules/mob/living/simple_animal/hostile/bluespace.dm index 0fb7387aa20b4..3b7a343c0e93c 100644 --- a/code/modules/mob/living/simple_animal/hostile/bluespace.dm +++ b/code/modules/mob/living/simple_animal/hostile/bluespace.dm @@ -16,8 +16,8 @@ say_list = /datum/say_list/bluespace natural_weapon = /obj/item/natural_weapon/bluespace light_color = "#4da6ff" - light_outer_range = 2 - light_max_bright = 1 + light_range = 2 + light_power = 1 bleed_colour = "#0000ff" /mob/living/simple_animal/hostile/bluespace/Process_Spacemove() diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spider/nurse.dm b/code/modules/mob/living/simple_animal/hostile/giant_spider/nurse.dm index f5ad5d0b47713..631de88335e2c 100644 --- a/code/modules/mob/living/simple_animal/hostile/giant_spider/nurse.dm +++ b/code/modules/mob/living/simple_animal/hostile/giant_spider/nurse.dm @@ -209,7 +209,7 @@ /obj/effect/spider/stickyweb/dark/Initialize() . = ..() - set_light(-1, 0.5, 1, 1, l_color = "#ffffff") + set_light(1, -1, l_color = "#ffffff") // The AI for nurse spiders. Wraps things in webs by 'attacking' them. /datum/ai_holder/simple_animal/melee/nurse_spider diff --git a/code/modules/mob/living/simple_animal/hostile/vagrant.dm b/code/modules/mob/living/simple_animal/hostile/vagrant.dm index 0aad8485596ec..d0b94a707501d 100644 --- a/code/modules/mob/living/simple_animal/hostile/vagrant.dm +++ b/code/modules/mob/living/simple_animal/hostile/vagrant.dm @@ -111,7 +111,7 @@ else //It's fight time alpha = 255 icon_state = "vagrant_glowing" - set_light(0.2, 0.1, 3) + set_light(3, 0.2) move_to_delay = 2 /mob/living/simple_animal/hostile/vagrant/swarm/Initialize() diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm index 2de61bafa0d8c..496e6e9c2a982 100644 --- a/code/modules/mob/login.dm +++ b/code/modules/mob/login.dm @@ -92,8 +92,8 @@ if(eyeobj) eyeobj.possess(src) - l_general = new() - client.screen += l_general + darksight = new() + client.screen += darksight CreateRenderers() @@ -118,3 +118,10 @@ . = ..() if(internals && internal) internals.icon_state = "internal1" + +/mob/observer/ghost/Login() + . = ..() + if(darksight) + darksight.icon_state = "ghost" + darksight.alpha = 127 + darksight.SetTransform(2) //Max darksight diff --git a/code/modules/mob/logout.dm b/code/modules/mob/logout.dm index 05d4206efcb14..1bf481795d7c5 100644 --- a/code/modules/mob/logout.dm +++ b/code/modules/mob/logout.dm @@ -4,11 +4,10 @@ log_access("Logout: [key_name(src)]") handle_admin_logout() if(my_client) - my_client.screen -= l_general - + my_client.screen -= darksight RemoveRenderers() - QDEL_NULL(l_general) + QDEL_NULL(darksight) hide_client_images() ..() diff --git a/code/modules/modular_computers/computers/modular_computer/core.dm b/code/modules/modular_computers/computers/modular_computer/core.dm index de518efca29bc..71907054b189e 100644 --- a/code/modules/modular_computers/computers/modular_computer/core.dm +++ b/code/modules/modular_computers/computers/modular_computer/core.dm @@ -86,7 +86,7 @@ AddOverlays(os.get_keyboard_overlay()) if(enabled) - set_light(0.2, 0.1, light_strength) + set_light(light_strength, 0.2) else set_light(0) diff --git a/code/modules/modular_computers/computers/subtypes/dev_console.dm b/code/modules/modular_computers/computers/subtypes/dev_console.dm index 1cdd458090d66..93dc31bab52e8 100644 --- a/code/modules/modular_computers/computers/subtypes/dev_console.dm +++ b/code/modules/modular_computers/computers/subtypes/dev_console.dm @@ -48,7 +48,7 @@ var/datum/extension/interactive/ntos/os = get_extension(src, /datum/extension/interactive/ntos) if(os) if(os.on) - set_light(light_max_bright_on, light_inner_range_on, light_outer_range_on, 2, light_color) + set_light(light_range_on, light_power_on, light_color) else set_light(0) diff --git a/code/modules/multiz/zmimic/mimic_movable.dm b/code/modules/multiz/zmimic/mimic_movable.dm index 81ed570387bc9..fe780df31e673 100644 --- a/code/modules/multiz/zmimic/mimic_movable.dm +++ b/code/modules/multiz/zmimic/mimic_movable.dm @@ -79,10 +79,11 @@ name = "openspace multiplier" desc = "You shouldn't see this." icon = 'icons/effects/lighting_overlay.dmi' - icon_state = "dark" + icon_state = LIGHTING_TRANSPARENT_ICON_STATE plane = OPENTURF_MAX_PLANE layer = MIMICED_LIGHTING_LAYER - color = "#0000004b" + color = SHADOWER_DARKENING_COLOR + //blend_mode = BLEND_MULTIPLY /atom/movable/openspace/multiplier/Destroy() var/turf/myturf = loc @@ -96,38 +97,36 @@ layer = MIMICED_LIGHTING_LAYER plane = OPENTURF_MAX_PLANE invisibility = 0 - blend_mode = BLEND_MULTIPLY - if (icon_state == null) + + if (icon_state == LIGHTING_BASE_ICON_STATE) + blend_mode = BLEND_MULTIPLY // We're using a color matrix, so just darken the colors across the board. - // Bay stores lights as inverted so the lighting PM can invert it for darksight, but - // we don't have a plane master, so invert it again. var/list/c_list = color - c_list[CL_MATRIX_RR] *= -SHADOWER_DARKENING_FACTOR - c_list[CL_MATRIX_RG] *= -SHADOWER_DARKENING_FACTOR - c_list[CL_MATRIX_RB] *= -SHADOWER_DARKENING_FACTOR - c_list[CL_MATRIX_GR] *= -SHADOWER_DARKENING_FACTOR - c_list[CL_MATRIX_GG] *= -SHADOWER_DARKENING_FACTOR - c_list[CL_MATRIX_GB] *= -SHADOWER_DARKENING_FACTOR - c_list[CL_MATRIX_BR] *= -SHADOWER_DARKENING_FACTOR - c_list[CL_MATRIX_BG] *= -SHADOWER_DARKENING_FACTOR - c_list[CL_MATRIX_BB] *= -SHADOWER_DARKENING_FACTOR - c_list[CL_MATRIX_AR] *= -SHADOWER_DARKENING_FACTOR - c_list[CL_MATRIX_AG] *= -SHADOWER_DARKENING_FACTOR - c_list[CL_MATRIX_AB] *= -SHADOWER_DARKENING_FACTOR + c_list[CL_MATRIX_RR] *= SHADOWER_DARKENING_FACTOR + c_list[CL_MATRIX_RG] *= SHADOWER_DARKENING_FACTOR + c_list[CL_MATRIX_RB] *= SHADOWER_DARKENING_FACTOR + c_list[CL_MATRIX_GR] *= SHADOWER_DARKENING_FACTOR + c_list[CL_MATRIX_GG] *= SHADOWER_DARKENING_FACTOR + c_list[CL_MATRIX_GB] *= SHADOWER_DARKENING_FACTOR + c_list[CL_MATRIX_BR] *= SHADOWER_DARKENING_FACTOR + c_list[CL_MATRIX_BG] *= SHADOWER_DARKENING_FACTOR + c_list[CL_MATRIX_BB] *= SHADOWER_DARKENING_FACTOR + c_list[CL_MATRIX_AR] *= SHADOWER_DARKENING_FACTOR + c_list[CL_MATRIX_AG] *= SHADOWER_DARKENING_FACTOR + c_list[CL_MATRIX_AB] *= SHADOWER_DARKENING_FACTOR color = c_list + + //Hackfix until I look into planes a bit more, we copy the plane of turf + var/turf/myturf = loc + if (istype(myturf)) + plane = myturf.plane else - // Not a color matrix, so we just ignore the lighting values. - icon_state = "dark" // this is actually just a white sprite, which is what this blending needs - color = list( - SHADOWER_DARKENING_FACTOR, 0, 0, - 0, SHADOWER_DARKENING_FACTOR, 0, - 0, 0, SHADOWER_DARKENING_FACTOR - ) + // Not a color matrix, so we can just use the color var ourselves. + color = (icon_state == LIGHTING_DARKNESS_ICON_STATE) ? COLOR_WHITE : SHADOWER_DARKENING_COLOR var/turf/parent = loc ASSERT(isturf(parent)) - if (LAZYLEN(parent.ao_overlays_mimic)) - AddOverlays(parent.ao_overlays_mimic) + UpdateOverlays() if (bound_overlay) update_above() diff --git a/code/modules/multiz/zmimic/mimic_turf.dm b/code/modules/multiz/zmimic/mimic_turf.dm index f708119d10c84..400170e114378 100644 --- a/code/modules/multiz/zmimic/mimic_turf.dm +++ b/code/modules/multiz/zmimic/mimic_turf.dm @@ -70,6 +70,23 @@ update_mimic(!mapload) // Only recursively update if the map isn't loading. + //Update lights if mapload, else if we're changing turf this will be overriden by corner copy step + if(mapload) + rebuild_zbleed() + +//Force reconsider zbleed +/turf/proc/rebuild_zbleed() + //Only relevant if dynamically lit + var/turf/under = GetBelow(src) + if(TURF_IS_DYNAMICALLY_LIT_UNSAFE(src) && under) + //We need to force recalculation of corners regardless, clear first + if(corners && length(corners)) + for (var/datum/lighting_corner/C in corners) + C.clear_below_lumcount() + if (under.corners && length(under.corners)) + for (var/datum/lighting_corner/C in under.corners) + C.rebuild_above_below_lumcount() + /// Cleans up Z-mimic objects for this turf. You shouldn't call this directly 99% of the time. /turf/proc/cleanup_zmimic() SSzcopy.openspace_turfs -= 1 diff --git a/code/modules/overmap/exoplanets/_exoplanet.dm b/code/modules/overmap/exoplanets/_exoplanet.dm index 4b22bddd8ce97..3deb2b495cbcc 100644 --- a/code/modules/overmap/exoplanets/_exoplanet.dm +++ b/code/modules/overmap/exoplanets/_exoplanet.dm @@ -15,11 +15,20 @@ GLOBAL_VAR(planet_repopulation_disabled) var/list/breathgas = list() //list of gases animals/plants require to survive var/badgas //id of gas that is toxic to life here - var/lightlevel = 0 //This default makes turfs not generate light. Adjust to have exoplanents be lit. - var/night = TRUE - var/daycycle //How often do we change day and night - var/daycolumn = 0 //Which column's light needs to be updated next? - var/daycycle_column_delay = 10 SECONDS + + //DAY/NIGHT CYCLE + var/daycycle_range = list(15 MINUTES, 30 MINUTES) + var/daycycle = 0//How often do we change day and night, at first list, to determine min and max day length + var/sun_process_interval = 1.5 MINUTES //How often we update planetary sunlight + var/sun_last_process = null // world.time + + /// 0 means midnight, 1 means noon. + var/sun_position = 0 + /// This a multiplier used to apply to the brightness of ambient lighting. 0.3 means 30% of the brightness of the sun. + var/sun_brightness_modifier = 0.5 + + /// Sun control + var/ambient_group_index = -1 var/maxx var/maxy @@ -181,22 +190,84 @@ GLOBAL_VAR(planet_repopulation_disabled) daddy.group_multiplier = Z.air.group_multiplier Z.air.equalize(daddy) - if (daycycle) - if (tick % round(daycycle / wait) == 0) - night = !night - daycolumn = 1 - if (daycolumn && tick % round(daycycle_column_delay / wait) == 0) - update_daynight() - -/obj/effect/overmap/visitable/sector/exoplanet/proc/update_daynight() - var/light = 0.1 - if (!night) - light = lightlevel - for (var/turf/simulated/floor/exoplanet/T in block(locate(daycolumn,1,min(map_z)),locate(daycolumn,maxy,max(map_z)))) - T.set_light(light, 0.1, 2) - daycolumn++ - if (daycolumn > maxx) - daycolumn = 0 + if(sun_last_process <= (world.time - sun_process_interval)) + update_sun() + +/obj/effect/overmap/visitable/sector/exoplanet/proc/generate_daycycle() + daycycle = rand(daycycle_range[1], daycycle_range[2]) + update_sun() + +// This changes the position of the sun on the planet. +/obj/effect/overmap/visitable/sector/exoplanet/proc/update_sun() + if(sun_last_process == world.time) //For now, calling it several times in same frame is not valid. Add a parameter to ignore this if weather is added + return + sun_last_process = world.time + + var/time_of_day = (world.time % daycycle) / daycycle //0 to 1 range. + + var/distance_from_noon = abs(time_of_day - 0.5) + sun_position = distance_from_noon / 0.5 // -1 to 1 range + sun_position = abs(sun_position - 1) + + var/low_brightness = null + var/high_brightness = null + + var/low_color = null + var/high_color = null + var/min = 0 + var/max = 0 + + //Now, each planet type may want to do its own thing for light, if so move most of this code into its own function and override it. + switch(sun_position) + if(0 to 0.40) // Night + low_brightness = 0.01 + low_color = "#000066" + + high_brightness = 0.2 + high_color = "#66004d" + min = 0 + max = 0.4 + + if(0.40 to 0.50) // Twilight + low_brightness = 0.2 + low_color = "#66004d" + + high_brightness = 0.5 + high_color = "#cc3300" + min = 0.40 + max = 0.50 + + if(0.50 to 0.70) // Sunrise/set + low_brightness = 0.5 + low_color = "#cc3300" + + high_brightness = 0.8 + high_color = "#ff9933" + min = 0.50 + max = 0.70 + + if(0.70 to 1.00) // Noon + low_brightness = 0.8 + low_color = "#dddddd" + + high_brightness = 1.0 + high_color = "#ffffff" + min = 0.70 + max = 1.0 + + //var/interpolate_weight = (abs(min - sun_position)) * 4 Cit interpolation, not sure + var/interpolate_weight = (sun_position - min) / (max - min) + + var/new_brightness = (Interpolate(low_brightness, high_brightness, interpolate_weight) ) * sun_brightness_modifier + + //We do a gradient instead of linear interpolation because linear interpolations of colours are unintuitive + var/new_color = UNLINT(gradient(low_color, high_color, space = COLORSPACE_HSV, index=interpolate_weight)) + + if(ambient_group_index > 0) + var/datum/ambient_group/A = SSambient_lighting.ambient_groups[ambient_group_index] + A.set_color(new_color, new_brightness) + else + ambient_group_index = SSambient_lighting.create_ambient_group(new_color, new_brightness) /obj/effect/overmap/visitable/sector/exoplanet/proc/generate_map() var/list/grasscolors = plant_colors.Copy() @@ -231,14 +302,6 @@ GLOBAL_VAR(planet_repopulation_disabled) for (var/mob/living/simple_animal/A in animals) adapt_animal(A) -/obj/effect/overmap/visitable/sector/exoplanet/proc/generate_daycycle() - if (lightlevel) - night = FALSE //we start with a day if we have light. - - //When you set daycycle ensure that the minimum is larger than [maxx * daycycle_column_delay]. - //Otherwise the right side of the exoplanet can get stuck in a forever day. - daycycle = rand(10 MINUTES, 40 MINUTES) - /obj/effect/landmark/exoplanet_spawn/Initialize() ..() return INITIALIZE_HINT_LATELOAD diff --git a/code/modules/overmap/exoplanets/planet_themes/radiation_bombing.dm b/code/modules/overmap/exoplanets/planet_themes/radiation_bombing.dm index c6323a782aa0c..9cfbfe94d4cb5 100644 --- a/code/modules/overmap/exoplanets/planet_themes/radiation_bombing.dm +++ b/code/modules/overmap/exoplanets/planet_themes/radiation_bombing.dm @@ -21,7 +21,7 @@ var/datum/radiation_source/S = new(T, radiation_power, FALSE) S.range = 4 SSradiation.add_source(S) - T.set_light(0.4, 1, 2, l_color = PIPE_COLOR_GREEN) + T.set_light(2, 0.4, l_color = PIPE_COLOR_GREEN) for (var/turf/simulated/floor/exoplanet/crater in circlerangeturfs(T, 3)) if (prob(10)) new/obj/item/remains/xeno/charred(crater) diff --git a/code/modules/overmap/exoplanets/planet_themes/ruined_city.dm b/code/modules/overmap/exoplanets/planet_themes/ruined_city.dm index 841effa9c5963..964d752068cd0 100644 --- a/code/modules/overmap/exoplanets/planet_themes/ruined_city.dm +++ b/code/modules/overmap/exoplanets/planet_themes/ruined_city.dm @@ -22,9 +22,6 @@ for (var/zlevel in E.map_z) new /datum/random_map/city(null,1,1,zlevel,E.maxx,E.maxy,0,1,1, E.planetary_area) - if (prob(50)) - E.lightlevel = rand(5,10)/10 //deserts are usually :lit: - if (prob(50)) var/datum/exoplanet_theme/robotic_guardians/T = new /datum/exoplanet_theme/robotic_guardians E.themes += T diff --git a/code/modules/overmap/exoplanets/planet_types/chlorine.dm b/code/modules/overmap/exoplanets/planet_types/chlorine.dm index 4b15ab2077924..c641ccc76b428 100644 --- a/code/modules/overmap/exoplanets/planet_types/chlorine.dm +++ b/code/modules/overmap/exoplanets/planet_types/chlorine.dm @@ -14,17 +14,11 @@ flora_diversity = 5 fauna_types = list(/mob/living/simple_animal/thinbug, /mob/living/simple_animal/hostile/retaliate/beast/samak/alt, /mob/living/simple_animal/yithian, /mob/living/simple_animal/tindalos, /mob/living/simple_animal/hostile/retaliate/jelly) megafauna_types = list(/mob/living/simple_animal/hostile/retaliate/jelly/mega) + sun_brightness_modifier = 0.5 //The dense atmosphere makes it all dark /obj/effect/overmap/visitable/sector/exoplanet/chlorine/get_atmosphere_color() return "#e5f2bd" -/obj/effect/overmap/visitable/sector/exoplanet/chlorine/generate_map() - if(prob(50)) - lightlevel = rand(7,10)/10 //It could be night. - else - lightlevel = 0.1 - ..() - /obj/effect/overmap/visitable/sector/exoplanet/chlorine/generate_atmosphere() ..() if(atmosphere) diff --git a/code/modules/overmap/exoplanets/planet_types/desert.dm b/code/modules/overmap/exoplanets/planet_types/desert.dm index 22dbcbe9c6600..8571f108e1538 100644 --- a/code/modules/overmap/exoplanets/planet_types/desert.dm +++ b/code/modules/overmap/exoplanets/planet_types/desert.dm @@ -16,7 +16,7 @@ /obj/effect/overmap/visitable/sector/exoplanet/desert/generate_map() if(prob(70)) - lightlevel = rand(5,10)/10 //deserts are usually :lit: + sun_brightness_modifier = rand(4,8)/10 //deserts are usually :lit: ..() /obj/effect/overmap/visitable/sector/exoplanet/desert/generate_atmosphere() diff --git a/code/modules/overmap/exoplanets/planet_types/grass.dm b/code/modules/overmap/exoplanets/planet_types/grass.dm index a548a8089a8d4..0d35671999305 100644 --- a/code/modules/overmap/exoplanets/planet_types/grass.dm +++ b/code/modules/overmap/exoplanets/planet_types/grass.dm @@ -12,11 +12,6 @@ fauna_types = list(/mob/living/simple_animal/yithian, /mob/living/simple_animal/tindalos, /mob/living/simple_animal/hostile/retaliate/jelly) megafauna_types = list(/mob/living/simple_animal/hostile/retaliate/parrot/space/megafauna, /mob/living/simple_animal/hostile/retaliate/goose/dire) -/obj/effect/overmap/visitable/sector/exoplanet/grass/generate_map() - if(prob(40)) - lightlevel = rand(1,7)/10 //give a chance of twilight jungle - ..() - /obj/effect/overmap/visitable/sector/exoplanet/grass/generate_atmosphere() ..() if(atmosphere) @@ -76,7 +71,7 @@ rock_colors = list(COLOR_ASTEROID_ROCK, COLOR_GRAY80, COLOR_BROWN) plant_colors = list("#2f573e","#24574e","#6e9280","#9eab88","#868b58", "#84be7c", "RANDOM") map_generators = list(/datum/random_map/noise/exoplanet/grass/terraformed) - lightlevel = 0.5 + sun_brightness_modifier = 0.8 //Fairly bright has_trees = TRUE flora_diversity = 8 fauna_types = list(/mob/living/simple_animal/passive/cat, /mob/living/simple_animal/passive/chicken, /mob/living/simple_animal/passive/mouse, /mob/living/simple_animal/passive/opossum, /mob/living/simple_animal/hostile/retaliate/goat, /mob/living/simple_animal/hostile/retaliate/goose, /mob/living/simple_animal/passive/cow) @@ -100,10 +95,6 @@ atmosphere.temperature = T0C + rand(0, 50) atmosphere.update_values() -/obj/effect/overmap/visitable/sector/exoplanet/grass/generate_map() - lightlevel = rand(0.7,0.9)/10 - ..() - /datum/random_map/noise/exoplanet/grass/terraformed descriptor = "terraformed grass exoplanet" flora_prob = 15 diff --git a/code/modules/overmap/exoplanets/planet_types/shrouded.dm b/code/modules/overmap/exoplanets/planet_types/shrouded.dm index 7c1b939fbeb32..e91c5aaccf2c0 100644 --- a/code/modules/overmap/exoplanets/planet_types/shrouded.dm +++ b/code/modules/overmap/exoplanets/planet_types/shrouded.dm @@ -7,7 +7,7 @@ plant_colors = list("#3c5434", "#2f6655", "#0e703f", "#495139", "#394c66", "#1a3b77", "#3e3166", "#52457c", "#402d56", "#580d6d") map_generators = list(/datum/random_map/noise/exoplanet/shrouded, /datum/random_map/noise/ore/poor) ruin_tags_blacklist = RUIN_HABITAT - lightlevel = -0.15 + sun_brightness_modifier = -0.5 surface_color = "#3e3960" water_color = "#2b2840" has_trees = TRUE diff --git a/code/modules/overmap/exoplanets/planet_types/volcanic.dm b/code/modules/overmap/exoplanets/planet_types/volcanic.dm index a2adbb188812d..2de90133a7671 100644 --- a/code/modules/overmap/exoplanets/planet_types/volcanic.dm +++ b/code/modules/overmap/exoplanets/planet_types/volcanic.dm @@ -94,16 +94,18 @@ turf_flags = TURF_DISALLOW_BLOB var/list/victims + ambient_light_multiplier = 1 + +/turf/simulated/floor/exoplanet/lava/setup_local_ambient() + set_ambient_light(COLOR_ORANGE, 1) + /turf/simulated/floor/exoplanet/lava/on_update_icon() return -/turf/simulated/floor/exoplanet/lava/Initialize() - . = ..() - set_light(0.95, 0.5, 2, l_color = COLOR_ORANGE) - /turf/simulated/floor/exoplanet/lava/Destroy() STOP_PROCESSING(SSobj, src) . = ..() + clear_ambient_light() /turf/simulated/floor/exoplanet/lava/Entered(atom/movable/AM) ..() diff --git a/code/modules/overmap/exoplanets/turfs.dm b/code/modules/overmap/exoplanets/turfs.dm index 1e0b9401c808d..d0abaff9f9f4c 100644 --- a/code/modules/overmap/exoplanets/turfs.dm +++ b/code/modules/overmap/exoplanets/turfs.dm @@ -20,8 +20,7 @@ else initial_gas = list() temperature = T0C - //Must be done here, as light data is not fully carried over by ChangeTurf (but overlays are). - set_light(E.lightlevel, 0.1, 2) + if(E.planetary_area && istype(loc, world.area)) ChangeArea(src, E.planetary_area) ..() @@ -216,6 +215,7 @@ dynamic_lighting = FALSE icon = null icon_state = null + permit_ao = FALSE /turf/simulated/planet_edge/Initialize() . = ..() diff --git a/code/modules/overmap/ships/computers/helm.dm b/code/modules/overmap/ships/computers/helm.dm index 3424e05539bdc..a0251cc7effa5 100644 --- a/code/modules/overmap/ships/computers/helm.dm +++ b/code/modules/overmap/ships/computers/helm.dm @@ -391,4 +391,4 @@ GLOBAL_LIST_EMPTY(overmap_helm_computers) set_light(0) else icon_state = "tele_nav" - set_light(light_max_bright_on, light_inner_range_on, light_outer_range_on, 2, light_color) + set_light(light_range_on, light_power_on, light_color) diff --git a/code/modules/overmap/ships/engines/gas_thruster.dm b/code/modules/overmap/ships/engines/gas_thruster.dm index 1e30a9d949c80..ad2c9a43d8966 100644 --- a/code/modules/overmap/ships/engines/gas_thruster.dm +++ b/code/modules/overmap/ships/engines/gas_thruster.dm @@ -206,7 +206,7 @@ /obj/effect/engine_exhaust/New(turf/nloc, ndir) ..(nloc) nloc.hotspot_expose(1000,125) - set_light(0.5, 1, 4) + set_light(4, 0.5) set_dir(ndir) spawn(20) qdel(src) diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index da4d04bc331df..e3d07abcb582f 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -345,7 +345,7 @@ if(update_state & (UPDATE_OPENED1|UPDATE_OPENED2|UPDATE_BROKE)) set_light(0) else if(update_state & UPDATE_BLUESCREEN) - set_light(0.8, 0.1, 1, 2, "#00ecff") + set_light(1, 0.8, "#00ecff") else if(!MACHINE_IS_BROKEN(src) && !GET_FLAGS(stat, MACHINE_STAT_MAINT) && update_state & UPDATE_ALLGOOD) var/color switch(charging) @@ -355,7 +355,7 @@ color = "#a8b0f8" if(2) color = "#82ff4c" - set_light(0.8, 0.1, 1, l_color = color) + set_light(1, 0.8, l_color = color) else set_light(0) diff --git a/code/modules/power/fusion/core/core_field.dm b/code/modules/power/fusion/core/core_field.dm index b280a56e904cf..ef4ba06c051ea 100644 --- a/code/modules/power/fusion/core/core_field.dm +++ b/code/modules/power/fusion/core/core_field.dm @@ -68,7 +68,7 @@ , filter(type="outline", size = 2, color = COLOR_RED) , filter(type="bloom", size=3, offset = 0.5, alpha = 235)) - set_light(light_min_power, light_min_range / 10, light_min_range) + set_light(light_min_range, light_min_power) last_range = light_min_range last_power = light_min_power @@ -147,7 +147,7 @@ alpha = 200 if (last_range != use_range || last_power != use_power || color != light_color) - set_light(min(use_power, 1), use_range / 6, use_range) //cap first arg at 1 to avoid breaking lighting stuff. + set_light(use_range, min(use_power, 1)) //cap first arg at 1 to avoid breaking lighting stuff. last_range = use_range last_power = use_power //Temperature based color @@ -286,7 +286,7 @@ /obj/effect/fusion_em_field/proc/Rupture() visible_message(SPAN_DANGER("\The [src] shudders like a dying animal before flaring to eye-searing brightness and rupturing!")) - set_light(1, 0.1, 15, 2, "#ccccff") + set_light(15, 1, "#ccccff") empulse(get_turf(src), ceil(plasma_temperature/1000), ceil(plasma_temperature/300)) sleep(5) RadiateAll() diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm index 69663b349c9e1..10d7ea0bc8637 100644 --- a/code/modules/power/lighting.dm +++ b/code/modules/power/lighting.dm @@ -238,6 +238,16 @@ on = powered() update_icon(FALSE) + switch (dir) + if(NORTH) + light_offset_y = WORLD_ICON_SIZE * 0.5 + if(SOUTH) + light_offset_y = WORLD_ICON_SIZE * -0.5 + if(EAST) + light_offset_x = WORLD_ICON_SIZE * 0.5 + if(WEST) + light_offset_x = WORLD_ICON_SIZE * -0.5 + /// Fetches the light's color based on area flags. Used for Init and for smartly installing new bulbs during runtime (See light replacers). /obj/machinery/light/proc/get_color_from_area() var/light_color = null @@ -301,14 +311,14 @@ if(current_mode && (current_mode in lightbulb.lighting_modes)) changed = set_light(arglist(lightbulb.lighting_modes[current_mode])) else - changed = set_light(lightbulb.b_max_bright, lightbulb.b_inner_range, lightbulb.b_outer_range, lightbulb.b_curve, lightbulb.b_colour) + changed = set_light(lightbulb.b_range, lightbulb.b_power, lightbulb.b_colour) if(trigger && changed && get_status() == LIGHT_OK) switch_check() else update_use_power(POWER_USE_OFF) set_light(0) - change_power_consumption((light_outer_range * light_max_bright) * LIGHTING_POWER_FACTOR, POWER_USE_ACTIVE) + change_power_consumption((light_range * light_power) * LIGHTING_POWER_FACTOR, POWER_USE_ACTIVE) /// Returns `lightbulb.status`. /obj/machinery/light/proc/get_status() @@ -639,21 +649,15 @@ var/broken_chance = 2 atom_flags = ATOM_FLAG_NO_TEMP_CHANGE | ATOM_FLAG_CAN_BE_PAINTED - /// Lighting `max_bright` value when turned on. - var/b_max_bright = 0.9 - /// Lighting `inner_range` value when turned on. - var/b_inner_range = 1 - /// Lighting `outer_range` value when turned on - var/b_outer_range = 5 - /// Lighting `curve` value when turned on. - var/b_curve = 2 + var/b_power = 0.7 + var/b_range = 5 /// Lighting `colour` value when turned on. var/b_colour = LIGHT_COLOUR_WARM /** * List of lists. Alternative lighting modes the bulb supports. Entry index should be the `LIGHTMODE_*` type supported, and the value should be a list of `l_*` lighting values to be applied when the mode is enabled. * - * Example: `LIGHTMODE_EMERGENCY = list(l_outer_range = 4, l_max_bright = 1, l_color = LIGHT_COLOUR_E_RED)` + * Example: `LIGHTMODE_EMERGENCY = list(l_range = 4, l_power = 1, l_color = LIGHT_COLOUR_E_RED)` */ var/list/lighting_modes = list() @@ -691,9 +695,9 @@ item_state = "c_tube" matter = list(MATERIAL_GLASS = 100, MATERIAL_ALUMINIUM = 20) - b_outer_range = 5 + b_range = 5 lighting_modes = list( - LIGHTMODE_EMERGENCY = list(l_outer_range = 4, l_max_bright = 1, l_color = LIGHT_COLOUR_E_RED), + LIGHTMODE_EMERGENCY = list(l_range = 4, l_power = 1, l_color = LIGHT_COLOUR_E_RED), ) sound_on = 'sound/machines/lightson.ogg' @@ -719,10 +723,7 @@ /obj/item/light/tube/large w_class = ITEM_SIZE_SMALL name = "large light tube" - b_max_bright = 0.95 - b_inner_range = 2 - b_outer_range = 8 - b_curve = 2.5 + b_range = 8 /obj/item/light/tube/large/warm name = "large light tube (warm)" @@ -752,12 +753,10 @@ broken_chance = 3 matter = list(MATERIAL_GLASS = 100) - b_max_bright = 0.6 - b_inner_range = 0.1 - b_outer_range = 4 - b_curve = 3 + b_power = 0.7 + b_range = 4 lighting_modes = list( - LIGHTMODE_EMERGENCY = list(l_outer_range = 3, l_max_bright = 1, l_color = LIGHT_COLOUR_E_RED) + LIGHTMODE_EMERGENCY = list(l_range = 3, l_power = 1, l_color = LIGHT_COLOUR_E_RED) ) /obj/item/light/bulb/warm @@ -782,7 +781,7 @@ /obj/item/light/bulb/red/readylight lighting_modes = list( - LIGHTMODE_READY = list(l_outer_range = 5, l_max_bright = 1, l_color = LIGHT_COLOUR_READY) + LIGHTMODE_READY = list(l_range = 5, l_power = 1, l_color = LIGHT_COLOUR_READY) ) /obj/item/light/throw_impact(atom/hit_atom) diff --git a/code/modules/power/port_gen.dm b/code/modules/power/port_gen.dm index 4a665e65efe14..e327da37f9d70 100644 --- a/code/modules/power/port_gen.dm +++ b/code/modules/power/port_gen.dm @@ -447,7 +447,7 @@ I.blend_mode = BLEND_ADD I.alpha = round(255*power_output/max_power_output) AddOverlays(I) - set_light(0.7, 0.1, rad_power + power_output - max_safe_output, 2, "#3b97ca") + set_light(rad_power + power_output - max_safe_output, 0.7, "#3b97ca") else set_light(0) diff --git a/code/modules/power/singularity/containment_field.dm b/code/modules/power/singularity/containment_field.dm index 506789e4ef7a1..679fcbb0feb8e 100644 --- a/code/modules/power/singularity/containment_field.dm +++ b/code/modules/power/singularity/containment_field.dm @@ -10,7 +10,7 @@ unacidable = TRUE use_power = POWER_USE_OFF uncreated_component_parts = null - light_outer_range = 4 + light_range = 4 movable_flags = MOVABLE_FLAG_PROXMOVE var/obj/machinery/field_generator/FG1 = null var/obj/machinery/field_generator/FG2 = null diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm index 4b9e7b6e9539e..d5481cb497a30 100644 --- a/code/modules/power/singularity/singularity.dm +++ b/code/modules/power/singularity/singularity.dm @@ -8,7 +8,7 @@ anchored = TRUE density = TRUE layer = SINGULARITY_LAYER - light_outer_range = 6 + light_range = 6 unacidable = TRUE var/current_size = 1 diff --git a/code/modules/projectiles/effects.dm b/code/modules/projectiles/effects.dm index 63f271a6247d3..3c50c83c253b3 100644 --- a/code/modules/projectiles/effects.dm +++ b/code/modules/projectiles/effects.dm @@ -4,8 +4,8 @@ plane = EFFECTS_ABOVE_LIGHTING_PLANE layer = BEAM_PROJECTILE_LAYER //Muzzle flashes would be above the lighting plane anyways. //Standard compiletime light vars aren't working here, so we've made some of our own. - light_outer_range = 2 - light_max_bright = 1 + light_range = 2 + light_power = 1 light_color = "#ff00dc" mouse_opacity = 0 @@ -75,7 +75,7 @@ // Heavy laser beam //---------------------------- /obj/effect/projectile/laser/heavy - light_max_bright = 1 + light_power = 1 /obj/effect/projectile/laser/heavy/tracer icon_state = "beam_heavy" @@ -90,7 +90,7 @@ // Pulse laser beam //---------------------------- /obj/effect/projectile/laser/pulse - light_max_bright = 1 + light_power = 1 light_color = COLOR_DEEP_SKY_BLUE /obj/effect/projectile/laser/pulse/tracer @@ -107,7 +107,7 @@ // Skrell laser beam //---------------------------- /obj/effect/projectile/laser/pulse/skrell - light_max_bright = 1 + light_power = 1 light_color = "#4c00ff" /obj/effect/projectile/laser/pulse/skrell/tracer @@ -124,7 +124,7 @@ //---------------------------- /obj/effect/projectile/pulse/muzzle icon_state = "muzzle_pulse" - light_max_bright = 1 + light_power = 1 light_color = COLOR_DEEP_SKY_BLUE //---------------------------- @@ -146,7 +146,7 @@ // Emitter beam //---------------------------- /obj/effect/projectile/laser/emitter - light_max_bright = 1 + light_power = 1 light_color = "#00cc00" /obj/effect/projectile/laser/emitter/tracer @@ -178,8 +178,8 @@ //---------------------------- /obj/effect/projectile/bullet/muzzle icon_state = "muzzle_bullet" - light_outer_range = 5 - light_max_bright = 1 + light_range = 5 + light_power = 1 light_color = COLOR_MUZZLE_FLASH //---------------------------- @@ -247,7 +247,7 @@ //---------------------------- /obj/effect/projectile/pointdefense light_color = COLOR_GOLD - light_max_bright = 1 + light_power = 1 /obj/effect/projectile/pointdefense/tracer icon_state = "beam_pointdef_d" @@ -271,4 +271,4 @@ icon_state = "muzzle_incen" /obj/effect/projectile/incen/impact - icon_state = "impact_incen" \ No newline at end of file + icon_state = "impact_incen" diff --git a/code/modules/projectiles/projectile/energy.dm b/code/modules/projectiles/projectile/energy.dm index e3694c1e7ba2e..249e3591e8c05 100644 --- a/code/modules/projectiles/projectile/energy.dm +++ b/code/modules/projectiles/projectile/energy.dm @@ -40,7 +40,7 @@ sparks.start() new /obj/effect/decal/cleanable/ash(src.loc) //always use src.loc so that ash doesn't end up inside windows - new /obj/effect/effect/smoke/illumination(T, 5, 4, 1, light_colour) + new /obj/effect/effect/smoke/illumination(T, 5, 4, 2, light_colour) //blinds people like the flash round, but in a larger area and can also be used for temporary illumination /obj/item/projectile/energy/flash/flare @@ -60,7 +60,7 @@ /obj/item/projectile/energy/flash/flare/on_impact(atom/A) light_colour = pick("#e58775", "#ffffff", "#faa159", "#e34e0e") - set_light(1, 2, 6, 1, light_colour) + set_light(6, 1, light_colour) ..() //initial flash //residual illumination diff --git a/code/modules/psionics/equipment/cerebro_enhancers.dm b/code/modules/psionics/equipment/cerebro_enhancers.dm index d018984511d28..3ab8a5c9fb92f 100644 --- a/code/modules/psionics/equipment/cerebro_enhancers.dm +++ b/code/modules/psionics/equipment/cerebro_enhancers.dm @@ -164,4 +164,4 @@ action_button_name = "Remove Psionic Amplifier" H.update_action_buttons() - set_light(0.5, 0.1, 3, 2, l_color = "#880000") + set_light(3, 0.5, l_color = "#880000") diff --git a/code/modules/random_map/drop/droppod.dm b/code/modules/random_map/drop/droppod.dm index bebfc40d4a308..8bf4cb500d3dd 100644 --- a/code/modules/random_map/drop/droppod.dm +++ b/code/modules/random_map/drop/droppod.dm @@ -123,7 +123,7 @@ /datum/random_map/droppod/proc/get_spawned_drop(turf/T) var/obj/structure/bed/chair/C = new(T) - C.set_light(0.5, 0.1, 3, 2, l_color = "#cc0000") + C.set_light(3, 0.5, l_color = "#cc0000") var/mob/living/drop // This proc expects a list of mobs to be passed to the spawner. // Use the supply pod if you don't want to drop mobs. diff --git a/code/modules/reagents/heat_sources/thermal_regulator.dm b/code/modules/reagents/heat_sources/thermal_regulator.dm index 68c9af8bea044..41fcb821023b3 100644 --- a/code/modules/reagents/heat_sources/thermal_regulator.dm +++ b/code/modules/reagents/heat_sources/thermal_regulator.dm @@ -129,7 +129,7 @@ AddOverlays(emissive_appearance(icon, "[icon_state]_lights_cold")) glow_icon.alpha = clamp(temperature - MINIMUM_GLOW_TEMPERATURE, MINIMUM_GLOW_VALUE, MAXIMUM_GLOW_VALUE) LAZYADD(adding_overlays, glow_icon) - set_light(0.2, 0.1, 1, l_color = COLOR_GREEN) + set_light(1, 0.2, l_color = COLOR_GREEN) else set_light(0) else diff --git a/code/modules/reagents/reagent_containers/food/drinks/bottle.dm b/code/modules/reagents/reagent_containers/food/drinks/bottle.dm index 5d695680b18b8..0ef2ef51f7d88 100644 --- a/code/modules/reagents/reagent_containers/food/drinks/bottle.dm +++ b/code/modules/reagents/reagent_containers/food/drinks/bottle.dm @@ -119,7 +119,7 @@ if (rag) var/underlay_image = image(icon='icons/obj/food/drinks.dmi', icon_state=rag.on_fire? "[rag_underlay]_lit" : rag_underlay) underlays += underlay_image - set_light(rag.light_max_bright, 0.1, rag.light_outer_range, 2, rag.light_color) + set_light(rag.light_range, rag.light_power, rag.light_color) else set_light(0) diff --git a/code/modules/research/message_server.dm b/code/modules/research/message_server.dm index fc58040f63ca5..c21b9f949f458 100644 --- a/code/modules/research/message_server.dm +++ b/code/modules/research/message_server.dm @@ -105,7 +105,7 @@ var/global/list/obj/machinery/message_server/message_servers = list() playsound(Console.loc, 'sound/machines/twobeep.ogg', 50, 1) Console.audible_message("[icon2html(Console, viewers(get_turf(Console)))][SPAN_NOTICE("\The [Console] announces: 'Message received from [sender].'")]", hearing_distance = 5) Console.message_log += "Message from [sender]
[authmsg]" - Console.set_light(0.3, 0.1, 2) + Console.set_light(2, 0.5) /obj/machinery/message_server/interface_interact(mob/user) diff --git a/code/modules/shieldgen/shieldwallgen.dm b/code/modules/shieldgen/shieldwallgen.dm index 3832a7c76b0f3..26cc56bde2575 100644 --- a/code/modules/shieldgen/shieldwallgen.dm +++ b/code/modules/shieldgen/shieldwallgen.dm @@ -261,7 +261,7 @@ anchored = TRUE density = TRUE unacidable = TRUE - light_outer_range = 3 + light_range = 3 var/needs_power = 0 var/active = 1 var/delay = 5 diff --git a/code/modules/shuttles/landmarks.dm b/code/modules/shuttles/landmarks.dm index 1c2f74004f32f..05c45e096055d 100644 --- a/code/modules/shuttles/landmarks.dm +++ b/code/modules/shuttles/landmarks.dm @@ -221,7 +221,7 @@ AddOverlays(image) pixel_x = rand(-6, 6) pixel_y = rand(-6, 6) - set_light(0.7, 0.1, 7, 2, "#85d1ff") + set_light(7, 0.7, "#85d1ff") else icon_state = initial(icon_state) ClearOverlays() diff --git a/code/modules/spells/aoe_turf/conjure/construct.dm b/code/modules/spells/aoe_turf/conjure/construct.dm index 7cbafc56e200f..703b542954bfb 100644 --- a/code/modules/spells/aoe_turf/conjure/construct.dm +++ b/code/modules/spells/aoe_turf/conjure/construct.dm @@ -117,4 +117,4 @@ icon = 'icons/effects/effects.dmi' icon_state = "m_shield_cult" light_color = "#b40000" - light_outer_range = 2 + light_range = 2 diff --git a/code/modules/spells/racial_wizard.dm b/code/modules/spells/racial_wizard.dm index d787c2123b45d..2e3008986150c 100644 --- a/code/modules/spells/racial_wizard.dm +++ b/code/modules/spells/racial_wizard.dm @@ -59,7 +59,7 @@ return var/obj/O = new /obj(T) - O.set_light(-10, 0.1, 10, 2, "#ffffff") + O.set_light(10, -10, "#ffffff") spawn(duration) qdel(O) diff --git a/code/modules/supermatter/supermatter.dm b/code/modules/supermatter/supermatter.dm index 2e7fd46063330..0caebad6954c4 100644 --- a/code/modules/supermatter/supermatter.dm +++ b/code/modules/supermatter/supermatter.dm @@ -31,7 +31,7 @@ icon_state = "supermatter" density = TRUE anchored = FALSE - light_outer_range = 4 + light_range = 4 layer = ABOVE_HUMAN_LAYER @@ -275,8 +275,8 @@ //Changes color and luminosity of the light to these values if they were not already set /obj/machinery/power/supermatter/proc/shift_light(lum, clr) - if(lum != light_outer_range || clr != light_color) - set_light(1, 0.1, lum, l_color = clr) + if(lum != light_range || clr != light_color) + set_light(lum, 1, l_color = clr) /obj/machinery/power/supermatter/proc/get_integrity() var/integrity = damage / explosion_point diff --git a/code/modules/xenoarcheaology/effects/hellportal/portals.dm b/code/modules/xenoarcheaology/effects/hellportal/portals.dm index 92b29c9c9c799..c9f1cef06da13 100644 --- a/code/modules/xenoarcheaology/effects/hellportal/portals.dm +++ b/code/modules/xenoarcheaology/effects/hellportal/portals.dm @@ -1,7 +1,7 @@ /obj/effect/gateway/artifact name = "reality tear" desc = "A piercing pain strikes your mind as you peer into the tear, witnessing horrors and suffering beyond comprehension." - light_outer_range=5 + light_range=5 light_color="#ff0000" spawnable = list( /mob/living/simple_animal/hostile/meat/abomination = 5, @@ -45,7 +45,7 @@ /obj/effect/gateway/artifact/big name = "interdimensional gateway" desc = "A huge hole in reality with a strange, pulsing heartbeat. Faint, agonized screams can be heard from inside it..." - light_outer_range = 10 + light_range = 10 ///Ticks down every so often until portal vanishes. var/health = 15 ///How many mobs we've spawned. diff --git a/config/example/config.txt b/config/example/config.txt index 66e295bc4b9dd..4f22546e84923 100644 --- a/config/example/config.txt +++ b/config/example/config.txt @@ -327,8 +327,8 @@ EVENT_CUSTOM_START_MAJOR 80;100 ## The delay in minutes before an observer that has returned to the main menu may rejoin the game. #RESPAWN_MENU_DELAY 0 -## Strength of ambient star light. Set to 0 or less to turn off. A value of 1 is unlikely to have a noticeable effect in most lighting systems. -STARLIGHT 0 +## Enables and disables starlight. This will make space turfs and some turfs considered to be in exterior areas to be lit based on the colour of the background parallax. +STARLIGHT 1 ## Defines which races are allowed to join as ERT, in singular form. If unset, defaults to only human. Casing matters, separate using ; ## Example races include: Human, Tajara, Skrell, Unathi diff --git a/icons/effects/lighting_overlay.dmi b/icons/effects/lighting_overlay.dmi index e5e4573b5845e..b2c14395dd2e7 100644 Binary files a/icons/effects/lighting_overlay.dmi and b/icons/effects/lighting_overlay.dmi differ diff --git a/icons/effects/lighting_overlay.png b/icons/effects/lighting_overlay.png deleted file mode 100644 index b317268b702d5..0000000000000 Binary files a/icons/effects/lighting_overlay.png and /dev/null differ diff --git a/icons/mob/darksight.dmi b/icons/mob/darksight.dmi new file mode 100644 index 0000000000000..5ef6261d414c6 Binary files /dev/null and b/icons/mob/darksight.dmi differ diff --git a/maps/away/blueriver/blueriver.dm b/maps/away/blueriver/blueriver.dm index 61e01002cc432..bb17447f1056a 100644 --- a/maps/away/blueriver/blueriver.dm +++ b/maps/away/blueriver/blueriver.dm @@ -142,7 +142,7 @@ /turf/simulated/floor/away/blueriver/alienfloor/Initialize() .=..() - set_light(0.7, 1, 5, l_color = "#0066ff") + set_light(5, 0.7, l_color = "#0066ff") /turf/unsimulated/wall/away/blueriver/livingwall name = "alien wall" @@ -173,7 +173,7 @@ .=..() icon_state = "bluespacecrystal[rand(1,3)]" - set_light(0.7, 1, 5, l_color = "#0066ff") + set_light(5, 1, l_color = "#0066ff") /turf/unsimulated/wall/supermatter/no_spread/Process() return PROCESS_KILL diff --git a/maps/away/magshield/magshield.dm b/maps/away/magshield/magshield.dm index b51502d381102..5947485744ee9 100644 --- a/maps/away/magshield/magshield.dm +++ b/maps/away/magshield/magshield.dm @@ -49,8 +49,8 @@ icon_state = "maggen" anchored = TRUE density = TRUE - light_outer_range = 3 - light_max_bright = 1 + light_range = 3 + light_power = 1 light_color = "#ffea61" var/heavy_range = 10 var/lighter_range = 20 @@ -130,13 +130,13 @@ icon_state = "nav_light_green" anchored = TRUE density = TRUE - light_outer_range = 10 - light_max_bright = 1 + light_range = 10 + light_power = 1 light_color = "#00ee00" /obj/structure/magshield/nav_light/New()//try make flashing through the process ..() - set_light(light_max_bright, light_outer_range / 6, light_outer_range, 2, light_color) + set_light(light_range, light_power, light_color) /obj/structure/magshield/nav_light/red desc = "Large and bright light regularly emitting red flashes." diff --git a/maps/away/skrellscoutship/skrellscoutship_machines.dm b/maps/away/skrellscoutship/skrellscoutship_machines.dm index e0413efe4ea9e..485a0e9a33338 100644 --- a/maps/away/skrellscoutship/skrellscoutship_machines.dm +++ b/maps/away/skrellscoutship/skrellscoutship_machines.dm @@ -41,7 +41,7 @@ if(on) AddOverlays(field_image) - set_light(0.8, 1, 6, l_color = COLOR_CYAN) + set_light(6, 0.8, l_color = COLOR_CYAN) icon_state = "core1" else CutOverlays(field_image) diff --git a/maps/event/placeholders/placeholders.dm b/maps/event/placeholders/placeholders.dm index 106e6944f0cd4..6ec350ba50644 100644 --- a/maps/event/placeholders/placeholders.dm +++ b/maps/event/placeholders/placeholders.dm @@ -40,16 +40,16 @@ Middle-Click / Ctrl-Click - Jump a placeholder to a point and deselect it if (isnull(option)) return selected.color = option -/* else if (option == "Sensor") + else if (option == "Sensor") option = alert(user, "Sensor Range", null, "Off", "Short", "Far") if (isnull(option)) return else if (option == "Off") selected.set_light(0) else if (option == "Short") - selected.set_light(1, 2, 3) + selected.set_light(3, 1) else if (option == "Far") - selected.set_light(1, 6, 7)*/ + selected.set_light(7, 1) else if (option == "Scan") var/scantext = "" option = input(user, "Placeholder Scan Description", null, scantext) as null | text diff --git a/maps/random_ruins/exoplanet_ruins/icarus/icarus.dm b/maps/random_ruins/exoplanet_ruins/icarus/icarus.dm index f556f1a546625..be9e05912afc9 100644 --- a/maps/random_ruins/exoplanet_ruins/icarus/icarus.dm +++ b/maps/random_ruins/exoplanet_ruins/icarus/icarus.dm @@ -38,7 +38,7 @@ S.update_rad_power(radiation_power) SSradiation.add_source(S) - loc.set_light(0.4, 1, req_range, l_color = COLOR_LIME) //The goo doesn't last, so this is another indicator + loc.set_light(req_range, 0.4, l_color = COLOR_LIME) //The goo doesn't last, so this is another indicator /obj/effect/icarus_irradiate/Destroy() . = ..() diff --git a/maps/random_ruins/exoplanet_ruins/monoliths/monoliths.dm b/maps/random_ruins/exoplanet_ruins/monoliths/monoliths.dm index c6556b1e5d070..1a467c6476abf 100644 --- a/maps/random_ruins/exoplanet_ruins/monoliths/monoliths.dm +++ b/maps/random_ruins/exoplanet_ruins/monoliths/monoliths.dm @@ -38,7 +38,7 @@ I.layer = ABOVE_LIGHTING_LAYER I.plane = EFFECTS_ABOVE_LIGHTING_PLANE AddOverlays(I) - set_light(0.3, 0.1, 2, l_color = I.color) + set_light(2, 0.3, l_color = I.color) var/turf/simulated/floor/exoplanet/T = get_turf(src) if(istype(T)) diff --git a/maps/torch/torch_security_state.dm b/maps/torch/torch_security_state.dm index 997a09050ba39..3d8ede4fd2279 100644 --- a/maps/torch/torch_security_state.dm +++ b/maps/torch/torch_security_state.dm @@ -29,9 +29,8 @@ icon = 'icons/misc/security_state.dmi' alarm_level = "off" - light_max_bright = 0.25 - light_inner_range = 0.1 - light_outer_range = 1 + light_range = 2 + light_power = 1 light_color_alarm = COLOR_GREEN light_color_status_display = COLOR_GREEN @@ -49,9 +48,8 @@ name = "code violet" alarm_level = "on" - light_max_bright = 0.5 - light_inner_range = 1 - light_outer_range = 2 + light_range = 2 + light_power = 1 light_color_alarm = COLOR_VIOLET light_color_status_display = COLOR_VIOLET @@ -68,9 +66,8 @@ name = "code orange" alarm_level = "on" - light_max_bright = 0.5 - light_inner_range = 1 - light_outer_range = 2 + light_range = 2 + light_power = 1 light_color_alarm = COLOR_ORANGE light_color_status_display = COLOR_ORANGE overlay_alarm = "alarm_orange" @@ -88,9 +85,8 @@ icon = 'icons/misc/security_state.dmi' alarm_level = "on" - light_max_bright = 0.5 - light_inner_range = 1 - light_outer_range = 2 + light_range = 2 + light_power = 1 light_color_alarm = COLOR_BLUE light_color_status_display = COLOR_BLUE overlay_alarm = "alarm_blue" @@ -107,9 +103,8 @@ icon = 'icons/misc/security_state.dmi' alarm_level = "on" - light_max_bright = 0.75 - light_inner_range = 1 - light_outer_range = 3 + light_range = 4 + light_power = 2 light_color_alarm = COLOR_RED light_color_status_display = COLOR_RED overlay_alarm = "alarm_red" @@ -135,9 +130,8 @@ icon = 'icons/misc/security_state.dmi' alarm_level = "on" - light_max_bright = 0.75 - light_inner_range = 0.1 - light_outer_range = 3 + light_range = 4 + light_power = 2 light_color_alarm = COLOR_RED light_color_status_display = COLOR_NAVY_BLUE diff --git a/maps/using.dm b/maps/using.dm index 926908a4411b1..76dcb3d2e26c0 100644 --- a/maps/using.dm +++ b/maps/using.dm @@ -1,5 +1,15 @@ //Easily change which map to build by uncommenting ONE below. +<<<<<<< ours +<<<<<<< ours //#include "example\map.dm" //#include "torch\map.dm" #include "sierra\map.dm" +======= +#include "example\map.dm" +//#include "torch\map.dm" +>>>>>>> theirs +======= +//#include "example\map.dm" +#include "torch\map.dm" +>>>>>>> theirs diff --git a/test/check-paths.sh b/test/check-paths.sh index a419f41432cac..203196b86495c 100755 --- a/test/check-paths.sh +++ b/test/check-paths.sh @@ -46,7 +46,7 @@ exactly 2 ">> uses" '(?)>>(?!>)' -P exactly 0 "incorrect indentations" '^( {4,})' -P exactly 24 "text2path uses" 'text2path' exactly 4 "update_icon() override" '/update_icon\((.*)\)' -P -exactly 5 "goto use" 'goto ' +exactly 4 "goto use" 'goto ' exactly 1 "NOOP match" 'NOOP' exactly 342 "spawn uses" '^\s*spawn\s*\(\s*(-\s*)?\d*\s*\)' -P exactly 0 "tag uses" '\stag = ' -P '**/*.dmm' @@ -56,7 +56,7 @@ exactly 0 "emagged = 0/1" 'emagged\s*=\s*\d' -P exactly 0 "simulated = 0/1" 'simulated\s*=\s*\d' -P exactly 2 "var/ in proc arguments" '(^/[^/].+/.+?\(.*?)var/' -P exactly 0 "tmp/ vars" 'var.*/tmp/' -P -exactly 6 "uses of .len" '\.len\b' -P +exactly 7 "uses of .len" '\.len\b' -P exactly 388 "attackby() override" '\/attackby\((.*)\)' -P exactly 15 "uses of examine()" '[.|\s]examine\(' -P # If this fails it's likely because you used '/atom/proc/examine(mob)' instead of '/proc/examinate(mob, atom)' - Exception: An examine()-proc may call other examine()-procs exactly 7 "direct modifications of overlays list" '\boverlays((\s*[|^=+&-])|(\.(Cut)|(Add)|(Copy)|(Remove)|(Remove)))' -P