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