Added error function and enchant (untested)

This commit is contained in:
ThePython10110 2024-05-27 08:59:28 -07:00
parent 3d12f3a0ce
commit dce185b31a
23 changed files with 392 additions and 222 deletions

View File

@ -92,7 +92,7 @@ function better_commands.complete_context(name, context)
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.anchor = context.anchor or "feet"
context.origin = context.origin or name
return context
end

View File

@ -1,6 +1,10 @@
--local bc = better_commands
local S = minetest.get_translator(minetest.get_current_modname())
function better_commands.error(str)
return better_commands.error(minetest.strip_colors(str))
end
---Registers an ACOVG command
---@param name string The name of the command (/<name>)
---@param def betterCommandDef The command definition
@ -13,7 +17,7 @@ function better_commands.register_command(name, def)
if context then
success, message, count = def.real_func(name,param,context)
else
success, message, count = false, minetest.colorize("red", S("Missing context")), 0
success, message, count = false, better_commands.error(S("Missing context")), 0
end
if message then
if minetest.settings:get_bool("better_commands.send_command_feedback", true) then

View File

@ -27,6 +27,8 @@ Initial release. Missing *lots* of commands, several `execute` subcommands, lots
* Added `/clear` command
* Added `/teammsg` command
* Added `/gamerule` and `/changesetting` commands
* Added `/remove` (removes entities instead of killing them)
* Removed `anchored` execute subcommand (there really is no good way of making it work)
* Command results are now shown to other players as well, unless `better_commands.send_command_feedback` is disabled
* Error messages are now red
* Command outputs now match ACOVG's better

View File

@ -20,10 +20,20 @@ local command_files = {
"settings",
}
local mcl_only = {
"enchant"
}
for _, file in ipairs(command_files) do
better_commands.run_file(file, "COMMANDS")
end
if better_commands.mcl then
for _, file in ipairs(mcl_only) do
better_commands.run_file(file, "COMMANDS")
end
end
better_commands.register_command("?", table.copy(minetest.registered_chatcommands.help))

View File

@ -12,10 +12,10 @@ better_commands.register_command("ability", {
end
local set = split_param[3] and split_param[3][3]:lower()
if set and set ~= "true" and set ~= "false" then
return false, minetest.colorize("red", S("[value] must be true or false (or missing), not '@1'", set)), 0
return false, better_commands.error(S("[value] must be true or false (or missing), not '@1'", set)), 0
end
local targets, err = better_commands.parse_selector(split_param[1], context, true)
if err or not targets then return false, minetest.colorize("red", err), 0 end
if err or not targets then return false, better_commands.error(err), 0 end
local priv = split_param[2] and split_param[2][3]
local target = targets[1]
if target.is_player and target:is_player() then
@ -43,12 +43,12 @@ better_commands.register_command("ability", {
if minetest.registered_privileges[priv] then
return true, S("@1 = @2", priv, tostring(privs[priv])), 1
else
return false, minetest.colorize("red", S("Invalid privilege '@1'", priv)), 0
return false, better_commands.error(S("Invalid privilege '@1'", priv)), 0
end
end
else
if not minetest.registered_privileges[priv] then
return false, minetest.colorize("red", S("Invalid privilege '@1'", priv)), 0
return false, better_commands.error(S("Invalid privilege '@1'", priv)), 0
else
if set == "true" then
privs[priv] = true
@ -71,6 +71,6 @@ better_commands.register_command("ability", {
end
end
end
return false, minetest.colorize("red", S("No entity was found")), 0
return false, better_commands.error(S("No entity was found")), 0
end
})

View File

@ -12,7 +12,7 @@ better_commands.register_command("say", {
if context.command_block or minetest.check_player_privs(context.origin, {server = true}) then
local err
message, err = better_commands.expand_selectors(param, split_param, 1, context)
if err then return false, minetest.colorize("red", err), 0 end
if err then return false, better_commands.error(err), 0 end
else
message = param
end
@ -31,13 +31,13 @@ better_commands.register_command("msg", {
return false, nil, 0
end
local targets, err = better_commands.parse_selector(split_param[1], context)
if err or not targets then return false, minetest.colorize("red", err), 0 end
if err or not targets then return false, better_commands.error(err), 0 end
local target_start = S("@1 whispers to you: ", better_commands.get_entity_name(context.executor))
local message
if context.command_block or minetest.check_player_privs(context.origin, {server = true}) then
local err
message, err = better_commands.expand_selectors(param, split_param, 2, context)
if err then return false, minetest.colorize("red", err), 0 end
if err then return false, better_commands.error(err), 0 end
else
---@diagnostic disable-next-line: param-type-mismatch
message = param:sub(split_param[2][1], -1)
@ -69,7 +69,7 @@ better_commands.register_command("me", {
if context.command_block or minetest.check_player_privs(context.origin, {server = true}) then
local err
message, err = better_commands.expand_selectors(param, split_param, 1, context)
if err then return false, minetest.colorize("red", err), 0 end
if err then return false, better_commands.error(err), 0 end
else
message = param
end
@ -88,19 +88,19 @@ better_commands.register_command("teammsg", {
return false, nil, 0
end
if not (context.executor.is_player and context.executor:is_player()) then
return false, minetest.colorize("red", S("An entity is required to run this command here")), 0
return false, better_commands.error(S("An entity is required to run this command here")), 0
end
local sender = context.executor:get_player_name()
local team = better_commands.teams.players[sender]
local team_color = better_commands.team_colors[better_commands.teams.teams[team].color or "white"]
local display_name = better_commands.teams.teams[team].display_name or team
if not team then return false, minetest.colorize("red", S("You must be on a team to message your team")), 0 end
if not team then return false, better_commands.error(S("You must be on a team to message your team")), 0 end
local start = S("[@1] <@2> ", minetest.colorize(team_color, display_name), better_commands.get_entity_name(context.executor))
local message
if context.command_block or minetest.check_player_privs(context.origin, {server = true}) then
local err
message, err = better_commands.expand_selectors(param, split_param, 1, context)
if err then return false, minetest.colorize("red", err), 0 end
if err then return false, better_commands.error(err), 0 end
else
---@diagnostic disable-next-line: param-type-mismatch
message = param:sub(split_param[1][1], -1)

View File

@ -12,7 +12,7 @@ better_commands.register_command("clear", {
targets = {context.executor}
else
targets, err = better_commands.parse_selector(selector, context)
if err or not targets then return false, minetest.colorize("red", err), 0 end
if err or not targets then return false, better_commands.error(err), 0 end
end
local filter, group
if split_param[2] then
@ -29,12 +29,12 @@ better_commands.register_command("clear", {
filter = split_param[2][3]:sub(7, -1)
else
filter, err = better_commands.parse_item(split_param[2], true)
if err or not filter then return false, minetest.colorize("red", err), 0 end
if err or not filter then return false, better_commands.error(err), 0 end
end
end
local remove_max = tonumber(split_param[3] and split_param[3][3])
if split_param[3] and not remove_max then
return false, minetest.colorize("red", S("maxCount must be a number")), 0
return false, better_commands.error(S("maxCount must be a number")), 0
end
if remove_max then
remove_max = math.floor(remove_max)
@ -126,10 +126,10 @@ better_commands.register_command("clear", {
end
end
if count < 1 then
return false, minetest.colorize("red", S("No player was found"))
return false, better_commands.error(S("No player was found"))
elseif count == 1 then
if match_total < 1 then
return false, minetest.colorize("red", S("No items were found on player @1", last))
return false, better_commands.error(S("No items were found on player @1", last))
elseif remove_max == 0 then
return true, S("Found @1 matching items(s) on player @2", match_total, last), match_total
elseif all and remove_max == -1 then
@ -139,7 +139,7 @@ better_commands.register_command("clear", {
end
else
if match_total < 1 then
return false, minetest.colorize("red", S("No items were found on @1 players", count))
return false, better_commands.error(S("No items were found on @1 players", count))
elseif remove_max == 0 then
return true, S("Found @1 matching items(s) on @2 players", match_total, count), match_total
elseif all and remove_max == -1 then

View File

@ -15,10 +15,10 @@ better_commands.register_command("bc", {
if privs then
return def.real_func(name, command_param, context)
else
return false, minetest.colorize("red", S("You don't have permission to run this command (missing privileges: @1)", table.concat(missing, ", "))), 0
return false, better_commands.error(S("You don't have permission to run this command (missing privileges: @1)", table.concat(missing, ", "))), 0
end
else
return false, minetest.colorize("red", S("Invalid command: @1", command)), 0
return false, better_commands.error(S("Invalid command: @1", command)), 0
end
end
})
@ -37,10 +37,10 @@ better_commands.register_command("old", {
if privs then
return def.real_func(name, command_param, context)
else
return false, minetest.colorize("red", S("You don't have permission to run this command (missing privileges: @1)", table.concat(missing, ", "))), 0
return false, better_commands.error(S("You don't have permission to run this command (missing privileges: @1)", table.concat(missing, ", "))), 0
end
else
return false, minetest.colorize("red", S("Invalid command: @1", command)), 0
return false, better_commands.error(S("Invalid command: @1", command)), 0
end
end
})

107
COMMANDS/enchant.lua Normal file
View File

@ -0,0 +1,107 @@
local S = minetest.get_translator("better_commands")
better_commands.register_command("enchant", {
description = S("Enchant an item"),
params = S("<selector> <enchantment> [<level>]"),
privs = { give = true },
func = function(name, param, context)
local split_param, err = better_commands.parse_params(param)
if err then return false, better_commands.error(err), 0 end
local enchantment = split_param[2] and split_param[2][3]
if not enchantment then return false, better_commands.error(S("Missing enchantment")), 0 end
local level_str = split_param[3] and split_param[3][3]
local level = tonumber(level_str or "1")
local targets, err = better_commands.parse_selector(split_param[1], context)
if err or not targets then return false, better_commands.error(err), 0 end
local count = 0
local last
for _, target in ipairs(targets) do
if target.is_player and target:is_player() then
local itemstack = target:get_wielded_item()
local can_enchant, errorstring, extra_info = mcl_enchanting.can_enchant(itemstack, enchantment, level)
if not can_enchant then
if errorstring == "enchantment invalid" then
return false, better_commands.error(S("Invalid enchantment '@1'", enchantment)), 0
elseif errorstring == "item missing" then
return false, better_commands.error(S("@1 is not holding any item", better_commands.get_entity_name(target))), 0
elseif errorstring == "item not supported" then
return false,
better_commands.error(S("@1 cannot support that enchantment", itemstack:get_short_description())), 0
elseif errorstring == "level invalid" then
return false, better_commands.error(S("Invalid integer '@1'", level_str)), 0
elseif errorstring == "level too high" then
return false,
better_commands.error(
S("@1 is higher than the maximum level of @2 supported by that enchantment", level_str,
extra_info)), 0
elseif errorstring == "level too small" then
return false,
better_commands.error(
S("@1 is lower than the minimum level of @2 supported by that enchantment", level_str,
extra_info)), 0
elseif errorstring == "incompatible" then
return false,
better_commands.error(
S("@1 can't be combined with @2.",
mcl_enchanting.get_enchantment_description(enchantment, level), extra_info)), 0
end
else
count = count + 1
last = better_commands.get_entity_name(target)
target:set_wielded_item(mcl_enchanting.enchant(itemstack, enchantment, level))
end
end
end
if count < 1 then
return false, better_commands.error(S("No player was found")), 0
elseif count == 1 then
return true, S("Enchanted item of @1.", last), 1
else
return true, S("Enchanted items of @1 players.", count), count
end
end
})
better_commands.register_command("forceenchant", {
description = S("Forcefully enchant an item"),
params = S("<selector> <enchantment> [<level>]"),
privs = { give = true },
func = function(name, param, context)
local split_param, err = better_commands.parse_params(param)
if err then return false, better_commands.error(err), 0 end
local selector = split_param[1]
local enchantment = split_param[2] and split_param[2][3]
local level_str = split_param[3] and split_param[3][3]
local level = tonumber(level_str or "1")
local targets, err = better_commands.parse_selector(selector, context)
if err or not targets then return false, better_commands.error(err), 0 end
local count = 0
local last
for _, target in ipairs(targets) do
if target.is_player and target:is_player() then
local itemstack = target:get_wielded_item()
local _, errorstring = mcl_enchanting.can_enchant(itemstack, enchantment, level)
if errorstring == "enchantment invalid" then
return false, better_commands.error(S("There is no such enchantment '@1'.", enchantment)), 0
elseif errorstring == "item missing" then
return false, better_commands.error(S("The target doesn't hold an item.")), 0
elseif errorstring == "item not supported" and not mcl_enchanting.is_enchantable(itemstack:get_name()) then
return false, better_commands.error(S("The target item is not enchantable.")), 0
elseif errorstring == "level invalid" then
return false, better_commands.error(S("'@1' is not a valid number.", level_str)), 0
else
target:set_wielded_item(mcl_enchanting.enchant(itemstack, enchantment, level))
count = count + 1
last = better_commands.get_entity_name(target)
end
end
end
if count < 1 then
return false, better_commands.error(S("No player was found")), 0
elseif count == 1 then
return true, S("Enchanted item of @1 with @2 @3", last, enchantment, level), 1
else
return true, S("Enchanted items of @1 players with @2 @3", count, enchantment, level), count
end
end
})

View File

@ -11,15 +11,16 @@ better_commands.execute_subcommands = {
align = function(branches, index)
local branch_data = branches[index]
local param = branches.param[branch_data.i+1]
if not param then return false, minetest.colorize("red", S("Missing argument for subcommand @1", "align")) end
if not param then return false, better_commands.error(S("Missing argument for subcommand @1", "align")) end
local axes = {param[3]:match("^([xyz])([xyz]?)([xyz]?)$")}
if not axes[1] then return false, minetest.colorize("red", S("Invalid swizzle, expected combination of 'x', 'y', and 'z'")) end
if not axes[1] then return false, better_commands.error(S("Invalid swizzle, expected combination of 'x', 'y', and 'z'")) end
for _ ,axis in pairs(axes) do
branch_data.pos[axis] = math.floor(branch_data.pos[axis])
end
branch_data.i = branch_data.i + 2
return true
end,
--[[
---Sets anchor to feet or eyes
---@param branches contextTable[]
---@param index integer
@ -29,16 +30,16 @@ better_commands.execute_subcommands = {
anchored = function(branches, index)
local branch_data = branches[index]
local param = branches.param[branch_data.i+1]
if not param then return false, minetest.colorize("red", S("Missing argument for subcommand @1", "anchored")) end
if not param then return false, better_commands.error(S("Missing argument for subcommand @1", "anchored")) end
local anchor = tostring(param[3]):lower()
if anchor == "feet" or anchor == "eyes" then
branch_data.anchor = anchor
else
return false, minetest.colorize("red", S("Invalid entity anchor position @1", anchor))
return false, better_commands.error(S("Invalid entity anchor position @1", anchor))
end
branch_data.i = branch_data.i + 2
return true
end,
end, --]]
---Changes executor
---@param branches contextTable[]
---@param index integer
@ -48,9 +49,9 @@ better_commands.execute_subcommands = {
as = function(branches, index)
local branch_data = branches[index]
local param = branches.param[branch_data.i+1]
if not param then return false, minetest.colorize("red", S("Missing argument for subcommand @1", "as")) end
if not param then return false, better_commands.error(S("Missing argument for subcommand @1", "as")) end
if param.type ~= "selector" then
return false, minetest.colorize("red", S("Invalid target: @1", table.concat(param, "", 3)))
return false, better_commands.error(S("Invalid target: @1", table.concat(param, "", 3)))
end
local targets, err = better_commands.parse_selector(param, branch_data)
if err or not targets then return false, err end
@ -79,9 +80,9 @@ better_commands.execute_subcommands = {
at = function(branches, index)
local branch_data = branches[index]
local param = branches.param[branch_data.i+1]
if not param then return false, minetest.colorize("red", S("Missing argument for subcommand @1", "at")) end
if not param then return false, better_commands.error(S("Missing argument for subcommand @1", "at")) end
if param.type ~= "selector" then
return false, minetest.colorize("red", S("Invalid target: @1", table.concat(param, "", 3)))
return false, better_commands.error(S("Invalid target: @1", table.concat(param, "", 3)))
end
local targets, err = better_commands.parse_selector(param, branch_data)
if err or not targets then return false, err end
@ -157,11 +158,11 @@ better_commands.execute_subcommands = {
positioned = function(branches, index)
local branch_data = branches[index]
local param = branches.param[branch_data.i+1]
if not param then return false, minetest.colorize("red", S("Missing argument for subcommand @1", "positioned")) end
if not param then return false, better_commands.error(S("Missing argument for subcommand @1", "positioned")) end
if param[3] == "as" then
local selector = branches.param[branch_data.i+2]
if not selector or selector.type ~= "selector" then
return false, minetest.colorize("red", S("Invalid argument for @1", "positioned"))
return false, better_commands.error(S("Invalid argument for @1", "positioned"))
end
local targets, err = better_commands.parse_selector(selector, branch_data)
if err or not targets then return false, err end
@ -184,7 +185,7 @@ better_commands.execute_subcommands = {
local pos, err = better_commands.parse_pos(branches.param, branch_data.i+1, branch_data)
if err then return false, err end
branch_data.pos = pos
branch_data.anchor = "feet"
--branch_data.anchor = "feet"
branch_data.i = branch_data.i + 4
return true
end
@ -198,11 +199,11 @@ better_commands.execute_subcommands = {
rotated = function(branches, index)
local branch_data = branches[index]
local param = branches.param[branch_data.i+1]
if not param then return false, minetest.colorize("red", S("Missing argument for subcommand @1", "rotated")) end
if not param then return false, better_commands.error(S("Missing argument for subcommand @1", "rotated")) end
if param[3] == "as" then
local selector = branches.param[branch_data.i+2]
if not selector or selector.type ~= "selector" then
return false, minetest.colorize("red", S("Invalid argument for rotated"))
return false, better_commands.error(S("Invalid argument for rotated"))
end
local targets, err = better_commands.parse_selector(selector, branch_data)
if err or not targets then return false, err end
@ -223,7 +224,7 @@ better_commands.execute_subcommands = {
end
else
if not (branches.param[branch_data.i+1] and branches.param[branch_data.i+2]) then
return false, minetest.colorize("red", S("Missing argument(s)) for rotated"))
return false, better_commands.error(S("Missing argument(s)) for rotated"))
end
local victim_rot = branch_data.rot
if branches.param[branch_data.i+1].type == "number" then
@ -231,14 +232,14 @@ better_commands.execute_subcommands = {
elseif branches.param[branch_data.i+1].type == "relative" then
victim_rot.y = victim_rot.y+math.rad(tonumber(branches.param[branch_data.i+1][3]:sub(2,-1)) or 0)
else
return false, minetest.colorize("red", S("Invalid argument for rotated"))
return false, better_commands.error(S("Invalid argument for rotated"))
end
if branches.param[branch_data.i+2].type == "number" then
victim_rot.x = math.rad(tonumber(branches.param[branch_data.i+2][3]) or 0)
elseif branches.param[branch_data.i+2].type == "relative" then
victim_rot.x = victim_rot.x+math.rad(tonumber(branches.param[branch_data.i+2][3]:sub(2,-1)) or 0)
else
return false, minetest.colorize("red", S("Invalid argument for rotated"))
return false, better_commands.error(S("Invalid argument for rotated"))
end
branch_data.rot = victim_rot
branch_data.i = branch_data.i + 3
@ -260,7 +261,7 @@ better_commands.execute_subcommands = {
) then
return "notarget"
end
if not branches.param[branch_data.i+1] then return false, minetest.colorize("red", S("Missing command")) end
if not branches.param[branch_data.i+1] then return false, better_commands.error(S("Missing command")) end
local command, command_param
command, command_param = branch_data.original_command:match(
"%/?([%S]+)%s*(.-)$",
@ -281,13 +282,14 @@ better_commands.execute_subcommands = {
if def and command ~= "old" and (branch_data.command_block or minetest.check_player_privs(branch_data.origin, def.privs)) then
return "done", def.real_func(branch_data.origin, command_param, table.copy(branch_data))
else
return false, minetest.colorize("red", S("Invalid command or privs: @1", command))
return false, better_commands.error(S("Invalid command or privs: @1", command))
end
end
}
better_commands.register_command("execute", {
params = S("<align|anchored|as|at|facing|positioned|rotated|run> ..."),
params = S("<align|as|at|facing|positioned|rotated|run> ..."),
--params = S("<align|anchored|as|at|facing|positioned|rotated|run> ..."),
description = S("Run any Better Command (not other commands) after changing the context"),
privs = {server = true, ban = true, privs = true},
func = function(name, param, context)
@ -312,7 +314,7 @@ better_commands.register_command("execute", {
break
end
else
return false, minetest.colorize("red", S("Invalid subcommand: @1", subcmd)), 0
return false, better_commands.error(S("Invalid subcommand: @1", subcmd)), 0
end
end
if status == "done" then

View File

@ -15,23 +15,23 @@ better_commands.register_command("gamemode", {
privs = {server = true},
func = function(name, param, context)
local split_param, err = better_commands.parse_params(param)
if err then return false, minetest.colorize("red", err), 0 end
if err then return false, better_commands.error(err), 0 end
local gamemode = split_param[1] and split_param[1][3]
if not gamemode then return false, minetest.colorize("red", S("Missing gamemode")), 0 end
if not gamemode then return false, better_commands.error(S("Missing gamemode")), 0 end
gamemode = better_commands.gamemode_aliases[gamemode] or gamemode
if better_commands.mcl then
if table.indexof(mcl_gamemode.gamemodes, gamemode) == -1 then
return false, minetest.colorize("red", S("Invalid gamemode @1", gamemode)), 0
return false, better_commands.error(S("Invalid gamemode @1", gamemode)), 0
end
elseif gamemode ~= "creative" and gamemode ~= "survival" then
return false, minetest.colorize("red", S("Invalid gamemode @1", gamemode)), 0
return false, better_commands.error(S("Invalid gamemode @1", gamemode)), 0
end
local targets = {context.executor}
local self = true
if split_param[2] then
local err
targets, err = better_commands.parse_selector(split_param[2], context)
if err or not targets then return false, minetest.colorize("red", err), 0 end
if err or not targets then return false, better_commands.error(err), 0 end
self = false
end
local count = 0
@ -57,7 +57,7 @@ better_commands.register_command("gamemode", {
end
end
if count < 1 then
return false, minetest.colorize("red", S("No player was found")), 0
return false, better_commands.error(S("No player was found")), 0
elseif count == 1 then
if self then
return true, S("Set own gamemode to @1", gamemode)

View File

@ -18,18 +18,18 @@ end
-- Modified from builtin/game/chat.lua
local function handle_give_command(receiver, stack_data)
local itemstack, err = better_commands.parse_item(stack_data)
if err or not itemstack then return false, minetest.colorize("red", err), 0 end
if err or not itemstack then return false, better_commands.error(err), 0 end
if itemstack:is_empty() then
return false, minetest.colorize("red", S("Cannot give an empty item")), 0
return false, better_commands.error(S("Cannot give an empty item")), 0
elseif (not itemstack:is_known()) or (itemstack:get_name() == "unknown") then
return false, minetest.colorize("red", S("Unknown item '@1'", itemstack:get_name())), 0
return false, better_commands.error(S("Unknown item '@1'", itemstack:get_name())), 0
-- Forbid giving 'ignore' due to unwanted side effects
elseif itemstack:get_name() == "ignore" then
return false, minetest.colorize("red", S("Giving 'ignore' is not allowed")), 0
return false, better_commands.error(S("Giving 'ignore' is not allowed")), 0
end
local receiverref = minetest.get_player_by_name(receiver)
if receiverref == nil then
return false, minetest.colorize("red", S("No player was found")), 0
return false, better_commands.error(S("No player was found")), 0
end
local leftover = receiverref:get_inventory():add_item("main", itemstack)
if not leftover:is_empty() then
@ -52,7 +52,7 @@ better_commands.register_command("give", {
end
local message
local targets, err = better_commands.parse_selector(split_param[1], context)
if err or not targets then return false, minetest.colorize("red", err), 0 end
if err or not targets then return false, better_commands.error(err), 0 end
local count = 0
for _, target in ipairs(targets) do
if target.is_player and target:is_player() then
@ -62,7 +62,7 @@ better_commands.register_command("give", {
end
end
if count < 1 then
return false, minetest.colorize("red", S("No player was found")), 0
return false, better_commands.error(S("No player was found")), 0
elseif count == 1 then
return true, message, 1
else

View File

@ -9,7 +9,7 @@ better_commands.register_command("kill", {
if param == "" then param = "@s" end
local split_param = better_commands.parse_params(param)
local targets, err = better_commands.parse_selector(split_param[1], context)
if err or not targets then return false, minetest.colorize("red", err), 0 end
if err or not targets then return false, better_commands.error(err), 0 end
local count = 0
local last
for _, target in ipairs(targets) do
@ -33,7 +33,7 @@ better_commands.register_command("kill", {
end
end
if count < 1 then
return false, minetest.colorize("red", S("No entity was found")), 0
return false, better_commands.error(S("No entity was found")), 0
elseif count == 1 then
return true, S("Killed @1", last), count
else
@ -47,7 +47,54 @@ better_commands.register_command("killme", {
description = S("Kills self"),
privs = {server = true},
func = function(name, param, context)
if param ~= "" then return false, minetest.colorize("red", S("Unexpected argument: @1", param)), 0 end
if param ~= "" then return false, better_commands.error(S("Unexpected argument(s) '@1'", param)), 0 end
return better_commands.commands.kill.real_func(name, "", context)
end
})
better_commands.register_command("remove", {
params = S("[target]"),
description = S("Kills players and removes entities"),
privs = {server = true},
func = function (name, param, context)
if param == "" then param = "@s" end
local split_param = better_commands.parse_params(param)
local targets, err = better_commands.parse_selector(split_param[1], context)
if err or not targets then return false, better_commands.error(err), 0 end
local count = 0
local last
for _, target in ipairs(targets) do
if target.is_player then
if target:is_player() then
if better_commands.settings.kill_creative_players or not (minetest.is_creative_enabled(target:get_player_name())) then
last = better_commands.get_entity_name(target)
better_commands.deal_damage(
---@diagnostic disable-next-line: param-type-mismatch
target,
math.max(target:get_hp(), 1000000000000), -- 1 trillion damage to make sure they die :D
{
type = "set_hp",
bypasses_totem = true,
flags = {bypasses_totem = true},
better_commands = "kill"
},
true
)
count = count + 1
end
else
count = count + 1
last = better_commands.get_entity_name(target)
target:get_luaentity():remove()
end
end
end
if count < 1 then
return false, better_commands.error(S("No entity was found")), 0
elseif count == 1 then
return true, S("Killed @1", last), count
else
return true, S("Killed @1 entities", count), count
end
end
})

View File

@ -13,11 +13,11 @@ better_commands.register_command("playsound", {
local targets, err, next
if split_param[2].type == "selector" then
targets, err = better_commands.parse_selector(split_param[2], context)
if err or not targets then return false, minetest.colorize("red", err), 0 end
if err or not targets then return false, better_commands.error(err), 0 end
next = 3
else
local pos, err = better_commands.parse_pos(split_param, 2, context)
if err or not pos then return false, minetest.colorize("red", err), 0
if err or not pos then return false, better_commands.error(err), 0
end
targets = {pos}
next = 5
@ -27,21 +27,21 @@ better_commands.register_command("playsound", {
---@diagnostic disable-next-line: cast-local-type
volume = split_param[next][3]
if volume and not tonumber(volume) then
return false, minetest.colorize("red", S("Must be a number, not @1", volume)), 0
return false, better_commands.error(S("Must be a number, not @1", volume)), 0
end
volume = tonumber(volume)
if split_param[next+1] then
---@diagnostic disable-next-line: cast-local-type
pitch = split_param[next+1][3]
if pitch and not tonumber(pitch) then
return false, minetest.colorize("red", S("Must be a number, not @1", pitch)), 0
return false, better_commands.error(S("Must be a number, not @1", pitch)), 0
end
pitch = tonumber(pitch)
if split_param[next+2] then
---@diagnostic disable-next-line: cast-local-type
distance = split_param[next+2][3]
if distance and not tonumber(distance) then
return false, minetest.colorize("red", S("Must be a number, not @1", distance)), 0
return false, better_commands.error(S("Must be a number, not @1", distance)), 0
end
distance = tonumber(distance)
end

View File

@ -22,21 +22,21 @@ better_commands.register_command("scoreboard", {
func = function(name, param, context)
local split_param = better_commands.parse_params(param)
if not (split_param[1] and split_param[2]) then
return false, minetest.colorize("red", S("Missing arguments")), 0
return false, better_commands.error(S("Missing arguments")), 0
end
--minetest.log(dump(split_param))
if split_param[1][3] == "objectives" then
local subcommand = split_param[2][3]
if subcommand == "add" then
local objective_name = split_param[3] and split_param[3][3]
if not objective_name then return false, minetest.colorize("red", S("Missing name")), 0 end
if not objective_name then return false, better_commands.error(S("Missing name")), 0 end
if better_commands.scoreboard.objectives[objective_name] then
return false, minetest.colorize("red", S("Objective @1 already exists", objective_name)), 0
return false, better_commands.error(S("Objective @1 already exists", objective_name)), 0
end
local criterion = split_param[4] and split_param[4][3]
if not criterion then return false, minetest.colorize("red", S("Missing criterion")), 0 end
if not criterion then return false, better_commands.error(S("Missing criterion")), 0 end
if not better_commands.validate_criterion(criterion) then
return false, minetest.colorize("red", S("Invalid criterion @1", criterion)), 0
return false, better_commands.error(S("Invalid criterion @1", criterion)), 0
end
local display_name = (split_param[5] and param:sub(split_param[5][1], -1)) or objective_name
better_commands.scoreboard.objectives[objective_name] = {
@ -64,15 +64,15 @@ better_commands.register_command("scoreboard", {
return true, S("There are @1 objective(s): @2", objective_count, result), objective_count
elseif subcommand == "modify" then
local objective = split_param[3] and split_param[3][3]
if not objective then return false, minetest.colorize("red", S("Missing objective")), 0 end
if not objective then return false, better_commands.error(S("Missing objective")), 0 end
if not better_commands.scoreboard.objectives[objective] then
return false, minetest.colorize("red", S("Unknown scoreboard objective '@1'", objective)), 0
return false, better_commands.error(S("Unknown scoreboard objective '@1'", objective)), 0
end
local key = split_param[4] and split_param[4][3]
if not key then return false, minetest.colorize("red", S("Must be 'displayname' or 'numberformat'")), 0 end
if not key then return false, better_commands.error(S("Must be 'displayname' or 'numberformat'")), 0 end
local value = split_param[5] and split_param[5][3]
if key == "displayname" then
if not value then return false, minetest.colorize("red", S("Missing display name")), 0 end
if not value then return false, better_commands.error(S("Missing display name")), 0 end
local display_name = param:sub(split_param[5][1], -1):trim() -- Allow spaces
better_commands.scoreboard.objectives[objective].display_name = display_name
return true, S("@1 set to @2", "displayname", display_name), 1
@ -85,7 +85,7 @@ better_commands.register_command("scoreboard", {
better_commands.scoreboard.objectives[objective].format = {type = "blank"}
return true, S("@1 set to @2", "numberformat", "blank"), 1
elseif value == "fixed" then
if not split_param[6] then return false, minetest.colorize("red", S("Missing argument")), 0 end
if not split_param[6] then return false, better_commands.error(S("Missing argument")), 0 end
local fixed = param:sub(split_param[6][1], -1):trim() -- Allow spaces
better_commands.scoreboard.objectives[objective].format = {type = "fixed", data = fixed}
return true, S("@1 set to @2", "numberformat", fixed), 1
@ -96,37 +96,37 @@ better_commands.register_command("scoreboard", {
else
format = minetest.colorspec_to_colorstring(format)
if not value then
return false, minetest.colorize("red", S("Invalid color")), 0
return false, better_commands.error(S("Invalid color")), 0
end
end
better_commands.scoreboard.objectives[objective].format = {type = "color", data = format}
return true, S("@1 set to @2", "numberformat", format), 1
else
return false, minetest.colorize("red", S("Must be 'blank', 'fixed', or 'styled'")), 0
return false, better_commands.error(S("Must be 'blank', 'fixed', or 'styled'")), 0
end
else
return false, minetest.colorize("red", S("Must be 'displayname' or 'numberformat'")), 0
return false, better_commands.error(S("Must be 'displayname' or 'numberformat'")), 0
end
elseif subcommand == "remove" then
local objective = split_param[3] and split_param[3][3]
if not objective then return false, minetest.colorize("red", S("Missing objective")), 0 end
if not objective then return false, better_commands.error(S("Missing objective")), 0 end
if not better_commands.scoreboard.objectives[objective] then
return false, minetest.colorize("red", S("Unknown scoreboard objective '@1'", objective)), 0
return false, better_commands.error(S("Unknown scoreboard objective '@1'", objective)), 0
end
better_commands.scoreboard.objectives[objective] = nil
return true, S("Removed objective @1", objective), 1
elseif subcommand == "setdisplay" then
local location = split_param[3] and split_param[3][3]
if not location then return false, minetest.colorize("red", S("Missing argument")), 0 end
if not location then return false, better_commands.error(S("Missing argument")), 0 end
local objective = split_param[4] and split_param[4][3]
if objective and not better_commands.scoreboard.objectives[objective] then
return false, minetest.colorize("red", S("Unknown scoreboard objective '@1'", objective)), 0
return false, better_commands.error(S("Unknown scoreboard objective '@1'", objective)), 0
end
local display, sortable
if location == "list" then
return false, minetest.colorize("red", S("`list` support has not been added yet.")), 0
return false, better_commands.error(S("`list` support has not been added yet.")), 0
elseif location == "below_name" then
return false, minetest.colorize("red", S("`below_name` support has not been added yet.")), 0
return false, better_commands.error(S("`below_name` support has not been added yet.")), 0
elseif location == "sidebar" then
better_commands.scoreboard.displays.sidebar = {objective = objective}
display = better_commands.scoreboard.displays.sidebar
@ -134,12 +134,12 @@ better_commands.register_command("scoreboard", {
else
local color = location:match("^sidebar%.team.(.+)")
if not color then
return false, minetest.colorize("red", S("Must be 'list', 'below_name', 'sidebar', or 'sidebar.team.<color>")), 0
return false, better_commands.error(S("Must be 'list', 'below_name', 'sidebar', or 'sidebar.team.<color>")), 0
elseif better_commands.team_colors[color] then
display = better_commands.scoreboard.displays.colors[color]
better_commands.scoreboard.displays.colors[color] = {objective = objective}
else
return false, minetest.colorize("red", S("Invalid color: @1", color)), 0
return false, better_commands.error(S("Invalid color: @1", color)), 0
end
end
local sort = split_param[5] and split_param[5][3]
@ -148,31 +148,31 @@ better_commands.register_command("scoreboard", {
if sort == "ascending" then
display.ascending = true
elseif sort ~= "descending" then
return false, minetest.colorize("red", S("Expected ascending|descending, got @1", sort)), 0
return false, better_commands.error(S("Expected ascending|descending, got @1", sort)), 0
end
else
return false, minetest.colorize("red", S("Display slot @1 does not support sorting.", location)), 0
return false, better_commands.error(S("Display slot @1 does not support sorting.", location)), 0
end
end
return true, S("Set display slot @1 to show objective @2", location, objective), 1
else
return false, minetest.colorize("red", S("Expected 'add', 'list', 'modify', 'remove', or 'setdisplay', got '@1'", subcommand)), 0
return false, better_commands.error(S("Expected 'add', 'list', 'modify', 'remove', or 'setdisplay', got '@1'", subcommand)), 0
end
elseif split_param[1][3] == "players" then
local subcommand = split_param[2][3]
if subcommand == "add" or subcommand == "set" or subcommand == "remove" then
local selector = split_param[3]
if not selector then return false, minetest.colorize("red", S("Missing target")), 0 end
if not selector then return false, better_commands.error(S("Missing target")), 0 end
local objective = split_param[4] and split_param[4][3]
if not objective then return false, ("Missing objective"), 0 end
if not better_commands.scoreboard.objectives[objective] then
return false, minetest.colorize("red", S("Unknown scoreboard objective '@1'", objective)), 0
return false, better_commands.error(S("Unknown scoreboard objective '@1'", objective)), 0
end
local score = tonumber(split_param[5] and split_param[5][3])
if not score then return false, minetest.colorize("red", S("Missing score")), 0 end
if not score then return false, better_commands.error(S("Missing score")), 0 end
score = math.floor(score)
local names, err = better_commands.get_scoreboard_names(selector, context, objective)
if err or not names then return false, minetest.colorize("red", err), 0 end
if err or not names then return false, better_commands.error(err), 0 end
local last
local scores = better_commands.scoreboard.objectives[objective].scores
for name in pairs(names) do
@ -190,7 +190,7 @@ better_commands.register_command("scoreboard", {
end
local name_count = better_commands.count_table(names) or 0
if name_count < 1 then
return false, minetest.colorize("red", S("No scores found")), 0
return false, better_commands.error(S("No scores found")), 0
elseif name_count == 1 then
return true, S("Set score for @1", better_commands.format_name(last)), 1
else
@ -198,14 +198,14 @@ better_commands.register_command("scoreboard", {
end
elseif subcommand == "display" then
local key = split_param[3] and split_param[3][3]
if not key then return false, minetest.colorize("red", S("Must be 'name' or 'numberformat'")), 0 end
if not key then return false, better_commands.error(S("Must be 'name' or 'numberformat'")), 0 end
if key == "name" then
local selector = split_param[4]
if not selector then return false, minetest.colorize("red", S("Missing target")), 0 end
if not selector then return false, better_commands.error(S("Missing target")), 0 end
local objective = split_param[5] and split_param[5][3]
if not objective then return false, ("Missing objective"), 0 end
if not better_commands.scoreboard.objectives[objective] then
return false, minetest.colorize("red", S("Invalid objective: @1", objective)), 0
return false, better_commands.error(S("Invalid objective: @1", objective)), 0
end
local display_name = nil
if split_param[6] then
@ -213,7 +213,7 @@ better_commands.register_command("scoreboard", {
end
local scores = better_commands.scoreboard.objectives[objective].scores
local names, err = better_commands.get_scoreboard_names(selector, context, objective)
if err or not names then return false, minetest.colorize("red", err), 0 end
if err or not names then return false, better_commands.error(err), 0 end
local last
for name in pairs(names) do
last = name
@ -222,7 +222,7 @@ better_commands.register_command("scoreboard", {
end
local name_count = better_commands.count_table(names) or 0
if name_count < 1 then
return false, minetest.colorize("red", S("No entities found")), 0
return false, better_commands.error(S("No entities found")), 0
elseif name_count == 1 then
return true, S("Set display name of @1 to @2", better_commands.format_name(last), display_name or "default"), 1
else
@ -230,11 +230,11 @@ better_commands.register_command("scoreboard", {
end
elseif key == "numberformat" then
local selector = split_param[4] and split_param[4]
if not selector then return false, minetest.colorize("red", S("Missing target")), 0 end
if not selector then return false, better_commands.error(S("Missing target")), 0 end
local objective = split_param[5] and split_param[5][3]
if not objective then return false, ("Missing objective"), 0 end
if not better_commands.scoreboard.objectives[objective] then
return false, minetest.colorize("red", S("Invalid objective: @1", objective)), 0
return false, better_commands.error(S("Invalid objective: @1", objective)), 0
end
local value = split_param[5] and split_param[5][3]
local format = split_param[6] and split_param[6][3]
@ -246,7 +246,7 @@ better_commands.register_command("scoreboard", {
result = {type = "blank"}
return_value = S("@1 set to @2", "numberformat", "blank")
elseif value == "fixed" then
if not split_param[6] then return false, minetest.colorize("red", S("Missing argument")), 0 end
if not split_param[6] then return false, better_commands.error(S("Missing argument")), 0 end
local fixed = param:sub(split_param[6][1], -1):trim() -- Allow spaces
result = {type = "fixed", data = fixed}
return_value = S("@1 set to @2", "numberformat", fixed)
@ -257,16 +257,16 @@ better_commands.register_command("scoreboard", {
else
format = minetest.colorspec_to_colorstring(format)
if not value then
return false, minetest.colorize("red", S("Invalid color")), 0
return false, better_commands.error(S("Invalid color")), 0
end
end
result = {type = "color", data = format}
return_value = S("@1 set to @2", "numberformat", format)
else
return false, minetest.colorize("red", S("Must be 'blank', 'fixed', or 'styled'")), 0
return false, better_commands.error(S("Must be 'blank', 'fixed', or 'styled'")), 0
end
local names, err = better_commands.get_scoreboard_names(selector, context, objective)
if err or not names then return false, minetest.colorize("red", err), 0 end
if err or not names then return false, better_commands.error(err), 0 end
local scores = better_commands.scoreboard.objectives[objective].scores
local count = 0
for name in pairs(names) do
@ -276,22 +276,22 @@ better_commands.register_command("scoreboard", {
end
return true, return_value, count
else
return false, minetest.colorize("red", S("Must be 'name' or 'numberformat', not @1", key)), 0
return false, better_commands.error(S("Must be 'name' or 'numberformat', not @1", key)), 0
end
elseif subcommand == "enable" then
local selector = split_param[3]
if not selector then return false, minetest.colorize("red", S("Missing target")), 0 end
if not selector then return false, better_commands.error(S("Missing target")), 0 end
local objective = split_param[4] and split_param[4][3]
if not objective then return false, ("Missing objective"), 0 end
local objective_data = better_commands.scoreboard.objectives[objective]
if not (objective_data) then
return false, minetest.colorize("red", S("Invalid objective: @1", objective)), 0
return false, better_commands.error(S("Invalid objective: @1", objective)), 0
end
if objective_data.criterion ~= "trigger" then
return false, minetest.colorize("red", S("@1 is not a trigger objective", objective)), 0
return false, better_commands.error(S("@1 is not a trigger objective", objective)), 0
end
local names, err = better_commands.get_scoreboard_names(selector, context, objective)
if err or not names then return false, minetest.colorize("red", err), 0 end
if err or not names then return false, better_commands.error(err), 0 end
local scores = objective_data.scores
local display_name = objective_data.display_name or objective
local last
@ -302,7 +302,7 @@ better_commands.register_command("scoreboard", {
end
local name_count = better_commands.count_table(names) or 0
if name_count < 1 then
return false, minetest.colorize("red", S("No players found")), 0
return false, better_commands.error(S("No players found")), 0
elseif name_count == 1 then
return true, S("Enabled trigger [@1] for @2", display_name, better_commands.format_name(last)), 1
else
@ -310,21 +310,21 @@ better_commands.register_command("scoreboard", {
end
elseif subcommand == "get" then
local selector = split_param[3] and split_param[3]
if not selector then return false, minetest.colorize("red", S("Missing target")), 0 end
if not selector then return false, better_commands.error(S("Missing target")), 0 end
local objective = split_param[4] and split_param[4][3]
if not objective then return false, ("Missing objective"), 0 end
if not better_commands.scoreboard.objectives[objective] then
return false, minetest.colorize("red", S("Unknown scoreboard objective '@1'", objective)), 0
return false, better_commands.error(S("Unknown scoreboard objective '@1'", objective)), 0
end
local names, err = better_commands.get_scoreboard_names(selector, context, objective, true)
if err or not names then return false, minetest.colorize("red", err), 0 end
if err or not names then return false, better_commands.error(err), 0 end
local name = names[1]
if name then
local score = better_commands.scoreboard.objectives[objective].scores[name]
local display_name = better_commands.scoreboard.objectives[objective].display_name or objective
return true, S("@1 has @2 [@3]", better_commands.format_name(name), score, display_name), 1
else
return false, minetest.colorize("red", S("@1 does not have a score for @2", better_commands.format(name)), objective), 1
return false, better_commands.error(S("@1 does not have a score for @2", better_commands.format(name), objective)), 1
end
elseif subcommand == "list" then
local selector = split_param[3]
@ -353,7 +353,7 @@ better_commands.register_command("scoreboard", {
return true, S("There are @1 tracked player(s): @2", result_count, result_string), result_count
else
local names, err = better_commands.get_scoreboard_names(selector, context, nil, true)
if err or not names then return false, minetest.colorize("red", err), 0 end
if err or not names then return false, better_commands.error(err), 0 end
local name = names[1]
local results = {}
for _, data in pairs(better_commands.scoreboard.objectives) do
@ -376,30 +376,30 @@ better_commands.register_command("scoreboard", {
end
elseif subcommand == "operation" then
local source_selector = split_param[3]
if not source_selector then return false, minetest.colorize("red", S("Missing source selector")), 0 end
if not source_selector then return false, better_commands.error(S("Missing source selector")), 0 end
local source_objective = split_param[4] and split_param[4][3]
if not source_objective then return false, minetest.colorize("red", S("Missing source objective")), 0 end
if not source_objective then return false, better_commands.error(S("Missing source objective")), 0 end
if not better_commands.scoreboard.objectives[source_objective] then
return false, minetest.colorize("red", S("Invalid source objective")), 0
return false, better_commands.error(S("Invalid source objective")), 0
end
local operator = split_param[5] and split_param[5][3]
if not operator then return false, minetest.colorize("red", S("Missing operator")), 0 end
if not operator then return false, better_commands.error(S("Missing operator")), 0 end
if not scoreboard_operators[operator] then
return false, minetest.colorize("red", S("Invalid operator: @1", operator)), 0
return false, better_commands.error(S("Invalid operator: @1", operator)), 0
end
local target_selector = split_param[6]
if not target_selector then return false, minetest.colorize("red", S("Missing target selector")), 0 end
if not target_selector then return false, better_commands.error(S("Missing target selector")), 0 end
local target_objective = split_param[7] and split_param[7][3]
if not target_objective then return false, minetest.colorize("red", S("Missing target objective")), 0 end
if not target_objective then return false, better_commands.error(S("Missing target objective")), 0 end
if not better_commands.scoreboard.objectives[target_objective] then
return false, minetest.colorize("red", S("Invalid target objective")), 0
return false, better_commands.error(S("Invalid target objective")), 0
end
local sources, err = better_commands.get_scoreboard_names(source_selector, context)
if err or not sources then return false, minetest.colorize("red", err), 0 end
if err or not sources then return false, better_commands.error(err), 0 end
local targets, err = better_commands.get_scoreboard_names(target_selector, context)
local source_scores = better_commands.scoreboard.objectives[source_objective].scores
local target_scores = better_commands.scoreboard.objectives[target_objective].scores
if err or not targets then return false, minetest.colorize("red", err), 0 end
if err or not targets then return false, better_commands.error(err), 0 end
local change_count, score_count = 0, 0
local last_source, last_target, op_string, preposition
local swap = false
@ -458,7 +458,7 @@ better_commands.register_command("scoreboard", {
end
end
if change_count < 1 then
return false, minetest.colorize("red", S("No entity was found")), 0
return false, better_commands.error(S("No entity was found")), 0
elseif change_count == 1 then
return true, S(
"@1 [@2] score of @3 @4 [@5] score of @6", -- a bit unnecessary, perhaps.
@ -474,23 +474,23 @@ better_commands.register_command("scoreboard", {
end
elseif subcommand == "random" then
local selector = split_param[3]
if not selector then return false, minetest.colorize("red", S("Missing selector")), 0 end
if not selector then return false, better_commands.error(S("Missing selector")), 0 end
local objective = split_param[4] and split_param[4][3]
if not objective then return false, minetest.colorize("red", S("Missing objective")), 0 end
if not objective then return false, better_commands.error(S("Missing objective")), 0 end
if not better_commands.scoreboard.objectives[objective] then
return false, minetest.colorize("red", S("Invalid objective")), 0
return false, better_commands.error(S("Invalid objective")), 0
end
local min = split_param[5] and split_param[5][3]
if not min then return false, minetest.colorize("red", S("Missing min")), 0 end
if not min then return false, better_commands.error(S("Missing min")), 0 end
---@diagnostic disable-next-line: cast-local-type
min = tonumber(min)
if not min then return false, minetest.colorize("red", S("Must be a number")), 0 end
if not min then return false, better_commands.error(S("Must be a number")), 0 end
local max = split_param[6] and split_param[6][3]
if not max then return false, minetest.colorize("red", S("Missing max")), 0 end
if not max then return false, better_commands.error(S("Missing max")), 0 end
max = tonumber(max)
if not max then return false, minetest.colorize("red", S("Must be a number")), 0 end
if not max then return false, better_commands.error(S("Must be a number")), 0 end
local names, err = better_commands.get_scoreboard_names(selector, context)
if err or not names then return false, minetest.colorize("red", err), 0 end
if err or not names then return false, better_commands.error(err), 0 end
local scores = better_commands.scoreboard.objectives[objective].scores
local count = 0
local last
@ -501,7 +501,7 @@ better_commands.register_command("scoreboard", {
scores[name].score = math.random(min, max)
end
if count < 1 then
return false, minetest.colorize("red", S("No target entities found")), 0
return false, better_commands.error(S("No target entities found")), 0
elseif count == 1 then
return true, S("Randomized score for @1", better_commands.format_name(last)), 1
else
@ -509,13 +509,13 @@ better_commands.register_command("scoreboard", {
end
elseif subcommand == "reset" then
local selector = split_param[3]
if not selector then return false, minetest.colorize("red", S("Missing selector")), 0 end
if not selector then return false, better_commands.error(S("Missing selector")), 0 end
local objective = split_param[4] and split_param[4][3]
if objective and not better_commands.scoreboard.objectives[objective] then
return false, minetest.colorize("red", S("Invalid objective")), 0
return false, better_commands.error(S("Invalid objective")), 0
end
local names, err = better_commands.get_scoreboard_names(selector, context)
if err or not names then return false, minetest.colorize("red", err), 0 end
if err or not names then return false, better_commands.error(err), 0 end
local count = 0
local last
for name in pairs(names) do
@ -538,35 +538,35 @@ better_commands.register_command("scoreboard", {
end
elseif subcommand == "test" then
local selector = split_param[3]
if not selector then return false, minetest.colorize("red", S("Missing selector")), 0 end
if not selector then return false, better_commands.error(S("Missing selector")), 0 end
local objective = split_param[4] and split_param[4][3]
if not objective then return false, minetest.colorize("red", S("Missing objective")), 0 end
if not objective then return false, better_commands.error(S("Missing objective")), 0 end
if not better_commands.scoreboard.objectives[objective] then
return false, minetest.colorize("red", S("Invalid objective")), 0
return false, better_commands.error(S("Invalid objective")), 0
end
local min = split_param[5] and split_param[5][3]
if not min then return false, minetest.colorize("red", S("Missing min")), 0 end
if not min then return false, better_commands.error(S("Missing min")), 0 end
if min == "*" then min = -99999999999999 end -- the minimum value before losing precision
min = tonumber(min)
if not min then return false, minetest.colorize("red", S("Must be a number")), 0 end
if not min then return false, better_commands.error(S("Must be a number")), 0 end
local max = split_param[6] and split_param[6][3]
if not max then return false, minetest.colorize("red", S("Missing max")), 0 end
if not max then return false, better_commands.error(S("Missing max")), 0 end
if max == "*" then max = 100000000000000 end -- the maximum value before losing precision
max = tonumber(max)
if not max then return false, minetest.colorize("red", S("Must be a number")), 0 end
if not max then return false, better_commands.error(S("Must be a number")), 0 end
local names, err = better_commands.get_scoreboard_names(selector, context, objective, true)
if err or not names then return false, minetest.colorize("red", err), 0 end
if err or not names then return false, better_commands.error(err), 0 end
local scoreboard_name = names[1]
local scores = better_commands.scoreboard.objectives[objective].scores
if not scores[scoreboard_name] then
return false, minetest.colorize("red", S("Player @1 has no scores recorded", better_commands.format_name(scoreboard_name))), 0
return false, better_commands.error(S("Player @1 has no scores recorded", better_commands.format_name(scoreboard_name))), 0
elseif scores[scoreboard_name].score >= min and scores[scoreboard_name].score <= max then
return true, S("Score @1 is in range @2 to @3", scores[scoreboard_name].score, min, max), 1
else
return false, minetest.colorize("red", S("Score @1 is NOT in range @2 to @3", scores[scoreboard_name].score, min, max)), 0
return false, better_commands.error(S("Score @1 is NOT in range @2 to @3", scores[scoreboard_name].score, min, max)), 0
end
else
return false, minetest.colorize("red", S("Expected 'add', 'display', 'enable', 'get', 'list', 'operation', 'random', 'reset', 'set', or 'test', got @1", subcommand)), 0
return false, better_commands.error(S("Expected 'add', 'display', 'enable', 'get', 'list', 'operation', 'random', 'reset', 'set', or 'test', got @1", subcommand)), 0
end
else
return false, nil, 0
@ -580,7 +580,7 @@ better_commands.register_command("trigger", {
param = "<objective> [add|set <value>]",
func = function (name, param, context)
if not (context.executor.is_player and context.executor:is_player()) then
return false, minetest.colorize("red", S("/trigger can only be used by players")), 0
return false, better_commands.error(S("/trigger can only be used by players")), 0
end
local player_name = context.executor:get_player_name()
local split_param = better_commands.parse_params(param)
@ -588,17 +588,17 @@ better_commands.register_command("trigger", {
if not objective then return false, nil, 0 end
local objective_data = better_commands.scoreboard.objectives[objective]
if not objective_data then
return false, minetest.colorize("red", S("Unknown scoreboard objective '@1'", objective)), 0
return false, better_commands.error(S("Unknown scoreboard objective '@1'", objective)), 0
end
if objective_data.criterion ~= "trigger" then
return false, minetest.colorize("red", S("You can only trigger objectives that are 'trigger' type")), 0
return false, better_commands.error(S("You can only trigger objectives that are 'trigger' type")), 0
end
local scores = objective_data.scores[player_name]
if not scores then
return false, minetest.colorize("red", S("You cannot trigger this objective yet")), 0
return false, better_commands.error(S("You cannot trigger this objective yet")), 0
end
if not scores.enabled then
return false, minetest.colorize("red", S("You cannot trigger this objective yet")), 0
return false, better_commands.error(S("You cannot trigger this objective yet")), 0
end
local subcommand = split_param[2] and split_param[2][3]
local display_name = objective_data.display_name or objective
@ -608,9 +608,9 @@ better_commands.register_command("trigger", {
return true, S("Triggered [@1]", display_name), scores.score
else
local value = split_param[3] and split_param[3][3]
if not value then return false, minetest.colorize("red", S("Missing value")), 0 end
if not value then return false, better_commands.error(S("Missing value")), 0 end
value = tonumber(value)
if not value then return false, minetest.colorize("red", S("Value must be a number")), 0 end
if not value then return false, better_commands.error(S("Value must be a number")), 0 end
if subcommand == "add" then
scores.score = scores.score + math.floor(value)
scores.enabled = false
@ -620,7 +620,7 @@ better_commands.register_command("trigger", {
scores.enabled = false
return true, S("Triggered [@1] (set value to @2)", display_name, value), scores.score
else
return false, minetest.colorize("red", S("Expected 'add' or 'set', got @1", subcommand)), 0
return false, better_commands.error(S("Expected 'add' or 'set', got @1", subcommand)), 0
end
end
end

View File

@ -13,16 +13,16 @@ better_commands.register_command("setblock", {
if split_param[5] then
keep = split_param[5][3]:lower()
if keep ~= "keep" and keep ~= "replace" then
return false, minetest.colorize("red", S("Last argument ust be either 'replace' (default)), 'keep', or missing, not @1", keep), 0)
return false, better_commands.error(S("Last argument ust be either 'replace' (default)), 'keep', or missing, not @1", keep)), 0
end
end
local pos, err = better_commands.parse_pos(split_param, 1, context)
if err or not pos then return false, minetest.colorize("red", err), 0 end
if err or not pos then return false, better_commands.error(err), 0 end
local node, meta, err = better_commands.parse_node(split_param[4])
if err or not node then return false, minetest.colorize("red", err), 0 end
if err or not node then return false, better_commands.error(err), 0 end
if keep == "keep" and minetest.get_node(pos).name ~= "air" then
return false, minetest.colorize("red", S("Position is not empty")), 0
return false, better_commands.error(S("Position is not empty")), 0
end
minetest.set_node(pos, node)

View File

@ -27,7 +27,7 @@ better_commands.register_command("gamerule", {
if value then
return true, S("@1 = @2", setting, value), 1
else
return false, minetest.colorize("red", S("Setting @1 has not been set", setting)), 1
return false, better_commands.error(S("Setting @1 has not been set", setting)), 1
end
end
end

View File

@ -6,18 +6,18 @@ better_commands.register_command("spawnpoint", {
params = S("[targets]"),
func = function (name, param, context)
local split_param, err = better_commands.parse_params(param)
if err then return false, minetest.colorize("red", err), 0 end
if err then return false, better_commands.error(err), 0 end
local selector = split_param[1]
if not selector then
if context.executor.is_player and context.executor:is_player() then
better_commands.spawnpoints[context.executor:get_player_name()] = context.pos
return true, S("Spawn point set"), 1
else
return false, minetest.colorize("red", S("Non-player entities are not supported by this command"))
return false, better_commands.error(S("Non-player entities are not supported by this command"))
end
else
local targets, err = better_commands.parse_selector(selector, context)
if err or not targets then return false, minetest.colorize("red", err), 0 end
if err or not targets then return false, better_commands.error(err), 0 end
local last
local count = 0
for _, target in ipairs(targets) do
@ -28,7 +28,7 @@ better_commands.register_command("spawnpoint", {
end
end
if count < 1 then
return false, minetest.colorize("red", S("No player was found.")), 0
return false, better_commands.error(S("No player was found.")), 0
elseif count == 1 then
return true, S("Set spawn point to @1 for @2", minetest.pos_to_string(context.pos, 1), last), 1
else
@ -44,18 +44,18 @@ better_commands.register_command("clearspawnpoint", {
params = S("[targets]"),
func = function (name, param, context)
local split_param, err = better_commands.parse_params(param)
if err then return false, minetest.colorize("red", err), 0 end
if err then return false, better_commands.error(err), 0 end
local selector = split_param[1]
if not selector then
if context.executor.is_player and context.executor:is_player() then
better_commands.spawnpoints[context.executor:get_player_name()] = nil
return true, S("Spawn point cleared"), 1
else
return false, minetest.colorize("red", S("Non-player entities are not supported by this command"))
return false, better_commands.error(S("Non-player entities are not supported by this command"))
end
else
local targets, err = better_commands.parse_selector(selector, context)
if err or not targets then return false, minetest.colorize("red", err), 0 end
if err or not targets then return false, better_commands.error(err), 0 end
local last
local count = 0
for _, target in ipairs(targets) do
@ -66,7 +66,7 @@ better_commands.register_command("clearspawnpoint", {
end
end
if count < 1 then
return false, minetest.colorize("red", S("No player was found.")), 0
return false, better_commands.error(S("No player was found.")), 0
elseif count == 1 then
return true, S("Cleared spawn point for @2", last), 1
else

View File

@ -8,23 +8,23 @@ better_commands.register_command("summon", {
func = function(name, param, context)
local split_param = better_commands.parse_params(param)
local entity = split_param[1]
if not entity then return false, minetest.colorize("red", S("Missing entity")), 0 end
if not entity then return false, better_commands.error(S("Missing entity")), 0 end
local checked_entity = better_commands.entity_from_alias(entity[3])
if not checked_entity then return false, minetest.colorize("red", S("Invalid entity: @1", entity[3])), 0 end
if not checked_entity then return false, better_commands.error(S("Invalid entity: @1", entity[3])), 0 end
local summoned
if split_param[2] then
local pos, err = better_commands.parse_pos(split_param, 2, context)
if err or not pos then return false, minetest.colorize("red", err), 0 end
if err or not pos then return false, better_commands.error(err), 0 end
summoned = minetest.add_entity(pos, checked_entity, entity[4])
if not summoned then return false, minetest.colorize("red", S("Could not summon @1", entity[3])), 0 end
if not summoned then return false, better_commands.error(S("Could not summon @1", entity[3])), 0 end
if split_param[5] then
local victim_rot, err = better_commands.get_tp_rot(context, summoned, split_param, 5)
if err or not victim_rot then return false, minetest.colorize("red", err), 0 end
if err or not victim_rot then return false, better_commands.error(err), 0 end
better_commands.set_entity_rotation(summoned, victim_rot)
end
else
summoned = minetest.add_entity(context.pos, checked_entity, entity[4])
if not summoned then return false, minetest.colorize("red", S("Could not summon @1", entity[3])), 0 end
if not summoned then return false, better_commands.error(S("Could not summon @1", entity[3])), 0 end
end
return true, S("Summoned @1", better_commands.get_entity_name(summoned)), 1
end

View File

@ -7,17 +7,17 @@ better_commands.register_command("team", {
privs = {server = true},
func = function (name, param, context)
local split_param, err = better_commands.parse_params(param)
if err then return false, minetest.colorize("red", err), 0 end
if not split_param[1] then return false, minetest.colorize("red", S("Missing subcommand")), 0 end
if err then return false, better_commands.error(err), 0 end
if not split_param[1] then return false, better_commands.error(S("Missing subcommand")), 0 end
local subcommand = split_param[1] and split_param[1][3]
if subcommand == "add" then
local team_name = split_param[2] and split_param[2][3]
if not team_name then return false, minetest.colorize("red", S("Missing team name")), 0 end
if not team_name then return false, better_commands.error(S("Missing team name")), 0 end
if better_commands.teams.teams[team_name] then
return false, minetest.colorize("red", S("Team @1 already exists", team_name)), 0
return false, better_commands.error(S("Team @1 already exists", team_name)), 0
end
if team_name:find("[^%w_]") then
return false, minetest.colorize("red", S("Invalid team name @1: Can only contain letters, numbers, and underscores", team_name)), 0
return false, better_commands.error(S("Invalid team name @1: Can only contain letters, numbers, and underscores", team_name)), 0
end
local display_name = split_param[3] and split_param[3][3]
if not display_name then display_name = team_name end
@ -25,7 +25,7 @@ better_commands.register_command("team", {
return true, S("Added team @1", team_name), 1
elseif subcommand == "empty" or subcommand == "remove" then
local team_name = split_param[2] and split_param[2][3]
if not team_name then return false, minetest.colorize("red", S("Missing team name")), 0 end
if not team_name then return false, better_commands.error(S("Missing team name")), 0 end
if better_commands.teams.teams[team_name] then
local display_name = better_commands.format_team_name(team_name)
if subcommand == "remove" then
@ -41,13 +41,13 @@ better_commands.register_command("team", {
end
return true, S("Removed all players from team [@1]", display_name), 1
else
return false, minetest.colorize("red", S("Team @1 does not exist", team_name)), 0
return false, better_commands.error(S("Team @1 does not exist", team_name)), 0
end
elseif subcommand == "join" then
local team_name = split_param[2] and split_param[2][3]
if not team_name then return false, minetest.colorize("red", S("Missing team name")), 0 end
if not team_name then return false, better_commands.error(S("Missing team name")), 0 end
if not better_commands.teams.teams[team_name] then
return false, minetest.colorize("red", S("Team @1 does not exist", team_name)), 0
return false, better_commands.error(S("Team @1 does not exist", team_name)), 0
end
local selector = split_param[3]
if not selector then
@ -59,14 +59,14 @@ better_commands.register_command("team", {
local count = 0
local last
local names, err = better_commands.get_scoreboard_names(selector, context)
if err or not names then return false, minetest.colorize("red", err), 0 end
if err or not names then return false, better_commands.error(err), 0 end
for name in pairs(names) do
if count < 1 then last = better_commands.format_name(name) end
count = count + 1
better_commands.teams.players[name] = team_name
end
if count < 1 then
return false, minetest.colorize("red", S("No target entities found")), 0
return false, better_commands.error(S("No target entities found")), 0
elseif count == 1 then
return true, S("Added @1 to team [@2]", better_commands.format_name(last), better_commands.format_team_name(team_name)), 1
else
@ -83,11 +83,11 @@ better_commands.register_command("team", {
count = 1
better_commands.teams.players[last] = nil
else
return false, minetest.colorize("red", S("Non-players cannot be on a team")), 0
return false, better_commands.error(S("Non-players cannot be on a team")), 0
end
else
local names, err = better_commands.get_scoreboard_names(selector, context)
if err or not names then return false, minetest.colorize("red", err), 0 end
if err or not names then return false, better_commands.error(err), 0 end
for _, name in ipairs(names) do
if better_commands.teams.players[name] then
count = count + 1
@ -97,7 +97,7 @@ better_commands.register_command("team", {
end
end
if count < 1 then
return false, minetest.colorize("red", S("No target entities found")), 0
return false, better_commands.error(S("No target entities found")), 0
elseif count == 1 then
return true, S("Removed @1 from any team", better_commands.format_name(last)), 1
else
@ -140,19 +140,19 @@ better_commands.register_command("team", {
return true, S("There are no members on team [@1]", display_name), 1
end
else
return false, minetest.colorize("red", S("Team [@1] does not exist", team_name)), 0
return false, better_commands.error(S("Team [@1] does not exist", team_name)), 0
end
elseif subcommand == "modify" then
local team_name = split_param[2] and split_param[2][3]
if not team_name then return false, minetest.colorize("red", S("Team name is required")), 0 end
if not team_name then return false, better_commands.error(S("Team name is required")), 0 end
local team_data = better_commands.teams.teams[team_name]
if not team_data then return false, minetest.colorize("red", S("Unknown team '@1'", team_name)), 0 end
if not team_data then return false, better_commands.error(S("Unknown team '@1'", team_name)), 0 end
local key = split_param[3] and split_param[3][3]
if not key then return false, minetest.colorize("red", S("Missing key")), 0 end
if not key then return false, better_commands.error(S("Missing key")), 0 end
local value = split_param[4] and split_param[4][3]
if key == "color" then
if value then
if not better_commands.team_colors[value] then return false, minetest.colorize("red", S("Invalid color: @1", value)), 0 end
if not better_commands.team_colors[value] then return false, better_commands.error(S("Invalid color: @1", value)), 0 end
team_data.color = value
return true, S("Set color of team [@1] to @2", better_commands.format_team_name(team_name), value), 1
else
@ -173,7 +173,7 @@ better_commands.register_command("team", {
elseif value == "false" then
team_data.pvp = false
else
return false, minetest.colorize("red", S("Value must be 'true' or 'false', not @1", value)), 0
return false, better_commands.error(S("Value must be 'true' or 'false', not @1", value)), 0
end
return true, S("Set friendly fire for team [@1] to @2", better_commands.format_team_name(team_name), value), 1
elseif key == "nameFormat" then
@ -185,9 +185,9 @@ better_commands.register_command("team", {
team_data.name_format = name_format
return true, S("Set name format for team [@1] to @2", better_commands.format_team_name(team_name), value), 1
else
return false, minetest.colorize("red", S("Value must be 'color', 'displayName', 'friendlyFire', or 'nameFormat'")), 0
return false, better_commands.error(S("Value must be 'color', 'displayName', 'friendlyFire', or 'nameFormat'")), 0
end
end
return false, minetest.colorize("red", S("Must be 'add', 'empty', 'join', 'leave', 'list', 'modify', or 'remove', not @1", subcommand)), 0
return false, better_commands.error(S("Must be 'add', 'empty', 'join', 'leave', 'list', 'modify', or 'remove', not @1", subcommand)), 0
end
})

View File

@ -12,10 +12,10 @@ better_commands.register_command("teleport", {
if split_param[1].type == "selector" then
if not split_param[2] then
if not context.executor.is_player then
return false, minetest.colorize("red", S("Command blocks can't teleport (although I did consider making it possible)")), 0
return false, better_commands.error(S("Command blocks can't teleport (although I did consider making it possible)")), 0
end
local targets, err = better_commands.parse_selector(split_param[1], context, true)
if err or not targets then return false, minetest.colorize("red", err), 0 end
if err or not targets then return false, better_commands.error(err), 0 end
local target_pos = targets[1].is_player and targets[1]:get_pos() or targets[1]
context.executor:set_pos(target_pos)
context.executor:add_velocity(-context.executor:get_velocity())
@ -24,15 +24,15 @@ better_commands.register_command("teleport", {
return true, S("Teleported @1 to @2", better_commands.get_entity_name(context.executor), better_commands.get_entity_name(targets[1])), 1
elseif split_param[2].type == "selector" then
if not context.executor.is_player and split_param[1][3] == "@s" then
return false, minetest.colorize("red", S("Command blocks can't teleport (although I did consider making it possible)")), 0
return false, better_commands.error(S("Command blocks can't teleport (although I did consider making it possible)")), 0
end
local victims, err = better_commands.parse_selector(split_param[1], context)
if err or not victims then return false, minetest.colorize("red", err), 0 end
if err or not victims then return false, better_commands.error(err), 0 end
if #victims == 0 then
return false, minetest.colorize("red", S("No entity was found")), 0
return false, better_commands.error(S("No entity was found")), 0
end
local targets, err = better_commands.parse_selector(split_param[2], context, true)
if err or not targets then return false, minetest.colorize("red", err), 0 end
if err or not targets then return false, better_commands.error(err), 0 end
local target_pos = targets[1].is_player and targets[1]:get_pos() or targets[1]
local count = 0
local last
@ -47,7 +47,7 @@ better_commands.register_command("teleport", {
end
end
if count < 1 then
return false, minetest.colorize("red", S("No entities found")), 0
return false, better_commands.error(S("No entities found")), 0
elseif count == 1 then
return true, S(
"Teleported @1 to @2",
@ -65,12 +65,12 @@ better_commands.register_command("teleport", {
end
elseif split_param[2].type == "number" or split_param[2].type == "relative" or split_param[2].type == "look_relative" then
if not context.executor.is_player and split_param[1][3] == "@s" then
return false, minetest.colorize("red", S("Command blocks can't teleport (although I did consider making it possible)")), 0
return false, better_commands.error(S("Command blocks can't teleport (although I did consider making it possible)")), 0
end
local victims, err = better_commands.parse_selector(split_param[1], context)
if err or not victims then return false, minetest.colorize("red", err), 0 end
if err or not victims then return false, better_commands.error(err), 0 end
local target_pos, err = better_commands.parse_pos(split_param, 2, context)
if err then return false, minetest.colorize("red", err), 0 end
if err then return false, better_commands.error(err), 0 end
local count = 0
local last
for _, victim in ipairs(victims) do
@ -85,14 +85,14 @@ better_commands.register_command("teleport", {
victim:add_velocity(-victim:get_velocity())
end
local victim_rot, err = better_commands.get_tp_rot(context, victim, split_param, 5)
if err then return false, minetest.colorize("red", err), 0 end
if err then return false, better_commands.error(err), 0 end
if victim_rot then
better_commands.set_entity_rotation(victim, victim_rot)
end
end
end
if count < 1 then
return false, minetest.colorize("red", S("No entities found")), 0
return false, better_commands.error(S("No entities found")), 0
elseif count == 1 then
return true, S("Teleported @1 to @2", last, minetest.pos_to_string(target_pos, 1)), 1
else
@ -101,11 +101,11 @@ better_commands.register_command("teleport", {
end
elseif split_param[1].type == "number" or split_param[1].type == "relative" or split_param[1].type == "look_relative" then
if not context.executor.is_player and split_param[1][3] == "@s" then
return false, minetest.colorize("red", S("Command blocks can't teleport (although I did consider making it possible)")), 0
return false, better_commands.error(S("Command blocks can't teleport (although I did consider making it possible)")), 0
end
local target_pos, err = better_commands.parse_pos(split_param, 1, context)
if err then
return false, minetest.colorize("red", err), 0
return false, better_commands.error(err), 0
end
context.executor:set_pos(target_pos)
if not (split_param[1].type == "look_relative"
@ -115,7 +115,7 @@ better_commands.register_command("teleport", {
context.executor:add_velocity(-context.executor:get_velocity())
end
local victim_rot, err = better_commands.get_tp_rot(context, context.executor, split_param, 4)
if err or not victim_rot then return false, minetest.colorize("red", err), 0 end
if err or not victim_rot then return false, better_commands.error(err), 0 end
better_commands.set_entity_rotation(context.executor, victim_rot)
return true, S("Teleported @1 to @2", better_commands.get_entity_name(context.executor), minetest.pos_to_string(target_pos, 1)), 1
end

View File

@ -21,7 +21,7 @@ better_commands.register_command("time", {
local time = split_param[2][3]:lower()
if action == "add" then
local new_time, err = better_commands.parse_time_string(time)
if err then return false, minetest.colorize("red", err), 0 end
if err then return false, better_commands.error(err), 0 end
minetest.set_timeofday(new_time)
return true, S("Time set"), 1
elseif action == "query" then
@ -36,13 +36,13 @@ better_commands.register_command("time", {
elseif time == "day" then
return true, S("Day count: @1", minetest.get_day_count()), 1
end
return false, minetest.colorize("red", S("Must be 'daytime', 'gametime', or 'day', got @1", time)), 0
return false, better_commands.error(S("Must be 'daytime', 'gametime', or 'day', got @1", time)), 0
elseif action == "set" then
local new_time, err = better_commands.parse_time_string(time, true)
if err then return false, minetest.colorize("red", err), 0 end
if err then return false, better_commands.error(err), 0 end
minetest.set_timeofday(new_time)
return true, S("Time set"), 1
end
return false, minetest.colorize("red", S("Must be 'add', 'set', or 'query'")), 0
return false, better_commands.error(S("Must be 'add', 'set', or 'query'")), 0
end
})

View File

@ -2,7 +2,6 @@
A place for me to write out my future plans. I decide what to do next [randomly](https://wheelofnames.com/24r-ygp) (yes, this is genuinely how I do it).
- [ ] Add scoreboard playerlist and nametags (?)
- [ ] Figure out feet/eyes since most entities don't have that
- [ ] Make output match ACOVG's (error messages, number results, etc.)
- [ ] Add more scoreboard criteria (settings to disable)
- [ ] `xp`/`level` (MCLA/VL only)
@ -76,10 +75,9 @@ A place for me to write out my future plans. I decide what to do next [randomly]
- [ ] `pardon`
- [ ] `pardon-ip`
- [ ] `particle`
- [ ] `place`
- [ ] `random` (although seeds seem to be somewhat inconsistent in MT)
- [ ] `recipe` (MCLA/VL only)
- [ ] `remove`
- [x] `remove`
- [ ] `replaceitem`
- [ ] `return`
- [ ] `ride`?