/* Minetest-c55 Copyright (C) 2011 celeron55, Perttu Ahola This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "common_irrlicht.h" #include "game.h" #include "client.h" #include "server.h" #include "guiPauseMenu.h" #include "guiInventoryMenu.h" #include "guiTextInputMenu.h" #include "guiFurnaceMenu.h" #include "materials.h" /* Setting this to 1 enables a special camera mode that forces the renderers to think that the camera statically points from the starting place to a static direction. This allows one to move around with the player and see what is actually drawn behind solid things and behind the player. */ #define FIELD_OF_VIEW_TEST 0 MapDrawControl draw_control; // Chat data struct ChatLine { ChatLine(): age(0.0) { } ChatLine(const std::wstring &a_text): age(0.0), text(a_text) { } float age; std::wstring text; }; /* Inventory stuff */ // Inventory actions from the menu are buffered here before sending Queue inventory_action_queue; // This is a copy of the inventory that the client's environment has Inventory local_inventory; u16 g_selected_item = 0; /* Text input system */ struct TextDestSign : public TextDest { TextDestSign(v3s16 blockpos, s16 id, Client *client) { m_blockpos = blockpos; m_id = id; m_client = client; } void gotText(std::wstring text) { std::string ntext = wide_to_narrow(text); dstream<<"Changing text of a sign object: " <sendSignText(m_blockpos, m_id, ntext); } v3s16 m_blockpos; s16 m_id; Client *m_client; }; struct TextDestChat : public TextDest { TextDestChat(Client *client) { m_client = client; } void gotText(std::wstring text) { // Discard empty line if(text == L"") return; // Parse command (server command starts with "/#") if(text[0] == L'/' && text[1] != L'#') { std::wstring reply = L"Local: "; reply += L"Local commands not yet supported. " L"Server prefix is \"/#\"."; m_client->addChatMessage(reply); return; } // Send to others m_client->sendChatMessage(text); // Show locally m_client->addChatMessage(text); } Client *m_client; }; struct TextDestSignNode : public TextDest { TextDestSignNode(v3s16 p, Client *client) { m_p = p; m_client = client; } void gotText(std::wstring text) { std::string ntext = wide_to_narrow(text); dstream<<"Changing text of a sign node: " <sendSignNodeText(m_p, ntext); } v3s16 m_p; Client *m_client; }; /* Render distance feedback loop */ void updateViewingRange(f32 frametime_in, Client *client) { if(draw_control.range_all == true) return; static f32 added_frametime = 0; static s16 added_frames = 0; added_frametime += frametime_in; added_frames += 1; // Actually this counter kind of sucks because frametime is busytime static f32 counter = 0; counter -= frametime_in; if(counter > 0) return; //counter = 0.1; counter = 0.2; /*dstream<<__FUNCTION_NAME <<": Collected "<getList("main"); if(mainlist == NULL) { dstream<<"WARNING: draw_hotbar(): mainlist == NULL"< barrect(0,0,width,height); barrect += pos; video::SColor bgcolor(255,128,128,128); driver->draw2DRectangle(bgcolor, barrect, NULL);*/ core::rect imgrect(0,0,imgsize,imgsize); for(s32 i=0; igetItem(i); core::rect rect = imgrect + pos + v2s32(padding+i*(imgsize+padding*2), padding); if(g_selected_item == i) { driver->draw2DRectangle(video::SColor(255,255,0,0), core::rect(rect.UpperLeftCorner - v2s32(1,1)*padding, rect.LowerRightCorner + v2s32(1,1)*padding), NULL); } else { video::SColor bgcolor2(128,0,0,0); driver->draw2DRectangle(bgcolor2, rect, NULL); } if(item != NULL) { drawInventoryItem(driver, font, item, rect, NULL); } } /* Draw hearts */ { video::ITexture *heart_texture = driver->getTexture(porting::getDataPath("heart.png").c_str()); v2s32 p = pos + v2s32(0, -20); for(s32 i=0; i rect(0,0,16,16); rect += p; driver->draw2DImage(heart_texture, rect, core::rect(core::position2d(0,0), core::dimension2di(heart_texture->getOriginalSize())), NULL, colors, true); p += v2s32(20,0); } if(halfheartcount % 2 == 1) { const video::SColor color(255,255,255,255); const video::SColor colors[] = {color,color,color,color}; core::rect rect(0,0,16/2,16); rect += p; core::dimension2di srcd(heart_texture->getOriginalSize()); srcd.Width /= 2; driver->draw2DImage(heart_texture, rect, core::rect(core::position2d(0,0), srcd), NULL, colors, true); p += v2s32(20,0); } } } /* Find what the player is pointing at */ void getPointedNode(Client *client, v3f player_position, v3f camera_direction, v3f camera_position, bool &nodefound, core::line3d shootline, v3s16 &nodepos, v3s16 &neighbourpos, core::aabbox3d &nodehilightbox, f32 d) { f32 mindistance = BS * 1001; v3s16 pos_i = floatToInt(player_position, BS); /*std::cout<<"pos_i=("<0 ? a : 1); s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1); s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1); for(s16 y = ystart; y <= yend; y++) for(s16 z = zstart; z <= zend; z++) for(s16 x = xstart; x <= xend; x++) { MapNode n; try { n = client->getNode(v3s16(x,y,z)); if(content_pointable(n.d) == false) continue; } catch(InvalidPositionException &e) { continue; } v3s16 np(x,y,z); v3f npf = intToFloat(np, BS); f32 d = 0.01; v3s16 dirs[6] = { v3s16(0,0,1), // back v3s16(0,1,0), // top v3s16(1,0,0), // right v3s16(0,0,-1), // front v3s16(0,-1,0), // bottom v3s16(-1,0,0), // left }; /* Meta-objects */ if(n.d == CONTENT_TORCH) { v3s16 dir = unpackDir(n.dir); v3f dir_f = v3f(dir.X, dir.Y, dir.Z); dir_f *= BS/2 - BS/6 - BS/20; v3f cpf = npf + dir_f; f32 distance = (cpf - camera_position).getLength(); core::aabbox3d box; // bottom if(dir == v3s16(0,-1,0)) { box = core::aabbox3d( npf - v3f(BS/6, BS/2, BS/6), npf + v3f(BS/6, -BS/2+BS/3*2, BS/6) ); } // top else if(dir == v3s16(0,1,0)) { box = core::aabbox3d( npf - v3f(BS/6, -BS/2+BS/3*2, BS/6), npf + v3f(BS/6, BS/2, BS/6) ); } // side else { box = core::aabbox3d( cpf - v3f(BS/6, BS/3, BS/6), cpf + v3f(BS/6, BS/3, BS/6) ); } if(distance < mindistance) { if(box.intersectsWithLine(shootline)) { nodefound = true; nodepos = np; neighbourpos = np; mindistance = distance; nodehilightbox = box; } } } else if(n.d == CONTENT_SIGN_WALL) { v3s16 dir = unpackDir(n.dir); v3f dir_f = v3f(dir.X, dir.Y, dir.Z); dir_f *= BS/2 - BS/6 - BS/20; v3f cpf = npf + dir_f; f32 distance = (cpf - camera_position).getLength(); v3f vertices[4] = { v3f(BS*0.42,-BS*0.35,-BS*0.4), v3f(BS*0.49, BS*0.35, BS*0.4), }; for(s32 i=0; i<2; i++) { if(dir == v3s16(1,0,0)) vertices[i].rotateXZBy(0); if(dir == v3s16(-1,0,0)) vertices[i].rotateXZBy(180); if(dir == v3s16(0,0,1)) vertices[i].rotateXZBy(90); if(dir == v3s16(0,0,-1)) vertices[i].rotateXZBy(-90); if(dir == v3s16(0,-1,0)) vertices[i].rotateXYBy(-90); if(dir == v3s16(0,1,0)) vertices[i].rotateXYBy(90); vertices[i] += npf; } core::aabbox3d box; box = core::aabbox3d(vertices[0]); box.addInternalPoint(vertices[1]); if(distance < mindistance) { if(box.intersectsWithLine(shootline)) { nodefound = true; nodepos = np; neighbourpos = np; mindistance = distance; nodehilightbox = box; } } } /* Regular blocks */ else { for(u16 i=0; i<6; i++) { v3f dir_f = v3f(dirs[i].X, dirs[i].Y, dirs[i].Z); v3f centerpoint = npf + dir_f * BS/2; f32 distance = (centerpoint - camera_position).getLength(); if(distance < mindistance) { core::CMatrix4 m; m.buildRotateFromTo(v3f(0,0,1), dir_f); // This is the back face v3f corners[2] = { v3f(BS/2, BS/2, BS/2), v3f(-BS/2, -BS/2, BS/2+d) }; for(u16 j=0; j<2; j++) { m.rotateVect(corners[j]); corners[j] += npf; } core::aabbox3d facebox(corners[0]); facebox.addInternalPoint(corners[1]); if(facebox.intersectsWithLine(shootline)) { nodefound = true; nodepos = np; neighbourpos = np + dirs[i]; mindistance = distance; //nodehilightbox = facebox; const float d = 0.502; core::aabbox3d nodebox (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d); v3f nodepos_f = intToFloat(nodepos, BS); nodebox.MinEdge += nodepos_f; nodebox.MaxEdge += nodepos_f; nodehilightbox = nodebox; } } // if distance < mindistance } // for dirs } // regular block } // for coords } void the_game( bool &kill, bool random_input, InputHandler *input, IrrlichtDevice *device, gui::IGUIFont* font, std::string map_dir, std::string playername, std::string address, u16 port, std::wstring &error_message ) { video::IVideoDriver* driver = device->getVideoDriver(); scene::ISceneManager* smgr = device->getSceneManager(); v2u32 screensize(0,0); v2u32 last_screensize(0,0); screensize = driver->getScreenSize(); const s32 hotbar_itemcount = 8; const s32 hotbar_imagesize = 36; /* Draw "Loading" screen */ const wchar_t *text = L"Loading and connecting..."; u32 text_height = font->getDimension(text).Height; core::vector2d center(screensize.X/2, screensize.Y/2); core::vector2d textsize(300, text_height); core::rect textrect(center - textsize/2, center + textsize/2); gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText( text, textrect, false, false); gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT); driver->beginScene(true, true, video::SColor(255,0,0,0)); guienv->drawAll(); driver->endScene(); std::cout< server; if(address == ""){ server = new Server(map_dir); server->start(port); } /* Create client */ Client client(device, playername.c_str(), draw_control); Address connect_address(0,0,0,0, port); try{ if(address == "") //connect_address.Resolve("localhost"); connect_address.setAddress(127,0,0,1); else connect_address.Resolve(address.c_str()); } catch(ResolveError &e) { std::cout<remove(); return; } dstream<beginScene(true, true, video::SColor(255,0,0,0)); guienv->drawAll(); driver->endScene(); // Update client and server client.step(0.1); if(server != NULL) server->step(0.1); // Delay a bit sleep_ms(100); } } catch(con::PeerNotFoundException &e) { std::cout<remove(); return; } /* Create skybox */ /*scene::ISceneNode* skybox; skybox = smgr->addSkyBoxSceneNode( driver->getTexture(porting::getDataPath("skybox2.png").c_str()), driver->getTexture(porting::getDataPath("skybox3.png").c_str()), driver->getTexture(porting::getDataPath("skybox1.png").c_str()), driver->getTexture(porting::getDataPath("skybox1.png").c_str()), driver->getTexture(porting::getDataPath("skybox1.png").c_str()), driver->getTexture(porting::getDataPath("skybox1.png").c_str()));*/ /* Create the camera node */ scene::ICameraSceneNode* camera = smgr->addCameraSceneNode( 0, // Camera parent v3f(BS*100, BS*2, BS*100), // Look from v3f(BS*100+1, BS*2, BS*100), // Look to -1 // Camera ID ); if(camera == NULL) { error_message = L"Failed to create the camera node"; return; } //video::SColor skycolor = video::SColor(255,90,140,200); //video::SColor skycolor = video::SColor(255,166,202,244); //video::SColor skycolor = video::SColor(255,120,185,244); video::SColor skycolor = video::SColor(255,140,186,250); camera->setFOV(FOV_ANGLE); // Just so big a value that everything rendered is visible camera->setFarValue(100000*BS); f32 camera_yaw = 0; // "right/left" f32 camera_pitch = 0; // "up/down" /* Move into game */ gui_loadingtext->remove(); /* Add some gui stuff */ // First line of debug text gui::IGUIStaticText *guitext = guienv->addStaticText( L"Minetest-c55", core::rect(5, 5, 795, 5+text_height), false, false); // Second line of debug text gui::IGUIStaticText *guitext2 = guienv->addStaticText( L"", core::rect(5, 5+(text_height+5)*1, 795, (5+text_height)*2), false, false); // At the middle of the screen // Object infos are shown in this gui::IGUIStaticText *guitext_info = guienv->addStaticText( L"", core::rect(0,0,400,text_height+5) + v2s32(100,200), false, false); // Chat text gui::IGUIStaticText *guitext_chat = guienv->addStaticText( L"", core::rect(0,0,0,0), false, false); // Disable word wrap as of now //false, true); //guitext_chat->setBackgroundColor(video::SColor(96,0,0,0)); core::list chat_lines; /*GUIQuickInventory *quick_inventory = new GUIQuickInventory (guienv, NULL, v2s32(10, 70), 5, &local_inventory);*/ /*GUIQuickInventory *quick_inventory = new GUIQuickInventory (guienv, NULL, v2s32(0, 0), quickinv_itemcount, &local_inventory);*/ // Test the text input system /*(new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr, NULL))->drop();*/ /*GUIMessageMenu *menu = new GUIMessageMenu(guienv, guiroot, -1, &g_menumgr, L"Asd"); menu->drop();*/ // Launch pause menu (new GUIPauseMenu(guienv, guiroot, -1, g_gamecallback, &g_menumgr))->drop(); // Enable texts /*guitext2->setVisible(true); guitext_info->setVisible(true); guitext_chat->setVisible(true);*/ //s32 guitext_chat_pad_bottom = 70; /* Some statistics are collected in these */ u32 drawtime = 0; u32 beginscenetime = 0; u32 scenetime = 0; u32 endscenetime = 0; // A test //throw con::PeerNotFoundException("lol"); core::list frametime_log; float damage_flash_timer = 0; /* Main loop */ bool first_loop_after_window_activation = true; // Time is in milliseconds // NOTE: getRealTime() causes strange problems in wine (imprecision?) // NOTE: So we have to use getTime() and call run()s between them u32 lasttime = device->getTimer()->getTime(); while(device->run() && kill == false) { if(g_gamecallback->disconnect_requested) { g_gamecallback->disconnect_requested = false; break; } /* Process TextureSource's queue */ ((TextureSource*)g_texturesource)->processQueue(); /* Random calculations */ last_screensize = screensize; screensize = driver->getScreenSize(); v2s32 displaycenter(screensize.X/2,screensize.Y/2); //bool screensize_changed = screensize != last_screensize; // Hilight boxes collected during the loop and displayed core::list< core::aabbox3d > hilightboxes; // Info text std::wstring infotext; // When screen size changes, update positions and sizes of stuff /*if(screensize_changed) { v2s32 pos(displaycenter.X-((quickinv_itemcount-1)*quickinv_spacing+quickinv_size)/2, screensize.Y-quickinv_spacing); quick_inventory->updatePosition(pos); }*/ //TimeTaker //timer1("//timer1"); // Time of frame without fps limit float busytime; u32 busytime_u32; { // not using getRealTime is necessary for wine u32 time = device->getTimer()->getTime(); if(time > lasttime) busytime_u32 = time - lasttime; else busytime_u32 = 0; busytime = busytime_u32 / 1000.0; } //std::cout<<"busytime_u32="<getTimer()->getTime() device->run(); /* Viewing range */ updateViewingRange(busytime, &client); /* FPS limiter */ { float fps_max = g_settings.getFloat("fps_max"); u32 frametime_min = 1000./fps_max; if(busytime_u32 < frametime_min) { u32 sleeptime = frametime_min - busytime_u32; device->sleep(sleeptime); } } // Necessary for device->getTimer()->getTime() device->run(); /* Time difference calculation */ f32 dtime; // in seconds u32 time = device->getTimer()->getTime(); if(time > lasttime) dtime = (time - lasttime) / 1000.0; else dtime = 0; lasttime = time; /* Log frametime for visualization */ frametime_log.push_back(dtime); if(frametime_log.size() > 100) { core::list::Iterator i = frametime_log.begin(); frametime_log.erase(i); } /* Visualize frametime in terminal */ /*for(u32 i=0; i jitter1_max) jitter1_max = dtime_jitter1; counter += dtime; if(counter > 0.0) { counter -= 3.0; dtime_jitter1_max_sample = jitter1_max; dtime_jitter1_max_fraction = dtime_jitter1_max_sample / (dtime_avg1+0.001); jitter1_max = 0.0; } } /* Busytime average and jitter calculation */ static f32 busytime_avg1 = 0.0; busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02; f32 busytime_jitter1 = busytime - busytime_avg1; static f32 busytime_jitter1_max_sample = 0.0; static f32 busytime_jitter1_min_sample = 0.0; { static f32 jitter1_max = 0.0; static f32 jitter1_min = 0.0; static f32 counter = 0.0; if(busytime_jitter1 > jitter1_max) jitter1_max = busytime_jitter1; if(busytime_jitter1 < jitter1_min) jitter1_min = busytime_jitter1; counter += dtime; if(counter > 0.0){ counter -= 3.0; busytime_jitter1_max_sample = jitter1_max; busytime_jitter1_min_sample = jitter1_min; jitter1_max = 0.0; jitter1_min = 0.0; } } /* Debug info for client */ { static float counter = 0.0; counter -= dtime; if(counter < 0) { counter = 30.0; client.printDebugInfo(std::cout); } } /* Direct handling of user input */ // Reset input if window not active or some menu is active if(device->isWindowActive() == false || noMenuActive() == false) { input->clear(); } // Input handler step() (used by the random input generator) input->step(dtime); /* Launch menus according to keys */ if(input->wasKeyDown(irr::KEY_KEY_I)) { dstream< draw_spec; draw_spec.push_back(GUIInventoryMenu::DrawSpec( "list", "current_player", "main", v2s32(0, 3), v2s32(8, 4))); draw_spec.push_back(GUIInventoryMenu::DrawSpec( "list", "current_player", "craft", v2s32(3, 0), v2s32(3, 3))); draw_spec.push_back(GUIInventoryMenu::DrawSpec( "list", "current_player", "craftresult", v2s32(7, 1), v2s32(1, 1))); menu->setDrawSpec(draw_spec); menu->drop(); } else if(input->wasKeyDown(irr::KEY_ESCAPE)) { dstream<drop(); } else if(input->wasKeyDown(irr::KEY_KEY_T)) { TextDest *dest = new TextDestChat(&client); (new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr, dest, L""))->drop(); } // Item selection with mouse wheel { s32 wheel = input->getMouseWheel(); u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE-1, hotbar_itemcount-1); if(wheel < 0) { if(g_selected_item < max_item) g_selected_item++; else g_selected_item = 0; } else if(wheel > 0) { if(g_selected_item > 0) g_selected_item--; else g_selected_item = max_item; } } // Item selection for(u16 i=0; i<10; i++) { s32 keycode = irr::KEY_KEY_1 + i; if(i == 9) keycode = irr::KEY_KEY_0; if(input->wasKeyDown((irr::EKEY_CODE)keycode)) { if(i < PLAYER_INVENTORY_SIZE && i < hotbar_itemcount) { g_selected_item = i; dstream<wasKeyDown(irr::KEY_KEY_R)) { if(draw_control.range_all) { draw_control.range_all = false; dstream<wasKeyDown(irr::KEY_KEY_P)) { dstream<<"-----------------------------------------" <isKeyDown(irr::KEY_KEY_W), input->isKeyDown(irr::KEY_KEY_S), input->isKeyDown(irr::KEY_KEY_A), input->isKeyDown(irr::KEY_KEY_D), input->isKeyDown(irr::KEY_SPACE), input->isKeyDown(irr::KEY_KEY_E), input->isKeyDown(irr::KEY_LSHIFT) || input->isKeyDown(irr::KEY_RSHIFT), camera_pitch, camera_yaw ); client.setPlayerControl(control); } /* Run server */ if(server != NULL) { //TimeTaker timer("server->step(dtime)"); server->step(dtime); } /* Process environment */ { //TimeTaker timer("client.step(dtime)"); client.step(dtime); //client.step(dtime_avg1); } // Read client events for(;;) { ClientEvent event = client.getClientEvent(); if(event.type == CE_NONE) { break; } else if(event.type == CE_PLAYER_DAMAGE) { //u16 damage = event.player_damage.amount; //dstream<<"Player damage: "<isWindowActive() && noMenuActive()) || random_input) { if(!random_input) device->getCursorControl()->setVisible(false); if(first_loop_after_window_activation){ //std::cout<<"window active, first loop"<getMousePos().X - displaycenter.X; s32 dy = input->getMousePos().Y - displaycenter.Y; //std::cout<<"window active, pos difference "< 89.5) camera_pitch = 89.5; } input->setMousePos(displaycenter.X, displaycenter.Y); } else{ device->getCursorControl()->setVisible(true); //std::cout<<"window inactive"<setPosition(camera_position); // *100.0 helps in large map coordinates camera->setTarget(camera_position + camera_direction * 100.0); if(FIELD_OF_VIEW_TEST){ client.updateCamera(v3f(0,0,0), v3f(0,0,1)); } else{ //TimeTaker timer("client.updateCamera"); client.updateCamera(camera_position, camera_direction); } //timer2.stop(); //TimeTaker //timer3("//timer3"); /* Calculate what block is the crosshair pointing to */ //u32 t1 = device->getTimer()->getRealTime(); //f32 d = 4; // max. distance f32 d = 4; // max. distance core::line3d shootline(camera_position, camera_position + camera_direction * BS * (d+1)); MapBlockObject *selected_object = client.getSelectedObject (d*BS, camera_position, shootline); ClientActiveObject *selected_active_object = client.getSelectedActiveObject (d*BS, camera_position, shootline); if(selected_object != NULL) { //dstream<<"Client returned selected_object != NULL"< box_on_map = selected_object->getSelectionBoxOnMap(); hilightboxes.push_back(box_on_map); infotext = narrow_to_wide(selected_object->infoText()); if(input->getLeftClicked()) { std::cout<getBlock()->getPos(), selected_object->getId(), g_selected_item); } else if(input->getRightClicked()) { std::cout<getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN) { dstream<<"Sign object right-clicked"<getBlock()->getPos(), selected_object->getId(), &client); SignObject *sign_object = (SignObject*)selected_object; std::wstring wtext = narrow_to_wide(sign_object->getText()); (new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr, dest, wtext))->drop(); } } /* Otherwise pass the event to the server as-is */ else { client.clickObject(1, selected_object->getBlock()->getPos(), selected_object->getId(), g_selected_item); } } } else if(selected_active_object != NULL) { //dstream<<"Client returned selected_active_object != NULL"< *selection_box = selected_active_object->getSelectionBox(); // Box should exist because object was returned in the // first place assert(selection_box); v3f pos = selected_active_object->getPosition(); core::aabbox3d box_on_map( selection_box->MinEdge + pos, selection_box->MaxEdge + pos ); hilightboxes.push_back(box_on_map); //infotext = narrow_to_wide("A ClientActiveObject"); infotext = narrow_to_wide(selected_active_object->infoText()); if(input->getLeftClicked()) { std::cout<getId(), g_selected_item); } else if(input->getRightClicked()) { std::cout< nodehilightbox; getPointedNode(&client, player_position, camera_direction, camera_position, nodefound, shootline, nodepos, neighbourpos, nodehilightbox, d); static float nodig_delay_counter = 0.0; if(nodefound) { static v3s16 nodepos_old(-32768,-32768,-32768); static float dig_time = 0.0; static u16 dig_index = 0; /* Visualize selection */ hilightboxes.push_back(nodehilightbox); /* Check information text of node */ NodeMetadata *meta = client.getNodeMetadata(nodepos); if(meta) { infotext = narrow_to_wide(meta->infoText()); } //MapNode node = client.getNode(nodepos); /* Handle digging */ if(input->getLeftReleased()) { client.clearTempMod(nodepos); dig_time = 0.0; } if(nodig_delay_counter > 0.0) { nodig_delay_counter -= dtime; } else { if(nodepos != nodepos_old) { std::cout<getLeftClicked() || (input->getLeftState() && nodepos != nodepos_old)) { dstream<getLeftClicked()) { client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, 0)); } if(input->getLeftState()) { MapNode n = client.getNode(nodepos); // Get tool name. Default is "" = bare hands std::string toolname = ""; InventoryList *mlist = local_inventory.getList("main"); if(mlist != NULL) { InventoryItem *item = mlist->getItem(g_selected_item); if(item && (std::string)item->getName() == "ToolItem") { ToolItem *titem = (ToolItem*)item; toolname = titem->getToolName(); } } // Get digging properties for material and tool u8 material = n.d; DiggingProperties prop = getDiggingProperties(material, toolname); float dig_time_complete = 0.0; if(prop.diggable == false) { /*dstream<<"Material "<<(int)material <<" not diggable with \"" <= 0.001) { dig_index = (u16)((float)CRACK_ANIMATION_LENGTH * dig_time/dig_time_complete); } // This is for torches else { dig_index = CRACK_ANIMATION_LENGTH; } if(dig_index < CRACK_ANIMATION_LENGTH) { //TimeTaker timer("client.setTempMod"); //dstream<<"dig_index="< 0.5) { nodig_delay_counter = 0.5; } // We want a slight delay to very little // time consuming nodes float mindelay = 0.15; if(nodig_delay_counter < mindelay) { nodig_delay_counter = mindelay; } } dig_time += dtime; } } if(input->getRightClicked()) { std::cout<typeId() == CONTENT_SIGN_WALL && !random_input) { dstream<<"Sign node right-clicked"<getText()); (new GUITextInputMenu(guienv, guiroot, -1, &g_menumgr, dest, wtext))->drop(); } else if(meta && meta->typeId() == CONTENT_CHEST && !random_input) { dstream<<"Chest node right-clicked"< draw_spec; draw_spec.push_back(GUIInventoryMenu::DrawSpec( "list", chest_inv_id, "0", v2s32(0, 0), v2s32(8, 4))); draw_spec.push_back(GUIInventoryMenu::DrawSpec( "list", "current_player", "main", v2s32(0, 5), v2s32(8, 4))); menu->setDrawSpec(draw_spec); menu->drop(); } else if(meta && meta->typeId() == CONTENT_FURNACE && !random_input) { dstream<<"Furnace node right-clicked"<drop(); } else { client.groundAction(1, nodepos, neighbourpos, g_selected_item); } } nodepos_old = nodepos; } else{ } } // selected_object == NULL input->resetLeftClicked(); input->resetRightClicked(); if(input->getLeftReleased()) { std::cout<getRightReleased()) { //std::cout<resetLeftReleased(); input->resetRightReleased(); /* Calculate stuff for drawing */ camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y); u32 daynight_ratio = client.getDayNightRatio(); u8 l = decode_light((daynight_ratio * LIGHT_SUN) / 1000); video::SColor bgcolor = video::SColor( 255, skycolor.getRed() * l / 255, skycolor.getGreen() * l / 255, skycolor.getBlue() * l / 255); /* Fog */ if(g_settings.getBool("enable_fog") == true) { //f32 range = draw_control.wanted_range * BS + MAP_BLOCKSIZE/2*BS; f32 range = draw_control.wanted_range * BS + 0.8*MAP_BLOCKSIZE*BS; //f32 range = draw_control.wanted_range * BS + 0.0*MAP_BLOCKSIZE*BS; if(draw_control.range_all) range = 100000*BS; driver->setFog( bgcolor, video::EFT_FOG_LINEAR, range*0.4, range*1.0, 0.01, false, // pixel fog false // range fog ); } else { driver->setFog( bgcolor, video::EFT_FOG_LINEAR, 100000*BS, 110000*BS, 0.01, false, // pixel fog false // range fog ); } /* Update gui stuff (0ms) */ //TimeTaker guiupdatetimer("Gui updating"); { static float drawtime_avg = 0; drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05; static float beginscenetime_avg = 0; beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05; static float scenetime_avg = 0; scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05; static float endscenetime_avg = 0; endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05; char temptext[300]; snprintf(temptext, 300, "Minetest-c55 (" "F: item=%i" ", R: range_all=%i" ")" " drawtime=%.0f, beginscenetime=%.0f" ", scenetime=%.0f, endscenetime=%.0f", g_selected_item, draw_control.range_all, drawtime_avg, beginscenetime_avg, scenetime_avg, endscenetime_avg ); guitext->setText(narrow_to_wide(temptext).c_str()); } { char temptext[300]; snprintf(temptext, 300, "(% .1f, % .1f, % .1f)" " (% .3f < btime_jitter < % .3f" ", dtime_jitter = % .1f %%" ", v_range = %.1f)", player_position.X/BS, player_position.Y/BS, player_position.Z/BS, busytime_jitter1_min_sample, busytime_jitter1_max_sample, dtime_jitter1_max_fraction * 100.0, draw_control.wanted_range ); guitext2->setText(narrow_to_wide(temptext).c_str()); } { guitext_info->setText(infotext.c_str()); } /* Get chat messages from client */ { // Get new messages std::wstring message; while(client.getChatMessage(message)) { chat_lines.push_back(ChatLine(message)); /*if(chat_lines.size() > 6) { core::list::Iterator i = chat_lines.begin(); chat_lines.erase(i); }*/ } // Append them to form the whole static text and throw // it to the gui element std::wstring whole; // This will correspond to the line number counted from // top to bottom, from size-1 to 0 s16 line_number = chat_lines.size(); // Count of messages to be removed from the top u16 to_be_removed_count = 0; for(core::list::Iterator i = chat_lines.begin(); i != chat_lines.end(); i++) { // After this, line number is valid for this loop line_number--; // Increment age (*i).age += dtime; /* This results in a maximum age of 60*6 to the lowermost line and a maximum of 6 lines */ float allowed_age = (6-line_number) * 60.0; if((*i).age > allowed_age) { to_be_removed_count++; continue; } whole += (*i).text + L'\n'; } for(u16 i=0; i::Iterator it = chat_lines.begin(); chat_lines.erase(it); } guitext_chat->setText(whole.c_str()); // Update gui element size and position /*core::rect rect( 10, screensize.Y - guitext_chat_pad_bottom - text_height*chat_lines.size(), screensize.X - 10, screensize.Y - guitext_chat_pad_bottom );*/ core::rect rect( 10, 50, screensize.X - 10, 50 + text_height*chat_lines.size() ); guitext_chat->setRelativePosition(rect); if(chat_lines.size() == 0) guitext_chat->setVisible(false); else guitext_chat->setVisible(true); } /* Inventory */ static u16 old_selected_item = 65535; if(client.getLocalInventoryUpdated() || g_selected_item != old_selected_item) { old_selected_item = g_selected_item; //std::cout<<"Updating local inventory"<beginScene(true, true, bgcolor); //driver->beginScene(false, true, bgcolor); beginscenetime = timer.stop(true); } //timer3.stop(); //std::cout<drawAll()"<drawAll(); scenetime = timer.stop(true); } { //TimeTaker timer9("auxiliary drawings"); // 0ms //timer9.stop(); //TimeTaker //timer10("//timer10"); video::SMaterial m; //m.Thickness = 10; m.Thickness = 3; m.Lighting = false; driver->setMaterial(m); driver->setTransform(video::ETS_WORLD, core::IdentityMatrix); for(core::list< core::aabbox3d >::Iterator i=hilightboxes.begin(); i != hilightboxes.end(); i++) { /*std::cout<<"hilightbox min=" <<"("<MinEdge.X<<","<MinEdge.Y<<","<MinEdge.Z<<")" <<" max=" <<"("<MaxEdge.X<<","<MaxEdge.Y<<","<MaxEdge.Z<<")" <draw3DBox(*i, video::SColor(255,0,0,0)); } /* Frametime log */ if(g_settings.getBool("frametime_graph") == true) { s32 x = 10; for(core::list::Iterator i = frametime_log.begin(); i != frametime_log.end(); i++) { driver->draw2DLine(v2s32(x,50), v2s32(x,50+(*i)*1000), video::SColor(255,255,255,255)); x++; } } /* Draw crosshair */ driver->draw2DLine(displaycenter - core::vector2d(10,0), displaycenter + core::vector2d(10,0), video::SColor(255,255,255,255)); driver->draw2DLine(displaycenter - core::vector2d(0,10), displaycenter + core::vector2d(0,10), video::SColor(255,255,255,255)); } // timer //timer10.stop(); //TimeTaker //timer11("//timer11"); /* Draw gui */ // 0-1ms guienv->drawAll(); /* Draw hotbar */ { draw_hotbar(driver, font, v2s32(displaycenter.X, screensize.Y), hotbar_imagesize, hotbar_itemcount, &local_inventory, client.getHP()); } /* Damage flash */ if(damage_flash_timer > 0.0) { damage_flash_timer -= dtime; video::SColor color(128,255,0,0); driver->draw2DRectangle(color, core::rect(0,0,screensize.X,screensize.Y), NULL); } /* End scene */ { TimeTaker timer("endScene"); driver->endScene(); endscenetime = timer.stop(true); } drawtime = drawtimer.stop(true); /* End of drawing */ static s16 lastFPS = 0; //u16 fps = driver->getFPS(); u16 fps = (1.0/dtime_avg1); if (lastFPS != fps) { core::stringw str = L"Minetest ["; str += driver->getName(); str += "] FPS:"; str += fps; device->setWindowCaption(str.c_str()); lastFPS = fps; } } }