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