misslestrike/init.lua

273 lines
8.7 KiB
Lua

-- meseor 0.2.0 by paramat.
-- License WTFPL, see license.txt.
-- Parameters.
local ONGEN = true -- (true / false) Enable / disable impacts on generated chunk.
local ONGCHA = 9 -- 9 -- Ongen 1/x chance of impact per generated chunk.
local ABM = false -- Enable / disable impacts by abm on surface material.
local ABMINT = 181 -- 181 -- Abm interval.
local ABMCHA = 100000 -- 100000 -- Abm 1/x chance per node.
local DAMAGE = true -- Enable / disable player damage loop.
local DAMMAX = 20 -- 20 -- Maximum damage. 20 = direct hit is fatal.
local RADMIN = 3 -- 3 -- Minimum crater radius.
local RADMAX = 31 -- 31 -- Maximum crater radius, on-gen craters are limited to 16.
local STOCHA = 5 -- 5 -- 1/x chance of stone boulders instead of gravel.
local XMIN = -16000 -- Impact area dimensions. Impacts only inside this area.
local XMAX = 16000
local ZMIN = -16000
local ZMAX = 16000
local SAXMIN = 0 -- Safe area dimensions. No impacts inside this area.
local SAXMAX = 0 -- When overlapping impact area, the safe area overrides.
local SAZMIN = 0
local SAZMAX = 0
local DEBUG = true
-- Stuff.
meseor = {}
-- On generated.
if ONGEN then
minetest.register_on_generated(function(minp, maxp, seed)
if math.random(ONGCHA) == 2 then
local env = minetest.env
local maxrad = RADMAX
if maxrad > 16 then maxrad = 16 end
local conrad = math.random(RADMIN, maxrad)
local rimrad = math.ceil(conrad * 2.4)
local x = math.random(minp.x + rimrad, maxp.x - rimrad)
local z = math.random(minp.z + rimrad, maxp.z - rimrad)
-- Find surface with air above, abort if water found, ignore trees and ice.
local surfy = false
local aa = false
for y = maxp.y, minp.y, -1 do
local nodename = env:get_node({x=x,y=y,z=z}).name
if nodename == "default:water_source" or nodename == "default:water_flowing" then
return
elseif nodename == "air" then
aa = true
elseif aa and nodename ~= "air" and nodename ~= "ignore" and nodename ~= "snow:ice"
and nodename ~= "default:leaves" and nodename ~= "default:jungleleaves"
and nodename ~= "default:tree" and nodename ~= "default:jungletree" then
surfy = y
break
end
end
if not surfy then
return
end
DAMAGE = false
crater({x=x,y=surfy,z=z},conrad)
end
end)
end
-- Abm.
if ABM then
minetest.register_abm({
nodenames = {
"default:dirt",
"default:dirt_with_grass",
"default:desert_sand",
"default:sand",
},
interval = ABMINT,
chance = ABMCHA,
action = function(pos, node, _, _)
local env = minetest.env
local x = pos.x
local y = pos.y
local z = pos.z
-- Find close surface above, abort if underwater or no close surface found.
local surfy = false
for j = 1, 4 do
local nodename = env:get_node({x=x,y=y+j,z=z}).name
if nodename == "default:water_source" or nodename == "default:water_flowing" then
return
elseif nodename == "air" then
surfy = y+j-1
break
end
end
if not surfy then
return
end
local conrad = math.random(RADMIN, RADMAX)
crater({x=x,y=surfy,z=z},conrad)
end,
})
end
-- Functions.
function crater(pos, conrad)
local env = minetest.env
local x = pos.x
local y = pos.y
local z = pos.z
local rimrad = math.ceil(conrad * 2.4)
-- If in safe zone or not in impact zone then abort.
if (x > SAXMIN - rimrad and x < SAXMAX + rimrad and z > SAZMIN - rimrad and z < SAZMAX + rimrad)
or not (x > XMIN and x < XMAX and z > ZMIN and z < ZMAX) then
return
end
-- Check enough depth.
for j = -conrad - 1, -1 do
local nodename = env:get_node({x=x,y=y+j,z=z}).name
if nodename == "air" or nodename == "ignore" then
return
end
end
-- Check pos open to sky.
for j = 1, 160 do
local nodename = env:get_node({x=x,y=y+j,z=z}).name
if nodename ~= "air" and nodename ~= "ignore"
and nodename ~= "default:leaves" and nodename ~= "default:jungleleaves"
and nodename ~= "snow:ice" and nodename ~= "snow:needles" then
return
end
end
-- Excavate path.
for j = 1, 160 do
local nodename = env:get_node({x=x,y=y+j,z=z}).name
if nodename ~= "air" and nodename ~= "ignore" then
env:remove_node({x=x,y=y+j,z=z})
end
end
-- Add meseorite.
env:add_node({x=x,y=y-conrad-1,z=z},{name="default:mese"})
-- Excavate cone and count excavated nodes.
local exsto = 0
local exdsto = 0
local exdirt = 0
local exdsan = 0
local exsan = 0
local extree = 0
local exjtree = 0
for j = 0, conrad * 2 do
for i = -j, j do
for k = -j, j do
if i ^ 2 + k ^ 2 <= j ^ 2 then
local exsno = false
local nodename = env:get_node({x=x+i,y=y-conrad+j,z=z+k}).name
if nodename == "default:stone" then
exsto = exsto + 1
elseif nodename == "default:desert_stone" then
exdsto = exdsto + 1
elseif nodename == "default:dirt" or nodename == "default:dirt_with_grass" then
exdirt = exdirt + 1
elseif nodename == "default:desert_sand" then
exdsan = exdsan + 1
elseif nodename == "default:sand" then
exsan = exsan + 1
elseif nodename == "default:tree" then
extree = extree + 1
elseif nodename == "default:jungletree" then
exjtree = exjtree + 1
elseif nodename == "snow:snow" then
exsno = true
end
if nodename ~= "air" then
env:remove_node({x=x+i,y=y-conrad+j,z=z+k})
end
if exsno then
env:remove_node({x=x+i,y=y-conrad+j,z=z+k})
end
end
end
end
end
-- Calculate proportions of ejecta.
local extot = exsto + exdsto + exdirt + exdsan + exsan + extree + exjtree
local pexsto = exsto / extot
local pexdsto = exdsto / extot
local pexdirt = exdirt / extot
local pexdsan = exdsan / extot
local pexsan = exsan / extot
local pextree = extree / extot
local pexjtree = exjtree / extot
-- Print to terminal.
if DEBUG then
print ("[meseor] Cone radius "..conrad.." node ("..x.." "..y.." "..z..")")
print ("[meseor] exsto "..exsto.." exdsto "..exdsto.." exdirt "..exdirt.." exdsan "..exdsan)
print ("[meseor] exsan "..exsan.." extree "..extree.." exjtree "..exjtree)
print ("[meseor] extot "..extot)
end
-- Add ejecta.
local addtot = 0
for rep = 1, 128 do
for i = -rimrad, rimrad do
for k = -rimrad, rimrad do
local rad = (i ^ 2 + k ^ 2) ^ 0.5
if rad <= rimrad and math.random() > math.abs(rad - conrad * 1.2) / (conrad * 1.2)
and addtot < extot and math.random(3) == 2 then
-- Find ground.
local groundy = false
for j = conrad - 1, -160, -1 do
local nodename = env:get_node({x=x+i,y=y+j,z=z+k}).name
if nodename == "default:leaves" or nodename == "default:jungleleaves"
or nodename == "default:papyrus" or nodename == "default:dry_shrub"
or nodename == "default:grass_1" or nodename == "default:grass_2"
or nodename == "default:grass_3" or nodename == "default:grass_4"
or nodename == "default:grass_5" or nodename == "default:apple"
or nodename == "default:junglegrass" or nodename == "snow:needles" then
env:remove_node({x=x+i,y=y+j,z=z+k})
elseif nodename ~= "air" and nodename ~= "ignore" and nodename ~= "snow:snow"
and nodename ~= "default:water_source" and nodename ~= "default:water_flowing" then
groundy = y+j
break
end
end
if groundy then
local x = x + i
local y = groundy + 1
local z = z + k
if math.random() < pexjtree then
env:add_node({x=x,y=y,z=z},{name="default:jungletree"})
elseif math.random() < pextree then
env:add_node({x=x,y=y,z=z},{name="default:tree"})
elseif math.random() < pexsan then
env:add_node({x=x,y=y,z=z},{name="default:sand"})
elseif math.random() < pexdsan then
env:add_node({x=x,y=y,z=z},{name="default:desert_sand"})
elseif math.random() < pexdirt then
env:add_node({x=x,y=y,z=z},{name="default:dirt"})
elseif math.random() < pexdsto then
env:add_node({x=x,y=y,z=z},{name="default:desert_stone"})
elseif math.random() < pexsto then
if math.random(STOCHA) == 2 then
env:add_node({x=x,y=y,z=z},{name="default:stone"})
else
env:add_node({x=x,y=y,z=z},{name="default:gravel"})
end
end
addtot = addtot + 1
end
end
end
end
if addtot == extot then break end
end
-- Play sound.
minetest.sound_play("meseor", {gain = 1})
-- Damage player if inside rimrad.
if DAMAGE then
for _,player in ipairs(minetest.get_connected_players()) do
local plapos = player:getpos()
local pladis = ((plapos.x - x) ^ 2 + (plapos.y - y) ^ 2 + (plapos.z - z) ^ 2) ^ 0.5
local pladam = math.ceil((1 - pladis / rimrad) * DAMMAX)
if pladam > 0 then
player:set_hp(player:get_hp() - pladam)
end
end
end
end