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