]> git.lizzy.rs Git - minetest.git/blob - src/hud.cpp
c385211f24ffe60c84dae2debbb6d01084c93f0f
[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(v3s16 camera_offset) {
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                                 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)
286                                         break;
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);
302                                 break; }
303                         default:
304                                 infostream << "Hud::drawLuaElements: ignoring drawform " << e->type <<
305                                         " of hud element ID " << i << " due to unrecognized type" << std::endl;
306                 }
307         }
308 }
309
310
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};
314         
315         video::ITexture *stat_texture = tsrc->getTexture(texture);
316         if (!stat_texture)
317                 return;
318                 
319         core::dimension2di srcd(stat_texture->getOriginalSize());
320
321         v2s32 p = pos;
322         if (corner & HUD_CORNER_LOWER)
323                 p -= srcd.Height;
324
325         p += offset;
326
327         v2s32 steppos;
328         switch (drawdir) {
329                 case HUD_DIR_RIGHT_LEFT:
330                         steppos = v2s32(-1, 0);
331                         break;
332                 case HUD_DIR_TOP_BOTTOM:
333                         steppos = v2s32(0, 1);
334                         break;
335                 case HUD_DIR_BOTTOM_TOP:
336                         steppos = v2s32(0, -1);
337                         break;
338                 default:
339                         steppos = v2s32(1, 0);
340         }
341         steppos.X *= srcd.Width;
342         steppos.Y *= srcd.Height;
343         
344         for (s32 i = 0; i < count / 2; i++)
345         {
346                 core::rect<s32> srcrect(0, 0, srcd.Width, srcd.Height);
347                 core::rect<s32> dstrect(srcrect);
348
349                 dstrect += p;
350                 driver->draw2DImage(stat_texture, dstrect, srcrect, NULL, colors, true);
351                 p += steppos;
352         }
353         
354         if (count % 2 == 1)
355         {
356                 core::rect<s32> srcrect(0, 0, srcd.Width / 2, srcd.Height);
357                 core::rect<s32> dstrect(srcrect);
358
359                 dstrect += p;
360                 driver->draw2DImage(stat_texture, dstrect, srcrect, NULL, colors, true);
361         }
362 }
363
364
365 void Hud::drawHotbar(s32 halfheartcount, u16 playeritem, s32 breath) {
366
367         v2s32 centerlowerpos(m_displaycenter.X, m_screensize.Y);
368
369         InventoryList *mainlist = inventory->getList("main");
370         if (mainlist == NULL) {
371                 //silently ignore this we may not be initialized completely
372                 return;
373         }
374         
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);
378
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);
383                 }
384         }
385         else {
386                 pos.X += width/4;
387
388                 v2s32 secondpos = pos;
389                 pos = pos - v2s32(0, m_hotbar_imagesize + m_padding);
390
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);
394                 }
395         }
396
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));
403 }
404
405
406 void Hud::drawCrosshair() {
407         if (!(player->hud_flags & HUD_FLAG_CROSSHAIR_VISIBLE) ||
408                         (player->camera_mode == CAMERA_MODE_THIRD_FRONT)) {
409                 return;
410         }
411                 
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);
420         } else {
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);
425         }
426 }
427
428
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);
434         }
435 }
436
437
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);
444         }
445 }
446
447 void drawItemStack(video::IVideoDriver *driver,
448                 gui::IGUIFont *font,
449                 const ItemStack &item,
450                 const core::rect<s32> &rect,
451                 const core::rect<s32> *clip,
452                 IGameDef *gamedef)
453 {
454         if(item.empty())
455                 return;
456         
457         const ItemDefinition &def = item.getDefinition(gamedef->idef());
458         video::ITexture *texture = gamedef->idef()->getInventoryTexture(def.name, gamedef);
459
460         // Draw the inventory texture
461         if(texture != NULL)
462         {
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())),
468                         clip, colors, true);
469         }
470
471         if(def.type == ITEM_TOOL && item.wear != 0)
472         {
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);
482
483                 // Shrink progressrect by amount of tool damage
484                 float wear = item.wear / 65535.0;
485                 int progressmid =
486                         wear * progressrect.UpperLeftCorner.X +
487                         (1-wear) * progressrect.LowerRightCorner.X;
488
489                 // Compute progressbar color
490                 //   wear = 0.0: green
491                 //   wear = 0.5: yellow
492                 //   wear = 1.0: red
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);
496                 if(wear_i <= 255)
497                         color.set(255, wear_i, 255, 0);
498                 else
499                         color.set(255, 255, 511-wear_i, 0);
500
501                 core::rect<s32> progressrect2 = progressrect;
502                 progressrect2.LowerRightCorner.X = progressmid;
503                 driver->draw2DRectangle(color, progressrect2, clip);
504
505                 color = video::SColor(255,0,0,0);
506                 progressrect2 = progressrect;
507                 progressrect2.UpperLeftCorner.X = progressmid;
508                 driver->draw2DRectangle(color, progressrect2, clip);
509         }
510
511         if(font != NULL && item.count >= 2)
512         {
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);
517
518                 core::rect<s32> rect2(
519                         /*rect.UpperLeftCorner,
520                         core::dimension2d<u32>(rect.getWidth(), 15)*/
521                         rect.LowerRightCorner - sdim,
522                         sdim
523                 );
524
525                 video::SColor bgcolor(128,0,0,0);
526                 driver->draw2DRectangle(bgcolor, rect2, clip);
527
528                 video::SColor color(255,255,255,255);
529                 font->draw(text.c_str(), rect2, color, false, false, clip);
530         }
531 }