------------------------------------------------------------------- -- Abms ------------------------------------------------------------------- -- player surface damage and hunger local dps_delay = 3 local last_dps_check = 0 local cold_delay = 5 local monster_delay = 3 local hunger_delay = 60 local dps_count = hunger_delay local players_in_orbit = {} local mushrooms = {"flowers:mushroom_brown", "flowers:mushroom_red"} local time_factor = (fun_caves.time_factor or 10) local light_max = (fun_caves.light_max or 10) -- fungal tree nodes local fungal_tree_leaves = {} for i = 1, 4 do fungal_tree_leaves[#fungal_tree_leaves+1] = "fun_caves:fungal_tree_leaves_"..i end local is_fungal_leaf = {} for _, leaf in ipairs(fungal_tree_leaves) do is_fungal_leaf[leaf] = true end local fungal_nodes = {} for _, leaf in pairs(fungal_tree_leaves) do fungal_nodes[#fungal_nodes+1] = {name = leaf} end fungal_nodes[#fungal_nodes+1] = {name = 'fun_caves:fungal_tree_fruit'} local fungi_to_explode = {} -- see below local exploding_fungi = fun_caves.exploding_fungi if not exploding_fungi then exploding_fungi = {} end -- hot spike parameters local spike_air = {} spike_air['default:lava_source'] = true spike_air['default:lava_source'] = true spike_air['default:lava_flowing'] = true local spike_soil = {} spike_soil['fun_caves:hot_cobble'] = true spike_soil['fun_caves:black_sand'] = true ------------------------------------------------------------ -- all the fun_caves globalstep functions ------------------------------------------------------------ local hot_stuff = {"group:surface_hot"} local cold_stuff = {"group:surface_cold"} local poison_stuff = {"group:poison"} local gravity_off = {gravity = 0.1} local gravity_on = {gravity = 1} local dungeon_group = {'group:dungeon'} local firework_active = false local function firework() local t = minetest.get_timeofday() if not (t and type(t) == 'number') then return end if not firework_active and (t < 0.25 or t > 0.75) then firework_active = true local ps = {} local players = minetest.get_connected_players() if not (players and type(players) == 'table') then return end for i = 1, #players do local pp = players[i]:getpos() if pp and pp.y > 0 then local sky = {} sky.bgcolor, sky.type, sky.textures = players[i]:get_sky() ps[#ps+1] = { p = players[i], sky = sky } --players[i]:set_sky(0xffffff, "plain", {}) players[i]:set_sky('#000000', 'skybox', {'fun_caves_firework_'..math.random(3)..'.png', 'fun_caves_blank.png', 'fun_caves_blank.png', 'fun_caves_blank.png', 'fun_caves_blank.png', 'fun_caves_blank.png'}) end minetest.sound_play("fireworks", {gain = 1, pos = pp, max_hear_distance = 2}) end minetest.after(4, function() for i = 1, #ps do ps[i].p:set_sky(ps[i].sky.bgcolor, ps[i].sky.type, ps[i].sky.textures) end firework_active = false end) end end minetest.register_globalstep(function(dtime) if not (dtime and type(dtime) == 'number') then return end if not (fun_caves.db.status and fun_caves.registered_status) then return end local time = minetest.get_gametime() if not (time and type(time) == 'number') then return end -- Execute only after an interval. if last_dps_check and time - last_dps_check < dps_delay then return end local out = io.open(fun_caves.world..'/fun_caves_data.txt','w') if out then out:write(minetest.serialize(fun_caves.db)) out:close() end if fun_caves.date and fun_caves.date[2] == 7 and fun_caves.date[3] == 4 and math.random(30) == 1 then firework() end -- Promote mobs based on spawn position for _, mob in pairs(minetest.luaentities) do if not mob.initial_promotion then local pos = mob.object:getpos() if mob.hp_max and mob.object and mob.health and mob.damage then local factor = 1 + (math.max(math.abs(pos.x), math.abs(pos.y), math.abs(pos.z)) / 6200) mob.hp_max = math.floor(mob.hp_max * factor) mob.damage = math.floor(mob.damage * factor) mob.health = math.floor(mob.health * factor) mob.object:set_hp(mob.health) mob.initial_promotion = true check_for_death(mob) --local name = mob.object:get_entity_name() or '' --print('Promoting '..name..': '..mob.health..' health, '..mob.damage..' damage') end end end local minetest_find_nodes_in_area = minetest.find_nodes_in_area local players = minetest.get_connected_players() if not (players and type(players) == 'table') then return end for i = 1, #players do local player = players[i] local pos = player:getpos() local player_name = player:get_player_name() if pos.y >= 11168 and pos.y <= 15168 then if not players_in_orbit[player_name] then player:set_physics_override(gravity_off) player:set_sky("#000000", "plain") players_in_orbit[player_name] = true end elseif players_in_orbit[player_name] then player:set_sky("#000000", "regular") minetest.after(20, function() player:set_physics_override(gravity_on) end) players_in_orbit[player_name] = false end -- environmental damage if fun_caves.DEBUG and player:get_hp() < 20 then -- Regenerate the player while testing. print("HP: "..player:get_hp()) player:set_hp(20) return else local minp = vector.subtract(pos, 0.5) local maxp = vector.add(pos, 0.5) -- Remove status effects. local status = fun_caves.db.status[player_name] for status_name, status_param in pairs(status) do local def = fun_caves.registered_status[status_name] if not def then print('Fun Caves: Error - unregistered status ' .. status_name) break end local remove if type(status_param.remove) == 'number' then if status_param.remove < time then remove = true end elseif def.remove then remove = def.remove(player) else print('Fun Caves: Error in status remove for ' .. status_name) end if remove then fun_caves.remove_status(player_name, status_name) elseif def.during then def.during(player) end end if fun_caves.db.status[player_name]['breathe'] then player:set_breath(11) end -- ... from standing on or near hot objects. local counts = minetest_find_nodes_in_area(minp, maxp, hot_stuff) if not (counts and type(counts) == 'table') then return end if #counts > 1 then player:set_hp(player:get_hp() - 1) end -- ... from standing on or near poison. local counts = minetest_find_nodes_in_area(minp, maxp, poison_stuff) if not (counts and type(counts) == 'table') then return end if #counts > 1 then player:set_hp(player:get_hp() - 1) end -- ... from standing on or near cold objects (less often). if dps_count % cold_delay == 0 then counts = minetest_find_nodes_in_area(minp, maxp, cold_stuff) if not (counts and type(counts) == 'table') then return end if #counts > 1 then player:set_hp(player:get_hp() - 1) end end -- ... from hunger (even less often). if dps_count % hunger_delay == 0 and fun_caves.hunger_change then fun_caves.hunger_change(player, -1) end end end -- Set this outside of the player loop, to affect everyone. if dps_count % hunger_delay == 0 then dps_count = hunger_delay end last_dps_check = minetest.get_gametime() if not (last_dps_check and type(last_dps_check) == 'number') then last_dps_check = 0 end dps_count = dps_count - 1 end) ------------------------------------------------------------ -- destruction ------------------------------------------------------------ minetest.register_abm({ nodenames = {"fun_caves:flare",}, interval = 5, chance = 10, action = function(pos, node) if not (pos and node) then return end minetest.remove_node(pos) end, }) minetest.register_abm({ nodenames = {"fun_caves:hot_cobble",}, neighbors = {"group:water"}, interval = 10, chance = 10, catch_up = false, action = function(pos, node) if not (pos and node) then return end minetest.set_node(pos, {name = "default:cobble"}) minetest.sound_play("default_cool_lava", {pos = pos, max_hear_distance = 16, gain = 0.25}) end, }) -- Exploding fungal fruit minetest.register_abm({ nodenames = {"fun_caves:fungal_tree_fruit"}, interval = 2 * time_factor, chance = 150, catch_up = false, action = function(pos, node) if not (pos and node) then return end fun_caves.soft_boom(pos) end }) -- Exploding fungal fruit -- in a fire minetest.register_abm({ nodenames = {"fun_caves:fungal_tree_fruit"}, neighbors = {"fire:basic_flame"}, interval = time_factor, chance = 50, catch_up = false, action = function(pos, node) if not (pos and node) then return end fun_caves.soft_boom(pos) end }) -- giant/huge mushroom "leaf decay" -- This should be more efficient than the normal leaf decay, -- since it only checks below the node. minetest.register_abm({ nodenames = {"fun_caves:giant_mushroom_cap", "fun_caves:huge_mushroom_cap"}, interval = time_factor, chance = 25, action = function(pos, node) if not (pos and node) then return end -- Check for stem under the cap. local node_under = minetest.get_node_or_nil({x = pos.x, y = pos.y - 1, z = pos.z}) if not node_under or node_under.name ~= "fun_caves:giant_mushroom_stem" then minetest.remove_node(pos) return end end }) -- Destroy mushroom caps in the light. minetest.register_abm({ nodenames = {"fun_caves:giant_mushroom_cap", "fun_caves:huge_mushroom_cap"}, interval = 2 * time_factor, chance = 110, action = function(pos, node) if not (pos and node) then return end if (minetest.get_node_light(pos, nil) or 99) >= light_max + 2 then minetest.remove_node(pos) return end end }) minetest.register_abm({ nodenames = {"fire:basic_flame"}, interval = time_factor, chance = 100, action = function(p0, node, _, _) minetest.remove_node(p0) end, }) minetest.register_abm({ nodenames = {"fun_caves:coffer"}, interval = time_factor, chance = 10, action = function(p0, node, _, _) minetest.remove_node(p0) end, }) ------------------------------------------------------------ -- creation ------------------------------------------------------------ minetest.register_abm({ nodenames = {"fun_caves:dungeon_floor_1"}, interval = 10 * time_factor, chance = 250, catch_up = false, action = function(pos, node, aoc, active_object_count_wider) if not (pos and node) or active_object_count_wider > 5 then return end for i = 1, 2 do pos.y = pos.y + 1 local node = minetest.get_node_or_nil(pos) if not node or node.name ~= "air" then return end end local target_level = math.max(2, math.ceil((pos.y - 3 ^ math.random(9)) / -2000)) local desc for i = 1, 100 do desc = fun_caves.dungeon_spawns[math.random(#fun_caves.dungeon_spawns)] if desc.level <= target_level then break else desc = nil end end if not desc then return end local name = desc.name local level = desc.level local obj = minetest.add_entity(pos, name) if not obj then return end local mob = obj:get_luaentity() if not mob then return end if mob.hp_max and mob.object and mob.health and mob.damage then local factor = 1 + (math.max(math.abs(pos.x), math.abs(pos.y), math.abs(pos.z)) / 6200) mob.started_in_dungeon = true factor = factor * 1.5 mob.hp_max = math.floor(mob.hp_max * factor) mob.damage = math.floor(mob.damage * factor) mob.health = math.floor(mob.health * factor) mob.object:set_hp(mob.health) mob.initial_promotion = true check_for_death(mob) --print('Dungeon quality '..name..': '..mob.health..' health, '..mob.damage..' damage') end end }) minetest.register_abm({ nodenames = {"fun_caves:dungeon_floor_1"}, interval = 20 * time_factor, chance = 500, catch_up = false, action = function(pos, node) if not (pos and node) then return end pos.y = pos.y + 1 local node = minetest.get_node_or_nil(pos) if not node or node.name ~= "air" then return end minetest.set_node(pos, {name = 'fun_caves:coffer'}) end }) minetest.register_abm({ nodenames = {"fun_caves:dungeon_wall_1"}, interval = time_factor, chance = 100, catch_up = false, action = function(pos, node) if not (pos and node) then return end pos = vector.round(pos) local dir = math.random(4) local p2 = 3 if dir == 1 then pos.x = pos.x + 1 elseif dir == 2 then pos.x = pos.x - 1 p2 = 2 elseif dir == 3 then pos.z = pos.z + 1 p2 = 5 elseif dir == 4 then pos.z = pos.z - 1 p2 = 4 end local node = minetest.get_node_or_nil(pos) if not node or node.name ~= "air" then return end if (minetest.get_node_light(pos, nil) or 99) > 0 then return end minetest.set_node(pos, {name = 'default:torch', param2 = p2}) end }) minetest.register_abm({ nodenames = {"fun_caves:tree"}, neighbors = {'fire:basic_flame'}, interval = time_factor, chance = 20, action = function(pos, node) if not (pos and node) then return end minetest.set_node(pos, {name = 'fun_caves:sap'}) end }) minetest.register_abm({ nodenames = {"default:leaves"}, interval = 20 * time_factor, chance = 200, catch_up = false, action = function(pos, node) if not (pos and node) then return end pos.y = pos.y - 1 local node_below = minetest.get_node_or_nil(pos) if node_below and node_below.name == 'air' then minetest.set_node(pos, {name = 'default:apple'}) end end }) minetest.register_abm({ nodenames = {"default:apple"}, interval = time_factor, chance = 80, catch_up = false, action = function(pos, node) if not (pos and node) then return end minetest.remove_node(pos) end }) -- Freezing vapor hardens into ice. local ice_node = {name = 'default:ice'} minetest.register_abm({ nodenames = {"fun_caves:freezing_vapor"}, interval = time_factor, chance = 10, action = function(pos, node) if not (pos and node) then return end minetest.set_node(pos, ice_node) end }) local no_tree_grow = {'fun_caves:bark', 'fun_caves:leaves'} local wood_nodes = {{name = 'fun_caves:diamondwood'}, {name = 'fun_caves:ironwood'}, {name = 'fun_caves:sap'}, {name = 'fun_caves:tree'}} minetest.register_abm({ neighbors = {'air'}, interval = 5 * time_factor, chance = 900, catch_up = false, action = function(pos, node) if not (pos and node) then return end local new_pos = minetest.find_node_near(pos, 1, no_tree_grow) if new_pos then return end new_pos = minetest.find_node_near(pos, 1, "air") if not new_pos then return end if math.random(50) == 1 then minetest.set_node(new_pos, wood_nodes[1]) elseif math.random(25) == 1 then minetest.set_node(new_pos, wood_nodes[2]) elseif math.random(25) == 1 then minetest.set_node(new_pos, wood_nodes[3]) else minetest.set_node(new_pos, wood_nodes[4]) end end }) -- vacuum sucks local vacuum = {name = 'fun_caves:vacuum'} local air_list = {'air'} minetest.register_abm({ nodenames = {"fun_caves:vacuum"}, neighbors = {"air"}, interval = time_factor, chance = 10, action = function(pos, node) if not (pos and node) then return end if pos.y <= 11168 or pos.y >= 15168 then return end local p1 = vector.subtract(pos, 1) local p2 = vector.add(pos, 1) local positions = minetest.find_nodes_in_area(p1, p2, air_list) if not (positions and type(positions) == 'table') then return end local minetest_get_node_or_nil = minetest.get_node_or_nil local minetest_set_node = minetest.set_node for _, p3 in pairs(positions) do local node2 = minetest_get_node_or_nil(p3) if node2 and node2.name == 'air' then minetest_set_node(p3, vacuum) end end end }) -- fungal spread minetest.register_abm({ nodenames = fungal_tree_leaves, neighbors = {"air", "group:liquid"}, interval = time_factor, chance = 50, catch_up = false, action = function(pos, node) if not (pos and node) then return end if (minetest.get_node_light(pos, nil) or 99) >= light_max + 2 then minetest.remove_node(pos) return end local grow_pos = {x=pos.x, y=pos.y-1, z=pos.z} local grow_node = minetest.get_node_or_nil(grow_pos) if grow_node and grow_node.name == "air" then minetest.set_node(grow_pos, node) return end grow_pos = {x=math.random(-1,1)+pos.x, y=math.random(-1,1)+pos.y, z=math.random(-1,1)+pos.z} grow_node = minetest.get_node_or_nil(grow_pos) if grow_node and grow_node.name == "air" and (minetest.get_node_light(grow_pos, nil) or 99) <= light_max then minetest.set_node(grow_pos, node) return elseif grow_node and is_fungal_leaf[grow_node.name] and grow_node.name ~= node.name then minetest.remove_node(grow_pos) return end if math.random(40) == 1 then minetest.set_node(pos, fungal_nodes[#fungal_nodes]) return end if math.random(100) == 1 then minetest.set_node(pos, fungal_nodes[math.random(#fungal_nodes - 1)]) return end end }) -- mushroom growth -- caps regenerate in time local huge_mushroom_cap_node = {name = 'fun_caves:huge_mushroom_cap'} minetest.register_abm({ nodenames = {"fun_caves:giant_mushroom_stem"}, interval = 2 * time_factor, chance = 150, action = function(pos, node) if not (pos and node) then return end local pos_up = {x=pos.x,y=pos.y+1,z=pos.z} local node_up = minetest.get_node_or_nil(pos_up) if not node_up or node_up.name ~= "air" then return end if (minetest.get_node_light(pos_up, nil) or 99) <= light_max then minetest.set_node(pos_up, huge_mushroom_cap_node) end end }) -- new fungi local mushroom_nodes = {} for _, mushroom in pairs(mushrooms) do mushroom_nodes[#mushroom_nodes+1] = {name = mushroom} end minetest.register_abm({ nodenames = {"default:dirt"}, neighbors = {"air"}, interval = 2 * time_factor, chance = 75, action = function(pos, node) if not (pos and node) then return end if pos.y > 0 then return end local grow_pos = {x=pos.x, y=pos.y+1, z=pos.z} local grow_node = minetest.get_node_or_nil(grow_pos) if grow_node and grow_node.name == "air" and (minetest.get_node_light(grow_pos, nil) or 99) <= light_max then if math.random(4) == 1 then minetest.set_node(grow_pos, fungal_nodes[math.random(#fungal_nodes - 1)]) else minetest.set_node(grow_pos, mushroom_nodes[math.random(#mushroom_nodes)]) end end end }) -- mushroom growth -- small into huge local giant_mushroom_stem_node = {name = 'fun_caves:giant_mushroom_stem'} minetest.register_abm({ nodenames = mushrooms, interval = 5 * time_factor, chance = 750, action = function(pos, node) if not (pos and node) then return end -- Clumsy, but it's the best way to limit them to caves. if pos.y > 0 then return end local pos_up = {x=pos.x,y=pos.y+1,z=pos.z} local node_up = minetest.get_node_or_nil(pos_up) if not node_up or node_up.name ~= "air" then return end local node_under = minetest.get_node_or_nil({x = pos.x, y = pos.y - 1, z = pos.z}) if not node_under or minetest.get_item_group(node_under.name, "soil") == 0 or (minetest.get_node_light(pos_up, nil) or 99) > light_max then return end minetest.set_node(pos_up, huge_mushroom_cap_node) minetest.set_node(pos, giant_mushroom_stem_node) end }) -- mushroom growth -- huge into giant local giant_mushroom_cap_node = {name = "fun_caves:giant_mushroom_cap"} minetest.register_abm({ nodenames = {"fun_caves:huge_mushroom_cap"}, interval = 9 * time_factor, chance = 2000, action = function(pos, node) if not (pos and node) then return end local pos_up = {x=pos.x,y=pos.y+1,z=pos.z} local node_up = minetest.get_node_or_nil(pos_up) if not node_up or node_up.name ~= "air" then return end -- Check for soil. local node_under = minetest.get_node_or_nil({x = pos.x, y = pos.y - 2, z = pos.z}) if not node_under or minetest.get_item_group(node_under.name, "soil") == 0 or (minetest.get_node_light(pos_up, nil) or 99) > light_max then return end minetest.set_node(pos_up, giant_mushroom_cap_node) minetest.set_node(pos, {name = "fun_caves:giant_mushroom_stem"}) end }) -- Spike spread and death minetest.register_abm({ nodenames = fun_caves.hot_spikes, interval = 3 * time_factor, chance = 300, action = function(pos, node) if not (pos and node) then return end if not (fun_caves.hot_spike and fun_caves.hot_spikes) then return end local spike_num = fun_caves.hot_spike[node.name] if not spike_num then return end if spike_num < #fun_caves.hot_spikes then minetest.set_node(pos, {name=fun_caves.hot_spikes[spike_num+1]}) return end local new_pos = { x = pos.x + math.random(-2, 2), y = pos.y + math.random(-1, 1), z = pos.z + math.random(-2, 2) } local new_node = minetest.get_node_or_nil(new_pos) if not (new_node and spike_air[new_node.name]) then return end local node_under = minetest.get_node_or_nil({x = new_pos.x, y = new_pos.y - 1, z = new_pos.z}) if not (node_under and spike_soil[node_under.name]) then return end minetest.set_node(new_pos, {name = hot_spikes[1]}) end }) ------------------------------------------------------------ -- meteors ------------------------------------------------------------ local last_meteor_strike = 0 -- meteor strikes minetest.register_abm({ nodenames = {"default:dirt_with_grass", "default:dirt_with_dry_grass", 'default:dirt_with_snow'}, neighbors = {"air", 'default:snow'}, interval = 25 * time_factor, catch_up = false, chance = 32767, action = function(pos, node) if not (pos and node) then return end minetest.set_node(pos, {name="fun_caves:meteorite_crater"}) local time = minetest.get_gametime() if time - last_meteor_strike < 10 then return end last_meteor_strike = time local ps = {} local players = minetest.get_connected_players() if not (players and type(players) == 'table') then return end for i = 1, #players do local pp = players[i]:getpos() local pd = vector.subtract(pp, pos) if pp and pp.y > 0 and math.abs(pd.x) < 1000 and math.abs(pd.z) < 1000 then local sky = {} sky.bgcolor, sky.type, sky.textures = players[i]:get_sky() ps[#ps+1] = { p = players[i], sky = sky } players[i]:set_sky(0xffffff, "plain") end end minetest.after(1, function() for i = 1, #ps do ps[i].p:set_sky(ps[i].sky.bgcolor, ps[i].sky.type, ps[i].sky.textures) end end) end }) -- Remove old craters. minetest.register_abm({ nodenames = {"fun_caves:meteorite_crater"}, interval = 3 * time_factor, chance = 330, action = function(pos, node) if not (pos and node) then return end minetest.set_node(pos, {name="default:dirt"}) end }) ------------------------------------------------------------ -- explosive functions ------------------------------------------------------------ -- All of this is copied from TNT, but modified to leave stone intact. -- Fill a list with data for content IDs, after all nodes are registered local cid_data = {} minetest.after(0, function() for name, def in pairs(minetest.registered_nodes) do cid_data[minetest.get_content_id(name)] = { name = name, --drops = def.drops, flammable = def.groups.flammable, choppy = def.groups.choppy, fleshy = def.groups.fleshy, snappy = def.groups.snappy, on_blast = def.on_blast, } end -- This also has to be done after content ids are established. for i = 1, 4 do fungi_to_explode[minetest.get_content_id("fun_caves:fungal_tree_leaves_"..i)] = true end fungi_to_explode[minetest.get_content_id('fun_caves:fungal_tree_fruit')] = true fungi_to_explode[minetest.get_content_id('fun_caves:giant_mushroom_cap')] = true fungi_to_explode[minetest.get_content_id('fun_caves:giant_mushroom_stem')] = true fungi_to_explode[minetest.get_content_id('fun_caves:huge_mushroom_cap')] = true end) local function add_effects(pos, radius) if not (pos and radius and type(radius) == 'number') then return end minetest.add_particlespawner({ amount = 128, time = 1, minpos = vector.subtract(pos, radius / 2), maxpos = vector.add(pos, radius / 2), minvel = {x=-20, y=-20, z=-20}, maxvel = {x=20, y=20, z=20}, minacc = vector.new(), maxacc = vector.new(), minexptime = 1, maxexptime = 3, minsize = 8, maxsize = 16, texture = "tnt_smoke.png", }) end local function destroy(pos, cid) if not (pos and cid) then return end local def = cid_data[cid] if not (def and def.flammable and def.flammable > 0 and not minetest.is_protected(pos, "")) then return end if def.on_blast then def.on_blast(vector.new(pos), 1) return end if def.snappy == nil and def.choppy == nil and def.fleshy == nil and def.name ~= "fire:basic_flame" then return end if exploding_fungi or fungi_to_explode[cid] then minetest.remove_node(pos) end end local function explode(pos, radius) if not (pos and radius and type(radius) == 'number') then return end local pos = vector.round(pos) local vm = VoxelManip() local p1 = vector.subtract(pos, radius) local p2 = vector.add(pos, radius) local minp, maxp = vm:read_from_map(p1, p2) local a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp}) local data = vm:get_data() local p = {} local c_air = minetest.get_content_id("air") local math_random = math.random for z = -radius, radius do for y = -radius, 4*radius do local vi = a:index(pos.x + (-radius), pos.y + y, pos.z + z) for x = -radius, radius do if (x * x) + (y * y / 4) + (z * z) <= (radius * radius) + math_random(-radius, radius) then local cid = data[vi] p.x = pos.x + x p.y = pos.y + y p.z = pos.z + z if cid ~= c_air then destroy(p, cid) end end vi = vi + 1 end end end end local function calc_velocity(pos1, pos2, old_vel, power) if not (pos1 and pos2 and old_vel and power) then return end local vel = vector.direction(pos1, pos2) vel = vector.normalize(vel) vel = vector.multiply(vel, power) -- Divide by distance local dist = vector.distance(pos1, pos2) dist = math.max(dist, 1) vel = vector.divide(vel, dist) -- Add old velocity vel = vector.add(vel, old_vel) return vel end local function entity_physics(pos, radius) if not (pos and radius and type(radius) == 'number') then return end -- Make the damage radius larger than the destruction radius radius = radius * 2 local objs = minetest.get_objects_inside_radius(pos, radius) if not (objs and type(objs) == 'table') then return end local math_max = math.max local vector_distance = vector.distance for _, obj in pairs(objs) do local obj_pos = obj:getpos() local obj_vel = obj:getvelocity() local dist = math_max(1, vector_distance(pos, obj_pos)) if obj_vel ~= nil then obj:setvelocity(calc_velocity(pos, obj_pos, obj_vel, radius * 10)) end local damage = (4 / dist) * radius obj:set_hp(obj:get_hp() - damage) end end fun_caves.soft_boom = function(pos) if not pos then return end local node = minetest.get_node_or_nil(pos) if not node then return end if exploding_fungi then minetest.sound_play("tnt_explode", {pos=pos, gain=1.5, max_hear_distance=2*64}) end local radius = 5 minetest.remove_node(pos) explode(pos, radius) if exploding_fungi then entity_physics(pos, radius) add_effects(pos, radius) end end --local function burn(pos) -- minetest.get_node_timer(pos):start(1) --end ----------------------------------------------- -- testing only -- remove before distribution ----------------------------------------------- -- Mushroom spread and death --minetest.register_abm({ -- nodenames = mushrooms, -- interval = 1 * time_factor, -- chance = 50, -- action = function(pos, node) -- if minetest.get_node_light(pos, nil) >= light_max + 2 then -- minetest.remove_node(pos) -- return -- end -- local random = { -- x = pos.x + math.random(-2, 2), -- y = pos.y + math.random(-1, 1), -- z = pos.z + math.random(-2, 2) -- } -- local random_node = minetest.get_node_or_nil(random) -- if not random_node or random_node.name ~= "air" then -- return -- end -- local node_under = minetest.get_node_or_nil({x = random.x, -- y = random.y - 1, z = random.z}) -- if not node_under then -- return -- end -- -- if (minetest.get_item_group(node_under.name, "soil") ~= 0 or -- minetest.get_item_group(node_under.name, "tree") ~= 0) and -- minetest.get_node_light(pos, 0.5) <= light_max and -- minetest.get_node_light(random, 0.5) <= light_max then -- minetest.set_node(random, {name = node.name}) -- end -- end --})