4 Copyright (C) 2016 - Auke Kok <sofar@foo-projects.org>
6 "lightning" is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as
8 published by the Free Software Foundation; either version 2.1
9 of the license, or (at your option) any later version.
15 lightning
.interval_low
= 17
16 lightning
.interval_high
= 503
17 lightning
.range_h
= 100
18 lightning
.range_v
= 50
20 -- disable this to stop lightning mod from striking
23 local rng
= PcgRandom(32321123312123)
28 local revertsky
= function(dtime
)
37 mcl_weather
.skycolor
.remove_layer("lightning")
42 minetest
.register_globalstep(revertsky
)
44 -- select a random strike point, midpoint
45 local function choose_pos(pos
)
47 local playerlist
= minetest
.get_connected_players()
48 local playercount
= table.getn(playerlist
)
51 if playercount
== 0 then
55 local r
= rng
:next(1, playercount
)
56 local randomplayer
= playerlist
[r
]
57 pos
= randomplayer
:get_pos()
59 -- avoid striking underground
64 pos
.x
= math
.floor(pos
.x
- (lightning
.range_h
/ 2) + rng
:next(1, lightning
.range_h
))
65 pos
.y
= pos
.y
+ (lightning
.range_v
/ 2)
66 pos
.z
= math
.floor(pos
.z
- (lightning
.range_h
/ 2) + rng
:next(1, lightning
.range_h
))
69 local b
, pos2
= minetest
.line_of_sight(pos
, {x
= pos
.x
, y
= pos
.y
- lightning
.range_v
, z
= pos
.z
}, 1)
71 -- nothing but air found
76 local n
= minetest
.get_node({x
= pos2
.x
, y
= pos2
.y
- 1/2, z
= pos2
.z
})
77 if n
.name
== "air" or n
.name
== "ignore" then
84 -- lightning strike API
85 -- * pos: optional, if not given a random pos will be chosen
86 -- * returns: bool - success if a strike happened
87 lightning
.strike
= function(pos
)
88 if lightning
.auto
then
89 minetest
.after(rng
:next(lightning
.interval_low
, lightning
.interval_high
), lightning
.strike
)
93 pos
, pos2
= choose_pos(pos
)
99 minetest
.add_particlespawner({
102 -- make it hit the top of a block exactly with the bottom
103 minpos
= {x
= pos2
.x
, y
= pos2
.y
+ (lightning
.size
/ 2) + 1/2, z
= pos2
.z
},
104 maxpos
= {x
= pos2
.x
, y
= pos2
.y
+ (lightning
.size
/ 2) + 1/2, z
= pos2
.z
},
105 minvel
= {x
= 0, y
= 0, z
= 0},
106 maxvel
= {x
= 0, y
= 0, z
= 0},
107 minacc
= {x
= 0, y
= 0, z
= 0},
108 maxacc
= {x
= 0, y
= 0, z
= 0},
111 minsize
= lightning
.size
* 10,
112 maxsize
= lightning
.size
* 10,
113 collisiondetection
= true,
115 -- to make it appear hitting the node that will get set on fire, make sure
116 -- to make the texture lightning bolt hit exactly in the middle of the
117 -- texture (e.g. 127/128 on a 256x wide texture)
118 texture
= "lightning_lightning_" .. rng
:next(1,3) .. ".png",
121 minetest
.sound_play({ pos
= pos
, name
= "lightning_thunder", gain
= 10, max_hear_distance
= 500 })
123 -- damage nearby objects, player or not
124 for _
, obj
in ipairs(minetest
.get_objects_inside_radius(pos
, 5)) do
125 -- nil as param#1 is supposed to work, but core can't handle it.
126 obj
:punch(obj
, 1.0, {full_punch_interval
= 1.0, damage_groups
= {fleshy
=8}}, nil)
129 local playerlist
= minetest
.get_connected_players()
130 for i
= 1, #playerlist
do
131 local player
= playerlist
[i
]
134 sky
.bgcolor
, sky
.type, sky
.textures
= player
:get_sky()
136 local name
= player
:get_player_name()
137 if ps
[name
] == nil then
138 ps
[name
] = {p
= player
, sky
= sky
}
139 mcl_weather
.skycolor
.add_layer("lightning", {{r
=255,g
=255,b
=255}}, true)
140 mcl_weather
.skycolor
.active
= true
144 -- trigger revert of skybox
147 -- Events caused by the lightning strike: Fire, damage, mob transformations, rare skeleton spawn
149 pos2
.y
= pos2
.y
+ 1/2
150 local skeleton_lightning
= false
151 if rng
:next(1,100) <= 3 then
152 skeleton_lightning
= true
154 if minetest
.get_item_group(minetest
.get_node({x
= pos2
.x
, y
= pos2
.y
- 1, z
= pos2
.z
}).name
, "liquid") < 1 then
155 if minetest
.get_node(pos2
).name
== "air" then
156 -- Low chance for a lightning to spawn skeleton horse + skeletons
157 if skeleton_lightning
then
158 minetest
.add_entity(pos2
, "mobs_mc:skeleton_horse")
161 angle
= math
.random(0, math
.pi
*2)
163 posadd
= {x
=math
.cos(angle
),y
=0,z
=math
.sin(angle
)}
164 posadd
= vector
.normalize(posadd
)
165 local mob
= minetest
.add_entity(vector
.add(pos2
, posadd
), "mobs_mc:skeleton")
166 mob
:setyaw(angle
-math
.pi
/2)
167 angle
= angle
+ (math
.pi
*2) / 3
170 -- Cause a fire, deal damage, transform mobs
172 minetest
.set_node(pos2
, {name
= "mcl_fire:fire"})
174 local objs
= minetest
.get_objects_inside_radius(pos2
, 3.5)
177 local lua
= obj
:get_luaentity()
178 if obj
:is_player() then
180 if minetest
.get_modpath("mcl_death_messages") then
181 mcl_death_messages
.player_damage(obj
, string.format("%s was struck by lightning.", obj
:get_player_name()))
183 obj
:set_hp(obj
:get_hp()-5)
185 elseif lua
and lua
._cmi_is_mob
then
186 -- pig → zombie pigman
187 if lua
.name
== "mobs_mc:pig" then
188 local rot
= obj
:get_yaw()
190 obj
= minetest
.add_entity(pos2
, "mobs_mc:pigman")
193 elseif lua
.name
== "mobs_mc:villager" then
194 -- Witches are incomplete, this code is unused
195 -- TODO: Enable this code when witches are working.
197 local rot = obj:get_yaw()
199 obj = minetest.add_entity(pos2, "mobs_mc:witch")
202 -- TODO: creeper → charged creeper
203 elseif lua
.name
== "mobs_mc:creeper" then
205 -- Other mobs: Just Damage
207 obj
:set_hp(obj
:get_hp()-5)
213 -- TODO: Charged creeper
218 -- if other mods disable auto lightning during initialization, don't trigger the first lightning.
219 minetest
.after(5, function(dtime
)
220 if lightning
.auto
then
221 minetest
.after(rng
:next(lightning
.interval_low
,
222 lightning
.interval_high
), lightning
.strike
)