diff --git a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_nt.dmm b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_nt.dmm
index ab7240c8943..a1661f6fd1c 100644
--- a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_nt.dmm
+++ b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_nt.dmm
@@ -432,15 +432,10 @@
/turf/simulated/floor/lava/mapping_lava,
/area/lavaland/surface/outdoors/outpost/catwalk)
"cZ" = (
-/obj/effect/spawner/random/loot_crate,
+/obj/machinery/radar,
/obj/effect/mapping_helpers/no_lava,
-/obj/structure/railing{
- dir = 4
- },
-/obj/structure/lattice/catwalk/mining,
-/obj/effect/decal/cleanable/dirt,
-/turf/simulated/floor/plating/asteroid/basalt/lava_land_surface,
-/area/lavaland/surface/outdoors/targetable)
+/turf/simulated/floor/engine/lavaland_air,
+/area/lavaland/surface/outdoors/outpost/catwalk)
"da" = (
/obj/effect/mapping_helpers/airlock/access/all/engineering/maintenance,
/obj/machinery/door/airlock/maintenance,
@@ -1164,6 +1159,12 @@
},
/turf/simulated/floor/plating,
/area/mine/outpost/smith_workshop)
+"gP" = (
+/obj/structure/lattice/catwalk/mining,
+/obj/effect/decal/cleanable/dirt,
+/obj/item/cigbutt,
+/turf/simulated/floor/plating/asteroid/basalt/lava_land_surface,
+/area/lavaland/surface/outdoors/outpost/catwalk)
"gQ" = (
/obj/effect/turf_decal/stripes/red/line,
/obj/effect/turf_decal/stripes/red/line{
@@ -1504,6 +1505,13 @@
icon_state = "darkfull"
},
/area/mine/outpost/hallway/west)
+"iy" = (
+/obj/structure/railing{
+ dir = 4
+ },
+/obj/effect/mapping_helpers/no_lava,
+/turf/simulated/floor/engine/lavaland_air,
+/area/lavaland/surface/outdoors/outpost/catwalk)
"iA" = (
/obj/machinery/atmospherics/pipe/simple/hidden/supply{
dir = 4
@@ -1591,6 +1599,13 @@
icon_state = "barber"
},
/area/mine/laborcamp)
+"iY" = (
+/obj/structure/railing{
+ dir = 9
+ },
+/obj/effect/mapping_helpers/no_lava,
+/turf/simulated/floor/engine/lavaland_air,
+/area/lavaland/surface/outdoors/outpost/catwalk)
"jc" = (
/obj/effect/decal/cleanable/dirt,
/obj/structure/rack,
@@ -1797,6 +1812,14 @@
},
/turf/simulated/floor/plasteel,
/area/mine/laborcamp)
+"kf" = (
+/obj/structure/railing{
+ dir = 4
+ },
+/obj/item/screwdriver,
+/obj/effect/mapping_helpers/no_lava,
+/turf/simulated/floor/engine/lavaland_air,
+/area/lavaland/surface/outdoors/outpost/catwalk)
"kl" = (
/turf/simulated/floor/plating/asteroid/basalt/lava_land_surface,
/area/lavaland/surface/outdoors/explored)
@@ -1817,6 +1840,14 @@
icon_state = "darkfull"
},
/area/mine/outpost/engineering)
+"kC" = (
+/obj/structure/lattice/catwalk/mining,
+/obj/structure/railing,
+/obj/structure/railing/corner{
+ dir = 4
+ },
+/turf/simulated/floor/lava/mapping_lava,
+/area/lavaland/surface/outdoors/outpost/catwalk)
"kG" = (
/obj/effect/turf_decal/stripes/red/line,
/turf/simulated/floor/plasteel{
@@ -3118,6 +3149,10 @@
icon_state = "darkredyellowfull"
},
/area/mine/laborcamp)
+"re" = (
+/obj/effect/mapping_helpers/no_lava,
+/turf/simulated/mineral/volcanic/lava_land_surface,
+/area/lavaland/surface/gulag_rock)
"rg" = (
/obj/structure/rack,
/obj/item/screwdriver,
@@ -4196,11 +4231,8 @@
/area/mine/outpost/maintenance/east)
"wX" = (
/obj/structure/lattice/catwalk/mining,
-/obj/structure/railing/corner{
- dir = 4
- },
/obj/structure/railing,
-/turf/simulated/floor/lava/mapping_lava,
+/turf/simulated/floor/plating/asteroid/basalt/lava_land_surface,
/area/lavaland/surface/outdoors/outpost/catwalk)
"xf" = (
/obj/machinery/computer/card/minor/qm,
@@ -4510,10 +4542,12 @@
},
/area/mine/laborcamp)
"zn" = (
-/obj/structure/marker_beacon/dock_marker/collision,
+/obj/structure/railing{
+ dir = 5
+ },
/obj/effect/mapping_helpers/no_lava,
-/turf/simulated/floor/plating/asteroid/basalt/lava_land_surface,
-/area/lavaland/surface/outdoors/targetable)
+/turf/simulated/floor/engine/lavaland_air,
+/area/lavaland/surface/outdoors/outpost/catwalk)
"zp" = (
/obj/structure/disposalpipe/segment{
dir = 8
@@ -4559,6 +4593,17 @@
dir = 8
},
/area/mine/outpost/mechbay)
+"zA" = (
+/obj/structure/railing/corner{
+ dir = 1
+ },
+/obj/structure/railing/corner{
+ dir = 1
+ },
+/obj/effect/baseturf_helper/lava_land,
+/obj/effect/mapping_helpers/no_lava,
+/turf/simulated/floor/engine/lavaland_air,
+/area/lavaland/surface/outdoors/outpost/catwalk)
"zB" = (
/obj/effect/spawner/random/cobweb/left/frequent,
/obj/machinery/alarm/directional/north,
@@ -4962,9 +5007,10 @@
/turf/simulated/wall/r_wall,
/area/mine/laborcamp)
"BB" = (
-/obj/structure/sign/nanotrasen,
-/turf/simulated/wall/mineral/iron,
-/area/lavaland/surface/outdoors/targetable)
+/obj/effect/spawner/random/trash,
+/obj/effect/mapping_helpers/no_lava,
+/turf/simulated/floor/engine/lavaland_air,
+/area/lavaland/surface/outdoors/outpost/catwalk)
"BE" = (
/obj/effect/decal/cleanable/dirt,
/obj/structure/extinguisher_cabinet{
@@ -5255,6 +5301,16 @@
"De" = (
/turf/template_noop,
/area/template_noop)
+"Df" = (
+/obj/structure/sign/nanotrasen{
+ pixel_y = -32
+ },
+/obj/structure/railing{
+ dir = 1
+ },
+/obj/effect/mapping_helpers/no_lava,
+/turf/simulated/floor/engine/lavaland_air,
+/area/lavaland/surface/outdoors/outpost/catwalk)
"Dj" = (
/obj/structure/chair,
/obj/item/cigbutt,
@@ -6840,6 +6896,13 @@
},
/turf/simulated/floor/plating/asteroid/basalt/lava_land_surface,
/area/lavaland/surface/outdoors/outpost/catwalk)
+"LJ" = (
+/obj/structure/railing{
+ dir = 8
+ },
+/obj/effect/mapping_helpers/no_lava,
+/turf/simulated/floor/engine/lavaland_air,
+/area/lavaland/surface/outdoors/outpost/catwalk)
"LK" = (
/obj/machinery/door/airlock{
name = "Custodial Storage"
@@ -7313,7 +7376,6 @@
/obj/structure/lattice/catwalk/mining,
/obj/effect/decal/cleanable/dirt,
/obj/structure/railing/corner,
-/obj/item/cigbutt,
/turf/simulated/floor/plating/asteroid/basalt/lava_land_surface,
/area/lavaland/surface/outdoors/outpost/catwalk)
"Oq" = (
@@ -8720,6 +8782,10 @@
"Wn" = (
/turf/simulated/mineral/volcanic/lava_land_surface,
/area/lavaland/surface/outdoors)
+"Wq" = (
+/obj/effect/mapping_helpers/no_lava,
+/turf/simulated/floor/engine/lavaland_air,
+/area/lavaland/surface/outdoors/outpost/catwalk)
"Ws" = (
/obj/structure/chair/stool/bar{
dir = 8
@@ -16873,8 +16939,8 @@ lR
lR
lR
lR
-lR
-lR
+re
+re
Yr
Yr
xt
@@ -17034,9 +17100,9 @@ lR
lR
lR
lR
-lR
-lR
-lR
+pB
+re
+re
Yr
Yr
PH
@@ -17195,13 +17261,13 @@ zP
zP
oO
oO
-Yh
-Yh
-Yh
-Yr
-Yr
-Yr
-PH
+oO
+IJ
+iY
+LJ
+LJ
+zA
+gP
gt
gt
gt
@@ -17352,17 +17418,17 @@ lR
lR
lR
lR
-sb
+IG
zP
zP
oO
oO
-Yh
-Yh
-Yh
-Yr
-Yr
-Yr
+IJ
+IJ
+Df
+Wq
+Wq
+Wq
Ol
gt
oO
@@ -17514,15 +17580,15 @@ sb
sb
sb
sb
-sb
+IG
zP
zP
oO
oO
IJ
-Yh
-Yh
-Yr
+IJ
+Df
+Wq
cZ
BB
wX
@@ -17676,18 +17742,18 @@ sb
sb
sb
sb
-sb
+IG
zP
zP
oO
oO
-Yh
-IJ
-zn
-zn
-IJ
oO
-dd
+IJ
+zn
+iy
+kf
+iy
+kC
oO
oO
zP
@@ -17842,14 +17908,14 @@ uv
zP
oO
oO
+Yh
+Yh
+Yh
+Yh
+IJ
+IJ
oO
-Yh
-Yr
-Yr
-Yh
-Yh
-oO
-dd
+VM
oO
oO
oO
diff --git a/_maps/map_files/templates/shelter_2.dmm b/_maps/map_files/templates/shelter_2.dmm
index 521798561d9..da889651ccc 100644
--- a/_maps/map_files/templates/shelter_2.dmm
+++ b/_maps/map_files/templates/shelter_2.dmm
@@ -1,46 +1,41 @@
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
-"a" = (
+"aa" = (
/turf/simulated/wall/mineral/titanium/survival,
-/area/survivalpod)
-"b" = (
+/area/survivalpod/luxurypod)
+"ab" = (
/obj/structure/sign/mining/survival{
dir = 8
},
/turf/simulated/wall/mineral/titanium/survival,
-/area/survivalpod)
-"c" = (
+/area/survivalpod/luxurypod)
+"ac" = (
/obj/machinery/smartfridge/survival_pod,
/turf/simulated/floor/pod,
-/area/survivalpod)
-"d" = (
+/area/survivalpod/luxurypod)
+"ad" = (
/turf/simulated/floor/pod,
-/area/survivalpod)
-"e" = (
+/area/survivalpod/luxurypod)
+"ae" = (
/obj/machinery/door/window/survival_pod{
dir = 1
},
/turf/simulated/floor/carpet/black,
-/area/survivalpod)
-"f" = (
+/area/survivalpod/luxurypod)
+"af" = (
/obj/machinery/light,
/turf/simulated/floor/carpet/black,
-/area/survivalpod)
-"g" = (
+/area/survivalpod/luxurypod)
+"ag" = (
/obj/structure/sign/mining/survival{
dir = 1
},
/turf/simulated/wall/mineral/titanium/survival,
-/area/survivalpod)
-"h" = (
+/area/survivalpod/luxurypod)
+"ah" = (
/obj/structure/fans,
/turf/simulated/floor/pod,
-/area/survivalpod)
-"i" = (
-/obj/machinery/sleeper/survival_pod,
-/obj/machinery/economy/vending/wallmed/directional/west,
-/turf/simulated/floor/pod,
-/area/survivalpod)
-"j" = (
+/area/survivalpod/luxurypod)
+"aj" = (
/obj/structure/table/survival_pod,
/obj/item/storage/firstaid/regular,
/obj/item/storage/firstaid/brute{
@@ -48,8 +43,8 @@
pixel_y = -2
},
/turf/simulated/floor/pod,
-/area/survivalpod)
-"k" = (
+/area/survivalpod/luxurypod)
+"ak" = (
/obj/structure/table/wood/fancy/black{
pixel_y = -9
},
@@ -58,26 +53,26 @@
},
/obj/machinery/kitchen_machine/microwave,
/turf/simulated/floor/carpet/black,
-/area/survivalpod)
-"l" = (
+/area/survivalpod/luxurypod)
+"al" = (
/obj/structure/sink/kitchen{
dir = 4;
pixel_x = -11
},
/turf/simulated/floor/carpet/black,
-/area/survivalpod)
-"m" = (
+/area/survivalpod/luxurypod)
+"am" = (
/obj/structure/sign/mining/survival,
/turf/simulated/wall/mineral/titanium/survival,
-/area/survivalpod)
-"n" = (
+/area/survivalpod/luxurypod)
+"an" = (
/obj/structure/curtain/black,
/obj/machinery/shower{
pixel_y = 20
},
/turf/simulated/floor/pod,
-/area/survivalpod)
-"o" = (
+/area/survivalpod/luxurypod)
+"ao" = (
/obj/structure/window/reinforced/survival_pod{
dir = 8;
pixel_y = -8
@@ -86,32 +81,32 @@
dir = 1
},
/turf/simulated/floor/carpet/black,
-/area/survivalpod)
-"p" = (
+/area/survivalpod/luxurypod)
+"aq" = (
/obj/structure/window/reinforced/survival_pod{
dir = 8;
pixel_y = -8
},
/turf/simulated/floor/carpet/black,
-/area/survivalpod)
-"r" = (
+/area/survivalpod/luxurypod)
+"ar" = (
/turf/simulated/floor/carpet/black,
-/area/survivalpod)
-"s" = (
+/area/survivalpod/luxurypod)
+"as" = (
/obj/item/gps/computer,
/turf/simulated/floor/pod,
-/area/survivalpod)
-"t" = (
+/area/survivalpod/luxurypod)
+"at" = (
/obj/structure/bed/pod,
/obj/item/bedsheet/brown,
/turf/simulated/floor/pod,
-/area/survivalpod)
-"u" = (
+/area/survivalpod/luxurypod)
+"au" = (
/obj/structure/tubes,
/obj/item/kirbyplants/large,
/turf/simulated/floor/pod,
-/area/survivalpod)
-"v" = (
+/area/survivalpod/luxurypod)
+"av" = (
/obj/structure/window/reinforced/survival_pod{
dir = 1
},
@@ -124,25 +119,25 @@
/turf/simulated/floor/carpet/black{
dir = 1
},
-/area/survivalpod)
-"w" = (
+/area/survivalpod/luxurypod)
+"aw" = (
/obj/structure/fans/tiny,
/obj/machinery/door/airlock/survival_pod/glass,
/turf/simulated/floor/pod,
-/area/survivalpod)
-"x" = (
+/area/survivalpod/luxurypod)
+"ax" = (
/obj/structure/sign/mining/survival{
dir = 4
},
/turf/simulated/wall/mineral/titanium/survival,
-/area/survivalpod)
-"y" = (
+/area/survivalpod/luxurypod)
+"ay" = (
/obj/structure/toilet/secret{
pixel_y = 12
},
/turf/simulated/floor/pod,
-/area/survivalpod)
-"z" = (
+/area/survivalpod/luxurypod)
+"az" = (
/obj/structure/window/reinforced/survival_pod{
dir = 1
},
@@ -152,8 +147,8 @@
name = "lusty xenomorph maid"
},
/turf/simulated/floor/carpet/black,
-/area/survivalpod)
-"A" = (
+/area/survivalpod/luxurypod)
+"aA" = (
/obj/structure/table/wood/fancy/black,
/obj/machinery/light{
dir = 4
@@ -164,8 +159,8 @@
},
/obj/item/reagent_containers/drinks/bottle/wine,
/turf/simulated/floor/carpet/black,
-/area/survivalpod)
-"B" = (
+/area/survivalpod/luxurypod)
+"aB" = (
/obj/structure/table/wood/fancy/black,
/obj/item/reagent_containers/drinks/drinkingglass{
pixel_x = -4;
@@ -173,78 +168,83 @@
},
/obj/effect/spawner/random/food_or_drink/three_course_meal,
/turf/simulated/floor/carpet/black,
-/area/survivalpod)
-"C" = (
+/area/survivalpod/luxurypod)
+"aC" = (
/obj/structure/chair/comfy/black{
dir = 1
},
/turf/simulated/floor/carpet/black,
-/area/survivalpod)
-"D" = (
+/area/survivalpod/luxurypod)
+"aD" = (
/obj/structure/sign/mining,
/turf/simulated/wall/mineral/titanium/survival,
-/area/survivalpod)
+/area/survivalpod/luxurypod)
+"aF" = (
+/obj/machinery/sleeper/survival_pod,
+/obj/machinery/economy/vending/wallmed/directional/west,
+/turf/simulated/floor/pod,
+/area/survivalpod/luxurypod)
(1,1,1) = {"
-a
-b
-a
-b
-a
-b
-a
+aa
+ab
+aa
+ab
+aa
+ab
+aa
"}
(2,1,1) = {"
-g
-h
-i
-j
-k
-l
-m
+ag
+ah
+aF
+aj
+ak
+al
+am
"}
(3,1,1) = {"
-a
-c
-d
-d
-e
-f
-a
+aa
+ac
+ad
+ad
+ae
+af
+aa
"}
(4,1,1) = {"
-g
-s
-t
-u
-v
-r
-w
+ag
+as
+at
+au
+av
+ar
+aw
"}
(5,1,1) = {"
-a
-n
-o
-p
-r
-r
-a
+aa
+an
+ao
+aq
+ar
+ar
+aa
"}
(6,1,1) = {"
-g
-y
-z
-A
-B
-C
-D
+ag
+ay
+az
+aA
+aB
+aC
+aD
"}
(7,1,1) = {"
-a
-x
-a
-x
-a
-x
-a
+aa
+ax
+aa
+ax
+aa
+ax
+aa
"}
diff --git a/code/__DEFINES/dcs/global_signals.dm b/code/__DEFINES/dcs/global_signals.dm
index 350f1603e96..509f60b1f15 100644
--- a/code/__DEFINES/dcs/global_signals.dm
+++ b/code/__DEFINES/dcs/global_signals.dm
@@ -24,3 +24,6 @@
/// Used by admin-tooling to remove radiation
#define COMSIG_ADMIN_DECONTAMINATE "admin_decontaminate"
+
+/// Used when a shelter capsule deploys
+#define COMSIG_GLOB_SHELTER_PLACED "!shelter_placed"
diff --git a/code/__HELPERS/lists.dm b/code/__HELPERS/lists.dm
index 8ff538f3d37..b3c050d8081 100644
--- a/code/__HELPERS/lists.dm
+++ b/code/__HELPERS/lists.dm
@@ -146,6 +146,14 @@
return TRUE
return FALSE
+/proc/is_path_in_list(P, list/L)
+ if(!L || !length(L) || !P)
+ return FALSE
+ for(var/type in L)
+ if(ispath(P, type))
+ return TRUE
+ return FALSE
+
//Checks for specific types in specifically structured (Assoc "type" = TRUE) lists ('typecaches')
/proc/is_type_in_typecache(atom/A, list/L)
if(!L || !length(L) || !A)
diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index 73e7b673f06..10e9a730ad0 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -1989,7 +1989,7 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
if(CHANNEL_FIREALARM)
return "Fire Alarms"
if(CHANNEL_ASH_STORM)
- return "Ash Storms"
+ return "Weather"
if(CHANNEL_RADIO_NOISE)
return "Radio Noise"
if(CHANNEL_BOSS_MUSIC)
diff --git a/code/controllers/subsystem/SSweather.dm b/code/controllers/subsystem/SSweather.dm
index b0dd088ebaf..eff570df97b 100644
--- a/code/controllers/subsystem/SSweather.dm
+++ b/code/controllers/subsystem/SSweather.dm
@@ -10,6 +10,7 @@ SUBSYSTEM_DEF(weather)
var/list/processing = list()
var/list/eligible_traits = list()
var/list/next_hit_by_zlevel = list() //Used by barometers to know when the next storm is coming
+ var/list/next_weather_by_zlevel = list() // For the weather radar detection
cpu_display = SS_CPUDISPLAY_LOW
/datum/controller/subsystem/weather/get_metrics()
@@ -28,6 +29,8 @@ SUBSYSTEM_DEF(weather)
var/mob/living/L = i
if(W.can_weather_act(L))
W.weather_act(L)
+ if(W.area_act == TRUE)
+ W.area_act()
// start random weather on relevant levels
while(length(eligible_traits))
@@ -39,6 +42,7 @@ SUBSYSTEM_DEF(weather)
for(var/z in zlevels)
addtimer(CALLBACK(src, PROC_REF(make_eligible), trait, possible_weathers), randTime + initial(W.weather_duration_upper), TIMER_UNIQUE) //Around 5-10 minutes between weathers
next_hit_by_zlevel["[z]"] = world.time + randTime + initial(W.telegraph_duration)
+ next_weather_by_zlevel["[z]"] = W
run_weather(W, zlevels)
eligible_traits.len--
@@ -68,17 +72,19 @@ SUBSYSTEM_DEF(weather)
var/list/zlevels = levels_by_trait(weather_datum_type::target_trait)
var/datum/weather/W = new weather_datum_type(zlevels)
W.telegraph()
+ SSblackbox.record_feedback("tally", "weather_event", 1, "[weather_datum_type]")
/datum/controller/subsystem/weather/proc/make_eligible(trait, list/possible_weathers)
eligible_traits[trait] = possible_weathers
for(var/zlevel in levels_by_trait(trait))
next_hit_by_zlevel["[zlevel]"] = null
+ next_weather_by_zlevel["[zlevel]"] = null
/datum/controller/subsystem/weather/proc/get_weather(z, area/active_area)
var/datum/weather/A
for(var/V in processing)
var/datum/weather/W = V
- if((z in W.impacted_z_levels) && W.area_type == active_area)
+ if((z in W.impacted_z_levels) && is_path_in_list(active_area, W.area_types))
A = W
break
return A
diff --git a/code/datums/looping_sounds/weather_sounds.dm b/code/datums/looping_sounds/weather_sounds.dm
index ccd7fa1a5ad..3a2e1fa2b72 100644
--- a/code/datums/looping_sounds/weather_sounds.dm
+++ b/code/datums/looping_sounds/weather_sounds.dm
@@ -4,7 +4,7 @@
'sound/weather/ashstorm/outside/active_mid1.ogg' = 1,
'sound/weather/ashstorm/outside/active_mid1.ogg' = 1
)
- mid_length = 80
+ mid_length = 65
start_sound = 'sound/weather/ashstorm/outside/active_start.ogg'
start_length = 130
end_sound = 'sound/weather/ashstorm/outside/active_end.ogg'
@@ -53,3 +53,44 @@
volume = 30
area_sound = TRUE
channel = CHANNEL_ASH_STORM
+
+/datum/looping_sound/active_outside_acid
+ mid_sounds = 'sound/weather/acid/outside/active_mid1.ogg'
+ mid_length = 105
+ start_sound = list('sound/weather/acid/outside/active_start.ogg' = 1)
+ start_length = 130
+ end_sound = 'sound/weather/acid/outside/active_end.ogg'
+ volume = 65
+ area_sound = TRUE
+ channel = CHANNEL_ASH_STORM
+
+/datum/looping_sound/weak_outside_acid
+ mid_sounds ='sound/weather/acid/outside/weak_mid1.ogg'
+ mid_length = 120
+ start_sound = list('sound/weather/acid/outside/weak_start.ogg' = 1)
+ start_length = 130
+ end_sound = 'sound/weather/acid/outside/weak_end.ogg'
+ volume = 60
+ area_sound = TRUE
+ channel = CHANNEL_ASH_STORM
+
+/datum/looping_sound/active_inside_acid
+ mid_sounds = 'sound/weather/acid/inside/active_mid1.ogg'
+ mid_length = 105
+ start_sound = list('sound/weather/acid/inside/active_start.ogg' = 1)
+ start_length = 130
+ end_sound = 'sound/weather/acid/inside/active_end.ogg'
+ volume = 50
+ area_sound = TRUE
+ channel = CHANNEL_ASH_STORM
+
+/datum/looping_sound/weak_inside_acid
+ mid_sounds = 'sound/weather/acid/inside/weak_mid1.ogg'
+ mid_length = 105
+ start_sound = list('sound/weather/acid/inside/weak_start.ogg' = 1)
+ start_length = 130
+ end_sound = 'sound/weather/acid/inside/weak_end.ogg'
+ volume = 50
+ area_sound = TRUE
+ channel = CHANNEL_ASH_STORM
+
diff --git a/code/datums/weather/weather_datum.dm b/code/datums/weather/weather_datum.dm
index adeceb51aba..60c1e9def20 100644
--- a/code/datums/weather/weather_datum.dm
+++ b/code/datums/weather/weather_datum.dm
@@ -1,5 +1,7 @@
//The effects of weather occur across an entire z-level. For instance, lavaland has periodic ash storms that scorch most unprotected creatures.
+GLOBAL_LIST_EMPTY(all_shelter_pods)
+
/datum/weather
var/name = "space wind"
var/desc = "Heavy gusts of wind blanket the area, periodically knocking down anyone caught in the open."
@@ -22,13 +24,15 @@
var/end_sound
var/end_overlay
- var/area_type = /area/space //Types of area to affect
+ var/area_types = list(/area/space) //Types of area to affect
var/list/impacted_areas = list() //Areas to be affected by the weather, calculated when the weather begins
var/list/protected_areas = list()//Areas that are protected and excluded from the affected areas.
var/impacted_z_levels // The list of z-levels that this weather is actively affecting
var/overlay_layer = AREA_LAYER //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_plane = AREA_PLANE
+ var/custom_overlay // Do we want to give it a non-standard weather effect
+ var/overlay_dir = NORTH
var/aesthetic = FALSE //If the weather has no purpose other than looks
var/immunity_type = "storm" //Used by mobs to prevent them from being affected by the weather
@@ -41,14 +45,27 @@
var/barometer_predictable = FALSE
var/next_hit_time = 0 //For barometers to know when the next storm will hit
+ var/area_act = FALSE // Does this affect more than just mobs, or the landscape?
+
+ var/list/inside_areas = list() // Any areas not in the outside terf
+ var/list/outside_areas = list() // Any areas listed as "outside"
+ var/list/eligible_areas = list() // For variable playing or not playing sounds for shuttles
+
/datum/weather/New(z_levels)
..()
impacted_z_levels = z_levels
+ RegisterSignal(SSdcs, COMSIG_GLOB_SHELTER_PLACED, PROC_REF(on_shelter_placed))
+
+/datum/weather/proc/on_shelter_placed(datum/source, turf/center)
+ SIGNAL_HANDLER // COMSIG_GLOB_SHELTER_PLACED
+ GLOB.all_shelter_pods += center
+ return
/datum/weather/proc/generate_area_list()
var/list/affectareas = list()
- for(var/V in get_areas(area_type))
- affectareas += V
+ for(var/area_type in area_types)
+ 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)
@@ -64,6 +81,8 @@
weather_duration = rand(weather_duration_lower, weather_duration_upper)
START_PROCESSING(SSweather, src)
update_areas()
+ update_eligible_areas()
+ update_audio()
for(var/M in GLOB.player_list)
var/turf/mob_turf = get_turf(M)
if(mob_turf && (mob_turf.z in impacted_z_levels))
@@ -71,6 +90,7 @@
to_chat(M, telegraph_message)
if(telegraph_sound)
SEND_SOUND(M, sound(telegraph_sound))
+
addtimer(CALLBACK(src, PROC_REF(start)), telegraph_duration)
/datum/weather/proc/start()
@@ -78,6 +98,7 @@
return
stage = WEATHER_MAIN_STAGE
update_areas()
+ update_audio()
for(var/M in GLOB.player_list)
var/turf/mob_turf = get_turf(M)
if(mob_turf && (mob_turf.z in impacted_z_levels))
@@ -92,6 +113,7 @@
return
stage = WEATHER_WIND_DOWN_STAGE
update_areas()
+ update_audio()
for(var/M in GLOB.player_list)
var/turf/mob_turf = get_turf(M)
if(mob_turf && (mob_turf.z in impacted_z_levels))
@@ -107,6 +129,7 @@
stage = WEATHER_END_STAGE
STOP_PROCESSING(SSweather, src)
update_areas()
+ update_audio()
/datum/weather/proc/can_weather_act(mob/living/L) //Can this weather impact a mob?
var/turf/mob_turf = get_turf(L)
@@ -128,7 +151,12 @@
var/area/N = V
N.layer = overlay_layer
N.plane = overlay_plane
- N.icon = 'icons/effects/weather_effects.dmi'
+ if(!custom_overlay)
+ N.icon = 'icons/effects/weather_effects.dmi'
+ else
+ N.icon = custom_overlay
+ if(overlay_dir)
+ N.dir = overlay_dir
N.invisibility = 0
N.color = weather_color
switch(stage)
@@ -142,6 +170,43 @@
N.color = null
N.icon_state = ""
N.icon = 'icons/turf/areas.dmi'
+ N.dir = NORTH
N.layer = initial(N.layer)
N.plane = initial(N.plane)
N.set_opacity(FALSE)
+
+
+/datum/weather/proc/update_eligible_areas()
+ for(var/z in impacted_z_levels)
+ for(var/area/A in GLOB.space_manager.areas_in_z["[z]"])
+ eligible_areas |= A
+
+ // Don't play storm audio to shuttles that are not at lavaland
+ var/miningShuttleDocked = is_shuttle_docked("mining", "mining_away")
+ if(!miningShuttleDocked)
+ eligible_areas -= get_areas(/area/shuttle/mining)
+
+ var/laborShuttleDocked = is_shuttle_docked("laborcamp", "laborcamp_away")
+ if(!laborShuttleDocked)
+ eligible_areas -= get_areas(/area/shuttle/siberia)
+
+ var/golemShuttleOnPlanet = is_shuttle_docked("freegolem", "freegolem_lavaland")
+ if(!golemShuttleOnPlanet)
+ eligible_areas -= get_areas(/area/shuttle/freegolem)
+
+ for(var/i in 1 to length(eligible_areas))
+ var/area/place = eligible_areas[i]
+ if(place.outdoors)
+ outside_areas |= place
+ else
+ inside_areas |= place
+
+/datum/weather/proc/is_shuttle_docked(shuttleId, dockId)
+ var/obj/docking_port/mobile/M = SSshuttle.getShuttle(shuttleId)
+ return M && M.getDockedId() == dockId
+
+/datum/weather/proc/area_act()
+ return
+
+/datum/weather/proc/update_audio()
+ return
diff --git a/code/datums/weather/weather_types/ash_storm.dm b/code/datums/weather/weather_types/ash_storm.dm
deleted file mode 100644
index af24f07e38f..00000000000
--- a/code/datums/weather/weather_types/ash_storm.dm
+++ /dev/null
@@ -1,148 +0,0 @@
-//Ash storms happen frequently on lavaland. They heavily obscure vision, and cause high fire damage to anyone caught outside.
-/datum/weather/ash_storm
- name = "ash storm"
- desc = "An intense atmospheric storm lifts ash off of the planet's surface and billows it down across the area, dealing intense fire damage to the unprotected."
-
- telegraph_message = "An eerie moan rises on the wind. Sheets of burning ash blacken the horizon. Seek shelter."
- telegraph_duration = 300
- telegraph_overlay = "light_ash"
-
- weather_message = "Smoldering clouds of scorching ash billow down around you! Get inside!"
- weather_duration_lower = 600
- weather_duration_upper = 1200
- weather_overlay = "ash_storm"
-
- end_message = "The shrieking wind whips away the last of the ash and falls to its usual murmur. It should be safe to go outside now."
- end_duration = 300
- end_overlay = "light_ash"
-
- area_type = /area/lavaland/surface/outdoors
- target_trait = ORE_LEVEL
-
- immunity_type = "ash"
-
- probability = 90
-
- barometer_predictable = TRUE
-
- var/datum/looping_sound/active_outside_ashstorm/sound_ao = new(list(), FALSE, TRUE)
- var/datum/looping_sound/active_inside_ashstorm/sound_ai = new(list(), FALSE, TRUE)
- var/datum/looping_sound/weak_outside_ashstorm/sound_wo = new(list(), FALSE, TRUE)
- var/datum/looping_sound/weak_inside_ashstorm/sound_wi = new(list(), FALSE, TRUE)
-
-/datum/weather/ash_storm/proc/is_shuttle_docked(shuttleId, dockId)
- var/obj/docking_port/mobile/M = SSshuttle.getShuttle(shuttleId)
- return M && M.getDockedId() == dockId
-
-/datum/weather/ash_storm/proc/update_eligible_areas()
- var/list/inside_areas = list()
- var/list/outside_areas = list()
- var/list/eligible_areas = list()
- for(var/z in impacted_z_levels)
- if("[z]" in GLOB.space_manager.areas_in_z)
- eligible_areas += GLOB.space_manager.areas_in_z["[z]"]
-
- // Don't play storm audio to shuttles that are not at lavaland
- var/miningShuttleDocked = is_shuttle_docked("mining", "mining_away")
- if(!miningShuttleDocked)
- eligible_areas -= get_areas(/area/shuttle/mining)
-
- var/laborShuttleDocked = is_shuttle_docked("laborcamp", "laborcamp_away")
- if(!laborShuttleDocked)
- eligible_areas -= get_areas(/area/shuttle/siberia)
-
- var/golemShuttleOnPlanet = is_shuttle_docked("freegolem", "freegolem_lavaland")
- if(!golemShuttleOnPlanet)
- eligible_areas -= get_areas(/area/shuttle/freegolem)
-
- while(length(eligible_areas))
- var/area/place = eligible_areas[length(eligible_areas)]
- if(place.outdoors)
- outside_areas += place
- else
- inside_areas += place
- eligible_areas.len--
-
- sound_ao.output_atoms = outside_areas
- sound_ai.output_atoms = inside_areas
- sound_wo.output_atoms = outside_areas
- sound_wi.output_atoms = inside_areas
-
-/datum/weather/ash_storm/proc/update_audio()
- switch(stage)
- if(WEATHER_STARTUP_STAGE)
- sound_wo.start()
- sound_wi.start()
-
- if(WEATHER_MAIN_STAGE)
- sound_wo.stop()
- sound_wi.stop()
-
- sound_ao.start()
- sound_ai.start()
-
- if(WEATHER_WIND_DOWN_STAGE)
- sound_ao.stop()
- sound_ai.stop()
-
- sound_wo.start()
- sound_wi.start()
-
- if(WEATHER_END_STAGE)
- sound_wo.stop()
- sound_wi.stop()
-
-/datum/weather/ash_storm/generate_area_list()
- . = ..()
- // I'm a lazy fuck and it's either this or refactor procgen again
- impacted_areas |= get_areas(/area/lavaland/surface/gulag_rock)
-
-/datum/weather/ash_storm/telegraph()
- . = ..()
- update_eligible_areas()
- update_audio()
-
-/datum/weather/ash_storm/start()
- . = ..()
- update_audio()
-
-/datum/weather/ash_storm/wind_down()
- . = ..()
- update_audio()
-
-/datum/weather/ash_storm/end()
- . = ..()
- update_audio()
-
-/datum/weather/ash_storm/proc/is_ash_immune(atom/L)
- while(L && !isturf(L))
- if(ismecha(L)) //Mechs are immune
- return TRUE
- if(ishuman(L)) //Are you immune?
- var/mob/living/carbon/human/H = L
- var/thermal_protection = H.get_thermal_protection()
- if(thermal_protection >= FIRE_IMMUNITY_MAX_TEMP_PROTECT)
- return TRUE
- L = L.loc //Matryoshka check
- return FALSE //RIP you
-
-/datum/weather/ash_storm/weather_act(mob/living/L)
- if(is_ash_immune(L))
- return
- L.adjustFireLoss(4)
-
-
-//Emberfalls are the result of an ash storm passing by close to the playable area of lavaland. They have a 10% chance to trigger in place of an ash storm.
-/datum/weather/ash_storm/emberfall
- name = "emberfall"
- desc = "A passing ash storm blankets the area in harmless embers."
-
- weather_message = "Gentle embers waft down around you like grotesque snow. The storm seems to have passed you by..."
- weather_overlay = "light_ash"
-
- end_message = "The emberfall slows, stops. Another layer of hardened soot to the basalt beneath your feet."
- end_sound = null
-
- aesthetic = TRUE
-
- probability = 10
diff --git a/code/datums/weather/weather_types/floor_is_lava.dm b/code/datums/weather/weather_types/floor_is_lava.dm
index 6cb2202593f..d0511f9faef 100644
--- a/code/datums/weather/weather_types/floor_is_lava.dm
+++ b/code/datums/weather/weather_types/floor_is_lava.dm
@@ -14,7 +14,7 @@
end_message = "The ground cools and returns to its usual form."
end_duration = 0
- area_type = /area
+ area_types = list(/area)
protected_areas = list(/area/space)
target_trait = STATION_LEVEL
diff --git a/code/datums/weather/weather_types/lavaland_weather.dm b/code/datums/weather/weather_types/lavaland_weather.dm
new file mode 100644
index 00000000000..61aab08f1c9
--- /dev/null
+++ b/code/datums/weather/weather_types/lavaland_weather.dm
@@ -0,0 +1,470 @@
+#define ROCKFALL_DELAY 10 // For how long until the boulders hit the ground
+
+/// MARK: Ash Storm
+//Ash storms happen frequently on lavaland. They heavily obscure vision, and cause high fire damage to anyone caught outside.
+/datum/weather/ash_storm
+ name = "Ash storm"
+ desc = "An intense atmospheric storm lifts ash off of the planet's surface and billows it down across the area, dealing intense fire damage to the unprotected."
+
+ telegraph_message = "An eerie moan rises on the wind. Sheets of burning ash blacken the horizon. Seek shelter."
+ telegraph_duration = 300
+ telegraph_overlay = "light_ash"
+
+ weather_message = "Smoldering clouds of scorching ash billow down around you! Get inside!"
+ weather_duration_lower = 600
+ weather_duration_upper = 1200
+ weather_overlay = "ash_storm"
+
+ end_message = "The shrieking wind whips away the last of the ash and falls to its usual murmur. It should be safe to go outside now."
+ end_duration = 300
+ end_overlay = "light_ash"
+
+ area_types = list(/area/lavaland/surface/outdoors, /area/lavaland/surface/gulag_rock)
+ target_trait = ORE_LEVEL
+ immunity_type = "ash"
+ probability = 25
+ barometer_predictable = TRUE
+
+ var/datum/looping_sound/active_outside_ashstorm/sound_ao = new(list(), FALSE, TRUE)
+ var/datum/looping_sound/active_inside_ashstorm/sound_ai = new(list(), FALSE, TRUE)
+ var/datum/looping_sound/weak_outside_ashstorm/sound_wo = new(list(), FALSE, TRUE)
+ var/datum/looping_sound/weak_inside_ashstorm/sound_wi = new(list(), FALSE, TRUE)
+
+/datum/weather/ash_storm/update_eligible_areas()
+ . = ..()
+ sound_ao.output_atoms = outside_areas
+ sound_ai.output_atoms = inside_areas
+ sound_wo.output_atoms = outside_areas
+ sound_wi.output_atoms = inside_areas
+
+/datum/weather/ash_storm/update_audio()
+ switch(stage)
+ if(WEATHER_STARTUP_STAGE)
+ sound_wo.start()
+ sound_wi.start()
+
+ if(WEATHER_MAIN_STAGE)
+ sound_wo.stop()
+ sound_wi.stop()
+
+ sound_ao.start()
+ sound_ai.start()
+
+ if(WEATHER_WIND_DOWN_STAGE)
+ sound_ao.stop()
+ sound_ai.stop()
+
+ sound_wo.start()
+ sound_wi.start()
+
+ if(WEATHER_END_STAGE)
+ sound_wo.stop()
+ sound_wi.stop()
+
+/datum/weather/ash_storm/on_shelter_placed(datum/source, turf/center)
+ . = ..()
+ if(center.z in impacted_z_levels)
+ var/area/A = get_area(center)
+ inside_areas |= A
+ sound_ai.output_atoms |= A
+ sound_wi.output_atoms |= A
+
+/datum/weather/ash_storm/proc/is_ash_immune(atom/L)
+ while(L && !isturf(L))
+ if(ismecha(L)) //Mechs are immune
+ return TRUE
+ if(ishuman(L)) //Are you immune?
+ if(is_human_ash_immune(L))
+ return TRUE
+ L = L.loc //Matryoshka check
+ return FALSE //RIP you
+
+/datum/weather/ash_storm/proc/is_human_ash_immune(mob/living/carbon/human/H)
+ var/thermal_protection = H.get_thermal_protection()
+ if(thermal_protection >= (FIRE_IMMUNITY_MAX_TEMP_PROTECT - 15))
+ return TRUE
+
+/datum/weather/ash_storm/weather_act(mob/living/L)
+ if(is_ash_immune(L))
+ return
+ L.adjustFireLoss(4)
+
+
+/// MARK: Heavy Ash Storm
+// Radar needed to detect the difference, but shouldnt matter much
+/datum/weather/ash_storm/heavy
+ name = "Heavy ash storm"
+ desc = "An even more intense atmospheric storm lifts ash off of the planet's surface and billows it down across the area, dealing intense fire damage to the unprotected."
+
+ //lasts longer
+ weather_duration_lower = 800
+ weather_duration_upper = 1600
+
+ probability = 10
+
+/datum/weather/ash_storm/heavy/is_human_ash_immune(mob/living/carbon/human/H)
+ var/thermal_protection = H.get_thermal_protection()
+ if(thermal_protection >= FIRE_IMMUNITY_MAX_TEMP_PROTECT)
+ return TRUE
+
+/datum/weather/ash_storm/heavy/weather_act(mob/living/L)
+ if(is_ash_immune(L))
+ return
+ L.adjustFireLoss(6) // does more damage
+
+/// MARK: Emberfall
+//Emberfalls are the result of an ash storm passing by close to the playable area of lavaland. They have a 10% chance to trigger in place of an ash storm.
+/datum/weather/ash_storm/emberfall
+ name = "Emberfall"
+ desc = "A passing ash storm blankets the area in harmless embers."
+
+ weather_message = "Gentle embers waft down around you like grotesque snow. The storm seems to have passed you by..."
+ weather_overlay = "light_ash"
+
+ end_message = "The emberfall slows, stops. Another layer of hardened soot to the basalt beneath your feet."
+ end_sound = null
+
+ aesthetic = TRUE
+ probability = 25
+
+/// MARK: Volcano
+/datum/weather/volcano
+ name = "Volcanic activity"
+ desc = "The shifting tectonic forces on the unstable planet have caused volcanic activity in the area. New rivers/chasms will form and chunks of rock will rain from the sky."
+
+ telegraph_message = "The ground rumbles with an ominous strength, threatening to shift below you. Seek shelter."
+ telegraph_duration = 600
+ telegraph_sound = 'sound/weather/volcano/lavaland_volcano_warning.ogg'
+
+ weather_message = "A massive plume of smoke and magma can be seen billowing in the distance. The ground quakes and threatens to split. Find shelter now!."
+ weather_duration_lower = 600
+ weather_duration_upper = 1200
+ weather_sound = 'sound/weather/volcano/lavaland_volcano_eruption.ogg'
+
+ // No end message. Radar will give you the safety message on this one, or your instincts
+ end_duration = 300
+ end_message = ""
+
+ area_types = list(/area/lavaland/surface/outdoors, /area/lavaland/surface/gulag_rock)
+ target_trait = ORE_LEVEL
+ probability = 5
+ barometer_predictable = TRUE
+ area_act = TRUE
+ // The time until the next rock falls from the sky
+ var/next_rubble
+ // Has a river already been generated this storm?
+ var/generated_river = FALSE
+ // What tiles can we hit with a rock?
+ var/list/valid_targets = list()
+
+/datum/weather/volcano/New()
+ next_rubble = world.time + 10 SECONDS // Short delay
+ . = ..()
+
+/datum/weather/volcano/area_act()
+ if(prob(1) && !generated_river)
+ generated_river = TRUE
+ var/datum/river_spawner/new_river = new /datum/river_spawner(pick(levels_by_trait(ORE_LEVEL)))
+ new_river.generate(nodes = 4, ignore_bridges = TRUE, warning = TRUE)
+ if(world.time >= next_rubble)
+ next_rubble = world.time + rand(3 DECISECONDS, 2 SECONDS)
+ var/hits = 0
+ var/target
+ for(var/turf/T in get_area_turfs(/area/lavaland/surface/outdoors))
+ if(istype(T, /turf/simulated/floor/)) // dont waste our time hitting walls
+ valid_targets += T
+ while(hits <= 150) //sling a bunch of rocks around the map
+ if(!valid_targets) // god forbid we run out of spots to sling rocks
+ break
+ target = pick(valid_targets)
+ new /obj/effect/temp_visual/rock_target(target)
+ hits++
+ valid_targets -= target
+ //just play the play from the last hit in the bundle, it reaches across the map anyways
+ INVOKE_ASYNC(src, PROC_REF(generate_boom), target)
+
+//lets just fake some booms
+/datum/weather/volcano/proc/generate_boom(turf/target)
+ sleep(ROCKFALL_DELAY)
+ for(var/mob/M in GLOB.player_list)
+ var/turf/M_turf = get_turf(M)
+ if(M_turf.z == target.z)
+ M.playsound_local(target, 'sound/effects/explosionfar.ogg', 50, 1, get_rand_frequency(), distance_multiplier = 0)
+ shake_camera(M, 2, 4)
+
+/obj/effect/temp_visual/rockfall
+ icon = 'icons/obj/meteor.dmi'
+ icon_state = "clownish" // we shouldnt be seeing this
+ name = "Ejected boulder"
+ desc = "Get out of the way!"
+ layer = FLY_LAYER
+ randomdir = FALSE
+ duration = ROCKFALL_DELAY
+ pixel_z = 270
+
+/obj/effect/temp_visual/rockfall/Initialize(mapload)
+ . = ..()
+ icon_state = pick("small","large", "sharp", "dust", "small1", "large1")
+ animate(src, pixel_z = 0, time = duration)
+ SpinAnimation()
+
+/obj/effect/temp_visual/rock_target
+ icon = 'icons/mob/actions/actions.dmi'
+ icon_state = "sniper_zoom"
+ layer = BELOW_MOB_LAYER
+ light_range = 2
+ duration = ROCKFALL_DELAY
+
+/obj/effect/temp_visual/rock_target/ex_act()
+ return
+
+/obj/effect/temp_visual/rock_target/Initialize(mapload)
+ . = ..()
+ SpinAnimation()
+ INVOKE_ASYNC(src, PROC_REF(fall))
+
+/obj/effect/temp_visual/rock_target/proc/fall()
+ var/turf/T = get_turf(src)
+ playsound(T,'sound/magic/fleshtostone.ogg', 100, TRUE)
+ new /obj/effect/temp_visual/rockfall(T)
+ sleep(duration)
+ T = get_turf(src)
+ var/turf_area = get_area(T)
+ if(ispath(turf_area, /area/shuttle)) //prevent hitting the shuttle when it moves
+ log_debug("we hit a shuttle area. breaking rock")
+ qdel(src)
+ return
+ playsound(T, 'sound/effects/break_stone.ogg', 80, TRUE)
+ playsound(T, get_sfx("explosion"), 80, TRUE)
+ for(var/mob/living/carbon/human/H in range(10, T))
+ shake_camera(H, 3, 8)
+ if(T.contents)
+ for(var/mob/living/L in T.contents) // dont want to be crushing the hazards
+ if(ismegafauna(L))
+ L.visible_message("[L.name] easily withstands the hit of the massive rock!")
+ return
+ else
+ L.visible_message("[L.name] is crushed under the massive impact of the boulder!", "You are crushed as a massive weight suddenly descends upon you!", "You hear wet splatters as something is hit with a massive object!")
+ L.gib()
+ if(!islava(T) && !istype(T, /turf/simulated/floor/chasm)) // Splash harmlessly into the lava pools
+ for(var/obj/structure/thing in T.contents) // dont cover the tendrils
+ if(thing.name == "necropolis tendril")
+ return
+ T.ChangeTurf(/turf/simulated/mineral/random/high_chance/volcanic)
+
+
+
+
+/// MARK: Acid Rain
+/datum/weather/acid
+ name = "Acidic rain"
+ desc = "Emissions of sulfur and carbon into the atmosphere results in the formation of acid particulate in the ashen clouds. Eventually, enough collects that it will fall back down as sulfuric acid rain. NT brand shelter pods capsules are not rated for this level of acid."
+
+ telegraph_message = "The sound of tiny drops begins to splatter against the ground, sizzling against the ash and stone. Seek shelter."
+ telegraph_duration = 600
+ telegraph_overlay = "light_ash"
+
+ weather_message = "Acidic rain begins to pour down in thick sheets, melting rock and flesh alike. Get inside now!"
+ weather_duration_lower = 600
+ weather_duration_upper = 1200
+ weather_overlay = "acid_rain"
+
+ end_message = "The pitter of acidic dropples slows to silence. It should be safe to go outside now."
+ end_duration = 300
+ end_overlay = "light_ash"
+
+ area_types = list(/area/lavaland/surface/outdoors, /area/lavaland/surface/gulag_rock)
+ target_trait = ORE_LEVEL
+ probability = 10
+ barometer_predictable = TRUE
+ area_act = TRUE
+ // how long do you get before it melts a hole?
+ var/melt_delay = 5 SECONDS
+
+ var/datum/looping_sound/active_outside_acid/sound_ao = new(list(), FALSE, TRUE)
+ var/datum/looping_sound/active_inside_acid/sound_ai = new(list(), FALSE, TRUE)
+ var/datum/looping_sound/weak_outside_acid/sound_wo = new(list(), FALSE, TRUE)
+ var/datum/looping_sound/weak_inside_acid/sound_wi = new(list(), FALSE, TRUE)
+
+/datum/weather/acid/update_eligible_areas()
+ . = ..()
+ sound_ao.output_atoms = outside_areas
+ sound_ai.output_atoms = inside_areas
+ sound_wo.output_atoms = outside_areas
+ sound_wi.output_atoms = inside_areas
+
+/datum/weather/acid/update_audio()
+
+ switch(stage)
+ if(WEATHER_STARTUP_STAGE)
+ sound_wo.start()
+ sound_wi.start()
+
+ if(WEATHER_MAIN_STAGE)
+ sound_wo.stop()
+ sound_wi.stop()
+
+ sound_ao.start()
+ sound_ai.start()
+
+ if(WEATHER_WIND_DOWN_STAGE)
+ sound_ao.stop()
+ sound_ai.stop()
+
+ sound_wo.start()
+ sound_wi.start()
+
+ if(WEATHER_END_STAGE)
+ sound_wo.stop()
+ sound_wi.stop()
+
+/datum/weather/acid/area_act()
+ if(!length(GLOB.all_shelter_pods)) // don't need to act if there's nothing in the list so we don't runtime
+ return
+ for(var/turf/current_turf in GLOB.all_shelter_pods)
+ if(!is_mining_level(current_turf.z)) // dont want to melt pods on other Z levels
+ continue
+ if(istype(get_area(current_turf), /area/survivalpod/luxurypod)) // luxury pods are immune to the storm
+ continue
+ if(prob(2))
+ current_turf.visible_message("The ceiling begins to drip as acid starts eating holes in the roof!", "You hear droplets hitting the floor as acid leaks in through the roof.")
+ addtimer(CALLBACK(src, PROC_REF(melt_pod), current_turf), melt_delay)
+
+/datum/weather/acid/on_shelter_placed(datum/source, turf/center)
+ . = ..()
+ if(center.z in impacted_z_levels)
+ var/area/A = get_area(center)
+ inside_areas |= A
+ sound_ai.output_atoms |= A
+ sound_wi.output_atoms |= A
+
+// lets make some holes!
+/datum/weather/acid/proc/melt_pod(turf/melt_this)
+ var/area/new_area = GLOB.all_unique_areas[/area/lavaland/surface/outdoors] || new /area/lavaland/surface/outdoors
+ for(var/turf/simulated/nearby_turf in RANGE_TURFS(3, melt_this)) // danger, but probably wont make the whole pod unusable unless you're VERY unlucky
+ var/area/turf_area = get_area(nearby_turf)
+ if(prob(70) && turf_area.type == /area/survivalpod)
+ new_area.contents.Add(nearby_turf)
+ if(isfloorturf(nearby_turf))
+ nearby_turf.break_tile()
+ if(iswallturf(nearby_turf))
+ nearby_turf.dismantle_wall()
+ for(var/obj/machinery/sleeper/survival_pod/pod in nearby_turf.contents)
+ pod.visible_message("The sleeper melts away into a useless heap of junk!", "You hear something nearby collapse from the acidic rain!")
+ pod.Destroy()
+
+ impacted_areas.Cut()
+ generate_area_list()
+ update_areas()
+ update_eligible_areas()
+
+/datum/weather/acid/weather_act(atom/L)
+ while(L && !isturf(L))
+ if(ismecha(L)) //Mechs are immune
+ return
+ if(!ishuman(L) || isgrey(L)) // greys and natural fauna shouldnt be affected by acid rain
+ return
+ var/mob/living/carbon/human/H = L
+ if(!H.wear_suit || !H.head) // No need to check further if they dont have clothing on
+ H.adjustFireLoss(2)
+ H.adjustBruteLoss(2)
+ return
+ if(!(H.head.resistance_flags & ACID_PROOF) && !(H.wear_suit.resistance_flags & ACID_PROOF))
+ H.adjustFireLoss(2)
+ H.adjustBruteLoss(2)
+ L = L.loc //Matryoshka check
+
+/// MARK: Wind Storm
+/datum/weather/wind
+ name = "High-velocity wind"
+ desc = "High-pressure barometrics in the area have caused a radical change in air pressure, resulting in high-speed winds in the immediate vicinity."
+
+ telegraph_message = "The wind begins to pick up, whipping against your body with an ominous intensity. Seek shelter."
+ telegraph_duration = 600
+ telegraph_overlay = "light_ash"
+
+ weather_message = "The wind rises into a furious rage, kicking up dirt and ash in its wake. Get inside now!"
+ weather_duration_lower = 600
+ weather_duration_upper = 1200
+ weather_overlay = "wind"
+
+ end_message = "The wind calms into its normal rhythms, dust settling back to the ashen surface. It should be safe to go outside now."
+ end_duration = 300
+ end_overlay = "light_ash"
+
+ area_types = list(/area/lavaland/surface/outdoors, /area/lavaland/surface/gulag_rock)
+ target_trait = ORE_LEVEL
+ probability = 25
+ barometer_predictable = TRUE
+ var/wind_dir
+ var/next_dir_change
+
+ var/datum/looping_sound/active_outside_ashstorm/sound_ao = new(list(), FALSE, TRUE)
+ var/datum/looping_sound/active_inside_ashstorm/sound_ai = new(list(), FALSE, TRUE)
+ var/datum/looping_sound/weak_outside_ashstorm/sound_wo = new(list(), FALSE, TRUE)
+ var/datum/looping_sound/weak_inside_ashstorm/sound_wi = new(list(), FALSE, TRUE)
+
+/datum/weather/wind/update_eligible_areas()
+ . = ..()
+ sound_ao.output_atoms = outside_areas
+ sound_ai.output_atoms = inside_areas
+ sound_wo.output_atoms = outside_areas
+ sound_wi.output_atoms = inside_areas
+
+/datum/weather/wind/update_audio()
+ switch(stage)
+ if(WEATHER_STARTUP_STAGE)
+ sound_wo.start()
+ sound_wi.start()
+
+ if(WEATHER_MAIN_STAGE)
+ sound_wo.stop()
+ sound_wi.stop()
+
+ sound_ao.start()
+ sound_ai.start()
+
+ if(WEATHER_WIND_DOWN_STAGE)
+ sound_ao.stop()
+ sound_ai.stop()
+
+ sound_wo.start()
+ sound_wi.start()
+
+ if(WEATHER_END_STAGE)
+ sound_wo.stop()
+ sound_wi.stop()
+
+/datum/weather/wind/on_shelter_placed(datum/source, turf/center)
+ . = ..()
+ if(center.z in impacted_z_levels)
+ var/area/A = get_area(center)
+ inside_areas |= A
+ sound_ai.output_atoms |= A
+ sound_wi.output_atoms |= A
+
+/datum/weather/wind/start() //give it our custom overlay
+ custom_overlay = 'icons/effects/tile_effects.dmi'
+ wind_dir = pick(GLOB.alldirs)
+ overlay_dir = wind_dir
+ next_dir_change = world.time + rand(10 SECONDS, 30 SECONDS)
+ . = ..()
+
+/datum/weather/wind/wind_down() // back to normal overlay
+ custom_overlay = null
+ overlay_dir = null
+ . = ..()
+
+/datum/weather/wind/weather_act(mob/living/L)
+ if(next_dir_change <= world.time)
+ next_dir_change = world.time + rand(10 SECONDS, 30 SECONDS)
+ wind_dir = pick(GLOB.alldirs)
+ overlay_dir = wind_dir
+ update_areas()
+ if(ismecha(L)) //Mechs are immune
+ return
+ if(ishuman(L)) // lets not push around lavaland mobs
+ L.air_push(wind_dir, MOVE_FORCE_NORMAL * 2)
+
+#undef ROCKFALL_DELAY
+
diff --git a/code/datums/weather/weather_types/radiation_storm.dm b/code/datums/weather/weather_types/radiation_storm.dm
index 442e5950c44..9437aec9161 100644
--- a/code/datums/weather/weather_types/radiation_storm.dm
+++ b/code/datums/weather/weather_types/radiation_storm.dm
@@ -16,7 +16,7 @@
end_duration = 100
end_message = "The air seems to be cooling off again."
var/pre_maint_all_access
- area_type = /area
+ area_types = list(/area)
protected_areas = list(
/area/station/maintenance,
/area/station/turret_protected/ai_upload,
diff --git a/code/datums/weather/weather_types/solar_flare.dm b/code/datums/weather/weather_types/solar_flare.dm
index eb0834dc246..3982c409035 100644
--- a/code/datums/weather/weather_types/solar_flare.dm
+++ b/code/datums/weather/weather_types/solar_flare.dm
@@ -14,7 +14,7 @@
end_duration = 10 // wind_down() does not do anything for this event, so we just trigger end() semi-immediately
end_message = null
- area_type = /area // read generate_area_list() as well below
+ area_types = list(/area) // read generate_area_list() as well below
protected_areas = list(/area/shuttle/arrival/station)
target_trait = REACHABLE_SPACE_ONLY
immunity_type = "burn"
diff --git a/code/game/machinery/radar.dm b/code/game/machinery/radar.dm
new file mode 100644
index 00000000000..2e0273b09cb
--- /dev/null
+++ b/code/game/machinery/radar.dm
@@ -0,0 +1,256 @@
+#define RADAR_NEEDS_WELDING 0
+#define RADAR_NEEDS_PLASTEEL 1
+#define RADAR_NEEDS_WRENCH 2
+#define RADAR_NEEDS_SCREWDRIVER 3
+
+/obj/machinery/radar
+ name = "Doppler Radar"
+ desc = "An age-tested technology for measuring and predicting weather patterns. This one has been loaded with information of the local expected conditions."
+ icon = 'icons/obj/machines/doppler_tower.dmi'
+ icon_state = "base"
+ density = TRUE
+ anchored = TRUE
+ layer = ABOVE_ALL_MOB_LAYER
+ power_state = NO_POWER_USE // going to be used outside
+ interact_offline = TRUE
+ idle_power_consumption = 0
+ pixel_x = -32
+ armor = list(MELEE = 80, BULLET = 10, LASER = 30, ENERGY = 30, BOMB = 50, RAD = 0, FIRE = 100, ACID = 100)
+ resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
+ /// Time between callouts
+ var/check_time
+ /// Internal radio to handle announcements over supply
+ var/obj/item/radio/radio
+ /// For checking what was the last weather stage an alert was sent out
+ var/last_stage
+ /// How accurate are we based off parts?
+ var/accuracy_coeff
+ /// Base Inaccuracy
+ var/inaccuracy = 5 MINUTES
+ /// What is our current prediction?
+ var/prediction
+ /// Don't want constant updates
+ var/dont_announce = TRUE
+ /// For fakeout calls
+ var/correct_prediction
+ /// Are we broken?
+ var/construction_state = RADAR_NEEDS_WELDING
+ luminosity = 1
+
+/obj/machinery/radar/Initialize(mapload)
+ . = ..()
+ component_parts = list()
+ component_parts += new /obj/item/circuitboard/machine/radar(null)
+ component_parts += new /obj/item/stock_parts/scanning_module(null)
+ component_parts += new /obj/item/stock_parts/scanning_module(null)
+ component_parts += new /obj/item/stock_parts/scanning_module(null)
+ component_parts += new /obj/item/stock_parts/scanning_module(null)
+ component_parts += new /obj/item/stack/cable_coil(null, 10)
+ RefreshParts()
+ set_light(5, 2, "#e2db98") // so the machine isnt pitch black
+
+ AddComponent(/datum/component/multitile, list(
+ list(1, 1),
+ list(1, MACH_CENTER),
+ ))
+
+ radio = new(src)
+ radio.listening = FALSE
+ radio.follow_target = src
+ radio.config(list("Supply" = 0))
+
+ AddComponent(/datum/component/largetransparency, -1, 2, 0, 1)
+ update_appearance(UPDATE_OVERLAYS)
+
+/obj/machinery/radar/update_overlays()
+ . = ..()
+ if(stat & BROKEN)
+ icon_state = "broken"
+ else
+ icon_state = "base"
+ . += "chicken"
+ . += "screen"
+
+/obj/machinery/radar/process()
+ if(stat & BROKEN)
+ return
+ if(check_time > world.time)
+ return
+
+ for(var/datum/weather/W in SSweather.processing)
+ if(!W)
+ break
+ if(W.barometer_predictable && is_path_in_list(/area/lavaland/surface/outdoors, W.area_types))
+ switch(W.stage)
+ if(WEATHER_STARTUP_STAGE)
+ if(last_stage == WEATHER_STARTUP_STAGE)
+ return
+ if(W.aesthetic)
+ if(correct_prediction) // unupgraded machines should still scare the poor bastards
+ radio.autosay("[W.name] detected settling over the sector. No further action required.", name, "Supply")
+ else
+ radio.autosay("Ash Storm detected converging over the local sector. Please finish any surface excavations.", name, "Supply")
+ else
+ radio.autosay("[W.name] detected converging over the local sector. Please finish any surface excavations.", name, "Supply")
+ last_stage = WEATHER_STARTUP_STAGE
+ check_time = world.time + W.telegraph_duration + 5 SECONDS
+ return
+ if(WEATHER_MAIN_STAGE)
+ if(last_stage == WEATHER_MAIN_STAGE)
+ return
+ radio.autosay("Inclement weather has reached the local sector. Seek shelter immediately.", name, "Supply")
+ last_stage = WEATHER_MAIN_STAGE
+ check_time = world.time + (W.weather_duration / 2)
+ return
+ if(WEATHER_WIND_DOWN_STAGE)
+ if(last_stage == WEATHER_WIND_DOWN_STAGE)
+ return
+ radio.autosay("Inclement weather has dispersed. It is now safe to resume surface excavations.", name, "Supply")
+ last_stage = WEATHER_WIND_DOWN_STAGE
+ dont_announce = FALSE
+ return
+
+ if(dont_announce)
+ return
+
+ var/datum/weather/next_weather = SSweather.next_weather_by_zlevel["3"]
+ var/next_hit = SSweather.next_hit_by_zlevel["3"]
+ var/next_difference = next_hit - world.time
+ var/difference_rounded = DisplayTimeText(max(1, next_difference))
+
+ if(next_weather == "0" || next_weather == null)
+ return
+ if(accuracy_coeff >= 4) //perfect accuracy
+ if(next_difference <= (3 MINUTES))
+ radio.autosay("Weather patterns successfully analyzed. Predicted weather event in [difference_rounded]: [next_weather.name] ", name, "Supply")
+ dont_announce = TRUE
+ correct_prediction = TRUE
+ else if(prob(accuracy_coeff) && next_difference <= 3 MINUTES && next_difference >= 30 SECONDS)
+ if(next_weather == "emberfall" && !prob(10 * accuracy_coeff)) // fake callout
+ radio.autosay("Weather patterns successfully analyzed. Predicted weather event in [difference_rounded]: ash storm ", name, "Supply")
+ dont_announce = TRUE
+ correct_prediction = FALSE
+ else
+ radio.autosay("Weather patterns successfully analyzed. Predicted weather event in [difference_rounded]: [next_weather.name] ", name, "Supply")
+ dont_announce = TRUE
+ correct_prediction = TRUE
+
+/obj/machinery/radar/examine(mob/user)
+ . = ..()
+ if(!(stat & BROKEN))
+ return
+
+ switch(construction_state)
+ if(RADAR_NEEDS_WELDING)
+ . += "The framework is damaged, and needs welding."
+ if(RADAR_NEEDS_PLASTEEL)
+ . += "The framework needs new plasteel plating."
+ if(RADAR_NEEDS_WRENCH)
+ . += "The plating needs wrenching into place."
+ if(RADAR_NEEDS_SCREWDRIVER)
+ . += "The cover screws are loose."
+
+// Interaction
+
+// REPAIRS //
+// Step 1
+// (stolen from gav gen teehee)
+/obj/machinery/radar/welder_act(mob/user, obj/item/I)
+ if(construction_state != RADAR_NEEDS_WELDING)
+ return
+ . = TRUE
+ if(!I.use_tool(src, user, null, 1, I.tool_volume))
+ return
+ to_chat(user, "You mend the damaged framework.")
+ construction_state = RADAR_NEEDS_PLASTEEL
+
+// Step 2
+/obj/machinery/radar/item_interaction(mob/living/user, obj/item/used, list/modifiers)
+ if(construction_state != RADAR_NEEDS_PLASTEEL)
+ return ..()
+
+ if(istype(used, /obj/item/stack/sheet/plasteel))
+ var/obj/item/stack/sheet/plasteel/PS = used
+ if(PS.amount < 10)
+ to_chat(user, "You need 10 sheets of plasteel.")
+ return ITEM_INTERACT_COMPLETE
+
+ to_chat(user, "You add new plating to the framework.")
+ construction_state = RADAR_NEEDS_WRENCH
+ update_icon()
+ return ITEM_INTERACT_COMPLETE
+
+ return ..()
+
+// Step 3
+/obj/machinery/radar/wrench_act(mob/living/user, obj/item/I)
+ if(construction_state != RADAR_NEEDS_WRENCH)
+ return
+ . = TRUE
+ if(!I.use_tool(src, user, volume = I.tool_volume))
+ return
+ to_chat(user, "You secure the plating to the framework.")
+ construction_state = RADAR_NEEDS_SCREWDRIVER
+
+
+// Step 4
+/obj/machinery/radar/screwdriver_act(mob/living/user, obj/item/I)
+ if(!(stat & BROKEN)) // Not actually broken
+ return
+ if(construction_state != RADAR_NEEDS_SCREWDRIVER)
+ return
+ . = TRUE
+ if(!I.use_tool(src, user, volume = I.tool_volume))
+ return
+ to_chat(user, "You screw the covers back into place.")
+ set_fixed()
+
+/obj/machinery/radar/RefreshParts()
+ accuracy_coeff = 0
+ for(var/obj/item/stock_parts/scanning_module/C in component_parts)
+ accuracy_coeff += C.rating
+ accuracy_coeff = (accuracy_coeff / 4) //average all the parts
+
+// We dont want them to rebuild this in random places. but if it does somehow delete, we should log it
+/obj/machinery/radar/Destroy()
+ investigate_log("was destroyed!", "radar")
+ qdel(radio)
+ component_parts -= /obj/item/circuitboard/machine/radar // dont let them get the board
+ new /obj/item/circuitboard/machine/radar/broken(src)
+ . = ..()
+
+/obj/machinery/radar/ex_act(severity)
+ if(severity == EXPLODE_DEVASTATE) // Very sturdy.
+ set_broken()
+
+/obj/machinery/radar/proc/set_broken()
+ stat |= BROKEN
+ investigate_log("has broken down.", "radar")
+ update_icon()
+
+/obj/machinery/radar/proc/set_fixed()
+ stat &= ~BROKEN
+ construction_state = initial(construction_state)
+ update_icon()
+
+/obj/item/circuitboard/machine/radar
+ board_name = "Doppler Radar"
+ icon_state = "supply"
+ build_path = /obj/machinery/radar
+ board_type = "machine"
+ origin_tech = "engineering=2"
+ req_components = list(
+ /obj/item/stack/cable_coil = 5,
+ /obj/item/stack/sheet/glass = 10,
+ /obj/item/stock_parts/scanning_module = 4,
+ )
+
+/obj/item/circuitboard/machine/radar/broken
+ board_name = "Doppler Radar"
+ desc = "Bits of char, plastic, and ash cling to the boards surface. How it was working before was nothing short of a miracle. It's probably not going to work again."
+ icon_state = "command_broken"
+
+#undef RADAR_NEEDS_WELDING
+#undef RADAR_NEEDS_PLASTEEL
+#undef RADAR_NEEDS_WRENCH
+#undef RADAR_NEEDS_SCREWDRIVER
diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index 3bbf948cb05..6b0267a08b3 100644
--- a/code/game/objects/items/devices/scanners.dm
+++ b/code/game/objects/items/devices/scanners.dm
@@ -653,7 +653,7 @@ SLIME SCANNER
for(var/V in SSweather.processing)
var/datum/weather/W = V
- if(W.barometer_predictable && (T.z in W.impacted_z_levels) && W.area_type == user_area.type && !(W.stage == WEATHER_END_STAGE))
+ if(W.barometer_predictable && (T.z in W.impacted_z_levels) && is_type_in_list(user_area, W.area_types) && !(W.stage == WEATHER_END_STAGE))
ongoing_weather = W
break
diff --git a/code/game/objects/structures/lavaland/necropolis_tendril.dm b/code/game/objects/structures/lavaland/necropolis_tendril.dm
index 8550806b072..dfe5dc3f78e 100644
--- a/code/game/objects/structures/lavaland/necropolis_tendril.dm
+++ b/code/game/objects/structures/lavaland/necropolis_tendril.dm
@@ -36,6 +36,8 @@ GLOBAL_LIST_EMPTY(tendrils)
if(ismineralturf(F))
var/turf/simulated/mineral/M = F
M.ChangeTurf(M.turf_type, FALSE, FALSE, TRUE)
+ var/turf/no_lava = F
+ no_lava.flags |= NO_LAVA_GEN
/obj/structure/spawner/lavaland/deconstruct(disassembled)
new /obj/effect/collapse(loc)
diff --git a/code/game/turfs/simulated/floor/plating.dm b/code/game/turfs/simulated/floor/plating.dm
index 8538d568f2f..0f088383be4 100644
--- a/code/game/turfs/simulated/floor/plating.dm
+++ b/code/game/turfs/simulated/floor/plating.dm
@@ -317,6 +317,13 @@
toxins = 10
atmos_mode = ATMOS_MODE_NO_DECAY
+/turf/simulated/floor/engine/lavaland_air
+ oxygen = LAVALAND_OXYGEN
+ nitrogen = LAVALAND_NITROGEN
+ temperature = LAVALAND_TEMPERATURE
+ atmos_mode = ATMOS_MODE_EXPOSED_TO_ENVIRONMENT
+ atmos_environment = ENVIRONMENT_LAVALAND
+
/turf/simulated/floor/engine/singularity_pull(S, current_size)
if(current_size >= STAGE_FIVE)
if(floor_tile)
diff --git a/code/game/turfs/simulated/river.dm b/code/game/turfs/simulated/river.dm
index 31c1b95d47b..2378579bf5b 100644
--- a/code/game/turfs/simulated/river.dm
+++ b/code/game/turfs/simulated/river.dm
@@ -4,6 +4,10 @@
#define RIVER_MIN_X 50
#define RIVER_MIN_Y 50
+#define WARNING_DELAY (4 SECONDS) // the warning time that a warning will be deisplayed before the lava conversion occurs
+
+#define BRIDGE_PROB 2 // the percent probability of a bridge to spawn for every tile forward the river travels
+
GLOBAL_LIST_EMPTY(river_waypoint_presets)
/obj/effect/landmark/river_waypoint
@@ -30,6 +34,12 @@ GLOBAL_LIST_EMPTY(river_waypoint_presets)
var/whitelist_turf_type = /turf/simulated/mineral
/// The turf used when a spread of the tile stops.
var/shoreline_turf_type = /turf/simulated/floor/plating/asteroid/basalt/lava_land_surface
+ /// To hold all the found turfs to convert to the lava type
+ var/list/collected_turfs = list()
+ /// Does the lava generate a warning beforehand
+ var/warning
+ /// Do we ignore building any bridges?
+ var/ignore_bridges
/datum/river_spawner/New(target_z_, spread_prob_ = 25, spread_prob_loss_ = 11)
target_z = target_z_
@@ -42,7 +52,9 @@ GLOBAL_LIST_EMPTY(river_waypoint_presets)
/// `nodes` is the number of unique points in those bounds the river will
/// connect to. Note that `nodes` says little about the resultant size of the
/// river due to its ability to detour far away from the direct path between them.
-/datum/river_spawner/proc/generate(nodes = 4, min_x = RIVER_MIN_X, min_y = RIVER_MIN_Y, max_x = RIVER_MAX_X, max_y = RIVER_MAX_Y)
+/// set ignore_bridges TRUE to not spawn any new bridges, and set warning to TRUE to
+/// allow for the new generations to have a telegraphed icon first
+/datum/river_spawner/proc/generate(nodes = 4, min_x = RIVER_MIN_X, min_y = RIVER_MIN_Y, max_x = RIVER_MAX_X, max_y = RIVER_MAX_Y, ignore_bridges = FALSE, warning = FALSE)
var/list/river_nodes = list()
var/num_spawned = 0
@@ -72,7 +84,9 @@ GLOBAL_LIST_EMPTY(river_waypoint_presets)
W.connected = TRUE
var/turf/cur_turf = get_turf(W)
if(istype(get_area(cur_turf), whitelist_area_type) && !(cur_turf.flags & NO_LAVA_GEN))
- cur_turf.ChangeTurf(river_turf_type, ignore_air = TRUE)
+ collected_turfs += cur_turf
+ else if(ignore_bridges && (cur_turf.flags & LAVA_BRIDGE))
+ collected_turfs += cur_turf
var/turf/target_turf = get_turf(pick(river_nodes - W))
if(!target_turf)
break
@@ -93,6 +107,7 @@ GLOBAL_LIST_EMPTY(river_waypoint_presets)
cur_dir = turn(cur_dir, -45)
else
cur_dir = get_dir(cur_turf, target_turf)
+
// we may veer off the map entirely, returning a null turf; if so, go back and try again
while(get_step(cur_turf, cur_dir) == null && attempts-- > 0)
@@ -107,15 +122,14 @@ GLOBAL_LIST_EMPTY(river_waypoint_presets)
cur_turf = get_step(cur_turf, cur_dir)
continue
else
- var/turf/river_turf = cur_turf.ChangeTurf(river_turf_type, ignore_air = TRUE)
- if(prob(1))
- new /obj/effect/spawner/dynamic_bridge(river_turf)
- spread_turf(river_turf, spread_prob, spread_prob_loss, whitelist_area_type)
+ collected_turfs += cur_turf
+
+ handle_change(warning, ignore_bridges)
for(var/WP in river_nodes)
qdel(WP)
-/datum/river_spawner/proc/spread_turf(turf/start_turf, probability = 30, prob_loss = 25, whitelisted_area)
+/datum/river_spawner/proc/spread_turf(turf/start_turf, probability = 30, prob_loss = 25, whitelisted_area, ignore_bridges)
if(probability <= 0)
return
var/list/cardinal_turfs = list()
@@ -124,7 +138,9 @@ GLOBAL_LIST_EMPTY(river_waypoint_presets)
for(var/F in RANGE_TURFS(1, start_turf) - start_turf)
var/turf/T = F
var/area/new_area = get_area(T)
- if(!T || (T.density && !istype(T, whitelist_turf_type)) || istype(T, /turf/simulated/floor/indestructible) || (whitelisted_area && !istype(new_area, whitelisted_area)) || (T.flags & NO_LAVA_GEN))
+ if(!ignore_bridges && (T.flags & LAVA_BRIDGE))
+ continue
+ else if(!T || (T.density && !istype(T, whitelist_turf_type)) || istype(T, /turf/simulated/floor/indestructible) || (whitelisted_area && !istype(new_area, whitelisted_area)) || (T.flags & NO_LAVA_GEN))
continue
if(get_dir(start_turf, F) in GLOB.cardinal)
@@ -136,8 +152,6 @@ GLOBAL_LIST_EMPTY(river_waypoint_presets)
var/turf/T = F
if(!istype(T, start_turf.type) && T.ChangeTurf(start_turf.type, ignore_air = TRUE) && prob(probability))
spread_turf(T, probability - prob_loss, prob_loss, whitelisted_area)
- if(prob(1))
- new /obj/effect/spawner/dynamic_bridge(T)
for(var/F in diagonal_turfs) //diagonal turfs only sometimes change, but will always spread if changed
var/turf/T = F
@@ -145,8 +159,40 @@ GLOBAL_LIST_EMPTY(river_waypoint_presets)
spread_turf(T, probability - prob_loss, prob_loss, whitelisted_area)
else if(istype(T, whitelist_turf_type) && !istype(T, start_turf.type))
T.ChangeTurf(shoreline_turf_type, ignore_air = TRUE)
- if(prob(1))
- new /obj/effect/spawner/dynamic_bridge(T)
+
+/// handles changing the lava turfs, and if it should delay it and place warnings
+/datum/river_spawner/proc/handle_change(warning, ignore_bridges)
+ var/lava_counter = 0
+ for(var/turf/listed_turf in collected_turfs)
+ var/affected_turf = get_turf(listed_turf)
+ if(warning)
+ if(lava_counter++ >= 2)
+ new /obj/effect/temp_visual/river_warning(affected_turf)
+ lava_counter = 0
+ addtimer(CALLBACK(src, PROC_REF(convert_turf), affected_turf), WARNING_DELAY)
+ else
+ convert_turf(affected_turf, ignore_bridges)
+
+ collected_turfs.Cut()
+
+/// actually convert the turf
+/datum/river_spawner/proc/convert_turf(turf/cur_turf, ignore_bridges)
+ var/turf/river_turf = cur_turf.ChangeTurf(river_turf_type, ignore_air = TRUE)
+ spread_turf(river_turf, spread_prob, spread_prob_loss, whitelist_area_type, ignore_bridges)
+ if(prob(BRIDGE_PROB) && !ignore_bridges)
+ new /obj/effect/spawner/dynamic_bridge(cur_turf)
+
+/obj/effect/temp_visual/river_warning
+ icon = 'icons/effects/96x96.dmi'
+ icon_state = "warning"
+ layer = BELOW_MOB_LAYER
+ duration = WARNING_DELAY
+ randomdir = FALSE
+ pixel_x = -32
+ pixel_y = -32
+
+#undef WARNING_DELAY
+#undef BRIDGE_PROB
#undef RIVER_MAX_X
#undef RIVER_MAX_Y
diff --git a/code/modules/mining/equipment/survival_pod.dm b/code/modules/mining/equipment/survival_pod.dm
index 9c9d2ca3fb4..5f15ccb3a9f 100644
--- a/code/modules/mining/equipment/survival_pod.dm
+++ b/code/modules/mining/equipment/survival_pod.dm
@@ -73,14 +73,19 @@
log_admin("[key_name(usr)] activated a bluespace capsule away from the mining level at [T.x], [T.y], [T.z]")
template.load(deploy_location, centered = TRUE)
new /obj/effect/particle_effect/smoke(get_turf(src))
+ SEND_GLOBAL_SIGNAL(COMSIG_GLOB_SHELTER_PLACED, T)
qdel(src)
/obj/item/survivalcapsule/luxury
name = "luxury bluespace shelter capsule"
- desc = "An exorbitantly expensive luxury suite stored within a pocket of bluespace."
+ desc = "An exorbitantly expensive luxury suite stored within a pocket of bluespace. It is made of durable materials more capable of withstanding harsh weather over standard capsules."
origin_tech = "engineering=3;bluespace=4"
template_id = "shelter_beta"
+// for things that shouldnt affect specifically the luxury pods
+/area/survivalpod/luxurypod
+ name = "\improper Luxury Shelter"
+
//Pod turfs and objects
//Window
diff --git a/code/modules/mining/lavaland/loot/legion_loot.dm b/code/modules/mining/lavaland/loot/legion_loot.dm
index 4eef64984e4..01b1788c9fc 100644
--- a/code/modules/mining/lavaland/loot/legion_loot.dm
+++ b/code/modules/mining/lavaland/loot/legion_loot.dm
@@ -71,7 +71,7 @@
var/datum/weather/A
for(var/V in SSweather.processing)
var/datum/weather/W = V
- if((user_turf.z in W.impacted_z_levels) && W.area_type == user_area.type)
+ if((user_turf.z in W.impacted_z_levels) && is_type_in_list(user_area, W.area_types))
A = W
break
@@ -119,7 +119,7 @@
var/power_boosted = FALSE
for(var/V in SSweather.processing)
var/datum/weather/W = V
- if((target_turf.z in W.impacted_z_levels) && W.area_type == target_area.type)
+ if((target_turf.z in W.impacted_z_levels) && is_type_in_list(target_area, W.area_types))
power_boosted = TRUE
break
playsound(src, 'sound/magic/lightningshock.ogg', 10, TRUE, extrarange = SILENCED_SOUND_EXTRARANGE, falloff_distance = 0)
diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm
index baf31a94e80..f19fe80adb6 100644
--- a/code/modules/shuttle/shuttle.dm
+++ b/code/modules/shuttle/shuttle.dm
@@ -570,14 +570,13 @@
mobile_port.loc = S1.loc
mobile_port.dir = S1.dir
- //update mining and labor shuttle ash storm audio
+ //update mining and labor shuttle weather audio
if(mobile_port.id in list("mining", "laborcamp"))
for(var/zlvl in levels_by_trait(ORE_LEVEL))
- var/datum/weather/ash_storm/W = SSweather.get_weather(zlvl, /area/lavaland/surface/outdoors)
+ var/datum/weather/W = SSweather.get_weather(zlvl, /area/lavaland/surface/outdoors)
if(W)
W.update_eligible_areas()
W.update_audio()
-
mobile_port.unlockPortDoors(S1)
/obj/docking_port/mobile/proc/is_turf_blacklisted_for_transit(turf/T)
diff --git a/icons/effects/weather_effects.dmi b/icons/effects/weather_effects.dmi
index c084ca04b5b..622153b2f2a 100644
Binary files a/icons/effects/weather_effects.dmi and b/icons/effects/weather_effects.dmi differ
diff --git a/icons/obj/machines/doppler_tower.dmi b/icons/obj/machines/doppler_tower.dmi
new file mode 100644
index 00000000000..1666493cbab
Binary files /dev/null and b/icons/obj/machines/doppler_tower.dmi differ
diff --git a/paradise.dme b/paradise.dme
index 627db15476a..ca8be1fdd19 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -728,8 +728,8 @@
#include "code\datums\uplink_items\uplink_special.dm"
#include "code\datums\uplink_items\uplink_traitor.dm"
#include "code\datums\weather\weather_datum.dm"
-#include "code\datums\weather\weather_types\ash_storm.dm"
#include "code\datums\weather\weather_types\floor_is_lava.dm"
+#include "code\datums\weather\weather_types\lavaland_weather.dm"
#include "code\datums\weather\weather_types\radiation_storm.dm"
#include "code\datums\weather\weather_types\snow_storm.dm"
#include "code\datums\weather\weather_types\solar_flare.dm"
@@ -943,6 +943,7 @@
#include "code\game\machinery\portable_tag_turret.dm"
#include "code\game\machinery\portable_turret.dm"
#include "code\game\machinery\quantum_pad.dm"
+#include "code\game\machinery\radar.dm"
#include "code\game\machinery\recharger.dm"
#include "code\game\machinery\rechargestation.dm"
#include "code\game\machinery\recycler.dm"
diff --git a/sound/weather/acid/inside/active_end.ogg b/sound/weather/acid/inside/active_end.ogg
new file mode 100644
index 00000000000..319a8a314ea
Binary files /dev/null and b/sound/weather/acid/inside/active_end.ogg differ
diff --git a/sound/weather/acid/inside/active_mid1.ogg b/sound/weather/acid/inside/active_mid1.ogg
new file mode 100644
index 00000000000..5f172c534f8
Binary files /dev/null and b/sound/weather/acid/inside/active_mid1.ogg differ
diff --git a/sound/weather/acid/inside/active_start.ogg b/sound/weather/acid/inside/active_start.ogg
new file mode 100644
index 00000000000..680c3a7e58c
Binary files /dev/null and b/sound/weather/acid/inside/active_start.ogg differ
diff --git a/sound/weather/acid/inside/weak_end.ogg b/sound/weather/acid/inside/weak_end.ogg
new file mode 100644
index 00000000000..0d4bf2b5fdb
Binary files /dev/null and b/sound/weather/acid/inside/weak_end.ogg differ
diff --git a/sound/weather/acid/inside/weak_mid1.ogg b/sound/weather/acid/inside/weak_mid1.ogg
new file mode 100644
index 00000000000..75f539aebe4
Binary files /dev/null and b/sound/weather/acid/inside/weak_mid1.ogg differ
diff --git a/sound/weather/acid/inside/weak_start.ogg b/sound/weather/acid/inside/weak_start.ogg
new file mode 100644
index 00000000000..9822442b4f3
Binary files /dev/null and b/sound/weather/acid/inside/weak_start.ogg differ
diff --git a/sound/weather/acid/outside/active_end.ogg b/sound/weather/acid/outside/active_end.ogg
new file mode 100644
index 00000000000..59a57c41341
Binary files /dev/null and b/sound/weather/acid/outside/active_end.ogg differ
diff --git a/sound/weather/acid/outside/active_mid1.ogg b/sound/weather/acid/outside/active_mid1.ogg
new file mode 100644
index 00000000000..68b17ddccf7
Binary files /dev/null and b/sound/weather/acid/outside/active_mid1.ogg differ
diff --git a/sound/weather/acid/outside/active_start.ogg b/sound/weather/acid/outside/active_start.ogg
new file mode 100644
index 00000000000..c67d950250f
Binary files /dev/null and b/sound/weather/acid/outside/active_start.ogg differ
diff --git a/sound/weather/acid/outside/weak_end.ogg b/sound/weather/acid/outside/weak_end.ogg
new file mode 100644
index 00000000000..980d7187544
Binary files /dev/null and b/sound/weather/acid/outside/weak_end.ogg differ
diff --git a/sound/weather/acid/outside/weak_mid1.ogg b/sound/weather/acid/outside/weak_mid1.ogg
new file mode 100644
index 00000000000..a598b712401
Binary files /dev/null and b/sound/weather/acid/outside/weak_mid1.ogg differ
diff --git a/sound/weather/acid/outside/weak_start.ogg b/sound/weather/acid/outside/weak_start.ogg
new file mode 100644
index 00000000000..9424a60c6dc
Binary files /dev/null and b/sound/weather/acid/outside/weak_start.ogg differ
diff --git a/sound/weather/volcano/lavaland_volcano_eruption.ogg b/sound/weather/volcano/lavaland_volcano_eruption.ogg
new file mode 100644
index 00000000000..88ef0d04da0
Binary files /dev/null and b/sound/weather/volcano/lavaland_volcano_eruption.ogg differ
diff --git a/sound/weather/volcano/lavaland_volcano_warning.ogg b/sound/weather/volcano/lavaland_volcano_warning.ogg
new file mode 100644
index 00000000000..f0d4abfc35f
Binary files /dev/null and b/sound/weather/volcano/lavaland_volcano_warning.ogg differ