-- Originally Teleport Request by Traxie21 and released with the WTFPL license
-- https://forum.minetest.net/viewtopic.php?id=4457
-- Updates by Zeno and ChaosWormz

local timeout_delay = 60

local version = '1.5'

local tpr_list = {}
local tphr_list = {}

local function find_free_position_near(pos)
   local tries = {
      {x=1,y=0,z=0},
      {x=-1,y=0,z=0},
      {x=0,y=0,z=1},
      {x=0,y=0,z=-1},
   }
   for _,d in pairs(tries) do
      local p = vector.add(pos, d)
      if not minetest.registered_nodes[minetest.get_node(p).name].walkable then
         return p, true
      end
   end
   return pos, false
end

--Teleport Request System
local function tpr_send(sender, receiver)
   local player = minetest.get_player_by_name(sender)
   if not player:get_inventory():contains_item('main', 'epic:teleport_powder') then
      minetest.chat_send_player(sender, 'You need teleport powder.')
      return
   end

   if receiver == '' then
      minetest.chat_send_player(sender, 'Usage: /tpr <Player name>')
      return
   end

   if not minetest.get_player_by_name(receiver) then
      minetest.chat_send_player(sender, 'There is no player by that name. Keep in mind this is case sensitive, and the player must be online.')
      return
   end

   minetest.chat_send_player(receiver, sender ..' is requesting to teleport to you. /tpy to accept.')
   minetest.chat_send_player(sender, 'Teleport request sent! It will time out in '.. timeout_delay ..' seconds.')

   --Write name values to list and clear old values.
   tpr_list[receiver] = sender
   --Teleport timeout delay
   minetest.after(timeout_delay, function(name)
      if tpr_list[name] then
         tpr_list[name] = nil
      end
   end, sender)
end

local function tphr_send(sender, receiver)
   local player = minetest.get_player_by_name(sender)
   if not player:get_inventory():contains_item('main', 'epic:teleport_powder') then
      minetest.chat_send_player(sender, 'You need teleport powder.')
      return
   end

   if receiver == '' then
      minetest.chat_send_player(sender, 'Usage: /tphr <Player name>')
      return
   end

   if not minetest.get_player_by_name(receiver) then
      minetest.chat_send_player(sender, 'There is no player by that name. Keep in mind this is case sensitive, and the player must be online.')
      return
   end

   minetest.chat_send_player(receiver, sender ..' is requesting that you teleport to them. /tpy to accept; /tpn to deny')
   minetest.chat_send_player(sender, 'Teleport request sent! It will time out in '.. timeout_delay ..' seconds.')

   --Write name values to list and clear old values.
   tphr_list[receiver] = sender
   --Teleport timeout delay
   minetest.after(timeout_delay, function(name)
      if tphr_list[name] then
         tphr_list[name] = nil
      end
   end, sender)
end

local function tpr_deny(name)
   if tpr_list[name] then
      minetest.chat_send_player(tpr_list[name], 'Teleport request denied.')
      tpr_list[name] = nil
   end
   if tphr_list[name] then
      minetest.chat_send_player(tphr_list[name], 'Teleport request denied.')
      tphr_list[name] = nil
   end
end

--Teleport Accept Systems
local function tpr_accept(name, param)

   --Check to prevent constant teleporting.
   if not tpr_list[name]
   and not tphr_list[name] then
      minetest.chat_send_player(name, 'Usage: /tpy allows you to accept teleport requests sent to you by other players.')
      return
   end

   local chatmsg, source, target, name2

   if tpr_list[name] then
      name2 = tpr_list[name]
      source = minetest.get_player_by_name(name)
      target = minetest.get_player_by_name(name2)
      takepotion = minetest.get_player_by_name(name2)
      chatmsg = name2 .. ' is teleporting to you.'
      tpr_list[name] = nil
   elseif tphr_list[name] then
      name2 = tphr_list[name]
      source = minetest.get_player_by_name(name2)
      target = minetest.get_player_by_name(name)
      takepotion = minetest.get_player_by_name(name2)
      chatmsg = 'You are teleporting to ' .. name2 .. '.'
      tphr_list[name] = nil
   else
      return
   end

   -- Could happen if either player disconnects (or timeout); if so just abort
   if not source
   or not target then
      return
   end

   minetest.chat_send_player(name2, 'Request Accepted!')
   minetest.chat_send_player(name, chatmsg)

   local target_coords=source:getpos()
   target:setpos(find_free_position_near(target_coords))
   takepotion:get_inventory():remove_item('main', 'epic:teleport_powder')
   minetest.sound_play('whoosh', {pos = target_coords, gain = 0.5, max_hear_distance = 10})
   particles_teleport(target_coords)
end

--Chat commands.
minetest.register_chatcommand('tpr', {
   description = 'Request teleport to another player',
   params = '<playername> | leave playername empty to see help message',
   privs = {interact=true},
   func = tpr_send
})

minetest.register_chatcommand('tphr', {
   description = 'Request player to teleport to you',
   params = '<playername> | leave playername empty to see help message',
   privs = {interact=true},
   func = tphr_send
})

minetest.register_chatcommand('tpy', {
   description = 'Accept teleport requests from another player',
   privs = {interact=true},
   func = tpr_accept
})

minetest.register_chatcommand('tpn', {
   description = 'Deny teleport requests from another player',
   privs = {interact=true},
   func = tpr_deny
})
