This commit is contained in:
Elias Fleckenstein 2021-01-17 10:12:11 +01:00
commit be5a228bad
7 changed files with 477 additions and 247 deletions

@ -1,8 +1,8 @@
# MineClone 2
An unofficial Minecraft-like game for Minetest. Forked from MineClone by davedevils.
Developed by Wuzzy and contributors. Not developed or endorsed by Mojang AB.
Developed by many people. Not developed or endorsed by Mojang AB.
Version: 0.69.1
Version: 0.70.0
### Gameplay
You start in a randomly-generated world made entirely of cubes. You can explore
@ -195,7 +195,7 @@ Please report all bugs and missing Minecraft features here:
There are so many people to list (sorry). Check out the respective mod directories for details. This section is only a rough overview of the core authors of this game.
### Coding
* [Wuzzy](https://forum.minetest.net/memberlist.php?mode=viewprofile&u=3082): Main programmer of most mods
* [Wuzzy](https://forum.minetest.net/memberlist.php?mode=viewprofile&u=3082): Main programmer of most mods (retired)
* davedevils: Creator of MineClone on which MineClone 2 is based on
* [ex-bart](https://github.com/ex-bart): Redstone comparators
* [Rootyjr](https://github.com/Rootyjr): Fishing rod and bugfixes

@ -65,7 +65,7 @@ minetest.register_globalstep(function(dtime)
if is_immortal or not enable_damage then
-- If damage is disabled, we can't kill players.
-- So we just teleport the player back to spawn.
local spawn = mcl_spawn.get_spawn_pos(player)
local spawn = mcl_spawn.get_player_spawn_pos(player)
player:set_pos(spawn)
mcl_worlds.dimension_change(player, mcl_worlds.pos_to_dimension(spawn))
minetest.chat_send_player(player:get_player_name(), S("The void is off-limits to you!"))

@ -284,6 +284,7 @@ if realtime then
mcl_observers.set_node = minetest.set_node
mcl_observers.swap_node = minetest.swap_node
mcl_observers.remove_node = minetest.remove_node
mcl_observers.bulk_set_node = minetest.bulk_set_node
minetest.add_node=function(pos,node)
mcl_observers.add_node(pos,node)
@ -393,6 +394,35 @@ if realtime then
mcl_observers.observer_activate({x=pos.x,y=pos.y+1,z=pos.z})
end
end
minetest.bulk_set_node=function(lst, node)
mcl_observers.bulk_set_node(lst, node)
for _, pos in pairs(lst) do
local n=minetest.get_node({x=pos.x+1,y=pos.y,z=pos.z})
if n and n.name and string.sub(n.name,1,24)=="mcl_observers:observer_o" and minetest.facedir_to_dir(n.param2).x==-1 then
mcl_observers.observer_activate({x=pos.x+1,y=pos.y,z=pos.z})
end
n=minetest.get_node({x=pos.x-1,y=pos.y,z=pos.z})
if n and n.name and string.sub(n.name,1,24)=="mcl_observers:observer_o" and minetest.facedir_to_dir(n.param2).x==1 then
mcl_observers.observer_activate({x=pos.x-1,y=pos.y,z=pos.z})
end
n=minetest.get_node({x=pos.x,y=pos.y,z=pos.z+1})
if n and n.name and string.sub(n.name,1,24)=="mcl_observers:observer_o" and minetest.facedir_to_dir(n.param2).z==-1 then
mcl_observers.observer_activate({x=pos.x,y=pos.y,z=pos.z+1})
end
n=minetest.get_node({x=pos.x,y=pos.y,z=pos.z-1})
if n and n.name and string.sub(n.name,1,24)=="mcl_observers:observer_o" and minetest.facedir_to_dir(n.param2).z==1 then
mcl_observers.observer_activate({x=pos.x,y=pos.y,z=pos.z-1})
end
n=minetest.get_node({x=pos.x,y=pos.y-1,z=pos.z})
if n and n.name and string.sub(n.name,1,24)=="mcl_observers:observer_u" then
mcl_observers.observer_activate({x=pos.x,y=pos.y-1,z=pos.z})
end
n=minetest.get_node({x=pos.x,y=pos.y+1,z=pos.z})
if n and n.name and string.sub(n.name,1,24)=="mcl_observers:observer_d" then
mcl_observers.observer_activate({x=pos.x,y=pos.y+1,z=pos.z})
end
end
end
else -- if realtime then ^^^ else:
minetest.register_abm({

@ -1,23 +1,23 @@
local S = minetest.get_translator("mcl_beds")
local reverse = true
local function destruct_bed(pos, is_top)
local node = minetest.get_node(pos)
local other
local function destruct_bed(pos, oldnode)
local node = oldnode or minetest.get_node(pos)
if not node then return end
local dir = minetest.facedir_to_dir(node.param2)
if is_top then
other = vector.subtract(pos, dir)
else
other = vector.add(pos, dir)
end
if reverse then
reverse = not reverse
minetest.remove_node(other)
minetest.check_for_falling(other)
else
reverse = not reverse
local pos2, node2
if string.sub(node.name, -4) == "_top" then
pos2 = vector.subtract(pos, dir)
node2 = minetest.get_node(pos2)
if node2 and string.sub(node2.name, -7) == "_bottom" then
minetest.remove_node(pos2)
end
minetest.check_for_falling(pos)
elseif string.sub(node.name, -7) == "_bottom" then
pos2 = vector.add(pos, dir)
node2 = minetest.get_node(pos2)
if node2 and string.sub(node2.name, -4) == "_top" then
minetest.remove_node(pos2)
end
end
end
@ -139,10 +139,7 @@ function mcl_beds.register_bed(name, def)
return itemstack
end,
on_destruct = function(pos)
destruct_bed(pos, false)
kick_player_after_destruct(pos)
end,
after_destruct = destruct_bed,
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
mcl_beds.on_rightclick(pos, clicker, false)
@ -205,7 +202,7 @@ function mcl_beds.register_bed(name, def)
_mcl_hardness = 0.2,
_mcl_blast_resistance = 1,
sounds = def.sounds or default_sounds,
drop = name .. "_bottom",
drop = "",
node_box = node_box_top,
selection_box = selection_box_top,
collision_box = collision_box_top,
@ -214,10 +211,7 @@ function mcl_beds.register_bed(name, def)
return itemstack
end,
on_rotate = false,
on_destruct = function(pos)
destruct_bed(pos, true)
kick_player_after_destruct(pos)
end,
after_destruct = destruct_bed,
})
minetest.register_alias(name, name .. "_bottom")

@ -21,6 +21,21 @@ local destroy_portal = function(pos)
end
end
local ep_scheme = {
{ o={x=0, y=0, z=1}, p=3 },
{ o={x=0, y=0, z=2}, p=3 },
{ o={x=0, y=0, z=3}, p=3 },
{ o={x=1, y=0, z=4}, p=0 },
{ o={x=2, y=0, z=4}, p=0 },
{ o={x=3, y=0, z=4}, p=0 },
{ o={x=4, y=0, z=3}, p=1 },
{ o={x=4, y=0, z=2}, p=1 },
{ o={x=4, y=0, z=1}, p=1 },
{ o={x=3, y=0, z=0}, p=2 },
{ o={x=2, y=0, z=0}, p=2 },
{ o={x=1, y=0, z=0}, p=2 },
}
-- End portal
minetest.register_node("mcl_portals:portal_end", {
description = S("End Portal"),
@ -52,7 +67,7 @@ minetest.register_node("mcl_portals:portal_end", {
paramtype = "light",
sunlight_propagates = true,
use_texture_alpha = true,
walkable = true,
walkable = false,
diggable = false,
pointable = false,
buildable_to = false,
@ -107,97 +122,19 @@ end
-- Check if pos is part of a valid end portal frame, filled with eyes of ender.
local function check_end_portal_frame(pos)
-- Check if pos has an end portal frame with eye of ender
local eframe = function(pos, param2)
local node = minetest.get_node(pos)
if node.name == "mcl_portals:end_portal_frame_eye" then
if param2 == nil or node.param2 == param2 then
return true, node
for i = 1, 12 do
local pos0 = vector.subtract(pos, ep_scheme[i].o)
local portal = true
for j = 1, 12 do
local p = vector.add(pos0, ep_scheme[j].o)
local node = minetest.get_node(p)
if not node or node.name ~= "mcl_portals:end_portal_frame_eye" or node.param2 ~= ep_scheme[j].p then
portal = false
break
end
end
return false
end
-- Step 1: Find a row of 3 end portal frames with eyes, all facing the same direction
local streak = 0
local streak_start, streak_end, streak_start_node, streak_end_node
local last_param2
local axes = { "x", "z" }
for a=1, #axes do
local axis = axes[a]
for b=pos[axis]-2, pos[axis]+2 do
local cpos = table.copy(pos)
cpos[axis] = b
local e, node = eframe(cpos, last_param2)
if e then
last_param2 = node.param2
streak = streak + 1
if streak == 1 then
streak_start = table.copy(pos)
streak_start[axis] = b
streak_start_node = node
elseif streak == 3 then
streak_end = table.copy(pos)
streak_end[axis] = b
streak_end_node = node
break
end
else
streak = 0
last_param2 = nil
end
end
if streak_end then
break
end
streak = 0
last_param2 = nil
end
-- Has a row been found?
if streak_end then
-- Step 2: Using the known facedir, check the remaining spots in which we expect
-- “eyed” end portal frames.
local dir = minetest.facedir_to_dir(streak_start_node.param2)
if dir.x ~= 0 then
for i=1, 3 do
if not eframe({x=streak_start.x + i*dir.x, y=streak_start.y, z=streak_start.z - 1}) then
return false
end
if not eframe({x=streak_start.x + i*dir.x, y=streak_start.y, z=streak_end.z + 1}) then
return false
end
if not eframe({x=streak_start.x + 4*dir.x, y=streak_start.y, z=streak_start.z + i-1}) then
return false
end
end
-- All checks survived! We have a valid portal!
local k
if dir.x > 0 then
k = 1
else
k = -3
end
return true, { x = streak_start.x + k, y = streak_start.y, z = streak_start.z }
elseif dir.z ~= 0 then
for i=1, 3 do
if not eframe({x=streak_start.x - 1, y=streak_start.y, z=streak_start.z + i*dir.z}) then
return false
end
if not eframe({x=streak_end.x + 1, y=streak_start.y, z=streak_start.z + i*dir.z}) then
return false
end
if not eframe({x=streak_start.x + i-1, y=streak_start.y, z=streak_start.z + 4*dir.z}) then
return false
end
end
local k
if dir.z > 0 then
k = 1
else
k = -3
end
-- All checks survived! We have a valid portal!
return true, { x = streak_start.x, y = streak_start.y, z = streak_start.z + k }
if portal then
return true, {x=pos0.x+1, y=pos0.y, z=pos0.z+1}
end
end
return false
@ -222,82 +159,92 @@ local function end_portal_area(pos, destroy)
minetest.bulk_set_node(posses, {name=name})
end
function mcl_portals.end_teleport(obj, pos)
if not obj then return end
local pos = pos or obj:get_pos()
if not pos then return end
local dim = mcl_worlds.pos_to_dimension(pos)
local target
if dim == "end" then
-- End portal in the End:
-- Teleport back to the player's spawn or world spawn in the Overworld.
if obj:is_player() then
target = mcl_spawn.get_player_spawn_pos(obj)
end
target = target or mcl_spawn.get_world_spawn_pos(obj)
else
-- End portal in any other dimension:
-- Teleport to the End at a fixed position and generate a
-- 5×5 obsidian platform below.
local platform_pos = mcl_vars.mg_end_platform_pos
-- force emerge of target1 area
minetest.get_voxel_manip():read_from_map(platform_pos, platform_pos)
if not minetest.get_node_or_nil(platform_pos) then
minetest.emerge_area(vector.subtract(platform_pos, 3), vector.add(platform_pos, 3))
end
-- Build destination
local function check_and_build_end_portal_destination(pos)
local n = minetest.get_node_or_nil(pos)
if n and n.name ~= "mcl_core:obsidian" then
build_end_portal_destination(pos)
minetest.after(2, check_and_build_end_portal_destination, pos)
elseif not n then
minetest.after(1, check_and_build_end_portal_destination, pos)
end
end
local platform
build_end_portal_destination(platform_pos)
check_and_build_end_portal_destination(platform_pos)
target = table.copy(platform_pos)
target.y = target.y + 1
end
-- Teleport
obj:set_pos(target)
if obj:is_player() then
-- Look towards the main End island
if dim ~= "end" then
obj:set_look_horizontal(math.pi/2)
end
mcl_worlds.dimension_change(obj, mcl_worlds.pos_to_dimension(target))
minetest.sound_play("mcl_portals_teleport", {pos=target, gain=0.5, max_hear_distance = 16}, true)
end
end
function mcl_portals.end_portal_teleport(pos, node)
for _,obj in ipairs(minetest.get_objects_inside_radius(pos, 1)) do
local lua_entity = obj:get_luaentity() --maikerumine added for objects to travel
if obj:is_player() or lua_entity then
local objpos = obj:get_pos()
if objpos == nil then
return
end
-- Check if object is actually in portal.
objpos.y = math.ceil(objpos.y)
if minetest.get_node(objpos).name ~= "mcl_portals:portal_end" then
return
end
mcl_portals.end_teleport(obj, objpos)
end
end
end
minetest.register_abm({
label = "End portal teleportation",
nodenames = {"mcl_portals:portal_end"},
interval = 0.1,
chance = 1,
action = function(pos, node)
for _,obj in ipairs(minetest.get_objects_inside_radius(pos, 1)) do
local lua_entity = obj:get_luaentity() --maikerumine added for objects to travel
if obj:is_player() or lua_entity then
local dim = mcl_worlds.pos_to_dimension(pos)
local objpos = obj:get_pos()
if objpos == nil then
return
end
-- Check if object is actually in portal.
objpos.y = math.ceil(objpos.y)
if minetest.get_node(objpos).name ~= "mcl_portals:portal_end" then
return
end
local target
if dim == "end" then
-- End portal in the End:
-- Teleport back to the player's spawn or world spawn in the Overworld.
if obj:is_player() then
_, target = mcl_spawn.spawn(obj)
end
target = target or mcl_spawn.get_world_spawn_pos(obj)
else
-- End portal in any other dimension:
-- Teleport to the End at a fixed position and generate a
-- 5×5 obsidian platform below.
local platform_pos = mcl_vars.mg_end_platform_pos
-- force emerge of target1 area
minetest.get_voxel_manip():read_from_map(platform_pos, platform_pos)
if not minetest.get_node_or_nil(platform_pos) then
minetest.emerge_area(vector.subtract(platform_pos, 3), vector.add(platform_pos, 3))
end
-- Build destination
local function check_and_build_end_portal_destination(pos)
local n = minetest.get_node_or_nil(pos)
if n and n.name ~= "mcl_core:obsidian" then
build_end_portal_destination(pos)
minetest.after(2, check_and_build_end_portal_destination, pos)
elseif not n then
minetest.after(1, check_and_build_end_portal_destination, pos)
end
end
local platform
build_end_portal_destination(platform_pos)
check_and_build_end_portal_destination(platform_pos)
target = table.copy(platform_pos)
target.y = target.y + 1
end
-- Teleport
obj:set_pos(target)
if obj:is_player() then
-- Look towards the main End island
if dim ~= "end" then
obj:set_look_horizontal(math.pi/2)
end
mcl_worlds.dimension_change(obj, mcl_worlds.pos_to_dimension(target))
minetest.sound_play("mcl_portals_teleport", {pos=target, gain=0.5, max_hear_distance = 16}, true)
end
end
end
end,
action = mcl_portals.end_portal_teleport,
})
local rotate_frame, rotate_frame_eye

@ -164,3 +164,37 @@ minetest.register_globalstep(function(dtime)
end
end
end)
-- Don't change HP if the player falls in the water or through End Portal:
minetest.register_on_player_hpchange(function(player, hp_change, reason)
if reason and reason.type == "fall" and player then
local pos = player:get_pos()
local node = minetest.get_node(pos)
local velocity = player:get_velocity() or player:get_player_velocity() or {x=0,y=-10,z=0}
local v_axis_max = math.max(math.abs(velocity.x), math.abs(velocity.y), math.abs(velocity.z))
local step = {x = velocity.x / v_axis_max, y = velocity.y / v_axis_max, z = velocity.z / v_axis_max}
for i = 1, math.ceil(v_axis_max/5)+1 do -- trace at least 1/5 of the way per second
if not node or node.name == "ignore" then
minetest.get_voxel_manip():read_from_map(pos, pos)
node = minetest.get_node(pos)
end
if node then
if minetest.registered_nodes[node.name].walkable then
return hp_change
end
if minetest.get_item_group(node.name, "water") ~= 0 then
return 0
end
if node.name == "mcl_portals:portal_end" then
if mcl_portals and mcl_portals.end_teleport then
mcl_portals.end_teleport(player)
end
return 0
end
end
pos = vector.add(pos, step)
node = minetest.get_node(pos)
end
end
return hp_change
end, true)

@ -2,7 +2,47 @@ mcl_spawn = {}
local S = minetest.get_translator("mcl_spawn")
local mg_name = minetest.get_mapgen_setting("mg_name")
local storage = minetest.get_mod_storage()
-- Parameters
-------------
local respawn_search_interval = 30 -- seconds
local respawn_search_initial_delay = 30 -- seconds
local node_groups_white_list = {"group:soil"}
local biomes_white_list = {
"ColdTaiga",
"Taiga",
"MegaTaiga",
"MegaSpruceTaiga",
"Plains",
"SunflowerPlains",
"Forest",
"FlowerForest",
"BirchForest",
"BirchForestM",
"Jungle",
"JungleM",
"JungleEdge",
"JungleEdgeM",
"Savanna",
"SavannaM",
}
-- Resolution of search grid in nodes.
local res = 64
local half_res = 32 -- for emerge areas around the position
local alt_min = -10
local alt_max = 200
-- Number of points checked in the square search grid (edge * edge).
local checks = 128 * 128
-- Starting point for biome checks. This also sets the y co-ordinate for all
-- points checked, so the suitable biomes must be active at this y.
local start_pos = minetest.setting_get_pos("static_spawnpoint") or {x = 0, y = 8, z = 0}
-- Table of suitable biomes
local biome_ids = {}
-- Bed spawning offsets
local node_search_list =
{
--[[1]] {x = 0, y = 0, z = -1}, --
@ -19,41 +59,205 @@ local node_search_list =
--[[C]] {x = 0, y = 1, z = 1}, --
}
local cached_world_spawn
-- End of parameters
--------------------
-- Initial variables
local success = storage:get_int("mcl_spawn_success")==1
local searched = (storage:get_int("mcl_spawn_searched")==1) or mg_name == "v6" or mg_name == "singlenode" or minetest.settings:get("static_spawnpoint")
local wsp = minetest.string_to_pos(storage:get_string("mcl_spawn_world_spawn_point")) or {} -- world spawn position
local check = storage:get_int("mcl_spawn_check") or 0
local cp = minetest.string_to_pos(storage:get_string("mcl_spawn_cp")) or {x=start_pos.x, y=start_pos.y, z=start_pos.z}
local edge_len = storage:get_int("mcl_spawn_edge_len") or 1
local edge_dist = storage:get_int("mcl_spawn_edge_dist") or 0
local dir_step = storage:get_int("mcl_spawn_dir_step") or 0
local dir_ind = storage:get_int("mcl_spawn_dir_ind") or 1
-- Get world 'mapgen_limit' and 'chunksize' to calculate 'spawn_limit'.
-- This accounts for how mapchunks are not generated if they or their shell exceed
-- 'mapgen_limit'.
local mapgen_limit = tonumber(minetest.get_mapgen_setting("mapgen_limit"))
local chunksize = tonumber(minetest.get_mapgen_setting("chunksize"))
local spawn_limit = math.max(mapgen_limit - (chunksize + 1) * 16, 0)
--Functions
-----------
local function get_far_node(pos)
local node = minetest.get_node(pos)
if node.name ~= "ignore" then
return node
end
minetest.get_voxel_manip():read_from_map(pos, pos)
return minetest.get_node(pos)
end
local function good_for_respawn(pos, player)
local pos0 = {x = pos.x, y = pos.y - 1, z = pos.z}
local pos1 = {x = pos.x, y = pos.y, z = pos.z}
local pos2 = {x = pos.x, y = pos.y + 1, z = pos.z}
local node0 = get_far_node(pos0)
local node1 = get_far_node(pos1)
local node2 = get_far_node(pos2)
local nn0, nn1, nn2 = node0.name, node1.name, node2.name
if minetest.get_item_group(nn0, "destroys_items") ~=0
or minetest.get_item_group(nn1, "destroys_items") ~=0
or minetest.get_item_group(nn2, "destroys_items") ~=0
or minetest.get_item_group(nn0, "portal") ~=0
or minetest.get_item_group(nn1, "portal") ~=0
or minetest.get_item_group(nn2, "portal") ~=0
or minetest.is_protected(pos0, player or "")
or minetest.is_protected(pos1, player or "")
or minetest.is_protected(pos2, player or "")
or (not player and minetest.get_node_light(pos1, 0.5) < 8)
or (not player and minetest.get_node_light(pos2, 0.5) < 8)
then
return false
end
local def0 = minetest.registered_nodes[nn0]
local def1 = minetest.registered_nodes[nn1]
local def2 = minetest.registered_nodes[nn2]
return def0.walkable and (not def1.walkable) and (not def2.walkable) and
(def1.damage_per_second == nil or def2.damage_per_second <= 0) and
(def1.damage_per_second == nil or def2.damage_per_second <= 0)
end
local function can_find_tree(pos1)
local trees = minetest.find_nodes_in_area(vector.subtract(pos1,half_res), vector.add(pos1,half_res), {"group:tree"}, false)
for _, pos2 in ipairs(trees) do
if not minetest.is_protected(pos2, "") then
if pos2.x < pos1.x then
pos2.x = pos2.x + 1
elseif pos2.x > pos1.x then
pos2.x = pos2.x - 1
end
if pos2.z < pos1.z then
pos2.z = pos2.z + 1
elseif pos2.z > pos1.z then
pos2.z = pos2.z - 1
end
local way = minetest.find_path(pos1, pos2, res, 1, 3, "A*_noprefetch")
if way then
return true
end
end
end
return false
end
local function next_pos()
if edge_dist >= edge_len then
edge_dist = 1
dir_ind = (dir_ind % 4) + 1
dir_step = dir_step + 1
edge_len = math.floor(dir_step / 2) + 1
else
edge_dist = edge_dist + 1
end
if dir_ind==1 then
cp.z = cp.z + res
elseif dir_ind==2 then
cp.x = cp.x - res
elseif dir_ind==3 then
cp.z = cp.z - res
else
cp.x = cp.x + res
end
end
-- Spawn position search
local function next_biome()
while check <= checks do
local biome_data = minetest.get_biome_data(cp)
-- Sometimes biome_data is nil
local biome = biome_data and biome_data.biome
if biome then
minetest.log("verbose", "[mcl_spawn] Search white-listed biome at "..minetest.pos_to_string(cp)..": "..minetest.get_biome_name(biome))
for _, biome_id in ipairs(biome_ids) do
if biome == biome_id then
cp.y = minetest.get_spawn_level(cp.x, cp.z) or start_pos.y
if cp.y then
wsp = {x = cp.x, y = cp.y, z = cp.z}
return true
end
break
end
end
end
next_pos()
-- Check for position being outside world edge
if math.abs(cp.x) > spawn_limit or math.abs(cp.z) > spawn_limit then
check = checks + 1
return false
end
check = check + 1
end
return false
end
local function ecb_search_continue(blockpos, action, calls_remaining, param)
if calls_remaining <= 0 then
local pos1 = {x = wsp.x-half_res, y = alt_min, z = wsp.z-half_res}
local pos2 = {x = wsp.x+half_res, y = alt_max, z = wsp.z+half_res}
local nodes = minetest.find_nodes_in_area_under_air(pos1, pos2, node_groups_white_list)
minetest.log("verbose", "[mcl_spawn] Data emerge callback: "..minetest.pos_to_string(wsp).." - "..tostring(nodes and #nodes) .. " node(s) found under air")
if nodes then
for i=1, #nodes do
wsp = nodes[i]
if wsp then
wsp.y = wsp.y + 1
if good_for_respawn(wsp) and can_find_tree(wsp) then
minetest.log("action", "[mcl_spawn] Dynamic world spawn determined to be "..minetest.pos_to_string(wsp))
searched = true
success = true
return
end
end
end
end
next_pos()
mcl_spawn.search()
end
end
function mcl_spawn.search()
if not next_biome() or check > checks then
return false
end
check = check + 1
if not wsp.y then
wsp.y = 8
end
local pos1 = {x = wsp.x-half_res, y = alt_min, z = wsp.z-half_res}
local pos2 = {x = wsp.x+half_res, y = alt_max, z = wsp.z+half_res}
minetest.emerge_area(pos1, pos2, ecb_search_continue)
end
mcl_spawn.get_world_spawn_pos = function()
local spawn
spawn = minetest.setting_get_pos("static_spawnpoint")
if spawn then
return spawn
end
if cached_world_spawn then
return cached_world_spawn
end
-- 32 attempts to find a suitable spawn point
spawn = { x=math.random(-16, 16), y=8, z=math.random(-16, 16) }
for i=1, 32 do
local y = minetest.get_spawn_level(spawn.x, spawn.z)
if y then
spawn.y = y
cached_world_spawn = spawn
minetest.log("action", "[mcl_spawn] Dynamic world spawn determined to be "..minetest.pos_to_string(spawn))
return spawn
end
-- Random walk
spawn.x = spawn.x + math.random(-64, 64)
spawn.z = spawn.z + math.random(-64, 64)
if success then
return wsp
end
minetest.log("action", "[mcl_spawn] Failed to determine dynamic world spawn!")
-- Use dummy position if nothing found
return { x=math.random(-16, 16), y=8, z=math.random(-16, 16) }
return start_pos
end
-- Returns a spawn position of player.
-- If player is nil or not a player, a world spawn point is returned.
-- The second return value is true if returned spawn point is player-chosen,
-- false otherwise.
mcl_spawn.get_spawn_pos = function(player)
mcl_spawn.get_bed_spawn_pos = function(player)
local spawn, custom_spawn = nil, false
if player ~= nil and player:is_player() then
local attr = player:get_meta():get_string("mcl_beds:spawn")
@ -101,29 +305,8 @@ mcl_spawn.set_spawn_pos = function(player, pos, message)
return spawn_changed
end
local function get_far_node(pos)
local node = minetest.get_node(pos)
if node.name ~= "ignore" then
return node
end
minetest.get_voxel_manip():read_from_map(pos, pos)
return minetest.get_node(pos)
end
local function good_for_respawn(pos)
local node0 = get_far_node({x = pos.x, y = pos.y - 1, z = pos.z})
local node1 = get_far_node({x = pos.x, y = pos.y, z = pos.z})
local node2 = get_far_node({x = pos.x, y = pos.y + 1, z = pos.z})
local def0 = minetest.registered_nodes[node0.name]
local def1 = minetest.registered_nodes[node1.name]
local def2 = minetest.registered_nodes[node2.name]
return def0.walkable and (not def1.walkable) and (not def2.walkable) and
(def1.damage_per_second == nil or def2.damage_per_second <= 0) and
(def1.damage_per_second == nil or def2.damage_per_second <= 0)
end
mcl_spawn.spawn = function(player)
local pos, custom_spawn = mcl_spawn.get_spawn_pos(player)
mcl_spawn.get_player_spawn_pos = function(player)
local pos, custom_spawn = mcl_spawn.get_bed_spawn_pos(player)
if pos and custom_spawn then
-- Check if bed is still there
local node_bed = get_far_node(pos)
@ -134,7 +317,7 @@ mcl_spawn.spawn = function(player)
player:get_meta():set_string("mcl_beds:spawn", "")
end
minetest.chat_send_player(player:get_player_name(), S("Your spawn bed was missing or blocked."))
return false
return mcl_spawn.get_world_spawn_pos(), false
end
-- Find spawning position on/near the bed free of solid or damaging blocks iterating a square spiral 15x15:
@ -151,17 +334,59 @@ mcl_spawn.spawn = function(player)
else -- dir.x == 1
offset = {x = -o.z, y = o.y, z = o.x}
end
local spawn_pos = vector.add(pos, offset)
if good_for_respawn(spawn_pos) then
player:set_pos(spawn_pos)
return true, spawn_pos
local player_spawn_pos = vector.add(pos, offset)
if good_for_respawn(player_spawn_pos, player:get_player_name()) then
return player_spawn_pos, true
end
end
-- We here if we didn't find suitable place for respawn:
return false
-- We here if we didn't find suitable place for respawn
end
return mcl_spawn.get_world_spawn_pos(), false
end
mcl_spawn.spawn = function(player)
local pos, in_bed = mcl_spawn.get_player_spawn_pos(player)
player:set_pos(pos)
return in_bed or success
end
-- Respawn player at specified respawn position
minetest.register_on_respawnplayer(mcl_spawn.spawn)
function mcl_spawn.shadow_worker()
if #biome_ids < 1 then
for _, biome_name in pairs(biomes_white_list) do
table.insert(biome_ids, minetest.get_biome_id(biome_name))
end
end
if not searched then
searched = true
mcl_spawn.search()
minetest.log("action", "[mcl_spawn] Started world spawn point search")
end
if success and ((not good_for_respawn(wsp)) or (not can_find_tree(wsp))) then
success = false
minetest.log("action", "[mcl_spawn] World spawn position isn't safe anymore: "..minetest.pos_to_string(wsp))
mcl_spawn.search()
end
minetest.after(respawn_search_interval, mcl_spawn.shadow_worker)
end
minetest.after(respawn_search_initial_delay, function()
mcl_spawn.shadow_worker()
minetest.register_on_shutdown(function()
storage:set_int("mcl_spawn_success", success and 1 or 0)
if wsp and wsp.x then
storage:set_string("mcl_spawn_world_spawn_point", minetest.pos_to_string(wsp))
end
storage:set_int("mcl_spawn_searched", searched and 1 or 0)
storage:set_int("mcl_spawn_check", check)
storage:set_string("mcl_spawn_cp", minetest.pos_to_string(cp))
storage:set_int("mcl_spawn_edge_len", edge_len)
storage:set_int("mcl_spawn_edge_dist", edge_dist)
storage:set_int("mcl_spawn_dir_step", dir_step)
storage:set_int("mcl_spawn_dir_ind", dir_ind)
end)
end)