]> git.lizzy.rs Git - dragonfireclient.git/blob - src/hud.cpp
make formspec textarea wordwrap
[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                 //silently ignore this we may not be initialized completely
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                 player->camera_mode == CAMERA_MODE_THIRD_FRONT)
389                 return;
390                 
391         if (use_crosshair_image) {
392                 video::ITexture *crosshair = tsrc->getTexture("crosshair.png");
393                 v2u32 size  = crosshair->getOriginalSize();
394                 v2s32 lsize = v2s32(displaycenter.X - (size.X / 2),
395                                                         displaycenter.Y - (size.Y / 2));
396                 driver->draw2DImage(crosshair, lsize,
397                                 core::rect<s32>(0, 0, size.X, size.Y),
398                                 0, crosshair_argb, true);
399         } else {
400                 driver->draw2DLine(displaycenter - v2s32(10, 0),
401                                 displaycenter + v2s32(10, 0), crosshair_argb);
402                 driver->draw2DLine(displaycenter - v2s32(0, 10),
403                                 displaycenter + v2s32(0, 10), crosshair_argb);
404         }
405 }
406
407
408 void Hud::drawSelectionBoxes(std::vector<aabb3f> &hilightboxes) {
409         for (std::vector<aabb3f>::const_iterator
410                         i = hilightboxes.begin();
411                         i != hilightboxes.end(); i++) {
412                 driver->draw3DBox(*i, selectionbox_argb);
413         }
414 }
415
416
417 void Hud::resizeHotbar() {
418         if (screensize.Y <= 800)
419                 hotbar_imagesize = 32;
420         else if (screensize.Y <= 1280)
421                 hotbar_imagesize = 48;
422         else
423                 hotbar_imagesize = 64;
424 }
425
426 void drawItemStack(video::IVideoDriver *driver,
427                 gui::IGUIFont *font,
428                 const ItemStack &item,
429                 const core::rect<s32> &rect,
430                 const core::rect<s32> *clip,
431                 IGameDef *gamedef)
432 {
433         if(item.empty())
434                 return;
435         
436         const ItemDefinition &def = item.getDefinition(gamedef->idef());
437         video::ITexture *texture = gamedef->idef()->getInventoryTexture(def.name, gamedef);
438
439         // Draw the inventory texture
440         if(texture != NULL)
441         {
442                 const video::SColor color(255,255,255,255);
443                 const video::SColor colors[] = {color,color,color,color};
444                 driver->draw2DImage(texture, rect,
445                         core::rect<s32>(core::position2d<s32>(0,0),
446                         core::dimension2di(texture->getOriginalSize())),
447                         clip, colors, true);
448         }
449
450         if(def.type == ITEM_TOOL && item.wear != 0)
451         {
452                 // Draw a progressbar
453                 float barheight = rect.getHeight()/16;
454                 float barpad_x = rect.getWidth()/16;
455                 float barpad_y = rect.getHeight()/16;
456                 core::rect<s32> progressrect(
457                         rect.UpperLeftCorner.X + barpad_x,
458                         rect.LowerRightCorner.Y - barpad_y - barheight,
459                         rect.LowerRightCorner.X - barpad_x,
460                         rect.LowerRightCorner.Y - barpad_y);
461
462                 // Shrink progressrect by amount of tool damage
463                 float wear = item.wear / 65535.0;
464                 int progressmid =
465                         wear * progressrect.UpperLeftCorner.X +
466                         (1-wear) * progressrect.LowerRightCorner.X;
467
468                 // Compute progressbar color
469                 //   wear = 0.0: green
470                 //   wear = 0.5: yellow
471                 //   wear = 1.0: red
472                 video::SColor color(255,255,255,255);
473                 int wear_i = MYMIN(floor(wear * 600), 511);
474                 wear_i = MYMIN(wear_i + 10, 511);
475                 if(wear_i <= 255)
476                         color.set(255, wear_i, 255, 0);
477                 else
478                         color.set(255, 255, 511-wear_i, 0);
479
480                 core::rect<s32> progressrect2 = progressrect;
481                 progressrect2.LowerRightCorner.X = progressmid;
482                 driver->draw2DRectangle(color, progressrect2, clip);
483
484                 color = video::SColor(255,0,0,0);
485                 progressrect2 = progressrect;
486                 progressrect2.UpperLeftCorner.X = progressmid;
487                 driver->draw2DRectangle(color, progressrect2, clip);
488         }
489
490         if(font != NULL && item.count >= 2)
491         {
492                 // Get the item count as a string
493                 std::string text = itos(item.count);
494                 v2u32 dim = font->getDimension(narrow_to_wide(text).c_str());
495                 v2s32 sdim(dim.X,dim.Y);
496
497                 core::rect<s32> rect2(
498                         /*rect.UpperLeftCorner,
499                         core::dimension2d<u32>(rect.getWidth(), 15)*/
500                         rect.LowerRightCorner - sdim,
501                         sdim
502                 );
503
504                 video::SColor bgcolor(128,0,0,0);
505                 driver->draw2DRectangle(bgcolor, rect2, clip);
506
507                 video::SColor color(255,255,255,255);
508                 font->draw(text.c_str(), rect2, color, false, false, clip);
509         }
510 }