Replace getpos() with get_pos()
[MineClone/MineClone2.git] / mods / ENVIRONMENT / lightning / init.lua
blobdfcfe0149a2e43983ab2d7337b30316542fc282b
2 --[[
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.
11 --]]
13 lightning = {}
15 lightning.interval_low = 17
16 lightning.interval_high = 503
17 lightning.range_h = 100
18 lightning.range_v = 50
19 lightning.size = 100
20 -- disable this to stop lightning mod from striking
21 lightning.auto = true
23 local rng = PcgRandom(32321123312123)
25 local ps = {}
26 local ttl = -1
28 local revertsky = function(dtime)
29 if ttl == 0 then
30 return
31 end
32 ttl = ttl - dtime
33 if ttl > 0 then
34 return
35 end
37 mcl_weather.skycolor.remove_layer("lightning")
39 ps = {}
40 end
42 minetest.register_globalstep(revertsky)
44 -- select a random strike point, midpoint
45 local function choose_pos(pos)
46 if not pos then
47 local playerlist = minetest.get_connected_players()
48 local playercount = table.getn(playerlist)
50 -- nobody on
51 if playercount == 0 then
52 return nil, nil
53 end
55 local r = rng:next(1, playercount)
56 local randomplayer = playerlist[r]
57 pos = randomplayer:get_pos()
59 -- avoid striking underground
60 if pos.y < -20 then
61 return nil, nil
62 end
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))
67 end
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
72 if b then
73 return nil, nil
74 end
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
78 return nil, nil
79 end
81 return pos, pos2
82 end
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)
90 end
92 local pos2
93 pos, pos2 = choose_pos(pos)
95 if not pos then
96 return false
97 end
99 minetest.add_particlespawner({
100 amount = 1,
101 time = 0.2,
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},
109 minexptime = 0.2,
110 maxexptime = 0.2,
111 minsize = lightning.size * 10,
112 maxsize = lightning.size * 10,
113 collisiondetection = true,
114 vertical = 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]
132 local sky = {}
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
145 ttl = 0.1
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")
160 local angle, posadd
161 angle = math.random(0, math.pi*2)
162 for i=1,3 do
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
171 else
172 minetest.set_node(pos2, {name = "mcl_fire:fire"})
174 local objs = minetest.get_objects_inside_radius(pos2, 3.5)
175 for o=1, #objs do
176 local obj = objs[o]
177 local lua = obj:get_luaentity()
178 if obj:is_player() then
179 -- Player damage
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)
184 -- Mobs
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()
189 obj:remove()
190 obj = minetest.add_entity(pos2, "mobs_mc:pigman")
191 obj:set_yaw(rot)
192 -- villager → witch
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.
196 --[[
197 local rot = obj:get_yaw()
198 obj:remove()
199 obj = minetest.add_entity(pos2, "mobs_mc:witch")
200 obj:set_yaw(rot)
202 -- TODO: creeper → charged creeper
203 elseif lua.name == "mobs_mc:creeper" then
205 -- Other mobs: Just Damage
206 else
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)
224 end)