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.
24 #include "util/numeric.h"
28 #include "inventory.h"
29 #include "client/tile.h"
30 #include "localplayer.h"
33 #include "fontengine.h"
34 #include "guiscalingfilter.h"
35 #include <IGUIStaticText.h>
37 #ifdef HAVE_TOUCHSCREENGUI
38 #include "touchscreengui.h"
41 Hud::Hud(video::IVideoDriver *driver, scene::ISceneManager* smgr,
42 gui::IGUIEnvironment* guienv, IGameDef *gamedef, LocalPlayer *player,
43 Inventory *inventory) {
44 this->driver = driver;
46 this->guienv = guienv;
47 this->gamedef = gamedef;
48 this->player = player;
49 this->inventory = inventory;
51 m_screensize = v2u32(0, 0);
52 m_displaycenter = v2s32(0, 0);
53 m_hotbar_imagesize = floor(HOTBAR_IMAGE_SIZE * porting::getDisplayDensity() + 0.5);
54 m_hotbar_imagesize *= g_settings->getFloat("hud_scaling");
55 m_padding = m_hotbar_imagesize / 12;
57 const video::SColor hbar_color(255, 255, 255, 255);
58 for (unsigned int i=0; i < 4; i++ ){
59 hbar_colors[i] = hbar_color;
62 tsrc = gamedef->getTextureSource();
64 v3f crosshair_color = g_settings->getV3F("crosshair_color");
65 u32 cross_r = rangelim(myround(crosshair_color.X), 0, 255);
66 u32 cross_g = rangelim(myround(crosshair_color.Y), 0, 255);
67 u32 cross_b = rangelim(myround(crosshair_color.Z), 0, 255);
68 u32 cross_a = rangelim(g_settings->getS32("crosshair_alpha"), 0, 255);
69 crosshair_argb = video::SColor(cross_a, cross_r, cross_g, cross_b);
71 v3f selectionbox_color = g_settings->getV3F("selectionbox_color");
72 u32 sbox_r = rangelim(myround(selectionbox_color.X), 0, 255);
73 u32 sbox_g = rangelim(myround(selectionbox_color.Y), 0, 255);
74 u32 sbox_b = rangelim(myround(selectionbox_color.Z), 0, 255);
75 selectionbox_argb = video::SColor(255, sbox_r, sbox_g, sbox_b);
77 use_crosshair_image = tsrc->isKnownSourceImage("crosshair.png");
80 use_hotbar_image = false;
81 hotbar_selected_image = "";
82 use_hotbar_selected_image = false;
85 void Hud::drawItem(const ItemStack &item, const core::rect<s32>& rect, bool selected) {
88 /* draw hihlighting around selected item */
89 if (use_hotbar_selected_image) {
90 core::rect<s32> imgrect2 = rect;
91 imgrect2.UpperLeftCorner.X -= (m_padding*2);
92 imgrect2.UpperLeftCorner.Y -= (m_padding*2);
93 imgrect2.LowerRightCorner.X += (m_padding*2);
94 imgrect2.LowerRightCorner.Y += (m_padding*2);
95 video::ITexture *texture = tsrc->getTexture(hotbar_selected_image);
96 core::dimension2di imgsize(texture->getOriginalSize());
97 draw2DImageFilterScaled(driver, texture, imgrect2,
98 core::rect<s32>(core::position2d<s32>(0,0), imgsize),
99 NULL, hbar_colors, true);
101 video::SColor c_outside(255,255,0,0);
102 //video::SColor c_outside(255,0,0,0);
103 //video::SColor c_inside(255,192,192,192);
104 s32 x1 = rect.UpperLeftCorner.X;
105 s32 y1 = rect.UpperLeftCorner.Y;
106 s32 x2 = rect.LowerRightCorner.X;
107 s32 y2 = rect.LowerRightCorner.Y;
108 // Black base borders
109 driver->draw2DRectangle(c_outside,
111 v2s32(x1 - m_padding, y1 - m_padding),
112 v2s32(x2 + m_padding, y1)
114 driver->draw2DRectangle(c_outside,
116 v2s32(x1 - m_padding, y2),
117 v2s32(x2 + m_padding, y2 + m_padding)
119 driver->draw2DRectangle(c_outside,
121 v2s32(x1 - m_padding, y1),
124 driver->draw2DRectangle(c_outside,
127 v2s32(x2 + m_padding, y2)
129 /*// Light inside borders
130 driver->draw2DRectangle(c_inside,
132 v2s32(x1 - padding/2, y1 - padding/2),
133 v2s32(x2 + padding/2, y1)
135 driver->draw2DRectangle(c_inside,
137 v2s32(x1 - padding/2, y2),
138 v2s32(x2 + padding/2, y2 + padding/2)
140 driver->draw2DRectangle(c_inside,
142 v2s32(x1 - padding/2, y1),
145 driver->draw2DRectangle(c_inside,
148 v2s32(x2 + padding/2, y2)
154 video::SColor bgcolor2(128, 0, 0, 0);
155 if (!use_hotbar_image)
156 driver->draw2DRectangle(bgcolor2, rect, NULL);
157 drawItemStack(driver, g_fontengine->getFont(), item, rect, NULL, gamedef);
160 //NOTE: selectitem = 0 -> no selected; selectitem 1-based
161 void Hud::drawItems(v2s32 upperleftpos, s32 itemcount, s32 offset,
162 InventoryList *mainlist, u16 selectitem, u16 direction)
164 #ifdef HAVE_TOUCHSCREENGUI
165 if ( (g_touchscreengui) && (offset == 0))
166 g_touchscreengui->resetHud();
169 s32 height = m_hotbar_imagesize + m_padding * 2;
170 s32 width = (itemcount - offset) * (m_hotbar_imagesize + m_padding * 2);
172 if (direction == HUD_DIR_TOP_BOTTOM || direction == HUD_DIR_BOTTOM_TOP) {
173 width = m_hotbar_imagesize + m_padding * 2;
174 height = (itemcount - offset) * (m_hotbar_imagesize + m_padding * 2);
177 // Position of upper left corner of bar
178 v2s32 pos = upperleftpos;
180 if (hotbar_image != player->hotbar_image) {
181 hotbar_image = player->hotbar_image;
182 if (hotbar_image != "")
183 use_hotbar_image = tsrc->isKnownSourceImage(hotbar_image);
185 use_hotbar_image = false;
188 if (hotbar_selected_image != player->hotbar_selected_image) {
189 hotbar_selected_image = player->hotbar_selected_image;
190 if (hotbar_selected_image != "")
191 use_hotbar_selected_image = tsrc->isKnownSourceImage(hotbar_selected_image);
193 use_hotbar_selected_image = false;
196 /* draw customized item background */
197 if (use_hotbar_image) {
198 core::rect<s32> imgrect2(-m_padding/2, -m_padding/2,
199 width+m_padding/2, height+m_padding/2);
200 core::rect<s32> rect2 = imgrect2 + pos;
201 video::ITexture *texture = tsrc->getTexture(hotbar_image);
202 core::dimension2di imgsize(texture->getOriginalSize());
203 draw2DImageFilterScaled(driver, texture, rect2,
204 core::rect<s32>(core::position2d<s32>(0,0), imgsize),
205 NULL, hbar_colors, true);
208 for (s32 i = offset; i < itemcount && (size_t)i < mainlist->getSize(); i++)
211 s32 fullimglen = m_hotbar_imagesize + m_padding * 2;
213 core::rect<s32> imgrect(0, 0, m_hotbar_imagesize, m_hotbar_imagesize);
216 case HUD_DIR_RIGHT_LEFT:
217 steppos = v2s32(-(m_padding + (i - offset) * fullimglen), m_padding);
219 case HUD_DIR_TOP_BOTTOM:
220 steppos = v2s32(m_padding, m_padding + (i - offset) * fullimglen);
222 case HUD_DIR_BOTTOM_TOP:
223 steppos = v2s32(m_padding, -(m_padding + (i - offset) * fullimglen));
226 steppos = v2s32(m_padding + (i - offset) * fullimglen, m_padding);
230 drawItem(mainlist->getItem(i), (imgrect + pos + steppos), (i +1) == selectitem );
232 #ifdef HAVE_TOUCHSCREENGUI
233 if (g_touchscreengui)
234 g_touchscreengui->registerHudItem(i, (imgrect + pos + steppos));
240 void Hud::drawLuaElements(v3s16 camera_offset) {
241 u32 text_height = g_fontengine->getTextHeight();
242 irr::gui::IGUIFont* font = g_fontengine->getFont();
243 for (size_t i = 0; i != player->maxHudId(); i++) {
244 HudElement *e = player->getHud(i);
248 v2s32 pos(floor(e->pos.X * (float) m_screensize.X + 0.5),
249 floor(e->pos.Y * (float) m_screensize.Y + 0.5));
251 case HUD_ELEM_IMAGE: {
252 video::ITexture *texture = tsrc->getTexture(e->text);
256 const video::SColor color(255, 255, 255, 255);
257 const video::SColor colors[] = {color, color, color, color};
258 core::dimension2di imgsize(texture->getOriginalSize());
259 v2s32 dstsize(imgsize.Width * e->scale.X,
260 imgsize.Height * e->scale.Y);
262 dstsize.X = m_screensize.X * (e->scale.X * -0.01);
264 dstsize.Y = m_screensize.Y * (e->scale.Y * -0.01);
265 v2s32 offset((e->align.X - 1.0) * dstsize.X / 2,
266 (e->align.Y - 1.0) * dstsize.Y / 2);
267 core::rect<s32> rect(0, 0, dstsize.X, dstsize.Y);
268 rect += pos + offset + v2s32(e->offset.X, e->offset.Y);
269 draw2DImageFilterScaled(driver, texture, rect,
270 core::rect<s32>(core::position2d<s32>(0,0), imgsize),
273 case HUD_ELEM_TEXT: {
274 video::SColor color(255, (e->number >> 16) & 0xFF,
275 (e->number >> 8) & 0xFF,
276 (e->number >> 0) & 0xFF);
277 core::rect<s32> size(0, 0, e->scale.X, text_height * e->scale.Y);
278 std::wstring text = narrow_to_wide(e->text);
279 core::dimension2d<u32> textsize = font->getDimension(text.c_str());
280 v2s32 offset((e->align.X - 1.0) * (textsize.Width / 2),
281 (e->align.Y - 1.0) * (textsize.Height / 2));
282 v2s32 offs(e->offset.X, e->offset.Y);
283 font->draw(text.c_str(), size + pos + offset + offs, color);
285 case HUD_ELEM_STATBAR: {
286 v2s32 offs(e->offset.X, e->offset.Y);
287 drawStatbar(pos, HUD_CORNER_UPPER, e->dir, e->text, e->number, offs, e->size);
289 case HUD_ELEM_INVENTORY: {
290 InventoryList *inv = inventory->getList(e->text);
291 drawItems(pos, e->number, 0, inv, e->item, e->dir);
293 case HUD_ELEM_WAYPOINT: {
294 v3f p_pos = player->getPosition() / BS;
295 v3f w_pos = e->world_pos * BS;
296 float distance = floor(10 * p_pos.getDistanceFrom(e->world_pos)) / 10;
297 scene::ICameraSceneNode* camera = smgr->getActiveCamera();
298 w_pos -= intToFloat(camera_offset, BS);
299 core::matrix4 trans = camera->getProjectionMatrix();
300 trans *= camera->getViewMatrix();
301 f32 transformed_pos[4] = { w_pos.X, w_pos.Y, w_pos.Z, 1.0f };
302 trans.multiplyWith1x4Matrix(transformed_pos);
303 if (transformed_pos[3] < 0)
305 f32 zDiv = transformed_pos[3] == 0.0f ? 1.0f :
306 core::reciprocal(transformed_pos[3]);
307 pos.X = m_screensize.X * (0.5 * transformed_pos[0] * zDiv + 0.5);
308 pos.Y = m_screensize.Y * (0.5 - transformed_pos[1] * zDiv * 0.5);
309 video::SColor color(255, (e->number >> 16) & 0xFF,
310 (e->number >> 8) & 0xFF,
311 (e->number >> 0) & 0xFF);
312 core::rect<s32> size(0, 0, 200, 2 * text_height);
313 std::wstring text = narrow_to_wide(e->name);
314 font->draw(text.c_str(), size + pos, color);
315 std::ostringstream os;
316 os<<distance<<e->text;
317 text = narrow_to_wide(os.str());
318 pos.Y += text_height;
319 font->draw(text.c_str(), size + pos, color);
322 infostream << "Hud::drawLuaElements: ignoring drawform " << e->type <<
323 " of hud element ID " << i << " due to unrecognized type" << std::endl;
329 void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture,
330 s32 count, v2s32 offset, v2s32 size)
332 const video::SColor color(255, 255, 255, 255);
333 const video::SColor colors[] = {color, color, color, color};
335 video::ITexture *stat_texture = tsrc->getTexture(texture);
339 core::dimension2di srcd(stat_texture->getOriginalSize());
340 core::dimension2di dstd;
341 if (size == v2s32()) {
344 double size_factor = g_settings->getFloat("hud_scaling") *
345 porting::getDisplayDensity();
346 dstd.Height = size.Y * size_factor;
347 dstd.Width = size.X * size_factor;
348 offset.X *= size_factor;
349 offset.Y *= size_factor;
353 if (corner & HUD_CORNER_LOWER)
360 case HUD_DIR_RIGHT_LEFT:
361 steppos = v2s32(-1, 0);
363 case HUD_DIR_TOP_BOTTOM:
364 steppos = v2s32(0, 1);
366 case HUD_DIR_BOTTOM_TOP:
367 steppos = v2s32(0, -1);
370 steppos = v2s32(1, 0);
372 steppos.X *= dstd.Width;
373 steppos.Y *= dstd.Height;
375 for (s32 i = 0; i < count / 2; i++)
377 core::rect<s32> srcrect(0, 0, srcd.Width, srcd.Height);
378 core::rect<s32> dstrect(0,0, dstd.Width, dstd.Height);
381 draw2DImageFilterScaled(driver, stat_texture, dstrect, srcrect, NULL, colors, true);
387 core::rect<s32> srcrect(0, 0, srcd.Width / 2, srcd.Height);
388 core::rect<s32> dstrect(0,0, dstd.Width / 2, dstd.Height);
391 draw2DImageFilterScaled(driver, stat_texture, dstrect, srcrect, NULL, colors, true);
396 void Hud::drawHotbar(u16 playeritem) {
398 v2s32 centerlowerpos(m_displaycenter.X, m_screensize.Y);
400 InventoryList *mainlist = inventory->getList("main");
401 if (mainlist == NULL) {
402 //silently ignore this we may not be initialized completely
406 s32 hotbar_itemcount = player->hud_hotbar_itemcount;
407 s32 width = hotbar_itemcount * (m_hotbar_imagesize + m_padding * 2);
408 v2s32 pos = centerlowerpos - v2s32(width / 2, m_hotbar_imagesize + m_padding * 3);
410 if ( (float) width / (float) porting::getWindowSize().X <=
411 g_settings->getFloat("hud_hotbar_max_width")) {
412 if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
413 drawItems(pos, hotbar_itemcount, 0, mainlist, playeritem + 1, 0);
419 v2s32 secondpos = pos;
420 pos = pos - v2s32(0, m_hotbar_imagesize + m_padding);
422 if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
423 drawItems(pos, hotbar_itemcount/2, 0, mainlist, playeritem + 1, 0);
424 drawItems(secondpos, hotbar_itemcount, hotbar_itemcount/2, mainlist, playeritem + 1, 0);
428 //////////////////////////// compatibility code to be removed //////////////
429 // this is ugly as hell but there's no other way to keep compatibility to
431 if ((player->hud_flags & HUD_FLAG_HEALTHBAR_VISIBLE)) {
432 drawStatbar(v2s32(floor(0.5 * (float)m_screensize.X + 0.5),
433 floor(1 * (float) m_screensize.Y + 0.5)),
434 HUD_CORNER_UPPER, 0, "heart.png",
435 player->hp, v2s32((-10*24)-25,-(48+24+10)), v2s32(24,24));
438 if ((player->hud_flags & HUD_FLAG_BREATHBAR_VISIBLE) &&
439 (player->getBreath() < 11)) {
440 drawStatbar(v2s32(floor(0.5 * (float)m_screensize.X + 0.5),
441 floor(1 * (float) m_screensize.Y + 0.5)),
442 HUD_CORNER_UPPER, 0, "bubble.png",
443 player->getBreath(), v2s32(25,-(48+24+10)), v2s32(24,24));
445 ////////////////////////////////////////////////////////////////////////////
449 void Hud::drawCrosshair() {
451 if (use_crosshair_image) {
452 video::ITexture *crosshair = tsrc->getTexture("crosshair.png");
453 v2u32 size = crosshair->getOriginalSize();
454 v2s32 lsize = v2s32(m_displaycenter.X - (size.X / 2),
455 m_displaycenter.Y - (size.Y / 2));
456 driver->draw2DImage(crosshair, lsize,
457 core::rect<s32>(0, 0, size.X, size.Y),
458 0, crosshair_argb, true);
460 driver->draw2DLine(m_displaycenter - v2s32(10, 0),
461 m_displaycenter + v2s32(10, 0), crosshair_argb);
462 driver->draw2DLine(m_displaycenter - v2s32(0, 10),
463 m_displaycenter + v2s32(0, 10), crosshair_argb);
468 void Hud::drawSelectionBoxes(std::vector<aabb3f> &hilightboxes) {
469 for (std::vector<aabb3f>::const_iterator
470 i = hilightboxes.begin();
471 i != hilightboxes.end(); i++) {
472 driver->draw3DBox(*i, selectionbox_argb);
477 void Hud::resizeHotbar() {
478 if (m_screensize != porting::getWindowSize()) {
479 m_hotbar_imagesize = floor(HOTBAR_IMAGE_SIZE * porting::getDisplayDensity() + 0.5);
480 m_hotbar_imagesize *= g_settings->getFloat("hud_scaling");
481 m_padding = m_hotbar_imagesize / 12;
482 m_screensize = porting::getWindowSize();
483 m_displaycenter = v2s32(m_screensize.X/2,m_screensize.Y/2);
487 void drawItemStack(video::IVideoDriver *driver,
489 const ItemStack &item,
490 const core::rect<s32> &rect,
491 const core::rect<s32> *clip,
497 const ItemDefinition &def = item.getDefinition(gamedef->idef());
498 video::ITexture *texture = gamedef->idef()->getInventoryTexture(def.name, gamedef);
500 // Draw the inventory texture
503 const video::SColor color(255,255,255,255);
504 const video::SColor colors[] = {color,color,color,color};
505 draw2DImageFilterScaled(driver, texture, rect,
506 core::rect<s32>(core::position2d<s32>(0,0),
507 core::dimension2di(texture->getOriginalSize())),
511 if(def.type == ITEM_TOOL && item.wear != 0)
513 // Draw a progressbar
514 float barheight = rect.getHeight()/16;
515 float barpad_x = rect.getWidth()/16;
516 float barpad_y = rect.getHeight()/16;
517 core::rect<s32> progressrect(
518 rect.UpperLeftCorner.X + barpad_x,
519 rect.LowerRightCorner.Y - barpad_y - barheight,
520 rect.LowerRightCorner.X - barpad_x,
521 rect.LowerRightCorner.Y - barpad_y);
523 // Shrink progressrect by amount of tool damage
524 float wear = item.wear / 65535.0;
526 wear * progressrect.UpperLeftCorner.X +
527 (1-wear) * progressrect.LowerRightCorner.X;
529 // Compute progressbar color
531 // wear = 0.5: yellow
533 video::SColor color(255,255,255,255);
534 int wear_i = MYMIN(floor(wear * 600), 511);
535 wear_i = MYMIN(wear_i + 10, 511);
537 color.set(255, wear_i, 255, 0);
539 color.set(255, 255, 511-wear_i, 0);
541 core::rect<s32> progressrect2 = progressrect;
542 progressrect2.LowerRightCorner.X = progressmid;
543 driver->draw2DRectangle(color, progressrect2, clip);
545 color = video::SColor(255,0,0,0);
546 progressrect2 = progressrect;
547 progressrect2.UpperLeftCorner.X = progressmid;
548 driver->draw2DRectangle(color, progressrect2, clip);
551 if(font != NULL && item.count >= 2)
553 // Get the item count as a string
554 std::string text = itos(item.count);
555 v2u32 dim = font->getDimension(narrow_to_wide(text).c_str());
556 v2s32 sdim(dim.X,dim.Y);
558 core::rect<s32> rect2(
559 /*rect.UpperLeftCorner,
560 core::dimension2d<u32>(rect.getWidth(), 15)*/
561 rect.LowerRightCorner - sdim,
565 video::SColor bgcolor(128,0,0,0);
566 driver->draw2DRectangle(bgcolor, rect2, clip);
568 video::SColor color(255,255,255,255);
569 font->draw(text.c_str(), rect2, color, false, false, clip);