]> git.lizzy.rs Git - minetest.git/blob - src/hud.cpp
183f94e4905140d2402c19dbdb04143755bbecd7
[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
33 #include <IGUIStaticText.h>
34
35
36 Hud::Hud(video::IVideoDriver *driver, gui::IGUIEnvironment* guienv,
37                 gui::IGUIFont *font, u32 text_height, IGameDef *gamedef,
38                 LocalPlayer *player, Inventory *inventory) {
39         this->driver      = driver;
40         this->guienv      = guienv;
41         this->font        = font;
42         this->text_height = text_height;
43         this->gamedef     = gamedef;
44         this->player      = player;
45         this->inventory   = inventory;
46         
47         screensize       = v2u32(0, 0);
48         displaycenter    = v2s32(0, 0);
49         hotbar_imagesize = 48;
50         
51         tsrc = gamedef->getTextureSource();
52         
53         v3f crosshair_color = g_settings->getV3F("crosshair_color");
54         u32 cross_r = rangelim(myround(crosshair_color.X), 0, 255);
55         u32 cross_g = rangelim(myround(crosshair_color.Y), 0, 255);
56         u32 cross_b = rangelim(myround(crosshair_color.Z), 0, 255);
57         u32 cross_a = rangelim(g_settings->getS32("crosshair_alpha"), 0, 255);
58         crosshair_argb = video::SColor(cross_a, cross_r, cross_g, cross_b);
59         
60         v3f selectionbox_color = g_settings->getV3F("selectionbox_color");
61         u32 sbox_r = rangelim(myround(selectionbox_color.X), 0, 255);
62         u32 sbox_g = rangelim(myround(selectionbox_color.Y), 0, 255);
63         u32 sbox_b = rangelim(myround(selectionbox_color.Z), 0, 255);
64         selectionbox_argb = video::SColor(255, sbox_r, sbox_g, sbox_b);
65         
66         use_crosshair_image = tsrc->isKnownSourceImage("crosshair.png");
67
68         hotbar_image = "";
69         use_hotbar_image = false;
70         hotbar_selected_image = "";
71         use_hotbar_selected_image = false;
72 }
73
74
75 //NOTE: selectitem = 0 -> no selected; selectitem 1-based
76 void Hud::drawItem(v2s32 upperleftpos, s32 imgsize, s32 itemcount,
77                 InventoryList *mainlist, u16 selectitem, u16 direction)
78 {
79         s32 padding = imgsize / 12;
80         s32 height  = imgsize + padding * 2;
81         s32 width   = itemcount * (imgsize + padding * 2);
82         if (direction == HUD_DIR_TOP_BOTTOM || direction == HUD_DIR_BOTTOM_TOP) {
83                 width  = imgsize + padding * 2;
84                 height = itemcount * (imgsize + padding * 2);
85         }
86         s32 fullimglen = imgsize + padding * 2;
87
88         // Position of upper left corner of bar
89         v2s32 pos = upperleftpos;
90
91         // Draw background color
92         /*core::rect<s32> barrect(0,0,width,height);
93         barrect += pos;
94         video::SColor bgcolor(255,128,128,128);
95         driver->draw2DRectangle(bgcolor, barrect, NULL);*/
96
97         core::rect<s32> imgrect(0, 0, imgsize, imgsize);
98         const video::SColor hbar_color(255, 255, 255, 255);
99         const video::SColor hbar_colors[] = {hbar_color, hbar_color, hbar_color, hbar_color};
100
101         if (hotbar_image != player->hotbar_image) {
102                 hotbar_image = player->hotbar_image;
103                 if (hotbar_image != "")
104                         use_hotbar_image = tsrc->isKnownSourceImage(hotbar_image);
105                 else
106                         use_hotbar_image = false;
107         }
108
109         if (hotbar_selected_image != player->hotbar_selected_image) {
110                 hotbar_selected_image = player->hotbar_selected_image;
111                 if (hotbar_selected_image != "")
112                         use_hotbar_selected_image = tsrc->isKnownSourceImage(hotbar_selected_image);
113                 else
114                         use_hotbar_selected_image = false;
115         }
116
117         if (use_hotbar_image) {
118                 core::rect<s32> imgrect2(-padding/2, -padding/2, width+padding/2, height+padding/2);
119                 core::rect<s32> rect2 = imgrect2 + pos;
120                 video::ITexture *texture = tsrc->getTexture(hotbar_image);
121                 core::dimension2di imgsize(texture->getOriginalSize());
122                 driver->draw2DImage(texture, rect2,
123                         core::rect<s32>(core::position2d<s32>(0,0), imgsize),
124                         NULL, hbar_colors, true);
125         }
126
127         for (s32 i = 0; i < itemcount; i++)
128         {
129                 const ItemStack &item = mainlist->getItem(i);
130
131                 v2s32 steppos;
132                 switch (direction) {
133                         case HUD_DIR_RIGHT_LEFT:
134                                 steppos = v2s32(-(padding + i * fullimglen), padding);
135                                 break;
136                         case HUD_DIR_TOP_BOTTOM:
137                                 steppos = v2s32(padding, padding + i * fullimglen);
138                                 break;
139                         case HUD_DIR_BOTTOM_TOP:
140                                 steppos = v2s32(padding, -(padding + i * fullimglen));
141                                 break;
142                         default:
143                                 steppos = v2s32(padding + i * fullimglen, padding);     
144                 }
145                         
146                 core::rect<s32> rect = imgrect + pos + steppos;
147
148                 if (selectitem == i + 1) {
149                         if (use_hotbar_selected_image) {
150                                 core::rect<s32> imgrect2(-padding*2, -padding*2, height, height);
151                                 rect = imgrect2 + pos + steppos;
152                                 video::ITexture *texture = tsrc->getTexture(hotbar_selected_image);
153                                 core::dimension2di imgsize(texture->getOriginalSize());
154                                 driver->draw2DImage(texture, rect,
155                                         core::rect<s32>(core::position2d<s32>(0,0), imgsize),
156                                         NULL, hbar_colors, true);
157                                 rect = imgrect + pos + steppos;
158                         } else {
159                                 rect = imgrect + pos + steppos;
160                                 video::SColor c_outside(255,255,0,0);
161                                 //video::SColor c_outside(255,0,0,0);
162                                 //video::SColor c_inside(255,192,192,192);
163                                 s32 x1 = rect.UpperLeftCorner.X;
164                                 s32 y1 = rect.UpperLeftCorner.Y;
165                                 s32 x2 = rect.LowerRightCorner.X;
166                                 s32 y2 = rect.LowerRightCorner.Y;
167                                 // Black base borders
168                                 driver->draw2DRectangle(c_outside,
169                                         core::rect<s32>(
170                                                 v2s32(x1 - padding, y1 - padding),
171                                                 v2s32(x2 + padding, y1)
172                                         ), NULL);
173                                 driver->draw2DRectangle(c_outside,
174                                         core::rect<s32>(
175                                                 v2s32(x1 - padding, y2),
176                                                 v2s32(x2 + padding, y2 + padding)
177                                         ), NULL);
178                                 driver->draw2DRectangle(c_outside,
179                                         core::rect<s32>(
180                                                 v2s32(x1 - padding, y1),
181                                                 v2s32(x1, y2)
182                                         ), NULL);
183                                 driver->draw2DRectangle(c_outside,
184                                         core::rect<s32>(
185                                                 v2s32(x2, y1),
186                                                 v2s32(x2 + padding, y2)
187                                         ), NULL);
188                                 /*// Light inside borders
189                                 driver->draw2DRectangle(c_inside,
190                                         core::rect<s32>(
191                                                 v2s32(x1 - padding/2, y1 - padding/2),
192                                                 v2s32(x2 + padding/2, y1)
193                                         ), NULL);
194                                 driver->draw2DRectangle(c_inside,
195                                         core::rect<s32>(
196                                                 v2s32(x1 - padding/2, y2),
197                                                 v2s32(x2 + padding/2, y2 + padding/2)
198                                         ), NULL);
199                                 driver->draw2DRectangle(c_inside,
200                                         core::rect<s32>(
201                                                 v2s32(x1 - padding/2, y1),
202                                                 v2s32(x1, y2)
203                                         ), NULL);
204                                 driver->draw2DRectangle(c_inside,
205                                         core::rect<s32>(
206                                                 v2s32(x2, y1),
207                                                 v2s32(x2 + padding/2, y2)
208                                         ), NULL);
209                                 */
210                         }
211                 }
212
213                 video::SColor bgcolor2(128, 0, 0, 0);
214                 if (!use_hotbar_image)
215                         driver->draw2DRectangle(bgcolor2, rect, NULL);
216                 drawItemStack(driver, font, item, rect, NULL, gamedef);
217         }
218 }
219
220
221 void Hud::drawLuaElements() {
222         for (size_t i = 0; i != player->hud.size(); i++) {
223                 HudElement *e = player->hud[i];
224                 if (!e)
225                         continue;
226                 
227                 v2s32 pos(e->pos.X * screensize.X, e->pos.Y * screensize.Y);
228                 switch (e->type) {
229                         case HUD_ELEM_IMAGE: {
230                                 video::ITexture *texture = tsrc->getTexture(e->text);
231                                 if (!texture)
232                                         continue;
233
234                                 const video::SColor color(255, 255, 255, 255);
235                                 const video::SColor colors[] = {color, color, color, color};
236                                 core::dimension2di imgsize(texture->getOriginalSize());
237                                 v2s32 dstsize(imgsize.Width * e->scale.X,
238                                               imgsize.Height * e->scale.Y);
239                                 if (e->scale.X < 0)
240                                         dstsize.X = screensize.X * (e->scale.X * -0.01);
241                                 if (e->scale.Y < 0)
242                                         dstsize.Y = screensize.Y * (e->scale.Y * -0.01);
243                                 v2s32 offset((e->align.X - 1.0) * dstsize.X / 2,
244                                              (e->align.Y - 1.0) * dstsize.Y / 2);
245                                 core::rect<s32> rect(0, 0, dstsize.X, dstsize.Y);
246                                 rect += pos + offset + v2s32(e->offset.X, e->offset.Y);
247                                 driver->draw2DImage(texture, rect,
248                                         core::rect<s32>(core::position2d<s32>(0,0), imgsize),
249                                         NULL, colors, true);
250                                 break; }
251                         case HUD_ELEM_TEXT: {
252                                 video::SColor color(255, (e->number >> 16) & 0xFF,
253                                                                                  (e->number >> 8)  & 0xFF,
254                                                                                  (e->number >> 0)  & 0xFF);
255                                 core::rect<s32> size(0, 0, e->scale.X, text_height * e->scale.Y);
256                                 std::wstring text = narrow_to_wide(e->text);
257                                 core::dimension2d<u32> textsize = font->getDimension(text.c_str());
258                                 v2s32 offset((e->align.X - 1.0) * (textsize.Width / 2),
259                                              (e->align.Y - 1.0) * (textsize.Height / 2));
260                                 v2s32 offs(e->offset.X, e->offset.Y);
261                                 font->draw(text.c_str(), size + pos + offset + offs, color);
262                                 break; }
263                         case HUD_ELEM_STATBAR: {
264                                 v2s32 offs(e->offset.X, e->offset.Y);
265                                 drawStatbar(pos, HUD_CORNER_UPPER, e->dir, e->text, e->number, offs);
266                                 break; }
267                         case HUD_ELEM_INVENTORY: {
268                                 InventoryList *inv = inventory->getList(e->text);
269                                 drawItem(pos, hotbar_imagesize, e->number, inv, e->item, e->dir);
270                                 break; }
271                         default:
272                                 infostream << "Hud::drawLuaElements: ignoring drawform " << e->type <<
273                                         " of hud element ID " << i << " due to unrecognized type" << std::endl;
274                 }
275         }
276 }
277
278
279 void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture, s32 count, v2s32 offset) {
280         const video::SColor color(255, 255, 255, 255);
281         const video::SColor colors[] = {color, color, color, color};
282         
283         video::ITexture *stat_texture = tsrc->getTexture(texture);
284         if (!stat_texture)
285                 return;
286                 
287         core::dimension2di srcd(stat_texture->getOriginalSize());
288
289         v2s32 p = pos;
290         if (corner & HUD_CORNER_LOWER)
291                 p -= srcd.Height;
292
293         p += offset;
294
295         v2s32 steppos;
296         switch (drawdir) {
297                 case HUD_DIR_RIGHT_LEFT:
298                         steppos = v2s32(-1, 0);
299                         break;
300                 case HUD_DIR_TOP_BOTTOM:
301                         steppos = v2s32(0, 1);
302                         break;
303                 case HUD_DIR_BOTTOM_TOP:
304                         steppos = v2s32(0, -1);
305                         break;
306                 default:
307                         steppos = v2s32(1, 0);  
308         }
309         steppos.X *= srcd.Width;
310         steppos.Y *= srcd.Height;
311         
312         for (s32 i = 0; i < count / 2; i++)
313         {
314                 core::rect<s32> srcrect(0, 0, srcd.Width, srcd.Height);
315                 core::rect<s32> dstrect(srcrect);
316
317                 dstrect += p;
318                 driver->draw2DImage(stat_texture, dstrect, srcrect, NULL, colors, true);
319                 p += steppos;
320         }
321         
322         if (count % 2 == 1)
323         {
324                 core::rect<s32> srcrect(0, 0, srcd.Width / 2, srcd.Height);
325                 core::rect<s32> dstrect(srcrect);
326
327                 dstrect += p;
328                 driver->draw2DImage(stat_texture, dstrect, srcrect, NULL, colors, true);
329         }
330 }
331
332
333 void Hud::drawHotbar(v2s32 centerlowerpos, s32 halfheartcount, u16 playeritem, s32 breath) {
334         InventoryList *mainlist = inventory->getList("main");
335         if (mainlist == NULL) {
336                 errorstream << "draw_hotbar(): mainlist == NULL" << std::endl;
337                 return;
338         }
339         
340         s32 hotbar_itemcount = player->hud_hotbar_itemcount;
341         s32 padding = hotbar_imagesize / 12;
342         s32 width = hotbar_itemcount * (hotbar_imagesize + padding * 2);
343         v2s32 pos = centerlowerpos - v2s32(width / 2, hotbar_imagesize + padding * 3);
344         
345         if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE)
346                 drawItem(pos, hotbar_imagesize, hotbar_itemcount, mainlist, playeritem + 1, 0);
347         if (player->hud_flags & HUD_FLAG_HEALTHBAR_VISIBLE)
348                 drawStatbar(pos - v2s32(0, 4), HUD_CORNER_LOWER, HUD_DIR_LEFT_RIGHT,
349                                 "heart.png", halfheartcount, v2s32(0, 0));
350         if (player->hud_flags & HUD_FLAG_BREATHBAR_VISIBLE && breath <= 10)
351                 drawStatbar(pos - v2s32(-180, 4), HUD_CORNER_LOWER, HUD_DIR_LEFT_RIGHT,
352                                 "bubble.png", breath*2, v2s32(0, 0));
353 }
354
355
356 void Hud::drawCrosshair() {
357         if (!(player->hud_flags & HUD_FLAG_CROSSHAIR_VISIBLE))
358                 return;
359                 
360         if (use_crosshair_image) {
361                 video::ITexture *crosshair = tsrc->getTexture("crosshair.png");
362                 v2u32 size  = crosshair->getOriginalSize();
363                 v2s32 lsize = v2s32(displaycenter.X - (size.X / 2),
364                                                         displaycenter.Y - (size.Y / 2));
365                 driver->draw2DImage(crosshair, lsize,
366                                 core::rect<s32>(0, 0, size.X, size.Y),
367                                 0, crosshair_argb, true);
368         } else {
369                 driver->draw2DLine(displaycenter - v2s32(10, 0),
370                                 displaycenter + v2s32(10, 0), crosshair_argb);
371                 driver->draw2DLine(displaycenter - v2s32(0, 10),
372                                 displaycenter + v2s32(0, 10), crosshair_argb);
373         }
374 }
375
376
377 void Hud::drawSelectionBoxes(std::vector<aabb3f> &hilightboxes) {
378         for (std::vector<aabb3f>::const_iterator
379                         i = hilightboxes.begin();
380                         i != hilightboxes.end(); i++) {
381                 driver->draw3DBox(*i, selectionbox_argb);
382         }
383 }
384
385
386 void Hud::resizeHotbar() {
387         if (screensize.Y <= 800)
388                 hotbar_imagesize = 32;
389         else if (screensize.Y <= 1280)
390                 hotbar_imagesize = 48;
391         else
392                 hotbar_imagesize = 64;
393 }
394
395 void drawItemStack(video::IVideoDriver *driver,
396                 gui::IGUIFont *font,
397                 const ItemStack &item,
398                 const core::rect<s32> &rect,
399                 const core::rect<s32> *clip,
400                 IGameDef *gamedef)
401 {
402         if(item.empty())
403                 return;
404         
405         const ItemDefinition &def = item.getDefinition(gamedef->idef());
406         video::ITexture *texture = gamedef->idef()->getInventoryTexture(def.name, gamedef);
407
408         // Draw the inventory texture
409         if(texture != NULL)
410         {
411                 const video::SColor color(255,255,255,255);
412                 const video::SColor colors[] = {color,color,color,color};
413                 driver->draw2DImage(texture, rect,
414                         core::rect<s32>(core::position2d<s32>(0,0),
415                         core::dimension2di(texture->getOriginalSize())),
416                         clip, colors, true);
417         }
418
419         if(def.type == ITEM_TOOL && item.wear != 0)
420         {
421                 // Draw a progressbar
422                 float barheight = rect.getHeight()/16;
423                 float barpad_x = rect.getWidth()/16;
424                 float barpad_y = rect.getHeight()/16;
425                 core::rect<s32> progressrect(
426                         rect.UpperLeftCorner.X + barpad_x,
427                         rect.LowerRightCorner.Y - barpad_y - barheight,
428                         rect.LowerRightCorner.X - barpad_x,
429                         rect.LowerRightCorner.Y - barpad_y);
430
431                 // Shrink progressrect by amount of tool damage
432                 float wear = item.wear / 65535.0;
433                 int progressmid =
434                         wear * progressrect.UpperLeftCorner.X +
435                         (1-wear) * progressrect.LowerRightCorner.X;
436
437                 // Compute progressbar color
438                 //   wear = 0.0: green
439                 //   wear = 0.5: yellow
440                 //   wear = 1.0: red
441                 video::SColor color(255,255,255,255);
442                 int wear_i = MYMIN(floor(wear * 600), 511);
443                 wear_i = MYMIN(wear_i + 10, 511);
444                 if(wear_i <= 255)
445                         color.set(255, wear_i, 255, 0);
446                 else
447                         color.set(255, 255, 511-wear_i, 0);
448
449                 core::rect<s32> progressrect2 = progressrect;
450                 progressrect2.LowerRightCorner.X = progressmid;
451                 driver->draw2DRectangle(color, progressrect2, clip);
452
453                 color = video::SColor(255,0,0,0);
454                 progressrect2 = progressrect;
455                 progressrect2.UpperLeftCorner.X = progressmid;
456                 driver->draw2DRectangle(color, progressrect2, clip);
457         }
458
459         if(font != NULL && item.count >= 2)
460         {
461                 // Get the item count as a string
462                 std::string text = itos(item.count);
463                 v2u32 dim = font->getDimension(narrow_to_wide(text).c_str());
464                 v2s32 sdim(dim.X,dim.Y);
465
466                 core::rect<s32> rect2(
467                         /*rect.UpperLeftCorner,
468                         core::dimension2d<u32>(rect.getWidth(), 15)*/
469                         rect.LowerRightCorner - sdim,
470                         sdim
471                 );
472
473                 video::SColor bgcolor(128,0,0,0);
474                 driver->draw2DRectangle(bgcolor, rect2, clip);
475
476                 video::SColor color(255,255,255,255);
477                 font->draw(text.c_str(), rect2, color, false, false, clip);
478         }
479 }
480