mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-26 06:29:23 +01:00
Merge pull request #8581 from MrPerson/lighting_2015
Object based lighting system
This commit is contained in:
+4
-12
@@ -53,18 +53,10 @@
|
||||
return 0
|
||||
return 1
|
||||
|
||||
|
||||
//Magic constants obtained by using linear regression on right-angled triangles of sides 0<x<1, 0<y<1
|
||||
//They should approximate pythagoras theorem well enough for our needs.
|
||||
#define k1 0.934
|
||||
#define k2 0.427
|
||||
/proc/cheap_hypotenuse(Ax,Ay,Bx,By) // T is just the second atom to check distance to center with
|
||||
var/dx = abs(Ax - Bx) //sides of right-angled triangle
|
||||
var/dy = abs(Ay - By)
|
||||
if(dx>=dy) return (k1*dx) + (k2*dy) //No sqrt or powers :)
|
||||
else return (k2*dx) + (k1*dy)
|
||||
#undef k1
|
||||
#undef k2
|
||||
//We used to use linear regression to approximate the answer, but Mloc realized this was actually faster.
|
||||
//And lo and behold, it is, and it's more accurate to boot.
|
||||
/proc/cheap_hypotenuse(Ax,Ay,Bx,By)
|
||||
return sqrt(abs(Ax - Bx)**2 + abs(Ay - By)**2) //A squared + B squared = C squared
|
||||
|
||||
/proc/circlerange(center=usr,radius=3)
|
||||
|
||||
|
||||
@@ -828,8 +828,6 @@ Turf and target are seperate in case you want to teleport some distance from a t
|
||||
//else populates the list first before returning it
|
||||
/proc/SortAreas()
|
||||
for(var/area/A in world)
|
||||
if(A.lighting_subarea)
|
||||
continue
|
||||
sortedAreas.Add(A)
|
||||
|
||||
sortTim(sortedAreas, /proc/cmp_name_asc)
|
||||
@@ -888,152 +886,6 @@ Turf and target are seperate in case you want to teleport some distance from a t
|
||||
var/y_pos = null
|
||||
var/z_pos = null
|
||||
|
||||
/area/proc/move_contents_to(var/area/A, var/turftoleave=null, var/direction = null)
|
||||
//Takes: Area. Optional: turf type to leave behind.
|
||||
//Returns: Nothing.
|
||||
//Notes: Attempts to move the contents of one area to another area.
|
||||
// Movement based on lower left corner. Tiles that do not fit
|
||||
// into the new area will not be moved.
|
||||
|
||||
if(!A || !src) return 0
|
||||
|
||||
var/list/turfs_src = get_area_turfs(src.type)
|
||||
var/list/turfs_trg = get_area_turfs(A.type)
|
||||
|
||||
var/src_min_x = 0
|
||||
var/src_min_y = 0
|
||||
for (var/turf/T in turfs_src)
|
||||
if(T.x < src_min_x || !src_min_x) src_min_x = T.x
|
||||
if(T.y < src_min_y || !src_min_y) src_min_y = T.y
|
||||
|
||||
var/trg_min_x = 0
|
||||
var/trg_min_y = 0
|
||||
for (var/turf/T in turfs_trg)
|
||||
if(T.x < trg_min_x || !trg_min_x) trg_min_x = T.x
|
||||
if(T.y < trg_min_y || !trg_min_y) trg_min_y = T.y
|
||||
|
||||
var/list/refined_src = new/list()
|
||||
for(var/turf/T in turfs_src)
|
||||
refined_src += T
|
||||
refined_src[T] = new/datum/coords
|
||||
var/datum/coords/C = refined_src[T]
|
||||
C.x_pos = (T.x - src_min_x)
|
||||
C.y_pos = (T.y - src_min_y)
|
||||
|
||||
var/list/refined_trg = new/list()
|
||||
for(var/turf/T in turfs_trg)
|
||||
refined_trg += T
|
||||
refined_trg[T] = new/datum/coords
|
||||
var/datum/coords/C = refined_trg[T]
|
||||
C.x_pos = (T.x - trg_min_x)
|
||||
C.y_pos = (T.y - trg_min_y)
|
||||
|
||||
var/list/fromupdate = new/list()
|
||||
var/list/toupdate = new/list()
|
||||
|
||||
moving:
|
||||
for (var/turf/T in refined_src)
|
||||
var/datum/coords/C_src = refined_src[T]
|
||||
for (var/turf/B in refined_trg)
|
||||
var/datum/coords/C_trg = refined_trg[B]
|
||||
if(C_src.x_pos == C_trg.x_pos && C_src.y_pos == C_trg.y_pos)
|
||||
|
||||
var/old_dir1 = T.dir
|
||||
var/old_icon_state1 = T.icon_state
|
||||
var/old_icon1 = T.icon
|
||||
|
||||
var/turf/X = new T.type(B)
|
||||
X.dir = old_dir1
|
||||
X.icon_state = old_icon_state1
|
||||
X.icon = old_icon1 //Shuttle floors are in shuttle.dmi while the defaults are floors.dmi
|
||||
|
||||
|
||||
// Give the new turf our air, if simulated
|
||||
if(istype(X, /turf/simulated) && istype(T, /turf/simulated))
|
||||
var/turf/simulated/sim = X
|
||||
sim.copy_air_with_tile(T)
|
||||
|
||||
/* Quick visual fix for some weird shuttle corner artefacts when on transit space tiles */
|
||||
if(direction && findtext(X.icon_state, "swall_s"))
|
||||
|
||||
// Spawn a new shuttle corner object
|
||||
var/obj/corner = new()
|
||||
corner.loc = X
|
||||
corner.density = 1
|
||||
corner.anchored = 1
|
||||
corner.icon = X.icon
|
||||
corner.icon_state = replacetext(X.icon_state, "_s", "_f")
|
||||
corner.tag = "delete me"
|
||||
corner.name = "wall"
|
||||
|
||||
// Find a new turf to take on the property of
|
||||
var/turf/nextturf = get_step(corner, direction)
|
||||
if(!nextturf || !istype(nextturf, /turf/space))
|
||||
nextturf = get_step(corner, turn(direction, 180))
|
||||
|
||||
|
||||
// Take on the icon of a neighboring scrolling space icon
|
||||
X.icon = nextturf.icon
|
||||
X.icon_state = nextturf.icon_state
|
||||
|
||||
|
||||
for(var/obj/O in T)
|
||||
|
||||
// Reset the shuttle corners
|
||||
if(O.tag == "delete me")
|
||||
X.icon = 'icons/turf/shuttle.dmi'
|
||||
X.icon_state = replacetext(O.icon_state, "_f", "_s") // revert the turf to the old icon_state
|
||||
X.name = "wall"
|
||||
qdel(O) // prevents multiple shuttle corners from stacking
|
||||
continue
|
||||
if(!istype(O,/obj)) continue
|
||||
O.loc = X
|
||||
for(var/mob/M in T)
|
||||
if(!M.move_on_shuttle)
|
||||
continue
|
||||
M.loc = X
|
||||
|
||||
// var/area/AR = X.loc
|
||||
|
||||
// if(AR.lighting_use_dynamic) //TODO: rewrite this code so it's not messed by lighting ~Carn
|
||||
// X.opacity = !X.opacity
|
||||
// X.SetOpacity(!X.opacity)
|
||||
|
||||
toupdate += X
|
||||
|
||||
if(turftoleave)
|
||||
var/turf/ttl = new turftoleave(T)
|
||||
|
||||
// var/area/AR2 = ttl.loc
|
||||
|
||||
// if(AR2.lighting_use_dynamic) //TODO: rewrite this code so it's not messed by lighting ~Carn
|
||||
// ttl.opacity = !ttl.opacity
|
||||
// ttl.sd_SetOpacity(!ttl.opacity)
|
||||
|
||||
fromupdate += ttl
|
||||
|
||||
else
|
||||
T.ChangeTurf(/turf/space)
|
||||
|
||||
refined_src -= T
|
||||
refined_trg -= B
|
||||
continue moving
|
||||
|
||||
|
||||
if(toupdate.len)
|
||||
for(var/turf/simulated/T1 in toupdate)
|
||||
SSair.remove_from_active(T1)
|
||||
T1.CalculateAdjacentTurfs()
|
||||
SSair.add_to_active(T1,1)
|
||||
|
||||
if(fromupdate.len)
|
||||
for(var/turf/simulated/T2 in fromupdate)
|
||||
SSair.remove_from_active(T2)
|
||||
T2.CalculateAdjacentTurfs()
|
||||
SSair.add_to_active(T2,1)
|
||||
|
||||
|
||||
|
||||
/proc/DuplicateObject(obj/original, var/perfectcopy = 0 , var/sameloc = 0)
|
||||
if(!original)
|
||||
return null
|
||||
|
||||
@@ -1,308 +0,0 @@
|
||||
/*
|
||||
Modified DynamicAreaLighting for TGstation - Coded by Carnwennan
|
||||
|
||||
This is TG's 'new' lighting system. It's basically a heavily modified combination of Forum_Account's and
|
||||
ShadowDarke's respective lighting libraries. Credits, where due, to them.
|
||||
|
||||
Like sd_DAL (what we used to use), it changes the shading overlays of areas by splitting each type of area into sub-areas
|
||||
by using the var/tag variable and moving turfs into the contents list of the correct sub-area. This method is
|
||||
much less costly than using overlays or objects.
|
||||
|
||||
Unlike sd_DAL however it uses a queueing system. Everytime we call a change to opacity or luminosity
|
||||
(through SetOpacity() or SetLuminosity()) we are simply updating variables and scheduling certain lights/turfs for an
|
||||
update. Actual updates are handled periodically by the SSlighting subsystem. This carries additional overheads, however it
|
||||
means that each thing is changed only once per SSlighting.wait deciseconds. Allowing for greater control
|
||||
over how much priority we'd like lighting updates to have.
|
||||
|
||||
UPDATE: we no longer postpone lighting updates by editting variables, there is now a subsystem/proc/postpone() procedure.
|
||||
So you would do SSlighting.postpone()
|
||||
|
||||
Unlike our old system there are hardcoded maximum luminositys (different for certain atoms).
|
||||
This is to cap the cost of creating lighting effects.
|
||||
(without this, an atom with luminosity of 20 would have to update 41^2 turfs!) :s
|
||||
|
||||
Also, in order for the queueing system to work, each light remembers the effect it casts on each turf. This is going to
|
||||
have larger memory requirements than our previous system but it's easily worth the hassle for the greater control we
|
||||
gain. It also reduces cost of removing lighting effects by a lot!
|
||||
|
||||
Known Issues/TODO:
|
||||
Shuttles still do not have support for dynamic lighting (I hope to fix this at some point)
|
||||
No directional lighting support. (prototype looked ugly)
|
||||
*/
|
||||
|
||||
#define USE_CIRCULAR_LIGHTING //comment this out to use old square lighting effects.
|
||||
|
||||
/datum/light_source
|
||||
var/atom/owner
|
||||
var/changed = 1
|
||||
var/list/effect = list()
|
||||
var/__x = 0 //x coordinate at last update
|
||||
var/__y = 0 //y coordinate at last update
|
||||
|
||||
|
||||
/datum/light_source/New(atom/A)
|
||||
if(!istype(A))
|
||||
CRASH("The first argument to the light object's constructor must be the atom that is the light source. Expected atom, received '[A]' instead.")
|
||||
..()
|
||||
owner = A
|
||||
__x = owner.x
|
||||
__y = owner.y
|
||||
// the lighting object maintains a list of all light sources
|
||||
SSlighting.lights += src
|
||||
|
||||
|
||||
//Check a light to see if its effect needs reprocessing. If it does, remove any old effect and create a new one
|
||||
/datum/light_source/proc/check()
|
||||
if(!owner)
|
||||
remove_effect()
|
||||
return 1 //causes it to be removed from our list of lights. The garbage collector will then destroy it.
|
||||
|
||||
// check to see if we've moved since last update
|
||||
if(owner.x != __x || owner.y != __y)
|
||||
__x = owner.x
|
||||
__y = owner.y
|
||||
changed = 1
|
||||
|
||||
if(changed)
|
||||
changed = 0
|
||||
remove_effect()
|
||||
return add_effect()
|
||||
return 0
|
||||
|
||||
|
||||
/datum/light_source/proc/remove_effect()
|
||||
// before we apply the effect we remove the light's current effect.
|
||||
for(var/turf/T in effect) // negate the effect of this light source
|
||||
T.update_lumcount(-effect[T])
|
||||
|
||||
if(T.affecting_lights && T.affecting_lights.len)
|
||||
T.affecting_lights -= src
|
||||
else
|
||||
T.affecting_lights = null
|
||||
|
||||
effect.Cut() // clear the effect list
|
||||
|
||||
/datum/light_source/proc/add_effect()
|
||||
// only do this if the light is turned on and is on the map
|
||||
if(owner.loc && owner.luminosity > 0)
|
||||
effect = list()
|
||||
var/turf/To = get_turf(owner)
|
||||
var/range = owner.get_light_range()
|
||||
|
||||
for(var/turf/T in view(range, To))
|
||||
var/delta_lumcount = T.lumen(src)
|
||||
if(delta_lumcount > 0)
|
||||
effect[T] = delta_lumcount
|
||||
T.update_lumcount(delta_lumcount)
|
||||
|
||||
if(!T.affecting_lights)
|
||||
T.affecting_lights = list()
|
||||
T.affecting_lights += src
|
||||
|
||||
return 0
|
||||
else
|
||||
owner.light = null
|
||||
return 1 //cause the light to be removed from the lights list and garbage collected once it's no
|
||||
//longer referenced by the queue
|
||||
|
||||
/turf/proc/lumen(datum/light_source/L)
|
||||
. = L.owner.luminosity
|
||||
#ifdef USE_CIRCULAR_LIGHTING
|
||||
. -= cheap_hypotenuse(x, y, L.__x, L.__y)
|
||||
#else
|
||||
. -= max(abs(x - L.__x), abs(y - L.__y))
|
||||
#endif
|
||||
return .
|
||||
|
||||
/turf/space/lumen()
|
||||
return 0
|
||||
|
||||
|
||||
/atom
|
||||
var/datum/light_source/light
|
||||
|
||||
|
||||
//Turfs with opacity when they are constructed will trigger nearby lights to update
|
||||
//Turfs and atoms with luminosity when they are constructed will create a light_source automatically
|
||||
/turf/New()
|
||||
..()
|
||||
if(luminosity)
|
||||
if(light) WARNING("[type] - Don't set lights up manually during New(), We do it automatically.")
|
||||
light = new(src)
|
||||
|
||||
//Movable atoms with opacity when they are constructed will trigger nearby lights to update
|
||||
//Movable atoms with luminosity when they are constructed will create a light_source automatically
|
||||
/atom/movable/New()
|
||||
..()
|
||||
if(opacity)
|
||||
UpdateAffectingLights()
|
||||
if(luminosity)
|
||||
if(light) WARNING("[type] - Don't set lights up manually during New(), We do it automatically.")
|
||||
light = new(src)
|
||||
|
||||
//Objects with opacity will trigger nearby lights to update at next lighting process.
|
||||
/atom/movable/Destroy()
|
||||
if(opacity)
|
||||
UpdateAffectingLights()
|
||||
return ..()
|
||||
|
||||
//Sets our luminosity.
|
||||
//If we have no light it will create one.
|
||||
//If we are setting luminosity to 0 the light will be cleaned up by the controller and garbage collected once all its
|
||||
//queues are complete.
|
||||
//if we have a light already it is merely updated, rather than making a new one.
|
||||
/atom/proc/SetLuminosity(new_luminosity)
|
||||
if(new_luminosity < 0)
|
||||
new_luminosity = 0
|
||||
if(light)
|
||||
if(luminosity != new_luminosity) //non-luminous lights are removed from the lights list in add_effect()
|
||||
light.changed = 1
|
||||
else
|
||||
if(new_luminosity)
|
||||
light = new(src)
|
||||
luminosity = new_luminosity
|
||||
|
||||
/atom/proc/AddLuminosity(delta_luminosity)
|
||||
SetLuminosity(luminosity + delta_luminosity)
|
||||
|
||||
/area/SetLuminosity(new_luminosity) //we don't want dynamic lighting for areas
|
||||
luminosity = !!new_luminosity
|
||||
|
||||
|
||||
//change our opacity (defaults to toggle), and then update all lights that affect us.
|
||||
/atom/proc/SetOpacity(new_opacity)
|
||||
if(new_opacity == null)
|
||||
new_opacity = !opacity //default = toggle opacity
|
||||
else if(opacity == new_opacity)
|
||||
return 0 //opacity hasn't changed! don't bother doing anything
|
||||
opacity = new_opacity //update opacity, the below procs now call light updates.
|
||||
UpdateAffectingLights()
|
||||
|
||||
|
||||
/turf
|
||||
var/lighting_lumcount = 0
|
||||
var/lighting_changed = 0
|
||||
var/list/affecting_lights //not initialised until used (even empty lists reserve a fair bit of memory)
|
||||
|
||||
/turf/space
|
||||
lighting_lumcount = 4 //starlight
|
||||
|
||||
/turf/proc/update_lumcount(amount)
|
||||
lighting_lumcount += amount
|
||||
if(!lighting_changed)
|
||||
SSlighting.changed_turfs += src
|
||||
lighting_changed = 1
|
||||
|
||||
/area/proc/lighting_tag(level)
|
||||
return tagbase + "sd_L[level]"
|
||||
|
||||
/area/proc/build_lighting_area(tag, level)
|
||||
var/area/A = locate(tag) // find an appropriate area
|
||||
if(A)
|
||||
return A
|
||||
A = new type() // create area if it wasn't found
|
||||
// replicate vars
|
||||
for(var/V in vars)
|
||||
switch(V)
|
||||
if("contents","last_light","overlays") continue
|
||||
else
|
||||
if(issaved(vars[V])) A.vars[V] = vars[V]
|
||||
|
||||
A.tag = tag
|
||||
A.lighting_subarea = 1
|
||||
A.lighting_space = 0 // in case it was copied from a space subarea
|
||||
A.SetLightLevel(level)
|
||||
|
||||
related += A
|
||||
return A
|
||||
|
||||
/turf/proc/shift_to_subarea()
|
||||
lighting_changed = 0
|
||||
var/area/Area = loc
|
||||
|
||||
if(!istype(Area) || !Area.lighting_use_dynamic) return
|
||||
|
||||
var/level = min(max(round(lighting_lumcount,1),0),SSlighting.lighting_images.len)
|
||||
var/new_tag = Area.lighting_tag(level)
|
||||
if(Area.tag!=new_tag) //skip if already in this area
|
||||
var/area/A = Area.build_lighting_area(new_tag,level)
|
||||
A.contents += src // move the turf into the area
|
||||
|
||||
/area
|
||||
var/lighting_use_dynamic = 1 //Turn this flag off to prevent sd_DynamicAreaLighting from affecting this area
|
||||
var/last_light //tracks the last light level set for this area (used for removing previously applied lighting overlays)
|
||||
var/lighting_subarea = 0 //tracks whether we're a lighting sub-area
|
||||
var/lighting_space = 0 // true for space-only lighting subareas
|
||||
var/tagbase
|
||||
|
||||
/area/proc/SetLightLevel(light)
|
||||
if(!src) return
|
||||
if(light <= 1)
|
||||
light = 1
|
||||
luminosity = 0
|
||||
else
|
||||
if(light > SSlighting.lighting_images.len)
|
||||
light = SSlighting.lighting_images.len
|
||||
luminosity = 1
|
||||
|
||||
if(last_light != light)
|
||||
if(last_light)
|
||||
overlays -= SSlighting.lighting_images[last_light]
|
||||
overlays += SSlighting.lighting_images[light]
|
||||
last_light = light
|
||||
|
||||
/area/proc/SetDynamicLighting()
|
||||
lighting_use_dynamic = 1
|
||||
for(var/turf/T in contents)
|
||||
T.update_lumcount(0)
|
||||
|
||||
/area/proc/InitializeLighting() //TODO: could probably improve this bit ~Carn
|
||||
tagbase = "[type]"
|
||||
if(!tag) tag = tagbase
|
||||
if(!lighting_use_dynamic)
|
||||
if(!lighting_subarea) // see if this is a lighting subarea already
|
||||
//show the dark overlay so areas, not yet in a lighting subarea, won't be bright as day and look silly.
|
||||
SetLightLevel(4)
|
||||
|
||||
#undef USE_CIRCULAR_LIGHTING
|
||||
|
||||
//set the changed status of all lights which could have possibly lit this atom.
|
||||
//We don't need to worry about lights which lit us but moved away, since they will have change status set already
|
||||
//This proc can cause lots of lights to be updated. :(
|
||||
/atom/proc/UpdateAffectingLights()
|
||||
|
||||
/atom/movable/UpdateAffectingLights()
|
||||
if(isturf(loc))
|
||||
loc.UpdateAffectingLights()
|
||||
|
||||
/turf/UpdateAffectingLights()
|
||||
if(affecting_lights)
|
||||
for(var/thing in affecting_lights)
|
||||
thing:changed = 1 //force it to update at next process()
|
||||
|
||||
|
||||
#define LIGHTING_MAX_LUMINOSITY_STATIC 8 //Maximum luminosity to reduce lag.
|
||||
#define LIGHTING_MAX_LUMINOSITY_MOBILE 5 //Moving objects have a lower max luminosity since these update more often. (lag reduction)
|
||||
#define LIGHTING_MAX_LUMINOSITY_MOB 5
|
||||
#define LIGHTING_MAX_LUMINOSITY_TURF 1 //turfs have a severely shortened range to protect from inevitable floor-lighttile spam.
|
||||
|
||||
//caps luminosity effects max-range based on what type the light's owner is.
|
||||
/atom/proc/get_light_range()
|
||||
return min(luminosity, LIGHTING_MAX_LUMINOSITY_STATIC)
|
||||
|
||||
/atom/movable/get_light_range()
|
||||
return min(luminosity, LIGHTING_MAX_LUMINOSITY_MOBILE)
|
||||
|
||||
/mob/get_light_range()
|
||||
return min(luminosity, LIGHTING_MAX_LUMINOSITY_MOB)
|
||||
|
||||
/obj/machinery/light/get_light_range()
|
||||
return min(luminosity, LIGHTING_MAX_LUMINOSITY_STATIC)
|
||||
|
||||
/turf/get_light_range()
|
||||
return min(luminosity, LIGHTING_MAX_LUMINOSITY_TURF)
|
||||
|
||||
#undef LIGHTING_MAX_LUMINOSITY_STATIC
|
||||
#undef LIGHTING_MAX_LUMINOSITY_MOBILE
|
||||
#undef LIGHTING_MAX_LUMINOSITY_MOB
|
||||
#undef LIGHTING_MAX_LUMINOSITY_TURF
|
||||
@@ -82,7 +82,8 @@ var/datum/subsystem/garbage_collector/SSgarbage
|
||||
// Return true if the the GC controller should allow the object to continue existing. (Useful if pooling objects.)
|
||||
/datum/proc/Destroy()
|
||||
//del(src)
|
||||
return
|
||||
tag = null
|
||||
return 0
|
||||
|
||||
/datum/var/gc_destroyed //Time when this object was destroyed.
|
||||
|
||||
|
||||
@@ -1,56 +1,50 @@
|
||||
var/datum/subsystem/lighting/SSlighting
|
||||
|
||||
#define MC_AVERAGE(average, current) (0.8*(average) + 0.2*(current))
|
||||
#define LIGHTING_ICON 'icons/effects/ss13_dark_alpha6.dmi'
|
||||
#define LIGHTING_LAYER 10 //Drawing layer for lighting overlays
|
||||
|
||||
/datum/subsystem/lighting
|
||||
name = "Lighting"
|
||||
wait = 5
|
||||
priority = 1
|
||||
|
||||
var/list/lighting_images = list() //replaces lighting_states (use lighting_images.len) ~carn
|
||||
var/list/lights = list() //list of all datum/light_source
|
||||
var/lights_workload = 0 //stats on the largest number of lights (max lights.len)
|
||||
var/list/changed_turfs = list() //list of all turfs which need moving to a new lighting subarea
|
||||
var/list/changed_lights = list() //list of all datum/light_source that need updating
|
||||
var/changed_lights_workload = 0 //stats on the largest number of lights (max changed_lights.len)
|
||||
var/list/changed_turfs = list() //list of all turfs which may have a different light level
|
||||
var/changed_turfs_workload = 0 //stats on the largest number of turfs changed (max changed_turfs.len)
|
||||
|
||||
|
||||
/datum/subsystem/lighting/New()
|
||||
NEW_SS_GLOBAL(SSlighting)
|
||||
|
||||
//cache lighting images
|
||||
if(!lighting_images.len)
|
||||
for(var/icon_state in icon_states(LIGHTING_ICON))
|
||||
lighting_images += image(LIGHTING_ICON, null, icon_state, LIGHTING_LAYER)
|
||||
|
||||
return ..()
|
||||
|
||||
|
||||
/datum/subsystem/lighting/stat_entry()
|
||||
stat(name, "[round(cost,0.001)]ds (CPU:[round(cpu,1)]%) L:[round(lights_workload,1)]/T:[round(changed_turfs_workload,1)]")
|
||||
stat(name, "[round(cost,0.001)]ds (CPU:[round(cpu,1)]%) L:[round(changed_lights_workload,1)]/T:[round(changed_turfs_workload,1)]")
|
||||
|
||||
|
||||
//Workhorse of lighting. It cycles through each light to see which ones need their effects updating. It updates their
|
||||
//effects and then processes every turf in the queue, moving the turfs to the corresponing lighting sub-area.
|
||||
//All queue lists prune themselves, which will cause lights with no luminosity to be garbage collected (cheaper and safer
|
||||
//than deleting them).
|
||||
//Workhorse of lighting. It cycles through each light that needs updating. It updates their
|
||||
//effects and then processes every turf in the queue, updating their lighting object's appearance
|
||||
//Any light that returns 1 in check() deletes itself
|
||||
//By using queues we are ensuring we don't perform more updates than are necessary
|
||||
/datum/subsystem/lighting/fire()
|
||||
lights_workload = MC_AVERAGE(lights_workload, lights.len)
|
||||
var/i=1
|
||||
for(var/thing in lights)
|
||||
if(thing && !thing:check()) //yes, cry that I'm using the : operator, it's much faster looping like this. And this gets called a lot. Dealwithit.
|
||||
++i
|
||||
continue
|
||||
lights.Cut(i, i+1)
|
||||
changed_lights_workload = MC_AVERAGE(changed_lights_workload, changed_lights.len)
|
||||
|
||||
// for(var/area/A in sortedAreas)
|
||||
// A.luminosity = 1
|
||||
|
||||
for(var/datum/light_source/thing in changed_lights)
|
||||
thing.check()
|
||||
changed_lights.Cut()
|
||||
|
||||
changed_turfs_workload = MC_AVERAGE(changed_turfs_workload, changed_turfs.len)
|
||||
for(var/thing in changed_turfs)
|
||||
if(thing && thing:lighting_changed)
|
||||
thing:shift_to_subarea()
|
||||
for(var/turf/thing in changed_turfs)
|
||||
if(thing && thing.lighting_changed)
|
||||
thing.redraw_lighting()
|
||||
changed_turfs.Cut()
|
||||
|
||||
// for(var/area/A in sortedAreas)
|
||||
// A.luminosity = !A.lighting_use_dynamic
|
||||
|
||||
//same as above except it attempts to shift ALL turfs in the world regardless of lighting_changed status
|
||||
//Does not loop. Should be run prior to process() being called for the first time.
|
||||
@@ -58,12 +52,13 @@ var/datum/subsystem/lighting/SSlighting
|
||||
//z-levels with the z_level argument
|
||||
/datum/subsystem/lighting/Initialize(timeofday, z_level)
|
||||
|
||||
var/i=1
|
||||
for(var/thing in lights)
|
||||
if(thing && !thing:check())
|
||||
++i
|
||||
continue
|
||||
lights.Cut(i, i+1)
|
||||
// for(var/area/A in sortedAreas)
|
||||
// A.luminosity = 1
|
||||
|
||||
for(var/datum/light_source/thing in changed_lights)
|
||||
thing.add_effect()
|
||||
thing.changed = 0
|
||||
changed_lights.Cut()
|
||||
|
||||
var/z_start = 1
|
||||
var/z_finish = world.maxz
|
||||
@@ -72,28 +67,24 @@ var/datum/subsystem/lighting/SSlighting
|
||||
z_start = z_level
|
||||
z_finish = z_level
|
||||
|
||||
for(var/z=z_start, z<=z_finish, ++z)
|
||||
for(var/x=1, x<=world.maxx, ++x)
|
||||
for(var/y=1, y<=world.maxy, ++y)
|
||||
var/turf/T = locate(x,y,z)
|
||||
if(T)
|
||||
T.shift_to_subarea()
|
||||
var/list/turfs_to_init = block(locate(1, 1, z_start), locate(world.maxx, world.maxy, z_finish))
|
||||
|
||||
for(var/turf/T in turfs_to_init)
|
||||
T.init_lighting()
|
||||
|
||||
if(z_level)
|
||||
//we need to loop through to clear only shifted turfs from the list. or we will cause errors
|
||||
i=1
|
||||
for(var/thing in changed_turfs)
|
||||
if(thing && thing:z < z_start && z_finish < thing:z)
|
||||
var/i=1
|
||||
for(var/turf/thing in changed_turfs)
|
||||
if(thing && thing.z < z_start && z_finish < thing.z)
|
||||
++i
|
||||
continue
|
||||
changed_turfs.Cut(i, i+1)
|
||||
else
|
||||
changed_turfs.Cut()
|
||||
|
||||
if(config.starlight)
|
||||
set background = 1
|
||||
for(var/turf/space/S in world)
|
||||
S.update_starlight()
|
||||
// for(var/area/A in sortedAreas)
|
||||
// A.luminosity = !A.lighting_use_dynamic
|
||||
|
||||
..()
|
||||
|
||||
@@ -103,21 +94,20 @@ var/datum/subsystem/lighting/SSlighting
|
||||
/datum/subsystem/lighting/Recover()
|
||||
if(!istype(SSlighting.changed_turfs))
|
||||
SSlighting.changed_turfs = list()
|
||||
if(!istype(SSlighting.lights))
|
||||
SSlighting.lights = list()
|
||||
if(!istype(SSlighting.changed_lights))
|
||||
SSlighting.changed_lights = list()
|
||||
|
||||
if(istype(SSlighting.lighting_images))
|
||||
lighting_images = SSlighting.lighting_images
|
||||
// for(var/area/A in sortedAreas)
|
||||
// A.luminosity = 1
|
||||
|
||||
for(var/datum/light_source/L in SSlighting.lights)
|
||||
for(var/datum/light_source/L in SSlighting.changed_lights)
|
||||
spawn(-1) //so we don't crash the loop (inefficient)
|
||||
L.check()
|
||||
lights += L //If we didn't runtime then this will get transferred over
|
||||
|
||||
for(var/turf/T in changed_turfs)
|
||||
if(T.lighting_changed)
|
||||
spawn(-1)
|
||||
T.shift_to_subarea()
|
||||
T.redraw_lighting()
|
||||
|
||||
var/msg = "## DEBUG: [time2text(world.timeofday)] [name] subsystem restarted. Reports:\n"
|
||||
for(var/varname in SSlighting.vars)
|
||||
@@ -132,5 +122,5 @@ var/datum/subsystem/lighting/SSlighting
|
||||
msg += "\t [varname] = [varval1] -> [varval2]\n"
|
||||
world.log << msg
|
||||
|
||||
#undef LIGHTING_ICON
|
||||
#undef LIGHTING_LAYER
|
||||
// for(var/area/A in sortedAreas)
|
||||
// A.luminosity = !A.lighting_use_dynamic
|
||||
@@ -194,8 +194,7 @@ proc/process_ghost_teleport_locs()
|
||||
|
||||
/area/asteroid/artifactroom/New()
|
||||
..()
|
||||
lighting_use_dynamic = 1
|
||||
InitializeLighting()
|
||||
SetDynamicLighting()
|
||||
|
||||
/area/planet/clown
|
||||
name = "\improper Clown Planet"
|
||||
|
||||
@@ -43,7 +43,6 @@
|
||||
..()
|
||||
|
||||
power_change() // all machines set to current power level, also updates lighting icon
|
||||
InitializeLighting()
|
||||
|
||||
blend_mode = BLEND_MULTIPLY // Putting this in the constructure so that it stops the icons being screwed up in the map editor.
|
||||
|
||||
@@ -223,7 +222,7 @@
|
||||
return
|
||||
|
||||
/area/proc/updateicon()
|
||||
if ((fire || eject || party) && (!requires_power||power_environ) && !lighting_space)//If it doesn't require power, can still activate this proc.
|
||||
if ((fire || eject || party) && (!requires_power||power_environ))//If it doesn't require power, can still activate this proc.
|
||||
if(fire && !eject && !party)
|
||||
icon_state = "blue"
|
||||
/*else if(atmosalm && !fire && !eject && !party)
|
||||
@@ -238,6 +237,8 @@
|
||||
// new lighting behaviour with obj lights
|
||||
icon_state = null
|
||||
|
||||
/area/space/updateicon()
|
||||
icon_state = null
|
||||
|
||||
/*
|
||||
#define EQUIP 1
|
||||
@@ -251,8 +252,6 @@
|
||||
return 1
|
||||
if(master.always_unpowered)
|
||||
return 0
|
||||
if(src.lighting_space)
|
||||
return 0 // Nope sorry
|
||||
switch(chan)
|
||||
if(EQUIP)
|
||||
return master.power_equip
|
||||
@@ -263,6 +262,9 @@
|
||||
|
||||
return 0
|
||||
|
||||
/area/space/powered(chan) //Nope.avi
|
||||
return 0
|
||||
|
||||
// called when power status changes
|
||||
|
||||
/area/proc/power_change()
|
||||
|
||||
@@ -46,11 +46,13 @@
|
||||
else if (step(src, WEST))
|
||||
. = step(src, SOUTH)
|
||||
|
||||
|
||||
if(!loc || (loc == oldloc && oldloc != newloc))
|
||||
last_move = 0
|
||||
return
|
||||
|
||||
if(.)
|
||||
Moved(oldloc, direct)
|
||||
|
||||
last_move = direct
|
||||
|
||||
spawn(5) // Causes space drifting. /tg/station has no concept of speed, we just use 5
|
||||
@@ -58,6 +60,10 @@
|
||||
if(loc == newloc) //Remove this check and people can accelerate. Not opening that can of worms just yet.
|
||||
newtonian_move(last_move)
|
||||
|
||||
//Called after a successful Move(). By this point, we've already moved
|
||||
/atom/movable/proc/Moved(atom/OldLoc, Dir)
|
||||
return 1
|
||||
|
||||
/atom/movable/Del()
|
||||
if(isnull(gc_destroyed) && loc)
|
||||
testing("GC: -- [type] was deleted via del() rather than qdel() --")
|
||||
@@ -68,14 +74,13 @@
|
||||
..()
|
||||
|
||||
/atom/movable/Destroy()
|
||||
. = ..()
|
||||
if(reagents)
|
||||
qdel(reagents)
|
||||
for(var/atom/movable/AM in contents)
|
||||
qdel(AM)
|
||||
tag = null
|
||||
loc = null
|
||||
invisibility = 101
|
||||
// Do not call ..()
|
||||
|
||||
// Previously known as HasEntered()
|
||||
// This is automatically called when something enters your square
|
||||
@@ -96,12 +101,15 @@
|
||||
|
||||
/atom/movable/proc/forceMove(atom/destination)
|
||||
if(destination)
|
||||
if(loc)
|
||||
loc.Exited(src)
|
||||
var/atom/oldloc = loc
|
||||
if(oldloc)
|
||||
oldloc.Exited(src, destination)
|
||||
loc = destination
|
||||
loc.Entered(src)
|
||||
for(var/atom/movable/AM in loc)
|
||||
destination.Entered(src, oldloc)
|
||||
for(var/atom/movable/AM in destination)
|
||||
if(AM == src) continue
|
||||
AM.Crossed(src)
|
||||
Moved(oldloc, 0)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
@@ -57,9 +57,7 @@
|
||||
|
||||
|
||||
/datum/station_state/proc/count()
|
||||
for(var/turf/T in world)
|
||||
if(T.z != ZLEVEL_STATION)
|
||||
continue
|
||||
for(var/turf/T in block(locate(1,1,1), locate(world.maxx,world.maxy,1)))
|
||||
|
||||
if(istype(T,/turf/simulated/floor))
|
||||
if(!(T:burnt))
|
||||
@@ -79,19 +77,16 @@
|
||||
else
|
||||
src.r_wall += 1
|
||||
|
||||
for(var/obj/O in world)
|
||||
if(O.z != ZLEVEL_STATION)
|
||||
continue
|
||||
|
||||
if(istype(O, /obj/structure/window))
|
||||
src.window += 1
|
||||
else if(istype(O, /obj/structure/grille) && (!O:destroyed))
|
||||
src.grille += 1
|
||||
else if(istype(O, /obj/machinery/door))
|
||||
src.door += 1
|
||||
else if(istype(O, /obj/machinery))
|
||||
src.mach += 1
|
||||
return
|
||||
for(var/obj/O in T.contents)
|
||||
if(istype(O, /obj/structure/window))
|
||||
src.window += 1
|
||||
else if(istype(O, /obj/structure/grille) && (!O:destroyed))
|
||||
src.grille += 1
|
||||
else if(istype(O, /obj/machinery/door))
|
||||
src.door += 1
|
||||
else if(istype(O, /obj/machinery))
|
||||
src.mach += 1
|
||||
|
||||
|
||||
/datum/station_state/proc/score(var/datum/station_state/result)
|
||||
|
||||
@@ -42,15 +42,15 @@
|
||||
pixel_y = ((src.dir & 3)? (src.dir ==1 ? 24 : -32) : (0))
|
||||
|
||||
spawn(20)
|
||||
for(var/obj/machinery/door/window/brigdoor/M in world)
|
||||
for(var/obj/machinery/door/window/brigdoor/M in range(20, src))
|
||||
if (M.id == src.id)
|
||||
targets += M
|
||||
|
||||
for(var/obj/machinery/flasher/F in world)
|
||||
for(var/obj/machinery/flasher/F in range(20, src))
|
||||
if(F.id == src.id)
|
||||
targets += F
|
||||
|
||||
for(var/obj/structure/closet/secure_closet/brig/C in world)
|
||||
for(var/obj/structure/closet/secure_closet/brig/C in range(20, src))
|
||||
if(C.id == src.id)
|
||||
targets += C
|
||||
|
||||
|
||||
@@ -181,7 +181,7 @@ var/list/obj/machinery/requests_console/allConsoles = list()
|
||||
if (Console.department == department)
|
||||
Console.newmessagepriority = 0
|
||||
Console.update_icon()
|
||||
Console.luminosity = 1
|
||||
Console.SetLuminosity(1)
|
||||
newmessagepriority = 0
|
||||
update_icon()
|
||||
var/messageComposite = ""
|
||||
@@ -317,7 +317,7 @@ var/list/obj/machinery/requests_console/allConsoles = list()
|
||||
else // Normal priority
|
||||
Console.createmessage(src, "Message from [department]", sending, 1, 1)
|
||||
screen = 6
|
||||
Console.luminosity = 2
|
||||
Console.SetLuminosity(2)
|
||||
|
||||
switch(priority)
|
||||
if(2)
|
||||
@@ -431,7 +431,7 @@ var/list/obj/machinery/requests_console/allConsoles = list()
|
||||
var/obj/item/weapon/paper/slip = new /obj/item/weapon/paper(src.loc)
|
||||
slip.info = "<b>From:</b> [unlinkedsender]<BR>[message]"
|
||||
slip.name = "Message - [unlinkedsender]"
|
||||
src.luminosity = 2
|
||||
SetLuminosity(2)
|
||||
|
||||
/obj/machinery/requests_console/attackby(var/obj/item/weapon/O as obj, var/mob/user as mob, params)
|
||||
if (istype(O, /obj/item/weapon/crowbar))
|
||||
|
||||
@@ -148,7 +148,6 @@
|
||||
return
|
||||
var/area/A = new
|
||||
A.name = str
|
||||
A.tagbase="[A.type]_[md5(str)]" // without this dynamic light system ruin everithing
|
||||
//var/ma
|
||||
//ma = A.master ? "[A.master]" : "(null)"
|
||||
//world << "DEBUG: create_area: <br>A.name=[A.name]<br>A.tag=[A.tag]<br>A.master=[ma]"
|
||||
|
||||
@@ -195,6 +195,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
|
||||
/obj/item/device/pda/ai
|
||||
icon_state = "NONE"
|
||||
ttone = "data"
|
||||
fon = 0
|
||||
mode = 5
|
||||
noreturn = 1
|
||||
detonate = 0
|
||||
@@ -223,6 +224,12 @@ var/global/list/obj/item/device/pda/PDAs = list()
|
||||
|
||||
/obj/item/device/pda/New()
|
||||
..()
|
||||
if(fon)
|
||||
if(!isturf(loc))
|
||||
loc.AddLuminosity(f_lum)
|
||||
SetLuminosity(0)
|
||||
else
|
||||
SetLuminosity(f_lum)
|
||||
PDAs += src
|
||||
if(default_cartridge)
|
||||
cartridge = new default_cartridge(src)
|
||||
|
||||
+1
-14
@@ -126,8 +126,7 @@
|
||||
/turf/proc/ChangeTurf(var/path)
|
||||
if(!path) return
|
||||
if(path == type) return src
|
||||
var/old_lumcount = lighting_lumcount - initial(lighting_lumcount)
|
||||
var/old_opacity = opacity
|
||||
|
||||
SSair.remove_from_active(src)
|
||||
|
||||
var/turf/W = new path(src)
|
||||
@@ -136,18 +135,6 @@
|
||||
W:Assimilate_Air()
|
||||
W.RemoveLattice()
|
||||
|
||||
W.lighting_lumcount += old_lumcount
|
||||
if(old_lumcount != W.lighting_lumcount) //light levels of the turf have changed. We need to shift it to another lighting-subarea
|
||||
W.lighting_changed = 1
|
||||
SSlighting.changed_turfs += W
|
||||
|
||||
if(old_opacity != W.opacity) //opacity has changed. Need to update surrounding lights
|
||||
if(W.lighting_lumcount) //unless we're being illuminated, don't bother (may be buggy, hard to test)
|
||||
W.UpdateAffectingLights()
|
||||
|
||||
for(var/turf/space/S in range(W,1))
|
||||
S.update_starlight()
|
||||
|
||||
W.levelupdate()
|
||||
W.CalculateAdjacentTurfs()
|
||||
return W
|
||||
|
||||
@@ -137,7 +137,7 @@
|
||||
|
||||
/datum/spacevine_mutation/light/on_grow(obj/effect/spacevine/holder)
|
||||
if(prob(10*severity))
|
||||
holder.luminosity = 4
|
||||
holder.SetLuminosity(4)
|
||||
|
||||
/datum/spacevine_mutation/toxicity
|
||||
name = "toxic"
|
||||
|
||||
@@ -0,0 +1,379 @@
|
||||
/*
|
||||
This is /tg/'s 'newer' lighting system. It's basically a combination of Forum_Account's and ShadowDarke's
|
||||
respective lighting libraries heavily modified by Carnwennan for /tg/station with further edits by
|
||||
MrPerson. Credits, where due, to them.
|
||||
|
||||
Originally, like all other lighting libraries on BYOND, we used areas to render different hard-coded light levels.
|
||||
The idea was that this was cheaper than using objects. Well as it turns out, the cost of the system is primarily from the
|
||||
massive loops the system has to run, not from the areas or objects actually doing any work. Thus the newer system uses objects
|
||||
so we can have more lighting states and smooth transitions between them.
|
||||
|
||||
This is a queueing system. Everytime we call a change to opacity or luminosity throwgh SetOpacity() or SetLuminosity(),
|
||||
we are simply updating variables and scheduling certain lights/turfs for an update. Actual updates are handled
|
||||
periodically by the SSlighting subsystem. Specifically, it runs check() on every light datum that ran changed().
|
||||
Then it runs redraw_lighting() on every turf that ran update_lumcount().
|
||||
|
||||
Unlike our older system, there are hardcoded maximum luminosities (different for certain atoms).
|
||||
This is to cap the cost of creating lighting effects.
|
||||
(without this, an atom with luminosity of 20 would have to update 41^2 turfs!) :s
|
||||
|
||||
Each light remembers the effect it casts on each turf. It reduces cost of removing lighting effects by a lot!
|
||||
|
||||
Known Issues/TODO:
|
||||
Shuttles still do not have support for dynamic lighting (I hope to fix this at some point) -probably trivial now
|
||||
No directional lighting support. (prototype looked ugly)
|
||||
Allow lights to be weaker than 'cap' radius
|
||||
Colored lights
|
||||
*/
|
||||
|
||||
#define LIGHTING_CIRCULAR 1 //comment this out to use old square lighting effects.
|
||||
#define LIGHTING_LAYER 15 //Drawing layer for lighting
|
||||
#define LIGHTING_CAP 10 //The lumcount level at which alpha is 0 and we're fully lit.
|
||||
#define LIGHTING_CAP_FRAC (255/LIGHTING_CAP) //A precal'd variable we'll use in turf/redraw_lighting()
|
||||
#define LIGHTING_ICON 'icons/effects/alphacolors.dmi'
|
||||
#define LIGHTING_ICON_STATE "white"
|
||||
#define LIGHTING_TIME 1.2 //Time to do any lighting change. Actual number pulled out of my ass
|
||||
#define LIGHTING_DARKEST_VISIBLE_ALPHA 230 //Anything darker than this is so dark, we'll just consider the whole tile unlit
|
||||
|
||||
/datum/light_source
|
||||
var/atom/owner
|
||||
var/radius = 0
|
||||
var/changed = 1
|
||||
var/list/effect = list()
|
||||
var/__x = 0 //x coordinate at last update
|
||||
var/__y = 0 //y coordinate at last update
|
||||
|
||||
/datum/light_source/New(atom/A)
|
||||
if(!istype(A))
|
||||
CRASH("The first argument to the light object's constructor must be the atom that is the light source. Expected atom, received '[A]' instead.")
|
||||
..()
|
||||
owner = A
|
||||
radius = A.luminosity
|
||||
__x = owner.x
|
||||
__y = owner.y
|
||||
SSlighting.changed_lights |= src
|
||||
|
||||
/datum/light_source/Destroy()
|
||||
if(owner && owner.light == src)
|
||||
remove_effect()
|
||||
owner.light = null
|
||||
owner = null
|
||||
return ..()
|
||||
|
||||
//Check a light to see if its effect needs reprocessing. If it does, remove any old effect and create a new one
|
||||
/datum/light_source/proc/check()
|
||||
if(!owner)
|
||||
remove_effect()
|
||||
return 0
|
||||
|
||||
if(changed)
|
||||
changed = 0
|
||||
remove_effect()
|
||||
return add_effect()
|
||||
|
||||
return 1
|
||||
|
||||
//Tell the lighting subsystem to check() next fire
|
||||
/datum/light_source/proc/changed()
|
||||
if(owner)
|
||||
__x = owner.x
|
||||
__y = owner.y
|
||||
if(!changed)
|
||||
changed = 1
|
||||
SSlighting.changed_lights |= src
|
||||
|
||||
//Remove current effect
|
||||
/datum/light_source/proc/remove_effect().
|
||||
for(var/turf/T in effect)
|
||||
T.update_lumcount(-effect[T])
|
||||
|
||||
if(T.affecting_lights && T.affecting_lights.len)
|
||||
T.affecting_lights -= src
|
||||
|
||||
effect.Cut()
|
||||
|
||||
//Apply a new effect
|
||||
/datum/light_source/proc/add_effect()
|
||||
// only do this if the light is turned on and is on the map
|
||||
if(owner && owner.loc && radius > 0)
|
||||
effect = list()
|
||||
var/turf/To = get_turf(owner)
|
||||
var/range = owner.get_light_range(radius)
|
||||
for(var/atom/movable/AM in To)
|
||||
if(AM == owner)
|
||||
continue
|
||||
if(AM.opacity)
|
||||
range = 0
|
||||
break
|
||||
|
||||
for(var/turf/T in view(range, To))
|
||||
var/delta_lumcount = T.lumen(src)
|
||||
if(delta_lumcount > 0)
|
||||
effect[T] = delta_lumcount
|
||||
T.update_lumcount(delta_lumcount)
|
||||
|
||||
if(!T.affecting_lights)
|
||||
T.affecting_lights = list()
|
||||
T.affecting_lights |= src
|
||||
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
|
||||
//How much light light_source L should apply to src
|
||||
/turf/proc/lumen(datum/light_source/L)
|
||||
var/distance = 0
|
||||
#ifdef LIGHTING_CIRCULAR
|
||||
distance = cheap_hypotenuse(x, y, L.__x, L.__y)
|
||||
#else
|
||||
distance = max(abs(x - L.__x), abs(y - L.__y))
|
||||
#endif
|
||||
return LIGHTING_CAP * (L.radius - distance) / L.radius
|
||||
//LIGHTING_CAP == strength for now
|
||||
|
||||
|
||||
/atom
|
||||
var/datum/light_source/light
|
||||
|
||||
|
||||
//Turfs with opacity when they are constructed will trigger nearby lights to update
|
||||
//Turfs and atoms with luminosity when they are constructed will create a light_source automatically
|
||||
/turf/New()
|
||||
..()
|
||||
if(luminosity)
|
||||
light = new(src)
|
||||
// luminosity = 0
|
||||
|
||||
//Movable atoms with opacity when they are constructed will trigger nearby lights to update
|
||||
//Movable atoms with luminosity when they are constructed will create a light_source automatically
|
||||
/atom/movable/New()
|
||||
..()
|
||||
if(opacity)
|
||||
UpdateAffectingLights()
|
||||
if(luminosity)
|
||||
light = new(src)
|
||||
// luminosity = 0
|
||||
|
||||
//Objects with opacity will trigger nearby lights to update at next SSlighting fire
|
||||
/atom/movable/Destroy()
|
||||
qdel(light)
|
||||
if(opacity)
|
||||
UpdateAffectingLights()
|
||||
return ..()
|
||||
|
||||
//Objects with opacity will trigger nearby lights of the old location to update at next SSlighting fire
|
||||
/atom/movable/Moved(atom/OldLoc, Dir)
|
||||
if(isturf(loc))
|
||||
if(opacity)
|
||||
OldLoc.UpdateAffectingLights()
|
||||
else
|
||||
if(light)
|
||||
light.changed()
|
||||
return ..()
|
||||
|
||||
//Sets our luminosity.
|
||||
//If we have no light it will create one.
|
||||
//If we are setting luminosity to 0 the light will be cleaned up by the controller and garbage collected once all its
|
||||
//queues are complete.
|
||||
//if we have a light already it is merely updated, rather than making a new one.
|
||||
/atom/proc/SetLuminosity(new_luminosity)
|
||||
if(new_luminosity < 0)
|
||||
new_luminosity = 0
|
||||
|
||||
if(!light)
|
||||
if(!new_luminosity)
|
||||
return
|
||||
light = new(src)
|
||||
else
|
||||
if(light.radius == new_luminosity)
|
||||
return
|
||||
light.radius = new_luminosity
|
||||
luminosity = new_luminosity
|
||||
light.changed()
|
||||
|
||||
/atom/proc/AddLuminosity(delta_luminosity)
|
||||
if(light)
|
||||
SetLuminosity(light.radius + delta_luminosity)
|
||||
else
|
||||
SetLuminosity(delta_luminosity)
|
||||
|
||||
/area/SetLuminosity(new_luminosity) //we don't want dynamic lighting for areas
|
||||
luminosity = !!new_luminosity
|
||||
|
||||
|
||||
//change our opacity (defaults to toggle), and then update all lights that affect us.
|
||||
/atom/proc/SetOpacity(new_opacity)
|
||||
if(new_opacity == null)
|
||||
new_opacity = !opacity //default = toggle opacity
|
||||
else if(opacity == new_opacity)
|
||||
return 0 //opacity hasn't changed! don't bother doing anything
|
||||
opacity = new_opacity //update opacity, the below procs now call light updates.
|
||||
UpdateAffectingLights()
|
||||
return 1
|
||||
|
||||
/atom/movable/light
|
||||
icon = LIGHTING_ICON
|
||||
icon_state = LIGHTING_ICON_STATE
|
||||
layer = LIGHTING_LAYER
|
||||
mouse_opacity = 0
|
||||
blend_mode = BLEND_OVERLAY
|
||||
invisibility = INVISIBILITY_LIGHTING
|
||||
color = "#000"
|
||||
luminosity = 0
|
||||
infra_luminosity = 1
|
||||
anchored = 1
|
||||
|
||||
/atom/movable/light/Destroy()
|
||||
return 1
|
||||
|
||||
/atom/movable/light/Move()
|
||||
return 0
|
||||
|
||||
/turf
|
||||
var/lighting_lumcount = 0
|
||||
var/lighting_changed = 0
|
||||
var/atom/movable/light/lighting_object //Will be null for space turfs and anything in a static lighting area
|
||||
var/list/affecting_lights //not initialised until used (even empty lists reserve a fair bit of memory)
|
||||
|
||||
/turf/ChangeTurf(var/path)
|
||||
if(!path || path == type) //Sucks this is here but it would cause problems otherwise.
|
||||
return ..()
|
||||
|
||||
if(light)
|
||||
qdel(light)
|
||||
|
||||
var/old_lumcount = lighting_lumcount - initial(lighting_lumcount)
|
||||
|
||||
var/list/our_lights //reset affecting_lights if needed
|
||||
if(opacity != initial(path:opacity) && old_lumcount)
|
||||
UpdateAffectingLights()
|
||||
|
||||
if(affecting_lights)
|
||||
our_lights = affecting_lights.Copy()
|
||||
|
||||
. = ..() //At this point the turf has changed
|
||||
|
||||
affecting_lights = our_lights
|
||||
|
||||
lighting_changed = 1 //Don't add ourself to SSlighting.changed_turfs
|
||||
update_lumcount(old_lumcount)
|
||||
lighting_object = locate() in src
|
||||
init_lighting()
|
||||
|
||||
for(var/turf/space/S in orange(src,1))
|
||||
S.update_starlight()
|
||||
|
||||
/turf/proc/update_lumcount(amount)
|
||||
lighting_lumcount += amount
|
||||
if(!lighting_changed)
|
||||
SSlighting.changed_turfs += src
|
||||
lighting_changed = 1
|
||||
|
||||
/turf/space/update_lumcount(amount) //Keep track in case the turf becomes a floor at some point, but don't process.
|
||||
lighting_lumcount += amount
|
||||
|
||||
/turf/proc/init_lighting()
|
||||
var/area/A = loc
|
||||
if(!A.lighting_use_dynamic || istype(src, /turf/space))
|
||||
lighting_changed = 0
|
||||
if(lighting_object)
|
||||
lighting_object.alpha = 0
|
||||
lighting_object = null
|
||||
else
|
||||
if(!lighting_object)
|
||||
lighting_object = new (src)
|
||||
redraw_lighting(1)
|
||||
|
||||
/turf/space/init_lighting()
|
||||
. = ..()
|
||||
if(config.starlight)
|
||||
update_starlight()
|
||||
|
||||
/turf/proc/redraw_lighting(var/instantly = 0)
|
||||
if(lighting_object)
|
||||
var/newalpha
|
||||
if(lighting_lumcount <= 0)
|
||||
newalpha = 255
|
||||
else
|
||||
lighting_object.luminosity = 1
|
||||
if(lighting_lumcount < LIGHTING_CAP)
|
||||
var/num = Clamp(lighting_lumcount * LIGHTING_CAP_FRAC, 0, 255)
|
||||
newalpha = 255-num
|
||||
else //if(lighting_lumcount >= LIGHTING_CAP)
|
||||
newalpha = 0
|
||||
|
||||
if(lighting_object.alpha != newalpha)
|
||||
var/change_time = LIGHTING_TIME
|
||||
if(instantly)
|
||||
change_time = 0
|
||||
animate(lighting_object, alpha = newalpha, time = change_time)
|
||||
if(newalpha >= LIGHTING_DARKEST_VISIBLE_ALPHA) //Doesn't actually make it darker or anything, just tells byond you can't see the tile
|
||||
animate(luminosity = 0, time = 0)
|
||||
|
||||
lighting_changed = 0
|
||||
|
||||
/area
|
||||
var/lighting_use_dynamic = 1 //Turn this flag off to make the area fullbright
|
||||
|
||||
/area/New()
|
||||
. = ..()
|
||||
if(!lighting_use_dynamic)
|
||||
luminosity = 1
|
||||
|
||||
/area/proc/SetDynamicLighting()
|
||||
lighting_use_dynamic = 1
|
||||
luminosity = 0
|
||||
for(var/turf/T in src.contents)
|
||||
T.init_lighting()
|
||||
T.update_lumcount(0)
|
||||
|
||||
#undef LIGHTING_LAYER
|
||||
#undef LIGHTING_CIRCULAR
|
||||
#undef LIGHTING_ICON
|
||||
#undef LIGHTING_ICON_STATE
|
||||
#undef LIGHTING_TIME
|
||||
#undef LIGHTING_CAP
|
||||
#undef LIGHTING_CAP_FRAC
|
||||
#undef LIGHTING_DARKEST_VISIBLE_ALPHA
|
||||
|
||||
|
||||
//set the changed status of all lights which could have possibly lit this atom.
|
||||
//We don't need to worry about lights which lit us but moved away, since they will have change status set already
|
||||
//This proc can cause lots of lights to be updated. :(
|
||||
/atom/proc/UpdateAffectingLights()
|
||||
|
||||
/atom/movable/UpdateAffectingLights()
|
||||
if(isturf(loc))
|
||||
loc.UpdateAffectingLights()
|
||||
|
||||
/turf/UpdateAffectingLights()
|
||||
if(affecting_lights)
|
||||
for(var/datum/light_source/thing in affecting_lights)
|
||||
thing.changed() //force it to update at next process()
|
||||
|
||||
|
||||
#define LIGHTING_MAX_LUMINOSITY_STATIC 8 //Maximum luminosity to reduce lag.
|
||||
#define LIGHTING_MAX_LUMINOSITY_MOBILE 5 //Moving objects have a lower max luminosity since these update more often. (lag reduction)
|
||||
#define LIGHTING_MAX_LUMINOSITY_MOB 5
|
||||
#define LIGHTING_MAX_LUMINOSITY_TURF 8 //turfs are static too, why was this 1?!
|
||||
|
||||
//caps luminosity effects max-range based on what type the light's owner is.
|
||||
/atom/proc/get_light_range(radius)
|
||||
return min(radius, LIGHTING_MAX_LUMINOSITY_STATIC)
|
||||
|
||||
/atom/movable/get_light_range(radius)
|
||||
return min(radius, LIGHTING_MAX_LUMINOSITY_MOBILE)
|
||||
|
||||
/mob/get_light_range(radius)
|
||||
return min(radius, LIGHTING_MAX_LUMINOSITY_MOB)
|
||||
|
||||
/obj/machinery/light/get_light_range(radius)
|
||||
return min(radius, LIGHTING_MAX_LUMINOSITY_STATIC)
|
||||
|
||||
/turf/get_light_range(radius)
|
||||
return min(radius, LIGHTING_MAX_LUMINOSITY_TURF)
|
||||
|
||||
#undef LIGHTING_MAX_LUMINOSITY_STATIC
|
||||
#undef LIGHTING_MAX_LUMINOSITY_MOBILE
|
||||
#undef LIGHTING_MAX_LUMINOSITY_MOB
|
||||
#undef LIGHTING_MAX_LUMINOSITY_TURF
|
||||
@@ -277,7 +277,7 @@
|
||||
|
||||
update_icon()
|
||||
if(on)
|
||||
if(luminosity != brightness)
|
||||
if(!light || light.radius != brightness)
|
||||
switchcount++
|
||||
if(rigged)
|
||||
if(status == LIGHT_OK && trigger)
|
||||
@@ -295,11 +295,11 @@
|
||||
use_power = 1
|
||||
SetLuminosity(0)
|
||||
|
||||
active_power_usage = (luminosity * 10)
|
||||
active_power_usage = (brightness * 10)
|
||||
if(on != on_gs)
|
||||
on_gs = on
|
||||
if(on)
|
||||
static_power_used = luminosity * 20 //20W per unit luminosity
|
||||
static_power_used = brightness * 20 //20W per unit luminosity
|
||||
addStaticPower(static_power_used, STATIC_LIGHT)
|
||||
else
|
||||
removeStaticPower(static_power_used, STATIC_LIGHT)
|
||||
|
||||
@@ -321,6 +321,8 @@
|
||||
for(var/obj/O in T0)
|
||||
if(O.invisibility >= 101)
|
||||
continue
|
||||
if(O == T0.lighting_object)
|
||||
continue
|
||||
O.loc = T1
|
||||
|
||||
//close open doors
|
||||
@@ -350,13 +352,13 @@
|
||||
|
||||
//air system updates
|
||||
for(var/turf/T1 in L1)
|
||||
T1.shift_to_subarea()
|
||||
T1.redraw_lighting()
|
||||
SSair.remove_from_active(T1)
|
||||
T1.CalculateAdjacentTurfs()
|
||||
SSair.add_to_active(T1,1)
|
||||
|
||||
for(var/turf/T0 in L0)
|
||||
T0.shift_to_subarea()
|
||||
T0.redraw_lighting()
|
||||
SSair.remove_from_active(T0)
|
||||
T0.CalculateAdjacentTurfs()
|
||||
SSair.add_to_active(T0,1)
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
author: MrPerson
|
||||
changes: []
|
||||
changes:
|
||||
- experiment: "New lighting system! Lighting now smoothly transfers from one lighting level to the next."
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 395 B After Width: | Height: | Size: 650 B |
+1
-1
@@ -118,7 +118,6 @@
|
||||
#include "code\ATMOSPHERICS\pipes\simple.dm"
|
||||
#include "code\ATMOSPHERICS\pipes\tank.dm"
|
||||
#include "code\ATMOSPHERICS\pipes\vent.dm"
|
||||
#include "code\controllers\_DynamicAreaLighting_TG.dm"
|
||||
#include "code\controllers\configuration.dm"
|
||||
#include "code\controllers\failsafe.dm"
|
||||
#include "code\controllers\master_controller.dm"
|
||||
@@ -1014,6 +1013,7 @@
|
||||
#include "code\modules\library\lib_items.dm"
|
||||
#include "code\modules\library\lib_machines.dm"
|
||||
#include "code\modules\library\lib_readme.dm"
|
||||
#include "code\modules\lighting\lighting_system.dm"
|
||||
#include "code\modules\mining\equipment_locker.dm"
|
||||
#include "code\modules\mining\machine_input_output_plates.dm"
|
||||
#include "code\modules\mining\machine_processing.dm"
|
||||
|
||||
Reference in New Issue
Block a user