diff --git a/builtin/game/features.lua b/builtin/game/features.lua index 286c3f146..8bda39140 100644 --- a/builtin/game/features.lua +++ b/builtin/game/features.lua @@ -40,6 +40,7 @@ core.features = { lsystem_decoration_type = true, item_meta_range = true, node_interaction_actor = true, + hotbar_hud_element = true, } function core.has_feature(arg) diff --git a/builtin/game/hud.lua b/builtin/game/hud.lua index 4d531215d..d75eef813 100644 --- a/builtin/game/hud.lua +++ b/builtin/game/hud.lua @@ -259,3 +259,18 @@ register_builtin_hud_element("minimap", { core.get_player_information(player:get_player_name()).protocol_version >= 44 end, }) + +--- Hotbar + +register_builtin_hud_element("hotbar", { + elem_def = { + type = "hotbar", + position = {x = 0.5, y = 1}, + direction = 0, + alignment = {x = 0, y = -1}, + offset = {x = 0, y = -4}, -- Extra padding below. + }, + show_elem = function(player, flags) + return flags.hotbar + end, +}) diff --git a/doc/lua_api.md b/doc/lua_api.md index 3cf586faa..6f63a1a5d 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -1751,6 +1751,13 @@ Displays a horizontal bar made up of half-images with an optional background. * `item`: Position of item that is selected. * `direction`: Direction the list will be displayed in * `offset`: offset in pixels from position. +* `alignment`: The alignment of the inventory. Aligned at the top left corner if not specified. + +### `hotbar` + +* `direction`: Direction the list will be displayed in +* `offset`: offset in pixels from position. +* `alignment`: The alignment of the inventory. ### `waypoint` @@ -5435,6 +5442,9 @@ Utilities -- Allow passing an optional "actor" ObjectRef to the following functions: -- minetest.place_node, minetest.dig_node, minetest.punch_node (5.9.0) node_interaction_actor = true, + -- HUD elements of type hotbar exist and + -- the predefined hotbar is a Lua HUD elements. (5.9.0) + hotbar_hud_element = true, } ``` @@ -7045,7 +7055,7 @@ Misc. (regardless of online status) * `minetest.hud_replace_builtin(name, hud_definition)` * Replaces definition of a builtin hud element - * `name`: `"breath"`, `"health"` or `"minimap"` + * `name`: `"breath"`, `"health"`, `"minimap"` or `"hotbar"` * `hud_definition`: definition to replace builtin definition * `minetest.parse_relative_number(arg, relative_to)`: returns number or nil * Helper function for chat commands. @@ -10531,7 +10541,7 @@ Used by `ObjectRef:hud_add`. Returned by `ObjectRef:hud_get`. ```lua { type = "image", - -- Type of element, can be "image", "text", "statbar", "inventory", + -- Type of element, can be "image", "text", "statbar", "inventory", "hotbar", -- "waypoint", "image_waypoint", "compass" or "minimap" -- If undefined "text" will be used. diff --git a/games/devtest/mods/testhud/init.lua b/games/devtest/mods/testhud/init.lua index 5fdae3d38..8c0cd9c89 100644 --- a/games/devtest/mods/testhud/init.lua +++ b/games/devtest/mods/testhud/init.lua @@ -206,9 +206,75 @@ minetest.register_chatcommand("zoomfov", { end, }) +-- Hotbars + +local hud_hotbar_defs = { + { + type = "hotbar", + position = {x=0.2, y=0.5}, + direction = 0, + alignment = {x=1, y=-1}, + }, + { + type = "hotbar", + position = {x=0.2, y=0.5}, + direction = 2, + alignment = {x=1, y=1}, + }, + { + type = "hotbar", + position = {x=0.7, y=0.5}, + direction = 0, + offset = {x=140, y=20}, + alignment = {x=-1, y=-1}, + }, + { + type = "hotbar", + position = {x=0.7, y=0.5}, + direction = 2, + offset = {x=140, y=20}, + alignment = {x=-1, y=1}, + }, +} + + +local player_hud_hotbars= {} +minetest.register_chatcommand("hudhotbars", { + description = "Shows some test Lua HUD elements of type hotbar. " .. + "(add: Adds elements (default). remove: Removes elements)", + params = "[ add | remove ]", + func = function(name, params) + local player = minetest.get_player_by_name(name) + if not player then + return false, "No player." + end + + local id_table = player_hud_hotbars[name] + if not id_table then + id_table = {} + player_hud_hotbars[name] = id_table + end + + if params == "remove" then + for _, id in ipairs(id_table) do + player:hud_remove(id) + end + return true, "Hotbars removed." + end + + -- params == "add" or default + for _, def in ipairs(hud_hotbar_defs) do + table.insert(id_table, player:hud_add(def)) + end + return true, #hud_hotbar_defs .." Hotbars added." + end +}) + + minetest.register_on_leaveplayer(function(player) player_font_huds[player:get_player_name()] = nil player_waypoints[player:get_player_name()] = nil + player_hud_hotbars[player:get_player_name()] = nil end) minetest.register_chatcommand("hudprint", { @@ -230,3 +296,26 @@ minetest.register_chatcommand("hudprint", { return true, s end }) + +local hud_flags = {"hotbar", "healthbar", "crosshair", "wielditem", "breathbar", + "minimap", "minimap_radar", "basic_debug", "chat"} + +minetest.register_chatcommand("hudtoggleflag", { + description = "Toggles a hud flag.", + params = "[ ".. table.concat(hud_flags, " | ") .." ]", + func = function(name, params) + local player = minetest.get_player_by_name(name) + if not player then + return false, "No player." + end + + local flags = player:hud_get_flags() + if flags[params] == nil then + return false, "Unknown hud flag." + end + + flags[params] = not flags[params] + player:hud_set_flags(flags) + return true, "Flag \"".. params .."\" set to ".. tostring(flags[params]) .. "." + end +}) diff --git a/src/client/hud.cpp b/src/client/hud.cpp index 0c1b3e81f..0b6cdb74a 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -236,7 +236,7 @@ void Hud::drawItem(const ItemStack &item, const core::rect& rect, // NOTE: selectitem = 0 -> no selected; selectitem is 1-based // mainlist can be NULL, but draw the frame anyway. -void Hud::drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount, +void Hud::drawItems(v2s32 screen_pos, v2s32 screen_offset, s32 itemcount, v2f alignment, s32 inv_offset, InventoryList *mainlist, u16 selectitem, u16 direction, bool is_hotbar) { @@ -249,9 +249,11 @@ void Hud::drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount, width = tmp; } - // Position of upper left corner of bar - v2s32 pos = screen_offset * m_scale_factor; - pos += upperleftpos; + // Position: screen_pos + screen_offset + alignment + v2s32 pos(screen_offset.X * m_scale_factor, screen_offset.Y * m_scale_factor); + pos += screen_pos; + pos.X += (alignment.X - 1.0f) * (width * 0.5f); + pos.Y += (alignment.Y - 1.0f) * (height * 0.5f); // Store hotbar_image in member variable, used by drawItem() if (hotbar_image != player->hotbar_image) { @@ -349,12 +351,21 @@ void Hud::drawLuaElements(const v3s16 &camera_offset) std::vector elems; elems.reserve(player->maxHudId()); - // Add builtin minimap if the server doesn't send it. + // Add builtin elements if the server doesn't send them. + // Declared here such that they have the same lifetime as the elems vector HudElement minimap; - if (client->getProtoVersion() < 44 && (player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE)) { - minimap = {HUD_ELEM_MINIMAP, v2f(1, 0), "", v2f(), "", 0 , 0, 0, v2f(-1, 1), - v2f(-10, 10), v3f(), v2s32(256, 256), 0, "", 0}; - elems.push_back(&minimap); + HudElement hotbar; + if (client->getProtoVersion() < 44) { + if (player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE) { + minimap = {HUD_ELEM_MINIMAP, v2f(1, 0), "", v2f(), "", 0 , 0, 0, v2f(-1, 1), + v2f(-10, 10), v3f(), v2s32(256, 256), 0, "", 0}; + elems.push_back(&minimap); + } + if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) { + hotbar = {HUD_ELEM_HOTBAR, v2f(0.5, 1), "", v2f(), "", 0 , 0, 0, v2f(-0.5, -1), + v2f(0, -4), v3f(), v2s32(), 0, "", 0}; + elems.push_back(&hotbar); + } } for (size_t i = 0; i != player->maxHudId(); i++) { @@ -420,7 +431,7 @@ void Hud::drawLuaElements(const v3s16 &camera_offset) InventoryList *inv = inventory->getList(e->text); if (!inv) warningstream << "HUD: Unknown inventory list. name=" << e->text << std::endl; - drawItems(pos, v2s32(e->offset.X, e->offset.Y), e->number, 0, + drawItems(pos, v2s32(e->offset.X, e->offset.Y), e->number, e->align, 0, inv, e->item, e->dir, false); break; } case HUD_ELEM_WAYPOINT: { @@ -545,6 +556,9 @@ void Hud::drawLuaElements(const v3s16 &camera_offset) e->offset.Y * m_scale_factor); client->getMinimap()->drawMinimap(rect); break; } + case HUD_ELEM_HOTBAR: { + drawHotbar(pos, e->offset, e->dir, e->align); + break; } default: infostream << "Hud::drawLuaElements: ignoring drawform " << e->type << " due to unrecognized type" << std::endl; @@ -754,9 +768,7 @@ void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, } } } - - -void Hud::drawHotbar(u16 playeritem) +void Hud::drawHotbar(const v2s32 &pos, const v2f &offset, u16 dir, const v2f &align) { if (g_touchscreengui) g_touchscreengui->resetHotbarRects(); @@ -767,30 +779,30 @@ void Hud::drawHotbar(u16 playeritem) return; } + u16 playeritem = player->getWieldIndex(); + v2s32 screen_offset(offset.X, offset.Y); + v2s32 centerlowerpos(m_displaycenter.X, m_screensize.Y); s32 hotbar_itemcount = player->hud_hotbar_itemcount; s32 width = hotbar_itemcount * (m_hotbar_imagesize + m_padding * 2); - v2s32 pos = centerlowerpos - v2s32(width / 2, m_hotbar_imagesize + m_padding * 3); const v2u32 &window_size = RenderingEngine::getWindowSize(); if ((float) width / (float) window_size.X <= g_settings->getFloat("hud_hotbar_max_width")) { - if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) { - drawItems(pos, v2s32(0, 0), hotbar_itemcount, 0, mainlist, playeritem + 1, 0, true); - } + drawItems(pos, screen_offset, hotbar_itemcount, align, 0, + mainlist, playeritem + 1, dir, true); } else { - pos.X += width/4; + v2s32 firstpos = pos; + firstpos.X += width/4; - v2s32 secondpos = pos; - pos = pos - v2s32(0, m_hotbar_imagesize + m_padding); + v2s32 secondpos = firstpos; + firstpos = firstpos - v2s32(0, m_hotbar_imagesize + m_padding); - if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) { - drawItems(pos, v2s32(0, 0), hotbar_itemcount / 2, 0, - mainlist, playeritem + 1, 0, true); - drawItems(secondpos, v2s32(0, 0), hotbar_itemcount, - hotbar_itemcount / 2, mainlist, playeritem + 1, 0, true); - } + drawItems(firstpos, screen_offset, hotbar_itemcount / 2, align, 0, + mainlist, playeritem + 1, dir, true); + drawItems(secondpos, screen_offset, hotbar_itemcount, align, + hotbar_itemcount / 2, mainlist, playeritem + 1, dir, true); } } diff --git a/src/client/hud.h b/src/client/hud.h index 746a1b33e..6bccf0b13 100644 --- a/src/client/hud.h +++ b/src/client/hud.h @@ -64,7 +64,7 @@ public: void disableBlockBounds(); void drawBlockBounds(); - void drawHotbar(u16 playeritem); + void drawHotbar(const v2s32 &pos, const v2f &offset, u16 direction, const v2f &align); void resizeHotbar(); void drawCrosshair(); void drawSelectionMesh(); @@ -100,7 +100,7 @@ private: const std::string &texture, const std::string& bgtexture, s32 count, s32 maxcount, v2s32 offset, v2s32 size = v2s32()); - void drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount, + void drawItems(v2s32 screen_pos, v2s32 screen_offset, s32 itemcount, v2f alignment, s32 inv_offset, InventoryList *mainlist, u16 selectitem, u16 direction, bool is_hotbar); diff --git a/src/client/render/plain.cpp b/src/client/render/plain.cpp index 60a732415..0ca2476ee 100644 --- a/src/client/render/plain.cpp +++ b/src/client/render/plain.cpp @@ -61,7 +61,6 @@ void DrawHUD::run(PipelineContext &context) if (context.draw_crosshair) context.hud->drawCrosshair(); - context.hud->drawHotbar(context.client->getEnv().getLocalPlayer()->getWieldIndex()); context.hud->drawLuaElements(context.client->getCamera()->getOffset()); context.client->getCamera()->drawNametags(); } diff --git a/src/hud.cpp b/src/hud.cpp index 9b336cdef..e66eff1e5 100644 --- a/src/hud.cpp +++ b/src/hud.cpp @@ -30,6 +30,7 @@ const struct EnumString es_HudElementType[] = {HUD_ELEM_IMAGE_WAYPOINT, "image_waypoint"}, {HUD_ELEM_COMPASS, "compass"}, {HUD_ELEM_MINIMAP, "minimap"}, + {HUD_ELEM_HOTBAR, "hotbar"}, {0, NULL}, }; diff --git a/src/hud.h b/src/hud.h index 5540828d4..d32ab784e 100644 --- a/src/hud.h +++ b/src/hud.h @@ -67,7 +67,8 @@ enum HudElementType { HUD_ELEM_WAYPOINT = 4, HUD_ELEM_IMAGE_WAYPOINT = 5, HUD_ELEM_COMPASS = 6, - HUD_ELEM_MINIMAP = 7 + HUD_ELEM_MINIMAP = 7, + HUD_ELEM_HOTBAR = 8, }; enum HudElementStat : u8 { diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index add80b3b2..929824507 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -222,7 +222,7 @@ with this program; if not, write to the Free Software Foundation, Inc., PROTOCOL VERSION 44: AO_CMD_SET_BONE_POSITION extended Add TOCLIENT_MOVE_PLAYER_REL - Move default minimap from client-side C++ to server-side builtin Lua + Move default minimap and hotbar from client-side C++ to server-side builtin Lua [scheduled bump for 5.9.0] */ diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index de4f2fe02..6f138d080 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -2303,7 +2303,10 @@ void read_hud_element(lua_State *L, HudElement *elem) elem->dir = getintfield_default(L, 2, "dir", 0); lua_getfield(L, 2, "alignment"); - elem->align = lua_istable(L, -1) ? read_v2f(L, -1) : v2f(); + if (lua_istable(L, -1)) + elem->align = read_v2f(L, -1); + else + elem->align = elem->type == HUD_ELEM_INVENTORY ? v2f(1.0f, 1.0f) : v2f(); lua_pop(L, 1); lua_getfield(L, 2, "offset");