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"
31 #include "localplayer.h"
34 #include <IGUIStaticText.h>
37 Hud::Hud(video::IVideoDriver *driver, scene::ISceneManager* smgr,
38 gui::IGUIEnvironment* guienv, gui::IGUIFont *font,
39 u32 text_height, IGameDef *gamedef,
40 LocalPlayer *player, Inventory *inventory) {
41 this->driver = driver;
43 this->guienv = guienv;
45 this->text_height = text_height;
46 this->gamedef = gamedef;
47 this->player = player;
48 this->inventory = inventory;
50 m_screensize = v2u32(0, 0);
51 m_displaycenter = v2s32(0, 0);
52 m_hotbar_imagesize = floor(HOTBAR_IMAGE_SIZE * porting::getDisplayDensity() + 0.5);
53 m_padding = m_hotbar_imagesize / 12;
55 const video::SColor hbar_color(255, 255, 255, 255);
56 for (unsigned int i=0; i < 4; i++ ){
57 hbar_colors[i] = hbar_color;
60 tsrc = gamedef->getTextureSource();
62 v3f crosshair_color = g_settings->getV3F("crosshair_color");
63 u32 cross_r = rangelim(myround(crosshair_color.X), 0, 255);
64 u32 cross_g = rangelim(myround(crosshair_color.Y), 0, 255);
65 u32 cross_b = rangelim(myround(crosshair_color.Z), 0, 255);
66 u32 cross_a = rangelim(g_settings->getS32("crosshair_alpha"), 0, 255);
67 crosshair_argb = video::SColor(cross_a, cross_r, cross_g, cross_b);
69 v3f selectionbox_color = g_settings->getV3F("selectionbox_color");
70 u32 sbox_r = rangelim(myround(selectionbox_color.X), 0, 255);
71 u32 sbox_g = rangelim(myround(selectionbox_color.Y), 0, 255);
72 u32 sbox_b = rangelim(myround(selectionbox_color.Z), 0, 255);
73 selectionbox_argb = video::SColor(255, sbox_r, sbox_g, sbox_b);
75 use_crosshair_image = tsrc->isKnownSourceImage("crosshair.png");
78 use_hotbar_image = false;
79 hotbar_selected_image = "";
80 use_hotbar_selected_image = false;
83 void Hud::drawItem(const ItemStack &item, const core::rect<s32>& rect, bool selected) {
86 if (use_hotbar_selected_image) {
87 core::rect<s32> imgrect2 = rect;
88 imgrect2.UpperLeftCorner.X -= m_padding;
89 imgrect2.UpperLeftCorner.Y -= m_padding;
90 imgrect2.LowerRightCorner.X += m_padding;
91 imgrect2.LowerRightCorner.Y += m_padding;
92 video::ITexture *texture = tsrc->getTexture(hotbar_selected_image);
93 core::dimension2di imgsize(texture->getOriginalSize());
94 driver->draw2DImage(texture, imgrect2,
95 core::rect<s32>(core::position2d<s32>(0,0), imgsize),
96 NULL, hbar_colors, true);
98 video::SColor c_outside(255,255,0,0);
99 //video::SColor c_outside(255,0,0,0);
100 //video::SColor c_inside(255,192,192,192);
101 s32 x1 = rect.UpperLeftCorner.X;
102 s32 y1 = rect.UpperLeftCorner.Y;
103 s32 x2 = rect.LowerRightCorner.X;
104 s32 y2 = rect.LowerRightCorner.Y;
105 // Black base borders
106 driver->draw2DRectangle(c_outside,
108 v2s32(x1 - m_padding, y1 - m_padding),
109 v2s32(x2 + m_padding, y1)
111 driver->draw2DRectangle(c_outside,
113 v2s32(x1 - m_padding, y2),
114 v2s32(x2 + m_padding, y2 + m_padding)
116 driver->draw2DRectangle(c_outside,
118 v2s32(x1 - m_padding, y1),
121 driver->draw2DRectangle(c_outside,
124 v2s32(x2 + m_padding, y2)
126 /*// Light inside borders
127 driver->draw2DRectangle(c_inside,
129 v2s32(x1 - padding/2, y1 - padding/2),
130 v2s32(x2 + padding/2, y1)
132 driver->draw2DRectangle(c_inside,
134 v2s32(x1 - padding/2, y2),
135 v2s32(x2 + padding/2, y2 + padding/2)
137 driver->draw2DRectangle(c_inside,
139 v2s32(x1 - padding/2, y1),
142 driver->draw2DRectangle(c_inside,
145 v2s32(x2 + padding/2, y2)
151 video::SColor bgcolor2(128, 0, 0, 0);
152 if (!use_hotbar_image)
153 driver->draw2DRectangle(bgcolor2, rect, NULL);
154 drawItemStack(driver, font, item, rect, NULL, gamedef);
157 //NOTE: selectitem = 0 -> no selected; selectitem 1-based
158 void Hud::drawItems(v2s32 upperleftpos, s32 itemcount, s32 offset,
159 InventoryList *mainlist, u16 selectitem, u16 direction)
161 s32 height = m_hotbar_imagesize + m_padding * 2;
162 s32 width = (itemcount - offset) * (m_hotbar_imagesize + m_padding * 2);
164 if (direction == HUD_DIR_TOP_BOTTOM || direction == HUD_DIR_BOTTOM_TOP) {
165 width = m_hotbar_imagesize + m_padding * 2;
166 height = (itemcount - offset) * (m_hotbar_imagesize + m_padding * 2);
169 // Position of upper left corner of bar
170 v2s32 pos = upperleftpos;
172 if (hotbar_image != player->hotbar_image) {
173 hotbar_image = player->hotbar_image;
174 if (hotbar_image != "")
175 use_hotbar_image = tsrc->isKnownSourceImage(hotbar_image);
177 use_hotbar_image = false;
180 if (hotbar_selected_image != player->hotbar_selected_image) {
181 hotbar_selected_image = player->hotbar_selected_image;
182 if (hotbar_selected_image != "")
183 use_hotbar_selected_image = tsrc->isKnownSourceImage(hotbar_selected_image);
185 use_hotbar_selected_image = false;
188 if (use_hotbar_image) {
189 core::rect<s32> imgrect2(-m_padding/2, -m_padding/2, width+m_padding/2, height+m_padding/2);
190 core::rect<s32> rect2 = imgrect2 + pos;
191 video::ITexture *texture = tsrc->getTexture(hotbar_image);
192 core::dimension2di imgsize(texture->getOriginalSize());
193 driver->draw2DImage(texture, rect2,
194 core::rect<s32>(core::position2d<s32>(0,0), imgsize),
195 NULL, hbar_colors, true);
198 for (s32 i = offset; i < itemcount && (size_t)i < mainlist->getSize(); i++)
201 s32 fullimglen = m_hotbar_imagesize + m_padding * 2;
203 core::rect<s32> imgrect(0, 0, m_hotbar_imagesize, m_hotbar_imagesize);
206 case HUD_DIR_RIGHT_LEFT:
207 steppos = v2s32(-(m_padding + (i - offset) * fullimglen), m_padding);
209 case HUD_DIR_TOP_BOTTOM:
210 steppos = v2s32(m_padding, m_padding + (i - offset) * fullimglen);
212 case HUD_DIR_BOTTOM_TOP:
213 steppos = v2s32(m_padding, -(m_padding + (i - offset) * fullimglen));
216 steppos = v2s32(m_padding + (i - offset) * fullimglen, m_padding);
220 drawItem(mainlist->getItem(i), (imgrect + pos + steppos), (i +1) == selectitem );
225 void Hud::drawLuaElements(v3s16 camera_offset) {
226 for (size_t i = 0; i != player->hud.size(); i++) {
227 HudElement *e = player->hud[i];
231 v2s32 pos(e->pos.X * m_screensize.X, e->pos.Y * m_screensize.Y);
233 case HUD_ELEM_IMAGE: {
234 video::ITexture *texture = tsrc->getTexture(e->text);
238 const video::SColor color(255, 255, 255, 255);
239 const video::SColor colors[] = {color, color, color, color};
240 core::dimension2di imgsize(texture->getOriginalSize());
241 v2s32 dstsize(imgsize.Width * e->scale.X,
242 imgsize.Height * e->scale.Y);
244 dstsize.X = m_screensize.X * (e->scale.X * -0.01);
246 dstsize.Y = m_screensize.Y * (e->scale.Y * -0.01);
247 v2s32 offset((e->align.X - 1.0) * dstsize.X / 2,
248 (e->align.Y - 1.0) * dstsize.Y / 2);
249 core::rect<s32> rect(0, 0, dstsize.X, dstsize.Y);
250 rect += pos + offset + v2s32(e->offset.X, e->offset.Y);
251 driver->draw2DImage(texture, rect,
252 core::rect<s32>(core::position2d<s32>(0,0), imgsize),
255 case HUD_ELEM_TEXT: {
256 video::SColor color(255, (e->number >> 16) & 0xFF,
257 (e->number >> 8) & 0xFF,
258 (e->number >> 0) & 0xFF);
259 core::rect<s32> size(0, 0, e->scale.X, text_height * e->scale.Y);
260 std::wstring text = narrow_to_wide(e->text);
261 core::dimension2d<u32> textsize = font->getDimension(text.c_str());
262 v2s32 offset((e->align.X - 1.0) * (textsize.Width / 2),
263 (e->align.Y - 1.0) * (textsize.Height / 2));
264 v2s32 offs(e->offset.X, e->offset.Y);
265 font->draw(text.c_str(), size + pos + offset + offs, color);
267 case HUD_ELEM_STATBAR: {
268 v2s32 offs(e->offset.X, e->offset.Y);
269 drawStatbar(pos, HUD_CORNER_UPPER, e->dir, e->text, e->number, offs);
271 case HUD_ELEM_INVENTORY: {
272 InventoryList *inv = inventory->getList(e->text);
273 drawItems(pos, e->number, 0, inv, e->item, e->dir);
275 case HUD_ELEM_WAYPOINT: {
276 v3f p_pos = player->getPosition() / BS;
277 v3f w_pos = e->world_pos * BS;
278 float distance = floor(10 * p_pos.getDistanceFrom(e->world_pos)) / 10;
279 scene::ICameraSceneNode* camera = smgr->getActiveCamera();
280 w_pos -= intToFloat(camera_offset, BS);
281 core::matrix4 trans = camera->getProjectionMatrix();
282 trans *= camera->getViewMatrix();
283 f32 transformed_pos[4] = { w_pos.X, w_pos.Y, w_pos.Z, 1.0f };
284 trans.multiplyWith1x4Matrix(transformed_pos);
285 if (transformed_pos[3] < 0)
287 f32 zDiv = transformed_pos[3] == 0.0f ? 1.0f :
288 core::reciprocal(transformed_pos[3]);
289 pos.X = m_screensize.X * (0.5 * transformed_pos[0] * zDiv + 0.5);
290 pos.Y = m_screensize.Y * (0.5 - transformed_pos[1] * zDiv * 0.5);
291 video::SColor color(255, (e->number >> 16) & 0xFF,
292 (e->number >> 8) & 0xFF,
293 (e->number >> 0) & 0xFF);
294 core::rect<s32> size(0, 0, 200, 2 * text_height);
295 std::wstring text = narrow_to_wide(e->name);
296 font->draw(text.c_str(), size + pos, color);
297 std::ostringstream os;
298 os<<distance<<e->text;
299 text = narrow_to_wide(os.str());
300 pos.Y += text_height;
301 font->draw(text.c_str(), size + pos, color);
304 infostream << "Hud::drawLuaElements: ignoring drawform " << e->type <<
305 " of hud element ID " << i << " due to unrecognized type" << std::endl;
311 void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture, s32 count, v2s32 offset) {
312 const video::SColor color(255, 255, 255, 255);
313 const video::SColor colors[] = {color, color, color, color};
315 video::ITexture *stat_texture = tsrc->getTexture(texture);
319 core::dimension2di srcd(stat_texture->getOriginalSize());
322 if (corner & HUD_CORNER_LOWER)
329 case HUD_DIR_RIGHT_LEFT:
330 steppos = v2s32(-1, 0);
332 case HUD_DIR_TOP_BOTTOM:
333 steppos = v2s32(0, 1);
335 case HUD_DIR_BOTTOM_TOP:
336 steppos = v2s32(0, -1);
339 steppos = v2s32(1, 0);
341 steppos.X *= srcd.Width;
342 steppos.Y *= srcd.Height;
344 for (s32 i = 0; i < count / 2; i++)
346 core::rect<s32> srcrect(0, 0, srcd.Width, srcd.Height);
347 core::rect<s32> dstrect(srcrect);
350 driver->draw2DImage(stat_texture, dstrect, srcrect, NULL, colors, true);
356 core::rect<s32> srcrect(0, 0, srcd.Width / 2, srcd.Height);
357 core::rect<s32> dstrect(srcrect);
360 driver->draw2DImage(stat_texture, dstrect, srcrect, NULL, colors, true);
365 void Hud::drawHotbar(s32 halfheartcount, u16 playeritem, s32 breath) {
367 v2s32 centerlowerpos(m_displaycenter.X, m_screensize.Y);
369 InventoryList *mainlist = inventory->getList("main");
370 if (mainlist == NULL) {
371 //silently ignore this we may not be initialized completely
375 s32 hotbar_itemcount = player->hud_hotbar_itemcount;
376 s32 width = hotbar_itemcount * (m_hotbar_imagesize + m_padding * 2);
377 v2s32 pos = centerlowerpos - v2s32(width / 2, m_hotbar_imagesize + m_padding * 3);
379 if ( (float) width / (float) porting::getWindowSize().X <=
380 g_settings->getFloat("hud_hotbar_max_width")) {
381 if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
382 drawItems(pos, hotbar_itemcount, 0, mainlist, playeritem + 1, 0);
388 v2s32 secondpos = pos;
389 pos = pos - v2s32(0, m_hotbar_imagesize + m_padding);
391 if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
392 drawItems(pos, hotbar_itemcount/2, 0, mainlist, playeritem + 1, 0);
393 drawItems(secondpos, hotbar_itemcount, hotbar_itemcount/2, mainlist, playeritem + 1, 0);
397 if (player->hud_flags & HUD_FLAG_HEALTHBAR_VISIBLE)
398 drawStatbar(pos - v2s32(0, 4), HUD_CORNER_LOWER, HUD_DIR_LEFT_RIGHT,
399 "heart.png", halfheartcount, v2s32(0, 0));
400 if (player->hud_flags & HUD_FLAG_BREATHBAR_VISIBLE && breath <= 10)
401 drawStatbar(pos - v2s32(-180, 4), HUD_CORNER_LOWER, HUD_DIR_LEFT_RIGHT,
402 "bubble.png", breath*2, v2s32(0, 0));
406 void Hud::drawCrosshair() {
407 if (!(player->hud_flags & HUD_FLAG_CROSSHAIR_VISIBLE) ||
408 (player->camera_mode == CAMERA_MODE_THIRD_FRONT)) {
412 if (use_crosshair_image) {
413 video::ITexture *crosshair = tsrc->getTexture("crosshair.png");
414 v2u32 size = crosshair->getOriginalSize();
415 v2s32 lsize = v2s32(m_displaycenter.X - (size.X / 2),
416 m_displaycenter.Y - (size.Y / 2));
417 driver->draw2DImage(crosshair, lsize,
418 core::rect<s32>(0, 0, size.X, size.Y),
419 0, crosshair_argb, true);
421 driver->draw2DLine(m_displaycenter - v2s32(10, 0),
422 m_displaycenter + v2s32(10, 0), crosshair_argb);
423 driver->draw2DLine(m_displaycenter - v2s32(0, 10),
424 m_displaycenter + v2s32(0, 10), crosshair_argb);
429 void Hud::drawSelectionBoxes(std::vector<aabb3f> &hilightboxes) {
430 for (std::vector<aabb3f>::const_iterator
431 i = hilightboxes.begin();
432 i != hilightboxes.end(); i++) {
433 driver->draw3DBox(*i, selectionbox_argb);
438 void Hud::resizeHotbar() {
439 if (m_screensize != porting::getWindowSize()) {
440 m_hotbar_imagesize = floor(HOTBAR_IMAGE_SIZE * porting::getDisplayDensity() + 0.5);
441 m_padding = m_hotbar_imagesize / 12;
442 m_screensize = porting::getWindowSize();
443 m_displaycenter = v2s32(m_screensize.X/2,m_screensize.Y/2);
447 void drawItemStack(video::IVideoDriver *driver,
449 const ItemStack &item,
450 const core::rect<s32> &rect,
451 const core::rect<s32> *clip,
457 const ItemDefinition &def = item.getDefinition(gamedef->idef());
458 video::ITexture *texture = gamedef->idef()->getInventoryTexture(def.name, gamedef);
460 // Draw the inventory texture
463 const video::SColor color(255,255,255,255);
464 const video::SColor colors[] = {color,color,color,color};
465 driver->draw2DImage(texture, rect,
466 core::rect<s32>(core::position2d<s32>(0,0),
467 core::dimension2di(texture->getOriginalSize())),
471 if(def.type == ITEM_TOOL && item.wear != 0)
473 // Draw a progressbar
474 float barheight = rect.getHeight()/16;
475 float barpad_x = rect.getWidth()/16;
476 float barpad_y = rect.getHeight()/16;
477 core::rect<s32> progressrect(
478 rect.UpperLeftCorner.X + barpad_x,
479 rect.LowerRightCorner.Y - barpad_y - barheight,
480 rect.LowerRightCorner.X - barpad_x,
481 rect.LowerRightCorner.Y - barpad_y);
483 // Shrink progressrect by amount of tool damage
484 float wear = item.wear / 65535.0;
486 wear * progressrect.UpperLeftCorner.X +
487 (1-wear) * progressrect.LowerRightCorner.X;
489 // Compute progressbar color
491 // wear = 0.5: yellow
493 video::SColor color(255,255,255,255);
494 int wear_i = MYMIN(floor(wear * 600), 511);
495 wear_i = MYMIN(wear_i + 10, 511);
497 color.set(255, wear_i, 255, 0);
499 color.set(255, 255, 511-wear_i, 0);
501 core::rect<s32> progressrect2 = progressrect;
502 progressrect2.LowerRightCorner.X = progressmid;
503 driver->draw2DRectangle(color, progressrect2, clip);
505 color = video::SColor(255,0,0,0);
506 progressrect2 = progressrect;
507 progressrect2.UpperLeftCorner.X = progressmid;
508 driver->draw2DRectangle(color, progressrect2, clip);
511 if(font != NULL && item.count >= 2)
513 // Get the item count as a string
514 std::string text = itos(item.count);
515 v2u32 dim = font->getDimension(narrow_to_wide(text).c_str());
516 v2s32 sdim(dim.X,dim.Y);
518 core::rect<s32> rect2(
519 /*rect.UpperLeftCorner,
520 core::dimension2d<u32>(rect.getWidth(), 15)*/
521 rect.LowerRightCorner - sdim,
525 video::SColor bgcolor(128,0,0,0);
526 driver->draw2DRectangle(bgcolor, rect2, clip);
528 video::SColor color(255,255,255,255);
529 font->draw(text.c_str(), rect2, color, false, false, clip);