1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-09-15 18:57:08 +00:00

Replace hotbar_itemcount by hotbar_source

This commit is contained in:
cx384 2025-03-15 01:11:02 +01:00
parent 4b85062caf
commit 29d1dcd23b
23 changed files with 682 additions and 189 deletions

View file

@ -1840,6 +1840,10 @@ Displays a horizontal bar made up of half-images with an optional background.
* `direction`: Direction the list will be displayed in * `direction`: Direction the list will be displayed in
* `offset`: offset in pixels from position. * `offset`: offset in pixels from position.
* `alignment`: The alignment of the inventory. * `alignment`: The alignment of the inventory.
* `world_pos`: Inventory of the hotbar depending on `hotbar_source`
`x` the index of the `hotbar_source` table to be used, if 0 use all next to each other
`y` an additional length to clamp the size, if 0 use whole length as defined in `hotbar_source`
`z` an additional offset to adjust where the displayed inventory starts
### `waypoint` ### `waypoint`
@ -8270,7 +8274,7 @@ child will follow movement and rotation of that bone.
* `get_inventory()`: returns an `InvRef` for players, otherwise returns `nil` * `get_inventory()`: returns an `InvRef` for players, otherwise returns `nil`
* `get_wield_list()`: returns the name of the inventory list the wielded item * `get_wield_list()`: returns the name of the inventory list the wielded item
is in. is in.
* `get_wield_index()`: returns the wield list index of the wielded item (starting with 1) * `get_wield_index()`: returns the index of the wielded item in the wield list
* `get_wielded_item()`: returns a copy of the wielded item as an `ItemStack` * `get_wielded_item()`: returns a copy of the wielded item as an `ItemStack`
* `set_wielded_item(item)`: replaces the wielded item, returns `true` if * `set_wielded_item(item)`: replaces the wielded item, returns `true` if
successful. successful.
@ -8632,8 +8636,16 @@ child will follow movement and rotation of that bone.
* `hud_set_hotbar_itemcount(count)`: sets number of items in builtin hotbar * `hud_set_hotbar_itemcount(count)`: sets number of items in builtin hotbar
* `count`: number of items, must be between `1` and `32` * `count`: number of items, must be between `1` and `32`
* If `count` exceeds the `"main"` list size, the list size will be used instead. * If `count` exceeds the `"main"` list size, the list size will be used instead.
* `hud_get_hotbar_itemcount()`: returns number of visible items * equal `set_hotbar_source({{list = "main", length = count}})`
* This value is also clamped by the `"main"` list size. * `hud_get_hotbar_itemcount()`: returns number of selectable items
* `get_hotbar_source()` returns used `hotbar_source`
* `set_hotbar_source({{list = "main", length = 6, offset = 24}, {list = "bag1", length = 4}, ...})`
* Sets inventory lists for the player to use in hotbar(s) and to select the wield item from.
`list` is a player inventory list
`length` is the amount of inventory slots
`offset` adjusts starting inventory position, 0 if not specified
* Note: Do not use this together with mods that relay on a fixed wield list and list size.
All mods should use `get_wield_list()` and `get_wield_index()` to get the wield position.
* `hud_set_hotbar_image(texturename)` * `hud_set_hotbar_image(texturename)`
* sets background image for hotbar * sets background image for hotbar
* `hud_get_hotbar_image()`: returns texturename * `hud_get_hotbar_image()`: returns texturename

View file

@ -211,16 +211,17 @@ core.register_chatcommand("zoomfov", {
-- Hotbars -- Hotbars
local hud_hotbar_defs = { local hud_hotbar_defs = {
{
{ {
type = "hotbar", type = "hotbar",
position = {x=0.2, y=0.5}, position = {x=0.2, y=0.5},
direction = 0, direction = 1,
alignment = {x=1, y=-1}, alignment = {x=1, y=-1},
}, },
{ {
type = "hotbar", type = "hotbar",
position = {x=0.2, y=0.5}, position = {x=0.2, y=0.5},
direction = 2, direction = 3,
alignment = {x=1, y=1}, alignment = {x=1, y=1},
}, },
{ {
@ -237,15 +238,118 @@ local hud_hotbar_defs = {
offset = {x=140, y=20}, offset = {x=140, y=20},
alignment = {x=-1, y=1}, alignment = {x=-1, y=1},
}, },
},
{
hotbar_source = {
{list = "craft", length = 4, offset = 3},
{list = "main", length = 5, offset = 0},
},
hotbar_image = "default_stone.png^[opacity:150",
{
type = "hotbar",
position = {x=0.5, y=0.2},
world_pos = {x=1},
},
{
type = "hotbar",
position = {x=0.5, y=0.3},
world_pos = {x=1, y=6},
},
{
type = "hotbar",
position = {x=0.5, y=0.4},
world_pos = {x=1, z=5, y=3},
},
{
type = "hotbar",
position = {x=0.5, y=0.5},
world_pos = {x=2},
},
{
type = "hotbar",
position = {x=0.5, y=0.6},
world_pos = {x=2, z=1, y=3},
},
{
type = "hotbar",
position = {x=0.5, y=0.7},
world_pos = {x=2},
direction = 1,
},
{
type = "hotbar",
position = {x=0.5, y=0.8},
world_pos = {x=2, z=1, y=3},
direction = 1,
},
{
type = "hotbar",
position = {x=0.2, y=0.5},
world_pos = {x=2, z=1, y=3},
direction = 2,
},
{
type = "hotbar",
position = {x=0.8, y=0.5},
world_pos = {x=2, z=1, y=3},
direction = 3,
},
},
{
hotbar_source = {
{list = "craft", length = 4, offset = 3},
{list = "main", length = 5, offset = 0},
{list = "main", length = 2, offset = 4},
{list = "craft", length = 4, offset = 3},
},
hotbar_image = "default_stone.png^[opacity:150",
{
type = "hotbar",
position = {x=0.5, y=0.8},
direction = 1,
},
{
type = "hotbar",
position = {x=0.5, y=0.7},
world_pos = {z=1, y=10},
direction = 1,
},
{
type = "hotbar",
position = {x=0.5, y=0.6},
world_pos = {z=1, y=10},
},
{
type = "hotbar",
position = {x=0.5, y=0.5},
world_pos = {z=17, y=5},
},
{
type = "hotbar",
position = {x=0.5, y=0.4},
world_pos = {z=10, y=10},
},
{
type = "hotbar",
position = {x=0.2, y=0.5},
direction = 2,
},
{
type = "hotbar",
position = {x=0.8, y=0.5},
direction = 3,
},
}
} }
local player_hud_hotbars= {} local player_hud_hotbars= {}
core.register_chatcommand("hudhotbars", { core.register_chatcommand("hudhotbars", {
description = "Shows some test Lua HUD elements of type hotbar. " .. description = "Shows some test Lua HUD elements of type hotbar. " ..
"(add: Adds elements (default). remove: Removes elements)", "(Cycles between: none, aligned using all direction and offset," ..
params = "[ add | remove ]", "changed hotbar_source each only using a single inventory," ..
func = function(name, params) "using multiple inventories (world_pos.x = 0))",
func = function(name)
local player = core.get_player_by_name(name) local player = core.get_player_by_name(name)
if not player then if not player then
return false, "No player." return false, "No player."
@ -253,22 +357,37 @@ core.register_chatcommand("hudhotbars", {
local id_table = player_hud_hotbars[name] local id_table = player_hud_hotbars[name]
if not id_table then if not id_table then
id_table = {} id_table = {mode = 0}
player_hud_hotbars[name] = id_table player_hud_hotbars[name] = id_table
end end
if params == "remove" then id_table.mode = (id_table.mode + 1) % (#hud_hotbar_defs + 1)
-- Reset
for _, id in ipairs(id_table) do for _, id in ipairs(id_table) do
player:hud_remove(id) player:hud_remove(id)
end end
player:hud_set_hotbar_itemcount(8)
player:hud_set_hotbar_image("")
if id_table.mode == 0 then
return true, "Hotbars removed." return true, "Hotbars removed."
end end
-- params == "add" or default for _, def in ipairs(hud_hotbar_defs[id_table.mode]) do
for _, def in ipairs(hud_hotbar_defs) do
table.insert(id_table, player:hud_add(def)) table.insert(id_table, player:hud_add(def))
end end
return true, #hud_hotbar_defs .." Hotbars added."
if hud_hotbar_defs[id_table.mode].hotbar_source then
player:set_hotbar_source(hud_hotbar_defs[id_table.mode].hotbar_source)
end
if hud_hotbar_defs[id_table.mode].hotbar_image then
player:hud_set_hotbar_image(hud_hotbar_defs[id_table.mode].hotbar_image)
end
return true, #hud_hotbar_defs[id_table.mode] .." Hotbars added."
end end
}) })

View file

@ -183,9 +183,32 @@ end
unittests.register("test_player_add_pos", run_player_add_pos_tests, {player=true}) unittests.register("test_player_add_pos", run_player_add_pos_tests, {player=true})
-- --
-- Hotbar selection clamp -- Hotbar Source
-- --
local function run_player_hotbar_clamp_tests(player)
local function table_equals(t1, t2)
local keys = {}
local checked = {}
for k, v in pairs(t1) do
if type(v) == "table" then
if not (checked[v] or table_equals(v, t2[k])) then
return false
end
checked[v] = true
elseif t2[k] ~= v then
return false
end
keys[k] = true
end
for k, _ in pairs(t2) do
if not keys[k] then
return false
end
end
return true
end
local function run_player_hotbar_source_tests(player)
local inv = player:get_inventory() local inv = player:get_inventory()
local old_inv_size = inv:get_size("main") local old_inv_size = inv:get_size("main")
local old_inv_list = inv:get_list("main") -- Avoid accidentally removing item local old_inv_list = inv:get_list("main") -- Avoid accidentally removing item
@ -195,12 +218,22 @@ local function run_player_hotbar_clamp_tests(player)
player:hud_set_hotbar_itemcount(2) player:hud_set_hotbar_itemcount(2)
assert(player:hud_get_hotbar_itemcount() == 2) assert(player:hud_get_hotbar_itemcount() == 2)
assert(table_equals(player:get_hotbar_source(), {{list = "main", length = 2, offset = 0}}))
-- Test clamp
player:hud_set_hotbar_itemcount(6) player:hud_set_hotbar_itemcount(6)
assert(player:hud_get_hotbar_itemcount() == 5) assert(player:hud_get_hotbar_itemcount() == 5)
assert(table_equals(player:get_hotbar_source(), {{list = "main", length = 5, offset = 0}}))
local hotbar_source = {{list = "test", length = 4, offset = 2}, {list = "myinv", length = 16, offset = 99}}
print(dump(player:get_hotbar_source()))
print(player:set_hotbar_source(hotbar_source))
print(dump(player:get_hotbar_source()))
assert(table_equals(player:get_hotbar_source(), hotbar_source))
assert(player:hud_get_hotbar_itemcount() == 20)
inv:set_size("main", old_inv_size) inv:set_size("main", old_inv_size)
inv:set_list("main", old_inv_list) inv:set_list("main", old_inv_list)
player:hud_set_hotbar_itemcount(old_bar_size) player:hud_set_hotbar_itemcount(old_bar_size)
end end
unittests.register("test_player_hotbar_clamp", run_player_hotbar_clamp_tests, {player=true}) unittests.register("test_player_hotbar_source", run_player_hotbar_source_tests, {player=true})

View file

@ -1957,7 +1957,7 @@ void Game::processItemSelection(u16 *new_playeritem)
LocalPlayer *player = client->getEnv().getLocalPlayer(); LocalPlayer *player = client->getEnv().getLocalPlayer();
*new_playeritem = player->getWieldIndex(); *new_playeritem = player->getWieldIndex();
u16 max_item = player->getMaxHotbarItemcount(); u16 max_item = player->hotbar_source.getMaxLength();
if (max_item == 0) if (max_item == 0)
return; return;
max_item -= 1; max_item -= 1;
@ -2009,10 +2009,15 @@ void Game::dropSelectedItem(bool single_item)
IDropAction *a = new IDropAction(); IDropAction *a = new IDropAction();
a->count = single_item ? 1 : 0; a->count = single_item ? 1 : 0;
a->from_inv.setCurrentPlayer(); a->from_inv.setCurrentPlayer();
a->from_list = "main";
a->from_i = client->getEnv().getLocalPlayer()->getWieldIndex(); LocalPlayer* player = client->getEnv().getLocalPlayer();
u16 index;
if (player->hotbar_source.getInventoryFromWieldIndex(player->getWieldIndex(),
a->from_list, index)) {
a->from_i = index;
client->inventoryAction(a); client->inventoryAction(a);
} }
}
void Game::openConsole(float scale, const wchar_t *line) void Game::openConsole(float scale, const wchar_t *line)
{ {

View file

@ -229,14 +229,37 @@ void Hud::drawItem(const ItemStack &item, const core::rect<s32>& rect,
client, selected ? IT_ROT_SELECTED : IT_ROT_NONE); client, selected ? IT_ROT_SELECTED : IT_ROT_NONE);
} }
// NOTE: selectitem = 0 -> no selected; selectitem is 1-based void Hud::drawItems(const v2s32& pos, s32 inv_size, s32 inv_offset, InventoryList *mainlist,
// mainlist can be NULL, but draw the frame anyway. u16 selectitem, u16 direction, bool is_hotbar, u16 hotbar_touchcontrol_offset)
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)
{ {
s32 height = m_hotbar_imagesize + m_padding * 2; // Store hotbar_selected_image in member variable, used by drawItem()
s32 width = (itemcount - inv_offset) * (m_hotbar_imagesize + m_padding * 2); if (hotbar_selected_image != player->hotbar_selected_image) {
hotbar_selected_image = player->hotbar_selected_image;
use_hotbar_selected_image = !hotbar_selected_image.empty();
}
// Draw items
core::rect<s32> imgrect(0, 0, m_hotbar_imagesize, m_hotbar_imagesize);
const s32 list_max = std::min(inv_size, (s32) (mainlist ? mainlist->getSize() : 0 ));
for (s32 i = inv_offset; i < list_max; i++) {
v2s32 steppos = getInventoryPosOffset(direction, i - inv_offset, list_max - 1 - inv_offset);
core::rect<s32> item_rect = imgrect + pos + v2s32{m_padding, m_padding} + steppos;
drawItem(mainlist->getItem(i), item_rect, (i + 1) == selectitem);
if (is_hotbar && g_touchcontrols)
g_touchcontrols->registerHotbarRect(i + hotbar_touchcontrol_offset, item_rect);
}
}
// Returns the total width, height and pos to draw a HUD inventory
void Hud::getInventoryDimensions(v2s32 screen_pos, const v2s32& screen_offset, s32 inv_length,
v2f alignment, u16 direction, v2s32& pos, s32& width, s32& height)
{
height = m_hotbar_imagesize + m_padding * 2;
width = inv_length * (m_hotbar_imagesize + m_padding * 2);
if (direction == HUD_DIR_TOP_BOTTOM || direction == HUD_DIR_BOTTOM_TOP) { if (direction == HUD_DIR_TOP_BOTTOM || direction == HUD_DIR_BOTTOM_TOP) {
s32 tmp = height; s32 tmp = height;
@ -245,63 +268,147 @@ void Hud::drawItems(v2s32 screen_pos, v2s32 screen_offset, s32 itemcount, v2f al
} }
// Position: screen_pos + screen_offset + alignment // Position: screen_pos + screen_offset + alignment
v2s32 pos(screen_offset.X * m_scale_factor, screen_offset.Y * m_scale_factor); pos.X = screen_pos.X + screen_offset.X * m_scale_factor +
pos += screen_pos; (alignment.X - 1.0f) * (width * 0.5f);
pos.X += (alignment.X - 1.0f) * (width * 0.5f); pos.Y = screen_pos.Y + screen_offset.Y * m_scale_factor +
pos.Y += (alignment.Y - 1.0f) * (height * 0.5f); (alignment.Y - 1.0f) * (height * 0.5f);
// Store hotbar_image in member variable, used by drawItem()
if (hotbar_image != player->hotbar_image) {
hotbar_image = player->hotbar_image;
use_hotbar_image = !hotbar_image.empty();
} }
// Store hotbar_selected_image in member variable, used by drawItem() // Returns an inventory position offset depending on the direction
if (hotbar_selected_image != player->hotbar_selected_image) { // arguments are the number of items before and the remaining number of items
hotbar_selected_image = player->hotbar_selected_image; v2s32 Hud::getInventoryPosOffset(u16 direction, s32 before, s32 remainder)
use_hotbar_selected_image = !hotbar_selected_image.empty(); {
}
// draw customized item background
if (use_hotbar_image) {
core::rect<s32> imgrect2(-m_padding/2, -m_padding/2,
width+m_padding/2, height+m_padding/2);
core::rect<s32> rect2 = imgrect2 + pos;
video::ITexture *texture = tsrc->getTexture(hotbar_image);
core::dimension2di imgsize(texture->getOriginalSize());
draw2DImageFilterScaled(driver, texture, rect2,
core::rect<s32>(core::position2d<s32>(0,0), imgsize),
NULL, hbar_colors, true);
}
// Draw items
core::rect<s32> imgrect(0, 0, m_hotbar_imagesize, m_hotbar_imagesize);
const s32 list_max = std::min(itemcount, (s32) (mainlist ? mainlist->getSize() : 0 ));
for (s32 i = inv_offset; i < list_max; i++) {
s32 fullimglen = m_hotbar_imagesize + m_padding * 2; s32 fullimglen = m_hotbar_imagesize + m_padding * 2;
v2s32 steppos; v2s32 steppos;
switch (direction) { switch (direction) {
case HUD_DIR_RIGHT_LEFT: case HUD_DIR_RIGHT_LEFT:
steppos = v2s32(m_padding + (list_max - 1 - i - inv_offset) * fullimglen, m_padding); steppos = v2s32((remainder - before) * fullimglen, 0);
break; break;
case HUD_DIR_TOP_BOTTOM: case HUD_DIR_TOP_BOTTOM:
steppos = v2s32(m_padding, m_padding + (i - inv_offset) * fullimglen); steppos = v2s32(0, before * fullimglen);
break; break;
case HUD_DIR_BOTTOM_TOP: case HUD_DIR_BOTTOM_TOP:
steppos = v2s32(m_padding, m_padding + (list_max - 1 - i - inv_offset) * fullimglen); steppos = v2s32(0, (remainder - before) * fullimglen);
break; break;
default: default:
steppos = v2s32(m_padding + (i - inv_offset) * fullimglen, m_padding); steppos = v2s32(before * fullimglen, 0);
break; break;
} }
core::rect<s32> item_rect = imgrect + pos + steppos; return steppos;
}
drawItem(mainlist->getItem(i), item_rect, (i + 1) == selectitem); void Hud::drawInventoryBackground(const v2s32& pos, s32 width, s32 height)
{
// Store hotbar_image in member variable
if (hotbar_image != player->hotbar_image) {
hotbar_image = player->hotbar_image;
use_hotbar_image = !hotbar_image.empty();
}
if (is_hotbar && g_touchcontrols) if (!use_hotbar_image) {
g_touchcontrols->registerHotbarRect(i, item_rect); return;
}
core::rect<s32> imgrect2(-m_padding/2, -m_padding/2, width+m_padding/2, height+m_padding/2);
core::rect<s32> rect2 = imgrect2 + pos;
video::ITexture *texture = tsrc->getTexture(hotbar_image);
core::dimension2di imgsize(texture->getOriginalSize());
draw2DImageFilterScaled(driver, texture, rect2,
core::rect<s32>(core::position2d<s32>(0,0), imgsize),
nullptr, hbar_colors, true);
}
// NOTE: selectitem = 0 -> no selected; selectitem is 1-based
// mainlist can be NULL, but draw the frame anyway.
void Hud::drawInventory(const v2s32& screen_pos, const v2f& offset, s32 itemcount, v2f alignment,
InventoryList *mainlist, u16 selectitem, u16 direction)
{
v2s32 screen_offset(offset.X, offset.Y);
s32 height, width;
v2s32 pos;
getInventoryDimensions(screen_pos, screen_offset, itemcount, alignment,
direction, pos, width, height);
drawInventoryBackground(pos, width, height);
drawItems(pos, itemcount, 0, mainlist, selectitem, direction);
}
void Hud::drawHotbar(const v2s32 &screen_pos, const v2f &offset, u16 direction,
const v2f &alignment, const v3f &world_pos)
{
if (g_touchcontrols)
g_touchcontrols->resetHotbarRects();
auto& sources = player->hotbar_source.getSources();
u16 wield_index = player->getWieldIndex() + 1;
v2s32 screen_offset(offset.X, offset.Y);
if (!world_pos.X) {
// All invs next to each other
if (world_pos.Z > player->hotbar_source.getMaxLength())
return;
s32 hotbar_length = world_pos.Y ? world_pos.Y : player->hotbar_source.getMaxLength() - world_pos.Z;
s32 height, width;
v2s32 pos;
getInventoryDimensions(screen_pos, screen_offset, hotbar_length, alignment,
direction, pos, width, height);
drawInventoryBackground(pos, width, height);
// Handle offset
std::size_t source_index = 0;
u16 length_before = 0;
for (s32 inv_offset = world_pos.Z; source_index < sources.size(); source_index++) {
const HotbarSource::Source& source = sources[source_index];
if (inv_offset < source.length) {
s32 inv_length = MYMIN(source.length - inv_offset, hotbar_length);
drawItems(pos + getInventoryPosOffset(direction, 0, hotbar_length - inv_length),
inv_length + source.offset + inv_offset, source.offset + inv_offset,
inventory->getList(source.list), wield_index + source.offset,
direction, true, 0);
length_before = inv_length;
source_index++;
break;
}
inv_offset -= source.length;
}
for (; source_index < sources.size(); source_index++) {
const HotbarSource::Source& source = sources[source_index];
s32 inv_length = MYMIN(source.length, hotbar_length - length_before);
if(inv_length <= 0)
break;
drawItems(pos + getInventoryPosOffset(direction, length_before, hotbar_length - inv_length),
inv_length + source.offset,
source.offset, inventory->getList(source.list),
wield_index - length_before + source.offset, direction, true, length_before);
length_before += inv_length;
}
} else {
// Only a single inventory
if (world_pos.X > sources.size())
return;
const HotbarSource::Source& source = sources[world_pos.X-1];
if (world_pos.Z > source.length)
return;
s32 inv_length = world_pos.Y ? world_pos.Y : source.length - world_pos.Z;
s32 height, width;
v2s32 pos;
getInventoryDimensions(screen_pos, screen_offset, inv_length, alignment, direction,
pos, width, height);
drawInventoryBackground(pos, width, height);
u16 length_before = player->hotbar_source.getLengthBefore(world_pos.X - 1);
s32 inv_offset = source.offset + world_pos.Z;
inv_length = MYMIN(inv_length, source.length);
drawItems(pos, inv_length + inv_offset, inv_offset, inventory->getList(source.list),
wield_index - length_before + inv_offset, direction, true, length_before);
} }
} }
@ -434,8 +541,7 @@ void Hud::drawLuaElements(const v3s16 &camera_offset)
InventoryList *inv = inventory->getList(e->text); InventoryList *inv = inventory->getList(e->text);
if (!inv) if (!inv)
warningstream << "HUD: Unknown inventory list. name=" << e->text << std::endl; warningstream << "HUD: Unknown inventory list. name=" << e->text << std::endl;
drawItems(pos, v2s32(e->offset.X, e->offset.Y), e->number, e->align, 0, drawInventory(pos, e->offset, e->number, e->align, inv, e->item, e->dir);
inv, e->item, e->dir, false);
break; } break; }
case HUD_ELEM_WAYPOINT: { case HUD_ELEM_WAYPOINT: {
if (!calculateScreenPos(camera_offset, e, &pos)) if (!calculateScreenPos(camera_offset, e, &pos))
@ -567,7 +673,25 @@ void Hud::drawLuaElements(const v3s16 &camera_offset)
client->getMinimap()->drawMinimap(rect); client->getMinimap()->drawMinimap(rect);
break; } break; }
case HUD_ELEM_HOTBAR: { case HUD_ELEM_HOTBAR: {
drawHotbar(pos, e->offset, e->dir, e->align); if (!e->world_pos.X) { // Handle splitting caused by hud_hotbar_max_width
u16 hotbar_itemcount = e->world_pos.Y ? e->world_pos.Y :
player->hotbar_source.getMaxLength();
hotbar_itemcount -= e->world_pos.Z;
s32 width = hotbar_itemcount * (m_hotbar_imagesize + m_padding * 2);
const v2u32 &window_size = RenderingEngine::getWindowSize();
if ((float) width / (float) window_size.X >
g_settings->getFloat("hud_hotbar_max_width")) {
v2s32 upper_pos = pos - v2s32(0, m_hotbar_imagesize + m_padding);
u16 upper_itemcount = hotbar_itemcount/2;
drawHotbar(upper_pos, e->offset, e->dir, e->align,
{0.f, (float) upper_itemcount, e->world_pos.Z});
drawHotbar(pos, e->offset, e->dir, e->align,
{0.f, (float) hotbar_itemcount - upper_itemcount,
e->world_pos.Z + upper_itemcount});
break;
}
}
drawHotbar(pos, e->offset, e->dir, e->align, e->world_pos);
break; } break; }
default: default:
infostream << "Hud::drawLuaElements: ignoring drawform " << e->type infostream << "Hud::drawLuaElements: ignoring drawform " << e->type
@ -778,38 +902,6 @@ void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir,
} }
} }
} }
void Hud::drawHotbar(const v2s32 &pos, const v2f &offset, u16 dir, const v2f &align)
{
if (g_touchcontrols)
g_touchcontrols->resetHotbarRects();
InventoryList *mainlist = inventory->getList("main");
if (mainlist == NULL) {
// Silently ignore this. We may not be initialized completely.
return;
}
u16 playeritem = player->getWieldIndex();
v2s32 screen_offset(offset.X, offset.Y);
s32 hotbar_itemcount = player->getMaxHotbarItemcount();
s32 width = hotbar_itemcount * (m_hotbar_imagesize + m_padding * 2);
const v2u32 &window_size = RenderingEngine::getWindowSize();
if ((float) width / (float) window_size.X <=
g_settings->getFloat("hud_hotbar_max_width")) {
drawItems(pos, screen_offset, hotbar_itemcount, align, 0,
mainlist, playeritem + 1, dir, true);
} else {
v2s32 upper_pos = pos - v2s32(0, m_hotbar_imagesize + m_padding);
drawItems(upper_pos, screen_offset, hotbar_itemcount / 2, align, 0,
mainlist, playeritem + 1, dir, true);
drawItems(pos, screen_offset, hotbar_itemcount, align,
hotbar_itemcount / 2, mainlist, playeritem + 1, dir, true);
}
}
void Hud::drawCrosshair() void Hud::drawCrosshair()
{ {

View file

@ -62,7 +62,6 @@ public:
void disableBlockBounds(); void disableBlockBounds();
void drawBlockBounds(); void drawBlockBounds();
void drawHotbar(const v2s32 &pos, const v2f &offset, u16 direction, const v2f &align);
void resizeHotbar(); void resizeHotbar();
void drawCrosshair(); void drawCrosshair();
void drawSelectionMesh(); void drawSelectionMesh();
@ -104,11 +103,20 @@ private:
const std::string &texture, const std::string& bgtexture, const std::string &texture, const std::string& bgtexture,
s32 count, s32 maxcount, v2s32 offset, v2s32 size = v2s32()); s32 count, s32 maxcount, v2s32 offset, v2s32 size = v2s32());
void drawItems(v2s32 screen_pos, v2s32 screen_offset, s32 itemcount, v2f alignment,
s32 inv_offset, InventoryList *mainlist, u16 selectitem,
u16 direction, bool is_hotbar);
void drawItem(const ItemStack &item, const core::rect<s32> &rect, bool selected); void drawItem(const ItemStack &item, const core::rect<s32> &rect, bool selected);
void drawItems(const v2s32& pos, s32 inv_size, s32 inv_offset, InventoryList *mainlist,
u16 selectitem, u16 direction, bool is_hotbar = false,
u16 hotbar_touchcontrol_offset = 0);
v2s32 getInventoryPosOffset(u16 direction, s32 before, s32 remainder);
void getInventoryDimensions(v2s32 screen_pos, const v2s32& screen_offset, s32 inv_length,
v2f alignment, u16 direction, v2s32& pos, s32& width, s32& height);
void drawInventoryBackground(const v2s32& pos, s32 width, s32 height);
void drawInventory(const v2s32& screen_pos, const v2f& offset, s32 itemcount,
v2f alignment, InventoryList *mainlist, u16 selectitem, u16 direction);
void drawHotbar(const v2s32 &pos, const v2f &offset, u16 direction, const v2f &alignment,
const v3f &world_pos);
void drawCompassTranslate(HudElement *e, video::ITexture *texture, void drawCompassTranslate(HudElement *e, video::ITexture *texture,
const core::rect<s32> &rect, int way); const core::rect<s32> &rect, int way);

View file

@ -11,6 +11,7 @@
#include "hud.h" #include "hud.h"
#include "log_internal.h" #include "log_internal.h"
#include "client/renderingengine.h" #include "client/renderingengine.h"
#include "util/hotbar_source.h" // HOTBAR_ITEMCOUNT_MAX
void KeyCache::populate_nonchanging() void KeyCache::populate_nonchanging()
{ {
@ -69,7 +70,7 @@ void KeyCache::populate()
key[KeyType::QUICKTUNE_INC] = getKeySetting("keymap_quicktune_inc"); key[KeyType::QUICKTUNE_INC] = getKeySetting("keymap_quicktune_inc");
key[KeyType::QUICKTUNE_DEC] = getKeySetting("keymap_quicktune_dec"); key[KeyType::QUICKTUNE_DEC] = getKeySetting("keymap_quicktune_dec");
for (int i = 0; i < HUD_HOTBAR_ITEMCOUNT_MAX; i++) { for (int i = 0; i < HOTBAR_ITEMCOUNT_MAX; i++) {
std::string slot_key_name = "keymap_slot" + std::to_string(i + 1); std::string slot_key_name = "keymap_slot" + std::to_string(i + 1);
key[KeyType::SLOT_1 + i] = getKeySetting(slot_key_name.c_str()); key[KeyType::SLOT_1 + i] = getKeySetting(slot_key_name.c_str());
} }

View file

@ -35,12 +35,10 @@
#define HUD_FLAG_BASIC_DEBUG (1 << 7) #define HUD_FLAG_BASIC_DEBUG (1 << 7)
#define HUD_FLAG_CHAT_VISIBLE (1 << 8) #define HUD_FLAG_CHAT_VISIBLE (1 << 8)
#define HUD_PARAM_HOTBAR_ITEMCOUNT 1 #define HUD_PARAM_HOTBAR_ITEMCOUNT 1 // Only send by servers with protocol version < 48
#define HUD_PARAM_HOTBAR_IMAGE 2 #define HUD_PARAM_HOTBAR_IMAGE 2
#define HUD_PARAM_HOTBAR_SELECTED_IMAGE 3 #define HUD_PARAM_HOTBAR_SELECTED_IMAGE 3
#define HUD_PARAM_HOTBAR_SOURCE 4
#define HUD_HOTBAR_ITEMCOUNT_DEFAULT 8
#define HUD_HOTBAR_ITEMCOUNT_MAX 32
#define HOTBAR_IMAGE_SIZE 48 #define HOTBAR_IMAGE_SIZE 48

View file

@ -1264,18 +1264,27 @@ void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
*pkt >> param >> value; *pkt >> param >> value;
LocalPlayer *player = m_env.getLocalPlayer(); LocalPlayer *player = m_env.getLocalPlayer();
assert(player != NULL); assert(player != nullptr);
if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) { switch(param) {
case HUD_PARAM_HOTBAR_ITEMCOUNT: {
if (value.size() != 4)
break;
s32 hotbar_itemcount = readS32((u8*) value.c_str()); s32 hotbar_itemcount = readS32((u8*) value.c_str());
if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX) player->hotbar_source.setHotbarItemcountLegacy(hotbar_itemcount);
player->hud_hotbar_itemcount = hotbar_itemcount; break; }
} case HUD_PARAM_HOTBAR_IMAGE:
else if (param == HUD_PARAM_HOTBAR_IMAGE) {
player->hotbar_image = value; player->hotbar_image = value;
} break;
else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) { case HUD_PARAM_HOTBAR_SELECTED_IMAGE:
player->hotbar_selected_image = value; player->hotbar_selected_image = value;
break;
case HUD_PARAM_HOTBAR_SOURCE: {
std::istringstream is(value);
player->hotbar_source.deSerialize(is);
break; }
default:
break;
} }
} }

View file

@ -64,6 +64,7 @@
[scheduled bump for 5.11.0] [scheduled bump for 5.11.0]
PROTOCOL VERSION 48 PROTOCOL VERSION 48
Add compression to some existing packets Add compression to some existing packets
Send HUD_PARAM_HOTBAR_SOURCE instead of HUD_PARAM_HOTBAR_ITEMCOUNT
[scheduled bump for 5.12.0] [scheduled bump for 5.12.0]
*/ */

View file

@ -819,11 +819,11 @@ void Server::handleCommand_PlayerItem(NetworkPacket* pkt)
*pkt >> item; *pkt >> item;
if (item >= player->getMaxHotbarItemcount()) { if (item >= player->hotbar_source.getMaxLength()) {
actionstream << "Player " << player->getName() actionstream << "Player " << player->getName()
<< " tried to access item=" << item << " tried to access item=" << item
<< " out of hotbar_itemcount=" << " while max="
<< player->getMaxHotbarItemcount() << player->hotbar_source.getMaxLength()
<< "; ignoring." << std::endl; << "; ignoring." << std::endl;
return; return;
} }
@ -916,11 +916,11 @@ void Server::handleCommand_Interact(NetworkPacket *pkt)
// Update wielded item // Update wielded item
if (item_i >= player->getMaxHotbarItemcount()) { if (item_i >= player->hotbar_source.getMaxLength()) {
actionstream << "Player " << player->getName() actionstream << "Player " << player->getName()
<< " tried to access item=" << item_i << " tried to access item=" << item_i
<< " out of hotbar_itemcount=" << " while max="
<< player->getMaxHotbarItemcount() << player->hotbar_source.getMaxLength()
<< "; ignoring." << std::endl; << "; ignoring." << std::endl;
return; return;
} }

View file

@ -72,7 +72,7 @@ Player::Player(const std::string &name, IItemDefManager *idef):
HUD_FLAG_MINIMAP_RADAR_VISIBLE | HUD_FLAG_BASIC_DEBUG | HUD_FLAG_MINIMAP_RADAR_VISIBLE | HUD_FLAG_BASIC_DEBUG |
HUD_FLAG_CHAT_VISIBLE; HUD_FLAG_CHAT_VISIBLE;
hud_hotbar_itemcount = HUD_HOTBAR_ITEMCOUNT_DEFAULT; hotbar_source.addSource(HOTBAR_INVENTORY_LIST_DEFAULT, HOTBAR_ITEMCOUNT_DEFAULT, 0);
} }
Player::~Player() Player::~Player()
@ -82,25 +82,27 @@ Player::~Player()
void Player::setWieldIndex(u16 index) void Player::setWieldIndex(u16 index)
{ {
const InventoryList *mlist = inventory.getList("main"); m_wield_index = MYMIN(index, hotbar_source.getMaxLength() - 1);
m_wield_index = MYMIN(index, mlist ? mlist->getSize() : 0);
} }
u16 Player::getWieldIndex() u16 Player::getWieldIndex()
{ {
return std::min(m_wield_index, getMaxHotbarItemcount()); return MYMIN(m_wield_index, hotbar_source.getMaxLength() - 1);
} }
ItemStack &Player::getWieldedItem(ItemStack *selected, ItemStack *hand) const ItemStack &Player::getWieldedItem(ItemStack *selected, ItemStack *hand) const
{ {
assert(selected); assert(selected);
const InventoryList *mlist = inventory.getList("main"); // TODO: Make this generic std::string list;
u16 index;
if (hotbar_source.getInventoryFromWieldIndex(m_wield_index, list, index)) {
const InventoryList *mlist = inventory.getList(list);
if (mlist && index < mlist->getSize())
*selected = mlist->getItem(index);
}
const InventoryList *hlist = inventory.getList("hand"); const InventoryList *hlist = inventory.getList("hand");
if (mlist && m_wield_index < mlist->getSize())
*selected = mlist->getItem(m_wield_index);
if (hand && hlist) if (hand && hlist)
*hand = hlist->getItem(0); *hand = hlist->getItem(0);
@ -160,12 +162,6 @@ void Player::clearHud()
} }
} }
u16 Player::getMaxHotbarItemcount()
{
InventoryList *mainlist = inventory.getList("main");
return mainlist ? std::min(mainlist->getSize(), (u32) hud_hotbar_itemcount) : 0;
}
void PlayerControl::setMovementFromKeys() void PlayerControl::setMovementFromKeys()
{ {
bool a_up = direction_keys & (1 << 0), bool a_up = direction_keys & (1 << 0),

View file

@ -12,6 +12,7 @@
#include <mutex> #include <mutex>
#include <functional> #include <functional>
#include <string> #include <string>
#include "util/hotbar_source.h"
#define PLAYERNAME_SIZE 20 #define PLAYERNAME_SIZE 20
@ -228,10 +229,7 @@ public:
void clearHud(); void clearHud();
u32 hud_flags; u32 hud_flags;
s32 hud_hotbar_itemcount; HotbarSource hotbar_source;
// Get actual usable number of hotbar items (clamped to size of "main" list)
u16 getMaxHotbarItemcount();
protected: protected:
std::string m_name; std::string m_name;

View file

@ -35,12 +35,8 @@ public:
RemotePlayerChatResult canSendChatMessage(); RemotePlayerChatResult canSendChatMessage();
void setHotbarItemcount(s32 hotbar_itemcount) void setHotbarSource(const HotbarSource& source) { hotbar_source = source; }
{ const HotbarSource& getHotbarSource() const { return hotbar_source; }
hud_hotbar_itemcount = hotbar_itemcount;
}
s32 getHotbarItemcount() const { return hud_hotbar_itemcount; }
void overrideDayNightRatio(bool do_override, float ratio) void overrideDayNightRatio(bool do_override, float ratio)
{ {

View file

@ -1933,7 +1933,7 @@ int ObjectRef::l_hud_set_hotbar_itemcount(lua_State *L)
s32 hotbar_itemcount = luaL_checkint(L, 2); s32 hotbar_itemcount = luaL_checkint(L, 2);
if (!getServer(L)->hudSetHotbarItemcount(player, hotbar_itemcount)) if (!getServer(L)->hudSetHotbarItemcountLegacy(player, hotbar_itemcount))
return 0; return 0;
lua_pushboolean(L, true); lua_pushboolean(L, true);
@ -1949,7 +1949,71 @@ int ObjectRef::l_hud_get_hotbar_itemcount(lua_State *L)
if (player == nullptr) if (player == nullptr)
return 0; return 0;
lua_pushinteger(L, player->getMaxHotbarItemcount()); lua_pushinteger(L, player->hotbar_source.getMaxLength());
return 1;
}
// get_hotbar_source(self)
int ObjectRef::l_get_hotbar_source(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkObject<ObjectRef>(L, 1);
RemotePlayer *player = getplayer(ref);
if (player == nullptr)
return 0;
lua_newtable(L);
auto& sources = player->hotbar_source.getSources();
for (std::size_t i = 0; i < sources.size(); i++) {
auto& source = sources[i];
lua_newtable(L);
lua_pushstring(L, source.list.c_str());
lua_setfield(L, -2, "list");
lua_pushinteger(L, source.length);
lua_setfield(L, -2, "length");
lua_pushinteger(L, source.offset);
lua_setfield(L, -2, "offset");
lua_rawseti(L,-2, i+1);
}
return 1;
}
// set_hotbar_source(self, sources)
int ObjectRef::l_set_hotbar_source(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkObject<ObjectRef>(L, 1);
RemotePlayer *player = getplayer(ref);
if (player == nullptr)
return 0;
if(!lua_istable(L, 2))
return 0;
HotbarSource hotbar_source;
lua_pushnil(L);
while(lua_next(L, 2) != 0){
std::string_view list;
if(lua_istable(L, -1) && getstringfield(L, -1, "list", list)) {
hotbar_source.addSource(list,
getintfield_default(L, -1, "length", 0),
getintfield_default(L, -1, "offset", 0));
}
lua_pop(L, 1);
}
if (!getServer(L)->hudSetHotbarSource(player, hotbar_source))
return 0;
return 1; return 1;
} }
@ -2905,6 +2969,8 @@ luaL_Reg ObjectRef::methods[] = {
luamethod(ObjectRef, hud_get_flags), luamethod(ObjectRef, hud_get_flags),
luamethod(ObjectRef, hud_set_hotbar_itemcount), luamethod(ObjectRef, hud_set_hotbar_itemcount),
luamethod(ObjectRef, hud_get_hotbar_itemcount), luamethod(ObjectRef, hud_get_hotbar_itemcount),
luamethod(ObjectRef, get_hotbar_source),
luamethod(ObjectRef, set_hotbar_source),
luamethod(ObjectRef, hud_set_hotbar_image), luamethod(ObjectRef, hud_set_hotbar_image),
luamethod(ObjectRef, hud_get_hotbar_image), luamethod(ObjectRef, hud_get_hotbar_image),
luamethod(ObjectRef, hud_set_hotbar_selected_image), luamethod(ObjectRef, hud_set_hotbar_selected_image),

View file

@ -314,6 +314,12 @@ private:
// hud_get_hotbar_itemcount(self) // hud_get_hotbar_itemcount(self)
static int l_hud_get_hotbar_itemcount(lua_State *L); static int l_hud_get_hotbar_itemcount(lua_State *L);
// get_hotbar_source(self)
static int l_get_hotbar_source(lua_State *L);
// set_hotbar_source(self, sources)
static int l_set_hotbar_source(lua_State *L);
// hud_set_hotbar_image(self, name) // hud_set_hotbar_image(self, name)
static int l_hud_set_hotbar_image(lua_State *L); static int l_hud_set_hotbar_image(lua_State *L);

View file

@ -64,6 +64,7 @@
#include "particles.h" #include "particles.h"
#include "gettext.h" #include "gettext.h"
#include "util/tracy_wrapper.h" #include "util/tracy_wrapper.h"
#include "util/hotbar_source.h"
class ClientNotFoundException : public BaseException class ClientNotFoundException : public BaseException
{ {
@ -3451,21 +3452,44 @@ bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
return true; return true;
} }
bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount) bool Server::hudSetHotbarItemcountLegacy(RemotePlayer *player, s32 hotbar_itemcount)
{ {
if (!player) if (!player)
return false; return false;
if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX) if (hotbar_itemcount <= 0 || hotbar_itemcount > HOTBAR_ITEMCOUNT_MAX)
return false; return false;
if (player->getHotbarItemcount() == hotbar_itemcount) InventoryList *mainlist = player->inventory.getList("main");
return true; hotbar_itemcount = mainlist ? MYMIN(mainlist->getSize(), (u32) hotbar_itemcount) : 0;
player->setHotbarItemcount(hotbar_itemcount); player->hotbar_source.setHotbarItemcountLegacy(hotbar_itemcount);
if (player->protocol_version < 48) {
std::ostringstream os(std::ios::binary); std::ostringstream os(std::ios::binary);
writeS32(os, hotbar_itemcount); writeS32(os, hotbar_itemcount);
SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str()); SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
} else {
std::ostringstream os(std::ios::binary);
player->hotbar_source.serialize(os);
SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SOURCE, os.str());
}
return true;
}
bool Server::hudSetHotbarSource(RemotePlayer *player, const HotbarSource& source)
{
if (!player)
return false;
if (source.getMaxLength() > HOTBAR_ITEMCOUNT_MAX)
return false;
player->setHotbarSource(source);
std::ostringstream os(std::ios::binary);
source.serialize(os);
SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SOURCE, os.str());
return true; return true;
} }

View file

@ -17,6 +17,7 @@
#include "util/thread.h" #include "util/thread.h"
#include "util/basic_macros.h" #include "util/basic_macros.h"
#include "util/metricsbackend.h" #include "util/metricsbackend.h"
#include "util/hotbar_source.h"
#include "serverenvironment.h" #include "serverenvironment.h"
#include "server/clientiface.h" #include "server/clientiface.h"
#include "threading/ordered_mutex.h" #include "threading/ordered_mutex.h"
@ -364,7 +365,8 @@ public:
bool hudRemove(RemotePlayer *player, u32 id); bool hudRemove(RemotePlayer *player, u32 id);
bool hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *value); bool hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *value);
bool hudSetFlags(RemotePlayer *player, u32 flags, u32 mask); bool hudSetFlags(RemotePlayer *player, u32 flags, u32 mask);
bool hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount); bool hudSetHotbarItemcountLegacy(RemotePlayer *player, s32 hotbar_itemcount);
bool hudSetHotbarSource(RemotePlayer *player, const HotbarSource& source);
void hudSetHotbarImage(RemotePlayer *player, const std::string &name); void hudSetHotbarImage(RemotePlayer *player, const std::string &name);
void hudSetHotbarSelectedImage(RemotePlayer *player, const std::string &name); void hudSetHotbarSelectedImage(RemotePlayer *player, const std::string &name);

View file

@ -582,9 +582,13 @@ ItemStack PlayerSAO::getWieldedItem(ItemStack *selected, ItemStack *hand) const
bool PlayerSAO::setWieldedItem(const ItemStack &item) bool PlayerSAO::setWieldedItem(const ItemStack &item)
{ {
InventoryList *mlist = m_player->inventory.getList(getWieldList()); std::string list;
if (mlist) { u16 index;
mlist->changeItem(m_player->getWieldIndex(), item); if (m_player->hotbar_source.getInventoryFromWieldIndex(getWieldIndex(), list, index)) {
InventoryList *mlist = m_player->inventory.getList(list);
if (!mlist)
return false;
mlist->changeItem(index, item);
return true; return true;
} }
return false; return false;

View file

@ -117,7 +117,6 @@ public:
Inventory *getInventory() const override; Inventory *getInventory() const override;
InventoryLocation getInventoryLocation() const override; InventoryLocation getInventoryLocation() const override;
void setInventoryModified() override {} void setInventoryModified() override {}
std::string getWieldList() const override { return "main"; }
u16 getWieldIndex() const override; u16 getWieldIndex() const override;
ItemStack getWieldedItem(ItemStack *selected, ItemStack *hand = nullptr) const override; ItemStack getWieldedItem(ItemStack *selected, ItemStack *hand = nullptr) const override;
bool setWieldedItem(const ItemStack &item) override; bool setWieldedItem(const ItemStack &item) override;

View file

@ -19,4 +19,5 @@ set(util_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/timetaker.cpp ${CMAKE_CURRENT_SOURCE_DIR}/timetaker.cpp
${CMAKE_CURRENT_SOURCE_DIR}/png.cpp ${CMAKE_CURRENT_SOURCE_DIR}/png.cpp
${CMAKE_CURRENT_SOURCE_DIR}/enum_string.cpp ${CMAKE_CURRENT_SOURCE_DIR}/enum_string.cpp
${CMAKE_CURRENT_SOURCE_DIR}/hotbar_source.cpp
PARENT_SCOPE) PARENT_SCOPE)

View file

@ -0,0 +1,71 @@
// Luanti
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (C) 2025 cx384
#include "hotbar_source.h"
#include "serialize.h"
void HotbarSource::setHotbarItemcountLegacy(s32 count)
{
sources.clear();
sources.push_back({HOTBAR_INVENTORY_LIST_DEFAULT, (u16) count, 0});
}
u16 HotbarSource::getMaxLength() const
{
u16 length = 0;
for (auto& source : sources) {
length += source.length;
}
return length ;
}
bool HotbarSource::getInventoryFromWieldIndex(u16 wield_index, std::string &list, u16 &index) const
{
for (auto& source : sources) {
if (wield_index < source.length) {
list = source.list;
index = wield_index + source.offset;
return true;
}
wield_index -= source.length;
}
return false;
}
u16 HotbarSource::getLengthBefore(std::size_t index) const {
u16 length_before = 0;
for (std::size_t i = 0; i < index && i < sources.size(); i++) {
length_before += sources[i].length;
}
return length_before;
}
void HotbarSource::serialize(std::ostream &os) const
{
writeU8(os, 0); // version
writeU16(os, sources.size());
for (const auto &source : sources) {
os << serializeString16(source.list);
writeU16(os, source.length);
writeU16(os, source.offset);
}
}
void HotbarSource::deSerialize(std::istream &is)
{
int version = readU8(is);
if (version != 0)
throw SerializationError("unsupported HotbarSource version");
sources.clear();
u16 size = readU16(is);
for (u16 i = 0; i < size; i++) {
Source source;
source.list = deSerializeString16(is);
source.length = readU16(is);
source.offset = readU16(is);
sources.push_back(source);
}
}

52
src/util/hotbar_source.h Normal file
View file

@ -0,0 +1,52 @@
// Luanti
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (C) 2025 cx384
// Stores a list of player inventories to be used by a hotbar
// Independent of the HUD element
#pragma once
#include <vector>
#include <string>
#include <string_view>
#include "irrlichttypes.h"
#define HOTBAR_ITEMCOUNT_DEFAULT 8
#define HOTBAR_INVENTORY_LIST_DEFAULT "main"
#define HOTBAR_ITEMCOUNT_MAX 32
struct HotbarSource {
struct Source {
std::string list;
u16 length;
u16 offset;
};
// Old functionality which could only use the "main" list
void setHotbarItemcountLegacy(s32 count);
// Returns the total length of all sources
u16 getMaxLength() const;
// Returns list and index of the inventory if it exists
bool getInventoryFromWieldIndex(u16 wield_index, std::string &list, u16 &index) const;
// Returns number of inventory slots before the source at index
u16 getLengthBefore(std::size_t index) const;
const std::vector<Source> getSources() const {
return sources;
};
void addSource(std::string_view list, u16 length, u16 offset) {
sources.push_back(Source{std::string{list}, length, offset});
};
void serialize(std::ostream &os) const;
void deSerialize(std::istream &is);
private:
std::vector<Source> sources;
};