medical/hitloc.lua
2020-04-13 16:35:50 -05:00

165 lines
5.2 KiB
Lua

local limb_location = {}
--todo: make these change depending on what state the patient is in.
--[[ standing locations
limb_location.head = {x=0,y=1.6,z=0}
limb_location.torso = {x=0,y=1,z=0}
limb_location.rightarm = {x=.3,y=1,z=0}
limb_location.leftarm = {x=-.3,y=1,z=0}
limb_location.rightleg = {x=.1,y=.4,z=0}
limb_location.leftleg = {x=-.1,y=.4,z=0}
--]]
--[[ sitting locations
limb_location.head = {x=0,y=.9,z=0}
limb_location.torso = {x=0,y=.4,z=0}
limb_location.rightarm = {x=.3,y=.4,z=0}
limb_location.leftarm = {x=-.3,y=.4,z=0}
limb_location.rightleg = {x=.1,y=.1,z=.35}
limb_location.leftleg = {x=-.1,y=.1,z=.35}
--]]
-- laying locations
limb_location.head = {x=0,y=.1,z=-.65}
limb_location.torso = {x=0,y=.1,z=-.2}
limb_location.rightarm = {x=.4,y=.1,z=-.125}
limb_location.leftarm = {x=-.4,y=.1,z=-.125}
limb_location.rightleg = {x=.2,y=.1,z=.5}
limb_location.leftleg = {x=-.2,y=.1,z=.5}
local DEBUG_WAYPOINT = true
local DEBUG_CHAT = true
local function rotateVector(x, y, a)
local c = math.cos(a)
local s = math.sin(a)
return c*x - s*y, s*x + c*y
end
function medical.gethitloc(player, hitter, tool_capabilities, dir)
if not player or not hitter then return end
local playerpos = player:get_pos()
local hitpos
local hitterpos = hitter:get_pos()
local adj_hitterpos = hitterpos
local isPlayer = hitter:is_player()
if isPlayer then
adj_hitterpos.y = adj_hitterpos.y + 1.45 -- eye offset
local offset, _ = hitter:get_eye_offset()
local hitteryaw = hitter:get_look_horizontal()
local x, z = rotateVector(offset.x, offset.z, hitteryaw)
offset = vector.multiply({x=x, y=offset.y, z=z}, .1)
adj_hitterpos = vector.add(adj_hitterpos, offset)
else
local properties = hitter:get_properties()
local offset = properties.eye_height or math.abs(properties.collisionbox[2] - properties.collisionbox[4])
adj_hitterpos.y = adj_hitterpos.y + offset/2
end
if tool_capabilities and dir and tool_capabilities.groupcaps.medical_dir ~= nil then
hitpos = vector.add(adj_hitterpos, vector.multiply(dir, vector.distance(playerpos, hitterpos)))
else
local pointdir = hitter:get_look_dir() or {}
if not pointdir or pointdir == nil or not isPlayer then
local yaw = hitter:getyaw()
local pitch = 0
pointdir.x = -1*math.cos(yaw)*math.cos(pitch)
pointdir.z = -1*math.sin(yaw)*math.cos(pitch)
pointdir.y = math.sin(pitch)
end
hitpos = vector.add(adj_hitterpos, vector.multiply(pointdir, vector.distance(playerpos, hitterpos)))
end
if minetest.raycast then
local ray = minetest.raycast(adj_hitterpos, hitpos) -- it checks the players exact front before anything else because the default hit dir is weird, this may cause inaccuracies if a weapon with spread gives a look vector as a dir and the ray that goes stright ahead still hits the player
local pointed = ray:next()
if pointed and pointed.ref and pointed.ref == hitter then
pointed = ray:next()
end
if pointed and pointed.ref == player then
hitpos = pointed.intersection_point
end
end
local playeryaw
if player:is_player() then
playeryaw = player:get_look_horizontal()
else
playeryaw = player:get_yaw()
end
local loc = vector.subtract(hitpos, playerpos)
local x, z = rotateVector(loc.x, loc.z, -playeryaw)
local local_hitpos = {x=x,y=loc.y,z=z}
if DEBUG_WAYPOINT then
local marker = hitter:hud_add({
hud_elem_type = "waypoint",
name = "hit",
number = 0xFF0000,
world_pos = hitpos
})
minetest.after(10, function() hitter:hud_remove(marker) end, hitter, marker)
end
return hitpos, local_hitpos
end
function medical.getclosest(table, local_hitpos)
local distance
local closest
for name, loc in pairs (table) do
if not distance then
distance = vector.distance(loc, local_hitpos)
closest = name
else
if vector.distance(loc, local_hitpos) < distance then
distance = vector.distance(loc, local_hitpos)
closest = name
end
end
end
return distance, closest
end
function medical.getlimb(player, hitter, tool_capabilities, dir, hitloc)
local hitpos
if hitloc then
hitpos = hitloc
else
hitpos = medical.gethitloc(player, hitter, tool_capabilities, dir)
if not hitpos then return end
end
local hitlimb
local hitdistance
local playeryaw
local playerpos = player:get_pos()
if player:is_player() then
playeryaw = player:get_look_horizontal()
else
playeryaw = player:get_yaw()
end
for id, pos in pairs(limb_location) do
local x, z = rotateVector(pos.x, pos.z, playeryaw)
local rot_pos = {x=x,y=pos.y,z=z}
local adj_pos = vector.add(playerpos, rot_pos)
local dist = vector.distance(adj_pos, hitpos)
if hitdistance == nil or dist < hitdistance then
hitdistance = dist
hitlimb = id
end
if DEBUG_WAYPOINT then
local mrker = hitter:hud_add({
hud_elem_type = "waypoint",
name = id,
number = 0xFF0000,
world_pos = adj_pos
})
minetest.after(5, function() hitter:hud_remove(mrker) end, hitter, mrker)
end
end
if DEBUG_CHAT then
minetest.chat_send_all(dump(hitlimb))
end
return hitlimb
end
minetest.register_on_player_hpchange(function(player, hp_change, reason)
if reason.type then minetest.chat_send_all(reason.type) end
return hp_change
end, true)
minetest.register_on_punchplayer(function(player, hitter, time_from_last_punch, tool_capabilities, dir, damage)
medical.getlimb(player, hitter, tool_capabilities, dir)
end)