bbec4489ead7203043c9ec5d30752a7387ca80d4
[oweals/minetest.git] / src / hud.cpp
1 /*
2 Minetest
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>
6
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.
11
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.
16
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.
20 */
21
22 #include "hud.h"
23 #include "main.h"
24 #include "settings.h"
25 #include "util/numeric.h"
26 #include "log.h"
27 #include "gamedef.h"
28 #include "itemdef.h"
29 #include "inventory.h"
30 #include "tile.h"
31 #include "localplayer.h"
32 #include "camera.h"
33 #include "porting.h"
34 #include <IGUIStaticText.h>
35
36
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;
42         this->smgr        = smgr;
43         this->guienv      = guienv;
44         this->font        = font;
45         this->text_height = text_height;
46         this->gamedef     = gamedef;
47         this->player      = player;
48         this->inventory   = inventory;
49         
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;
54
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;
58         }
59         
60         tsrc = gamedef->getTextureSource();
61         
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);
68         
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);
74         
75         use_crosshair_image = tsrc->isKnownSourceImage("crosshair.png");
76
77         hotbar_image = "";
78         use_hotbar_image = false;
79         hotbar_selected_image = "";
80         use_hotbar_selected_image = false;
81 }
82
83 void Hud::drawItem(const ItemStack &item, const core::rect<s32>& rect, bool selected) {
84
85         if (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);
97                         } else {
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,
107                                         core::rect<s32>(
108                                         v2s32(x1 - m_padding, y1 - m_padding),
109                                         v2s32(x2 + m_padding, y1)
110                                         ), NULL);
111                                 driver->draw2DRectangle(c_outside,
112                                         core::rect<s32>(
113                                         v2s32(x1 - m_padding, y2),
114                                         v2s32(x2 + m_padding, y2 + m_padding)
115                                         ), NULL);
116                                 driver->draw2DRectangle(c_outside,
117                                         core::rect<s32>(
118                                         v2s32(x1 - m_padding, y1),
119                                                 v2s32(x1, y2)
120                                         ), NULL);
121                                 driver->draw2DRectangle(c_outside,
122                                         core::rect<s32>(
123                                                 v2s32(x2, y1),
124                                         v2s32(x2 + m_padding, y2)
125                                         ), NULL);
126                                 /*// Light inside borders
127                                 driver->draw2DRectangle(c_inside,
128                                         core::rect<s32>(
129                                                 v2s32(x1 - padding/2, y1 - padding/2),
130                                                 v2s32(x2 + padding/2, y1)
131                                         ), NULL);
132                                 driver->draw2DRectangle(c_inside,
133                                         core::rect<s32>(
134                                                 v2s32(x1 - padding/2, y2),
135                                                 v2s32(x2 + padding/2, y2 + padding/2)
136                                         ), NULL);
137                                 driver->draw2DRectangle(c_inside,
138                                         core::rect<s32>(
139                                                 v2s32(x1 - padding/2, y1),
140                                                 v2s32(x1, y2)
141                                         ), NULL);
142                                 driver->draw2DRectangle(c_inside,
143                                         core::rect<s32>(
144                                                 v2s32(x2, y1),
145                                                 v2s32(x2 + padding/2, y2)
146                                         ), NULL);
147                                 */
148                         }
149                 }
150
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);
155         }
156
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)
160 {
161         s32 height  = m_hotbar_imagesize + m_padding * 2;
162         s32 width   = (itemcount - offset) * (m_hotbar_imagesize + m_padding * 2);
163
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);
167         }
168
169         // Position of upper left corner of bar
170         v2s32 pos = upperleftpos;
171
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);
176                 else
177                         use_hotbar_image = false;
178         }
179
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);
184                 else
185                         use_hotbar_selected_image = false;
186         }
187
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);
196         }
197
198         for (s32 i = offset; i < itemcount && (size_t)i < mainlist->getSize(); i++)
199         {
200                 v2s32 steppos;
201                 s32 fullimglen = m_hotbar_imagesize + m_padding * 2;
202
203                 core::rect<s32> imgrect(0, 0, m_hotbar_imagesize, m_hotbar_imagesize);
204
205                 switch (direction) {
206                         case HUD_DIR_RIGHT_LEFT:
207                                 steppos = v2s32(-(m_padding + (i - offset) * fullimglen), m_padding);
208                                 break;
209                         case HUD_DIR_TOP_BOTTOM:
210                                 steppos = v2s32(m_padding, m_padding + (i - offset) * fullimglen);
211                                 break;
212                         case HUD_DIR_BOTTOM_TOP:
213                                 steppos = v2s32(m_padding, -(m_padding + (i - offset) * fullimglen));
214                                 break;
215                         default:
216                                 steppos = v2s32(m_padding + (i - offset) * fullimglen, m_padding);
217                                 break;
218                 }
219
220                 drawItem(mainlist->getItem(i), (imgrect + pos + steppos), (i +1) == selectitem );
221         }
222 }
223
224
225 void Hud::drawLuaElements() {
226         for (size_t i = 0; i != player->hud.size(); i++) {
227                 HudElement *e = player->hud[i];
228                 if (!e)
229                         continue;
230                 
231                 v2s32 pos(e->pos.X * m_screensize.X, e->pos.Y * m_screensize.Y);
232                 switch (e->type) {
233                         case HUD_ELEM_IMAGE: {
234                                 video::ITexture *texture = tsrc->getTexture(e->text);
235                                 if (!texture)
236                                         continue;
237
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);
243                                 if (e->scale.X < 0)
244                                         dstsize.X = m_screensize.X * (e->scale.X * -0.01);
245                                 if (e->scale.Y < 0)
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),
253                                         NULL, colors, true);
254                                 break; }
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);
266                                 break; }
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);
270                                 break; }
271                         case HUD_ELEM_INVENTORY: {
272                                 InventoryList *inv = inventory->getList(e->text);
273                                 drawItems(pos, e->number, 0, inv, e->item, e->dir);
274                                 break; }
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                                 core::matrix4 trans = camera->getProjectionMatrix();
281                                 trans *= camera->getViewMatrix();
282                                 f32 transformed_pos[4] = { w_pos.X, w_pos.Y, w_pos.Z, 1.0f };
283                                 trans.multiplyWith1x4Matrix(transformed_pos);
284                                 if (transformed_pos[3] < 0)
285                                         break;
286                                 f32 zDiv = transformed_pos[3] == 0.0f ? 1.0f :
287                                         core::reciprocal(transformed_pos[3]);
288                                 pos.X = m_screensize.X * (0.5 * transformed_pos[0] * zDiv + 0.5);
289                                 pos.Y = m_screensize.Y * (0.5 - transformed_pos[1] * zDiv * 0.5);
290                                 video::SColor color(255, (e->number >> 16) & 0xFF,
291                                                                                  (e->number >> 8)  & 0xFF,
292                                                                                  (e->number >> 0)  & 0xFF);
293                                 core::rect<s32> size(0, 0, 200, 2 * text_height);
294                                 std::wstring text = narrow_to_wide(e->name);
295                                 font->draw(text.c_str(), size + pos, color);
296                                 std::ostringstream os;
297                                 os<<distance<<e->text;
298                                 text = narrow_to_wide(os.str());
299                                 pos.Y += text_height;
300                                 font->draw(text.c_str(), size + pos, color);
301                                 break; }
302                         default:
303                                 infostream << "Hud::drawLuaElements: ignoring drawform " << e->type <<
304                                         " of hud element ID " << i << " due to unrecognized type" << std::endl;
305                 }
306         }
307 }
308
309
310 void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture, s32 count, v2s32 offset) {
311         const video::SColor color(255, 255, 255, 255);
312         const video::SColor colors[] = {color, color, color, color};
313         
314         video::ITexture *stat_texture = tsrc->getTexture(texture);
315         if (!stat_texture)
316                 return;
317                 
318         core::dimension2di srcd(stat_texture->getOriginalSize());
319
320         v2s32 p = pos;
321         if (corner & HUD_CORNER_LOWER)
322                 p -= srcd.Height;
323
324         p += offset;
325
326         v2s32 steppos;
327         switch (drawdir) {
328                 case HUD_DIR_RIGHT_LEFT:
329                         steppos = v2s32(-1, 0);
330                         break;
331                 case HUD_DIR_TOP_BOTTOM:
332                         steppos = v2s32(0, 1);
333                         break;
334                 case HUD_DIR_BOTTOM_TOP:
335                         steppos = v2s32(0, -1);
336                         break;
337                 default:
338                         steppos = v2s32(1, 0);
339         }
340         steppos.X *= srcd.Width;
341         steppos.Y *= srcd.Height;
342         
343         for (s32 i = 0; i < count / 2; i++)
344         {
345                 core::rect<s32> srcrect(0, 0, srcd.Width, srcd.Height);
346                 core::rect<s32> dstrect(srcrect);
347
348                 dstrect += p;
349                 driver->draw2DImage(stat_texture, dstrect, srcrect, NULL, colors, true);
350                 p += steppos;
351         }
352         
353         if (count % 2 == 1)
354         {
355                 core::rect<s32> srcrect(0, 0, srcd.Width / 2, srcd.Height);
356                 core::rect<s32> dstrect(srcrect);
357
358                 dstrect += p;
359                 driver->draw2DImage(stat_texture, dstrect, srcrect, NULL, colors, true);
360         }
361 }
362
363
364 void Hud::drawHotbar(s32 halfheartcount, u16 playeritem, s32 breath) {
365
366         v2s32 centerlowerpos(m_displaycenter.X, m_screensize.Y);
367
368         InventoryList *mainlist = inventory->getList("main");
369         if (mainlist == NULL) {
370                 //silently ignore this we may not be initialized completely
371                 return;
372         }
373         
374         s32 hotbar_itemcount = player->hud_hotbar_itemcount;
375         s32 width = hotbar_itemcount * (m_hotbar_imagesize + m_padding * 2);
376         v2s32 pos = centerlowerpos - v2s32(width / 2, m_hotbar_imagesize + m_padding * 3);
377
378         if ( (float) width / (float) porting::getWindowSize().X <=
379                         g_settings->getFloat("hud_hotbar_max_width")) {
380                 if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
381                         drawItems(pos, hotbar_itemcount, 0, mainlist, playeritem + 1, 0);
382                 }
383         }
384         else {
385                 pos.X += width/4;
386
387                 v2s32 secondpos = pos;
388                 pos = pos - v2s32(0, m_hotbar_imagesize + m_padding);
389
390                 if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
391                         drawItems(pos, hotbar_itemcount/2, 0, mainlist, playeritem + 1, 0);
392                         drawItems(secondpos, hotbar_itemcount, hotbar_itemcount/2, mainlist, playeritem + 1, 0);
393                 }
394         }
395
396         if (player->hud_flags & HUD_FLAG_HEALTHBAR_VISIBLE)
397                 drawStatbar(pos - v2s32(0, 4), HUD_CORNER_LOWER, HUD_DIR_LEFT_RIGHT,
398                                 "heart.png", halfheartcount, v2s32(0, 0));
399         if (player->hud_flags & HUD_FLAG_BREATHBAR_VISIBLE && breath <= 10)
400                 drawStatbar(pos - v2s32(-180, 4), HUD_CORNER_LOWER, HUD_DIR_LEFT_RIGHT,
401                                 "bubble.png", breath*2, v2s32(0, 0));
402 }
403
404
405 void Hud::drawCrosshair() {
406         if (!(player->hud_flags & HUD_FLAG_CROSSHAIR_VISIBLE) ||
407                         (player->camera_mode == CAMERA_MODE_THIRD_FRONT)) {
408                 return;
409         }
410                 
411         if (use_crosshair_image) {
412                 video::ITexture *crosshair = tsrc->getTexture("crosshair.png");
413                 v2u32 size  = crosshair->getOriginalSize();
414                 v2s32 lsize = v2s32(m_displaycenter.X - (size.X / 2),
415                                 m_displaycenter.Y - (size.Y / 2));
416                 driver->draw2DImage(crosshair, lsize,
417                                 core::rect<s32>(0, 0, size.X, size.Y),
418                                 0, crosshair_argb, true);
419         } else {
420                 driver->draw2DLine(m_displaycenter - v2s32(10, 0),
421                                 m_displaycenter + v2s32(10, 0), crosshair_argb);
422                 driver->draw2DLine(m_displaycenter - v2s32(0, 10),
423                                 m_displaycenter + v2s32(0, 10), crosshair_argb);
424         }
425 }
426
427
428 void Hud::drawSelectionBoxes(std::vector<aabb3f> &hilightboxes) {
429         for (std::vector<aabb3f>::const_iterator
430                         i = hilightboxes.begin();
431                         i != hilightboxes.end(); i++) {
432                 driver->draw3DBox(*i, selectionbox_argb);
433         }
434 }
435
436
437 void Hud::resizeHotbar() {
438         if (m_screensize != porting::getWindowSize()) {
439                 m_hotbar_imagesize = floor(HOTBAR_IMAGE_SIZE * porting::getDisplayDensity() + 0.5);
440                 m_padding = m_hotbar_imagesize / 12;
441                 m_screensize = porting::getWindowSize();
442                 m_displaycenter = v2s32(m_screensize.X/2,m_screensize.Y/2);
443         }
444 }
445
446 void drawItemStack(video::IVideoDriver *driver,
447                 gui::IGUIFont *font,
448                 const ItemStack &item,
449                 const core::rect<s32> &rect,
450                 const core::rect<s32> *clip,
451                 IGameDef *gamedef)
452 {
453         if(item.empty())
454                 return;
455         
456         const ItemDefinition &def = item.getDefinition(gamedef->idef());
457         video::ITexture *texture = gamedef->idef()->getInventoryTexture(def.name, gamedef);
458
459         // Draw the inventory texture
460         if(texture != NULL)
461         {
462                 const video::SColor color(255,255,255,255);
463                 const video::SColor colors[] = {color,color,color,color};
464                 driver->draw2DImage(texture, rect,
465                         core::rect<s32>(core::position2d<s32>(0,0),
466                         core::dimension2di(texture->getOriginalSize())),
467                         clip, colors, true);
468         }
469
470         if(def.type == ITEM_TOOL && item.wear != 0)
471         {
472                 // Draw a progressbar
473                 float barheight = rect.getHeight()/16;
474                 float barpad_x = rect.getWidth()/16;
475                 float barpad_y = rect.getHeight()/16;
476                 core::rect<s32> progressrect(
477                         rect.UpperLeftCorner.X + barpad_x,
478                         rect.LowerRightCorner.Y - barpad_y - barheight,
479                         rect.LowerRightCorner.X - barpad_x,
480                         rect.LowerRightCorner.Y - barpad_y);
481
482                 // Shrink progressrect by amount of tool damage
483                 float wear = item.wear / 65535.0;
484                 int progressmid =
485                         wear * progressrect.UpperLeftCorner.X +
486                         (1-wear) * progressrect.LowerRightCorner.X;
487
488                 // Compute progressbar color
489                 //   wear = 0.0: green
490                 //   wear = 0.5: yellow
491                 //   wear = 1.0: red
492                 video::SColor color(255,255,255,255);
493                 int wear_i = MYMIN(floor(wear * 600), 511);
494                 wear_i = MYMIN(wear_i + 10, 511);
495                 if(wear_i <= 255)
496                         color.set(255, wear_i, 255, 0);
497                 else
498                         color.set(255, 255, 511-wear_i, 0);
499
500                 core::rect<s32> progressrect2 = progressrect;
501                 progressrect2.LowerRightCorner.X = progressmid;
502                 driver->draw2DRectangle(color, progressrect2, clip);
503
504                 color = video::SColor(255,0,0,0);
505                 progressrect2 = progressrect;
506                 progressrect2.UpperLeftCorner.X = progressmid;
507                 driver->draw2DRectangle(color, progressrect2, clip);
508         }
509
510         if(font != NULL && item.count >= 2)
511         {
512                 // Get the item count as a string
513                 std::string text = itos(item.count);
514                 v2u32 dim = font->getDimension(narrow_to_wide(text).c_str());
515                 v2s32 sdim(dim.X,dim.Y);
516
517                 core::rect<s32> rect2(
518                         /*rect.UpperLeftCorner,
519                         core::dimension2d<u32>(rect.getWidth(), 15)*/
520                         rect.LowerRightCorner - sdim,
521                         sdim
522                 );
523
524                 video::SColor bgcolor(128,0,0,0);
525                 driver->draw2DRectangle(bgcolor, rect2, clip);
526
527                 video::SColor color(255,255,255,255);
528                 font->draw(text.c_str(), rect2, color, false, false, clip);
529         }
530 }