From daede2a183ed3f36699c11935d199389e87c80a2 Mon Sep 17 00:00:00 2001 From: kay27 Date: Wed, 9 Sep 2020 14:35:44 +0400 Subject: [PATCH] Improve around-the-bed-respawn place search algorithm: square spiral up to 7 x/z nodes, thanks anon5, https://git.minetest.land/Wuzzy/MineClone2/issues/785 --- mods/ITEMS/mcl_beds/functions.lua | 12 +----- mods/PLAYER/mcl_spawn/init.lua | 66 ++++++++++++++++++++----------- 2 files changed, 46 insertions(+), 32 deletions(-) diff --git a/mods/ITEMS/mcl_beds/functions.lua b/mods/ITEMS/mcl_beds/functions.lua index 1d7845bfc..d7bf13bc8 100644 --- a/mods/ITEMS/mcl_beds/functions.lua +++ b/mods/ITEMS/mcl_beds/functions.lua @@ -151,10 +151,8 @@ local function lay_down(player, pos, bed_pos, state, skip) else local n1 = minetest.get_node({x = bed_pos.x, y = bed_pos.y + 1, z = bed_pos.z}) local n2 = minetest.get_node({x = bed_pos2.x, y = bed_pos2.y + 1, z = bed_pos2.z}) - local n3 = minetest.get_node({x = bed_pos.x, y = bed_pos.y + 2, z = bed_pos.z}) local def1 = minetest.registered_nodes[n1.name] local def2 = minetest.registered_nodes[n2.name] - local def3 = minetest.registered_nodes[n3.name] if def1.walkable or def2.walkable then minetest.chat_send_player(name, S("You can't sleep, the bed is obstructed!")) return false @@ -165,14 +163,8 @@ local function lay_down(player, pos, bed_pos, state, skip) local spawn_changed = false if minetest.get_modpath("mcl_spawn") then - local spos - if def3.walkable then -- no place for spawning in bed - use player's current pos (near the bed) - spos = table.copy(pos) - else - spos = table.copy(bed_pos) - spos.y = spos.y + 0.1 - end - spawn_changed = mcl_spawn.set_spawn_pos(player, spos) -- save respawn position when entering bed + -- save respawn position when entering bed + spawn_changed = mcl_spawn.set_spawn_pos(player, bed_pos, false) end -- Check day of time and weather diff --git a/mods/PLAYER/mcl_spawn/init.lua b/mods/PLAYER/mcl_spawn/init.lua index 66fe8cdd0..bf048e4b8 100644 --- a/mods/PLAYER/mcl_spawn/init.lua +++ b/mods/PLAYER/mcl_spawn/init.lua @@ -70,16 +70,17 @@ mcl_spawn.set_spawn_pos = function(player, pos, message) meta:set_string("mcl_beds:spawn", "") else local oldpos = minetest.string_to_pos(meta:get_string("mcl_beds:spawn")) + meta:set_string("mcl_beds:spawn", minetest.pos_to_string(pos)) if oldpos then -- We don't bother sending a message if the new spawn pos is basically the same - if vector.distance(pos, oldpos) > 0.1 then - spawn_changed = true - if message then - minetest.chat_send_player(player:get_player_name(), S("New respawn position set!")) - end - end + spawn_changed = vector.distance(pos, oldpos) > 0.1 + else + -- If it wasn't set and now it will be set, it means it is changed + spawn_changed = true + end + if spawn_changed and message then + minetest.chat_send_player(player:get_player_name(), S("New respawn position set!")) end - meta:set_string("mcl_beds:spawn", minetest.pos_to_string(pos)) end return spawn_changed end @@ -93,31 +94,52 @@ local function get_far_node(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 + -- Respawn player at specified respawn position minetest.register_on_respawnplayer(function(player) local pos, custom_spawn = mcl_spawn.get_spawn_pos(player) if pos and custom_spawn then -- Check if bed is still there - -- and the spawning position is free of solid or damaging blocks. local node_bed = get_far_node(pos) - local node_up1 = get_far_node({x=pos.x,y=pos.y+1,z=pos.z}) - local node_up2 = get_far_node({x=pos.x,y=pos.y+2,z=pos.z}) local bgroup = minetest.get_item_group(node_bed.name, "bed") - local def1 = minetest.registered_nodes[node_up1.name] - local def2 = minetest.registered_nodes[node_up2.name] - if (bgroup == 1 or bgroup == 2) 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) then - player:set_pos(pos) - return true - else - -- Forget spawn if bed was missing - if (bgroup ~= 1 and bgroup ~= 2) then - mcl_spawn.set_spawn_pos(player, nil) + if bgroup ~= 1 and bgroup ~= 2 then + -- Bed is destroyed: + if player ~= nil and player:is_player() then + 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 end + -- Find spawning position on/near the bed free of solid or damaging blocks iterating a square spiral 15x15: + local x, z, dx, dz = 0, 0, 0, -1 + for i = 1, 225 do + if x > -8 and x < 8 and z > -8 and z < 8 then + for _,y in ipairs({0, 1, -1, 2, -2, 3, -3, 4, -4}) do + local spawn_pos = {x = pos.x - z, y=pos.y + y, z = pos.z - x} + if good_for_respawn(spawn_pos) then + player:set_pos(spawn_pos) + return true + end + end + end + if x == z or (x < 0 and x == -z) or (x > 0 and x == 1 - z) then + dx, dz = -dz, dx + end + x, z = x + dx, z + dz + end + -- We here if we didn't find suitable place for respawn: + return false end end)