diff --git a/builtin/game/statbars.lua b/builtin/game/statbars.lua index 6b5b54428..d192029c5 100644 --- a/builtin/game/statbars.lua +++ b/builtin/game/statbars.lua @@ -5,7 +5,9 @@ local health_bar_definition = { hud_elem_type = "statbar", position = {x = 0.5, y = 1}, text = "heart.png", + text2 = "heart_gone.png", number = core.PLAYER_MAX_HP_DEFAULT, + item = core.PLAYER_MAX_HP_DEFAULT, direction = 0, size = {x = 24, y = 24}, offset = {x = (-10 * 24) - 25, y = -(48 + 24 + 16)}, @@ -15,7 +17,9 @@ local breath_bar_definition = { hud_elem_type = "statbar", position = {x = 0.5, y = 1}, text = "bubble.png", + text2 = "bubble_gone.png", number = core.PLAYER_MAX_BREATH_DEFAULT, + item = core.PLAYER_MAX_BREATH_DEFAULT * 2, direction = 0, size = {x = 24, y = 24}, offset = {x = 25, y= -(48 + 24 + 16)}, diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 961e1ff37..4078e21a1 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1289,9 +1289,9 @@ To account for differing resolutions, the position coordinates are the percentage of the screen, ranging in value from `0` to `1`. The name field is not yet used, but should contain a description of what the -HUD element represents. The direction field is the direction in which something -is drawn. +HUD element represents. +The `direction` field is the direction in which something is drawn. `0` draws from left to right, `1` draws from right to left, `2` draws from top to bottom, and `3` draws from bottom to top. @@ -1355,12 +1355,16 @@ Displays text on the HUD. ### `statbar` -Displays a horizontal bar made up of half-images. +Displays a horizontal bar made up of half-images with an optional background. -* `text`: The name of the texture that is used. +* `text`: The name of the texture to use. +* `text2`: Optional texture name to enable a background / "off state" + texture (useful to visualize the maximal value). Both textures + must have the same size. * `number`: The number of half-textures that are displayed. If odd, will end with a vertically center-split texture. -* `direction` +* `item`: Same as `number` but for the "off state" texture +* `direction`: To which direction the images will extend to * `offset`: offset in pixels from position. * `size`: If used, will force full-image size to this value (override texture pack image size) @@ -7772,6 +7776,8 @@ Used by `Player:hud_add`. Returned by `Player:hud_get`. text = "", + text2 = "", + number = 2, item = 3, diff --git a/doc/texture_packs.txt b/doc/texture_packs.txt index 4e7bc93c4..94151f1a4 100644 --- a/doc/texture_packs.txt +++ b/doc/texture_packs.txt @@ -64,6 +64,8 @@ by texture packs. All existing fallback textures can be found in the directory * `bubble.png`: the bubble texture when the player is drowning (default size: 12×12) +* `bubble_gone.png`: like `bubble.png`, but denotes lack of breath + (transparent by default, same size as bubble.png) * `crack_anylength.png`: node overlay texture when digging @@ -76,6 +78,8 @@ by texture packs. All existing fallback textures can be found in the directory * `heart.png`: used to display the health points of the player (default size: 12×12) +* `heart_gone.png`: like `heart.png`, but denotes lack of health points + (transparent by default, same size as heart.png) * `minimap_mask_round.png`: round minimap mask, white gets replaced by the map * `minimap_mask_square.png`: mask used for the square minimap diff --git a/src/client/clientevent.h b/src/client/clientevent.h index f5689c25b..7f3984b03 100644 --- a/src/client/clientevent.h +++ b/src/client/clientevent.h @@ -136,6 +136,7 @@ struct ClientEvent v3f *world_pos; v2s32 *size; s16 z_index; + std::string *text2; } hudadd; struct { diff --git a/src/client/game.cpp b/src/client/game.cpp index 4d7a85526..422e17d4f 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -2672,6 +2672,7 @@ void Game::handleClientEvent_HudAdd(ClientEvent *event, CameraOrientation *cam) delete event->hudadd.offset; delete event->hudadd.world_pos; delete event->hudadd.size; + delete event->hudadd.text2; return; } @@ -2689,6 +2690,7 @@ void Game::handleClientEvent_HudAdd(ClientEvent *event, CameraOrientation *cam) e->world_pos = *event->hudadd.world_pos; e->size = *event->hudadd.size; e->z_index = event->hudadd.z_index; + e->text2 = *event->hudadd.text2; hud_server_to_client[server_id] = player->addHud(e); delete event->hudadd.pos; @@ -2699,6 +2701,7 @@ void Game::handleClientEvent_HudAdd(ClientEvent *event, CameraOrientation *cam) delete event->hudadd.offset; delete event->hudadd.world_pos; delete event->hudadd.size; + delete event->hudadd.text2; } void Game::handleClientEvent_HudRemove(ClientEvent *event, CameraOrientation *cam) @@ -2771,6 +2774,10 @@ void Game::handleClientEvent_HudChange(ClientEvent *event, CameraOrientation *ca case HUD_STAT_Z_INDEX: e->z_index = event->hudchange.data; break; + + case HUD_STAT_TEXT2: + e->text2 = *event->hudchange.sdata; + break; } delete event->hudchange.v3fdata; diff --git a/src/client/hud.cpp b/src/client/hud.cpp index 56763e7e4..f8f712762 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -332,7 +332,8 @@ void Hud::drawLuaElements(const v3s16 &camera_offset) break; } case HUD_ELEM_STATBAR: { v2s32 offs(e->offset.X, e->offset.Y); - drawStatbar(pos, HUD_CORNER_UPPER, e->dir, e->text, e->number, offs, e->size); + drawStatbar(pos, HUD_CORNER_UPPER, e->dir, e->text, e->text2, + e->number, e->item, offs, e->size); break; } case HUD_ELEM_INVENTORY: { InventoryList *inv = inventory->getList(e->text); @@ -401,8 +402,9 @@ void Hud::drawLuaElements(const v3s16 &camera_offset) } -void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, const std::string &texture, - s32 count, v2s32 offset, v2s32 size) +void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, + const std::string &texture, const std::string &bgtexture, + s32 count, s32 maxcount, v2s32 offset, v2s32 size) { const video::SColor color(255, 255, 255, 255); const video::SColor colors[] = {color, color, color, color}; @@ -411,6 +413,11 @@ void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, const std::string &tex if (!stat_texture) return; + video::ITexture *stat_texture_bg = nullptr; + if (!bgtexture.empty()) { + stat_texture_bg = tsrc->getTexture(bgtexture); + } + core::dimension2di srcd(stat_texture->getOriginalSize()); core::dimension2di dstd; if (size == v2s32()) { @@ -430,43 +437,100 @@ void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, const std::string &tex p += offset; v2s32 steppos; - core::rect srchalfrect, dsthalfrect; switch (drawdir) { case HUD_DIR_RIGHT_LEFT: steppos = v2s32(-1, 0); - srchalfrect = core::rect(srcd.Width / 2, 0, srcd.Width, srcd.Height); - dsthalfrect = core::rect(dstd.Width / 2, 0, dstd.Width, dstd.Height); break; case HUD_DIR_TOP_BOTTOM: steppos = v2s32(0, 1); - srchalfrect = core::rect(0, 0, srcd.Width, srcd.Height / 2); - dsthalfrect = core::rect(0, 0, dstd.Width, dstd.Height / 2); break; case HUD_DIR_BOTTOM_TOP: steppos = v2s32(0, -1); - srchalfrect = core::rect(0, srcd.Height / 2, srcd.Width, srcd.Height); - dsthalfrect = core::rect(0, dstd.Height / 2, dstd.Width, dstd.Height); break; default: + // From left to right steppos = v2s32(1, 0); - srchalfrect = core::rect(0, 0, srcd.Width / 2, srcd.Height); - dsthalfrect = core::rect(0, 0, dstd.Width / 2, dstd.Height); + break; } + + auto calculate_clipping_rect = [] (core::dimension2di src, + v2s32 steppos) -> core::rect { + + // Create basic rectangle + core::rect rect(0, 0, + src.Width - std::abs(steppos.X) * src.Width / 2, + src.Height - std::abs(steppos.Y) * src.Height / 2 + ); + // Move rectangle left or down + if (steppos.X == -1) + rect += v2s32(src.Width / 2, 0); + if (steppos.Y == -1) + rect += v2s32(0, src.Height / 2); + return rect; + }; + // Rectangles for 1/2 the actual value to display + core::rect srchalfrect, dsthalfrect; + // Rectangles for 1/2 the "off state" texture + core::rect srchalfrect2, dsthalfrect2; + + if (count % 2 == 1) { + // Need to draw halves: Calculate rectangles + srchalfrect = calculate_clipping_rect(srcd, steppos); + dsthalfrect = calculate_clipping_rect(dstd, steppos); + srchalfrect2 = calculate_clipping_rect(srcd, steppos * -1); + dsthalfrect2 = calculate_clipping_rect(dstd, steppos * -1); + } + steppos.X *= dstd.Width; steppos.Y *= dstd.Height; + // Draw full textures for (s32 i = 0; i < count / 2; i++) { core::rect srcrect(0, 0, srcd.Width, srcd.Height); - core::rect dstrect(0,0, dstd.Width, dstd.Height); + core::rect dstrect(0, 0, dstd.Width, dstd.Height); dstrect += p; - draw2DImageFilterScaled(driver, stat_texture, dstrect, srcrect, NULL, colors, true); + draw2DImageFilterScaled(driver, stat_texture, + dstrect, srcrect, NULL, colors, true); p += steppos; } if (count % 2 == 1) { - dsthalfrect += p; - draw2DImageFilterScaled(driver, stat_texture, dsthalfrect, srchalfrect, NULL, colors, true); + // Draw half a texture + draw2DImageFilterScaled(driver, stat_texture, + dsthalfrect + p, srchalfrect, NULL, colors, true); + + if (stat_texture_bg && maxcount > count) { + draw2DImageFilterScaled(driver, stat_texture_bg, + dsthalfrect2 + p, srchalfrect2, + NULL, colors, true); + p += steppos; + } + } + + if (stat_texture_bg && maxcount > count / 2) { + // Draw "off state" textures + s32 start_offset; + if (count % 2 == 1) + start_offset = count / 2 + 1; + else + start_offset = count / 2; + for (s32 i = start_offset; i < maxcount / 2; i++) { + core::rect srcrect(0, 0, srcd.Width, srcd.Height); + core::rect dstrect(0, 0, dstd.Width, dstd.Height); + + dstrect += p; + draw2DImageFilterScaled(driver, stat_texture_bg, + dstrect, srcrect, + NULL, colors, true); + p += steppos; + } + + if (maxcount % 2 == 1) { + draw2DImageFilterScaled(driver, stat_texture_bg, + dsthalfrect + p, srchalfrect, + NULL, colors, true); + } } } diff --git a/src/client/hud.h b/src/client/hud.h index cab115990..6274b1a83 100644 --- a/src/client/hud.h +++ b/src/client/hud.h @@ -82,8 +82,9 @@ public: private: bool calculateScreenPos(const v3s16 &camera_offset, HudElement *e, v2s32 *pos); - void drawStatbar(v2s32 pos, u16 corner, u16 drawdir, const std::string &texture, - s32 count, v2s32 offset, v2s32 size = v2s32()); + void drawStatbar(v2s32 pos, u16 corner, u16 drawdir, + 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, s32 inv_offset, InventoryList *mainlist, u16 selectitem, diff --git a/src/hud.cpp b/src/hud.cpp index 39625b5fd..3079b5cd8 100644 --- a/src/hud.cpp +++ b/src/hud.cpp @@ -46,6 +46,7 @@ const struct EnumString es_HudElementStat[] = {HUD_STAT_WORLD_POS, "world_pos"}, {HUD_STAT_SIZE, "size"}, {HUD_STAT_Z_INDEX, "z_index"}, + {HUD_STAT_TEXT2, "text2"}, {0, NULL}, }; diff --git a/src/hud.h b/src/hud.h index b0977c6a4..bab420ed2 100644 --- a/src/hud.h +++ b/src/hud.h @@ -77,6 +77,7 @@ enum HudElementStat { HUD_STAT_WORLD_POS, HUD_STAT_SIZE, HUD_STAT_Z_INDEX, + HUD_STAT_TEXT2, }; struct HudElement { @@ -93,6 +94,7 @@ struct HudElement { v3f world_pos; v2s32 size; s16 z_index = 0; + std::string text2; }; extern const EnumString es_HudElementType[]; diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 8d0225a3d..7b1b1368c 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -1102,22 +1102,16 @@ void Client::handleCommand_HudAdd(NetworkPacket* pkt) v3f world_pos; v2s32 size; s16 z_index = 0; + std::string text2; *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item >> dir >> align >> offset; try { *pkt >> world_pos; - } - catch(SerializationError &e) {}; - - try { *pkt >> size; - } catch(SerializationError &e) {}; - - try { *pkt >> z_index; - } - catch(PacketError &e) {} + *pkt >> text2; + } catch(PacketError &e) {}; ClientEvent *event = new ClientEvent(); event->type = CE_HUDADD; @@ -1135,6 +1129,7 @@ void Client::handleCommand_HudAdd(NetworkPacket* pkt) event->hudadd.world_pos = new v3f(world_pos); event->hudadd.size = new v2s32(size); event->hudadd.z_index = z_index; + event->hudadd.text2 = new std::string(text2); m_client_event_queue.push(event); } @@ -1171,7 +1166,7 @@ void Client::handleCommand_HudChange(NetworkPacket* pkt) if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE || stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET) *pkt >> v2fdata; - else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT) + else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT || stat == HUD_STAT_TEXT2) *pkt >> sdata; else if (stat == HUD_STAT_WORLD_POS) *pkt >> v3fdata; diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 527ebba7c..ab924f1db 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -560,10 +560,10 @@ enum ToClientCommand u32 id u8 type v2f1000 pos - u32 len + u16 len u8[len] name v2f1000 scale - u32 len2 + u16 len2 u8[len2] text u32 number u32 item @@ -573,6 +573,8 @@ enum ToClientCommand v3f1000 world_pos v2s32 size s16 z_index + u16 len3 + u8[len3] text2 */ TOCLIENT_HUDRM = 0x4a, diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index dac828316..540b7222f 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -1871,6 +1871,7 @@ void read_hud_element(lua_State *L, HudElement *elem) elem->dir = getintfield_default(L, 2, "direction", 0); elem->z_index = MYMAX(S16_MIN, MYMIN(S16_MAX, getintfield_default(L, 2, "z_index", 0))); + elem->text2 = getstringfield_default(L, 2, "text2", ""); // Deprecated, only for compatibility's sake if (elem->dir == 0) @@ -1939,6 +1940,9 @@ void push_hud_element(lua_State *L, HudElement *elem) lua_pushnumber(L, elem->z_index); lua_setfield(L, -2, "z_index"); + + lua_pushstring(L, elem->text2.c_str()); + lua_setfield(L, -2, "text2"); } HudElementStat read_hud_change(lua_State *L, HudElement *elem, void **value) @@ -2000,6 +2004,10 @@ HudElementStat read_hud_change(lua_State *L, HudElement *elem, void **value) elem->z_index = MYMAX(S16_MIN, MYMIN(S16_MAX, luaL_checknumber(L, 4))); *value = &elem->z_index; break; + case HUD_STAT_TEXT2: + elem->text2 = luaL_checkstring(L, 4); + *value = &elem->text2; + break; } return stat; } diff --git a/src/server.cpp b/src/server.cpp index 16e026ce2..b28c30e1e 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1621,7 +1621,7 @@ void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form) pkt << id << (u8) form->type << form->pos << form->name << form->scale << form->text << form->number << form->item << form->dir << form->align << form->offset << form->world_pos << form->size - << form->z_index; + << form->z_index << form->text2; Send(&pkt); } @@ -1647,6 +1647,7 @@ void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void break; case HUD_STAT_NAME: case HUD_STAT_TEXT: + case HUD_STAT_TEXT2: pkt << *(std::string *) value; break; case HUD_STAT_WORLD_POS: diff --git a/textures/base/pack/bubble_gone.png b/textures/base/pack/bubble_gone.png new file mode 100644 index 000000000..240ca4f8d Binary files /dev/null and b/textures/base/pack/bubble_gone.png differ diff --git a/textures/base/pack/heart_gone.png b/textures/base/pack/heart_gone.png new file mode 100644 index 000000000..240ca4f8d Binary files /dev/null and b/textures/base/pack/heart_gone.png differ