]> git.lizzy.rs Git - dragonfireclient.git/blob - src/game.cpp
Implement formspec
[dragonfireclient.git] / src / game.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "game.h"
21 #include "irrlichttypes_extrabloated.h"
22 #include <IGUICheckBox.h>
23 #include <IGUIEditBox.h>
24 #include <IGUIButton.h>
25 #include <IGUIStaticText.h>
26 #include <IGUIFont.h>
27 #include "client.h"
28 #include "server.h"
29 #include "guiPauseMenu.h"
30 #include "guiPasswordChange.h"
31 #include "guiFormSpecMenu.h"
32 #include "guiTextInputMenu.h"
33 #include "guiDeathScreen.h"
34 #include "tool.h"
35 #include "guiChatConsole.h"
36 #include "config.h"
37 #include "clouds.h"
38 #include "camera.h"
39 #include "farmesh.h"
40 #include "mapblock.h"
41 #include "settings.h"
42 #include "profiler.h"
43 #include "mainmenumanager.h"
44 #include "gettext.h"
45 #include "log.h"
46 #include "filesys.h"
47 // Needed for determining pointing to nodes
48 #include "nodedef.h"
49 #include "nodemetadata.h"
50 #include "main.h" // For g_settings
51 #include "itemdef.h"
52 #include "tile.h" // For TextureSource
53 #include "logoutputbuffer.h"
54 #include "subgame.h"
55 #include "quicktune_shortcutter.h"
56 #include "clientmap.h"
57 #include "sky.h"
58 #include "sound.h"
59 #if USE_SOUND
60         #include "sound_openal.h"
61 #endif
62 #include "event_manager.h"
63 #include <list>
64 #include "util/directiontables.h"
65
66 /*
67         Text input system
68 */
69
70 struct TextDestChat : public TextDest
71 {
72         TextDestChat(Client *client)
73         {
74                 m_client = client;
75         }
76         void gotText(std::wstring text)
77         {
78                 m_client->typeChatMessage(text);
79         }
80         void gotText(std::map<std::string, std::string> fields)
81         {
82                 m_client->typeChatMessage(narrow_to_wide(fields["text"]));
83         }
84
85         Client *m_client;
86 };
87
88 struct TextDestNodeMetadata : public TextDest
89 {
90         TextDestNodeMetadata(v3s16 p, Client *client)
91         {
92                 m_p = p;
93                 m_client = client;
94         }
95         // This is deprecated I guess? -celeron55
96         void gotText(std::wstring text)
97         {
98                 std::string ntext = wide_to_narrow(text);
99                 infostream<<"Submitting 'text' field of node at ("<<m_p.X<<","
100                                 <<m_p.Y<<","<<m_p.Z<<"): "<<ntext<<std::endl;
101                 std::map<std::string, std::string> fields;
102                 fields["text"] = ntext;
103                 m_client->sendNodemetaFields(m_p, "", fields);
104         }
105         void gotText(std::map<std::string, std::string> fields)
106         {
107                 m_client->sendNodemetaFields(m_p, "", fields);
108         }
109
110         v3s16 m_p;
111         Client *m_client;
112 };
113
114 /* Respawn menu callback */
115
116 class MainRespawnInitiator: public IRespawnInitiator
117 {
118 public:
119         MainRespawnInitiator(bool *active, Client *client):
120                 m_active(active), m_client(client)
121         {
122                 *m_active = true;
123         }
124         void respawn()
125         {
126                 *m_active = false;
127                 m_client->sendRespawn();
128         }
129 private:
130         bool *m_active;
131         Client *m_client;
132 };
133
134 /* Form update callback */
135
136 class NodeMetadataFormSource: public IFormSource
137 {
138 public:
139         NodeMetadataFormSource(ClientMap *map, v3s16 p):
140                 m_map(map),
141                 m_p(p)
142         {
143         }
144         std::string getForm()
145         {
146                 NodeMetadata *meta = m_map->getNodeMetadata(m_p);
147                 if(!meta)
148                         return "";
149                 return meta->getString("formspec");
150         }
151         std::string resolveText(std::string str)
152         {
153                 NodeMetadata *meta = m_map->getNodeMetadata(m_p);
154                 if(!meta)
155                         return str;
156                 return meta->resolveString(str);
157         }
158
159         ClientMap *m_map;
160         v3s16 m_p;
161 };
162
163 class PlayerInventoryFormSource: public IFormSource
164 {
165 public:
166         PlayerInventoryFormSource(Client *client):
167                 m_client(client)
168         {
169         }
170         std::string getForm()
171         {
172                 LocalPlayer* player = m_client->getEnv().getLocalPlayer();
173                 return player->inventory_formspec;
174         }
175
176         Client *m_client;
177 };
178
179 /*
180         Hotbar draw routine
181 */
182 void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font,
183                 IGameDef *gamedef,
184                 v2s32 centerlowerpos, s32 imgsize, s32 itemcount,
185                 Inventory *inventory, s32 halfheartcount, u16 playeritem)
186 {
187         InventoryList *mainlist = inventory->getList("main");
188         if(mainlist == NULL)
189         {
190                 errorstream<<"draw_hotbar(): mainlist == NULL"<<std::endl;
191                 return;
192         }
193         
194         s32 padding = imgsize/12;
195         //s32 height = imgsize + padding*2;
196         s32 width = itemcount*(imgsize+padding*2);
197         
198         // Position of upper left corner of bar
199         v2s32 pos = centerlowerpos - v2s32(width/2, imgsize+padding*2);
200         
201         // Draw background color
202         /*core::rect<s32> barrect(0,0,width,height);
203         barrect += pos;
204         video::SColor bgcolor(255,128,128,128);
205         driver->draw2DRectangle(bgcolor, barrect, NULL);*/
206
207         core::rect<s32> imgrect(0,0,imgsize,imgsize);
208
209         for(s32 i=0; i<itemcount; i++)
210         {
211                 const ItemStack &item = mainlist->getItem(i);
212                 
213                 core::rect<s32> rect = imgrect + pos
214                                 + v2s32(padding+i*(imgsize+padding*2), padding);
215                 
216                 if(playeritem == i)
217                 {
218                         video::SColor c_outside(255,255,0,0);
219                         //video::SColor c_outside(255,0,0,0);
220                         //video::SColor c_inside(255,192,192,192);
221                         s32 x1 = rect.UpperLeftCorner.X;
222                         s32 y1 = rect.UpperLeftCorner.Y;
223                         s32 x2 = rect.LowerRightCorner.X;
224                         s32 y2 = rect.LowerRightCorner.Y;
225                         // Black base borders
226                         driver->draw2DRectangle(c_outside,
227                                         core::rect<s32>(
228                                                 v2s32(x1 - padding, y1 - padding),
229                                                 v2s32(x2 + padding, y1)
230                                         ), NULL);
231                         driver->draw2DRectangle(c_outside,
232                                         core::rect<s32>(
233                                                 v2s32(x1 - padding, y2),
234                                                 v2s32(x2 + padding, y2 + padding)
235                                         ), NULL);
236                         driver->draw2DRectangle(c_outside,
237                                         core::rect<s32>(
238                                                 v2s32(x1 - padding, y1),
239                                                 v2s32(x1, y2)
240                                         ), NULL);
241                         driver->draw2DRectangle(c_outside,
242                                         core::rect<s32>(
243                                                 v2s32(x2, y1),
244                                                 v2s32(x2 + padding, y2)
245                                         ), NULL);
246                         /*// Light inside borders
247                         driver->draw2DRectangle(c_inside,
248                                         core::rect<s32>(
249                                                 v2s32(x1 - padding/2, y1 - padding/2),
250                                                 v2s32(x2 + padding/2, y1)
251                                         ), NULL);
252                         driver->draw2DRectangle(c_inside,
253                                         core::rect<s32>(
254                                                 v2s32(x1 - padding/2, y2),
255                                                 v2s32(x2 + padding/2, y2 + padding/2)
256                                         ), NULL);
257                         driver->draw2DRectangle(c_inside,
258                                         core::rect<s32>(
259                                                 v2s32(x1 - padding/2, y1),
260                                                 v2s32(x1, y2)
261                                         ), NULL);
262                         driver->draw2DRectangle(c_inside,
263                                         core::rect<s32>(
264                                                 v2s32(x2, y1),
265                                                 v2s32(x2 + padding/2, y2)
266                                         ), NULL);
267                         */
268                 }
269
270                 video::SColor bgcolor2(128,0,0,0);
271                 driver->draw2DRectangle(bgcolor2, rect, NULL);
272                 drawItemStack(driver, font, item, rect, NULL, gamedef);
273         }
274         
275         /*
276                 Draw hearts
277         */
278         video::ITexture *heart_texture =
279                 gamedef->getTextureSource()->getTextureRaw("heart.png");
280         if(heart_texture)
281         {
282                 v2s32 p = pos + v2s32(0, -20);
283                 for(s32 i=0; i<halfheartcount/2; i++)
284                 {
285                         const video::SColor color(255,255,255,255);
286                         const video::SColor colors[] = {color,color,color,color};
287                         core::rect<s32> rect(0,0,16,16);
288                         rect += p;
289                         driver->draw2DImage(heart_texture, rect,
290                                 core::rect<s32>(core::position2d<s32>(0,0),
291                                 core::dimension2di(heart_texture->getOriginalSize())),
292                                 NULL, colors, true);
293                         p += v2s32(16,0);
294                 }
295                 if(halfheartcount % 2 == 1)
296                 {
297                         const video::SColor color(255,255,255,255);
298                         const video::SColor colors[] = {color,color,color,color};
299                         core::rect<s32> rect(0,0,16/2,16);
300                         rect += p;
301                         core::dimension2di srcd(heart_texture->getOriginalSize());
302                         srcd.Width /= 2;
303                         driver->draw2DImage(heart_texture, rect,
304                                 core::rect<s32>(core::position2d<s32>(0,0), srcd),
305                                 NULL, colors, true);
306                         p += v2s32(16,0);
307                 }
308         }
309 }
310
311 /*
312         Check if a node is pointable
313 */
314 inline bool isPointableNode(const MapNode& n,
315                 Client *client, bool liquids_pointable)
316 {
317         const ContentFeatures &features = client->getNodeDefManager()->get(n);
318         return features.pointable ||
319                 (liquids_pointable && features.isLiquid());
320 }
321
322 /*
323         Find what the player is pointing at
324 */
325 PointedThing getPointedThing(Client *client, v3f player_position,
326                 v3f camera_direction, v3f camera_position,
327                 core::line3d<f32> shootline, f32 d,
328                 bool liquids_pointable,
329                 bool look_for_object,
330                 std::vector<aabb3f> &hilightboxes,
331                 ClientActiveObject *&selected_object)
332 {
333         PointedThing result;
334
335         hilightboxes.clear();
336         selected_object = NULL;
337
338         INodeDefManager *nodedef = client->getNodeDefManager();
339         ClientMap &map = client->getEnv().getClientMap();
340
341         // First try to find a pointed at active object
342         if(look_for_object)
343         {
344                 selected_object = client->getSelectedActiveObject(d*BS,
345                                 camera_position, shootline);
346
347                 if(selected_object != NULL)
348                 {
349                         if(selected_object->doShowSelectionBox())
350                         {
351                                 aabb3f *selection_box = selected_object->getSelectionBox();
352                                 // Box should exist because object was
353                                 // returned in the first place
354                                 assert(selection_box);
355
356                                 v3f pos = selected_object->getPosition();
357                                 hilightboxes.push_back(aabb3f(
358                                                 selection_box->MinEdge + pos,
359                                                 selection_box->MaxEdge + pos));
360                         }
361
362
363                         result.type = POINTEDTHING_OBJECT;
364                         result.object_id = selected_object->getId();
365                         return result;
366                 }
367         }
368
369         // That didn't work, try to find a pointed at node
370
371         f32 mindistance = BS * 1001;
372         
373         v3s16 pos_i = floatToInt(player_position, BS);
374
375         /*infostream<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
376                         <<std::endl;*/
377
378         s16 a = d;
379         s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
380         s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
381         s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
382         s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
383         s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
384         s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
385         
386         // Prevent signed number overflow
387         if(yend==32767)
388                 yend=32766;
389         if(zend==32767)
390                 zend=32766;
391         if(xend==32767)
392                 xend=32766;
393
394         for(s16 y = ystart; y <= yend; y++)
395         for(s16 z = zstart; z <= zend; z++)
396         for(s16 x = xstart; x <= xend; x++)
397         {
398                 MapNode n;
399                 try
400                 {
401                         n = map.getNode(v3s16(x,y,z));
402                 }
403                 catch(InvalidPositionException &e)
404                 {
405                         continue;
406                 }
407                 if(!isPointableNode(n, client, liquids_pointable))
408                         continue;
409
410                 std::vector<aabb3f> boxes = n.getSelectionBoxes(nodedef);
411
412                 v3s16 np(x,y,z);
413                 v3f npf = intToFloat(np, BS);
414
415                 for(std::vector<aabb3f>::const_iterator
416                                 i = boxes.begin();
417                                 i != boxes.end(); i++)
418                 {
419                         aabb3f box = *i;
420                         box.MinEdge += npf;
421                         box.MaxEdge += npf;
422
423                         for(u16 j=0; j<6; j++)
424                         {
425                                 v3s16 facedir = g_6dirs[j];
426                                 aabb3f facebox = box;
427
428                                 f32 d = 0.001*BS;
429                                 if(facedir.X > 0)
430                                         facebox.MinEdge.X = facebox.MaxEdge.X-d;
431                                 else if(facedir.X < 0)
432                                         facebox.MaxEdge.X = facebox.MinEdge.X+d;
433                                 else if(facedir.Y > 0)
434                                         facebox.MinEdge.Y = facebox.MaxEdge.Y-d;
435                                 else if(facedir.Y < 0)
436                                         facebox.MaxEdge.Y = facebox.MinEdge.Y+d;
437                                 else if(facedir.Z > 0)
438                                         facebox.MinEdge.Z = facebox.MaxEdge.Z-d;
439                                 else if(facedir.Z < 0)
440                                         facebox.MaxEdge.Z = facebox.MinEdge.Z+d;
441
442                                 v3f centerpoint = facebox.getCenter();
443                                 f32 distance = (centerpoint - camera_position).getLength();
444                                 if(distance >= mindistance)
445                                         continue;
446                                 if(!facebox.intersectsWithLine(shootline))
447                                         continue;
448
449                                 v3s16 np_above = np + facedir;
450
451                                 result.type = POINTEDTHING_NODE;
452                                 result.node_undersurface = np;
453                                 result.node_abovesurface = np_above;
454                                 mindistance = distance;
455
456                                 hilightboxes.clear();
457                                 for(std::vector<aabb3f>::const_iterator
458                                                 i2 = boxes.begin();
459                                                 i2 != boxes.end(); i2++)
460                                 {
461                                         aabb3f box = *i2;
462                                         box.MinEdge += npf + v3f(-d,-d,-d);
463                                         box.MaxEdge += npf + v3f(d,d,d);
464                                         hilightboxes.push_back(box);
465                                 }
466                         }
467                 }
468         } // for coords
469
470         return result;
471 }
472
473 /*
474         Draws a screen with a single text on it.
475         Text will be removed when the screen is drawn the next time.
476 */
477 /*gui::IGUIStaticText **/
478 void draw_load_screen(const std::wstring &text,
479                 video::IVideoDriver* driver, gui::IGUIFont* font)
480 {
481         v2u32 screensize = driver->getScreenSize();
482         const wchar_t *loadingtext = text.c_str();
483         core::vector2d<u32> textsize_u = font->getDimension(loadingtext);
484         core::vector2d<s32> textsize(textsize_u.X,textsize_u.Y);
485         core::vector2d<s32> center(screensize.X/2, screensize.Y/2);
486         core::rect<s32> textrect(center - textsize/2, center + textsize/2);
487
488         gui::IGUIStaticText *guitext = guienv->addStaticText(
489                         loadingtext, textrect, false, false);
490         guitext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
491
492         driver->beginScene(true, true, video::SColor(255,0,0,0));
493         guienv->drawAll();
494         driver->endScene();
495         
496         guitext->remove();
497         
498         //return guitext;
499 }
500
501 /* Profiler display */
502
503 void update_profiler_gui(gui::IGUIStaticText *guitext_profiler,
504                 gui::IGUIFont *font, u32 text_height,
505                 u32 show_profiler, u32 show_profiler_max)
506 {
507         if(show_profiler == 0)
508         {
509                 guitext_profiler->setVisible(false);
510         }
511         else
512         {
513
514                 std::ostringstream os(std::ios_base::binary);
515                 g_profiler->printPage(os, show_profiler, show_profiler_max);
516                 std::wstring text = narrow_to_wide(os.str());
517                 guitext_profiler->setText(text.c_str());
518                 guitext_profiler->setVisible(true);
519
520                 s32 w = font->getDimension(text.c_str()).Width;
521                 if(w < 400)
522                         w = 400;
523                 core::rect<s32> rect(6, 4+(text_height+5)*2, 12+w,
524                                 8+(text_height+5)*2 +
525                                 font->getDimension(text.c_str()).Height);
526                 guitext_profiler->setRelativePosition(rect);
527                 guitext_profiler->setVisible(true);
528         }
529 }
530
531 class ProfilerGraph
532 {
533 private:
534         struct Piece{
535                 Profiler::GraphValues values;
536         };
537         struct Meta{
538                 float min;
539                 float max;
540                 video::SColor color;
541                 Meta(float initial=0, video::SColor color=
542                                 video::SColor(255,255,255,255)):
543                         min(initial),
544                         max(initial),
545                         color(color)
546                 {}
547         };
548         std::list<Piece> m_log;
549 public:
550         u32 m_log_max_size;
551
552         ProfilerGraph():
553                 m_log_max_size(200)
554         {}
555
556         void put(const Profiler::GraphValues &values)
557         {
558                 Piece piece;
559                 piece.values = values;
560                 m_log.push_back(piece);
561                 while(m_log.size() > m_log_max_size)
562                         m_log.erase(m_log.begin());
563         }
564         
565         void draw(s32 x_left, s32 y_bottom, video::IVideoDriver *driver,
566                         gui::IGUIFont* font) const
567         {
568                 std::map<std::string, Meta> m_meta;
569                 for(std::list<Piece>::const_iterator k = m_log.begin();
570                                 k != m_log.end(); k++)
571                 {
572                         const Piece &piece = *k;
573                         for(Profiler::GraphValues::const_iterator i = piece.values.begin();
574                                         i != piece.values.end(); i++){
575                                 const std::string &id = i->first;
576                                 const float &value = i->second;
577                                 std::map<std::string, Meta>::iterator j =
578                                                 m_meta.find(id);
579                                 if(j == m_meta.end()){
580                                         m_meta[id] = Meta(value);
581                                         continue;
582                                 }
583                                 if(value < j->second.min)
584                                         j->second.min = value;
585                                 if(value > j->second.max)
586                                         j->second.max = value;
587                         }
588                 }
589
590                 // Assign colors
591                 static const video::SColor usable_colors[] = {
592                         video::SColor(255,255,100,100),
593                         video::SColor(255,90,225,90),
594                         video::SColor(255,100,100,255),
595                         video::SColor(255,255,150,50),
596                         video::SColor(255,220,220,100)
597                 };
598                 static const u32 usable_colors_count =
599                                 sizeof(usable_colors) / sizeof(*usable_colors);
600                 u32 next_color_i = 0;
601                 for(std::map<std::string, Meta>::iterator i = m_meta.begin();
602                                 i != m_meta.end(); i++){
603                         Meta &meta = i->second;
604                         video::SColor color(255,200,200,200);
605                         if(next_color_i < usable_colors_count)
606                                 color = usable_colors[next_color_i++];
607                         meta.color = color;
608                 }
609
610                 s32 graphh = 50;
611                 s32 textx = x_left + m_log_max_size + 15;
612                 s32 textx2 = textx + 200 - 15;
613                 
614                 // Draw background
615                 /*{
616                         u32 num_graphs = m_meta.size();
617                         core::rect<s32> rect(x_left, y_bottom - num_graphs*graphh,
618                                         textx2, y_bottom);
619                         video::SColor bgcolor(120,0,0,0);
620                         driver->draw2DRectangle(bgcolor, rect, NULL);
621                 }*/
622                 
623                 s32 meta_i = 0;
624                 for(std::map<std::string, Meta>::const_iterator i = m_meta.begin();
625                                 i != m_meta.end(); i++){
626                         const std::string &id = i->first;
627                         const Meta &meta = i->second;
628                         s32 x = x_left;
629                         s32 y = y_bottom - meta_i * 50;
630                         float show_min = meta.min;
631                         float show_max = meta.max;
632                         if(show_min >= -0.0001 && show_max >= -0.0001){
633                                 if(show_min <= show_max * 0.5)
634                                         show_min = 0;
635                         }
636                         s32 texth = 15;
637                         char buf[10];
638                         snprintf(buf, 10, "%.3g", show_max);
639                         font->draw(narrow_to_wide(buf).c_str(),
640                                         core::rect<s32>(textx, y - graphh,
641                                         textx2, y - graphh + texth),
642                                         meta.color);
643                         snprintf(buf, 10, "%.3g", show_min);
644                         font->draw(narrow_to_wide(buf).c_str(),
645                                         core::rect<s32>(textx, y - texth,
646                                         textx2, y),
647                                         meta.color);
648                         font->draw(narrow_to_wide(id).c_str(),
649                                         core::rect<s32>(textx, y - graphh/2 - texth/2,
650                                         textx2, y - graphh/2 + texth/2),
651                                         meta.color);
652                         s32 graph1y = y;
653                         s32 graph1h = graphh;
654                         bool relativegraph = (show_min != 0 && show_min != show_max);
655                         float lastscaledvalue = 0.0;
656                         bool lastscaledvalue_exists = false;
657                         for(std::list<Piece>::const_iterator j = m_log.begin();
658                                         j != m_log.end(); j++)
659                         {
660                                 const Piece &piece = *j;
661                                 float value = 0;
662                                 bool value_exists = false;
663                                 Profiler::GraphValues::const_iterator k =
664                                                 piece.values.find(id);
665                                 if(k != piece.values.end()){
666                                         value = k->second;
667                                         value_exists = true;
668                                 }
669                                 if(!value_exists){
670                                         x++;
671                                         lastscaledvalue_exists = false;
672                                         continue;
673                                 }
674                                 float scaledvalue = 1.0;
675                                 if(show_max != show_min)
676                                         scaledvalue = (value - show_min) / (show_max - show_min);
677                                 if(scaledvalue == 1.0 && value == 0){
678                                         x++;
679                                         lastscaledvalue_exists = false;
680                                         continue;
681                                 }
682                                 if(relativegraph){
683                                         if(lastscaledvalue_exists){
684                                                 s32 ivalue1 = lastscaledvalue * graph1h;
685                                                 s32 ivalue2 = scaledvalue * graph1h;
686                                                 driver->draw2DLine(v2s32(x-1, graph1y - ivalue1),
687                                                                 v2s32(x, graph1y - ivalue2), meta.color);
688                                         }
689                                         lastscaledvalue = scaledvalue;
690                                         lastscaledvalue_exists = true;
691                                 } else{
692                                         s32 ivalue = scaledvalue * graph1h;
693                                         driver->draw2DLine(v2s32(x, graph1y),
694                                                         v2s32(x, graph1y - ivalue), meta.color);
695                                 }
696                                 x++;
697                         }
698                         meta_i++;
699                 }
700         }
701 };
702
703 class NodeDugEvent: public MtEvent
704 {
705 public:
706         v3s16 p;
707         MapNode n;
708         
709         NodeDugEvent(v3s16 p, MapNode n):
710                 p(p),
711                 n(n)
712         {}
713         const char* getType() const
714         {return "NodeDug";}
715 };
716
717 class SoundMaker
718 {
719         ISoundManager *m_sound;
720         INodeDefManager *m_ndef;
721 public:
722         float m_player_step_timer;
723
724         SimpleSoundSpec m_player_step_sound;
725         SimpleSoundSpec m_player_leftpunch_sound;
726         SimpleSoundSpec m_player_rightpunch_sound;
727
728         SoundMaker(ISoundManager *sound, INodeDefManager *ndef):
729                 m_sound(sound),
730                 m_ndef(ndef),
731                 m_player_step_timer(0)
732         {
733         }
734
735         void playPlayerStep()
736         {
737                 if(m_player_step_timer <= 0 && m_player_step_sound.exists()){
738                         m_player_step_timer = 0.03;
739                         m_sound->playSound(m_player_step_sound, false);
740                 }
741         }
742
743         static void viewBobbingStep(MtEvent *e, void *data)
744         {
745                 SoundMaker *sm = (SoundMaker*)data;
746                 sm->playPlayerStep();
747         }
748
749         static void playerRegainGround(MtEvent *e, void *data)
750         {
751                 SoundMaker *sm = (SoundMaker*)data;
752                 sm->playPlayerStep();
753         }
754
755         static void playerJump(MtEvent *e, void *data)
756         {
757                 //SoundMaker *sm = (SoundMaker*)data;
758         }
759
760         static void cameraPunchLeft(MtEvent *e, void *data)
761         {
762                 SoundMaker *sm = (SoundMaker*)data;
763                 sm->m_sound->playSound(sm->m_player_leftpunch_sound, false);
764         }
765
766         static void cameraPunchRight(MtEvent *e, void *data)
767         {
768                 SoundMaker *sm = (SoundMaker*)data;
769                 sm->m_sound->playSound(sm->m_player_rightpunch_sound, false);
770         }
771
772         static void nodeDug(MtEvent *e, void *data)
773         {
774                 SoundMaker *sm = (SoundMaker*)data;
775                 NodeDugEvent *nde = (NodeDugEvent*)e;
776                 sm->m_sound->playSound(sm->m_ndef->get(nde->n).sound_dug, false);
777         }
778
779         void registerReceiver(MtEventManager *mgr)
780         {
781                 mgr->reg("ViewBobbingStep", SoundMaker::viewBobbingStep, this);
782                 mgr->reg("PlayerRegainGround", SoundMaker::playerRegainGround, this);
783                 mgr->reg("PlayerJump", SoundMaker::playerJump, this);
784                 mgr->reg("CameraPunchLeft", SoundMaker::cameraPunchLeft, this);
785                 mgr->reg("CameraPunchRight", SoundMaker::cameraPunchRight, this);
786                 mgr->reg("NodeDug", SoundMaker::nodeDug, this);
787         }
788
789         void step(float dtime)
790         {
791                 m_player_step_timer -= dtime;
792         }
793 };
794
795 // Locally stored sounds don't need to be preloaded because of this
796 class GameOnDemandSoundFetcher: public OnDemandSoundFetcher
797 {
798         std::set<std::string> m_fetched;
799 public:
800
801         void fetchSounds(const std::string &name,
802                         std::set<std::string> &dst_paths,
803                         std::set<std::string> &dst_datas)
804         {
805                 if(m_fetched.count(name))
806                         return;
807                 m_fetched.insert(name);
808                 std::string base = porting::path_share + DIR_DELIM + "testsounds";
809                 dst_paths.insert(base + DIR_DELIM + name + ".ogg");
810                 dst_paths.insert(base + DIR_DELIM + name + ".0.ogg");
811                 dst_paths.insert(base + DIR_DELIM + name + ".1.ogg");
812                 dst_paths.insert(base + DIR_DELIM + name + ".2.ogg");
813                 dst_paths.insert(base + DIR_DELIM + name + ".3.ogg");
814                 dst_paths.insert(base + DIR_DELIM + name + ".4.ogg");
815                 dst_paths.insert(base + DIR_DELIM + name + ".5.ogg");
816                 dst_paths.insert(base + DIR_DELIM + name + ".6.ogg");
817                 dst_paths.insert(base + DIR_DELIM + name + ".7.ogg");
818                 dst_paths.insert(base + DIR_DELIM + name + ".8.ogg");
819                 dst_paths.insert(base + DIR_DELIM + name + ".9.ogg");
820         }
821 };
822
823 void the_game(
824         bool &kill,
825         bool random_input,
826         InputHandler *input,
827         IrrlichtDevice *device,
828         gui::IGUIFont* font,
829         std::string map_dir,
830         std::string playername,
831         std::string password,
832         std::string address, // If "", local server is used
833         u16 port,
834         std::wstring &error_message,
835         std::string configpath,
836         ChatBackend &chat_backend,
837         const SubgameSpec &gamespec, // Used for local game,
838         bool simple_singleplayer_mode
839 )
840 {
841         video::IVideoDriver* driver = device->getVideoDriver();
842         scene::ISceneManager* smgr = device->getSceneManager();
843         
844         // Calculate text height using the font
845         u32 text_height = font->getDimension(L"Random test string").Height;
846
847         v2u32 screensize(0,0);
848         v2u32 last_screensize(0,0);
849         screensize = driver->getScreenSize();
850
851         const s32 hotbar_itemcount = 8;
852         //const s32 hotbar_imagesize = 36;
853         //const s32 hotbar_imagesize = 64;
854         s32 hotbar_imagesize = 48;
855         
856         /*
857                 Draw "Loading" screen
858         */
859
860         draw_load_screen(L"Loading...", driver, font);
861         
862         // Create texture source
863         IWritableTextureSource *tsrc = createTextureSource(device);
864         
865         // These will be filled by data received from the server
866         // Create item definition manager
867         IWritableItemDefManager *itemdef = createItemDefManager();
868         // Create node definition manager
869         IWritableNodeDefManager *nodedef = createNodeDefManager();
870         
871         // Sound fetcher (useful when testing)
872         GameOnDemandSoundFetcher soundfetcher;
873
874         // Sound manager
875         ISoundManager *sound = NULL;
876         bool sound_is_dummy = false;
877 #if USE_SOUND
878         if(g_settings->getBool("enable_sound")){
879                 infostream<<"Attempting to use OpenAL audio"<<std::endl;
880                 sound = createOpenALSoundManager(&soundfetcher);
881                 if(!sound)
882                         infostream<<"Failed to initialize OpenAL audio"<<std::endl;
883         } else {
884                 infostream<<"Sound disabled."<<std::endl;
885         }
886 #endif
887         if(!sound){
888                 infostream<<"Using dummy audio."<<std::endl;
889                 sound = &dummySoundManager;
890                 sound_is_dummy = true;
891         }
892
893         // Event manager
894         EventManager eventmgr;
895
896         // Sound maker
897         SoundMaker soundmaker(sound, nodedef);
898         soundmaker.registerReceiver(&eventmgr);
899         
900         // Add chat log output for errors to be shown in chat
901         LogOutputBuffer chat_log_error_buf(LMT_ERROR);
902
903         // Create UI for modifying quicktune values
904         QuicktuneShortcutter quicktune;
905
906         /*
907                 Create server.
908                 SharedPtr will delete it when it goes out of scope.
909         */
910         SharedPtr<Server> server;
911         if(address == ""){
912                 draw_load_screen(L"Creating server...", driver, font);
913                 infostream<<"Creating server"<<std::endl;
914                 server = new Server(map_dir, configpath, gamespec,
915                                 simple_singleplayer_mode);
916                 server->start(port);
917         }
918
919         try{
920         do{ // Client scope (breakable do-while(0))
921         
922         /*
923                 Create client
924         */
925
926         draw_load_screen(L"Creating client...", driver, font);
927         infostream<<"Creating client"<<std::endl;
928         
929         MapDrawControl draw_control;
930
931         Client client(device, playername.c_str(), password, draw_control,
932                         tsrc, itemdef, nodedef, sound, &eventmgr);
933         
934         // Client acts as our GameDef
935         IGameDef *gamedef = &client;
936                         
937         draw_load_screen(L"Resolving address...", driver, font);
938         Address connect_address(0,0,0,0, port);
939         try{
940                 if(address == "")
941                         //connect_address.Resolve("localhost");
942                         connect_address.setAddress(127,0,0,1);
943                 else
944                         connect_address.Resolve(address.c_str());
945         }
946         catch(ResolveError &e)
947         {
948                 error_message = L"Couldn't resolve address";
949                 errorstream<<wide_to_narrow(error_message)<<std::endl;
950                 // Break out of client scope
951                 break;
952         }
953
954         /*
955                 Attempt to connect to the server
956         */
957         
958         infostream<<"Connecting to server at ";
959         connect_address.print(&infostream);
960         infostream<<std::endl;
961         client.connect(connect_address);
962         
963         /*
964                 Wait for server to accept connection
965         */
966         bool could_connect = false;
967         bool connect_aborted = false;
968         try{
969                 float frametime = 0.033;
970                 float time_counter = 0.0;
971                 input->clear();
972                 while(device->run())
973                 {
974                         // Update client and server
975                         client.step(frametime);
976                         if(server != NULL)
977                                 server->step(frametime);
978                         
979                         // End condition
980                         if(client.connectedAndInitialized()){
981                                 could_connect = true;
982                                 break;
983                         }
984                         // Break conditions
985                         if(client.accessDenied()){
986                                 error_message = L"Access denied. Reason: "
987                                                 +client.accessDeniedReason();
988                                 errorstream<<wide_to_narrow(error_message)<<std::endl;
989                                 break;
990                         }
991                         if(input->wasKeyDown(EscapeKey)){
992                                 connect_aborted = true;
993                                 infostream<<"Connect aborted [Escape]"<<std::endl;
994                                 break;
995                         }
996                         
997                         // Display status
998                         std::wostringstream ss;
999                         ss<<L"Connecting to server... (press Escape to cancel)\n";
1000                         std::wstring animation = L"/-\\|";
1001                         ss<<animation[(int)(time_counter/0.2)%4];
1002                         draw_load_screen(ss.str(), driver, font);
1003                         
1004                         // Delay a bit
1005                         sleep_ms(1000*frametime);
1006                         time_counter += frametime;
1007                 }
1008         }
1009         catch(con::PeerNotFoundException &e)
1010         {}
1011         
1012         /*
1013                 Handle failure to connect
1014         */
1015         if(!could_connect){
1016                 if(error_message == L"" && !connect_aborted){
1017                         error_message = L"Connection failed";
1018                         errorstream<<wide_to_narrow(error_message)<<std::endl;
1019                 }
1020                 // Break out of client scope
1021                 break;
1022         }
1023         
1024         /*
1025                 Wait until content has been received
1026         */
1027         bool got_content = false;
1028         bool content_aborted = false;
1029         {
1030                 float frametime = 0.033;
1031                 float time_counter = 0.0;
1032                 input->clear();
1033                 while(device->run())
1034                 {
1035                         // Update client and server
1036                         client.step(frametime);
1037                         if(server != NULL)
1038                                 server->step(frametime);
1039                         
1040                         // End condition
1041                         if(client.texturesReceived() &&
1042                                         client.itemdefReceived() &&
1043                                         client.nodedefReceived()){
1044                                 got_content = true;
1045                                 break;
1046                         }
1047                         // Break conditions
1048                         if(!client.connectedAndInitialized()){
1049                                 error_message = L"Client disconnected";
1050                                 errorstream<<wide_to_narrow(error_message)<<std::endl;
1051                                 break;
1052                         }
1053                         if(input->wasKeyDown(EscapeKey)){
1054                                 content_aborted = true;
1055                                 infostream<<"Connect aborted [Escape]"<<std::endl;
1056                                 break;
1057                         }
1058                         
1059                         // Display status
1060                         std::wostringstream ss;
1061                         ss<<L"Waiting content... (press Escape to cancel)\n";
1062
1063                         ss<<(client.itemdefReceived()?L"[X]":L"[  ]");
1064                         ss<<L" Item definitions\n";
1065                         ss<<(client.nodedefReceived()?L"[X]":L"[  ]");
1066                         ss<<L" Node definitions\n";
1067                         ss<<L"["<<(int)(client.mediaReceiveProgress()*100+0.5)<<L"%] ";
1068                         ss<<L" Media\n";
1069
1070                         draw_load_screen(ss.str(), driver, font);
1071                         
1072                         // Delay a bit
1073                         sleep_ms(1000*frametime);
1074                         time_counter += frametime;
1075                 }
1076         }
1077
1078         if(!got_content){
1079                 if(error_message == L"" && !content_aborted){
1080                         error_message = L"Something failed";
1081                         errorstream<<wide_to_narrow(error_message)<<std::endl;
1082                 }
1083                 // Break out of client scope
1084                 break;
1085         }
1086
1087         /*
1088                 After all content has been received:
1089                 Update cached textures, meshes and materials
1090         */
1091         client.afterContentReceived();
1092
1093         /*
1094                 Create the camera node
1095         */
1096         Camera camera(smgr, draw_control, gamedef);
1097         if (!camera.successfullyCreated(error_message))
1098                 return;
1099
1100         f32 camera_yaw = 0; // "right/left"
1101         f32 camera_pitch = 0; // "up/down"
1102
1103         /*
1104                 Clouds
1105         */
1106         
1107         Clouds *clouds = NULL;
1108         if(g_settings->getBool("enable_clouds"))
1109         {
1110                 clouds = new Clouds(smgr->getRootSceneNode(), smgr, -1, time(0));
1111         }
1112
1113         /*
1114                 Skybox thingy
1115         */
1116
1117         Sky *sky = NULL;
1118         sky = new Sky(smgr->getRootSceneNode(), smgr, -1);
1119         
1120         /*
1121                 FarMesh
1122         */
1123
1124         FarMesh *farmesh = NULL;
1125         if(g_settings->getBool("enable_farmesh"))
1126         {
1127                 farmesh = new FarMesh(smgr->getRootSceneNode(), smgr, -1, client.getMapSeed(), &client);
1128         }
1129
1130         /*
1131                 A copy of the local inventory
1132         */
1133         Inventory local_inventory(itemdef);
1134
1135         /*
1136                 Find out size of crack animation
1137         */
1138         int crack_animation_length = 5;
1139         {
1140                 video::ITexture *t = tsrc->getTextureRaw("crack_anylength.png");
1141                 v2u32 size = t->getOriginalSize();
1142                 crack_animation_length = size.Y / size.X;
1143         }
1144
1145         /*
1146                 Add some gui stuff
1147         */
1148
1149         // First line of debug text
1150         gui::IGUIStaticText *guitext = guienv->addStaticText(
1151                         L"Minetest-c55",
1152                         core::rect<s32>(5, 5, 795, 5+text_height),
1153                         false, false);
1154         // Second line of debug text
1155         gui::IGUIStaticText *guitext2 = guienv->addStaticText(
1156                         L"",
1157                         core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),
1158                         false, false);
1159         // At the middle of the screen
1160         // Object infos are shown in this
1161         gui::IGUIStaticText *guitext_info = guienv->addStaticText(
1162                         L"",
1163                         core::rect<s32>(0,0,400,text_height*5+5) + v2s32(100,200),
1164                         false, false);
1165         
1166         // Status text (displays info when showing and hiding GUI stuff, etc.)
1167         gui::IGUIStaticText *guitext_status = guienv->addStaticText(
1168                         L"<Status>",
1169                         core::rect<s32>(0,0,0,0),
1170                         false, false);
1171         guitext_status->setVisible(false);
1172         
1173         std::wstring statustext;
1174         float statustext_time = 0;
1175         
1176         // Chat text
1177         gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
1178                         L"",
1179                         core::rect<s32>(0,0,0,0),
1180                         //false, false); // Disable word wrap as of now
1181                         false, true);
1182         // Remove stale "recent" chat messages from previous connections
1183         chat_backend.clearRecentChat();
1184         // Chat backend and console
1185         GUIChatConsole *gui_chat_console = new GUIChatConsole(guienv, guienv->getRootGUIElement(), -1, &chat_backend, &client);
1186         
1187         // Profiler text (size is updated when text is updated)
1188         gui::IGUIStaticText *guitext_profiler = guienv->addStaticText(
1189                         L"<Profiler>",
1190                         core::rect<s32>(0,0,0,0),
1191                         false, false);
1192         guitext_profiler->setBackgroundColor(video::SColor(120,0,0,0));
1193         guitext_profiler->setVisible(false);
1194         
1195         /*
1196                 Some statistics are collected in these
1197         */
1198         u32 drawtime = 0;
1199         u32 beginscenetime = 0;
1200         u32 scenetime = 0;
1201         u32 endscenetime = 0;
1202         
1203         float recent_turn_speed = 0.0;
1204         
1205         ProfilerGraph graph;
1206         // Initially clear the profiler
1207         Profiler::GraphValues dummyvalues;
1208         g_profiler->graphGet(dummyvalues);
1209
1210         float nodig_delay_timer = 0.0;
1211         float dig_time = 0.0;
1212         u16 dig_index = 0;
1213         PointedThing pointed_old;
1214         bool digging = false;
1215         bool ldown_for_dig = false;
1216
1217         float damage_flash_timer = 0;
1218         s16 farmesh_range = 20*MAP_BLOCKSIZE;
1219
1220         const float object_hit_delay = 0.2;
1221         float object_hit_delay_timer = 0.0;
1222         float time_from_last_punch = 10;
1223
1224         bool invert_mouse = g_settings->getBool("invert_mouse");
1225
1226         bool respawn_menu_active = false;
1227         bool update_wielded_item_trigger = false;
1228
1229         bool show_hud = true;
1230         bool show_chat = true;
1231         bool force_fog_off = false;
1232         bool disable_camera_update = false;
1233         bool show_debug = g_settings->getBool("show_debug");
1234         bool show_profiler_graph = false;
1235         u32 show_profiler = 0;
1236         u32 show_profiler_max = 3;  // Number of pages
1237
1238         float time_of_day = 0;
1239         float time_of_day_smooth = 0;
1240
1241         /*
1242                 Main loop
1243         */
1244
1245         bool first_loop_after_window_activation = true;
1246
1247         // TODO: Convert the static interval timers to these
1248         // Interval limiter for profiler
1249         IntervalLimiter m_profiler_interval;
1250
1251         // Time is in milliseconds
1252         // NOTE: getRealTime() causes strange problems in wine (imprecision?)
1253         // NOTE: So we have to use getTime() and call run()s between them
1254         u32 lasttime = device->getTimer()->getTime();
1255
1256         for(;;)
1257         {
1258                 if(device->run() == false || kill == true)
1259                         break;
1260
1261                 // Time of frame without fps limit
1262                 float busytime;
1263                 u32 busytime_u32;
1264                 {
1265                         // not using getRealTime is necessary for wine
1266                         u32 time = device->getTimer()->getTime();
1267                         if(time > lasttime)
1268                                 busytime_u32 = time - lasttime;
1269                         else
1270                                 busytime_u32 = 0;
1271                         busytime = busytime_u32 / 1000.0;
1272                 }
1273                 
1274                 g_profiler->graphAdd("mainloop_other", busytime - (float)drawtime/1000.0f);
1275
1276                 // Necessary for device->getTimer()->getTime()
1277                 device->run();
1278
1279                 /*
1280                         FPS limiter
1281                 */
1282
1283                 {
1284                         float fps_max = g_settings->getFloat("fps_max");
1285                         u32 frametime_min = 1000./fps_max;
1286                         
1287                         if(busytime_u32 < frametime_min)
1288                         {
1289                                 u32 sleeptime = frametime_min - busytime_u32;
1290                                 device->sleep(sleeptime);
1291                                 g_profiler->graphAdd("mainloop_sleep", (float)sleeptime/1000.0f);
1292                         }
1293                 }
1294
1295                 // Necessary for device->getTimer()->getTime()
1296                 device->run();
1297
1298                 /*
1299                         Time difference calculation
1300                 */
1301                 f32 dtime; // in seconds
1302                 
1303                 u32 time = device->getTimer()->getTime();
1304                 if(time > lasttime)
1305                         dtime = (time - lasttime) / 1000.0;
1306                 else
1307                         dtime = 0;
1308                 lasttime = time;
1309
1310                 g_profiler->graphAdd("mainloop_dtime", dtime);
1311
1312                 /* Run timers */
1313
1314                 if(nodig_delay_timer >= 0)
1315                         nodig_delay_timer -= dtime;
1316                 if(object_hit_delay_timer >= 0)
1317                         object_hit_delay_timer -= dtime;
1318                 time_from_last_punch += dtime;
1319                 
1320                 g_profiler->add("Elapsed time", dtime);
1321                 g_profiler->avg("FPS", 1./dtime);
1322
1323                 /*
1324                         Time average and jitter calculation
1325                 */
1326
1327                 static f32 dtime_avg1 = 0.0;
1328                 dtime_avg1 = dtime_avg1 * 0.96 + dtime * 0.04;
1329                 f32 dtime_jitter1 = dtime - dtime_avg1;
1330
1331                 static f32 dtime_jitter1_max_sample = 0.0;
1332                 static f32 dtime_jitter1_max_fraction = 0.0;
1333                 {
1334                         static f32 jitter1_max = 0.0;
1335                         static f32 counter = 0.0;
1336                         if(dtime_jitter1 > jitter1_max)
1337                                 jitter1_max = dtime_jitter1;
1338                         counter += dtime;
1339                         if(counter > 0.0)
1340                         {
1341                                 counter -= 3.0;
1342                                 dtime_jitter1_max_sample = jitter1_max;
1343                                 dtime_jitter1_max_fraction
1344                                                 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
1345                                 jitter1_max = 0.0;
1346                         }
1347                 }
1348                 
1349                 /*
1350                         Busytime average and jitter calculation
1351                 */
1352
1353                 static f32 busytime_avg1 = 0.0;
1354                 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
1355                 f32 busytime_jitter1 = busytime - busytime_avg1;
1356                 
1357                 static f32 busytime_jitter1_max_sample = 0.0;
1358                 static f32 busytime_jitter1_min_sample = 0.0;
1359                 {
1360                         static f32 jitter1_max = 0.0;
1361                         static f32 jitter1_min = 0.0;
1362                         static f32 counter = 0.0;
1363                         if(busytime_jitter1 > jitter1_max)
1364                                 jitter1_max = busytime_jitter1;
1365                         if(busytime_jitter1 < jitter1_min)
1366                                 jitter1_min = busytime_jitter1;
1367                         counter += dtime;
1368                         if(counter > 0.0){
1369                                 counter -= 3.0;
1370                                 busytime_jitter1_max_sample = jitter1_max;
1371                                 busytime_jitter1_min_sample = jitter1_min;
1372                                 jitter1_max = 0.0;
1373                                 jitter1_min = 0.0;
1374                         }
1375                 }
1376
1377                 /*
1378                         Handle miscellaneous stuff
1379                 */
1380                 
1381                 if(client.accessDenied())
1382                 {
1383                         error_message = L"Access denied. Reason: "
1384                                         +client.accessDeniedReason();
1385                         errorstream<<wide_to_narrow(error_message)<<std::endl;
1386                         break;
1387                 }
1388
1389                 if(g_gamecallback->disconnect_requested)
1390                 {
1391                         g_gamecallback->disconnect_requested = false;
1392                         break;
1393                 }
1394
1395                 if(g_gamecallback->changepassword_requested)
1396                 {
1397                         (new GUIPasswordChange(guienv, guiroot, -1,
1398                                 &g_menumgr, &client))->drop();
1399                         g_gamecallback->changepassword_requested = false;
1400                 }
1401
1402                 /*
1403                         Process TextureSource's queue
1404                 */
1405                 tsrc->processQueue();
1406
1407                 /*
1408                         Random calculations
1409                 */
1410                 last_screensize = screensize;
1411                 screensize = driver->getScreenSize();
1412                 v2s32 displaycenter(screensize.X/2,screensize.Y/2);
1413                 //bool screensize_changed = screensize != last_screensize;
1414
1415                 // Resize hotbar
1416                 if(screensize.Y <= 800)
1417                         hotbar_imagesize = 32;
1418                 else if(screensize.Y <= 1280)
1419                         hotbar_imagesize = 48;
1420                 else
1421                         hotbar_imagesize = 64;
1422                 
1423                 // Hilight boxes collected during the loop and displayed
1424                 std::vector<aabb3f> hilightboxes;
1425                 
1426                 // Info text
1427                 std::wstring infotext;
1428
1429                 /*
1430                         Debug info for client
1431                 */
1432                 {
1433                         static float counter = 0.0;
1434                         counter -= dtime;
1435                         if(counter < 0)
1436                         {
1437                                 counter = 30.0;
1438                                 client.printDebugInfo(infostream);
1439                         }
1440                 }
1441
1442                 /*
1443                         Profiler
1444                 */
1445                 float profiler_print_interval =
1446                                 g_settings->getFloat("profiler_print_interval");
1447                 bool print_to_log = true;
1448                 if(profiler_print_interval == 0){
1449                         print_to_log = false;
1450                         profiler_print_interval = 5;
1451                 }
1452                 if(m_profiler_interval.step(dtime, profiler_print_interval))
1453                 {
1454                         if(print_to_log){
1455                                 infostream<<"Profiler:"<<std::endl;
1456                                 g_profiler->print(infostream);
1457                         }
1458
1459                         update_profiler_gui(guitext_profiler, font, text_height,
1460                                         show_profiler, show_profiler_max);
1461
1462                         g_profiler->clear();
1463                 }
1464
1465                 /*
1466                         Direct handling of user input
1467                 */
1468                 
1469                 // Reset input if window not active or some menu is active
1470                 if(device->isWindowActive() == false
1471                                 || noMenuActive() == false
1472                                 || guienv->hasFocus(gui_chat_console))
1473                 {
1474                         input->clear();
1475                 }
1476
1477                 // Input handler step() (used by the random input generator)
1478                 input->step(dtime);
1479
1480                 /*
1481                         Launch menus and trigger stuff according to keys
1482                 */
1483                 if(input->wasKeyDown(getKeySetting("keymap_drop")))
1484                 {
1485                         // drop selected item
1486                         IDropAction *a = new IDropAction();
1487                         a->count = 0;
1488                         a->from_inv.setCurrentPlayer();
1489                         a->from_list = "main";
1490                         a->from_i = client.getPlayerItem();
1491                         client.inventoryAction(a);
1492                 }
1493                 else if(input->wasKeyDown(getKeySetting("keymap_inventory")))
1494                 {
1495                         infostream<<"the_game: "
1496                                         <<"Launching inventory"<<std::endl;
1497                         
1498                         GUIFormSpecMenu *menu =
1499                                 new GUIFormSpecMenu(guienv, guiroot, -1,
1500                                         &g_menumgr,
1501                                         &client, gamedef);
1502
1503                         InventoryLocation inventoryloc;
1504                         inventoryloc.setCurrentPlayer();
1505
1506                         PlayerInventoryFormSource *src = new PlayerInventoryFormSource(&client);
1507                         assert(src);
1508                         menu->setFormSpec(src->getForm(), inventoryloc);
1509                         menu->setFormSource(src);
1510                         menu->drop();
1511                 }
1512                 else if(input->wasKeyDown(EscapeKey))
1513                 {
1514                         infostream<<"the_game: "
1515                                         <<"Launching pause menu"<<std::endl;
1516                         // It will delete itself by itself
1517                         (new GUIPauseMenu(guienv, guiroot, -1, g_gamecallback,
1518                                         &g_menumgr, simple_singleplayer_mode))->drop();
1519
1520                         // Move mouse cursor on top of the disconnect button
1521                         if(simple_singleplayer_mode)
1522                                 input->setMousePos(displaycenter.X, displaycenter.Y+0);
1523                         else
1524                                 input->setMousePos(displaycenter.X, displaycenter.Y+25);
1525                 }
1526                 else if(input->wasKeyDown(getKeySetting("keymap_chat")))
1527                 {
1528                         TextDest *dest = new TextDestChat(&client);
1529
1530                         (new GUITextInputMenu(guienv, guiroot, -1,
1531                                         &g_menumgr, dest,
1532                                         L""))->drop();
1533                 }
1534                 else if(input->wasKeyDown(getKeySetting("keymap_cmd")))
1535                 {
1536                         TextDest *dest = new TextDestChat(&client);
1537
1538                         (new GUITextInputMenu(guienv, guiroot, -1,
1539                                         &g_menumgr, dest,
1540                                         L"/"))->drop();
1541                 }
1542                 else if(input->wasKeyDown(getKeySetting("keymap_console")))
1543                 {
1544                         if (!gui_chat_console->isOpenInhibited())
1545                         {
1546                                 // Open up to over half of the screen
1547                                 gui_chat_console->openConsole(0.6);
1548                                 guienv->setFocus(gui_chat_console);
1549                         }
1550                 }
1551                 else if(input->wasKeyDown(getKeySetting("keymap_freemove")))
1552                 {
1553                         if(g_settings->getBool("free_move"))
1554                         {
1555                                 g_settings->set("free_move","false");
1556                                 statustext = L"free_move disabled";
1557                                 statustext_time = 0;
1558                         }
1559                         else
1560                         {
1561                                 g_settings->set("free_move","true");
1562                                 statustext = L"free_move enabled";
1563                                 statustext_time = 0;
1564                                 if(!client.checkPrivilege("fly"))
1565                                         statustext += L" (note: no 'fly' privilege)";
1566                         }
1567                 }
1568                 else if(input->wasKeyDown(getKeySetting("keymap_fastmove")))
1569                 {
1570                         if(g_settings->getBool("fast_move"))
1571                         {
1572                                 g_settings->set("fast_move","false");
1573                                 statustext = L"fast_move disabled";
1574                                 statustext_time = 0;
1575                         }
1576                         else
1577                         {
1578                                 g_settings->set("fast_move","true");
1579                                 statustext = L"fast_move enabled";
1580                                 statustext_time = 0;
1581                                 if(!client.checkPrivilege("fast"))
1582                                         statustext += L" (note: no 'fast' privilege)";
1583                         }
1584                 }
1585                 else if(input->wasKeyDown(getKeySetting("keymap_screenshot")))
1586                 {
1587                         irr::video::IImage* const image = driver->createScreenShot(); 
1588                         if (image) { 
1589                                 irr::c8 filename[256]; 
1590                                 snprintf(filename, 256, "%s" DIR_DELIM "screenshot_%u.png", 
1591                                                  g_settings->get("screenshot_path").c_str(),
1592                                                  device->getTimer()->getRealTime()); 
1593                                 if (driver->writeImageToFile(image, filename)) {
1594                                         std::wstringstream sstr;
1595                                         sstr<<"Saved screenshot to '"<<filename<<"'";
1596                                         infostream<<"Saved screenshot to '"<<filename<<"'"<<std::endl;
1597                                         statustext = sstr.str();
1598                                         statustext_time = 0;
1599                                 } else{
1600                                         infostream<<"Failed to save screenshot '"<<filename<<"'"<<std::endl;
1601                                 }
1602                                 image->drop(); 
1603                         }                        
1604                 }
1605                 else if(input->wasKeyDown(getKeySetting("keymap_toggle_hud")))
1606                 {
1607                         show_hud = !show_hud;
1608                         if(show_hud)
1609                                 statustext = L"HUD shown";
1610                         else
1611                                 statustext = L"HUD hidden";
1612                         statustext_time = 0;
1613                 }
1614                 else if(input->wasKeyDown(getKeySetting("keymap_toggle_chat")))
1615                 {
1616                         show_chat = !show_chat;
1617                         if(show_chat)
1618                                 statustext = L"Chat shown";
1619                         else
1620                                 statustext = L"Chat hidden";
1621                         statustext_time = 0;
1622                 }
1623                 else if(input->wasKeyDown(getKeySetting("keymap_toggle_force_fog_off")))
1624                 {
1625                         force_fog_off = !force_fog_off;
1626                         if(force_fog_off)
1627                                 statustext = L"Fog disabled";
1628                         else
1629                                 statustext = L"Fog enabled";
1630                         statustext_time = 0;
1631                 }
1632                 else if(input->wasKeyDown(getKeySetting("keymap_toggle_update_camera")))
1633                 {
1634                         disable_camera_update = !disable_camera_update;
1635                         if(disable_camera_update)
1636                                 statustext = L"Camera update disabled";
1637                         else
1638                                 statustext = L"Camera update enabled";
1639                         statustext_time = 0;
1640                 }
1641                 else if(input->wasKeyDown(getKeySetting("keymap_toggle_debug")))
1642                 {
1643                         // Initial / 3x toggle: Chat only
1644                         // 1x toggle: Debug text with chat
1645                         // 2x toggle: Debug text with profiler graph
1646                         if(!show_debug)
1647                         {
1648                                 show_debug = true;
1649                                 show_profiler_graph = false;
1650                                 statustext = L"Debug info shown";
1651                                 statustext_time = 0;
1652                         }
1653                         else if(show_profiler_graph)
1654                         {
1655                                 show_debug = false;
1656                                 show_profiler_graph = false;
1657                                 statustext = L"Debug info and profiler graph hidden";
1658                                 statustext_time = 0;
1659                         }
1660                         else
1661                         {
1662                                 show_profiler_graph = true;
1663                                 statustext = L"Profiler graph shown";
1664                                 statustext_time = 0;
1665                         }
1666                 }
1667                 else if(input->wasKeyDown(getKeySetting("keymap_toggle_profiler")))
1668                 {
1669                         show_profiler = (show_profiler + 1) % (show_profiler_max + 1);
1670
1671                         // FIXME: This updates the profiler with incomplete values
1672                         update_profiler_gui(guitext_profiler, font, text_height,
1673                                         show_profiler, show_profiler_max);
1674
1675                         if(show_profiler != 0)
1676                         {
1677                                 std::wstringstream sstr;
1678                                 sstr<<"Profiler shown (page "<<show_profiler
1679                                         <<" of "<<show_profiler_max<<")";
1680                                 statustext = sstr.str();
1681                                 statustext_time = 0;
1682                         }
1683                         else
1684                         {
1685                                 statustext = L"Profiler hidden";
1686                                 statustext_time = 0;
1687                         }
1688                 }
1689                 else if(input->wasKeyDown(getKeySetting("keymap_increase_viewing_range_min")))
1690                 {
1691                         s16 range = g_settings->getS16("viewing_range_nodes_min");
1692                         s16 range_new = range + 10;
1693                         g_settings->set("viewing_range_nodes_min", itos(range_new));
1694                         statustext = narrow_to_wide(
1695                                         "Minimum viewing range changed to "
1696                                         + itos(range_new));
1697                         statustext_time = 0;
1698                 }
1699                 else if(input->wasKeyDown(getKeySetting("keymap_decrease_viewing_range_min")))
1700                 {
1701                         s16 range = g_settings->getS16("viewing_range_nodes_min");
1702                         s16 range_new = range - 10;
1703                         if(range_new < 0)
1704                                 range_new = range;
1705                         g_settings->set("viewing_range_nodes_min",
1706                                         itos(range_new));
1707                         statustext = narrow_to_wide(
1708                                         "Minimum viewing range changed to "
1709                                         + itos(range_new));
1710                         statustext_time = 0;
1711                 }
1712                 
1713                 // Handle QuicktuneShortcutter
1714                 if(input->wasKeyDown(getKeySetting("keymap_quicktune_next")))
1715                         quicktune.next();
1716                 if(input->wasKeyDown(getKeySetting("keymap_quicktune_prev")))
1717                         quicktune.prev();
1718                 if(input->wasKeyDown(getKeySetting("keymap_quicktune_inc")))
1719                         quicktune.inc();
1720                 if(input->wasKeyDown(getKeySetting("keymap_quicktune_dec")))
1721                         quicktune.dec();
1722                 {
1723                         std::string msg = quicktune.getMessage();
1724                         if(msg != ""){
1725                                 statustext = narrow_to_wide(msg);
1726                                 statustext_time = 0;
1727                         }
1728                 }
1729
1730                 // Item selection with mouse wheel
1731                 u16 new_playeritem = client.getPlayerItem();
1732                 {
1733                         s32 wheel = input->getMouseWheel();
1734                         u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE-1,
1735                                         hotbar_itemcount-1);
1736
1737                         if(wheel < 0)
1738                         {
1739                                 if(new_playeritem < max_item)
1740                                         new_playeritem++;
1741                                 else
1742                                         new_playeritem = 0;
1743                         }
1744                         else if(wheel > 0)
1745                         {
1746                                 if(new_playeritem > 0)
1747                                         new_playeritem--;
1748                                 else
1749                                         new_playeritem = max_item;
1750                         }
1751                 }
1752                 
1753                 // Item selection
1754                 for(u16 i=0; i<10; i++)
1755                 {
1756                         const KeyPress *kp = NumberKey + (i + 1) % 10;
1757                         if(input->wasKeyDown(*kp))
1758                         {
1759                                 if(i < PLAYER_INVENTORY_SIZE && i < hotbar_itemcount)
1760                                 {
1761                                         new_playeritem = i;
1762
1763                                         infostream<<"Selected item: "
1764                                                         <<new_playeritem<<std::endl;
1765                                 }
1766                         }
1767                 }
1768
1769                 // Viewing range selection
1770                 if(input->wasKeyDown(getKeySetting("keymap_rangeselect")))
1771                 {
1772                         draw_control.range_all = !draw_control.range_all;
1773                         if(draw_control.range_all)
1774                         {
1775                                 infostream<<"Enabled full viewing range"<<std::endl;
1776                                 statustext = L"Enabled full viewing range";
1777                                 statustext_time = 0;
1778                         }
1779                         else
1780                         {
1781                                 infostream<<"Disabled full viewing range"<<std::endl;
1782                                 statustext = L"Disabled full viewing range";
1783                                 statustext_time = 0;
1784                         }
1785                 }
1786
1787                 // Print debug stacks
1788                 if(input->wasKeyDown(getKeySetting("keymap_print_debug_stacks")))
1789                 {
1790                         dstream<<"-----------------------------------------"
1791                                         <<std::endl;
1792                         dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
1793                         dstream<<"-----------------------------------------"
1794                                         <<std::endl;
1795                         debug_stacks_print();
1796                 }
1797
1798                 /*
1799                         Mouse and camera control
1800                         NOTE: Do this before client.setPlayerControl() to not cause a camera lag of one frame
1801                 */
1802                 
1803                 float turn_amount = 0;
1804                 if((device->isWindowActive() && noMenuActive()) || random_input)
1805                 {
1806                         if(!random_input)
1807                         {
1808                                 // Mac OSX gets upset if this is set every frame
1809                                 if(device->getCursorControl()->isVisible())
1810                                         device->getCursorControl()->setVisible(false);
1811                         }
1812
1813                         if(first_loop_after_window_activation){
1814                                 //infostream<<"window active, first loop"<<std::endl;
1815                                 first_loop_after_window_activation = false;
1816                         }
1817                         else{
1818                                 s32 dx = input->getMousePos().X - displaycenter.X;
1819                                 s32 dy = input->getMousePos().Y - displaycenter.Y;
1820                                 if(invert_mouse)
1821                                         dy = -dy;
1822                                 //infostream<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
1823                                 
1824                                 /*const float keyspeed = 500;
1825                                 if(input->isKeyDown(irr::KEY_UP))
1826                                         dy -= dtime * keyspeed;
1827                                 if(input->isKeyDown(irr::KEY_DOWN))
1828                                         dy += dtime * keyspeed;
1829                                 if(input->isKeyDown(irr::KEY_LEFT))
1830                                         dx -= dtime * keyspeed;
1831                                 if(input->isKeyDown(irr::KEY_RIGHT))
1832                                         dx += dtime * keyspeed;*/
1833                                 
1834                                 float d = 0.2;
1835                                 camera_yaw -= dx*d;
1836                                 camera_pitch += dy*d;
1837                                 if(camera_pitch < -89.5) camera_pitch = -89.5;
1838                                 if(camera_pitch > 89.5) camera_pitch = 89.5;
1839                                 
1840                                 turn_amount = v2f(dx, dy).getLength() * d;
1841                         }
1842                         input->setMousePos(displaycenter.X, displaycenter.Y);
1843                 }
1844                 else{
1845                         // Mac OSX gets upset if this is set every frame
1846                         if(device->getCursorControl()->isVisible() == false)
1847                                 device->getCursorControl()->setVisible(true);
1848
1849                         //infostream<<"window inactive"<<std::endl;
1850                         first_loop_after_window_activation = true;
1851                 }
1852                 recent_turn_speed = recent_turn_speed * 0.9 + turn_amount * 0.1;
1853                 //std::cerr<<"recent_turn_speed = "<<recent_turn_speed<<std::endl;
1854
1855                 /*
1856                         Player speed control
1857                 */
1858                 {
1859                         /*bool a_up,
1860                         bool a_down,
1861                         bool a_left,
1862                         bool a_right,
1863                         bool a_jump,
1864                         bool a_superspeed,
1865                         bool a_sneak,
1866                         float a_pitch,
1867                         float a_yaw*/
1868                         PlayerControl control(
1869                                 input->isKeyDown(getKeySetting("keymap_forward")),
1870                                 input->isKeyDown(getKeySetting("keymap_backward")),
1871                                 input->isKeyDown(getKeySetting("keymap_left")),
1872                                 input->isKeyDown(getKeySetting("keymap_right")),
1873                                 input->isKeyDown(getKeySetting("keymap_jump")),
1874                                 input->isKeyDown(getKeySetting("keymap_special1")),
1875                                 input->isKeyDown(getKeySetting("keymap_sneak")),
1876                                 camera_pitch,
1877                                 camera_yaw
1878                         );
1879                         client.setPlayerControl(control);
1880                 }
1881                 
1882                 /*
1883                         Run server
1884                 */
1885
1886                 if(server != NULL)
1887                 {
1888                         //TimeTaker timer("server->step(dtime)");
1889                         server->step(dtime);
1890                 }
1891
1892                 /*
1893                         Process environment
1894                 */
1895                 
1896                 {
1897                         //TimeTaker timer("client.step(dtime)");
1898                         client.step(dtime);
1899                         //client.step(dtime_avg1);
1900                 }
1901
1902                 {
1903                         // Read client events
1904                         for(;;)
1905                         {
1906                                 ClientEvent event = client.getClientEvent();
1907                                 if(event.type == CE_NONE)
1908                                 {
1909                                         break;
1910                                 }
1911                                 else if(event.type == CE_PLAYER_DAMAGE)
1912                                 {
1913                                         //u16 damage = event.player_damage.amount;
1914                                         //infostream<<"Player damage: "<<damage<<std::endl;
1915                                         damage_flash_timer = 0.05;
1916                                         if(event.player_damage.amount >= 2){
1917                                                 damage_flash_timer += 0.05 * event.player_damage.amount;
1918                                         }
1919                                 }
1920                                 else if(event.type == CE_PLAYER_FORCE_MOVE)
1921                                 {
1922                                         camera_yaw = event.player_force_move.yaw;
1923                                         camera_pitch = event.player_force_move.pitch;
1924                                 }
1925                                 else if(event.type == CE_DEATHSCREEN)
1926                                 {
1927                                         if(respawn_menu_active)
1928                                                 continue;
1929
1930                                         /*bool set_camera_point_target =
1931                                                         event.deathscreen.set_camera_point_target;
1932                                         v3f camera_point_target;
1933                                         camera_point_target.X = event.deathscreen.camera_point_target_x;
1934                                         camera_point_target.Y = event.deathscreen.camera_point_target_y;
1935                                         camera_point_target.Z = event.deathscreen.camera_point_target_z;*/
1936                                         MainRespawnInitiator *respawner =
1937                                                         new MainRespawnInitiator(
1938                                                                         &respawn_menu_active, &client);
1939                                         GUIDeathScreen *menu =
1940                                                         new GUIDeathScreen(guienv, guiroot, -1, 
1941                                                                 &g_menumgr, respawner);
1942                                         menu->drop();
1943                                         
1944                                         chat_backend.addMessage(L"", L"You died.");
1945
1946                                         /* Handle visualization */
1947
1948                                         damage_flash_timer = 0;
1949
1950                                         /*LocalPlayer* player = client.getLocalPlayer();
1951                                         player->setPosition(player->getPosition() + v3f(0,-BS,0));
1952                                         camera.update(player, busytime, screensize);*/
1953                                 }
1954                                 else if(event.type == CE_TEXTURES_UPDATED)
1955                                 {
1956                                         update_wielded_item_trigger = true;
1957                                 }
1958                         }
1959                 }
1960                 
1961                 //TimeTaker //timer2("//timer2");
1962
1963                 /*
1964                         For interaction purposes, get info about the held item
1965                         - What item is it?
1966                         - Is it a usable item?
1967                         - Can it point to liquids?
1968                 */
1969                 ItemStack playeritem;
1970                 bool playeritem_usable = false;
1971                 bool playeritem_liquids_pointable = false;
1972                 {
1973                         InventoryList *mlist = local_inventory.getList("main");
1974                         if(mlist != NULL)
1975                         {
1976                                 playeritem = mlist->getItem(client.getPlayerItem());
1977                                 playeritem_usable = playeritem.getDefinition(itemdef).usable;
1978                                 playeritem_liquids_pointable = playeritem.getDefinition(itemdef).liquids_pointable;
1979                         }
1980                 }
1981                 ToolCapabilities playeritem_toolcap =
1982                                 playeritem.getToolCapabilities(itemdef);
1983                 
1984                 /*
1985                         Update camera
1986                 */
1987
1988                 LocalPlayer* player = client.getEnv().getLocalPlayer();
1989                 float full_punch_interval = playeritem_toolcap.full_punch_interval;
1990                 float tool_reload_ratio = time_from_last_punch / full_punch_interval;
1991                 tool_reload_ratio = MYMIN(tool_reload_ratio, 1.0);
1992                 camera.update(player, busytime, screensize, tool_reload_ratio);
1993                 camera.step(dtime);
1994
1995                 v3f player_position = player->getPosition();
1996                 v3f camera_position = camera.getPosition();
1997                 v3f camera_direction = camera.getDirection();
1998                 f32 camera_fov = camera.getFovMax();
1999                 
2000                 if(!disable_camera_update){
2001                         client.getEnv().getClientMap().updateCamera(camera_position,
2002                                 camera_direction, camera_fov);
2003                 }
2004                 
2005                 // Update sound listener
2006                 sound->updateListener(camera.getCameraNode()->getPosition(),
2007                                 v3f(0,0,0), // velocity
2008                                 camera.getDirection(),
2009                                 camera.getCameraNode()->getUpVector());
2010                 sound->setListenerGain(g_settings->getFloat("sound_volume"));
2011
2012                 /*
2013                         Update sound maker
2014                 */
2015                 {
2016                         soundmaker.step(dtime);
2017                         
2018                         ClientMap &map = client.getEnv().getClientMap();
2019                         MapNode n = map.getNodeNoEx(player->getStandingNodePos());
2020                         soundmaker.m_player_step_sound = nodedef->get(n).sound_footstep;
2021                 }
2022
2023                 /*
2024                         Calculate what block is the crosshair pointing to
2025                 */
2026                 
2027                 //u32 t1 = device->getTimer()->getRealTime();
2028                 
2029                 f32 d = 4; // max. distance
2030                 core::line3d<f32> shootline(camera_position,
2031                                 camera_position + camera_direction * BS * (d+1));
2032
2033                 ClientActiveObject *selected_object = NULL;
2034
2035                 PointedThing pointed = getPointedThing(
2036                                 // input
2037                                 &client, player_position, camera_direction,
2038                                 camera_position, shootline, d,
2039                                 playeritem_liquids_pointable, !ldown_for_dig,
2040                                 // output
2041                                 hilightboxes,
2042                                 selected_object);
2043
2044                 if(pointed != pointed_old)
2045                 {
2046                         infostream<<"Pointing at "<<pointed.dump()<<std::endl;
2047                         //dstream<<"Pointing at "<<pointed.dump()<<std::endl;
2048                 }
2049
2050                 /*
2051                         Stop digging when
2052                         - releasing left mouse button
2053                         - pointing away from node
2054                 */
2055                 if(digging)
2056                 {
2057                         if(input->getLeftReleased())
2058                         {
2059                                 infostream<<"Left button released"
2060                                         <<" (stopped digging)"<<std::endl;
2061                                 digging = false;
2062                         }
2063                         else if(pointed != pointed_old)
2064                         {
2065                                 if (pointed.type == POINTEDTHING_NODE
2066                                         && pointed_old.type == POINTEDTHING_NODE
2067                                         && pointed.node_undersurface == pointed_old.node_undersurface)
2068                                 {
2069                                         // Still pointing to the same node,
2070                                         // but a different face. Don't reset.
2071                                 }
2072                                 else
2073                                 {
2074                                         infostream<<"Pointing away from node"
2075                                                 <<" (stopped digging)"<<std::endl;
2076                                         digging = false;
2077                                 }
2078                         }
2079                         if(!digging)
2080                         {
2081                                 client.interact(1, pointed_old);
2082                                 client.setCrack(-1, v3s16(0,0,0));
2083                                 dig_time = 0.0;
2084                         }
2085                 }
2086                 if(!digging && ldown_for_dig && !input->getLeftState())
2087                 {
2088                         ldown_for_dig = false;
2089                 }
2090
2091                 bool left_punch = false;
2092                 soundmaker.m_player_leftpunch_sound.name = "";
2093
2094                 if(playeritem_usable && input->getLeftState())
2095                 {
2096                         if(input->getLeftClicked())
2097                                 client.interact(4, pointed);
2098                 }
2099                 else if(pointed.type == POINTEDTHING_NODE)
2100                 {
2101                         v3s16 nodepos = pointed.node_undersurface;
2102                         v3s16 neighbourpos = pointed.node_abovesurface;
2103
2104                         /*
2105                                 Check information text of node
2106                         */
2107                         
2108                         ClientMap &map = client.getEnv().getClientMap();
2109                         NodeMetadata *meta = map.getNodeMetadata(nodepos);
2110                         if(meta){
2111                                 infotext = narrow_to_wide(meta->getString("infotext"));
2112                         } else {
2113                                 MapNode n = map.getNode(nodepos);
2114                                 if(nodedef->get(n).tiledef[0].name == "unknown_block.png"){
2115                                         infotext = L"Unknown node: ";
2116                                         infotext += narrow_to_wide(nodedef->get(n).name);
2117                                 }
2118                         }
2119                         
2120                         // We can't actually know, but assume the sound of right-clicking
2121                         // to be the sound of placing a node
2122                         soundmaker.m_player_rightpunch_sound.gain = 0.5;
2123                         soundmaker.m_player_rightpunch_sound.name = "default_place_node";
2124                         
2125                         /*
2126                                 Handle digging
2127                         */
2128                         
2129                         if(nodig_delay_timer <= 0.0 && input->getLeftState())
2130                         {
2131                                 if(!digging)
2132                                 {
2133                                         infostream<<"Started digging"<<std::endl;
2134                                         client.interact(0, pointed);
2135                                         digging = true;
2136                                         ldown_for_dig = true;
2137                                 }
2138                                 MapNode n = client.getEnv().getClientMap().getNode(nodepos);
2139                                 
2140                                 // NOTE: Similar piece of code exists on the server side for
2141                                 // cheat detection.
2142                                 // Get digging parameters
2143                                 DigParams params = getDigParams(nodedef->get(n).groups,
2144                                                 &playeritem_toolcap);
2145                                 // If can't dig, try hand
2146                                 if(!params.diggable){
2147                                         const ItemDefinition &hand = itemdef->get("");
2148                                         const ToolCapabilities *tp = hand.tool_capabilities;
2149                                         if(tp)
2150                                                 params = getDigParams(nodedef->get(n).groups, tp);
2151                                 }
2152                                 
2153                                 SimpleSoundSpec sound_dig = nodedef->get(n).sound_dig;
2154                                 if(sound_dig.exists()){
2155                                         if(sound_dig.name == "__group"){
2156                                                 if(params.main_group != ""){
2157                                                         soundmaker.m_player_leftpunch_sound.gain = 0.5;
2158                                                         soundmaker.m_player_leftpunch_sound.name =
2159                                                                         std::string("default_dig_") +
2160                                                                                         params.main_group;
2161                                                 }
2162                                         } else{
2163                                                 soundmaker.m_player_leftpunch_sound = sound_dig;
2164                                         }
2165                                 }
2166
2167                                 float dig_time_complete = 0.0;
2168
2169                                 if(params.diggable == false)
2170                                 {
2171                                         // I guess nobody will wait for this long
2172                                         dig_time_complete = 10000000.0;
2173                                 }
2174                                 else
2175                                 {
2176                                         dig_time_complete = params.time;
2177                                 }
2178
2179                                 if(dig_time_complete >= 0.001)
2180                                 {
2181                                         dig_index = (u16)((float)crack_animation_length
2182                                                         * dig_time/dig_time_complete);
2183                                 }
2184                                 // This is for torches
2185                                 else
2186                                 {
2187                                         dig_index = crack_animation_length;
2188                                 }
2189
2190                                 // Don't show cracks if not diggable
2191                                 if(dig_time_complete >= 100000.0)
2192                                 {
2193                                 }
2194                                 else if(dig_index < crack_animation_length)
2195                                 {
2196                                         //TimeTaker timer("client.setTempMod");
2197                                         //infostream<<"dig_index="<<dig_index<<std::endl;
2198                                         client.setCrack(dig_index, nodepos);
2199                                 }
2200                                 else
2201                                 {
2202                                         infostream<<"Digging completed"<<std::endl;
2203                                         client.interact(2, pointed);
2204                                         client.setCrack(-1, v3s16(0,0,0));
2205                                         MapNode wasnode = map.getNode(nodepos);
2206                                         client.removeNode(nodepos);
2207
2208                                         dig_time = 0;
2209                                         digging = false;
2210
2211                                         nodig_delay_timer = dig_time_complete
2212                                                         / (float)crack_animation_length;
2213
2214                                         // We don't want a corresponding delay to
2215                                         // very time consuming nodes
2216                                         if(nodig_delay_timer > 0.3)
2217                                                 nodig_delay_timer = 0.3;
2218                                         // We want a slight delay to very little
2219                                         // time consuming nodes
2220                                         float mindelay = 0.15;
2221                                         if(nodig_delay_timer < mindelay)
2222                                                 nodig_delay_timer = mindelay;
2223                                         
2224                                         // Send event to trigger sound
2225                                         MtEvent *e = new NodeDugEvent(nodepos, wasnode);
2226                                         gamedef->event()->put(e);
2227                                 }
2228
2229                                 dig_time += dtime;
2230
2231                                 camera.setDigging(0);  // left click animation
2232                         }
2233
2234                         if(input->getRightClicked())
2235                         {
2236                                 infostream<<"Ground right-clicked"<<std::endl;
2237                                 
2238                                 // Sign special case, at least until formspec is properly implemented.
2239                                 // Deprecated?
2240                                 if(meta && meta->getString("formspec") == "hack:sign_text_input" && !random_input)
2241                                 {
2242                                         infostream<<"Launching metadata text input"<<std::endl;
2243                                         
2244                                         // Get a new text for it
2245
2246                                         TextDest *dest = new TextDestNodeMetadata(nodepos, &client);
2247
2248                                         std::wstring wtext = narrow_to_wide(meta->getString("text"));
2249
2250                                         (new GUITextInputMenu(guienv, guiroot, -1,
2251                                                         &g_menumgr, dest,
2252                                                         wtext))->drop();
2253                                 }
2254                                 // If metadata provides an inventory view, activate it
2255                                 else if(meta && meta->getString("formspec") != "" && !random_input)
2256                                 {
2257                                         infostream<<"Launching custom inventory view"<<std::endl;
2258
2259                                         InventoryLocation inventoryloc;
2260                                         inventoryloc.setNodeMeta(nodepos);
2261                                         
2262                                         /* Create menu */
2263
2264                                         GUIFormSpecMenu *menu =
2265                                                 new GUIFormSpecMenu(guienv, guiroot, -1,
2266                                                         &g_menumgr,
2267                                                         &client, gamedef);
2268                                         menu->setFormSpec(meta->getString("formspec"),
2269                                                         inventoryloc);
2270                                         menu->setFormSource(new NodeMetadataFormSource(
2271                                                         &client.getEnv().getClientMap(), nodepos));
2272                                         menu->setTextDest(new TextDestNodeMetadata(nodepos, &client));
2273                                         menu->drop();
2274                                 }
2275                                 // Otherwise report right click to server
2276                                 else
2277                                 {
2278                                         // Report to server
2279                                         client.interact(3, pointed);
2280                                         camera.setDigging(1);  // right click animation
2281                                         
2282                                         // If the wielded item has node placement prediction,
2283                                         // make that happen
2284                                         const ItemDefinition &def =
2285                                                         playeritem.getDefinition(itemdef);
2286                                         if(def.node_placement_prediction != "")
2287                                         do{ // breakable
2288                                                 verbosestream<<"Node placement prediction for "
2289                                                                 <<playeritem.name<<" is "
2290                                                                 <<def.node_placement_prediction<<std::endl;
2291                                                 v3s16 p = neighbourpos;
2292                                                 content_t id;
2293                                                 bool found =
2294                                                         nodedef->getId(def.node_placement_prediction, id);
2295                                                 if(!found){
2296                                                         errorstream<<"Node placement prediction failed for "
2297                                                                         <<playeritem.name<<" (places "
2298                                                                         <<def.node_placement_prediction
2299                                                                         <<") - Name not known"<<std::endl;
2300                                                         break;
2301                                                 }
2302                                                 MapNode n(id);
2303                                                 try{
2304                                                         // This triggers the required mesh update too
2305                                                         client.addNode(p, n);
2306                                                 }catch(InvalidPositionException &e){
2307                                                         errorstream<<"Node placement prediction failed for "
2308                                                                         <<playeritem.name<<" (places "
2309                                                                         <<def.node_placement_prediction
2310                                                                         <<") - Position not loaded"<<std::endl;
2311                                                 }
2312                                         }while(0);
2313                                 }
2314                         }
2315                 }
2316                 else if(pointed.type == POINTEDTHING_OBJECT)
2317                 {
2318                         infotext = narrow_to_wide(selected_object->infoText());
2319
2320                         if(infotext == L"" && show_debug){
2321                                 infotext = narrow_to_wide(selected_object->debugInfoText());
2322                         }
2323
2324                         //if(input->getLeftClicked())
2325                         if(input->getLeftState())
2326                         {
2327                                 bool do_punch = false;
2328                                 bool do_punch_damage = false;
2329                                 if(object_hit_delay_timer <= 0.0){
2330                                         do_punch = true;
2331                                         do_punch_damage = true;
2332                                         object_hit_delay_timer = object_hit_delay;
2333                                 }
2334                                 if(input->getLeftClicked()){
2335                                         do_punch = true;
2336                                 }
2337                                 if(do_punch){
2338                                         infostream<<"Left-clicked object"<<std::endl;
2339                                         left_punch = true;
2340                                 }
2341                                 if(do_punch_damage){
2342                                         // Report direct punch
2343                                         v3f objpos = selected_object->getPosition();
2344                                         v3f dir = (objpos - player_position).normalize();
2345                                         
2346                                         bool disable_send = selected_object->directReportPunch(
2347                                                         dir, &playeritem, time_from_last_punch);
2348                                         time_from_last_punch = 0;
2349                                         if(!disable_send)
2350                                                 client.interact(0, pointed);
2351                                 }
2352                         }
2353                         else if(input->getRightClicked())
2354                         {
2355                                 infostream<<"Right-clicked object"<<std::endl;
2356                                 client.interact(3, pointed);  // place
2357                         }
2358                 }
2359                 else if(input->getLeftState())
2360                 {
2361                         // When button is held down in air, show continuous animation
2362                         left_punch = true;
2363                 }
2364
2365                 pointed_old = pointed;
2366                 
2367                 if(left_punch || input->getLeftClicked())
2368                 {
2369                         camera.setDigging(0); // left click animation
2370                 }
2371
2372                 input->resetLeftClicked();
2373                 input->resetRightClicked();
2374
2375                 input->resetLeftReleased();
2376                 input->resetRightReleased();
2377                 
2378                 /*
2379                         Calculate stuff for drawing
2380                 */
2381
2382                 /*
2383                         Fog range
2384                 */
2385         
2386                 f32 fog_range;
2387                 if(farmesh)
2388                 {
2389                         fog_range = BS*farmesh_range;
2390                 }
2391                 else
2392                 {
2393                         fog_range = draw_control.wanted_range*BS + 0.0*MAP_BLOCKSIZE*BS;
2394                         fog_range *= 0.9;
2395                         if(draw_control.range_all)
2396                                 fog_range = 100000*BS;
2397                 }
2398
2399                 /*
2400                         Calculate general brightness
2401                 */
2402                 u32 daynight_ratio = client.getEnv().getDayNightRatio();
2403                 float time_brightness = (float)decode_light(
2404                                 (daynight_ratio * LIGHT_SUN) / 1000) / 255.0;
2405                 float direct_brightness = 0;
2406                 bool sunlight_seen = false;
2407                 if(g_settings->getBool("free_move")){
2408                         direct_brightness = time_brightness;
2409                         sunlight_seen = true;
2410                 } else {
2411                         ScopeProfiler sp(g_profiler, "Detecting background light", SPT_AVG);
2412                         float old_brightness = sky->getBrightness();
2413                         direct_brightness = (float)client.getEnv().getClientMap()
2414                                         .getBackgroundBrightness(MYMIN(fog_range*1.2, 60*BS),
2415                                         daynight_ratio, (int)(old_brightness*255.5), &sunlight_seen)
2416                                         / 255.0;
2417                 }
2418                 
2419                 time_of_day = client.getEnv().getTimeOfDayF();
2420                 float maxsm = 0.05;
2421                 if(fabs(time_of_day - time_of_day_smooth) > maxsm &&
2422                                 fabs(time_of_day - time_of_day_smooth + 1.0) > maxsm &&
2423                                 fabs(time_of_day - time_of_day_smooth - 1.0) > maxsm)
2424                         time_of_day_smooth = time_of_day;
2425                 float todsm = 0.05;
2426                 if(time_of_day_smooth > 0.8 && time_of_day < 0.2)
2427                         time_of_day_smooth = time_of_day_smooth * (1.0-todsm)
2428                                         + (time_of_day+1.0) * todsm;
2429                 else
2430                         time_of_day_smooth = time_of_day_smooth * (1.0-todsm)
2431                                         + time_of_day * todsm;
2432                         
2433                 sky->update(time_of_day_smooth, time_brightness, direct_brightness,
2434                                 sunlight_seen);
2435                 
2436                 float brightness = sky->getBrightness();
2437                 video::SColor bgcolor = sky->getBgColor();
2438                 video::SColor skycolor = sky->getSkyColor();
2439
2440                 /*
2441                         Update clouds
2442                 */
2443                 if(clouds){
2444                         if(sky->getCloudsVisible()){
2445                                 clouds->setVisible(true);
2446                                 clouds->step(dtime);
2447                                 clouds->update(v2f(player_position.X, player_position.Z),
2448                                                 sky->getCloudColor());
2449                         } else{
2450                                 clouds->setVisible(false);
2451                         }
2452                 }
2453                 
2454                 /*
2455                         Update farmesh
2456                 */
2457                 if(farmesh)
2458                 {
2459                         farmesh_range = draw_control.wanted_range * 10;
2460                         if(draw_control.range_all && farmesh_range < 500)
2461                                 farmesh_range = 500;
2462                         if(farmesh_range > 1000)
2463                                 farmesh_range = 1000;
2464
2465                         farmesh->step(dtime);
2466                         farmesh->update(v2f(player_position.X, player_position.Z),
2467                                         brightness, farmesh_range);
2468                 }
2469                 
2470                 /*
2471                         Fog
2472                 */
2473                 
2474                 if(g_settings->getBool("enable_fog") == true && !force_fog_off)
2475                 {
2476                         driver->setFog(
2477                                 bgcolor,
2478                                 video::EFT_FOG_LINEAR,
2479                                 fog_range*0.4,
2480                                 fog_range*1.0,
2481                                 0.01,
2482                                 false, // pixel fog
2483                                 false // range fog
2484                         );
2485                 }
2486                 else
2487                 {
2488                         driver->setFog(
2489                                 bgcolor,
2490                                 video::EFT_FOG_LINEAR,
2491                                 100000*BS,
2492                                 110000*BS,
2493                                 0.01,
2494                                 false, // pixel fog
2495                                 false // range fog
2496                         );
2497                 }
2498
2499                 /*
2500                         Update gui stuff (0ms)
2501                 */
2502
2503                 //TimeTaker guiupdatetimer("Gui updating");
2504                 
2505                 const char program_name_and_version[] =
2506                         "Minetest-c55 " VERSION_STRING;
2507
2508                 if(show_debug)
2509                 {
2510                         static float drawtime_avg = 0;
2511                         drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
2512                         /*static float beginscenetime_avg = 0;
2513                         beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
2514                         static float scenetime_avg = 0;
2515                         scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
2516                         static float endscenetime_avg = 0;
2517                         endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;*/
2518                         
2519                         char temptext[300];
2520                         snprintf(temptext, 300, "%s ("
2521                                         "R: range_all=%i"
2522                                         ")"
2523                                         " drawtime=%.0f, dtime_jitter = % .1f %%"
2524                                         ", v_range = %.1f, RTT = %.3f",
2525                                         program_name_and_version,
2526                                         draw_control.range_all,
2527                                         drawtime_avg,
2528                                         dtime_jitter1_max_fraction * 100.0,
2529                                         draw_control.wanted_range,
2530                                         client.getRTT()
2531                                         );
2532                         
2533                         guitext->setText(narrow_to_wide(temptext).c_str());
2534                         guitext->setVisible(true);
2535                 }
2536                 else if(show_hud || show_chat)
2537                 {
2538                         guitext->setText(narrow_to_wide(program_name_and_version).c_str());
2539                         guitext->setVisible(true);
2540                 }
2541                 else
2542                 {
2543                         guitext->setVisible(false);
2544                 }
2545                 
2546                 if(show_debug)
2547                 {
2548                         char temptext[300];
2549                         snprintf(temptext, 300,
2550                                         "(% .1f, % .1f, % .1f)"
2551                                         " (yaw = %.1f) (seed = %lli)",
2552                                         player_position.X/BS,
2553                                         player_position.Y/BS,
2554                                         player_position.Z/BS,
2555                                         wrapDegrees_0_360(camera_yaw),
2556                                         client.getMapSeed());
2557
2558                         guitext2->setText(narrow_to_wide(temptext).c_str());
2559                         guitext2->setVisible(true);
2560                 }
2561                 else
2562                 {
2563                         guitext2->setVisible(false);
2564                 }
2565                 
2566                 {
2567                         guitext_info->setText(infotext.c_str());
2568                         guitext_info->setVisible(show_hud && g_menumgr.menuCount() == 0);
2569                 }
2570
2571                 {
2572                         float statustext_time_max = 1.5;
2573                         if(!statustext.empty())
2574                         {
2575                                 statustext_time += dtime;
2576                                 if(statustext_time >= statustext_time_max)
2577                                 {
2578                                         statustext = L"";
2579                                         statustext_time = 0;
2580                                 }
2581                         }
2582                         guitext_status->setText(statustext.c_str());
2583                         guitext_status->setVisible(!statustext.empty());
2584
2585                         if(!statustext.empty())
2586                         {
2587                                 s32 status_y = screensize.Y - 130;
2588                                 core::rect<s32> rect(
2589                                                 10,
2590                                                 status_y - guitext_status->getTextHeight(),
2591                                                 screensize.X - 10,
2592                                                 status_y
2593                                 );
2594                                 guitext_status->setRelativePosition(rect);
2595
2596                                 // Fade out
2597                                 video::SColor initial_color(255,0,0,0);
2598                                 if(guienv->getSkin())
2599                                         initial_color = guienv->getSkin()->getColor(gui::EGDC_BUTTON_TEXT);
2600                                 video::SColor final_color = initial_color;
2601                                 final_color.setAlpha(0);
2602                                 video::SColor fade_color =
2603                                         initial_color.getInterpolated_quadratic(
2604                                                 initial_color,
2605                                                 final_color,
2606                                                 pow(statustext_time / (float)statustext_time_max, 2.0f));
2607                                 guitext_status->setOverrideColor(fade_color);
2608                                 guitext_status->enableOverrideColor(true);
2609                         }
2610                 }
2611                 
2612                 /*
2613                         Get chat messages from client
2614                 */
2615                 {
2616                         // Get new messages from error log buffer
2617                         while(!chat_log_error_buf.empty())
2618                         {
2619                                 chat_backend.addMessage(L"", narrow_to_wide(
2620                                                 chat_log_error_buf.get()));
2621                         }
2622                         // Get new messages from client
2623                         std::wstring message;
2624                         while(client.getChatMessage(message))
2625                         {
2626                                 chat_backend.addUnparsedMessage(message);
2627                         }
2628                         // Remove old messages
2629                         chat_backend.step(dtime);
2630
2631                         // Display all messages in a static text element
2632                         u32 recent_chat_count = chat_backend.getRecentBuffer().getLineCount();
2633                         std::wstring recent_chat = chat_backend.getRecentChat();
2634                         guitext_chat->setText(recent_chat.c_str());
2635
2636                         // Update gui element size and position
2637                         s32 chat_y = 5+(text_height+5);
2638                         if(show_debug)
2639                                 chat_y += (text_height+5);
2640                         core::rect<s32> rect(
2641                                 10,
2642                                 chat_y,
2643                                 screensize.X - 10,
2644                                 chat_y + guitext_chat->getTextHeight()
2645                         );
2646                         guitext_chat->setRelativePosition(rect);
2647
2648                         // Don't show chat if disabled or empty or profiler is enabled
2649                         guitext_chat->setVisible(show_chat && recent_chat_count != 0
2650                                         && !show_profiler);
2651                 }
2652
2653                 /*
2654                         Inventory
2655                 */
2656                 
2657                 if(client.getPlayerItem() != new_playeritem)
2658                 {
2659                         client.selectPlayerItem(new_playeritem);
2660                 }
2661                 if(client.getLocalInventoryUpdated())
2662                 {
2663                         //infostream<<"Updating local inventory"<<std::endl;
2664                         client.getLocalInventory(local_inventory);
2665                         
2666                         update_wielded_item_trigger = true;
2667                 }
2668                 if(update_wielded_item_trigger)
2669                 {
2670                         update_wielded_item_trigger = false;
2671                         // Update wielded tool
2672                         InventoryList *mlist = local_inventory.getList("main");
2673                         ItemStack item;
2674                         if(mlist != NULL)
2675                                 item = mlist->getItem(client.getPlayerItem());
2676                         camera.wield(item);
2677                 }
2678                 
2679                 /*
2680                         Drawing begins
2681                 */
2682
2683                 TimeTaker tt_draw("mainloop: draw");
2684
2685                 
2686                 {
2687                         TimeTaker timer("beginScene");
2688                         //driver->beginScene(false, true, bgcolor);
2689                         //driver->beginScene(true, true, bgcolor);
2690                         driver->beginScene(true, true, skycolor);
2691                         beginscenetime = timer.stop(true);
2692                 }
2693                 
2694                 //timer3.stop();
2695         
2696                 //infostream<<"smgr->drawAll()"<<std::endl;
2697                 {
2698                         TimeTaker timer("smgr");
2699                         smgr->drawAll();
2700                         scenetime = timer.stop(true);
2701                 }
2702                 
2703                 {
2704                 //TimeTaker timer9("auxiliary drawings");
2705                 // 0ms
2706                 
2707                 //timer9.stop();
2708                 //TimeTaker //timer10("//timer10");
2709                 
2710                 video::SMaterial m;
2711                 //m.Thickness = 10;
2712                 m.Thickness = 3;
2713                 m.Lighting = false;
2714                 driver->setMaterial(m);
2715
2716                 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
2717
2718                 if(show_hud)
2719                 {
2720                         for(std::vector<aabb3f>::const_iterator
2721                                         i = hilightboxes.begin();
2722                                         i != hilightboxes.end(); i++)
2723                         {
2724                                 /*infostream<<"hilightbox min="
2725                                                 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
2726                                                 <<" max="
2727                                                 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
2728                                                 <<std::endl;*/
2729                                 driver->draw3DBox(*i, video::SColor(255,0,0,0));
2730                         }
2731                 }
2732
2733                 /*
2734                         Wielded tool
2735                 */
2736                 if(show_hud)
2737                 {
2738                         // Warning: This clears the Z buffer.
2739                         camera.drawWieldedTool();
2740                 }
2741
2742                 /*
2743                         Post effects
2744                 */
2745                 {
2746                         client.getEnv().getClientMap().renderPostFx();
2747                 }
2748
2749                 /*
2750                         Profiler graph
2751                 */
2752                 if(show_profiler_graph)
2753                 {
2754                         graph.draw(10, screensize.Y - 10, driver, font);
2755                 }
2756
2757                 /*
2758                         Draw crosshair
2759                 */
2760                 if(show_hud)
2761                 {
2762                         driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
2763                                         displaycenter + core::vector2d<s32>(10,0),
2764                                         video::SColor(255,255,255,255));
2765                         driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
2766                                         displaycenter + core::vector2d<s32>(0,10),
2767                                         video::SColor(255,255,255,255));
2768                 }
2769
2770                 } // timer
2771
2772                 //timer10.stop();
2773                 //TimeTaker //timer11("//timer11");
2774
2775                 /*
2776                         Draw gui
2777                 */
2778                 // 0-1ms
2779                 guienv->drawAll();
2780
2781                 /*
2782                         Draw hotbar
2783                 */
2784                 if(show_hud)
2785                 {
2786                         draw_hotbar(driver, font, gamedef,
2787                                         v2s32(displaycenter.X, screensize.Y),
2788                                         hotbar_imagesize, hotbar_itemcount, &local_inventory,
2789                                         client.getHP(), client.getPlayerItem());
2790                 }
2791
2792                 /*
2793                         Damage flash
2794                 */
2795                 if(damage_flash_timer > 0.0)
2796                 {
2797                         damage_flash_timer -= dtime;
2798                         
2799                         video::SColor color(128,255,0,0);
2800                         driver->draw2DRectangle(color,
2801                                         core::rect<s32>(0,0,screensize.X,screensize.Y),
2802                                         NULL);
2803                 }
2804
2805                 /*
2806                         End scene
2807                 */
2808                 {
2809                         TimeTaker timer("endScene");
2810                         endSceneX(driver);
2811                         endscenetime = timer.stop(true);
2812                 }
2813
2814                 drawtime = tt_draw.stop(true);
2815                 g_profiler->graphAdd("mainloop_draw", (float)drawtime/1000.0f);
2816
2817                 /*
2818                         End of drawing
2819                 */
2820
2821                 static s16 lastFPS = 0;
2822                 //u16 fps = driver->getFPS();
2823                 u16 fps = (1.0/dtime_avg1);
2824
2825                 if (lastFPS != fps)
2826                 {
2827                         core::stringw str = L"Minetest [";
2828                         str += driver->getName();
2829                         str += "] FPS=";
2830                         str += fps;
2831
2832                         device->setWindowCaption(str.c_str());
2833                         lastFPS = fps;
2834                 }
2835
2836                 /*
2837                         Log times and stuff for visualization
2838                 */
2839                 Profiler::GraphValues values;
2840                 g_profiler->graphGet(values);
2841                 graph.put(values);
2842         }
2843
2844         /*
2845                 Drop stuff
2846         */
2847         if(clouds)
2848                 clouds->drop();
2849         if(gui_chat_console)
2850                 gui_chat_console->drop();
2851         
2852         /*
2853                 Draw a "shutting down" screen, which will be shown while the map
2854                 generator and other stuff quits
2855         */
2856         {
2857                 /*gui::IGUIStaticText *gui_shuttingdowntext = */
2858                 draw_load_screen(L"Shutting down stuff...", driver, font);
2859                 /*driver->beginScene(true, true, video::SColor(255,0,0,0));
2860                 guienv->drawAll();
2861                 driver->endScene();
2862                 gui_shuttingdowntext->remove();*/
2863         }
2864
2865         chat_backend.addMessage(L"", L"# Disconnected.");
2866         chat_backend.addMessage(L"", L"");
2867
2868         // Client scope (client is destructed before destructing *def and tsrc)
2869         }while(0);
2870         } // try-catch
2871         catch(SerializationError &e)
2872         {
2873                 error_message = L"A serialization error occurred:\n"
2874                                 + narrow_to_wide(e.what()) + L"\n\nThe server is probably "
2875                                 L" running a different version of Minetest.";
2876                 errorstream<<wide_to_narrow(error_message)<<std::endl;
2877         }
2878         
2879         if(!sound_is_dummy)
2880                 delete sound;
2881         delete nodedef;
2882         delete itemdef;
2883         delete tsrc;
2884 }
2885
2886