diff --git a/init.lua b/init.lua index 9406af0..bbf914a 100644 --- a/init.lua +++ b/init.lua @@ -3,35 +3,141 @@ -- Defines the edge of a world local edge = 30000 +-- Radius which should be checked for a good teleportation place +local radius = 2 -------------- local count = 0 +local waiting_list = {} +--[[ Explanation of waiting_list table + Index = Player name + Value = { + player = Player to teleport + pos = Destination + obj = Attacked entity + notified = When the player must wait longer... + } +]] + minetest.register_globalstep(function(dtime) - count = count + dtime - if count < 5 then - return - end - count = 0 - - local newedge = edge - 5 - -- Check if the players are near the edge and teleport them - local players = minetest.get_connected_players() - for _,player in pairs(players) do - local pos = player:getpos() - if pos.x >= edge then - player:moveto({x = -newedge, y = pos.y, z = pos.z}) - end - if pos.x <= -edge then - player:moveto({x = newedge, y = pos.y, z = pos.z}) - end - - if pos.z >= edge then - player:moveto({x = pos.x, y = pos.y, z = -newedge}) - end - if pos.z <= -edge then - player:moveto({x = pos.x, y = pos.y, z = newedge}) - end - end + count = count + dtime + if count < 3 then + return + end + count = 0 + + for k, v in pairs(waiting_list) do + if v.player and v.player:is_player() then + local pos = get_surface_pos(v.pos) + if pos then + v.obj:setpos(pos) + minetest.after(0.2, function(p, o) + p:set_detach() + o:remove() + end, v.player, v.obj) + waiting_list[k] = nil + elseif not v.notified then + v.notified = true + minetest.chat_send_player(k, "Sorry, we have not found a free place yet. Please be patient.") + end + else + v.obj:remove() + waiting_list[k] = nil + end + end + + local newedge = edge - 5 + -- Check if the players are near the edge and teleport them + local players = minetest.get_connected_players() + for i, player in ipairs(players) do + local name = player:get_player_name() + if not waiting_list[name] then + local pos = vector.round(player:getpos()) + local newpos = nil + if pos.x >= edge then + newpos = {x = -newedge, y = 10, z = pos.z} + elseif pos.x <= -edge then + newpos = {x = newedge, y = 10, z = pos.z} + end + + if pos.z >= edge then + newpos = {x = pos.x, y = 10, z = -newedge} + elseif pos.z <= -edge then + newpos = {x = pos.x, y = 10, z = newedge} + end + + -- Teleport the player + if newpos then + minetest.chat_send_player(name, "Please wait a few seconds. We will teleport you soon.") + local obj = minetest.add_entity(newpos, "worldedge:lock") + player:set_attach(obj, "", {x=0, y=0, z=0}, {x=0, y=0, z=0}) + waiting_list[name] = { + player = player, + pos = newpos, + obj = obj + } + obj:setpos(newpos) + end + end + end end) +function get_surface_pos(pos) + local minp = { + x = pos.x - radius - 1, + y = -10, + z = pos.z - radius - 1 + } + local maxp = { + x = pos.x + radius - 1, + y = 50, + z = pos.z + radius - 1 + } + + local c_air = minetest.get_content_id("air") + local c_ignore = minetest.get_content_id("ignore") + + local vm = minetest.get_voxel_manip() + local emin, emax = vm:read_from_map(minp, maxp) + local area = VoxelArea:new{MinEdge = emin, MaxEdge = emax} + local data = vm:get_data() + + local seen_air = false + local deepest_place = vector.new(pos) + deepest_place.y = 50 + + for x = minp.x, maxp.x do + for z = minp.z, maxp.z do + local solid = 0 + for y = deepest_place.y, -10, -1 do + local node = data[area:index(x, y, z)] + if y < deepest_place.y and node == c_air then + deepest_place = vector.new(x, y, z) + seen_air = true + end + if solid > 5 then + -- Do not find caves! + break + end + if node ~= c_air and node ~= c_ignore then + solid = solid + 1 + end + end + end + end + + if seen_air then + return deepest_place + else + return false + end +end +minetest.register_entity("worldedge:lock", { + initial_properties = { + is_visible = false + }, + on_activate = function(staticdata, dtime_s) + --self.object:set_armor_groups({immortal = 1}) + end +}) \ No newline at end of file