diff --git a/code/WorkInProgress/Aryn/Lighting/Controller.dm b/code/WorkInProgress/Aryn/Lighting/Controller.dm new file mode 100644 index 00000000000..aaeb9ce17f8 --- /dev/null +++ b/code/WorkInProgress/Aryn/Lighting/Controller.dm @@ -0,0 +1,115 @@ +/* + +Overview: + Unlike the previous lighting controller, this is mostly here to hold global lighting procs and vars. + It does not process every tick, because not even DAL did something so stupid. + +Global Vars: + initial_lights - This holds all lights formed before the lighting controller started up. It becomes null on initialization. + +Class Vars: + starlight - The light value of space. + + icon_updates - The list of turfs which need an update to their overlays. + + light_border - Space turfs which are adjacent to non-space turfs. + +Class Procs: + Initialize() + Starts the lighting system, creating all light points and turf overlays. + + StarLight(n) + Sets the light produced by space. If a solar eclipse suddenly happens, it'll probably lag. + + MarkIconUpdate(turf/T) + Called when a turf needs an update to its light icon. Ensures that it only gets calculated once per turf. + + FlushIconUpdates() + Called when a light is done marking icon updates. Updates every marked turf. + + AddBorder(turf/T) & RemoveBorder(turf/T) + Called by turf/CheckForOpaqueObjects() to modify the light_border list. + + +*/ + +var/datum/controller/lighting/lighting_controller +var/list/initial_lights = list() +var/all_lightpoints_made = 0 + +/datum/controller/lighting + + var/starlight = 4 + var/list/icon_updates = list() + + var/list/light_border = list() + + //var/icon/border = icon('Icons/Test.dmi', "border") + +/datum/controller/lighting/New() + lighting_controller = src + +/datum/controller/lighting/proc/Initialize() + + var/start_time = world.timeofday + world << "Processing lights..." + + var/turfs_updated = 0 + + for(var/z = 1, z <= world.maxz, z++) + for(var/y = 0, y <= world.maxy, y++) + for(var/x = 0, x <= world.maxx, x++) + if(x > 0 && y > 0) + var/turf/T = locate(x,y,z) + if(!T.light_overlay) + T.light_overlay = new(T) + T.CheckForOpaqueObjects() + turfs_updated++ + if(!all_lightpoints_made) new/lightpoint(x+0.5,y+0.5,z) + + all_lightpoints_made = 1 + + for(var/atom/movable/M in initial_lights) + if(!M.light) + M.SetLight(M.luminosity,M.luminosity) + else + M.light.Reset() + + StarLight(starlight) + initial_lights = null + + world << "Lighting initialization took [(world.timeofday-start_time)/world.fps] seconds." + world << "Updated [turfs_updated] turfs." + +/datum/controller/lighting/proc/StarLight(n) + starlight = n + for(var/turf/T in light_border) + T.SetLight(5,n-1) + + for(var/turf/T) + if(T.is_outside) + T.ResetValue() + + FlushIconUpdates() + +/datum/controller/lighting/proc/MarkIconUpdate(turf/T) + if(!T.needs_light_update) + icon_updates.Add(T) + T.needs_light_update = 1 + +/datum/controller/lighting/proc/FlushIconUpdates() + for(var/turf/T in icon_updates) + T.UpdateLight() + T.needs_light_update = 0 + icon_updates = list() + +/datum/controller/lighting/proc/AddBorder(turf/T) + if(!T.is_border) + light_border.Add(T) + T.is_border = 1 + //T.overlays.Add(border) + +/datum/controller/lighting/proc/RemoveBorder(turf/T) + if(T.is_border) + light_border.Remove(T) + T.is_border = 0 diff --git a/code/WorkInProgress/Aryn/Lighting/Engine.dm b/code/WorkInProgress/Aryn/Lighting/Engine.dm new file mode 100644 index 00000000000..9c7dcfa713e --- /dev/null +++ b/code/WorkInProgress/Aryn/Lighting/Engine.dm @@ -0,0 +1,176 @@ +/* + +Overview: + Procs given to atom and turf by the lighting engine, as well as the lighting overlay object. + +Atom Vars: + light - Contains the light object this atom is currently shining with. + +Turf Vars: + light_overlay - Contains an object showing the lighting icon over this turf. + + lit_value - Stores how brightly lit the turf is. + + has_opaque - A cached value updated by CheckForOpaqueObjects() + + is_outside - Any turf with this set to true will be considered as bright as space. + + needs_light_update - Turf is marked for icon updates when true. + + lightNE, lightSE, lightNW, lightSW - Hold the lightpoints on the four corners of this turf. See Lightpoint.dm + + lit_by - A list of lights that are lighting this turf. + +Atom Procs: + SetLight(intensity, radius) + A more versatile SetLuminosity() that allows independent control of intensity and radius. + Called behind the scenes of SetLuminosity(). + + SetOpacity(opacity) + Does the same thing as DAL. + +Turf Procs: + UpdateLight() + Called by the lighting controller. It is advisable not to call this manually due to the cost of lightpoint/max_value() + + AddLight(light/light) + Called by light/Reset() to light this turf with a particular light. + + RemoveLight(light/light) + Called by light/Off() to unlight turfs that were lit by it. + + ResetValue() + Called when lights are reset or starlight is changed. + + ResetCachedValues() + Resets cached values of all four light points. Called by ResetValue(). + + CheckForOpaqueObjects() + Called by lighting_controller.Initialize(), SetOpacity() or when a turf might change opacity. + Resets the opacity cache and looks for opaque objects. Also responsible for adding and removing borders to space. +*/ + + +#define LIGHTCLAMP(x) ( max(0,min(3,round(x,1))) ) + +obj/effect/lighting_overlay + //anchored = 1 + layer = 9 + mouse_opacity = 0 + icon = 'icons/effects/ArynLights.dmi' + icon_state = "0000" + +atom/var/light/light + +turf/var/obj/effect/lighting_overlay/light_overlay + +turf/var/lit_value = 0 +turf/var/has_opaque = 0 +turf/var/is_outside = 0 +turf/var/is_border = 0 +turf/var/needs_light_update = 0 + +turf/var/lightpoint/lightNE +turf/var/lightpoint/lightNW +turf/var/lightpoint/lightSE +turf/var/lightpoint/lightSW +turf/var/list/lit_by + +atom/New() + . = ..() + if(luminosity || light) + if(!lighting_controller) + initial_lights.Add(src) + else + if(!light) + SetLight(luminosity,luminosity) + else + light.Reset() + if(lighting_controller) + if(opacity) + opacity = 0 + SetOpacity(1) + +atom/movable/Move() + var/o = opacity + if(o) SetOpacity(0) + . = ..() + if(.) + if(o) SetOpacity(1) + if(light) + light.Reset() + lighting_controller.FlushIconUpdates() + +atom/proc/SetLight(intensity, radius) + if(!light) light = new(src) + light.radius = min(radius,15) + light.intensity = intensity + light.Reset() + lighting_controller.FlushIconUpdates() + +atom/proc/SetOpacity(o) + if(o == opacity) return + opacity = o + var/turf/T = loc + if(isturf(T)) + for(var/light/A in T.lit_by) + A.Reset() + lighting_controller.FlushIconUpdates() + +turf/proc/UpdateLight() + if(light_overlay) + light_overlay.icon_state = "[lightSE.max_value()][lightSW.max_value()][lightNW.max_value()][lightNE.max_value()]" + +turf/proc/AddLight(light/light) + if(!lit_by) lit_by = list() + lit_by.Add(light) + if(!has_opaque) + var/brightness = light.CalculateBrightness(src) + if(brightness > lit_value) + lit_value = LIGHTCLAMP(brightness) + ResetCachedValues() + for(var/turf/T in range(1,src)) + lighting_controller.MarkIconUpdate(T) + +turf/proc/RemoveLight(light/light) + lit_by.Remove(light) + ResetValue() + if(!lit_by.len) lit_by = null + +turf/proc/ResetValue() + CheckForOpaqueObjects() + if(has_opaque) + lit_value = 0 + else + var/max_brightness = (is_outside?(lighting_controller.starlight):0) + for(var/light/light in lit_by) + var/brightness = light.CalculateBrightness(src) + if(brightness > max_brightness) + max_brightness = brightness + lit_value = LIGHTCLAMP(max_brightness) + ResetCachedValues() + for(var/turf/T in range(1,src)) + lighting_controller.MarkIconUpdate(T) + +turf/proc/ResetCachedValues() + lightNE.cached_value = -1 + lightNW.cached_value = -1 + lightSE.cached_value = -1 + lightSW.cached_value = -1 + +turf/proc/CheckForOpaqueObjects() + has_opaque = opacity + if(!opacity) + for(var/atom/movable/M in contents) + if(M.opacity) + has_opaque = 1 + break + if(is_outside) + for(var/d = 1, d < 16, d*=2) + var/turf/T = get_step(src,d) + if(T && !T.is_outside) + lighting_controller.AddBorder(src) + return + if(is_border) lighting_controller.RemoveBorder(src) + +#undef LIGHTCLAMP \ No newline at end of file diff --git a/code/WorkInProgress/Aryn/Lighting/Light.dm b/code/WorkInProgress/Aryn/Lighting/Light.dm new file mode 100644 index 00000000000..40cf88120d4 --- /dev/null +++ b/code/WorkInProgress/Aryn/Lighting/Light.dm @@ -0,0 +1,70 @@ +/* + +Overview: + This object functions similarly to /tg/'s /light. It is responsible for calculating what turfs are lit by it. + +Class Vars: + radius - Set by atom/SetLight(). This stores how far out turfs will be lit up. + intensity - Set by atom/SetLight(). Stores the amount of light generated at the center. + lit_turfs - A list of turfs being lit by this light. + atom - The atom this light is attached to. + +Class Procs: + Reset() + This is called whenever the light changes, or the underlying atom changes position. + + Off() + A quick way to turn off a light. Removes the light from all turfs in lit_turfs. + + CalculateBrightness(turf/T) + Returns the brightness that should be displayed by this light on a specific turf. + +*/ + + +light/var/radius = 0 +light/var/intensity = 0 +light/var/list/lit_turfs +light/var/atom/atom + +light/New(atom/atom) + src.atom = atom + +light/proc/Reset() + Off() + + lit_turfs = list() + + if(intensity > 0) + for(var/turf/T in view(atom,radius+1)) + T.AddLight(src) + lit_turfs.Add(T) + +light/proc/Off() + for(var/turf/T in lit_turfs) + T.RemoveLight(src) + +light/proc/CalculateBrightness(turf/T) + var/square = get_square_dist(atom.x,atom.y,atom.z,T.x,T.y,T.z) + if(square > (radius+2)*(radius+2)) return 0 + //+2 offset gives an ambient light effect. + + var/value = ((radius)/(2*fsqrt(square) + 1)) * intensity - 0.48 + /* + lightRadius + ---------------- * lightValue - 0.48 + 2 * distance + 1 + + The light decreases by twice the distance, starting from the radius. + The + 1 causes the graph to shift to the left one unit so that division by zero is prevented on the source tile. + + This is then multiplied by the light value to give the final result. + The -0.48 offset causes the value to be near zero at the radius. + + This gives a result which is likely close to the inverse-square law in two dimensions instead of three. + */ + + + return max(min( value , intensity), 0) //Ensure the value never goes above the maximum light value or below zero. + + //return cos(90 * sqrt(square) / max(1,lightRadius)) * lightValue \ No newline at end of file diff --git a/code/WorkInProgress/Aryn/Lighting/Lightpoint.dm b/code/WorkInProgress/Aryn/Lighting/Lightpoint.dm new file mode 100644 index 00000000000..dfca9db4fd2 --- /dev/null +++ b/code/WorkInProgress/Aryn/Lighting/Lightpoint.dm @@ -0,0 +1,60 @@ +/* + +Overview: + Perhaps the most cryptic datum in the lighting engine, there are four of these at the corners of every turf. + Any two turfs that share a corner will also have the same lightpoint. Because of the nature of the icons used, + light is shown at the corner of the turf rather than in the middle, necessitating some way to keep track of what + icon state to use. + +Class Vars: + x, y, z - The position of the lightpoint. x and y will usually be expressed in terms of 0.5 due to its location on the corner. + + NE, NW, SE, SW - The turfs that are in these directions relative to the lightpoint. + + cached_value - A cached value of max_value(). + +Class Procs: + max_value() + The maximum of the light amounts on the four turfs of this light point. + +*/ + +lightpoint + var/x + var/y + var/z + + var/turf/NE + var/turf/NW + var/turf/SW + var/turf/SE + + var/cached_value = -1 + + New(x,y,z) + var/turf/T = locate(x+0.5,y+0.5,z) + if(T) + NE = T + T.lightSW = src + T = locate(x-0.5,y+0.5,z) + if(T) + NW = T + T.lightSE = src + T = locate(x-0.5,y-0.5,z) + if(T) + SW = T + T.lightNE = src + T = locate(x+0.5,y-0.5,z) + if(T) + SE = T + T.lightNW = src + + proc/max_value() + if(cached_value < 0) + var + valueA = (NW?(NW.lit_value):0) + valueB = (NE?(NE.lit_value):0) + valueC = (SW?(SW.lit_value):0) + valueD = (SE?(SE.lit_value):0) + cached_value = max(valueA,valueB,valueC,valueD) + return cached_value \ No newline at end of file diff --git a/code/WorkInProgress/Aryn/Lighting/Math.dm b/code/WorkInProgress/Aryn/Lighting/Math.dm new file mode 100644 index 00000000000..cd8716373be --- /dev/null +++ b/code/WorkInProgress/Aryn/Lighting/Math.dm @@ -0,0 +1,22 @@ +/* + +Some math procs used by lighting, including ul's fastroot. + +*/ + +var/list/fastroot = list(0, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7) + +proc/get_square_dist(Ax,Ay,Az,Bx,By,Bz) + var/X = (Ax - Bx) + var/Y = (Ay - By) + var/Z = (Az - Bz) + return (X * X + Y * Y + Z * Z) + +proc/fsqrt(n) + if (n > fastroot.len) + //world << "Adding [n-fastroot.len] entries to root table." + for(var/i = fastroot.len, i <= n, i++) + fastroot += round(sqrt(i)) + return fastroot[n + 1] \ No newline at end of file diff --git a/icons/effects/ArynLights.dmi b/icons/effects/ArynLights.dmi new file mode 100644 index 00000000000..f3d21402a33 Binary files /dev/null and b/icons/effects/ArynLights.dmi differ