EinsDreiDreiSieben/mods/i_have_hands/init.lua
2025-05-18 04:02:23 +02:00

552 lines
19 KiB
Lua

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)