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