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,
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,
159 gamedef, selected, false, false);
162 //NOTE: selectitem = 0 -> no selected; selectitem 1-based
163 void Hud::drawItems(v2s32 upperleftpos, s32 itemcount, s32 offset,
164 InventoryList *mainlist, u16 selectitem, u16 direction)
166 #ifdef HAVE_TOUCHSCREENGUI
167 if ( (g_touchscreengui) && (offset == 0))
168 g_touchscreengui->resetHud();
171 s32 height = m_hotbar_imagesize + m_padding * 2;
172 s32 width = (itemcount - offset) * (m_hotbar_imagesize + m_padding * 2);
174 if (direction == HUD_DIR_TOP_BOTTOM || direction == HUD_DIR_BOTTOM_TOP) {
175 width = m_hotbar_imagesize + m_padding * 2;
176 height = (itemcount - offset) * (m_hotbar_imagesize + m_padding * 2);
179 // Position of upper left corner of bar
180 v2s32 pos = upperleftpos;
182 if (hotbar_image != player->hotbar_image) {
183 hotbar_image = player->hotbar_image;
184 if (hotbar_image != "")
185 use_hotbar_image = tsrc->isKnownSourceImage(hotbar_image);
187 use_hotbar_image = false;
190 if (hotbar_selected_image != player->hotbar_selected_image) {
191 hotbar_selected_image = player->hotbar_selected_image;
192 if (hotbar_selected_image != "")
193 use_hotbar_selected_image = tsrc->isKnownSourceImage(hotbar_selected_image);
195 use_hotbar_selected_image = false;
198 /* draw customized item background */
199 if (use_hotbar_image) {
200 core::rect<s32> imgrect2(-m_padding/2, -m_padding/2,
201 width+m_padding/2, height+m_padding/2);
202 core::rect<s32> rect2 = imgrect2 + pos;
203 video::ITexture *texture = tsrc->getTexture(hotbar_image);
204 core::dimension2di imgsize(texture->getOriginalSize());
205 draw2DImageFilterScaled(driver, texture, rect2,
206 core::rect<s32>(core::position2d<s32>(0,0), imgsize),
207 NULL, hbar_colors, true);
210 for (s32 i = offset; i < itemcount && (size_t)i < mainlist->getSize(); i++)
213 s32 fullimglen = m_hotbar_imagesize + m_padding * 2;
215 core::rect<s32> imgrect(0, 0, m_hotbar_imagesize, m_hotbar_imagesize);
218 case HUD_DIR_RIGHT_LEFT:
219 steppos = v2s32(-(m_padding + (i - offset) * fullimglen), m_padding);
221 case HUD_DIR_TOP_BOTTOM:
222 steppos = v2s32(m_padding, m_padding + (i - offset) * fullimglen);
224 case HUD_DIR_BOTTOM_TOP:
225 steppos = v2s32(m_padding, -(m_padding + (i - offset) * fullimglen));
228 steppos = v2s32(m_padding + (i - offset) * fullimglen, m_padding);
232 drawItem(mainlist->getItem(i), (imgrect + pos + steppos), (i +1) == selectitem );
234 #ifdef HAVE_TOUCHSCREENGUI
235 if (g_touchscreengui)
236 g_touchscreengui->registerHudItem(i, (imgrect + pos + steppos));
242 void Hud::drawLuaElements(v3s16 camera_offset) {
243 u32 text_height = g_fontengine->getTextHeight();
244 irr::gui::IGUIFont* font = g_fontengine->getFont();
245 for (size_t i = 0; i != player->maxHudId(); i++) {
246 HudElement *e = player->getHud(i);
250 v2s32 pos(floor(e->pos.X * (float) m_screensize.X + 0.5),
251 floor(e->pos.Y * (float) m_screensize.Y + 0.5));
253 case HUD_ELEM_IMAGE: {
254 video::ITexture *texture = tsrc->getTexture(e->text);
258 const video::SColor color(255, 255, 255, 255);
259 const video::SColor colors[] = {color, color, color, color};
260 core::dimension2di imgsize(texture->getOriginalSize());
261 v2s32 dstsize(imgsize.Width * e->scale.X,
262 imgsize.Height * e->scale.Y);
264 dstsize.X = m_screensize.X * (e->scale.X * -0.01);
266 dstsize.Y = m_screensize.Y * (e->scale.Y * -0.01);
267 v2s32 offset((e->align.X - 1.0) * dstsize.X / 2,
268 (e->align.Y - 1.0) * dstsize.Y / 2);
269 core::rect<s32> rect(0, 0, dstsize.X, dstsize.Y);
270 rect += pos + offset + v2s32(e->offset.X, e->offset.Y);
271 draw2DImageFilterScaled(driver, texture, rect,
272 core::rect<s32>(core::position2d<s32>(0,0), imgsize),
275 case HUD_ELEM_TEXT: {
276 video::SColor color(255, (e->number >> 16) & 0xFF,
277 (e->number >> 8) & 0xFF,
278 (e->number >> 0) & 0xFF);
279 core::rect<s32> size(0, 0, e->scale.X, text_height * e->scale.Y);
280 std::wstring text = utf8_to_wide(e->text);
281 core::dimension2d<u32> textsize = font->getDimension(text.c_str());
282 v2s32 offset((e->align.X - 1.0) * (textsize.Width / 2),
283 (e->align.Y - 1.0) * (textsize.Height / 2));
284 v2s32 offs(e->offset.X, e->offset.Y);
285 font->draw(text.c_str(), size + pos + offset + offs, color);
287 case HUD_ELEM_STATBAR: {
288 v2s32 offs(e->offset.X, e->offset.Y);
289 drawStatbar(pos, HUD_CORNER_UPPER, e->dir, e->text, e->number, offs, e->size);
291 case HUD_ELEM_INVENTORY: {
292 InventoryList *inv = inventory->getList(e->text);
293 drawItems(pos, e->number, 0, inv, e->item, e->dir);
295 case HUD_ELEM_WAYPOINT: {
296 v3f p_pos = player->getPosition() / BS;
297 v3f w_pos = e->world_pos * BS;
298 float distance = floor(10 * p_pos.getDistanceFrom(e->world_pos)) / 10;
299 scene::ICameraSceneNode* camera = smgr->getActiveCamera();
300 w_pos -= intToFloat(camera_offset, BS);
301 core::matrix4 trans = camera->getProjectionMatrix();
302 trans *= camera->getViewMatrix();
303 f32 transformed_pos[4] = { w_pos.X, w_pos.Y, w_pos.Z, 1.0f };
304 trans.multiplyWith1x4Matrix(transformed_pos);
305 if (transformed_pos[3] < 0)
307 f32 zDiv = transformed_pos[3] == 0.0f ? 1.0f :
308 core::reciprocal(transformed_pos[3]);
309 pos.X = m_screensize.X * (0.5 * transformed_pos[0] * zDiv + 0.5);
310 pos.Y = m_screensize.Y * (0.5 - transformed_pos[1] * zDiv * 0.5);
311 video::SColor color(255, (e->number >> 16) & 0xFF,
312 (e->number >> 8) & 0xFF,
313 (e->number >> 0) & 0xFF);
314 core::rect<s32> size(0, 0, 200, 2 * text_height);
315 std::wstring text = utf8_to_wide(e->name);
316 font->draw(text.c_str(), size + pos, color);
317 std::ostringstream os;
318 os << distance << e->text;
319 text = utf8_to_wide(os.str());
320 pos.Y += text_height;
321 font->draw(text.c_str(), size + pos, color);
324 infostream << "Hud::drawLuaElements: ignoring drawform " << e->type <<
325 " of hud element ID " << i << " due to unrecognized type" << std::endl;
331 void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture,
332 s32 count, v2s32 offset, v2s32 size)
334 const video::SColor color(255, 255, 255, 255);
335 const video::SColor colors[] = {color, color, color, color};
337 video::ITexture *stat_texture = tsrc->getTexture(texture);
341 core::dimension2di srcd(stat_texture->getOriginalSize());
342 core::dimension2di dstd;
343 if (size == v2s32()) {
346 double size_factor = g_settings->getFloat("hud_scaling") *
347 porting::getDisplayDensity();
348 dstd.Height = size.Y * size_factor;
349 dstd.Width = size.X * size_factor;
350 offset.X *= size_factor;
351 offset.Y *= size_factor;
355 if (corner & HUD_CORNER_LOWER)
362 case HUD_DIR_RIGHT_LEFT:
363 steppos = v2s32(-1, 0);
365 case HUD_DIR_TOP_BOTTOM:
366 steppos = v2s32(0, 1);
368 case HUD_DIR_BOTTOM_TOP:
369 steppos = v2s32(0, -1);
372 steppos = v2s32(1, 0);
374 steppos.X *= dstd.Width;
375 steppos.Y *= dstd.Height;
377 for (s32 i = 0; i < count / 2; i++)
379 core::rect<s32> srcrect(0, 0, srcd.Width, srcd.Height);
380 core::rect<s32> dstrect(0,0, dstd.Width, dstd.Height);
383 draw2DImageFilterScaled(driver, stat_texture, dstrect, srcrect, NULL, colors, true);
389 core::rect<s32> srcrect(0, 0, srcd.Width / 2, srcd.Height);
390 core::rect<s32> dstrect(0,0, dstd.Width / 2, dstd.Height);
393 draw2DImageFilterScaled(driver, stat_texture, dstrect, srcrect, NULL, colors, true);
398 void Hud::drawHotbar(u16 playeritem) {
400 v2s32 centerlowerpos(m_displaycenter.X, m_screensize.Y);
402 InventoryList *mainlist = inventory->getList("main");
403 if (mainlist == NULL) {
404 //silently ignore this we may not be initialized completely
408 s32 hotbar_itemcount = player->hud_hotbar_itemcount;
409 s32 width = hotbar_itemcount * (m_hotbar_imagesize + m_padding * 2);
410 v2s32 pos = centerlowerpos - v2s32(width / 2, m_hotbar_imagesize + m_padding * 3);
412 if ( (float) width / (float) porting::getWindowSize().X <=
413 g_settings->getFloat("hud_hotbar_max_width")) {
414 if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
415 drawItems(pos, hotbar_itemcount, 0, mainlist, playeritem + 1, 0);
421 v2s32 secondpos = pos;
422 pos = pos - v2s32(0, m_hotbar_imagesize + m_padding);
424 if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
425 drawItems(pos, hotbar_itemcount/2, 0, mainlist, playeritem + 1, 0);
426 drawItems(secondpos, hotbar_itemcount, hotbar_itemcount/2, mainlist, playeritem + 1, 0);
430 //////////////////////////// compatibility code to be removed //////////////
431 // this is ugly as hell but there's no other way to keep compatibility to
433 if ((player->hud_flags & HUD_FLAG_HEALTHBAR_VISIBLE)) {
434 drawStatbar(v2s32(floor(0.5 * (float)m_screensize.X + 0.5),
435 floor(1 * (float) m_screensize.Y + 0.5)),
436 HUD_CORNER_UPPER, 0, "heart.png",
437 player->hp, v2s32((-10*24)-25,-(48+24+10)), v2s32(24,24));
440 if ((player->hud_flags & HUD_FLAG_BREATHBAR_VISIBLE) &&
441 (player->getBreath() < 11)) {
442 drawStatbar(v2s32(floor(0.5 * (float)m_screensize.X + 0.5),
443 floor(1 * (float) m_screensize.Y + 0.5)),
444 HUD_CORNER_UPPER, 0, "bubble.png",
445 player->getBreath(), v2s32(25,-(48+24+10)), v2s32(24,24));
447 ////////////////////////////////////////////////////////////////////////////
451 void Hud::drawCrosshair() {
453 if (use_crosshair_image) {
454 video::ITexture *crosshair = tsrc->getTexture("crosshair.png");
455 v2u32 size = crosshair->getOriginalSize();
456 v2s32 lsize = v2s32(m_displaycenter.X - (size.X / 2),
457 m_displaycenter.Y - (size.Y / 2));
458 driver->draw2DImage(crosshair, lsize,
459 core::rect<s32>(0, 0, size.X, size.Y),
460 0, crosshair_argb, true);
462 driver->draw2DLine(m_displaycenter - v2s32(10, 0),
463 m_displaycenter + v2s32(10, 0), crosshair_argb);
464 driver->draw2DLine(m_displaycenter - v2s32(0, 10),
465 m_displaycenter + v2s32(0, 10), crosshair_argb);
470 void Hud::drawSelectionBoxes(std::vector<aabb3f> &hilightboxes) {
471 for (std::vector<aabb3f>::const_iterator
472 i = hilightboxes.begin();
473 i != hilightboxes.end(); ++i) {
474 driver->draw3DBox(*i, selectionbox_argb);
479 void Hud::resizeHotbar() {
480 if (m_screensize != porting::getWindowSize()) {
481 m_hotbar_imagesize = floor(HOTBAR_IMAGE_SIZE * porting::getDisplayDensity() + 0.5);
482 m_hotbar_imagesize *= g_settings->getFloat("hud_scaling");
483 m_padding = m_hotbar_imagesize / 12;
484 m_screensize = porting::getWindowSize();
485 m_displaycenter = v2s32(m_screensize.X/2,m_screensize.Y/2);
489 void drawItemStack(video::IVideoDriver *driver,
491 const ItemStack &item,
492 const core::rect<s32> &rect,
493 const core::rect<s32> *clip,
499 static s32 hovered_time;
500 static s32 selected_time;
501 static s32 dragged_time;
502 static scene::IMesh *hovered_mesh;
503 static scene::IMesh *selected_mesh;
504 static scene::IMesh *dragged_mesh;
505 bool enable_animations =
506 g_settings->getBool("inventory_items_animations");
510 selected_mesh = NULL;
511 } else if (hovered) {
513 } else if (dragged) {
519 const ItemDefinition &def = item.getDefinition(gamedef->idef());
520 scene::IMesh* mesh = gamedef->idef()->getWieldMesh(def.name, gamedef);
523 driver->clearZBuffer();
526 if (mesh != selected_mesh) {
527 selected_mesh = mesh;
528 selected_time = getTimeMs();
530 delta = porting::getDeltaMs(selected_time, getTimeMs()) % 100000;
532 } else if (hovered) {
533 if (mesh != hovered_mesh) {
535 hovered_time = getTimeMs();
537 delta = porting::getDeltaMs(hovered_time, getTimeMs()) % 100000;
539 } else if (dragged) {
540 if (mesh != dragged_mesh) {
542 dragged_time = getTimeMs();
544 delta = porting::getDeltaMs(dragged_time, getTimeMs()) % 100000;
547 core::rect<s32> oldViewPort = driver->getViewPort();
548 core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION);
549 core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW);
550 core::matrix4 ProjMatrix;
551 ProjMatrix.buildProjectionMatrixOrthoLH(2, 2, -1, 100);
552 driver->setTransform(video::ETS_PROJECTION, ProjMatrix);
553 driver->setTransform(video::ETS_VIEW, ProjMatrix);
554 core::matrix4 matrix;
555 matrix.makeIdentity();
557 if (enable_animations) {
558 float timer_f = (float)delta / 5000.0;
559 matrix.setRotationDegrees(core::vector3df(0, 360 * timer_f, 0));
562 driver->setTransform(video::ETS_WORLD, matrix);
563 driver->setViewPort(rect);
565 u32 mc = mesh->getMeshBufferCount();
566 for (u32 j = 0; j < mc; ++j) {
567 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
568 video::SMaterial &material = buf->getMaterial();
569 material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
570 material.Lighting = false;
571 driver->setMaterial(material);
572 driver->drawMeshBuffer(buf);
575 driver->setTransform(video::ETS_VIEW, oldViewMat);
576 driver->setTransform(video::ETS_PROJECTION, oldProjMat);
577 driver->setViewPort(oldViewPort);
580 if(def.type == ITEM_TOOL && item.wear != 0)
582 // Draw a progressbar
583 float barheight = rect.getHeight()/16;
584 float barpad_x = rect.getWidth()/16;
585 float barpad_y = rect.getHeight()/16;
586 core::rect<s32> progressrect(
587 rect.UpperLeftCorner.X + barpad_x,
588 rect.LowerRightCorner.Y - barpad_y - barheight,
589 rect.LowerRightCorner.X - barpad_x,
590 rect.LowerRightCorner.Y - barpad_y);
592 // Shrink progressrect by amount of tool damage
593 float wear = item.wear / 65535.0;
595 wear * progressrect.UpperLeftCorner.X +
596 (1-wear) * progressrect.LowerRightCorner.X;
598 // Compute progressbar color
600 // wear = 0.5: yellow
602 video::SColor color(255,255,255,255);
603 int wear_i = MYMIN(floor(wear * 600), 511);
604 wear_i = MYMIN(wear_i + 10, 511);
606 color.set(255, wear_i, 255, 0);
608 color.set(255, 255, 511-wear_i, 0);
610 core::rect<s32> progressrect2 = progressrect;
611 progressrect2.LowerRightCorner.X = progressmid;
612 driver->draw2DRectangle(color, progressrect2, clip);
614 color = video::SColor(255,0,0,0);
615 progressrect2 = progressrect;
616 progressrect2.UpperLeftCorner.X = progressmid;
617 driver->draw2DRectangle(color, progressrect2, clip);
620 if(font != NULL && item.count >= 2)
622 // Get the item count as a string
623 std::string text = itos(item.count);
624 v2u32 dim = font->getDimension(utf8_to_wide(text).c_str());
625 v2s32 sdim(dim.X,dim.Y);
627 core::rect<s32> rect2(
628 /*rect.UpperLeftCorner,
629 core::dimension2d<u32>(rect.getWidth(), 15)*/
630 rect.LowerRightCorner - sdim,
634 video::SColor bgcolor(128,0,0,0);
635 driver->draw2DRectangle(bgcolor, rect2, clip);
637 video::SColor color(255,255,255,255);
638 font->draw(text.c_str(), rect2, color, false, false, clip);