Noch mehr mods

This commit is contained in:
N-Nachtigal 2025-05-18 04:02:23 +02:00
parent a063db5d3b
commit cf017b2ca1
527 changed files with 21113 additions and 181 deletions

21
mods/i_have_hands/LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 KingTheGuy
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,68 @@
![sample-gif](./repo-assets/crunchy.gif)
---
Carry a storage block/node without breaking it.
you can carry a chest, a barrel.. just about anything that has an inventory.
**how?**
_Hold `sneak` then `right_click(place button)` with an `empty_hand` to pickup/place down the storage containers_
[view the roadmap](/ROADMAP.md)
---
_1.0.9 CHANGELOG:_
bugfix:
- crash when dropping a chest with pipeworks installed
_1.0.8 CHANGELOG:_
bugfix:
- VoxeLibre players could not open door with an empty hand
_1.0.7 CHANGELOG:_
- \*\*add support for _mineclonia_
- add a short delay to the hud popup
- don't show the hud when the player is already holding a "chest/inventory"
_1.0.6 CHANGELOG:_
- updated gif
- hud over
- bug fixes
_1.0.5 CHANGELOG:_
- added support for pipeworks [link](https://content.minetest.net/packages/mt-mods/pipeworks/)
- on storage containers pickup/drop tubes will update
_1.0.4 CHANGELOG:_
- held storage containers can now be placed in water
- voxelibre chests once again render in hand
_1.0.3 CHANGELOG:_
- added support for the drawers mod (Storage Drawers) [link](https://content.minetest.net/packages/LNJ/drawers/)
- faster animation
- removed usage of keyword `goto`
_1.0.2 CHANGELOG:_
- first person visual indicator
- block animation for placing down.
- added mcl double chest support
- can now view mcl chests in \*hand (not happy with how ive had to implement this)
- mcl disabled picking up shulkers
- bug fixes
_1.0.1 CHANGELOG:_
- should take into account protected areas/blocks

552
mods/i_have_hands/init.lua Normal file
View file

@ -0,0 +1,552 @@
dofile(minetest.get_modpath("i_have_hands") .. "/utils.lua")
--moving a hot furnace with just your hands.. i don't this so buddy
local RayDistance = 4; --this should be changed to the players reach
--invs to block
local blacklist = { "furnace", "shulker" } --if the name contains any of
local data_storage = core.get_mod_storage()
---@class Animate
---@field player table The damn player
---@field rotation integer Not sure if this is vector
---@field object table Is this a table?
---@field frame integer Not sure if frame is the correct term here
---@field item_name string This is probabliy the only thing that is correct
local to_animate = {}
---@param this_string string the string
---@param split string sub to split at
function Split(this_string, split)
local new_word = {}
local index = string.find(this_string, split)
if index == nil then
return nil
end
local split_index = index
local split_start = ""
for x = 0, split_index - 1, 1 do
split_start = split_start .. string.sub(this_string, x, x)
end
new_word[1] = split_start
local split_end = ""
for x = split_index + #split, #this_string, 1 do
split_end = split_end .. string.sub(this_string, x, x)
end
new_word[2] = split_end
return new_word
end
local function placeDown(placer, rot, obj, above, frame, held_item_name)
table.insert(to_animate,
{ player = placer, rot = rot, obj = obj, pos = above, frame = frame, item = held_item_name })
end
local function quantize_direction(yaw)
local angle = math.deg(yaw) % 360 -- Convert yaw to degrees and get its modulo 360
if angle < 45 or angle >= 315 then
return math.rad(0) -- Facing North
elseif angle >= 45 and angle < 135 then
return math.rad(90) -- Facing East
elseif angle >= 135 and angle < 225 then
return math.rad(180) -- Facing South
else
return math.rad(270) -- Facing West
end
end
--object, pos, frame
--not in use atm
local function animatePlace()
for i, v in pairs(to_animate) do
if v.frame == 0 then
v.obj:set_detach()
v.obj:set_yaw(v.rot)
local obj_rot = v.obj:get_rotation()
v.obj:set_rotation({ x = math.rad(-20), y = obj_rot.y, z = obj_rot.z })
v.obj:set_properties({ visual_size = { x = 0.5, y = 0.5, z = 0.5 } })
v.obj:set_pos(v.pos)
v.obj:set_properties({ pointable = true })
core.sound_play({ name = "i_have_hands_pickup_node" }, { pos = v.pos, pitch = 0.7 }, true)
end
if v.frame == 1 then
local obj_rot = v.obj:get_rotation()
v.obj:set_rotation({ x = math.rad(0), y = obj_rot.y, z = obj_rot.z })
v.obj:set_properties({ visual_size = { x = 0.6, y = 0.6, z = 0.6 } })
end
if v.frame == 2 then
v.obj:set_properties({ visual_size = { x = 0.65, y = 0.65, z = 0.65 } })
end
if v.frame == 1 then
local found_meta = data_storage:get_string(v.obj:get_luaentity().initial_pos)
data_storage:set_string(v.obj:get_luaentity().initial_pos, "") --clear it
core.set_node(v.pos, { name = v.item, param2 = core.dir_to_fourdir(core.yaw_to_dir(v.rot)) })
core.sound_play({ name = "i_have_hands_place_down_node" }, { pos = v.pos }, true)
local meta = core.get_meta(v.pos)
local node_containers = {}
for i, v in pairs(core.deserialize(found_meta)["data"]) do
local found_container = {}
for container, container_items in pairs(v) do
local found_inv = {}
if type(container_items) == "string" then
found_container[container] = container_items
else
for slot, item in pairs(container_items) do
found_inv[slot] = item
end
found_container[container] = found_inv
end
end
node_containers[i] = found_container
end
meta:from_table(node_containers)
--NOTE(COMPAT): this adds support for the storage_drawers mod
if core.get_modpath("drawers") and drawers then
drawers.spawn_visuals(v.pos)
end
--NOTE(COMPAT): pipeworks update pipe, on place down
if core.get_modpath("pipeworks") and pipeworks then
pipeworks.after_place(v.pos)
end
end
v.frame = v.frame + 1
if v.frame >= 6 then
v.obj:remove()
v.obj = nil
table.remove(to_animate, i)
end
end
end
---@param pos table
---@param user table
---@return boolean
local function checkProtection(pos, user)
local protected = core.is_protected(pos, user:get_player_name())
local owner = core.get_meta(pos):get_string("owner")
local player_name = user:get_player_name()
if owner ~= "" then
if owner ~= player_name then
core.chat_send_player(player_name, core.colorize("pink", "You are not the owner."))
return true
end
end
if protected then
core.chat_send_player(player_name, core.colorize("pink", "This is protected"))
return true
end
return false
end
local function isInventory(meta)
local count = 0
for _ in pairs(meta:to_table()["inventory"]) do count = count + 1 end
if count < 1 then --inve have a value of 1 or greater
return false
end
return true
end
local function isBlacklisted(pos)
for _, v in ipairs(blacklist) do
if utils.StringContains(core.get_node(pos).name, v) then
return true
end
end
return false
end
local function find_empty_position(pos, radius)
local x, y, z = pos.x, pos.y, pos.z
local found = false
local empty_pos = nil
for r = 0, radius do
for a = 0, 360, 10 do
local dx = math.floor(r * math.cos(math.rad(a)))
local dz = math.floor(r * math.sin(math.rad(a)))
local nx, nz = x + dx, z + dz
local ny = y
while ny < 100 and not found do
local node = minetest.get_node({ x = nx, y = ny, z = nz })
if node.name == "air" then
empty_pos = { x = nx, y = ny, z = nz }
found = true
end
ny = ny + 1
end
end
end
return empty_pos
end
local handdef = core.registered_items[""]
local on_place = handdef and handdef.on_place
local function hands(itemstack, placer, pointed_thing)
local contains = false
if placer:get_player_control()["sneak"] == true then
-- core.debug("what is this?",core.get_node(pointed_thing.under).name)
-- core.debug(string.format("location: %s", dump(core.get_modpath("drawers"))))
-- core.debug(core.colorize("yellow", "howdy mate, ive got the shits"))
if #placer:get_children() > 0 then --this is getting all connect objects
for index, obj in pairs(placer:get_children()) do
-- core.debug(dump(obj:get_luaentity().name))
-- core.debug("got something: "..obj.name)
-- end
-- for index, value in pairs(placer:get_children()) do
local above = pointed_thing.above
-- core.debug("node: "..core.get_node(above).name)
if checkProtection(above, placer) == false then
if obj:get_luaentity().name == "i_have_hands:held" then
contains = true
-- core.debug("ok: "..type(held).."-"..held.."-")
local try_inside = core.registered_nodes[core.get_node(pointed_thing.under).name]
-- core.debug("buildabled? ",try_inside.buildable_to)
if core.get_node(above).name ~= "air" then
-- if core.get_node(above).name == "water" then
if utils.StringContains(core.get_node(above).name, "water") then
--do nothing
else
return itemstack
end
end
if #core.get_objects_inside_radius(above, 0.5) > 0 then
return itemstack
end
if try_inside.buildable_to == true then
above = pointed_thing.under
end
local held_item_name = core.registered_nodes[obj:get_properties().wield_item].name
-- local player_p = core.dir_to_fourdir(placer:get_look_dir())
-- obj:set_pos(above)
-- animatePlace(obj,above)
-- table.insert(to_animate,
-- { player = placer, rot = rot, obj = obj, pos = above, frame = 0, item = held_item_name })
local rot = quantize_direction(placer:get_look_horizontal())
placeDown(placer, rot, obj, above, 0, held_item_name)
end
end
end
end
if contains == false then
local is_blacklisted = false
if isBlacklisted(pointed_thing.under) then
is_blacklisted = true
end
if is_blacklisted == false then
if checkProtection(pointed_thing.under, placer) then
return itemstack
end
local meta = core.get_meta(pointed_thing.under)
if isInventory(meta) == false then
return itemstack
end
local obj = core.add_entity(placer:get_pos(), "i_have_hands:held")
obj:set_attach(placer, "", { x = 0, y = 9, z = 3.2 }, { x = 0, y = math.rad(90), z = 0 }, true)
--NOTE: attaching to the head just does not look very good, so lets not do that.
-- obj:set_attach(placer, "Head", { x = 0, y = -2, z = -3.2 }, { x = 0, y = math.rad(90), z = 0 }, true)
obj:set_properties({
wield_item = core.registered_nodes[core.get_node(pointed_thing.under).name]
.name
})
obj:get_luaentity().initial_pos = vector.to_string(obj:get_pos())
--NOTE(COMPAT): this takes care of voxelibre chests
if utils.StringContains(core.registered_nodes[core.get_node(pointed_thing.under).name].name, "mcl_chests") then
obj:set_properties({ wield_item = "mcl_chests:chest" })
end
-- core.debug(core.colorize("yellow",dump(core.registered_nodes[core.get_node(pointed_thing.under).name])))
-- core.debug(core.colorize("blue", "all: \n" .. dump(meta:to_table())))
local node_containers = {}
for i, v in pairs(meta:to_table()) do
local found_container = {}
for container, container_items in pairs(v) do
local found_inv = {}
if type(container_items) == "table" then
for slot, item in pairs(container_items) do
table.insert(found_inv, slot, item:to_string())
end
found_container[container] = found_inv
else
found_container[container] = container_items
end
end
node_containers[i] = found_container
end
local full_data = { node = core.get_node(pointed_thing.under), data = node_containers }
-- core.debug("full_data: ".. dump(full_data.data))
local pos = vector.to_string(obj:get_pos())
data_storage:set_string(pos, core.serialize(full_data))
obj:get_luaentity().initial_pos = pos
-- placer:get_meta():set_string("obj_obj",core.write_json(obj))
core.remove_node(pointed_thing.under)
core.sound_play({ name = "i_have_hands_pickup_node" }, { pos = pointed_thing.under }, true)
--NOTE(COMPAT): pipeworks update pipe, on pickup
if core.get_modpath("pipeworks") and pipeworks then
pipeworks.after_place(pointed_thing.under)
end
-- core.sound_play({ name = "i_have_hands_pickup" }, { pos = pointed_thing.under,gain = 0.1}, true)
end
end
end
--you know, return itemstack
end
-- local original_on_place = minetest.registered_items[""].on_place
core.override_item("", {
on_place = function(itemstack, placer, pointed_thing)
itemstack = on_place(itemstack, placer, pointed_thing)
hands(itemstack, placer, pointed_thing)
-- Call the original on_place function if it exists
-- if original_on_place then
-- return original_on_place(itemstack, placer, pointed_thing)
-- end
return itemstack
end,
-- on_secondary_use = function(itemstack, placer, pointed_thing)
-- hands(itemstack, placer, pointed_thing)
-- end
})
--check if the player is holding an inventory
local function isHolding(player)
if #player:get_children() > 0 then --this is getting all connect objects
for index, obj in pairs(player:get_children()) do
if obj:get_luaentity().name == "i_have_hands:held" then
-- core.debug("this dude is holding")
return true
end
-- core.debug("nope not holding")
return false
end
end
return false
end
core.register_entity("i_have_hands:held", {
selectionbox = { -0.0, -0.0, -0.0, 0.0, 0.0, 0.0, rotate = false },
pointable = false,
physical = false,
collide_with_objects = false,
visual = "item",
wield_item = "",
visual_size = { x = 0.35, y = 0.35, z = 0.35 },
_initial_pos = "",
on_step = function(self, dtime, moveresult)
-- core.debug(core.colorize("cyan", "dropping: \n" .. dump(data_storage:get_keys())))
if self.object:get_attach() == nil then
local contains = false
for i, v in pairs(to_animate) do
if v.obj == self.object then
-- core.debug("should not delete this yet")
contains = true
end
end
if contains == false then
local pos = self.object:get_luaentity().initial_pos
for i, v in pairs(data_storage:get_keys()) do
if v == pos then
core.set_node(vector.from_string(pos), core.deserialize(data_storage:get_string(v))["node"])
local meta = core.get_meta(vector.from_string(pos))
meta:from_table(utils.DeserializeMetaData(core.deserialize(data_storage:get_string(v))["data"]))
data_storage:set_string(v, "")
end
end
self.object:remove()
end
end
--updute pos and data
if self.object:get_luaentity() then
if self.object:get_luaentity().initial_pos ~= nil then
local pos = self.object:get_luaentity().initial_pos
local data = data_storage:get_string(pos)
data_storage:set_string(pos)
self.object:get_luaentity().initial_pos = vector.to_string(self.object:get_pos())
data_storage:set_string(vector.to_string(self.object:get_pos()), data)
end
end
end,
})
local player_hud_id = {}
local function getPlayerFromPlayerHuds(player_name)
for _, ph in ipairs(player_hud_id) do
if ph.player_name == player_name then
return ph
end
end
return nil
end
local function getPlayerHud(player_name)
-- core.debug("player_huds are " .. #player_hud_id .. " in length.")
for _, ph in ipairs(player_hud_id) do
if ph.player_name == player_name then
if ph.player_hud == nil then return nil end
return ph.player_hud
end
end
end
local function removePlayerHud(player)
local hud_id = getPlayerHud(player:get_player_name())
if hud_id ~= nil then
player:hud_remove(hud_id)
for index, ph in ipairs(player_hud_id) do
if ph.player_name == player:get_player_name() then
table.remove(player_hud_id, index)
end
end
end
end
--FIXME: only raycast if the player's "hand" is empty (no need to cast when the player cant event pick it up to start with)
local function raycast()
local player = core.get_connected_players()
if #player > 0 then
for _, p in ipairs(player) do
local eye_height = p:get_properties().eye_height
local player_look_dir = p:get_look_dir()
local pos = p:get_pos():add(player_look_dir)
local player_pos = { x = pos.x, y = pos.y + eye_height, z = pos.z }
local new_pos = p:get_look_dir():multiply(RayDistance):add(player_pos)
local raycast_result = core.raycast(player_pos, new_pos, false, false):next()
if isHolding(p) then
removePlayerHud(p)
return
end
local hud_id = nil; --FIXME this need to be added to list of all player HUDS
if raycast_result then
local pointed_node = core.get_node(raycast_result.under)
if isBlacklisted(raycast_result.under) then
removePlayerHud(p)
return
end
if p:get_wielded_item():get_name() ~= "" then
removePlayerHud(p)
return
end
if isInventory(core.get_meta(raycast_result.under)) then
hud_id = getPlayerHud(p:get_player_name())
local player_with_hud = getPlayerFromPlayerHuds(p:get_player_name())
if player_with_hud == nil then
local this_players_hud = { player_name = p:get_player_name(), player_hud = hud_id , hud_delay = 6, chest_location = raycast_result.under}
table.insert(player_hud_id, this_players_hud)
else
-- core.debug("what do we have here? "..player_with_hud.hud_delay)
if player_with_hud.hud_delay == 0 then
if hud_id == nil then
hud_id = p:hud_add({
hud_elem_type = "text",
position = { x = 0.5, y = 0.6 },
direction = 0,
name = "ihh",
scale = { x = 1, y = 1 },
-- text = "crouch & interact to lift this",
text = "Carry: crouch & interact",
number = "0xFFFFFF",
z_index = 0,
})
end
player_with_hud.player_hud = hud_id
end
if player_with_hud.chest_location ~= raycast_result.under then
removePlayerHud(p)
end
end
-- core.debug("so wtf is this then? " .. tostring(hud_id))
else
removePlayerHud(p)
end
-- core.debug(string.format("what is this?",core.registered_nodes[pointed_node].name))
else
removePlayerHud(p)
end
end
end
end
local function hotbarSlotNotEmpty()
local player = core.get_connected_players()
if #player > 0 then
for _, p in ipairs(player) do
if p:get_wielded_item():get_name() ~= "" then
if #p:get_children() > 0 then --this is getting all connect objects
for index, obj in pairs(p:get_children()) do
if obj:get_luaentity().name == "i_have_hands:held" then
local held_item_name = core.registered_nodes[obj:get_properties().wield_item].name
placeDown(p, 0, obj, find_empty_position(p:get_pos(), 10), 0, held_item_name)
end
end
end
end
end
end
end
local function tickHudDelay()
for _,h in pairs(player_hud_id) do
if h.hud_delay > 0 then
h.hud_delay = h.hud_delay - 1
end
end
end
local ran_once = false
local tick = 0
core.register_globalstep(function(dtime)
-- raycast()
tick = tick + 0.5
if tick > 2 then
animatePlace()
raycast()
hotbarSlotNotEmpty()
tickHudDelay()
tick = 0
end
if ran_once == false then
ran_once = true
for i, v in pairs(data_storage:get_keys()) do
local pos = vector.from_string(v)
core.set_node(pos, core.deserialize(data_storage:get_string(v))["node"])
local meta = core.get_meta(pos)
meta:from_table(utils.DeserializeMetaData(core.deserialize(data_storage:get_string(v))["data"]))
data_storage:set_string(v, "")
end
end
end)
core.register_on_dieplayer(function(ObjectRef, reason)
if #ObjectRef:get_children() > 0 then --this is getting all connect objects
for index, obj in pairs(ObjectRef:get_children()) do
if obj:get_luaentity().name == "i_have_hands:held" then
local held_item_name = core.registered_nodes[obj:get_properties().wield_item].name
placeDown(ObjectRef, 0, obj, find_empty_position(ObjectRef:get_pos(), 10), 0, held_item_name)
end
end
end
-- core.debug("what death? " .. ObjectRef:get_player_name())
end)

View file

@ -0,0 +1,6 @@
name = i_have_hands
description = Carry nodes/blocks with inventories without breaking them.
author = SURV
title = I Have Hands
optional_depends = drawers, pipeworks, mcl_meshhand
release = 30818

Binary file not shown.

View file

@ -0,0 +1,56 @@
utils = {}
function utils.Distance(x1, y1, z1, x2, y2, z2)
local dx = x2 - x1
local dy = y2 - y1
local dz = z2 - z1
return math.sqrt(dx * dx + dy * dy + dz * dz)
end
function utils.StringContains(str, find)
str = string.upper(str)
find = string.upper(find)
local i, _ = string.find(str, find)
return i
end
function utils.SerializeMetaData(data)
local node_containers = {}
for i, v in pairs(data:to_table()) do
local found_container = {}
for container, container_items in pairs(v) do
local found_inv = {}
if type(container_items) == "table" then
for slot, item in pairs(container_items) do
table.insert(found_inv, slot, item:to_string())
end
found_container[container] = found_inv
else
found_container[container] = container_items
end
end
node_containers[i] = found_container
end
return core.serialize(node_containers)
end
function utils.DeserializeMetaData(data)
local node_containers = {}
for i, v in pairs(data) do
local found_container = {}
for container, container_items in pairs(v) do
local found_inv = {}
if type(container_items) == "string" then
found_container[container] = container_items
else
for slot, item in pairs(container_items) do
found_inv[slot] = item
end
found_container[container] = found_inv
end
end
node_containers[i] = found_container
end
return node_containers
end