diff --git a/code/controllers/subsystem/nightcycle.dm b/code/controllers/subsystem/nightcycle.dm deleted file mode 100644 index 589b21802..000000000 --- a/code/controllers/subsystem/nightcycle.dm +++ /dev/null @@ -1,116 +0,0 @@ -/* 6:00 AM - 21600 - 6:45 AM - 24300 - 11:45 AM - 42300 - 4:45 PM - 60300 - 9:45 PM - 78300 - 10:30 PM - 81000 */ -#define CYCLE_SUNRISE 216000 -#define CYCLE_MORNING 243000 -#define CYCLE_DAYTIME 423000 -#define CYCLE_AFTERNOON 603000 -#define CYCLE_SUNSET 783000 -#define CYCLE_NIGHTTIME 810000 - -GLOBAL_LIST_INIT(nightcycle_turfs, typecacheof(list( - /turf/open/indestructible/ground/outside, - /turf/open/floor/plating/f13/outside))) - -SUBSYSTEM_DEF(nightcycle) - name = "Day/Night Cycle" - wait = 20 //20 ticks in between checks, this thing doesn't need to fire so fast, as it's tied to gameclock not its own ticker - //This will also give the game time to light up the columns and not choke - //var/flags = 0 //see MC.dm in __DEFINES Most flags must be set on world start to take full effect. (You can also restart the mc to force them to process again - can_fire = TRUE - //var/list/timeBrackets = list("SUNRISE" = , "MORNING" = , "DAYTIME" = , "EVENING" = , "" = ,) - var/currentTime - var/sunColour - var/sunPower - var/sunRange - var/currentColumn - var/working = 3 - var/doColumns //number of columns to do at a time - var/newTime - -/datum/controller/subsystem/nightcycle/fire(resumed = FALSE) - if (working) - doWork() - return - if (nextBracket()) - working = 1 - currentColumn = 1 - -/datum/controller/subsystem/nightcycle/proc/nextBracket() - var/Time = station_time() - - switch (Time) - if (CYCLE_SUNRISE to CYCLE_MORNING - 1) - newTime = "SUNRISE" - if (CYCLE_MORNING to CYCLE_DAYTIME - 1) - newTime = "MORNING" - if (CYCLE_DAYTIME to CYCLE_AFTERNOON - 1) - newTime = "DAYTIME" - if (CYCLE_AFTERNOON to CYCLE_SUNSET - 1) - newTime = "AFTERNOON" - if (CYCLE_SUNSET to CYCLE_NIGHTTIME - 1) - newTime = "SUNSET" - else - newTime = "NIGHTTIME" - - if (newTime != currentTime) - currentTime = newTime - updateLight(currentTime) - if(newTime == "MORNING") //Only change lamps when we need to - for(var/obj/structure/lamp_post/LP in GLOB.lamppost) - LP.icon_state = "[initial(LP.icon_state)]" - LP.set_light(0) - else if(newTime == "SUNSET") - for(var/obj/structure/lamp_post/LP in GLOB.lamppost) - LP.icon_state = "[initial(LP.icon_state)]-on" - LP.set_light(LP.on_range,LP.on_power,LP.light_color) - . = TRUE - -/datum/controller/subsystem/nightcycle/proc/doWork() - var/list/currentTurfs = list() - var/x = min(currentColumn + doColumns, world.maxx) - for (var/z in SSmapping.levels_by_trait(ZTRAIT_STATION)) - currentTurfs += block(locate(currentColumn,1,z), locate(x,world.maxy,z)) //this is probably brutal on the overhead - for (var/t in currentTurfs) - var/turf/T = t - if(T.type in GLOB.nightcycle_turfs) - T.set_light(T.turf_light_range, sunPower, sunColour) - - currentColumn = x + 1 - if (currentColumn > world.maxx) - currentColumn = 1 - working = 0 - return - -/datum/controller/subsystem/nightcycle/proc/updateLight(newTime) - switch (newTime) - if ("SUNRISE") - sunColour = "#ffd1b3" - sunPower = 0.3 - if ("MORNING") - sunColour = "#fff2e6" - sunPower = 0.5 - if ("DAYTIME") - sunColour = "#FFFFFF" - sunPower = 0.75 - if ("AFTERNOON") - sunColour = "#fff2e6" - sunPower = 0.5 - if ("SUNSET") - sunColour = "#ffcccc" - sunPower = 0.3 - if("NIGHTTIME") - sunColour = "#00111a" - sunPower = 0.20 - - - -#undef CYCLE_SUNRISE -#undef CYCLE_MORNING -#undef CYCLE_DAYTIME -#undef CYCLE_AFTERNOON -#undef CYCLE_SUNSET -#undef CYCLE_NIGHTTIME diff --git a/code/controllers/subsystem/sun.dm b/code/controllers/subsystem/sun.dm index 7a3528cc3..a2e8df465 100644 --- a/code/controllers/subsystem/sun.dm +++ b/code/controllers/subsystem/sun.dm @@ -1,39 +1,27 @@ +#define DAYLENGTH (10 MINUTES) +GLOBAL_LIST_INIT(daylight_turfs, typecacheof(list( + /turf/open/indestructible/ground/outside, + /turf/open/floor/plating/f13/outside))) + SUBSYSTEM_DEF(sun) name = "Sun" - wait = 600 + wait = 2 MINUTES flags = SS_NO_TICK_CHECK|SS_NO_INIT - var/angle - var/dx - var/dy - var/rate + var/tmp/angle + var/tmp/dir + var/tmp/power + var/tmp/dx + var/tmp/dz var/list/solars = list() /datum/controller/subsystem/sun/PreInit() - angle = rand (0,360) // the station position to the sun is randomised at round start - rate = rand(50,200)/100 // 50% - 200% of standard rotation - if(prob(50)) // same chance to rotate clockwise than counter-clockwise - rate = -rate + fire() /datum/controller/subsystem/sun/stat_entry(msg) - ..("P:[solars.len]") + ..("A:[angle] P:[solars.len]") /datum/controller/subsystem/sun/fire() - angle = (360 + angle + rate * 6) % 360 // increase/decrease the angle to the sun, adjusted by the rate - - // now calculate and cache the (dx,dy) increments for line drawing - var/s = sin(angle) - var/c = cos(angle) - - // Either "abs(s) < abs(c)" or "abs(s) >= abs(c)" - // In both cases, the greater is greater than 0, so, no "if 0" check is needed for the divisions - - if(abs(s) < abs(c)) - dx = s / abs(c) - dy = c / abs(c) - else - dx = s / abs(s) - dy = c / abs(s) - + update_angle(((world.realtime/DAYLENGTH * 360) - 90) % 360) // 0 to 180 is up; 0 to 30 and 150 to 180 is sunrise/sunset; 90 is noon //now tell the solar control computers to update their status and linked devices for(var/obj/machinery/power/solar_control/SC in solars) if(!SC.powernet) @@ -41,9 +29,19 @@ SUBSYSTEM_DEF(sun) continue SC.update() - - - - - - +/datum/controller/subsystem/sun/proc/update_angle(var/ang) + if(!SSsunlight) + return + angle = ang + power = max(sin(angle*2)**2, 75/10000) + SSsunlight.set_overall_light(SSsunlight.sun_accuracy * 1.2, power, getcolor()) + + +/datum/controller/subsystem/sun/proc/getcolor() + switch(angle) + if(180 to 360) + return "#000000" + if(30 to 150) + return "#fdfbd3" + var/dist_from_horizon = angle < 90 ? 30-angle : angle-150 + return BlendRGB("#fdfbd3", "#fd5e53", 1 - dist_from_horizon/30) \ No newline at end of file diff --git a/code/controllers/subsystem/sunlight.dm b/code/controllers/subsystem/sunlight.dm new file mode 100644 index 000000000..d80f45f21 --- /dev/null +++ b/code/controllers/subsystem/sunlight.dm @@ -0,0 +1,54 @@ +SUBSYSTEM_DEF(sunlight) + name = "Sunlight" + flags = SS_NO_FIRE + + var/list/light_points = list() + var/sun_target_z = 2 + var/sun_accuracy = 8 + + var/list/presets + +/datum/controller/subsystem/sunlight/New() + NEW_SS_GLOBAL(SSsunlight) + +/datum/controller/subsystem/sunlight/stat_entry() + ..("A:[sun_accuracy] LP:[light_points.len] Z:[sun_target_z]") + +/datum/controller/subsystem/sunlight/Initialize() + var/thing + var/turf/T + for (thing in block(locate(1, 1, sun_target_z), locate(world.maxx, world.maxy, sun_target_z))) + T = thing + if (!(T.x % sun_accuracy) && !(T.y % sun_accuracy)) + light_points += new /atom/movable/sunobj(thing) + CHECK_TICK + + log_game("sunlight: [light_points.len] sun emitters.") + ..() + +/datum/controller/subsystem/sunlight/proc/set_overall_light(...) + . = 0 + for (var/thing in light_points) + var/atom/movable/AM = thing + AM.set_light(arglist(args)) + .++ + CHECK_TICK + +/atom/movable/sunobj + name = "sunlight emitter" + desc = "Weren't you told to never look directly at the sun? (but seriously, you shouldn't see this)" + light_novis = TRUE + light_range = 16 + mouse_opacity = FALSE + +/atom/movable/sunobj/Destroy(force = FALSE) + if (!force) + stack_trace("Something attempted to delete a sunobj!") + return QDEL_HINT_LETMELIVE + + SSsunlight.light_points -= src + return ..() + +/atom/movable/sunobj/Initialize() + light_range = CEILING(SSsunlight.sun_accuracy * 1.2, 1) + return ..() diff --git a/code/modules/lighting/lighting_atom.dm b/code/modules/lighting/lighting_atom.dm index 89ec6a3a7..e4f1726ed 100644 --- a/code/modules/lighting/lighting_atom.dm +++ b/code/modules/lighting/lighting_atom.dm @@ -4,6 +4,7 @@ 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/light_novis // If TRUE, visibility checks will be skipped when calculating this 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. @@ -46,6 +47,8 @@ if (light) // Update the light or create it if it does not exist. light.update(.) + else if (light_novis) + light = new/datum/light_source/novis(src, .) else light = new/datum/light_source(src, .) diff --git a/code/modules/lighting/lighting_source.dm b/code/modules/lighting/lighting_source.dm index b4e6c77da..4fc329dbd 100644 --- a/code/modules/lighting/lighting_source.dm +++ b/code/modules/lighting/lighting_source.dm @@ -30,6 +30,8 @@ var/lightFlag = NONE //tags such as GLOBAL_LIGHTING - No other use so far + var/skip_falloff = FALSE // ONLY for use with sunlight, behavior is undefined if TRUE on regular sources. + /datum/light_source/New(var/atom/owner, var/atom/top) source_atom = owner // Set our new owner. LAZYADD(source_atom.light_sources, src) @@ -121,6 +123,20 @@ // 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))) +// APPLY_CORNER without LUM_FALLOFF. +#define APPLY_CORNER_SIMPLE(C) \ + . = light_power; \ + var/OLD = effect_str[C]; \ + \ + effect_str[C] = .; \ + \ + C.update_lumcount \ + ( \ + (. * lum_r) - (OLD * applied_lum_r), \ + (. * lum_g) - (OLD * applied_lum_g), \ + (. * lum_b) - (OLD * applied_lum_b) \ + ); + #define APPLY_CORNER(C) \ . = LUM_FALLOFF(C, pixel_turf); \ . *= light_power; \ @@ -171,9 +187,14 @@ REMOVE_CORNER(C) effect_str[C] = 0 - APPLY_CORNER(C) + + if (skip_falloff) + APPLY_CORNER_SIMPLE(C) + else + APPLY_CORNER(C) UNSETEMPTY(effect_str) +// If you update this, update the equivalent proc in lighting_source_novis.dm. /datum/light_source/proc/update_corners() var/update = FALSE var/atom/source_atom = src.source_atom @@ -321,12 +342,4 @@ return 0 else return 0 - return 1 - - - - -#undef EFFECT_UPDATE -#undef LUM_FALLOFF -#undef REMOVE_CORNER -#undef APPLY_CORNER + return 1 \ No newline at end of file diff --git a/code/modules/lighting/lighting_source_novis.dm b/code/modules/lighting/lighting_source_novis.dm new file mode 100644 index 000000000..81b36d18b --- /dev/null +++ b/code/modules/lighting/lighting_source_novis.dm @@ -0,0 +1,150 @@ +/datum/light_source/novis + //skip_falloff = TRUE + +/datum/light_source/novis/update_corners() + var/update = FALSE + var/atom/source_atom = src.source_atom + + if (QDELETED(source_atom)) + qdel(src) + return + + if (source_atom.light_power != light_power) + light_power = source_atom.light_power + update = TRUE + + if (source_atom.light_range != light_range) + light_range = source_atom.light_range + update = TRUE + + if (!top_atom) + top_atom = source_atom + update = TRUE + + if (!light_range || !light_power) + qdel(src) + return + + if (isturf(top_atom)) + if (source_turf != top_atom) + source_turf = top_atom + pixel_turf = source_turf + update = TRUE + else if (top_atom.loc != source_turf) + source_turf = top_atom.loc + pixel_turf = get_turf_pixel(top_atom) + update = TRUE + else + var/P = get_turf_pixel(top_atom) + if (P != pixel_turf) + pixel_turf = P + update = TRUE + + if (!isturf(source_turf)) + if (applied) + remove_lum() + return + + if (light_range && light_power && !applied) + update = TRUE + + if (source_atom.light_color != light_color) + light_color = source_atom.light_color + parse_light_color() + update = TRUE + + else if (applied_lum_r != lum_r || applied_lum_g != lum_g || applied_lum_b != lum_b) + update = TRUE + + if (update) + needs_update = LIGHTING_CHECK_UPDATE + applied = TRUE + else if (needs_update == LIGHTING_CHECK_UPDATE) + 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 + if (source_turf) + if(checkAdjacent() & isturf(source_atom)) + T = source_atom + turfs += T + for (thing in T.get_corners(source_turf)) + C = thing + corners[C] = 0 + else + var/oldlum = source_turf.luminosity + source_turf.luminosity = CEILING(light_range, 1) + for(T in RANGE_TURFS(CEILING(light_range, 1), source_turf)) // We don't need no damn vis checks! + if( (lightFlag & GLOBAL_LIGHTING) && (T.flags_2 & GLOBAL_LIGHT_TURF_2 ) && (T != source_atom)) + continue + if(!(T.type in GLOB.daylight_turfs)) + continue + for (thing in T.get_corners(source_turf)) + C = thing + corners[C] = 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) + + 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_SIMPLE(C) + 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_SIMPLE(C) + + for (thing in corners - L) // Existing corners + C = thing + if (!C.active) + effect_str[C] = 0 + continue + APPLY_CORNER_SIMPLE(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 + + 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 +#undef REMOVE_CORNER +#undef APPLY_CORNER +#undef APPLY_CORNER_SIMPLE \ No newline at end of file diff --git a/code/modules/power/solar.dm b/code/modules/power/solar.dm index 18162a0bc..6822675e7 100644 --- a/code/modules/power/solar.dm +++ b/code/modules/power/solar.dm @@ -147,27 +147,9 @@ //trace towards sun to see if we're in shadow /obj/machinery/power/solar/proc/occlusion() - - var/ax = x // start at the solar panel - var/ay = y - var/turf/T = null - var/dx = SSsun.dx - var/dy = SSsun.dy - - for(var/i = 1 to 5) // 5 steps is enough - ax += dx // do step - ay += dy - - T = locate( round(ax,0.5),round(ay,0.5),z) - - if(T.x == 1 || T.x==world.maxx || T.y==1 || T.y==world.maxy) // not obscured if we reach the edge - break - - if(T.density) // if we hit a solid turf, panel is obscured - obscured = 1 - return - - obscured = 0 // if hit the edge or stepped 20 times, not obscured + obscured = TRUE + if(locate(/atom/movable/sunobj) in view(src)) + obscured = FALSE update_solar_exposure() diff --git a/tgstation.dme b/tgstation.dme index 29fbc36b4..2a89d4f45 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -230,7 +230,6 @@ #include "code\controllers\subsystem\mob_spawners.dm" #include "code\controllers\subsystem\mobs.dm" #include "code\controllers\subsystem\moods.dm" -#include "code\controllers\subsystem\nightcycle.dm" #include "code\controllers\subsystem\nightshift.dm" #include "code\controllers\subsystem\npcpool.dm" #include "code\controllers\subsystem\orbit.dm" @@ -251,6 +250,7 @@ #include "code\controllers\subsystem\squeak.dm" #include "code\controllers\subsystem\stickyban.dm" #include "code\controllers\subsystem\sun.dm" +#include "code\controllers\subsystem\sunlight.dm" #include "code\controllers\subsystem\tgui.dm" #include "code\controllers\subsystem\throwing.dm" #include "code\controllers\subsystem\ticker.dm" @@ -1881,6 +1881,7 @@ #include "code\modules\lighting\lighting_object.dm" #include "code\modules\lighting\lighting_setup.dm" #include "code\modules\lighting\lighting_source.dm" +#include "code\modules\lighting\lighting_source_novis.dm" #include "code\modules\lighting\lighting_turf.dm" #include "code\modules\mapping\dmm_suite.dm" #include "code\modules\mapping\map_template.dm"