]> git.lizzy.rs Git - dragonfireclient.git/blob - src/game.cpp
c806034f2e0b58abff34d97bb167d1795b78c3c3
[dragonfireclient.git] / src / game.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 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 "common_irrlicht.h"
21 #include "game.h"
22 #include "client.h"
23 #include "server.h"
24 #include "guiPauseMenu.h"
25 #include "guiInventoryMenu.h"
26 #include "guiTextInputMenu.h"
27 #include "guiFurnaceMenu.h"
28 #include "materials.h"
29
30 /*
31         Setting this to 1 enables a special camera mode that forces
32         the renderers to think that the camera statically points from
33         the starting place to a static direction.
34
35         This allows one to move around with the player and see what
36         is actually drawn behind solid things and behind the player.
37 */
38 #define FIELD_OF_VIEW_TEST 0
39
40
41 MapDrawControl draw_control;
42
43 // Chat data
44 struct ChatLine
45 {
46         ChatLine():
47                 age(0.0)
48         {
49         }
50         ChatLine(const std::wstring &a_text):
51                 age(0.0),
52                 text(a_text)
53         {
54         }
55         float age;
56         std::wstring text;
57 };
58
59 /*
60         Inventory stuff
61 */
62
63 // Inventory actions from the menu are buffered here before sending
64 Queue<InventoryAction*> inventory_action_queue;
65 // This is a copy of the inventory that the client's environment has
66 Inventory local_inventory;
67
68 u16 g_selected_item = 0;
69
70 /*
71         Text input system
72 */
73
74 struct TextDestSign : public TextDest
75 {
76         TextDestSign(v3s16 blockpos, s16 id, Client *client)
77         {
78                 m_blockpos = blockpos;
79                 m_id = id;
80                 m_client = client;
81         }
82         void gotText(std::wstring text)
83         {
84                 std::string ntext = wide_to_narrow(text);
85                 dstream<<"Changing text of a sign object: "
86                                 <<ntext<<std::endl;
87                 m_client->sendSignText(m_blockpos, m_id, ntext);
88         }
89
90         v3s16 m_blockpos;
91         s16 m_id;
92         Client *m_client;
93 };
94
95 struct TextDestChat : public TextDest
96 {
97         TextDestChat(Client *client)
98         {
99                 m_client = client;
100         }
101         void gotText(std::wstring text)
102         {
103                 // Discard empty line
104                 if(text == L"")
105                         return;
106                 
107                 // Parse command (server command starts with "/#")
108                 if(text[0] == L'/' && text[1] != L'#')
109                 {
110                         std::wstring reply = L"Local: ";
111
112                         reply += L"Local commands not yet supported. "
113                                         L"Server prefix is \"/#\".";
114                         
115                         m_client->addChatMessage(reply);
116                         return;
117                 }
118
119                 // Send to others
120                 m_client->sendChatMessage(text);
121                 // Show locally
122                 m_client->addChatMessage(text);
123         }
124
125         Client *m_client;
126 };
127
128 struct TextDestSignNode : public TextDest
129 {
130         TextDestSignNode(v3s16 p, Client *client)
131         {
132                 m_p = p;
133                 m_client = client;
134         }
135         void gotText(std::wstring text)
136         {
137                 std::string ntext = wide_to_narrow(text);
138                 dstream<<"Changing text of a sign node: "
139                                 <<ntext<<std::endl;
140                 m_client->sendSignNodeText(m_p, ntext);
141         }
142
143         v3s16 m_p;
144         Client *m_client;
145 };
146
147 /*
148         Render distance feedback loop
149 */
150 void updateViewingRange(f32 frametime_in, Client *client)
151 {
152         if(draw_control.range_all == true)
153                 return;
154         
155         static f32 added_frametime = 0;
156         static s16 added_frames = 0;
157
158         added_frametime += frametime_in;
159         added_frames += 1;
160
161         // Actually this counter kind of sucks because frametime is busytime
162         static f32 counter = 0;
163         counter -= frametime_in;
164         if(counter > 0)
165                 return;
166         //counter = 0.1;
167         counter = 0.2;
168
169         /*dstream<<__FUNCTION_NAME
170                         <<": Collected "<<added_frames<<" frames, total of "
171                         <<added_frametime<<"s."<<std::endl;*/
172         
173         /*dstream<<"draw_control.blocks_drawn="
174                         <<draw_control.blocks_drawn
175                         <<", draw_control.blocks_would_have_drawn="
176                         <<draw_control.blocks_would_have_drawn
177                         <<std::endl;*/
178         
179         float range_min = g_settings.getS16("viewing_range_nodes_min");
180         float range_max = g_settings.getS16("viewing_range_nodes_max");
181         
182         draw_control.wanted_min_range = range_min;
183         draw_control.wanted_max_blocks = (1.2*draw_control.blocks_drawn)+1;
184         
185         float block_draw_ratio = 1.0;
186         if(draw_control.blocks_would_have_drawn != 0)
187         {
188                 block_draw_ratio = (float)draw_control.blocks_drawn
189                         / (float)draw_control.blocks_would_have_drawn;
190         }
191
192         // Calculate the average frametime in the case that all wanted
193         // blocks had been drawn
194         f32 frametime = added_frametime / added_frames / block_draw_ratio;
195         
196         added_frametime = 0.0;
197         added_frames = 0;
198         
199         float wanted_fps = g_settings.getFloat("wanted_fps");
200         float wanted_frametime = 1.0 / wanted_fps;
201         
202         f32 wanted_frametime_change = wanted_frametime - frametime;
203         //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
204         
205         // If needed frametime change is small, just return
206         if(fabs(wanted_frametime_change) < wanted_frametime*0.4)
207         {
208                 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
209                 return;
210         }
211
212         float range = draw_control.wanted_range;
213         float new_range = range;
214
215         static s16 range_old = 0;
216         static f32 frametime_old = 0;
217         
218         float d_range = range - range_old;
219         f32 d_frametime = frametime - frametime_old;
220         // A sane default of 30ms per 50 nodes of range
221         static f32 time_per_range = 30. / 50;
222         if(d_range != 0)
223         {
224                 time_per_range = d_frametime / d_range;
225         }
226         
227         // The minimum allowed calculated frametime-range derivative:
228         // Practically this sets the maximum speed of changing the range.
229         // The lower this value, the higher the maximum changing speed.
230         // A low value here results in wobbly range (0.001)
231         // A high value here results in slow changing range (0.0025)
232         // SUGG: This could be dynamically adjusted so that when
233         //       the camera is turning, this is lower
234         //float min_time_per_range = 0.0015;
235         float min_time_per_range = 0.0010;
236         //float min_time_per_range = 0.05 / range;
237         if(time_per_range < min_time_per_range)
238         {
239                 time_per_range = min_time_per_range;
240                 //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl;
241         }
242         else
243         {
244                 //dstream<<"time_per_range="<<time_per_range<<std::endl;
245         }
246
247         f32 wanted_range_change = wanted_frametime_change / time_per_range;
248         // Dampen the change a bit to kill oscillations
249         //wanted_range_change *= 0.9;
250         //wanted_range_change *= 0.75;
251         wanted_range_change *= 0.5;
252         //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
253
254         // If needed range change is very small, just return
255         if(fabs(wanted_range_change) < 0.001)
256         {
257                 //dstream<<"ignoring small wanted_range_change"<<std::endl;
258                 return;
259         }
260
261         new_range += wanted_range_change;
262         //dstream<<"new_range="<<new_range/*<<std::endl*/;
263         
264         //float new_range_unclamped = new_range;
265         if(new_range < range_min)
266                 new_range = range_min;
267         if(new_range > range_max)
268                 new_range = range_max;
269         
270         /*if(new_range != new_range_unclamped)
271                 dstream<<", clamped to "<<new_range<<std::endl;
272         else
273                 dstream<<std::endl;*/
274
275         draw_control.wanted_range = new_range;
276
277         range_old = new_range;
278         frametime_old = frametime;
279 }
280
281 /*
282         Hotbar draw routine
283 */
284 void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font,
285                 v2s32 centerlowerpos, s32 imgsize, s32 itemcount,
286                 Inventory *inventory, s32 halfheartcount)
287 {
288         InventoryList *mainlist = inventory->getList("main");
289         if(mainlist == NULL)
290         {
291                 dstream<<"WARNING: draw_hotbar(): mainlist == NULL"<<std::endl;
292                 return;
293         }
294         
295         s32 padding = imgsize/12;
296         //s32 height = imgsize + padding*2;
297         s32 width = itemcount*(imgsize+padding*2);
298         
299         // Position of upper left corner of bar
300         v2s32 pos = centerlowerpos - v2s32(width/2, imgsize+padding*2);
301         
302         // Draw background color
303         /*core::rect<s32> barrect(0,0,width,height);
304         barrect += pos;
305         video::SColor bgcolor(255,128,128,128);
306         driver->draw2DRectangle(bgcolor, barrect, NULL);*/
307
308         core::rect<s32> imgrect(0,0,imgsize,imgsize);
309
310         for(s32 i=0; i<itemcount; i++)
311         {
312                 InventoryItem *item = mainlist->getItem(i);
313                 
314                 core::rect<s32> rect = imgrect + pos
315                                 + v2s32(padding+i*(imgsize+padding*2), padding);
316                 
317                 if(g_selected_item == i)
318                 {
319                         driver->draw2DRectangle(video::SColor(255,255,0,0),
320                                         core::rect<s32>(rect.UpperLeftCorner - v2s32(1,1)*padding,
321                                                         rect.LowerRightCorner + v2s32(1,1)*padding),
322                                         NULL);
323                 }
324                 else
325                 {
326                         video::SColor bgcolor2(128,0,0,0);
327                         driver->draw2DRectangle(bgcolor2, rect, NULL);
328                 }
329
330                 if(item != NULL)
331                 {
332                         drawInventoryItem(driver, font, item, rect, NULL);
333                 }
334         }
335         
336         /*
337                 Draw hearts
338         */
339         {
340                 video::ITexture *heart_texture =
341                                 driver->getTexture(porting::getDataPath("heart.png").c_str());
342                 v2s32 p = pos + v2s32(0, -20);
343                 for(s32 i=0; i<halfheartcount/2; i++)
344                 {
345                         const video::SColor color(255,255,255,255);
346                         const video::SColor colors[] = {color,color,color,color};
347                         core::rect<s32> rect(0,0,16,16);
348                         rect += p;
349                         driver->draw2DImage(heart_texture, rect,
350                                 core::rect<s32>(core::position2d<s32>(0,0),
351                                 core::dimension2di(heart_texture->getOriginalSize())),
352                                 NULL, colors, true);
353                         p += v2s32(20,0);
354                 }
355                 if(halfheartcount % 2 == 1)
356                 {
357                         const video::SColor color(255,255,255,255);
358                         const video::SColor colors[] = {color,color,color,color};
359                         core::rect<s32> rect(0,0,16/2,16);
360                         rect += p;
361                         core::dimension2di srcd(heart_texture->getOriginalSize());
362                         srcd.Width /= 2;
363                         driver->draw2DImage(heart_texture, rect,
364                                 core::rect<s32>(core::position2d<s32>(0,0), srcd),
365                                 NULL, colors, true);
366                         p += v2s32(20,0);
367                 }
368         }
369 }
370
371 /*
372         Find what the player is pointing at
373 */
374 void getPointedNode(Client *client, v3f player_position,
375                 v3f camera_direction, v3f camera_position,
376                 bool &nodefound, core::line3d<f32> shootline,
377                 v3s16 &nodepos, v3s16 &neighbourpos,
378                 core::aabbox3d<f32> &nodehilightbox,
379                 f32 d)
380 {
381         f32 mindistance = BS * 1001;
382         
383         v3s16 pos_i = floatToInt(player_position, BS);
384
385         /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
386                         <<std::endl;*/
387
388         s16 a = d;
389         s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
390         s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
391         s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
392         s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
393         s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
394         s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
395         
396         for(s16 y = ystart; y <= yend; y++)
397         for(s16 z = zstart; z <= zend; z++)
398         for(s16 x = xstart; x <= xend; x++)
399         {
400                 MapNode n;
401                 try
402                 {
403                         n = client->getNode(v3s16(x,y,z));
404                         if(content_pointable(n.d) == false)
405                                 continue;
406                 }
407                 catch(InvalidPositionException &e)
408                 {
409                         continue;
410                 }
411
412                 v3s16 np(x,y,z);
413                 v3f npf = intToFloat(np, BS);
414                 
415                 f32 d = 0.01;
416                 
417                 v3s16 dirs[6] = {
418                         v3s16(0,0,1), // back
419                         v3s16(0,1,0), // top
420                         v3s16(1,0,0), // right
421                         v3s16(0,0,-1), // front
422                         v3s16(0,-1,0), // bottom
423                         v3s16(-1,0,0), // left
424                 };
425                 
426                 /*
427                         Meta-objects
428                 */
429                 if(n.d == CONTENT_TORCH)
430                 {
431                         v3s16 dir = unpackDir(n.dir);
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                         // bottom
440                         if(dir == v3s16(0,-1,0))
441                         {
442                                 box = core::aabbox3d<f32>(
443                                         npf - v3f(BS/6, BS/2, BS/6),
444                                         npf + v3f(BS/6, -BS/2+BS/3*2, BS/6)
445                                 );
446                         }
447                         // top
448                         else if(dir == v3s16(0,1,0))
449                         {
450                                 box = core::aabbox3d<f32>(
451                                         npf - v3f(BS/6, -BS/2+BS/3*2, BS/6),
452                                         npf + v3f(BS/6, BS/2, BS/6)
453                                 );
454                         }
455                         // side
456                         else
457                         {
458                                 box = core::aabbox3d<f32>(
459                                         cpf - v3f(BS/6, BS/3, BS/6),
460                                         cpf + v3f(BS/6, BS/3, BS/6)
461                                 );
462                         }
463
464                         if(distance < mindistance)
465                         {
466                                 if(box.intersectsWithLine(shootline))
467                                 {
468                                         nodefound = true;
469                                         nodepos = np;
470                                         neighbourpos = np;
471                                         mindistance = distance;
472                                         nodehilightbox = box;
473                                 }
474                         }
475                 }
476                 else if(n.d == CONTENT_SIGN_WALL)
477                 {
478                         v3s16 dir = unpackDir(n.dir);
479                         v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
480                         dir_f *= BS/2 - BS/6 - BS/20;
481                         v3f cpf = npf + dir_f;
482                         f32 distance = (cpf - camera_position).getLength();
483
484                         v3f vertices[4] =
485                         {
486                                 v3f(BS*0.42,-BS*0.35,-BS*0.4),
487                                 v3f(BS*0.49, BS*0.35, BS*0.4),
488                         };
489
490                         for(s32 i=0; i<2; i++)
491                         {
492                                 if(dir == v3s16(1,0,0))
493                                         vertices[i].rotateXZBy(0);
494                                 if(dir == v3s16(-1,0,0))
495                                         vertices[i].rotateXZBy(180);
496                                 if(dir == v3s16(0,0,1))
497                                         vertices[i].rotateXZBy(90);
498                                 if(dir == v3s16(0,0,-1))
499                                         vertices[i].rotateXZBy(-90);
500                                 if(dir == v3s16(0,-1,0))
501                                         vertices[i].rotateXYBy(-90);
502                                 if(dir == v3s16(0,1,0))
503                                         vertices[i].rotateXYBy(90);
504
505                                 vertices[i] += npf;
506                         }
507
508                         core::aabbox3d<f32> box;
509
510                         box = core::aabbox3d<f32>(vertices[0]);
511                         box.addInternalPoint(vertices[1]);
512
513                         if(distance < mindistance)
514                         {
515                                 if(box.intersectsWithLine(shootline))
516                                 {
517                                         nodefound = true;
518                                         nodepos = np;
519                                         neighbourpos = np;
520                                         mindistance = distance;
521                                         nodehilightbox = box;
522                                 }
523                         }
524                 }
525                 /*
526                         Regular blocks
527                 */
528                 else
529                 {
530                         for(u16 i=0; i<6; i++)
531                         {
532                                 v3f dir_f = v3f(dirs[i].X,
533                                                 dirs[i].Y, dirs[i].Z);
534                                 v3f centerpoint = npf + dir_f * BS/2;
535                                 f32 distance =
536                                                 (centerpoint - camera_position).getLength();
537                                 
538                                 if(distance < mindistance)
539                                 {
540                                         core::CMatrix4<f32> m;
541                                         m.buildRotateFromTo(v3f(0,0,1), dir_f);
542
543                                         // This is the back face
544                                         v3f corners[2] = {
545                                                 v3f(BS/2, BS/2, BS/2),
546                                                 v3f(-BS/2, -BS/2, BS/2+d)
547                                         };
548                                         
549                                         for(u16 j=0; j<2; j++)
550                                         {
551                                                 m.rotateVect(corners[j]);
552                                                 corners[j] += npf;
553                                         }
554
555                                         core::aabbox3d<f32> facebox(corners[0]);
556                                         facebox.addInternalPoint(corners[1]);
557
558                                         if(facebox.intersectsWithLine(shootline))
559                                         {
560                                                 nodefound = true;
561                                                 nodepos = np;
562                                                 neighbourpos = np + dirs[i];
563                                                 mindistance = distance;
564
565                                                 //nodehilightbox = facebox;
566
567                                                 const float d = 0.502;
568                                                 core::aabbox3d<f32> nodebox
569                                                                 (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
570                                                 v3f nodepos_f = intToFloat(nodepos, BS);
571                                                 nodebox.MinEdge += nodepos_f;
572                                                 nodebox.MaxEdge += nodepos_f;
573                                                 nodehilightbox = nodebox;
574                                         }
575                                 } // if distance < mindistance
576                         } // for dirs
577                 } // regular block
578         } // for coords
579 }
580
581 void the_game(
582         bool &kill,
583         bool random_input,
584         InputHandler *input,
585         IrrlichtDevice *device,
586         gui::IGUIFont* font,
587         std::string map_dir,
588         std::string playername,
589         std::string address,
590         u16 port,
591         std::wstring &error_message
592 )
593 {
594         video::IVideoDriver* driver = device->getVideoDriver();
595         scene::ISceneManager* smgr = device->getSceneManager();
596
597         v2u32 screensize(0,0);
598         v2u32 last_screensize(0,0);
599         screensize = driver->getScreenSize();
600
601         const s32 hotbar_itemcount = 8;
602         const s32 hotbar_imagesize = 36;
603         
604         /*
605                 Draw "Loading" screen
606         */
607         const wchar_t *text = L"Loading and connecting...";
608         u32 text_height = font->getDimension(text).Height;
609         core::vector2d<s32> center(screensize.X/2, screensize.Y/2);
610         core::vector2d<s32> textsize(300, text_height);
611         core::rect<s32> textrect(center - textsize/2, center + textsize/2);
612
613         gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
614                         text, textrect, false, false);
615         gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
616
617         driver->beginScene(true, true, video::SColor(255,0,0,0));
618         guienv->drawAll();
619         driver->endScene();
620
621         std::cout<<DTIME<<"Creating server and client"<<std::endl;
622         
623         /*
624                 Create server.
625                 SharedPtr will delete it when it goes out of scope.
626         */
627         SharedPtr<Server> server;
628         if(address == ""){
629                 server = new Server(map_dir);
630                 server->start(port);
631         }
632         
633         /*
634                 Create client
635         */
636
637         Client client(device, playername.c_str(), draw_control);
638                         
639         Address connect_address(0,0,0,0, port);
640         try{
641                 if(address == "")
642                         //connect_address.Resolve("localhost");
643                         connect_address.setAddress(127,0,0,1);
644                 else
645                         connect_address.Resolve(address.c_str());
646         }
647         catch(ResolveError &e)
648         {
649                 std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
650                 //return 0;
651                 error_message = L"Couldn't resolve address";
652                 gui_loadingtext->remove();
653                 return;
654         }
655
656         /*
657                 Attempt to connect to the server
658         */
659         
660         dstream<<DTIME<<"Connecting to server at ";
661         connect_address.print(&dstream);
662         dstream<<std::endl;
663         client.connect(connect_address);
664
665         bool could_connect = false;
666         
667         try{
668                 float time_counter = 0.0;
669                 for(;;)
670                 {
671                         if(client.connectedAndInitialized())
672                         {
673                                 could_connect = true;
674                                 break;
675                         }
676                         // Wait for 10 seconds
677                         if(time_counter >= 10.0)
678                         {
679                                 break;
680                         }
681
682                         // Update screen
683                         driver->beginScene(true, true, video::SColor(255,0,0,0));
684                         guienv->drawAll();
685                         driver->endScene();
686
687                         // Update client and server
688
689                         client.step(0.1);
690
691                         if(server != NULL)
692                                 server->step(0.1);
693                         
694                         // Delay a bit
695                         sleep_ms(100);
696                         time_counter += 0.1;
697                 }
698         }
699         catch(con::PeerNotFoundException &e)
700         {}
701
702         if(could_connect == false)
703         {
704                 std::cout<<DTIME<<"Timed out."<<std::endl;
705                 error_message = L"Connection timed out.";
706                 gui_loadingtext->remove();
707                 return;
708         }
709
710         /*
711                 Create skybox
712         */
713         /*scene::ISceneNode* skybox;
714         skybox = smgr->addSkyBoxSceneNode(
715                 driver->getTexture(porting::getDataPath("skybox2.png").c_str()),
716                 driver->getTexture(porting::getDataPath("skybox3.png").c_str()),
717                 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
718                 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
719                 driver->getTexture(porting::getDataPath("skybox1.png").c_str()),
720                 driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/
721         
722         /*
723                 Create the camera node
724         */
725
726         scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
727                 0, // Camera parent
728                 v3f(BS*100, BS*2, BS*100), // Look from
729                 v3f(BS*100+1, BS*2, BS*100), // Look to
730                 -1 // Camera ID
731         );
732
733         if(camera == NULL)
734         {
735                 error_message = L"Failed to create the camera node";
736                 return;
737         }
738
739         //video::SColor skycolor = video::SColor(255,90,140,200);
740         //video::SColor skycolor = video::SColor(255,166,202,244);
741         //video::SColor skycolor = video::SColor(255,120,185,244);
742         video::SColor skycolor = video::SColor(255,140,186,250);
743
744         camera->setFOV(FOV_ANGLE);
745
746         // Just so big a value that everything rendered is visible
747         camera->setFarValue(100000*BS);
748         
749         f32 camera_yaw = 0; // "right/left"
750         f32 camera_pitch = 0; // "up/down"
751
752         /*
753                 Move into game
754         */
755         
756         gui_loadingtext->remove();
757
758         /*
759                 Add some gui stuff
760         */
761
762         // First line of debug text
763         gui::IGUIStaticText *guitext = guienv->addStaticText(
764                         L"Minetest-c55",
765                         core::rect<s32>(5, 5, 795, 5+text_height),
766                         false, false);
767         // Second line of debug text
768         gui::IGUIStaticText *guitext2 = guienv->addStaticText(
769                         L"",
770                         core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),
771                         false, false);
772         
773         // At the middle of the screen
774         // Object infos are shown in this
775         gui::IGUIStaticText *guitext_info = guienv->addStaticText(
776                         L"",
777                         core::rect<s32>(0,0,400,text_height+5) + v2s32(100,200),
778                         false, false);
779         
780         // Chat text
781         gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
782                         L"",
783                         core::rect<s32>(0,0,0,0),
784                         false, false); // Disable word wrap as of now
785                         //false, true);
786         //guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));
787         core::list<ChatLine> chat_lines;
788         
789         /*GUIQuickInventory *quick_inventory = new GUIQuickInventory
790                         (guienv, NULL, v2s32(10, 70), 5, &local_inventory);*/
791         /*GUIQuickInventory *quick_inventory = new GUIQuickInventory
792                         (guienv, NULL, v2s32(0, 0), quickinv_itemcount, &local_inventory);*/
793         
794         // Test the text input system
795         /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,
796                         NULL))->drop();*/
797         /*GUIMessageMenu *menu =
798                         new GUIMessageMenu(guienv, guiroot, -1, 
799                                 &g_menumgr,
800                                 L"Asd");
801         menu->drop();*/
802         
803         // Launch pause menu
804         (new GUIPauseMenu(guienv, guiroot, -1, g_gamecallback,
805                         &g_menumgr))->drop();
806         
807         // Enable texts
808         /*guitext2->setVisible(true);
809         guitext_info->setVisible(true);
810         guitext_chat->setVisible(true);*/
811
812         //s32 guitext_chat_pad_bottom = 70;
813
814         /*
815                 Some statistics are collected in these
816         */
817         u32 drawtime = 0;
818         u32 beginscenetime = 0;
819         u32 scenetime = 0;
820         u32 endscenetime = 0;
821         
822         // A test
823         //throw con::PeerNotFoundException("lol");
824
825         core::list<float> frametime_log;
826
827         float damage_flash_timer = 0;
828
829         /*
830                 Main loop
831         */
832
833         bool first_loop_after_window_activation = true;
834
835         // Time is in milliseconds
836         // NOTE: getRealTime() causes strange problems in wine (imprecision?)
837         // NOTE: So we have to use getTime() and call run()s between them
838         u32 lasttime = device->getTimer()->getTime();
839
840         while(device->run() && kill == false)
841         {
842                 if(g_gamecallback->disconnect_requested)
843                 {
844                         g_gamecallback->disconnect_requested = false;
845                         break;
846                 }
847
848                 /*
849                         Process TextureSource's queue
850                 */
851                 ((TextureSource*)g_texturesource)->processQueue();
852
853                 /*
854                         Random calculations
855                 */
856                 last_screensize = screensize;
857                 screensize = driver->getScreenSize();
858                 v2s32 displaycenter(screensize.X/2,screensize.Y/2);
859                 //bool screensize_changed = screensize != last_screensize;
860                 
861                 // Hilight boxes collected during the loop and displayed
862                 core::list< core::aabbox3d<f32> > hilightboxes;
863                 
864                 // Info text
865                 std::wstring infotext;
866
867                 // When screen size changes, update positions and sizes of stuff
868                 /*if(screensize_changed)
869                 {
870                         v2s32 pos(displaycenter.X-((quickinv_itemcount-1)*quickinv_spacing+quickinv_size)/2, screensize.Y-quickinv_spacing);
871                         quick_inventory->updatePosition(pos);
872                 }*/
873
874                 //TimeTaker //timer1("//timer1");
875                 
876                 // Time of frame without fps limit
877                 float busytime;
878                 u32 busytime_u32;
879                 {
880                         // not using getRealTime is necessary for wine
881                         u32 time = device->getTimer()->getTime();
882                         if(time > lasttime)
883                                 busytime_u32 = time - lasttime;
884                         else
885                                 busytime_u32 = 0;
886                         busytime = busytime_u32 / 1000.0;
887                 }
888
889                 //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
890         
891                 // Necessary for device->getTimer()->getTime()
892                 device->run();
893
894                 /*
895                         Viewing range
896                 */
897                 
898                 updateViewingRange(busytime, &client);
899                 
900                 /*
901                         FPS limiter
902                 */
903
904                 {
905                         float fps_max = g_settings.getFloat("fps_max");
906                         u32 frametime_min = 1000./fps_max;
907                         
908                         if(busytime_u32 < frametime_min)
909                         {
910                                 u32 sleeptime = frametime_min - busytime_u32;
911                                 device->sleep(sleeptime);
912                         }
913                 }
914
915                 // Necessary for device->getTimer()->getTime()
916                 device->run();
917
918                 /*
919                         Time difference calculation
920                 */
921                 f32 dtime; // in seconds
922                 
923                 u32 time = device->getTimer()->getTime();
924                 if(time > lasttime)
925                         dtime = (time - lasttime) / 1000.0;
926                 else
927                         dtime = 0;
928                 lasttime = time;
929
930                 /*
931                         Log frametime for visualization
932                 */
933                 frametime_log.push_back(dtime);
934                 if(frametime_log.size() > 100)
935                 {
936                         core::list<float>::Iterator i = frametime_log.begin();
937                         frametime_log.erase(i);
938                 }
939
940                 /*
941                         Visualize frametime in terminal
942                 */
943                 /*for(u32 i=0; i<dtime*400; i++)
944                         std::cout<<"X";
945                 std::cout<<std::endl;*/
946
947                 /*
948                         Time average and jitter calculation
949                 */
950
951                 static f32 dtime_avg1 = 0.0;
952                 dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
953                 f32 dtime_jitter1 = dtime - dtime_avg1;
954
955                 static f32 dtime_jitter1_max_sample = 0.0;
956                 static f32 dtime_jitter1_max_fraction = 0.0;
957                 {
958                         static f32 jitter1_max = 0.0;
959                         static f32 counter = 0.0;
960                         if(dtime_jitter1 > jitter1_max)
961                                 jitter1_max = dtime_jitter1;
962                         counter += dtime;
963                         if(counter > 0.0)
964                         {
965                                 counter -= 3.0;
966                                 dtime_jitter1_max_sample = jitter1_max;
967                                 dtime_jitter1_max_fraction
968                                                 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
969                                 jitter1_max = 0.0;
970                         }
971                 }
972                 
973                 /*
974                         Busytime average and jitter calculation
975                 */
976
977                 static f32 busytime_avg1 = 0.0;
978                 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
979                 f32 busytime_jitter1 = busytime - busytime_avg1;
980                 
981                 static f32 busytime_jitter1_max_sample = 0.0;
982                 static f32 busytime_jitter1_min_sample = 0.0;
983                 {
984                         static f32 jitter1_max = 0.0;
985                         static f32 jitter1_min = 0.0;
986                         static f32 counter = 0.0;
987                         if(busytime_jitter1 > jitter1_max)
988                                 jitter1_max = busytime_jitter1;
989                         if(busytime_jitter1 < jitter1_min)
990                                 jitter1_min = busytime_jitter1;
991                         counter += dtime;
992                         if(counter > 0.0){
993                                 counter -= 3.0;
994                                 busytime_jitter1_max_sample = jitter1_max;
995                                 busytime_jitter1_min_sample = jitter1_min;
996                                 jitter1_max = 0.0;
997                                 jitter1_min = 0.0;
998                         }
999                 }
1000                 
1001                 /*
1002                         Debug info for client
1003                 */
1004                 {
1005                         static float counter = 0.0;
1006                         counter -= dtime;
1007                         if(counter < 0)
1008                         {
1009                                 counter = 30.0;
1010                                 client.printDebugInfo(std::cout);
1011                         }
1012                 }
1013
1014                 /*
1015                         Direct handling of user input
1016                 */
1017                 
1018                 // Reset input if window not active or some menu is active
1019                 if(device->isWindowActive() == false || noMenuActive() == false)
1020                 {
1021                         input->clear();
1022                 }
1023
1024                 // Input handler step() (used by the random input generator)
1025                 input->step(dtime);
1026
1027                 /*
1028                         Launch menus according to keys
1029                 */
1030                 if(input->wasKeyDown(irr::KEY_KEY_I))
1031                 {
1032                         dstream<<DTIME<<"the_game: "
1033                                         <<"Launching inventory"<<std::endl;
1034                         
1035                         GUIInventoryMenu *menu =
1036                                 new GUIInventoryMenu(guienv, guiroot, -1,
1037                                         &g_menumgr, v2s16(8,7),
1038                                         client.getInventoryContext(),
1039                                         &client);
1040
1041                         core::array<GUIInventoryMenu::DrawSpec> draw_spec;
1042                         draw_spec.push_back(GUIInventoryMenu::DrawSpec(
1043                                         "list", "current_player", "main",
1044                                         v2s32(0, 3), v2s32(8, 4)));
1045                         draw_spec.push_back(GUIInventoryMenu::DrawSpec(
1046                                         "list", "current_player", "craft",
1047                                         v2s32(3, 0), v2s32(3, 3)));
1048                         draw_spec.push_back(GUIInventoryMenu::DrawSpec(
1049                                         "list", "current_player", "craftresult",
1050                                         v2s32(7, 1), v2s32(1, 1)));
1051
1052                         menu->setDrawSpec(draw_spec);
1053
1054                         menu->drop();
1055                 }
1056                 else if(input->wasKeyDown(irr::KEY_ESCAPE))
1057                 {
1058                         dstream<<DTIME<<"the_game: "
1059                                         <<"Launching pause menu"<<std::endl;
1060                         // It will delete itself by itself
1061                         (new GUIPauseMenu(guienv, guiroot, -1, g_gamecallback,
1062                                         &g_menumgr))->drop();
1063                 }
1064                 else if(input->wasKeyDown(irr::KEY_KEY_T))
1065                 {
1066                         TextDest *dest = new TextDestChat(&client);
1067
1068                         (new GUITextInputMenu(guienv, guiroot, -1,
1069                                         &g_menumgr, dest,
1070                                         L""))->drop();
1071                 }
1072
1073                 // Item selection with mouse wheel
1074                 {
1075                         s32 wheel = input->getMouseWheel();
1076                         u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE-1,
1077                                         hotbar_itemcount-1);
1078
1079                         if(wheel < 0)
1080                         {
1081                                 if(g_selected_item < max_item)
1082                                         g_selected_item++;
1083                                 else
1084                                         g_selected_item = 0;
1085                         }
1086                         else if(wheel > 0)
1087                         {
1088                                 if(g_selected_item > 0)
1089                                         g_selected_item--;
1090                                 else
1091                                         g_selected_item = max_item;
1092                         }
1093                 }
1094                 
1095                 // Item selection
1096                 for(u16 i=0; i<10; i++)
1097                 {
1098                         s32 keycode = irr::KEY_KEY_1 + i;
1099                         if(i == 9)
1100                                 keycode = irr::KEY_KEY_0;
1101                         if(input->wasKeyDown((irr::EKEY_CODE)keycode))
1102                         {
1103                                 if(i < PLAYER_INVENTORY_SIZE && i < hotbar_itemcount)
1104                                 {
1105                                         g_selected_item = i;
1106
1107                                         dstream<<DTIME<<"Selected item: "
1108                                                         <<g_selected_item<<std::endl;
1109                                 }
1110                         }
1111                 }
1112
1113                 // Viewing range selection
1114                 if(input->wasKeyDown(irr::KEY_KEY_R))
1115                 {
1116                         if(draw_control.range_all)
1117                         {
1118                                 draw_control.range_all = false;
1119                                 dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
1120                         }
1121                         else
1122                         {
1123                                 draw_control.range_all = true;
1124                                 dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
1125                         }
1126                 }
1127
1128                 // Print debug stacks
1129                 if(input->wasKeyDown(irr::KEY_KEY_P))
1130                 {
1131                         dstream<<"-----------------------------------------"
1132                                         <<std::endl;
1133                         dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
1134                         dstream<<"-----------------------------------------"
1135                                         <<std::endl;
1136                         debug_stacks_print();
1137                 }
1138
1139                 /*
1140                         Player speed control
1141                 */
1142                 
1143                 {
1144                         /*bool a_up,
1145                         bool a_down,
1146                         bool a_left,
1147                         bool a_right,
1148                         bool a_jump,
1149                         bool a_superspeed,
1150                         bool a_sneak,
1151                         float a_pitch,
1152                         float a_yaw*/
1153                         PlayerControl control(
1154                                 input->isKeyDown(irr::KEY_KEY_W),
1155                                 input->isKeyDown(irr::KEY_KEY_S),
1156                                 input->isKeyDown(irr::KEY_KEY_A),
1157                                 input->isKeyDown(irr::KEY_KEY_D),
1158                                 input->isKeyDown(irr::KEY_SPACE),
1159                                 input->isKeyDown(irr::KEY_KEY_E),
1160                                 input->isKeyDown(irr::KEY_LSHIFT)
1161                                                 || input->isKeyDown(irr::KEY_RSHIFT),
1162                                 camera_pitch,
1163                                 camera_yaw
1164                         );
1165                         client.setPlayerControl(control);
1166                 }
1167                 
1168                 /*
1169                         Run server
1170                 */
1171
1172                 if(server != NULL)
1173                 {
1174                         //TimeTaker timer("server->step(dtime)");
1175                         server->step(dtime);
1176                 }
1177
1178                 /*
1179                         Process environment
1180                 */
1181                 
1182                 {
1183                         //TimeTaker timer("client.step(dtime)");
1184                         client.step(dtime);
1185                         //client.step(dtime_avg1);
1186                 }
1187
1188                 // Read client events
1189                 for(;;)
1190                 {
1191                         ClientEvent event = client.getClientEvent();
1192                         if(event.type == CE_NONE)
1193                         {
1194                                 break;
1195                         }
1196                         else if(event.type == CE_PLAYER_DAMAGE)
1197                         {
1198                                 //u16 damage = event.player_damage.amount;
1199                                 //dstream<<"Player damage: "<<damage<<std::endl;
1200                                 damage_flash_timer = 0.05;
1201                         }
1202                         else if(event.type == CE_PLAYER_FORCE_MOVE)
1203                         {
1204                                 camera_yaw = event.player_force_move.yaw;
1205                                 camera_pitch = event.player_force_move.pitch;
1206                         }
1207                 }
1208                 
1209                 // Get player position
1210                 v3f player_position = client.getPlayerPosition();
1211                 
1212                 //TimeTaker //timer2("//timer2");
1213
1214                 /*
1215                         Mouse and camera control
1216                 */
1217                 
1218                 if((device->isWindowActive() && noMenuActive()) || random_input)
1219                 {
1220                         if(!random_input)
1221                                 device->getCursorControl()->setVisible(false);
1222
1223                         if(first_loop_after_window_activation){
1224                                 //std::cout<<"window active, first loop"<<std::endl;
1225                                 first_loop_after_window_activation = false;
1226                         }
1227                         else{
1228                                 s32 dx = input->getMousePos().X - displaycenter.X;
1229                                 s32 dy = input->getMousePos().Y - displaycenter.Y;
1230                                 //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
1231                                 camera_yaw -= dx*0.2;
1232                                 camera_pitch += dy*0.2;
1233                                 if(camera_pitch < -89.5) camera_pitch = -89.5;
1234                                 if(camera_pitch > 89.5) camera_pitch = 89.5;
1235                         }
1236                         input->setMousePos(displaycenter.X, displaycenter.Y);
1237                 }
1238                 else{
1239                         device->getCursorControl()->setVisible(true);
1240
1241                         //std::cout<<"window inactive"<<std::endl;
1242                         first_loop_after_window_activation = true;
1243                 }
1244
1245                 camera_yaw = wrapDegrees(camera_yaw);
1246                 camera_pitch = wrapDegrees(camera_pitch);
1247                 
1248                 v3f camera_direction = v3f(0,0,1);
1249                 camera_direction.rotateYZBy(camera_pitch);
1250                 camera_direction.rotateXZBy(camera_yaw);
1251                 
1252                 // This is at the height of the eyes of the current figure
1253                 //v3f camera_position = player_position + v3f(0, BS+BS/2, 0);
1254                 // This is more like in minecraft
1255                 v3f camera_position = player_position + v3f(0, BS+BS*0.625, 0);
1256
1257                 camera->setPosition(camera_position);
1258                 // *100.0 helps in large map coordinates
1259                 camera->setTarget(camera_position + camera_direction * 100.0);
1260
1261                 if(FIELD_OF_VIEW_TEST){
1262                         client.updateCamera(v3f(0,0,0), v3f(0,0,1));
1263                 }
1264                 else{
1265                         //TimeTaker timer("client.updateCamera");
1266                         client.updateCamera(camera_position, camera_direction);
1267                 }
1268                 
1269                 //timer2.stop();
1270                 //TimeTaker //timer3("//timer3");
1271
1272                 /*
1273                         Calculate what block is the crosshair pointing to
1274                 */
1275                 
1276                 //u32 t1 = device->getTimer()->getRealTime();
1277                 
1278                 //f32 d = 4; // max. distance
1279                 f32 d = 4; // max. distance
1280                 core::line3d<f32> shootline(camera_position,
1281                                 camera_position + camera_direction * BS * (d+1));
1282
1283                 MapBlockObject *selected_object = client.getSelectedObject
1284                                 (d*BS, camera_position, shootline);
1285
1286                 ClientActiveObject *selected_active_object
1287                                 = client.getSelectedActiveObject
1288                                         (d*BS, camera_position, shootline);
1289
1290                 if(selected_object != NULL)
1291                 {
1292                         //dstream<<"Client returned selected_object != NULL"<<std::endl;
1293
1294                         core::aabbox3d<f32> box_on_map
1295                                         = selected_object->getSelectionBoxOnMap();
1296
1297                         hilightboxes.push_back(box_on_map);
1298
1299                         infotext = narrow_to_wide(selected_object->infoText());
1300
1301                         if(input->getLeftClicked())
1302                         {
1303                                 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
1304                                 client.clickObject(0, selected_object->getBlock()->getPos(),
1305                                                 selected_object->getId(), g_selected_item);
1306                         }
1307                         else if(input->getRightClicked())
1308                         {
1309                                 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
1310                                 /*
1311                                         Check if we want to modify the object ourselves
1312                                 */
1313                                 if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
1314                                 {
1315                                         dstream<<"Sign object right-clicked"<<std::endl;
1316                                         
1317                                         if(random_input == false)
1318                                         {
1319                                                 // Get a new text for it
1320
1321                                                 TextDest *dest = new TextDestSign(
1322                                                                 selected_object->getBlock()->getPos(),
1323                                                                 selected_object->getId(),
1324                                                                 &client);
1325
1326                                                 SignObject *sign_object = (SignObject*)selected_object;
1327
1328                                                 std::wstring wtext =
1329                                                                 narrow_to_wide(sign_object->getText());
1330
1331                                                 (new GUITextInputMenu(guienv, guiroot, -1,
1332                                                                 &g_menumgr, dest,
1333                                                                 wtext))->drop();
1334                                         }
1335                                 }
1336                                 /*
1337                                         Otherwise pass the event to the server as-is
1338                                 */
1339                                 else
1340                                 {
1341                                         client.clickObject(1, selected_object->getBlock()->getPos(),
1342                                                         selected_object->getId(), g_selected_item);
1343                                 }
1344                         }
1345                 }
1346                 else if(selected_active_object != NULL)
1347                 {
1348                         //dstream<<"Client returned selected_active_object != NULL"<<std::endl;
1349                         
1350                         core::aabbox3d<f32> *selection_box
1351                                         = selected_active_object->getSelectionBox();
1352                         // Box should exist because object was returned in the
1353                         // first place
1354                         assert(selection_box);
1355
1356                         v3f pos = selected_active_object->getPosition();
1357
1358                         core::aabbox3d<f32> box_on_map(
1359                                         selection_box->MinEdge + pos,
1360                                         selection_box->MaxEdge + pos
1361                         );
1362
1363                         hilightboxes.push_back(box_on_map);
1364
1365                         //infotext = narrow_to_wide("A ClientActiveObject");
1366                         infotext = narrow_to_wide(selected_active_object->infoText());
1367
1368                         if(input->getLeftClicked())
1369                         {
1370                                 std::cout<<DTIME<<"Left-clicked object"<<std::endl;
1371                                 client.clickActiveObject(0,
1372                                                 selected_active_object->getId(), g_selected_item);
1373                         }
1374                         else if(input->getRightClicked())
1375                         {
1376                                 std::cout<<DTIME<<"Right-clicked object"<<std::endl;
1377                         }
1378                 }
1379                 else // selected_object == NULL
1380                 {
1381
1382                 /*
1383                         Find out which node we are pointing at
1384                 */
1385                 
1386                 bool nodefound = false;
1387                 v3s16 nodepos;
1388                 v3s16 neighbourpos;
1389                 core::aabbox3d<f32> nodehilightbox;
1390
1391                 getPointedNode(&client, player_position,
1392                                 camera_direction, camera_position,
1393                                 nodefound, shootline,
1394                                 nodepos, neighbourpos,
1395                                 nodehilightbox, d);
1396         
1397                 static float nodig_delay_counter = 0.0;
1398
1399                 if(nodefound)
1400                 {
1401                         static v3s16 nodepos_old(-32768,-32768,-32768);
1402
1403                         static float dig_time = 0.0;
1404                         static u16 dig_index = 0;
1405                         
1406                         /*
1407                                 Visualize selection
1408                         */
1409
1410                         hilightboxes.push_back(nodehilightbox);
1411
1412                         /*
1413                                 Check information text of node
1414                         */
1415
1416                         NodeMetadata *meta = client.getNodeMetadata(nodepos);
1417                         if(meta)
1418                         {
1419                                 infotext = narrow_to_wide(meta->infoText());
1420                         }
1421                         
1422                         //MapNode node = client.getNode(nodepos);
1423
1424                         /*
1425                                 Handle digging
1426                         */
1427                         
1428                         if(input->getLeftReleased())
1429                         {
1430                                 client.clearTempMod(nodepos);
1431                                 dig_time = 0.0;
1432                         }
1433                         
1434                         if(nodig_delay_counter > 0.0)
1435                         {
1436                                 nodig_delay_counter -= dtime;
1437                         }
1438                         else
1439                         {
1440                                 if(nodepos != nodepos_old)
1441                                 {
1442                                         std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
1443                                                         <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
1444
1445                                         if(nodepos_old != v3s16(-32768,-32768,-32768))
1446                                         {
1447                                                 client.clearTempMod(nodepos_old);
1448                                                 dig_time = 0.0;
1449                                         }
1450                                 }
1451
1452                                 if(input->getLeftClicked() ||
1453                                                 (input->getLeftState() && nodepos != nodepos_old))
1454                                 {
1455                                         dstream<<DTIME<<"Started digging"<<std::endl;
1456                                         client.groundAction(0, nodepos, neighbourpos, g_selected_item);
1457                                 }
1458                                 if(input->getLeftClicked())
1459                                 {
1460                                         client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0));
1461                                 }
1462                                 if(input->getLeftState())
1463                                 {
1464                                         MapNode n = client.getNode(nodepos);
1465                                 
1466                                         // Get tool name. Default is "" = bare hands
1467                                         std::string toolname = "";
1468                                         InventoryList *mlist = local_inventory.getList("main");
1469                                         if(mlist != NULL)
1470                                         {
1471                                                 InventoryItem *item = mlist->getItem(g_selected_item);
1472                                                 if(item && (std::string)item->getName() == "ToolItem")
1473                                                 {
1474                                                         ToolItem *titem = (ToolItem*)item;
1475                                                         toolname = titem->getToolName();
1476                                                 }
1477                                         }
1478
1479                                         // Get digging properties for material and tool
1480                                         u8 material = n.d;
1481                                         DiggingProperties prop =
1482                                                         getDiggingProperties(material, toolname);
1483                                         
1484                                         float dig_time_complete = 0.0;
1485
1486                                         if(prop.diggable == false)
1487                                         {
1488                                                 /*dstream<<"Material "<<(int)material
1489                                                                 <<" not diggable with \""
1490                                                                 <<toolname<<"\""<<std::endl;*/
1491                                                 // I guess nobody will wait for this long
1492                                                 dig_time_complete = 10000000.0;
1493                                         }
1494                                         else
1495                                         {
1496                                                 dig_time_complete = prop.time;
1497                                         }
1498                                         
1499                                         if(dig_time_complete >= 0.001)
1500                                         {
1501                                                 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
1502                                                                 * dig_time/dig_time_complete);
1503                                         }
1504                                         // This is for torches
1505                                         else
1506                                         {
1507                                                 dig_index = CRACK_ANIMATION_LENGTH;
1508                                         }
1509
1510                                         if(dig_index < CRACK_ANIMATION_LENGTH)
1511                                         {
1512                                                 //TimeTaker timer("client.setTempMod");
1513                                                 //dstream<<"dig_index="<<dig_index<<std::endl;
1514                                                 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
1515                                         }
1516                                         else
1517                                         {
1518                                                 dstream<<DTIME<<"Digging completed"<<std::endl;
1519                                                 client.groundAction(3, nodepos, neighbourpos, g_selected_item);
1520                                                 client.clearTempMod(nodepos);
1521                                                 client.removeNode(nodepos);
1522
1523                                                 dig_time = 0;
1524
1525                                                 nodig_delay_counter = dig_time_complete
1526                                                                 / (float)CRACK_ANIMATION_LENGTH;
1527
1528                                                 // We don't want a corresponding delay to
1529                                                 // very time consuming nodes
1530                                                 if(nodig_delay_counter > 0.5)
1531                                                 {
1532                                                         nodig_delay_counter = 0.5;
1533                                                 }
1534                                                 // We want a slight delay to very little
1535                                                 // time consuming nodes
1536                                                 float mindelay = 0.15;
1537                                                 if(nodig_delay_counter < mindelay)
1538                                                 {
1539                                                         nodig_delay_counter = mindelay;
1540                                                 }
1541                                         }
1542
1543                                         dig_time += dtime;
1544                                 }
1545                         }
1546                         
1547                         if(input->getRightClicked())
1548                         {
1549                                 std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
1550                                 
1551                                 if(meta && meta->typeId() == CONTENT_SIGN_WALL && !random_input)
1552                                 {
1553                                         dstream<<"Sign node right-clicked"<<std::endl;
1554                                         
1555                                         SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
1556                                         
1557                                         // Get a new text for it
1558
1559                                         TextDest *dest = new TextDestSignNode(nodepos, &client);
1560
1561                                         std::wstring wtext =
1562                                                         narrow_to_wide(signmeta->getText());
1563
1564                                         (new GUITextInputMenu(guienv, guiroot, -1,
1565                                                         &g_menumgr, dest,
1566                                                         wtext))->drop();
1567                                 }
1568                                 else if(meta && meta->typeId() == CONTENT_CHEST && !random_input)
1569                                 {
1570                                         dstream<<"Chest node right-clicked"<<std::endl;
1571                                         
1572                                         //ChestNodeMetadata *chestmeta = (ChestNodeMetadata*)meta;
1573
1574                                         std::string chest_inv_id;
1575                                         chest_inv_id += "nodemeta:";
1576                                         chest_inv_id += itos(nodepos.X);
1577                                         chest_inv_id += ",";
1578                                         chest_inv_id += itos(nodepos.Y);
1579                                         chest_inv_id += ",";
1580                                         chest_inv_id += itos(nodepos.Z);
1581                                         
1582                                         GUIInventoryMenu *menu =
1583                                                 new GUIInventoryMenu(guienv, guiroot, -1,
1584                                                         &g_menumgr, v2s16(8,9),
1585                                                         client.getInventoryContext(),
1586                                                         &client);
1587
1588                                         core::array<GUIInventoryMenu::DrawSpec> draw_spec;
1589                                         
1590                                         draw_spec.push_back(GUIInventoryMenu::DrawSpec(
1591                                                         "list", chest_inv_id, "0",
1592                                                         v2s32(0, 0), v2s32(8, 4)));
1593                                         draw_spec.push_back(GUIInventoryMenu::DrawSpec(
1594                                                         "list", "current_player", "main",
1595                                                         v2s32(0, 5), v2s32(8, 4)));
1596
1597                                         menu->setDrawSpec(draw_spec);
1598
1599                                         menu->drop();
1600
1601                                 }
1602                                 else if(meta && meta->typeId() == CONTENT_FURNACE && !random_input)
1603                                 {
1604                                         dstream<<"Furnace node right-clicked"<<std::endl;
1605                                         
1606                                         GUIFurnaceMenu *menu =
1607                                                 new GUIFurnaceMenu(guienv, guiroot, -1,
1608                                                         &g_menumgr, nodepos, &client);
1609
1610                                         menu->drop();
1611
1612                                 }
1613                                 else
1614                                 {
1615                                         client.groundAction(1, nodepos, neighbourpos, g_selected_item);
1616                                 }
1617                         }
1618                         
1619                         nodepos_old = nodepos;
1620                 }
1621                 else{
1622                 }
1623
1624                 } // selected_object == NULL
1625                 
1626                 input->resetLeftClicked();
1627                 input->resetRightClicked();
1628                 
1629                 if(input->getLeftReleased())
1630                 {
1631                         std::cout<<DTIME<<"Left button released (stopped digging)"
1632                                         <<std::endl;
1633                         client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
1634                 }
1635                 if(input->getRightReleased())
1636                 {
1637                         //std::cout<<DTIME<<"Right released"<<std::endl;
1638                         // Nothing here
1639                 }
1640                 
1641                 input->resetLeftReleased();
1642                 input->resetRightReleased();
1643                 
1644                 /*
1645                         Calculate stuff for drawing
1646                 */
1647
1648                 camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
1649                 
1650                 u32 daynight_ratio = client.getDayNightRatio();
1651                 u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
1652                 video::SColor bgcolor = video::SColor(
1653                                 255,
1654                                 skycolor.getRed() * l / 255,
1655                                 skycolor.getGreen() * l / 255,
1656                                 skycolor.getBlue() * l / 255);
1657
1658                 /*
1659                         Fog
1660                 */
1661                 
1662                 if(g_settings.getBool("enable_fog") == true)
1663                 {
1664                         //f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/2*BS;
1665                         f32 range = draw_control.wanted_range * BS + 0.8*MAP_BLOCKSIZE*BS;
1666                         //f32 range = draw_control.wanted_range * BS + 0.0*MAP_BLOCKSIZE*BS;
1667                         if(draw_control.range_all)
1668                                 range = 100000*BS;
1669
1670                         driver->setFog(
1671                                 bgcolor,
1672                                 video::EFT_FOG_LINEAR,
1673                                 range*0.4,
1674                                 range*1.0,
1675                                 0.01,
1676                                 false, // pixel fog
1677                                 false // range fog
1678                         );
1679                 }
1680                 else
1681                 {
1682                         driver->setFog(
1683                                 bgcolor,
1684                                 video::EFT_FOG_LINEAR,
1685                                 100000*BS,
1686                                 110000*BS,
1687                                 0.01,
1688                                 false, // pixel fog
1689                                 false // range fog
1690                         );
1691                 }
1692
1693
1694                 /*
1695                         Update gui stuff (0ms)
1696                 */
1697
1698                 //TimeTaker guiupdatetimer("Gui updating");
1699                 
1700                 {
1701                         static float drawtime_avg = 0;
1702                         drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
1703                         static float beginscenetime_avg = 0;
1704                         beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
1705                         static float scenetime_avg = 0;
1706                         scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
1707                         static float endscenetime_avg = 0;
1708                         endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
1709                         
1710                         char temptext[300];
1711                         snprintf(temptext, 300, "Minetest-c55 ("
1712                                         "F: item=%i"
1713                                         ", R: range_all=%i"
1714                                         ")"
1715                                         " drawtime=%.0f, beginscenetime=%.0f"
1716                                         ", scenetime=%.0f, endscenetime=%.0f",
1717                                         g_selected_item,
1718                                         draw_control.range_all,
1719                                         drawtime_avg,
1720                                         beginscenetime_avg,
1721                                         scenetime_avg,
1722                                         endscenetime_avg
1723                                         );
1724                         
1725                         guitext->setText(narrow_to_wide(temptext).c_str());
1726                 }
1727                 
1728                 {
1729                         char temptext[300];
1730                         snprintf(temptext, 300,
1731                                         "(% .1f, % .1f, % .1f)"
1732                                         " (% .3f < btime_jitter < % .3f"
1733                                         ", dtime_jitter = % .1f %%"
1734                                         ", v_range = %.1f)",
1735                                         player_position.X/BS,
1736                                         player_position.Y/BS,
1737                                         player_position.Z/BS,
1738                                         busytime_jitter1_min_sample,
1739                                         busytime_jitter1_max_sample,
1740                                         dtime_jitter1_max_fraction * 100.0,
1741                                         draw_control.wanted_range
1742                                         );
1743
1744                         guitext2->setText(narrow_to_wide(temptext).c_str());
1745                 }
1746                 
1747                 {
1748                         guitext_info->setText(infotext.c_str());
1749                 }
1750                 
1751                 /*
1752                         Get chat messages from client
1753                 */
1754                 {
1755                         // Get new messages
1756                         std::wstring message;
1757                         while(client.getChatMessage(message))
1758                         {
1759                                 chat_lines.push_back(ChatLine(message));
1760                                 /*if(chat_lines.size() > 6)
1761                                 {
1762                                         core::list<ChatLine>::Iterator
1763                                                         i = chat_lines.begin();
1764                                         chat_lines.erase(i);
1765                                 }*/
1766                         }
1767                         // Append them to form the whole static text and throw
1768                         // it to the gui element
1769                         std::wstring whole;
1770                         // This will correspond to the line number counted from
1771                         // top to bottom, from size-1 to 0
1772                         s16 line_number = chat_lines.size();
1773                         // Count of messages to be removed from the top
1774                         u16 to_be_removed_count = 0;
1775                         for(core::list<ChatLine>::Iterator
1776                                         i = chat_lines.begin();
1777                                         i != chat_lines.end(); i++)
1778                         {
1779                                 // After this, line number is valid for this loop
1780                                 line_number--;
1781                                 // Increment age
1782                                 (*i).age += dtime;
1783                                 /*
1784                                         This results in a maximum age of 60*6 to the
1785                                         lowermost line and a maximum of 6 lines
1786                                 */
1787                                 float allowed_age = (6-line_number) * 60.0;
1788
1789                                 if((*i).age > allowed_age)
1790                                 {
1791                                         to_be_removed_count++;
1792                                         continue;
1793                                 }
1794                                 whole += (*i).text + L'\n';
1795                         }
1796                         for(u16 i=0; i<to_be_removed_count; i++)
1797                         {
1798                                 core::list<ChatLine>::Iterator
1799                                                 it = chat_lines.begin();
1800                                 chat_lines.erase(it);
1801                         }
1802                         guitext_chat->setText(whole.c_str());
1803
1804                         // Update gui element size and position
1805
1806                         /*core::rect<s32> rect(
1807                                         10,
1808                                         screensize.Y - guitext_chat_pad_bottom
1809                                                         - text_height*chat_lines.size(),
1810                                         screensize.X - 10,
1811                                         screensize.Y - guitext_chat_pad_bottom
1812                         );*/
1813                         core::rect<s32> rect(
1814                                         10,
1815                                         50,
1816                                         screensize.X - 10,
1817                                         50 + text_height*chat_lines.size()
1818                         );
1819
1820                         guitext_chat->setRelativePosition(rect);
1821
1822                         if(chat_lines.size() == 0)
1823                                 guitext_chat->setVisible(false);
1824                         else
1825                                 guitext_chat->setVisible(true);
1826                 }
1827
1828                 /*
1829                         Inventory
1830                 */
1831                 
1832                 static u16 old_selected_item = 65535;
1833                 if(client.getLocalInventoryUpdated()
1834                                 || g_selected_item != old_selected_item)
1835                 {
1836                         old_selected_item = g_selected_item;
1837                         //std::cout<<"Updating local inventory"<<std::endl;
1838                         client.getLocalInventory(local_inventory);
1839                 }
1840                 
1841                 /*
1842                         Send actions returned by the inventory menu
1843                 */
1844                 while(inventory_action_queue.size() != 0)
1845                 {
1846                         InventoryAction *a = inventory_action_queue.pop_front();
1847
1848                         client.sendInventoryAction(a);
1849                         // Eat it
1850                         delete a;
1851                 }
1852
1853                 /*
1854                         Drawing begins
1855                 */
1856
1857                 TimeTaker drawtimer("Drawing");
1858
1859                 
1860                 {
1861                         TimeTaker timer("beginScene");
1862                         driver->beginScene(true, true, bgcolor);
1863                         //driver->beginScene(false, true, bgcolor);
1864                         beginscenetime = timer.stop(true);
1865                 }
1866
1867                 //timer3.stop();
1868                 
1869                 //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
1870                 
1871                 {
1872                         TimeTaker timer("smgr");
1873                         smgr->drawAll();
1874                         scenetime = timer.stop(true);
1875                 }
1876                 
1877                 {
1878                 //TimeTaker timer9("auxiliary drawings");
1879                 // 0ms
1880                 
1881                 //timer9.stop();
1882                 //TimeTaker //timer10("//timer10");
1883                 
1884                 video::SMaterial m;
1885                 //m.Thickness = 10;
1886                 m.Thickness = 3;
1887                 m.Lighting = false;
1888                 driver->setMaterial(m);
1889
1890                 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
1891
1892                 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
1893                                 i != hilightboxes.end(); i++)
1894                 {
1895                         /*std::cout<<"hilightbox min="
1896                                         <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
1897                                         <<" max="
1898                                         <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
1899                                         <<std::endl;*/
1900                         driver->draw3DBox(*i, video::SColor(255,0,0,0));
1901                 }
1902
1903                 /*
1904                         Frametime log
1905                 */
1906                 if(g_settings.getBool("frametime_graph") == true)
1907                 {
1908                         s32 x = 10;
1909                         for(core::list<float>::Iterator
1910                                         i = frametime_log.begin();
1911                                         i != frametime_log.end();
1912                                         i++)
1913                         {
1914                                 driver->draw2DLine(v2s32(x,50),
1915                                                 v2s32(x,50+(*i)*1000),
1916                                                 video::SColor(255,255,255,255));
1917                                 x++;
1918                         }
1919                 }
1920
1921                 /*
1922                         Draw crosshair
1923                 */
1924                 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
1925                                 displaycenter + core::vector2d<s32>(10,0),
1926                                 video::SColor(255,255,255,255));
1927                 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
1928                                 displaycenter + core::vector2d<s32>(0,10),
1929                                 video::SColor(255,255,255,255));
1930
1931                 } // timer
1932
1933                 //timer10.stop();
1934                 //TimeTaker //timer11("//timer11");
1935
1936                 /*
1937                         Draw gui
1938                 */
1939                 // 0-1ms
1940                 guienv->drawAll();
1941
1942                 /*
1943                         Draw hotbar
1944                 */
1945                 {
1946                         draw_hotbar(driver, font, v2s32(displaycenter.X, screensize.Y),
1947                                         hotbar_imagesize, hotbar_itemcount, &local_inventory,
1948                                         client.getHP());
1949                 }
1950
1951                 /*
1952                         Damage flash
1953                 */
1954                 if(damage_flash_timer > 0.0)
1955                 {
1956                         damage_flash_timer -= dtime;
1957                         
1958                         video::SColor color(128,255,0,0);
1959                         driver->draw2DRectangle(color,
1960                                         core::rect<s32>(0,0,screensize.X,screensize.Y),
1961                                         NULL);
1962                 }
1963                 
1964                 /*
1965                         End scene
1966                 */
1967                 {
1968                         TimeTaker timer("endScene");
1969                         driver->endScene();
1970                         endscenetime = timer.stop(true);
1971                 }
1972
1973                 drawtime = drawtimer.stop(true);
1974
1975                 /*
1976                         End of drawing
1977                 */
1978
1979                 static s16 lastFPS = 0;
1980                 //u16 fps = driver->getFPS();
1981                 u16 fps = (1.0/dtime_avg1);
1982
1983                 if (lastFPS != fps)
1984                 {
1985                         core::stringw str = L"Minetest [";
1986                         str += driver->getName();
1987                         str += "] FPS:";
1988                         str += fps;
1989
1990                         device->setWindowCaption(str.c_str());
1991                         lastFPS = fps;
1992                 }
1993         }
1994 }
1995
1996