feature: hud (#37)

* build: replace FetchContent with direct includes for glm

* build(deps): replace SOIL2 with stb for image and vorbis decoding

* build: replace FetchContent with direct includes for toml++

* refactor(texture): bump block texture size to 512 and disable items tab

* feat: add hotbar system with ItemStack and RowLayout

* fix(ui): restrict Ctrl key handling to typing mode

* feat(ui): add widget border support and highlight selected hotbar slot

* fix(ui): update borders on scale change and correct Image width

- Added update_border() calls in set_scale() for Button, ChatBox, Image, Label, and TextField.
- Fixed Image::width() to multiply by width instead of height.
- Added early return in Widget::update_border() if border is not supported.

* feat(ui): add border visual feedback on slider drag

* feat(ui): add ItemSlot widget and refactor hotbar to use it

* feat(world_scene): add inventory UI and pause type enum

* feat(ui): add item slot tooltip and make window size static

* fix(ui label): account for background offset in width/height

* refactor(ui): centralize item info label in inventory and add update

* feat(ui): add hotbar interaction in inventory UI

* fix(inventory-ui): correct row creation logic for first item

The loop starting at index 0 created a new row on the first iteration
(i % 10 == 0). Shift the loop to start at 1 and adjust modulus condition
to create rows correctly every 10 items.

* feat(block): add name_key for localized block names

Add `name_key` field to all block TOML definitions, enabling per-block localization. Update `BlockData` struct and `BlockManager` with `local_name()` method that returns a localized string via the translation system. Add corresponding entries to `en_US.json` and `zh_CN.json`. Modify inventory UI to use the localized name when displaying block info, and fix a bug where emptying a hotbar slot did not clear the item locally.

* fix(ui): change inventory item slot color from white to gray

* fix(ui): add missing include for std::array
This commit is contained in:
zhenyan121
2026-07-23 14:07:11 +08:00
committed by GitHub
parent 45d2c8234a
commit def311c7dd
565 changed files with 98955 additions and 214 deletions

View File

@@ -1,5 +1,6 @@
#include "Cubed/gameplay/block.hpp"
#include "Cubed/localization.hpp"
#include "Cubed/tools/cubed_assert.hpp"
#include "Cubed/tools/log.hpp"
#include "Cubed/tools/toml.utils.hpp"
@@ -38,7 +39,13 @@ const std::string& BlockManager::name_form_id(BlockType id) {
}
return m_datas[id].name;
}
std::string BlockManager::local_name(BlockType id) {
if (id >= sums()) {
Logger::error("Id {}, is Over The Max Id", id, sums() - 1);
return tr(m_datas[0].name_key);
}
return tr(m_datas[id].name_key);
}
bool BlockManager::is_gas(BlockType id) {
if (id >= sums()) {
Logger::error("Id {}, is Over The Max Id", id, sums() - 1);
@@ -146,7 +153,8 @@ void BlockManager::init() {
auto is_blend = safe_get_value(block, "is_blend", false);
auto is_transitional = safe_get_value(block, "is_transitional", false);
auto roughness = safe_get_value(block, "roughness", 1.0);
m_datas.emplace_back(*id, *name, *is_liquid, *is_passable,
auto name_key = safe_get_value(block, "name_key", "Unknow_key");
m_datas.emplace_back(*name, *name_key, *id, *is_liquid, *is_passable,
*is_cross_plane, *is_transparent, *is_gas,
*is_discard, *is_blend, *is_transitional,
static_cast<float>(*roughness));

View File

@@ -126,8 +126,6 @@ void ClientPlayer::reload_config() {
}
void ClientPlayer::set_player_pos(const glm::vec3& pos) { m_player_pos = pos; }
void ClientPlayer::set_place_block(unsigned id) { m_place_block = id; }
void ClientPlayer::update(float delta_time) {
m_gait = compute_gait();
update_move(delta_time);
@@ -193,6 +191,7 @@ bool ClientPlayer::update_player_move_state(Key key, KeyAction action) {
}
} else if (key == Key::LEFT_CTRL) {
if (action == KeyAction::PRESS) {
m_sprinting = true;
}
/*
@@ -207,6 +206,26 @@ bool ClientPlayer::update_player_move_state(Key key, KeyAction action) {
change_mode(CREATIVE);
}
}
} else if (key == Key::NUMPAD_1 || key == Key::DIGIT_1) {
m_selected_hotbar = 0;
} else if (key == Key::NUMPAD_2 || key == Key::DIGIT_2) {
m_selected_hotbar = 1;
} else if (key == Key::NUMPAD_3 || key == Key::DIGIT_3) {
m_selected_hotbar = 2;
} else if (key == Key::NUMPAD_4 || key == Key::DIGIT_4) {
m_selected_hotbar = 3;
} else if (key == Key::NUMPAD_5 || key == Key::DIGIT_5) {
m_selected_hotbar = 4;
} else if (key == Key::NUMPAD_6 || key == Key::DIGIT_6) {
m_selected_hotbar = 5;
} else if (key == Key::NUMPAD_7 || key == Key::DIGIT_7) {
m_selected_hotbar = 6;
} else if (key == Key::NUMPAD_8 || key == Key::DIGIT_8) {
m_selected_hotbar = 7;
} else if (key == Key::NUMPAD_9 || key == Key::DIGIT_9) {
m_selected_hotbar = 8;
} else if (key == Key::NUMPAD_0 || key == Key::DIGIT_0) {
m_selected_hotbar = 9;
} else {
return false;
}
@@ -287,16 +306,30 @@ void ClientPlayer::place_block(float dt) {
}
}
if (m_mouse_state.right) {
glm::ivec3 near_pos = m_look_block->pos + m_look_block->normal;
if (!m_world.is_solid(near_pos)) {
AABB block_box = ClientWorld::get_block_aabb(near_pos);
AABB player_box = get_aabb(get_player_pos());
if (!player_box.intersects(block_box)) {
m_world.report_block_change(near_pos, m_place_block);
auto type = m_hotbar[m_selected_hotbar].type;
if (type != 0) {
glm::ivec3 near_pos = m_look_block->pos + m_look_block->normal;
if (!m_world.is_solid(near_pos)) {
AABB block_box = ClientWorld::get_block_aabb(near_pos);
AABB player_box = get_aabb(get_player_pos());
if (!player_box.intersects(block_box)) {
m_world.report_block_change(near_pos, type);
}
}
}
}
}
int ClientPlayer::selected_hotbar() const { return m_selected_hotbar; }
void ClientPlayer::set_hotbar(int pos, const ItemStack& item) {
ASSERT(pos >= 0 && static_cast<size_t>(pos) < HOTBAR_SUM);
m_hotbar[pos] = item;
}
std::span<const ItemStack, ClientPlayer::HOTBAR_SUM>
ClientPlayer::get_hotbar() const {
return m_hotbar;
}
void ClientPlayer::update_move(float delta_time) {
// if frame rate less than 1 frame per second, don't update
if (delta_time > 1.0f) {
@@ -537,14 +570,14 @@ bool ClientPlayer::update_scroll(float yoffset) {
}
if (m_game_mode == CREATIVE) {
if (yoffset < 0) {
m_place_block += 1;
if (m_place_block >= BlockManager::sums()) {
m_place_block = 1;
m_selected_hotbar += 1;
if (m_selected_hotbar >= 10) {
m_selected_hotbar = 0;
}
} else {
m_place_block -= 1;
if (m_place_block <= 0) {
m_place_block = BlockManager::sums() - 1;
m_selected_hotbar -= 1;
if (m_selected_hotbar < 0) {
m_selected_hotbar = 0;
}
}
}
@@ -615,7 +648,9 @@ float& ClientPlayer::acceleration() { return m_acceleration; }
float& ClientPlayer::deceleration() { return m_deceleration; }
float& ClientPlayer::g() { return m_g; }
float& ClientPlayer::fly_y_speed() { return m_fly_y_speed; }
unsigned ClientPlayer::get_current_block() const { return m_place_block; };
const ItemStack& ClientPlayer::get_current_itemstack() const {
return m_hotbar[m_selected_hotbar];
};
void ClientPlayer::set_gait(Gait gait) { m_gait = gait; }
GameMode& ClientPlayer::game_mode() { return m_game_mode; }
ClientWorld& ClientPlayer::get_world() { return m_world; }
@@ -662,6 +697,10 @@ void ClientPlayer::init(std::string_view name) {
audio.play_3d(sound, m_player_pos);
Logger::debug("Player block {} walk sound", name);
});
for (int i = 0; i < 10; i++) {
m_hotbar[i].type = i;
}
}
bool ClientPlayer::is_underwater() const { return m_underwater; }