diff --git a/source/application/CMakeLists.txt b/source/application/CMakeLists.txt index 0394655..dbaa236 100644 --- a/source/application/CMakeLists.txt +++ b/source/application/CMakeLists.txt @@ -9,13 +9,11 @@ SET (star_application_HEADERS StarApplication.hpp StarApplicationController.hpp StarMainApplication.hpp - StarInputEvent.hpp StarRenderer.hpp ) SET (star_application_SOURCES StarApplication.cpp - StarInputEvent.cpp StarRenderer.cpp ) diff --git a/source/client/StarClientApplication.cpp b/source/client/StarClientApplication.cpp index 562128c..eedb399 100644 --- a/source/client/StarClientApplication.cpp +++ b/source/client/StarClientApplication.cpp @@ -167,7 +167,7 @@ void ClientApplication::applicationInit(ApplicationControllerPtr appController) m_mainMixer->setVolume(0.5); m_guiContext = make_shared(m_mainMixer->mixer(), appController); - + m_input = make_shared(); auto configuration = m_root->configuration(); bool vsync = configuration->get("vsync").toBool(); diff --git a/source/client/StarClientApplication.hpp b/source/client/StarClientApplication.hpp index 5e577e8..186efaf 100644 --- a/source/client/StarClientApplication.hpp +++ b/source/client/StarClientApplication.hpp @@ -11,6 +11,7 @@ #include "StarErrorScreen.hpp" #include "StarCinematic.hpp" #include "StarKeyBindings.hpp" +#include "StarInput.hpp" #include "StarMainApplication.hpp" namespace Star { @@ -74,6 +75,7 @@ private: // Valid after applicationInit is called MainMixerPtr m_mainMixer; GuiContextPtr m_guiContext; + InputPtr m_input; // Valid after renderInit is called the first time CinematicPtr m_cinematicOverlay; diff --git a/source/core/CMakeLists.txt b/source/core/CMakeLists.txt index cf64c3a..08701d7 100644 --- a/source/core/CMakeLists.txt +++ b/source/core/CMakeLists.txt @@ -40,6 +40,7 @@ SET (star_core_HEADERS StarIdMap.hpp StarImage.hpp StarImageProcessing.hpp + StarInputEvent.hpp StarInterpolation.hpp StarRefPtr.hpp StarIterator.hpp @@ -142,6 +143,7 @@ SET (star_core_SOURCES StarIODevice.cpp StarImage.cpp StarImageProcessing.cpp + StarInputEvent.cpp StarJson.cpp StarJsonBuilder.cpp StarJsonExtra.cpp diff --git a/source/application/StarInputEvent.cpp b/source/core/StarInputEvent.cpp similarity index 100% rename from source/application/StarInputEvent.cpp rename to source/core/StarInputEvent.cpp diff --git a/source/application/StarInputEvent.hpp b/source/core/StarInputEvent.hpp similarity index 100% rename from source/application/StarInputEvent.hpp rename to source/core/StarInputEvent.hpp diff --git a/source/game/CMakeLists.txt b/source/game/CMakeLists.txt index 7328601..97fe8f2 100644 --- a/source/game/CMakeLists.txt +++ b/source/game/CMakeLists.txt @@ -56,6 +56,7 @@ SET (star_game_HEADERS StarGameTypes.hpp StarHumanoid.hpp StarImageMetadataDatabase.hpp + StarInput.hpp StarInteractionTypes.hpp StarInterpolationTracker.hpp StarInventoryTypes.hpp @@ -314,6 +315,7 @@ SET (star_game_SOURCES StarGameTypes.cpp StarHumanoid.cpp StarImageMetadataDatabase.cpp + StarInput.cpp StarInteractionTypes.cpp StarInterpolationTracker.cpp StarInventoryTypes.cpp diff --git a/source/game/StarInput.cpp b/source/game/StarInput.cpp new file mode 100644 index 0000000..0a9b56d --- /dev/null +++ b/source/game/StarInput.cpp @@ -0,0 +1,58 @@ +#include "StarInput.hpp" +#include "StarAssets.hpp" +#include "StarRoot.hpp" + +namespace Star { + +size_t hash::operator()(InputVariant const& v) const { + size_t indexHash = hashOf(v.typeIndex()); + if (auto key = v.ptr()) + hashCombine(indexHash, hashOf(*key)); + else if (auto mButton = v.ptr()) + hashCombine(indexHash, hashOf(*mButton)); + else if (auto cButton = v.ptr()) + hashCombine(indexHash, hashOf(*cButton)); + + return indexHash; +} + +Input* Input::s_singleton; + +Input* Input::singletonPtr() { + return s_singleton; +} + +Input& Input::singleton() { + if (!s_singleton) + throw InputException("Input::singleton() called with no Input instance available"); + else + return *s_singleton; +} + +Input::Input() { + if (s_singleton) + throw InputException("Singleton Input has been constructed twice"); + + s_singleton = this; + +} + +Input::~Input() { + s_singleton = nullptr; +} + +void Input::reset() { + +} + +void Input::reload() { + reset(); + + auto assets = Root::singleton().assets(); + + for (auto& bindPath : assets->scanExtension("binds")) { + Json config = assets->json(bindPath); + } +} + +} \ No newline at end of file diff --git a/source/game/StarInput.hpp b/source/game/StarInput.hpp new file mode 100644 index 0000000..f329cc2 --- /dev/null +++ b/source/game/StarInput.hpp @@ -0,0 +1,160 @@ +#ifndef STAR_INPUT_HPP +#define STAR_INPUT_HPP + +#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: + struct NoBind { + String error; + + NoBind(String err); + }; + + 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 Variant Bind; + + static Bind bindFromJson(Json const& json); + static Json bindToJson(Bind const& bind); + + struct Category; + + struct BindEntry { + // The internal ID of this entry. + String id; + // The category this entry belongs to. + String category; + // The user-facing name of this entry. + String name; + + // The default binds. + List defaultBinds; + // The user-configured binds. + List customBinds; + + BindEntry(Json const& config, Category const& category); + }; + + struct BindRef { + KeyMod mods; + uint8_t priority; + + // Invalidated on reload, careful! + BindEntry* entry; + }; + + struct BindRefSorter { + inline bool operator() (BindRef const& a, BindRef const& b) { + return a.priority > b.priority; + } + }; + + struct BindCategory { + String id; + String name; + Json meta; + + StringMap entries; + + BindCategory(Json const& data); + }; + + struct InputState { + size_t presses = 0; + size_t releases = 0; + + // 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(); + } + } + + constexpr bool held() { + return presses < releases; + } + }; + + // Get pointer to the singleton root instance, if it exists. Otherwise, + // returns nullptr. + static Input* singletonPtr(); + + // Gets reference to GuiContext singleton, throws GuiContextException if root + // is not initialized. + static Input& singleton(); + + Input(); + ~Input(); + + Input(Input const&) = delete; + Input& operator=(Input const&) = delete; + + // Clears input state. Should be done at the end of the client loop. + void reset(); + + // Handles an input event. + bool handleInput(InputEvent const& event); + + // Loads input categories and their binds from Assets. + void reload(); + private: + static Input* s_singleton; + + // Regenerated on reload. + StringMap m_bindCategories; + // Contains raw pointers to bind entries in categories, so also regenerated on reload. + HashMap> m_inputsToBinds; + + ListenerPtr m_rootReloadListener; + + // Per-frame input state maps. + + //Raw input state + HashMap m_inputStates; + //Bind input state + HashMap m_bindStates; + }; +} + +#endif \ No newline at end of file