3 Copyright (C) 2016 - Auke Kok <sofar@foo-projects.org>
5 "lightning" is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as
7 published by the Free Software Foundation; either version 2.1
8 of the license, or (at your option) any later version.
12 local S
= minetest
.get_translator("lightning")
16 lightning
.interval_low
= 17
17 lightning
.interval_high
= 503
18 lightning
.range_h
= 100
19 lightning
.range_v
= 50
21 -- disable this to stop lightning mod from striking
24 local rng
= PcgRandom(32321123312123)
29 local revertsky
= function(dtime
)
38 mcl_weather
.skycolor
.remove_layer("lightning")
43 minetest
.register_globalstep(revertsky
)
45 -- select a random strike point, midpoint
46 local function choose_pos(pos
)
48 local playerlist
= minetest
.get_connected_players()
49 local playercount
= table.getn(playerlist
)
52 if playercount
== 0 then
56 local r
= rng
:next(1, playercount
)
57 local randomplayer
= playerlist
[r
]
58 pos
= randomplayer
:get_pos()
60 -- avoid striking underground
65 pos
.x
= math
.floor(pos
.x
- (lightning
.range_h
/ 2) + rng
:next(1, lightning
.range_h
))
66 pos
.y
= pos
.y
+ (lightning
.range_v
/ 2)
67 pos
.z
= math
.floor(pos
.z
- (lightning
.range_h
/ 2) + rng
:next(1, lightning
.range_h
))
70 local b
, pos2
= minetest
.line_of_sight(pos
, {x
= pos
.x
, y
= pos
.y
- lightning
.range_v
, z
= pos
.z
}, 1)
72 -- nothing but air found
77 local n
= minetest
.get_node({x
= pos2
.x
, y
= pos2
.y
- 1/2, z
= pos2
.z
})
78 if n
.name
== "air" or n
.name
== "ignore" then
85 -- lightning strike API
86 -- * pos: optional, if not given a random pos will be chosen
87 -- * returns: bool - success if a strike happened
88 lightning
.strike
= function(pos
)
89 if lightning
.auto
then
90 minetest
.after(rng
:next(lightning
.interval_low
, lightning
.interval_high
), lightning
.strike
)
94 pos
, pos2
= choose_pos(pos
)
100 minetest
.add_particlespawner({
103 -- make it hit the top of a block exactly with the bottom
104 minpos
= {x
= pos2
.x
, y
= pos2
.y
+ (lightning
.size
/ 2) + 1/2, z
= pos2
.z
},
105 maxpos
= {x
= pos2
.x
, y
= pos2
.y
+ (lightning
.size
/ 2) + 1/2, z
= pos2
.z
},
106 minvel
= {x
= 0, y
= 0, z
= 0},
107 maxvel
= {x
= 0, y
= 0, z
= 0},
108 minacc
= {x
= 0, y
= 0, z
= 0},
109 maxacc
= {x
= 0, y
= 0, z
= 0},
112 minsize
= lightning
.size
* 10,
113 maxsize
= lightning
.size
* 10,
114 collisiondetection
= true,
116 -- to make it appear hitting the node that will get set on fire, make sure
117 -- to make the texture lightning bolt hit exactly in the middle of the
118 -- texture (e.g. 127/128 on a 256x wide texture)
119 texture
= "lightning_lightning_" .. rng
:next(1,3) .. ".png",
120 glow
= minetest
.LIGHT_MAX
,
123 minetest
.sound_play({ pos
= pos
, name
= "lightning_thunder", gain
= 10, max_hear_distance
= 500 }, true)
125 -- damage nearby objects, transform mobs
126 local objs
= minetest
.get_objects_inside_radius(pos2
, 3.5)
129 local lua
= obj
:get_luaentity()
130 if obj
:is_player() then
132 if minetest
.get_modpath("mcl_death_messages") then
133 mcl_death_messages
.player_damage(obj
, S("@1 was struck by lightning.", obj
:get_player_name()))
135 obj
:set_hp(obj
:get_hp()-5, { type = "punch", from
= "mod" })
137 elseif lua
and lua
._cmi_is_mob
then
138 -- pig → zombie pigman (no damage)
139 if lua
.name
== "mobs_mc:pig" then
140 local rot
= obj
:get_yaw()
142 obj
= minetest
.add_entity(pos2
, "mobs_mc:pigman")
144 -- mooshroom: toggle color red/brown (no damage)
145 elseif lua
.name
== "mobs_mc:mooshroom" then
146 if lua
.base_texture
[1] == "mobs_mc_mooshroom.png" then
147 lua
.base_texture
= { "mobs_mc_mooshroom_brown.png", "mobs_mc_mushroom_brown.png" }
149 lua
.base_texture
= { "mobs_mc_mooshroom.png", "mobs_mc_mushroom_red.png" }
151 obj
:set_properties({textures
= lua
.base_texture
})
152 -- villager → witch (no damage)
153 elseif lua
.name
== "mobs_mc:villager" then
154 -- Witches are incomplete, this code is unused
155 -- TODO: Enable this code when witches are working.
157 local rot = obj:get_yaw()
159 obj = minetest.add_entity(pos2, "mobs_mc:witch")
162 -- TODO: creeper → charged creeper (no damage)
163 elseif lua
.name
== "mobs_mc:creeper" then
165 -- Other mobs: Just damage
167 obj
:set_hp(obj
:get_hp()-5, { type = "punch", from
= "mod" })
172 local playerlist
= minetest
.get_connected_players()
173 for i
= 1, #playerlist
do
174 local player
= playerlist
[i
]
177 sky
.bgcolor
, sky
.type, sky
.textures
= player
:get_sky()
179 local name
= player
:get_player_name()
180 if ps
[name
] == nil then
181 ps
[name
] = {p
= player
, sky
= sky
}
182 mcl_weather
.skycolor
.add_layer("lightning", {{r
=255,g
=255,b
=255}}, true)
183 mcl_weather
.skycolor
.active
= true
187 -- trigger revert of skybox
190 -- Events caused by the lightning strike: Fire, damage, mob transformations, rare skeleton spawn
192 pos2
.y
= pos2
.y
+ 1/2
193 local skeleton_lightning
= false
194 if rng
:next(1,100) <= 3 then
195 skeleton_lightning
= true
197 if minetest
.get_item_group(minetest
.get_node({x
= pos2
.x
, y
= pos2
.y
- 1, z
= pos2
.z
}).name
, "liquid") < 1 then
198 if minetest
.get_node(pos2
).name
== "air" then
199 -- Low chance for a lightning to spawn skeleton horse + skeletons
200 if skeleton_lightning
then
201 minetest
.add_entity(pos2
, "mobs_mc:skeleton_horse")
204 angle
= math
.random(0, math
.pi
*2)
206 posadd
= {x
=math
.cos(angle
),y
=0,z
=math
.sin(angle
)}
207 posadd
= vector
.normalize(posadd
)
208 local mob
= minetest
.add_entity(vector
.add(pos2
, posadd
), "mobs_mc:skeleton")
209 mob
:set_yaw(angle
-math
.pi
/2)
210 angle
= angle
+ (math
.pi
*2) / 3
215 minetest
.set_node(pos2
, {name
= "mcl_fire:fire"})
222 -- if other mods disable auto lightning during initialization, don't trigger the first lightning.
223 minetest
.after(5, function(dtime
)
224 if lightning
.auto
then
225 minetest
.after(rng
:next(lightning
.interval_low
,
226 lightning
.interval_high
), lightning
.strike
)
230 minetest
.register_chatcommand("lightning", {
231 params
= "[<X> <Y> <Z>]",
232 description
= S("Let lightning strike at the specified position or yourself"),
233 privs
= { maphack
= true },
234 func
= function(name
, param
)
236 pos
.x
, pos
.y
, pos
.z
= string.match(param
, "^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$")
237 pos
.x
= tonumber(pos
.x
)
238 pos
.y
= tonumber(pos
.y
)
239 pos
.z
= tonumber(pos
.z
)
240 if not (pos
.x
and pos
.y
and pos
.z
) then
243 if name
== "" and pos
== nil then
244 return false, "No position specified and unknown player"
247 lightning
.strike(pos
)
249 local player
= minetest
.get_player_by_name(name
)
251 lightning
.strike(player
:get_pos())
253 return false, S("No position specified and unknown player")