EinsDreiDreiSieben/mods/futil/minetest/object.lua

200 lines
5.5 KiB
Lua

local v_new = vector.new
-- if object is attached, get the velocity of the object it is attached to
function futil.get_velocity(object)
local parent = object:get_attach()
while parent do
object = parent
parent = object:get_attach()
end
return object:get_velocity()
end
function futil.get_horizontal_speed(object)
local velocity = futil.get_velocity(object)
velocity.y = 0
return vector.length(velocity)
end
local function insert_connected(boxes, something)
if futil.is_box(something) then
table.insert(boxes, something)
elseif futil.is_boxes(something) then
table.insert_all(boxes, something)
end
end
local function get_boxes(cb)
if not cb then
return { { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 } }
end
if cb.type == "regular" then
return { { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 } }
elseif cb.type == "fixed" then
if futil.is_box(cb.fixed) then
return { cb.fixed }
elseif futil.is_boxes(cb.fixed) then
return cb.fixed
else
return { { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 } }
end
elseif cb.type == "leveled" then
-- TODO: have to check param2
if futil.is_box(cb.fixed) then
return { cb.fixed }
elseif futil.is_boxes(cb.fixed) then
return cb.fixed
else
return { { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 } }
end
elseif cb.type == "wallmounted" then
-- TODO: have to check param2? or?
local boxes = {}
if futil.is_box(cb.wall_top) then
table.insert(boxes, cb.wall_top)
end
if futil.is_box(cb.wall_bottom) then
table.insert(boxes, cb.wall_bottom)
end
if futil.is_box(cb.wall_side) then
table.insert(boxes, cb.wall_side)
end
if #boxes > 0 then
return boxes
else
return { { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 } }
end
elseif cb.type == "connected" then
-- TODO: very very complicated to check, just fudge and add everything
local boxes = {}
insert_connected(boxes, cb.fixed)
insert_connected(boxes, cb.connect_top)
insert_connected(boxes, cb.connect_bottom)
insert_connected(boxes, cb.connect_front)
insert_connected(boxes, cb.connect_left)
insert_connected(boxes, cb.connect_back)
insert_connected(boxes, cb.connect_right)
insert_connected(boxes, cb.disconnected_top)
insert_connected(boxes, cb.disconnected_bottom)
insert_connected(boxes, cb.disconnected_front)
insert_connected(boxes, cb.disconnected_left)
insert_connected(boxes, cb.disconnected_back)
insert_connected(boxes, cb.disconnected_right)
insert_connected(boxes, cb.disconnected)
insert_connected(boxes, cb.disconnected_sides)
if #boxes > 0 then
return boxes
else
return { { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 } }
end
end
return { { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 } }
end
local function get_collision_boxes(node)
local node_def = minetest.registered_nodes[node.name]
if not node_def then
-- unknown nodes are regular solid nodes
return { { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 } }
end
if not node_def.walkable then
return {}
end
local boxes
if node_def.collision_box then
boxes = get_boxes(node_def.collision_box)
elseif node_def.drawtype == "nodebox" then
boxes = get_boxes(node_def.node_box)
else
boxes = { { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 } }
end
--[[
if node_def.paramtype2 == "facedir" then
-- TODO: re-orient boxes
end
]]
return boxes
end
local function is_pos_on_ground(feet_pos, player_box)
local node = minetest.get_node(feet_pos)
local node_boxes = get_collision_boxes(node)
for _, node_box in ipairs(node_boxes) do
local actual_node_box = futil.box_offset(node_box, feet_pos)
if futil.boxes_intersect(actual_node_box, player_box) then
return true
end
end
return false
end
function futil.is_on_ground(player)
local p_pos = player:get_pos()
local cb = player:get_properties().collisionbox
-- collect the positions of the nodes below the player's feet
local feet_poss = {
v_new(math.round(p_pos.x + cb[1]), math.ceil(p_pos.y + cb[2] - 0.5), math.round(p_pos.z + cb[3])),
v_new(math.round(p_pos.x + cb[1]), math.ceil(p_pos.y + cb[2] - 0.5), math.round(p_pos.z + cb[6])),
v_new(math.round(p_pos.x + cb[4]), math.ceil(p_pos.y + cb[2] - 0.5), math.round(p_pos.z + cb[3])),
v_new(math.round(p_pos.x + cb[4]), math.ceil(p_pos.y + cb[2] - 0.5), math.round(p_pos.z + cb[6])),
}
for _, feet_pos in ipairs(feet_poss) do
if is_pos_on_ground(feet_pos, futil.box_offset(cb, p_pos)) then
return true
end
end
return false
end
function futil.get_object_center(object)
local pos = object:get_pos()
if not pos then
return
end
local cb = object:get_properties().collisionbox
return v_new(pos.x + (cb[1] + cb[4]) / 2, pos.y + (cb[2] + cb[5]) / 2, pos.z + (cb[3] + cb[6]) / 2)
end
function futil.is_player(obj)
return minetest.is_player(obj) and not obj.is_fake_player
end
function futil.is_valid_object(obj)
return obj and type(obj.get_pos) == "function" and vector.check(obj:get_pos())
end
-- this is meant to be able to get the HP of any object, including "immortal" ones whose health is managed themselves
-- it is *NOT* complete - i've got no idea where every mob API stores its hp.
-- "health" is mobs_redo (which is actually redundant with `:get_hp()` because they're not actually immortal.
-- "hp" is mobkit (and petz, which comes with its own fork of mobkit), and also creatura.
function futil.get_hp(obj)
if not futil.is_valid_object(obj) then
-- not an object or dead
return 0
end
local ent = obj:get_luaentity()
if ent and (type(ent.hp) == "number" or type(ent.health) == "number") then
return ent.hp or ent.health
end
local armor_groups = obj:get_armor_groups()
if (armor_groups["immortal"] or 0) == 0 then
return obj:get_hp()
end
return math.huge -- presumably actually immortal
end