-
Notifications
You must be signed in to change notification settings - Fork 224
/
Copy pathlighting_turf.dm
228 lines (177 loc) · 7.26 KB
/
lighting_turf.dm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
/turf
var/dynamic_lighting = TRUE
/// If non-null, a hex RGB light color that should be applied to this turf.
var/ambient_light
/// The power of the above is multiplied by this. Setting too high may drown out normal lights on the same turf.
var/ambient_light_multiplier = 0.3
luminosity = 1
var/tmp/lighting_corners_initialised = FALSE
/// List of light sources affecting this turf.
var/tmp/list/datum/light_source/affecting_lights
/// Our lighting overlay, used to apply multiplicative lighting to the tile and its contents.
var/tmp/atom/movable/lighting_overlay/lighting_overlay
var/tmp/list/datum/lighting_corner/corners
/// Not to be confused with opacity, this will be TRUE if there's any opaque atom on the tile.
var/tmp/has_opaque_atom = FALSE
/// If this is TRUE, an above turf's ambient light is affecting this turf.
var/tmp/ambient_has_indirect = FALSE
// Record-keeping, do not touch -- that means you, admins.
var/tmp/ambient_light_old
var/tmp/ambient_light_old_r = 0
var/tmp/ambient_light_old_g = 0
var/tmp/ambient_light_old_b = 0
/turf/proc/set_ambient_light(color, multiplier)
if (color == ambient_light && multiplier == ambient_light_multiplier)
return
ambient_light = color || ambient_light
ambient_light_multiplier = multiplier || ambient_light_multiplier
if (!ambient_light_multiplier)
ambient_light_multiplier = initial(ambient_light_multiplier)
update_ambient_light()
/turf/proc/clear_ambient_light()
if (ambient_light == null)
return
ambient_light = null
update_ambient_light()
/turf/proc/update_ambient_light(no_corner_update = FALSE)
// These are deltas.
var/ambient_r = 0
var/ambient_g = 0
var/ambient_b = 0
if (ambient_light)
ambient_r = round(((HEX_RED(ambient_light) / 255) * ambient_light_multiplier)/4 - ambient_light_old_r, LIGHTING_ROUND_VALUE)
ambient_g = round(((HEX_GREEN(ambient_light) / 255) * ambient_light_multiplier)/4 - ambient_light_old_g, LIGHTING_ROUND_VALUE)
ambient_b = round(((HEX_BLUE(ambient_light) / 255) * ambient_light_multiplier)/4 - ambient_light_old_b, LIGHTING_ROUND_VALUE)
else
ambient_r = -ambient_light_old_r
ambient_g = -ambient_light_old_g
ambient_b = -ambient_light_old_b
ambient_light_old_r += ambient_r
ambient_light_old_g += ambient_g
ambient_light_old_b += ambient_b
if (abs(ambient_r + ambient_g + ambient_b) == 0)
return
// Unlit turfs will have corners if they have a lit neighbor -- don't generate corners for them, but do update them if they're there.
if (!corners)
var/force_build_corners = FALSE
for (var/turf/T as anything in RANGE_TURFS(src, 1))
if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(T))
force_build_corners = TRUE
break
if (force_build_corners || TURF_IS_DYNAMICALLY_LIT_UNSAFE(src))
generate_missing_corners()
else
return
// This list can contain nulls on things like space turfs -- they only have their neighbors' corners.
for (var/datum/lighting_corner/C in corners)
C.update_ambient_lumcount(ambient_r, ambient_g, ambient_b, no_corner_update)
if (ambient_light_old == null && ambient_light != ambient_light_old)
SSlighting.total_ambient_turfs += 1
else if (ambient_light_old != null && ambient_light == null)
SSlighting.total_ambient_turfs -= 1
ambient_light_old = ambient_light
/// Causes any affecting light sources to be queued for a visibility update, for example a door got opened.
/turf/proc/reconsider_lights()
var/datum/light_source/L
for (var/thing in affecting_lights)
L = thing
L.vis_update()
/// Forces a lighting update. Reconsider lights is preferred when possible.
/turf/proc/force_update_lights()
var/datum/light_source/L
for (var/thing in affecting_lights)
L = thing
L.force_update()
/turf/proc/lighting_clear_overlay()
if (lighting_overlay)
if (lighting_overlay.loc != src)
PRINT_STACK_TRACE("Lighting overlay variable on turf [log_info_line(src)] is insane, lighting overlay actually located on [log_info_line(lighting_overlay.loc)]!")
qdel(lighting_overlay, TRUE)
lighting_overlay = null
for (var/datum/lighting_corner/C in corners)
C.update_active()
// Builds a lighting overlay for us, but only if our area is dynamic.
/turf/proc/lighting_build_overlay(now = FALSE)
if (lighting_overlay)
CRASH("Attempted to create lighting_overlay on tile that already had one.")
if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(src))
if (!lighting_corners_initialised || !corners)
generate_missing_corners()
new /atom/movable/lighting_overlay(src, now)
for (var/datum/lighting_corner/C in corners)
if (!C.active) // We would activate the corner, calculate the lighting for it.
for (var/L in C.affecting)
var/datum/light_source/S = L
S.recalc_corner(C, TRUE)
C.active = TRUE
/// Returns the average color of this tile. Roughly corresponds to the color of a single old-style lighting overlay.
/turf/proc/get_avg_color()
if (!lighting_overlay)
return null
var/lum_r
var/lum_g
var/lum_b
for (var/datum/lighting_corner/L in corners)
lum_r += L.apparent_r
lum_g += L.apparent_g
lum_b += L.apparent_b
lum_r = CLAMP01(lum_r / length(corners)) * 255
lum_g = CLAMP01(lum_g / length(corners)) * 255
lum_b = CLAMP01(lum_b / length(corners)) * 255
return rgb(lum_r, lum_g, lum_b)
#define SCALE(targ,min,max) (targ - min) / (max - min)
/// Returns a lumcount (average intensity of color channels) scaled between minlum and maxlum.
/turf/proc/get_lumcount(minlum = 0, maxlum = 1)
if (!lighting_overlay)
return 0.5
var/totallums = 0
for (var/datum/lighting_corner/L in corners)
totallums += L.apparent_r + L.apparent_b + L.apparent_g
totallums /= 12 // 4 corners, each with 3 channels, get the average.
totallums = SCALE(totallums, minlum, maxlum)
return CLAMP01(totallums)
#undef SCALE
/// Can't think of a good name, this proc will recalculate the has_opaque_atom variable.
/turf/proc/recalc_atom_opacity()
#ifdef AO_USE_LIGHTING_OPACITY
var/old = has_opaque_atom
#endif
has_opaque_atom = FALSE
if (opacity)
has_opaque_atom = TRUE
else
for (var/thing in src) // Loop through every movable atom on our tile
var/atom/movable/A = thing
if (A.opacity)
has_opaque_atom = TRUE
break // No need to continue if we find something opaque.
#ifdef AO_USE_LIGHTING_OPACITY
if (old != has_opaque_atom)
regenerate_ao()
#endif
/turf/Exited(atom/movable/Obj, atom/newloc)
. = ..()
if (!Obj)
return
if (Obj.opacity)
recalc_atom_opacity() // Make sure to do this before reconsider_lights(), incase we're on instant updates.
reconsider_lights()
// This block isn't needed now, but it's here if supporting area dyn lighting changes is needed later.
// /turf/change_area(area/old_area, area/new_area)
// if (new_area.dynamic_lighting != old_area.dynamic_lighting)
// if (TURF_IS_DYNAMICALLY_LIT_UNSAFE(src))
// lighting_build_overlay()
// else
// lighting_clear_overlay()
// This is inlined in lighting_source.dm.
// Update it too if you change this.
/turf/proc/generate_missing_corners()
if (!TURF_IS_DYNAMICALLY_LIT_UNSAFE(src) && !light_source_solo && !light_source_multi && !(z_flags & ZM_ALLOW_LIGHTING) && !ambient_light && !ambient_has_indirect)
return
lighting_corners_initialised = TRUE
if (!corners)
corners = new(4)
for (var/i = 1 to 4)
if (corners[i]) // Already have a corner on this direction.
continue
corners[i] = new/datum/lighting_corner(src, LIGHTING_CORNER_DIAGONAL[i], i)