diff --git a/code/controllers/subsystems/planets.dm b/code/controllers/subsystems/planets.dm
index a163a3b1e7..f7fc49b661 100644
--- a/code/controllers/subsystems/planets.dm
+++ b/code/controllers/subsystems/planets.dm
@@ -57,6 +57,7 @@ SUBSYSTEM_DEF(planets)
else
P.planet_walls -= T
T.vis_contents -= P.weather_holder.visuals
+ T.vis_contents -= P.weather_holder.special_visuals
/datum/controller/subsystem/planets/proc/allocateTurfs(var/initial = FALSE)
var/list/currentlist = new_outdoor_turfs
@@ -67,6 +68,7 @@ SUBSYSTEM_DEF(planets)
var/datum/planet/P = z_to_planet[OT.z]
P.planet_floors |= OT
OT.vis_contents |= P.weather_holder.visuals
+ OT.vis_contents |= P.weather_holder.special_visuals
if(!initial && MC_TICK_CHECK)
return
@@ -85,6 +87,7 @@ SUBSYSTEM_DEF(planets)
var/datum/planet/P = z_to_planet[T.z]
P.planet_floors -= T
T.vis_contents -= P.weather_holder.visuals
+ T.vis_contents -= P.weather_holder.special_visuals
/datum/controller/subsystem/planets/fire(resumed = 0)
diff --git a/code/game/objects/effects/misc.dm b/code/game/objects/effects/misc.dm
index aaebab4355..d7db6f2a0c 100644
--- a/code/game/objects/effects/misc.dm
+++ b/code/game/objects/effects/misc.dm
@@ -46,4 +46,21 @@
/obj/effect/temporary_effect/shuttle_landing/initialize()
flick("shuttle_warning", src) // flick() forces the animation to always begin at the start.
+ . = ..()
+
+// The manifestation of Zeus's might. Or just a really unlucky day.
+// This is purely a visual effect, this isn't the part of the code that hurts things.
+/obj/effect/temporary_effect/lightning_strike
+ name = "lightning"
+ desc = "How shocked you must be, to see this text. You must have lightning reflexes. \
+ The humor in this description is just so electrifying."
+ icon = 'icons/effects/96x256.dmi'
+ icon_state = "lightning_strike"
+ plane = PLANE_LIGHTING_ABOVE
+ time_to_die = 1 SECOND
+ pixel_x = -32
+
+/obj/effect/temporary_effect/lightning_strike/initialize()
+ icon_state += "[rand(1,2)]" // To have two variants of lightning sprites.
+ animate(src, alpha = 0, time = time_to_die - 1)
. = ..()
\ No newline at end of file
diff --git a/code/game/objects/structures/flora/trees.dm b/code/game/objects/structures/flora/trees.dm
index f0a7498390..8b7f5bf042 100644
--- a/code/game/objects/structures/flora/trees.dm
+++ b/code/game/objects/structures/flora/trees.dm
@@ -4,7 +4,7 @@
anchored = 1
density = 1
pixel_x = -16
- plane = MOB_LAYER // You know what, let's play it safe.
+ plane = MOB_PLANE // You know what, let's play it safe.
layer = ABOVE_MOB_LAYER
var/base_state = null // Used for stumps.
var/health = 200 // Used for chopping down trees.
@@ -52,12 +52,12 @@
animate(transform=null, pixel_x=init_px, time=6, easing=ELASTIC_EASING)
// Used when the tree gets hurt.
-/obj/structure/flora/tree/proc/adjust_health(var/amount, var/is_ranged = FALSE)
+/obj/structure/flora/tree/proc/adjust_health(var/amount, var/damage_wood = FALSE)
if(is_stump)
return
// Bullets and lasers ruin some of the wood
- if(is_ranged && product_amount > 0)
+ if(damage_wood && product_amount > 0)
var/wood = initial(product_amount)
product_amount -= round(wood * (abs(amount)/max_health))
@@ -90,12 +90,16 @@
set_light(0)
/obj/structure/flora/tree/ex_act(var/severity)
- adjust_health(-(max_health / severity))
+ adjust_health(-(max_health / severity), TRUE)
/obj/structure/flora/tree/bullet_act(var/obj/item/projectile/Proj)
if(Proj.get_structure_damage())
adjust_health(-Proj.get_structure_damage(), TRUE)
+/obj/structure/flora/tree/tesla_act(power, explosive)
+ adjust_health(-power / 100, TRUE) // Kills most trees in one lightning strike.
+ ..()
+
/obj/structure/flora/tree/get_description_interaction()
var/list/results = list()
diff --git a/code/game/sound.dm b/code/game/sound.dm
index 2db1e433bd..8e206300a8 100644
--- a/code/game/sound.dm
+++ b/code/game/sound.dm
@@ -130,6 +130,9 @@
if ("mechstep") soundin = pick('sound/mecha/mechstep1.ogg', 'sound/mecha/mechstep2.ogg')
if ("geiger") soundin = pick('sound/items/geiger1.ogg', 'sound/items/geiger2.ogg', 'sound/items/geiger3.ogg', 'sound/items/geiger4.ogg', 'sound/items/geiger5.ogg')
if ("geiger_weak") soundin = pick('sound/items/geiger_weak1.ogg', 'sound/items/geiger_weak2.ogg', 'sound/items/geiger_weak3.ogg', 'sound/items/geiger_weak4.ogg')
+ if ("thunder") soundin = pick('sound/effects/thunder/thunder1.ogg', 'sound/effects/thunder/thunder2.ogg', 'sound/effects/thunder/thunder3.ogg', 'sound/effects/thunder/thunder4.ogg',
+ 'sound/effects/thunder/thunder5.ogg', 'sound/effects/thunder/thunder6.ogg', 'sound/effects/thunder/thunder7.ogg', 'sound/effects/thunder/thunder8.ogg', 'sound/effects/thunder/thunder9.ogg',
+ 'sound/effects/thunder/thunder10.ogg')
return soundin
//Are these even used?
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index dfbd142908..56a440b639 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -133,7 +133,8 @@ var/list/admin_verbs_fun = list(
/client/proc/roll_dices,
/datum/admins/proc/call_supply_drop,
/datum/admins/proc/call_drop_pod,
- /client/proc/smite
+ /client/proc/smite,
+ /client/proc/admin_lightning_strike
)
var/list/admin_verbs_spawn = list(
diff --git a/code/modules/admin/verbs/lightning_strike.dm b/code/modules/admin/verbs/lightning_strike.dm
new file mode 100644
index 0000000000..ca8d743614
--- /dev/null
+++ b/code/modules/admin/verbs/lightning_strike.dm
@@ -0,0 +1,115 @@
+/client/proc/admin_lightning_strike()
+ set name = "Lightning Stirke"
+ set desc = "Causes lightning to strike on your tile. This will hurt things on or nearby it severely."
+ set category = "Fun"
+
+ if(!check_rights(R_FUN))
+ return
+
+ var/result = alert(src, "Really strike your tile with lightning?", "Confirm Badmin" , "No", "Yes (Cosmetic)", "Yes (Real)")
+
+ if(result == "No")
+ return
+ var/fake_lightning = result == "Yes (Cosmetic)"
+
+ lightning_strike(get_turf(usr), fake_lightning)
+ log_and_message_admins("[key_name(src)] has caused [fake_lightning ? "cosmetic":"harmful"] lightning to strike at their position ([src.mob.x], [src.mob.y], [src.mob.z]). \
+ (JMP)")
+
+#define LIGHTNING_REDIRECT_RANGE 28 // How far in tiles certain things draw lightning from.
+#define LIGHTNING_ZAP_RANGE 3 // How far the tesla effect zaps, as well as the bad effects from a direct strike.
+#define LIGHTNING_POWER 20000 // How much 'zap' is in a strike, used for tesla_zap().
+
+// The real lightning proc.
+// This is global until I can figure out a better place for it.
+// T is the turf that is being struck. If cosmetic is true, the lightning won't actually hurt anything.
+/proc/lightning_strike(turf/T, cosmetic = FALSE)
+ // First, visuals.
+
+ // Do a lightning flash for the whole planet, if the turf belongs to a planet.
+ var/datum/planet/P = null
+ P = SSplanets.z_to_planet[T.z]
+ if(P)
+ var/datum/weather_holder/holder = P.weather_holder
+ flick("lightning_flash", holder.special_visuals)
+
+ // Before we do the other visuals, we need to see if something is going to hijack our intended target.
+ var/obj/machinery/power/grounding_rod/ground = null // Most of the bad effects of lightning will get negated if a grounding rod is nearby.
+ var/obj/machinery/power/tesla_coil/coil = null // However a tesla coil has higher priority and the strike will bounce.
+
+ for(var/obj/machinery/power/thing in range(LIGHTNING_REDIRECT_RANGE, T))
+ if(istype(thing, /obj/machinery/power/tesla_coil))
+ var/turf/simulated/coil_turf = get_turf(thing)
+ if(istype(coil_turf) && thing.anchored && coil_turf.outdoors)
+ coil = thing
+ break
+
+ if(istype(thing, /obj/machinery/power/grounding_rod))
+ var/turf/simulated/rod_turf = get_turf(thing)
+ if(istype(rod_turf) && thing.anchored && rod_turf.outdoors)
+ ground = thing
+
+ if(coil) // Coil gets highest priority.
+ T = coil.loc
+ else if(ground)
+ T = ground.loc
+
+ // Now make the lightning strike sprite. It will fade and delete itself in a second.
+ new /obj/effect/temporary_effect/lightning_strike(T)
+
+ // For those close up.
+ playsound(T, 'sound/effects/lightningbolt.ogg', 100, 1)
+
+ // And for those far away. If the strike happens on a planet, everyone on the planet will hear it.
+ // Otherwise only those on the current z-level will hear it.
+ var/sound = get_sfx("thunder")
+ for(var/mob/M in player_list)
+ if((P && M.z in P.expected_z_levels) || M.z == T.z)
+ M.playsound_local(get_turf(M), soundin = sound, vol = 70, vary = FALSE, is_global = TRUE)
+
+ if(cosmetic) // Everything beyond here involves potentially damaging things. If we don't want to do that, stop now.
+ return
+
+ if(ground) // All is well.
+ ground.tesla_act(LIGHTNING_POWER, FALSE)
+ return
+
+ else if(coil) // Otherwise lets bounce off the tesla coil.
+ coil.tesla_act(LIGHTNING_POWER, TRUE)
+
+ else // Striking the turf directly.
+ tesla_zap(T, zap_range = LIGHTNING_ZAP_RANGE, power = LIGHTNING_POWER, explosive = FALSE, stun_mobs = TRUE)
+
+ // Some extra effects.
+ // Some apply to those within zap range, others if they were a bit farther away.
+ for(var/mob/living/L in view(5, T))
+ if(get_dist(L, T) <= LIGHTNING_ZAP_RANGE) // They probably got zapped.
+ // The actual damage/electrocution is handled by tesla_zap().
+ L.Paralyse(5)
+ L.stuttering += 20
+ L.make_jittery(20)
+ L.emp_act(1)
+ to_chat(L, span("critical", "You've been struck by lightning!"))
+
+ // If a non-player simplemob was struck, inflict huge damage.
+ // If the damage is fatal, the SA is turned to ash.
+ if(istype(L, /mob/living/simple_animal) && !L.key)
+ var/mob/living/simple_animal/SA = L
+ SA.adjustFireLoss(200)
+ SA.updatehealth()
+ if(SA.health <= 0) // Might be best to check/give simple_mobs siemens when this gets ported to new mobs.
+ SA.visible_message(span("critical", "\The [SA] disintegrates into ash!"))
+ SA.ash()
+ continue // No point deafening something that wont exist.
+
+ // Deafen them.
+ if(L.get_ear_protection() < 2)
+ L.AdjustSleeping(-100)
+ if(iscarbon(L))
+ var/mob/living/carbon/C = L
+ C.ear_deaf += 10
+ to_chat(L, span("danger", "Lightning struck nearby, and the thunderclap is deafening!"))
+
+#undef GROUNDING_ROD_RANGE
+#undef LIGHTNING_ZAP_RANGE
+#undef LIGHTNING_POWER
\ No newline at end of file
diff --git a/code/modules/planet/sif.dm b/code/modules/planet/sif.dm
index b8bbf7ba6a..3491fac2db 100644
--- a/code/modules/planet/sif.dm
+++ b/code/modules/planet/sif.dm
@@ -264,6 +264,9 @@ datum/weather/sif
temp_low = 233.15 // -40c
light_modifier = 0.3
flight_failure_modifier = 10
+ var/next_lightning_strike = 0 // world.time when lightning will strike.
+ var/min_lightning_cooldown = 5 SECONDS
+ var/max_lightning_cooldown = 1 MINUTE
transition_chances = list(
@@ -298,6 +301,17 @@ datum/weather/sif
L.water_act(2)
to_chat(L, "Rain falls on you, drenching you in water.")
+ handle_lightning()
+
+// This gets called to do lightning periodically.
+// There is a seperate function to do the actual lightning strike, so that badmins can play with it.
+/datum/weather/sif/storm/proc/handle_lightning()
+ if(world.time < next_lightning_strike)
+ return // It's too soon to strike again.
+ next_lightning_strike = world.time + rand(min_lightning_cooldown, max_lightning_cooldown)
+ var/turf/T = pick(holder.our_planet.planet_floors) // This has the chance to 'strike' the sky, but that might be a good thing, to scare reckless pilots.
+ lightning_strike(T)
+
/datum/weather/sif/hail
name = "hail"
icon_state = "hail"
diff --git a/code/modules/planet/weather.dm b/code/modules/planet/weather.dm
index 724540c875..c3939956c5 100644
--- a/code/modules/planet/weather.dm
+++ b/code/modules/planet/weather.dm
@@ -10,6 +10,7 @@
// Holds the weather icon, using vis_contents. Documentation says an /atom/movable is required for placing inside another atom's vis_contents.
var/atom/movable/weather_visuals/visuals = null
+ var/atom/movable/weather_visuals/special/special_visuals = null
/datum/weather_holder/New(var/source)
..()
@@ -19,6 +20,7 @@
if(istype(W))
W.holder = src
visuals = new()
+ special_visuals = new()
/datum/weather_holder/proc/change_weather(var/new_weather)
var/old_light_modifier = null
@@ -87,3 +89,8 @@
icon = 'icons/effects/weather.dmi'
mouse_opacity = 0
plane = PLANE_PLANETLIGHTING
+
+// This is for special effects for specific types of weather, such as lightning flashes in a storm.
+// It's a seperate object to allow the use of flick().
+/atom/movable/weather_visuals/special
+ plane = PLANE_LIGHTING_ABOVE
\ No newline at end of file
diff --git a/code/modules/power/tesla/coil.dm b/code/modules/power/tesla/coil.dm
index 07dedf5b2b..82e84718cf 100644
--- a/code/modules/power/tesla/coil.dm
+++ b/code/modules/power/tesla/coil.dm
@@ -18,6 +18,9 @@
var/last_zap = 0
var/datum/wires/tesla_coil/wires = null
+/obj/machinery/power/tesla_coil/pre_mapped
+ anchored = TRUE
+
/obj/machinery/power/tesla_coil/New()
..()
wires = new(src)
@@ -103,6 +106,9 @@
can_buckle = TRUE
buckle_lying = FALSE
+/obj/machinery/power/grounding_rod/pre_mapped
+ anchored = TRUE
+
/obj/machinery/power/grounding_rod/update_icon()
if(panel_open)
icon_state = "grounding_rod_open[anchored]"
diff --git a/code/modules/power/tesla/tesla_act.dm b/code/modules/power/tesla/tesla_act.dm
index 8e3f1c6996..605c9e3021 100644
--- a/code/modules/power/tesla/tesla_act.dm
+++ b/code/modules/power/tesla/tesla_act.dm
@@ -45,7 +45,10 @@
/obj/machinery/light/tesla_act(power, explosive = FALSE)
if(explosive)
explosion(loc, 0, 0, 0/*, flame_range = 5*/, adminlog = FALSE)
- qdel(src)
+ qdel(src)
+ return
+ on = TRUE
+ broken()
/obj/structure/closet/tesla_act(var/power)
..() //extend the zap
diff --git a/icons/effects/96x256.dmi b/icons/effects/96x256.dmi
new file mode 100644
index 0000000000..978f0fbcda
Binary files /dev/null and b/icons/effects/96x256.dmi differ
diff --git a/icons/effects/weather.dmi b/icons/effects/weather.dmi
index d0df90a44a..5ae794c898 100644
Binary files a/icons/effects/weather.dmi and b/icons/effects/weather.dmi differ
diff --git a/polaris.dme b/polaris.dme
index 587300118e..d24cfc7d9b 100644
--- a/polaris.dme
+++ b/polaris.dme
@@ -1243,6 +1243,7 @@
#include "code\modules\admin\verbs\dice.dm"
#include "code\modules\admin\verbs\getlogs.dm"
#include "code\modules\admin\verbs\grief_fixers.dm"
+#include "code\modules\admin\verbs\lightning_strike.dm"
#include "code\modules\admin\verbs\map_template_loadverb.dm"
#include "code\modules\admin\verbs\mapping.dm"
#include "code\modules\admin\verbs\massmodvar.dm"
diff --git a/sound/effects/thunder/thunder1.ogg b/sound/effects/thunder/thunder1.ogg
new file mode 100644
index 0000000000..7ec34e88e8
Binary files /dev/null and b/sound/effects/thunder/thunder1.ogg differ
diff --git a/sound/effects/thunder/thunder10.ogg b/sound/effects/thunder/thunder10.ogg
new file mode 100644
index 0000000000..43aefb098f
Binary files /dev/null and b/sound/effects/thunder/thunder10.ogg differ
diff --git a/sound/effects/thunder/thunder2.ogg b/sound/effects/thunder/thunder2.ogg
new file mode 100644
index 0000000000..bc2f581dda
Binary files /dev/null and b/sound/effects/thunder/thunder2.ogg differ
diff --git a/sound/effects/thunder/thunder3.ogg b/sound/effects/thunder/thunder3.ogg
new file mode 100644
index 0000000000..0df287aa78
Binary files /dev/null and b/sound/effects/thunder/thunder3.ogg differ
diff --git a/sound/effects/thunder/thunder4.ogg b/sound/effects/thunder/thunder4.ogg
new file mode 100644
index 0000000000..6c671b8010
Binary files /dev/null and b/sound/effects/thunder/thunder4.ogg differ
diff --git a/sound/effects/thunder/thunder5.ogg b/sound/effects/thunder/thunder5.ogg
new file mode 100644
index 0000000000..3d5187a716
Binary files /dev/null and b/sound/effects/thunder/thunder5.ogg differ
diff --git a/sound/effects/thunder/thunder6.ogg b/sound/effects/thunder/thunder6.ogg
new file mode 100644
index 0000000000..65aef886ff
Binary files /dev/null and b/sound/effects/thunder/thunder6.ogg differ
diff --git a/sound/effects/thunder/thunder7.ogg b/sound/effects/thunder/thunder7.ogg
new file mode 100644
index 0000000000..b72848a2b6
Binary files /dev/null and b/sound/effects/thunder/thunder7.ogg differ
diff --git a/sound/effects/thunder/thunder8.ogg b/sound/effects/thunder/thunder8.ogg
new file mode 100644
index 0000000000..a25fe21e32
Binary files /dev/null and b/sound/effects/thunder/thunder8.ogg differ
diff --git a/sound/effects/thunder/thunder9.ogg b/sound/effects/thunder/thunder9.ogg
new file mode 100644
index 0000000000..9ebc1045bf
Binary files /dev/null and b/sound/effects/thunder/thunder9.ogg differ