184 lines
7.4 KiB
Lua

--local bc = better_commands
local S = minetest.get_translator(minetest.get_current_modname())
---Gets the name of an entity
---@param obj minetest.ObjectRef|vector.Vector
---@param no_id? boolean
---@return string
function better_commands.get_entity_name(obj, no_id, no_format)
if not obj.is_player then
return S("Command Block")
end
if obj:is_player() then
local player_name = obj:get_player_name()
if no_format then return player_name end
return better_commands.format_name(obj:get_player_name())
else
local luaentity = obj.get_luaentity and obj:get_luaentity()
if luaentity then
if no_id then
return luaentity._nametag or luaentity.nametag or ""
else
local name = luaentity._nametag or luaentity.nametag
if (not name) or name == "" then
name = luaentity.name
if name == "__builtin:item" then
local stack = ItemStack(luaentity.itemstring)
return stack:get_short_description()
elseif name == "__builtin:falling_node" then
local stack = ItemStack(luaentity.node.name)
if not stack:is_known() then return S("Unknown Falling Node") end
return S("Falling @1", stack:get_short_description())
end
return luaentity.description or better_commands.entity_names[name] or name
else
return name
end
end
else
return S("???")
end
end
end
---Gets an entity's current rotation
---@param obj minetest.ObjectRef|vector.Vector
---@return vector.Vector
function better_commands.get_entity_rotation(obj)
if obj.is_player and obj:is_player() then
return {x = obj:get_look_vertical(), y = obj:get_look_horizontal(), z = 0}
elseif obj.get_rotation then
return obj:get_rotation()
else
return vector.zero()
end
end
---Sets an entity's rotation
---@param obj minetest.ObjectRef|any
---@param rotation vector.Vector
function better_commands.set_entity_rotation(obj, rotation)
if not obj.is_player then return end
if obj:is_player() then
obj:set_look_vertical(rotation.x)
obj:set_look_horizontal(rotation.y)
elseif obj.set_rotation then
obj:set_rotation(rotation)
end
end
---Takes an object and a position, returns the rotation at which the object points at the position
---@param obj minetest.ObjectRef|vector.Vector
---@param pos vector.Vector
---@return vector.Vector
function better_commands.point_at_pos(obj, pos)
local obj_pos = obj.get_pos and obj:get_pos() or obj
if obj:is_player() then
obj_pos.y = obj_pos.y + obj:get_properties().eye_height
end
---@diagnostic disable-next-line: param-type-mismatch
local result = vector.dir_to_rotation(vector.direction(obj_pos, pos))
result.x = -result.x -- no clue why this is necessary
return result
end
---Completes a context table
---@param name string The name of the player to use as context.executor if not supplied
---@param context? table The context table to complete (optional)
---@return contextTable?
function better_commands.complete_context(name, context)
if not context then context = {} end
context.executor = context.executor or minetest.get_player_by_name(name)
if not context.executor then minetest.log("error", "Missing executor") return end
context.pos = context.pos or context.executor:get_pos()
context.rot = context.rot or better_commands.get_entity_rotation(context.executor)
context.anchor = context.anchor or "feet"
context.origin = context.origin or name
return context
end
function better_commands.entity_from_alias(alias, list)
if minetest.registered_entities[alias] then return alias end
local entities = better_commands.unique_entities[alias]
if not entities then return end
if list then return entities end
return entities[math.random(1, #entities)]
end
---Handles rotation when summoning/teleporting
---@param context contextTable
---@param victim minetest.ObjectRef|vector.Vector
---@param split_param splitParam[]
---@param i integer
---@return vector.Vector? result
---@return string? err
---@nodiscard
function better_commands.get_tp_rot(context, victim, split_param, i)
local victim_rot = table.copy(context.rot)
if split_param[i] then
local yaw_pitch
local facing
if split_param[i].type == "number" then
victim_rot.y = math.rad(tonumber(split_param[i][3]) or 0)
yaw_pitch = true
elseif split_param[i].type == "relative" then
victim_rot.y = victim_rot.y+math.rad(tonumber(split_param[i][3]:sub(2,-1)) or 0)
yaw_pitch = true
elseif split_param[i][3] == "facing" then
facing = true
end
if yaw_pitch and split_param[i+1] then
if split_param[i+1].type == "number" then
victim_rot.x = math.rad(tonumber(split_param[i+1][3]) or 0)
elseif split_param[i+1].type == "relative" then
victim_rot.x = victim_rot.x+math.rad(tonumber(split_param[i+1][3]:sub(2,-1)) or 0)
end
elseif facing and split_param[i+1] then
if split_param[i+1].type == "selector" then
local targets, err = better_commands.parse_selector(split_param[i+1], context, true)
if err or not targets then return nil, err end
local target_pos = targets[1].is_player and targets[1]:get_pos() or targets[1]
---@diagnostic disable-next-line: param-type-mismatch
victim_rot = better_commands.point_at_pos(victim, target_pos)
elseif split_param[i+1][3] == "entity" and split_param[i+2].type == "selector" then
local targets, err = better_commands.parse_selector(split_param[i+2], context, true)
if err or not targets then return nil, err end
local target_pos = targets[1].is_player and targets[1]:get_pos() or targets[1]
---@diagnostic disable-next-line: param-type-mismatch
victim_rot = better_commands.point_at_pos(victim, target_pos)
else
local target_pos, err = better_commands.parse_pos(split_param, i+1, context)
if err or not target_pos then return nil, err end
victim_rot = better_commands.point_at_pos(victim, target_pos)
end
end
if yaw_pitch or facing then
return victim_rot
end
end
return victim_rot
end
---Gets a player's gamemode
---@param player minetest.Player
---@return string?
function better_commands.get_gamemode(player)
if player.is_player and player:is_player() then
local gamemode
if better_commands.mcl then
gamemode = mcl_gamemode.get_gamemode(player)
else
gamemode = minetest.is_creative_enabled(player:get_player_name()) and "creative" or "survival"
end
return gamemode
end
end
better_commands.gamemode_aliases = {
[0] = "survival",
[1] = "creative",
["0"] = "survival", -- not sure whether these are necessary
["1"] = "creative",
s = "survival",
c = "creative",
}