#pragma once #include "StarInputEvent.hpp" #include "StarJson.hpp" #include "StarListener.hpp" #include "StarHash.hpp" namespace Star { STAR_CLASS(Input); STAR_EXCEPTION(InputException, StarException); typedef Variant InputVariant; template <> struct hash { size_t operator()(InputVariant const& v) const; }; class Input { public: static Json inputEventToJson(InputEvent const& event); struct KeyBind { Key key = Key::Zero; KeyMod mods = KeyMod::NoMod; uint8_t priority = 0; inline bool operator<(KeyBind const& rhs) const { return priority < rhs.priority; } inline bool operator>(KeyBind const& rhs) const { return priority > rhs.priority; } }; struct MouseBind { MouseButton button = MouseButton::Left; KeyMod mods = KeyMod::NoMod; uint8_t priority = 0; }; struct ControllerBind { unsigned int controller = 0; ControllerButton button = ControllerButton::Invalid; }; typedef MVariant Bind; static Bind bindFromJson(Json const& json); static Json bindToJson(Bind const& bind); struct BindCategory; struct BindEntry { // The internal ID of this entry. String id; // The user-facing name of this entry. String name; // The category this entry belongs to. BindCategory const* category; // The default binds. List defaultBinds; // The user-configured binds. List customBinds; BindEntry(String entryId, Json const& config, BindCategory const& parentCategory); void updated(); }; struct BindRef { KeyMod mods; uint8_t priority = 0; BindEntry* entry = nullptr; // Invalidated on reload, careful! BindRef(BindEntry& bindEntry, KeyBind& keyBind); BindRef(BindEntry& bindEntry, MouseBind& mouseBind); BindRef(BindEntry& bindEntry); }; struct BindCategory { String id; String name; Json config; StableHashMap entries; BindCategory(String categoryId, Json const& categoryConfig); }; struct InputState { unsigned presses = 0; unsigned releases = 0; bool pressed = false; bool held = false; bool released = false; // Calls the passed functions for each press and release. template void forEach(PressFunction&& pressed, ReleaseFunction&& released) { for (size_t i = 0; i != releases; ++i) { pressed(); released(); } } inline void reset() { presses = releases = 0; pressed = released = false; } inline void press() { pressed = ++presses; held = true; } inline void release() { released = ++releases; held = false; } }; struct KeyInputState : InputState { KeyMod mods = KeyMod::NoMod; }; struct MouseInputState : InputState { List pressPositions; List releasePositions; }; typedef InputState ControllerInputState; // Get pointer to the singleton Input instance, if it exists. Otherwise, // returns nullptr. static Input* singletonPtr(); // Gets reference to Input singleton, throws InputException if root // is not initialized. static Input& singleton(); Input(); ~Input(); Input(Input const&) = delete; Input& operator=(Input const&) = delete; List> const& inputEventsThisFrame() const; // Clears input state. Should be done at the very start or end of the client loop. void reset(); void update(); // Handles an input event. bool handleInput(InputEvent const& input, bool gameProcessed); void rebuildMappings(); // Loads input categories and their binds from Assets. void reload(); void setTextInputActive(bool active); Maybe bindDown(String const& categoryId, String const& bindId); bool bindHeld(String const& categoryId, String const& bindId); Maybe bindUp (String const& categoryId, String const& bindId); Maybe keyDown(Key key, Maybe keyMod); bool keyHeld(Key key); Maybe keyUp (Key key); Maybe> mouseDown(MouseButton button); bool mouseHeld(MouseButton button); Maybe> mouseUp (MouseButton button); Vec2I mousePosition() const; void resetBinds(String const& categoryId, String const& bindId); void setBinds(String const& categoryId, String const& bindId, Json const& binds); Json getDefaultBinds(String const& categoryId, String const& bindId); Json getBinds(String const& categoryId, String const& bindId); private: List filterBindEntries(List const& binds, KeyMod mods) const; BindEntry* bindEntryPtr(String const& categoryId, String const& bindId); BindEntry& bindEntry(String const& categoryId, String const& bindId); InputState* bindStatePtr(String const& categoryId, String const& bindId); static Input* s_singleton; // Regenerated on reload. StableHashMap m_bindCategories; // Contains raw pointers to bind entries in categories, so also regenerated on reload. HashMap> m_bindMappings; ListenerPtr m_rootReloadListener; // Per-frame input event storage for Lua. List> m_inputEvents; // Per-frame input state maps. //Input states HashMap m_keyStates; HashMap m_mouseStates; HashMap m_controllerStates; //Bind states HashMap m_bindStates; KeyMod m_pressedMods; bool m_textInputActive; Vec2I m_mousePosition; }; }