visualbar/init.lua
2019-10-05 18:17:35 -07:00

217 lines
6.3 KiB
Lua

--[[ Config ]]--
local disable_inventory = false
local R = 1 -- Size of vbar radius
local shape = "belt" -- Mode (belt or ring)
local visuals = {}
local hud = {}
local toggle = {}
local def_size = vector.multiply({x = 0.25, y = 0.25, z = 0.25}, R)
local def_sel = vector.multiply({x = 0.265, y = 0.265, z = 0.265}, R)
-- Swap hotbar items around
local function set_item(entity, player)
local inv = player:get_inventory()
local main = inv:get_list("main")
inv:set_stack("main", entity.idx, player:get_wielded_item())
player:set_wielded_item(main[entity.idx])
visuals[player:get_player_name()] = nil
end
vector.cross = vector.cross or function(a, b)
return {
x = a.y * b.z - a.z * b.y,
y = a.z * b.x - a.x * b.z,
z = a.x * b.y - a.y * b.x
}
end
local function dir_to_pitch(dir)
local pitch = minetest.dir_to_yaw({x = -dir.y, y = 0, z = math.sqrt(1 - dir.y * dir.y)})
return (pitch > math.pi) and (pitch - math.pi * 2) or pitch
end
local shapes = {
belt = function(player, slot)
local pos = player:get_pos()
pos.y = pos.y + 1
local angle = (-(360 / 8) * slot) + 45
local y = player:get_look_horizontal()
local rel = vector.multiply(minetest.yaw_to_dir(math.rad(angle) + y), R)
local target = vector.add(rel, pos)
local rot = vector.direction(target, pos)
return target, {x = 0, y = math.rad(angle) + y, z = 0}
end,
ring = function(player, slot) -- Thanks to Aaron Suen (Warr1024) for this
local camera_z = player:get_look_dir()
local camera_x = minetest.yaw_to_dir(player:get_look_horizontal() + math.pi / 2)
local camera_y = vector.cross(camera_x, camera_z)
local angle = ((360 / 8) * slot) + 45
local cv = {x = math.cos(math.rad(angle)) * R, y = math.sin(math.rad(angle)) * R, z = R * 2}
local wv = player:get_pos()
wv.y = wv.y + player:get_properties().eye_height
wv = vector.add(wv, vector.multiply(camera_x, cv.x))
wv = vector.add(wv, vector.multiply(camera_y, cv.y))
wv = vector.add(wv, vector.multiply(camera_z, cv.z))
return wv, {x = dir_to_pitch(camera_z), y = minetest.dir_to_yaw(camera_z), z = 0}
end,
}
minetest.register_entity("visualbar:item", {
initial_properties = {
visual = "wielditem",
wield_item = "",
visual_size = def_size,
selectionbox = {-0.25 * R, -0.25 * R, -0.25 * R, 0.25 * R, 0.25 * R, 0.25 * R},
collisionbox = {-0.25 * R, -0.25 * R, -0.25 * R, 0.25 * R, 0.25 * R, 0.25 * R},
},
parent = nil, -- You aren't really supposed to store references, but... sue me
timer = 0,
idx = 0,
on_activate = function(self)
self.object:set_armor_groups({immortal = 1})
end,
on_step = function(self, dt) -- Most of this is just fancy visuals
self.timer = self.timer + dt
if self.timer > 0.1 then
if not self.parent or not visuals[self.parent:get_player_name()] then
self.object:remove()
elseif self.object:get_properties().wield_item ~= "" then
local pointed = false
local player = self.parent
local pos = player:get_pos()
pos.y = pos.y + player:get_properties().eye_height
local dir = player:get_look_dir()
local ray = minetest.raycast(pos, vector.add(pos, vector.multiply(dir, 2)))
for pt in ray do
if pt.type == "object" and pt.ref == self.object then
pointed = true
break
end
end
if pointed then
self.object:set_properties({
visual_size = def_sel
})
else
self.object:set_properties({
visual_size = def_size
})
end
end
self.timer = 0
end
end,
on_punch = set_item,
on_rightclick = set_item,
})
local function display(player)
local name = player:get_player_name()
local inv = player:get_inventory()
local list = inv:get_list("main")
local pos = player:get_pos()
visuals[name] = table.copy(pos)
for i = 1, 8 do
local target, rot = shapes[shape](player, i)
local ent = minetest.add_entity(target, "visualbar:item")
local luen = ent:get_luaentity()
local item = list[i]:get_name()
if item ~= "" then
ent:set_properties({wield_item = item, infotext = minetest.registered_items[item].description or item})
ent:set_rotation(rot)
if list[i]:get_count() > 1 then
ent:set_nametag_attributes({
color = "white",
text = list[i]:get_count()
})
end
else
ent:set_properties({visual_size = {x = 0, y = 0, z = 0}})
end
luen.parent = player
luen.idx = i
end
end
minetest.register_on_joinplayer(function(player)
local name = player:get_player_name()
player:hud_set_flags({
hotbar = false
})
player:hud_set_hotbar_itemcount(1) -- Force player to hold selected item
-- Display stack size
hud[name] = player:hud_add({
hud_elem_type = "text",
position = {x = 0.5, y = 1},
text = "",
alignment = 0,
offset = {x = 0, y = -20},
number = 0xFFFFFF,
})
if disable_inventory then
minetest.after(0, function(name)
local player = minetest.get_player_by_name(name)
if player then
player:set_inventory_formspec("size[0,0] no_prepend[] bgcolor[#00000000]")
end
end, name)
player:get_inventory():set_size("main", 8)
else
player:get_inventory():set_size("main", 8 * 4)
minetest.register_on_player_inventory_action(function(player)
visuals[player:get_player_name()] = nil
end)
end
minetest.register_on_placenode(function(_, _, player)
visuals[player:get_player_name()] = nil
end)
minetest.register_on_dignode(function(_, _, player)
visuals[player:get_player_name()] = nil
end)
end)
minetest.register_on_leaveplayer(function(player)
visuals[player:get_player_name()] = nil
end)
local function precise_time()
return minetest.get_us_time() / 1000000
end
minetest.register_globalstep(function()
for _, player in pairs(minetest.get_connected_players()) do
local name = player:get_player_name()
local ctrl = player:get_player_control()
if visuals[name] then -- Remove visuals if aux1 is pressed or the player moves
if ctrl.aux1 or not vector.equals(vector.floor(player:get_pos()), vector.floor(visuals[name])) then
visuals[name] = nil
toggle[name] = 0
end
else
if ctrl.aux1 then -- Start the timer if aux1 is pressed
toggle[name] = toggle[name] or precise_time()
elseif toggle[name] then
if precise_time() - toggle[name] <= 0.12 then
display(player) -- Display if aux1 was released within 0.12 seconds
end
toggle[name] = nil
end
end
-- Update stack count HUD element
local count = player:get_wielded_item():get_count()
if count < 1 then
count = ""
end
player:hud_change(hud[name], "text", count)
end
end)