Initialize utility.h return values to 0 to make lazily error-checked deserialization...
[oweals/minetest.git] / src / player.cpp
index 539244709ee5a48b3be1329a5f008145565893c6..0d4a1cb695c05553fb9c21c7b8ac2273f7122475 100644 (file)
@@ -22,77 +22,41 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "connection.h"
 #include "constants.h"
 #include "utility.h"
-
-// Convert a privileges value into a human-readable string,
-// with each component separated by a comma.
-std::wstring privsToString(u64 privs)
-{
-       std::wostringstream os(std::ios_base::binary);
-       if(privs & PRIV_BUILD)
-               os<<L"build,";
-       if(privs & PRIV_TELEPORT)
-               os<<L"teleport,";
-       if(privs & PRIV_SETTIME)
-               os<<L"settime,";
-       if(privs & PRIV_PRIVS)
-               os<<L"privs,";
-       if(os.tellp())
-       {
-               // Drop the trailing comma. (Why on earth can't
-               // you truncate a C++ stream anyway???)
-               std::wstring tmp = os.str();
-               return tmp.substr(0, tmp.length() -1);
-       }
-       return os.str();
-}
-
-// Converts a comma-seperated list of privilege values into a
-// privileges value. The reverse of privsToString(). Returns
-// PRIV_INVALID if there is anything wrong with the input.
-u64 stringToPrivs(std::wstring str)
-{
-       u64 privs=0;
-       std::vector<std::wstring> pr;
-       pr=str_split(str, ',');
-       for(std::vector<std::wstring>::iterator i = pr.begin();
-               i != pr.end(); ++i)
-       {
-               if(*i == L"build")
-                       privs |= PRIV_BUILD;
-               else if(*i == L"teleport")
-                       privs |= PRIV_TELEPORT;
-               else if(*i == L"settime")
-                       privs |= PRIV_SETTIME;
-               else if(*i == L"privs")
-                       privs |= PRIV_PRIVS;
-               else
-                       return PRIV_INVALID;
-       }
-       return privs;
-}
-
-
-Player::Player():
+#ifndef SERVER
+#include <ITextSceneNode.h>
+#endif
+#include "main.h" // For g_settings
+#include "settings.h"
+#include "nodedef.h"
+#include "collision.h"
+#include "environment.h"
+#include "gamedef.h"
+#include "event.h"
+
+Player::Player(IGameDef *gamedef):
        touching_ground(false),
        in_water(false),
        in_water_stable(false),
+       is_climbing(false),
        swimming_up(false),
-       craftresult_is_preview(true),
+       inventory(gamedef->idef()),
+       inventory_backup(NULL),
        hp(20),
-       privs(PRIV_DEFAULT),
        peer_id(PEER_ID_INEXISTENT),
+// protected
+       m_gamedef(gamedef),
        m_pitch(0),
        m_yaw(0),
        m_speed(0,0,0),
        m_position(0,0,0)
 {
        updateName("<not set>");
-       updatePassword("");
        resetInventory();
 }
 
 Player::~Player()
 {
+       delete inventory_backup;
 }
 
 void Player::resetInventory()
@@ -100,6 +64,7 @@ void Player::resetInventory()
        inventory.clear();
        inventory.addList("main", PLAYER_INVENTORY_SIZE);
        inventory.addList("craft", 9);
+       inventory.addList("craftpreview", 1);
        inventory.addList("craftresult", 1);
 }
 
@@ -140,25 +105,33 @@ void Player::accelerate(v3f target_speed, f32 max_increase)
 #endif
 }
 
+v3s16 Player::getLightPosition() const
+{
+       return floatToInt(m_position + v3f(0,BS+BS/2,0), BS);
+}
+
 void Player::serialize(std::ostream &os)
 {
        // Utilize a Settings object for storing values
        Settings args;
        args.setS32("version", 1);
        args.set("name", m_name);
-       args.set("password", m_password);
+       //args.set("password", m_password);
        args.setFloat("pitch", m_pitch);
        args.setFloat("yaw", m_yaw);
        args.setV3F("position", m_position);
-       args.setBool("craftresult_is_preview", craftresult_is_preview);
        args.setS32("hp", hp);
-       args.setU64("privs", privs);
 
        args.writeLines(os);
 
        os<<"PlayerArgsEnd\n";
-
-       inventory.serialize(os);
+       
+       // If actual inventory is backed up due to creative mode, save it
+       // instead of the dummy creative mode inventory
+       if(inventory_backup)
+               inventory_backup->serialize(os);
+       else
+               inventory.serialize(os);
 }
 
 void Player::deSerialize(std::istream &is)
@@ -178,169 +151,43 @@ void Player::deSerialize(std::istream &is)
                args.parseConfigLine(line);
        }
 
-       //args.getS32("version");
+       //args.getS32("version"); // Version field value not used
        std::string name = args.get("name");
        updateName(name.c_str());
-       std::string password = "";
-       if(args.exists("password"))
-               password = args.get("password");
-       updatePassword(password.c_str());
-       m_pitch = args.getFloat("pitch");
-       m_yaw = args.getFloat("yaw");
-       m_position = args.getV3F("position");
-       try{
-               craftresult_is_preview = args.getBool("craftresult_is_preview");
-       }catch(SettingNotFoundException &e){
-               craftresult_is_preview = true;
-       }
+       setPitch(args.getFloat("pitch"));
+       setYaw(args.getFloat("yaw"));
+       setPosition(args.getV3F("position"));
        try{
                hp = args.getS32("hp");
        }catch(SettingNotFoundException &e){
                hp = 20;
        }
-       try{
-               std::string sprivs = args.get("privs");
-               if(sprivs == "all")
-               {
-                       privs = PRIV_ALL;
-               }
-               else
-               {
-                       std::istringstream ss(sprivs);
-                       ss>>privs;
-               }
-       }catch(SettingNotFoundException &e){
-               privs = PRIV_DEFAULT;
-       }
 
        inventory.deSerialize(is);
-}
-
-/*
-       RemotePlayer
-*/
-
-#ifndef SERVER
 
-RemotePlayer::RemotePlayer(
-               scene::ISceneNode* parent,
-               IrrlichtDevice *device,
-               s32 id):
-       scene::ISceneNode(parent, (device==NULL)?NULL:device->getSceneManager(), id),
-       m_text(NULL)
-{
-       m_box = core::aabbox3d<f32>(-BS/2,0,-BS/2,BS/2,BS*2,BS/2);
-
-       if(parent != NULL && device != NULL)
+       if(inventory.getList("craftpreview") == NULL)
        {
-               // ISceneNode stores a member called SceneManager
-               scene::ISceneManager* mgr = SceneManager;
-               video::IVideoDriver* driver = mgr->getVideoDriver();
-               gui::IGUIEnvironment* gui = device->getGUIEnvironment();
-
-               // Add a text node for showing the name
-               wchar_t wname[1] = {0};
-               m_text = mgr->addTextSceneNode(gui->getBuiltInFont(),
-                               wname, video::SColor(255,255,255,255), this);
-               m_text->setPosition(v3f(0, (f32)BS*2.1, 0));
-
-               // Attach a simple mesh to the player for showing an image
-               scene::SMesh *mesh = new scene::SMesh();
-               { // Front
-               scene::IMeshBuffer *buf = new scene::SMeshBuffer();
-               video::SColor c(255,255,255,255);
-               video::S3DVertex vertices[4] =
-               {
-                       video::S3DVertex(-BS/2,0,0, 0,0,0, c, 0,1),
-                       video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1),
-                       video::S3DVertex(BS/2,BS*2,0, 0,0,0, c, 1,0),
-                       video::S3DVertex(-BS/2,BS*2,0, 0,0,0, c, 0,0),
-               };
-               u16 indices[] = {0,1,2,2,3,0};
-               buf->append(vertices, 4, indices, 6);
-               // Set material
-               buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
-               //buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
-               buf->getMaterial().setTexture(0, driver->getTexture(porting::getDataPath("player.png").c_str()));
-               buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
-               buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
-               //buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
-               buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
-               // Add to mesh
-               mesh->addMeshBuffer(buf);
-               buf->drop();
-               }
-               { // Back
-               scene::IMeshBuffer *buf = new scene::SMeshBuffer();
-               video::SColor c(255,255,255,255);
-               video::S3DVertex vertices[4] =
+               // Convert players without craftpreview
+               inventory.addList("craftpreview", 1);
+
+               bool craftresult_is_preview = true;
+               if(args.exists("craftresult_is_preview"))
+                       craftresult_is_preview = args.getBool("craftresult_is_preview");
+               if(craftresult_is_preview)
                {
-                       video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1),
-                       video::S3DVertex(-BS/2,0,0, 0,0,0, c, 0,1),
-                       video::S3DVertex(-BS/2,BS*2,0, 0,0,0, c, 0,0),
-                       video::S3DVertex(BS/2,BS*2,0, 0,0,0, c, 1,0),
-               };
-               u16 indices[] = {0,1,2,2,3,0};
-               buf->append(vertices, 4, indices, 6);
-               // Set material
-               buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
-               //buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
-               buf->getMaterial().setTexture(0, driver->getTexture(porting::getDataPath("player_back.png").c_str()));
-               buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
-               buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
-               buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
-               // Add to mesh
-               mesh->addMeshBuffer(buf);
-               buf->drop();
+                       // Clear craftresult
+                       inventory.getList("craftresult")->changeItem(0, ItemStack());
                }
-               m_node = mgr->addMeshSceneNode(mesh, this);
-               mesh->drop();
-               m_node->setPosition(v3f(0,0,0));
        }
 }
 
-RemotePlayer::~RemotePlayer()
-{
-       if(SceneManager != NULL)
-               ISceneNode::remove();
-}
-
-void RemotePlayer::updateName(const char *name)
-{
-       Player::updateName(name);
-       if(m_text != NULL)
-       {
-               wchar_t wname[PLAYERNAME_SIZE];
-               mbstowcs(wname, m_name, strlen(m_name)+1);
-               m_text->setText(wname);
-       }
-}
-
-void RemotePlayer::move(f32 dtime, Map &map, f32 pos_max_d)
-{
-       m_pos_animation_time_counter += dtime;
-       m_pos_animation_counter += dtime;
-       v3f movevector = m_position - m_oldpos;
-       f32 moveratio;
-       if(m_pos_animation_time < 0.001)
-               moveratio = 1.0;
-       else
-               moveratio = m_pos_animation_counter / m_pos_animation_time;
-       if(moveratio > 1.5)
-               moveratio = 1.5;
-       m_showpos = m_oldpos + movevector * moveratio;
-       
-       ISceneNode::setPosition(m_showpos);
-}
-
-#endif
-
 #ifndef SERVER
 /*
        LocalPlayer
 */
 
-LocalPlayer::LocalPlayer():
+LocalPlayer::LocalPlayer(IGameDef *gamedef):
+       Player(gamedef),
        m_sneak_node(32767,32767,32767),
        m_sneak_node_exists(false)
 {
@@ -356,10 +203,14 @@ LocalPlayer::~LocalPlayer()
 void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
                core::list<CollisionInfo> *collision_info)
 {
+       INodeDefManager *nodemgr = m_gamedef->ndef();
+
        v3f position = getPosition();
        v3f oldpos = position;
        v3s16 oldpos_i = floatToInt(oldpos, BS);
 
+       v3f old_speed = m_speed;
+
        /*std::cout<<"oldpos_i=("<<oldpos_i.X<<","<<oldpos_i.Y<<","
                        <<oldpos_i.Z<<")"<<std::endl;*/
 
@@ -367,9 +218,9 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
                Calculate new position
        */
        position += m_speed * dtime;
-
+       
        // Skip collision detection if a special movement mode is used
-       bool free_move = g_settings.getBool("free_move");
+       bool free_move = g_settings->getBool("free_move");
        if(free_move)
        {
                setPosition(position);
@@ -391,13 +242,13 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
                if(in_water)
                {
                        v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0), BS);
-                       in_water = content_liquid(map.getNode(pp).d);
+                       in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
                }
                // If not in water, the threshold of going in is at lower y
                else
                {
                        v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0), BS);
-                       in_water = content_liquid(map.getNode(pp).d);
+                       in_water = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
                }
        }
        catch(InvalidPositionException &e)
@@ -410,13 +261,28 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
        */
        try{
                v3s16 pp = floatToInt(position + v3f(0,0,0), BS);
-               in_water_stable = content_liquid(map.getNode(pp).d);
+               in_water_stable = nodemgr->get(map.getNode(pp).getContent()).isLiquid();
        }
        catch(InvalidPositionException &e)
        {
                in_water_stable = false;
        }
 
+       /*
+               Check if player is climbing
+       */
+
+       try {
+               v3s16 pp = floatToInt(position + v3f(0,0.5*BS,0), BS);
+               v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS);
+               is_climbing = ((nodemgr->get(map.getNode(pp).getContent()).climbable ||
+               nodemgr->get(map.getNode(pp2).getContent()).climbable) && !free_move);
+       }
+       catch(InvalidPositionException &e)
+       {
+               is_climbing = false;
+       }
+
        /*
                Collision uncertainty radius
                Make it a bit larger than the maximum distance of movement
@@ -428,8 +294,8 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
        // This should always apply, otherwise there are glitches
        assert(d > pos_max_d);
 
-       float player_radius = BS*0.35;
-       float player_height = BS*1.7;
+       float player_radius = BS*0.30;
+       float player_height = BS*1.55;
        
        // Maximum distance over border for sneaking
        f32 sneak_max = BS*0.4;
@@ -456,8 +322,23 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
                if(position.Y < min_y)
                {
                        position.Y = min_y;
+
+                       //v3f old_speed = m_speed;
+
                        if(m_speed.Y < 0)
                                m_speed.Y = 0;
+
+                       /*if(collision_info)
+                       {
+                               // Report fall collision
+                               if(old_speed.Y < m_speed.Y - 0.1)
+                               {
+                                       CollisionInfo info;
+                                       info.t = COLLISION_FALL;
+                                       info.speed = m_speed.Y - old_speed.Y;
+                                       collision_info->push_back(info);
+                               }
+                       }*/
                }
        }
 
@@ -487,14 +368,17 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
 
                Player is allowed to jump when this is true.
        */
+       bool touching_ground_was = touching_ground;
        touching_ground = false;
-       
+
        /*std::cout<<"Checking collisions for ("
                        <<oldpos_i.X<<","<<oldpos_i.Y<<","<<oldpos_i.Z
                        <<") -> ("
                        <<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z
                        <<"):"<<std::endl;*/
        
+       bool standing_on_unloaded = false;
+       
        /*
                Go through every node around the player
        */
@@ -502,13 +386,15 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
        for(s16 z = oldpos_i.Z - 1; z <= oldpos_i.Z + 1; z++)
        for(s16 x = oldpos_i.X - 1; x <= oldpos_i.X + 1; x++)
        {
+               bool is_unloaded = false;
                try{
                        // Player collides into walkable nodes
-                       if(content_walkable(map.getNode(v3s16(x,y,z)).d) == false)
+                       if(nodemgr->get(map.getNode(v3s16(x,y,z))).walkable == false)
                                continue;
                }
                catch(InvalidPositionException &e)
                {
+                       is_unloaded = true;
                        // Doing nothing here will block the player from
                        // walking over map borders
                }
@@ -533,6 +419,8 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
                                && nodebox.MinEdge.Z+d < playerbox.MaxEdge.Z
                ){
                        touching_ground = true;
+                       if(is_unloaded)
+                               standing_on_unloaded = true;
                }
                
                // If player doesn't intersect with node, ignore node.
@@ -608,13 +496,13 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
                        */
                        if(other_axes_overlap && main_axis_collides)
                        {
-                               v3f old_speed = m_speed;
+                               //v3f old_speed = m_speed;
 
                                m_speed -= m_speed.dotProduct(dirs[i]) * dirs[i];
                                position -= position.dotProduct(dirs[i]) * dirs[i];
                                position += oldpos.dotProduct(dirs[i]) * dirs[i];
                                
-                               if(collision_info)
+                               /*if(collision_info)
                                {
                                        // Report fall collision
                                        if(old_speed.Y < m_speed.Y - 0.1)
@@ -624,7 +512,7 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
                                                info.speed = m_speed.Y - old_speed.Y;
                                                collision_info->push_back(info);
                                        }
-                               }
+                               }*/
                        }
                
                }
@@ -667,10 +555,10 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
 
                        try{
                                // The node to be sneaked on has to be walkable
-                               if(content_walkable(map.getNode(p).d) == false)
+                               if(nodemgr->get(map.getNode(p)).walkable == false)
                                        continue;
                                // And the node above it has to be nonwalkable
-                               if(content_walkable(map.getNode(p+v3s16(0,1,0)).d) == true)
+                               if(nodemgr->get(map.getNode(p+v3s16(0,1,0))).walkable == true)
                                        continue;
                        }
                        catch(InvalidPositionException &e)
@@ -707,6 +595,26 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d,
                Set new position
        */
        setPosition(position);
+       
+       /*
+               Report collisions
+       */
+       if(collision_info)
+       {
+               // Report fall collision
+               if(old_speed.Y < m_speed.Y - 0.1 && !standing_on_unloaded)
+               {
+                       CollisionInfo info;
+                       info.t = COLLISION_FALL;
+                       info.speed = m_speed.Y - old_speed.Y;
+                       collision_info->push_back(info);
+               }
+       }
+
+       if(!touching_ground_was && touching_ground){
+               MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
+               m_gamedef->event()->put(e);
+       }
 }
 
 void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
@@ -731,11 +639,11 @@ void LocalPlayer::applyControl(float dtime)
        
        v3f speed = v3f(0,0,0);
 
-       bool free_move = g_settings.getBool("free_move");
-       bool fast_move = g_settings.getBool("fast_move");
-       bool continuous_forward = g_settings.getBool("continuous_forward");
+       bool free_move = g_settings->getBool("free_move");
+       bool fast_move = g_settings->getBool("fast_move");
+       bool continuous_forward = g_settings->getBool("continuous_forward");
 
-       if(free_move)
+       if(free_move || is_climbing)
        {
                v3f speed = getSpeed();
                speed.Y = 0;
@@ -762,6 +670,12 @@ void LocalPlayer::applyControl(float dtime)
                                speed.Y = -walkspeed_max;
                        setSpeed(speed);
                }
+               else if(is_climbing)
+               {
+                       v3f speed = getSpeed();
+                       speed.Y = -3*BS;
+                       setSpeed(speed);
+               }
                else
                {
                        // If not free movement but fast is allowed, aux1 is
@@ -806,14 +720,20 @@ void LocalPlayer::applyControl(float dtime)
                }
                else if(touching_ground)
                {
-                       v3f speed = getSpeed();
                        /*
                                NOTE: The d value in move() affects jump height by
                                raising the height at which the jump speed is kept
                                at its starting value
                        */
-                       speed.Y = 6.5*BS;
-                       setSpeed(speed);
+                       v3f speed = getSpeed();
+                       if(speed.Y >= -0.5*BS)
+                       {
+                               speed.Y = 6.5*BS;
+                               setSpeed(speed);
+                               
+                               MtEvent *e = new SimpleTriggerEvent("PlayerJump");
+                               m_gamedef->event()->put(e);
+                       }
                }
                // Use the oscillating value for getting out of water
                // (so that the player doesn't fly on the surface)
@@ -824,6 +744,12 @@ void LocalPlayer::applyControl(float dtime)
                        setSpeed(speed);
                        swimming_up = true;
                }
+               else if(is_climbing)
+               {
+                       v3f speed = getSpeed();
+                       speed.Y = 3*BS;
+                       setSpeed(speed);
+               }
        }
 
        // The speed of the player (Y is ignored)
@@ -843,5 +769,13 @@ void LocalPlayer::applyControl(float dtime)
        // Accelerate to target speed with maximum increment
        accelerate(speed, inc);
 }
+
+v3s16 LocalPlayer::getStandingNodePos()
+{
+       if(m_sneak_node_exists)
+               return m_sneak_node;
+       return floatToInt(getPosition(), BS);
+}
+
 #endif