3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
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.
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.
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.
21 #include "common_irrlicht.h"
22 #include <IGUICheckBox.h>
23 #include <IGUIEditBox.h>
24 #include <IGUIButton.h>
25 #include <IGUIStaticText.h>
29 #include "guiPauseMenu.h"
30 #include "guiPasswordChange.h"
31 #include "guiInventoryMenu.h"
32 #include "guiTextInputMenu.h"
33 #include "guiDeathScreen.h"
34 #include "materials.h"
42 #include "mainmenumanager.h"
43 #include "craftitemdef.h"
47 // Needed for determining pointing to nodes
49 #include "nodemetadata.h"
50 #include "main.h" // For g_settings
52 #include "tile.h" // For TextureSource
53 #include "logoutputbuffer.h"
56 Setting this to 1 enables a special camera mode that forces
57 the renderers to think that the camera statically points from
58 the starting place to a static direction.
60 This allows one to move around with the player and see what
61 is actually drawn behind solid things and behind the player.
63 #define FIELD_OF_VIEW_TEST 0
73 ChatLine(const std::wstring &a_text):
86 // Inventory actions from the menu are buffered here before sending
87 Queue<InventoryAction*> inventory_action_queue;
88 // This is a copy of the inventory that the client's environment has
89 Inventory local_inventory;
95 struct TextDestChat : public TextDest
97 TextDestChat(Client *client)
101 void gotText(std::wstring text)
103 // Discard empty line
108 m_client->sendChatMessage(text);
110 m_client->addChatMessage(text);
116 struct TextDestNodeMetadata : public TextDest
118 TextDestNodeMetadata(v3s16 p, Client *client)
123 void gotText(std::wstring text)
125 std::string ntext = wide_to_narrow(text);
126 infostream<<"Changing text of a sign node: "
128 m_client->sendSignNodeText(m_p, ntext);
135 /* Respawn menu callback */
137 class MainRespawnInitiator: public IRespawnInitiator
140 MainRespawnInitiator(bool *active, Client *client):
141 m_active(active), m_client(client)
148 m_client->sendRespawn();
158 void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font,
159 ITextureSource *tsrc,
160 v2s32 centerlowerpos, s32 imgsize, s32 itemcount,
161 Inventory *inventory, s32 halfheartcount, u16 playeritem)
163 InventoryList *mainlist = inventory->getList("main");
166 errorstream<<"draw_hotbar(): mainlist == NULL"<<std::endl;
170 s32 padding = imgsize/12;
171 //s32 height = imgsize + padding*2;
172 s32 width = itemcount*(imgsize+padding*2);
174 // Position of upper left corner of bar
175 v2s32 pos = centerlowerpos - v2s32(width/2, imgsize+padding*2);
177 // Draw background color
178 /*core::rect<s32> barrect(0,0,width,height);
180 video::SColor bgcolor(255,128,128,128);
181 driver->draw2DRectangle(bgcolor, barrect, NULL);*/
183 core::rect<s32> imgrect(0,0,imgsize,imgsize);
185 for(s32 i=0; i<itemcount; i++)
187 InventoryItem *item = mainlist->getItem(i);
189 core::rect<s32> rect = imgrect + pos
190 + v2s32(padding+i*(imgsize+padding*2), padding);
194 video::SColor c_outside(255,255,0,0);
195 //video::SColor c_outside(255,0,0,0);
196 //video::SColor c_inside(255,192,192,192);
197 s32 x1 = rect.UpperLeftCorner.X;
198 s32 y1 = rect.UpperLeftCorner.Y;
199 s32 x2 = rect.LowerRightCorner.X;
200 s32 y2 = rect.LowerRightCorner.Y;
201 // Black base borders
202 driver->draw2DRectangle(c_outside,
204 v2s32(x1 - padding, y1 - padding),
205 v2s32(x2 + padding, y1)
207 driver->draw2DRectangle(c_outside,
209 v2s32(x1 - padding, y2),
210 v2s32(x2 + padding, y2 + padding)
212 driver->draw2DRectangle(c_outside,
214 v2s32(x1 - padding, y1),
217 driver->draw2DRectangle(c_outside,
220 v2s32(x2 + padding, y2)
222 /*// Light inside borders
223 driver->draw2DRectangle(c_inside,
225 v2s32(x1 - padding/2, y1 - padding/2),
226 v2s32(x2 + padding/2, y1)
228 driver->draw2DRectangle(c_inside,
230 v2s32(x1 - padding/2, y2),
231 v2s32(x2 + padding/2, y2 + padding/2)
233 driver->draw2DRectangle(c_inside,
235 v2s32(x1 - padding/2, y1),
238 driver->draw2DRectangle(c_inside,
241 v2s32(x2 + padding/2, y2)
246 video::SColor bgcolor2(128,0,0,0);
247 driver->draw2DRectangle(bgcolor2, rect, NULL);
251 drawInventoryItem(driver, font, item, rect, NULL, tsrc);
258 video::ITexture *heart_texture = tsrc->getTextureRaw("heart.png");
261 v2s32 p = pos + v2s32(0, -20);
262 for(s32 i=0; i<halfheartcount/2; i++)
264 const video::SColor color(255,255,255,255);
265 const video::SColor colors[] = {color,color,color,color};
266 core::rect<s32> rect(0,0,16,16);
268 driver->draw2DImage(heart_texture, rect,
269 core::rect<s32>(core::position2d<s32>(0,0),
270 core::dimension2di(heart_texture->getOriginalSize())),
274 if(halfheartcount % 2 == 1)
276 const video::SColor color(255,255,255,255);
277 const video::SColor colors[] = {color,color,color,color};
278 core::rect<s32> rect(0,0,16/2,16);
280 core::dimension2di srcd(heart_texture->getOriginalSize());
282 driver->draw2DImage(heart_texture, rect,
283 core::rect<s32>(core::position2d<s32>(0,0), srcd),
291 Check if a node is pointable
293 inline bool isPointableNode(const MapNode& n,
294 Client *client, bool liquids_pointable)
296 const ContentFeatures &features = client->getNodeDefManager()->get(n);
297 return features.pointable ||
298 (liquids_pointable && features.isLiquid());
302 Find what the player is pointing at
304 PointedThing getPointedThing(Client *client, v3f player_position,
305 v3f camera_direction, v3f camera_position,
306 core::line3d<f32> shootline, f32 d,
307 bool liquids_pointable,
308 bool look_for_object,
309 core::aabbox3d<f32> &hilightbox,
310 bool &should_show_hilightbox,
311 ClientActiveObject *&selected_object)
315 hilightbox = core::aabbox3d<f32>(0,0,0,0,0,0);
316 should_show_hilightbox = false;
317 selected_object = NULL;
319 // First try to find a pointed at active object
322 selected_object = client->getSelectedActiveObject(d*BS,
323 camera_position, shootline);
325 if(selected_object != NULL)
327 core::aabbox3d<f32> *selection_box
328 = selected_object->getSelectionBox();
329 // Box should exist because object was returned in the
331 assert(selection_box);
333 v3f pos = selected_object->getPosition();
335 hilightbox = core::aabbox3d<f32>(
336 selection_box->MinEdge + pos,
337 selection_box->MaxEdge + pos
340 should_show_hilightbox = selected_object->doShowSelectionBox();
342 result.type = POINTEDTHING_OBJECT;
343 result.object_id = selected_object->getId();
347 // That didn't work, try to find a pointed at node
349 f32 mindistance = BS * 1001;
351 v3s16 pos_i = floatToInt(player_position, BS);
353 /*infostream<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
357 s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
358 s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
359 s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
360 s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
361 s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
362 s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
364 for(s16 y = ystart; y <= yend; y++)
365 for(s16 z = zstart; z <= zend; z++)
366 for(s16 x = xstart; x <= xend; x++)
371 n = client->getNode(v3s16(x,y,z));
373 catch(InvalidPositionException &e)
377 if(!isPointableNode(n, client, liquids_pointable))
381 v3f npf = intToFloat(np, BS);
386 v3s16(0,0,1), // back
388 v3s16(1,0,0), // right
389 v3s16(0,0,-1), // front
390 v3s16(0,-1,0), // bottom
391 v3s16(-1,0,0), // left
394 const ContentFeatures &f = client->getNodeDefManager()->get(n);
396 if(f.selection_box.type == NODEBOX_FIXED)
398 core::aabbox3d<f32> box = f.selection_box.fixed;
402 v3s16 facedirs[6] = {
411 core::aabbox3d<f32> faceboxes[6] = {
414 box.MinEdge.X, box.MinEdge.Y, box.MinEdge.Z,
415 box.MinEdge.X+d, box.MaxEdge.Y, box.MaxEdge.Z
419 box.MaxEdge.X-d, box.MinEdge.Y, box.MinEdge.Z,
420 box.MaxEdge.X, box.MaxEdge.Y, box.MaxEdge.Z
424 box.MinEdge.X, box.MinEdge.Y, box.MinEdge.Z,
425 box.MaxEdge.X, box.MinEdge.Y+d, box.MaxEdge.Z
429 box.MinEdge.X, box.MaxEdge.Y-d, box.MinEdge.Z,
430 box.MaxEdge.X, box.MaxEdge.Y, box.MaxEdge.Z
434 box.MinEdge.X, box.MinEdge.Y, box.MinEdge.Z,
435 box.MaxEdge.X, box.MaxEdge.Y, box.MinEdge.Z+d
439 box.MinEdge.X, box.MinEdge.Y, box.MaxEdge.Z-d,
440 box.MaxEdge.X, box.MaxEdge.Y, box.MaxEdge.Z
444 for(u16 i=0; i<6; i++)
446 v3f facedir_f(facedirs[i].X, facedirs[i].Y, facedirs[i].Z);
447 v3f centerpoint = npf + facedir_f * BS/2;
448 f32 distance = (centerpoint - camera_position).getLength();
449 if(distance >= mindistance)
451 if(!faceboxes[i].intersectsWithLine(shootline))
453 result.type = POINTEDTHING_NODE;
454 result.node_undersurface = np;
455 result.node_abovesurface = np+facedirs[i];
456 mindistance = distance;
458 should_show_hilightbox = true;
461 else if(f.selection_box.type == NODEBOX_WALLMOUNTED)
463 v3s16 dir = unpackDir(n.param2);
464 v3f dir_f = v3f(dir.X, dir.Y, dir.Z);
465 dir_f *= BS/2 - BS/6 - BS/20;
466 v3f cpf = npf + dir_f;
467 f32 distance = (cpf - camera_position).getLength();
469 core::aabbox3d<f32> box;
472 if(dir == v3s16(0,1,0)){
473 box = f.selection_box.wall_top;
476 else if(dir == v3s16(0,-1,0)){
477 box = f.selection_box.wall_bottom;
483 f.selection_box.wall_side.MinEdge,
484 f.selection_box.wall_side.MaxEdge
487 for(s32 i=0; i<2; i++)
489 if(dir == v3s16(-1,0,0))
490 vertices[i].rotateXZBy(0);
491 if(dir == v3s16(1,0,0))
492 vertices[i].rotateXZBy(180);
493 if(dir == v3s16(0,0,-1))
494 vertices[i].rotateXZBy(90);
495 if(dir == v3s16(0,0,1))
496 vertices[i].rotateXZBy(-90);
499 box = core::aabbox3d<f32>(vertices[0]);
500 box.addInternalPoint(vertices[1]);
506 if(distance < mindistance)
508 if(box.intersectsWithLine(shootline))
510 result.type = POINTEDTHING_NODE;
511 result.node_undersurface = np;
512 result.node_abovesurface = np;
513 mindistance = distance;
515 should_show_hilightbox = true;
519 else // NODEBOX_REGULAR
521 for(u16 i=0; i<6; i++)
523 v3f dir_f = v3f(dirs[i].X,
524 dirs[i].Y, dirs[i].Z);
525 v3f centerpoint = npf + dir_f * BS/2;
527 (centerpoint - camera_position).getLength();
529 if(distance < mindistance)
531 core::CMatrix4<f32> m;
532 m.buildRotateFromTo(v3f(0,0,1), dir_f);
534 // This is the back face
536 v3f(BS/2, BS/2, BS/2),
537 v3f(-BS/2, -BS/2, BS/2+d)
540 for(u16 j=0; j<2; j++)
542 m.rotateVect(corners[j]);
546 core::aabbox3d<f32> facebox(corners[0]);
547 facebox.addInternalPoint(corners[1]);
549 if(facebox.intersectsWithLine(shootline))
551 result.type = POINTEDTHING_NODE;
552 result.node_undersurface = np;
553 result.node_abovesurface = np + dirs[i];
554 mindistance = distance;
556 //hilightbox = facebox;
558 const float d = 0.502;
559 core::aabbox3d<f32> nodebox
560 (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
561 v3f nodepos_f = intToFloat(np, BS);
562 nodebox.MinEdge += nodepos_f;
563 nodebox.MaxEdge += nodepos_f;
564 hilightbox = nodebox;
565 should_show_hilightbox = true;
567 } // if distance < mindistance
575 void update_skybox(video::IVideoDriver* driver, ITextureSource *tsrc,
576 scene::ISceneManager* smgr, scene::ISceneNode* &skybox,
584 /*// Disable skybox if FarMesh is enabled
585 if(g_settings->getBool("enable_farmesh"))
588 if(brightness >= 0.5)
590 skybox = smgr->addSkyBoxSceneNode(
591 tsrc->getTextureRaw("skybox2.png"),
592 tsrc->getTextureRaw("skybox3.png"),
593 tsrc->getTextureRaw("skybox1.png"),
594 tsrc->getTextureRaw("skybox1.png"),
595 tsrc->getTextureRaw("skybox1.png"),
596 tsrc->getTextureRaw("skybox1.png"));
598 else if(brightness >= 0.2)
600 skybox = smgr->addSkyBoxSceneNode(
601 tsrc->getTextureRaw("skybox2_dawn.png"),
602 tsrc->getTextureRaw("skybox3_dawn.png"),
603 tsrc->getTextureRaw("skybox1_dawn.png"),
604 tsrc->getTextureRaw("skybox1_dawn.png"),
605 tsrc->getTextureRaw("skybox1_dawn.png"),
606 tsrc->getTextureRaw("skybox1_dawn.png"));
610 skybox = smgr->addSkyBoxSceneNode(
611 tsrc->getTextureRaw("skybox2_night.png"),
612 tsrc->getTextureRaw("skybox3_night.png"),
613 tsrc->getTextureRaw("skybox1_night.png"),
614 tsrc->getTextureRaw("skybox1_night.png"),
615 tsrc->getTextureRaw("skybox1_night.png"),
616 tsrc->getTextureRaw("skybox1_night.png"));
621 Draws a screen with a single text on it.
622 Text will be removed when the screen is drawn the next time.
624 /*gui::IGUIStaticText **/
625 void draw_load_screen(const std::wstring &text,
626 video::IVideoDriver* driver, gui::IGUIFont* font)
628 v2u32 screensize = driver->getScreenSize();
629 const wchar_t *loadingtext = text.c_str();
630 core::vector2d<u32> textsize_u = font->getDimension(loadingtext);
631 core::vector2d<s32> textsize(textsize_u.X,textsize_u.Y);
632 core::vector2d<s32> center(screensize.X/2, screensize.Y/2);
633 core::rect<s32> textrect(center - textsize/2, center + textsize/2);
635 gui::IGUIStaticText *guitext = guienv->addStaticText(
636 loadingtext, textrect, false, false);
637 guitext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
639 driver->beginScene(true, true, video::SColor(255,0,0,0));
652 IrrlichtDevice *device,
655 std::string playername,
656 std::string password,
659 std::wstring &error_message,
660 std::string configpath
663 video::IVideoDriver* driver = device->getVideoDriver();
664 scene::ISceneManager* smgr = device->getSceneManager();
666 // Calculate text height using the font
667 u32 text_height = font->getDimension(L"Random test string").Height;
669 v2u32 screensize(0,0);
670 v2u32 last_screensize(0,0);
671 screensize = driver->getScreenSize();
673 const s32 hotbar_itemcount = 8;
674 //const s32 hotbar_imagesize = 36;
675 //const s32 hotbar_imagesize = 64;
676 s32 hotbar_imagesize = 48;
678 // The color of the sky
680 //video::SColor skycolor = video::SColor(255,140,186,250);
682 video::SColor bgcolor_bright = video::SColor(255,170,200,230);
685 Draw "Loading" screen
688 draw_load_screen(L"Loading...", driver, font);
690 // Create texture source
691 IWritableTextureSource *tsrc = createTextureSource(device);
693 // These will be filled by data received from the server
694 // Create tool definition manager
695 IWritableToolDefManager *tooldef = createToolDefManager();
696 // Create node definition manager
697 IWritableNodeDefManager *nodedef = createNodeDefManager();
698 // Create CraftItem definition manager
699 IWritableCraftItemDefManager *craftitemdef = createCraftItemDefManager();
701 // Add chat log output for errors to be shown in chat
702 LogOutputBuffer chat_log_error_buf(LMT_ERROR);
706 SharedPtr will delete it when it goes out of scope.
708 SharedPtr<Server> server;
710 draw_load_screen(L"Creating server...", driver, font);
711 infostream<<"Creating server"<<std::endl;
712 server = new Server(map_dir, configpath);
722 draw_load_screen(L"Creating client...", driver, font);
723 infostream<<"Creating client"<<std::endl;
725 MapDrawControl draw_control;
727 Client client(device, playername.c_str(), password, draw_control,
728 tsrc, tooldef, nodedef, craftitemdef);
730 // Client acts as our GameDef
731 IGameDef *gamedef = &client;
733 draw_load_screen(L"Resolving address...", driver, font);
734 Address connect_address(0,0,0,0, port);
737 //connect_address.Resolve("localhost");
738 connect_address.setAddress(127,0,0,1);
740 connect_address.Resolve(address.c_str());
742 catch(ResolveError &e)
744 errorstream<<"Couldn't resolve address"<<std::endl;
746 error_message = L"Couldn't resolve address";
747 //gui_loadingtext->remove();
752 Attempt to connect to the server
755 infostream<<"Connecting to server at ";
756 connect_address.print(&infostream);
757 infostream<<std::endl;
758 client.connect(connect_address);
761 Wait for server to accept connection
763 bool could_connect = false;
765 float frametime = 0.033;
766 const float timeout = 10.0;
767 float time_counter = 0.0;
770 // Update client and server
771 client.step(frametime);
773 server->step(frametime);
776 if(client.connectedAndInitialized()){
777 could_connect = true;
781 if(client.accessDenied())
783 if(time_counter >= timeout)
787 std::wostringstream ss;
788 ss<<L"Connecting to server... (timeout in ";
789 ss<<(int)(timeout - time_counter + 1.0);
791 draw_load_screen(ss.str(), driver, font);
794 sleep_ms(1000*frametime);
795 time_counter += frametime;
798 catch(con::PeerNotFoundException &e)
802 Handle failure to connect
804 if(could_connect == false)
806 if(client.accessDenied())
808 error_message = L"Access denied. Reason: "
809 +client.accessDeniedReason();
810 errorstream<<wide_to_narrow(error_message)<<std::endl;
814 error_message = L"Connection timed out.";
815 errorstream<<"Timed out."<<std::endl;
817 //gui_loadingtext->remove();
822 Wait until content has been received
824 bool got_content = false;
826 float frametime = 0.033;
827 const float timeout = 30.0;
828 float time_counter = 0.0;
831 // Update client and server
832 client.step(frametime);
834 server->step(frametime);
837 if(client.texturesReceived() &&
838 client.tooldefReceived() &&
839 client.nodedefReceived() &&
840 client.craftitemdefReceived()){
845 if(!client.connectedAndInitialized())
847 if(time_counter >= timeout)
851 std::wostringstream ss;
852 ss<<L"Waiting content... (continuing anyway in ";
853 ss<<(int)(timeout - time_counter + 1.0);
856 ss<<(client.tooldefReceived()?L"[X]":L"[ ]");
857 ss<<L" Tool definitions\n";
858 ss<<(client.nodedefReceived()?L"[X]":L"[ ]");
859 ss<<L" Node definitions\n";
860 ss<<(client.craftitemdefReceived()?L"[X]":L"[ ]");
861 ss<<L" Item definitions\n";
862 //ss<<(client.texturesReceived()?L"[X]":L"[ ]");
863 ss<<L"["<<(int)(client.textureReceiveProgress()*100+0.5)<<L"%] ";
866 draw_load_screen(ss.str(), driver, font);
869 sleep_ms(1000*frametime);
870 time_counter += frametime;
877 float old_brightness = 1.0;
878 scene::ISceneNode* skybox = NULL;
879 update_skybox(driver, tsrc, smgr, skybox, 1.0);
882 Create the camera node
884 Camera camera(smgr, draw_control);
885 if (!camera.successfullyCreated(error_message))
888 f32 camera_yaw = 0; // "right/left"
889 f32 camera_pitch = 0; // "up/down"
895 float cloud_height = BS*100;
896 Clouds *clouds = NULL;
897 if(g_settings->getBool("enable_clouds"))
899 clouds = new Clouds(smgr->getRootSceneNode(), smgr, -1,
900 cloud_height, time(0));
907 FarMesh *farmesh = NULL;
908 if(g_settings->getBool("enable_farmesh"))
910 farmesh = new FarMesh(smgr->getRootSceneNode(), smgr, -1, client.getMapSeed(), &client);
917 //gui_loadingtext->remove();
923 // First line of debug text
924 gui::IGUIStaticText *guitext = guienv->addStaticText(
926 core::rect<s32>(5, 5, 795, 5+text_height),
928 // Second line of debug text
929 gui::IGUIStaticText *guitext2 = guienv->addStaticText(
931 core::rect<s32>(5, 5+(text_height+5)*1, 795, (5+text_height)*2),
933 // At the middle of the screen
934 // Object infos are shown in this
935 gui::IGUIStaticText *guitext_info = guienv->addStaticText(
937 core::rect<s32>(0,0,400,text_height+5) + v2s32(100,200),
941 gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
943 core::rect<s32>(0,0,0,0),
944 //false, false); // Disable word wrap as of now
946 //guitext_chat->setBackgroundColor(video::SColor(96,0,0,0));
947 core::list<ChatLine> chat_lines;
949 // Profiler text (size is updated when text is updated)
950 gui::IGUIStaticText *guitext_profiler = guienv->addStaticText(
952 core::rect<s32>(6, 4+(text_height+5)*2, 400,
953 (text_height+5)*2 + text_height*35),
955 guitext_profiler->setBackgroundColor(video::SColor(80,0,0,0));
956 guitext_profiler->setVisible(false);
958 /*GUIQuickInventory *quick_inventory = new GUIQuickInventory
959 (guienv, NULL, v2s32(10, 70), 5, &local_inventory);*/
960 /*GUIQuickInventory *quick_inventory = new GUIQuickInventory
961 (guienv, NULL, v2s32(0, 0), quickinv_itemcount, &local_inventory);*/
963 // Test the text input system
964 /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr,
966 /*GUIMessageMenu *menu =
967 new GUIMessageMenu(guienv, guiroot, -1,
973 (new GUIPauseMenu(guienv, guiroot, -1, g_gamecallback,
974 &g_menumgr))->drop();
977 /*guitext2->setVisible(true);
978 guitext_info->setVisible(true);
979 guitext_chat->setVisible(true);*/
981 //s32 guitext_chat_pad_bottom = 70;
984 Some statistics are collected in these
987 u32 beginscenetime = 0;
989 u32 endscenetime = 0;
992 //throw con::PeerNotFoundException("lol");
994 float brightness = 1.0;
996 core::list<float> frametime_log;
998 float nodig_delay_timer = 0.0;
999 float dig_time = 0.0;
1001 PointedThing pointed_old;
1002 bool digging = false;
1003 bool ldown_for_dig = false;
1005 float damage_flash_timer = 0;
1006 s16 farmesh_range = 20*MAP_BLOCKSIZE;
1008 const float object_hit_delay = 0.2;
1009 float object_hit_delay_timer = 0.0;
1011 bool invert_mouse = g_settings->getBool("invert_mouse");
1013 bool respawn_menu_active = false;
1015 bool show_profiler = false;
1016 bool force_fog_off = false;
1017 bool disable_camera_update = false;
1023 bool first_loop_after_window_activation = true;
1025 // TODO: Convert the static interval timers to these
1026 // Interval limiter for profiler
1027 IntervalLimiter m_profiler_interval;
1029 // Time is in milliseconds
1030 // NOTE: getRealTime() causes strange problems in wine (imprecision?)
1031 // NOTE: So we have to use getTime() and call run()s between them
1032 u32 lasttime = device->getTimer()->getTime();
1034 while(device->run() && kill == false)
1036 //std::cerr<<"frame"<<std::endl;
1038 if(client.accessDenied())
1040 error_message = L"Access denied. Reason: "
1041 +client.accessDeniedReason();
1042 errorstream<<wide_to_narrow(error_message)<<std::endl;
1046 if(g_gamecallback->disconnect_requested)
1048 g_gamecallback->disconnect_requested = false;
1052 if(g_gamecallback->changepassword_requested)
1054 (new GUIPasswordChange(guienv, guiroot, -1,
1055 &g_menumgr, &client))->drop();
1056 g_gamecallback->changepassword_requested = false;
1060 Process TextureSource's queue
1062 tsrc->processQueue();
1067 last_screensize = screensize;
1068 screensize = driver->getScreenSize();
1069 v2s32 displaycenter(screensize.X/2,screensize.Y/2);
1070 //bool screensize_changed = screensize != last_screensize;
1073 if(screensize.Y <= 800)
1074 hotbar_imagesize = 32;
1075 else if(screensize.Y <= 1280)
1076 hotbar_imagesize = 48;
1078 hotbar_imagesize = 64;
1080 // Hilight boxes collected during the loop and displayed
1081 core::list< core::aabbox3d<f32> > hilightboxes;
1084 std::wstring infotext;
1086 // When screen size changes, update positions and sizes of stuff
1087 /*if(screensize_changed)
1089 v2s32 pos(displaycenter.X-((quickinv_itemcount-1)*quickinv_spacing+quickinv_size)/2, screensize.Y-quickinv_spacing);
1090 quick_inventory->updatePosition(pos);
1093 //TimeTaker //timer1("//timer1");
1095 // Time of frame without fps limit
1099 // not using getRealTime is necessary for wine
1100 u32 time = device->getTimer()->getTime();
1102 busytime_u32 = time - lasttime;
1105 busytime = busytime_u32 / 1000.0;
1108 //infostream<<"busytime_u32="<<busytime_u32<<std::endl;
1110 // Necessary for device->getTimer()->getTime()
1118 float fps_max = g_settings->getFloat("fps_max");
1119 u32 frametime_min = 1000./fps_max;
1121 if(busytime_u32 < frametime_min)
1123 u32 sleeptime = frametime_min - busytime_u32;
1124 device->sleep(sleeptime);
1128 // Necessary for device->getTimer()->getTime()
1132 Time difference calculation
1134 f32 dtime; // in seconds
1136 u32 time = device->getTimer()->getTime();
1138 dtime = (time - lasttime) / 1000.0;
1145 if(nodig_delay_timer >= 0)
1146 nodig_delay_timer -= dtime;
1147 if(object_hit_delay_timer >= 0)
1148 object_hit_delay_timer -= dtime;
1150 g_profiler->add("Elapsed time", dtime);
1151 g_profiler->avg("FPS", 1./dtime);
1154 Log frametime for visualization
1156 frametime_log.push_back(dtime);
1157 if(frametime_log.size() > 100)
1159 core::list<float>::Iterator i = frametime_log.begin();
1160 frametime_log.erase(i);
1164 Visualize frametime in terminal
1166 /*for(u32 i=0; i<dtime*400; i++)
1168 infostream<<std::endl;*/
1171 Time average and jitter calculation
1174 static f32 dtime_avg1 = 0.0;
1175 dtime_avg1 = dtime_avg1 * 0.96 + dtime * 0.04;
1176 f32 dtime_jitter1 = dtime - dtime_avg1;
1178 static f32 dtime_jitter1_max_sample = 0.0;
1179 static f32 dtime_jitter1_max_fraction = 0.0;
1181 static f32 jitter1_max = 0.0;
1182 static f32 counter = 0.0;
1183 if(dtime_jitter1 > jitter1_max)
1184 jitter1_max = dtime_jitter1;
1189 dtime_jitter1_max_sample = jitter1_max;
1190 dtime_jitter1_max_fraction
1191 = dtime_jitter1_max_sample / (dtime_avg1+0.001);
1197 Busytime average and jitter calculation
1200 static f32 busytime_avg1 = 0.0;
1201 busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
1202 f32 busytime_jitter1 = busytime - busytime_avg1;
1204 static f32 busytime_jitter1_max_sample = 0.0;
1205 static f32 busytime_jitter1_min_sample = 0.0;
1207 static f32 jitter1_max = 0.0;
1208 static f32 jitter1_min = 0.0;
1209 static f32 counter = 0.0;
1210 if(busytime_jitter1 > jitter1_max)
1211 jitter1_max = busytime_jitter1;
1212 if(busytime_jitter1 < jitter1_min)
1213 jitter1_min = busytime_jitter1;
1217 busytime_jitter1_max_sample = jitter1_max;
1218 busytime_jitter1_min_sample = jitter1_min;
1225 Debug info for client
1228 static float counter = 0.0;
1233 client.printDebugInfo(infostream);
1240 float profiler_print_interval =
1241 g_settings->getFloat("profiler_print_interval");
1242 bool print_to_log = true;
1243 if(profiler_print_interval == 0){
1244 print_to_log = false;
1245 profiler_print_interval = 5;
1247 if(m_profiler_interval.step(dtime, profiler_print_interval))
1250 infostream<<"Profiler:"<<std::endl;
1251 g_profiler->print(infostream);
1254 std::ostringstream os(std::ios_base::binary);
1255 g_profiler->print(os);
1256 std::wstring text = narrow_to_wide(os.str());
1257 guitext_profiler->setText(text.c_str());
1259 g_profiler->clear();
1261 s32 w = font->getDimension(text.c_str()).Width;
1264 core::rect<s32> rect(6, 4+(text_height+5)*2, 12+w,
1265 8+(text_height+5)*2 +
1266 font->getDimension(text.c_str()).Height);
1267 guitext_profiler->setRelativePosition(rect);
1271 Direct handling of user input
1274 // Reset input if window not active or some menu is active
1275 if(device->isWindowActive() == false || noMenuActive() == false)
1280 // Input handler step() (used by the random input generator)
1284 Launch menus and trigger stuff according to keys
1286 if(input->wasKeyDown(getKeySetting("keymap_drop")))
1288 // drop selected item
1289 IDropAction *a = new IDropAction();
1291 a->from_inv = "current_player";
1292 a->from_list = "main";
1293 a->from_i = client.getPlayerItem();
1294 client.inventoryAction(a);
1296 else if(input->wasKeyDown(getKeySetting("keymap_inventory")))
1298 infostream<<"the_game: "
1299 <<"Launching inventory"<<std::endl;
1301 GUIInventoryMenu *menu =
1302 new GUIInventoryMenu(guienv, guiroot, -1,
1303 &g_menumgr, v2s16(8,7),
1304 client.getInventoryContext(),
1307 core::array<GUIInventoryMenu::DrawSpec> draw_spec;
1308 draw_spec.push_back(GUIInventoryMenu::DrawSpec(
1309 "list", "current_player", "main",
1310 v2s32(0, 3), v2s32(8, 4)));
1311 draw_spec.push_back(GUIInventoryMenu::DrawSpec(
1312 "list", "current_player", "craft",
1313 v2s32(3, 0), v2s32(3, 3)));
1314 draw_spec.push_back(GUIInventoryMenu::DrawSpec(
1315 "list", "current_player", "craftresult",
1316 v2s32(7, 1), v2s32(1, 1)));
1318 menu->setDrawSpec(draw_spec);
1322 else if(input->wasKeyDown(EscapeKey))
1324 infostream<<"the_game: "
1325 <<"Launching pause menu"<<std::endl;
1326 // It will delete itself by itself
1327 (new GUIPauseMenu(guienv, guiroot, -1, g_gamecallback,
1328 &g_menumgr))->drop();
1330 // Move mouse cursor on top of the disconnect button
1331 input->setMousePos(displaycenter.X, displaycenter.Y+25);
1333 else if(input->wasKeyDown(getKeySetting("keymap_chat")))
1335 TextDest *dest = new TextDestChat(&client);
1337 (new GUITextInputMenu(guienv, guiroot, -1,
1341 else if(input->wasKeyDown(getKeySetting("keymap_cmd")))
1343 TextDest *dest = new TextDestChat(&client);
1345 (new GUITextInputMenu(guienv, guiroot, -1,
1349 else if(input->wasKeyDown(getKeySetting("keymap_freemove")))
1351 if(g_settings->getBool("free_move"))
1353 g_settings->set("free_move","false");
1354 chat_lines.push_back(ChatLine(L"free_move disabled"));
1358 g_settings->set("free_move","true");
1359 chat_lines.push_back(ChatLine(L"free_move enabled"));
1362 else if(input->wasKeyDown(getKeySetting("keymap_fastmove")))
1364 if(g_settings->getBool("fast_move"))
1366 g_settings->set("fast_move","false");
1367 chat_lines.push_back(ChatLine(L"fast_move disabled"));
1371 g_settings->set("fast_move","true");
1372 chat_lines.push_back(ChatLine(L"fast_move enabled"));
1375 else if(input->wasKeyDown(getKeySetting("keymap_frametime_graph")))
1377 if(g_settings->getBool("frametime_graph"))
1379 g_settings->set("frametime_graph","false");
1380 chat_lines.push_back(ChatLine(L"frametime_graph disabled"));
1384 g_settings->set("frametime_graph","true");
1385 chat_lines.push_back(ChatLine(L"frametime_graph enabled"));
1388 else if(input->wasKeyDown(getKeySetting("keymap_screenshot")))
1390 irr::video::IImage* const image = driver->createScreenShot();
1392 irr::c8 filename[256];
1393 snprintf(filename, 256, "%s" DIR_DELIM "screenshot_%u.png",
1394 g_settings->get("screenshot_path").c_str(),
1395 device->getTimer()->getRealTime());
1396 if (driver->writeImageToFile(image, filename)) {
1397 std::wstringstream sstr;
1398 sstr<<"Saved screenshot to '"<<filename<<"'";
1399 infostream<<"Saved screenshot to '"<<filename<<"'"<<std::endl;
1400 chat_lines.push_back(ChatLine(sstr.str()));
1402 infostream<<"Failed to save screenshot '"<<filename<<"'"<<std::endl;
1407 else if(input->wasKeyDown(getKeySetting("keymap_toggle_profiler")))
1409 show_profiler = !show_profiler;
1410 guitext_profiler->setVisible(show_profiler);
1412 chat_lines.push_back(ChatLine(L"Profiler disabled"));
1414 chat_lines.push_back(ChatLine(L"Profiler enabled"));
1416 else if(input->wasKeyDown(getKeySetting("keymap_toggle_force_fog_off")))
1418 force_fog_off = !force_fog_off;
1420 chat_lines.push_back(ChatLine(L"Fog disabled"));
1422 chat_lines.push_back(ChatLine(L"Fog enabled"));
1424 else if(input->wasKeyDown(getKeySetting("keymap_toggle_update_camera")))
1426 disable_camera_update = !disable_camera_update;
1427 if(disable_camera_update)
1428 chat_lines.push_back(ChatLine(L"Camera update disabled"));
1430 chat_lines.push_back(ChatLine(L"Camera update enabled"));
1433 // Item selection with mouse wheel
1434 u16 new_playeritem = client.getPlayerItem();
1436 s32 wheel = input->getMouseWheel();
1437 u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE-1,
1438 hotbar_itemcount-1);
1442 if(new_playeritem < max_item)
1449 if(new_playeritem > 0)
1452 new_playeritem = max_item;
1457 for(u16 i=0; i<10; i++)
1459 const KeyPress *kp = NumberKey + (i + 1) % 10;
1460 if(input->wasKeyDown(*kp))
1462 if(i < PLAYER_INVENTORY_SIZE && i < hotbar_itemcount)
1466 infostream<<"Selected item: "
1467 <<new_playeritem<<std::endl;
1472 // Viewing range selection
1473 if(input->wasKeyDown(getKeySetting("keymap_rangeselect")))
1475 if(draw_control.range_all)
1477 draw_control.range_all = false;
1478 infostream<<"Disabled full viewing range"<<std::endl;
1482 draw_control.range_all = true;
1483 infostream<<"Enabled full viewing range"<<std::endl;
1487 // Print debug stacks
1488 if(input->wasKeyDown(getKeySetting("keymap_print_debug_stacks")))
1490 dstream<<"-----------------------------------------"
1492 dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
1493 dstream<<"-----------------------------------------"
1495 debug_stacks_print();
1499 Mouse and camera control
1500 NOTE: Do this before client.setPlayerControl() to not cause a camera lag of one frame
1503 if((device->isWindowActive() && noMenuActive()) || random_input)
1507 // Mac OSX gets upset if this is set every frame
1508 if(device->getCursorControl()->isVisible())
1509 device->getCursorControl()->setVisible(false);
1512 if(first_loop_after_window_activation){
1513 //infostream<<"window active, first loop"<<std::endl;
1514 first_loop_after_window_activation = false;
1517 s32 dx = input->getMousePos().X - displaycenter.X;
1518 s32 dy = input->getMousePos().Y - displaycenter.Y;
1521 //infostream<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
1523 /*const float keyspeed = 500;
1524 if(input->isKeyDown(irr::KEY_UP))
1525 dy -= dtime * keyspeed;
1526 if(input->isKeyDown(irr::KEY_DOWN))
1527 dy += dtime * keyspeed;
1528 if(input->isKeyDown(irr::KEY_LEFT))
1529 dx -= dtime * keyspeed;
1530 if(input->isKeyDown(irr::KEY_RIGHT))
1531 dx += dtime * keyspeed;*/
1533 camera_yaw -= dx*0.2;
1534 camera_pitch += dy*0.2;
1535 if(camera_pitch < -89.5) camera_pitch = -89.5;
1536 if(camera_pitch > 89.5) camera_pitch = 89.5;
1538 input->setMousePos(displaycenter.X, displaycenter.Y);
1541 // Mac OSX gets upset if this is set every frame
1542 if(device->getCursorControl()->isVisible() == false)
1543 device->getCursorControl()->setVisible(true);
1545 //infostream<<"window inactive"<<std::endl;
1546 first_loop_after_window_activation = true;
1550 Player speed control
1553 if(!noMenuActive() || !device->isWindowActive())
1555 PlayerControl control(
1566 client.setPlayerControl(control);
1579 PlayerControl control(
1580 input->isKeyDown(getKeySetting("keymap_forward")),
1581 input->isKeyDown(getKeySetting("keymap_backward")),
1582 input->isKeyDown(getKeySetting("keymap_left")),
1583 input->isKeyDown(getKeySetting("keymap_right")),
1584 input->isKeyDown(getKeySetting("keymap_jump")),
1585 input->isKeyDown(getKeySetting("keymap_special1")),
1586 input->isKeyDown(getKeySetting("keymap_sneak")),
1590 client.setPlayerControl(control);
1599 //TimeTaker timer("server->step(dtime)");
1600 server->step(dtime);
1608 //TimeTaker timer("client.step(dtime)");
1610 //client.step(dtime_avg1);
1614 // Read client events
1617 ClientEvent event = client.getClientEvent();
1618 if(event.type == CE_NONE)
1622 else if(event.type == CE_PLAYER_DAMAGE)
1624 //u16 damage = event.player_damage.amount;
1625 //infostream<<"Player damage: "<<damage<<std::endl;
1626 damage_flash_timer = 0.05;
1627 if(event.player_damage.amount >= 2){
1628 damage_flash_timer += 0.05 * event.player_damage.amount;
1631 else if(event.type == CE_PLAYER_FORCE_MOVE)
1633 camera_yaw = event.player_force_move.yaw;
1634 camera_pitch = event.player_force_move.pitch;
1636 else if(event.type == CE_DEATHSCREEN)
1638 if(respawn_menu_active)
1641 /*bool set_camera_point_target =
1642 event.deathscreen.set_camera_point_target;
1643 v3f camera_point_target;
1644 camera_point_target.X = event.deathscreen.camera_point_target_x;
1645 camera_point_target.Y = event.deathscreen.camera_point_target_y;
1646 camera_point_target.Z = event.deathscreen.camera_point_target_z;*/
1647 MainRespawnInitiator *respawner =
1648 new MainRespawnInitiator(
1649 &respawn_menu_active, &client);
1650 GUIDeathScreen *menu =
1651 new GUIDeathScreen(guienv, guiroot, -1,
1652 &g_menumgr, respawner);
1655 /* Handle visualization */
1657 damage_flash_timer = 0;
1659 /*LocalPlayer* player = client.getLocalPlayer();
1660 player->setPosition(player->getPosition() + v3f(0,-BS,0));
1661 camera.update(player, busytime, screensize);*/
1663 else if(event.type == CE_TEXTURES_UPDATED)
1665 update_skybox(driver, tsrc, smgr, skybox, brightness);
1670 //TimeTaker //timer2("//timer2");
1672 LocalPlayer* player = client.getLocalPlayer();
1673 camera.update(player, busytime, screensize);
1676 v3f player_position = player->getPosition();
1677 v3f camera_position = camera.getPosition();
1678 v3f camera_direction = camera.getDirection();
1679 f32 camera_fov = camera.getFovMax();
1681 if(!disable_camera_update){
1682 client.updateCamera(camera_position,
1683 camera_direction, camera_fov);
1687 //TimeTaker //timer3("//timer3");
1690 For interaction purposes, get info about the held item
1691 - Is it a tool, and what is the toolname?
1692 - Is it a usable item?
1693 - Can it point to liquids?
1695 std::string playeritem_toolname = "";
1696 bool playeritem_usable = false;
1697 bool playeritem_liquids_pointable = false;
1699 InventoryList *mlist = local_inventory.getList("main");
1702 InventoryItem *item = mlist->getItem(client.getPlayerItem());
1705 if((std::string)item->getName() == "ToolItem")
1707 ToolItem *titem = (ToolItem*)item;
1708 playeritem_toolname = titem->getToolName();
1711 playeritem_usable = item->isUsable();
1713 playeritem_liquids_pointable =
1714 item->areLiquidsPointable();
1720 Calculate what block is the crosshair pointing to
1723 //u32 t1 = device->getTimer()->getRealTime();
1725 f32 d = 4; // max. distance
1726 core::line3d<f32> shootline(camera_position,
1727 camera_position + camera_direction * BS * (d+1));
1729 core::aabbox3d<f32> hilightbox;
1730 bool should_show_hilightbox = false;
1731 ClientActiveObject *selected_object = NULL;
1733 PointedThing pointed = getPointedThing(
1735 &client, player_position, camera_direction,
1736 camera_position, shootline, d,
1737 playeritem_liquids_pointable, !ldown_for_dig,
1739 hilightbox, should_show_hilightbox,
1742 if(pointed != pointed_old)
1744 infostream<<"Pointing at "<<pointed.dump()<<std::endl;
1745 //dstream<<"Pointing at "<<pointed.dump()<<std::endl;
1751 if(should_show_hilightbox)
1752 hilightboxes.push_back(hilightbox);
1756 - releasing left mouse button
1757 - pointing away from node
1761 if(input->getLeftReleased())
1763 infostream<<"Left button released"
1764 <<" (stopped digging)"<<std::endl;
1767 else if(pointed != pointed_old)
1769 if (pointed.type == POINTEDTHING_NODE
1770 && pointed_old.type == POINTEDTHING_NODE
1771 && pointed.node_undersurface == pointed_old.node_undersurface)
1773 // Still pointing to the same node,
1774 // but a different face. Don't reset.
1778 infostream<<"Pointing away from node"
1779 <<" (stopped digging)"<<std::endl;
1785 client.interact(1, pointed_old);
1786 client.clearTempMod(pointed_old.node_undersurface);
1790 if(!digging && ldown_for_dig && !input->getLeftState())
1792 ldown_for_dig = false;
1795 bool left_punch = false;
1796 bool left_punch_muted = false;
1798 if(playeritem_usable && input->getLeftState())
1800 if(input->getLeftClicked())
1801 client.interact(4, pointed);
1803 else if(pointed.type == POINTEDTHING_NODE)
1805 v3s16 nodepos = pointed.node_undersurface;
1806 v3s16 neighbourpos = pointed.node_abovesurface;
1809 Check information text of node
1812 NodeMetadata *meta = client.getNodeMetadata(nodepos);
1815 infotext = narrow_to_wide(meta->infoText());
1819 MapNode n = client.getNode(nodepos);
1820 if(nodedef->get(n).tname_tiles[0] == "unknown_block.png"){
1821 infotext = L"Unknown node: ";
1822 infotext += narrow_to_wide(nodedef->get(n).name);
1831 if(nodig_delay_timer <= 0.0 && input->getLeftState())
1835 infostream<<"Started digging"<<std::endl;
1836 client.interact(0, pointed);
1838 ldown_for_dig = true;
1840 MapNode n = client.getNode(nodepos);
1842 // Get digging properties for material and tool
1843 content_t material = n.getContent();
1844 ToolDiggingProperties tp =
1845 tooldef->getDiggingProperties(playeritem_toolname);
1846 DiggingProperties prop =
1847 getDiggingProperties(material, &tp, nodedef);
1849 float dig_time_complete = 0.0;
1851 if(prop.diggable == false)
1853 /*infostream<<"Material "<<(int)material
1854 <<" not diggable with \""
1855 <<playeritem_toolname<<"\""<<std::endl;*/
1856 // I guess nobody will wait for this long
1857 dig_time_complete = 10000000.0;
1861 dig_time_complete = prop.time;
1864 if(dig_time_complete >= 0.001)
1866 dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
1867 * dig_time/dig_time_complete);
1869 // This is for torches
1872 dig_index = CRACK_ANIMATION_LENGTH;
1875 if(dig_index < CRACK_ANIMATION_LENGTH)
1877 //TimeTaker timer("client.setTempMod");
1878 //infostream<<"dig_index="<<dig_index<<std::endl;
1879 client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
1883 infostream<<"Digging completed"<<std::endl;
1884 client.interact(2, pointed);
1885 client.clearTempMod(nodepos);
1886 client.removeNode(nodepos);
1891 nodig_delay_timer = dig_time_complete
1892 / (float)CRACK_ANIMATION_LENGTH;
1894 // We don't want a corresponding delay to
1895 // very time consuming nodes
1896 if(nodig_delay_timer > 0.5)
1898 nodig_delay_timer = 0.5;
1900 // We want a slight delay to very little
1901 // time consuming nodes
1902 float mindelay = 0.15;
1903 if(nodig_delay_timer < mindelay)
1905 nodig_delay_timer = mindelay;
1911 camera.setDigging(0); // left click animation
1914 if(input->getRightClicked())
1916 infostream<<"Ground right-clicked"<<std::endl;
1918 // If metadata provides an inventory view, activate it
1919 if(meta && meta->getInventoryDrawSpecString() != "" && !random_input)
1921 infostream<<"Launching custom inventory view"<<std::endl;
1923 Construct the unique identification string of the node
1925 std::string current_name;
1926 current_name += "nodemeta:";
1927 current_name += itos(nodepos.X);
1928 current_name += ",";
1929 current_name += itos(nodepos.Y);
1930 current_name += ",";
1931 current_name += itos(nodepos.Z);
1937 core::array<GUIInventoryMenu::DrawSpec> draw_spec;
1939 GUIInventoryMenu::makeDrawSpecArrayFromString(
1941 meta->getInventoryDrawSpecString(),
1944 GUIInventoryMenu *menu =
1945 new GUIInventoryMenu(guienv, guiroot, -1,
1946 &g_menumgr, invsize,
1947 client.getInventoryContext(),
1949 menu->setDrawSpec(draw_spec);
1952 // If metadata provides text input, activate text input
1953 else if(meta && meta->allowsTextInput() && !random_input)
1955 infostream<<"Launching metadata text input"<<std::endl;
1957 // Get a new text for it
1959 TextDest *dest = new TextDestNodeMetadata(nodepos, &client);
1961 std::wstring wtext = narrow_to_wide(meta->getText());
1963 (new GUITextInputMenu(guienv, guiroot, -1,
1967 // Otherwise report right click to server
1970 client.interact(3, pointed);
1971 camera.setDigging(1); // right click animation
1975 else if(pointed.type == POINTEDTHING_OBJECT)
1977 infotext = narrow_to_wide(selected_object->infoText());
1979 //if(input->getLeftClicked())
1980 if(input->getLeftState())
1982 bool do_punch = false;
1983 bool do_punch_damage = false;
1984 if(object_hit_delay_timer <= 0.0){
1986 do_punch_damage = true;
1987 object_hit_delay_timer = object_hit_delay;
1989 if(input->getLeftClicked()){
1993 infostream<<"Left-clicked object"<<std::endl;
1996 if(do_punch_damage){
1997 // Report direct punch
1998 v3f objpos = selected_object->getPosition();
1999 v3f dir = (objpos - player_position).normalize();
2001 bool disable_send = selected_object->directReportPunch(playeritem_toolname, dir);
2003 client.interact(0, pointed);
2006 else if(input->getRightClicked())
2008 infostream<<"Right-clicked object"<<std::endl;
2009 client.interact(3, pointed); // place
2013 pointed_old = pointed;
2015 if(left_punch || (input->getLeftClicked() && !left_punch_muted))
2017 camera.setDigging(0); // left click animation
2020 input->resetLeftClicked();
2021 input->resetRightClicked();
2023 input->resetLeftReleased();
2024 input->resetRightReleased();
2027 Calculate stuff for drawing
2031 Calculate general brightness
2033 u32 daynight_ratio = client.getDayNightRatio();
2034 u8 light8 = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
2035 brightness = (float)light8/255.0;
2036 video::SColor bgcolor = video::SColor(
2038 bgcolor_bright.getRed() * brightness,
2039 bgcolor_bright.getGreen() * brightness,
2040 bgcolor_bright.getBlue() * brightness);
2041 /*skycolor.getRed() * brightness,
2042 skycolor.getGreen() * brightness,
2043 skycolor.getBlue() * brightness);*/
2048 if(fabs(brightness - old_brightness) > 0.01)
2049 update_skybox(driver, tsrc, smgr, skybox, brightness);
2056 clouds->step(dtime);
2057 clouds->update(v2f(player_position.X, player_position.Z),
2058 0.05+brightness*0.95);
2066 farmesh_range = draw_control.wanted_range * 10;
2067 if(draw_control.range_all && farmesh_range < 500)
2068 farmesh_range = 500;
2069 if(farmesh_range > 1000)
2070 farmesh_range = 1000;
2072 farmesh->step(dtime);
2073 farmesh->update(v2f(player_position.X, player_position.Z),
2074 0.05+brightness*0.95, farmesh_range);
2077 // Store brightness value
2078 old_brightness = brightness;
2084 if(g_settings->getBool("enable_fog") == true && !force_fog_off)
2089 range = BS*farmesh_range;
2093 range = draw_control.wanted_range*BS + MAP_BLOCKSIZE*BS*1.5;
2095 if(draw_control.range_all)
2098 range = range * 0.5 + 25*BS;*/
2103 video::EFT_FOG_LINEAR,
2115 video::EFT_FOG_LINEAR,
2125 Update gui stuff (0ms)
2128 //TimeTaker guiupdatetimer("Gui updating");
2131 static float drawtime_avg = 0;
2132 drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
2133 static float beginscenetime_avg = 0;
2134 beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
2135 static float scenetime_avg = 0;
2136 scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
2137 static float endscenetime_avg = 0;
2138 endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;
2141 snprintf(temptext, 300, "Minetest-c55 %s ("
2144 " drawtime=%.0f, beginscenetime=%.0f"
2145 ", scenetime=%.0f, endscenetime=%.0f",
2147 draw_control.range_all,
2154 guitext->setText(narrow_to_wide(temptext).c_str());
2159 snprintf(temptext, 300,
2160 "(% .1f, % .1f, % .1f)"
2161 " (% .3f < btime_jitter < % .3f"
2162 ", dtime_jitter = % .1f %%"
2163 ", v_range = %.1f, RTT = %.3f)",
2164 player_position.X/BS,
2165 player_position.Y/BS,
2166 player_position.Z/BS,
2167 busytime_jitter1_min_sample,
2168 busytime_jitter1_max_sample,
2169 dtime_jitter1_max_fraction * 100.0,
2170 draw_control.wanted_range,
2174 guitext2->setText(narrow_to_wide(temptext).c_str());
2178 guitext_info->setText(infotext.c_str());
2182 Get chat messages from client
2185 // Get new messages from error log buffer
2186 while(!chat_log_error_buf.empty())
2188 chat_lines.push_back(ChatLine(narrow_to_wide(
2189 chat_log_error_buf.get())));
2191 // Get new messages from client
2192 std::wstring message;
2193 while(client.getChatMessage(message))
2195 chat_lines.push_back(ChatLine(message));
2196 /*if(chat_lines.size() > 6)
2198 core::list<ChatLine>::Iterator
2199 i = chat_lines.begin();
2200 chat_lines.erase(i);
2203 // Append them to form the whole static text and throw
2204 // it to the gui element
2206 // This will correspond to the line number counted from
2207 // top to bottom, from size-1 to 0
2208 s16 line_number = chat_lines.size();
2209 // Count of messages to be removed from the top
2210 u16 to_be_removed_count = 0;
2211 for(core::list<ChatLine>::Iterator
2212 i = chat_lines.begin();
2213 i != chat_lines.end(); i++)
2215 // After this, line number is valid for this loop
2220 This results in a maximum age of 60*6 to the
2221 lowermost line and a maximum of 6 lines
2223 float allowed_age = (6-line_number) * 60.0;
2225 if((*i).age > allowed_age)
2227 to_be_removed_count++;
2230 whole += (*i).text + L'\n';
2232 for(u16 i=0; i<to_be_removed_count; i++)
2234 core::list<ChatLine>::Iterator
2235 it = chat_lines.begin();
2236 chat_lines.erase(it);
2238 guitext_chat->setText(whole.c_str());
2240 // Update gui element size and position
2242 /*core::rect<s32> rect(
2244 screensize.Y - guitext_chat_pad_bottom
2245 - text_height*chat_lines.size(),
2247 screensize.Y - guitext_chat_pad_bottom
2249 core::rect<s32> rect(
2253 50 + guitext_chat->getTextHeight()
2256 guitext_chat->setRelativePosition(rect);
2258 // Don't show chat if empty or profiler is enabled
2259 if(chat_lines.size() == 0 || show_profiler)
2260 guitext_chat->setVisible(false);
2262 guitext_chat->setVisible(true);
2269 if(client.getPlayerItem() != new_playeritem)
2271 client.selectPlayerItem(new_playeritem);
2273 if(client.getLocalInventoryUpdated())
2275 //infostream<<"Updating local inventory"<<std::endl;
2276 client.getLocalInventory(local_inventory);
2278 // Update wielded tool
2279 InventoryList *mlist = local_inventory.getList("main");
2280 InventoryItem *item = NULL;
2282 item = mlist->getItem(client.getPlayerItem());
2283 camera.wield(item, gamedef);
2287 Send actions returned by the inventory menu
2289 while(inventory_action_queue.size() != 0)
2291 InventoryAction *a = inventory_action_queue.pop_front();
2293 client.sendInventoryAction(a);
2302 TimeTaker drawtimer("Drawing");
2306 TimeTaker timer("beginScene");
2307 driver->beginScene(true, true, bgcolor);
2308 //driver->beginScene(false, true, bgcolor);
2309 beginscenetime = timer.stop(true);
2314 //infostream<<"smgr->drawAll()"<<std::endl;
2317 TimeTaker timer("smgr");
2319 scenetime = timer.stop(true);
2323 //TimeTaker timer9("auxiliary drawings");
2327 //TimeTaker //timer10("//timer10");
2333 driver->setMaterial(m);
2335 driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
2337 for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
2338 i != hilightboxes.end(); i++)
2340 /*infostream<<"hilightbox min="
2341 <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
2343 <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
2345 driver->draw3DBox(*i, video::SColor(255,0,0,0));
2352 // Warning: This clears the Z buffer.
2353 camera.drawWieldedTool();
2360 client.renderPostFx();
2366 if(g_settings->getBool("frametime_graph") == true)
2369 for(core::list<float>::Iterator
2370 i = frametime_log.begin();
2371 i != frametime_log.end();
2374 driver->draw2DLine(v2s32(x,50),
2375 v2s32(x,50+(*i)*1000),
2376 video::SColor(255,255,255,255));
2384 driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
2385 displaycenter + core::vector2d<s32>(10,0),
2386 video::SColor(255,255,255,255));
2387 driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
2388 displaycenter + core::vector2d<s32>(0,10),
2389 video::SColor(255,255,255,255));
2394 //TimeTaker //timer11("//timer11");
2406 draw_hotbar(driver, font, tsrc,
2407 v2s32(displaycenter.X, screensize.Y),
2408 hotbar_imagesize, hotbar_itemcount, &local_inventory,
2409 client.getHP(), client.getPlayerItem());
2415 if(damage_flash_timer > 0.0)
2417 damage_flash_timer -= dtime;
2419 video::SColor color(128,255,0,0);
2420 driver->draw2DRectangle(color,
2421 core::rect<s32>(0,0,screensize.X,screensize.Y),
2429 TimeTaker timer("endScene");
2431 endscenetime = timer.stop(true);
2434 drawtime = drawtimer.stop(true);
2440 static s16 lastFPS = 0;
2441 //u16 fps = driver->getFPS();
2442 u16 fps = (1.0/dtime_avg1);
2446 core::stringw str = L"Minetest [";
2447 str += driver->getName();
2451 device->setWindowCaption(str.c_str());
2463 Draw a "shutting down" screen, which will be shown while the map
2464 generator and other stuff quits
2467 /*gui::IGUIStaticText *gui_shuttingdowntext = */
2468 draw_load_screen(L"Shutting down stuff...", driver, font);
2469 /*driver->beginScene(true, true, video::SColor(255,0,0,0));
2472 gui_shuttingdowntext->remove();*/
2475 } // Client scope (must be destructed before destructing *def and tsrc