mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2025-12-21 07:32:02 +00:00
448 lines
13 KiB
Plaintext
448 lines
13 KiB
Plaintext
//So much copypasta in this file, supposedly in the name of performance. If you change anything make sure to consider other places where the code may have been copied.
|
|
|
|
/datum/light_source
|
|
var/atom/top_atom
|
|
var/atom/source_atom
|
|
|
|
var/turf/source_turf
|
|
var/light_power
|
|
var/light_range
|
|
var/light_color // string, decomposed by parse_light_color()
|
|
|
|
var/lum_r
|
|
var/lum_g
|
|
var/lum_b
|
|
|
|
//hold onto the actual applied lum values so we can undo them when the lighting changes
|
|
var/tmp/applied_lum_r
|
|
var/tmp/applied_lum_g
|
|
var/tmp/applied_lum_b
|
|
|
|
var/list/effect_str
|
|
var/list/effect_turf
|
|
|
|
var/applied
|
|
|
|
var/vis_update //Whetever we should smartly recalculate visibility. and then only update tiles that became (in) visible to us
|
|
var/needs_update
|
|
var/destroyed
|
|
var/force_update
|
|
|
|
/datum/light_source/New(atom/owner, atom/top)
|
|
source_atom = owner
|
|
if(!source_atom.light_sources) source_atom.light_sources = list()
|
|
source_atom.light_sources += src
|
|
top_atom = top
|
|
if(top_atom != source_atom)
|
|
if(!top.light_sources) top.light_sources = list()
|
|
top_atom.light_sources += src
|
|
|
|
source_turf = top_atom
|
|
light_power = source_atom.light_power
|
|
light_range = source_atom.light_range
|
|
light_color = source_atom.light_color
|
|
|
|
parse_light_color()
|
|
|
|
effect_str = list()
|
|
effect_turf = list()
|
|
|
|
update()
|
|
|
|
return ..()
|
|
|
|
|
|
//This proc is called manually on a light if you want it to be more responsive.
|
|
//It forces an update right now instead of waiting for the controller to get around to it, which can be up to 2.1 seconds
|
|
//Update is forced on this light source, and all tiles it effects.
|
|
//This can be very expensive and inefficient, use sparingly
|
|
/datum/light_source/proc/instant_update()
|
|
remove_lum()
|
|
if(!destroyed)
|
|
apply_lum()
|
|
|
|
else if(vis_update) //We smartly update only tiles that became (in) visible to use.
|
|
smart_vis_update()
|
|
|
|
vis_update = 0
|
|
force_update = 0
|
|
needs_update = 0
|
|
|
|
for (var/turf/T in effect_turf)
|
|
if (T.lighting_overlay)
|
|
T.lighting_overlay.update_overlay()
|
|
T.lighting_overlay.needs_update = 0
|
|
|
|
|
|
/datum/light_source/proc/destroy()
|
|
destroyed = 1
|
|
force_update()
|
|
if(source_atom && source_atom.light_sources) source_atom.light_sources -= src
|
|
if(top_atom && top_atom.light_sources) top_atom.light_sources -= src
|
|
|
|
/datum/light_source/proc/update(atom/new_top_atom)
|
|
if(new_top_atom && new_top_atom != top_atom)
|
|
if(top_atom != source_atom) top_atom.light_sources -= src
|
|
top_atom = new_top_atom
|
|
if(top_atom != source_atom)
|
|
if(!top_atom.light_sources) top_atom.light_sources = list()
|
|
top_atom.light_sources += src
|
|
|
|
if(!needs_update) //Incase we're already updating either way.
|
|
lighting_update_lights += src
|
|
needs_update = 1
|
|
|
|
/datum/light_source/proc/force_update()
|
|
force_update = 1
|
|
if(!needs_update) //Incase we're already updating either way.
|
|
needs_update = 1
|
|
lighting_update_lights += src
|
|
|
|
/datum/light_source/proc/vis_update()
|
|
if(!needs_update)
|
|
needs_update = 1
|
|
lighting_update_lights += src
|
|
|
|
vis_update = 1
|
|
|
|
/datum/light_source/proc/check()
|
|
if(!source_atom || !light_range || !light_power)
|
|
destroy()
|
|
return 1
|
|
|
|
if(!top_atom)
|
|
top_atom = source_atom
|
|
. = 1
|
|
|
|
if(istype(top_atom, /turf))
|
|
if(source_turf != top_atom)
|
|
source_turf = top_atom
|
|
. = 1
|
|
else if(top_atom.loc != source_turf)
|
|
source_turf = top_atom.loc
|
|
. = 1
|
|
|
|
if(source_atom.light_power != light_power)
|
|
light_power = source_atom.light_power
|
|
. = 1
|
|
|
|
if(source_atom.light_range != light_range)
|
|
light_range = source_atom.light_range
|
|
. = 1
|
|
|
|
if(light_range && light_power && !applied)
|
|
. = 1
|
|
|
|
if(source_atom.light_color != light_color)
|
|
light_color = source_atom.light_color
|
|
parse_light_color()
|
|
. = 1
|
|
|
|
/datum/light_source/proc/parse_light_color()
|
|
if(light_color)
|
|
lum_r = GetRedPart(light_color) / 255
|
|
lum_g = GetGreenPart(light_color) / 255
|
|
lum_b = GetBluePart(light_color) / 255
|
|
else
|
|
lum_r = 1
|
|
lum_g = 1
|
|
lum_b = 1
|
|
|
|
#if LIGHTING_FALLOFF == 1 //circular
|
|
#define LUM_DISTANCE(swapvar, O, T) swapvar = (O.x - T.x)**2 + (O.y - T.y)**2 + LIGHTING_HEIGHT
|
|
#if LIGHTING_LAMBERTIAN == 1
|
|
#define LUM_ATTENUATION(swapvar) swapvar = CLAMP01((1 - CLAMP01(sqrt(swapvar) / max(1,light_range))) * (1 / sqrt(swapvar + 1)))
|
|
#else
|
|
#define LUM_ATTENUATION(swapvar) swapvar = 1 - CLAMP01(sqrt(swapvar) / max(1,light_range))
|
|
#endif
|
|
#elif LIGHTING_FALLOFF == 2 //square
|
|
#define LUM_DISTANCE(swapvar, O, T) swapvar = abs(O.x - T.x) + abs(O.y - T.y) + LIGHTING_HEIGHT
|
|
#if LIGHTING_LAMBERTIAN == 1
|
|
#define LUM_ATTENUATION(swapvar) swapvar = CLAMP01((1 - CLAMP01(swapvar / max(1,light_range))) * (1 / sqrt(swapvar**2 + 1)))
|
|
#else
|
|
#define LUM_ATTENUATION(swapvar) swapvar = CLAMP01(swapvar / max(1,light_range))
|
|
#endif
|
|
#endif
|
|
|
|
#define LUM_FALLOFF(swapvar, O, T) \
|
|
LUM_DISTANCE(swapvar, O, T); \
|
|
LUM_ATTENUATION(swapvar);
|
|
|
|
/datum/light_source/proc/apply_lum()
|
|
applied = 1
|
|
|
|
if (istype(source_atom.loc, /mob))//If the light is carried by a mob
|
|
var/mob/M = source_atom.loc
|
|
if (source_atom.offset_light)//And its an offset light
|
|
apply_lum_offset(M)//Then we call the special offset variant and terminate there.
|
|
return//This is split off to minimise overhead added to the majority of non-offset lights
|
|
|
|
|
|
//Keep track of the last applied lum values so that the lighting can be reversed
|
|
applied_lum_r = lum_r
|
|
applied_lum_g = lum_g
|
|
applied_lum_b = lum_b
|
|
|
|
if(istype(source_turf))
|
|
FOR_DVIEW(var/turf/T, light_range, source_turf, INVISIBILITY_LIGHTING)
|
|
if(T.lighting_overlay)
|
|
var/strength
|
|
LUM_FALLOFF(strength, T, source_turf)
|
|
strength *= light_power
|
|
|
|
if(!strength) //Don't add turfs that aren't affected to the affected turfs.
|
|
continue
|
|
|
|
strength = round(strength, LIGHTING_ROUND_VALUE) //Screw sinking points.
|
|
|
|
effect_str += strength
|
|
|
|
T.lighting_overlay.update_lumcount(
|
|
applied_lum_r * strength,
|
|
applied_lum_g * strength,
|
|
applied_lum_b * strength
|
|
)
|
|
|
|
else
|
|
effect_str += 0
|
|
|
|
if(!T.affecting_lights)
|
|
T.affecting_lights = list()
|
|
|
|
T.affecting_lights += src
|
|
effect_turf += T
|
|
END_FOR_DVIEW
|
|
|
|
|
|
//Duplicated code for speed. This is a variant of apply_lum for directional/offset lights carried by a mob
|
|
/datum/light_source/proc/apply_lum_offset(var/mob/M)//M is passed in for speed since we already fetched it
|
|
var/turf/lightfrom = get_step(M, M.dir)//Light source is offset infront of the user, simulates a directional light
|
|
var/list/dview = list()
|
|
|
|
//We run a special DVIEW call to fetch the list of tiles viewable from the MOB's position
|
|
//This is cross referenced with the below DVIEW loop which runs through tiles viewable from the lightfrom position
|
|
//This is used to prevent offset lights shining through walls
|
|
DVIEW(dview, light_range, source_turf, INVISIBILITY_LIGHTING)
|
|
|
|
|
|
applied = 1
|
|
|
|
//Keep track of the last applied lum values so that the lighting can be reversed
|
|
applied_lum_r = lum_r
|
|
applied_lum_g = lum_g
|
|
applied_lum_b = lum_b
|
|
|
|
if(istype(lightfrom))
|
|
FOR_DVIEW(var/turf/T, light_range, lightfrom, INVISIBILITY_LIGHTING)//List of turfs visible from the light centre
|
|
if(T.lighting_overlay)
|
|
|
|
if (!(T in dview))//If the turf is not also visible from the mob, then it's obscured and invalid
|
|
continue//Don't light this tile. This prevents offset lights from shining through walls
|
|
|
|
var/strength
|
|
LUM_FALLOFF(strength, T, lightfrom)
|
|
|
|
if (M && T == get_turf(M))//The light applied to the tile the holder is on is reduced, simulates directional light
|
|
strength *= light_power * (source_atom.owner_light_mult)
|
|
else
|
|
strength *= light_power
|
|
|
|
if(!strength) //Don't add turfs that aren't affected to the affected turfs.
|
|
continue
|
|
|
|
strength = round(strength, LIGHTING_ROUND_VALUE) //Screw sinking points.
|
|
|
|
effect_str += strength
|
|
|
|
T.lighting_overlay.update_lumcount(
|
|
applied_lum_r * strength,
|
|
applied_lum_g * strength,
|
|
applied_lum_b * strength
|
|
)
|
|
|
|
else
|
|
effect_str += 0
|
|
|
|
if(!T.affecting_lights)
|
|
T.affecting_lights = list()
|
|
|
|
T.affecting_lights += src
|
|
effect_turf += T
|
|
END_FOR_DVIEW
|
|
|
|
|
|
/datum/light_source/proc/remove_lum()
|
|
applied = 0
|
|
var/i = 1
|
|
for(var/turf/T in effect_turf)
|
|
if(T.affecting_lights)
|
|
T.affecting_lights -= src
|
|
|
|
if(T.lighting_overlay)
|
|
var/str = effect_str[i]
|
|
T.lighting_overlay.update_lumcount(
|
|
-str * applied_lum_r,
|
|
-str * applied_lum_g,
|
|
-str * applied_lum_b
|
|
)
|
|
|
|
i++
|
|
|
|
effect_str.Cut()
|
|
effect_turf.Cut()
|
|
|
|
//Smartly updates the lighting, only removes lum from and adds lum to turfs that actually got changed.
|
|
//This is for lights that need to reconsider due to nearby opacity changes.
|
|
//Stupid dumb copy pasta because BYOND and speed.
|
|
/datum/light_source/proc/smart_vis_update()
|
|
var/list/view[0]
|
|
FOR_DVIEW(var/turf/T, light_range, source_turf, INVISIBILITY_LIGHTING)
|
|
view += T //Filter out turfs.
|
|
END_FOR_DVIEW
|
|
//This is the part where we calculate new turfs (if any)
|
|
var/list/new_turfs = view - effect_turf //This will result with all the tiles that are added.
|
|
for(var/turf/T in new_turfs)
|
|
if(T.lighting_overlay)
|
|
LUM_FALLOFF(., T, source_turf)
|
|
. *= light_power
|
|
|
|
if(!.) //Don't add turfs that aren't affected to the affected turfs.
|
|
continue
|
|
|
|
. = round(., LIGHTING_ROUND_VALUE)
|
|
|
|
effect_str += .
|
|
|
|
T.lighting_overlay.update_lumcount(
|
|
applied_lum_r * .,
|
|
applied_lum_g * .,
|
|
applied_lum_b * .
|
|
)
|
|
|
|
else
|
|
effect_str += 0
|
|
|
|
if(!T.affecting_lights)
|
|
T.affecting_lights = list()
|
|
|
|
T.affecting_lights += src
|
|
effect_turf += T
|
|
|
|
var/list/old_turfs = effect_turf - view
|
|
for(var/turf/T in old_turfs)
|
|
//Insert not-so-huge copy paste from remove_lum().
|
|
var/idx = effect_turf.Find(T) //Get the index, luckily Find() is cheap in small lists like this. (with small I mean under a couple thousand len)
|
|
if(T.affecting_lights)
|
|
T.affecting_lights -= src
|
|
|
|
if(T.lighting_overlay)
|
|
var/str = effect_str[idx]
|
|
T.lighting_overlay.update_lumcount(-str * applied_lum_r, -str * applied_lum_g, -str * applied_lum_b)
|
|
|
|
effect_turf.Cut(idx, idx + 1)
|
|
effect_str.Cut(idx, idx + 1)
|
|
|
|
//Whoop yet not another copy pasta because speed ~~~~BYOND.
|
|
//Calculates and applies lighting for a single turf. This is intended for when a turf switches to
|
|
//using dynamic lighting when it was not doing so previously (when constructing a floor on space, for example).
|
|
//Assumes the turf is visible and such.
|
|
//For the love of god don't call this proc when it's not needed! Lighting artifacts WILL happen!
|
|
/datum/light_source/proc/calc_turf(var/turf/T)
|
|
var/idx = effect_turf.Find(T)
|
|
if(!idx)
|
|
return //WHY.
|
|
|
|
if(T.lighting_overlay)
|
|
#if LIGHTING_FALLOFF == 1 // circular
|
|
. = (T.lighting_overlay.x - source_turf.x)**2 + (T.lighting_overlay.y - source_turf.y)**2 + LIGHTING_HEIGHT
|
|
#if LIGHTING_LAMBERTIAN == 1
|
|
. = CLAMP01((1 - CLAMP01(sqrt(.) / light_range)) * (1 / (sqrt(. + 1))))
|
|
#else
|
|
. = 1 - CLAMP01(sqrt(.) / light_range)
|
|
#endif
|
|
|
|
#elif LIGHTING_FALLOFF == 2 // square
|
|
. = abs(T.lighting_overlay.x - source_turf.x) + abs(T.lighting_overlay.y - source_turf.y) + LIGHTING_HEIGHT
|
|
#if LIGHTING_LAMBERTIAN == 1
|
|
. = CLAMP01((1 - CLAMP01(. / light_range)) * (1 / (sqrt(.)**2 + )))
|
|
#else
|
|
. = 1 - CLAMP01(. / light_range)
|
|
#endif
|
|
#endif
|
|
. *= light_power
|
|
|
|
. = round(., LIGHTING_ROUND_VALUE)
|
|
|
|
effect_str[idx] = .
|
|
|
|
//Since the applied_lum values are what are (later) removed by remove_lum.
|
|
//Anything we apply to the lighting overlays HAS to match what remove_lum uses.
|
|
T.lighting_overlay.update_lumcount(
|
|
applied_lum_r * .,
|
|
applied_lum_g * .,
|
|
applied_lum_b * .
|
|
)
|
|
|
|
|
|
|
|
//This function returns the illumination it would/did apply to the specified turf.
|
|
//It is useful for gathering information on a particular source's contribution to a turf's light
|
|
/datum/light_source/proc/get_lum(var/turf/T)
|
|
var/turf/lightfrom = source_turf
|
|
var/mob/M = null
|
|
var/list/dview = list()
|
|
var/list/castview = list()
|
|
|
|
|
|
if (istype(source_atom.loc, /mob))
|
|
M = source_atom.loc
|
|
if (source_atom.offset_light)
|
|
DVIEW(dview, light_range, source_turf, INVISIBILITY_LIGHTING)
|
|
lightfrom = get_step(M, M.dir)//Light source is offset infront of the user, simulates a directional light
|
|
|
|
applied = 1
|
|
|
|
//Keep track of the last applied lum values so that the lighting can be reversed
|
|
applied_lum_r = lum_r
|
|
applied_lum_g = lum_g
|
|
applied_lum_b = lum_b
|
|
|
|
if(istype(lightfrom))
|
|
|
|
//Castview is a list of tiles seen from the light centre.
|
|
//If the desired turf isn't in it, then we couldn't contribute anything to that tile, return a zero list
|
|
DVIEW(castview, light_range, lightfrom, INVISIBILITY_LIGHTING)
|
|
if (!(T in castview))
|
|
return list(0,0,0)
|
|
|
|
if(T.lighting_overlay)
|
|
//Check for offset lights shining through walls
|
|
if (source_atom.offset_light)
|
|
if (!(T in dview))
|
|
return list(0,0,0)
|
|
|
|
var/strength
|
|
LUM_FALLOFF(strength, T, lightfrom)
|
|
|
|
if (M && T == get_turf(M))//The light applied to the tile the holder is on is reduced, simulates directional light
|
|
strength *= light_power * (source_atom.owner_light_mult)
|
|
else
|
|
strength *= light_power
|
|
|
|
if(!strength) //If no strength, then we contributed nothing.
|
|
return list(0,0,0)
|
|
|
|
strength = round(strength, LIGHTING_ROUND_VALUE)
|
|
|
|
//If we're here, then we've confirmed this light does affect the passed tile, and how much.
|
|
//Return the values we've applied to it.
|
|
return list(
|
|
applied_lum_r * strength,
|
|
applied_lum_g * strength,
|
|
applied_lum_b * strength)
|
|
|
|
|
|
#undef LUM_FALLOFF
|
|
#undef LUM_DISTANCE
|
|
#undef LUM_ATTENUATION
|