diff options
33 files changed, 1610 insertions, 511 deletions
diff --git a/data/chest_lock.png b/data/chest_lock.png Binary files differnew file mode 100644 index 000000000..ae038fae5 --- /dev/null +++ b/data/chest_lock.png diff --git a/data/tool_steelaxe.png b/data/tool_steelaxe.png Binary files differindex 390dbb087..0ad23c93b 100644 --- a/data/tool_steelaxe.png +++ b/data/tool_steelaxe.png diff --git a/data/tool_stoneaxe.png b/data/tool_stoneaxe.png Binary files differindex 0c5414af5..698ac918f 100644 --- a/data/tool_stoneaxe.png +++ b/data/tool_stoneaxe.png diff --git a/data/tool_woodaxe.png b/data/tool_woodaxe.png Binary files differindex 34f54eff9..3daa4af72 100644 --- a/data/tool_woodaxe.png +++ b/data/tool_woodaxe.png diff --git a/doc/mapformat.txt b/doc/mapformat.txt index a0241804e..206f5f350 100644 --- a/doc/mapformat.txt +++ b/doc/mapformat.txt @@ -85,5 +85,10 @@ Furnace metadata: Chest metadata: TBD +Locking Chest metadata: + u16 string_len + u8[string_len] string + TBD + // END diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c479b4e73..a753b6d87 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -142,6 +142,7 @@ set(minetest_SRCS mapblock_mesh.cpp farmesh.cpp keycode.cpp + camera.cpp clouds.cpp clientobject.cpp guiMainMenu.cpp @@ -188,10 +189,10 @@ if(BUILD_CLIENT) ${PNG_LIBRARIES} ${X11_LIBRARIES} ${GETTEXT_LIBRARY} - ${PLATFORM_LIBS} - ${CLIENT_PLATFORM_LIBS} ${JTHREAD_LIBRARY} ${SQLITE3_LIBRARY} + ${PLATFORM_LIBS} + ${CLIENT_PLATFORM_LIBS} ) endif(BUILD_CLIENT) @@ -200,9 +201,9 @@ if(BUILD_SERVER) target_link_libraries( ${PROJECT_NAME}server ${ZLIB_LIBRARIES} - ${PLATFORM_LIBS} ${JTHREAD_LIBRARY} ${SQLITE3_LIBRARY} + ${PLATFORM_LIBS} ) endif(BUILD_SERVER) diff --git a/src/camera.cpp b/src/camera.cpp new file mode 100644 index 000000000..34226b90b --- /dev/null +++ b/src/camera.cpp @@ -0,0 +1,898 @@ +/* +Minetest-c55 +Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com> + +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 "camera.h" +#include "debug.h" +#include "client.h" +#include "main.h" // for g_settings +#include "map.h" +#include "player.h" +#include "tile.h" +#include <cmath> + +Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control): + m_smgr(smgr), + m_playernode(NULL), + m_headnode(NULL), + m_cameranode(NULL), + + m_wieldmgr(NULL), + m_wieldnode(NULL), + + m_draw_control(draw_control), + m_viewing_range_min(5.0), + m_viewing_range_max(5.0), + + m_camera_position(0,0,0), + m_camera_direction(0,0,0), + + m_aspect(1.0), + m_fov_x(1.0), + m_fov_y(1.0), + + m_wanted_frametime(0.0), + m_added_frametime(0), + m_added_frames(0), + m_range_old(0), + m_frametime_old(0), + m_frametime_counter(0), + m_time_per_range(30. / 50), // a sane default of 30ms per 50 nodes of range + + m_view_bobbing_anim(0), + m_view_bobbing_state(0), + m_view_bobbing_speed(0), + + m_digging_anim(0), + m_digging_button(-1) +{ + //dstream<<__FUNCTION_NAME<<std::endl; + + // note: making the camera node a child of the player node + // would lead to unexpected behaviour, so we don't do that. + m_playernode = smgr->addEmptySceneNode(smgr->getRootSceneNode()); + m_headnode = smgr->addEmptySceneNode(m_playernode); + m_cameranode = smgr->addCameraSceneNode(smgr->getRootSceneNode()); + m_cameranode->bindTargetAndRotation(true); + + // This needs to be in its own scene manager. It is drawn after + // all other 3D scene nodes and before the GUI. + m_wieldmgr = smgr->createNewSceneManager(); + m_wieldmgr->addCameraSceneNode(); + m_wieldnode = new ExtrudedSpriteSceneNode(m_wieldmgr->getRootSceneNode(), m_wieldmgr); + + updateSettings(); +} + +Camera::~Camera() +{ + m_wieldmgr->drop(); + m_wieldnode->drop(); +} + +bool Camera::successfullyCreated(std::wstring& error_message) +{ + if (m_playernode == NULL) + { + error_message = L"Failed to create the player scene node"; + return false; + } + if (m_headnode == NULL) + { + error_message = L"Failed to create the head scene node"; + return false; + } + if (m_cameranode == NULL) + { + error_message = L"Failed to create the camera scene node"; + return false; + } + if (m_wieldmgr == NULL) + { + error_message = L"Failed to create the wielded item scene manager"; + return false; + } + if (m_wieldnode == NULL) + { + error_message = L"Failed to create the wielded item scene node"; + return false; + } + return true; +} + +// Returns the fractional part of x +inline f32 my_modf(f32 x) +{ + double dummy; + return modf(x, &dummy); +} + +void Camera::step(f32 dtime) +{ + if (m_view_bobbing_state != 0) + { + //f32 offset = dtime * m_view_bobbing_speed * 0.035; + f32 offset = dtime * m_view_bobbing_speed * 0.030; + if (m_view_bobbing_state == 2) + { +#if 0 + // Animation is getting turned off + if (m_view_bobbing_anim < 0.5) + m_view_bobbing_anim -= offset; + else + m_view_bobbing_anim += offset; + if (m_view_bobbing_anim <= 0 || m_view_bobbing_anim >= 1) + { + m_view_bobbing_anim = 0; + m_view_bobbing_state = 0; + } +#endif +#if 1 + // Animation is getting turned off + if(m_view_bobbing_anim < 0.25){ + m_view_bobbing_anim -= offset; + } else if(m_view_bobbing_anim > 0.75){ + m_view_bobbing_anim += offset; + } if(m_view_bobbing_anim < 0.5){ + m_view_bobbing_anim += offset; + if(m_view_bobbing_anim > 0.5) + m_view_bobbing_anim = 0.5; + } else { + m_view_bobbing_anim -= offset; + if(m_view_bobbing_anim < 0.5) + m_view_bobbing_anim = 0.5; + } + if(m_view_bobbing_anim <= 0 || m_view_bobbing_anim >= 1 || + fabs(m_view_bobbing_anim - 0.5) < 0.01) + { + m_view_bobbing_anim = 0; + m_view_bobbing_state = 0; + } +#endif + } + else + { + m_view_bobbing_anim = my_modf(m_view_bobbing_anim + offset); + } + } + + if (m_digging_button != -1) + { + f32 offset = dtime * 3.5; + m_digging_anim += offset; + if (m_digging_anim >= 1) + { + m_digging_anim = 0; + m_digging_button = -1; + } + } +} + +void Camera::update(LocalPlayer* player, f32 frametime, v2u32 screensize) +{ + // Set player node transformation + m_playernode->setPosition(player->getPosition()); + m_playernode->setRotation(v3f(0, -1 * player->getYaw(), 0)); + m_playernode->updateAbsolutePosition(); + + // Set head node transformation + m_headnode->setPosition(player->getEyeOffset()); + m_headnode->setRotation(v3f(player->getPitch(), 0, 0)); + m_headnode->updateAbsolutePosition(); + + // Compute relative camera position and target + v3f rel_cam_pos = v3f(0,0,0); + v3f rel_cam_target = v3f(0,0,1); + v3f rel_cam_up = v3f(0,1,0); + + if (m_view_bobbing_anim != 0) + { + f32 bobfrac = my_modf(m_view_bobbing_anim * 2); + f32 bobdir = (m_view_bobbing_anim < 0.5) ? 1.0 : -1.0; + + #if 1 + f32 bobknob = 1.2; + f32 bobtmp = sin(pow(bobfrac, bobknob) * PI); + //f32 bobtmp2 = cos(pow(bobfrac, bobknob) * PI); + + v3f bobvec = v3f( + 0.3 * bobdir * sin(bobfrac * PI), + -0.28 * bobtmp * bobtmp, + 0.); + + //rel_cam_pos += 0.2 * bobvec; + //rel_cam_target += 0.03 * bobvec; + //rel_cam_up.rotateXYBy(0.02 * bobdir * bobtmp * PI); + float f = 1.0; + rel_cam_pos += bobvec * f; + //rel_cam_target += 0.995 * bobvec * f; + rel_cam_target += bobvec * f; + rel_cam_target.Z -= 0.005 * bobvec.Z * f; + //rel_cam_target.X -= 0.005 * bobvec.X * f; + //rel_cam_target.Y -= 0.005 * bobvec.Y * f; + rel_cam_up.rotateXYBy(-0.03 * bobdir * bobtmp * PI * f); + #else + f32 angle_deg = 1 * bobdir * sin(bobfrac * PI); + f32 angle_rad = angle_deg * PI / 180; + f32 r = 0.05; + v3f off = v3f( + r * sin(angle_rad), + r * (cos(angle_rad) - 1), + 0); + rel_cam_pos += off; + //rel_cam_target += off; + rel_cam_up.rotateXYBy(angle_deg); + #endif + + } + + // Compute absolute camera position and target + m_headnode->getAbsoluteTransformation().transformVect(m_camera_position, rel_cam_pos); + m_headnode->getAbsoluteTransformation().rotateVect(m_camera_direction, rel_cam_target - rel_cam_pos); + + v3f abs_cam_up; + m_headnode->getAbsoluteTransformation().rotateVect(abs_cam_up, rel_cam_up); + + // Set camera node transformation + m_cameranode->setPosition(m_camera_position); + m_cameranode->setUpVector(abs_cam_up); + // *100.0 helps in large map coordinates + m_cameranode->setTarget(m_camera_position + 100 * m_camera_direction); + + // FOV and and aspect ratio + m_aspect = (f32)screensize.X / (f32) screensize.Y; + m_fov_x = 2 * atan(0.5 * m_aspect * tan(m_fov_y)); + m_cameranode->setAspectRatio(m_aspect); + m_cameranode->setFOV(m_fov_y); + // Just so big a value that everything rendered is visible + // Some more allowance that m_viewing_range_max * BS because of active objects etc. + m_cameranode->setFarValue(m_viewing_range_max * BS * 10); + + // Position the wielded item + v3f wield_position = v3f(45, -35, 65); + v3f wield_rotation = v3f(-100, 110, -100); + if (m_digging_button != -1) + { + f32 digfrac = m_digging_anim; + wield_position.X -= 30 * sin(pow(digfrac, 0.8f) * PI); + wield_position.Y += 15 * sin(digfrac * 2 * PI); + wield_position.Z += 5 * digfrac; + + // Euler angles are PURE EVIL, so why not use quaternions? + core::quaternion quat_begin(wield_rotation * core::DEGTORAD); + core::quaternion quat_end(v3f(90, -10, -130) * core::DEGTORAD); + core::quaternion quat_slerp; + quat_slerp.slerp(quat_begin, quat_end, sin(digfrac * PI)); + quat_slerp.toEuler(wield_rotation); + wield_rotation *= core::RADTODEG; + } + else { + f32 bobfrac = my_modf(m_view_bobbing_anim); + wield_position.X -= sin(bobfrac*PI*2.0) * 3.0; + wield_position.Y += sin(my_modf(bobfrac*2.0)*PI) * 3.0; + } + m_wieldnode->setPosition(wield_position); + m_wieldnode->setRotation(wield_rotation); + m_wieldnode->updateLight(player->light); + + // Render distance feedback loop + updateViewingRange(frametime); + + // If the player seems to be walking on solid ground, + // view bobbing is enabled and free_move is off, + // start (or continue) the view bobbing animation. + v3f speed = player->getSpeed(); + if ((hypot(speed.X, speed.Z) > BS) && + (player->touching_ground) && + (g_settings.getBool("view_bobbing") == true) && + (g_settings.getBool("free_move") == false)) + { + // Start animation + m_view_bobbing_state = 1; + m_view_bobbing_speed = MYMIN(speed.getLength(), 40); + } + else if (m_view_bobbing_state == 1) + { + // Stop animation + m_view_bobbing_state = 2; + m_view_bobbing_speed = 60; + } +} + +void Camera::updateViewingRange(f32 frametime_in) +{ + if (m_draw_control.range_all) + return; + + m_added_frametime += frametime_in; + m_added_frames += 1; + + // Actually this counter kind of sucks because frametime is busytime + m_frametime_counter -= frametime_in; + if (m_frametime_counter > 0) + return; + m_frametime_counter = 0.2; + + /*dstream<<__FUNCTION_NAME + <<": Collected "<<m_added_frames<<" frames, total of " + <<m_added_frametime<<"s."<<std::endl; + + dstream<<"m_draw_control.blocks_drawn=" + <<m_draw_control.blocks_drawn + <<", m_draw_control.blocks_would_have_drawn=" + <<m_draw_control.blocks_would_have_drawn + <<std::endl;*/ + + m_draw_control.wanted_min_range = m_viewing_range_min; + m_draw_control.wanted_max_blocks = (1.5*m_draw_control.blocks_would_have_drawn)+1; + if (m_draw_control.wanted_max_blocks < 10) + m_draw_control.wanted_max_blocks = 10; + + f32 block_draw_ratio = 1.0; + if (m_draw_control.blocks_would_have_drawn != 0) + { + block_draw_ratio = (f32)m_draw_control.blocks_drawn + / (f32)m_draw_control.blocks_would_have_drawn; + } + + // Calculate the average frametime in the case that all wanted + // blocks had been drawn + f32 frametime = m_added_frametime / m_added_frames / block_draw_ratio; + + m_added_frametime = 0.0; + m_added_frames = 0; + + f32 wanted_frametime_change = m_wanted_frametime - frametime; + //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl; + + // If needed frametime change is small, just return + if (fabs(wanted_frametime_change) < m_wanted_frametime*0.4) + { + //dstream<<"ignoring small wanted_frametime_change"<<std::endl; + return; + } + + f32 range = m_draw_control.wanted_range; + f32 new_range = range; + + f32 d_range = range - m_range_old; + f32 d_frametime = frametime - m_frametime_old; + if (d_range != 0) + { + m_time_per_range = d_frametime / d_range; + } + + // The minimum allowed calculated frametime-range derivative: + // Practically this sets the maximum speed of changing the range. + // The lower this value, the higher the maximum changing speed. + // A low value here results in wobbly range (0.001) + // A high value here results in slow changing range (0.0025) + // SUGG: This could be dynamically adjusted so that when + // the camera is turning, this is lower + //f32 min_time_per_range = 0.0015; + f32 min_time_per_range = 0.0010; + //f32 min_time_per_range = 0.05 / range; + if(m_time_per_range < min_time_per_range) + { + m_time_per_range = min_time_per_range; + //dstream<<"m_time_per_range="<<m_time_per_range<<" (min)"<<std::endl; + } + else + { + //dstream<<"m_time_per_range="<<m_time_per_range<<std::endl; + } + + f32 wanted_range_change = wanted_frametime_change / m_time_per_range; + // Dampen the change a bit to kill oscillations + //wanted_range_change *= 0.9; + //wanted_range_change *= 0.75; + wanted_range_change *= 0.5; + //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl; + + // If needed range change is very small, just return + if(fabs(wanted_range_change) < 0.001) + { + //dstream<<"ignoring small wanted_range_change"<<std::endl; + return; + } + + new_range += wanted_range_change; + + //f32 new_range_unclamped = new_range; + new_range = MYMAX(new_range, m_viewing_range_min); + new_range = MYMIN(new_range, m_viewing_range_max); + /*dstream<<"new_range="<<new_range_unclamped + <<", clamped to "<<new_range<<std::endl;*/ + + m_draw_control.wanted_range = new_range; + + m_range_old = new_range; + m_frametime_old = frametime; +} + +void Camera::updateSettings() +{ + m_viewing_range_min = g_settings.getS16("viewing_range_nodes_min"); + m_viewing_range_min = MYMAX(5.0, m_viewing_range_min); + + m_viewing_range_max = g_settings.getS16("viewing_range_nodes_max"); + m_viewing_range_max = MYMAX(m_viewing_range_min, m_viewing_range_max); + + f32 fov_degrees = g_settings.getFloat("fov"); + fov_degrees = MYMAX(fov_degrees, 10.0); + fov_degrees = MYMIN(fov_degrees, 170.0); + m_fov_y = fov_degrees * PI / 180.0; + + f32 wanted_fps = g_settings.getFloat("wanted_fps"); + wanted_fps = MYMAX(wanted_fps, 1.0); + m_wanted_frametime = 1.0 / wanted_fps; +} + +void Camera::wield(const InventoryItem* item) +{ + if (item != NULL) + { + bool isCube = false; + + // Try to make a MaterialItem cube. + if (std::string(item->getName()) == "MaterialItem") + { + // A block-type material + MaterialItem* mat_item = (MaterialItem*) item; + content_t content = mat_item->getMaterial(); + if (content_features(content).solidness || content_features(content).visual_solidness) + { + m_wieldnode->setCube(content_features(content).tiles); + m_wieldnode->setScale(v3f(30)); + isCube = true; + } + } + + // If that failed, make an extruded sprite. + if (!isCube) + { + m_wieldnode->setSprite(item->getImageRaw()); + m_wieldnode->setScale(v3f(40)); + } + + m_wieldnode->setVisible(true); + } + else + { + // Bare hands + m_wieldnode->setVisible(false); + } +} + +void Camera::setDigging(s32 button) +{ + if (m_digging_button == -1) + m_digging_button = button; +} + +void Camera::drawWieldedTool() +{ + m_wieldmgr->getVideoDriver()->clearZBuffer(); + + scene::ICameraSceneNode* cam = m_wieldmgr->getActiveCamera(); + cam->setAspectRatio(m_cameranode->getAspectRatio()); + cam->setFOV(m_cameranode->getFOV()); + cam->setNearValue(0.1); + cam->setFarValue(100); + m_wieldmgr->drawAll(); +} + + +ExtrudedSpriteSceneNode::ExtrudedSpriteSceneNode( + scene::ISceneNode* parent, + scene::ISceneManager* mgr, + s32 id, + const v3f& position, + const v3f& rotation, + const v3f& scale +): + ISceneNode(parent, mgr, id, position, rotation, scale) +{ + m_meshnode = mgr->addMeshSceneNode(NULL, this, -1, v3f(0,0,0), v3f(0,0,0), v3f(1,1,1), true); + m_thickness = 0.1; + m_cubemesh = NULL; + m_is_cube = false; + m_light = LIGHT_MAX; +} + +ExtrudedSpriteSceneNode::~ExtrudedSpriteSceneNode() +{ + removeChild(m_meshnode); + if (m_cubemesh) + m_cubemesh->drop(); +} + +void ExtrudedSpriteSceneNode::setSprite(video::ITexture* texture) +{ + if (texture == NULL) + { + m_meshnode->setVisible(false); + return; + } + + io::path name = getExtrudedName(texture); + scene::IMeshCache* cache = SceneManager->getMeshCache(); + scene::IAnimatedMesh* mesh = cache->getMeshByName(name); + if (mesh != NULL) + { + // Extruded texture has been found in cache. + m_meshnode->setMesh(mesh); + } + else + { + // Texture was not yet extruded, do it now and save in cache + mesh = extrude(texture); + if (mesh == NULL) + { + dstream << "Warning: failed to extrude sprite" << std::endl; + m_meshnode->setVisible(false); + return; + } + cache->addMesh(name, mesh); + m_meshnode->setMesh(mesh); + mesh->drop(); + } + + m_meshnode->setScale(v3f(1, 1, m_thickness)); + m_meshnode->getMaterial(0).setTexture(0, texture); + m_meshnode->getMaterial(0).setFlag(video::EMF_LIGHTING, false); + m_meshnode->getMaterial(0).setFlag(video::EMF_BILINEAR_FILTER, false); + m_meshnode->getMaterial(0).MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; + m_meshnode->setVisible(true); + m_is_cube = false; + updateLight(m_light); +} + +void ExtrudedSpriteSceneNode::setCube(const TileSpec tiles[6]) +{ + if (m_cubemesh == NULL) + m_cubemesh = createCubeMesh(); + + m_meshnode->setMesh(m_cubemesh); + m_meshnode->setScale(v3f(1)); + for (int i = 0; i < 6; ++i) + { + // Get the tile texture and atlas transformation + video::ITexture* atlas = tiles[i].texture.atlas; + v2f pos = tiles[i].texture.pos; + v2f size = tiles[i].texture.size; + + // Set material flags and texture + video::SMaterial& material = m_meshnode->getMaterial(i); + material.setFlag(video::EMF_LIGHTING, false); + material.setFlag(video::EMF_BILINEAR_FILTER, false); + tiles[i].applyMaterialOptions(material); + material.setTexture(0, atlas); + material.getTextureMatrix(0).setTextureTranslate(pos.X, pos.Y); + material.getTextureMatrix(0).setTextureScale(size.X, size.Y); + } + m_meshnode->setVisible(true); + m_is_cube = true; + updateLight(m_light); +} + +void ExtrudedSpriteSceneNode::updateLight(u8 light) +{ + m_light = light; + + u8 li = decode_light(light); + // Set brightness one lower than incoming light + diminish_light(li); + video::SColor color(255,li,li,li); + setMeshVerticesColor(m_meshnode->getMesh(), color); +} + +void ExtrudedSpriteSceneNode::removeSpriteFromCache(video::ITexture* texture) +{ + scene::IMeshCache* cache = SceneManager->getMeshCache(); + scene::IAnimatedMesh* mesh = cache->getMeshByName(getExtrudedName(texture)); + if (mesh != NULL) + cache->removeMesh(mesh); +} + +void ExtrudedSpriteSceneNode::setSpriteThickness(f32 thickness) +{ + m_thickness = thickness; + if (!m_is_cube) + m_meshnode->setScale(v3f(1, 1, thickness)); +} + +const core::aabbox3d<f32>& ExtrudedSpriteSceneNode::getBoundingBox() const +{ + return m_meshnode->getBoundingBox(); +} + +void ExtrudedSpriteSceneNode::OnRegisterSceneNode() +{ + if (IsVisible) + SceneManager->registerNodeForRendering(this); + ISceneNode::OnRegisterSceneNode(); +} + +void ExtrudedSpriteSceneNode::render() +{ + // do nothing +} + +io::path ExtrudedSpriteSceneNode::getExtrudedName(video::ITexture* texture) +{ + io::path path = texture->getName(); + path.append("/[extruded]"); + return path; +} + +scene::IAnimatedMesh* ExtrudedSpriteSceneNode::extrudeARGB(u32 width, u32 height, u8* data) +{ + const s32 argb_wstep = 4 * width; + const s32 alpha_threshold = 1; + + scene::IMeshBuffer* buf = new scene::SMeshBuffer(); + video::SColor c(255,255,255,255); + + // Front and back + { + video::S3DVertex vertices[8] = + { + video::S3DVertex(-0.5,-0.5,-0.5, 0,0,-1, c, 0,1), + video::S3DVertex(-0.5,+0.5,-0.5, 0,0,-1, c, 0,0), + video::S3DVertex(+0.5,+0.5,-0.5, 0,0,-1, c, 1,0), + video::S3DVertex(+0.5,-0.5,-0.5, 0,0,-1, c, 1,1), + video::S3DVertex(+0.5,-0.5,+0.5, 0,0,+1, c, 1,1), + video::S3DVertex(+0.5,+0.5,+0.5, 0,0,+1, c, 1,0), + video::S3DVertex(-0.5,+0.5,+0.5, 0,0,+1, c, 0,0), + video::S3DVertex(-0.5,-0.5,+0.5, 0,0,+1, c, 0,1), + }; + u16 indices[12] = {0,1,2,2,3,0,4,5,6,6,7,4}; + buf->append(vertices, 8, indices, 12); + } + + // "Interior" + // (add faces where a solid pixel is next to a transparent one) + u8* solidity = new u8[(width+2) * (height+2)]; + u32 wstep = width + 2; + for (u32 y = 0; y < height + 2; ++y) + { + u8* scanline = solidity + y * wstep; + if (y == 0 || y == height + 1) + { + for (u32 x = 0; x < width + 2; ++x) + scanline[x] = 0; + } + else + { + scanline[0] = 0; + u8* argb_scanline = data + (y - 1) * argb_wstep; + for (u32 x = 0; x < width; ++x) + scanline[x+1] = (argb_scanline[x*4+3] >= alpha_threshold); + scanline[width + 1] = 0; + } + } + + // without this, there would be occasional "holes" in the mesh + f32 eps = 0.01; + + for (u32 y = 0; y <= height; ++y) + { + u8* scanline = solidity + y * wstep + 1; + for (u32 x = 0; x <= width; ++x) + { + if (scanline[x] && !scanline[x + wstep]) + { + u32 xx = x + 1; + while (scanline[xx] && !scanline[xx + wstep]) + ++xx; + f32 vx1 = (x - eps) / (f32) width - 0.5; + f32 vx2 = (xx + eps) / (f32) width - 0.5; + f32 vy = 0.5 - (y - eps) / (f32) height; + f32 tx1 = x / (f32) width; + f32 tx2 = xx / (f32) width; + f32 ty = (y - 0.5) / (f32) height; + video::S3DVertex vertices[8] = + { + video::S3DVertex(vx1,vy,-0.5, 0,-1,0, c, tx1,ty), + video::S3DVertex(vx2,vy,-0.5, 0,-1,0, c, tx2,ty), + video::S3DVertex(vx2,vy,+0.5, 0,-1,0, c, tx2,ty), + video::S3DVertex(vx1,vy,+0.5, 0,-1,0, c, tx1,ty), + }; + u16 indices[6] = {0,1,2,2,3,0}; + buf->append(vertices, 4, indices, 6); + x = xx - 1; + } + if (!scanline[x] && scanline[x + wstep]) + { + u32 xx = x + 1; + while (!scanline[xx] && scanline[xx + wstep]) + ++xx; + f32 vx1 = (x - eps) / (f32) width - 0.5; + f32 vx2 = (xx + eps) / (f32) width - 0.5; + f32 vy = 0.5 - (y + eps) / (f32) height; + f32 tx1 = x / (f32) width; + f32 tx2 = xx / (f32) width; + f32 ty = (y + 0.5) / (f32) height; + video::S3DVertex vertices[8] = + { + video::S3DVertex(vx1,vy,-0.5, 0,1,0, c, tx1,ty), + video::S3DVertex(vx1,vy,+0.5, 0,1,0, c, tx1,ty), + video::S3DVertex(vx2,vy,+0.5, 0,1,0, c, tx2,ty), + video::S3DVertex(vx2,vy,-0.5, 0,1,0, c, tx2,ty), + }; + u16 indices[6] = {0,1,2,2,3,0}; + buf->append(vertices, 4, indices, 6); + x = xx - 1; + } + } + } + + for (u32 x = 0; x <= width; ++x) + { + u8* scancol = solidity + x + wstep; + for (u32 y = 0; y <= height; ++y) + { + if (scancol[y * wstep] && !scancol[y * wstep + 1]) + { + u32 yy = y + 1; + while (scancol[yy * wstep] && !scancol[yy * wstep + 1]) + ++yy; + f32 vx = (x - eps) / (f32) width - 0.5; + f32 vy1 = 0.5 - (y - eps) / (f32) height; + f32 vy2 = 0.5 - (yy + eps) / (f32) height; + f32 tx = (x - 0.5) / (f32) width; + f32 ty1 = y / (f32) height; + f32 ty2 = yy / (f32) height; + video::S3DVertex vertices[8] = + { + video::S3DVertex(vx,vy1,-0.5, 1,0,0, c, tx,ty1), + video::S3DVertex(vx,vy1,+0.5, 1,0,0, c, tx,ty1), + video::S3DVertex(vx,vy2,+0.5, 1,0,0, c, tx,ty2), + video::S3DVertex(vx,vy2,-0.5, 1,0,0, c, tx,ty2), + }; + u16 indices[6] = {0,1,2,2,3,0}; + buf->append(vertices, 4, indices, 6); + y = yy - 1; + } + if (!scancol[y * wstep] && scancol[y * wstep + 1]) + { + u32 yy = y + 1; + while (!scancol[yy * wstep] && scancol[yy * wstep + 1]) + ++yy; + f32 vx = (x + eps) / (f32) width - 0.5; + f32 vy1 = 0.5 - (y - eps) / (f32) height; + f32 vy2 = 0.5 - (yy + eps) / (f32) height; + f32 tx = (x + 0.5) / (f32) width; + f32 ty1 = y / (f32) height; + f32 ty2 = yy / (f32) height; + video::S3DVertex vertices[8] = + { + video::S3DVertex(vx,vy1,-0.5, -1,0,0, c, tx,ty1), + video::S3DVertex(vx,vy2,-0.5, -1,0,0, c, tx,ty2), + video::S3DVertex(vx,vy2,+0.5, -1,0,0, c, tx,ty2), + video::S3DVertex(vx,vy1,+0.5, -1,0,0, c, tx,ty1), + }; + u16 indices[6] = {0,1,2,2,3,0}; + buf->append(vertices, 4, indices, 6); + y = yy - 1; + } + } + } + + // Add to mesh + scene::SMesh* mesh = new scene::SMesh(); + buf->recalculateBoundingBox(); + mesh->addMeshBuffer(buf); + buf->drop(); + mesh->recalculateBoundingBox(); + scene::SAnimatedMesh* anim_mesh = new scene::SAnimatedMesh(mesh); + mesh->drop(); + return anim_mesh; +} + +scene::IAnimatedMesh* ExtrudedSpriteSceneNode::extrude(video::ITexture* texture) +{ + scene::IAnimatedMesh* mesh = NULL; + core::dimension2d<u32> size = texture->getSize(); + video::ECOLOR_FORMAT format = texture->getColorFormat(); + if (format == video::ECF_A8R8G8B8) + { + // Texture is in the correct color format, we can pass it + // to extrudeARGB right away. + void* data = texture->lock(true); + if (data == NULL) + return NULL; + mesh = extrudeARGB(size.Width, size.Height, (u8*) data); + texture->unlock(); + } + else + { + video::IVideoDriver* driver = SceneManager->getVideoDriver(); + + video::IImage* img1 = driver->createImageFromData(format, size, texture->lock(true)); + if (img1 == NULL) + return NULL; + + // img1 is in the texture's color format, convert to 8-bit ARGB + video::IImage* img2 = driver->createImage(video::ECF_A8R8G8B8, size); + if (img2 != NULL) + { + img1->copyTo(img2); + img1->drop(); + + mesh = extrudeARGB(size.Width, size.Height, (u8*) img2->lock()); + img2->unlock(); + img2->drop(); + } + img1->drop(); + } + return mesh; +} + +scene::IMesh* ExtrudedSpriteSceneNode::createCubeMesh() +{ + video::SColor c(255,255,255,255); + video::S3DVertex vertices[24] = + { + // Up + video::S3DVertex(-0.5,+0.5,-0.5, 0,1,0, c, 0,1), + video::S3DVertex(-0.5,+0.5,+0.5, 0,1,0, c, 0,0), + video::S3DVertex(+0.5,+0.5,+0.5, 0,1,0, c, 1,0), + video::S3DVertex(+0.5,+0.5,-0.5, 0,1,0, c, 1,1), + // Down + video::S3DVertex(-0.5,-0.5,-0.5, 0,-1,0, c, 0,0), + video::S3DVertex(+0.5,-0.5,-0.5, 0,-1,0, c, 1,0), + video::S3DVertex(+0.5,-0.5,+0.5, 0,-1,0, c, 1,1), + video::S3DVertex(-0.5,-0.5,+0.5, 0,-1,0, c, 0,1), + // Right + video::S3DVertex(+0.5,-0.5,-0.5, 1,0,0, c, 0,1), + video::S3DVertex(+0.5,+0.5,-0.5, 1,0,0, c, 0,0), + video::S3DVertex(+0.5,+0.5,+0.5, 1,0,0, c, 1,0), + video::S3DVertex(+0.5,-0.5,+0.5, 1,0,0, c, 1,1), + // Left + video::S3DVertex(-0.5,-0.5,-0.5, -1,0,0, c, 1,1), + video::S3DVertex(-0.5,-0.5,+0.5, -1,0,0, c, 0,1), + video::S3DVertex(-0.5,+0.5,+0.5, -1,0,0, c, 0,0), + video::S3DVertex(-0.5,+0.5,-0.5, -1,0,0, c, 1,0), + // Back + video::S3DVertex(-0.5,-0.5,+0.5, 0,0,1, c, 1,1), + video::S3DVertex(+0.5,-0.5,+0.5, 0,0,1, c, 0,1), + video::S3DVertex(+0.5,+0.5,+0.5, 0,0,1, c, 0,0), + video::S3DVertex(-0.5,+0.5,+0.5, 0,0,1, c, 1,0), + // Front + video::S3DVertex(-0.5,-0.5,-0.5, 0,0,-1, c, 0,1), + video::S3DVertex(-0.5,+0.5,-0.5, 0,0,-1, c, 0,0), + video::S3DVertex(+0.5,+0.5,-0.5, 0,0,-1, c, 1,0), + video::S3DVertex(+0.5,-0.5,-0.5, 0,0,-1, c, 1,1), + }; + + u16 indices[6] = {0,1,2,2,3,0}; + + scene::SMesh* mesh = new scene::SMesh(); + for (u32 i=0; i<6; ++i) + { + scene::IMeshBuffer* buf = new scene::SMeshBuffer(); + buf->append(vertices + 4 * i, 4, indices, 6); + buf->recalculateBoundingBox(); + mesh->addMeshBuffer(buf); + buf->drop(); + } + mesh->recalculateBoundingBox(); + return mesh; +} diff --git a/src/camera.h b/src/camera.h new file mode 100644 index 000000000..66825e372 --- /dev/null +++ b/src/camera.h @@ -0,0 +1,230 @@ +/* +Minetest-c55 +Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com> + +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. +*/ + +#ifndef CAMERA_HEADER +#define CAMERA_HEADER + +#include "common_irrlicht.h" +#include "inventory.h" +#include "tile.h" +#include "utility.h" + +class LocalPlayer; +class MapDrawControl; +class ExtrudedSpriteSceneNode; + +/* + Client camera class, manages the player and camera scene nodes, the viewing distance + and performs view bobbing etc. It also displays the wielded tool in front of the + first-person camera. +*/ +class Camera +{ +public: + Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control); + ~Camera(); + + // Get player scene node. + // This node is positioned at the player's torso (without any view bobbing), + // as given by Player::m_position. Yaw is applied but not pitch. + inline scene::ISceneNode* getPlayerNode() const + { + return m_playernode; + } + + // Get head scene node. + // It has the eye transformation and pitch applied, + // but no view bobbing. + inline scene::ISceneNode* getHeadNode() const + { + return m_headnode; + } + + // Get camera scene node. + // It has the eye transformation, pitch and view bobbing applied. + inline scene::ICameraSceneNode* getCameraNode() const + { + return m_cameranode; + } + + // Get the camera position (in absolute scene coordinates). + // This has view bobbing applied. + inline v3f getPosition() const + { + return m_camera_position; + } + + // Get the camera direction (in absolute camera coordinates). + // This has view bobbing applied. + inline v3f getDirection() const + { + return m_camera_direction; + } + + // Horizontal field of view + inline f32 getFovX() const + { + return m_fov_x; + } + + // Vertical field of view + inline f32 getFovY() const + { + return m_fov_y; + } + + // Get maximum of getFovX() and getFovY() + inline f32 getFovMax() const + { + return MYMAX(m_fov_x, m_fov_y); + } + + // Checks if the constructor was able to create the scene nodes + bool successfullyCreated(std::wstring& error_message); + + // Step the camera: updates the viewing range and view bobbing. + void step(f32 dtime); + + // Update the camera from the local player's position. + // frametime is used to adjust the viewing range. + void update(LocalPlayer* player, f32 frametime, v2u32 screensize); + + // Render distance feedback loop + void updateViewingRange(f32 frametime_in); + + // Update settings from g_settings + void updateSettings(); + + // Replace the wielded item mesh + void wield(const InventoryItem* item); + + // Start digging animation + // Pass 0 for left click, 1 for right click + void setDigging(s32 button); + + // Draw the wielded tool. + // This has to happen *after* the main scene is drawn. + // Warning: This clears the Z buffer. + void drawWieldedTool(); + +private: + // Scene manager and nodes + scene::ISceneManager* m_smgr; + scene::ISceneNode* m_playernode; + scene::ISceneNode* m_headnode; + scene::ICameraSceneNode* m_cameranode; + + scene::ISceneManager* m_wieldmgr; + ExtrudedSpriteSceneNode* m_wieldnode; + + // draw control + MapDrawControl& m_draw_control; + + // viewing_range_min_nodes setting + f32 m_viewing_range_min; + // viewing_range_max_nodes setting + f32 m_viewing_range_max; + + // Absolute camera position + v3f m_camera_position; + // Absolute camera direction + v3f m_camera_direction; + + // Field of view and aspect ratio stuff + f32 m_aspect; + f32 m_fov_x; + f32 m_fov_y; + + // Stuff for viewing range calculations + f32 m_wanted_frametime; + f32 m_added_frametime; + s16 m_added_frames; + f32 m_range_old; + f32 m_frametime_old; + f32 m_frametime_counter; + f32 m_time_per_range; + + // View bobbing animation frame (0 <= m_view_bobbing_anim < 1) + f32 m_view_bobbing_anim; + // If 0, view bobbing is off (e.g. player is standing). + // If 1, view bobbing is on (player is walking). + // If 2, view bobbing is getting switched off. + s32 m_view_bobbing_state; + // Speed of view bobbing animation + f32 m_view_bobbing_speed; + + // Digging animation frame (0 <= m_digging_anim < 1) + f32 m_digging_anim; + // If -1, no digging animation + // If 0, left-click digging animation + // If 1, right-click digging animation + s32 m_digging_button; +}; + + +/* + A scene node that displays a 2D mesh extruded into the third dimension, + to add an illusion of depth. + + Since this class was created to display the wielded tool of the local + player, and only tools and items are rendered like this (but not solid + content like stone and mud, which are shown as cubes), the option to + draw a textured cube instead is provided. + */ +class ExtrudedSpriteSceneNode: public scene::ISceneNode +{ +public: + ExtrudedSpriteSceneNode( + scene::ISceneNode* parent, + scene::ISceneManager* mgr, + s32 id = -1, + const v3f& position = v3f(0,0,0), + const v3f& rotation = v3f(0,0,0), + const v3f& scale = v3f(1,1,1)); + ~ExtrudedSpriteSceneNode(); + + void setSprite(video::ITexture* texture); + void setCube(const TileSpec tiles[6]); + + f32 getSpriteThickness() const { return m_thickness; } + void setSpriteThickness(f32 thickness); + + void updateLight(u8 light); + + void removeSpriteFromCache(video::ITexture* texture); + + virtual const core::aabbox3d<f32>& getBoundingBox() const; + virtual void OnRegisterSceneNode(); + virtual void render(); + +private: + scene::IMeshSceneNode* m_meshnode; + f32 m_thickness; + scene::IMesh* m_cubemesh; + bool m_is_cube; + u8 m_light; + + // internal extrusion helper methods + io::path getExtrudedName(video::ITexture* texture); + scene::IAnimatedMesh* extrudeARGB(u32 width, u32 height, u8* data); + scene::IAnimatedMesh* extrude(video::ITexture* texture); + scene::IMesh* createCubeMesh(); +}; + +#endif diff --git a/src/client.cpp b/src/client.cpp index fcc5a0a1e..fa600719e 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -189,8 +189,6 @@ Client::Client( ), m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this), m_device(device), - camera_position(0,0,0), - camera_direction(0,0,1), m_server_ser_ver(SER_FMT_VER_INVALID), m_inventory_updated(false), m_time_of_day(0), @@ -1963,7 +1961,8 @@ void Client::addNode(v3s16 p, MapNode n) try { //TimeTaker timer3("Client::addNode(): addNodeAndUpdate"); - m_env.getMap().addNodeAndUpdate(p, n, modified_blocks); + std::string st = std::string(""); + m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, st); } catch(InvalidPositionException &e) {} @@ -1980,11 +1979,14 @@ void Client::addNode(v3s16 p, MapNode n) } } -void Client::updateCamera(v3f pos, v3f dir) +void Client::updateCamera(v3f pos, v3f dir, f32 fov) { - m_env.getClientMap().updateCamera(pos, dir); - camera_position = pos; - camera_direction = dir; + m_env.getClientMap().updateCamera(pos, dir, fov); +} + +void Client::renderPostFx() +{ + m_env.getClientMap().renderPostFx(); } MapNode Client::getNode(v3s16 p) @@ -1998,14 +2000,9 @@ NodeMetadata* Client::getNodeMetadata(v3s16 p) return m_env.getMap().getNodeMetadata(p); } -v3f Client::getPlayerPosition(v3f *eye_position) +LocalPlayer* Client::getLocalPlayer() { - //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out - LocalPlayer *player = m_env.getLocalPlayer(); - assert(player != NULL); - if (eye_position) - *eye_position = player->getEyePosition(); - return player->getPosition(); + return m_env.getLocalPlayer(); } void Client::setPlayerControl(PlayerControl &control) diff --git a/src/client.h b/src/client.h index 1a7ef924a..930987c02 100644 --- a/src/client.h +++ b/src/client.h @@ -199,16 +199,16 @@ public: // locks envlock void addNode(v3s16 p, MapNode n); - void updateCamera(v3f pos, v3f dir); + void updateCamera(v3f pos, v3f dir, f32 fov); + + void renderPostFx(); // Returns InvalidPositionException if not found MapNode getNode(v3s16 p); // Wrapper to Map NodeMetadata* getNodeMetadata(v3s16 p); - // Get the player position, and optionally put the - // eye position in *eye_position - v3f getPlayerPosition(v3f *eye_position=NULL); + LocalPlayer* getLocalPlayer(); void setPlayerControl(PlayerControl &control); @@ -302,15 +302,6 @@ public: { return m_access_denied_reason; } - - /* - This should only be used for calling the special drawing stuff in - ClientEnvironment - */ - ClientEnvironment * getEnv() - { - return &m_env; - } private: @@ -342,9 +333,6 @@ private: IrrlichtDevice *m_device; - v3f camera_position; - v3f camera_direction; - // Server serialization version u8 m_server_ser_ver; diff --git a/src/constants.h b/src/constants.h index 1af5f1f1b..9ba10b51c 100644 --- a/src/constants.h +++ b/src/constants.h @@ -44,9 +44,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #define PI 3.14159 -// This is the same as in minecraft and everything else -#define FOV_ANGLE (PI/2.5) - // The absolute working limit is (2^15 - viewing_range). // I really don't want to make every algorithm to check if it's // going near the limit or not, so this is lower. diff --git a/src/content_cao.cpp b/src/content_cao.cpp index dfeaea85a..b6543061a 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -224,22 +224,7 @@ void ItemCAO::updateLight(u8 light_at_pos) u8 li = decode_light(light_at_pos); video::SColor color(255,li,li,li); - - scene::IMesh *mesh = m_node->getMesh(); - if(mesh == NULL) - return; - - u16 mc = mesh->getMeshBufferCount(); - for(u16 j=0; j<mc; j++) - { - scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); - video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices(); - u16 vc = buf->getVertexCount(); - for(u16 i=0; i<vc; i++) - { - vertices[i].Color = color; - } - } + setMeshVerticesColor(m_node->getMesh(), color); } v3s16 ItemCAO::getLightPosition() @@ -430,22 +415,7 @@ void RatCAO::updateLight(u8 light_at_pos) u8 li = decode_light(light_at_pos); video::SColor color(255,li,li,li); - - scene::IMesh *mesh = m_node->getMesh(); - if(mesh == NULL) - return; - - u16 mc = mesh->getMeshBufferCount(); - for(u16 j=0; j<mc; j++) - { - scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); - video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices(); - u16 vc = buf->getVertexCount(); - for(u16 i=0; i<vc; i++) - { - vertices[i].Color = color; - } - } + setMeshVerticesColor(m_node->getMesh(), color); } v3s16 RatCAO::getLightPosition() @@ -601,22 +571,7 @@ void Oerkki1CAO::updateLight(u8 light_at_pos) u8 li = decode_light(light_at_pos); video::SColor color(255,li,li,li); - - scene::IMesh *mesh = m_node->getMesh(); - if(mesh == NULL) - return; - - u16 mc = mesh->getMeshBufferCount(); - for(u16 j=0; j<mc; j++) - { - scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); - video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices(); - u16 vc = buf->getVertexCount(); - for(u16 i=0; i<vc; i++) - { - vertices[i].Color = color; - } - } + setMeshVerticesColor(m_node->getMesh(), color); } v3s16 Oerkki1CAO::getLightPosition() @@ -833,22 +788,7 @@ void FireflyCAO::updateLight(u8 light_at_pos) u8 li = 255; video::SColor color(255,li,li,li); - - scene::IMesh *mesh = m_node->getMesh(); - if(mesh == NULL) - return; - - u16 mc = mesh->getMeshBufferCount(); - for(u16 j=0; j<mc; j++) - { - scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); - video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices(); - u16 vc = buf->getVertexCount(); - for(u16 i=0; i<vc; i++) - { - vertices[i].Color = color; - } - } + setMeshVerticesColor(m_node->getMesh(), color); } v3s16 FireflyCAO::getLightPosition() diff --git a/src/content_craft.cpp b/src/content_craft.cpp index 8e8b17a9b..866c1e532 100644 --- a/src/content_craft.cpp +++ b/src/content_craft.cpp @@ -297,6 +297,24 @@ InventoryItem *craft_get_result(InventoryItem **items) } } + // Locking Chest + { + ItemSpec specs[9]; + specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot"); + specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + if(checkItemCombination(items, specs)) + { + return new MaterialItem(CONTENT_LOCKABLE_CHEST, 1); + } + } + // Furnace { ItemSpec specs[9]; diff --git a/src/content_mapnode.cpp b/src/content_mapnode.cpp index 09a84156a..a573ca141 100644 --- a/src/content_mapnode.cpp +++ b/src/content_mapnode.cpp @@ -236,6 +236,8 @@ void content_mapnode_init() if(new_style_leaves) { f->solidness = 0; // drawn separately, makes no faces + f->visual_solidness = 1; + f->setAllTextures("leaves.png"); f->setInventoryTextureCube("leaves.png", "leaves.png", "leaves.png"); } else @@ -287,6 +289,8 @@ void content_mapnode_init() f->is_ground_content = true; f->dug_item = std::string("MaterialItem2 ")+itos(i)+" 1"; f->solidness = 0; // drawn separately, makes no faces + f->visual_solidness = 1; + f->setAllTextures("glass.png"); f->setInventoryTextureCube("glass.png", "glass.png", "glass.png"); setWoodLikeDiggingProperties(f->digging_properties, 0.15); @@ -385,6 +389,7 @@ void content_mapnode_init() f->liquid_alternative_source = CONTENT_WATERSOURCE; f->liquid_viscosity = WATER_VISC; f->vertex_alpha = WATER_ALPHA; + f->post_effect_color = video::SColor(64, 100, 100, 200); if(f->special_material == NULL && g_texturesource) { // Flowing water material @@ -433,6 +438,7 @@ void content_mapnode_init() f->liquid_alternative_source = CONTENT_WATERSOURCE; f->liquid_viscosity = WATER_VISC; f->vertex_alpha = WATER_ALPHA; + f->post_effect_color = video::SColor(64, 100, 100, 200); if(f->special_material == NULL && g_texturesource) { // Flowing water material @@ -465,6 +471,7 @@ void content_mapnode_init() f->liquid_alternative_source = CONTENT_LAVASOURCE; f->liquid_viscosity = LAVA_VISC; f->damage_per_second = 4*2; + f->post_effect_color = video::SColor(192, 255, 64, 0); if(f->special_material == NULL && g_texturesource) { // Flowing lava material @@ -514,6 +521,7 @@ void content_mapnode_init() f->liquid_alternative_source = CONTENT_LAVASOURCE; f->liquid_viscosity = LAVA_VISC; f->damage_per_second = 4*2; + f->post_effect_color = video::SColor(192, 255, 64, 0); if(f->special_material == NULL && g_texturesource) { // Flowing lava material @@ -573,6 +581,20 @@ void content_mapnode_init() f->initial_metadata = new ChestNodeMetadata(); setWoodLikeDiggingProperties(f->digging_properties, 1.0); + i = CONTENT_LOCKABLE_CHEST; + f = &content_features(i); + f->param_type = CPT_FACEDIR_SIMPLE; + f->setAllTextures("chest_side.png"); + f->setTexture(0, "chest_top.png"); + f->setTexture(1, "chest_top.png"); + f->setTexture(5, "chest_lock.png"); // Z- + f->setInventoryTexture("chest_lock.png"); + //f->setInventoryTextureCube("chest_top.png", "chest_side.png", "chest_side.png"); + f->dug_item = std::string("MaterialItem2 ")+itos(i)+" 1"; + if(f->initial_metadata == NULL) + f->initial_metadata = new LockingChestNodeMetadata(); + setWoodLikeDiggingProperties(f->digging_properties, 1.0); + i = CONTENT_FURNACE; f = &content_features(i); f->param_type = CPT_FACEDIR_SIMPLE; diff --git a/src/content_mapnode.h b/src/content_mapnode.h index 35dcc20ec..366c9b1a0 100644 --- a/src/content_mapnode.h +++ b/src/content_mapnode.h @@ -48,6 +48,7 @@ MapNode mapnode_translate_to_internal(MapNode n_from, u8 version); #define CONTENT_SIGN_WALL 14 #define CONTENT_CHEST 15 #define CONTENT_FURNACE 16 +#define CONTENT_LOCKABLE_CHEST 17 #define CONTENT_FENCE 21 diff --git a/src/content_nodemeta.cpp b/src/content_nodemeta.cpp index 1552c8e15..e79ff6d54 100644 --- a/src/content_nodemeta.cpp +++ b/src/content_nodemeta.cpp @@ -118,6 +118,70 @@ std::string ChestNodeMetadata::getInventoryDrawSpecString() } /* + LockingChestNodeMetadata +*/ + +// Prototype +LockingChestNodeMetadata proto_LockingChestNodeMetadata; + +LockingChestNodeMetadata::LockingChestNodeMetadata() +{ + NodeMetadata::registerType(typeId(), create); + + m_inventory = new Inventory(); + m_inventory->addList("0", 8*4); +} +LockingChestNodeMetadata::~LockingChestNodeMetadata() +{ + delete m_inventory; +} +u16 LockingChestNodeMetadata::typeId() const +{ + return CONTENT_LOCKABLE_CHEST; +} +NodeMetadata* LockingChestNodeMetadata::create(std::istream &is) +{ + LockingChestNodeMetadata *d = new LockingChestNodeMetadata(); + d->setOwner(deSerializeString(is)); + d->m_inventory->deSerialize(is); + return d; +} +NodeMetadata* LockingChestNodeMetadata::clone() +{ + LockingChestNodeMetadata *d = new LockingChestNodeMetadata(); + *d->m_inventory = *m_inventory; + return d; +} +void LockingChestNodeMetadata::serializeBody(std::ostream &os) +{ + os<<serializeString(m_text); + m_inventory->serialize(os); +} +std::string LockingChestNodeMetadata::infoText() +{ + return "Locking Chest"; +} +bool LockingChestNodeMetadata::nodeRemovalDisabled() +{ + /* + Disable removal if chest contains something + */ + InventoryList *list = m_inventory->getList("0"); + if(list == NULL) + return false; + if(list->getUsedSlots() == 0) + return false; + return true; +} +std::string LockingChestNodeMetadata::getInventoryDrawSpecString() +{ + return + "invsize[8,9;]" + "list[current_name;0;0,0;8,4;]" + "list[current_player;main;0,5;8,4;]"; +} + +/* FurnaceNodeMetadata */ diff --git a/src/content_nodemeta.h b/src/content_nodemeta.h index 50decd910..e20334312 100644 --- a/src/content_nodemeta.h +++ b/src/content_nodemeta.h @@ -62,6 +62,29 @@ private: Inventory *m_inventory; }; +class LockingChestNodeMetadata : public NodeMetadata +{ +public: + LockingChestNodeMetadata(); + ~LockingChestNodeMetadata(); + + virtual u16 typeId() const; + static NodeMetadata* create(std::istream &is); + virtual NodeMetadata* clone(); + virtual void serializeBody(std::ostream &os); + virtual std::string infoText(); + virtual Inventory* getInventory() {return m_inventory;} + virtual bool nodeRemovalDisabled(); + virtual std::string getInventoryDrawSpecString(); + + virtual std::string getOwner(){ return m_text; } + virtual void setOwner(std::string t){ m_text = t; } + +private: + Inventory *m_inventory; + std::string m_text; +}; + class FurnaceNodeMetadata : public NodeMetadata { public: diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 1e17f2f3a..dec7f17bd 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -59,6 +59,8 @@ void set_default_settings() g_settings.setDefault("random_input", "false"); g_settings.setDefault("client_unload_unused_data_timeout", "600"); g_settings.setDefault("enable_fog", "true"); + g_settings.setDefault("fov", "72"); + g_settings.setDefault("view_bobbing", "true"); g_settings.setDefault("new_style_water", "false"); g_settings.setDefault("new_style_leaves", "true"); g_settings.setDefault("smooth_lighting", "true"); @@ -77,6 +79,7 @@ void set_default_settings() // Server stuff g_settings.setDefault("motd", ""); + g_settings.setDefault("max_users", "20"); g_settings.setDefault("enable_experimental", "false"); g_settings.setDefault("creative_mode", "false"); g_settings.setDefault("enable_damage", "true"); diff --git a/src/environment.cpp b/src/environment.cpp index 8103b7110..ff570554d 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -1345,8 +1345,6 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) i.atEnd()==false; i++) { ServerActiveObject* obj = i.getNode()->getValue(); - u16 id = i.getNode()->getKey(); - v3f objectpos = obj->getBasePosition(); // This shouldn't happen but check it if(obj == NULL) @@ -1357,9 +1355,12 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) continue; } + u16 id = i.getNode()->getKey(); + v3f objectpos = obj->getBasePosition(); + // The block in which the object resides in v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS)); - + // If block is active, don't remove if(m_active_blocks.contains(blockpos_o)) continue; @@ -1686,18 +1687,19 @@ void ClientEnvironment::step(float dtime) // Move player->move(dtime, *m_map, 100*BS); - // Update lighting on remote players on client - u8 light = LIGHT_MAX; - try{ - // Get node at head - v3s16 p = player->getLightPosition(); - MapNode n = m_map->getNode(p); - light = n.getLightBlend(getDayNightRatio()); - } - catch(InvalidPositionException &e) {} - player->updateLight(light); } + // Update lighting on all players on client + u8 light = LIGHT_MAX; + try{ + // Get node at head + v3s16 p = player->getLightPosition(); + MapNode n = m_map->getNode(p); + light = n.getLightBlend(getDayNightRatio()); + } + catch(InvalidPositionException &e) {} + player->updateLight(light); + /* Add footsteps to grass */ @@ -1940,29 +1942,6 @@ ClientEnvEvent ClientEnvironment::getClientEvent() return m_client_event_queue.pop_front(); } -void ClientEnvironment::drawPostFx(video::IVideoDriver* driver, v3f camera_pos) -{ - /*LocalPlayer *player = getLocalPlayer(); - assert(player); - v3f pos_f = player->getPosition() + v3f(0,BS*1.625,0);*/ - v3f pos_f = camera_pos; - v3s16 p_nodes = floatToInt(pos_f, BS); - MapNode n = m_map->getNodeNoEx(p_nodes); - if(n.getContent() == CONTENT_WATER || n.getContent() == CONTENT_WATERSOURCE) - { - v2u32 ss = driver->getScreenSize(); - core::rect<s32> rect(0,0, ss.X, ss.Y); - driver->draw2DRectangle(video::SColor(64, 100, 100, 200), rect); - } - else if(content_features(n).solidness == 2 && - g_settings.getBool("free_move") == false) - { - v2u32 ss = driver->getScreenSize(); - core::rect<s32> rect(0,0, ss.X, ss.Y); - driver->draw2DRectangle(video::SColor(255, 0, 0, 0), rect); - } -} - #endif // #ifndef SERVER diff --git a/src/environment.h b/src/environment.h index d9248d2ad..055e2b1f6 100644 --- a/src/environment.h +++ b/src/environment.h @@ -406,9 +406,6 @@ public: // Get event from queue. CEE_NONE is returned if queue is empty. ClientEnvEvent getClientEvent(); - - // Post effects - void drawPostFx(video::IVideoDriver* driver, v3f camera_pos); private: ClientMap *m_map; diff --git a/src/game.cpp b/src/game.cpp index dc3ed2456..13b06db6c 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "materials.h" #include "config.h" #include "clouds.h" +#include "camera.h" #include "farmesh.h" #include "mapblock.h" @@ -49,8 +50,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #define FIELD_OF_VIEW_TEST 0 -MapDrawControl draw_control; - // Chat data struct ChatLine { @@ -144,144 +143,6 @@ struct TextDestSignNode : public TextDest }; /* - 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 "<<added_frames<<" frames, total of " - <<added_frametime<<"s."<<std::endl;*/ - - /*dstream<<"draw_control.blocks_drawn=" - <<draw_control.blocks_drawn - <<", draw_control.blocks_would_have_drawn=" - <<draw_control.blocks_would_have_drawn - <<std::endl;*/ - - float range_min = g_settings.getS16("viewing_range_nodes_min"); - float range_max = g_settings.getS16("viewing_range_nodes_max"); - - // Limit minimum to keep the feedback loop stable - if(range_min < 5) - range_min = 5; - - draw_control.wanted_min_range = range_min; - //draw_control.wanted_max_blocks = (1.5*draw_control.blocks_drawn)+1; - draw_control.wanted_max_blocks = (1.5*draw_control.blocks_would_have_drawn)+1; - if(draw_control.wanted_max_blocks < 10) - draw_control.wanted_max_blocks = 10; - - float block_draw_ratio = 1.0; - if(draw_control.blocks_would_have_drawn != 0) - { - block_draw_ratio = (float)draw_control.blocks_drawn - / (float)draw_control.blocks_would_have_drawn; - } - - // Calculate the average frametime in the case that all wanted - // blocks had been drawn - f32 frametime = added_frametime / added_frames / block_draw_ratio; - - added_frametime = 0.0; - added_frames = 0; - - float wanted_fps = g_settings.getFloat("wanted_fps"); - float wanted_frametime = 1.0 / wanted_fps; - - f32 wanted_frametime_change = wanted_frametime - frametime; - //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl; - - // If needed frametime change is small, just return - if(fabs(wanted_frametime_change) < wanted_frametime*0.4) - { - //dstream<<"ignoring small wanted_frametime_change"<<std::endl; - return; - } - - float range = draw_control.wanted_range; - float new_range = range; - - static s16 range_old = 0; - static f32 frametime_old = 0; - - float d_range = range - range_old; - f32 d_frametime = frametime - frametime_old; - // A sane default of 30ms per 50 nodes of range - static f32 time_per_range = 30. / 50; - if(d_range != 0) - { - time_per_range = d_frametime / d_range; - } - - // The minimum allowed calculated frametime-range derivative: - // Practically this sets the maximum speed of changing the range. - // The lower this value, the higher the maximum changing speed. - // A low value here results in wobbly range (0.001) - // A high value here results in slow changing range (0.0025) - // SUGG: This could be dynamically adjusted so that when - // the camera is turning, this is lower - //float min_time_per_range = 0.0015; - float min_time_per_range = 0.0010; - //float min_time_per_range = 0.05 / range; - if(time_per_range < min_time_per_range) - { - time_per_range = min_time_per_range; - //dstream<<"time_per_range="<<time_per_range<<" (min)"<<std::endl; - } - else - { - //dstream<<"time_per_range="<<time_per_range<<std::endl; - } - - f32 wanted_range_change = wanted_frametime_change / time_per_range; - // Dampen the change a bit to kill oscillations - //wanted_range_change *= 0.9; - //wanted_range_change *= 0.75; - wanted_range_change *= 0.5; - //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl; - - // If needed range change is very small, just return - if(fabs(wanted_range_change) < 0.001) - { - //dstream<<"ignoring small wanted_range_change"<<std::endl; - return; - } - - new_range += wanted_range_change; - - //float new_range_unclamped = new_range; - if(new_range < range_min) - new_range = range_min; - if(new_range > range_max) - new_range = range_max; - - /*dstream<<"new_range="<<new_range_unclamped - <<", clamped to "<<new_range<<std::endl;*/ - - draw_control.wanted_range = new_range; - - range_old = new_range; - frametime_old = frametime; -} - -/* Hotbar draw routine */ void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font, @@ -847,6 +708,7 @@ void the_game( draw_load_screen(L"Creating client...", driver, font); std::cout<<DTIME<<"Creating client"<<std::endl; + MapDrawControl draw_control; Client client(device, playername.c_str(), password, draw_control); draw_load_screen(L"Resolving address...", driver, font); @@ -950,25 +812,10 @@ void the_game( /* 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"; + Camera camera(smgr, draw_control); + if (!camera.successfullyCreated(error_message)) return; - } - - 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" @@ -1169,12 +1016,6 @@ void the_game( device->run(); /* - Viewing range - */ - - updateViewingRange(busytime, &client); - - /* FPS limiter */ @@ -1496,6 +1337,57 @@ void the_game( } /* + Mouse and camera control + NOTE: Do this before client.setPlayerControl() to not cause a camera lag of one frame + */ + + if((device->isWindowActive() && noMenuActive()) || random_input) + { + if(!random_input) + { + // Mac OSX gets upset if this is set every frame + if(device->getCursorControl()->isVisible()) + device->getCursorControl()->setVisible(false); + } + + if(first_loop_after_window_activation){ + //std::cout<<"window active, first loop"<<std::endl; + first_loop_after_window_activation = false; + } + else{ + s32 dx = input->getMousePos().X - displaycenter.X; + s32 dy = input->getMousePos().Y - displaycenter.Y; + if(invert_mouse) + dy = -dy; + //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl; + + /*const float keyspeed = 500; + if(input->isKeyDown(irr::KEY_UP)) + dy -= dtime * keyspeed; + if(input->isKeyDown(irr::KEY_DOWN)) + dy += dtime * keyspeed; + if(input->isKeyDown(irr::KEY_LEFT)) + dx -= dtime * keyspeed; + if(input->isKeyDown(irr::KEY_RIGHT)) + dx += dtime * keyspeed;*/ + + camera_yaw -= dx*0.2; + camera_pitch += dy*0.2; + if(camera_pitch < -89.5) camera_pitch = -89.5; + if(camera_pitch > 89.5) camera_pitch = 89.5; + } + input->setMousePos(displaycenter.X, displaycenter.Y); + } + else{ + // Mac OSX gets upset if this is set every frame + if(device->getCursorControl()->isVisible() == false) + device->getCursorControl()->setVisible(true); + + //std::cout<<"window inactive"<<std::endl; + first_loop_after_window_activation = true; + } + + /* Player speed control TODO: Cache the keycodes from getKeySetting */ @@ -1565,81 +1457,27 @@ void the_game( } } - // Get player position - v3f camera_position; - v3f player_position = client.getPlayerPosition(&camera_position); - //TimeTaker //timer2("//timer2"); - /* - Mouse and camera control - */ - - if((device->isWindowActive() && noMenuActive()) || random_input) - { - if(!random_input) - { - // Mac OSX gets upset if this is set every frame - if(device->getCursorControl()->isVisible()) - device->getCursorControl()->setVisible(false); - } + LocalPlayer* player = client.getLocalPlayer(); + camera.update(player, busytime, screensize); + camera.step(dtime); - if(first_loop_after_window_activation){ - //std::cout<<"window active, first loop"<<std::endl; - first_loop_after_window_activation = false; - } - else{ - s32 dx = input->getMousePos().X - displaycenter.X; - s32 dy = input->getMousePos().Y - displaycenter.Y; - if(invert_mouse) - dy = -dy; - //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl; - - /*const float keyspeed = 500; - if(input->isKeyDown(irr::KEY_UP)) - dy -= dtime * keyspeed; - if(input->isKeyDown(irr::KEY_DOWN)) - dy += dtime * keyspeed; - if(input->isKeyDown(irr::KEY_LEFT)) - dx -= dtime * keyspeed; - if(input->isKeyDown(irr::KEY_RIGHT)) - dx += dtime * keyspeed;*/ + v3f player_position = player->getPosition(); + v3f camera_position = camera.getPosition(); + v3f camera_direction = camera.getDirection(); + f32 camera_fov = camera.getFovMax(); - camera_yaw -= dx*0.2; - camera_pitch += dy*0.2; - if(camera_pitch < -89.5) camera_pitch = -89.5; - if(camera_pitch > 89.5) camera_pitch = 89.5; - } - input->setMousePos(displaycenter.X, displaycenter.Y); + if(FIELD_OF_VIEW_TEST) + { + client.updateCamera(v3f(0,0,0), v3f(0,0,1), camera_fov); } - else{ - // Mac OSX gets upset if this is set every frame - if(device->getCursorControl()->isVisible() == false) - device->getCursorControl()->setVisible(true); - - //std::cout<<"window inactive"<<std::endl; - first_loop_after_window_activation = true; + else + { + client.updateCamera(camera_position, + camera_direction, camera_fov); } - camera_yaw = wrapDegrees(camera_yaw); - camera_pitch = wrapDegrees(camera_pitch); - - v3f camera_direction = v3f(0,0,1); - camera_direction.rotateYZBy(camera_pitch); - camera_direction.rotateXZBy(camera_yaw); - - camera->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"); @@ -1917,9 +1755,12 @@ void the_game( } dig_time += dtime; + + camera.setDigging(0); // left click animation } } + if(input->getRightClicked()) { std::cout<<DTIME<<"Ground right-clicked"<<std::endl; @@ -1978,16 +1819,20 @@ void the_game( else { client.groundAction(1, nodepos, neighbourpos, g_selected_item); + camera.setDigging(1); // right click animation } } nodepos_old = nodepos; } - else{ - } } // selected_object == NULL + if(input->getLeftClicked()) + { + camera.setDigging(0); // left click animation + } + input->resetLeftClicked(); input->resetRightClicked(); @@ -2010,8 +1855,6 @@ void the_game( 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( @@ -2032,7 +1875,7 @@ void the_game( update_skybox(driver, smgr, skybox, brightness); /* - Update coulds + Update clouds */ if(clouds) { @@ -2103,7 +1946,6 @@ void the_game( ); } - /* Update gui stuff (0ms) */ @@ -2249,6 +2091,13 @@ void the_game( old_selected_item = g_selected_item; //std::cout<<"Updating local inventory"<<std::endl; client.getLocalInventory(local_inventory); + + // Update wielded tool + InventoryList *mlist = local_inventory.getList("main"); + InventoryItem *item = NULL; + if(mlist != NULL) + item = mlist->getItem(g_selected_item); + camera.wield(item); } /* @@ -2314,6 +2163,21 @@ void the_game( } /* + Wielded tool + */ + { + // Warning: This clears the Z buffer. + camera.drawWieldedTool(); + } + + /* + Post effects + */ + { + client.renderPostFx(); + } + + /* Frametime log */ if(g_settings.getBool("frametime_graph") == true) @@ -2353,13 +2217,6 @@ void the_game( guienv->drawAll(); /* - Environment post fx - */ - { - client.getEnv()->drawPostFx(driver, camera_position); - } - - /* Draw hotbar */ { diff --git a/src/inventory.cpp b/src/inventory.cpp index f31e19f77..a3e35c92c 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -158,7 +158,7 @@ InventoryItem *MaterialItem::createCookResult() const */ #ifndef SERVER -video::ITexture * CraftItem::getImage() +video::ITexture * CraftItem::getImage() const { if(g_texturesource == NULL) return NULL; @@ -224,7 +224,7 @@ bool CraftItem::use(ServerEnvironment *env, Player *player) TODO: Remove */ #ifndef SERVER -video::ITexture * MapBlockObjectItem::getImage() +video::ITexture * MapBlockObjectItem::getImage() const { if(m_inventorystring.substr(0,3) == "Rat") return g_texturesource->getTextureRaw("rat.png"); diff --git a/src/inventory.h b/src/inventory.h index 44ba6a5ad..3e05015ef 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -53,8 +53,12 @@ public: // Shall make an exact clone of the item virtual InventoryItem* clone() = 0; #ifndef SERVER - // Shall return an image to show in the GUI (or NULL) - virtual video::ITexture * getImage() { return NULL; } + // Return the name of the image for this item + virtual std::string getBasename() const { return ""; } + // Shall return an image of the item (or NULL) + virtual video::ITexture * getImage() const { return NULL; } + // Shall return an image of the item without embellishments (or NULL) + virtual video::ITexture * getImageRaw() const { return getImage(); } #endif // Shall return a text to show in the GUI virtual std::string getText() { return ""; } @@ -149,10 +153,9 @@ public: return new MaterialItem(m_content, m_count); } #ifndef SERVER - video::ITexture * getImage() + video::ITexture * getImage() const { return content_features(m_content).inventory_texture; - return NULL; } #endif std::string getText() @@ -225,7 +228,7 @@ public: } #ifndef SERVER - video::ITexture * getImage(); + video::ITexture * getImage() const; #endif std::string getText(); @@ -276,7 +279,7 @@ public: return new CraftItem(m_subname, m_count); } #ifndef SERVER - video::ITexture * getImage(); + video::ITexture * getImage() const; #endif std::string getText() { @@ -353,40 +356,43 @@ public: return new ToolItem(m_toolname, m_wear); } #ifndef SERVER - video::ITexture * getImage() - { - if(g_texturesource == NULL) - return NULL; - - std::string basename; + std::string getBasename() const { if(m_toolname == "WPick") - basename = "tool_woodpick.png"; + return "tool_woodpick.png"; else if(m_toolname == "STPick") - basename = "tool_stonepick.png"; + return "tool_stonepick.png"; else if(m_toolname == "SteelPick") - basename = "tool_steelpick.png"; + return "tool_steelpick.png"; else if(m_toolname == "MesePick") - basename = "tool_mesepick.png"; + return "tool_mesepick.png"; else if(m_toolname == "WShovel") - basename = "tool_woodshovel.png"; + return "tool_woodshovel.png"; else if(m_toolname == "STShovel") - basename = "tool_stoneshovel.png"; + return "tool_stoneshovel.png"; else if(m_toolname == "SteelShovel") - basename = "tool_steelshovel.png"; + return "tool_steelshovel.png"; else if(m_toolname == "WAxe") - basename = "tool_woodaxe.png"; + return "tool_woodaxe.png"; else if(m_toolname == "STAxe") - basename = "tool_stoneaxe.png"; + return "tool_stoneaxe.png"; else if(m_toolname == "SteelAxe") - basename = "tool_steelaxe.png"; + return "tool_steelaxe.png"; else if(m_toolname == "WSword") - basename = "tool_woodsword.png"; + return "tool_woodsword.png"; else if(m_toolname == "STSword") - basename = "tool_stonesword.png"; + return "tool_stonesword.png"; else if(m_toolname == "SteelSword") - basename = "tool_steelsword.png"; + return "tool_steelsword.png"; else - basename = "cloud.png"; + return "cloud.png"; +} + + video::ITexture * getImage() const + { + if(g_texturesource == NULL) + return NULL; + + std::string basename = getBasename(); /* Calculate a progress value with sane amount of @@ -401,6 +407,14 @@ public: return g_texturesource->getTextureRaw(os.str()); } + + video::ITexture * getImageRaw() const + { + if(g_texturesource == NULL) + return NULL; + + return g_texturesource->getTextureRaw(getBasename()); + } #endif std::string getText() { diff --git a/src/map.cpp b/src/map.cpp index 7de79c75d..eb55d3a57 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -890,7 +890,7 @@ void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks, /* */ void Map::addNodeAndUpdate(v3s16 p, MapNode n, - core::map<v3s16, MapBlock*> &modified_blocks) + core::map<v3s16, MapBlock*> &modified_blocks, std::string &player_name) { /*PrintInfo(m_dout); m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=(" @@ -1014,6 +1014,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n, if(meta_proto) { NodeMetadata *meta = meta_proto->clone(); + meta->setOwner(player_name); setNodeMetadata(p, meta); } @@ -1290,7 +1291,8 @@ bool Map::addNodeWithEvent(v3s16 p, MapNode n) bool succeeded = true; try{ core::map<v3s16, MapBlock*> modified_blocks; - addNodeAndUpdate(p, n, modified_blocks); + std::string st = std::string(""); + addNodeAndUpdate(p, n, modified_blocks, st); // Copy modified_blocks to event for(core::map<v3s16, MapBlock*>::Iterator @@ -3604,7 +3606,8 @@ ClientMap::ClientMap( m_client(client), m_control(control), m_camera_position(0,0,0), - m_camera_direction(0,0,1) + m_camera_direction(0,0,1), + m_camera_fov(PI) { m_camera_mutex.Init(); assert(m_camera_mutex.IsInitialized()); @@ -3713,6 +3716,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) m_camera_mutex.Lock(); v3f camera_position = m_camera_position; v3f camera_direction = m_camera_direction; + f32 camera_fov = m_camera_fov; m_camera_mutex.Unlock(); /* @@ -3805,7 +3809,8 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) float d = 0.0; if(isBlockInSight(block->getPos(), camera_position, - camera_direction, range, &d) == false) + camera_direction, camera_fov, + range, &d) == false) { continue; } @@ -3925,6 +3930,35 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/ } +void ClientMap::renderPostFx() +{ + // Sadly ISceneManager has no "post effects" render pass, in that case we + // could just register for that and handle it in renderMap(). + + m_camera_mutex.Lock(); + v3f camera_position = m_camera_position; + m_camera_mutex.Unlock(); + + MapNode n = getNodeNoEx(floatToInt(camera_position, BS)); + + // - If the player is in a solid node, make everything black. + // - If the player is in liquid, draw a semi-transparent overlay. + ContentFeatures& features = content_features(n); + video::SColor post_effect_color = features.post_effect_color; + if(features.solidness == 2 && g_settings.getBool("free_move") == false) + { + post_effect_color = video::SColor(255, 0, 0, 0); + } + if (post_effect_color.getAlpha() != 0) + { + // Draw a full-screen rectangle + video::IVideoDriver* driver = SceneManager->getVideoDriver(); + v2u32 ss = driver->getScreenSize(); + core::rect<s32> rect(0,0, ss.X, ss.Y); + driver->draw2DRectangle(post_effect_color, rect); + } +} + bool ClientMap::setTempMod(v3s16 p, NodeMod mod, core::map<v3s16, MapBlock*> *affected_blocks) { @@ -205,7 +205,7 @@ public: These handle lighting but not faces. */ void addNodeAndUpdate(v3s16 p, MapNode n, - core::map<v3s16, MapBlock*> &modified_blocks); + core::map<v3s16, MapBlock*> &modified_blocks, std::string &player_name); void removeNodeAndUpdate(v3s16 p, core::map<v3s16, MapBlock*> &modified_blocks); @@ -518,11 +518,12 @@ public: ISceneNode::drop(); } - void updateCamera(v3f pos, v3f dir) + void updateCamera(v3f pos, v3f dir, f32 fov) { JMutexAutoLock lock(m_camera_mutex); m_camera_position = pos; m_camera_direction = dir; + m_camera_fov = fov; } /* @@ -552,6 +553,8 @@ public: void renderMap(video::IVideoDriver* driver, s32 pass); + void renderPostFx(); + /* Methods for setting temporary modifications to nodes for drawing. @@ -601,6 +604,7 @@ private: v3f m_camera_position; v3f m_camera_direction; + f32 m_camera_fov; JMutex m_camera_mutex; core::map<v2s16, bool> m_last_drawn_sectors; diff --git a/src/mapblockobject.h b/src/mapblockobject.h index 804494715..b852812e2 100644 --- a/src/mapblockobject.h +++ b/src/mapblockobject.h @@ -484,20 +484,7 @@ public: u8 li = decode_light(light_at_pos); video::SColor color(255,li,li,li); - - scene::IMesh *mesh = m_node->getMesh(); - - u16 mc = mesh->getMeshBufferCount(); - for(u16 j=0; j<mc; j++) - { - scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); - video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices(); - u16 vc = buf->getVertexCount(); - for(u16 i=0; i<vc; i++) - { - vertices[i].Color = color; - } - } + setMeshVerticesColor(m_node->getMesh(), color); } #endif @@ -676,20 +663,7 @@ public: u8 li = decode_light(light_at_pos); video::SColor color(255,li,li,li); - - scene::IMesh *mesh = m_node->getMesh(); - - u16 mc = mesh->getMeshBufferCount(); - for(u16 j=0; j<mc; j++) - { - scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); - video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices(); - u16 vc = buf->getVertexCount(); - for(u16 i=0; i<vc; i++) - { - vertices[i].Color = color; - } - } + setMeshVerticesColor(m_node->getMesh(), color); } #endif @@ -836,20 +810,7 @@ public: u8 li = decode_light(light_at_pos); video::SColor color(255,li,li,li); - - scene::IMesh *mesh = m_node->getMesh(); - - u16 mc = mesh->getMeshBufferCount(); - for(u16 j=0; j<mc; j++) - { - scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); - video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices(); - u16 vc = buf->getVertexCount(); - for(u16 i=0; i<vc; i++) - { - vertices[i].Color = color; - } - } + setMeshVerticesColor(m_node->getMesh(), color); } #endif @@ -982,20 +943,7 @@ public: u8 li = decode_light(light_at_pos); video::SColor color(255,li,li,li); - - scene::IMesh *mesh = m_node->getMesh(); - - u16 mc = mesh->getMeshBufferCount(); - for(u16 j=0; j<mc; j++) - { - scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); - video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices(); - u16 vc = buf->getVertexCount(); - for(u16 i=0; i<vc; i++) - { - vertices[i].Color = color; - } - } + setMeshVerticesColor(m_node->getMesh(), color); } #endif diff --git a/src/mapnode.h b/src/mapnode.h index 3ad67aaf6..19dfb2802 100644 --- a/src/mapnode.h +++ b/src/mapnode.h @@ -160,6 +160,8 @@ struct ContentFeatures u8 liquid_viscosity; // Used currently for flowing liquids u8 vertex_alpha; + // Post effect color, drawn when the camera is inside the node. + video::SColor post_effect_color; // Special irrlicht material, used sometimes video::SMaterial *special_material; AtlasPointer *special_atlas; @@ -197,6 +199,7 @@ struct ContentFeatures liquid_alternative_source = CONTENT_IGNORE; liquid_viscosity = 0; vertex_alpha = 255; + post_effect_color = video::SColor(0, 0, 0, 0); special_material = NULL; special_atlas = NULL; light_source = 0; diff --git a/src/nodemetadata.h b/src/nodemetadata.h index de682f9b6..d81ade96c 100644 --- a/src/nodemetadata.h +++ b/src/nodemetadata.h @@ -65,6 +65,9 @@ public: // Used to make custom inventory menus. // See format in guiInventoryMenu.cpp. virtual std::string getInventoryDrawSpecString(){return "";} + // primarily used for locking chests, but others can play too + virtual std::string getOwner(){ return std::string(""); } + virtual void setOwner(std::string t){ } protected: static void registerType(u16 id, Factory f); diff --git a/src/player.h b/src/player.h index 13cffa205..ce1001992 100644 --- a/src/player.h +++ b/src/player.h @@ -67,12 +67,17 @@ public: return floatToInt(m_position + v3f(0,BS+BS/2,0), BS); } - v3f getEyePosition() + v3f getEyeOffset() { // This is at the height of the eyes of the current figure - // return m_position + v3f(0, BS+BS/2, 0); + // return v3f(0, BS+BS/2, 0); // This is more like in minecraft - return m_position + v3f(0,BS+(5*BS)/8,0); + return v3f(0,BS+(5*BS)/8,0); + } + + v3f getEyePosition() + { + return m_position + getEyeOffset(); } virtual void setPosition(const v3f &position) @@ -121,7 +126,10 @@ public: virtual bool isLocal() const = 0; - virtual void updateLight(u8 light_at_pos) {}; + virtual void updateLight(u8 light_at_pos) + { + light = light_at_pos; + } // NOTE: Use peer_id == 0 for disconnected /*virtual bool isClientConnected() { return false; } @@ -144,6 +152,8 @@ public: bool swimming_up; bool is_frozen; + u8 light; + Inventory inventory; // Actual inventory is backed up here when creative mode is used Inventory *inventory_backup; @@ -261,25 +271,14 @@ public: virtual void updateLight(u8 light_at_pos) { + Player::updateLight(light_at_pos); + if(m_node == NULL) return; u8 li = decode_light(light_at_pos); video::SColor color(255,li,li,li); - - scene::IMesh *mesh = m_node->getMesh(); - - u16 mc = mesh->getMeshBufferCount(); - for(u16 j=0; j<mc; j++) - { - scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); - video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices(); - u16 vc = buf->getVertexCount(); - for(u16 i=0; i<vc; i++) - { - vertices[i].Color = color; - } - } + setMeshVerticesColor(m_node->getMesh(), color); } void move(f32 dtime, Map &map, f32 pos_max_d); @@ -356,7 +355,7 @@ public: { return true; } - + void move(f32 dtime, Map &map, f32 pos_max_d, core::list<CollisionInfo> *collision_info); void move(f32 dtime, Map &map, f32 pos_max_d); diff --git a/src/server.cpp b/src/server.cpp index a04417074..5395d7618 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -582,9 +582,12 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, /* Don't generate or send if not in sight + FIXME This only works if the client uses a small enough + FOV setting. The default of 72 degrees is fine. */ - if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false) + float camera_fov = (72.0*PI/180) * 4./3.; + if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false) { continue; } @@ -2096,11 +2099,21 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) stringToPrivs(g_settings.get("default_privs"))); m_authmanager.save(); } + + // Enforce user limit. + // Don't enforce for users that have some admin right + if(m_clients.size() >= g_settings.getU16("max_users") && + (m_authmanager.getPrivs(playername) + & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS)) == 0 && + playername != g_settings.get("name")) + { + SendAccessDenied(m_con, peer_id, L"Too many users."); + return; + } // Get player Player *player = emergePlayer(playername, password, peer_id); - /*{ // DEBUG: Test serialization std::ostringstream test_os; @@ -2881,7 +2894,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) { MapEditEventIgnorer ign(&m_ignore_map_edit_events); - m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks); + std::string p_name = std::string(player->getName()); + m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name); } /* Set blocks not sent to far players @@ -3208,7 +3222,46 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) } // Disallow moving items if not allowed to build else if((getPlayerPrivs(player) & PRIV_BUILD) == 0) + { return; + } + // if it's a locking chest, only allow the owner or server admins to move items + else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0) + { + Strfnd fn(ma->from_inv); + std::string id0 = fn.next(":"); + if(id0 == "nodemeta") + { + v3s16 p; + p.X = stoi(fn.next(",")); + p.Y = stoi(fn.next(",")); + p.Z = stoi(fn.next(",")); + NodeMetadata *meta = m_env.getMap().getNodeMetadata(p); + if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) { + LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta; + if (lcm->getOwner() != player->getName()) + return; + } + } + } + else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0) + { + Strfnd fn(ma->to_inv); + std::string id0 = fn.next(":"); + if(id0 == "nodemeta") + { + v3s16 p; + p.X = stoi(fn.next(",")); + p.Y = stoi(fn.next(",")); + p.Z = stoi(fn.next(",")); + NodeMetadata *meta = m_env.getMap().getNodeMetadata(p); + if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) { + LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta; + if (lcm->getOwner() != player->getName()) + return; + } + } + } } if(disable_action == false) @@ -4109,7 +4162,7 @@ void Server::UpdateCrafting(u16 peer_id) InventoryList *clist = player->inventory.getList("craft"); InventoryList *rlist = player->inventory.getList("craftresult"); - if(rlist->getUsedSlots() == 0) + if(rlist && rlist->getUsedSlots() == 0) player->craftresult_is_preview = true; if(rlist && player->craftresult_is_preview) @@ -4386,16 +4439,16 @@ void Server::handlePeerChange(PeerChange &c) // Collect information about leaving in chat std::wstring message; { - std::wstring name = L"unknown"; Player *player = m_env.getPlayer(c.peer_id); if(player != NULL) - name = narrow_to_wide(player->getName()); - - message += L"*** "; - message += name; - message += L" left game"; - if(c.timeout) - message += L" (timed out)"; + { + std::wstring name = narrow_to_wide(player->getName()); + message += L"*** "; + message += name; + message += L" left game"; + if(c.timeout) + message += L" (timed out)"; + } } /*// Delete player diff --git a/src/tile.h b/src/tile.h index 216d76508..5cebb0eaa 100644 --- a/src/tile.h +++ b/src/tile.h @@ -304,7 +304,7 @@ struct TileSpec } // Sets everything else except the texture in the material - void applyMaterialOptions(video::SMaterial &material) + void applyMaterialOptions(video::SMaterial &material) const { if(alpha != 255 && material_type != MATERIAL_ALPHA_VERTEX) dstream<<"WARNING: TileSpec: alpha != 255 " diff --git a/src/utility.cpp b/src/utility.cpp index 0721100cb..3c6c2f286 100644 --- a/src/utility.cpp +++ b/src/utility.cpp @@ -156,14 +156,33 @@ void mysrand(unsigned seed) next = seed; } +// Sets the color of all vertices in the mesh +void setMeshVerticesColor(scene::IMesh* mesh, video::SColor& color) +{ + if(mesh == NULL) + return; + + u16 mc = mesh->getMeshBufferCount(); + for(u16 j=0; j<mc; j++) + { + scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); + video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices(); + u16 vc = buf->getVertexCount(); + for(u16 i=0; i<vc; i++) + { + vertices[i].Color = color; + } + } +} + /* blockpos: position of block in block coordinates camera_pos: position of camera in nodes camera_dir: an unit vector pointing to camera direction range: viewing range */ -bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, f32 range, - f32 *distance_ptr) +bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, + f32 camera_fov, f32 range, f32 *distance_ptr) { v3s16 blockpos_nodes = blockpos_b * MAP_BLOCKSIZE; @@ -211,8 +230,7 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, f32 range, cosangle += block_max_radius / dforward; // If block is not in the field of view, skip it - //if(cosangle < cos(FOV_ANGLE/2)) - if(cosangle < cos(FOV_ANGLE/2. * 4./3.)) + if(cosangle < cos(camera_fov / 2)) return false; } diff --git a/src/utility.h b/src/utility.h index ea7c11846..2fefd8667 100644 --- a/src/utility.h +++ b/src/utility.h @@ -553,6 +553,9 @@ private: u32 *m_result; }; +// Sets the color of all vertices in the mesh +void setMeshVerticesColor(scene::IMesh* mesh, video::SColor& color); + // Calculates the borders of a "d-radius" cube inline void getFacePositions(core::list<v3s16> &list, u16 d) { @@ -1765,8 +1768,8 @@ inline int myrand_range(int min, int max) Miscellaneous functions */ -bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, f32 range, - f32 *distance_ptr=NULL); +bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, + f32 camera_fov, f32 range, f32 *distance_ptr=NULL); /* Queue with unique values with fast checking of value existence |