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 ? IT_ROT_SELECTED : IT_ROT_NONE);
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 struct MeshTimeInfo {
494 void drawItemStack(video::IVideoDriver *driver,
496 const ItemStack &item,
497 const core::rect<s32> &rect,
498 const core::rect<s32> *clip,
500 ItemRotationKind rotation_kind)
502 static MeshTimeInfo rotation_time_infos[IT_ROT_NONE];
503 static bool enable_animations =
504 g_settings->getBool("inventory_items_animations");
507 if (rotation_kind < IT_ROT_NONE) {
508 rotation_time_infos[rotation_kind].mesh = NULL;
513 const ItemDefinition &def = item.getDefinition(gamedef->idef());
514 scene::IMesh* mesh = gamedef->idef()->getWieldMesh(def.name, gamedef);
517 driver->clearZBuffer();
519 if (rotation_kind < IT_ROT_NONE) {
520 MeshTimeInfo &ti = rotation_time_infos[rotation_kind];
521 if (mesh != ti.mesh) {
523 ti.time = getTimeMs();
525 delta = porting::getDeltaMs(ti.time, getTimeMs()) % 100000;
528 core::rect<s32> oldViewPort = driver->getViewPort();
529 core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION);
530 core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW);
531 core::matrix4 ProjMatrix;
532 ProjMatrix.buildProjectionMatrixOrthoLH(2, 2, -1, 100);
533 driver->setTransform(video::ETS_PROJECTION, ProjMatrix);
534 driver->setTransform(video::ETS_VIEW, ProjMatrix);
535 core::matrix4 matrix;
536 matrix.makeIdentity();
538 if (enable_animations) {
539 float timer_f = (float)delta / 5000.0;
540 matrix.setRotationDegrees(core::vector3df(0, 360 * timer_f, 0));
543 driver->setTransform(video::ETS_WORLD, matrix);
544 driver->setViewPort(rect);
546 u32 mc = mesh->getMeshBufferCount();
547 for (u32 j = 0; j < mc; ++j) {
548 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
549 video::SMaterial &material = buf->getMaterial();
550 material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
551 material.Lighting = false;
552 driver->setMaterial(material);
553 driver->drawMeshBuffer(buf);
556 driver->setTransform(video::ETS_VIEW, oldViewMat);
557 driver->setTransform(video::ETS_PROJECTION, oldProjMat);
558 driver->setViewPort(oldViewPort);
561 if(def.type == ITEM_TOOL && item.wear != 0)
563 // Draw a progressbar
564 float barheight = rect.getHeight()/16;
565 float barpad_x = rect.getWidth()/16;
566 float barpad_y = rect.getHeight()/16;
567 core::rect<s32> progressrect(
568 rect.UpperLeftCorner.X + barpad_x,
569 rect.LowerRightCorner.Y - barpad_y - barheight,
570 rect.LowerRightCorner.X - barpad_x,
571 rect.LowerRightCorner.Y - barpad_y);
573 // Shrink progressrect by amount of tool damage
574 float wear = item.wear / 65535.0;
576 wear * progressrect.UpperLeftCorner.X +
577 (1-wear) * progressrect.LowerRightCorner.X;
579 // Compute progressbar color
581 // wear = 0.5: yellow
583 video::SColor color(255,255,255,255);
584 int wear_i = MYMIN(floor(wear * 600), 511);
585 wear_i = MYMIN(wear_i + 10, 511);
587 color.set(255, wear_i, 255, 0);
589 color.set(255, 255, 511-wear_i, 0);
591 core::rect<s32> progressrect2 = progressrect;
592 progressrect2.LowerRightCorner.X = progressmid;
593 driver->draw2DRectangle(color, progressrect2, clip);
595 color = video::SColor(255,0,0,0);
596 progressrect2 = progressrect;
597 progressrect2.UpperLeftCorner.X = progressmid;
598 driver->draw2DRectangle(color, progressrect2, clip);
601 if(font != NULL && item.count >= 2)
603 // Get the item count as a string
604 std::string text = itos(item.count);
605 v2u32 dim = font->getDimension(utf8_to_wide(text).c_str());
606 v2s32 sdim(dim.X,dim.Y);
608 core::rect<s32> rect2(
609 /*rect.UpperLeftCorner,
610 core::dimension2d<u32>(rect.getWidth(), 15)*/
611 rect.LowerRightCorner - sdim,
615 video::SColor bgcolor(128,0,0,0);
616 driver->draw2DRectangle(bgcolor, rect2, clip);
618 video::SColor color(255,255,255,255);
619 font->draw(text.c_str(), rect2, color, false, false, clip);