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