]> git.lizzy.rs Git - minetest.git/blobdiff - src/client/inputhandler.h
Ratelimit MeshUpdateQueue::cleanupCache() runs
[minetest.git] / src / client / inputhandler.h
index 885f34e05dfd9f402e7022290488a452c8c3133e..3db105c518ecf6d066d1436247b6f558e3f1f21c 100644 (file)
@@ -21,9 +21,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "irrlichttypes_extrabloated.h"
 #include "joystick_controller.h"
+#include <list>
 #include "keycode.h"
 #include "renderingengine.h"
-#include <unordered_set>
 
 #ifdef HAVE_TOUCHSCREENGUI
 #include "gui/touchscreengui.h"
@@ -61,33 +61,105 @@ struct KeyCache
        InputHandler *handler;
 };
 
+class KeyList : private std::list<KeyPress>
+{
+       typedef std::list<KeyPress> super;
+       typedef super::iterator iterator;
+       typedef super::const_iterator const_iterator;
+
+       virtual const_iterator find(const KeyPress &key) const
+       {
+               const_iterator f(begin());
+               const_iterator e(end());
+
+               while (f != e) {
+                       if (*f == key)
+                               return f;
+
+                       ++f;
+               }
+
+               return e;
+       }
+
+       virtual iterator find(const KeyPress &key)
+       {
+               iterator f(begin());
+               iterator e(end());
+
+               while (f != e) {
+                       if (*f == key)
+                               return f;
+
+                       ++f;
+               }
+
+               return e;
+       }
+
+public:
+       void clear() { super::clear(); }
+
+       void set(const KeyPress &key)
+       {
+               if (find(key) == end())
+                       push_back(key);
+       }
+
+       void unset(const KeyPress &key)
+       {
+               iterator p(find(key));
+
+               if (p != end())
+                       erase(p);
+       }
+
+       void toggle(const KeyPress &key)
+       {
+               iterator p(this->find(key));
+
+               if (p != end())
+                       erase(p);
+               else
+                       push_back(key);
+       }
+
+       bool operator[](const KeyPress &key) const { return find(key) != end(); }
+};
+
 class MyEventReceiver : public IEventReceiver
 {
 public:
        // This is the one method that we have to implement
        virtual bool OnEvent(const SEvent &event);
 
-       bool IsKeyDown(const KeyPress &keyCode) const { return keyIsDown.count(keyCode); }
+       bool IsKeyDown(const KeyPress &keyCode) const { return keyIsDown[keyCode]; }
 
        // Checks whether a key was down and resets the state
        bool WasKeyDown(const KeyPress &keyCode)
        {
-               bool b = keyWasDown.count(keyCode);
+               bool b = keyWasDown[keyCode];
                if (b)
-                       keyWasDown.erase(keyCode);
+                       keyWasDown.unset(keyCode);
                return b;
        }
 
        // Checks whether a key was just pressed. State will be cleared
        // in the subsequent iteration of Game::processPlayerInteraction
-       bool WasKeyPressed(const KeyPress &keycode) const { return keyWasPressed.count(keycode); }
+       bool WasKeyPressed(const KeyPress &keycode) const { return keyWasPressed[keycode]; }
 
        // Checks whether a key was just released. State will be cleared
        // in the subsequent iteration of Game::processPlayerInteraction
-       bool WasKeyReleased(const KeyPress &keycode) const { return keyWasReleased.count(keycode); }
+       bool WasKeyReleased(const KeyPress &keycode) const { return keyWasReleased[keycode]; }
 
-       void listenForKey(const KeyPress &keyCode) { keysListenedFor.insert(keyCode); }
-       void dontListenForKeys() { keysListenedFor.clear(); }
+       void listenForKey(const KeyPress &keyCode)
+       {
+               keysListenedFor.set(keyCode);
+       }
+       void dontListenForKeys()
+       {
+               keysListenedFor.clear();
+       }
 
        s32 getMouseWheel()
        {
@@ -123,8 +195,6 @@ class MyEventReceiver : public IEventReceiver
 #endif
        }
 
-       s32 mouse_wheel = 0;
-
        JoystickController *joystick = nullptr;
 
 #ifdef HAVE_TOUCHSCREENGUI
@@ -132,20 +202,26 @@ class MyEventReceiver : public IEventReceiver
 #endif
 
 private:
-       //! The current state of keys
-       std::unordered_set<KeyPress> keyIsDown;
+       s32 mouse_wheel = 0;
 
-       //! Whether a key was down
-       std::unordered_set<KeyPress> keyWasDown;
+       // The current state of keys
+       KeyList keyIsDown;
 
-       //! Whether a key has just been pressed
-       std::unordered_set<KeyPress> keyWasPressed;
+       // Like keyIsDown but only reset when that key is read
+       KeyList keyWasDown;
 
-       //! Whether a key has just been released
-       std::unordered_set<KeyPress> keyWasReleased;
+       // Whether a key has just been pressed
+       KeyList keyWasPressed;
 
-       //! List of keys we listen for
-       std::unordered_set<KeyPress> keysListenedFor;
+       // Whether a key has just been released
+       KeyList keyWasReleased;
+
+       // List of keys we listen for
+       // TODO perhaps the type of this is not really
+       // performant as KeyList is designed for few but
+       // often changing keys, and keysListenedFor is expected
+       // to change seldomly but contain lots of keys.
+       KeyList keysListenedFor;
 };
 
 class InputHandler
@@ -170,6 +246,9 @@ class InputHandler
        virtual bool wasKeyReleased(GameKeyType k) = 0;
        virtual bool cancelPressed() = 0;
 
+       virtual float getMovementSpeed() = 0;
+       virtual float getMovementDirection() = 0;
+
        virtual void clearWasKeyPressed() {}
        virtual void clearWasKeyReleased() {}
 
@@ -199,6 +278,12 @@ class RealInputHandler : public InputHandler
        {
                m_receiver->joystick = &joystick;
        }
+
+       virtual ~RealInputHandler()
+       {
+               m_receiver->joystick = nullptr;
+       }
+
        virtual bool isKeyDown(GameKeyType k)
        {
                return m_receiver->IsKeyDown(keycache.key[k]) || joystick.isKeyDown(k);
@@ -209,16 +294,58 @@ class RealInputHandler : public InputHandler
        }
        virtual bool wasKeyPressed(GameKeyType k)
        {
-               return m_receiver->WasKeyPressed(keycache.key[k]) || joystick.wasKeyReleased(k);
+               return m_receiver->WasKeyPressed(keycache.key[k]) || joystick.wasKeyPressed(k);
        }
        virtual bool wasKeyReleased(GameKeyType k)
        {
                return m_receiver->WasKeyReleased(keycache.key[k]) || joystick.wasKeyReleased(k);
        }
+
+       virtual float getMovementSpeed()
+       {
+               bool f = m_receiver->IsKeyDown(keycache.key[KeyType::FORWARD]),
+                       b = m_receiver->IsKeyDown(keycache.key[KeyType::BACKWARD]),
+                       l = m_receiver->IsKeyDown(keycache.key[KeyType::LEFT]),
+                       r = m_receiver->IsKeyDown(keycache.key[KeyType::RIGHT]);
+               if (f || b || l || r)
+               {
+                       // if contradictory keys pressed, stay still
+                       if (f && b && l && r)
+                               return 0.0f;
+                       else if (f && b && !l && !r)
+                               return 0.0f;
+                       else if (!f && !b && l && r)
+                               return 0.0f;
+                       return 1.0f; // If there is a keyboard event, assume maximum speed
+               }
+               return joystick.getMovementSpeed();
+       }
+
+       virtual float getMovementDirection()
+       {
+               float x = 0, z = 0;
+
+               /* Check keyboard for input */
+               if (m_receiver->IsKeyDown(keycache.key[KeyType::FORWARD]))
+                       z += 1;
+               if (m_receiver->IsKeyDown(keycache.key[KeyType::BACKWARD]))
+                       z -= 1;
+               if (m_receiver->IsKeyDown(keycache.key[KeyType::RIGHT]))
+                       x += 1;
+               if (m_receiver->IsKeyDown(keycache.key[KeyType::LEFT]))
+                       x -= 1;
+
+               if (x != 0 || z != 0) /* If there is a keyboard event, it takes priority */
+                       return atan2(x, z);
+               else
+                       return joystick.getMovementDirection();
+       }
+
        virtual bool cancelPressed()
        {
                return wasKeyDown(KeyType::ESC) || m_receiver->WasKeyDown(CancelKey);
        }
+
        virtual void clearWasKeyPressed()
        {
                m_receiver->clearWasKeyPressed();
@@ -227,17 +354,21 @@ class RealInputHandler : public InputHandler
        {
                m_receiver->clearWasKeyReleased();
        }
+
        virtual void listenForKey(const KeyPress &keyCode)
        {
                m_receiver->listenForKey(keyCode);
        }
-       virtual void dontListenForKeys() { m_receiver->dontListenForKeys(); }
+       virtual void dontListenForKeys()
+       {
+               m_receiver->dontListenForKeys();
+       }
+
        virtual v2s32 getMousePos()
        {
-               if (RenderingEngine::get_raw_device()->getCursorControl()) {
-                       return RenderingEngine::get_raw_device()
-                                       ->getCursorControl()
-                                       ->getPosition();
+               auto control = RenderingEngine::get_raw_device()->getCursorControl();
+               if (control) {
+                       return control->getPosition();
                }
 
                return m_mousepos;
@@ -245,16 +376,18 @@ class RealInputHandler : public InputHandler
 
        virtual void setMousePos(s32 x, s32 y)
        {
-               if (RenderingEngine::get_raw_device()->getCursorControl()) {
-                       RenderingEngine::get_raw_device()
-                                       ->getCursorControl()
-                                       ->setPosition(x, y);
+               auto control = RenderingEngine::get_raw_device()->getCursorControl();
+               if (control) {
+                       control->setPosition(x, y);
                } else {
                        m_mousepos = v2s32(x, y);
                }
        }
 
-       virtual s32 getMouseWheel() { return m_receiver->getMouseWheel(); }
+       virtual s32 getMouseWheel()
+       {
+               return m_receiver->getMouseWheel();
+       }
 
        void clear()
        {
@@ -277,11 +410,13 @@ class RandomInputHandler : public InputHandler
                return true;
        }
 
-       virtual bool isKeyDown(GameKeyType k) { return keydown.count(keycache.key[k]); }
+       virtual bool isKeyDown(GameKeyType k) { return keydown[keycache.key[k]]; }
        virtual bool wasKeyDown(GameKeyType k) { return false; }
        virtual bool wasKeyPressed(GameKeyType k) { return false; }
        virtual bool wasKeyReleased(GameKeyType k) { return false; }
        virtual bool cancelPressed() { return false; }
+       virtual float getMovementSpeed() { return movementSpeed; }
+       virtual float getMovementDirection() { return movementDirection; }
        virtual v2s32 getMousePos() { return mousepos; }
        virtual void setMousePos(s32 x, s32 y) { mousepos = v2s32(x, y); }
 
@@ -292,7 +427,9 @@ class RandomInputHandler : public InputHandler
        s32 Rand(s32 min, s32 max);
 
 private:
-       std::unordered_set<KeyPress> keydown;
+       KeyList keydown;
        v2s32 mousepos;
        v2s32 mousespeed;
+       float movementSpeed;
+       float movementDirection;
 };