]> git.lizzy.rs Git - minetest.git/blob - src/hud.cpp
Use player:set_hotbar_image() instead of hardcoded hotbar.png
[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                                 core::rect<s32> rect(0, 0, imgsize.Width  * e->scale.X,
238                                                                                imgsize.Height * e->scale.X);
239                                 rect += pos;
240                                 v2s32 offset((e->align.X - 1.0) * ((imgsize.Width  * e->scale.X) / 2),
241                                              (e->align.Y - 1.0) * ((imgsize.Height * e->scale.X) / 2));
242                                 rect += offset;
243                                 rect += v2s32(e->offset.X, e->offset.Y);
244                                 driver->draw2DImage(texture, rect,
245                                         core::rect<s32>(core::position2d<s32>(0,0), imgsize),
246                                         NULL, colors, true);
247                                 break; }
248                         case HUD_ELEM_TEXT: {
249                                 video::SColor color(255, (e->number >> 16) & 0xFF,
250                                                                                  (e->number >> 8)  & 0xFF,
251                                                                                  (e->number >> 0)  & 0xFF);
252                                 core::rect<s32> size(0, 0, e->scale.X, text_height * e->scale.Y);
253                                 std::wstring text = narrow_to_wide(e->text);
254                                 core::dimension2d<u32> textsize = font->getDimension(text.c_str());
255                                 v2s32 offset((e->align.X - 1.0) * (textsize.Width / 2),
256                                              (e->align.Y - 1.0) * (textsize.Height / 2));
257                                 v2s32 offs(e->offset.X, e->offset.Y);
258                                 font->draw(text.c_str(), size + pos + offset + offs, color);
259                                 break; }
260                         case HUD_ELEM_STATBAR: {
261                                 v2s32 offs(e->offset.X, e->offset.Y);
262                                 drawStatbar(pos, HUD_CORNER_UPPER, e->dir, e->text, e->number, offs);
263                                 break; }
264                         case HUD_ELEM_INVENTORY: {
265                                 InventoryList *inv = inventory->getList(e->text);
266                                 drawItem(pos, hotbar_imagesize, e->number, inv, e->item, e->dir);
267                                 break; }
268                         default:
269                                 infostream << "Hud::drawLuaElements: ignoring drawform " << e->type <<
270                                         " of hud element ID " << i << " due to unrecognized type" << std::endl;
271                 }
272         }
273 }
274
275
276 void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture, s32 count, v2s32 offset) {
277         const video::SColor color(255, 255, 255, 255);
278         const video::SColor colors[] = {color, color, color, color};
279         
280         video::ITexture *stat_texture = tsrc->getTexture(texture);
281         if (!stat_texture)
282                 return;
283                 
284         core::dimension2di srcd(stat_texture->getOriginalSize());
285
286         v2s32 p = pos;
287         if (corner & HUD_CORNER_LOWER)
288                 p -= srcd.Height;
289
290         p += offset;
291
292         v2s32 steppos;
293         switch (drawdir) {
294                 case HUD_DIR_RIGHT_LEFT:
295                         steppos = v2s32(-1, 0);
296                         break;
297                 case HUD_DIR_TOP_BOTTOM:
298                         steppos = v2s32(0, 1);
299                         break;
300                 case HUD_DIR_BOTTOM_TOP:
301                         steppos = v2s32(0, -1);
302                         break;
303                 default:
304                         steppos = v2s32(1, 0);  
305         }
306         steppos.X *= srcd.Width;
307         steppos.Y *= srcd.Height;
308         
309         for (s32 i = 0; i < count / 2; i++)
310         {
311                 core::rect<s32> srcrect(0, 0, srcd.Width, srcd.Height);
312                 core::rect<s32> dstrect(srcrect);
313
314                 dstrect += p;
315                 driver->draw2DImage(stat_texture, dstrect, srcrect, NULL, colors, true);
316                 p += steppos;
317         }
318         
319         if (count % 2 == 1)
320         {
321                 core::rect<s32> srcrect(0, 0, srcd.Width / 2, srcd.Height);
322                 core::rect<s32> dstrect(srcrect);
323
324                 dstrect += p;
325                 driver->draw2DImage(stat_texture, dstrect, srcrect, NULL, colors, true);
326         }
327 }
328
329
330 void Hud::drawHotbar(v2s32 centerlowerpos, s32 halfheartcount, u16 playeritem, s32 breath) {
331         InventoryList *mainlist = inventory->getList("main");
332         if (mainlist == NULL) {
333                 errorstream << "draw_hotbar(): mainlist == NULL" << std::endl;
334                 return;
335         }
336         
337         s32 hotbar_itemcount = player->hud_hotbar_itemcount;
338         s32 padding = hotbar_imagesize / 12;
339         s32 width = hotbar_itemcount * (hotbar_imagesize + padding * 2);
340         v2s32 pos = centerlowerpos - v2s32(width / 2, hotbar_imagesize + padding * 2);
341         
342         if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE)
343                 drawItem(pos, hotbar_imagesize, hotbar_itemcount, mainlist, playeritem + 1, 0);
344         if (player->hud_flags & HUD_FLAG_HEALTHBAR_VISIBLE)
345                 drawStatbar(pos - v2s32(0, 4), HUD_CORNER_LOWER, HUD_DIR_LEFT_RIGHT,
346                                 "heart.png", halfheartcount, v2s32(0, 0));
347         if (player->hud_flags & HUD_FLAG_BREATHBAR_VISIBLE && breath <= 10)
348                 drawStatbar(pos - v2s32(-180, 4), HUD_CORNER_LOWER, HUD_DIR_LEFT_RIGHT,
349                                 "bubble.png", breath*2, v2s32(0, 0));
350 }
351
352
353 void Hud::drawCrosshair() {
354         if (!(player->hud_flags & HUD_FLAG_CROSSHAIR_VISIBLE))
355                 return;
356                 
357         if (use_crosshair_image) {
358                 video::ITexture *crosshair = tsrc->getTexture("crosshair.png");
359                 v2u32 size  = crosshair->getOriginalSize();
360                 v2s32 lsize = v2s32(displaycenter.X - (size.X / 2),
361                                                         displaycenter.Y - (size.Y / 2));
362                 driver->draw2DImage(crosshair, lsize,
363                                 core::rect<s32>(0, 0, size.X, size.Y),
364                                 0, crosshair_argb, true);
365         } else {
366                 driver->draw2DLine(displaycenter - v2s32(10, 0),
367                                 displaycenter + v2s32(10, 0), crosshair_argb);
368                 driver->draw2DLine(displaycenter - v2s32(0, 10),
369                                 displaycenter + v2s32(0, 10), crosshair_argb);
370         }
371 }
372
373
374 void Hud::drawSelectionBoxes(std::vector<aabb3f> &hilightboxes) {
375         for (std::vector<aabb3f>::const_iterator
376                         i = hilightboxes.begin();
377                         i != hilightboxes.end(); i++) {
378                 driver->draw3DBox(*i, selectionbox_argb);
379         }
380 }
381
382
383 void Hud::resizeHotbar() {
384         if (screensize.Y <= 800)
385                 hotbar_imagesize = 32;
386         else if (screensize.Y <= 1280)
387                 hotbar_imagesize = 48;
388         else
389                 hotbar_imagesize = 64;
390 }
391
392 void drawItemStack(video::IVideoDriver *driver,
393                 gui::IGUIFont *font,
394                 const ItemStack &item,
395                 const core::rect<s32> &rect,
396                 const core::rect<s32> *clip,
397                 IGameDef *gamedef)
398 {
399         if(item.empty())
400                 return;
401         
402         const ItemDefinition &def = item.getDefinition(gamedef->idef());
403         video::ITexture *texture = gamedef->idef()->getInventoryTexture(def.name, gamedef);
404
405         // Draw the inventory texture
406         if(texture != NULL)
407         {
408                 const video::SColor color(255,255,255,255);
409                 const video::SColor colors[] = {color,color,color,color};
410                 driver->draw2DImage(texture, rect,
411                         core::rect<s32>(core::position2d<s32>(0,0),
412                         core::dimension2di(texture->getOriginalSize())),
413                         clip, colors, true);
414         }
415
416         if(def.type == ITEM_TOOL && item.wear != 0)
417         {
418                 // Draw a progressbar
419                 float barheight = rect.getHeight()/16;
420                 float barpad_x = rect.getWidth()/16;
421                 float barpad_y = rect.getHeight()/16;
422                 core::rect<s32> progressrect(
423                         rect.UpperLeftCorner.X + barpad_x,
424                         rect.LowerRightCorner.Y - barpad_y - barheight,
425                         rect.LowerRightCorner.X - barpad_x,
426                         rect.LowerRightCorner.Y - barpad_y);
427
428                 // Shrink progressrect by amount of tool damage
429                 float wear = item.wear / 65535.0;
430                 int progressmid =
431                         wear * progressrect.UpperLeftCorner.X +
432                         (1-wear) * progressrect.LowerRightCorner.X;
433
434                 // Compute progressbar color
435                 //   wear = 0.0: green
436                 //   wear = 0.5: yellow
437                 //   wear = 1.0: red
438                 video::SColor color(255,255,255,255);
439                 int wear_i = MYMIN(floor(wear * 600), 511);
440                 wear_i = MYMIN(wear_i + 10, 511);
441                 if(wear_i <= 255)
442                         color.set(255, wear_i, 255, 0);
443                 else
444                         color.set(255, 255, 511-wear_i, 0);
445
446                 core::rect<s32> progressrect2 = progressrect;
447                 progressrect2.LowerRightCorner.X = progressmid;
448                 driver->draw2DRectangle(color, progressrect2, clip);
449
450                 color = video::SColor(255,0,0,0);
451                 progressrect2 = progressrect;
452                 progressrect2.UpperLeftCorner.X = progressmid;
453                 driver->draw2DRectangle(color, progressrect2, clip);
454         }
455
456         if(font != NULL && item.count >= 2)
457         {
458                 // Get the item count as a string
459                 std::string text = itos(item.count);
460                 v2u32 dim = font->getDimension(narrow_to_wide(text).c_str());
461                 v2s32 sdim(dim.X,dim.Y);
462
463                 core::rect<s32> rect2(
464                         /*rect.UpperLeftCorner,
465                         core::dimension2d<u32>(rect.getWidth(), 15)*/
466                         rect.LowerRightCorner - sdim,
467                         sdim
468                 );
469
470                 video::SColor bgcolor(128,0,0,0);
471                 driver->draw2DRectangle(bgcolor, rect2, clip);
472
473                 video::SColor color(255,255,255,255);
474                 font->draw(text.c_str(), rect2, color, false, false, clip);
475         }
476 }
477