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