Minetest-WorldEditAdditions/worldeditadditions_core/core/pos.lua

252 lines
10 KiB
Lua

local wea_c = worldeditadditions_core
local Vector3 = wea_c.Vector3
local positions_count_limit = 999
local positions = {}
--- Position manager.
-- @event set { player_name: string, i: number, pos: Vector3 } A new position has been set in a player's list at a specific position.
-- @event push { player_name: string, i: number, pos: Vector3 } A new position has been pushed onto a player's stack.
-- @event pop { player_name: string, i: number, pos: Vector3 } A new position has been pushed onto a player's stack.
-- @event clear { player_name: string } The positions for a player have been cleared.
local anchor = nil
--- Ensures that a table exists for the given player.
-- @param player_name string The name of the player to check.
local function ensure_player(player_name)
if player_name == nil then
minetest.log("error", "[wea core:pos:ensure_player] player_name is nil")
end
if not positions[player_name] then
positions[player_name] = {}
end
end
--- Transparently fetches from worldedit pos1 for compatibility.
-- Called whenever pos1 is accessed in this API.
-- @private
-- @param player_name string The name of the player to fetch the position for.
-- @returns void
local function compat_worldedit_pos1_get(player_name)
if worldedit and worldedit.pos1 and worldedit.pos1[player_name] then
ensure_player(player_name)
local new_pos1 = Vector3.clone(worldedit.pos1[player_name])
local existing_pos1 = positions[player_name][1]
positions[player_name][1] = new_pos1
if new_pos1 ~= existing_pos1 then
anchor:emit("set", { player_name = player_name, i = 1, pos = new_pos1 })
end
end
end
--- Transparently fetches from worldedit pos2 for compatibility.
-- Called whenever pos2 is accessed in this API.
-- @private
-- @param player_name string The name of the player to fetch the position for.
-- @returns void
local function compat_worldedit_pos2_get(player_name)
if worldedit and worldedit.pos2 and worldedit.pos2[player_name] then
ensure_player(player_name)
local new_pos2 = Vector3.clone(worldedit.pos2[player_name])
local existing_pos2 = positions[player_name][2]
positions[player_name][2] = new_pos2
if new_pos2 ~= existing_pos2 then
anchor:emit("set", { player_name = player_name, i = 2, pos = new_pos2 })
end
end
end
--- Sets pos1/pos2 in worldedit for compatibility.
-- @param player_name string The name of the player to set the position for.
-- @param i number The index of the position to set.
-- @returns Vector3? The position to set.
local function compat_worldedit_set(player_name, i, pos)
print("DEBUG:compat_worldedit_set i", i, "pos", pos)
if not worldedit then return end
if i == 1 and worldedit.pos1 then
worldedit.pos1[player_name] = nil
if worldedit.marker_update then worldedit.marker_update(player_name) end
worldedit.pos1[player_name] = pos:clone()
elseif i == 2 and worldedit.pos2 then
worldedit.pos2[player_name] = nil
if worldedit.marker_update then worldedit.marker_update(player_name) end
worldedit.pos2[player_name] = pos:clone()
end
end
--- Fetches pos1/pos2 from WorldEdit (if available) and sets them in WorldEditAdditions' postional subsystem
-- @param player_name string The name of the player to sync the positions for.
local function compat_worldedit_get(player_name)
compat_worldedit_pos1_get(player_name)
compat_worldedit_pos2_get(player_name)
end
--- Gets the position with the given index for the given player.
-- @param player_name string The name of the player to fetch the position for.
-- @param i number The index of the position to fetch.
-- @returns Vector3? The position requested, or nil if it doesn't exist.
local function get_pos(player_name, i)
ensure_player(player_name)
if i == 2 then compat_worldedit_pos1_get(player_name)
elseif i == 2 then compat_worldedit_pos2_get(player_name) end
return positions[player_name][i]
end
--- Convenience function that returns position 1 for the given player.
-- @param player_name string The name of the player to fetch the position for.
-- @returns Vector3? The position requested, or nil if it doesn't exist.
local function get_pos1(player_name) return get_pos(player_name, 1) end
--- Convenience function that returns position 1 for the given player.
-- @param player_name string The name of the player to fetch the position for.
-- @returns Vector3? The position requested, or nil if it doesn't exist.
local function get_pos2(player_name) return get_pos(player_name, 2) end
--- Gets a list of all the positions for the given player.
-- @param player_name string The name of the player to fetch the position for.
-- @returns Vector3[] A list of positions for the given player.
local function get_all(player_name)
ensure_player(player_name)
compat_worldedit_pos1_get(player_name)
compat_worldedit_pos2_get(player_name)
return wea_c.table.deepcopy(positions[player_name])
end
--- Get a bounding box that encloses all the positions currently defined by a given player.
-- @param player_name string The name of the player to fetch the boudnign box for.
-- @returns nil|(Vector3,Vector3) 2 positions that define opposite corners of a region that fully encloses all the defined points for the given player, or nil ir the specified player has no points currently defined.
local function get_bounds(player_name)
ensure_player(player_name)
compat_worldedit_pos1_get(player_name)
compat_worldedit_pos2_get(player_name)
if #positions[player_name] < 1 then return nil end
local pos1, pos2 = positions[player_name][1], positions[player_name][1]
for _, pos in pairs(positions[player_name]) do
pos1, pos2 = Vector3.expand_region(pos, pos1, pos2)
end
return pos1, pos2
end
--- Counts the number of positioons registered to a given player.
-- @param player_name string The name of the player to fetch the position for.
-- @returns number The number of positions registered for the given player.
local function pos_count(player_name)
ensure_player(player_name)
compat_worldedit_pos1_get(player_name)
compat_worldedit_pos2_get(player_name)
return #positions[player_name]
end
--- Sets the position at the given index for the given player.
-- You probably want push_pos, not this function.
-- @param player_name string The name of the player to set the position for.
-- @param i number The index to set the position at.
-- @param pos Vector3 The position to set.
-- @returns bool Whether the operation was successful or not (players aren't allowed more than positions_count_limit number of positions at a time).
local function set_pos(player_name, i, pos)
if i > positions_count_limit then return false end
ensure_player(player_name)
positions[player_name][i] = pos
compat_worldedit_set(player_name, i, pos)
anchor:emit("set", { player_name = player_name, i = i, pos = pos })
return true
end
--- Convenience function that set position 1 for the given player.
-- @param player_name string The name of the player to set pos1 for.
-- @param pos Vector3? The new Vector3 for pos1 to set.
-- @returns bool Whether the operation was successful or not, but in this case will always return true so can be ignored.
local function set_pos1(player_name, pos)
return set_pos(player_name, 1, pos)
end
--- Convenience function that set position 2 for the given player.
-- @param player_name string The name of the player to set pos1 for.
-- @param pos Vector3? The new Vector3 for pos2 to set.
-- @returns bool Whether the operation was successful or not, but in this case will always return true so can be ignored.
local function set_pos2(player_name, pos)
return set_pos(player_name, 2, pos)
end
--- Sets the all the positions for the given player.
-- You probably want push_pos, not this function.
-- @param player_name string The name of the player to set the positions for.
-- @param pos_list Vector3 The table of positions to set.
-- @returns bool Whether the operation was successful or not (players aren't allowed more than positions_count_limit number of positions at a time).
local function set_pos_all(player_name, pos_list)
if #pos_list > positions_count_limit then return false end
positions[player_name] = pos_list
for i,pos_new in ipairs(positions[player_name]) do
compat_worldedit_set(player_name, i, pos_new)
anchor:emit("push", { player_name = player_name, pos = pos_new, i = i })
end
return true
end
--- Clears all the positions for the given player.
-- @param player_name string The name of the player to clear the positions for.
-- @returns void
local function clear(player_name)
if positions[player_name] then
positions[player_name] = nil
end
if worldedit then
if worldedit.pos1 then worldedit.pos1[player_name] = nil end
if worldedit.pos2 then worldedit.pos2[player_name] = nil end
end
anchor:emit("clear", { player_name = player_name })
end
--- Removes the last position from the for a given player and returns it.
-- @param player_name string The name of the player to pop the position for.
-- @returns Vector3? The position removed, or nil if it doesn't exist.
local function pop_pos(player_name)
ensure_player(player_name)
if #positions[player_name] <= 0 then return nil end
local count = #positions[player_name]
local last_pos = table.remove(positions[player_name])
if worldedit then
if count == 2 and worldedit.pos2 then worldedit.pos2[player_name] = nil
elseif count == 1 and worldedit.pos1 then worldedit.pos1[player_name] = nil end
end
anchor:emit("pop", { player_name = player_name, pos = last_pos, i = count })
return last_pos
end
--- Adds a position to the list for a given player.
-- @param player_name string The name of the player to add the position for.
-- @param pos Vector3 The position to add.
-- @returns number The new number of positions for that player.
local function push_pos(player_name, pos)
ensure_player(player_name)
table.insert(positions[player_name], pos)
print("DEBUG poslist", wea_c.inspect(positions[player_name]))
compat_worldedit_set(player_name, #positions[player_name], pos)
anchor:emit("push", { player_name = player_name, pos = pos, i = #positions[player_name] })
return #positions[player_name]
end
anchor = wea_c.EventEmitter.new({
get = get_pos,
get1 = get_pos1,
get2 = get_pos2,
get_all = get_all,
get_bounds = get_bounds,
count = pos_count,
clear = clear,
pop = pop_pos,
push = push_pos,
set = set_pos,
set_pos1 = set_pos1,
set_pos2 = set_pos2,
set_all = set_pos_all,
compat_worldedit_get = compat_worldedit_get
})
return anchor