From cb0f6e7e12a3346493ce936960e788ddcc80916f Mon Sep 17 00:00:00 2001 From: Ling Date: Mon, 2 Jan 2023 14:19:07 +0200 Subject: [PATCH 01/13] Backports SSlighting optimizations --- code/controllers/subsystem/lighting.dm | 33 +-- code/game/turfs/change_turf.dm | 13 +- .../bloodsuckers/powers/targeted/brawn.dm | 4 +- code/modules/lighting/lighting_atom.dm | 7 +- code/modules/lighting/lighting_corner.dm | 163 ++++++++------ code/modules/lighting/lighting_object.dm | 54 +++-- code/modules/lighting/lighting_source.dm | 201 ++++++++---------- code/modules/lighting/lighting_turf.dm | 87 ++++---- .../shadowling/shadowling_abilities.dm | 2 +- 9 files changed, 281 insertions(+), 283 deletions(-) diff --git a/code/controllers/subsystem/lighting.dm b/code/controllers/subsystem/lighting.dm index f57421ba7161..ecae0bfa30dc 100644 --- a/code/controllers/subsystem/lighting.dm +++ b/code/controllers/subsystem/lighting.dm @@ -1,17 +1,16 @@ -GLOBAL_LIST_EMPTY(lighting_update_lights) // List of lighting sources queued for update. -GLOBAL_LIST_EMPTY(lighting_update_corners) // List of lighting corners queued for update. -GLOBAL_LIST_EMPTY(lighting_update_objects) // List of lighting objects queued for update. - SUBSYSTEM_DEF(lighting) name = "Lighting" wait = 2 init_order = INIT_ORDER_LIGHTING flags = SS_TICKER + var/static/list/sources_queue = list() // List of lighting sources queued for update. + var/static/list/corners_queue = list() // List of lighting corners queued for update. + var/static/list/objects_queue = list() // List of lighting objects queued for update. loading_points = 6 SECONDS // Yogs -- loading times /datum/controller/subsystem/lighting/stat_entry(msg) - msg = "L:[GLOB.lighting_update_lights.len]|C:[GLOB.lighting_update_corners.len]|O:[GLOB.lighting_update_objects.len]" + msg = "L:[length(sources_queue)]|C:[length(corners_queue)]|O:[length(objects_queue)]" return ..() @@ -34,9 +33,10 @@ SUBSYSTEM_DEF(lighting) MC_SPLIT_TICK_INIT(3) if(!init_tick_checks) MC_SPLIT_TICK + var/list/queue = sources_queue var/i = 0 - for (i in 1 to GLOB.lighting_update_lights.len) - var/datum/light_source/L = GLOB.lighting_update_lights[i] + for (i in 1 to length(queue)) + var/datum/light_source/L = queue[i] L.update_corners() @@ -47,31 +47,34 @@ SUBSYSTEM_DEF(lighting) else if (MC_TICK_CHECK) break if (i) - GLOB.lighting_update_lights.Cut(1, i+1) + queue.Cut(1, i+1) i = 0 if(!init_tick_checks) MC_SPLIT_TICK - for (i in 1 to GLOB.lighting_update_corners.len) - var/datum/lighting_corner/C = GLOB.lighting_update_corners[i] + queue = corners_queue + for (i in 1 to length(queue)) + var/datum/lighting_corner/C = queue[i] + C.needs_update = FALSE //update_objects() can call qdel if the corner is storing no data C.update_objects() - C.needs_update = FALSE + if(init_tick_checks) CHECK_TICK else if (MC_TICK_CHECK) break if (i) - GLOB.lighting_update_corners.Cut(1, i+1) + queue.Cut(1, i+1) i = 0 if(!init_tick_checks) MC_SPLIT_TICK - for (i in 1 to GLOB.lighting_update_objects.len) - var/datum/lighting_object/O = GLOB.lighting_update_objects[i] + queue = objects_queue + for (i in 1 to length(queue)) + var/datum/lighting_object/O = queue[i] if (QDELETED(O)) continue @@ -83,7 +86,7 @@ SUBSYSTEM_DEF(lighting) else if (MC_TICK_CHECK) break if (i) - GLOB.lighting_update_objects.Cut(1, i+1) + queue.Cut(1, i+1) /datum/controller/subsystem/lighting/Recover() diff --git a/code/game/turfs/change_turf.dm b/code/game/turfs/change_turf.dm index 2aeca9a435fc..f03fb6ae0ebf 100644 --- a/code/game/turfs/change_turf.dm +++ b/code/game/turfs/change_turf.dm @@ -80,9 +80,11 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list( var/old_opacity = opacity var/old_dynamic_lighting = dynamic_lighting - var/old_affecting_lights = affecting_lights var/old_lighting_object = lighting_object - var/old_corners = corners + var/old_lighting_corner_NE = lighting_corner_NE + var/old_lighting_corner_SE = lighting_corner_SE + var/old_lighting_corner_SW = lighting_corner_SW + var/old_lighting_corner_NW = lighting_corner_NW var/old_exl = explosion_level var/old_exi = explosion_id @@ -117,11 +119,14 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list( W.blueprint_data = old_bp + lighting_corner_NE = old_lighting_corner_NE + lighting_corner_SE = old_lighting_corner_SE + lighting_corner_SW = old_lighting_corner_SW + lighting_corner_NW = old_lighting_corner_NW + if(SSlighting.initialized) recalc_atom_opacity() lighting_object = old_lighting_object - affecting_lights = old_affecting_lights - corners = old_corners if (old_opacity != opacity || dynamic_lighting != old_dynamic_lighting) reconsider_lights() diff --git a/code/modules/antagonists/bloodsuckers/powers/targeted/brawn.dm b/code/modules/antagonists/bloodsuckers/powers/targeted/brawn.dm index fcb5dc33d384..97ea1ae6040d 100644 --- a/code/modules/antagonists/bloodsuckers/powers/targeted/brawn.dm +++ b/code/modules/antagonists/bloodsuckers/powers/targeted/brawn.dm @@ -213,7 +213,7 @@ var/mob/living/carbon/human/H = target_atom H.apply_status_effect(STATUS_EFFECT_SHADOWAFFLICTED) var/turf/T = get_turf(H) - for(var/datum/light_source/LS in T.affecting_lights) + for(var/datum/light_source/LS in T.light_sources) var/atom/LO = LS.source_atom if(isitem(LO)) var/obj/item/I = LO @@ -234,4 +234,4 @@ var/mob/living/silicon/robot/borg = LO if(!borg.lamp_cooldown) borg.smash_headlamp() - . = ..() \ No newline at end of file + . = ..() diff --git a/code/modules/lighting/lighting_atom.dm b/code/modules/lighting/lighting_atom.dm index 4960ab1a3dfb..3c27d4aa75a7 100644 --- a/code/modules/lighting/lighting_atom.dm +++ b/code/modules/lighting/lighting_atom.dm @@ -81,11 +81,8 @@ /atom/movable/Moved(atom/OldLoc, Dir) . = ..() - var/datum/light_source/L - var/thing - for (thing in light_sources) // Cycle through the light sources on this atom and tell them to update. - L = thing - L.source_atom.update_light() + for (var/datum/light_source/light as anything in light_sources) // Cycle through the light sources on this atom and tell them to update. + light.source_atom.update_light() /atom/vv_edit_var(var_name, var_value) switch (var_name) diff --git a/code/modules/lighting/lighting_corner.dm b/code/modules/lighting/lighting_corner.dm index 44fed0f02d91..a19f2e23bc26 100644 --- a/code/modules/lighting/lighting_corner.dm +++ b/code/modules/lighting/lighting_corner.dm @@ -2,32 +2,35 @@ // And corners get shared between multiple turfs (unless you're on the corners of the map, then 1 corner doesn't). // For the record: these should never ever ever be deleted, even if the turf doesn't have dynamic lighting. -// This list is what the code that assigns corners listens to, the order in this list is the order in which corners are added to the /turf/corners list. -GLOBAL_LIST_INIT(LIGHTING_CORNER_DIAGONAL, list(NORTHEAST, SOUTHEAST, SOUTHWEST, NORTHWEST)) - /datum/lighting_corner - var/list/turf/masters var/list/datum/light_source/affecting // Light sources affecting us. - var/active = FALSE // TRUE if one of our masters has dynamic lighting. - var/x = 0 - var/y = 0 + var/x = 0 + var/y = 0 + + var/turf/master_NE + var/turf/master_SE + var/turf/master_SW + var/turf/master_NW var/lum_r = 0 var/lum_g = 0 var/lum_b = 0 - var/needs_update = FALSE + //true color values, guaranteed to be between 0 and 1 + var/cache_r = LIGHTING_SOFT_THRESHOLD + var/cache_g = LIGHTING_SOFT_THRESHOLD + var/cache_b = LIGHTING_SOFT_THRESHOLD - var/cache_r = LIGHTING_SOFT_THRESHOLD - var/cache_g = LIGHTING_SOFT_THRESHOLD - var/cache_b = LIGHTING_SOFT_THRESHOLD - var/cache_mx = 0 + ///the maximum of lum_r, lum_g, and lum_b. if this is > 1 then the three cached color values are divided by this + var/largest_color_luminosity = 0 + + ///whether we are to be added to SSlighting's corners_queue list for an update + var/needs_update = FALSE /datum/lighting_corner/New(var/turf/new_turf, var/diagonal) . = ..() - masters = list() - masters[new_turf] = turn(diagonal, 180) + save_master(new_turf, turn(diagonal, 180)) var/vertical = diagonal & ~(diagonal - 1) // The horizontal directions (4 and 8) are bigger than the vertical ones (1 and 2), so we can reliably say the lsb is the horizontal direction. var/horizontal = diagonal & ~vertical // Now that we know the horizontal one we can get the vertical one. @@ -38,50 +41,49 @@ GLOBAL_LIST_INIT(LIGHTING_CORNER_DIAGONAL, list(NORTHEAST, SOUTHEAST, SOUTHWEST, // My initial plan was to make this loop through a list of all the dirs (horizontal, vertical, diagonal). // Issue being that the only way I could think of doing it was very messy, slow and honestly overengineered. // So we'll have this hardcode instead. - var/turf/T - var/i + var/turf/new_master_turf // Diagonal one is easy. - T = get_step(new_turf, diagonal) - if (T) // In case we're on the map's border. - if (!T.corners) - T.corners = list(null, null, null, null) - - masters[T] = diagonal - i = GLOB.LIGHTING_CORNER_DIAGONAL.Find(turn(diagonal, 180)) - T.corners[i] = src + new_master_turf = get_step(new_turf, diagonal) + if (new_master_turf) // In case we're on the map's border. + save_master(new_master_turf, diagonal) // Now the horizontal one. - T = get_step(new_turf, horizontal) - if (T) // Ditto. - if (!T.corners) - T.corners = list(null, null, null, null) - - masters[T] = ((T.x > x) ? EAST : WEST) | ((T.y > y) ? NORTH : SOUTH) // Get the dir based on coordinates. - i = GLOB.LIGHTING_CORNER_DIAGONAL.Find(turn(masters[T], 180)) - T.corners[i] = src + new_master_turf = get_step(new_turf, horizontal) + if (new_master_turf) // Ditto. + save_master(new_master_turf, ((new_master_turf.x > x) ? EAST : WEST) | ((new_master_turf.y > y) ? NORTH : SOUTH)) // Get the dir based on coordinates. // And finally the vertical one. - T = get_step(new_turf, vertical) - if (T) - if (!T.corners) - T.corners = list(null, null, null, null) - - masters[T] = ((T.x > x) ? EAST : WEST) | ((T.y > y) ? NORTH : SOUTH) // Get the dir based on coordinates. - i = GLOB.LIGHTING_CORNER_DIAGONAL.Find(turn(masters[T], 180)) - T.corners[i] = src - - update_active() - -/datum/lighting_corner/proc/update_active() - active = FALSE - var/turf/T - var/thing - for (thing in masters) - T = thing - if (T.lighting_object) - active = TRUE - return + new_master_turf = get_step(new_turf, vertical) + if (new_master_turf) + save_master(new_master_turf, ((new_master_turf.x > x) ? EAST : WEST) | ((new_master_turf.y > y) ? NORTH : SOUTH)) // Get the dir based on coordinates. + +/datum/lighting_corner/proc/save_master(turf/master, dir) + switch (dir) + if (NORTHEAST) + master_NE = master + master.lighting_corner_SW = src + if (SOUTHEAST) + master_SE = master + master.lighting_corner_NW = src + if (SOUTHWEST) + master_SW = master + master.lighting_corner_NE = src + if (NORTHWEST) + master_NW = master + master.lighting_corner_SE = src + +/datum/lighting_corner/proc/self_destruct_if_idle() + if (!LAZYLEN(affecting)) + qdel(src, force = TRUE) + +/datum/lighting_corner/proc/vis_update() + for (var/datum/light_source/light_source as anything in affecting) + light_source.vis_update() + +/datum/lighting_corner/proc/full_update() + for (var/datum/light_source/light_source as anything in affecting) + light_source.recalc_corner(src) // God that was a mess, now to do the rest of the corner code! Hooray! /datum/lighting_corner/proc/update_lumcount(delta_r, delta_g, delta_b) @@ -94,20 +96,20 @@ GLOBAL_LIST_INIT(LIGHTING_CORNER_DIAGONAL, list(NORTHEAST, SOUTHEAST, SOUTHWEST, if (!needs_update) needs_update = TRUE - GLOB.lighting_update_corners += src + SSlighting.corners_queue += src /datum/lighting_corner/proc/update_objects() // Cache these values ahead of time so 4 individual lighting objects don't all calculate them individually. var/lum_r = src.lum_r var/lum_g = src.lum_g var/lum_b = src.lum_b - var/mx = max(lum_r, lum_g, lum_b) // Scale it so one of them is the strongest lum, if it is above 1. + var/largest_color_luminosity = max(lum_r, lum_g, lum_b) // Scale it so one of them is the strongest lum, if it is above 1. . = 1 // factor - if (mx > 1) - . = 1 / mx + if (largest_color_luminosity > 1) + . = 1 / largest_color_luminosity #if LIGHTING_SOFT_THRESHOLD != 0 - else if (mx < LIGHTING_SOFT_THRESHOLD) + else if (largest_color_luminosity < LIGHTING_SOFT_THRESHOLD) . = 0 // 0 means soft lighting. cache_r = round(lum_r * ., LIGHTING_ROUND_VALUE) || LIGHTING_SOFT_THRESHOLD @@ -118,13 +120,29 @@ GLOBAL_LIST_INIT(LIGHTING_CORNER_DIAGONAL, list(NORTHEAST, SOUTHEAST, SOUTHWEST, cache_g = round(lum_g * ., LIGHTING_ROUND_VALUE) cache_b = round(lum_b * ., LIGHTING_ROUND_VALUE) #endif - cache_mx = round(mx, LIGHTING_ROUND_VALUE) + src.largest_color_luminosity = round(largest_color_luminosity, LIGHTING_ROUND_VALUE) + + var/datum/lighting_object/lighting_object = master_NE?.lighting_object + if (lighting_object && !lighting_object.needs_update) + lighting_object.needs_update = TRUE + SSlighting.objects_queue += lighting_object + + lighting_object = master_SE?.lighting_object + if (lighting_object && !lighting_object.needs_update) + lighting_object.needs_update = TRUE + SSlighting.objects_queue += lighting_object + + lighting_object = master_SW?.lighting_object + if (lighting_object && !lighting_object.needs_update) + lighting_object.needs_update = TRUE + SSlighting.objects_queue += lighting_object + + lighting_object = master_NW?.lighting_object + if (lighting_object && !lighting_object.needs_update) + lighting_object.needs_update = TRUE + SSlighting.objects_queue += lighting_object - for (var/TT in masters) - var/turf/T = TT - if (T.lighting_object && !T.lighting_object.needs_update) - T.lighting_object.needs_update = TRUE - GLOB.lighting_update_objects += T.lighting_object + self_destruct_if_idle() /datum/lighting_corner/dummy/New() @@ -134,6 +152,23 @@ GLOBAL_LIST_INIT(LIGHTING_CORNER_DIAGONAL, list(NORTHEAST, SOUTHEAST, SOUTHWEST, if (!force) return QDEL_HINT_LETMELIVE - stack_trace("Ok, Look, /tg/, I need you to find whatever fucker decided to call qdel on a fucking lighting corner, then tell him very nicely and politely that he is 100% retarded and needs his head checked. Thanks. Send them my regards by the way.") + for (var/datum/light_source/light_source as anything in affecting) + LAZYREMOVE(light_source.effect_str, src) + affecting = null + + if (master_NE) + master_NE.lighting_corner_SW = null + master_NE.lighting_corners_initialised = FALSE + if (master_SE) + master_SE.lighting_corner_NW = null + master_SE.lighting_corners_initialised = FALSE + if (master_SW) + master_SW.lighting_corner_NE = null + master_SW.lighting_corners_initialised = FALSE + if (master_NW) + master_NW.lighting_corner_SE = null + master_NW.lighting_corners_initialised = FALSE + if (needs_update) + SSlighting.corners_queue -= src return ..() diff --git a/code/modules/lighting/lighting_object.dm b/code/modules/lighting/lighting_object.dm index 0845bb5d495a..49b3ec20e454 100644 --- a/code/modules/lighting/lighting_object.dm +++ b/code/modules/lighting/lighting_object.dm @@ -29,12 +29,12 @@ space_tile.update_starlight() needs_update = TRUE - GLOB.lighting_update_objects += src + SSlighting.objects_queue += src /datum/lighting_object/Destroy(force) if (!force) return QDEL_HINT_LETMELIVE - GLOB.lighting_update_objects -= src + SSlighting.objects_queue -= src if (isturf(affected_turf)) affected_turf.lighting_object = null affected_turf.luminosity = 1 @@ -54,34 +54,28 @@ var/static/datum/lighting_corner/dummy/dummy_lighting_corner = new - var/list/corners = affected_turf.corners - var/datum/lighting_corner/cr = dummy_lighting_corner - var/datum/lighting_corner/cg = dummy_lighting_corner - var/datum/lighting_corner/cb = dummy_lighting_corner - var/datum/lighting_corner/ca = dummy_lighting_corner - if (corners) //done this way for speed - cr = corners[3] || dummy_lighting_corner - cg = corners[2] || dummy_lighting_corner - cb = corners[4] || dummy_lighting_corner - ca = corners[1] || dummy_lighting_corner - - var/max = max(cr.cache_mx, cg.cache_mx, cb.cache_mx, ca.cache_mx) - - var/rr = cr.cache_r - var/rg = cr.cache_g - var/rb = cr.cache_b - - var/gr = cg.cache_r - var/gg = cg.cache_g - var/gb = cg.cache_b - - var/br = cb.cache_r - var/bg = cb.cache_g - var/bb = cb.cache_b - - var/ar = ca.cache_r - var/ag = ca.cache_g - var/ab = ca.cache_b + var/datum/lighting_corner/red_corner = affected_turf.lighting_corner_SW || dummy_lighting_corner + var/datum/lighting_corner/green_corner = affected_turf.lighting_corner_SE || dummy_lighting_corner + var/datum/lighting_corner/blue_corner = affected_turf.lighting_corner_NW || dummy_lighting_corner + var/datum/lighting_corner/alpha_corner = affected_turf.lighting_corner_NE || dummy_lighting_corner + + var/max = max(red_corner.largest_color_luminosity, green_corner.largest_color_luminosity, blue_corner.largest_color_luminosity, alpha_corner.largest_color_luminosity) + + var/rr = red_corner.cache_r + var/rg = red_corner.cache_g + var/rb = red_corner.cache_b + + var/gr = green_corner.cache_r + var/gg = green_corner.cache_g + var/gb = green_corner.cache_b + + var/br = blue_corner.cache_r + var/bg = blue_corner.cache_g + var/bb = blue_corner.cache_b + + var/ar = alpha_corner.cache_r + var/ag = alpha_corner.cache_g + var/ab = alpha_corner.cache_b #if LIGHTING_SOFT_THRESHOLD != 0 var/set_luminosity = max > LIGHTING_SOFT_THRESHOLD diff --git a/code/modules/lighting/lighting_source.dm b/code/modules/lighting/lighting_source.dm index a09f734ab71d..13164805fdec 100644 --- a/code/modules/lighting/lighting_source.dm +++ b/code/modules/lighting/lighting_source.dm @@ -2,14 +2,21 @@ // These are the main datums that emit light. /datum/light_source - var/atom/top_atom // The atom we're emitting light from (for example a mob if we're from a flashlight that's being held). - var/atom/source_atom // The atom that we belong to. - - var/turf/source_turf // The turf under the above. - var/turf/pixel_turf // The turf the top_atom appears to over. - var/light_power // Intensity of the emitter light. - var/light_range // The range of the emitted light. - var/light_color // The colour of the light, string, decomposed by parse_light_color() + ///The atom we're emitting light from (for example a mob if we're from a flashlight that's being held). + var/atom/top_atom + ///The atom that we belong to. + var/atom/source_atom + + ///The turf under the source atom. + var/turf/source_turf + ///The turf the top_atom appears to over. + var/turf/pixel_turf + ///Intensity of the emitter light. + var/light_power + /// The range of the emitted light. + var/light_range + /// The colour of the light, string, decomposed by parse_light_color() + var/light_color // Variables for keeping track of the colour. var/lum_r @@ -21,12 +28,14 @@ var/tmp/applied_lum_g var/tmp/applied_lum_b - var/list/datum/lighting_corner/effect_str // List used to store how much we're affecting corners. - var/list/turf/affecting_turfs + /// List used to store how much we're affecting corners. + var/list/datum/lighting_corner/effect_str - var/applied = FALSE // Whether we have applied our light yet or not. + /// Whether we have applied our light yet or not. + var/applied = FALSE - var/needs_update = LIGHTING_NO_UPDATE // Whether we are queued for an update. + /// whether we are to be added to SSlighting's sources_queue list for an update + var/needs_update = LIGHTING_NO_UPDATE /datum/light_source/New(var/atom/owner, var/atom/top) @@ -56,7 +65,7 @@ LAZYREMOVE(top_atom.light_sources, src) if (needs_update) - GLOB.lighting_update_lights -= src + SSlighting.sources_queue -= src . = ..() @@ -65,7 +74,7 @@ // Actually that'd be great if you could! #define EFFECT_UPDATE(level) \ if (needs_update == LIGHTING_NO_UPDATE) \ - GLOB.lighting_update_lights += src; \ + SSlighting.sources_queue += src; \ if (needs_update < level) \ needs_update = level; \ @@ -99,56 +108,43 @@ // The braces and semicolons are there to be able to do this on a single line. #define LUM_FALLOFF(C, T) (1 - CLAMP01(sqrt((C.x - T.x) ** 2 + (C.y - T.y) ** 2 + LIGHTING_HEIGHT) / max(1, light_range))) -#define APPLY_CORNER(C) \ - . = LUM_FALLOFF(C, pixel_turf); \ - . *= light_power; \ - var/OLD = effect_str[C]; \ - effect_str[C] = .; \ - \ - C.update_lumcount \ - ( \ - (. * lum_r) - (OLD * applied_lum_r), \ - (. * lum_g) - (OLD * applied_lum_g), \ - (. * lum_b) - (OLD * applied_lum_b) \ - ); - -#define REMOVE_CORNER(C) \ - . = -effect_str[C]; \ - C.update_lumcount \ - ( \ - . * applied_lum_r, \ - . * applied_lum_g, \ - . * applied_lum_b \ +#define APPLY_CORNER(C) \ + . = LUM_FALLOFF(C, pixel_turf); \ + . *= light_power; \ + var/OLD = effect_str[C]; \ + \ + C.update_lumcount \ + ( \ + (. * lum_r) - (OLD * applied_lum_r), \ + (. * lum_g) - (OLD * applied_lum_g), \ + (. * lum_b) - (OLD * applied_lum_b) \ + ); \ + +#define REMOVE_CORNER(C) \ + . = -effect_str[C]; \ + C.update_lumcount \ + ( \ + . * applied_lum_r, \ + . * applied_lum_g, \ + . * applied_lum_b \ ); // This is the define used to calculate falloff. /datum/light_source/proc/remove_lum() applied = FALSE - var/thing - for (thing in affecting_turfs) - var/turf/T = thing - LAZYREMOVE(T.affecting_lights, src) - - affecting_turfs = null - - var/datum/lighting_corner/C - for (thing in effect_str) - C = thing - REMOVE_CORNER(C) + for (var/datum/lighting_corner/corner as anything in effect_str) + REMOVE_CORNER(corner) + LAZYREMOVE(corner.affecting, src) - LAZYREMOVE(C.affecting, src) - - effect_str = null - -/datum/light_source/proc/recalc_corner(var/datum/lighting_corner/C) +/datum/light_source/proc/recalc_corner(var/datum/lighting_corner/corner) LAZYINITLIST(effect_str) - if (effect_str[C]) // Already have one. - REMOVE_CORNER(C) - effect_str[C] = 0 + if (effect_str[corner]) // Already have one. + REMOVE_CORNER(corner) + effect_str[corner] = 0 - APPLY_CORNER(C) - UNSETEMPTY(effect_str) + APPLY_CORNER(corner) + effect_str[corner] = . /datum/light_source/proc/update_corners() @@ -185,9 +181,9 @@ pixel_turf = get_turf_pixel(top_atom) update = TRUE else - var/P = get_turf_pixel(top_atom) - if (P != pixel_turf) - pixel_turf = P + var/pixel_loc = get_turf_pixel(top_atom) + if (pixel_loc != pixel_turf) + pixel_turf = pixel_loc update = TRUE if (!isturf(source_turf)) @@ -213,77 +209,56 @@ return //nothing's changed var/list/datum/lighting_corner/corners = list() - var/list/turf/turfs = list() - var/thing - var/datum/lighting_corner/C - var/turf/T + var/list/turf/turfs = list() if (source_turf) var/oldlum = source_turf.luminosity source_turf.luminosity = CEILING(light_range, 1) - for(T in view(CEILING(light_range, 1), source_turf)) - if((!IS_DYNAMIC_LIGHTING(T) && !T.light_sources) || T.has_opaque_atom) - continue - if (!T.lighting_corners_initialised) - T.generate_missing_corners() - for (thing in T.corners) - C = thing - corners[C] = 0 + for(var/turf/T in view(CEILING(light_range, 1), source_turf)) + if(!T.has_opaque_atom) + if (!T.lighting_corners_initialised) + T.generate_missing_corners() + corners[T.lighting_corner_NE] = 0 + corners[T.lighting_corner_SE] = 0 + corners[T.lighting_corner_SW] = 0 + corners[T.lighting_corner_NW] = 0 turfs += T source_turf.luminosity = oldlum - LAZYINITLIST(affecting_turfs) - var/list/L = turfs - affecting_turfs // New turfs, add us to the affecting lights of them. - affecting_turfs += L - for (thing in L) - T = thing - LAZYADD(T.affecting_lights, src) - - L = affecting_turfs - turfs // Now-gone turfs, remove us from the affecting lights. - affecting_turfs -= L - for (thing in L) - T = thing - LAZYREMOVE(T.affecting_lights, src) - + var/list/datum/lighting_corner/new_corners = (corners - effect_str) LAZYINITLIST(effect_str) if (needs_update == LIGHTING_VIS_UPDATE) - for (thing in corners - effect_str) // New corners - C = thing - LAZYADD(C.affecting, src) - if (!C.active) - effect_str[C] = 0 - continue - APPLY_CORNER(C) + for (var/datum/lighting_corner/corner as anything in new_corners) + APPLY_CORNER(corner) + if (. != 0) + LAZYADD(corner.affecting, src) + effect_str[corner] = . else - L = corners - effect_str - for (thing in L) // New corners - C = thing - LAZYADD(C.affecting, src) - if (!C.active) - effect_str[C] = 0 - continue - APPLY_CORNER(C) - - for (thing in corners - L) // Existing corners - C = thing - if (!C.active) - effect_str[C] = 0 - continue - APPLY_CORNER(C) - - L = effect_str - corners - for (thing in L) // Old, now gone, corners. - C = thing - REMOVE_CORNER(C) - LAZYREMOVE(C.affecting, src) - effect_str -= L + for (var/datum/lighting_corner/corner as anything in new_corners) + APPLY_CORNER(corner) + if (. != 0) + LAZYADD(corner.affecting, src) + effect_str[corner] = . + + for (var/datum/lighting_corner/corner as anything in corners - new_corners) // Existing corners + APPLY_CORNER(corner) + if (. != 0) + effect_str[corner] = . + else + LAZYREMOVE(corner.affecting, src) + effect_str -= corner + + var/list/datum/lighting_corner/gone_corners = effect_str - corners + for (var/datum/lighting_corner/corner as anything in gone_corners) + REMOVE_CORNER(corner) + LAZYREMOVE(corner.affecting, src) + effect_str -= gone_corners applied_lum_r = lum_r applied_lum_g = lum_g applied_lum_b = lum_b UNSETEMPTY(effect_str) - UNSETEMPTY(affecting_turfs) #undef EFFECT_UPDATE #undef LUM_FALLOFF diff --git a/code/modules/lighting/lighting_turf.dm b/code/modules/lighting/lighting_turf.dm index 975a1a2bf9c7..d050a2a7dd5a 100644 --- a/code/modules/lighting/lighting_turf.dm +++ b/code/modules/lighting/lighting_turf.dm @@ -4,71 +4,58 @@ var/tmp/lighting_corners_initialised = FALSE - var/tmp/list/datum/light_source/affecting_lights // List of light sources affecting this turf. var/tmp/datum/lighting_object/lighting_object // Our lighting object. - var/tmp/list/datum/lighting_corner/corners + + ///Lighting Corner datums. + var/tmp/datum/lighting_corner/lighting_corner_NE + var/tmp/datum/lighting_corner/lighting_corner_SE + var/tmp/datum/lighting_corner/lighting_corner_SW + var/tmp/datum/lighting_corner/lighting_corner_NW + var/tmp/has_opaque_atom = FALSE // Not to be confused with opacity, this will be TRUE if there's any opaque atom on the tile. // Causes any affecting light sources to be queued for a visibility update, for example a door got opened. /turf/proc/reconsider_lights() - var/datum/light_source/L - var/thing - for (thing in affecting_lights) - L = thing - L.vis_update() + lighting_corner_NE?.vis_update() + lighting_corner_SE?.vis_update() + lighting_corner_SW?.vis_update() + lighting_corner_NW?.vis_update() /turf/proc/lighting_clear_overlay() if (lighting_object) - qdel(lighting_object, TRUE) - - var/datum/lighting_corner/C - var/thing - for (thing in corners) - if(!thing) - continue - C = thing - C.update_active() + qdel(lighting_object, force=TRUE) // Builds a lighting object for us, but only if our area is dynamic. /turf/proc/lighting_build_overlay() if (lighting_object) - qdel(lighting_object,force=TRUE) //Shitty fix for lighting objects persisting after death + qdel(lighting_object, force=TRUE) //Shitty fix for lighting objects persisting after death - var/area/A = loc - if (!IS_DYNAMIC_LIGHTING(A) && !light_sources) + var/area/our_area = loc + if (!IS_DYNAMIC_LIGHTING(our_area) && !light_sources) return - if (!lighting_corners_initialised) - generate_missing_corners() - new/datum/lighting_object(src) - var/thing - var/datum/lighting_corner/C - var/datum/light_source/S - for (thing in corners) - if(!thing) - continue - C = thing - if (!C.active) // We would activate the corner, calculate the lighting for it. - for (thing in C.affecting) - S = thing - S.recalc_corner(C) - C.active = TRUE - // Used to get a scaled lumcount. /turf/proc/get_lumcount(var/minlum = 0, var/maxlum = 1) if (!lighting_object) return 1 var/totallums = 0 - var/thing var/datum/lighting_corner/L - for (thing in corners) - if(!thing) - continue - L = thing + L = lighting_corner_NE + if (L) + totallums += L.lum_r + L.lum_b + L.lum_g + L = lighting_corner_SE + if (L) + totallums += L.lum_r + L.lum_b + L.lum_g + L = lighting_corner_SW + if (L) totallums += L.lum_r + L.lum_b + L.lum_g + L = lighting_corner_NW + if (L) + totallums += L.lum_r + L.lum_b + L.lum_g + totallums /= 12 // 4 corners, each with 3 channels, get the average. @@ -111,14 +98,16 @@ lighting_clear_overlay() /turf/proc/generate_missing_corners() - if (!IS_DYNAMIC_LIGHTING(src) && !light_sources) - return - lighting_corners_initialised = TRUE - if (!corners) - corners = list(null, null, null, null) + if (!lighting_corner_NE) + lighting_corner_NE = new/datum/lighting_corner(src, NORTH|EAST) - for (var/i = 1 to 4) - if (corners[i]) // Already have a corner on this direction. - continue + if (!lighting_corner_SE) + lighting_corner_SE = new/datum/lighting_corner(src, SOUTH|EAST) - corners[i] = new/datum/lighting_corner(src, GLOB.LIGHTING_CORNER_DIAGONAL[i]) + if (!lighting_corner_SW) + lighting_corner_SW = new/datum/lighting_corner(src, SOUTH|WEST) + + if (!lighting_corner_NW) + lighting_corner_NW = new/datum/lighting_corner(src, NORTH|WEST) + + lighting_corners_initialised = TRUE diff --git a/yogstation/code/modules/antagonists/shadowling/shadowling_abilities.dm b/yogstation/code/modules/antagonists/shadowling/shadowling_abilities.dm index ea8d832aeb18..8e4d9f652f15 100644 --- a/yogstation/code/modules/antagonists/shadowling/shadowling_abilities.dm +++ b/yogstation/code/modules/antagonists/shadowling/shadowling_abilities.dm @@ -159,7 +159,7 @@ return to_chat(user, span_shadowling("You silently disable all nearby lights.")) var/turf/T = get_turf(user) - for(var/datum/light_source/LS in T.affecting_lights) + for(var/datum/light_source/LS in T.light_sources) var/atom/LO = LS.source_atom if(isitem(LO)) extinguishItem(LO) From 7a4b747defc1d5b6ef7874970ed5aa47cf11ac70 Mon Sep 17 00:00:00 2001 From: Ling Date: Mon, 2 Jan 2023 15:36:55 +0200 Subject: [PATCH 02/13] Overlay lighting, first version compiles --- code/__DEFINES/components.dm | 20 + code/__DEFINES/layers.dm | 8 +- code/__DEFINES/lighting.dm | 10 + code/__DEFINES/mobs.dm | 3 + code/_onclick/hud/plane_master.dm | 13 +- code/datums/components/overlay_lighting.dm | 352 ++++++++++++++++++ code/datums/mutations/body.dm | 2 +- code/game/atoms.dm | 9 +- code/game/atoms_movable.dm | 8 + code/game/machinery/computer/_computer.dm | 9 +- code/game/machinery/dance_machine.dm | 24 +- code/game/machinery/flasher.dm | 9 +- code/game/mecha/mecha.dm | 2 + code/game/movable_luminosity.dm | 19 + .../effects/effect_system/effects_sparks.dm | 5 +- code/game/objects/effects/misc.dm | 10 +- code/game/objects/effects/overlays.dm | 11 + code/game/objects/items/cigs_lighters.dm | 9 +- code/game/objects/items/devices/PDA/PDA.dm | 27 +- .../objects/items/devices/PDA/PDA_types.dm | 1 - code/game/objects/items/devices/flashlight.dm | 90 +++-- code/game/objects/items/discoball.dm | 26 +- code/game/objects/items/flamethrower.dm | 3 + code/game/objects/items/grenades/flashbang.dm | 2 +- code/game/objects/items/melee/energy.dm | 10 +- code/game/objects/items/tools/weldingtool.dm | 32 +- code/game/objects/items/twohanded.dm | 9 +- code/game/turfs/space/space.dm | 2 +- .../bloodsuckers/powers/targeted/brawn.dm | 10 +- code/modules/antagonists/cult/blood_magic.dm | 4 +- code/modules/antagonists/cult/cult_items.dm | 7 +- code/modules/assembly/flash.dm | 9 +- .../atmospherics/environmental/LINDA_fire.dm | 7 +- code/modules/clothing/head/hardhat.dm | 19 +- code/modules/clothing/head/helmet.dm | 44 ++- code/modules/clothing/head/misc_special.dm | 6 +- code/modules/clothing/shoes/miscellaneous.dm | 17 +- code/modules/clothing/spacesuits/hardsuit.dm | 20 +- code/modules/clothing/spacesuits/plasmamen.dm | 18 +- code/modules/hydroponics/grown/ambrosia.dm | 1 + code/modules/hydroponics/plant_genes.dm | 5 +- code/modules/lighting/lighting_atom.dm | 94 +++-- code/modules/lighting/lighting_turf.dm | 5 + .../mining/lavaland/necropolis_chests.dm | 2 + code/modules/mining/minebot.dm | 10 +- code/modules/mob/dead/observer/observer.dm | 9 +- .../carbon/human/species_types/ethereal.dm | 39 +- .../carbon/human/species_types/jellypeople.dm | 2 + .../human/species_types/shadowpeople.dm | 4 +- .../modules/mob/living/silicon/robot/robot.dm | 16 +- .../mob/living/simple_animal/bot/bot.dm | 9 +- .../living/simple_animal/guardian/guardian.dm | 9 +- .../hostile/megafauna/colossus.dm | 1 + .../hostile/megafauna/swarmer.dm | 1 + .../hostile/mining_mobs/basilisk.dm | 1 + .../simple_animal/hostile/retaliate/ghost.dm | 4 +- .../computers/item/computer.dm | 2 - code/modules/photography/camera/camera.dm | 11 +- code/modules/projectiles/gun.dm | 42 +-- .../projectiles/guns/energy/energy_gun.dm | 2 +- code/modules/projectiles/projectile/beams.dm | 4 +- .../chemistry/reagents/food_reagents.dm | 24 +- .../chemistry/recipes/pyrotechnics.dm | 4 +- code/modules/spells/spell_types/lichdom.dm | 4 +- code/modules/surgery/organs/eyes.dm | 29 +- code/modules/surgery/tools.dm | 10 +- code/modules/swarmers/swarmer.dm | 12 +- icons/effects/light_overlays/light_128.dmi | Bin 0 -> 7040 bytes icons/effects/light_overlays/light_160.dmi | Bin 0 -> 9920 bytes icons/effects/light_overlays/light_192.dmi | Bin 0 -> 13534 bytes icons/effects/light_overlays/light_224.dmi | Bin 0 -> 17004 bytes icons/effects/light_overlays/light_256.dmi | Bin 0 -> 21365 bytes icons/effects/light_overlays/light_288.dmi | Bin 0 -> 26103 bytes icons/effects/light_overlays/light_32.dmi | Bin 0 -> 1140 bytes icons/effects/light_overlays/light_320.dmi | Bin 0 -> 30919 bytes icons/effects/light_overlays/light_352.dmi | Bin 0 -> 35566 bytes icons/effects/light_overlays/light_64.dmi | Bin 0 -> 2482 bytes icons/effects/light_overlays/light_96.dmi | Bin 0 -> 4643 bytes yogstation.dme | 2 + .../darkspawn_objects/umbral_tendrils.dm | 4 +- .../shadowling/shadowling_abilities.dm | 10 +- 81 files changed, 952 insertions(+), 306 deletions(-) create mode 100644 code/datums/components/overlay_lighting.dm create mode 100644 code/game/movable_luminosity.dm create mode 100644 icons/effects/light_overlays/light_128.dmi create mode 100644 icons/effects/light_overlays/light_160.dmi create mode 100644 icons/effects/light_overlays/light_192.dmi create mode 100644 icons/effects/light_overlays/light_224.dmi create mode 100644 icons/effects/light_overlays/light_256.dmi create mode 100644 icons/effects/light_overlays/light_288.dmi create mode 100644 icons/effects/light_overlays/light_32.dmi create mode 100644 icons/effects/light_overlays/light_320.dmi create mode 100644 icons/effects/light_overlays/light_352.dmi create mode 100644 icons/effects/light_overlays/light_64.dmi create mode 100644 icons/effects/light_overlays/light_96.dmi diff --git a/code/__DEFINES/components.dm b/code/__DEFINES/components.dm index 30c54cc0882e..d964c76b653c 100644 --- a/code/__DEFINES/components.dm +++ b/code/__DEFINES/components.dm @@ -438,3 +438,23 @@ #define COMPONENT_BAKING_BAD_RESULT (1<<2) ///Called when an object is turned into another item through baking in an oven #define COMSIG_BAKE_COMPLETED "item_bake_completed" + +//Lightning +///Called right before the atom changes the value of light_range to a different one, from base atom/set_light_range(): (new_range) +#define COMSIG_ATOM_SET_LIGHT_RANGE "atom_set_light_range" +///Called right before the atom changes the value of light_power to a different one, from base atom/set_light_power(): (new_power) +#define COMSIG_ATOM_SET_LIGHT_POWER "atom_set_light_power" +///Called right before the atom changes the value of light_color to a different one, from base atom/set_light_color(): (new_color) +#define COMSIG_ATOM_SET_LIGHT_COLOR "atom_set_light_color" +///Called right before the atom changes the value of light_on to a different one, from base atom/set_light_on(): (new_value) +#define COMSIG_ATOM_SET_LIGHT_ON "atom_set_light_on" +///Called right before the atom changes the value of light_flags to a different one, from base atom/set_light_flags(): (new_value) +#define COMSIG_ATOM_SET_LIGHT_FLAGS "atom_set_light_flags" +///Called when the movable tries to change its dynamic light color setting, from base atom/movable/lighting_overlay_set_color(): (color) +#define COMSIG_MOVABLE_LIGHT_OVERLAY_SET_RANGE "movable_light_overlay_set_color" +///Called when the movable tries to change its dynamic light power setting, from base atom/movable/lighting_overlay_set_power(): (power) +#define COMSIG_MOVABLE_LIGHT_OVERLAY_SET_POWER "movable_light_overlay_set_power" +///Called when the movable tries to change its dynamic light range setting, from base atom/movable/lighting_overlay_set_range(): (range) +#define COMSIG_MOVABLE_LIGHT_OVERLAY_SET_COLOR "movable_light_overlay_set_range" +///Called when the movable tries to toggle its dynamic light LIGHTING_ON status, from base atom/movable/lighting_overlay_toggle_on(): (new_state) +#define COMSIG_MOVABLE_LIGHT_OVERLAY_TOGGLE_ON "movable_light_overlay_toggle_on" diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm index b7389061fba6..f393e916f481 100644 --- a/code/__DEFINES/layers.dm +++ b/code/__DEFINES/layers.dm @@ -107,8 +107,12 @@ #define RAD_TEXT_LAYER 15.1 -#define ABOVE_LIGHTING_PLANE 16 -#define ABOVE_LIGHTING_LAYER 16 +#define O_LIGHTING_VISUAL_PLANE 16 +#define O_LIGHTING_VISUAL_LAYER 16 +#define O_LIGHTING_VISUAL_RENDER_TARGET "O_LIGHT_VISUAL_PLANE" + +#define ABOVE_LIGHTING_PLANE 17 +#define ABOVE_LIGHTING_LAYER 17 #define ABOVE_LIGHTING_RENDER_TARGET "ABOVE_LIGHTING_PLANE" #define FLOOR_OPENSPACE_PLANE 17 diff --git a/code/__DEFINES/lighting.dm b/code/__DEFINES/lighting.dm index 1549afd6c5c8..c441d47f7e18 100644 --- a/code/__DEFINES/lighting.dm +++ b/code/__DEFINES/lighting.dm @@ -1,3 +1,13 @@ +///Object doesn't use any of the light systems. Should be changed to add a light source to the object. +#define NO_LIGHT_SUPPORT 0 +///Light made with the lighting datums, applying a matrix. +#define STATIC_LIGHT 1 +///Light made by masking the lighting darkness plane. +#define MOVABLE_LIGHT 2 + +///Is a movable light source attached to another movable (its loc), meaning that the lighting component should go one level deeper. +#define LIGHT_ATTACHED (1<<0) + //Bay lighting engine shit, not in /code/modules/lighting because BYOND is being shit about it /// frequency, in 1/10ths of a second, of the lighting process #define LIGHTING_INTERVAL 5 diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 3928b4630398..d8d983ef20d8 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -381,3 +381,6 @@ ///Define for spawning megafauna instead of a mob for cave gen #define SPAWN_MEGAFAUNA "bluh bluh huge boss" + +///Swarmer flags +#define SWARMER_LIGHT_ON (1<<0) diff --git a/code/_onclick/hud/plane_master.dm b/code/_onclick/hud/plane_master.dm index 28984b7c19a8..e1197a0ad176 100644 --- a/code/_onclick/hud/plane_master.dm +++ b/code/_onclick/hud/plane_master.dm @@ -76,8 +76,9 @@ /atom/movable/screen/plane_master/lighting/Initialize() . = ..() - filters += filter(type="alpha", render_source=EMISSIVE_RENDER_TARGET, flags=MASK_INVERSE) - filters += filter(type="alpha", render_source=EMISSIVE_UNBLOCKABLE_RENDER_TARGET, flags=MASK_INVERSE) + //filters += filter(type="alpha", render_source=EMISSIVE_RENDER_TARGET, flags=MASK_INVERSE) + //filters += filter(type="alpha", render_source=EMISSIVE_UNBLOCKABLE_RENDER_TARGET, flags=MASK_INVERSE) + filters += filter(type="lighting", render_source = O_LIGHTING_VISUAL_RENDER_TARGET, flags = MASK_INVERSE) /** * Things placed on this mask the lighting plane. Doesn't render directly. @@ -150,3 +151,11 @@ filters = list() if(istype(mymob) && mymob.client?.prefs?.ambientocclusion) filters += AMBIENT_OCCLUSION + +/atom/movable/screen/plane_master/o_light_visual + name = "overlight light visual plane master" + layer = O_LIGHTING_VISUAL_LAYER + plane = O_LIGHTING_VISUAL_PLANE + render_target = O_LIGHTING_VISUAL_RENDER_TARGET + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + blend_mode = BLEND_MULTIPLY diff --git a/code/datums/components/overlay_lighting.dm b/code/datums/components/overlay_lighting.dm new file mode 100644 index 000000000000..347808e07efd --- /dev/null +++ b/code/datums/components/overlay_lighting.dm @@ -0,0 +1,352 @@ +///For switchable lights, is it on and currently emitting light? +#define LIGHTING_ON (1<<0) +///Is the parent attached to something else, its loc? Then we need to keep an eye of this. +#define LIGHTING_ATTACHED (1<<1) + +#define GET_PARENT (parent_attached_to || parent) + +/** + * Movable atom overlay-based lighting component. + * + * * Component works by applying a visual object to the parent target. + * + * * The component tracks the parent's loc to determine the current_holder. + * * The current_holder is either the parent or its loc, whichever is on a turf. If none, then the current_holder is null and the light is not visible. + * + * * Lighting works at its base by applying a dark overlay and "cutting" said darkness with light, adding (possibly colored) transparency. + * * This component uses the visible_mask visual object to apply said light mask on the darkness. + * + * * The main limitation of this system is that it uses a limited number of pre-baked geometrical shapes, but for most uses it does the job. + * + * * Another limitation is for big lights: you only see the light if you see the object emiting it. + * * For small objects this is good (you can't see them behind a wall), but for big ones this quickly becomes prety clumsy. +*/ +/datum/component/overlay_lighting + ///How far the light reaches, float. + var/range = 1 + ///Ceiling of range, integer without decimal entries. + var/lumcount_range = 0 + ///How much this light affects the dynamic_lumcount of turfs. + var/lum_power = 0.5 + ///Transparency value. + var/set_alpha = 0 + ///For light sources that can be turned on and off. + var/overlay_lighting_flags = NONE + + ///Cache of the possible light overlays, according to size. + var/static/list/light_overlays = list( + "32" = 'icons/effects/light_overlays/light_32.dmi', + "64" = 'icons/effects/light_overlays/light_64.dmi', + "96" = 'icons/effects/light_overlays/light_96.dmi', + "128" = 'icons/effects/light_overlays/light_128.dmi', + "160" = 'icons/effects/light_overlays/light_160.dmi', + "192" = 'icons/effects/light_overlays/light_192.dmi', + "224" = 'icons/effects/light_overlays/light_224.dmi', + "256" = 'icons/effects/light_overlays/light_256.dmi', + "288" = 'icons/effects/light_overlays/light_288.dmi', + "320" = 'icons/effects/light_overlays/light_320.dmi', + "352" = 'icons/effects/light_overlays/light_352.dmi', + ) + + ///Overlay effect to cut into the darkness and provide light. + var/obj/effect/overlay/light_visible/visible_mask + ///Lazy list to track the turfs being affected by our light, to determine their visibility. + var/list/turf/affected_turfs + ///Movable atom currently holding the light. Parent might be a flashlight, for example, but that might be held by a mob or something else. + var/atom/movable/current_holder + ///Movable atom the parent is attached to. For example, a flashlight into a helmet or gun. We'll need to track the thing the parent is attached to as if it were the parent itself. + var/atom/movable/parent_attached_to + + +/datum/component/overlay_lighting/Initialize(_range, _power, _color, starts_on) + if(!ismovable(parent)) + return COMPONENT_INCOMPATIBLE + + var/atom/movable/movable_parent = parent + if(movable_parent.light_system != MOVABLE_LIGHT) + stack_trace("[type] added to [parent], with [movable_parent.light_system] value for the light_system var. Use [MOVABLE_LIGHT] instead.") + return COMPONENT_INCOMPATIBLE + + . = ..() + + visible_mask = new() + if(!isnull(_range)) + movable_parent.set_light_range(_range) + set_range(parent, movable_parent.light_range) + if(!isnull(_power)) + movable_parent.set_light_power(_power) + set_power(parent, movable_parent.light_power) + if(!isnull(_color)) + movable_parent.set_light_color(_color) + set_color(parent, movable_parent.light_color) + if(!isnull(starts_on)) + movable_parent.set_light_on(starts_on) + + +/datum/component/overlay_lighting/RegisterWithParent() + . = ..() + RegisterSignal(parent, COMSIG_MOVABLE_MOVED, .proc/on_parent_moved) + RegisterSignal(parent, COMSIG_ATOM_SET_LIGHT_RANGE, .proc/set_range) + RegisterSignal(parent, COMSIG_ATOM_SET_LIGHT_POWER, .proc/set_power) + RegisterSignal(parent, COMSIG_ATOM_SET_LIGHT_COLOR, .proc/set_color) + RegisterSignal(parent, COMSIG_ATOM_SET_LIGHT_ON, .proc/on_toggle) + RegisterSignal(parent, COMSIG_ATOM_SET_LIGHT_FLAGS, .proc/on_light_flags_change) + var/atom/movable/movable_parent = parent + if(movable_parent.light_flags & LIGHT_ATTACHED) + overlay_lighting_flags |= LIGHTING_ATTACHED + set_parent_attached_to(ismovable(movable_parent.loc) ? movable_parent.loc : null) + check_holder() + if(movable_parent.light_on) + turn_on() + + +/datum/component/overlay_lighting/UnregisterFromParent() + overlay_lighting_flags &= ~LIGHTING_ATTACHED + set_parent_attached_to(null) + set_holder(null) + clean_old_turfs() + UnregisterSignal(parent, list( + COMSIG_MOVABLE_MOVED, + COMSIG_ATOM_SET_LIGHT_RANGE, + COMSIG_ATOM_SET_LIGHT_POWER, + COMSIG_ATOM_SET_LIGHT_COLOR, + COMSIG_ATOM_SET_LIGHT_ON, + COMSIG_ATOM_SET_LIGHT_FLAGS, + )) + if(overlay_lighting_flags & LIGHTING_ON) + turn_off() + return ..() + + +/datum/component/overlay_lighting/Destroy() + set_parent_attached_to(null) + set_holder(null) + clean_old_turfs() + QDEL_NULL(visible_mask) + return ..() + + +///Clears the affected_turfs lazylist, removing from its contents the effects of being near the light. +/datum/component/overlay_lighting/proc/clean_old_turfs() + for(var/t in affected_turfs) + var/turf/lit_turf = t + lit_turf.dynamic_lumcount -= lum_power + affected_turfs = null + + +///Populates the affected_turfs lazylist, adding to its contents the effects of being near the light. +/datum/component/overlay_lighting/proc/get_new_turfs() + if(!current_holder) + return + for(var/turf/lit_turf in view(lumcount_range, get_turf(current_holder))) + lit_turf.dynamic_lumcount += lum_power + LAZYADD(affected_turfs, lit_turf) + + +///Clears the old affected turfs and populates the new ones. +/datum/component/overlay_lighting/proc/make_luminosity_update() + clean_old_turfs() + if(!isturf(current_holder?.loc)) + return + get_new_turfs() + + +///Adds the luminosity and source for the afected movable atoms to keep track of their visibility. +/datum/component/overlay_lighting/proc/add_dynamic_lumi(atom/movable/affected_movable) + LAZYSET(affected_movable.affected_dynamic_lights, src, lumcount_range + 1) + affected_movable.vis_contents += visible_mask + affected_movable.update_dynamic_luminosity() + + +///Removes the luminosity and source for the afected movable atoms to keep track of their visibility. +/datum/component/overlay_lighting/proc/remove_dynamic_lumi(atom/movable/affected_movable) + LAZYREMOVE(affected_movable.affected_dynamic_lights, src) + affected_movable.vis_contents -= visible_mask + affected_movable.update_dynamic_luminosity() + + +///Called to change the value of parent_attached_to. +/datum/component/overlay_lighting/proc/set_parent_attached_to(atom/movable/new_parent_attached_to) + if(new_parent_attached_to == parent_attached_to) + return + . = parent_attached_to + parent_attached_to = new_parent_attached_to + if(.) + var/atom/movable/old_parent_attached_to = . + UnregisterSignal(old_parent_attached_to, list(COMSIG_PARENT_QDELETING, COMSIG_MOVABLE_MOVED)) + if(old_parent_attached_to == current_holder) + RegisterSignal(old_parent_attached_to, COMSIG_PARENT_QDELETING, .proc/on_holder_qdel) + RegisterSignal(old_parent_attached_to, COMSIG_MOVABLE_MOVED, .proc/on_holder_moved) + if(parent_attached_to) + if(parent_attached_to == current_holder) + UnregisterSignal(current_holder, list(COMSIG_PARENT_QDELETING, COMSIG_MOVABLE_MOVED)) + RegisterSignal(parent_attached_to, COMSIG_PARENT_QDELETING, .proc/on_parent_attached_to_qdel) + RegisterSignal(parent_attached_to, COMSIG_MOVABLE_MOVED, .proc/on_parent_attached_to_moved) + check_holder() + + +///Called to change the value of current_holder. +/datum/component/overlay_lighting/proc/set_holder(atom/movable/new_holder) + if(new_holder == current_holder) + return + if(current_holder) + if(current_holder != parent && current_holder != parent_attached_to) + UnregisterSignal(current_holder, list(COMSIG_PARENT_QDELETING, COMSIG_MOVABLE_MOVED)) + if(overlay_lighting_flags & LIGHTING_ON) + remove_dynamic_lumi(current_holder) + current_holder = new_holder + if(new_holder == null) + clean_old_turfs() + return + if(overlay_lighting_flags & LIGHTING_ON) + add_dynamic_lumi(new_holder) + if(new_holder != parent && new_holder != parent_attached_to) + RegisterSignal(new_holder, COMSIG_PARENT_QDELETING, .proc/on_holder_qdel) + RegisterSignal(new_holder, COMSIG_MOVABLE_MOVED, .proc/on_holder_moved) + + +///Used to determine the new valid current_holder from the parent's loc. +/datum/component/overlay_lighting/proc/check_holder() + var/atom/movable/movable_parent = GET_PARENT + if(isturf(movable_parent.loc)) + set_holder(movable_parent) + return + var/atom/inside = movable_parent.loc //Parent's loc + if(isnull(inside)) + set_holder(null) + return + if(isturf(inside.loc)) + set_holder(inside) + return + set_holder(null) + + +///Called when the current_holder is qdeleted, to remove the light effect. +/datum/component/overlay_lighting/proc/on_holder_qdel(atom/movable/source, force) + UnregisterSignal(current_holder, list(COMSIG_PARENT_QDELETING, COMSIG_MOVABLE_MOVED)) + set_holder(null) + + +///Called when current_holder changes loc. +/datum/component/overlay_lighting/proc/on_holder_moved(atom/movable/source, OldLoc, Dir, Forced) + if(!(overlay_lighting_flags & LIGHTING_ON)) + return + make_luminosity_update() + + +///Called when parent changes loc. +/datum/component/overlay_lighting/proc/on_parent_moved(atom/movable/source, OldLoc, Dir, Forced) + var/atom/movable/movable_parent = parent + if(overlay_lighting_flags & LIGHTING_ATTACHED) + set_parent_attached_to(ismovable(movable_parent.loc) ? movable_parent.loc : null) + check_holder() + if(!(overlay_lighting_flags & LIGHTING_ON) || !current_holder) + return + make_luminosity_update() + + +///Called when the current_holder is qdeleted, to remove the light effect. +/datum/component/overlay_lighting/proc/on_parent_attached_to_qdel(atom/movable/source, force) + UnregisterSignal(parent_attached_to, list(COMSIG_PARENT_QDELETING, COMSIG_MOVABLE_MOVED)) + if(parent_attached_to == current_holder) + set_holder(null) + set_parent_attached_to(null) + + +///Called when parent_attached_to changes loc. +/datum/component/overlay_lighting/proc/on_parent_attached_to_moved(atom/movable/source, OldLoc, Dir, Forced) + check_holder() + if(!(overlay_lighting_flags & LIGHTING_ON) || !current_holder) + return + make_luminosity_update() + + +///Changes the range which the light reaches. 0 means no light, 6 is the maximum value. +/datum/component/overlay_lighting/proc/set_range(atom/source, new_range) + if(range == new_range) + return + if(range == 0) + turn_off() + range = clamp(CEILING(new_range, 0.5), 1, 6) + var/pixel_bounds = ((range - 1) * 64) + 32 + lumcount_range = CEILING(range, 1) + visible_mask.icon = light_overlays["[pixel_bounds]"] + if(pixel_bounds == 32) + visible_mask.transform = null + return + var/offset = (pixel_bounds - 32) * 0.5 + var/matrix/transform = new + transform.Translate(-offset, -offset) + visible_mask.transform = transform + if(overlay_lighting_flags & LIGHTING_ON) + make_luminosity_update() + + +///Changes the intensity/brightness of the light by altering the visual object's alpha. +/datum/component/overlay_lighting/proc/set_power(atom/source, new_power) + set_lum_power(new_power >= 0 ? 0.5 : -0.5) + set_alpha = min(230, (abs(new_power) * 120) + 30) + visible_mask.alpha = set_alpha + + +///Changes the light's color, pretty straightforward. +/datum/component/overlay_lighting/proc/set_color(atom/source, new_color) + visible_mask.color = new_color + + +///Toggles the light on and off. +/datum/component/overlay_lighting/proc/on_toggle(atom/source, new_value) + if(new_value) //Truthy value input, turn on. + turn_on() + return + turn_off() //Falsey value, turn off. + + +///Triggered right before the parent light flags change. +/datum/component/overlay_lighting/proc/on_light_flags_change(atom/source, new_value) + var/atom/movable/movable_parent = parent + if(new_value & LIGHT_ATTACHED) + if(!(movable_parent.light_flags & LIGHT_ATTACHED)) //Gained the LIGHT_ATTACHED property. + overlay_lighting_flags |= LIGHTING_ATTACHED + if(ismovable(movable_parent.loc)) + set_parent_attached_to(movable_parent.loc) + else if(movable_parent.light_flags & LIGHT_ATTACHED) //Lost the LIGHT_ATTACHED property. + overlay_lighting_flags &= ~LIGHTING_ATTACHED + set_parent_attached_to(null) + + +///Toggles the light on. +/datum/component/overlay_lighting/proc/turn_on() + if(overlay_lighting_flags & LIGHTING_ON) + return + if(current_holder) + add_dynamic_lumi(current_holder) + overlay_lighting_flags |= LIGHTING_ON + get_new_turfs() + + +///Toggles the light off. +/datum/component/overlay_lighting/proc/turn_off() + if(!(overlay_lighting_flags & LIGHTING_ON)) + return + if(current_holder) + remove_dynamic_lumi(current_holder) + overlay_lighting_flags &= ~LIGHTING_ON + clean_old_turfs() + + +///Here we append the behavior associated to changing lum_power. +/datum/component/overlay_lighting/proc/set_lum_power(new_lum_power) + if(lum_power == new_lum_power) + return + . = lum_power + lum_power = new_lum_power + var/difference = . - lum_power + for(var/t in affected_turfs) + var/turf/lit_turf = t + lit_turf.dynamic_lumcount -= difference + + +#undef LIGHTING_ON +#undef LIGHTING_ATTACHED +#undef GET_PARENT diff --git a/code/datums/mutations/body.dm b/code/datums/mutations/body.dm index df74902d042b..62126523bb8a 100644 --- a/code/datums/mutations/body.dm +++ b/code/datums/mutations/body.dm @@ -218,7 +218,7 @@ color += owner.dna.features["mcolor"][1] + owner.dna.features["mcolor"][1] + owner.dna.features["mcolor"][2] + owner.dna.features["mcolor"][2] + owner.dna.features["mcolor"][3] + owner.dna.features["mcolor"][3] else color += owner.dna.features["mcolor"] - glowth.set_light(range * power, glow * power, color) + glowth.set_light_range_power_color(range * power, glow * power, color) /datum/mutation/human/glow/on_losing(mob/living/carbon/human/owner) . = ..() diff --git a/code/game/atoms.dm b/code/game/atoms.dm index a43461b0ec25..ccbbb06e8dba 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -72,6 +72,13 @@ ///Bitfield for how the atom handles materials. var/material_flags = NONE + ///Light systems, both shouldn't be active at the same time. + var/light_system = STATIC_LIGHT + ///Boolean variable for toggleable lights. Has no effect without the proper light_system, light_range and light_power values. + var/light_on = TRUE + ///Bitflags to determine lighting-related atom properties. + var/light_flags = NONE + var/chat_color_name // Last name used to calculate a color for the chatmessage overlays var/chat_color // Last color calculated for the the chatmessage overlays @@ -152,7 +159,7 @@ if(color) add_atom_colour(color, FIXED_COLOUR_PRIORITY) - if (light_power && light_range) + if (light_system == STATIC_LIGHT && light_power && light_range) update_light() if (opacity && isturf(loc)) diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 255766e1e7b8..e2cb40222a83 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -53,6 +53,11 @@ /// The degree of pressure protection that mobs in list/contents have from the external environment, between 0 and 1 var/contents_pressure_protection = 0 + ///Lazylist to keep track on the sources of illumination. + var/list/affected_dynamic_lights + ///Highest-intensity light affecting us, which determines our visibility. + var/affecting_dynamic_lumi = 0 + /atom/movable/Initialize(mapload) . = ..() @@ -63,6 +68,9 @@ render_target = ref(src) em_block = new(src, render_target) vis_contents += em_block + + if(light_system == MOVABLE_LIGHT) + AddComponent(/datum/component/overlay_lighting) /atom/movable/Destroy() QDEL_NULL(em_block) diff --git a/code/game/machinery/computer/_computer.dm b/code/game/machinery/computer/_computer.dm index ff550b0f5a47..42c8a300fd3e 100644 --- a/code/game/machinery/computer/_computer.dm +++ b/code/game/machinery/computer/_computer.dm @@ -9,7 +9,10 @@ max_integrity = 200 integrity_failure = 100 armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, RAD = 0, FIRE = 40, ACID = 20) - var/brightness_on = 1 + light_system = STATIC_LIGHT + light_range = 2 + light_power = 1 + light_on = TRUE var/icon_keyboard = "generic_key" var/icon_screen = "generic" var/clockwork = FALSE @@ -98,9 +101,9 @@ if(!.) return // reduce unneeded light changes if(stat & NOPOWER) - set_light(0) + set_light(FALSE) else - set_light(brightness_on) + set_light(TRUE) /obj/machinery/computer/screwdriver_act(mob/living/user, obj/item/I) if(..()) diff --git a/code/game/machinery/dance_machine.dm b/code/game/machinery/dance_machine.dm index d60096e53a4f..13c3a293fb23 100644 --- a/code/game/machinery/dance_machine.dm +++ b/code/game/machinery/dance_machine.dm @@ -190,56 +190,56 @@ var/obj/item/flashlight/spotlight/L = new /obj/item/flashlight/spotlight(t) L.light_color = LIGHT_COLOR_RED L.light_power = 30-(get_dist(src,L)*8) - L.range = 1+get_dist(src, L) + L.light_range = 1+get_dist(src, L) spotlights+=L continue if(t.x == cen.x && t.y < cen.y) var/obj/item/flashlight/spotlight/L = new /obj/item/flashlight/spotlight(t) L.light_color = LIGHT_COLOR_PURPLE L.light_power = 30-(get_dist(src,L)*8) - L.range = 1+get_dist(src, L) + L.light_range = 1+get_dist(src, L) spotlights+=L continue if(t.x > cen.x && t.y == cen.y) var/obj/item/flashlight/spotlight/L = new /obj/item/flashlight/spotlight(t) L.light_color = LIGHT_COLOR_YELLOW L.light_power = 30-(get_dist(src,L)*8) - L.range = 1+get_dist(src, L) + L.light_range = 1+get_dist(src, L) spotlights+=L continue if(t.x < cen.x && t.y == cen.y) var/obj/item/flashlight/spotlight/L = new /obj/item/flashlight/spotlight(t) L.light_color = LIGHT_COLOR_GREEN L.light_power = 30-(get_dist(src,L)*8) - L.range = 1+get_dist(src, L) + L.light_range = 1+get_dist(src, L) spotlights+=L continue if((t.x+1 == cen.x && t.y+1 == cen.y) || (t.x+2==cen.x && t.y+2 == cen.y)) var/obj/item/flashlight/spotlight/L = new /obj/item/flashlight/spotlight(t) L.light_color = LIGHT_COLOR_ORANGE L.light_power = 30-(get_dist(src,L)*8) - L.range = 1.4+get_dist(src, L) + L.light_range = 1.4+get_dist(src, L) spotlights+=L continue if((t.x-1 == cen.x && t.y-1 == cen.y) || (t.x-2==cen.x && t.y-2 == cen.y)) var/obj/item/flashlight/spotlight/L = new /obj/item/flashlight/spotlight(t) L.light_color = LIGHT_COLOR_CYAN L.light_power = 30-(get_dist(src,L)*8) - L.range = 1.4+get_dist(src, L) + L.light_range = 1.4+get_dist(src, L) spotlights+=L continue if((t.x-1 == cen.x && t.y+1 == cen.y) || (t.x-2==cen.x && t.y+2 == cen.y)) var/obj/item/flashlight/spotlight/L = new /obj/item/flashlight/spotlight(t) L.light_color = LIGHT_COLOR_BLUEGREEN L.light_power = 30-(get_dist(src,L)*8) - L.range = 1.4+get_dist(src, L) + L.light_range = 1.4+get_dist(src, L) spotlights+=L continue if((t.x+1 == cen.x && t.y-1 == cen.y) || (t.x+2==cen.x && t.y-2 == cen.y)) var/obj/item/flashlight/spotlight/L = new /obj/item/flashlight/spotlight(t) L.light_color = LIGHT_COLOR_BLUE L.light_power = 30-(get_dist(src,L)*8) - L.range = 1.4+get_dist(src, L) + L.light_range = 1.4+get_dist(src, L) spotlights+=L continue continue @@ -286,7 +286,7 @@ continue if(glow.light_color == LIGHT_COLOR_BLUE) glow.light_color = LIGHT_COLOR_GREEN - glow.light_range = glow.range * DISCO_INFENO_RANGE + glow.light_range = glow.light_range * DISCO_INFENO_RANGE glow.light_power = glow.light_power * 2 // Any changes to power must come in pairs to neutralize it for other colors glow.update_light() continue @@ -299,7 +299,7 @@ if(glow.light_color == LIGHT_COLOR_ORANGE) glow.light_color = LIGHT_COLOR_PURPLE glow.light_power = glow.light_power * 2.27 - glow.light_range = glow.range * DISCO_INFENO_RANGE + glow.light_range = glow.light_range * DISCO_INFENO_RANGE glow.update_light() continue if(glow.light_color == LIGHT_COLOR_PURPLE) @@ -310,7 +310,7 @@ continue if(glow.light_color == LIGHT_COLOR_BLUEGREEN) glow.light_color = LIGHT_COLOR_YELLOW - glow.light_range = glow.range * DISCO_INFENO_RANGE + glow.light_range = glow.light_range * DISCO_INFENO_RANGE glow.update_light() continue if(glow.light_color == LIGHT_COLOR_YELLOW) @@ -321,7 +321,7 @@ if(glow.light_color == LIGHT_COLOR_CYAN) glow.light_color = LIGHT_COLOR_RED glow.light_power = glow.light_power * 0.68 - glow.light_range = glow.range * DISCO_INFENO_RANGE + glow.light_range = glow.light_range * DISCO_INFENO_RANGE glow.update_light() continue if(prob(2)) // Unique effects for the dance floor that show up randomly to mix things up diff --git a/code/game/machinery/flasher.dm b/code/game/machinery/flasher.dm index 68a6934bf0d8..e66a081cfb2e 100644 --- a/code/game/machinery/flasher.dm +++ b/code/game/machinery/flasher.dm @@ -24,6 +24,9 @@ anchored = FALSE base_state = "pflash" density = TRUE + light_system = MOVABLE_LIGHT //Used as a flash here. + light_range = FLASH_LIGHT_RANGE + light_on = FALSE /obj/machinery/flasher/Initialize(mapload, ndir = 0, built = 0) . = ..() // ..() is EXTREMELY IMPORTANT, never forget to add it @@ -108,7 +111,9 @@ playsound(src.loc, 'sound/weapons/flash.ogg', 100, 1) flick("[base_state]_flash", src) - flash_lighting_fx(FLASH_LIGHT_RANGE, light_power, light_color) + set_light_on(TRUE) + addtimer(CALLBACK(src, .proc/flash_end), FLASH_LIGHT_DURATION, TIMER_OVERRIDE|TIMER_UNIQUE) + last_flash = world.time use_power(1000) @@ -126,6 +131,8 @@ return 1 +/obj/machinery/flasher/proc/flash_end() + set_light_on(FALSE) /obj/machinery/flasher/emp_act(severity) . = ..() diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 7fd7d48e8712..2599967a5748 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -22,6 +22,8 @@ layer = BELOW_MOB_LAYER//icon draw layer infra_luminosity = 15 //byond implementation is bugged. force = 5 + light_system = MOVABLE_LIGHT + light_on = FALSE flags_1 = HEAR_1 var/ruin_mecha = FALSE //if the mecha starts on a ruin, don't automatically give it a tracking beacon to prevent metagaming. var/can_move = 0 //time of next allowed movement diff --git a/code/game/movable_luminosity.dm b/code/game/movable_luminosity.dm new file mode 100644 index 000000000000..7a6eaddafd9e --- /dev/null +++ b/code/game/movable_luminosity.dm @@ -0,0 +1,19 @@ +///Keeps track of the sources of dynamic luminosity and updates our visibility with the highest. +/atom/movable/proc/update_dynamic_luminosity() + var/highest = 0 + for(var/i in affected_dynamic_lights) + if(affected_dynamic_lights[i] <= highest) + continue + highest = affected_dynamic_lights[i] + if(highest == affecting_dynamic_lumi) + return + luminosity -= affecting_dynamic_lumi + affecting_dynamic_lumi = highest + luminosity += affecting_dynamic_lumi + + +///Helper to change several lighting overlay settings. +/atom/movable/proc/set_light_range_power_color(range, power, color) + set_light_range(range) + set_light_power(power) + set_light_color(color) diff --git a/code/game/objects/effects/effect_system/effects_sparks.dm b/code/game/objects/effects/effect_system/effects_sparks.dm index b3f84240e94b..224dfe7a492a 100644 --- a/code/game/objects/effects/effect_system/effects_sparks.dm +++ b/code/game/objects/effects/effect_system/effects_sparks.dm @@ -20,8 +20,9 @@ name = "sparks" icon_state = "sparks" anchored = TRUE - light_power = 1.3 - light_range = MINIMUM_USEFUL_LIGHT_RANGE + light_system = MOVABLE_LIGHT + light_range = 2 + light_power = 0.5 light_color = LIGHT_COLOR_FIRE /obj/effect/particle_effect/sparks/Initialize() diff --git a/code/game/objects/effects/misc.dm b/code/game/objects/effects/misc.dm index dd0a8ba9db61..171292b2e3f6 100644 --- a/code/game/objects/effects/misc.dm +++ b/code/game/objects/effects/misc.dm @@ -75,12 +75,18 @@ desc = "Tell a coder if you're seeing this." icon_state = "nothing" light_color = "#FFFFFF" + light_system = MOVABLE_LIGHT light_range = MINIMUM_USEFUL_LIGHT_RANGE mouse_opacity = MOUSE_OPACITY_TRANSPARENT -/obj/effect/dummy/lighting_obj/Initialize(mapload, _color, _range, _power, _duration) +/obj/effect/dummy/lighting_obj/Initialize(mapload, _range, _power, _color, _duration) . = ..() - set_light(_range ? _range : light_range, _power ? _power : light_power, _color ? _color : light_color) + if(!isnull(_range)) + set_light_range(_range) + if(!isnull(_power)) + set_light_power(_power) + if(!isnull(_color)) + set_light_color(_color) if(_duration) QDEL_IN(src, _duration) diff --git a/code/game/objects/effects/overlays.dm b/code/game/objects/effects/overlays.dm index 0267386a3936..19def920e8cd 100644 --- a/code/game/objects/effects/overlays.dm +++ b/code/game/objects/effects/overlays.dm @@ -77,3 +77,14 @@ layer = FLOAT_LAYER vis_flags = VIS_INHERIT_ID appearance_flags = KEEP_TOGETHER | LONG_GLIDE | PIXEL_SCALE + +/obj/effect/overlay/light_visible + name = "" + icon = 'icons/effects/light_overlays/light_32.dmi' + icon_state = "light" + layer = O_LIGHTING_VISUAL_LAYER + plane = O_LIGHTING_VISUAL_PLANE + appearance_flags = RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + alpha = 0 + vis_flags = NONE diff --git a/code/game/objects/items/cigs_lighters.dm b/code/game/objects/items/cigs_lighters.dm index 10af06bf1306..5d0de6463519 100644 --- a/code/game/objects/items/cigs_lighters.dm +++ b/code/game/objects/items/cigs_lighters.dm @@ -542,6 +542,10 @@ CIGARETTE PACKETS ARE IN FANCY.DM w_class = WEIGHT_CLASS_TINY flags_1 = CONDUCT_1 slot_flags = ITEM_SLOT_BELT + light_system = MOVABLE_LIGHT + light_range = 2 + light_power = 0.6 + light_on = FALSE var/lit = 0 var/fancy = TRUE var/overlay_state @@ -588,20 +592,21 @@ CIGARETTE PACKETS ARE IN FANCY.DM playsound(src, 'sound/items/lighter/light.ogg', 50, 2) /obj/item/lighter/proc/set_lit(new_lit) + if(lit == new_lit) + return lit = new_lit if(lit) force = 5 damtype = BURN hitsound = 'sound/items/welder.ogg' attack_verb = list("burnt", "singed") - set_light(1) START_PROCESSING(SSobj, src) else hitsound = "swing_hit" force = 0 attack_verb = null //human_defense.dm takes care of it - set_light(0) STOP_PROCESSING(SSobj, src) + set_light_on(lit) update_icon() /obj/item/lighter/extinguish() diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm index 395810d763b1..a908b1f693b9 100644 --- a/code/game/objects/items/devices/PDA/PDA.dm +++ b/code/game/objects/items/devices/PDA/PDA.dm @@ -40,7 +40,11 @@ GLOBAL_LIST_EMPTY(PDAs) slot_flags = ITEM_SLOT_ID | ITEM_SLOT_BELT armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, RAD = 0, FIRE = 100, ACID = 100) resistance_flags = FIRE_PROOF | ACID_PROOF - + light_system = MOVABLE_LIGHT + light_range = 2.3 + light_power = 0.6 + light_color = "#FFCC66" + light_on = FALSE //Main variables var/owner = null // String name of owner @@ -63,8 +67,6 @@ GLOBAL_LIST_EMPTY(PDAs) //Secondary variables var/scanmode = PDA_SCANNER_NONE - var/fon = FALSE //Is the flashlight function on? - var/f_lum = 2.3 //Luminosity for the flashlight function var/silent = FALSE //To beep or not to beep, that is the question var/toff = FALSE //If TRUE, messenger disabled var/list/tnote = list() //Current list of received signals, which are transmuted into messages on-the-spot. Can also be just plain strings, y'know, like, who really gives a shit, y'know @@ -119,8 +121,6 @@ GLOBAL_LIST_EMPTY(PDAs) /obj/item/pda/Initialize() . = ..() - if(fon) - set_light(f_lum) GLOB.PDAs += src if(default_cartridge) @@ -207,7 +207,7 @@ GLOBAL_LIST_EMPTY(PDAs) if(inserted_item) overlay.icon_state = "insert_overlay" add_overlay(new /mutable_appearance(overlay)) - if(fon) + if(light_on) overlay.icon_state = "light_overlay" add_overlay(new /mutable_appearance(overlay)) if(pai) @@ -345,7 +345,7 @@ GLOBAL_LIST_EMPTY(PDAs) if(id && id.registered_account && id.registered_account.account_job.paycheck_department) dat += "
  • [PDAIMG(notes)]Show Department Goals
  • " dat += "
  • [PDAIMG(atmos)]Atmospheric Scan
  • " - dat += "
  • [PDAIMG(flashlight)][fon ? "Disable" : "Enable"] Flashlight
  • " + dat += "
  • [PDAIMG(flashlight)][light_on ? "Disable" : "Enable"] Flashlight
  • " if (pai) if(pai.loc != src) pai = null @@ -945,13 +945,14 @@ GLOBAL_LIST_EMPTY(PDAs) /obj/item/pda/proc/toggle_light() if(issilicon(usr) || !usr.canUseTopic(src, BE_CLOSE)) return - if(fon) - fon = FALSE - set_light(0) - else if(f_lum) - fon = TRUE - set_light(f_lum) + if(light_on) + set_light_on(FALSE) + else if(light_range) + set_light_on(TRUE) update_icon() + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() /obj/item/pda/proc/remove_pen() diff --git a/code/game/objects/items/devices/PDA/PDA_types.dm b/code/game/objects/items/devices/PDA/PDA_types.dm index 26c1c8171736..10af702ac042 100644 --- a/code/game/objects/items/devices/PDA/PDA_types.dm +++ b/code/game/objects/items/devices/PDA/PDA_types.dm @@ -21,7 +21,6 @@ /obj/item/pda/ai icon = null ttone = "data" - fon = FALSE /obj/item/pda/ai/attack_self(mob/user) if ((honkamt > 0) && (prob(60)))//For clown virus. diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm index 65980511991c..8a3a96233388 100644 --- a/code/game/objects/items/devices/flashlight.dm +++ b/code/game/objects/items/devices/flashlight.dm @@ -12,9 +12,11 @@ slot_flags = ITEM_SLOT_BELT materials = list(/datum/material/iron=50, /datum/material/glass=20) actions_types = list(/datum/action/item_action/toggle_light) + light_system = MOVABLE_LIGHT + light_range = 4 + light_power = 1 + light_on = FALSE var/on = FALSE - var/brightness_on = 4 //range of light when on - var/flashlight_power = 1 //strength of the light when on /obj/item/flashlight/Initialize() . = ..() @@ -22,16 +24,14 @@ on = TRUE update_brightness() -/obj/item/flashlight/proc/update_brightness(mob/user = null) +/obj/item/flashlight/proc/update_brightness(mob/user) if(on) icon_state = "[initial(icon_state)]-on" - if(flashlight_power) - set_light(l_range = brightness_on, l_power = flashlight_power) - else - set_light(brightness_on) else icon_state = initial(icon_state) - set_light(0) + set_light_on(on) + if(light_system == STATIC_LIGHT) + update_light() /obj/item/flashlight/attack_self(mob/user) on = !on @@ -67,7 +67,7 @@ to_chat(user, span_warning("[M] doesn't have a head!")) return - if(flashlight_power < 1) + if(light_power < 1) to_chat(user, "[span_warning("\The [src] isn't bright enough to see anything!")] ") return @@ -171,7 +171,7 @@ icon_state = "penlight" item_state = "" flags_1 = CONDUCT_1 - brightness_on = 2 + light_range = 2 var/holo_cooldown = 0 /obj/item/flashlight/pen/afterattack(atom/target, mob/user, proximity_flag) @@ -219,7 +219,7 @@ lefthand_file = 'icons/mob/inhands/equipment/security_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/security_righthand.dmi' force = 9 // Not as good as a stun baton. - brightness_on = 5 // A little better than the standard flashlight. + light_range = 5 // A little better than the standard flashlight. hitsound = 'sound/weapons/genhit1.ogg' // the desk lamps are a bit special @@ -231,7 +231,7 @@ lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' righthand_file = 'icons/mob/inhands/items_righthand.dmi' force = 10 - brightness_on = 5 + light_range = 5 w_class = WEIGHT_CLASS_BULKY flags_1 = CONDUCT_1 materials = list() @@ -267,7 +267,7 @@ name = "flare" desc = "A red Nanotrasen issued flare. There are instructions on the side, it reads 'pull cord, make light'." w_class = WEIGHT_CLASS_SMALL - brightness_on = 7 // Pretty bright. + light_range = 7 // Pretty bright. icon_state = "flare" item_state = "flare" lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' @@ -353,7 +353,7 @@ /obj/item/flashlight/flare/emergency name = "safety flare" desc = "A flare issued to Nanotrasen employees for emergencies. There are instructions on the side, it reads 'pull cord, make light, obey Nanotrasen'." - brightness_on = 3 + light_range = 3 item_state = "flare" icon_state = "flaresafety" ignition_sound = 'sound/items/flare_strike_2.ogg' @@ -363,8 +363,8 @@ /obj/item/flashlight/flare/signal name = "signalling flare" desc = "A specialized formulation of the standard Nanotrasen-issued flare, containing increased magnesium content. There are instructions on the side, it reads 'pull cord, make intense light'." - brightness_on = 5 - flashlight_power = 2 + light_range = 5 + light_power = 2 item_state = "flaresignal" icon_state = "flaresignal" light_color = LIGHT_COLOR_HALOGEN @@ -377,7 +377,7 @@ name = "torch" desc = "A torch fashioned from some leaves and a log." w_class = WEIGHT_CLASS_BULKY - brightness_on = 4 + light_range = 4 icon_state = "torch" item_state = "torch" lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' @@ -394,19 +394,19 @@ lefthand_file = 'icons/mob/inhands/equipment/mining_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/mining_righthand.dmi' desc = "A mining lantern." - brightness_on = 6 // luminosity when on + light_range = 6 // luminosity when on /obj/item/flashlight/lantern/heirloom_moth name = "old lantern" desc = "An old lantern that has seen plenty of use." - brightness_on = 4 + light_range = 4 /obj/item/flashlight/lantern/syndicate name = "suspicious lantern" desc = "A suspicious looking lantern." icon_state = "syndilantern" item_state = "syndilantern" - brightness_on = 10 + light_range = 10 /obj/item/flashlight/lantern/jade name = "jade lantern" @@ -424,7 +424,7 @@ w_class = WEIGHT_CLASS_SMALL slot_flags = ITEM_SLOT_BELT materials = list() - brightness_on = 6 //luminosity when on + light_range = 6 //luminosity when on /obj/item/flashlight/emp var/emp_max_charges = 4 @@ -490,7 +490,7 @@ desc = "A military-grade glowstick." custom_price = 10 w_class = WEIGHT_CLASS_SMALL - brightness_on = 4 + light_range = 4 color = LIGHT_COLOR_GREEN icon_state = "glowstick" item_state = "glowstick" @@ -499,12 +499,12 @@ /obj/item/flashlight/glowstick/Initialize() fuel = rand(1600, 2000) - light_color = color + set_light_color(color) . = ..() /obj/item/flashlight/glowstick/Destroy() STOP_PROCESSING(SSobj, src) - . = ..() + return ..() /obj/item/flashlight/glowstick/process() fuel = max(fuel - 1, 0) @@ -523,13 +523,13 @@ if(!fuel) icon_state = "glowstick-empty" cut_overlays() - set_light(0) + set_light_on(FALSE) else if(on) var/mutable_appearance/glowstick_overlay = mutable_appearance(icon, "glowstick-glow") glowstick_overlay.color = color add_overlay(glowstick_overlay) item_state = "glowstick-on" - set_light(brightness_on) + set_light_on(TRUE) else icon_state = "glowstick" cut_overlays() @@ -596,30 +596,52 @@ name = "disco light" desc = "Groovy..." icon_state = null - light_color = null - brightness_on = 0 - light_range = 0 + light_range = 4 light_power = 10 alpha = 0 layer = 0 on = TRUE anchored = TRUE - var/range = null resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + ///Boolean that switches when a full color flip ends, so the light can appear in all colors. + var/even_cycle = FALSE + ///Base light_range that can be set on Initialize to use in smooth light range expansions and contractions. + var/base_light_range = 4 + +/obj/item/flashlight/spotlight/Initialize(mapload, _light_range, _light_power, _light_color) + . = ..() + if(!isnull(_light_range)) + base_light_range = _light_range + set_light_range(_light_range) + if(!isnull(_light_power)) + set_light_power(_light_power) + if(!isnull(_light_color)) + set_light_color(_light_color) /obj/item/flashlight/flashdark name = "flashdark" desc = "A strange device manufactured with mysterious elements that somehow emits darkness. Or maybe it just sucks in light? Nobody knows for sure." icon_state = "flashdark" item_state = "flashdark" - brightness_on = 2.5 - flashlight_power = -3 + light_system = STATIC_LIGHT //The overlay light component is not yet ready to produce darkness. + light_range = 0 + ///Variable to preserve old lighting behavior in flashlights, to handle darkness. + var/dark_light_range = 2.5 + ///Variable to preserve old lighting behavior in flashlights, to handle darkness. + var/dark_light_power = -3 + +/obj/item/flashlight/flashdark/update_brightness(mob/user) + . = ..() + if(on) + set_light(dark_light_range, dark_light_power) + else + set_light(0) /obj/item/flashlight/eyelight name = "eyelight" desc = "This shouldn't exist outside of someone's head, how are you seeing this?" - brightness_on = 15 - flashlight_power = 1 + light_range = 15 + light_power = 1 flags_1 = CONDUCT_1 item_flags = DROPDEL actions_types = list() diff --git a/code/game/objects/items/discoball.dm b/code/game/objects/items/discoball.dm index 926bf3da9409..6ec6bdd235eb 100644 --- a/code/game/objects/items/discoball.dm +++ b/code/game/objects/items/discoball.dm @@ -67,7 +67,7 @@ /obj/structure/discoball/proc/DiscoFever() remove_atom_colour(TEMPORARY_COLOUR_PRIORITY) current_color = random_color() - set_light(range, power, current_color) + set_light_color(current_color) add_atom_colour("#[current_color]", FIXED_COLOUR_PRIORITY) update_icon() TimerID = addtimer(CALLBACK(src, .proc/DiscoFever), 5, TIMER_STOPPABLE) //Call ourselves every 0.5 seconds to change colors @@ -103,56 +103,56 @@ var/obj/item/flashlight/spotlight/L = new /obj/item/flashlight/spotlight(t) L.light_color = LIGHT_COLOR_RED L.light_power = 30-(get_dist(src,L)*8) - L.range = 1+get_dist(src, L) + L.light_range = 1+get_dist(src, L) spotlights+=L continue if(t.x == cen.x && t.y < cen.y) var/obj/item/flashlight/spotlight/L = new /obj/item/flashlight/spotlight(t) L.light_color = LIGHT_COLOR_PURPLE L.light_power = 30-(get_dist(src,L)*8) - L.range = 1+get_dist(src, L) + L.light_range = 1+get_dist(src, L) spotlights+=L continue if(t.x > cen.x && t.y == cen.y) var/obj/item/flashlight/spotlight/L = new /obj/item/flashlight/spotlight(t) L.light_color = LIGHT_COLOR_YELLOW L.light_power = 30-(get_dist(src,L)*8) - L.range = 1+get_dist(src, L) + L.light_range = 1+get_dist(src, L) spotlights+=L continue if(t.x < cen.x && t.y == cen.y) var/obj/item/flashlight/spotlight/L = new /obj/item/flashlight/spotlight(t) L.light_color = LIGHT_COLOR_GREEN L.light_power = 30-(get_dist(src,L)*8) - L.range = 1+get_dist(src, L) + L.light_range = 1+get_dist(src, L) spotlights+=L continue if((t.x+1 == cen.x && t.y+1 == cen.y) || (t.x+2==cen.x && t.y+2 == cen.y)) var/obj/item/flashlight/spotlight/L = new /obj/item/flashlight/spotlight(t) L.light_color = LIGHT_COLOR_ORANGE L.light_power = 30-(get_dist(src,L)*8) - L.range = 1.4+get_dist(src, L) + L.light_range = 1.4+get_dist(src, L) spotlights+=L continue if((t.x-1 == cen.x && t.y-1 == cen.y) || (t.x-2==cen.x && t.y-2 == cen.y)) var/obj/item/flashlight/spotlight/L = new /obj/item/flashlight/spotlight(t) L.light_color = LIGHT_COLOR_CYAN L.light_power = 30-(get_dist(src,L)*8) - L.range = 1.4+get_dist(src, L) + L.light_range = 1.4+get_dist(src, L) spotlights+=L continue if((t.x-1 == cen.x && t.y+1 == cen.y) || (t.x-2==cen.x && t.y+2 == cen.y)) var/obj/item/flashlight/spotlight/L = new /obj/item/flashlight/spotlight(t) L.light_color = LIGHT_COLOR_BLUEGREEN L.light_power = 30-(get_dist(src,L)*8) - L.range = 1.4+get_dist(src, L) + L.light_range = 1.4+get_dist(src, L) spotlights+=L continue if((t.x+1 == cen.x && t.y-1 == cen.y) || (t.x+2==cen.x && t.y-2 == cen.y)) var/obj/item/flashlight/spotlight/L = new /obj/item/flashlight/spotlight(t) L.light_color = LIGHT_COLOR_BLUE L.light_power = 30-(get_dist(src,L)*8) - L.range = 1.4+get_dist(src, L) + L.light_range = 1.4+get_dist(src, L) spotlights+=L continue continue @@ -197,7 +197,7 @@ continue if(glow.light_color == LIGHT_COLOR_BLUE) glow.light_color = LIGHT_COLOR_GREEN - glow.light_range = glow.range * DISCO_INFENO_RANGE + glow.light_range = glow.light_range * DISCO_INFENO_RANGE glow.light_power = glow.light_power * 2 // Any changes to power must come in pairs to neutralize it for other colors glow.update_light() continue @@ -210,7 +210,7 @@ if(glow.light_color == LIGHT_COLOR_ORANGE) glow.light_color = LIGHT_COLOR_PURPLE glow.light_power = glow.light_power * 2.27 - glow.light_range = glow.range * DISCO_INFENO_RANGE + glow.light_range = glow.light_range * DISCO_INFENO_RANGE glow.update_light() continue if(glow.light_color == LIGHT_COLOR_PURPLE) @@ -221,7 +221,7 @@ continue if(glow.light_color == LIGHT_COLOR_BLUEGREEN) glow.light_color = LIGHT_COLOR_YELLOW - glow.light_range = glow.range * DISCO_INFENO_RANGE + glow.light_range = glow.light_range * DISCO_INFENO_RANGE glow.update_light() continue if(glow.light_color == LIGHT_COLOR_YELLOW) @@ -232,7 +232,7 @@ if(glow.light_color == LIGHT_COLOR_CYAN) glow.light_color = LIGHT_COLOR_RED glow.light_power = glow.light_power * 0.68 - glow.light_range = glow.range * DISCO_INFENO_RANGE + glow.light_range = glow.light_range * DISCO_INFENO_RANGE glow.update_light() continue if(prob(2)) // Unique effects for the dance floor that show up randomly to mix things up diff --git a/code/game/objects/items/flamethrower.dm b/code/game/objects/items/flamethrower.dm index ab0370f1e873..4babeeafcf02 100644 --- a/code/game/objects/items/flamethrower.dm +++ b/code/game/objects/items/flamethrower.dm @@ -16,6 +16,8 @@ w_class = WEIGHT_CLASS_NORMAL materials = list(/datum/material/iron=500) resistance_flags = FIRE_PROOF + light_system = MOVABLE_LIGHT + light_on = FALSE var/status = FALSE var/lit = FALSE //on or off light_color = LIGHT_COLOR_FIRE @@ -173,6 +175,7 @@ set_light(0) playsound(loc, deac_sound, 50, TRUE) STOP_PROCESSING(SSobj,src) + set_light_on(lit) update_icon() /obj/item/flamethrower/CheckParts(list/parts_list) diff --git a/code/game/objects/items/grenades/flashbang.dm b/code/game/objects/items/grenades/flashbang.dm index ad37fd547790..6a6e82b7a65a 100644 --- a/code/game/objects/items/grenades/flashbang.dm +++ b/code/game/objects/items/grenades/flashbang.dm @@ -13,7 +13,7 @@ return do_sparks(rand(5, 9), FALSE, src) playsound(flashbang_turf, 'sound/weapons/flashbang.ogg', 100, TRUE, 8, 0.9) - new /obj/effect/dummy/lighting_obj (flashbang_turf, LIGHT_COLOR_WHITE, (flashbang_range + 2), 4, 2) + new /obj/effect/dummy/lighting_obj (flashbang_turf, flashbang_range + 2, 4, COLOR_WHITE, 2) for(var/mob/living/M in get_hearers_in_view(flashbang_range, flashbang_turf)) bang(get_turf(M), M) qdel(src) diff --git a/code/game/objects/items/melee/energy.dm b/code/game/objects/items/melee/energy.dm index 772bc212ee19..60e92e1eb62e 100644 --- a/code/game/objects/items/melee/energy.dm +++ b/code/game/objects/items/melee/energy.dm @@ -5,13 +5,15 @@ max_integrity = 200 armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, RAD = 0, FIRE = 100, ACID = 30) resistance_flags = FIRE_PROOF - var/brightness_on = 3 + light_system = MOVABLE_LIGHT + light_range = 3 + light_power = 1 + light_on = FALSE var/saber_color = null /obj/item/melee/transforming/energy/Initialize() . = ..() if(active) - set_light(brightness_on) START_PROCESSING(SSobj, src) /obj/item/melee/transforming/energy/Destroy() @@ -40,10 +42,9 @@ if(saber_color) icon_state = "sword[saber_color]" START_PROCESSING(SSobj, src) - set_light(brightness_on) else STOP_PROCESSING(SSobj, src) - set_light(0) + set_light_on(active) /obj/item/melee/transforming/energy/is_hot() return active * heat @@ -167,7 +168,6 @@ if(hacked) var/set_color = pick(possible_colors) light_color = possible_colors[set_color] - update_light() /obj/item/melee/transforming/energy/sword/saber/red possible_colors = list("red" = LIGHT_COLOR_RED) diff --git a/code/game/objects/items/tools/weldingtool.dm b/code/game/objects/items/tools/weldingtool.dm index 54a171f5ff43..5e1102325c7b 100644 --- a/code/game/objects/items/tools/weldingtool.dm +++ b/code/game/objects/items/tools/weldingtool.dm @@ -17,6 +17,10 @@ pickup_sound = 'sound/items/handling/weldingtool_pickup.ogg' var/acti_sound = 'sound/items/welderactivate.ogg' var/deac_sound = 'sound/items/welderdeactivate.ogg' + light_system = MOVABLE_LIGHT + light_range = 2 + light_power = 0.75 + light_on = FALSE throw_speed = 3 throw_range = 5 w_class = WEIGHT_CLASS_SMALL @@ -24,12 +28,12 @@ resistance_flags = FIRE_PROOF materials = list(/datum/material/iron=70, /datum/material/glass=30) - var/welding = 0 //Whether or not the welding tool is off(0), on(1) or currently welding(2) + ///Whether the welding tool is on or off. + var/welding = FALSE var/status = TRUE //Whether the welder is secured or unsecured (able to attach rods to it to make a flamethrower) var/max_fuel = 20 //The max amount of fuel the welder can hold var/change_icons = 1 var/can_off_process = 0 - var/light_intensity = 2 //how powerful the emitted light is when used. var/progress_flash_divisor = 10 var/burned_fuel_for = 0 //when fuel was last removed heat = 3800 @@ -150,8 +154,6 @@ message_admins("[ADMIN_LOOKUPFLW(user)] activated a rigged welder at [AREACOORD(user)].") explode() switched_on(user) - if(welding) - set_light(light_intensity) update_icon() @@ -179,10 +181,18 @@ else return FALSE +//Toggles the welding value. +/obj/item/weldingtool/proc/set_welding(new_value) + if(welding == new_value) + return + . = welding + welding = new_value + set_light_on(welding) //Turns off the welder if there is no more fuel (does this really need to be its own proc?) /obj/item/weldingtool/proc/check_fuel(mob/user) if(get_fuel() <= 0 && welding) + set_light_on(FALSE) switched_on(user) update_icon() //mob icon update @@ -198,7 +208,7 @@ if(!status) to_chat(user, span_warning("[src] can't be turned on while unsecured!")) return - welding = !welding + set_welding(!welding) if(welding) if(get_fuel() >= 1) to_chat(user, span_notice("You switch [src] on.")) @@ -218,8 +228,7 @@ //Switches the welder off /obj/item/weldingtool/proc/switched_off(mob/user) - welding = 0 - set_light(0) + set_welding(FALSE) force = 3 damtype = "brute" @@ -246,7 +255,7 @@ var/mob/living/carbon/human/H = user if(istype(H.head,/obj/item/clothing/head/helmet/space/plasmaman)) return - user.flash_act(light_intensity) + user.flash_act(light_range) // Flash the user during welding progress /obj/item/weldingtool/tool_check_callback(mob/living/user, amount, datum/callback/extra_checks) @@ -257,7 +266,7 @@ var/mob/living/carbon/human/H = user if(istype(H.head,/obj/item/clothing/head/helmet/space/plasmaman)) return - user.flash_act(min(light_intensity,1)) + user.flash_act(min(light_range,1)) progress_flash_divisor = initial(progress_flash_divisor) else progress_flash_divisor-- @@ -347,7 +356,8 @@ icon = 'icons/obj/abductor.dmi' icon_state = "welder_alien" toolspeed = 0.1 - light_intensity = 0 + light_system = NO_LIGHT_SUPPORT + light_range = 0 change_icons = 0 /obj/item/weldingtool/abductor/process() @@ -373,7 +383,7 @@ var/last_gen = 0 change_icons = 0 can_off_process = 1 - light_intensity = 1 + light_range = 1 toolspeed = 0.5 var/nextrefueltick = 0 diff --git a/code/game/objects/items/twohanded.dm b/code/game/objects/items/twohanded.dm index f54c87972aeb..deb68f952ee3 100644 --- a/code/game/objects/items/twohanded.dm +++ b/code/game/objects/items/twohanded.dm @@ -316,10 +316,13 @@ max_integrity = 200 armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, RAD = 0, FIRE = 100, ACID = 70) resistance_flags = FIRE_PROOF + light_system = MOVABLE_LIGHT + light_range = 6 + light_power = 1 + light_on = TRUE wound_bonus = -10 bare_wound_bonus = 20 var/hacked = FALSE - var/brightness_on = 6 //TWICE AS BRIGHT AS A REGULAR ESWORD var/list/possible_colors = list("red", "blue", "green", "purple") /obj/item/twohanded/dualsaber/suicide_act(mob/living/carbon/user) @@ -420,7 +423,7 @@ w_class = w_class_on hitsound = 'sound/weapons/blade1.ogg' START_PROCESSING(SSobj, src) - set_light(brightness_on) + set_light(TRUE) /obj/item/twohanded/dualsaber/unwield() //Specific unwield () to switch hitsounds. sharpness = initial(sharpness) @@ -428,7 +431,7 @@ ..() hitsound = "swing_hit" STOP_PROCESSING(SSobj, src) - set_light(0) + set_light(FALSE) /obj/item/twohanded/dualsaber/process() if(wielded) diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm index 537bf53e53ba..528f4381e122 100644 --- a/code/game/turfs/space/space.dm +++ b/code/game/turfs/space/space.dm @@ -46,7 +46,7 @@ if(requires_activation) SSair.add_to_active(src) - if (light_power && light_range) + if (light_system == STATIC_LIGHT && light_power && light_range) update_light() if (opacity) diff --git a/code/modules/antagonists/bloodsuckers/powers/targeted/brawn.dm b/code/modules/antagonists/bloodsuckers/powers/targeted/brawn.dm index 97ea1ae6040d..34c48aedc3ce 100644 --- a/code/modules/antagonists/bloodsuckers/powers/targeted/brawn.dm +++ b/code/modules/antagonists/bloodsuckers/powers/targeted/brawn.dm @@ -219,17 +219,13 @@ var/obj/item/I = LO if(istype(I, /obj/item/clothing/head/helmet/space/hardsuit)) var/obj/item/clothing/head/helmet/space/hardsuit/HA = I - if(HA.on) - HA.on = FALSE + HA.set_light_on(FALSE) if(istype(I, /obj/item/clothing/head/helmet/space/plasmaman)) var/obj/item/clothing/head/helmet/space/plasmaman/PA = I - if(PA.on) - PA.on = FALSE + PA.set_light_on(FALSE) if(istype(I, /obj/item/flashlight)) var/obj/item/flashlight/F = I - if(F.on) - F.on = FALSE - F.update_brightness() + F.set_light_on(FALSE) if(istype(LO, /mob/living/silicon/robot)) var/mob/living/silicon/robot/borg = LO if(!borg.lamp_cooldown) diff --git a/code/modules/antagonists/cult/blood_magic.dm b/code/modules/antagonists/cult/blood_magic.dm index 4467f2edd586..ffc219b907db 100644 --- a/code/modules/antagonists/cult/blood_magic.dm +++ b/code/modules/antagonists/cult/blood_magic.dm @@ -424,12 +424,12 @@ user.visible_message(span_warning("[user] holds up [user.p_their()] hand, which explodes in a flash of red light!"), \ span_cultitalic("You attempt to stun [L] with the spell!")) - user.mob_light(_color = LIGHT_COLOR_BLOOD_MAGIC, _range = 3, _duration = 2) + user.mob_light(_range = 3, _color = LIGHT_COLOR_BLOOD_MAGIC, _duration = 0.2 SECONDS) var/anti_magic_source = L.anti_magic_check() if(anti_magic_source) - L.mob_light(_color = LIGHT_COLOR_HOLY_MAGIC, _range = 2, _duration = 100) + L.mob_light(_range = 2, _color = LIGHT_COLOR_HOLY_MAGIC, _duration = 10 SECONDS) var/mutable_appearance/forbearance = mutable_appearance('icons/effects/genetics.dmi', "servitude", -MUTATIONS_LAYER) L.add_overlay(forbearance) addtimer(CALLBACK(L, /atom/proc/cut_overlay, forbearance), 100) diff --git a/code/modules/antagonists/cult/cult_items.dm b/code/modules/antagonists/cult/cult_items.dm index 0c35ee1df4aa..bbdda7a0ca8c 100644 --- a/code/modules/antagonists/cult/cult_items.dm +++ b/code/modules/antagonists/cult/cult_items.dm @@ -101,6 +101,8 @@ throw_range = 3 sharpness = SHARP_EDGED light_color = "#ff0000" + light_system = MOVABLE_LIGHT + light_range = 4 attack_verb = list("cleaved", "slashed", "torn", "hacked", "ripped", "diced", "carved") icon = 'icons/obj/weapons/swords.dmi' icon_state = "cultbastard" @@ -120,7 +122,6 @@ /obj/item/twohanded/required/cult_bastard/Initialize() . = ..() - set_light(4) jaunt = new(src) linked_action = new(src) AddComponent(/datum/component/butchering, 50, 80) @@ -361,7 +362,7 @@ icon_state = "cult_helmet" item_state = "cult_helmet" armor = list(MELEE = 60, BULLET = 50, LASER = 30,ENERGY = 15, BOMB = 30, BIO = 30, RAD = 30, FIRE = 40, ACID = 75) - brightness_on = 0 + light_system = NO_LIGHT_SUPPORT actions_types = list() /obj/item/clothing/suit/space/hardsuit/cult @@ -608,7 +609,7 @@ GLOBAL_VAR_INIT(curselimit, 0) name = "void torch" desc = "Used by veteran cultists to instantly transport items to their needful brethren." w_class = WEIGHT_CLASS_SMALL - brightness_on = 1 + light_range = 1 icon_state = "torch" item_state = "torch" color = "#ff0000" diff --git a/code/modules/assembly/flash.dm b/code/modules/assembly/flash.dm index d25a7836536b..0d4c13dc6180 100644 --- a/code/modules/assembly/flash.dm +++ b/code/modules/assembly/flash.dm @@ -10,7 +10,10 @@ w_class = WEIGHT_CLASS_TINY materials = list(/datum/material/iron = 300, /datum/material/glass = 300) light_color = LIGHT_COLOR_WHITE + light_system = MOVABLE_LIGHT //Used as a flash here. + light_range = FLASH_LIGHT_RANGE light_power = FLASH_LIGHT_POWER + light_on = FALSE ///flicked when we flash var/flashing_overlay = "flash-f" ///Number of times the flash has been used. @@ -109,7 +112,8 @@ return FALSE last_trigger = world.time playsound(src, 'sound/weapons/flash.ogg', 100, TRUE) - flash_lighting_fx(FLASH_LIGHT_RANGE, light_power, light_color) + set_light_on(TRUE) + addtimer(CALLBACK(src, .proc/flash_end), FLASH_LIGHT_DURATION, TIMER_OVERRIDE|TIMER_UNIQUE) times_used++ flash_recharge() update_icon(TRUE) @@ -117,6 +121,9 @@ return FALSE return TRUE +/obj/item/assembly/flash/proc/flash_end() + set_light_on(FALSE) + /obj/item/assembly/flash/proc/flash_carbon(mob/living/carbon/M, mob/user, power = 15, targeted = TRUE, generic_message = FALSE) if(!istype(M)) return diff --git a/code/modules/atmospherics/environmental/LINDA_fire.dm b/code/modules/atmospherics/environmental/LINDA_fire.dm index 7444c2595f22..a5c10e3735d8 100644 --- a/code/modules/atmospherics/environmental/LINDA_fire.dm +++ b/code/modules/atmospherics/environmental/LINDA_fire.dm @@ -47,9 +47,11 @@ icon = 'icons/effects/fire.dmi' icon_state = "1" layer = GASFIRE_LAYER + blend_mode = BLEND_ADD + light_system = MOVABLE_LIGHT light_range = LIGHT_RANGE_FIRE + light_power = 1 light_color = LIGHT_COLOR_FIRE - blend_mode = BLEND_ADD var/volume = 125 var/temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST @@ -144,7 +146,7 @@ add_overlay(fusion_overlay) add_overlay(rainbow_overlay) - set_light(l_color = rgb(LERP(250,heat_r,greyscale_fire),LERP(160,heat_g,greyscale_fire),LERP(25,heat_b,greyscale_fire))) + set_light_color(rgb(LERP(250, heat_r, greyscale_fire), LERP(160, heat_g, greyscale_fire), LERP(25, heat_b, greyscale_fire))) heat_r /= 255 heat_g /= 255 @@ -206,7 +208,6 @@ return TRUE /obj/effect/hotspot/Destroy() - set_light(0) SSair.hotspots -= src var/turf/open/T = loc if(istype(T) && T.active_hotspot == src) diff --git a/code/modules/clothing/head/hardhat.dm b/code/modules/clothing/head/hardhat.dm index 8bd77e70cc76..f33955cfece0 100644 --- a/code/modules/clothing/head/hardhat.dm +++ b/code/modules/clothing/head/hardhat.dm @@ -4,8 +4,6 @@ icon_state = "hardhat0_yellow" item_state = "hardhat0_yellow" mob_overlay_icon = 'icons/mob/clothing/head/head.dmi' - var/brightness_on = 4 //luminosity when on - var/on = FALSE //Determines used sprites: hardhat[on]_[hat_type] var/hat_type = "yellow" armor = list(MELEE = 15, BULLET = 5, LASER = 20, ENERGY = 10, BOMB = 20, BIO = 10, RAD = 20, FIRE = 100, ACID = 50, WOUND = 10) @@ -14,9 +12,16 @@ resistance_flags = FIRE_PROOF dynamic_hair_suffix = "+generic" hattable = FALSE + light_system = MOVABLE_LIGHT + light_range = 4 + light_power = 0.8 + light_on = FALSE dog_fashion = /datum/dog_fashion/head + ///Whether the headlamp is on or off. + var/on = FALSE + /obj/item/clothing/head/hardhat/attack_self(mob/living/user) toggle_helmet_light(user) @@ -40,10 +45,10 @@ ..() /obj/item/clothing/head/hardhat/proc/turn_on(mob/user) - set_light(brightness_on) + set_light_on(TRUE) /obj/item/clothing/head/hardhat/proc/turn_off(mob/user) - set_light(0) + set_light_on(FALSE) /obj/item/clothing/head/hardhat/orange icon_state = "hardhat0_orange" @@ -68,7 +73,7 @@ desc = "By applying state of the art lighting technology to a fire helmet, and using photo-chemical hardening methods, this hardhat will protect you from robust workplace hazards." icon_state = "hardhat0_purple" item_state = "hardhat0_purple" - brightness_on = 5 + light_range = 5 heat_protection = HEAD max_heat_protection_temperature = FIRE_HELM_MAX_TEMP_PROTECT cold_protection = HEAD @@ -110,7 +115,7 @@ /obj/item/clothing/head/hardhat/weldhat name = "welding hard hat" desc = "A piece of headgear used in dangerous working conditions to protect the head. Comes with a built-in flashlight AND welding shield! The bulb seems a little smaller though." - brightness_on = 3 //Needs a little bit of tradeoff + light_range = 3 //Needs a little bit of tradeoff dog_fashion = null actions_types = list(/datum/action/item_action/toggle_helmet_light, /datum/action/item_action/toggle_welding_screen) flash_protect = 2 @@ -159,7 +164,7 @@ desc = "A piece of headgear used in dangerous working conditions to protect the head. Comes with a built-in flashlight AND welding shield!" //This bulb is not smaller icon_state = "hardhat0_white" item_state = "hardhat0_white" - brightness_on = 4 //Boss always takes the best stuff + light_range = 4 //Boss always takes the best stuff hat_type = "white" clothing_flags = STOPSPRESSUREDAMAGE heat_protection = HEAD diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm index f6c6d9a93718..91b69b9b12c6 100644 --- a/code/modules/clothing/head/helmet.dm +++ b/code/modules/clothing/head/helmet.dm @@ -30,6 +30,12 @@ . = ..() AddComponent(/datum/component/wearertargeting/earprotection, list(SLOT_HEAD)) +/obj/item/clothing/head/helmet/Destroy() + var/obj/item/flashlight/seclite/old_light = set_attached_light(null) + if(old_light) + qdel(old_light) + return ..() + /obj/item/clothing/head/helmet/examine(mob/user) .=..() if(attached_light) @@ -39,18 +45,31 @@ else if(can_flashlight) . += "It has a mounting point for a seclite." -/obj/item/clothing/head/helmet/Destroy() - QDEL_NULL(attached_light) - return ..() - /obj/item/clothing/head/helmet/handle_atom_del(atom/A) if(A == attached_light) - attached_light = null + set_attached_light(null) update_helmlight() update_icon() QDEL_NULL(alight) + qdel(A) return ..() +///Called when attached_light value changes. +/obj/item/clothing/head/helmet/proc/set_attached_light(obj/item/flashlight/seclite/new_attached_light) + if(attached_light == new_attached_light) + return + . = attached_light + attached_light = new_attached_light + if(attached_light) + attached_light.set_light_flags(attached_light.light_flags | LIGHT_ATTACHED) + if(attached_light.loc != src) + attached_light.forceMove(src) + else if(.) + var/obj/item/flashlight/seclite/old_attached_light = . + old_attached_light.set_light_flags(old_attached_light.light_flags & ~LIGHT_ATTACHED) + if(old_attached_light.loc == src) + old_attached_light.forceMove(get_turf(src)) + /obj/item/clothing/head/helmet/sec can_flashlight = TRUE @@ -377,9 +396,7 @@ if(!user.transferItemToLoc(S, src)) return to_chat(user, span_notice("You click [S] into place on [src].")) - if(S.on) - set_light(0) - attached_light = S + set_attached_light(S) update_icon() update_helmlight() alight = new(src) @@ -397,8 +414,7 @@ if(Adjacent(user) && !issilicon(user)) user.put_in_hands(attached_light) - var/obj/item/flashlight/removed_light = attached_light - attached_light = null + var/obj/item/flashlight/removed_light = set_attached_light(null) update_helmlight() removed_light.update_brightness(user) update_icon() @@ -418,6 +434,7 @@ if(user.incapacitated()) return attached_light.on = !attached_light.on + attached_light.update_brightness() to_chat(user, span_notice("You toggle the helmet-light [attached_light.on ? "on":"off"].")) playsound(user, 'sound/weapons/empty.ogg', 100, TRUE) @@ -425,14 +442,7 @@ /obj/item/clothing/head/helmet/proc/update_helmlight() if(attached_light) - if(attached_light.on) - set_light(attached_light.brightness_on) - else - set_light(0) update_icon() - - else - set_light(0) for(var/X in actions) var/datum/action/A = X A.UpdateButtonIcon() diff --git a/code/modules/clothing/head/misc_special.dm b/code/modules/clothing/head/misc_special.dm index df6a1b3d0705..54441ada851e 100644 --- a/code/modules/clothing/head/misc_special.dm +++ b/code/modules/clothing/head/misc_special.dm @@ -46,7 +46,7 @@ hitsound = 'sound/weapons/tap.ogg' flags_inv = HIDEEARS|HIDEHAIR armor = list(MELEE = 0, BULLET = 0, LASER = 0,ENERGY = 0, BOMB = 0, BIO = 0, RAD = 0, FIRE = 0, ACID = 0) - brightness_on = 2 //luminosity when on + light_range = 2 //luminosity when on flags_cover = HEADCOVERSEYES heat = 999 @@ -116,7 +116,7 @@ hat_type = "pumpkin" flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR armor = list(MELEE = 0, BULLET = 0, LASER = 0,ENERGY = 0, BOMB = 0, BIO = 0, RAD = 0, FIRE = 0, ACID = 0) - brightness_on = 2 //luminosity when on + light_range = 2 //luminosity when on flags_cover = HEADCOVERSEYES hattable = FALSE @@ -166,7 +166,7 @@ hat_type = "reindeer" flags_inv = 0 armor = list(MELEE = 0, BULLET = 0, LASER = 0,ENERGY = 0, BOMB = 0, BIO = 0, RAD = 0, FIRE = 0, ACID = 0) - brightness_on = 1 //luminosity when on + light_range = 1 //luminosity when on dynamic_hair_suffix = "" dog_fashion = /datum/dog_fashion/head/reindeer diff --git a/code/modules/clothing/shoes/miscellaneous.dm b/code/modules/clothing/shoes/miscellaneous.dm index ddcb5907052e..62900468a578 100644 --- a/code/modules/clothing/shoes/miscellaneous.dm +++ b/code/modules/clothing/shoes/miscellaneous.dm @@ -338,23 +338,28 @@ actions_types = list(/datum/action/item_action/kindleKicks) var/lightCycle = 0 var/active = FALSE + light_system = MOVABLE_LIGHT + light_range = 2 + light_power = 3 + light_on = FALSE /obj/item/clothing/shoes/kindleKicks/ui_action_click(mob/user, action) if(active) return active = TRUE - set_light(2, 3, rgb(rand(0,255),rand(0,255),rand(0,255))) - addtimer(CALLBACK(src, .proc/lightUp), 5) + set_light_color(rgb(rand(0, 255), rand(0, 255), rand(0, 255))) + set_light_on(active) + addtimer(CALLBACK(src, .proc/lightUp), 0.5 SECONDS) /obj/item/clothing/shoes/kindleKicks/proc/lightUp(mob/user) if(lightCycle < 15) - set_light(2, 3, rgb(rand(0,255),rand(0,255),rand(0,255))) - lightCycle += 1 - addtimer(CALLBACK(src, .proc/lightUp), 5) + set_light_color(rgb(rand(0, 255), rand(0, 255), rand(0, 255))) + lightCycle++ + addtimer(CALLBACK(src, .proc/lightUp), 0.5 SECONDS) else - set_light(0) lightCycle = 0 active = FALSE + set_light_on(active) /obj/item/clothing/shoes/russian name = "russian boots" diff --git a/code/modules/clothing/spacesuits/hardsuit.dm b/code/modules/clothing/spacesuits/hardsuit.dm index e3320e840cc9..9c1b00e5f0dc 100644 --- a/code/modules/clothing/spacesuits/hardsuit.dm +++ b/code/modules/clothing/spacesuits/hardsuit.dm @@ -6,8 +6,10 @@ item_state = "eng_helm" max_integrity = 300 armor = list(MELEE = 10, BULLET = 5, LASER = 10, ENERGY = 5, BOMB = 10, BIO = 100, RAD = 75, FIRE = 50, ACID = 75) + light_range = 4 + light_power = 1 + light_on = FALSE var/basestate = "hardsuit" - var/brightness_on = 4 //luminosity when on var/on = FALSE var/obj/item/clothing/suit/space/hardsuit/suit //Determines used sprites: hardsuit[on]-[hardsuit_type] @@ -33,10 +35,8 @@ icon_state = "[basestate][on]-[hardsuit_type]" user.update_inv_head() //so our mob-overlays update - if(on) - set_light(brightness_on) - else - set_light(0) + set_light_on(on) + for(var/X in actions) var/datum/action/A = X A.UpdateButtonIcon() @@ -262,7 +262,7 @@ resistance_flags = FIRE_PROOF heat_protection = HEAD armor = list(MELEE = 30, BULLET = 5, LASER = 10, ENERGY = 5, BOMB = 50, BIO = 100, RAD = 50, FIRE = 50, ACID = 75, WOUND = 15) - brightness_on = 7 + light_range = 7 allowed = list(/obj/item/flashlight, /obj/item/tank/internals, /obj/item/resonator, /obj/item/mining_scanner, /obj/item/t_scanner/adv_mining_scanner, /obj/item/gun/energy/kinetic_accelerator) /obj/item/clothing/head/helmet/space/hardsuit/mining/Initialize() @@ -317,7 +317,7 @@ to_chat(user, span_notice("You switch your hardsuit to EVA mode, sacrificing speed for space protection.")) name = initial(name) desc = initial(desc) - set_light(brightness_on) + set_light_on(TRUE) clothing_flags |= visor_flags flags_cover |= HEADCOVERSEYES | HEADCOVERSMOUTH flags_inv |= visor_flags_inv @@ -326,7 +326,7 @@ to_chat(user, span_notice("You switch your hardsuit to combat mode and can now run at full speed.")) name += " (combat)" desc = alt_desc - set_light(0) + set_light_on(FALSE) clothing_flags &= ~visor_flags flags_cover &= ~(HEADCOVERSEYES | HEADCOVERSMOUTH) flags_inv &= ~visor_flags_inv @@ -428,7 +428,7 @@ icon_state = "carp_helm" item_state = "syndicate" armor = list(MELEE = -20, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 100, RAD = 75, FIRE = 60, ACID = 75) //As whimpy as a space carp - brightness_on = 0 //luminosity when on + light_range = 0 //luminosity when on actions_types = list() /obj/item/clothing/head/helmet/space/hardsuit/carp/Initialize() @@ -701,7 +701,7 @@ hardsuit_type = "ert_commander" armor = list(MELEE = 65, BULLET = 50, LASER = 50, ENERGY = 50, BOMB = 50, BIO = 100, RAD = 100, FIRE = 80, ACID = 80) strip_delay = 130 - brightness_on = 7 + light_range = 7 resistance_flags = FIRE_PROOF max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT diff --git a/code/modules/clothing/spacesuits/plasmamen.dm b/code/modules/clothing/spacesuits/plasmamen.dm index 28281e3b4f2f..9f9d586e900f 100644 --- a/code/modules/clothing/spacesuits/plasmamen.dm +++ b/code/modules/clothing/spacesuits/plasmamen.dm @@ -44,8 +44,10 @@ strip_delay = 80 armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 100, RAD = 0, FIRE = 100, ACID = 75) resistance_flags = FIRE_PROOF | ACID_PROOF - var/brightness_on = 4 //luminosity when the light is on - var/on = FALSE + light_system = MOVABLE_LIGHT + light_range = 4 + light_on = FALSE + var/helmet_on = FALSE actions_types = list(/datum/action/item_action/toggle_helmet_light) flash_protect = 0 var/base_icon_state @@ -59,15 +61,13 @@ toggle_helmet_light(user) /obj/item/clothing/head/helmet/space/plasmaman/proc/toggle_helmet_light(mob/user) - on = !on - if(on) - set_light(brightness_on) - else - set_light(0) - - icon_state = "[base_icon_state][on ? "-light":""]" + helmet_on = !helmet_on + icon_state = "[initial(icon_state)][helmet_on ? "-light":""]" item_state = icon_state user.update_inv_head() + + set_light_on(helmet_on) + for(var/X in actions) var/datum/action/A=X A.UpdateButtonIcon() diff --git a/code/modules/hydroponics/grown/ambrosia.dm b/code/modules/hydroponics/grown/ambrosia.dm index 0f717ded716b..b7655d8fa6cf 100644 --- a/code/modules/hydroponics/grown/ambrosia.dm +++ b/code/modules/hydroponics/grown/ambrosia.dm @@ -73,6 +73,7 @@ desc = "Eating this makes you immortal." icon_state = "ambrosia_gaia" filling_color = rgb(255, 175, 0) + light_system = MOVABLE_LIGHT light_range = 3 seed = /obj/item/seeds/ambrosia/gaia wine_power = 70 diff --git a/code/modules/hydroponics/plant_genes.dm b/code/modules/hydroponics/plant_genes.dm index 38b42ea567bb..939e5bcd2e5f 100644 --- a/code/modules/hydroponics/plant_genes.dm +++ b/code/modules/hydroponics/plant_genes.dm @@ -260,8 +260,9 @@ return max(S.potency*(rate + 0.01), 0.1) /datum/plant_gene/trait/glow/on_new(obj/item/reagent_containers/food/snacks/grown/G, newloc) - ..() - G.set_light(glow_range(G.seed), glow_power(G.seed), glow_color) + . = ..() + G.light_system = MOVABLE_LIGHT + G.AddComponent(/datum/component/overlay_lighting, glow_range(G.seed), glow_power(G.seed), glow_color) /datum/plant_gene/trait/glow/shadow //makes plant emit slightly purple shadows diff --git a/code/modules/lighting/lighting_atom.dm b/code/modules/lighting/lighting_atom.dm index 3c27d4aa75a7..e33f137e4c96 100644 --- a/code/modules/lighting/lighting_atom.dm +++ b/code/modules/lighting/lighting_atom.dm @@ -35,6 +35,9 @@ if (QDELETED(src)) return + if(light_system != STATIC_LIGHT) + CRASH("update_light() for [src] with following light_system value: [light_system]") + if (!light_power || !light_range) // We won't emit light anyways, destroy the light source. QDEL_NULL(light) else @@ -87,46 +90,91 @@ /atom/vv_edit_var(var_name, var_value) switch (var_name) if ("light_range") - set_light(l_range=var_value) + if(light_system == STATIC_LIGHT) + set_light(l_range = var_value) + else + set_light_range(var_value) datum_flags |= DF_VAR_EDITED return TRUE if ("light_power") - set_light(l_power=var_value) + if(light_system == STATIC_LIGHT) + set_light(l_power = var_value) + else + set_light_power(var_value) datum_flags |= DF_VAR_EDITED return TRUE if ("light_color") - set_light(l_color=var_value) + if(light_system == STATIC_LIGHT) + set_light(l_color = var_value) + else + set_light_color(var_value) datum_flags |= DF_VAR_EDITED return TRUE return ..() -/atom/proc/flash_lighting_fx(_range = FLASH_LIGHT_RANGE, _power = FLASH_LIGHT_POWER, _color = LIGHT_COLOR_WHITE, _duration = FLASH_LIGHT_DURATION, _reset_lighting = TRUE) +/atom/proc/flash_lighting_fx(_range = FLASH_LIGHT_RANGE, _power = FLASH_LIGHT_POWER, _color = COLOR_WHITE, _duration = FLASH_LIGHT_DURATION) return -/turf/flash_lighting_fx(_range = FLASH_LIGHT_RANGE, _power = FLASH_LIGHT_POWER, _color = LIGHT_COLOR_WHITE, _duration = FLASH_LIGHT_DURATION, _reset_lighting = TRUE) +/turf/flash_lighting_fx(_range = FLASH_LIGHT_RANGE, _power = FLASH_LIGHT_POWER, _color = COLOR_WHITE, _duration = FLASH_LIGHT_DURATION) if(!_duration) stack_trace("Lighting FX obj created on a turf without a duration") - new /obj/effect/dummy/lighting_obj (src, _color, _range, _power, _duration) - -/obj/flash_lighting_fx(_range = FLASH_LIGHT_RANGE, _power = FLASH_LIGHT_POWER, _color = LIGHT_COLOR_WHITE, _duration = FLASH_LIGHT_DURATION, _reset_lighting = TRUE) - var/temp_color - var/temp_power - var/temp_range - if(!_reset_lighting) //incase the obj already has a lighting color that you don't want cleared out after, ie computer monitors. - temp_color = light_color - temp_power = light_power - temp_range = light_range - set_light(_range, _power, _color) - addtimer(CALLBACK(src, /atom/proc/set_light, _reset_lighting ? initial(light_range) : temp_range, _reset_lighting ? initial(light_power) : temp_power, _reset_lighting ? initial(light_color) : temp_color), _duration, TIMER_OVERRIDE|TIMER_UNIQUE) - -/mob/living/flash_lighting_fx(_range = FLASH_LIGHT_RANGE, _power = FLASH_LIGHT_POWER, _color = LIGHT_COLOR_WHITE, _duration = FLASH_LIGHT_DURATION, _reset_lighting = TRUE) - mob_light(_color, _range, _power, _duration) - -/mob/living/proc/mob_light(_color, _range, _power, _duration) - var/obj/effect/dummy/lighting_obj/moblight/mob_light_obj = new (src, _color, _range, _power, _duration) + new /obj/effect/dummy/lighting_obj (src, _range, _power, _color, _duration) + +/obj/flash_lighting_fx(_range = FLASH_LIGHT_RANGE, _power = FLASH_LIGHT_POWER, _color = COLOR_WHITE, _duration = FLASH_LIGHT_DURATION) + if(!_duration) + stack_trace("Lighting FX obj created on a obj without a duration") + new /obj/effect/dummy/lighting_obj (get_turf(src), _range, _power, _color, _duration) + + +/mob/living/flash_lighting_fx(_range = FLASH_LIGHT_RANGE, _power = FLASH_LIGHT_POWER, _color = COLOR_WHITE, _duration = FLASH_LIGHT_DURATION) + mob_light(_range, _power, _color, _duration) + + +/mob/living/proc/mob_light(_range, _power, _color, _duration) + var/obj/effect/dummy/lighting_obj/moblight/mob_light_obj = new (src, _range, _power, _color, _duration) return mob_light_obj + +/atom/proc/set_light_range(new_range) + if(new_range == light_range) + return + SEND_SIGNAL(src, COMSIG_ATOM_SET_LIGHT_RANGE, new_range) + . = light_range + light_range = new_range + + +/atom/proc/set_light_power(new_power) + if(new_power == light_power) + return + SEND_SIGNAL(src, COMSIG_ATOM_SET_LIGHT_POWER, new_power) + . = light_power + light_power = new_power + + +/atom/proc/set_light_color(new_color) + if(new_color == light_color) + return + SEND_SIGNAL(src, COMSIG_ATOM_SET_LIGHT_COLOR, new_color) + . = light_color + light_color = new_color + + +/atom/proc/set_light_on(new_value) + if(new_value == light_on) + return + SEND_SIGNAL(src, COMSIG_ATOM_SET_LIGHT_ON, new_value) + . = light_on + light_on = new_value + + +/atom/proc/set_light_flags(new_value) + if(new_value == light_flags) + return + SEND_SIGNAL(src, COMSIG_ATOM_SET_LIGHT_FLAGS, new_value) + . = light_flags + light_flags = new_value + diff --git a/code/modules/lighting/lighting_turf.dm b/code/modules/lighting/lighting_turf.dm index d050a2a7dd5a..0a41faf9c2c1 100644 --- a/code/modules/lighting/lighting_turf.dm +++ b/code/modules/lighting/lighting_turf.dm @@ -14,6 +14,9 @@ var/tmp/has_opaque_atom = FALSE // Not to be confused with opacity, this will be TRUE if there's any opaque atom on the tile. + ///Lumcount added by sources other than lighting datum objects, such as the overlay lighting component. + var/dynamic_lumcount = 0 + // Causes any affecting light sources to be queued for a visibility update, for example a door got opened. /turf/proc/reconsider_lights() lighting_corner_NE?.vis_update() @@ -61,6 +64,8 @@ totallums = (totallums - minlum) / (maxlum - minlum) + totallums += dynamic_lumcount + return CLAMP01(totallums) // Returns a boolean whether the turf is on soft lighting. diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm index 853c5efef801..e62870036865 100644 --- a/code/modules/mining/lavaland/necropolis_chests.dm +++ b/code/modules/mining/lavaland/necropolis_chests.dm @@ -340,7 +340,9 @@ GLOBAL_LIST_EMPTY(bloodmen_list) desc = "Happy to light your way." icon = 'icons/obj/lighting.dmi' icon_state = "orb" + light_system = MOVABLE_LIGHT light_range = 7 + light_flags = LIGHT_ATTACHED layer = ABOVE_ALL_MOB_LAYER var/sight_flags = SEE_MOBS var/lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE diff --git a/code/modules/mining/minebot.dm b/code/modules/mining/minebot.dm index 5678594a0d0e..f8ac55148d16 100644 --- a/code/modules/mining/minebot.dm +++ b/code/modules/mining/minebot.dm @@ -37,8 +37,10 @@ healable = 0 loot = list(/obj/effect/decal/cleanable/robot_debris) del_on_death = TRUE + light_system = MOVABLE_LIGHT + light_range = 6 + light_on = FALSE var/mode = MINEDRONE_COLLECT - var/light_on = 0 var/obj/item/gun/energy/kinetic_accelerator/minebot/stored_gun /mob/living/simple_animal/hostile/mining_drone/Initialize() @@ -234,11 +236,7 @@ /datum/action/innate/minedrone/toggle_light/Activate() var/mob/living/simple_animal/hostile/mining_drone/user = owner - if(user.light_on) - user.set_light(0) - else - user.set_light(6) - user.light_on = !user.light_on + user.set_light_on(!user.light_on) to_chat(user, span_notice("You toggle your light [user.light_on ? "on" : "off"].")) /datum/action/innate/minedrone/toggle_mode diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index d6be58845bf3..9ed3a02f9a94 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -17,6 +17,10 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER) invisibility = INVISIBILITY_OBSERVER hud_type = /datum/hud/ghost movement_type = GROUND | FLYING + light_system = MOVABLE_LIGHT + light_range = 1 + light_power = 2 + light_on = FALSE var/can_reenter_corpse var/bootime = 0 var/started_as_observer //This variable is set to 1 when you enter the game as an observer. @@ -926,10 +930,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp /mob/dead/observer/proc/set_invisibility(value) invisibility = value - if(!value) - set_light(1, 2) - else - set_light(0, 0) + set_light_on(!value ? TRUE : FALSE) // Ghosts have no momentum, being massless ectoplasm /mob/dead/observer/Process_Spacemove(movement_dir) diff --git a/code/modules/mob/living/carbon/human/species_types/ethereal.dm b/code/modules/mob/living/carbon/human/species_types/ethereal.dm index 7aa228f49ccb..1c3b769a3a1f 100644 --- a/code/modules/mob/living/carbon/human/species_types/ethereal.dm +++ b/code/modules/mob/living/carbon/human/species_types/ethereal.dm @@ -47,20 +47,32 @@ smells_like = "crackling sweetness" + var/obj/effect/dummy/lighting_obj/ethereal_light + + +/datum/species/ethereal/Destroy(force) + if(ethereal_light) + QDEL_NULL(ethereal_light) + return ..() + /datum/species/ethereal/on_species_gain(mob/living/carbon/C, datum/species/old_species, pref_load) - .=..() - if(ishuman(C)) - var/mob/living/carbon/human/H = C - default_color = "#" + H.dna.features["ethcolor"] - r1 = GETREDPART(default_color) - g1 = GETGREENPART(default_color) - b1 = GETBLUEPART(default_color) - spec_updatehealth(H) + . = ..() + if(!ishuman(C)) + return + var/mob/living/carbon/human/ethereal = C + default_color = "#[ethereal.dna.features["ethcolor"]]" + r1 = GETREDPART(default_color) + g1 = GETGREENPART(default_color) + b1 = GETBLUEPART(default_color) + //RegisterSignal(ethereal, COMSIG_ATOM_EMAG_ACT, .proc/on_emag_act) + //RegisterSignal(ethereal, COMSIG_ATOM_EMP_ACT, .proc/on_emp_act) + ethereal_light = ethereal.mob_light() + spec_updatehealth(ethereal) /datum/species/ethereal/on_species_loss(mob/living/carbon/human/C, datum/species/new_species, pref_load) - .=..() - C.set_light(0) + QDEL_NULL(ethereal_light) + return ..() /datum/species/ethereal/random_name(gender,unique,lastname) if(unique) @@ -71,15 +83,16 @@ return randname /datum/species/ethereal/spec_updatehealth(mob/living/carbon/human/H) - .=..() + . = ..() if(H.stat != DEAD && !EMPeffect) var/healthpercent = max(H.health, 0) / 100 if(!emageffect) current_color = rgb(r2 + ((r1-r2)*healthpercent), g2 + ((g1-g2)*healthpercent), b2 + ((b1-b2)*healthpercent)) - H.set_light(1 + (3 * healthpercent), 1 + (2 * healthpercent), current_color) + ethereal_light.set_light_range_power_color(1 + (2 * healthpercent), 1 + (1 * healthpercent), current_color) + ethereal_light.set_light_on(TRUE) fixed_mut_color = copytext_char(current_color, 2) else - H.set_light(0) + ethereal_light.set_light_on(FALSE) fixed_mut_color = rgb(128,128,128) H.update_body() diff --git a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm index 7690f3b666ea..687d4c154759 100644 --- a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm @@ -435,6 +435,8 @@ icon_state = "nothing" light_color = "#FFFFFF" light_range = LUMINESCENT_DEFAULT_GLOW + light_system = MOVABLE_LIGHT + light_power = 2.5 /obj/effect/dummy/luminescent_glow/Initialize() . = ..() diff --git a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm index da8f8c3aac82..ca4dc0ed64a1 100644 --- a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm @@ -211,9 +211,7 @@ /obj/item/light_eater/proc/disintegrate(obj/item/O) if(istype(O, /obj/item/pda)) var/obj/item/pda/PDA = O - PDA.set_light(0) - PDA.fon = FALSE - PDA.f_lum = 0 + PDA.set_light_on(FALSE) PDA.update_icon() visible_message(span_danger("The light in [PDA] shorts out!")) else diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index d873bd0530b9..2b9023781aa8 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -10,6 +10,8 @@ has_limbs = 1 hud_type = /datum/hud/robot blocks_emissive = EMISSIVE_BLOCK_GENERIC + light_system = MOVABLE_LIGHT + light_on = FALSE var/custom_name = "" var/braintype = "Cyborg" @@ -795,17 +797,17 @@ //if both lamp is enabled AND the update_color flag is on, keep the lamp on. Otherwise, if anything listed is true, disable the lamp. if(!(update_color && lamp_enabled) && (turn_off || lamp_enabled || update_color || !lamp_functional || stat || low_power_mode)) if(lamp_functional && stat != DEAD) - set_light(l_power = TRUE) //If the lamp isn't broken and borg isn't dead, doomsday borgs cannot disable their light fully. - set_light(l_color = "#FF0000") //This should only matter for doomsday borgs, as any other time the lamp will be off and the color not seen - set_light(l_range = 1) //Again, like above, this only takes effect when the light is forced on by doomsday mode. - set_light(l_power = FALSE) + set_light_on(TRUE) //If the lamp isn't broken and borg isn't dead, doomsday borgs cannot disable their light fully. + set_light_color("#FF0000") //This should only matter for doomsday borgs, as any other time the lamp will be off and the color not seen + set_light_range(1) //Again, like above, this only takes effect when the light is forced on by doomsday mode. + set_light_on(FALSE) lamp_enabled = FALSE lampButton?.update_icon() update_icons() return - set_light(l_range = lamp_intensity) - set_light(l_color = lamp_color) - set_light(l_power = TRUE) + set_light_range(lamp_intensity) + set_light_color(lamp_color) + set_light_on(TRUE) lamp_enabled = TRUE lampButton?.update_icon() update_icons() diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm index 204de0d28e0d..9fdf05c98dba 100644 --- a/code/modules/mob/living/simple_animal/bot/bot.dm +++ b/code/modules/mob/living/simple_animal/bot/bot.dm @@ -4,8 +4,6 @@ layer = MOB_LAYER gender = NEUTER mob_biotypes = list(MOB_ROBOTIC) - light_range = 3 - stop_automated_movement = 1 wander = 0 healable = 0 damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0) @@ -23,6 +21,9 @@ bubble_icon = "machine" speech_span = SPAN_ROBOT faction = list("neutral", "silicon" , "turret") + light_system = MOVABLE_LIGHT + light_range = 3 + light_power = 0.9 var/obj/machinery/bot_core/bot_core = null var/bot_core_type = /obj/machinery/bot_core @@ -128,7 +129,7 @@ return FALSE on = TRUE update_mobility() - set_light(initial(light_range)) + set_light_on(on) update_icon() diag_hud_set_botstat() return TRUE @@ -136,7 +137,7 @@ /mob/living/simple_animal/bot/proc/turn_off() on = FALSE update_mobility() - set_light(0) + set_light_on(on) bot_reset() //Resets an AI's call, should it exist. update_icon() diff --git a/code/modules/mob/living/simple_animal/guardian/guardian.dm b/code/modules/mob/living/simple_animal/guardian/guardian.dm index b212f4c36d1c..393c044f627b 100644 --- a/code/modules/mob/living/simple_animal/guardian/guardian.dm +++ b/code/modules/mob/living/simple_animal/guardian/guardian.dm @@ -38,6 +38,9 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians melee_damage_upper = 15 butcher_results = list(/obj/item/ectoplasm = 1) AIStatus = AI_OFF + light_system = MOVABLE_LIGHT + light_range = 3 + light_on = FALSE hud_type = /datum/hud/guardian dextrous_hud_type = /datum/hud/dextrous/guardian //if we're set to dextrous, account for it. var/list/guardian_overlays[GUARDIAN_TOTAL_LAYERS] @@ -338,12 +341,12 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians to_chat(src, "You don't have another mode!") /mob/living/simple_animal/hostile/guardian/proc/ToggleLight() - if(light_range<3) + if(!light_on) to_chat(src, span_notice("You activate your light.")) - set_light(3) + set_light_on(TRUE) else to_chat(src, span_notice("You deactivate your light.")) - set_light(0) + set_light_on(FALSE) /mob/living/simple_animal/hostile/guardian/verb/ShowType() set name = "Check Guardian Type" diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm index c5acf374c89f..0133cfdcc167 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm @@ -234,6 +234,7 @@ Difficulty: Very Hard icon = 'icons/effects/effects.dmi' icon_state = "at_shield2" layer = FLY_LAYER + light_system = MOVABLE_LIGHT light_range = 2 duration = 8 var/target diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm index 556a3316021f..feaa39c3a7c0 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm @@ -62,6 +62,7 @@ GLOBAL_LIST_INIT(AISwarmerCapsByType, list(/mob/living/simple_animal/hostile/swa var/call_help_cooldown = 0 var/call_help_cooldown_amt = 150 //Deciseconds between calling swarmers to help us when attacked var/static/list/swarmer_caps + /mob/living/simple_animal/hostile/megafauna/swarmer_swarm_beacon/Initialize() . = ..() diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/basilisk.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/basilisk.dm index 0a28870c431c..670283b278db 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/basilisk.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/basilisk.dm @@ -101,6 +101,7 @@ icon_dead = "watcher_magmawing_dead" maxHealth = 235 //Compensate for the lack of slowdown on projectiles with a bit of extra health health = 235 + light_system = MOVABLE_LIGHT light_range = 3 light_power = 2.5 light_color = LIGHT_COLOR_LAVA diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/ghost.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/ghost.dm index 32e1c4d047be..25621a8ff420 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/ghost.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/ghost.dm @@ -31,6 +31,9 @@ movement_type = FLYING pressure_resistance = 300 gold_core_spawnable = NO_SPAWN //too spooky for science + light_system = MOVABLE_LIGHT + light_range = 1 // same glowing as visible player ghosts + light_power = 2 var/ghost_hair_style var/ghost_hair_color var/mutable_appearance/ghost_hair @@ -42,7 +45,6 @@ /mob/living/simple_animal/hostile/retaliate/ghost/Initialize() . = ..() give_hair() - set_light(1, 2) // same glowing as visible player ghosts if(random) switch(rand(0,1)) if(0) diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm index c8675c806667..ca735e206e35 100644 --- a/code/modules/modular_computers/computers/item/computer.dm +++ b/code/modules/modular_computers/computers/item/computer.dm @@ -67,8 +67,6 @@ var/obj/physical = null ///If the computer has a flashlight/LED light/what-have-you installed var/has_light = FALSE - ///If that light is enabled - var/light_on = FALSE ///The brightness of that light var/comp_light_luminosity = 3 ///The color of that light diff --git a/code/modules/photography/camera/camera.dm b/code/modules/photography/camera/camera.dm index 4baad0269772..7294b3cce730 100644 --- a/code/modules/photography/camera/camera.dm +++ b/code/modules/photography/camera/camera.dm @@ -8,8 +8,11 @@ item_state = "camera" lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' - light_color = LIGHT_COLOR_WHITE + light_system = MOVABLE_LIGHT //Used as a flash here. + light_range = 8 + light_color = COLOR_WHITE light_power = FLASH_LIGHT_POWER + light_on = FALSE w_class = WEIGHT_CLASS_SMALL flags_1 = CONDUCT_1 slot_flags = ITEM_SLOT_NECK @@ -200,7 +203,8 @@ /obj/item/camera/proc/captureimage(atom/target, mob/user, flag, size_x = 1, size_y = 1) if(flash_enabled) - flash_lighting_fx(8, light_power, light_color) + set_light_on(TRUE) + addtimer(CALLBACK(src, .proc/flash_end), FLASH_LIGHT_DURATION, TIMER_OVERRIDE|TIMER_UNIQUE) blending = TRUE var/turf/target_turf = get_turf(target) if(!isturf(target_turf)) @@ -248,6 +252,9 @@ after_picture(user, P, flag) blending = FALSE +/obj/item/camera/proc/flash_end() + set_light_on(FALSE) + /obj/item/camera/proc/after_picture(mob/user, datum/picture/picture, proximity_flag) printpicture(user, picture) diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 4341b21331de..bacac756eae7 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -479,9 +479,7 @@ if(!user.transferItemToLoc(I, src)) return to_chat(user, span_notice("You click [S] into place on [src].")) - if(S.on) - set_light(0) - gun_light = S + set_gun_light(S) update_gunlight() alight = new(src) if(loc == user) @@ -569,12 +567,28 @@ if(!gun_light) return var/obj/item/flashlight/seclite/removed_light = gun_light - gun_light = null + set_gun_light(null) update_gunlight() removed_light.update_brightness() QDEL_NULL(alight) return TRUE +///Called when gun_light value changes. +/obj/item/gun/proc/set_gun_light(obj/item/flashlight/seclite/new_light) + if(gun_light == new_light) + return + . = gun_light + gun_light = new_light + if(gun_light) + gun_light.set_light_flags(gun_light.light_flags | LIGHT_ATTACHED) + if(gun_light.loc != src) + gun_light.forceMove(src) + else if(.) + var/obj/item/flashlight/seclite/old_gun_light = . + old_gun_light.set_light_flags(old_gun_light.light_flags & ~LIGHT_ATTACHED) + if(old_gun_light.loc == src) + old_gun_light.forceMove(get_turf(src)) + /obj/item/gun/ui_action_click(mob/user, actiontype) if(istype(actiontype, alight)) toggle_gunlight() @@ -587,30 +601,14 @@ var/mob/living/carbon/human/user = usr gun_light.on = !gun_light.on + gun_light.update_brightness() to_chat(user, span_notice("You toggle the gunlight [gun_light.on ? "on":"off"].")) playsound(user, 'sound/weapons/empty.ogg', 100, TRUE) update_gunlight() /obj/item/gun/proc/update_gunlight() - if(gun_light) - if(gun_light.on) - set_light(gun_light.brightness_on) - else - set_light(0) - cut_overlay(flashlight_overlay, TRUE) - var/state = "flight[gun_light.on? "_on":""]" //Generic state. - if(gun_light.icon_state in icon_states('icons/obj/guns/flashlights.dmi')) //Snowflake state? - state = gun_light.icon_state - flashlight_overlay = mutable_appearance('icons/obj/guns/flashlights.dmi', state) - flashlight_overlay.pixel_x = flight_x_offset - flashlight_overlay.pixel_y = flight_y_offset - add_overlay(flashlight_overlay, TRUE) - else - set_light(0) - cut_overlay(flashlight_overlay, TRUE) - flashlight_overlay = null - update_icon(TRUE) + update_icon() for(var/X in actions) var/datum/action/A = X A.UpdateButtonIcon() diff --git a/code/modules/projectiles/guns/energy/energy_gun.dm b/code/modules/projectiles/guns/energy/energy_gun.dm index e2d1345b647d..1999c8f06278 100644 --- a/code/modules/projectiles/guns/energy/energy_gun.dm +++ b/code/modules/projectiles/guns/energy/energy_gun.dm @@ -22,7 +22,7 @@ can_flashlight = FALSE // Can't attach or detach the flashlight, and override it's icon update /obj/item/gun/energy/e_gun/mini/Initialize() - gun_light = new /obj/item/flashlight/seclite(src) + set_gun_light(new /obj/item/flashlight/seclite(src)) return ..() /obj/item/gun/energy/e_gun/mini/update_icon() diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm index cee6db923837..8c3e8f701b00 100644 --- a/code/modules/projectiles/projectile/beams.dm +++ b/code/modules/projectiles/projectile/beams.dm @@ -5,13 +5,15 @@ damage = 20 wound_bonus = -20 bare_wound_bonus = 10 - light_range = 2 damage_type = BURN hitsound = 'sound/weapons/sear.ogg' hitsound_wall = 'sound/weapons/effects/searwall.ogg' flag = LASER eyeblur = 2 impact_effect_type = /obj/effect/temp_visual/impact_effect/red_laser + light_system = MOVABLE_LIGHT + light_range = 2 + light_power = 1 light_color = LIGHT_COLOR_RED ricochets_max = 50 //Honk! ricochet_chance = 80 diff --git a/code/modules/reagents/chemistry/reagents/food_reagents.dm b/code/modules/reagents/chemistry/reagents/food_reagents.dm index dbc95036aecf..46bb9d062199 100644 --- a/code/modules/reagents/chemistry/reagents/food_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/food_reagents.dm @@ -690,12 +690,30 @@ description = "A stimulating ichor which causes luminescent fungi to grow on the skin. " color = "#b5a213" taste_description = "tingling mushroom" + //Lazy list of mobs affected by the luminosity of this reagent. + var/list/mobs_affected -/datum/reagent/consumable/tinlux/on_mob_metabolize(mob/living/M) - M.set_light(2) +/datum/reagent/consumable/tinlux/reaction_mob(mob/living/M) + add_reagent_light(M) /datum/reagent/consumable/tinlux/on_mob_end_metabolize(mob/living/M) - M.set_light(-2) + remove_reagent_light(M) + +/datum/reagent/consumable/tinlux/proc/on_living_holder_deletion(mob/living/source) + + remove_reagent_light(source) + +/datum/reagent/consumable/tinlux/proc/add_reagent_light(mob/living/living_holder) + var/obj/effect/dummy/lighting_obj/moblight/mob_light_obj = living_holder.mob_light(2) + LAZYSET(mobs_affected, living_holder, mob_light_obj) + RegisterSignal(living_holder, COMSIG_PARENT_QDELETING, .proc/on_living_holder_deletion) + +/datum/reagent/consumable/tinlux/proc/remove_reagent_light(mob/living/living_holder) + UnregisterSignal(living_holder, COMSIG_PARENT_QDELETING) + var/obj/effect/dummy/lighting_obj/moblight/mob_light_obj = LAZYACCESS(mobs_affected, living_holder) + LAZYREMOVE(mobs_affected, living_holder) + if(mob_light_obj) + qdel(mob_light_obj) /datum/reagent/consumable/vitfro name = "Vitrium Froth" diff --git a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm index dc8d709ef516..24f81e284d08 100644 --- a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm +++ b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm @@ -251,7 +251,7 @@ var/range = created_volume/3 if(isatom(holder.my_atom)) var/atom/A = holder.my_atom - A.flash_lighting_fx(_range = (range + 2), _reset_lighting = FALSE) + A.flash_lighting_fx(_range = (range + 2)) for(var/mob/living/carbon/C in get_hearers_in_view(range, location)) if(C.flash_act()) if(get_dist(C, location) < 4) @@ -272,7 +272,7 @@ var/range = created_volume/10 if(isatom(holder.my_atom)) var/atom/A = holder.my_atom - A.flash_lighting_fx(_range = (range + 2), _reset_lighting = FALSE) + A.flash_lighting_fx(_range = (range + 2)) for(var/mob/living/carbon/C in get_hearers_in_view(range, location)) if(C.flash_act()) if(get_dist(C, location) < 4) diff --git a/code/modules/spells/spell_types/lichdom.dm b/code/modules/spells/spell_types/lichdom.dm index 980c28bf78ff..7c135ac2eb20 100644 --- a/code/modules/spells/spell_types/lichdom.dm +++ b/code/modules/spells/spell_types/lichdom.dm @@ -82,7 +82,8 @@ icon_state = "bluespace" color = "#003300" light_color = "#003300" - var/lon_range = 3 + light_system = MOVABLE_LIGHT + light_range = 3 var/resurrections = 0 var/datum/mind/mind var/respawn_time = 1800 @@ -97,7 +98,6 @@ active_phylacteries++ GLOB.poi_list |= src START_PROCESSING(SSobj, src) - set_light(lon_range) if(initial(SSticker.mode.round_ends_with_antag_death)) SSticker.mode.round_ends_with_antag_death = FALSE diff --git a/code/modules/surgery/organs/eyes.dm b/code/modules/surgery/organs/eyes.dm index a6388afb6c05..648901ea203a 100644 --- a/code/modules/surgery/organs/eyes.dm +++ b/code/modules/surgery/organs/eyes.dm @@ -258,7 +258,7 @@ var/active = FALSE var/max_light_beam_distance = 5 var/light_beam_distance = 5 - var/light_object_range = 1 + var/light_object_range = 2 var/light_object_power = 2 var/list/obj/effect/abstract/eye_lighting/eye_lighting var/obj/effect/abstract/eye_lighting/on_mob @@ -369,6 +369,7 @@ clear_visuals() var/turf/scanning = scanfrom var/stop = FALSE + on_mob.set_light_flags(on_mob.light_flags & ~LIGHT_ATTACHED) on_mob.forceMove(scanning) for(var/i in 1 to light_beam_distance) scanning = get_step(scanning, scandir) @@ -389,6 +390,7 @@ var/obj/effect/abstract/eye_lighting/L = i L.forceMove(src) if(!QDELETED(on_mob)) + on_mob.set_light_flags(on_mob.light_flags | LIGHT_ATTACHED) on_mob.forceMove(src) /obj/item/organ/eyes/robotic/glow/proc/start_visuals() @@ -405,26 +407,35 @@ /obj/item/organ/eyes/robotic/glow/proc/regenerate_light_effects() clear_visuals(TRUE) - on_mob = new(src) + on_mob = new (src, light_object_range, light_object_power, current_color_string, LIGHT_ATTACHED) for(var/i in 1 to light_beam_distance) - LAZYADD(eye_lighting,new /obj/effect/abstract/eye_lighting(src)) + LAZYADD(eye_lighting, new /obj/effect/abstract/eye_lighting(src, light_object_range, light_object_power, current_color_string)) sync_light_effects() /obj/item/organ/eyes/robotic/glow/proc/sync_light_effects() - for(var/I in eye_lighting) - var/obj/effect/abstract/eye_lighting/L = I - L.set_light(light_object_range, light_object_power, current_color_string) - if(on_mob) - on_mob.set_light(1, 1, current_color_string) + for(var/e in eye_lighting) + var/obj/effect/abstract/eye_lighting/eye_lighting = e + eye_lighting.set_light_color(current_color_string) + on_mob?.set_light_color(current_color_string) /obj/effect/abstract/eye_lighting + light_system = MOVABLE_LIGHT var/obj/item/organ/eyes/robotic/glow/parent -/obj/effect/abstract/eye_lighting/Initialize() +/obj/effect/abstract/eye_lighting/Initialize(mapload, light_object_range, light_object_power, current_color_string, light_flags) . = ..() parent = loc if(!istype(parent)) + stack_trace("/obj/effect/abstract/eye_lighting added to improper parent ([loc]). Deleting.") return INITIALIZE_HINT_QDEL + if(!isnull(light_object_range)) + set_light_range(light_object_range) + if(!isnull(light_object_power)) + set_light_power(light_object_power) + if(!isnull(current_color_string)) + set_light_color(current_color_string) + if(!isnull(light_flags)) + set_light_flags(light_flags) /obj/item/organ/eyes/moth name = "moth eyes" diff --git a/code/modules/surgery/tools.dm b/code/modules/surgery/tools.dm index aca532c20ef5..11ac9b4b2acb 100644 --- a/code/modules/surgery/tools.dm +++ b/code/modules/surgery/tools.dm @@ -394,6 +394,8 @@ hitsound = 'sound/weapons/blade1.ogg' force = 16 toolspeed = 0.7 + light_system = MOVABLE_LIGHT + light_range = 1 light_color = LIGHT_COLOR_GREEN sharpness = SHARP_EDGED @@ -403,13 +405,13 @@ if(tool_behaviour == TOOL_SCALPEL) tool_behaviour = TOOL_SAW to_chat(user, span_notice("You increase the power, now it can cut bones.")) - set_light(2) + set_light_range(2) force += 1 //we don't want to ruin sharpened stuff icon_state = "saw_a" else tool_behaviour = TOOL_SCALPEL to_chat(user, span_notice("You lower the power, it can now make precise incisions.")) - set_light(1) + set_light_range(1) force -= 1 icon_state = "scalpel_a" @@ -448,10 +450,6 @@ toolspeed = 0.7 light_color = LIGHT_COLOR_RED -/obj/item/cautery/advanced/Initialize() - . = ..() - set_light(1) - /obj/item/cautery/advanced/attack_self(mob/user) if(tool_behaviour == TOOL_CAUTERY) diff --git a/code/modules/swarmers/swarmer.dm b/code/modules/swarmers/swarmer.dm index cba60fc899ca..455d8b7d5a49 100644 --- a/code/modules/swarmers/swarmer.dm +++ b/code/modules/swarmers/swarmer.dm @@ -69,6 +69,10 @@ var/list/mob/living/simple_animal/hostile/swarmer/melee/dronelist ///Prevents alert spam var/last_alert = 0 + light_system = MOVABLE_LIGHT + light_range = 3 + ///Bitflags to store boolean conditions, such as whether the light is on or off. + var/swarmer_flags = NONE /mob/living/simple_animal/hostile/swarmer/Initialize() . = ..() @@ -399,19 +403,19 @@ */ /mob/living/simple_animal/hostile/swarmer/proc/toggle_light() if(!light_range) - set_light(3) + set_light_on(TRUE) if(!mind) return for(var/d in dronelist) var/mob/living/simple_animal/hostile/swarmer/melee/drone = d - drone.set_light(3) + drone.set_light_on(TRUE) else - set_light(0) + set_light_on(FALSE) if(!mind) return for(var/d in dronelist) var/mob/living/simple_animal/hostile/swarmer/melee/drone = d - drone.set_light(0) + drone.set_light_on(FALSE) balloon_alert(src, "light toggled") /** diff --git a/icons/effects/light_overlays/light_128.dmi b/icons/effects/light_overlays/light_128.dmi new file mode 100644 index 0000000000000000000000000000000000000000..22dc0b0108702f5dbc74a2d1170026743fa7dd9d GIT binary patch literal 7040 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 literal 0 HcmV?d00001 diff --git a/icons/effects/light_overlays/light_224.dmi b/icons/effects/light_overlays/light_224.dmi new file mode 100644 index 0000000000000000000000000000000000000000..9fab531d1a691cf1cc53c3f54aec69c2e4bfba09 GIT binary patch literal 17004 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 P00000NkvXXu0mjf9iK$$ literal 0 HcmV?d00001 diff --git a/icons/effects/light_overlays/light_256.dmi b/icons/effects/light_overlays/light_256.dmi new file mode 100644 index 0000000000000000000000000000000000000000..701562efcd8f85fea23b0bf9475c33698c57c338 GIT binary patch literal 21365 zcmXtfXH=8R_jL*#1SyIrO?n4Gq!$&b0!k62D*__Y385#Uh+L75s8lb#2BjyIAPNGK zAT6{&yp#Ze1cA^Ae}C^<@25GRW}Y?AI(zm$XP=Y@RwftNuCf6DfD2}(Mm7Kd@NWnN zurU2yTtce6|1N=1c8+02UZI{LzCmHWfdK$OM1kUf4jWwMA|pmS`k&#h=MIwzwo|WO$l3j0kzKO zsD}RivtL%=+n9_jk;q8wAoo2RJ~Qqs9OV+p=Vw${#LTeUzRuR+jL`p!BKPaRui8_;W+gr0V?ni$HvUpsgZDWk0re&4;1l*QMQx6 zv8@#n7&NjFecY|`1b02zRK5V>X+JD1A@l*yyzzKk3 z=dTE)*ohArg7*gi_P>2?5oMmQ}%DuJJSZ(j^< zmVY=F;9c5KJD!P|>Pm>7w7dm=nrAS}yf%yS#S!l@EVwbKG@2394GnBS&UX@iDF+ck z13j3&H61w*qob+++ehK=DZLcHDZ@mV>&~GoK_F!2>A`$;&wPhMb($;kvb)iCdKZh~ zOVD8mTLPN@F`E=K#c#d|Px%Im_>WbH@;vA=uGKmU8l_y9m0$T5#uq5l3ClwG>ML=T@bEHHQ| zhLS!~W;c$rcIZuu#tc3000FQ6_#oWZOPAmBA%=B6#HHTJ#2P>8`|-x@PL}|#kfm3a zLGryoI!ulGREp;i!qCWj*yI5Xi3?jkjyiicVmPvRm2>l`EDyNe&~uYULuIlBJ=+$G zzqaLYW{FARiV$2Al+mI-Pcx~`$6BA$1!ZGjY>2E^ZghVr&Ypm5rN@PJ&hz6d$miUV zsV@3zJ~4&ZE`52W;;9cXl$@QVO zIEaS$?_umGHvxG@($GzKjDOilxqfB1 zD9!sN6S0)+{WO}fU7mT;0)Rixdk|Owdl6n|kjW6I0DQT3tcHPlg!@Z8{wUG1zb-?f zxJ8L*ym}Z{qjqvt5g`lw`GQJcqo@m!__Ujg{Th`@+jlMw=LmVG)Uf^(Jko+Ew zgd9dOhTy}YMQK*O?Yjc%8SvjbJnst>phX6*(KmeUFLM$Ju{vUhEiZ@z7corR>C1GM z!@_R{X7twz^KoA}fMJOh{$C54T=RD_zae#*JMG?eM$OKLdf-(prNx34l_kEbhH?_4 zvoUSb@C&dwKL$EB-$5JUSl(GnehM>TYC2(luPK%P5cI<8g*0v!*x6wHEh8sB+E&FD zM+;m%VNkRn0lJl0WBfqV06HZ$Xcj@ma40OAiO5=0fAcZ+NJGzlbys~c44pVZtG38l zk3lbsVr_M}TuG1-Mu9`lJ@c!w&(>ZWQa|)>^si%kBw1seLDRBFCVyJrX(4Mx>H%&j zwdcbNWt`bYAF@)!k$Z9>Z}zA!Z_%w2Gs@cH#}UpjL2kRa7a9clBo?cdvK!F9AH>fc zWAXQY&mH^bb%L##qnO-kD&1>*-F8mdj?*0Hr;UTp>v*mxZtwZWl?#>3uevhNKZc{D z3Mz-hldjBmUn8eqVC}vHoR>M0?|DOQ*?g^TD#W@b40o$^qw{U_E~9sqT!Jz9-CEh& z7=2WT37e8p5ImH+bE$;05#lNq|H9cG%!`%z7DS}laefR1EG95?UjS}0oTuWgb*NVV zNLx2xpqQ!fy>6>Z66K#v4jztjaOaGeFEkGqFE@d2{nXk9hUkTRgd6I4UN|&jNXyW^ z1GvT0>mZE?ybXXdz)1GIm`iJvv$z%CdAFV714hA_=N+xcXIWo?CLs;?F9GDi`lx0d zkri|yU2R2jFfKEyhRNoHbGtO-_DSW^S*FDH3af|eg7w1ZlcWNPL9z=FdXr?2mj(DI zpFpxg7@N`Q@BW;jv^H|!Q^odx6uz7Nct1hef)y9U2*$^ws61^BV5k0@QX7l!4v98M zW9YsK)CHWb$Li(P?uC;uJoD>U&x}3ndS;VklbcIDg%J63cDDD3m-kFpD^(dice?qH zgWW##)*;WPYkO>aas^RmNsraHOHGNxJQ%BOopp}GgSC$X3)A0sCY8?1P;4=@!-A<+ z*XP82_N-*6C_c#J&D+bT&j*AYh; z5uKFR8C!@Bk){dk@ee}f;#u!&u&Cb*3S`ewXzvD}w}~N?O#nI=>aBi08KJ{*r7rPA ziIXF+$o_tsL_23KtI(S)1Q^YeBo2PRTRWjR?i!a@yxct%eB%gRjWe`90dPDg9SN6*yLmMdDg}N-JGm3vY=@eIs=o3BK4B!W}$t*UtHFiImoqegBNdo{q??Aj#X#{ zp!maFvrHkwt1_I8!zIIi1_%|!*&7^jCJg4PKFiU*(KSqAo~myj4kCt8H{r99i$tShi4q7B#eFu-q zppv<_B$U;x`|?%V+GK@zyp^P8Zg6gl$(>mPA@*f$)5m#C+eZ@Hk}O0kBDJ$Ld)u5- z+6;a1dSpy}-d9FRZqxjATB*2fp?rOx7iFH?kbfYgD9V_*CWrJo;Vzi5I}1K%x~xju zsN+C&+ldsgJ)~`Wni9uZJ;V>wvx?*RPFWM#sx)jHmV3VucQm)`l6d}^JA#^vW!}*c zaq1^w2EIaYB}2nwOagca{9#Z~H-)v#Qjf6(Y>;r4>L#FxGpIG6cSO)MuY0K+ozM7W zV&>Y1mR^Dn5nDv4;n)QYX>$5DaZ1(y0L}#pu0fFP1P<<|7@pK%!oPhAEe&-fIaHUj zPbxYV!Dti}lF}rZr?ZkTVkUpJ6CzJRN1tR(+LPIxZHfu= zGG-f@Km(`56h1bVjiU*$)TN+|U65Gxb0&WG&|c<7zfQ&&VQ7o}IuvDP&(3S~wtlwy zq@b*Gwn1Np_4&zNp2J<70ax)SkbV+C3VJfkcM8wz+fY=;Ql9QHL@+8RvvE$>2wQER z1(i5Vme7D)^yMS&G2fy|)cfMW5({*Yp~n1`O=$%$O#^p*WtCz}H$uY65i|WdaDSUw z0pVg|4>AYUxdccel46-yV_vWru0E3^_9ERD2LEHwX8Irw=-R&^JjxbVd2x?U5w2mn zxjdSIF^eWXzCD`W0nc9Z8o+t*uLleO}?Lu zaneV%sVVPR<^1=8djz(f(n&G*Sa4E^N?!OM`T?LP%9gN&$Hk1->Ja8_sZ{u>)B!1w zqy1FWSD(4X*_UF_Hn8L{xHuT6f#X;cFMl$|v1bIpl0_tu4ZpZxY+)#NcMpdStlRsQ z<6La6ifcqkP0B!Y&P~#v>6tW};<<3lxgAf09PmEf(+V^2#km)`xG3)dsqEfaT)rBo zhRKh?E=h|$SSdNVpyFRuW0puXq~;iY03VyO2GP|gyu@UvHRv__N3o zw^6Y_%u^4*HJz)8_P}%pGGb%B?eZjyNnM`dfdG{^Oajv?itwq1S6;+mp=UKr6YSdX zMib~C?Sy&6xc@@=MZ?|(o%2)oD6X-z{l~W{0OZnSNAPdV^97R%4-x^^Hplh%b$2c5 zVd?#T$nhR@oVl|Rr?5iS&xpFu;Kdwzfj4!EY+ADM@;c&0{=lE={P^M)R!#B|FN>x@ z?6bZVtD2@ZhX;PKjObc-e^qYHM)U;(<~X6`d}dQr!!NCHl9Dy_s70ZR(bPa;S7^JC z?ZH#x`@-7}CbVr-(UoT$J8JaEN7OJ~Gl8CW6K{><{60xwXndzAtsiH8tiQSUgFx@6 z_i8&cn;|@odbb)G2+mal!tOYG3e>~;+i#X1U5uLM(Iu)LS^@Tenv5KliBc`{vud!e z1obHvq268ybr91W>&WVTIChLkpW~?e+-0UGwFZ?#W*9C;04%S5bJ-9jvO?br??88TSDCNU^sq2`z^fwc&2Ozo*pU?quVo$^7G zhqz}y#t}vA^W5<>kOX?LmhI3{m)`@X$q7R^o3^28PqpA6(+9gHJH_WjQyY#+s2lXu z>(fFhz`g9DD0IVcoJ$hnlMD#BQNdWV(s_94lueY`bbpOwG;7O>RYof)*56vMYj?W_ z)GorYUBO$NdaESYw#!+78M;(8r9M^6wJA=EvCQ0o@il_c_fk zsC2-0X69E#YcDSh$nba|u^!$}J>j7zy5SBa-+dL(71MKu7+0dzPEDnqeTitDHtc=K zJIcl{SLcF9T*!sRExqS;)LX2sGT9Ru;)7{+*Vk3wT3Fjud;*XQ0e@`znafva*I_T(&^2>u}cX zP;EtT>7+|`n@8Cs#SCdfX}Fq};2$nTl{D0y4Q2T)p@4kFMbDlA)h_sO=X6IsfKoV; z2yhs50C^xE45(-aGUf(;!H-h-1`GmWj$B&Na>ZuA$9CA5!13hZvf^yEfN)dTEz){{iGoUak-f$~UlsPG=b;d7Ez0iS2ar!}hJ6eq zn*G+=;ia`G$|@e=nu6f>umLLZjRpec()EhEcgB-&!bM9oxupY2ZY6bx%dQRXYN8QN zXrDmo`lN}%B_maCY-i5-gvgKU7G5(wrpOT+7#e}6C=Xh)NF6();9)LcuTPMfz88uy z$Xeu1VZY9|ox;H*yX`ptK3C8lNM3d(~zvcVIN{+0fpmN_`CvWAE7+bD-_J4 zyR6DT$L;8Vquy@WKF;+!JvV75-JXSb;P*IynpEU2iy1W1E8vOi{n4F-`*ps-}$5!^5`bc0vTyE`0V?+!S;-woYo zFsUYWpW$PHXHkvE6vK)V15Zo1IS7+{N86@b{K+GR&U2tbaY|NSpB-j|q49U20ZD6H zS`2XNEdVYD*X=EG80uw#!9_sR{@OX$&_H%Of$fKV-~jiN!l8gx zlR(f0M$1b3V+hl5O2fl{gTBmuU!}+!FrJGz@)~CCL@A&3i_((1VnOuOzfa2j1y~tC z6A7qYX}WIcU$oAAn}cpnK8d6M4puwd%Zr|<)v*Uu*Y4W%7jyZ=wG8_`(m0tW?_SyX z%k3h8B4bct&z3Eg;|4jR6zEC>OqxcXS9HonVXiW;v_t@T4hG|&I$uRt_F$(mo);!OL? zfF1QQUe>V?st-6cs9r=EB=J}YwZ{T3a2Gh48E~Fzo1_79UXK6zAW!b>iQ{&R{OIk~ zYa)dJzY`_xAvPwjT)OFEJz$4%@cPEnQ#X5-KNPmfvETLp8E4zr|Jc*(zo$st?kCTQ zsQXx`EfCG>vE*k!^KeNg%LqLG2;0-^j4vh&m30eSA4a4jMF8dacypeQ6gQx7`F#XZ z6q=Y}2f*ntMZOq3aS*m}V-B%ICt-uT*cl5dnWvVCIe@iJ-7ispcE+T&k@R;wGL2|F zqAtQp3KuJn>A%jMUOT(qd3Jr7+G-*LFD*cSkGi?ujIozh39<|Do5`OD{;U(dV^B7a zaO>)t6Ac@9e6;u$4elN$l{Wo~H#ZmEVzd(m7veq$7C6{#%m0YHZ^qrY9!LP515WF! zcasJDv@X8ela?dJGCX3}Wu709pTeb@E&IxO@B?2S${RRUyty^##LUw$3}1>0luEx{ z17A6V8NRgBjP&1vZcVlj1Vn zt$%bf+NWn*K3$7tb2*60=%R@@#%pf@HA8~(;Yx|YavsKXyr2$a$q~zX?V$BSUp0Wh zOvF2GtgBN7TVT7H1F={FHyFW`rbH=S zmWE4j|Iq?>4IM5OgY~<)iDZJlp*D2m8#gfBBn1OZBt_SP-hWnn0;v4JG7nna>n-~r zx`z+bvL0A_8mpu{ez=$f=;Z$4`RoJoMh`n~10~_AZp(GT z^IDDcg0{3{x8mE>1zN&J9u%p4k7a}A)BknDMla@ZCIh%|eBPM$`PkL#FKC90IOrT_ zW<|j?@}MozvhZe1)`5ZP!oxAPR~my3L4HD(I?BaQBhT~=se~>%`Dq+8-H2)ML|V)+ z)b3g)f}Lw(HxxB@3Z7#BXpe}Bm1Lm*j&MH!8&3f%;X{spd7C6a0r(Tidwb<7o65A7 z9kKECByTT5}?xwYkM9!wuj2p_0G_QU<_9r-P~D=$|!4GaGA{KhZ)D8 z4mRM6vH*>q06N49Qe^UOxu>F#ZL|?Cc_Seoh!GBHj`QySZ`F%Q@HqW8oa>ABugt}z z;wr0^rtxFv4b?xBO_nenOf_6~j)zHi!@|%*c4a;uBJ9o|4NNyPL@;HaOKo+8ST^wl z%O#|g*n%ide{Z#kdmF7~X+*^8gt{$8UdyXwC^_OhMPW=BDtoSF93$OV?29$`mMtxr zRKfb(AES;@-74RaAZ438Qt}py!GM~aN1o7teO*TqJ~Dq9rYk-GaeP8Gt%INAt? zn;v+)-kvqSo{F%Kt%W?=IW;16t~=DgCvWG5UcvY|T)KE(UZU;uvaIJ3M~n=!J@*DJ z&<5C+2pAmq>)5r@HD!#n%*bA26Fj)&zO&u5aJTD$SkmK}zbvBY>j?M#sLL$Qo&AsG zM^%ZDz=|Mv?*&=X$Q_Ta3<4g;Y@7ZTO2Oq+A`jcUDLa0fxU9v=YSQ=XR+AR6qqRhu z2zULhBU+{S6Ak9l$j39mc29Pi7AGp4Qv-T+0)H3+r$qk?)vM7SRk@xSzv_Q9jFFwc zf>?apmOW-Jm}W#dD&0cs|65&a<1nDMD_Brt?(Q=cYJt1g!(q9B<<@pDJzG}EMIut! zRjo_J8;h(xv?`sH3$0}jy4XY`qph0qkm97``$GCq4@h>M2)*n9=6e{*P)8KXKBefw zSPv=zf1m@%M_$)t5sxi|6L$%#ledQoKtu=EO-Wa@y757a^Q5eR8-9oTZ_-Ce)p0m( z)aH>w_lJ%*(va2qIxP=;w?bP!!8Gmc4d3eccL(gBdO5RV6l?5n;lS#u8xwICMP4Fpqj4 z$7bzDY2eOQZ)W4ncHmw=aaJbe8<=#*ZXqH}&mU3v zT=B`yqkCgUTQ9;ri}o;(U}Ws@C z*R3dRF2(O&ODiql|Fw1+zN=>m3J?R%pHh0Xe=l2<3t?tOP8>5LUw=HyjJ7;r=i1neky8UwrkT-d{7RaN03A5Fxfh6A)^hl5ScYn`z z(MIUhwdcgn(cp^tw@3Lob)0*2`vbWxE!y`ZO3d>nhSA7(b`Clld>{$;F4w1_1KnOj z?Y>+kwg7SJXy&VZok$c`XT!yCL-U$maed>FLkhKE%How!2lW zU{j9y=hGM^Aw8-Q`?uR5Oc2KWnA5Qj&iKN82>Apxf=dNj98LKVyoE`%mF5h+L{Cp5 zz9ZnDY7%nk;n?Y>{G$J6HQOj;sE6zhL!~Yq9Y4%!|3TZ#fUgw2qHw8HZ^xzZ7&AxM z+kYNQE&pCsg7=&M%CtiM*4XAy(w;o@DSoQ{_*koPccDSIrBdR*4QbXYNV~tOUZJu^ z8#YUN{bg%h4PFb5z0r=+c36KbF}7vuWae6&uAKlrrj2VchTE02hIN5p#O7T*UgsuPsN{ltu$Nm#u*9Czbc65#8W~d}-4-sQ#CRZuwBR z)r&)1S(UWbES}28jN2Vium@dHQi4dd2EYZWOG1ow-iin;nYxTO!?iZ7{ArZ?_2o4391fCH91#$GV$TkWbv%dr@(P}_Y_bQrs4a=LNiKM=+6xKZG zeQQq=7lJet=w00`9JvT=jH5&e*0VF8D&h2;3H;`McZ$87{5p+YT@OxM7JYS7ewz46 z*B3t*2VKdnO#1k~ko;Ear~lOCGMJqFmCMO!w~=LUZPZO3K^Iu#d&x4qu3G z>y1@4}c@2j|!(AURFCVhXG6 ztrV(5QBs0_hCOu}p!)(l=pDy|uH5;t7=PfoL(_(B+rtP+0uSwyK50yBl)6s9OSqdTGmQcg+@$f zUqs3~ZGl3-b*+!b0oT!`73j{)Eykc1p2L0>C~TMFnm&Ix^1|>?{uE?`aHQpOR|rZe z>WJPyWxh>Q{Kh(rhjjKw6lmXi#8P@5usN0f4i}@`*k}d(snVh9nsqumb$tTwmvh9o zJ=9G(#zqI`9;q3U{`k(0^cLwl4A7Q}S}Xk>7Dp^UDr+DqE?o`I)|3zE5Pr8BRY;nY zme~6c|3%sxd{2IHBEg7<*+Xa*_X@CXy#Ayj=_|x43kMiy_5NNW8pd@KI6HVuxvn`1 z9l|JnRk~nSI}(3B)0+FzyMKBl6YT?vC4JB4CAQ?6-UZI&t{<~T-|0JnzP;Jr+A8I&16#2Lw)9~@>Ed_{aHgBOV4-2 zTfNA`5&jzuVSYZsJmvWaed*Nwmoa;A_*+R#cSEFg4f%RKDyqmjdErcRlgZkl{+=PBI?D~EdN7@R^W3D!$id>zU zWkvKzcB_K-{ZartuT3PN9e3vWW3qZNoo z4X%IDK`P6dQ?#B@l0ETMsbH&rN-Hhkt-qT=8o>M7FW1w@`PaeS$yT}s7rUi^mmJeW zn=cx(v#&;(MqagbP=5Sksd9RXW;?(sx?Wk^=Qa2+>~niV+lTi(ae_tO1G6(V1qUas ziGUP{O$YeiQ|ANA7C-9`F7Bmk9cU?F+#Xz8_5Jnu9E~qjj#KXHj@^I&_4M(=FQHpu z#PBV-_dZj(X-(jekG%fY!_99Sy6hLG^x%qx#E^P?lCaR|d~8Hkd#XX(!~>7;Cu>)9 zFlc(YHX*`$rzcx4YtP%P6pRKpfC7wU3^^_2mFv-EQW3g@X|TLB;5IFrx#LUG18-e6oazJ6wJo&X_Kd}e=4 z{`i4jsW0u4I{W@h7aOJ}`)PQW^{j#!M?+*rX{*Mp-TIHpVQQUtM_%l-9_iWMPZw=q zjFGCi*eKZ;z3-$kDKb+xp1SVLQsTVqyxU^wZLc)$kTmPfeE3j-bUCq=e60DU{tdm2 z#{|^;uAFSVFrkOETb+8+W?Ya!7QBD#T^?%u0{U25?emXE7MS->+QV=Xr0h*?k241c z?_+#s3i`Zi1REzmN_|2IHN%Yip+)MMmo3r1KCT@%;}P?3FB++eJsOl-DL-5i~G-?a;#X$Ja znx7?vJ^`TpoPEB4C$wuS|1*CE)yG-Sd-58>%ZR0c%U7r(%9&;3@*JUG7_=sZnI!B0 zd`u-`RX1l%xyqt7UES~l6VyD#Ep7?a_oi^+%N2heL9^72^43T_=oW#n_bRODb0WRQ=A>E&j~C z=DmUb@*z5nqVA`rO3k#{Mo){cV)TYHD=B~m+BkoylR-;gMNb$*C}p-|?rJseJTUe` zMEY^`wBNfc^AC}W@<(pr`25 z)me(>9MS|IJ9G9j{hz8Ydy8*c=RIqmhT&sSS36100FP_71XXTGF~!}qBYaHH#3s03 z|Lh4}Y;H%;_VI!8(BC4={m)?+BAb?*25z;527*jm%RQUU5f%AA>Wg5juv$W&+cTBc zt{VReJ*>rb;!m1W_ezfxaQ9qjQ0t4{hWKn3iwBtju*9s=x6^h%{!5$#)vMT(;+~{s zO&NRXTol@-bnr?jU5fre9kpJZKzX}V{KP>kpye0S)2KDud^u(GjE!U7=A+~ivZ)uU zh^&97(kSq8sr3yL0D5NG82wal=EZTN4%cTqy7Hta2NPO1Hyqg?JHwhfHIRD8j7ITC z7wmW4f$M2N;n4i(HidX2nd(@6SQq^K=vEYyc)AX%dus3zTzH%7m2nw{M#%qLI0VGN zybG44qNk*YibCO8x|XWe*iAj5=ta+X_fK~M+u#tgdXxXwjP(_6W@NRAcDAekVl?k| z5jc#um6?dyIUhowwC|-7qC1t*Bl{8C+6&s}C?ylQT{(k>`s6ub&=m;f>CSw`kQ)&q z+D(r>e< z!dRK;%l$8-m7V7{Pk$m#7KOg!^!FXMu@_^-1;bu>CDhpAVSMNo_ZuWuI*1}L( zQ(csNe$Pwa=+&2db;b+^p;XkPyZu<-p9^gYnO68XCzysjUL0^r!@H+DeoF`S-(OSd z+o+~&mKmtNjF{0UPxVc;WaX5LGmoHu+<091vTd5u(x%QZjaoAyu1~9(N&7p@1^HuT zvJ|u1Az$0f=kf>v;*;-kesiRhS+3}9cqSq;3C-S=qpRDO12+vfg-0LBPM9@6rKH_7 zzMjzZ>*?{9XipNyLHta8ZBFGu$9H+0W&mXR_{kz*LdQ#n z0F8GIownQu475OLmKNvSnZJlu;_jnzRNc^u{!U@r&XfebUl02@Eg(4>8QCR;*b<|; zpwMml6ZLY$qJ{sw<~o<^?#_%Ap^fqHPlB)E?=v*?Y&6>}@>cB2A#%6u0pd8))3O<6 z)m>p2!w<@B2-kc|_VFk!(2g$lk5>LeN1@-9Eg1#4*V{fRva}Jc`sT|%6cQG#<=k@g z!=1Tk#G-ANN0r2f&b`TpF|R`{?>>eP7Z!9=HkYL)K(tkUK( zq}i&Yk=O8rM{|Y=yFTzi@5ZxMKKL-XQ1$4Cf4n^N3!AXxSbAA_SRv%Z*vaH1uRj+9yG0ZH^%I-k;(Qmb zXfzrbQ9phoM&qb~Yj*1LD%2kmsdh{3ZSe~=KQF3_HnH-Q+srPm%?cGAdd9a4#hQ%2 zAPD-qSqlMEI~}e@SkCM88&2)dC+bXDJo}njI2())D=P6MeEY97X=iTpkq4APPwY?g zwVraNgM0Q>4Z{p^{wJyGg8)w-CflNo3tI+%JtA7$@2n(=Wa(@ncE9L=()79UWN>x9 z@LzSbtGLK75n47qc~l)vzT(4DNbVr5O+9F{3hOaHXxyV;J&_Lbb0SL!+KJ4o_wD=K zh5rJ0_i{Z0+<3lNUZ})59y2f+75nt~S6`T?g%PozeyvwuXBA79EJ26VFKtlm8P%d6 zaGsi7D5KlaXC|PbISTLc`k=73n0{}^;Ylc$T9>Mpr(QMZ`xI8*Z4(ZpQ7)x=Y3y^w z?z3p&ZHG3!GwV<-3kIQ#Hp;0^B~W%LPWy|DMb@H_N%Rw6E0@A4Z{5Ec2pd^E9^18{K?l6si zhF?OtSQ?HcC5(Wk<^gON@X8oV3%-s*$zrR6HCuhO3}lzN8|4!S%$q_eD{~ zBr5r&@U@5Xp%3XL(_a^!c1}YqLB2}2k~8atCAnV~gy&KWZNKuNzG+3h#Ix!5g|e7$ z#E&Gy`tZ!-Q;&J3`%#7;ts>3(A#WP5mCg#sADXweou_EF#iPZ;MnZ4Ik_3&52sHU5 z;gS#`s=zShf>tr`j8Ff7?hdH06ta1@a;yzt(gZ*11IKxovt7NMdh7AtC%NGE?^8*P z0i0n!2V3K#R)51@ZLK?qeft$N0(UPSeqQWjBv!5HMw;FK@hPVqf(-pt4y{jN1SUEl z(*86nK>86)U|ZL|K%m`=!s;||L$){NQ#oBTWb}{Dt#F3Coeo7ybf}Me3WIU%`i~ne zxfnvOBmhmS)!FnJf?EAnh#XvR*OPHgzq%i;iTa2lsOP_==D`D+o_@PC`!MZjILilm z0(Ox`%w)p-z^hINfab*}QR_G7!V=pbj8sBl4wU}F%RR&7*Co3f>oe&E$$tD5BTm;) z0oPYJPsODN-%Z)exleeSz|sv^rxbVgE~*oN$moce*a57$RE?Ak%V_nR2AImgP4~7e zK-&q2>U0RQo>=UvMo6C`Pcb18&8vqU8`l8-?tMUvJfm>F?R!P|wK*pP9?Z+T1aKJf zf%im;B~R+fZ>i4gvl>y(5VDb1<*@ zP@#%T!R^WKhn|cTwDJFRMxt|d_v2yB+giw7jBB?WdFqvX=$4#pm1nI;rJ&0Ui+E>u zv)h+_ElT7CO($a1k_3$BHP1AhX7or$+z`#UL>KNwSLaRKbK*!VBz!dut=g>&XO|iO zE6RITgq7UKoqHGSFE|A5CnMHOMfJ|HvXjRk{i3cKHvMph%3TuHXZTU87+_ZPih7Q) z%LYrrw3+BYMcP{-w3ONEe{}3P`g`^*0i8=226a%h35Zfa+E{oN^agS^q0M?qfT$Uu z8c-o58cu3U5Yfs!7)rJtSeeSdmXMEb?r&ZgU69&v`Ao-uZ}9Y*tUhiVEKQE?ALzN? zJ=(Q5s|M%QmujV}hq^-P2L3hzKY0(wm4t{Z6US@)_5>|{W;)h!U6Qz-P)H?TW;GZEzca#|dGS=0ro9>X8miLw zjdRmM_F5*qb@%xkTZ|PI2#X3YG@S>PN6J8%JETNI- zGgIzNR|na>WTX=!Mbj=ET@Te^-6k+h^#SySjrcF83Ns8qP@>t%SM`48RPO#>wXT;( ze@7)v6XxD}${(UhH^5;IMGf-IOARZuiTZ>u66eS)YA33-9RK2s;LTccv?NLaN{4S4 z_Imtx?6b#(xv^@B{R5%Q)|>}4K!pFzekU~TV+y#A)S+Ug2~ACkG~R8Uu~|@Dy#^@i z9|A9tS60*K6WPHYW4{wd*wJPXYKeW&-87>mGW(Jx=)03!$U-jGuO!eWT?8~7mxg?N zD=){#4EN{g;ZWoamnvaAX_nG%R`csBm|?1KzH zZSI9N=}dAeuW4E@_lZuq(38gOc5`QQrx?9uH~tRefp5DNMwOuzp+X~C`-=JSD9Yu3 z-&Njw008Ys@U8h-m%yT2+;+@k$GpN2&s@H%_wmH! zvD0(b!**?QAHYsZ0MBqPqPkDc;BxNdZDXPAl)0bd`!%VHS95~%YXHr{L@JfemMxyV z7Y_I#0nGlZ`%)f58ts8UdEnr4mP6Fhtx}e;bb8j~*{V=vG2vHBcG`x=e2m+jnaxmV z$31f~e($Re>oz6_m`jd7W5icUp#-&v${0PYLj1B2&u}Gk(GcX}cHjL_bx<1i_s=Ht zBIt07Y#>7rBN}W0`t_Ox)oCYP&z^f+;BHv2UKxCTUj*_}J5QVIC8ytdXNw|RN#_Ie z-Mh^C+L8a>8e;z8)ZbkF$u^PLul$q?g*ccUv35!?`jjJx8+&ajEh!r-{@wFs1f}xeDw42 zw|Bb?(wS}CNlGJLKDM|n*Te)B48icj(B}`H{WVCP(v+3)#Bi?Mx!-F#yP)hb9e1AL2>v`>*#OC76wni)D{zyEB^#qVui z_tgmeN7kOR`sGlo9DIkOcBKBEx6n^-|D9w$GOPQ6j(tV(=@fs0pj_2UWH`Kq#{=hX z@{Z(Sj_>#Sa#f_~-?~2{)tt2q_PhZ~_LiE4(EJ1yuNP_($Rnk#&dJw5_#Pw}9$5Q;O;HcL4KMbJa|fdKP*YSR)=V0=g@n=@wVE zF%4v_K0x$=m|O{JW-#fa$T}ZwAIz?!0P!lPOLY-XUq!Oj7PZ5wUi_5+Xq4o9w6T@t zw1Ju7{;Kni=%D-;3P`$AO!c*8Ve0tjLPcDK%pO|ag|*yTl|Ey$Dx8FE<-RPA4pX76 zsyFp=i;@3G9Oe98EDy*E-ng#aN?q2E2D>U8FG+kFw)S!wIP>@y`xE!G%VyDA*Nu5Y zX)_d8U#kSVy||4=m)?UuS&@2}KBG-{}k%E>oIIy?Tf< z=8T~_1G+!ISx4I38Y1<8V#;F}m6SOdSq%6jg<`=NhM;d*9EZufg0jTLaO+R0mMMso zA%xpqL=8086WSAuaVh&c4dEUcsU}E#?Ax zJ5)T3TeXj>-}DNZKpHAsx~4YM(Ml^2;ub)keN`YGiyK4Gg~+rF%CF3{=!F`WJ=9;h znK*bDgzqf~{NjAc5CYe;i40V#y;)U22K(Wh@!QYVUI`@w>MqkO{;VqlbEORubswO1 z#H!+_BDbY;R$)kMi zFe_vkcLpi@*K_pquI7uv8^p*4A7FQtMiWfq?8ZO7FT>(`L#|ih@!$J+zw<7Bu8EdG z8M>qnu-{uLGS^c=Klo5onUt#wlOykh-H-%8XarFB`b@a3h>1Un^m{0)5=snPFT1kW z;c9s_If2(EXgk;}-MG_Gzw}`i;L4m0%RXQZumx<~FqVU-`^3b^)xrd-n1+Rg4`08D z%mfTm0SmxWM0&(IR|nlSt&S7EB7f%xByiija&jf7{aIm6-#Dh539nZ#Vv^js00O{?fo*bT^K-B&28OdguV|&g5+|OOKRX z^qlxDU?E#jjbRro_iytQUig`V)zBBagpZ+yafZhF1l+CT10;hKXp<8s)dM7LX4$3A zy^pF%pqPn2sr}Q1>7C<0)3g2oiE$T*b23kJAJ#4`I)Ys0s#GbZQ^EOfHyYo|L~OZA z5F%kk(Id%4frk-+I>7&xLhuIXr)q=rFYHs)GkI!!`r)~uufAKkeh;4c9r_@*K|8O!MfkWogOujj=p*%F2x#crNA;U#GK%>ij zww>%{dwTDLEYeB1TdEy1fQxJchQ6%HjgUunPkkP{|EyD_ikQwKA z9a7|WU{r7l{SCin{m6;nDXo3z=Tt|<_!TFRRH1lwtojUw3SsF#XM%X6uk6_=T-m-YvivW zgr!$g)OX$~xE!G8VeHwZ=Y`CYch0UcHW7;@;fmKW&k<5b)YouCUT3*nJXT(Z+F($s zp;>)a#^&;x_~U01qj?Ox*u4cE2_pux0k+?y@;9%FRHY7U*?EZOtw+N+xp+#BY=6u3 zywZ)6*_;vj;LCz>qbJf{Ny{UK@FRccbphlHw{Myx?y8!26bMJwr2!)T=l~=@9=HNK zKs1a*JJPL2P$tD{LChZehbl*AG>JR0|NKQC3HYD}s1uH${cDq3(*FdmbPUp_Qg+>X z5gWPH2heMgfgzNgdB?r0X-5ckGZ}#im)tg=EO^c&7p^1fOi|8buOPzZV`XryI3g^} zFoeV?L3o*;GN;~eO|&+@ds4ooiE%hLT!MsFKO2JlDx*G4e*iENlTs;o!(dWp1HHbeV z217_{3+Cl?mc1zK!r1rOjX#%`@3Rq-^;l2;F9;L&?CCmvli>R8`VXwfdZl_*4oW+s zP`iuw2kt-sjSApJuN~x2ASnR-E}(u&@^je0;IE@2h7>{o%g=*H6RN-mr4<}SnSkTG z000uHNkl9h{&j)#0|wUn6%6t!cY6T`$#;3|<@MEIQUEh5V2dTm>hj!= zQvpf<>okuLR4w2gAqe=NnzaaXJ&#Ny-`Yl|E4UQS$134*2># z0I((lEd%C=pi=;8k*cS6q?v%9q&|~C7msVquIfM~zJ?2D`C+oSVy!5Ww|9iVM_zNf zX_(gAL+z0YfJs0-FH9Mez}1{HIn={=lXTVNO+oHY8E+U?SWtiC5C68w?Ey!LD!O49+DgkuA6Tl$~&%>-G zHKYqR5kLYw=O-1ALICIS`5Kk~N*oUG3H*TXmkU)`C9tRGIpncJ(rymb9-;{DssJ`@ z2-Z;trShNu@@@g2-;pcm(*i%k1A_a&(w^futyBF1mJg5tUv2Tbe=T#8-vOTg)w%Qd z9SK14wwMxtlc+P`2i9IPoin)Di!%5U;FSy*)K_HiJHh~#grLNu?gfluez!j`T6pU9o%hoCSG|AbX$;N4QLnGwU!{546JU?8M8WMi z^1$F)r($#rSIN^kb4n^5HGMbm0}mj8!N)=D;h6;ZHK2Y1%XvxyN(}sGyPQJkth~B@ z3CIs#|IJx}nvXTEH@cone${%;$8e2(GIef}nH0j^7Lb%XygD0Ia&ze^)0FfC~va z0!W~6vR}cZpyxaWE=z2U(Njo}_6-#LN$#s6(QztCq&Z5YfF^xc?+-jT1aRg;0ez;ZFxZa;hW3zq-w=xXXPAsi&1_e+!jfF%L43eY_c zPOCCF%emUjVsv0v&p!$U)T3Xq1Qj2h0#G^6@qXH{b6!7@4yy+RKZhbpR8IZqyy<+W z^M`^Tcpw4n2n7nTb*>7a>MPiJ7u{I_)9uW<3B6$X9YNJAMesKrd3V1+&61yY`?(Ik z38=;6;(zxBfW1Dj^!S|$7`+lqbK&NJ%be$Pz^^j(o(@C`G6(+T&0RRa^>kB^6~vY5 zbe^9{gT1^{13DKvcYvS!r`SUkz{XGjSdxI2^SLfej`!VU)^+at05%1AJxtx)t2Q~W z>Tp{O>^C{@Ey60tycGxD0-$23VrA*B*I& z-7iR+hFpig2~pxw<)W&_sU4m(K91BDfX8@$;B!I%M*@i$lmvJYR;Wzdk(tZNR`hN=OM`JMb%;JY7~ zG#`^WveZ21wK}<*z|Wv@lHZjn2$M&5p@CByqpu8S6iM>>X}(kqsJhbh!|f>wVB+rv zFu)khzuB$N}B+d00 z=o(sSxwFde7NvT81v(^8fe$%nV7jy(RS}Fp+-=|`Nv{N;;CHWx75sG_&z+Kko9C51 z()$CS`xbynfIU8w0VtniVkU$U30$Ndz>@>_qtf+&-Cz1)56_C>Ds{^1SADL-ia^J4 zrZ#{zG4Nc>cz(ABz}67};H7LG&!$cn2At`-LCd)^{6zGvMd3EBKWp z6Q~#Yj|M;RL;?t*Kt}><9P{g|F9i+P;Ym-g%F1}L9!(u3h*AYA^t?+ug5aOOsXZ4)ICALJ28SD<_BwS_Y0m4E zm8PF&PgMXLNr2Bm`Ah;Vxi45RVt#FwynbD)1mPAxs}BCv6 zu3JT9;Vssp6+jg!hjfy<`ta&LCoBnsL8q?=iySyi;mtkm zFYxgk81!7On}NI^04&LXi9lsP&q=qyT$uvD@-9-w%0SOYn`i2Y6QI>g>*k^6JDKl( zUAMcR8%NEBDkC>HZZ0(q;CbK&K4}kNa#OIZTdau$)YJ1`fRaS={N=q_jp&C;tUmbT zpDMdb854oV^q@N2-y$9j2Y^fhkUTy|I9D&FbIt(Za`kWy_zW^&9h)m5xZQTQ6YoL= z_WHp3O@SjArxuh5YJB{CfF3*Wr+Ho2Gr|t(%bVl3n~7G`ZHPo^DorOwD*v@DC;eTBqQnCH zFz^FkWDj8S^&m@gex+9On?b=l@St+u7pT};_Xzl&8bhB#y6Q4`IdtGYY8azqtVP5U zzWU`z4pxC;rYjw2jbSNr1ffFB5c;EO1LvJFZA zSib94@8~?DlhG%st2^`h3q8)GA47eoG&Iz3>cd^*#-Oh-l^{TMA^9`r_79W{bn{E| z9U%bg4L0{$jX?;kJ$wQ)s|5ApY41S$oruegN$2X@fFJl`1OQBt0H4p0fC8$nV<2li z1~p56-IzN_xwc>Vde0PkC;@5zMnBzL&CDy17wrY>MXcU0g#y6(i$7HfN(Ne{z;>?) zwSAWF@6Gjf-zwKoK*u%$zs}Q_1V8Wv2>{qk0xl*7*1zrNysGo;F!`Lv`vcCScN>qY z0vt&tB~X3fqa!;vKQ4c`NHLcGl~ymxX$KnjWu1$^75KWD=txE7yS}?sI5J z08>ZP?)=MuANZmK0Bj}!AjiZCpd_o4TzC0zracBbu-sqa`s&eHNoYQwYY2l4X(a(* zy)YdyXc*uKA<2AT4JuUwb&s?+z$D^H~ z2~@fBq$z~w7B=(%e{m5CAu%(XfP9pGI^P^O^fd{l+NANVE&07Mc1G7%(+tYC6#fa|@P`rR&l7fL8GX$as{f?ku9 z4B*F`=4Z4h)8gkc1&x+fzKgq)S09y+HVxf9fHm;B%@oR{u>$M)v-A0t!4G^R0s!tx z0-YLg@;`ZeZYzDQr%HnJxE;P;`;}xr-=B$s%Us&oEFp9N;c`wpZ_k9FYQoiZal7;E zdOf7>Rj29)2kAPe6r7}Y;SAdr1&~Y^vi_}wB&VH6b%0jT=_|rIcKZNH%BTCdzNYcG zv8MBH1N^dYO8~%4A;BomdE->(&Qm67t-I_g z?IgeErMe0DXxacg@U?$h=FS0pKkx(JoB)8ELxMVX#NdcvqzWK)oy<@1z4PYoevT}3 ztjAoQL|+W#8KulDT;0N6wVmFJ|f0%;2o zaDD=@E9ZQzqkZiZi?*ZnUA|gcz#}QVMHSRB0bMG->|&Z{}W!08&|}21W}v0e?mwJ89mLK?mef{qCL!Wo*C7 z>$CkR0s!u&27oN*xt`Whkh^*vNJr|y^*MbAq&Bk`FtzZdtc&ZDhwVPXN$of#knXo- zQ}5S#eYPJ*0A-&{1TMA*ytH@c-R^#;PPFf6@;XbNQuACodD^p_tJih=0@_}Z^Dp4< zv>!_VKuZEZFP~kV&f9kzdk&cH3&5@~T{xg^cf>bK1hc>&?FGyd#OPYLfj6_)a7zfY zvY!Ti;D-}H*^O#|>$#1)QH(z0?bH51*9S*7uKp;mY?ntw2635-uV(=3KtAi?Q%wKz z`eL(RiG6Ep9$@kk-J-2>-Hg)BC;me1jQoxJ1^@ik+&k6N-;8tjKGSvja}GZJ8U#I}s25JBp z?*(XC+RU5XxSP4YYh&wplFfQ~HRqk3cb@@d%A4tn>qn~lpZzA_m;Gu2D0>1CaK4tM z5M#@3{^%`0QhK8WzqMRFZUX8^qHj%?y@0>bUIajPXCeT`zp$g%cI1)nbIU%!tc}8^ zRIdZ%mVJgUjvw78+HVGa*^2;--9QAcjPv#=hSB|;ZT%YnTSJ4bfJ^o3ntNaO0{*1E z2%xbEA^>{u-{Q@;kj$2keA2yvxj28eZU@e;?Gfm=-tRwq0e@{T0+_Hn5P{3vdah;n z0Q|_d0Ol4#m?_`d?tk_Iezq3@Y_Lfp0LFU*TGnm&jq0TOkF*&;&46_l&@;?R> zy$E2Z-I)xw)Z_YF0e{PDz?p0I5W{R7{{sC3>_q@K+6E#h+w$?%v!wo3yJu*S>Td$_ z3;6r&MF4lS9b|B;7yn8MpsstQdRW*C^taoK03KvJ$$*brulp?93&L)grdlA4V*qsRB9`(0=2&C8DtKPz1fPbF72;d9Y6A0m1 z_5%DjGyQ+`FZ%>b0BDFI0p=^d1^CZiK>vF7B7iS!9bf`EbO3vl%wO0G=!778h~C*HY8EL(jouc?>Sgs{MT_26?=?{t z%S!Z~*Z;%&Vb1+>=bV`{&%JZ!dG18&>8RbJVxa;60JqeitGoaJfSmsu6l4GZ;9fWe zd1E{R3{BuFc0RV=PM&Zl4|f3IZPw!+7!^{Kj)bV+_ zlk89Y-455+d}<=jy(}p6#lB+zfD@puqHOp!Yd3h;mmo7NnBa?Xalb;%Lv=H=I3yNW zLxCFJSs}ZgB&=Y9`ZSYd9J$(Jq5>zjHw4H(q?Y0MqzzWFZ(uE2L^ieEr`^ znAT;`&)z3_YTru;{l3-`VDa(LKYUD!H=Y^s>MOGYz6qF8k2MV9ERZ?1f9XR#2wEd- z49dC_KL{9_82F;gId|jgyarfN-{DSHZxaJm1h#GS`&B2-nhcf^CV@Rg*=m&~R{JF1 zp7?|G;wk^gQoUtU;^q~nr{6ZOQft1|yr1MWMXI^uzz{!%uxFZT{#(FDr`>-b_M;cl zCP95KpU~z+VqF@L;xp1DMND@8i|XZ-j}>VewLJr|6NdxMJ23JAyT_(a;f+ zeB{8}Im4T%Au3IcX}ycUSEDpcix+uOM_egu3lEGd3LKb*h0HY8rT0m`3h4rV(gm^K zlB>y01Qh!GvbWXciw?D4S5Hl3_H!XoU_=|xd7fd|r$4?*btTOU>7$SMzOM!z(V3DM zFug}IVgGUR&zZnX^qi;*VJ?lkQ7tgfRw4-FOIxcN-DBaRDoxM%4m3)8#s%C|@+r4ephc~LQ**E{ILq-CHw_M><{b9_1w)q&ikeEP4SgItNc(G1Nr z&>O6w^c!Gnm@@7lTX4*C#jnsM4&rp^!PmIHS!1x^_8>9h%4J3k*BV2MM5CZ@lYn1mlsXl4CXTSOWoV{!^(T1V=jx84%Gl7 zz&7v#sEz7RR}an**jU#a|3c~V zwbCJ9*hn0CE_qEhPX0x6*r!r-6u2=h-bL3&qi<<)Yi_EKpC85GUGGQ_;J0>UnF)&n zr<3@W3cvDGRR2fv_1{_#XoL}%?bAyD^i&u54jZYB5BETW`ES=Is8^g-l!J&YSI4&>Q@HFB%2JEu&0rK zg>rT;C_i880qTJja!BR+4G)b2NMr6)I)()7Z=}t; z?rc(QndgrU3C)mD;5IR{hiJg2^2&G@0!qgO)b(zHrc5*% z7heqdN{>C-)`1}Hrj1v#J0~wqPbMOJ*7@{&07Yk}^>dCFs+_xP?2Kjf7CUXe`oj$tecFM1tPxlG zN)w@YA=dL%rA;%?2)Mn6G!_!RxqtUhWfbXQo6p^*3ErNJF$39_VUizxF`8Gws9rze zsj{f4$DSwm20~L(v~_!h3$lDBYcY4YG+l8hSo6kmuOInWkDK5@>e>J$%G6$cN`cun zZ8FE7E;PD}@DA7EwG^A{*Nk|{rmG$r8&_*1#~$qNDS94{q%#j<-9}I5zb#Ub1*rQE`H| z^wwIBqpNSg$_1a3oW9jOFLr@HLGZ44MQ=gORy=neg&NsdQ}~vAT5I$HEHl1uxdjM* zc*-BHZ~gX8AStyS&&xPa9E^8G!$Y|#a5HTEQKURiljIg`XDTiXfAR}Y{PFh~`gs?+aPPPH98fA<7Y;7tq&;Ang7=%m;X61wKL1`8cYCCep8 z#+gM&hRRev-y?XwBIg!~K?#yy`i|CGuvAWc_a)9o+~PqMXxcOBnQ9>&L?h4mPi0aI z%q~qa<6^77&QYZsZN-AC#Hzp#0Pk$TwQ37BMer*?0Ox5QDb=ak8fsuI+j0mLXZxRX z#ZGr<{8H2sdI0|;UG0c#tF3s<9VnYwn_EX4GYqgL`&q?S7u<7B49eeDTYm_buFjT>ytR?9VGiV}n*t(1famur;+FDsZaNj*{fa@^o_>akX*&AJ8=4 zR>`T#5it8$Qo*-W%N;iBQ`BxEgB`cDkyG2Rz zE3bx`=RF#;uA(-Rycz>SAG2!eHs4+K+CnqOo83eIho;tJ^oq3QY4cSU1T;rI4mY#; z&-7DRJeUpd+erA+Ty0?!8owF`{3>!(k$xM*F(m-oG{4VysV$6)N^)4yPdRufm2aS@ zi7^XcDR9%NpoCX2D#1~fJ<5DXeVc}=Fa@jSMkqqj_;Oyvu4B68(!u9!cmvhn(26&( z6M3PVfA&`F%p>U}%#Fh^@HFJivay-+unvNdcPIZTdwqSeH*QS&RRpX=|F4ugmi+4T70%$kc_HY~ZP+QPf`dOcge#;}`<-iRlJ==M$#D6db zCAcJ*XVC_CHtUW zgy2KA_MG=0ARJu<8a<5<#keAHN5JQ|(SCR<9OMEH2%tNi7!}K+4a(A7UEpGj@&c|i zx+)KaCfoqYH1=it8fv9o^`KT{Qh1g-SDofL4{7#d*!U0O?%#l~QCptp+S@O3{co=? zYhRr|%mZyvL~OGkE*UI)DHbiob{T`dimZl3a_hZdwG{eMsk(xO_Joy`g}tw4$G_N$ zW4w+kHh7hYeK)7tlCs2AX{Rx#R^o$9MDhW($(4RY;AVs$-dLta*L5$~PquP=u8eOR z1sTv(gX8pPwbV*uQY z&8-m@vQw&AN&GDw+ZUY{3%*lcfegL4`mh7@yMaD@`DN>I`Y}f**~Z(71`AX02(H}5 z+Lqxbb$2Cp)RYTnM(8KR0>*d&!~;$ayR*zy-?VcU zB6+3RLPZhG0$8L}2!K<2+#~-Tr~Jf(0LYKRi*CxKZoS0#Cy4ily)f?=%T5}e9<4pj z3}fck{g2T=I+7V~zSG;Kl(5t&(Ixs;f9hw@gUqpiAC>_f!QJZ(!FvsM)_m2a*fS9t zLuOR{!iGe?Ea#B!PFiv37+IU#Syr&5@hvUfS8(nAP--k!*>$bz1QHUR-vT9Z|Tlsa}OMW zyFiYUHySw#0$>{kc(;e=sEbVfXTq8bw;%ESWBzSKv9?C`5poZ=JytCwLT9Ha_p zKetC#ixOlp#*Yp@Kc?W;d;L967n?;W5P=o=eT34z_?`r?Pg>%lQVI~V|LLmE$Wuwm zWk>$QaUf=t=`I*q{Shk#rXbF_24Z?>c zi>{7y)$l+HNmQVtz}s@kI`|*95>c;VJ@=50VR{UIdDoZL-+rm?cQ$YKP#zZJ^1V*|q3CLhGniM7Ap>~CLx z{`Du=$BLnCLbWsa&$X35=_1x0)v7nw6;R>!i37M=!v z@rE31wS~1?lPL5j-E(blClhQ7XTZB;%5-(XVwF_U}O(M&b#->#6-P3GxqMoAq-?PEqfY* zqJq8E+^MCcUxz)r6iAS~EeCVd3UnyYou+40Pa#UkLpH|W`{&oOVNo?(X>lo|QoI`m zSGisf?4Wa>BCm=RVo;f<`F>CYCDQC9obi!Ji(fzf#C77OY*Z|>{^Rd{>eJ(*WcR*1 z7#Rwc;EtALRXe`v_UgQ`vi*ttiGXvGxVHO|s6NV}eR`Wb7v+^BY{jT<8ci7_Q{KK} zY@pEiqGY^hGm>(vF4;~Nq7~v@zaV=8nvTfa}l9Yc4{5@_#_k~myhuzDX&@E!`I&wb5E4V9| zrg|05B2u^8Rv61dFHq^JWJs;ODu-PqB#KzRr%`3OC9pm|7n;nO4!TMF#R~)&X#p2vF)(# zYCBeyV)67nw-G}aT2}MZKJ#>p0_$$9onT)1d7Xd$*WrJMZEn`n zT_IFSr@ho=xM9PTMGp5GQ1ZoK75DCN0RwtS zhDywm82FV&*CgqYVDHuLvT_pimTR#o$>*y78)shoDb_01-z(!n?P8t#m_|>l{mt zdSa8(=4M94X8C8HN9eV+omo7pdt#v(#Qwfko1^=)!cS7dj1I(M+`*MGZX3|LLO=gD zSrUvCN9i|`XvG4>8e}$BL#QNm$QQz-z}3e*DuqtO;aAM0`KSv714KlM?7o77FS@5Q zXd& z2%*O^HG_G58!gw&LnGTiFy>yDSWTcFUD&W*-iw|$L;k#KizDdTfET)5OiEBJNti0c zPFy?GZEDAPAFX=B^O1PiJO8$oQB@* z9@B$jkoe}Xk$;uJ;@zRgI%%(+-c2-{|58q4=Bcj_j324~hF%i|WL(xml~mm(wQsGntlo! z@F#!HVq({gj*7SlTjaCMV|%*+$-(D@aCIyFw$bHz_sc5&wNVE#^&;Zsf6RAsVA9FJ zj~6eMdykT1jc?AtTKN-T#Af9cA`8zDEBug@ab@bhPxLcjcjI&Cy|$i4JBT9>KqEKN zL=UQKJrOE(QWF*vc8A-8+ZEl0u3{hYha^)9bc{B1H&yA|1FCHSLT2&7bw?tuv$x;n zQY8v!wpJqQPZx6%*94zSGHQ zsP}ymSO|VV#!jvQIdxRztNfIw=9cs;>gn_M3Y(F+8at;z{dsyHCDy z4z__$ueX#Uc((RZngEXE7hd!Wk3sV9#GsaM``-XsJ()$>lp2vf-c= zot^f`p3wNP%CNWFerdJff`nPhS<-ynylJ%q#wvW*fd8ZpBi@?x1?NUXd^MF$X%eoP z-{h@I(d9FbTjm3k4Szm+z;&Lu#%Gl0lE?64t~tuB(WJtr2a}3@eZOA52We*r{$3C0_;T;fJ?_R%PP0=}+P7>0$y6 zGNLS1IitaTGMi64*b$%*bjFDcEPi=-y?t zpBT4w00}Nd;mVSz+(~_=#64HS&m^IRhxX4v1-q5_!Hij{bR;wR&9;~0Wu$lda*GW`yl6~!XH6jc! zeWU39EvKtM1xU8Zj5N!oGqdAxZ@-CG&Nj}8P+i7n-ZU-~Qlw1e4U~>)|2>j%GDXDK z!gB7DGEU_KYQxmGo*Z{j_cuC50uinejM;&|NnI^MdWxKdy+!BB>!}=FR+t-ZYDBB# z+hblq&#lyv3z^?j^L@J9*DM*}_rNTRdd8}Zbe>J8GfTT`wD0dlEPS&E$h@Ye8w~5W zzjvTs#Yf6X%0aG5g{6(4r+C>aMv;FVp0x-3>i!8%v&ntN_MX_bMRQvP>m!Ji{WEzH z#29>39QZr`r27Tnr@u&2;h^f?ftC}k#=(=Qbx|cTpVhz-sg{+LUD*QgsC+c3{JvQw z^RWboNjQ;7JK0VN;2w^{o4>1d3)3<|Y`!tcOxU-_wQT5b4 z8--*e(J_cI^B3Faq$-m-F)lIyn~xnQo&|E(iEN}Gox47sE}t!USH3G+@>?CG-T#~B zbBWj5VpKnX=_aR|#EbXdW1Q=i7jk4qMqkwZ3|YvVhTKH9^?*$ltON1GI`A>sy;y35 z@XLOml|&o8t+Ws-r8$vpGX8mWe3_3eu*%1Y&;11MFF2jJz8LKmq(*AThnG6k?naCWad7E6}VI=Xa#HZ&4)kHG0IQ8#C_3d9+_Mc%2VZe5_ z(0NqmF6@s0Oh*F!tE<4{fkc#-mO#~AGA44>#njz^H=Z_hUO9=}#)Df8@7SoluS&0Q zcsf+#D&+6}F#qnHngWY_Eb*O0iLYw>8vH%@_c`+hMb)I7AI4h*{5ukdr_!KO5rK!?Wo^+V z4_It2Znpi8MrGl+mA^tk2O;vnn}c8P@2W3icd097SU16lARVEHN&}dE>e|@(ZT+x~FzdTq6X0M;`*dzk8 zaXNl`?ss9+f{l)sr_G1L?47#}5s&Mm2C7SMZc4W|u;!eMroJQZOv!)uRfG+8OC>8l zIJbf{^AYsFTi&ev5_`drcjBAYpi@~Le#PCUPS4R`Va+nEpBm06RHWtI%oHTET*Ydy z+#m5muPFp_%003@Ga=|c|EnJt_NreKxq)lS39}?GQpbn5FJJ?$wmcTiubsDjT~iLm z%)NIJu1j^h_b`2#IxH@`R@P08Fe!7y!?jnh+9IsHZ-X;lD<&7_c0h?EhM3GYkvHh7 z`=mE2DW7_)UNH2|!tY5+)QnC~2((@>6sgnF>3es@FFL{w@wE7gCPt*GRMwdBRspT` z#bMd@#$Ha^S*7_J#R9g$JyEFnIJ59npu;kGaNNoaSMw9jvIi z&7h6Otf>#jjqawo0b{C3Qyxh#86>-qyn@;h>Ar8;!1^E&d#L+g(Nh7~^SwWRxJM$h zOp;Q5MJ6J9s+0Br!s>DG zgWbeK&tG&5wbP_Ni6+B{wg&;97TmogAX6XApdmSG$`62_Y+~PZiaa! zHrUEL{e240AiF$`<{cX2=Dt|7d6`YDF4L6EVNS4BbU1CLo!OmZVbVPrXtyknQxrnb zd`_pIH)I`F#i!9*nSrM-V`iA^g?708iMJf(jUZ66{7-$EDO^kMry_A`E57=U?08t^ zFOC{A%)qYH4^Fk{?#W+aQSOYphuDF})7U6*J_?b7XzD_9Wir zwhDc2E63yD+TIH>n6FhZ{&;kLydT9TJH!d@$G1`%p3%NJ-Md>{O1jYf@o?Cyq4m6I z#max~(}MITtQBGu{lO!GFTyOvq z^H-x3W#%~ra!i@}i-O0vl)bN|f^O`nrP;r48vy9%97fwprF%FlXp@ei!fO zzr*}o>)F=BCcQ}X)bV3K-U<$Xy-45jcNkghc?@5?I(@uBBIJbM-hs#zEXwV=i>i^U z#2>&7XQ@?cth>H(qIHW*jL#*1>CRkFs)PVXfp?~t<^R4gTk%^Kq;UR`H%4_g_lJag zj_dumvW(lw+g`*pcasJ3pgpL=c0leW6xU$x7djeYgBa7kk$?Sd#F#u=gl&W?2KnW+ zM{K|4U`HS*`#;-hL+Y+a=OW&ywd|O-p3pD0&m>$a6YUJK0cz__ge^AZO3l_zcKQx@ z=Ex53@0lu`6{23JT@}xcZ&Zxvx$4DaVW_9c1NNMvA-t94tj4L?-W?CYbAJzA3cff2 z9Y1+~+I~1ujkM|<)I;v(6^o>lMjs3zlCcQ&SThA38)?G}y2%!gW zhe%kXuL`1jL!r@2`Njyr-@I?gZy4%bW6yM;HtE+#9h{xhm=C20AE5}K`h2G%nFJXh z8FhB)<{ zu{|17m+3)FH4j#JmwY~R-!?D}i-NR@gD#ieH{M3L4IMuTi_xfMZhV~ryhSx68MZFe zBdLybb5P!gJm=8^eW!2N{+_O$=aqGbt}${4S!5{3$m-TKq2 z-KZqsh&%49kqSE~*j_8U$ujBHs;zN2i8Rd`L`jF8DKU`m^s-+&P z71<4QDCP^*CI30+Q3gKuy5TKeS!?*B`S@^_e7@ugoEEd(qN4;Oj)MC9`<@GT3fRD( z!eTEBSbiwWHedWnvQ@k6j065BE!*<0nXK}PzMRVOqrAjqtAc6^q}HxQrsHVo}u+LVu&<*>t6M6Q*v=j;l=4Wam&QJ z$3E=^-{8~3EvA}uADMlGMxk!LBt_XEUdt)ObmRKaxmmLl^KzY#Gb_W2%7g{&<@@Ol z=sd8xFkioS$U1w&b6l!<49;e;ntk^Iprc;+kv_Nd&Y5uYfqmX6^rkhxC!@O4z57OR zr6U0@dY)%?haAKcD1glzghnoQE(eOZhkDHc?{c6BFRgzP?}krACE1CN$riTt9Hq$6 zjrZ*F8QebMYKo$=W;+?3VbNqM6i9!qw8*XF@6=<2e9aj;TIk(&+?Ni9Ld?}NA@b~@ zxJ^H*>0j7w_G9EQMZEQ)`n0s(3pjLcIpD0 zaNWs7nu=92!AU0fk_;_D&VVdN9zPHmA~Zk!f_8flOnNEjq07K5?`Gh2L9V5mAiV< z`qtat;4hZuhW4F_J=$6B`@YkDiQm8sX|`id*@R>K`(NEl zF(NhA4%DZziAx@LNe)ZsGfchl8x5E3IrEM`Tlj?>!EPo-8zN zzbSNFsUqrR^Zn|v03E`$H`IXKDCjoC#EC<;nSf4@Ozy{MIG>>3kU*Ui&vkcIGgC8R zi*jS|3`U{_dwECmNup&6$|+~*WHt$Z%kqLMfxJ)CM`XxCFc8oiX{puzX&i%I==6lC z*Vi<-i1y|wKMxo0fF~_($~Q!oxV@}IfMvjvYN5kIGeIQ$F-j4LMW$#5$G(Wkf*7Zd zJU+}!X5`O<6wM1$_NK@y{?`6&s;*fPm)|qz3?M^d)kQP1YOt7a3-ZSHV)S(n$Qq`P z+uvk88UNb}Zcy;_74^*^ z!gUm=;A3?smha%h5>Z{DDq}t^wnTS@7?Y~7RG<2UfprB*?Vbr=X{$*=rlVWj&1OVI zdqZqumT?AaxZ65MR#k6%9wbxXmy?l#f9%F~tbgGgB?hg9Katuzc?$7wagGamE(TuQjB+zp<6FLEZuEvULe-y3RO6gj=t~=PxX}FQn=4xbX-e!-Q@|tn zb0IX?w7$jA7Z}@516YhgTKM~QxEJ!v&qEo7fH;cFdlchue8OdIo);_H@0(i<_3Hu) znK?6d3*SiBw8MRRLX|TTi5*U)$q9GDrwO=)!qN^PM}2#;-`RjS*~fqH_3X$Tjg$p% zWw2V4$ltEbE_{1?1gs0<`;Jl{Uh4V0!;Ib&3fyxd8yp9*hsLwc-+Q_#b)(4w-$?78 ze)-XK&5pD&BUd%uAo@#Tky8Cz_8#60UUe9fM^4Dv1FWxOnOdmEo>m z{3BxNF?_n+y{&E)0vijk|E;Vu>^5 z+YPc?q%c2JfW@bO88xr}$v)i-ki>2d4&`M&r{@oiUWx)|8;Z=skej?}8A=u?;(0(0 zSEk}iBckGLEK=nF84_e6FJP5pkMJpuy5nU*3~2M!mhtqqPU);`pB>zEXOR??0B<9a z<6QCE(7buOnF3qkh3@-)1GgH!m&15b8;e};pfZT+F+3NN#Z94^LhXCQHR31uR-&?>?u>U)759a&_k(@gUVR zXNt_789&`NhoE!@c9WY~w^d`Ul@@VdmR@^K=WJcVAr2A;3h$f#saUCT%2F3_=^0o+ z-}PUKZ;#&@*Uucy8uwrS2^0`*b^t@fLcUMJt7Q#iRXbk2+3^iakW4GHMLyrt@Wi{M zPZUhF(EeFCE4tEc)HPxn(n@y+#%_cQpKI_13QN?mX9a_#CiBIKsBgCyPmKVM-(N32 z)M_b9fspz+ggKYwI5E!0Y-@otZ(94~5+|=YVhU>@Zr>#iaI;>PCuuFVQaFrDs+s0X zzW^BtQ!|4xd2!wQ73QuCwNSt&)PS&qu(u6A`9g*u(GcX!UZeE2jNMEf<(1Ch zLO1SUMw1bU#_!nktQHp!D&3v(<$itP8TqDv+0|%^&jglPQO+3No@Ri&^FfJRyF=?B zamhV|Z)Iy^h-PK5?8B_A^jwkJ`bn=djQ#wcEB9ic^drca^O9q3)49PS`h5JzTps0? zz&Em!*qk}<94z7U*iE6t*u%OT=D9oK0(Z;)@(DTRApeo$fgL%M@UZ;HY1=tBBfqwZ zsdVfMi0+xmY|Vq1?cWEZ_W8b;h5jF7)is23N7WV~wUB6RDhqC+!(TA`rkQI}f+d~{ z=}d<*IujxS|ztd-&tmIElh(JiBOV@TX} z_#2e_l?o$xZ%lWYp`5CHw~wUiNw%f`)Zs^C63?Aoz`G|I((@f!mUKk{T3OI36K^(z73NS40{ z@>7<=Gx|-#+r`srrvi2HjiKhdf0`3_1>VIDR_C~f!{eqbB0w|UPsUBX2(BNBcT*$) zH~(%FIqGCoLrQ(q0<&c3zmmJ>s8&W3>%f{PYwjBY?az;o|JCQta&QJpPhyWHgl9B<*bhHl^Oiut0urQP%#d8!Cgw9C&bQSi zSK_dxK#LMC^N00kxK1I5w%1a`S}LL@lcxubBZIlBD-ZBlq2~1F-1=r|spdEaCy!ir z+&56rQTR4I%QN=Z(m%=4PEsCB_k;aV9vdh%hhCdT>;ZE}V~VJ2$3i96g|(No&R%UH z9rv2c%r6A2o~cs5MbKi0UhS$L#hFZLoe94)bSTUz+0)l;H(C%jw(nH2Pou?KM7u$A zn3G0xm%#zLBeFX7X@8aijAvS5y1ScFbE8M9-#ncBjs_Q27x!mH9y;S=&5LItos;>9 zL7j;7b8I`yt{jReU@Kd)tZFUha~0d3=Te@uE46OuYiEn_zUNczO#;~cW~psalPh7C z<6>ZAh#7p;aHDgIOTe~nwr9kk5FLVO$GEDxyW>7W?Fp7cO`1$d;#OLq-Tg20)ap47 zo*8afh+S~q`lD%oeuvrIQoiAGVt##(9?IT8AS{zolbwv zin%Ppsf5O{)berq%Yr71XKSMH&Omet2ee%(9XWAC9%5zmaIwqO=;>PUX!avT|AJJ-fF%i zAK`Vh&7zKQUJQzn{5SToOq}}TxX(U1Cu~TlUl8f%3QKoeQlA!MQusN82=5F3I<1Qj z@YuS3F{_iURAXhtfO}-{{9s@U>o=|Hb4NL6p&k=V#Oh3U8N!q@Q{(})4t^%ZO{|>q z1vtp9H(234DRbXp;wo;u&cQ85oUaBcVKzL-Td;1#KrE62gZ@p9+-3C8Abh}k??1;L zJo^vmxcT?DxSZ@?i1zuw&fO{kjH%nBB-6;3 ze>sqVi0el-H#F`;uW*^qwSsd^q*2}77B2Qr7v^Kr`@*`rXK^!eYkc7&*^TM@Mr~vd zP#a45#+C_?p;=+`_}Jp;u&`3FQ`R$gW? zfv;Eogm?}ou`kxDZ*TbP=Z_s(%y>QhnZPvk>SUZ;Cid@UvoGF>oiA4h4>*?K^c!7| z`h;y5$69?)PBZo03SLjpu-)9A(|5=n=uH2KabIVGv*h(Qyn7Rwcb)P+8SrUbHC zCn0h;#yZKQX{y3!O6)6<(wuQZLB;6$@i2eIGp&?+m+L{CetE>ZDBbO}cvcydOhB-M z1htTbRng$#uN;!6rB_c9rN^4Y(ypACQG%3d*rCEq-Pqv5W0&SV-Z=cbNT2nEfJL6T zQwG-wnOz~Gq-p?WAT*p1H04F!bP|D-V}^fo%v5Vb2Q;UqQZwc~Kj4o{hnc@vRwiZktPx z){G+a2{QA{j3VmNUQObPcOszb2i@z4bx99jAm6mZT0*0?-oU{RZ-gL0_XOZ$2no-c zT7C0!d1Da}e;NK1RR(F;amt!1w`!OcLu|nEVVs`UXqc|??RjwhQ6VL{D_>j_o_dLL?~`-^J;8bJ+x_|chQ9{3 zkZMMCi9vQH5s|em6S7Xo8B5soEt5y+W2c9WKtC|dMn7N~!(q<;(|?TTOhc&_l)WVv zdYfPrg2CLOllh(~CD)wd)6VATh1yHVX~Pf%r;3{VM^X|8UO9rpxxxHs%sc7t zPCwLq{>qz^&)}}y74m&r?6Jm^vUl}&y%MILtPsw4xB%N^L6WC6PNaUZz$4ryfi za`UbU5Kn8O+9mVE5=MLQ)s#1VF<%h07 zTQaSQ6pxJIv;UsTZ`+VvKEzA$`*<})=_DBzg0t`3(#cKY^^*Ska`>t^cwRd!FZ0VQ zJz1*L!#OUC?Kr6OwNM*;M|SA;V|<%UXA0X}FAFSf14g>v_~UZ3mGO}ihw?sjgTzOH zr<8`@f?F(gw2?3}Mo`L2rSwOdUqLCTLY%}K`hLMB>UwA0s=l}nYP2&4$kseH=t06sG{-A}1lgBIFUO@OvjVB@n184qn6Ikm z%I!MrKHD`*J4`SEc(-IUnmpB3@QW&`LC}AO-AnQX+DmUbiDF(buJCoi0?xO2S7>Iq zMYXS_P33aARF=20*ls;3L0X^ou5O(z z)mE4<6P|IBYBXnOH&tz7>c31gbCn!Qei>GC%aYm#t&rA3-u`Tb`21`I6oV}T;)z#^ zi;=j-?16V@K|h|eTO}Txnn>~4mL{qUJlnzL;bS;&v7J1QzsTHeRA6r^tumOUeu{a_ z<#p^d<^z3POQKMdscgf&sEXdPArb9URn}_Xxpk%pK4=8XVnjj{rxMR6)!9x|oQ(jz z>W{fw8ienXKgXWlH<`h}X^aq->*g&w)gS?OI&xK=3Zy|#2E~sE=C>Lp7Bn(*96|qA z(|1S1)xF@8 z(V`F01@WEFZ>`_I=dN|nz2`jlIp^;4?7eeSY?0=owmTB8yOka^ z_cGf+s`R(#ZkknkVG~uW$a|R>gb4Hr;iWT7BFzRR{`s^h%L$auoj2Vqrp{~h|7!t^ zffg=kY;J-1GM^dlGd6txni%%!k;&wjZwi303;)02PdQASHnXSuo5waSSSIuPw0jZh zW<1mie5Mwt)8la)$cK&>>~3;W2;tX0TKt*kLeHFz_1IMIaMahJD!K3M4v64PLAWoeDSf`nr_2-g7Hr)Us{IfIe$s@ndkH!nOdcU^az2z$or zSrAwd{poCr*#G5Pr4PCLV)1~}vFIWU(Zp&KSEI<{6vv2p_|rIwR7ttMm2NEkk624q zL&&qV_k-5A-?&TWLU&$2jV%0~Q=>FlCzCH8c(tX;a}L@Qu9NLy))WvO8@Hh;qtY$X z>%Z#yVQjcRsn)As=3f;IeJy*o2H;1QYtRM~qy@3yRPcT^d-(O!ji9W_gp6QEw4Z(9 zMd9T6Ml@$PxQ_3WQx9dk4I^tOz56@Q)b0s*mWnky2?UleOJ4s!ab}Yl|4_b4YF(^E()Bg;n}| zPww`NZ$5azICm67`%%eOpwqT5fz)acpl1f2niEk-K+2Ele;Y3FB^0Re*NI=eE*hfA=<>K>YlS%S?T_z_>_^OD%UtBC}Wf!AD6$dv78pvSY~HfDp;Ka5_qnmDb|fx7tLA>D1#9v??#Mk8@QJ)5%%MQ@QtSxD2C3N`!9 z0=HtiWrWsyq6v;PGC^UoOIv@+!MQZIt&#moe)T`&iAtS`=%b*E;ft6b&AvU!@Wh?i zOq;}fE~D;i_3e)2qjiqT?YE4f(86g-xH~xorD4r~92-NMOG<*brnX{>+ns4OLv7b? z5fxrYiB-%5r~1J1Hb>Q8DN!jQ$To%Nb1I0^5D#K>pPNxoj(i0&78}JG3#QN*2}P38 z-R=$pBI9`Zyx|`O+mPwv*t1?-qRxmCd3ZnTTf!M+jv0OTRHVPYxds{`L4W+FdL;c%dPARy7g8QW9E!TFX#7{TG2S54D4)#@fgD95*s z`=T0;bvbe({Z^ILH?mPFgMprph)qFbsp2~iN7mmx_|wQIF}yNdKq(Gh4R5sF;87S( z99ZEyjTrVrCxm)H{R6E9`T?;%DWrhLZ*YD&5V789pkLiuZKM46%yTx5T=RUzD2XVTQ1LDn;L2=U965N4XTMprYrH((;c5iBl_Rv#~2&&?>INFU4nK^VHECDXqQG?eY z5xyYrCE!_^4jzBe>zyhz>DPIbCF7*pLr8!1$xf}vM77?@msuiTUwbgH)#~BufM#AQ zKmn)kZbbT(@4-Han7PgVwtT8CVz!k0!*BOhvga)CQ+vUM%}Q%fiZC|AuMJC7hUlI$ zz|Wm)eLgfK@mj2z6tF@C$Eyxm7i>*6Q(mRW!0VE%?ofydbAKBXkp58>qmA`nxbqZM z3lO(G^23clygzT(T2ovt-U|t9lb6uN;E&uI%TvY<4|~MewZ^O;)qM|&*%2#UP=;#s z<*=tuy;`(QSqe2d%7&9E6POU-N%eHtjYeGuH2M4ouZv@?9+|=fmGcZ zzhhkwR>SK#(Uk7Y+4T_`J+?iE@Gp7|Ow>p0>utF2n7TJ!IrDiou~}=T&=+bdk&RKhf^MQGZRZ4Y zM9k`IMwL3UeKuq}(`NcqqZ_CUuR#y5vvVOMOJAjLkgJ-nTQ`|aH!Cgv`k=di9`Tvy znYk+&bJ5bPv?@!ss!X{*&Fpzz#3kHRT0L;%cFj$@GtGYZRBO9c6H=mg!4?t`HT`Yr zXnP}*CO&B6Bz&IG`J0auI!vfxpvKX{AzpwY<($IH{K(9=&?ns&DY0aUcdNNAR>g1E zY%C7;+_I~^F@Dp)PJxlZHA#nO^jeE<5zq3&twWnqrVjqD}eo0wX0Mk_*aB!3d96Wp1x8Vyr90gw>48045=P9l;o;)F5K7x4QBsp zZzD866HPKB+s7}6z7X17pX*J=EY8WP2;dC)H1p3L>z%UZDAPb>gE^7suXD(YhG zUP;}xsj&p8@wWB1;^nZZBXQXxkA-!ifFc+2LMXV-u7kX^XbHbE{C&;5bGuTA3=BE~ z&%yPq^?Cw$r>N{LI~wHON%oNR_P&N^oZ|VDm}8?!^CL4Ku4IF}vWxy#_yV5$fU_y) z-SlIu97!AaC`BBvnrSHDATw*9_s9-V7ga{(g!EOizqL}KMEPSjt+@HL{Jv}yh3ynk z_M>^Dohu)uF|D;1qc&6&z2GxUJl@f7M)@{@-+~jZI4+`67*yfgbHy~O3=i@fSZ5^( z4Z)A<6V9|9@0nNOrce`0SleC!cr&o8!QLPl;8?XaoXk^OOEAOZ3Q9Mdf%<0*pMAgx3haB zMq;S%RY~&Ny~%%77yzQ?y)EDZH<9URnUprzYCWZQ)H;%8%N9_`o2GDcM@vVv)<8MWZNn(T@O zhoEvd(atc_Rg8m*K$T#9yxcaxCGu2^F2+Ld{eEA8HEQna$Y1uhkq{yliQbX__rn%7 z*`a{X6CuF*GEoGNQ4^tK+Ot)+)YRsR7_=7WUKuE|+jr_=kCcBMj|nS|IV&A;$zN(+ z{_AAq;%VSUH$^414X&fSP_CdGE&da~8$mG7W-{;4t=-{=vyjNg3V+h6n48rALaF@z zk|E`%i9lN|-T=Af|B_)D)^|6TNOb?j)A;5B00j)uFpfYz z2CipM>-*6*b7ex^fDcHDf0?fnc8Dvnzu7&pTcxl5{5005b-o#7_-Dk0l>1j{=1*1d zr{4lZ3_N)f$KuJTA?YP}&5xL^2=KmGB8XkEd$O+GI!f|oK=*h~{lppKZG|tdm*-BU zfh&B&VsChb#ZDz2sT4CslRoTSmAYBI7H;O|ONZ(>ZWmFav#aZvtOee-;e5BM!tMH+HMJvY#z zh!0nH3v!>HpLa-hhOwU9GF!@tcSZe^gSa1F<#Wu4G@snODc=*CSc~KExTkdrrgq0C zZsFAG^84+G#PGZGqTr#211?-f4z=SPX}|Vio~9L1T~&FoXkVPI_?KuQ1RJej%%$Rw zf9T!&f9NqH~VRmG}Dy2A@r&z|iXo0*(4iEO?1Lh;hM@^4}h0CC5NwdQy8 zc*!-z-E|A901Jf2S&T^pX2Co3EukeiJBL67=GRSjhG-m@5w<6;taXjJ^DB7MP)C9$ zj}Y#g*yM}fH@2pwP2yCV06F_dTZ_j2{$})3>tK{A1@w+Hf3gi)5JoI|t)AYVzCY6Y zc3k04YtoO_rXo9Gyv-<_GTJy4lw)C3$hM|vG=iUBM-JIn+6zA7fxPaBgnu;rEyOv8 z=_>_`UHI@aJjnA&i%ecc7zff(`*%h&#;r68&4tr=kf5AKk9QXUvfwKbDWI8@k*#nq zp5A_M^uEL@JT@O(_EOeodZfj^7putig=~7<_v$+}2f41s-pIc1tId_ox~Z->5N`0q zlRnz_H%9$AR1X$E0xf>&ZEaozf`N5FA6_Wj`ht%7m3@|V#(QJ15Xegm#S)wBg!vdj z9+rTb>}umN_%P&&rZdV)qwVfe8gqsUus&)4A>xsCR*iR5c5&M&3cbGRW!QeAdhJ>I z6|YMKDtwKXXC#6Nd)&+>C_c6Ok+zOe`2sod!LFc81gPab%c|>R*mM#~ zQu(Y)E6$ko%kkY&prmFv^DY3;BrDh-^)@38eoMp8>7nd@Zp1^}kH}+Ziw62AM3R{ zJ4#)0=D)r-S#lKg zz1jEsjl}zOvIp=oHZ2~rm2@Op6zu^0?0y8fXKmEJWG}F?E#(*49(cL@oSFjZuo#*G z%c9$K4(6`?<{TZiV?NoM*3u9`cOUIe=X6r>ZtjvDLHyVEvPj_ljD%p&Cq!;_Fp(pG z-50(Kkb4g-#2&nFfB@$xKm5LF&ESlvz|!;4n+9sf%i=)ILQ>Z7b++pWacKOU2TT!# z4%fba&=krmsP4uOXLdxRtPsxE7&KBUZquE#(A`x94I{XKZA&Pap4I9mKxXJ$e7Q)k z<=#Qu7X;Zfn0IsG9T6d5;4^QU+mdueOu)2t#G02*ANg+9lJwd0*7QXntja6{{y0* z*av-~FJfBIajJM^o8rU+GnEBPuq+UK$BWSa&4=P*T}oCZoEUunzL^IYv3&~v(KN)m zmR&`~qC(QeAOA#o;OKVkUgvFg0FC&*wz0eUa=Ng1<>i-Y9rERC*^L439Z$kY#z2wA z(AcV~n4b|x?=~HO7NfAP?JLG)ure1G)5_D4_pv;^`UaVUE?F26j~%%7Iw0>PW*u&k z^S3Te6}Z&>Usy|J`FP7^KF<00%?wvO_G!GaXA_x2uS89bpyHq`B&fqA15B9hlpNe% z6WaZ}C;L0M1B;|G)cGJ~l6B-Hw9XEG=LXR0kVjSs;;Yq2Bf$*j5f$jiW{XKaa?Tet zb%orx@~g!C9r#8o<_duLCZI~1;Muqz7I^vH{Wei$p~Mh=P<3`G)(=P`q_@Az4H4gM zn77>1rG`TRlBB#5+Sn%d3b9VrM;sP6@pi*ntQX){vARCtOEM`*jT zb*<#In31*TrcG7sJ_8pQO$rdv^#bEFw@wlc1k=?Q-iLXG4AutTEsU+Z`mNGtc0m>9W~4x^r#Xcc9`FSP;`g@)5dx+ z{CX7lhJNV)E^c%kUQzz8*ZfDm;EKZ?kM{lF`o6Ga)`r5it6#|9an3PU)kpfW6$hs! zDr*c@zEfWw2cKFrLHIBAgp6IYuLn)Fp7wdffU`g!IIF+l#=}m(yG8Z_>yvO9W_+c3 zzH$VE%KMY^3140np57r>hOnOi1enlKzXiVAXlV zQwBxFimCYUXXD0`{_~+;cA1v!p1qiZV#8L&RFg@2%t`E1JA3Rf_HMJe=;u;L6?o22 zqa!I0su*&|Iy-w32ipw1DhUv-G#b(hOfkSchnk{k1*nCvMNfrx?VqOCqArb2J!xxY z`Nuln@ZL9Kaqz@0KW?Gm^G1AZTDJSxG#n-SA`eIfARgNE_u|*2e<$D`2=vRI$Ve9o z*3RVW%|P&)xHZ=o%xze?FY>UE0Ap|)r>bXjS7RC6mvd*KiMMp2bKs>IYIj&6ovrut z9t+AymE1ms;G+~7$S>_DY(Y;#IHR3t?h&>}slY~M16el02nUwZ32l0O8gj>psVBC? z0}~j)>Xm0@t2jnH-0;8`dO)}@ZrWPkan@NkKizR)Y9Nd!pI(ke`wXh86VU#N!Vai=l+NFyQ=rChhKTGB|`4aOd3QKIE2a z^(_{M$-+IIF=0uT#m+wTqw+LI#TgA7>f!vxAIi%UZ0zHTnaZcMBe@;18FAn&*xOn@ z7`7>ZbQ`elK|%|+9-M7 za?h3NxeR~D>Q_2H^%`r?jn!D3uef4Axc}uHSyYowphs{JukE3%*zY8Svvbe@pp4M5 zJyK)~vwC#K9$&ld7e=D8SkSQN8&;Ogl~lYyE9I`Db3s{GRc41NZ|U`w->*utRslX7 zA+?vL(4@LO?ky<5;D1`e$J~<_!Z`ZR2AuHCxy2DX$xWMZzOc}C%+}smT&{CV{Ht}F z`%0L!h8(uK?IoERih0R>(rrxr!B5?aSKyu{p;?qyUhRL84SGoI4J*Bix*Pp9d@`XIXU?Zqk;q(uyj4=HqIgYK6AI|10pJq@s|=E;KAp27sMR=~b|) zaI+WrKcUh5CpV~5_eHQ)NAvgi6-Q2*f~5e)W~Xx(vqo6x_h}WSB}HTX_ngD~X_UwQ zka{W8!N((z+ipu-T=UG8G0-Z>qu8gON?ATejelB}IP&Hc*E$~C>FBN9M6O0E0T%ZL zgiY#_2;D%#6d&}Xi#0%r1Nw1Xs?TK0$k&5b(#3{~Pn*PAh~zG?l@4%M-Iex(aTBf1 z(gU~egpROlRUBVn#j5nW?sf}P#)zyx=&u`+rsRQnST0BH=#u?(^Ec-Pe`BX7Y z632YJNtfmbFW83b|_^3|Zj*^&OkCJc<+P zrpox~WQsb9bCsY1`0$&?M;qaaoRQO=(`#0iF;`sYL>nC*!x9Jmp1R^;}T+DAFKCt@r>ZE_EEVsIvI`VsIr?eKgq&p|F8iaU?T)=p33LQxn zPIo#u6UioQ6NrzjPH4D@-1Rut*g!=u#&`SF4+BNc|Do@@AYswC4-;dLPW)%0iXSRX ziE*atqi{u2o<=b5c2dE0K6TfLK^kkmedGFvj?%F=#t#+K&mA@}z!h_G=IWK&AD?)A zOlZ>vtS>$1w}FD98~lp?>KVgK;F0}gcymd_r5Bd*Z*_17`eWUniMyVg74Pz+BpUggb9N`4|MBOFM>hHmd7V{aNGKM~QVIHJ4?Q6^MmXc$6psMSfG4yy3HIcM8 zx2Ov!kbm;CD%M}TDEeP__5k*>10>-n6UKh+*`qVkMf2Zq?dE+cdQ}1^RFM3Esxijt zBiz;K)5e?h$lRmOx*M`|wiFDVE-12pF-cX}LGb6zli27Q|7Dm9e}Mnz zRKXDizGFMYFVq zmhB9*$(;b^X|PyV76*KkYw9+5T$!V7_-f6NDjnpqxn(hr4-2GD<5d7~bcgrj8ufXRp*4}RkI{;OJ0$7kN&GP7ij+(s=j{Op|QUJQs z?9uu`$vKOrRzx={S6>EoG`ihIa?pyBZWNWqFu<&G!OS$TN~Egbt2#<`MOfJ-Z&x++ z2!*OO3lt#pfLJPdUXLX7D#m-}8UHy5cnaTr|a){qup1%KMWU9C2<);urC?Vgd5ad>Ufc; zE`wI=6?I=c<7B5hE7q0o-Gc9N{G61zVIp@{MrVH6gMEut3nuB%|CSvdQlKYlm>*7> zgrxEayIiz}`tkq%^K{s{gn$RQ2^eXf9Pz@DKN6R(fbb8 zRl6kygE;1G@9`q*5$tI)uw-u4!mwjbDyp{K9cv9cY0j3t%JDH z5((B$r5VC|wmK^E?KYfKx`)eR&c3@aAqQeIf&9dQqe_1Fr=UWE<1f@FyaAx$Ww<9K zPcs*)6ehpUEm)Gf;B!HMgr#q{+|h8=hYn|}Q0T2EZ<-zlxxbS5w7p#C>pr&W`gG$7 zpLETZSG$GU`+otmYHwtMK6V^FKbPb2y9xk61pem{5P3-8u#hv{)JKT8hnYj;)wTEM zPNh!isU4sJAj*5j&oSWQ*#16%F^ItcWwvaEpuL+)6D=O+WHz}X*;U+HqYv+;Yv5h7 zZ>G^G@p1ReLwMI`$Bh1CQ*?$PnCuAw=`1ka*uKcQd)re9!Pip>B~eP^fexf1T@h+97)QOdua;M>`;+CGbr;4P$o4z{-zHk$KbJ5fCKBnGb zQSUl-?9GVlJep($U^bRD*TaX%c-u+gTHa3U-&=?iU8vZZ6 zduNUf$}em85oh<+59eT>J2O}dykR(-FVAz@O&t#+z^EJOrgcbKg#IZqy8ggymf|=( z7I|>1)Dc}HB~da4%J@3EpAir@({NdmFr`WK)>xG!ua;-|&R`_NKqr7OK>)CMYN+IC z9m9j7f6uR?ZyE74+KH16$N2#pR@Vj$qx_k*3YniADq9DCx&t5ldS@MQu0-uw6T~7- zbHk2Fu}lEsZXQQ3H^!&yzBRMRPl}t#y#+LKP|jDL5Jnx}bUX$*BIm!yI?tr-;Yl&) zuGCm)2bfmVJ&?p%xu_vOeS;6==dC=8yzq6`mg&V#VN$O~FDmAL(}}aHp0381_xfXa zPTAU_0iYqN>8m{6e-bH>9|c)nD@_fC=9-Dz=nXe>K=D>Hq^X*0juVIESO+oh77t9j zFB?K~s74YAylbf`9yG|$u_@K!6UlROEtD5FE^0ODW|sG=1#l*-Pa6Lzd^zR}>NfQM zr$`yt)n!nxH(KWN=ODd}(Z(MmyH(U9?C*Xp0NDtQHsffDy6me?mr!e(uqS|(+uy9v zqhsc%DDNp#6mSAp)U09dG8yTMzs7y1E(P%QhbQ$I`q?wo3qy04>Pulm@^lXymR1IM zkAOg%HnjjLrlQiEd6d04I}OR`=d^Dh;e)N}jPcej*4zL?^7=A8I*B?`vR-N2Lz(ud z^`PS)VP-jQUwUp!W_k@KZfmLed>PpR@ssIQg{aOIAN4?2rI`UW(XOT*zWVSQXgYE| zCqCvt6F1D%oblO5SgnUG;1~6#s1-p$&@Z%;;m@2+fA0z&%nqAO`4T^Q>ggqWo-)ow zpK51ObQ^GWi0FU~jNH>A{EwGXcTOkm>}Ew!`no``EI>6>KpJrnp3^)aC{y*Jo|sRz zQ27jPi{5T zzd(p}A8MYH>tNz*QX8Bz%~=l*8$_fe|W( zpD+=yU78%to=q18vfjQL>jP{J%@oo43Z#(QDM43S1G>j(xIq447c^Rkftrkvv>MOK0BMPYEB& zNA8rI(Iag&|7TM2jHp?q*{dmCM9Ni-UZ%F1P+Mc1hyq4uFmE~|W#DRX*S`3aLTYUn pUAom@b70|jz#_f;y!l_Xc7w|Sz_Tn6)yV~*{m|e+`8~Vv{{xSQ#{U2S literal 0 HcmV?d00001 diff --git a/icons/effects/light_overlays/light_32.dmi b/icons/effects/light_overlays/light_32.dmi new file mode 100644 index 0000000000000000000000000000000000000000..5269b1fba36d8ad0dcde7862da177a51a4632e26 GIT binary patch literal 1140 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 TI$=2z10LMB_#1QAE$RON=Yb3V literal 0 HcmV?d00001 diff --git a/icons/effects/light_overlays/light_352.dmi b/icons/effects/light_overlays/light_352.dmi new file mode 100644 index 0000000000000000000000000000000000000000..f895792da42d63143e15641a198443e290f3671c GIT binary patch literal 35566 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 literal 0 HcmV?d00001 diff --git a/icons/effects/light_overlays/light_96.dmi b/icons/effects/light_overlays/light_96.dmi new file mode 100644 index 0000000000000000000000000000000000000000..b689a137016322424accea3488b7cd02c6c846ac GIT binary patch literal 4643 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 Z{{vX6W?B4;ZxR3i002ovPDHLkV1k{4F%AF# literal 0 HcmV?d00001 diff --git a/yogstation.dme b/yogstation.dme index fb1f3ef4738d..1830674e60f1 100644 --- a/yogstation.dme +++ b/yogstation.dme @@ -450,6 +450,7 @@ #include "code\datums\components\nanites.dm" #include "code\datums\components\ntnet_interface.dm" #include "code\datums\components\orbiter.dm" +#include "code\datums\components\overlay_lighting.dm" #include "code\datums\components\paintable.dm" #include "code\datums\components\rad_insulation.dm" #include "code\datums\components\radioactive.dm" @@ -668,6 +669,7 @@ #include "code\game\atoms_movable.dm" #include "code\game\communications.dm" #include "code\game\data_huds.dm" +#include "code\game\movable_luminosity.dm" #include "code\game\say.dm" #include "code\game\shuttle_engines.dm" #include "code\game\sound.dm" diff --git a/yogstation/code/modules/antagonists/darkspawn/darkspawn_objects/umbral_tendrils.dm b/yogstation/code/modules/antagonists/darkspawn/darkspawn_objects/umbral_tendrils.dm index 766984d00dc7..5aec8b8c0b97 100644 --- a/yogstation/code/modules/antagonists/darkspawn/darkspawn_objects/umbral_tendrils.dm +++ b/yogstation/code/modules/antagonists/darkspawn/darkspawn_objects/umbral_tendrils.dm @@ -78,9 +78,7 @@ /obj/item/umbral_tendrils/proc/disintegrate(obj/item/O) if(istype(O, /obj/item/pda)) var/obj/item/pda/PDA = O - PDA.set_light(0) - PDA.fon = FALSE - PDA.f_lum = 0 + PDA.set_light_on(FALSE) PDA.update_icon() visible_message(span_danger("The light in [PDA] shorts out!")) else diff --git a/yogstation/code/modules/antagonists/shadowling/shadowling_abilities.dm b/yogstation/code/modules/antagonists/shadowling/shadowling_abilities.dm index 8e4d9f652f15..415c7bdfb2e0 100644 --- a/yogstation/code/modules/antagonists/shadowling/shadowling_abilities.dm +++ b/yogstation/code/modules/antagonists/shadowling/shadowling_abilities.dm @@ -113,7 +113,7 @@ var/blacklisted_lights = list(/obj/item/flashlight/flare, /obj/item/flashlight/slime) if(istype(I, /obj/item/flashlight)) var/obj/item/flashlight/F = I - if(F.on) + if(F.light_on) if(cold) if(is_type_in_list(F, blacklisted_lights)) F.visible_message(span_warning("The sheer cold shatters [F]!")) @@ -122,13 +122,13 @@ return if(is_type_in_list(I, blacklisted_lights)) I.visible_message(span_danger("[I] dims slightly before scattering the shadows around it.")) - return F.brightness_on //Necessary because flashlights become 0-luminosity when held. I don't make the rules of lightcode. - F.on = FALSE + return F.light_power //Necessary because flashlights become 0-luminosity when held. I don't make the rules of lightcode. + F.set_light_on(FALSE) F.update_brightness() else if(istype(I, /obj/item/pda)) var/obj/item/pda/P = I - P.fon = FALSE - I.set_light(0) + P.set_light_on(FALSE) + I.set_light_on(FALSE) return I.luminosity /obj/effect/proc_holder/spell/aoe_turf/proc/extinguishMob(mob/living/H, cold = FALSE) From 2e920699a55fc4bef11de644883c8e896b665170 Mon Sep 17 00:00:00 2001 From: Ling Date: Mon, 2 Jan 2023 15:57:39 +0200 Subject: [PATCH 03/13] Change conflicting defines --- code/__DEFINES/machines.dm | 25 ++++++++---- code/game/area/areas.dm | 38 +++++++++---------- code/game/machinery/_machinery.dm | 14 +++---- code/game/machinery/airlock_control.dm | 2 +- code/game/machinery/airlock_cycle_control.dm | 2 +- code/game/machinery/buttons.dm | 2 +- code/game/machinery/cell_charger.dm | 2 +- code/game/machinery/defibrillator_mount.dm | 2 +- code/game/machinery/doors/door.dm | 2 +- code/game/machinery/doors/ministile.dm | 2 +- code/game/machinery/doors/turnstile.dm | 2 +- .../embedded_controller/access_controller.dm | 2 +- .../embedded_controller/airlock_controller.dm | 4 +- .../simple_vent_controller.dm | 2 +- code/game/machinery/fat_sucker.dm | 8 ++-- code/game/machinery/firealarm.dm | 2 +- code/game/machinery/harvester.dm | 4 +- code/game/machinery/lightswitch.dm | 2 +- .../machinery/porta_turret/portable_turret.dm | 2 +- code/game/machinery/sci_bombardment.dm | 2 +- .../game/mecha/equipment/tools/other_tools.dm | 4 +- .../objects/items/devices/radio/intercom.dm | 2 +- code/game/objects/structures/electricchair.dm | 4 +- .../atmospherics/machinery/airalarm.dm | 2 +- .../atmospherics/machinery/atmosmachinery.dm | 2 +- .../components/unary_devices/vent_pump.dm | 2 +- .../atmospherics/machinery/other/meter.dm | 2 +- code/modules/holodeck/items.dm | 2 +- code/modules/mob/living/silicon/ai/life.dm | 4 +- .../modular_computers/hardware/recharger.dm | 4 +- code/modules/paperwork/photocopier.dm | 2 +- code/modules/power/apc.dm | 12 +++--- code/modules/power/gravitygenerator.dm | 2 +- code/modules/power/lighting.dm | 6 +-- .../security_levels/keycard_authentication.dm | 2 +- .../structures/beds_chairs/electric_bed.dm | 4 +- 36 files changed, 93 insertions(+), 84 deletions(-) diff --git a/code/__DEFINES/machines.dm b/code/__DEFINES/machines.dm index 28f8d9c72679..0f8f0e07cf2c 100644 --- a/code/__DEFINES/machines.dm +++ b/code/__DEFINES/machines.dm @@ -1,11 +1,20 @@ -// channel numbers for power -#define EQUIP 1 -#define LIGHT 2 -#define ENVIRON 3 -#define TOTAL 4 //for total power used only -#define STATIC_EQUIP 5 -#define STATIC_LIGHT 6 -#define STATIC_ENVIRON 7 +// These are indexes in a list, and indexes for "dynamic" and static channels should be kept contiguous +#define AREA_USAGE_EQUIP 1 +#define AREA_USAGE_LIGHT 2 +#define AREA_USAGE_ENVIRON 3 +#define AREA_USAGE_TOTAL 4 +#define AREA_USAGE_STATIC_EQUIP 5 +#define AREA_USAGE_STATIC_LIGHT 6 +#define AREA_USAGE_STATIC_ENVIRON 7 +#define AREA_USAGE_LEN AREA_USAGE_STATIC_ENVIRON // largest idx +/// Index of the first dynamic usage channel +#define AREA_USAGE_DYNAMIC_START AREA_USAGE_EQUIP +/// Index of the last dynamic usage channel +#define AREA_USAGE_DYNAMIC_END AREA_USAGE_ENVIRON +/// Index of the first static usage channel +#define AREA_USAGE_STATIC_START AREA_USAGE_STATIC_EQUIP +/// Index of the last static usage channel +#define AREA_USAGE_STATIC_END AREA_USAGE_STATIC_ENVIRON //Power use #define NO_POWER_USE 0 diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index c2a3828e402b..2eda8fda45eb 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -553,11 +553,11 @@ GLOBAL_LIST_EMPTY(teleportlocs) if(always_unpowered) return 0 switch(chan) - if(EQUIP) + if(AREA_USAGE_EQUIP) return power_equip - if(LIGHT) + if(AREA_USAGE_LIGHT) return power_light - if(ENVIRON) + if(AREA_USAGE_ENVIRON) return power_environ return 0 @@ -584,19 +584,19 @@ GLOBAL_LIST_EMPTY(teleportlocs) /area/proc/usage(chan) var/used = 0 switch(chan) - if(LIGHT) + if(AREA_USAGE_LIGHT) used += used_light - if(EQUIP) + if(AREA_USAGE_EQUIP) used += used_equip - if(ENVIRON) + if(AREA_USAGE_ENVIRON) used += used_environ - if(TOTAL) + if(AREA_USAGE_TOTAL) used += used_light + used_equip + used_environ - if(STATIC_EQUIP) + if(AREA_USAGE_STATIC_EQUIP) used += static_equip - if(STATIC_LIGHT) + if(AREA_USAGE_STATIC_LIGHT) used += static_light - if(STATIC_ENVIRON) + if(AREA_USAGE_STATIC_ENVIRON) used += static_environ return used @@ -604,17 +604,17 @@ GLOBAL_LIST_EMPTY(teleportlocs) * Add a static amount of power load to an area * * Possible channels - * *STATIC_EQUIP - * *STATIC_LIGHT - * *STATIC_ENVIRON + * *AREA_USAGE_STATIC_EQUIP + * *AREA_USAGE_STATIC_LIGHT + * *AREA_USAGE_STATIC_ENVIRON */ /area/proc/addStaticPower(value, powerchannel) switch(powerchannel) - if(STATIC_EQUIP) + if(AREA_USAGE_STATIC_EQUIP) static_equip += value - if(STATIC_LIGHT) + if(AREA_USAGE_STATIC_LIGHT) static_light += value - if(STATIC_ENVIRON) + if(AREA_USAGE_STATIC_ENVIRON) static_environ += value /** @@ -633,11 +633,11 @@ GLOBAL_LIST_EMPTY(teleportlocs) /area/proc/use_power(amount, chan) amount *= POWER_MOD switch(chan) - if(EQUIP) + if(AREA_USAGE_EQUIP) used_equip += amount - if(LIGHT) + if(AREA_USAGE_LIGHT) used_light += amount - if(ENVIRON) + if(AREA_USAGE_ENVIRON) used_environ += amount /** diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm index 821cf780543c..52456d51bd1f 100644 --- a/code/game/machinery/_machinery.dm +++ b/code/game/machinery/_machinery.dm @@ -21,9 +21,9 @@ Class Variables: power_channel (num) What channel to draw from when drawing power for power mode Possible Values: - EQUIP:0 -- Equipment Channel - LIGHT:2 -- Lighting Channel - ENVIRON:3 -- Environment Channel + AREA_USAGE_EQUIP:0 -- Equipment Channel + AREA_USAGE_LIGHT:2 -- Lighting Channel + AREA_USAGE_ENVIRON:3 -- Environment Channel component_parts (list) A list of component parts of machine used by frame based machines. @@ -53,11 +53,11 @@ Class Procs: Default definition uses 'use_power', 'power_channel', 'active_power_usage', 'idle_power_usage', 'powered()', and 'use_power()' implement behavior. - powered(chan = EQUIP) 'modules/power/power.dm' + powered(chan = AREA_USAGE_EQUIP) 'modules/power/power.dm' Checks to see if area that contains the object has power available for power channel given in 'chan'. - use_power(amount, chan=EQUIP) 'modules/power/power.dm' + use_power(amount, chan=AREA_USAGE_EQUIP) 'modules/power/power.dm' Deducts 'amount' from the power channel 'chan' of the area that contains the object. power_change() 'modules/power/power.dm' @@ -103,8 +103,8 @@ Class Procs: //2 = run auto, use active var/idle_power_usage = 0 var/active_power_usage = 0 - var/power_channel = EQUIP - //EQUIP,ENVIRON or LIGHT + var/power_channel = AREA_USAGE_EQUIP + //AREA_USAGE_EQUIP, AREA_USAGE_ENVIRON or AREA_USAGE_LIGHT var/wire_compatible = FALSE var/list/component_parts = null //list of all the parts used to build it, if made from certain kinds of frames. diff --git a/code/game/machinery/airlock_control.dm b/code/game/machinery/airlock_control.dm index 83ee9915e3a2..72f0ce01acd6 100644 --- a/code/game/machinery/airlock_control.dm +++ b/code/game/machinery/airlock_control.dm @@ -90,7 +90,7 @@ name = "airlock sensor" resistance_flags = FIRE_PROOF - power_channel = ENVIRON + power_channel = AREA_USAGE_ENVIRON var/id_tag var/master_tag diff --git a/code/game/machinery/airlock_cycle_control.dm b/code/game/machinery/airlock_cycle_control.dm index 6a4e39ffda0f..278c0cf1a03a 100644 --- a/code/game/machinery/airlock_cycle_control.dm +++ b/code/game/machinery/airlock_cycle_control.dm @@ -49,7 +49,7 @@ use_power = IDLE_POWER_USE idle_power_usage = 4 active_power_usage = 8 - power_channel = ENVIRON + power_channel = AREA_USAGE_ENVIRON req_access = list(ACCESS_ATMOSPHERICS) max_integrity = 250 integrity_failure = 80 diff --git a/code/game/machinery/buttons.dm b/code/game/machinery/buttons.dm index 27f65e5ca4e9..49d36b3c6c1d 100644 --- a/code/game/machinery/buttons.dm +++ b/code/game/machinery/buttons.dm @@ -4,7 +4,7 @@ icon = 'icons/obj/stationobjs.dmi' icon_state = "doorctrl" var/skin = "doorctrl" - power_channel = ENVIRON + power_channel = AREA_USAGE_ENVIRON var/obj/item/assembly/device var/obj/item/electronics/airlock/board var/device_type = null diff --git a/code/game/machinery/cell_charger.dm b/code/game/machinery/cell_charger.dm index 1dd78d9bd31b..68a74daa5636 100644 --- a/code/game/machinery/cell_charger.dm +++ b/code/game/machinery/cell_charger.dm @@ -6,7 +6,7 @@ use_power = IDLE_POWER_USE idle_power_usage = 5 active_power_usage = 60 - power_channel = EQUIP + power_channel = AREA_USAGE_EQUIP circuit = /obj/item/circuitboard/machine/cell_charger pass_flags = PASSTABLE var/obj/item/stock_parts/cell/charging = null diff --git a/code/game/machinery/defibrillator_mount.dm b/code/game/machinery/defibrillator_mount.dm index d567f0c2fefc..7000b0768fce 100644 --- a/code/game/machinery/defibrillator_mount.dm +++ b/code/game/machinery/defibrillator_mount.dm @@ -9,7 +9,7 @@ density = FALSE use_power = IDLE_POWER_USE idle_power_usage = 1 - power_channel = EQUIP + power_channel = AREA_USAGE_EQUIP req_one_access = list(ACCESS_MEDICAL, ACCESS_HEADS, ACCESS_SECURITY) //used to control clamps var/obj/item/defibrillator/defib //this mount's defibrillator var/clamps_locked = FALSE //if true, and a defib is loaded, it can't be removed without unlocking the clamps diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 0688a16a63fa..b2aad8bb28c2 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -7,7 +7,7 @@ density = TRUE move_resist = MOVE_FORCE_VERY_STRONG layer = OPEN_DOOR_LAYER - power_channel = ENVIRON + power_channel = AREA_USAGE_ENVIRON max_integrity = 350 armor = list(MELEE = 30, BULLET = 30, LASER = 20, ENERGY = 20, BOMB = 10, BIO = 100, RAD = 100, FIRE = 80, ACID = 70) CanAtmosPass = ATMOS_PASS_DENSITY diff --git a/code/game/machinery/doors/ministile.dm b/code/game/machinery/doors/ministile.dm index 9ad153a681c2..e119865b075c 100644 --- a/code/game/machinery/doors/ministile.dm +++ b/code/game/machinery/doors/ministile.dm @@ -3,7 +3,7 @@ desc = "A mechanical door that permits one-way access to an area." icon = 'icons/obj/objects.dmi' icon_state = "ministile_map" - power_channel = ENVIRON + power_channel = AREA_USAGE_ENVIRON density = TRUE obj_integrity = 150 max_integrity = 150 diff --git a/code/game/machinery/doors/turnstile.dm b/code/game/machinery/doors/turnstile.dm index 44f639c9f826..188d842212ad 100644 --- a/code/game/machinery/doors/turnstile.dm +++ b/code/game/machinery/doors/turnstile.dm @@ -3,7 +3,7 @@ desc = "A mechanical door that permits one-way access to an area." icon = 'icons/obj/objects.dmi' icon_state = "turnstile_map" - power_channel = ENVIRON + power_channel = AREA_USAGE_ENVIRON density = TRUE obj_integrity = 250 max_integrity = 250 diff --git a/code/game/machinery/embedded_controller/access_controller.dm b/code/game/machinery/embedded_controller/access_controller.dm index f4ec0aa85a09..3e7908f5deea 100644 --- a/code/game/machinery/embedded_controller/access_controller.dm +++ b/code/game/machinery/embedded_controller/access_controller.dm @@ -5,7 +5,7 @@ #define CYCLE_INTERIOR 5 /obj/machinery/doorButtons - power_channel = ENVIRON + power_channel = AREA_USAGE_ENVIRON use_power = IDLE_POWER_USE idle_power_usage = 2 active_power_usage = 4 diff --git a/code/game/machinery/embedded_controller/airlock_controller.dm b/code/game/machinery/embedded_controller/airlock_controller.dm index 2d4ddc48a190..bb838b650fc2 100644 --- a/code/game/machinery/embedded_controller/airlock_controller.dm +++ b/code/game/machinery/embedded_controller/airlock_controller.dm @@ -201,7 +201,7 @@ density = FALSE frequency = FREQ_AIRLOCK_CONTROL - power_channel = ENVIRON + power_channel = AREA_USAGE_ENVIRON // Setup parameters only var/id_tag @@ -312,4 +312,4 @@ [state_options]"} - return output \ No newline at end of file + return output diff --git a/code/game/machinery/embedded_controller/simple_vent_controller.dm b/code/game/machinery/embedded_controller/simple_vent_controller.dm index 33be1e3f79ad..7679080ea1f9 100644 --- a/code/game/machinery/embedded_controller/simple_vent_controller.dm +++ b/code/game/machinery/embedded_controller/simple_vent_controller.dm @@ -39,7 +39,7 @@ density = FALSE frequency = FREQ_ATMOS_CONTROL - power_channel = ENVIRON + power_channel = AREA_USAGE_ENVIRON // Setup parameters only var/airpump_tag diff --git a/code/game/machinery/fat_sucker.dm b/code/game/machinery/fat_sucker.dm index 1ae2d1034daf..609bf1f17377 100644 --- a/code/game/machinery/fat_sucker.dm +++ b/code/game/machinery/fat_sucker.dm @@ -117,12 +117,12 @@ else overlays += "[icon_state]_door_off" if(occupant) - if(powered(EQUIP)) + if(powered(AREA_USAGE_EQUIP)) overlays += "[icon_state]_stack" overlays += "[icon_state]_yellow" else overlays += "[icon_state]_red" - else if(powered(EQUIP)) + else if(powered(AREA_USAGE_EQUIP)) overlays += "[icon_state]_red" if(panel_open) overlays += "[icon_state]_panel" @@ -130,7 +130,7 @@ /obj/machinery/fat_sucker/process() if(!processing) return - if(!powered(EQUIP) || !occupant || !iscarbon(occupant)) + if(!powered(AREA_USAGE_EQUIP) || !occupant || !iscarbon(occupant)) open_machine() return @@ -151,7 +151,7 @@ use_power(500) /obj/machinery/fat_sucker/proc/start_extracting() - if(state_open || !occupant || processing || !powered(EQUIP)) + if(state_open || !occupant || processing || !powered(AREA_USAGE_EQUIP)) return if(iscarbon(occupant)) var/mob/living/carbon/C = occupant diff --git a/code/game/machinery/firealarm.dm b/code/game/machinery/firealarm.dm index ddf247310c8f..303562183071 100644 --- a/code/game/machinery/firealarm.dm +++ b/code/game/machinery/firealarm.dm @@ -23,7 +23,7 @@ use_power = IDLE_POWER_USE idle_power_usage = 2 active_power_usage = 6 - power_channel = ENVIRON + power_channel = AREA_USAGE_ENVIRON resistance_flags = FIRE_PROOF light_power = 0 diff --git a/code/game/machinery/harvester.dm b/code/game/machinery/harvester.dm index 2b0a10a0bc17..7fa81fae7e1d 100644 --- a/code/game/machinery/harvester.dm +++ b/code/game/machinery/harvester.dm @@ -57,7 +57,7 @@ start_harvest() /obj/machinery/harvester/proc/can_harvest() - if(!powered(EQUIP) || state_open || !occupant || !iscarbon(occupant)) + if(!powered(AREA_USAGE_EQUIP) || state_open || !occupant || !iscarbon(occupant)) return var/mob/living/carbon/C = occupant if(!allow_clothing) @@ -92,7 +92,7 @@ /obj/machinery/harvester/proc/harvest() update_icon() - if(!harvesting || state_open || !powered(EQUIP) || !occupant || !iscarbon(occupant)) + if(!harvesting || state_open || !powered(AREA_USAGE_EQUIP) || !occupant || !iscarbon(occupant)) return playsound(src, 'sound/machines/juicer.ogg', 20, 1) var/mob/living/carbon/C = occupant diff --git a/code/game/machinery/lightswitch.dm b/code/game/machinery/lightswitch.dm index 7e819f7d4963..139ce45525ed 100644 --- a/code/game/machinery/lightswitch.dm +++ b/code/game/machinery/lightswitch.dm @@ -4,7 +4,7 @@ icon = 'icons/obj/power.dmi' icon_state = "light-p" desc = "Make dark." - power_channel = LIGHT + power_channel = AREA_USAGE_LIGHT light_power = 0 light_range = 7 diff --git a/code/game/machinery/porta_turret/portable_turret.dm b/code/game/machinery/porta_turret/portable_turret.dm index 200ea939bb4f..7180f3b7f036 100644 --- a/code/game/machinery/porta_turret/portable_turret.dm +++ b/code/game/machinery/porta_turret/portable_turret.dm @@ -16,7 +16,7 @@ idle_power_usage = 50 //when inactive, this turret takes up constant 50 Equipment power active_power_usage = 300 //when active, this turret takes up constant 300 Equipment power req_access = list(ACCESS_SEC_DOORS) - power_channel = EQUIP //drains power from the EQUIPMENT channel + power_channel = AREA_USAGE_EQUIP //drains power from the EQUIPMENT channel var/base_icon_state = "standard" var/scan_range = 7 diff --git a/code/game/machinery/sci_bombardment.dm b/code/game/machinery/sci_bombardment.dm index daee5b1ea05a..0ec1dec53e30 100644 --- a/code/game/machinery/sci_bombardment.dm +++ b/code/game/machinery/sci_bombardment.dm @@ -28,7 +28,7 @@ use_power = IDLE_POWER_USE idle_power_usage = 500 active_power_usage = 5000 - power_channel = EQUIP + power_channel = AREA_USAGE_EQUIP density = TRUE verb_say = "states coldly" var/countdown = 30 diff --git a/code/game/mecha/equipment/tools/other_tools.dm b/code/game/mecha/equipment/tools/other_tools.dm index 21ff45d7cb71..6fd24687c249 100644 --- a/code/game/mecha/equipment/tools/other_tools.dm +++ b/code/game/mecha/equipment/tools/other_tools.dm @@ -280,7 +280,7 @@ energy_drain = 0 range = 0 var/coeff = 100 - var/list/use_channels = list(EQUIP,ENVIRON,LIGHT) + var/list/use_channels = list(AREA_USAGE_EQUIP,AREA_USAGE_ENVIRON,AREA_USAGE_LIGHT) selectable = 0 /obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/Destroy() @@ -343,7 +343,7 @@ var/area/A = get_area(chassis) if(A) var/pow_chan - for(var/c in list(EQUIP,ENVIRON,LIGHT)) + for(var/c in list(AREA_USAGE_EQUIP,AREA_USAGE_ENVIRON,AREA_USAGE_LIGHT)) if(A.powered(c)) pow_chan = c break diff --git a/code/game/objects/items/devices/radio/intercom.dm b/code/game/objects/items/devices/radio/intercom.dm index 2b922ae40d09..d796c50dea18 100644 --- a/code/game/objects/items/devices/radio/intercom.dm +++ b/code/game/objects/items/devices/radio/intercom.dm @@ -130,7 +130,7 @@ if(!A || emped) on = FALSE else - on = A.powered(EQUIP) // set "on" to the power status + on = A.powered(AREA_USAGE_EQUIP) // set "on" to the power status if(!on) icon_state = "intercom-p" diff --git a/code/game/objects/structures/electricchair.dm b/code/game/objects/structures/electricchair.dm index 1011f6926607..a7e0e80c69b2 100644 --- a/code/game/objects/structures/electricchair.dm +++ b/code/game/objects/structures/electricchair.dm @@ -29,9 +29,9 @@ var/area/A = get_area(src) if(!isarea(A)) return - if(!A.powered(EQUIP)) + if(!A.powered(AREA_USAGE_EQUIP)) return - A.use_power(EQUIP, 5000) + A.use_power(AREA_USAGE_EQUIP, 5000) flick("echair_shock", src) var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread diff --git a/code/modules/atmospherics/machinery/airalarm.dm b/code/modules/atmospherics/machinery/airalarm.dm index 89da6cf17ed6..666fe21e6c25 100644 --- a/code/modules/atmospherics/machinery/airalarm.dm +++ b/code/modules/atmospherics/machinery/airalarm.dm @@ -64,7 +64,7 @@ use_power = IDLE_POWER_USE idle_power_usage = 4 active_power_usage = 8 - power_channel = ENVIRON + power_channel = AREA_USAGE_ENVIRON req_access = list(ACCESS_ATMOSPHERICS) max_integrity = 250 integrity_failure = 80 diff --git a/code/modules/atmospherics/machinery/atmosmachinery.dm b/code/modules/atmospherics/machinery/atmosmachinery.dm index 587100913ea5..44cbca225bc0 100644 --- a/code/modules/atmospherics/machinery/atmosmachinery.dm +++ b/code/modules/atmospherics/machinery/atmosmachinery.dm @@ -18,7 +18,7 @@ GLOBAL_LIST_EMPTY(pipeimages) move_resist = INFINITY //Moving a connected machine without actually doing the normal (dis)connection things will probably cause a LOT of issues. idle_power_usage = 0 active_power_usage = 0 - power_channel = ENVIRON + power_channel = AREA_USAGE_ENVIRON layer = GAS_PIPE_HIDDEN_LAYER //under wires resistance_flags = FIRE_PROOF max_integrity = 200 diff --git a/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm b/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm index 08c93f4da1cf..c61b24eabb59 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm @@ -352,7 +352,7 @@ /obj/machinery/atmospherics/components/unary/vent_pump/high_volume name = "large air vent" - power_channel = EQUIP + power_channel = AREA_USAGE_EQUIP /obj/machinery/atmospherics/components/unary/vent_pump/high_volume/New() ..() diff --git a/code/modules/atmospherics/machinery/other/meter.dm b/code/modules/atmospherics/machinery/other/meter.dm index 64ce14205513..e05e29c009d4 100644 --- a/code/modules/atmospherics/machinery/other/meter.dm +++ b/code/modules/atmospherics/machinery/other/meter.dm @@ -4,7 +4,7 @@ icon = 'icons/obj/atmospherics/pipes/meter.dmi' icon_state = "meterX" layer = GAS_METER_LAYER - power_channel = ENVIRON + power_channel = AREA_USAGE_ENVIRON use_power = IDLE_POWER_USE idle_power_usage = 2 active_power_usage = 4 diff --git a/code/modules/holodeck/items.dm b/code/modules/holodeck/items.dm index 9b482ba878df..95b12ccbcef0 100644 --- a/code/modules/holodeck/items.dm +++ b/code/modules/holodeck/items.dm @@ -156,7 +156,7 @@ use_power = IDLE_POWER_USE idle_power_usage = 2 active_power_usage = 6 - power_channel = ENVIRON + power_channel = AREA_USAGE_ENVIRON /obj/machinery/readybutton/attack_ai(mob/user as mob) to_chat(user, "The station AI is not to interact with these devices.") diff --git a/code/modules/mob/living/silicon/ai/life.dm b/code/modules/mob/living/silicon/ai/life.dm index 6114b8cfa00f..213fe23ed952 100644 --- a/code/modules/mob/living/silicon/ai/life.dm +++ b/code/modules/mob/living/silicon/ai/life.dm @@ -47,8 +47,8 @@ if(!lacks_power()) var/area/home = get_area(src) - if(home.powered(EQUIP)) - home.use_power(1000, EQUIP) + if(home.powered(AREA_USAGE_EQUIP)) + home.use_power(1000, AREA_USAGE_EQUIP) if(aiRestorePowerRoutine >= POWER_RESTORATION_SEARCH_APC) ai_restore_power() diff --git a/code/modules/modular_computers/hardware/recharger.dm b/code/modules/modular_computers/hardware/recharger.dm index 5c65d9d9a028..37a86e5b5947 100644 --- a/code/modules/modular_computers/hardware/recharger.dm +++ b/code/modules/modular_computers/hardware/recharger.dm @@ -41,8 +41,8 @@ if(!istype(A)) return FALSE - if(A.powered(EQUIP)) - A.use_power(amount, EQUIP) + if(A.powered(AREA_USAGE_EQUIP)) + A.use_power(amount, AREA_USAGE_EQUIP) return TRUE return FALSE diff --git a/code/modules/paperwork/photocopier.dm b/code/modules/paperwork/photocopier.dm index 331e1872b2b7..19bfc350c5e3 100644 --- a/code/modules/paperwork/photocopier.dm +++ b/code/modules/paperwork/photocopier.dm @@ -17,7 +17,7 @@ use_power = IDLE_POWER_USE idle_power_usage = 30 active_power_usage = 200 - power_channel = EQUIP + power_channel = AREA_USAGE_EQUIP max_integrity = 300 integrity_failure = 100 var/insert_anim = "photocopier1" diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index a09578c0b697..082fe37bfc95 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -1255,12 +1255,12 @@ force_update = 1 return - lastused_light = area.usage(STATIC_LIGHT) - lastused_light += area.usage(LIGHT) - lastused_equip = area.usage(EQUIP) - lastused_equip += area.usage(STATIC_EQUIP) - lastused_environ = area.usage(ENVIRON) - lastused_environ += area.usage(STATIC_ENVIRON) + lastused_light = area.usage(AREA_USAGE_STATIC_LIGHT) + lastused_light += area.usage(AREA_USAGE_LIGHT) + lastused_equip = area.usage(AREA_USAGE_EQUIP) + lastused_equip += area.usage(AREA_USAGE_STATIC_EQUIP) + lastused_environ = area.usage(AREA_USAGE_ENVIRON) + lastused_environ += area.usage(AREA_USAGE_STATIC_ENVIRON) area.clear_usage() lastused_total = lastused_light + lastused_equip + lastused_environ diff --git a/code/modules/power/gravitygenerator.dm b/code/modules/power/gravitygenerator.dm index 902f8528160f..7adcdd8e07cb 100644 --- a/code/modules/power/gravitygenerator.dm +++ b/code/modules/power/gravitygenerator.dm @@ -112,7 +112,7 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne icon_state = "on_8" idle_power_usage = 0 active_power_usage = 3000 - power_channel = ENVIRON + power_channel = AREA_USAGE_ENVIRON sprite_number = 8 use_power = IDLE_POWER_USE interaction_flags_machine = INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_OFFLINE diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm index 21102fbf7f5d..c40cc5eec4d0 100644 --- a/code/modules/power/lighting.dm +++ b/code/modules/power/lighting.dm @@ -218,7 +218,7 @@ use_power = ACTIVE_POWER_USE idle_power_usage = 2 active_power_usage = 20 - power_channel = LIGHT //Lights are calc'd via area so they dont need to be in the machine list + power_channel = AREA_USAGE_LIGHT //Lights are calc'd via area so they dont need to be in the machine list var/on = FALSE // 1 if on, 0 if off var/on_gs = FALSE var/forced_off = FALSE @@ -415,9 +415,9 @@ on_gs = on if(on) static_power_used = brightness * 20 //20W per unit luminosity - addStaticPower(static_power_used, STATIC_LIGHT) + addStaticPower(static_power_used, AREA_USAGE_STATIC_LIGHT) else - removeStaticPower(static_power_used, STATIC_LIGHT) + removeStaticPower(static_power_used, AREA_USAGE_STATIC_LIGHT) broken_sparks(start_only=TRUE) diff --git a/code/modules/security_levels/keycard_authentication.dm b/code/modules/security_levels/keycard_authentication.dm index abb96d99fb7a..fc4c858f33e1 100644 --- a/code/modules/security_levels/keycard_authentication.dm +++ b/code/modules/security_levels/keycard_authentication.dm @@ -12,7 +12,7 @@ GLOBAL_DATUM_INIT(keycard_events, /datum/events, new) use_power = IDLE_POWER_USE idle_power_usage = 2 active_power_usage = 6 - power_channel = ENVIRON + power_channel = AREA_USAGE_ENVIRON req_access = list(ACCESS_KEYCARD_AUTH) resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF var/datum/callback/ev diff --git a/yogstation/code/game/objects/structures/beds_chairs/electric_bed.dm b/yogstation/code/game/objects/structures/beds_chairs/electric_bed.dm index b17033d78e52..b91ca88bea9d 100644 --- a/yogstation/code/game/objects/structures/beds_chairs/electric_bed.dm +++ b/yogstation/code/game/objects/structures/beds_chairs/electric_bed.dm @@ -26,9 +26,9 @@ var/area/A = get_area(src) if(!isarea(A)) return - if(!A.powered(EQUIP)) + if(!A.powered(AREA_USAGE_EQUIP)) return - A.use_power(EQUIP, 5000) + A.use_power(AREA_USAGE_EQUIP, 5000) var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread s.set_up(12, 1, src) From b9874d5f67dab606f907746504b95eb060b670b4 Mon Sep 17 00:00:00 2001 From: Ling Date: Mon, 2 Jan 2023 16:03:54 +0200 Subject: [PATCH 04/13] Sparks and beams dont count towards lumcount --- code/__DEFINES/lighting.dm | 3 ++ code/datums/components/overlay_lighting.dm | 44 ++++++++++++++++--- .../effects/effect_system/effects_sparks.dm | 1 + code/modules/projectiles/projectile/beams.dm | 1 + 4 files changed, 42 insertions(+), 7 deletions(-) diff --git a/code/__DEFINES/lighting.dm b/code/__DEFINES/lighting.dm index c441d47f7e18..05b2b9a13013 100644 --- a/code/__DEFINES/lighting.dm +++ b/code/__DEFINES/lighting.dm @@ -8,6 +8,9 @@ ///Is a movable light source attached to another movable (its loc), meaning that the lighting component should go one level deeper. #define LIGHT_ATTACHED (1<<0) +///This light doesn't affect turf's lumcount calculations. Set to 1<<15 to ignore conflicts +#define LIGHT_NO_LUMCOUNT (1<<15) + //Bay lighting engine shit, not in /code/modules/lighting because BYOND is being shit about it /// frequency, in 1/10ths of a second, of the lighting process #define LIGHTING_INTERVAL 5 diff --git a/code/datums/components/overlay_lighting.dm b/code/datums/components/overlay_lighting.dm index 347808e07efd..ef9d52bf95a0 100644 --- a/code/datums/components/overlay_lighting.dm +++ b/code/datums/components/overlay_lighting.dm @@ -27,7 +27,9 @@ ///Ceiling of range, integer without decimal entries. var/lumcount_range = 0 ///How much this light affects the dynamic_lumcount of turfs. - var/lum_power = 0.5 + var/real_lum_power = 0.5 + ///The lum power being used + var/used_lum_power = 0.5 ///Transparency value. var/set_alpha = 0 ///For light sources that can be turned on and off. @@ -95,6 +97,9 @@ if(movable_parent.light_flags & LIGHT_ATTACHED) overlay_lighting_flags |= LIGHTING_ATTACHED set_parent_attached_to(ismovable(movable_parent.loc) ? movable_parent.loc : null) + if(movable_parent.light_flags & LIGHT_NO_LUMCOUNT) + overlay_lighting_flags |= LIGHT_NO_LUMCOUNT + set_lum_power(real_lum_power) check_holder() if(movable_parent.light_on) turn_on() @@ -130,7 +135,7 @@ /datum/component/overlay_lighting/proc/clean_old_turfs() for(var/t in affected_turfs) var/turf/lit_turf = t - lit_turf.dynamic_lumcount -= lum_power + lit_turf.dynamic_lumcount -= used_lum_power affected_turfs = null @@ -139,7 +144,7 @@ if(!current_holder) return for(var/turf/lit_turf in view(lumcount_range, get_turf(current_holder))) - lit_turf.dynamic_lumcount += lum_power + lit_turf.dynamic_lumcount += used_lum_power LAZYADD(affected_turfs, lit_turf) @@ -313,6 +318,16 @@ else if(movable_parent.light_flags & LIGHT_ATTACHED) //Lost the LIGHT_ATTACHED property. overlay_lighting_flags &= ~LIGHTING_ATTACHED set_parent_attached_to(null) + + if(new_value & LIGHT_NO_LUMCOUNT) + if(!(movable_parent.light_flags & LIGHT_NO_LUMCOUNT)) //Gained the NO_LUMCOUNT property + overlay_lighting_flags |= LIGHT_NO_LUMCOUNT + //Recalculate affecting + set_lum_power(real_lum_power) + else if(movable_parent.light_flags & LIGHT_NO_LUMCOUNT) //Lost the NO_LUMCOUNT property + overlay_lighting_flags &= ~LIGHT_NO_LUMCOUNT + //Recalculate affecting + set_lum_power(real_lum_power) ///Toggles the light on. @@ -337,11 +352,26 @@ ///Here we append the behavior associated to changing lum_power. /datum/component/overlay_lighting/proc/set_lum_power(new_lum_power) - if(lum_power == new_lum_power) + //Get the simulated luminosity count (If we have no lumcount, this is set to 0) + var/simulated_lum_power = new_lum_power + if(overlay_lighting_flags & LIGHT_NO_LUMCOUNT) + simulated_lum_power = 0 + //The new lum power is the same + if(used_lum_power == simulated_lum_power) + //This light doesn't affect lumcount, but lum_power must be updated regardless + if(new_lum_power != simulated_lum_power) + . = real_lum_power + real_lum_power = new_lum_power return - . = lum_power - lum_power = new_lum_power - var/difference = . - lum_power + //Set the return value to the old lum power + . = real_lum_power + real_lum_power = new_lum_power + //Get the old used lum power + var/old_lum_power = used_lum_power + used_lum_power = simulated_lum_power + //Calculate the difference + var/difference = old_lum_power - used_lum_power + //Apply it to any turf we are affecting for(var/t in affected_turfs) var/turf/lit_turf = t lit_turf.dynamic_lumcount -= difference diff --git a/code/game/objects/effects/effect_system/effects_sparks.dm b/code/game/objects/effects/effect_system/effects_sparks.dm index 224dfe7a492a..bcd82c07b1c2 100644 --- a/code/game/objects/effects/effect_system/effects_sparks.dm +++ b/code/game/objects/effects/effect_system/effects_sparks.dm @@ -24,6 +24,7 @@ light_range = 2 light_power = 0.5 light_color = LIGHT_COLOR_FIRE + light_flags = LIGHT_NO_LUMCOUNT /obj/effect/particle_effect/sparks/Initialize() ..() diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm index 8c3e8f701b00..060348d986ba 100644 --- a/code/modules/projectiles/projectile/beams.dm +++ b/code/modules/projectiles/projectile/beams.dm @@ -15,6 +15,7 @@ light_range = 2 light_power = 1 light_color = LIGHT_COLOR_RED + light_flags = LIGHT_NO_LUMCOUNT ricochets_max = 50 //Honk! ricochet_chance = 80 reflectable = REFLECT_NORMAL From 312093c019bf8d612d4bcf3099d81c90def5eed4 Mon Sep 17 00:00:00 2001 From: Ling Date: Mon, 2 Jan 2023 16:39:56 +0200 Subject: [PATCH 05/13] Fix rendering --- code/_onclick/hud/plane_master.dm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/_onclick/hud/plane_master.dm b/code/_onclick/hud/plane_master.dm index e1197a0ad176..77ecd40f69c8 100644 --- a/code/_onclick/hud/plane_master.dm +++ b/code/_onclick/hud/plane_master.dm @@ -76,9 +76,9 @@ /atom/movable/screen/plane_master/lighting/Initialize() . = ..() - //filters += filter(type="alpha", render_source=EMISSIVE_RENDER_TARGET, flags=MASK_INVERSE) - //filters += filter(type="alpha", render_source=EMISSIVE_UNBLOCKABLE_RENDER_TARGET, flags=MASK_INVERSE) - filters += filter(type="lighting", render_source = O_LIGHTING_VISUAL_RENDER_TARGET, flags = MASK_INVERSE) + filters += filter(type="alpha", render_source = EMISSIVE_RENDER_TARGET, flags = MASK_INVERSE) + filters += filter(type="alpha", render_source = EMISSIVE_UNBLOCKABLE_RENDER_TARGET, flags = MASK_INVERSE) + filters += filter(type="alpha", render_source = O_LIGHTING_VISUAL_RENDER_TARGET, flags = MASK_INVERSE) /** * Things placed on this mask the lighting plane. Doesn't render directly. From 03c6d9bce4c4384c4cfddd374027a3caf50ff84f Mon Sep 17 00:00:00 2001 From: Ling Date: Mon, 2 Jan 2023 17:32:01 +0200 Subject: [PATCH 06/13] Various fixes --- _maps/_basemap.dm | 2 +- code/game/machinery/dance_machine.dm | 165 ++++++++---------- code/game/machinery/hologram.dm | 10 +- .../mecha/equipment/weapons/melee_weapons.dm | 6 +- code/game/mecha/mecha.dm | 3 +- code/game/mecha/mecha_actions.dm | 4 +- code/game/objects/items/candle.dm | 8 +- code/game/objects/items/discoball.dm | 95 +++------- code/game/objects/items/tools/weldingtool.dm | 2 +- code/game/objects/items/twohanded.dm | 37 ++-- code/game/turfs/turf.dm | 2 +- code/modules/clothing/head/hardhat.dm | 4 +- code/modules/clothing/shoes/miscellaneous.dm | 4 +- code/modules/clothing/spacesuits/hardsuit.dm | 2 + code/modules/clothing/under/miscellaneous.dm | 8 +- .../carbon/human/species_types/jellypeople.dm | 2 +- .../human/species_types/shadowpeople.dm | 1 + .../living/simple_animal/hostile/hivebot.dm | 2 +- code/modules/projectiles/projectile/beams.dm | 1 - .../chemistry/reagents/food_reagents.dm | 2 +- code/modules/swarmers/swarmer.dm | 22 ++- yogstation/code/modules/clothing/chameleon.dm | 3 +- yogstation/code/modules/guardian/guardian.dm | 9 +- 23 files changed, 178 insertions(+), 216 deletions(-) diff --git a/_maps/_basemap.dm b/_maps/_basemap.dm index faf2c1a18c8b..150f974d7a53 100644 --- a/_maps/_basemap.dm +++ b/_maps/_basemap.dm @@ -1,4 +1,4 @@ -//#define LOWMEMORYMODE //uncomment this to load centcom and runtime station and thats it. +#define LOWMEMORYMODE //uncomment this to load centcom and runtime station and thats it. #include "map_files\generic\CentCom.dmm" diff --git a/code/game/machinery/dance_machine.dm b/code/game/machinery/dance_machine.dm index 13c3a293fb23..add9b62fbbcb 100644 --- a/code/game/machinery/dance_machine.dm +++ b/code/game/machinery/dance_machine.dm @@ -187,60 +187,28 @@ var/turf/cen = get_turf(src) FOR_DVIEW(var/turf/t, 3, get_turf(src),INVISIBILITY_LIGHTING) if(t.x == cen.x && t.y > cen.y) - var/obj/item/flashlight/spotlight/L = new /obj/item/flashlight/spotlight(t) - L.light_color = LIGHT_COLOR_RED - L.light_power = 30-(get_dist(src,L)*8) - L.light_range = 1+get_dist(src, L) - spotlights+=L + spotlights += new /obj/item/flashlight/spotlight(t, 1 + get_dist(src, t), 30 - (get_dist(src, t) * 8), LIGHT_COLOR_RED) continue if(t.x == cen.x && t.y < cen.y) - var/obj/item/flashlight/spotlight/L = new /obj/item/flashlight/spotlight(t) - L.light_color = LIGHT_COLOR_PURPLE - L.light_power = 30-(get_dist(src,L)*8) - L.light_range = 1+get_dist(src, L) - spotlights+=L + spotlights += new /obj/item/flashlight/spotlight(t, 1 + get_dist(src, t), 30 - (get_dist(src, t) * 8), LIGHT_COLOR_PURPLE) continue if(t.x > cen.x && t.y == cen.y) - var/obj/item/flashlight/spotlight/L = new /obj/item/flashlight/spotlight(t) - L.light_color = LIGHT_COLOR_YELLOW - L.light_power = 30-(get_dist(src,L)*8) - L.light_range = 1+get_dist(src, L) - spotlights+=L + spotlights += new /obj/item/flashlight/spotlight(t, 1 + get_dist(src, t), 30 - (get_dist(src, t) * 8), LIGHT_COLOR_YELLOW) continue if(t.x < cen.x && t.y == cen.y) - var/obj/item/flashlight/spotlight/L = new /obj/item/flashlight/spotlight(t) - L.light_color = LIGHT_COLOR_GREEN - L.light_power = 30-(get_dist(src,L)*8) - L.light_range = 1+get_dist(src, L) - spotlights+=L + spotlights += new /obj/item/flashlight/spotlight(t, 1 + get_dist(src, t), 30 - (get_dist(src, t) * 8), LIGHT_COLOR_GREEN) continue if((t.x+1 == cen.x && t.y+1 == cen.y) || (t.x+2==cen.x && t.y+2 == cen.y)) - var/obj/item/flashlight/spotlight/L = new /obj/item/flashlight/spotlight(t) - L.light_color = LIGHT_COLOR_ORANGE - L.light_power = 30-(get_dist(src,L)*8) - L.light_range = 1.4+get_dist(src, L) - spotlights+=L + spotlights += new /obj/item/flashlight/spotlight(t, 1.4 + get_dist(src, t), 30 - (get_dist(src, t) * 8), LIGHT_COLOR_ORANGE) continue if((t.x-1 == cen.x && t.y-1 == cen.y) || (t.x-2==cen.x && t.y-2 == cen.y)) - var/obj/item/flashlight/spotlight/L = new /obj/item/flashlight/spotlight(t) - L.light_color = LIGHT_COLOR_CYAN - L.light_power = 30-(get_dist(src,L)*8) - L.light_range = 1.4+get_dist(src, L) - spotlights+=L + spotlights += new /obj/item/flashlight/spotlight(t, 1.4 + get_dist(src, t), 30 - (get_dist(src, t) * 8), LIGHT_COLOR_CYAN) continue if((t.x-1 == cen.x && t.y+1 == cen.y) || (t.x-2==cen.x && t.y+2 == cen.y)) - var/obj/item/flashlight/spotlight/L = new /obj/item/flashlight/spotlight(t) - L.light_color = LIGHT_COLOR_BLUEGREEN - L.light_power = 30-(get_dist(src,L)*8) - L.light_range = 1.4+get_dist(src, L) - spotlights+=L + spotlights += new /obj/item/flashlight/spotlight(t, 1.4 + get_dist(src, t), 30 - (get_dist(src, t) * 8), LIGHT_COLOR_BLUEGREEN) continue if((t.x+1 == cen.x && t.y-1 == cen.y) || (t.x+2==cen.x && t.y-2 == cen.y)) - var/obj/item/flashlight/spotlight/L = new /obj/item/flashlight/spotlight(t) - L.light_color = LIGHT_COLOR_BLUE - L.light_power = 30-(get_dist(src,L)*8) - L.light_range = 1.4+get_dist(src, L) - spotlights+=L + spotlights += new /obj/item/flashlight/spotlight(t, 1.4 + get_dist(src, t), 30 - (get_dist(src, t) * 8), LIGHT_COLOR_BLUE) continue continue FOR_DVIEW_END @@ -272,61 +240,80 @@ sleep(0.7 SECONDS) if(selection.song_name == "Engineering's Ultimate High-Energy Hustle") sleep(28 SECONDS) - for(var/obj/reveal in sparkles) + for(var/s in sparkles) + var/obj/effect/overlay/sparkles/reveal = s reveal.alpha = 255 while(active) - for(var/obj/item/flashlight/spotlight/glow in spotlights) // The multiples reflects custom adjustments to each colors after dozens of tests - if(QDELETED(src) || !active || QDELETED(glow)) + for(var/g in spotlights) // The multiples reflects custom adjustments to each colors after dozens of tests + var/obj/item/flashlight/spotlight/glow = g + if(QDELETED(glow)) + stack_trace("[glow?.gc_destroyed ? "Qdeleting glow" : "null entry"] found in [src].[gc_destroyed ? " Source qdeleting at the time." : ""]") return - if(glow.light_color == LIGHT_COLOR_RED) - glow.light_color = LIGHT_COLOR_BLUE - glow.light_power = glow.light_power * 1.48 - glow.light_range = 0 - glow.update_light() - continue - if(glow.light_color == LIGHT_COLOR_BLUE) - glow.light_color = LIGHT_COLOR_GREEN - glow.light_range = glow.light_range * DISCO_INFENO_RANGE - glow.light_power = glow.light_power * 2 // Any changes to power must come in pairs to neutralize it for other colors - glow.update_light() - continue - if(glow.light_color == LIGHT_COLOR_GREEN) - glow.light_color = LIGHT_COLOR_ORANGE - glow.light_power = glow.light_power * 0.5 - glow.light_range = 0 - glow.update_light() - continue - if(glow.light_color == LIGHT_COLOR_ORANGE) - glow.light_color = LIGHT_COLOR_PURPLE - glow.light_power = glow.light_power * 2.27 - glow.light_range = glow.light_range * DISCO_INFENO_RANGE - glow.update_light() - continue - if(glow.light_color == LIGHT_COLOR_PURPLE) - glow.light_color = LIGHT_COLOR_BLUEGREEN - glow.light_power = glow.light_power * 0.44 - glow.light_range = 0 - glow.update_light() - continue - if(glow.light_color == LIGHT_COLOR_BLUEGREEN) - glow.light_color = LIGHT_COLOR_YELLOW - glow.light_range = glow.light_range * DISCO_INFENO_RANGE - glow.update_light() - continue - if(glow.light_color == LIGHT_COLOR_YELLOW) - glow.light_color = LIGHT_COLOR_CYAN - glow.light_range = 0 - glow.update_light() - continue - if(glow.light_color == LIGHT_COLOR_CYAN) - glow.light_color = LIGHT_COLOR_RED - glow.light_power = glow.light_power * 0.68 - glow.light_range = glow.light_range * DISCO_INFENO_RANGE - glow.update_light() - continue + switch(glow.light_color) + if(LIGHT_COLOR_RED) + if(glow.even_cycle) + glow.set_light_on(FALSE) + glow.set_light_color(LIGHT_COLOR_BLUE) + else + glow.set_light_range_power_color(glow.base_light_range * DISCO_INFENO_RANGE, glow.light_power * 1.48, LIGHT_COLOR_BLUE) + glow.set_light_on(TRUE) + if(LIGHT_COLOR_BLUE) + if(glow.even_cycle) + glow.set_light_range_power_color(glow.base_light_range * DISCO_INFENO_RANGE, glow.light_power * 2, LIGHT_COLOR_GREEN) + glow.set_light_on(TRUE) + else + glow.set_light_on(FALSE) + glow.set_light_color(LIGHT_COLOR_GREEN) + if(LIGHT_COLOR_GREEN) + if(glow.even_cycle) + glow.set_light_on(FALSE) + glow.set_light_color(LIGHT_COLOR_ORANGE) + else + glow.set_light_range_power_color(glow.base_light_range * DISCO_INFENO_RANGE, glow.light_power * 0.5, LIGHT_COLOR_ORANGE) + glow.set_light_on(TRUE) + if(LIGHT_COLOR_ORANGE) + if(glow.even_cycle) + glow.set_light_range_power_color(glow.base_light_range * DISCO_INFENO_RANGE, glow.light_power * 2.27, LIGHT_COLOR_PURPLE) + glow.set_light_on(TRUE) + else + glow.set_light_on(FALSE) + glow.set_light_color(LIGHT_COLOR_PURPLE) + if(LIGHT_COLOR_PURPLE) + if(glow.even_cycle) + glow.set_light_on(FALSE) + glow.set_light_color(LIGHT_COLOR_BLUEGREEN) + else + glow.set_light_range_power_color(glow.base_light_range * DISCO_INFENO_RANGE, glow.light_power * 0.44, LIGHT_COLOR_BLUEGREEN) + glow.set_light_on(TRUE) + if(LIGHT_COLOR_BLUEGREEN) + if(glow.even_cycle) + glow.set_light_range(glow.base_light_range * DISCO_INFENO_RANGE) + glow.set_light_color(LIGHT_COLOR_YELLOW) + glow.set_light_on(TRUE) + else + glow.set_light_on(FALSE) + glow.set_light_color(LIGHT_COLOR_YELLOW) + if(LIGHT_COLOR_YELLOW) + if(glow.even_cycle) + glow.set_light_on(FALSE) + glow.set_light_color(LIGHT_COLOR_CYAN) + else + glow.set_light_range(glow.base_light_range * DISCO_INFENO_RANGE) + glow.set_light_color(LIGHT_COLOR_CYAN) + glow.set_light_on(TRUE) + if(LIGHT_COLOR_CYAN) + if(glow.even_cycle) + glow.set_light_range_power_color(glow.base_light_range * DISCO_INFENO_RANGE, glow.light_power * 0.68, LIGHT_COLOR_RED) + glow.set_light_on(TRUE) + else + glow.set_light_on(FALSE) + glow.set_light_color(LIGHT_COLOR_RED) + glow.even_cycle = !glow.even_cycle if(prob(2)) // Unique effects for the dance floor that show up randomly to mix things up INVOKE_ASYNC(src, .proc/hierofunk) sleep(selection.song_beat) + if(QDELETED(src)) + return #undef DISCO_INFENO_RANGE diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index 87ef262352f8..c122e183cb1e 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -427,7 +427,6 @@ obj/machinery/holopad/secure/Initialize() Hologram.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them. Hologram.setAnchored(TRUE)//So space wind cannot drag it. Hologram.name = "[user.name] (Hologram)"//If someone decides to right click. - Hologram.set_light(2) //hologram lighting move_hologram() if(AI) @@ -465,9 +464,9 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ use_power = total_users > 0 ? ACTIVE_POWER_USE : IDLE_POWER_USE active_power_usage = HOLOPAD_PASSIVE_POWER_USAGE + (HOLOGRAM_POWER_USAGE * total_users) if(total_users || replay_mode) - set_light(2) + set_light_on(TRUE) else - set_light(0) + set_light_on(FALSE) update_icon() /obj/machinery/holopad/update_icon() @@ -599,7 +598,6 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ Hologram.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them. Hologram.setAnchored(TRUE)//So space wind cannot drag it. Hologram.name = "[record.caller_name] (Hologram)"//If someone decides to right click. - Hologram.set_light(2) //hologram lighting visible_message(span_notice("A holographic image of [record.caller_name] flickers to life before your eyes!")) return Hologram @@ -701,6 +699,10 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ var/mob/living/Impersonation var/datum/holocall/HC + light_system = MOVABLE_LIGHT + light_range = 4 + light_color = COLOR_CYAN + /obj/effect/overlay/holo_pad_hologram/Destroy() Impersonation = null if(!QDELETED(HC)) diff --git a/code/game/mecha/equipment/weapons/melee_weapons.dm b/code/game/mecha/equipment/weapons/melee_weapons.dm index a20b32014e92..ad5f790c20d2 100644 --- a/code/game/mecha/equipment/weapons/melee_weapons.dm +++ b/code/game/mecha/equipment/weapons/melee_weapons.dm @@ -190,6 +190,8 @@ structure_damage_mult = 4 //Think obi-wan cutting through a bulkhead with his lightsaber but he's a giant mech with a huge terrifying axe minimum_damage = 40 attack_speed_modifier = 1.5 //Kinda chunky + light_system = MOVABLE_LIGHT + light_range = 5 light_color = LIGHT_COLOR_RED /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/energy_axe/cleave_attack() //Mostly copy-pasted sword cleave code with minor tweaks. @@ -239,11 +241,11 @@ /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/energy_axe/on_select() START_PROCESSING(SSobj, src) - set_light(5) + set_light_on(TRUE) /obj/item/mecha_parts/mecha_equipment/melee_weapon/sword/energy_axe/on_deselect() STOP_PROCESSING(SSobj, src) - set_light(0) + set_light_on(FALSE) /obj/item/mecha_parts/mecha_equipment/melee_weapon/rocket_fist //Passive upgrade weapon when selected, makes your mech punch harder AND faster name = "\improper DD-2 \"Atom Smasher\" rocket fist" diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 2599967a5748..94f3a05671c2 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -23,6 +23,8 @@ infra_luminosity = 15 //byond implementation is bugged. force = 5 light_system = MOVABLE_LIGHT + light_range = 3 + light_power = 6 light_on = FALSE flags_1 = HEAR_1 var/ruin_mecha = FALSE //if the mecha starts on a ruin, don't automatically give it a tracking beacon to prevent metagaming. @@ -50,7 +52,6 @@ var/list/proc_res = list() //stores proc owners, like proc_res["functionname"] = owner reference var/datum/effect_system/spark_spread/spark_system = new var/lights = FALSE - var/lights_power = 6 var/last_user_hud = 1 // used to show/hide the mecha hud while preserving previous preference var/completely_disabled = FALSE //stops the mech from doing anything var/omnidirectional_attacks = FALSE //lets mech shoot anywhere, not just in front of it diff --git a/code/game/mecha/mecha_actions.dm b/code/game/mecha/mecha_actions.dm index 3d5ffb1bdf51..13a083c53220 100644 --- a/code/game/mecha/mecha_actions.dm +++ b/code/game/mecha/mecha_actions.dm @@ -114,10 +114,10 @@ return chassis.lights = !chassis.lights if(chassis.lights) - chassis.set_light(chassis.lights_power) + chassis.set_light_on(TRUE) button_icon_state = "mech_lights_on" else - chassis.set_light(-chassis.lights_power) + chassis.set_light_on(FALSE) button_icon_state = "mech_lights_off" chassis.occupant_message("Toggled lights [chassis.lights?"on":"off"].") chassis.log_message("Toggled lights [chassis.lights?"on":"off"].", LOG_MECHA) diff --git a/code/game/objects/items/candle.dm b/code/game/objects/items/candle.dm index ee00770982ca..02eb881d0529 100644 --- a/code/game/objects/items/candle.dm +++ b/code/game/objects/items/candle.dm @@ -1,4 +1,5 @@ #define CANDLE_LUMINOSITY 2 + /obj/item/candle name = "red candle" desc = "In Greek myth, Prometheus stole fire from the Gods and gave it to \ @@ -7,7 +8,10 @@ icon_state = "candle1" item_state = "candle1" w_class = WEIGHT_CLASS_TINY + light_system = MOVABLE_LIGHT + light_range = CANDLE_LUMINOSITY light_color = LIGHT_COLOR_FIRE + light_on = FALSE heat = 1000 var/wax = 1000 var/lit = FALSE @@ -43,7 +47,7 @@ lit = TRUE if(show_message) usr.visible_message(show_message) - set_light(CANDLE_LUMINOSITY) + set_light_on(TRUE) START_PROCESSING(SSobj, src) update_icon() @@ -52,7 +56,7 @@ return lit = FALSE update_icon() - set_light(0) + set_light_on(FALSE) return TRUE /obj/item/candle/extinguish() diff --git a/code/game/objects/items/discoball.dm b/code/game/objects/items/discoball.dm index 6ec6bdd235eb..d466372c782d 100644 --- a/code/game/objects/items/discoball.dm +++ b/code/game/objects/items/discoball.dm @@ -100,60 +100,28 @@ var/turf/cen = get_turf(src) FOR_DVIEW(var/turf/t, 3, get_turf(src),INVISIBILITY_LIGHTING) if(t.x == cen.x && t.y > cen.y) - var/obj/item/flashlight/spotlight/L = new /obj/item/flashlight/spotlight(t) - L.light_color = LIGHT_COLOR_RED - L.light_power = 30-(get_dist(src,L)*8) - L.light_range = 1+get_dist(src, L) - spotlights+=L + spotlights += new /obj/item/flashlight/spotlight(t, 1 + get_dist(src, t), 30 - (get_dist(src, t) * 8), LIGHT_COLOR_RED) continue if(t.x == cen.x && t.y < cen.y) - var/obj/item/flashlight/spotlight/L = new /obj/item/flashlight/spotlight(t) - L.light_color = LIGHT_COLOR_PURPLE - L.light_power = 30-(get_dist(src,L)*8) - L.light_range = 1+get_dist(src, L) - spotlights+=L + spotlights += new /obj/item/flashlight/spotlight(t, 1 + get_dist(src, t), 30 - (get_dist(src, t) * 8), LIGHT_COLOR_PURPLE) continue if(t.x > cen.x && t.y == cen.y) - var/obj/item/flashlight/spotlight/L = new /obj/item/flashlight/spotlight(t) - L.light_color = LIGHT_COLOR_YELLOW - L.light_power = 30-(get_dist(src,L)*8) - L.light_range = 1+get_dist(src, L) - spotlights+=L + spotlights += new /obj/item/flashlight/spotlight(t, 1 + get_dist(src, t), 30 - (get_dist(src, t) * 8), LIGHT_COLOR_YELLOW) continue if(t.x < cen.x && t.y == cen.y) - var/obj/item/flashlight/spotlight/L = new /obj/item/flashlight/spotlight(t) - L.light_color = LIGHT_COLOR_GREEN - L.light_power = 30-(get_dist(src,L)*8) - L.light_range = 1+get_dist(src, L) - spotlights+=L + spotlights += new /obj/item/flashlight/spotlight(t, 1 + get_dist(src, t), 30 - (get_dist(src, t) * 8), LIGHT_COLOR_GREEN) continue if((t.x+1 == cen.x && t.y+1 == cen.y) || (t.x+2==cen.x && t.y+2 == cen.y)) - var/obj/item/flashlight/spotlight/L = new /obj/item/flashlight/spotlight(t) - L.light_color = LIGHT_COLOR_ORANGE - L.light_power = 30-(get_dist(src,L)*8) - L.light_range = 1.4+get_dist(src, L) - spotlights+=L + spotlights += new /obj/item/flashlight/spotlight(t, 1.4 + get_dist(src, t), 30 - (get_dist(src, t) * 8), LIGHT_COLOR_ORANGE) continue if((t.x-1 == cen.x && t.y-1 == cen.y) || (t.x-2==cen.x && t.y-2 == cen.y)) - var/obj/item/flashlight/spotlight/L = new /obj/item/flashlight/spotlight(t) - L.light_color = LIGHT_COLOR_CYAN - L.light_power = 30-(get_dist(src,L)*8) - L.light_range = 1.4+get_dist(src, L) - spotlights+=L + spotlights += new /obj/item/flashlight/spotlight(t, 1.4 + get_dist(src, t), 30 - (get_dist(src, t) * 8), LIGHT_COLOR_CYAN) continue if((t.x-1 == cen.x && t.y+1 == cen.y) || (t.x-2==cen.x && t.y+2 == cen.y)) - var/obj/item/flashlight/spotlight/L = new /obj/item/flashlight/spotlight(t) - L.light_color = LIGHT_COLOR_BLUEGREEN - L.light_power = 30-(get_dist(src,L)*8) - L.light_range = 1.4+get_dist(src, L) - spotlights+=L + spotlights += new /obj/item/flashlight/spotlight(t, 1.4 + get_dist(src, t), 30 - (get_dist(src, t) * 8), LIGHT_COLOR_BLUEGREEN) continue if((t.x+1 == cen.x && t.y-1 == cen.y) || (t.x+2==cen.x && t.y-2 == cen.y)) - var/obj/item/flashlight/spotlight/L = new /obj/item/flashlight/spotlight(t) - L.light_color = LIGHT_COLOR_BLUE - L.light_power = 30-(get_dist(src,L)*8) - L.light_range = 1.4+get_dist(src, L) - spotlights+=L + spotlights += new /obj/item/flashlight/spotlight(t, 1.4 + get_dist(src, t), 30 - (get_dist(src, t) * 8), LIGHT_COLOR_BLUE) continue continue FOR_DVIEW_END @@ -183,57 +151,38 @@ S.pixel_y = 7 S.forceMove(get_turf(src)) sleep(0.7 SECONDS) - for(var/obj/reveal in sparkles) + for(var/s in sparkles) + var/obj/effect/overlay/sparkles/reveal = s reveal.alpha = 255 while(TurnedOn) - for(var/obj/item/flashlight/spotlight/glow in spotlights) // The multiples reflects custom adjustments to each colors after dozens of tests - if(QDELETED(src) || !TurnedOn || QDELETED(glow)) + for(var/g in spotlights) // The multiples reflects custom adjustments to each colors after dozens of tests + var/obj/item/flashlight/spotlight/glow = g + if(QDELETED(glow)) + stack_trace("[glow?.gc_destroyed ? "Qdeleting glow" : "null entry"] found in [src].[gc_destroyed ? " Source qdeleting at the time." : ""]") return if(glow.light_color == LIGHT_COLOR_RED) - glow.light_color = LIGHT_COLOR_BLUE - glow.light_power = glow.light_power * 1.48 - glow.light_range = 0 - glow.update_light() + glow.set_light_range_power_color(0, glow.light_power * 1.48, LIGHT_COLOR_BLUE) continue if(glow.light_color == LIGHT_COLOR_BLUE) - glow.light_color = LIGHT_COLOR_GREEN - glow.light_range = glow.light_range * DISCO_INFENO_RANGE - glow.light_power = glow.light_power * 2 // Any changes to power must come in pairs to neutralize it for other colors - glow.update_light() + glow.set_light_range_power_color(glow.light_range * DISCO_INFENO_RANGE, glow.light_power * 2, LIGHT_COLOR_GREEN) continue if(glow.light_color == LIGHT_COLOR_GREEN) - glow.light_color = LIGHT_COLOR_ORANGE - glow.light_power = glow.light_power * 0.5 - glow.light_range = 0 - glow.update_light() + glow.set_light_range_power_color(0, glow.light_power * 0.5, LIGHT_COLOR_ORANGE) continue if(glow.light_color == LIGHT_COLOR_ORANGE) - glow.light_color = LIGHT_COLOR_PURPLE - glow.light_power = glow.light_power * 2.27 - glow.light_range = glow.light_range * DISCO_INFENO_RANGE - glow.update_light() + glow.set_light_range_power_color(glow.light_range * DISCO_INFENO_RANGE, glow.light_power * 2.27, LIGHT_COLOR_PURPLE) continue if(glow.light_color == LIGHT_COLOR_PURPLE) - glow.light_color = LIGHT_COLOR_BLUEGREEN - glow.light_power = glow.light_power * 0.44 - glow.light_range = 0 - glow.update_light() + glow.set_light_range_power_color(0, glow.light_power * 0.44, LIGHT_COLOR_BLUEGREEN) continue if(glow.light_color == LIGHT_COLOR_BLUEGREEN) - glow.light_color = LIGHT_COLOR_YELLOW - glow.light_range = glow.light_range * DISCO_INFENO_RANGE - glow.update_light() + glow.set_light_range_power_color(glow.light_range * DISCO_INFENO_RANGE, glow.light_power, LIGHT_COLOR_YELLOW) continue if(glow.light_color == LIGHT_COLOR_YELLOW) - glow.light_color = LIGHT_COLOR_CYAN - glow.light_range = 0 - glow.update_light() + glow.set_light_range_power_color(0, glow.light_power, LIGHT_COLOR_CYAN) continue if(glow.light_color == LIGHT_COLOR_CYAN) - glow.light_color = LIGHT_COLOR_RED - glow.light_power = glow.light_power * 0.68 - glow.light_range = glow.light_range * DISCO_INFENO_RANGE - glow.update_light() + glow.set_light_range_power_color(glow.light_range * DISCO_INFENO_RANGE, glow.light_power * 0.68, LIGHT_COLOR_RED) continue if(prob(2)) // Unique effects for the dance floor that show up randomly to mix things up INVOKE_ASYNC(src, .proc/hierofunk) diff --git a/code/game/objects/items/tools/weldingtool.dm b/code/game/objects/items/tools/weldingtool.dm index 5e1102325c7b..11af3390f35c 100644 --- a/code/game/objects/items/tools/weldingtool.dm +++ b/code/game/objects/items/tools/weldingtool.dm @@ -140,7 +140,7 @@ var/turf/location = get_turf(user) location.hotspot_expose(700, 50, 1) if(get_fuel() <= 0) - set_light(0) + set_light_on(FALSE) if(isliving(O)) var/mob/living/L = O diff --git a/code/game/objects/items/twohanded.dm b/code/game/objects/items/twohanded.dm index deb68f952ee3..105edd5390f7 100644 --- a/code/game/objects/items/twohanded.dm +++ b/code/game/objects/items/twohanded.dm @@ -310,16 +310,15 @@ hitsound = "swing_hit" armour_penetration = 35 var/saber_color = "green" - light_color = "#00ff00"//green attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") block_chance = 75 max_integrity = 200 armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, RAD = 0, FIRE = 100, ACID = 70) resistance_flags = FIRE_PROOF light_system = MOVABLE_LIGHT - light_range = 6 - light_power = 1 - light_on = TRUE + light_range = 6 //TWICE AS BRIGHT AS A REGULAR ESWORD + light_color = "#00ff00" //green + light_on = FALSE wound_bonus = -10 bare_wound_bonus = 20 var/hacked = FALSE @@ -354,15 +353,17 @@ . = ..() if(LAZYLEN(possible_colors)) saber_color = pick(possible_colors) + var/new_color switch(saber_color) if("red") - light_color = LIGHT_COLOR_RED + new_color = LIGHT_COLOR_RED if("green") - light_color = LIGHT_COLOR_GREEN + new_color = LIGHT_COLOR_GREEN if("blue") - light_color = LIGHT_COLOR_LIGHT_CYAN + new_color = LIGHT_COLOR_LIGHT_CYAN if("purple") - light_color = LIGHT_COLOR_LAVENDER + new_color = LIGHT_COLOR_LAVENDER + set_light_color(new_color) /obj/item/twohanded/dualsaber/Destroy() STOP_PROCESSING(SSobj, src) @@ -423,7 +424,7 @@ w_class = w_class_on hitsound = 'sound/weapons/blade1.ogg' START_PROCESSING(SSobj, src) - set_light(TRUE) + set_light_on(TRUE) /obj/item/twohanded/dualsaber/unwield() //Specific unwield () to switch hitsounds. sharpness = initial(sharpness) @@ -431,7 +432,7 @@ ..() hitsound = "swing_hit" STOP_PROCESSING(SSobj, src) - set_light(FALSE) + set_light_on(FALSE) /obj/item/twohanded/dualsaber/process() if(wielded) @@ -754,10 +755,10 @@ force = 19 throwforce = 24 force_wielded = 6 - -/obj/item/twohanded/pitchfork/demonic/Initialize() - . = ..() - set_light(3,6,LIGHT_COLOR_RED) + light_system = MOVABLE_LIGHT + light_range = 3 + light_power = 6 + light_color = LIGHT_COLOR_RED /obj/item/twohanded/pitchfork/demonic/greater force = 24 @@ -994,6 +995,10 @@ w_class = WEIGHT_CLASS_HUGE slot_flags = ITEM_SLOT_BACK actions_types = list(/datum/action/item_action/charge_hammer) + light_system = MOVABLE_LIGHT + light_color = LIGHT_COLOR_LIGHT_CYAN + light_range = 2 + light_power = 2 var/datum/effect_system/spark_spread/spark_system //It's a surprise tool that'll help us later var/charging = FALSE var/supercharged = FALSE @@ -1045,11 +1050,11 @@ /obj/item/twohanded/vxtvulhammer/proc/supercharge() //Proc to handle when it's charged for light + sprite + damage supercharged = !supercharged if(supercharged) - set_light(2) //Glows when charged + set_light_on(TRUE) //Glows when charged force = initial(force) + (wielded ? force_wielded : 0) + 12 //12 additional damage for a total of 40 has to be a massively irritating check because of how force_wielded works armour_penetration = 100 else - set_light(0) + set_light_on(TRUE) force = initial(force) + (wielded ? force_wielded : 0) armour_penetration = initial(armour_penetration) update_icon() diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index b54880c5725e..d3b970f48426 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -71,7 +71,7 @@ GLOBAL_LIST_EMPTY(station_turfs) if(color) add_atom_colour(color, FIXED_COLOUR_PRIORITY) - if (light_power && light_range) + if (light_system == STATIC_LIGHT && light_power && light_range) update_light() var/turf/T = SSmapping.get_turf_above(src) diff --git a/code/modules/clothing/head/hardhat.dm b/code/modules/clothing/head/hardhat.dm index f33955cfece0..a093f75b13bb 100644 --- a/code/modules/clothing/head/hardhat.dm +++ b/code/modules/clothing/head/hardhat.dm @@ -4,8 +4,6 @@ icon_state = "hardhat0_yellow" item_state = "hardhat0_yellow" mob_overlay_icon = 'icons/mob/clothing/head/head.dmi' - //Determines used sprites: hardhat[on]_[hat_type] - var/hat_type = "yellow" armor = list(MELEE = 15, BULLET = 5, LASER = 20, ENERGY = 10, BOMB = 20, BIO = 10, RAD = 20, FIRE = 100, ACID = 50, WOUND = 10) flags_inv = 0 actions_types = list(/datum/action/item_action/toggle_helmet_light) @@ -19,6 +17,8 @@ dog_fashion = /datum/dog_fashion/head + //Determines used sprites: hardhat[on]_[hat_type] + var/hat_type = "yellow" ///Whether the headlamp is on or off. var/on = FALSE diff --git a/code/modules/clothing/shoes/miscellaneous.dm b/code/modules/clothing/shoes/miscellaneous.dm index 62900468a578..88a9237e02e1 100644 --- a/code/modules/clothing/shoes/miscellaneous.dm +++ b/code/modules/clothing/shoes/miscellaneous.dm @@ -336,12 +336,12 @@ icon_state = "kindleKicks" item_state = "kindleKicks" actions_types = list(/datum/action/item_action/kindleKicks) - var/lightCycle = 0 - var/active = FALSE light_system = MOVABLE_LIGHT light_range = 2 light_power = 3 light_on = FALSE + var/lightCycle = 0 + var/active = FALSE /obj/item/clothing/shoes/kindleKicks/ui_action_click(mob/user, action) if(active) diff --git a/code/modules/clothing/spacesuits/hardsuit.dm b/code/modules/clothing/spacesuits/hardsuit.dm index 9c1b00e5f0dc..b7c487a80b45 100644 --- a/code/modules/clothing/spacesuits/hardsuit.dm +++ b/code/modules/clothing/spacesuits/hardsuit.dm @@ -6,6 +6,7 @@ item_state = "eng_helm" max_integrity = 300 armor = list(MELEE = 10, BULLET = 5, LASER = 10, ENERGY = 5, BOMB = 10, BIO = 100, RAD = 75, FIRE = 50, ACID = 75) + light_system = MOVABLE_LIGHT light_range = 4 light_power = 1 light_on = FALSE @@ -428,6 +429,7 @@ icon_state = "carp_helm" item_state = "syndicate" armor = list(MELEE = -20, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 100, RAD = 75, FIRE = 60, ACID = 75) //As whimpy as a space carp + light_system = NO_LIGHT_SUPPORT light_range = 0 //luminosity when on actions_types = list() diff --git a/code/modules/clothing/under/miscellaneous.dm b/code/modules/clothing/under/miscellaneous.dm index 75c22a7ed523..1fde8cf7e107 100644 --- a/code/modules/clothing/under/miscellaneous.dm +++ b/code/modules/clothing/under/miscellaneous.dm @@ -816,7 +816,9 @@ item_state = "lampskirt_male" body_parts_covered = CHEST|GROIN|LEGS|FEET can_adjust = FALSE - var/brightness_on = 1 //luminosity when the light is on + light_system = MOVABLE_LIGHT + light_range = 2 + light_on = FALSE var/on = FALSE actions_types = list(/datum/action/item_action/toggle_helmet_light) @@ -827,10 +829,10 @@ user.update_inv_w_uniform() //So the mob overlay updates if(on) - set_light(brightness_on) + set_light_on(TRUE) user.visible_message(span_notice("[user] discreetly pulls a cord for the bulbs under [user.p_their()] skirt, turning [user.p_them()] on.")) else - set_light(0) + set_light_on(FALSE) for(var/X in actions) var/datum/action/A=X diff --git a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm index 687d4c154759..a48e4a53db86 100644 --- a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm @@ -427,7 +427,7 @@ /datum/species/jelly/luminescent/proc/update_glow(mob/living/carbon/C, intensity) if(intensity) glow_intensity = intensity - glow.set_light(glow_intensity, glow_intensity, C.dna.features["mcolor"]) + glow.set_light_range_power_color(glow_intensity, glow_intensity, C.dna.features["mcolor"]) /obj/effect/dummy/luminescent_glow name = "luminescent glow" diff --git a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm index ca4dc0ed64a1..cb2616228e44 100644 --- a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm @@ -212,6 +212,7 @@ if(istype(O, /obj/item/pda)) var/obj/item/pda/PDA = O PDA.set_light_on(FALSE) + PDA.set_light_range(0) //It won't be turning on again. PDA.update_icon() visible_message(span_danger("The light in [PDA] shorts out!")) else diff --git a/code/modules/mob/living/simple_animal/hostile/hivebot.dm b/code/modules/mob/living/simple_animal/hostile/hivebot.dm index 12d5f2485d8f..8b7b4622573c 100644 --- a/code/modules/mob/living/simple_animal/hostile/hivebot.dm +++ b/code/modules/mob/living/simple_animal/hostile/hivebot.dm @@ -60,7 +60,7 @@ QDEL_NULL(alert_light) if(a_intent != INTENT_HELP) icon_state = "[initial(icon_state)]_attack" - alert_light = mob_light(COLOR_RED_LIGHT, 6, 0.4) + alert_light = mob_light(6, 0.4, COLOR_RED_LIGHT) else icon_state = initial(icon_state) diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm index 060348d986ba..ec00a7b45f19 100644 --- a/code/modules/projectiles/projectile/beams.dm +++ b/code/modules/projectiles/projectile/beams.dm @@ -13,7 +13,6 @@ impact_effect_type = /obj/effect/temp_visual/impact_effect/red_laser light_system = MOVABLE_LIGHT light_range = 2 - light_power = 1 light_color = LIGHT_COLOR_RED light_flags = LIGHT_NO_LUMCOUNT ricochets_max = 50 //Honk! diff --git a/code/modules/reagents/chemistry/reagents/food_reagents.dm b/code/modules/reagents/chemistry/reagents/food_reagents.dm index 46bb9d062199..926cd63242aa 100644 --- a/code/modules/reagents/chemistry/reagents/food_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/food_reagents.dm @@ -700,11 +700,11 @@ remove_reagent_light(M) /datum/reagent/consumable/tinlux/proc/on_living_holder_deletion(mob/living/source) - remove_reagent_light(source) /datum/reagent/consumable/tinlux/proc/add_reagent_light(mob/living/living_holder) var/obj/effect/dummy/lighting_obj/moblight/mob_light_obj = living_holder.mob_light(2) + mob_light_obj.set_light_color("#b5a213") LAZYSET(mobs_affected, living_holder, mob_light_obj) RegisterSignal(living_holder, COMSIG_PARENT_QDELETING, .proc/on_living_holder_deletion) diff --git a/code/modules/swarmers/swarmer.dm b/code/modules/swarmers/swarmer.dm index 455d8b7d5a49..7dff5c7f2d2e 100644 --- a/code/modules/swarmers/swarmer.dm +++ b/code/modules/swarmers/swarmer.dm @@ -402,20 +402,26 @@ * Proc used to allow a swarmer to toggle its light on and off. If a swarmer has any drones, change their light settings to match their master's. */ /mob/living/simple_animal/hostile/swarmer/proc/toggle_light() - if(!light_range) - set_light_on(TRUE) - if(!mind) - return - for(var/d in dronelist) - var/mob/living/simple_animal/hostile/swarmer/melee/drone = d - drone.set_light_on(TRUE) - else + if(swarmer_flags & SWARMER_LIGHT_ON) + swarmer_flags = ~SWARMER_LIGHT_ON set_light_on(FALSE) if(!mind) return for(var/d in dronelist) var/mob/living/simple_animal/hostile/swarmer/melee/drone = d + drone.swarmer_flags = ~SWARMER_LIGHT_ON drone.set_light_on(FALSE) + return + swarmer_flags |= SWARMER_LIGHT_ON + set_light_on(TRUE) + if(!mind) + return + + for(var/d in dronelist) + var/mob/living/simple_animal/hostile/swarmer/melee/drone = d + drone.swarmer_flags |= SWARMER_LIGHT_ON + drone.set_light_on(TRUE) + balloon_alert(src, "light toggled") /** diff --git a/yogstation/code/modules/clothing/chameleon.dm b/yogstation/code/modules/clothing/chameleon.dm index 95b215ebc806..74215f6649ea 100644 --- a/yogstation/code/modules/clothing/chameleon.dm +++ b/yogstation/code/modules/clothing/chameleon.dm @@ -1,5 +1,4 @@ /datum/action/item_action/chameleon/change/update_item(obj/item/picked_item, obj/item/target = target) ..() if(ispath(picked_item, /obj/item/pda) && istype(target, /obj/item/pda)) - target.light_color = initial(picked_item.light_color) - target.update_light() + target.set_light_color(initial(picked_item.light_color)) diff --git a/yogstation/code/modules/guardian/guardian.dm b/yogstation/code/modules/guardian/guardian.dm index bc8349ec17ac..a4b0e6bb390d 100644 --- a/yogstation/code/modules/guardian/guardian.dm +++ b/yogstation/code/modules/guardian/guardian.dm @@ -44,6 +44,9 @@ GLOBAL_LIST_INIT(guardian_projectile_damage, list( melee_damage_lower = 15 melee_damage_upper = 15 AIStatus = AI_OFF + light_system = MOVABLE_LIGHT + light_range = 3 + light_on = FALSE hud_type = /datum/hud/guardian see_in_dark = 8 var/list/barrier_images = list() @@ -547,12 +550,12 @@ GLOBAL_LIST_INIT(guardian_projectile_damage, list( cooldown = world.time + 10 /mob/living/simple_animal/hostile/guardian/proc/ToggleLight() - if (light_range<3) + if(!light_on) to_chat(src, span_notice("You activate your light.")) - set_light(3) + set_light_on(TRUE) else to_chat(src, span_notice("You deactivate your light.")) - set_light(0) + set_light_on(FALSE) /mob/living/simple_animal/hostile/guardian/verb/show_detail() set name = "Show Powers" From d92b20b8a07949e5078d15e38dd749b8eb356f99 Mon Sep 17 00:00:00 2001 From: Ling Date: Mon, 2 Jan 2023 17:39:20 +0200 Subject: [PATCH 07/13] Fix errors --- code/game/mecha/mecha_actions.dm | 3 +-- code/game/mecha/working/clarke.dm | 2 +- code/game/mecha/working/ripley.dm | 6 +++--- code/modules/swarmers/swarmer.dm | 4 ++-- yogstation/code/game/mecha/makeshift/lockermech.dm | 2 +- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/code/game/mecha/mecha_actions.dm b/code/game/mecha/mecha_actions.dm index 13a083c53220..0da03b6a7e20 100644 --- a/code/game/mecha/mecha_actions.dm +++ b/code/game/mecha/mecha_actions.dm @@ -114,11 +114,10 @@ return chassis.lights = !chassis.lights if(chassis.lights) - chassis.set_light_on(TRUE) button_icon_state = "mech_lights_on" else - chassis.set_light_on(FALSE) button_icon_state = "mech_lights_off" + chassis.set_light_on(chassis.lights) chassis.occupant_message("Toggled lights [chassis.lights?"on":"off"].") chassis.log_message("Toggled lights [chassis.lights?"on":"off"].", LOG_MECHA) UpdateButtonIcon() diff --git a/code/game/mecha/working/clarke.dm b/code/game/mecha/working/clarke.dm index 709209dffa2d..a360e565b88d 100644 --- a/code/game/mecha/working/clarke.dm +++ b/code/game/mecha/working/clarke.dm @@ -9,7 +9,7 @@ fast_pressure_step_in = 1.25 slow_pressure_step_in = 1.8 resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF - lights_power = 7 + light_power = 7 deflect_chance = 10 step_energy_drain = 15 //slightly higher energy drain since you movin those wheels FAST armor = list(MELEE = 20, BULLET = 10, LASER = 20, ENERGY = 0, BOMB = 60, BIO = 0, RAD = 70, FIRE = 100, ACID = 100) diff --git a/code/game/mecha/working/ripley.dm b/code/game/mecha/working/ripley.dm index 55d2dc384533..ff4d11749b6b 100644 --- a/code/game/mecha/working/ripley.dm +++ b/code/game/mecha/working/ripley.dm @@ -6,7 +6,7 @@ step_in = 1.5 //Move speed, lower is faster. max_temperature = 20000 max_integrity = 200 - lights_power = 7 + light_power = 7 deflect_chance = 15 armor = list(MELEE = 40, BULLET = 20, LASER = 10, ENERGY = 0, BOMB = 40, BIO = 0, RAD = 20, FIRE = 100, ACID = 100) max_equip = 6 @@ -84,7 +84,7 @@ slow_pressure_step_in = 4 //step_in while in normal pressure conditions step_in = 4 resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF - lights_power = 7 + light_power = 7 armor = list(MELEE = 40, BULLET = 30, LASER = 30, ENERGY = 30, BOMB = 60, BIO = 100, RAD = 70, FIRE = 100, ACID = 100) max_equip = 5 // More armor, less tools wreckage = /obj/structure/mecha_wreckage/ripley/firefighter @@ -103,7 +103,7 @@ step_in = 4 slow_pressure_step_in = 3 opacity=0 - lights_power = 7 + light_power = 7 wreckage = /obj/structure/mecha_wreckage/ripley/deathripley step_energy_drain = 0 enclosed = TRUE diff --git a/code/modules/swarmers/swarmer.dm b/code/modules/swarmers/swarmer.dm index 7dff5c7f2d2e..62f9160effb1 100644 --- a/code/modules/swarmers/swarmer.dm +++ b/code/modules/swarmers/swarmer.dm @@ -411,7 +411,7 @@ var/mob/living/simple_animal/hostile/swarmer/melee/drone = d drone.swarmer_flags = ~SWARMER_LIGHT_ON drone.set_light_on(FALSE) - return + return swarmer_flags |= SWARMER_LIGHT_ON set_light_on(TRUE) if(!mind) @@ -421,7 +421,7 @@ var/mob/living/simple_animal/hostile/swarmer/melee/drone = d drone.swarmer_flags |= SWARMER_LIGHT_ON drone.set_light_on(TRUE) - + balloon_alert(src, "light toggled") /** diff --git a/yogstation/code/game/mecha/makeshift/lockermech.dm b/yogstation/code/game/mecha/makeshift/lockermech.dm index debf217e4a13..2bd3abe045a4 100644 --- a/yogstation/code/game/mecha/makeshift/lockermech.dm +++ b/yogstation/code/game/mecha/makeshift/lockermech.dm @@ -4,7 +4,7 @@ icon = 'yogstation/icons/mecha/lockermech.dmi' icon_state = "lockermech" max_integrity = 100 //its made of scraps - lights_power = 5 + light_power = 5 step_in = 4 //Same speed as a ripley, for now. armor = list(MELEE = 20, BULLET = 10, LASER = 10, ENERGY = 0, BOMB = 10, BIO = 0, RAD = 0, FIRE = 70, ACID = 60) //Same armour as a locker internal_damage_threshold = 30 //Its got shitty durability From 43f4fd1c7b55f36e1f8d2d42bfa921906faa0b5f Mon Sep 17 00:00:00 2001 From: Ling Date: Mon, 2 Jan 2023 18:10:30 +0200 Subject: [PATCH 08/13] Fix PDA light 1 --- code/datums/components/overlay_lighting.dm | 3 +- .../computers/item/computer.dm | 39 ++++++++++++++++++- .../computers/item/computer_ui.dm | 12 +----- 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/code/datums/components/overlay_lighting.dm b/code/datums/components/overlay_lighting.dm index ef9d52bf95a0..0d6a87e48197 100644 --- a/code/datums/components/overlay_lighting.dm +++ b/code/datums/components/overlay_lighting.dm @@ -133,8 +133,7 @@ ///Clears the affected_turfs lazylist, removing from its contents the effects of being near the light. /datum/component/overlay_lighting/proc/clean_old_turfs() - for(var/t in affected_turfs) - var/turf/lit_turf = t + for(var/turf/lit_turf as anything in affected_turfs) lit_turf.dynamic_lumcount -= used_lum_power affected_turfs = null diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm index ca735e206e35..5821e57cf19f 100644 --- a/code/modules/modular_computers/computers/item/computer.dm +++ b/code/modules/modular_computers/computers/item/computer.dm @@ -55,6 +55,12 @@ max_integrity = 100 armor = list(MELEE = 0, BULLET = 20, LASER = 20, ENERGY = 100, BOMB = 0, BIO = 100, RAD = 100, FIRE = 0, ACID = 0) + light_system = MOVABLE_LIGHT + light_range = 3 + light_power = 0.6 + light_color = "#FFFFFF" + light_on = FALSE + /// List of "connection ports" in this computer and the components with which they are plugged var/list/all_components = list() /// Lazy List of extra hardware slots that can be used modularly. @@ -70,7 +76,7 @@ ///The brightness of that light var/comp_light_luminosity = 3 ///The color of that light - var/comp_light_color + var/comp_light_color = "#FFFFFF" // Preset Stuff var/list/starting_components = list() @@ -86,7 +92,8 @@ START_PROCESSING(SSobj, src) if(!physical) physical = src - comp_light_color = "#FFFFFF" + set_light_color(comp_light_color) + set_light_range(comp_light_luminosity) idle_threads = list() install_starting_components() install_starting_files() @@ -501,6 +508,34 @@ update_icon() play_computer_sound(shutdown_sound, get_clamped_volume(), FALSE) +/** + * Toggles the computer's flashlight, if it has one. + * + * Called from ui_act(), does as the name implies. + * It is seperated from ui_act() to be overwritten as needed. +*/ +/obj/item/modular_computer/proc/toggle_flashlight() + if(!has_light) + return FALSE + set_light_on(!light_on) + update_icon() + return TRUE + +/** + * Sets the computer's light color, if it has a light. + * + * Called from ui_act(), this proc takes a color string and applies it. + * It is seperated from ui_act() to be overwritten as needed. + * Arguments: + ** color is the string that holds the color value that we should use. Proc auto-fails if this is null. +*/ +/obj/item/modular_computer/proc/set_flashlight_color(color) + if(!has_light || !color) + return FALSE + comp_light_color = color + set_light_color(color) + return TRUE + /obj/item/modular_computer/screwdriver_act(mob/user, obj/item/tool) if(!all_components.len) to_chat(user, "This device doesn't have any components installed.") diff --git a/code/modules/modular_computers/computers/item/computer_ui.dm b/code/modules/modular_computers/computers/item/computer_ui.dm index a071d6e9f56f..cb6f3f7aa702 100644 --- a/code/modules/modular_computers/computers/item/computer_ui.dm +++ b/code/modules/modular_computers/computers/item/computer_ui.dm @@ -176,12 +176,7 @@ if("PC_toggle_light") play_interact_sound() - light_on = !light_on - if(light_on) - set_light(comp_light_luminosity, 1, comp_light_color) - else - set_light(0) - return TRUE + return toggle_flashlight() if("PC_light_color") var/mob/user = usr @@ -195,10 +190,7 @@ if(color_hex2num(new_color) < 200) //Colors too dark are rejected to_chat(user, span_warning("That color is too dark! Choose a lighter one.")) new_color = null - comp_light_color = new_color - light_color = new_color - update_light() - return TRUE + return set_flashlight_color(new_color) if("PC_Eject_Disk") var/param = params["name"] From 67f835a6a7acad9f6519dca6a7d831f8790a0f72 Mon Sep 17 00:00:00 2001 From: Ling Date: Wed, 11 Jan 2023 23:48:51 +0200 Subject: [PATCH 09/13] a --- _maps/_basemap.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_maps/_basemap.dm b/_maps/_basemap.dm index 150f974d7a53..faf2c1a18c8b 100644 --- a/_maps/_basemap.dm +++ b/_maps/_basemap.dm @@ -1,4 +1,4 @@ -#define LOWMEMORYMODE //uncomment this to load centcom and runtime station and thats it. +//#define LOWMEMORYMODE //uncomment this to load centcom and runtime station and thats it. #include "map_files\generic\CentCom.dmm" From beeddc74e2527bf859a91f5211fed3de3ca003f8 Mon Sep 17 00:00:00 2001 From: Jamie D <993128+JamieD1@users.noreply.github.com> Date: Thu, 12 Jan 2023 01:24:47 +0000 Subject: [PATCH 10/13] Update game_options.txt --- config/game_options.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/game_options.txt b/config/game_options.txt index 6d37a1f8401f..0a4b26aa9553 100644 --- a/config/game_options.txt +++ b/config/game_options.txt @@ -126,7 +126,7 @@ PROBABILITY CULT 5 PROBABILITY CLOCKWORK_CULT 5 # Snowflakes -PROBABILITY SHADOWLING 6 +PROBABILITY SHADOWLING 0 //For now PROBABILITY WIZARD 8 PROBABILITY NUCLEAR 9 PROBABILITY MALF 8 From 75cbc198e41c24d8e2456ba8a92c1a54fa1f0190 Mon Sep 17 00:00:00 2001 From: Ling Date: Thu, 12 Jan 2023 18:48:18 +0200 Subject: [PATCH 11/13] Fix plasmaman helmet --- code/modules/clothing/spacesuits/plasmamen.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/clothing/spacesuits/plasmamen.dm b/code/modules/clothing/spacesuits/plasmamen.dm index 5fdd264a0e60..a45d14f6d2a3 100644 --- a/code/modules/clothing/spacesuits/plasmamen.dm +++ b/code/modules/clothing/spacesuits/plasmamen.dm @@ -62,7 +62,7 @@ /obj/item/clothing/head/helmet/space/plasmaman/proc/toggle_helmet_light(mob/user) helmet_on = !helmet_on - icon_state = "[initial(icon_state)][helmet_on ? "-light":""]" + icon_state = "[base_icon_state][helmet_on ? "-light":""]" item_state = icon_state user.update_inv_head() From c7268431979b56b58b3c36c4a8e17a0aa0413c11 Mon Sep 17 00:00:00 2001 From: Ling Date: Fri, 13 Jan 2023 14:41:44 +0200 Subject: [PATCH 12/13] Fixes --- _maps/RandomRuins/SpaceRuins/bigape.dmm | 2 +- code/game/atoms.dm | 7 ------ .../bloodsuckers/powers/targeted/brawn.dm | 2 +- code/modules/lighting/lighting_atom.dm | 22 ++++++++++++++----- code/modules/lighting/lighting_turf.dm | 22 +++++++++++++++++++ .../shadowling/shadowling_abilities.dm | 2 +- 6 files changed, 41 insertions(+), 16 deletions(-) diff --git a/_maps/RandomRuins/SpaceRuins/bigape.dmm b/_maps/RandomRuins/SpaceRuins/bigape.dmm index 09e85e129d9d..973da4e447ca 100644 --- a/_maps/RandomRuins/SpaceRuins/bigape.dmm +++ b/_maps/RandomRuins/SpaceRuins/bigape.dmm @@ -31,7 +31,7 @@ /area/ruin/powered) "g" = ( /obj/item/flashlight/lamp/bananalamp{ - brightness_on = 10 + light_range = 10 }, /obj/structure/table/wood, /obj/structure/fans/tiny/invisible, diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 1fb016b9e9cd..babf4b0429f5 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -72,13 +72,6 @@ ///Bitfield for how the atom handles materials. var/material_flags = NONE - ///Light systems, both shouldn't be active at the same time. - var/light_system = STATIC_LIGHT - ///Boolean variable for toggleable lights. Has no effect without the proper light_system, light_range and light_power values. - var/light_on = TRUE - ///Bitflags to determine lighting-related atom properties. - var/light_flags = NONE - var/chat_color_name // Last name used to calculate a color for the chatmessage overlays var/chat_color // Last color calculated for the the chatmessage overlays diff --git a/code/modules/antagonists/bloodsuckers/powers/targeted/brawn.dm b/code/modules/antagonists/bloodsuckers/powers/targeted/brawn.dm index 34c48aedc3ce..9a577d21341c 100644 --- a/code/modules/antagonists/bloodsuckers/powers/targeted/brawn.dm +++ b/code/modules/antagonists/bloodsuckers/powers/targeted/brawn.dm @@ -213,7 +213,7 @@ var/mob/living/carbon/human/H = target_atom H.apply_status_effect(STATUS_EFFECT_SHADOWAFFLICTED) var/turf/T = get_turf(H) - for(var/datum/light_source/LS in T.light_sources) + for(var/datum/light_source/LS in T.get_affecting_lights()) var/atom/LO = LS.source_atom if(isitem(LO)) var/obj/item/I = LO diff --git a/code/modules/lighting/lighting_atom.dm b/code/modules/lighting/lighting_atom.dm index e33f137e4c96..65749bbaa8b9 100644 --- a/code/modules/lighting/lighting_atom.dm +++ b/code/modules/lighting/lighting_atom.dm @@ -1,11 +1,21 @@ /atom - var/light_power = 1 // Intensity of the light. - var/light_range = 0 // Range in tiles of the light. - var/light_color // Hexadecimal RGB string representing the colour of the light. - - var/tmp/datum/light_source/light // Our light source. Don't fuck with this directly unless you have a good reason! - var/tmp/list/light_sources // Any light sources that are "inside" of us, for example, if src here was a mob that's carrying a flashlight, that flashlight's light source would be part of this list. + ///Light systems, both shouldn't be active at the same time. + var/light_system = STATIC_LIGHT + ///Range of the light in tiles. Zero means no light. + var/light_range = 0 + ///Intensity of the light. The stronger, the less shadows you will see on the lit area. + var/light_power = 1 + ///Hexadecimal RGB string representing the colour of the light. White by default. + var/light_color = COLOR_WHITE + ///Boolean variable for toggleable lights. Has no effect without the proper light_system, light_range and light_power values. + var/light_on = TRUE + ///Bitflags to determine lighting-related atom properties. + var/light_flags = NONE + ///Our light source. Don't fuck with this directly unless you have a good reason! + var/tmp/datum/light_source/light + ///Any light sources that are "inside" of us, for example, if src here was a mob that's carrying a flashlight, that flashlight's light source would be part of this list. + var/tmp/list/light_sources // The proc you should always use to set the light of this atom. // Nonesensical value for l_color default, so we can detect if it gets set to null. diff --git a/code/modules/lighting/lighting_turf.dm b/code/modules/lighting/lighting_turf.dm index 0a41faf9c2c1..a99698ff33be 100644 --- a/code/modules/lighting/lighting_turf.dm +++ b/code/modules/lighting/lighting_turf.dm @@ -116,3 +116,25 @@ lighting_corner_NW = new/datum/lighting_corner(src, NORTH|WEST) lighting_corners_initialised = TRUE + +/turf/proc/get_affecting_lights() + var/list/affecting = list() + + if (!lighting_object) + return affecting + + var/datum/lighting_corner/L + L = lighting_corner_NE + if (L) + affecting += L.affecting + L = lighting_corner_SE + if (L) + affecting += L.affecting + L = lighting_corner_SW + if (L) + affecting += L.affecting + L = lighting_corner_NW + if (L) + affecting += L.affecting + + return uniqueList(affecting) diff --git a/yogstation/code/modules/antagonists/shadowling/shadowling_abilities.dm b/yogstation/code/modules/antagonists/shadowling/shadowling_abilities.dm index 415c7bdfb2e0..5c39824f1cc3 100644 --- a/yogstation/code/modules/antagonists/shadowling/shadowling_abilities.dm +++ b/yogstation/code/modules/antagonists/shadowling/shadowling_abilities.dm @@ -159,7 +159,7 @@ return to_chat(user, span_shadowling("You silently disable all nearby lights.")) var/turf/T = get_turf(user) - for(var/datum/light_source/LS in T.light_sources) + for(var/datum/light_source/LS in T.get_affecting_lights()) var/atom/LO = LS.source_atom if(isitem(LO)) extinguishItem(LO) From 382cbc4930bb576f4ecef37e90db3bb2ec5b8cd7 Mon Sep 17 00:00:00 2001 From: Ling Date: Fri, 13 Jan 2023 14:54:08 +0200 Subject: [PATCH 13/13] Glowy changes --- code/datums/mutations/body.dm | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/code/datums/mutations/body.dm b/code/datums/mutations/body.dm index 62126523bb8a..c29716165200 100644 --- a/code/datums/mutations/body.dm +++ b/code/datums/mutations/body.dm @@ -194,7 +194,6 @@ var/obj/effect/dummy/luminescent_glow/glowth //shamelessly copied from luminescents var/glow = 3.5 var/range = 2.5 - var/color var/current_nullify_timer // For veil yogstation\code\modules\antagonists\shadowling\shadowling_abilities.dm power_coeff = 1 conflicts = list(/datum/mutation/human/glow/anti) @@ -209,16 +208,19 @@ /datum/mutation/human/glow/modify() if(!glowth) return - var/power = GET_MUTATION_POWER(src) + + var/glow_color + if(owner.dna.features["mcolor"][1] != "#") //if it doesn't start with a pound, it needs that for the color - color += "#" + glow_color += "#" if(length(owner.dna.features["mcolor"]) < 6) //this atrocity converts shorthand hex rgb back into full hex that's required for light to be given a functional value - color += owner.dna.features["mcolor"][1] + owner.dna.features["mcolor"][1] + owner.dna.features["mcolor"][2] + owner.dna.features["mcolor"][2] + owner.dna.features["mcolor"][3] + owner.dna.features["mcolor"][3] + glow_color += owner.dna.features["mcolor"][1] + owner.dna.features["mcolor"][1] + owner.dna.features["mcolor"][2] + owner.dna.features["mcolor"][2] + owner.dna.features["mcolor"][3] + owner.dna.features["mcolor"][3] else - color += owner.dna.features["mcolor"] - glowth.set_light_range_power_color(range * power, glow * power, color) + glow_color += owner.dna.features["mcolor"] + + glowth.set_light_range_power_color(range * GET_MUTATION_POWER(src), glow, glow_color) /datum/mutation/human/glow/on_losing(mob/living/carbon/human/owner) . = ..()