minetest-mod-invhack/init.lua

239 lines
8.4 KiB
Lua

hacktool = {}
-- Are the optional dependencies enabled?
local xdecor_enabled = minetest.get_modpath("xdecor")
local unified_inventory_enabled = minetest.get_modpath("unified_inventory")
local mailbox_enabled = minetest.get_modpath("mailbox")
-- Build a list of inventories depening on the mods enabled
local inventories = {{ label = "Main", name = "main" }}
if xdecor_enabled then
table.insert(inventories, { label = "Ender", name = "enderchest" })
end
if mailbox_enabled then
table.insert(inventories, { label = "Mail", name = "mailbox" })
end
if unified_inventory_enabled then
for bag_i = 1, 4 do
table.insert(inventories, {
label = ("Bag %i"):format(bag_i),
name = ("bag%icontents"):format(bag_i)
})
end
end
-- Make the form wider if there are too many buttons
local formspec_width = math.max(8, #inventories*2)
-- Formspec contexts, to maintain the same target user while switching formspecs
local contexts = {}
local function get_target_player(user_name)
local context = contexts[user_name]
if not context then return end
local player = context.target
if not player or player:is_player() ~= true then return end
return player
end
-- Does str start with start, if so return everything else
local function starts_with(str, start)
local len = #start
if str:sub(1, len) == start then
return str:sub(len+1)
end
end
-- Show the formspec for a given inventory to the given user
local function show_formspec(user_name, inventory_name)
local player = get_target_player(user_name)
if not player then return end
if not inventory_name then inventory_name = "main" end
minetest.show_formspec(
user_name,
"invhack.form",
hacktool.formspec(user_name, player, inventory_name)
)
end
-- Generate a formspec for the given inventory positioned at X, y
local function inventory_formspec(name, inv, X, y)
if not y then y = 0 end
if not X then X = 0 end
local x = X
local formspec = ""
local size = inv:get_size(name)
for i = 1, size, 1 do
local stack = inv:get_stack(name, i)
local count = stack:get_count()
formspec = formspec .. ("item_image_button[%f,%f;1,1;%s;item:%s:%i;\n\n\b\b\b%s]"):format(
x, y,
stack:get_name(),
name, i,
count > 0 and tostring(count) or ""
)
x = x + 1
if x >= X + 8 then
x = X
y = y + 1
end
end
return formspec, y
end
local grab = false
-- Generate the entire formspec for this tool
hacktool.formspec = function(user_name, player, inventory_name)
local formspec = ("size[8,5.4]checkbox[0,5;grab;Grab item;"..tostring(grab).."]")
for i, inv_def in ipairs(inventories) do
formspec = formspec .. ("button[%f,4.2;1,1;inventory:%s;%s]"):format((i - 1) * 1, inv_def.name, inv_def.label)
end
formspec = formspec .. inventory_formspec(inventory_name, player:get_inventory(), 0, 0)
--formspec = formspec .. inventory_formspec(inventory_name, player:get_inventory(), (formspec_width - 8) / 1, 0)
return formspec
end
minetest.register_privilege("invhack", {
description = "Allows check sus players' inventories",
give_to_singleplayer = true,
})
-- Respond to a button press on the formspec
minetest.register_on_player_receive_fields(function(user, form, pressed)
-- Not our concern, exit
if form ~= "invhack.form" then return end
local user_name = user:get_player_name()
-- Exit
if pressed.quit then
contexts[user_name] = nil
return
end
-- No privs, this should not happen, but might
if not minetest.check_player_privs(user_name, {invhack=true}) then
minetest.chat_send_player(user_name, "Missing privilege: invhack")
return
end
-- Check if target player exists
local player = get_target_player(user_name)
if not player then
contexts[user_name] = nil
minetest.chat_send_player(user_name, "The formspec context is empty or the player is offline")
return
end
-- Figure out which action is intended
local selected_inventory
local selected_item
for name,_ in pairs(pressed) do
selected_inventory = starts_with(name, "inventory:")
if selected_inventory then break end
selected_item = starts_with(name, "item:")
if selected_item then break end
end
if pressed.grab then
grab = not grab
end
-- Move an item from the target players inventory to the invhack user's inventory
if selected_item then
-- Split apart the button name to get the inventory and position
local separator_index = selected_item:find(":", 1, true)
local inventory_name = selected_item:sub(1, separator_index - 1)
local stack_index = tonumber(selected_item:sub(separator_index + 1))
-- Get the stack of the target player's inventory, if empty do nothing
local target_inventory = player:get_inventory()
local stack = target_inventory:get_stack(inventory_name, stack_index)
if stack:get_count() == 0 then
return
end
-- Find the first empty slot in the invhack user's inventory
local user_inventory = user:get_inventory()
local empty_slot = 0
local empty_found = false
for search_index = 1, 32, 1 do
empty_slot = search_index
if user_inventory:get_stack("main", search_index):get_count() == 0 then
empty_found = true
break
end
end
-- Warn them if their inventory is full
if not empty_found then
minetest.chat_send_player(user_name, "Error: Your inventory is full")
return
end
-- Move the item into the invhack user's inventory
user_inventory:set_stack("main", empty_slot, stack)
-- Remove the item from the target player's inventory
if grab == true then
target_inventory:set_stack(inventory_name, stack_index, nil)
end
-- Trigger formspec reload - see below
selected_inventory = inventory_name
end
-- Switch the formspec to the selected inventory
if selected_inventory then
show_formspec(user_name, selected_inventory)
end
end)
minetest.register_tool("invhack:tool", {
description = "Inventory hack tool",
range = 15,
--inventory_image = "hacktool_inv.png",
inventory_image = "[png:iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2N0I5QTQxQTBERDExMUU1OTFDQkRCRURBODFERDJGRSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo2N0I5QTQxQjBERDExMUU1OTFDQkRCRURBODFERDJGRSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjM1QjMyOUQxMEREMTExRTU5MUNCREJFREE4MUREMkZFIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjM1QjMyOUQyMEREMTExRTU5MUNCREJFREE4MUREMkZFIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+qhktZwAAAWlJREFUeNrEVwsOgyAMFeLl9MB6PGadTbC2paWyNSE6HLxHPw9IpZTJYyklccAxV5qcliwENNAomTwC3DNO9EAvsNcTefqz5ZGrt8yVPQO2bQOX3hr0hQjSHOAItCrlGNOdC3Nj0O9zAJkynhlCVPXAvu83ECAhgeF3jpgaIppU16BSvi/n80i08502zqTv1+8nFteJJCQQNCAmEagXUPU9cFQd4EoM3AkNw1OHqScnZi00MDnGDydtTc4lL5cfqhJCJVDgwF4Q2w05F3vBLy/4hWhZFrH0tJL0kM+adNK4YQJydS/FeF1XdUueLW6U8qElTuY4aVqgCQ001AJNmDQM04EEXY+rfWMbNoeAhqGONcnyd84DnCrif7ishkqBfnhK54RoEqaaRA2slVpLAV33AuoJix7UBDQPDDkV16QuEiVMQBImbvWehEyRu6ELSAhF9oJbt+XQxUQDD0tvjxDRurbcA6z2EWAAYmcOt9YFCGEAAAAASUVORK5CYII=",
groups = {not_in_creative_inventory=1},
on_use = function(itemstack, user, pointed_thing)
local user_name = user:get_player_name()
if not minetest.check_player_privs(user_name, {invhack=true}) then
minetest.chat_send_player(user_name, "Missing privilege: invhack")
return
end
if pointed_thing.type ~= "object" then return end
local player = pointed_thing.ref
if player:is_player() == false then return end
contexts[user_name] = {target=player}
show_formspec(user_name)
return itemstack
end,
})
minetest.register_chatcommand("invhack", {
description = "View player's inventory",
params = "<playername>",
privs = {invhack=true},
func = function(name, param)
local player = minetest.get_player_by_name(param)
local user_name = name
contexts[user_name] = {target=player}
if not player then return end
if not inventory_name then inventory_name = "main" end
minetest.show_formspec(
user_name,
"invhack.form",
hacktool.formspec(name, player, inventory_name)
)
end,
})