3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 Copyright (C) 2010-2013 blue42u, Jonathon Anderson <anderjon@umail.iu.edu>
5 Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License along
18 with this program; if not, write to the Free Software Foundation, Inc.,
19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 #include "util/numeric.h"
29 #include "inventory.h"
30 #include "client/tile.h"
31 #include "localplayer.h"
34 #include "fontengine.h"
35 #include "guiscalingfilter.h"
36 #include <IGUIStaticText.h>
38 #ifdef HAVE_TOUCHSCREENGUI
39 #include "touchscreengui.h"
42 Hud::Hud(video::IVideoDriver *driver, scene::ISceneManager* smgr,
43 gui::IGUIEnvironment* guienv, IGameDef *gamedef, LocalPlayer *player,
44 Inventory *inventory) {
45 this->driver = driver;
47 this->guienv = guienv;
48 this->gamedef = gamedef;
49 this->player = player;
50 this->inventory = inventory;
52 m_screensize = v2u32(0, 0);
53 m_displaycenter = v2s32(0, 0);
54 m_hotbar_imagesize = floor(HOTBAR_IMAGE_SIZE * porting::getDisplayDensity() + 0.5);
55 m_hotbar_imagesize *= g_settings->getFloat("hud_scaling");
56 m_padding = m_hotbar_imagesize / 12;
58 const video::SColor hbar_color(255, 255, 255, 255);
59 for (unsigned int i=0; i < 4; i++ ){
60 hbar_colors[i] = hbar_color;
63 tsrc = gamedef->getTextureSource();
65 v3f crosshair_color = g_settings->getV3F("crosshair_color");
66 u32 cross_r = rangelim(myround(crosshair_color.X), 0, 255);
67 u32 cross_g = rangelim(myround(crosshair_color.Y), 0, 255);
68 u32 cross_b = rangelim(myround(crosshair_color.Z), 0, 255);
69 u32 cross_a = rangelim(g_settings->getS32("crosshair_alpha"), 0, 255);
70 crosshair_argb = video::SColor(cross_a, cross_r, cross_g, cross_b);
72 v3f selectionbox_color = g_settings->getV3F("selectionbox_color");
73 u32 sbox_r = rangelim(myround(selectionbox_color.X), 0, 255);
74 u32 sbox_g = rangelim(myround(selectionbox_color.Y), 0, 255);
75 u32 sbox_b = rangelim(myround(selectionbox_color.Z), 0, 255);
76 selectionbox_argb = video::SColor(255, sbox_r, sbox_g, sbox_b);
78 use_crosshair_image = tsrc->isKnownSourceImage("crosshair.png");
81 use_hotbar_image = false;
82 hotbar_selected_image = "";
83 use_hotbar_selected_image = false;
86 void Hud::drawItem(const ItemStack &item, const core::rect<s32>& rect, bool selected) {
89 /* draw hihlighting around selected item */
90 if (use_hotbar_selected_image) {
91 core::rect<s32> imgrect2 = rect;
92 imgrect2.UpperLeftCorner.X -= (m_padding*2);
93 imgrect2.UpperLeftCorner.Y -= (m_padding*2);
94 imgrect2.LowerRightCorner.X += (m_padding*2);
95 imgrect2.LowerRightCorner.Y += (m_padding*2);
96 video::ITexture *texture = tsrc->getTexture(hotbar_selected_image);
97 core::dimension2di imgsize(texture->getOriginalSize());
98 draw2DImageFilterScaled(driver, texture, imgrect2,
99 core::rect<s32>(core::position2d<s32>(0,0), imgsize),
100 NULL, hbar_colors, true);
102 video::SColor c_outside(255,255,0,0);
103 //video::SColor c_outside(255,0,0,0);
104 //video::SColor c_inside(255,192,192,192);
105 s32 x1 = rect.UpperLeftCorner.X;
106 s32 y1 = rect.UpperLeftCorner.Y;
107 s32 x2 = rect.LowerRightCorner.X;
108 s32 y2 = rect.LowerRightCorner.Y;
109 // Black base borders
110 driver->draw2DRectangle(c_outside,
112 v2s32(x1 - m_padding, y1 - m_padding),
113 v2s32(x2 + m_padding, y1)
115 driver->draw2DRectangle(c_outside,
117 v2s32(x1 - m_padding, y2),
118 v2s32(x2 + m_padding, y2 + m_padding)
120 driver->draw2DRectangle(c_outside,
122 v2s32(x1 - m_padding, y1),
125 driver->draw2DRectangle(c_outside,
128 v2s32(x2 + m_padding, y2)
130 /*// Light inside borders
131 driver->draw2DRectangle(c_inside,
133 v2s32(x1 - padding/2, y1 - padding/2),
134 v2s32(x2 + padding/2, y1)
136 driver->draw2DRectangle(c_inside,
138 v2s32(x1 - padding/2, y2),
139 v2s32(x2 + padding/2, y2 + padding/2)
141 driver->draw2DRectangle(c_inside,
143 v2s32(x1 - padding/2, y1),
146 driver->draw2DRectangle(c_inside,
149 v2s32(x2 + padding/2, y2)
155 video::SColor bgcolor2(128, 0, 0, 0);
156 if (!use_hotbar_image)
157 driver->draw2DRectangle(bgcolor2, rect, NULL);
158 drawItemStack(driver, g_fontengine->getFont(), item, rect, NULL, gamedef);
161 //NOTE: selectitem = 0 -> no selected; selectitem 1-based
162 void Hud::drawItems(v2s32 upperleftpos, s32 itemcount, s32 offset,
163 InventoryList *mainlist, u16 selectitem, u16 direction)
165 #ifdef HAVE_TOUCHSCREENGUI
166 if ( (g_touchscreengui) && (offset == 0))
167 g_touchscreengui->resetHud();
170 s32 height = m_hotbar_imagesize + m_padding * 2;
171 s32 width = (itemcount - offset) * (m_hotbar_imagesize + m_padding * 2);
173 if (direction == HUD_DIR_TOP_BOTTOM || direction == HUD_DIR_BOTTOM_TOP) {
174 width = m_hotbar_imagesize + m_padding * 2;
175 height = (itemcount - offset) * (m_hotbar_imagesize + m_padding * 2);
178 // Position of upper left corner of bar
179 v2s32 pos = upperleftpos;
181 if (hotbar_image != player->hotbar_image) {
182 hotbar_image = player->hotbar_image;
183 if (hotbar_image != "")
184 use_hotbar_image = tsrc->isKnownSourceImage(hotbar_image);
186 use_hotbar_image = false;
189 if (hotbar_selected_image != player->hotbar_selected_image) {
190 hotbar_selected_image = player->hotbar_selected_image;
191 if (hotbar_selected_image != "")
192 use_hotbar_selected_image = tsrc->isKnownSourceImage(hotbar_selected_image);
194 use_hotbar_selected_image = false;
197 /* draw customized item background */
198 if (use_hotbar_image) {
199 core::rect<s32> imgrect2(-m_padding/2, -m_padding/2,
200 width+m_padding/2, height+m_padding/2);
201 core::rect<s32> rect2 = imgrect2 + pos;
202 video::ITexture *texture = tsrc->getTexture(hotbar_image);
203 core::dimension2di imgsize(texture->getOriginalSize());
204 draw2DImageFilterScaled(driver, texture, rect2,
205 core::rect<s32>(core::position2d<s32>(0,0), imgsize),
206 NULL, hbar_colors, true);
209 for (s32 i = offset; i < itemcount && (size_t)i < mainlist->getSize(); i++)
212 s32 fullimglen = m_hotbar_imagesize + m_padding * 2;
214 core::rect<s32> imgrect(0, 0, m_hotbar_imagesize, m_hotbar_imagesize);
217 case HUD_DIR_RIGHT_LEFT:
218 steppos = v2s32(-(m_padding + (i - offset) * fullimglen), m_padding);
220 case HUD_DIR_TOP_BOTTOM:
221 steppos = v2s32(m_padding, m_padding + (i - offset) * fullimglen);
223 case HUD_DIR_BOTTOM_TOP:
224 steppos = v2s32(m_padding, -(m_padding + (i - offset) * fullimglen));
227 steppos = v2s32(m_padding + (i - offset) * fullimglen, m_padding);
231 drawItem(mainlist->getItem(i), (imgrect + pos + steppos), (i +1) == selectitem );
233 #ifdef HAVE_TOUCHSCREENGUI
234 if (g_touchscreengui)
235 g_touchscreengui->registerHudItem(i, (imgrect + pos + steppos));
241 void Hud::drawLuaElements(v3s16 camera_offset) {
242 u32 text_height = g_fontengine->getTextHeight();
243 irr::gui::IGUIFont* font = g_fontengine->getFont();
244 for (size_t i = 0; i != player->maxHudId(); i++) {
245 HudElement *e = player->getHud(i);
249 v2s32 pos(floor(e->pos.X * (float) m_screensize.X + 0.5),
250 floor(e->pos.Y * (float) m_screensize.Y + 0.5));
252 case HUD_ELEM_IMAGE: {
253 video::ITexture *texture = tsrc->getTexture(e->text);
257 const video::SColor color(255, 255, 255, 255);
258 const video::SColor colors[] = {color, color, color, color};
259 core::dimension2di imgsize(texture->getOriginalSize());
260 v2s32 dstsize(imgsize.Width * e->scale.X,
261 imgsize.Height * e->scale.Y);
263 dstsize.X = m_screensize.X * (e->scale.X * -0.01);
265 dstsize.Y = m_screensize.Y * (e->scale.Y * -0.01);
266 v2s32 offset((e->align.X - 1.0) * dstsize.X / 2,
267 (e->align.Y - 1.0) * dstsize.Y / 2);
268 core::rect<s32> rect(0, 0, dstsize.X, dstsize.Y);
269 rect += pos + offset + v2s32(e->offset.X, e->offset.Y);
270 draw2DImageFilterScaled(driver, texture, rect,
271 core::rect<s32>(core::position2d<s32>(0,0), imgsize),
274 case HUD_ELEM_TEXT: {
275 video::SColor color(255, (e->number >> 16) & 0xFF,
276 (e->number >> 8) & 0xFF,
277 (e->number >> 0) & 0xFF);
278 core::rect<s32> size(0, 0, e->scale.X, text_height * e->scale.Y);
279 std::wstring text = narrow_to_wide(e->text);
280 core::dimension2d<u32> textsize = font->getDimension(text.c_str());
281 v2s32 offset((e->align.X - 1.0) * (textsize.Width / 2),
282 (e->align.Y - 1.0) * (textsize.Height / 2));
283 v2s32 offs(e->offset.X, e->offset.Y);
284 font->draw(text.c_str(), size + pos + offset + offs, color);
286 case HUD_ELEM_STATBAR: {
287 v2s32 offs(e->offset.X, e->offset.Y);
288 drawStatbar(pos, HUD_CORNER_UPPER, e->dir, e->text, e->number, offs, e->size);
290 case HUD_ELEM_INVENTORY: {
291 InventoryList *inv = inventory->getList(e->text);
292 drawItems(pos, e->number, 0, inv, e->item, e->dir);
294 case HUD_ELEM_WAYPOINT: {
295 v3f p_pos = player->getPosition() / BS;
296 v3f w_pos = e->world_pos * BS;
297 float distance = floor(10 * p_pos.getDistanceFrom(e->world_pos)) / 10;
298 scene::ICameraSceneNode* camera = smgr->getActiveCamera();
299 w_pos -= intToFloat(camera_offset, BS);
300 core::matrix4 trans = camera->getProjectionMatrix();
301 trans *= camera->getViewMatrix();
302 f32 transformed_pos[4] = { w_pos.X, w_pos.Y, w_pos.Z, 1.0f };
303 trans.multiplyWith1x4Matrix(transformed_pos);
304 if (transformed_pos[3] < 0)
306 f32 zDiv = transformed_pos[3] == 0.0f ? 1.0f :
307 core::reciprocal(transformed_pos[3]);
308 pos.X = m_screensize.X * (0.5 * transformed_pos[0] * zDiv + 0.5);
309 pos.Y = m_screensize.Y * (0.5 - transformed_pos[1] * zDiv * 0.5);
310 video::SColor color(255, (e->number >> 16) & 0xFF,
311 (e->number >> 8) & 0xFF,
312 (e->number >> 0) & 0xFF);
313 core::rect<s32> size(0, 0, 200, 2 * text_height);
314 std::wstring text = narrow_to_wide(e->name);
315 font->draw(text.c_str(), size + pos, color);
316 std::ostringstream os;
317 os<<distance<<e->text;
318 text = narrow_to_wide(os.str());
319 pos.Y += text_height;
320 font->draw(text.c_str(), size + pos, color);
323 infostream << "Hud::drawLuaElements: ignoring drawform " << e->type <<
324 " of hud element ID " << i << " due to unrecognized type" << std::endl;
330 void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture,
331 s32 count, v2s32 offset, v2s32 size)
333 const video::SColor color(255, 255, 255, 255);
334 const video::SColor colors[] = {color, color, color, color};
336 video::ITexture *stat_texture = tsrc->getTexture(texture);
340 core::dimension2di srcd(stat_texture->getOriginalSize());
341 core::dimension2di dstd;
342 if (size == v2s32()) {
345 double size_factor = g_settings->getFloat("hud_scaling") *
346 porting::getDisplayDensity();
347 dstd.Height = size.Y * size_factor;
348 dstd.Width = size.X * size_factor;
349 offset.X *= size_factor;
350 offset.Y *= size_factor;
354 if (corner & HUD_CORNER_LOWER)
361 case HUD_DIR_RIGHT_LEFT:
362 steppos = v2s32(-1, 0);
364 case HUD_DIR_TOP_BOTTOM:
365 steppos = v2s32(0, 1);
367 case HUD_DIR_BOTTOM_TOP:
368 steppos = v2s32(0, -1);
371 steppos = v2s32(1, 0);
373 steppos.X *= dstd.Width;
374 steppos.Y *= dstd.Height;
376 for (s32 i = 0; i < count / 2; i++)
378 core::rect<s32> srcrect(0, 0, srcd.Width, srcd.Height);
379 core::rect<s32> dstrect(0,0, dstd.Width, dstd.Height);
382 draw2DImageFilterScaled(driver, stat_texture, dstrect, srcrect, NULL, colors, true);
388 core::rect<s32> srcrect(0, 0, srcd.Width / 2, srcd.Height);
389 core::rect<s32> dstrect(0,0, dstd.Width / 2, dstd.Height);
392 draw2DImageFilterScaled(driver, stat_texture, dstrect, srcrect, NULL, colors, true);
397 void Hud::drawHotbar(u16 playeritem) {
399 v2s32 centerlowerpos(m_displaycenter.X, m_screensize.Y);
401 InventoryList *mainlist = inventory->getList("main");
402 if (mainlist == NULL) {
403 //silently ignore this we may not be initialized completely
407 s32 hotbar_itemcount = player->hud_hotbar_itemcount;
408 s32 width = hotbar_itemcount * (m_hotbar_imagesize + m_padding * 2);
409 v2s32 pos = centerlowerpos - v2s32(width / 2, m_hotbar_imagesize + m_padding * 3);
411 if ( (float) width / (float) porting::getWindowSize().X <=
412 g_settings->getFloat("hud_hotbar_max_width")) {
413 if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
414 drawItems(pos, hotbar_itemcount, 0, mainlist, playeritem + 1, 0);
420 v2s32 secondpos = pos;
421 pos = pos - v2s32(0, m_hotbar_imagesize + m_padding);
423 if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
424 drawItems(pos, hotbar_itemcount/2, 0, mainlist, playeritem + 1, 0);
425 drawItems(secondpos, hotbar_itemcount, hotbar_itemcount/2, mainlist, playeritem + 1, 0);
429 //////////////////////////// compatibility code to be removed //////////////
430 // this is ugly as hell but there's no other way to keep compatibility to
432 if ((player->hud_flags & HUD_FLAG_HEALTHBAR_VISIBLE)) {
433 drawStatbar(v2s32(floor(0.5 * (float)m_screensize.X + 0.5),
434 floor(1 * (float) m_screensize.Y + 0.5)),
435 HUD_CORNER_UPPER, 0, "heart.png",
436 player->hp, v2s32((-10*24)-25,-(48+24+10)), v2s32(24,24));
439 if ((player->hud_flags & HUD_FLAG_BREATHBAR_VISIBLE) &&
440 (player->getBreath() < 11)) {
441 drawStatbar(v2s32(floor(0.5 * (float)m_screensize.X + 0.5),
442 floor(1 * (float) m_screensize.Y + 0.5)),
443 HUD_CORNER_UPPER, 0, "bubble.png",
444 player->getBreath(), v2s32(25,-(48+24+10)), v2s32(24,24));
446 ////////////////////////////////////////////////////////////////////////////
450 void Hud::drawCrosshair() {
452 if (use_crosshair_image) {
453 video::ITexture *crosshair = tsrc->getTexture("crosshair.png");
454 v2u32 size = crosshair->getOriginalSize();
455 v2s32 lsize = v2s32(m_displaycenter.X - (size.X / 2),
456 m_displaycenter.Y - (size.Y / 2));
457 driver->draw2DImage(crosshair, lsize,
458 core::rect<s32>(0, 0, size.X, size.Y),
459 0, crosshair_argb, true);
461 driver->draw2DLine(m_displaycenter - v2s32(10, 0),
462 m_displaycenter + v2s32(10, 0), crosshair_argb);
463 driver->draw2DLine(m_displaycenter - v2s32(0, 10),
464 m_displaycenter + v2s32(0, 10), crosshair_argb);
469 void Hud::drawSelectionBoxes(std::vector<aabb3f> &hilightboxes) {
470 for (std::vector<aabb3f>::const_iterator
471 i = hilightboxes.begin();
472 i != hilightboxes.end(); i++) {
473 driver->draw3DBox(*i, selectionbox_argb);
478 void Hud::resizeHotbar() {
479 if (m_screensize != porting::getWindowSize()) {
480 m_hotbar_imagesize = floor(HOTBAR_IMAGE_SIZE * porting::getDisplayDensity() + 0.5);
481 m_hotbar_imagesize *= g_settings->getFloat("hud_scaling");
482 m_padding = m_hotbar_imagesize / 12;
483 m_screensize = porting::getWindowSize();
484 m_displaycenter = v2s32(m_screensize.X/2,m_screensize.Y/2);
488 void drawItemStack(video::IVideoDriver *driver,
490 const ItemStack &item,
491 const core::rect<s32> &rect,
492 const core::rect<s32> *clip,
498 const ItemDefinition &def = item.getDefinition(gamedef->idef());
499 video::ITexture *texture = gamedef->idef()->getInventoryTexture(def.name, gamedef);
501 // Draw the inventory texture
504 const video::SColor color(255,255,255,255);
505 const video::SColor colors[] = {color,color,color,color};
506 draw2DImageFilterScaled(driver, texture, rect,
507 core::rect<s32>(core::position2d<s32>(0,0),
508 core::dimension2di(texture->getOriginalSize())),
512 if(def.type == ITEM_TOOL && item.wear != 0)
514 // Draw a progressbar
515 float barheight = rect.getHeight()/16;
516 float barpad_x = rect.getWidth()/16;
517 float barpad_y = rect.getHeight()/16;
518 core::rect<s32> progressrect(
519 rect.UpperLeftCorner.X + barpad_x,
520 rect.LowerRightCorner.Y - barpad_y - barheight,
521 rect.LowerRightCorner.X - barpad_x,
522 rect.LowerRightCorner.Y - barpad_y);
524 // Shrink progressrect by amount of tool damage
525 float wear = item.wear / 65535.0;
527 wear * progressrect.UpperLeftCorner.X +
528 (1-wear) * progressrect.LowerRightCorner.X;
530 // Compute progressbar color
532 // wear = 0.5: yellow
534 video::SColor color(255,255,255,255);
535 int wear_i = MYMIN(floor(wear * 600), 511);
536 wear_i = MYMIN(wear_i + 10, 511);
538 color.set(255, wear_i, 255, 0);
540 color.set(255, 255, 511-wear_i, 0);
542 core::rect<s32> progressrect2 = progressrect;
543 progressrect2.LowerRightCorner.X = progressmid;
544 driver->draw2DRectangle(color, progressrect2, clip);
546 color = video::SColor(255,0,0,0);
547 progressrect2 = progressrect;
548 progressrect2.UpperLeftCorner.X = progressmid;
549 driver->draw2DRectangle(color, progressrect2, clip);
552 if(font != NULL && item.count >= 2)
554 // Get the item count as a string
555 std::string text = itos(item.count);
556 v2u32 dim = font->getDimension(narrow_to_wide(text).c_str());
557 v2s32 sdim(dim.X,dim.Y);
559 core::rect<s32> rect2(
560 /*rect.UpperLeftCorner,
561 core::dimension2d<u32>(rect.getWidth(), 15)*/
562 rect.LowerRightCorner - sdim,
566 video::SColor bgcolor(128,0,0,0);
567 driver->draw2DRectangle(bgcolor, rect2, clip);
569 video::SColor color(255,255,255,255);
570 font->draw(text.c_str(), rect2, color, false, false, clip);