mirror of
https://github.com/luanti-org/minetest_game.git
synced 2025-05-20 22:33:16 -04:00
270 lines
8.7 KiB
Lua
270 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:obsidian",
|
|
"moontest:dust",
|
|
},
|
|
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
|