/** * 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 = "The wind begins to pick up." /// 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 = "The wind begins to blow ferociously!" ///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 = "The wind relents its assault." /// 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 = BLACKNESS_PLANE /// If the weather has no purpose but aesthetics. 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 /// 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 stage = STARTUP_STAGE var/list/affectareas = list() for(var/V in get_areas(area_type)) var/area/A = V affectareas |= A if(A.sub_areas) affectareas |= A.sub_areas 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) START_PROCESSING(SSweather, src) //The reason this doesn't start and stop at main stage is because processing list is also used to see active running weathers (for example, you wouldn't want two ash storms starting at once.) update_areas() for(var/M in GLOB.player_list) var/turf/mob_turf = get_turf(M) if(mob_turf && (mob_turf.z in impacted_z_levels)) if(telegraph_message) to_chat(M, telegraph_message) if(telegraph_sound) SEND_SOUND(M, sound(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() for(var/z_level in impacted_z_levels) for(var/mob/player as anything in SSmobs.clients_by_zlevel[z_level]) var/turf/mob_turf = get_turf(player) if(!mob_turf) continue if(weather_message) to_chat(player, weather_message) if(weather_sound) SEND_SOUND(player, sound(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 stage = WIND_DOWN_STAGE update_areas() for(var/M in GLOB.player_list) var/turf/mob_turf = get_turf(M) if(mob_turf && (mob_turf.z in impacted_z_levels)) if(end_message) to_chat(M, end_message) if(end_sound) SEND_SOUND(M, sound(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 TRUE stage = END_STAGE STOP_PROCESSING(SSweather, src) update_areas() /datum/weather/process() if(aesthetic || (stage != MAIN_STAGE)) return for(var/i in GLOB.mob_living_list) var/mob/living/L = i if(can_weather_act(L)) weather_act(L) /** * 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 /datum/weather/proc/weather_act_turf(area/N) //What effect does this weather have on the area? return /** * Updates the overlays on impacted areas * */ /datum/weather/proc/update_areas() for(var/V in impacted_areas) var/area/N = V N.layer = overlay_layer N.icon = 'icons/effects/weather_effects.dmi' N.color = weather_color switch(stage) if(STARTUP_STAGE) N.icon_state = telegraph_overlay if(MAIN_STAGE) N.icon_state = weather_overlay weather_act_turf(N) if(WIND_DOWN_STAGE) N.icon_state = end_overlay if(END_STAGE) N.color = null N.icon_state = "" N.icon = 'icons/turf/areas.dmi' N.layer = AREA_LAYER //Just default back to normal area stuff since I assume setting a var is faster than initial N.set_opacity(FALSE)