/
weather.dm
265 lines (235 loc) · 9 KB
/
weather.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
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
/**
* Causes weather to occur on a z level in certain area types
*
* The effects of weather occur across an entire z-level. For instance, lavaland has periodic ash storms that scorch most unprotected creatures.
* Weather always occurs on different z levels at different times, regardless of weather type.
* Can have custom durations, targets, and can automatically protect indoor areas.
*
*/
/datum/weather
/// name of weather
var/name = "space wind"
/// description of weather
var/desc = "Heavy gusts of wind blanket the area, periodically knocking down anyone caught in the open."
/// The message displayed in chat to foreshadow the weather's beginning
var/telegraph_message = "<span class='warning'>The wind begins to pick up.</span>"
/// In deciseconds, how long from the beginning of the telegraph until the weather begins
var/telegraph_duration = 300
/// The sound file played to everyone on an affected z-level
var/telegraph_sound
/// The overlay applied to all tiles on the z-level
var/telegraph_overlay
/// Displayed in chat once the weather begins in earnest
var/weather_message = "<span class='userdanger'>The wind begins to blow ferociously!</span>"
/// In deciseconds, how long the weather lasts once it begins
var/weather_duration = 1200
/// See above - this is the lowest possible duration
var/weather_duration_lower = 1200
/// See above - this is the highest possible duration
var/weather_duration_upper = 1500
/// Looping sound while weather is occuring
var/weather_sound
/// Area overlay while the weather is occuring
var/weather_overlay
/// Color to apply to the area while weather is occuring
var/weather_color = null
/// Displayed once the weather is over
var/end_message = "<span class='danger'>The wind relents its assault.</span>"
/// In deciseconds, how long the "wind-down" graphic will appear before vanishing entirely
var/end_duration = 300
/// Sound that plays while weather is ending
var/end_sound
/// Area overlay while weather is ending
var/end_overlay
/// Types of area to affect
var/area_type = /area/space
/// TRUE value protects areas with outdoors marked as false, regardless of area type
var/protect_indoors = FALSE
/// Areas to be affected by the weather, calculated when the weather begins
var/list/impacted_areas = list()
/// Areas that are protected and excluded from the affected areas.
var/list/protected_areas = list()
/// The list of z-levels that this weather is actively affecting
var/impacted_z_levels
/// Since it's above everything else, this is the layer used by default. TURF_LAYER is below mobs and walls if you need to use that.
var/overlay_layer = AREA_LAYER
/// Plane for the overlay
var/overlay_plane = AREA_PLANE
/// If the weather has no purpose other than looks
var/aesthetic = FALSE
/// Used by mobs (or movables containing mobs, such as enviro bags) to prevent them from being affected by the weather.
var/immunity_type
/// If this bit of weather should also draw an overlay that's uneffected by lighting onto the area
/// Taken from weather_glow.dmi
var/use_glow = TRUE
/// List of all overlays to apply to our turfs
var/list/overlay_cache
/// The stage of the weather, from 1-4
var/stage = END_STAGE
/// Weight amongst other eligible weather. If zero, will never happen randomly.
var/probability = 0
/// The z-level trait to affect when run randomly or when not overridden.
var/target_trait = ZTRAIT_STATION
/// Whether a barometer can predict when the weather will happen
var/barometer_predictable = FALSE
/// For barometers to know when the next storm will hit
var/next_hit_time = 0
/// This causes the weather to only end if forced to
var/perpetual = FALSE
/datum/weather/New(z_levels)
..()
impacted_z_levels = z_levels
/**
* Telegraphs the beginning of the weather on the impacted z levels
*
* Sends sounds and details to mobs in the area
* Calculates duration and hit areas, and makes a callback for the actual weather to start
*
*/
/datum/weather/proc/telegraph()
if(stage == STARTUP_STAGE)
return
SEND_GLOBAL_SIGNAL(COMSIG_WEATHER_TELEGRAPH(type))
stage = STARTUP_STAGE
var/list/affectareas = list()
for(var/V in get_areas(area_type))
affectareas += V
for(var/V in protected_areas)
affectareas -= get_areas(V)
for(var/V in affectareas)
var/area/A = V
if(protect_indoors && !A.outdoors)
continue
if(A.z in impacted_z_levels)
impacted_areas |= A
weather_duration = rand(weather_duration_lower, weather_duration_upper)
SSweather.processing |= src
update_areas()
send_alert(telegraph_message, telegraph_sound)
addtimer(CALLBACK(src, PROC_REF(start)), telegraph_duration)
/**
* Starts the actual weather and effects from it
*
* Updates area overlays and sends sounds and messages to mobs to notify them
* Begins dealing effects from weather to mobs in the area
*
*/
/datum/weather/proc/start()
if(stage >= MAIN_STAGE)
return
SEND_GLOBAL_SIGNAL(COMSIG_WEATHER_START(type))
stage = MAIN_STAGE
update_areas()
send_alert(weather_message, weather_sound)
if(!perpetual)
addtimer(CALLBACK(src, PROC_REF(wind_down)), weather_duration)
/**
* Weather enters the winding down phase, stops effects
*
* Updates areas to be in the winding down phase
* Sends sounds and messages to mobs to notify them
*
*/
/datum/weather/proc/wind_down()
if(stage >= WIND_DOWN_STAGE)
return
SEND_GLOBAL_SIGNAL(COMSIG_WEATHER_WINDDOWN(type))
stage = WIND_DOWN_STAGE
update_areas()
send_alert(end_message, end_sound)
addtimer(CALLBACK(src, PROC_REF(end)), end_duration)
/**
* Fully ends the weather
*
* Effects no longer occur and area overlays are removed
* Removes weather from processing completely
*
*/
/datum/weather/proc/end()
if(stage == END_STAGE)
return
SEND_GLOBAL_SIGNAL(COMSIG_WEATHER_END(type))
stage = END_STAGE
SSweather.processing -= src
update_areas()
// handles sending all alerts
/datum/weather/proc/send_alert(alert_msg, alert_sfx)
for(var/z_level in impacted_z_levels)
for(var/mob/player as anything in SSmobs.clients_by_zlevel[z_level])
if(!can_get_alert(player))
continue
if(alert_msg)
to_chat(player, alert_msg)
if(alert_sfx)
SEND_SOUND(player, sound(alert_sfx))
// the checks for if a mob should recieve alerts, returns TRUE if can
/datum/weather/proc/can_get_alert(mob/player)
var/turf/mob_turf = get_turf(player)
return !isnull(mob_turf)
/**
* Returns TRUE if the living mob can be affected by the weather
*
*/
/datum/weather/proc/can_weather_act(mob/living/mob_to_check)
var/turf/mob_turf = get_turf(mob_to_check)
if(!mob_turf)
return
if(!(mob_turf.z in impacted_z_levels))
return
if((immunity_type && HAS_TRAIT(mob_to_check, immunity_type)) || HAS_TRAIT(mob_to_check, TRAIT_WEATHER_IMMUNE))
return
var/atom/loc_to_check = mob_to_check.loc
while(loc_to_check != mob_turf)
if((immunity_type && HAS_TRAIT(loc_to_check, immunity_type)) || HAS_TRAIT(loc_to_check, TRAIT_WEATHER_IMMUNE))
return
loc_to_check = loc_to_check.loc
if(!(get_area(mob_to_check) in impacted_areas))
return
return TRUE
/**
* Affects the mob with whatever the weather does
*
*/
/datum/weather/proc/weather_act(mob/living/L)
return
/**
* Updates the overlays on impacted areas
*
*/
/datum/weather/proc/update_areas()
var/list/new_overlay_cache = generate_overlay_cache()
for(var/area/impacted as anything in impacted_areas)
if(length(overlay_cache))
impacted.overlays -= overlay_cache
if(length(new_overlay_cache))
impacted.overlays += new_overlay_cache
overlay_cache = new_overlay_cache
/// Returns a list of visual offset -> overlays to use
/datum/weather/proc/generate_overlay_cache()
// We're ending, so no overlays at all
if(stage == END_STAGE)
return list()
var/weather_state = ""
switch(stage)
if(STARTUP_STAGE)
weather_state = telegraph_overlay
if(MAIN_STAGE)
weather_state = weather_overlay
if(WIND_DOWN_STAGE)
weather_state = end_overlay
// Use all possible offsets
// Yes this is a bit annoying, but it's too slow to calculate and store these from turfs, and it shouldn't (I hope) look weird
var/list/gen_overlay_cache = list()
for(var/offset in 0 to SSmapping.max_plane_offset)
// Note: what we do here is effectively apply two overlays to each area, for every unique multiz layer they inhabit
// One is the base, which will be masked by lighting. the other is "glowing", and provides a nice contrast
// This method of applying one overlay per z layer has some minor downsides, in that it could lead to improperly doubled effects if some have alpha
// I prefer it to creating 2 extra plane masters however, so it's a cost I'm willing to pay
// LU
var/mutable_appearance/glow_overlay = mutable_appearance('icons/effects/glow_weather.dmi', weather_state, overlay_layer, null, ABOVE_LIGHTING_PLANE, 100, offset_const = offset)
glow_overlay.color = weather_color
gen_overlay_cache += glow_overlay
var/mutable_appearance/weather_overlay = mutable_appearance('icons/effects/weather_effects.dmi', weather_state, overlay_layer, plane = overlay_plane, offset_const = offset)
weather_overlay.color = weather_color
gen_overlay_cache += weather_overlay
return gen_overlay_cache