diff --git a/CMakeLists.txt b/CMakeLists.txt index 22e17d0..a36a823 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -215,6 +215,27 @@ add_executable(Dicewars_Siedler src/main.cpp src/engine/renderer/model/GuiTextureBuilder.h src/game/ui/components/UiBuildingMenuContainer.cpp src/game/ui/components/UiBuildingMenuContainer.h + src/engine/core/inputsOutputs/stateControl/states/IState.cpp + src/engine/core/inputsOutputs/stateControl/states/IState.h + src/engine/core/inputsOutputs/stateControl/InputUser.cpp + src/engine/core/inputsOutputs/stateControl/InputUser.h + src/engine/core/inputsOutputs/inputs/Keyboard.cpp + src/engine/core/inputsOutputs/inputs/Keyboard.h + src/engine/core/inputsOutputs/inputs/Mouse.cpp + src/engine/core/inputsOutputs/inputs/Mouse.h + src/engine/core/inputsOutputs/inputs/MouseButton.h + src/engine/core/inputsOutputs/stateControl/RegisteredUsers.cpp + src/engine/core/inputsOutputs/stateControl/RegisteredUsers.h + src/engine/core/inputsOutputs/stateControl/StateManager.cpp + src/engine/core/inputsOutputs/stateControl/StateManager.h + src/engine/core/inputsOutputs/stateControl/states/State.cpp + src/engine/core/inputsOutputs/stateControl/states/State.h + src/engine/core/inputsOutputs/stateControl/states/EmptyState.cpp + src/engine/core/inputsOutputs/stateControl/states/EmptyState.h + src/engine/core/inputsOutputs/stateControl/inputUser/GameInputUser.cpp + src/engine/core/inputsOutputs/stateControl/inputUser/GameInputUser.h + src/engine/core/inputsOutputs/stateControl/StateRegistry.cpp + src/engine/core/inputsOutputs/stateControl/StateRegistry.h ) target_compile_options(Dicewars_Siedler PRIVATE diff --git a/src/engine/core/Application.cpp b/src/engine/core/Application.cpp index c3d1a4f..2558ff8 100644 --- a/src/engine/core/Application.cpp +++ b/src/engine/core/Application.cpp @@ -7,6 +7,9 @@ #include "EngineTime.h" #include "../layer/Layer.h" #include "../platform/glfw/InputManager.h" +#include "inputsOutputs/stateControl/StateRegistry.h" +#include "inputsOutputs/stateControl/states/EmptyState.h" +#include "inputsOutputs/stateControl/states/State.h" Application* Application::instance = nullptr; @@ -16,7 +19,7 @@ void Application::updateTime() { EngineTime::totalTime += EngineTime::deltaTime; lastFrame = now; - printf("Frametime: %f\n", EngineTime::deltaTime); + // printf("Frametime: %f\n", EngineTime::deltaTime); } void Application::pushLayer(Layer* layer) { @@ -35,6 +38,9 @@ Application::Application() window_props.VSync = true; window.reset(Window::Create(window_props)); + keyboard = std::make_unique(*window); + mouse = std::make_unique(*window); + stateManager = std::make_unique(StateRegistry::get().empty, StateRegistry::get().game); } Application::~Application() @@ -51,6 +57,10 @@ void Application::run() { } window->OnUpdate(); InputManager::update(); + + mouse->update(); + keyboard->update(); + stateManager->updateState(); } } diff --git a/src/engine/core/Application.h b/src/engine/core/Application.h index 860f8fc..d15915f 100644 --- a/src/engine/core/Application.h +++ b/src/engine/core/Application.h @@ -9,6 +9,10 @@ #include "Window.h" #include "events/EventBus.h" +#include "inputsOutputs/inputs/Keyboard.h" +#include "inputsOutputs/inputs/Mouse.h" +#include "inputsOutputs/stateControl/StateManager.h" +#include "inputsOutputs/stateControl/states/State.h" class Layer; @@ -25,6 +29,11 @@ public: static Application& getInstance(); [[nodiscard]] Window& getWindow() const {return *window;} EventBus& getEventBus() {return eventBus;} + std::unique_ptr keyboard; + std::unique_ptr mouse; + std::unique_ptr stateManager; + + std::shared_ptr gameState; private: bool running = true; std::unique_ptr window; diff --git a/src/engine/core/gui/uiComponent/UiButton.cpp b/src/engine/core/gui/uiComponent/UiButton.cpp index 2b7b587..f64af0f 100644 --- a/src/engine/core/gui/uiComponent/UiButton.cpp +++ b/src/engine/core/gui/uiComponent/UiButton.cpp @@ -38,9 +38,9 @@ void UiButton::onCollectRenderData(UiRenderBundle &uiRenderBundle) { VisualStyle UiButton::getVisualStyle() const { switch (state) { - case UiState::PRESSED: + case UiElementState::PRESSED: return {1.0f, glm::vec3(0.3, 0.6, 1.0), 0.15f}; - case UiState::HOVERED: + case UiElementState::HOVERED: return {1.15f, glm::vec3(0.0f), 0.0f}; default: return {}; diff --git a/src/engine/core/gui/uiComponent/UiComponent.cpp b/src/engine/core/gui/uiComponent/UiComponent.cpp index ddbb732..935257b 100644 --- a/src/engine/core/gui/uiComponent/UiComponent.cpp +++ b/src/engine/core/gui/uiComponent/UiComponent.cpp @@ -59,15 +59,15 @@ void UiComponent::onUpdate(float) { bool releasedFrame = InputManager::isMouseButtonReleased(GLFW_MOUSE_BUTTON_LEFT); if (pressedThisFrame) { - state = UiState::PRESSED; + state = UiElementState::PRESSED; } else if (releasedFrame) { - if (state == UiState::PRESSED && hoveredNow) { + if (state == UiElementState::PRESSED && hoveredNow) { onClick(); } - state = hoveredNow ? UiState::HOVERED : UiState::NORMAL; + state = hoveredNow ? UiElementState::HOVERED : UiElementState::NORMAL; } else { - if (state != UiState::PRESSED) - state = hoveredNow ? UiState::HOVERED : UiState::NORMAL; + if (state != UiElementState::PRESSED) + state = hoveredNow ? UiElementState::HOVERED : UiElementState::NORMAL; } } diff --git a/src/engine/core/gui/uiComponent/UiComponent.h b/src/engine/core/gui/uiComponent/UiComponent.h index c380754..edf7650 100644 --- a/src/engine/core/gui/uiComponent/UiComponent.h +++ b/src/engine/core/gui/uiComponent/UiComponent.h @@ -11,7 +11,7 @@ #include "UiRenderBundle.h" #include "../../../renderer/model/GUITexture.h" -enum class UiState { +enum class UiElementState { NORMAL, HOVERED, PRESSED }; @@ -38,13 +38,13 @@ public: uiPositioner.setLayout(style); } - [[nodiscard]] bool isHovered() const { return state == UiState::HOVERED; } + [[nodiscard]] bool isHovered() const { return state == UiElementState::HOVERED; } virtual void onClick() {}; UiComponent* getChildAtIndex(size_t t) const; protected: bool visible = true; - UiState state = UiState::NORMAL; + UiElementState state = UiElementState::NORMAL; virtual void onUpdate(float ); virtual void onCollectRenderData(UiRenderBundle& uiRenderBundle) {} diff --git a/src/engine/core/inputsOutputs/inputs/Keyboard.cpp b/src/engine/core/inputsOutputs/inputs/Keyboard.cpp new file mode 100644 index 0000000..5f3ee9f --- /dev/null +++ b/src/engine/core/inputsOutputs/inputs/Keyboard.cpp @@ -0,0 +1,68 @@ +// +// Created by sebastian on 15.02.26. +// + +#include "Keyboard.h" +#include "../../Window.h" +#include "../../Application.h" +#include + +Keyboard::Keyboard(Window &window) { + addKeyListener(window); + addTextListener(window); +} + +void Keyboard::update() { + keysPressedThisFrame.clear(); + keysReleasedThisFrame.clear(); + keysRepeatedThisFrame.clear(); + charsThisFrame.clear(); +} + +bool Keyboard::isKeyDown(int key) { + return keysDown.contains(key); +} + +const std::vector & Keyboard::getCharsThisFrame() { + return charsThisFrame; +} + +bool Keyboard::keyPressEvent(int key) { + return keysPressedThisFrame.contains(key); +} + +bool Keyboard::keyPressEvent(int key, bool checkRepeats) { + return keysPressedThisFrame.contains(key) || (checkRepeats && keysRepeatedThisFrame.contains(key)); +} + +bool Keyboard::keyReleaseEvent(int key) { + return keysReleasedThisFrame.contains(key); +} + +void Keyboard::addTextListener(Window &window) { + glfwSetCharCallback(static_cast(window.GetNativeWindow()), [](GLFWwindow *window, const unsigned int key) { + Application::getInstance().keyboard->charsThisFrame.push_back(static_cast(key)); + }); +} + +void Keyboard::addKeyListener(Window &window) { + glfwSetKeyCallback(static_cast(window.GetNativeWindow()), [](GLFWwindow *window, int key, int scancode, int action, int mods) { + if (action == GLFW_PRESS) { + Application::getInstance().keyboard->reportKeyPress(key); + } else if (action == GLFW_RELEASE) { + Application::getInstance().keyboard->reportKeyRelease(key); + } else if (action == GLFW_REPEAT) { + Application::getInstance().keyboard->keysRepeatedThisFrame.insert(key); + } + }); +} + +void Keyboard::reportKeyPress(int key) { + keysDown.insert(key); + keysPressedThisFrame.insert(key); +} + +void Keyboard::reportKeyRelease(int key) { + keysDown.erase(key); + keysReleasedThisFrame.insert(key); +} diff --git a/src/engine/core/inputsOutputs/inputs/Keyboard.h b/src/engine/core/inputsOutputs/inputs/Keyboard.h new file mode 100644 index 0000000..e705a27 --- /dev/null +++ b/src/engine/core/inputsOutputs/inputs/Keyboard.h @@ -0,0 +1,38 @@ +// +// Created by sebastian on 15.02.26. +// + +#ifndef DICEWARS_SIEDLER_KEYBOARD_H +#define DICEWARS_SIEDLER_KEYBOARD_H +#include +#include + + +class Window; + +class Keyboard { +public: + explicit Keyboard(Window& window); + + void update(); + bool isKeyDown(int key); + const std::vector& getCharsThisFrame(); + bool keyPressEvent(int key); + bool keyPressEvent(int key, bool checkRepeats); + bool keyReleaseEvent(int key); +private: + std::unordered_set keysPressedThisFrame; + std::unordered_set keysRepeatedThisFrame; + std::unordered_set keysReleasedThisFrame; + std::unordered_set keysDown; + std::vector charsThisFrame; + + static void addTextListener(Window& window); + + static void addKeyListener(Window& window); + void reportKeyPress(int key); + void reportKeyRelease(int key); +}; + + +#endif //DICEWARS_SIEDLER_KEYBOARD_H \ No newline at end of file diff --git a/src/engine/core/inputsOutputs/inputs/Mouse.cpp b/src/engine/core/inputsOutputs/inputs/Mouse.cpp new file mode 100644 index 0000000..c24671c --- /dev/null +++ b/src/engine/core/inputsOutputs/inputs/Mouse.cpp @@ -0,0 +1,107 @@ +// +// Created by sebastian on 15.02.26. +// + +#include "Mouse.h" +#include "../../Window.h" +#include "../../Application.h" +#include + +Mouse::Mouse(Window &window) { + addMoveListener(window); + addScrollListener(window); + addClickListener(window); +} + +bool Mouse::isButtonDown(MouseButton button) { + return buttonsDown.contains(MouseButtonToInt(button)); +} + +bool Mouse::isClickEvent(MouseButton button) { + return buttonsClickedThisFrame.contains(MouseButtonToInt(button)); +} + +bool Mouse::isReleaseEvent(MouseButton button) { + return buttonsReleasedThisFrame.contains(MouseButtonToInt(button)); +} + +float Mouse::getX() { + return x; +} + +float Mouse::getY() { + return y; +} + +float Mouse::getDX() { + return dx; +} + +float Mouse::getDY() { + return dy; +} + +float Mouse::getScroll() { + return scroll; +} + +void Mouse::update() { + buttonsClickedThisFrame.clear(); + buttonsReleasedThisFrame.clear(); + updateDetlas(); + scroll = 0.0f; +} + +void Mouse::reportButtonClick(int button) { + buttonsDown.insert(button); + buttonsClickedThisFrame.insert(button); +} + +void Mouse::reportButtonRelease(int button) { + buttonsDown.erase(button); + buttonsReleasedThisFrame.insert(button); +} + +void Mouse::updateDetlas() { + dx = x - lastX; + dy = y - lastY; + lastX = x; + lastY = y; +} + +void Mouse::addMoveListener(Window &window) { + glfwSetCursorPosCallback(static_cast(window.GetNativeWindow()), [](GLFWwindow *window, double xpos, double ypos) { + Application::getInstance().mouse->x = static_cast(xpos / Application::getInstance().getWindow().GetWidth() ); + Application::getInstance().mouse->y = static_cast(ypos / Application::getInstance().getWindow().GetHeight()); + }); +} + +void Mouse::addScrollListener(Window &window) { + glfwSetScrollCallback(static_cast(window.GetNativeWindow()), [](GLFWwindow *window, double xoff, double yoff) { + Application::getInstance().mouse->scroll = static_cast(yoff); + }); +} + +void Mouse::addClickListener(Window &window) { + glfwSetMouseButtonCallback(static_cast(window.GetNativeWindow()), [](GLFWwindow *window, int button, int action, int mods) { + if (action == GLFW_PRESS) { + Application::getInstance().mouse->reportButtonClick(button); + } else if (action == GLFW_RELEASE) { + Application::getInstance().mouse->reportButtonRelease(button); + } + }); +} + +int Mouse::MouseButtonToInt(MouseButton button) { + int glfwButton = 0; + switch (button) { + case MouseButton::LEFT: glfwButton = GLFW_MOUSE_BUTTON_LEFT; break; + case MouseButton::RIGHT: glfwButton = GLFW_MOUSE_BUTTON_RIGHT; break; + case MouseButton::MIDDLE: glfwButton = GLFW_MOUSE_BUTTON_MIDDLE; break; + } + return glfwButton; +} + + + + diff --git a/src/engine/core/inputsOutputs/inputs/Mouse.h b/src/engine/core/inputsOutputs/inputs/Mouse.h new file mode 100644 index 0000000..b62bfbc --- /dev/null +++ b/src/engine/core/inputsOutputs/inputs/Mouse.h @@ -0,0 +1,47 @@ +// +// Created by sebastian on 15.02.26. +// + +#ifndef DICEWARS_SIEDLER_MOUSE_H +#define DICEWARS_SIEDLER_MOUSE_H +#include + +#include "MouseButton.h" + +class Window; + +class Mouse { +public: + explicit Mouse(Window& window); + bool isButtonDown(MouseButton button); + bool isClickEvent(MouseButton button); + bool isReleaseEvent(MouseButton button); + float getX(); + float getY(); + float getDX(); + float getDY(); + float getScroll(); + void update(); +private: + std::unordered_set buttonsDown; + std::unordered_set buttonsClickedThisFrame; + std::unordered_set buttonsReleasedThisFrame; + + float x, y; + float dx, dy; + float scroll; + float lastX, lastY; + + void reportButtonClick(int button); + void reportButtonRelease(int button); + void updateDetlas(); + + static void addMoveListener(Window& window); + static void addScrollListener(Window& window); + static void addClickListener(Window& window); + + static int MouseButtonToInt(MouseButton button); +}; + + +#endif //DICEWARS_SIEDLER_MOUSE_H \ No newline at end of file diff --git a/src/engine/core/inputsOutputs/inputs/MouseButton.h b/src/engine/core/inputsOutputs/inputs/MouseButton.h new file mode 100644 index 0000000..668a74c --- /dev/null +++ b/src/engine/core/inputsOutputs/inputs/MouseButton.h @@ -0,0 +1,14 @@ +// +// Created by sebastian on 15.02.26. +// + +#ifndef DICEWARS_SIEDLER_MOUSEBUTTON_H +#define DICEWARS_SIEDLER_MOUSEBUTTON_H + + +enum class MouseButton { + LEFT, + RIGHT, + MIDDLE +}; +#endif //DICEWARS_SIEDLER_MOUSEBUTTON_H \ No newline at end of file diff --git a/src/engine/core/inputsOutputs/stateControl/InputUser.cpp b/src/engine/core/inputsOutputs/stateControl/InputUser.cpp new file mode 100644 index 0000000..c9f8cdf --- /dev/null +++ b/src/engine/core/inputsOutputs/stateControl/InputUser.cpp @@ -0,0 +1,5 @@ +// +// Created by sebastian on 15.02.26. +// + +#include "InputUser.h" \ No newline at end of file diff --git a/src/engine/core/inputsOutputs/stateControl/InputUser.h b/src/engine/core/inputsOutputs/stateControl/InputUser.h new file mode 100644 index 0000000..8229838 --- /dev/null +++ b/src/engine/core/inputsOutputs/stateControl/InputUser.h @@ -0,0 +1,29 @@ +// +// Created by sebastian on 15.02.26. +// + +#ifndef DICEWARS_SIEDLER_INPUTUSER_H +#define DICEWARS_SIEDLER_INPUTUSER_H +#include "../inputs/Keyboard.h" + + +class Mouse; + +class InputUser { +protected: + bool mouseEnabled = true; + bool keyboardEnabled = true; +public: + virtual ~InputUser() = default; + + void enableKeyboard(bool enable) {keyboardEnabled = enable;} + void enableMouse(bool enable) {mouseEnabled = enable;} + + bool isKeyboardEnabled() const {return keyboardEnabled;} + bool isMouseEnabled() const {return mouseEnabled;} + + bool isFullyEnabled() const {return keyboardEnabled && mouseEnabled;} +}; + + +#endif //DICEWARS_SIEDLER_INPUTUSER_H \ No newline at end of file diff --git a/src/engine/core/inputsOutputs/stateControl/RegisteredUsers.cpp b/src/engine/core/inputsOutputs/stateControl/RegisteredUsers.cpp new file mode 100644 index 0000000..85aa00a --- /dev/null +++ b/src/engine/core/inputsOutputs/stateControl/RegisteredUsers.cpp @@ -0,0 +1,54 @@ +// +// Created by sebastian on 15.02.26. +// + +#include "RegisteredUsers.h" + +void RegisteredUsers::registerMouseUser(IState *state, InputUser* user) { + registerInputUser(state, user, registeredMouseUsers); +} + +void RegisteredUsers::registerKeyboardUser(IState *state, InputUser *user) { + registerInputUser(state, user, registeredKeyboardUsers); +} + +void RegisteredUsers::registerUser(IState *state, InputUser *user) { + registerInputUser(state, user, registeredKeyboardUsers); + registerInputUser(state, user, registeredMouseUsers); +} + +void RegisteredUsers::initState(IState *state) { + enableMouseUsers(state, true); + enableKeyboardUsers(state, true); +} + +void RegisteredUsers::endState(IState *state) { + enableMouseUsers(state, false); + enableKeyboardUsers(state, false); +} + +void RegisteredUsers::registerInputUser(IState *state, InputUser* user, + std::unordered_map> ®isteredUsers) { + + registeredUsers[state].insert(user); +} + +void RegisteredUsers::enableMouseUsers(IState *state, bool enable) { + auto it = registeredMouseUsers.find(state); + if (it == registeredMouseUsers.end()) + return; + + for (InputUser *user : it->second) { + user->enableMouse(enable); + } +} + +void RegisteredUsers::enableKeyboardUsers(IState *state, bool enable) { + auto it = registeredKeyboardUsers.find(state); + if (it == registeredKeyboardUsers.end()) + return; + + for (InputUser *user : it->second) { + user->enableKeyboard(enable); + } +} diff --git a/src/engine/core/inputsOutputs/stateControl/RegisteredUsers.h b/src/engine/core/inputsOutputs/stateControl/RegisteredUsers.h new file mode 100644 index 0000000..73f8261 --- /dev/null +++ b/src/engine/core/inputsOutputs/stateControl/RegisteredUsers.h @@ -0,0 +1,32 @@ +// +// Created by sebastian on 15.02.26. +// + +#ifndef DICEWARS_SIEDLER_REGISTEREDUSERS_H +#define DICEWARS_SIEDLER_REGISTEREDUSERS_H +#include +#include + +#include "InputUser.h" +#include "states/IState.h" + + +class RegisteredUsers { +public: + void registerMouseUser(IState* state, InputUser* user); + void registerKeyboardUser(IState* state, InputUser* user); + void registerUser(IState* state, InputUser* user); + void initState(IState* state); + void endState(IState* state); +private: + std::unordered_map> registeredMouseUsers; + std::unordered_map> registeredKeyboardUsers; + + static void registerInputUser(IState* state, InputUser* user, std::unordered_map>& registeredUsers); + + void enableMouseUsers(IState* state, bool enable); + void enableKeyboardUsers(IState* state, bool enable); +}; + + +#endif //DICEWARS_SIEDLER_REGISTEREDUSERS_H \ No newline at end of file diff --git a/src/engine/core/inputsOutputs/stateControl/StateManager.cpp b/src/engine/core/inputsOutputs/stateControl/StateManager.cpp new file mode 100644 index 0000000..400c5c8 --- /dev/null +++ b/src/engine/core/inputsOutputs/stateControl/StateManager.cpp @@ -0,0 +1,76 @@ +// +// Created by sebastian on 15.02.26. +// + +#include "StateManager.h" + +#include +#include + +void StateManager::suggestState(std::shared_ptr state, bool waitForEndRequest) { + QueuedState queuedState(state, waitForEndRequest); + sortStateIntoQueue(queuedState); +} + +void StateManager::endState(std::shared_ptr state) { + queuedStates.erase(std::remove_if(queuedStates.begin(), queuedStates.end(), [&](const QueuedState &queuedState) { + return queuedState.state == state; + }), queuedStates.end()); +} + +void StateManager::updateState() { + auto nextState = getNextState(); + cleanQueue(); + if (currentState == nextState) { + return; + } + switchState(nextState); +} + +void StateManager::registerUser(InputUser *user, std::initializer_list> states) { + for (auto& state : states) { + registeredUsers.registerUser(state.get(), user); + } +} + +void StateManager::registerKeyboardUser(InputUser *user, std::initializer_list> states) { + for (auto& state : states) { + registeredUsers.registerKeyboardUser(state.get(), user); + } +} + +void StateManager::registerMouseUser(InputUser *user, std::initializer_list> states) { + for (auto& state : states) { + registeredUsers.registerMouseUser(state.get(), user); + } +} + +void StateManager::sortStateIntoQueue(const QueuedState &queued_state) { + auto it = std::ranges::find_if(queuedStates, + [&](const QueuedState &s) { + return s.state->getPriority() > queued_state.state->getPriority(); + }); + + queuedStates.insert(it, queued_state); +} + +std::shared_ptr StateManager::getNextState() { + if (queuedStates.empty()) { + return defaultState; + } + return queuedStates.front().state; +} + +void StateManager::cleanQueue() { + queuedStates.erase( + std::remove_if(queuedStates.begin(), queuedStates.end(), + [](const QueuedState& s) { return !s.waitForEndRequest; }), + queuedStates.end() + ); +} + +void StateManager::switchState(std::shared_ptr newState) { + registeredUsers.endState(currentState.get()); + currentState = std::move(newState); + registeredUsers.initState(currentState.get()); +} diff --git a/src/engine/core/inputsOutputs/stateControl/StateManager.h b/src/engine/core/inputsOutputs/stateControl/StateManager.h new file mode 100644 index 0000000..b66cff2 --- /dev/null +++ b/src/engine/core/inputsOutputs/stateControl/StateManager.h @@ -0,0 +1,47 @@ +// +// Created by sebastian on 15.02.26. +// + +#ifndef DICEWARS_SIEDLER_STATEMANAGER_H +#define DICEWARS_SIEDLER_STATEMANAGER_H +#include +#include + +#include "states/IState.h" +#include "RegisteredUsers.h" + + +class StateManager { +public: + StateManager(std::shared_ptr defaultState, std::shared_ptr initialState) : defaultState(defaultState), currentState(initialState) { + suggestState(initialState, true); + }; + + void suggestState(std::shared_ptr state, bool waitForEndRequest); + void endState(std::shared_ptr state); + std::shared_ptr getCurrentState() const {return currentState;} + void updateState(); + + void registerUser(InputUser* user, std::initializer_list> states); + void registerKeyboardUser(InputUser* user, std::initializer_list> states); + void registerMouseUser(InputUser* user, std::initializer_list> states); +private: + struct QueuedState { + std::shared_ptr state; + bool waitForEndRequest; + }; + + std::shared_ptr defaultState; + std::shared_ptr currentState; + + RegisteredUsers registeredUsers; + std::vector queuedStates; + + void sortStateIntoQueue(const QueuedState& queued_state); + std::shared_ptr getNextState(); + void cleanQueue(); + void switchState(std::shared_ptr newState); +}; + + +#endif //DICEWARS_SIEDLER_STATEMANAGER_H \ No newline at end of file diff --git a/src/engine/core/inputsOutputs/stateControl/StateRegistry.cpp b/src/engine/core/inputsOutputs/stateControl/StateRegistry.cpp new file mode 100644 index 0000000..193c6ab --- /dev/null +++ b/src/engine/core/inputsOutputs/stateControl/StateRegistry.cpp @@ -0,0 +1,5 @@ +// +// Created by sebastian on 15.02.26. +// + +#include "StateRegistry.h" \ No newline at end of file diff --git a/src/engine/core/inputsOutputs/stateControl/StateRegistry.h b/src/engine/core/inputsOutputs/stateControl/StateRegistry.h new file mode 100644 index 0000000..69efc72 --- /dev/null +++ b/src/engine/core/inputsOutputs/stateControl/StateRegistry.h @@ -0,0 +1,37 @@ +// +// Created by sebastian on 15.02.26. +// + +#ifndef DICEWARS_SIEDLER_STATEREGISTRY_H +#define DICEWARS_SIEDLER_STATEREGISTRY_H +#include + +#include "states/EmptyState.h" +#include "states/State.h" + + +class StateRegistry { +public: + static StateRegistry& get() { + static StateRegistry instance; + return instance; + } + + std::shared_ptr game; + std::shared_ptr empty; + std::shared_ptr uiState; + +private: + StateRegistry() { + game = std::make_shared(); + empty = std::make_shared(); + uiState = std::make_shared(); + } + + // Keine Kopien erlaubt +StateRegistry(const StateRegistry&) = delete; + StateRegistry& operator=(const StateRegistry&) = delete; +}; + + +#endif //DICEWARS_SIEDLER_STATEREGISTRY_H \ No newline at end of file diff --git a/src/engine/core/inputsOutputs/stateControl/inputUser/GameInputUser.cpp b/src/engine/core/inputsOutputs/stateControl/inputUser/GameInputUser.cpp new file mode 100644 index 0000000..1d41d71 --- /dev/null +++ b/src/engine/core/inputsOutputs/stateControl/inputUser/GameInputUser.cpp @@ -0,0 +1,5 @@ +// +// Created by sebastian on 15.02.26. +// + +#include "GameInputUser.h" \ No newline at end of file diff --git a/src/engine/core/inputsOutputs/stateControl/inputUser/GameInputUser.h b/src/engine/core/inputsOutputs/stateControl/inputUser/GameInputUser.h new file mode 100644 index 0000000..669aa69 --- /dev/null +++ b/src/engine/core/inputsOutputs/stateControl/inputUser/GameInputUser.h @@ -0,0 +1,41 @@ +// +// Created by sebastian on 15.02.26. +// + +#ifndef DICEWARS_SIEDLER_GAMEINPUTUSER_H +#define DICEWARS_SIEDLER_GAMEINPUTUSER_H +#include "../InputUser.h" +#include "../../../Application.h" +#include "../../../../../game/hexWorld/ecs/systems/TileHighlightSystem.h" + + +class GameInputUser : public InputUser { +public: + GameInputUser(TileHighlightSystem& highlightSystem, + EntityManager& em, + MousePicker& picker, + Camera& camera, + GameMode& gameMode) + : highlightSystem(highlightSystem), + em(em), + picker(picker), + camera(camera), + gameMode(gameMode) {} + + void update() { + if (!isMouseEnabled()) { + return; + } + + highlightSystem.update(em, picker, camera, gameMode); + } +private: + TileHighlightSystem& highlightSystem; + EntityManager& em; + MousePicker& picker; + Camera& camera; + GameMode& gameMode; +}; + + +#endif //DICEWARS_SIEDLER_GAMEINPUTUSER_H \ No newline at end of file diff --git a/src/engine/core/inputsOutputs/stateControl/states/EmptyState.cpp b/src/engine/core/inputsOutputs/stateControl/states/EmptyState.cpp new file mode 100644 index 0000000..09da6ef --- /dev/null +++ b/src/engine/core/inputsOutputs/stateControl/states/EmptyState.cpp @@ -0,0 +1,5 @@ +// +// Created by sebastian on 15.02.26. +// + +#include "EmptyState.h" \ No newline at end of file diff --git a/src/engine/core/inputsOutputs/stateControl/states/EmptyState.h b/src/engine/core/inputsOutputs/stateControl/states/EmptyState.h new file mode 100644 index 0000000..0e96d8b --- /dev/null +++ b/src/engine/core/inputsOutputs/stateControl/states/EmptyState.h @@ -0,0 +1,17 @@ +// +// Created by sebastian on 15.02.26. +// + +#ifndef DICEWARS_SIEDLER_EMPTYSTATE_H +#define DICEWARS_SIEDLER_EMPTYSTATE_H +#include + +#include "IState.h" + + +class EmptyState : public IState{ + int getPriority() override { return std::numeric_limits::max(); } +}; + + +#endif //DICEWARS_SIEDLER_EMPTYSTATE_H \ No newline at end of file diff --git a/src/engine/core/inputsOutputs/stateControl/states/IState.cpp b/src/engine/core/inputsOutputs/stateControl/states/IState.cpp new file mode 100644 index 0000000..ed8cc71 --- /dev/null +++ b/src/engine/core/inputsOutputs/stateControl/states/IState.cpp @@ -0,0 +1,5 @@ +// +// Created by sebastian on 15.02.26. +// + +#include "IState.h" diff --git a/src/engine/core/inputsOutputs/stateControl/states/IState.h b/src/engine/core/inputsOutputs/stateControl/states/IState.h new file mode 100644 index 0000000..2106981 --- /dev/null +++ b/src/engine/core/inputsOutputs/stateControl/states/IState.h @@ -0,0 +1,17 @@ +// +// Created by sebastian on 15.02.26. +// + +#ifndef DICEWARS_SIEDLER_ISTATE_H +#define DICEWARS_SIEDLER_ISTATE_H + + +class IState { +public: + virtual ~IState() = default; + + virtual int getPriority() = 0; +}; + + +#endif //DICEWARS_SIEDLER_ISTATE_H \ No newline at end of file diff --git a/src/engine/core/inputsOutputs/stateControl/states/State.cpp b/src/engine/core/inputsOutputs/stateControl/states/State.cpp new file mode 100644 index 0000000..977915c --- /dev/null +++ b/src/engine/core/inputsOutputs/stateControl/states/State.cpp @@ -0,0 +1,5 @@ +// +// Created by sebastian on 15.02.26. +// + +#include "State.h" \ No newline at end of file diff --git a/src/engine/core/inputsOutputs/stateControl/states/State.h b/src/engine/core/inputsOutputs/stateControl/states/State.h new file mode 100644 index 0000000..9ab044e --- /dev/null +++ b/src/engine/core/inputsOutputs/stateControl/states/State.h @@ -0,0 +1,26 @@ +// +// Created by sebastian on 15.02.26. +// + +#ifndef DICEWARS_SIEDLER_STATE_H +#define DICEWARS_SIEDLER_STATE_H +#include "IState.h" + + +class GameState : public IState { +public: + int getPriority() override { return 10; } +}; + +class UiState : public IState { +public: + int getPriority() override { return 5; } +}; + +class ModalState : public IState { +public: + int getPriority() override { return 0; } // höchste Priorität +}; + + +#endif //DICEWARS_SIEDLER_STATE_H \ No newline at end of file diff --git a/src/game/GameLayer.cpp b/src/game/GameLayer.cpp index 7801a1e..542715a 100644 --- a/src/game/GameLayer.cpp +++ b/src/game/GameLayer.cpp @@ -9,6 +9,7 @@ #include "../engine/core/animations/AnimationSystem.h" #include "../engine/core/ECS/ModelStateComponent.h" #include "../engine/core/ECS/WorldSpriteComponent.h" +#include "../engine/core/inputsOutputs/stateControl/StateRegistry.h" #include "../engine/platform/glfw/InputManager.h" #include "../engine/renderer/Renderer.h" #include "../engine/renderer/loader/OBJLoader.h" @@ -66,6 +67,9 @@ void GameLayer::onAttach() events.subscribe([this](const TurnChangedEvent& event) { ProducingSystem::onTurnEnded(*entityManager); }); + + gameInputUser = std::make_unique(*tileHighlightSystem, *entityManager, *mousePicker, *camera, *gameMode); + Application::getInstance().stateManager->registerMouseUser(gameInputUser.get(), {StateRegistry::get().game}); } void GameLayer::onDetach() @@ -100,7 +104,11 @@ void GameLayer::onUpdate() AnimationSystem::update(*entityManager, EngineTime::totalTime); camera->move(moveDir, 0.5f); - tileHighlightSystem->update(*entityManager, *mousePicker, *camera, *gameMode); + if (gameInputUser->isMouseEnabled()) { + tileHighlightSystem->update(*entityManager, *mousePicker, *camera, *gameMode); + } else { + tileHighlightSystem->reset(*entityManager); + } buildingPlacementSystem->update(*entityManager, *gameMode, 0, *turnState); CollectResourceSystem::update(*entityManager, *gameMode); RenderSystem::render(*entityManager, *renderer); diff --git a/src/game/GameLayer.h b/src/game/GameLayer.h index 4a25ace..54e0987 100644 --- a/src/game/GameLayer.h +++ b/src/game/GameLayer.h @@ -5,6 +5,7 @@ #ifndef DICEWARS_SIEDLER_GAMELAYER_H #define DICEWARS_SIEDLER_GAMELAYER_H #include "../engine/core/ECS/RenderSystem.h" +#include "../engine/core/inputsOutputs/stateControl/inputUser/GameInputUser.h" #include "../engine/layer/Layer.h" #include "../engine/platform/glfw/MousePicker.h" #include "../engine/renderer/Renderer.h" @@ -48,6 +49,8 @@ private: std::unique_ptr turnSystem = std::make_unique(); std::unique_ptr turnState = std::make_unique(); + std::unique_ptr gameInputUser; + EntityID testEntity; }; diff --git a/src/game/UILayer.cpp b/src/game/UILayer.cpp index efff703..7cf2406 100644 --- a/src/game/UILayer.cpp +++ b/src/game/UILayer.cpp @@ -12,6 +12,7 @@ #include "../engine/core/gui/uiComponent/UiButton.h" #include "../engine/core/gui/uiComponent/UiImage.h" #include "../engine/core/gui/uiComponent/UiText.h" +#include "../engine/core/inputsOutputs/stateControl/StateRegistry.h" #include "../engine/renderer/loader/AssetManager.h" #include "../engine/renderer/loader/Loader.h" #include "../engine/renderer/model/GUITexture.h" @@ -139,6 +140,11 @@ void UILayer::onUpdate() { auto renderTexts = renderBundle.getGUITexts(); textRenderer->renderGuiTexts(renderTexts); + if (rootContainer->isMouseOver(Application::getInstance().mouse->getX(), Application::getInstance().mouse->getY())) { + Application::getInstance().stateManager->suggestState(StateRegistry::get().uiState, false); + } else { + Application::getInstance().stateManager->suggestState(StateRegistry::get().game, false); + } } void UILayer::onDetach() { diff --git a/src/game/hexWorld/ecs/systems/TileHighlightSystem.cpp b/src/game/hexWorld/ecs/systems/TileHighlightSystem.cpp index 258fd71..66f499c 100644 --- a/src/game/hexWorld/ecs/systems/TileHighlightSystem.cpp +++ b/src/game/hexWorld/ecs/systems/TileHighlightSystem.cpp @@ -42,6 +42,15 @@ void TileHighlightSystem::update(EntityManager &entityManager, const MousePicker } } +void TileHighlightSystem::reset(EntityManager &entityManager) { + for (EntityID entityID : entityManager.getAllEntities()) { + auto tileRenderComponent = entityManager.getComponent(entityID); + if (tileRenderComponent) { + tileRenderComponent->isHighlighted = false; + } + } +} + bool TileHighlightSystem::intersectTile(const glm::vec3 &rayOrigin, const glm::vec3 &rayDirection, glm::vec3 worldPos, float hexRadius, glm::vec3& intersectionPoint) { float t = -rayOrigin.y / rayDirection.y; if (t < 0) return false; diff --git a/src/game/hexWorld/ecs/systems/TileHighlightSystem.h b/src/game/hexWorld/ecs/systems/TileHighlightSystem.h index 0f8e089..95f5271 100644 --- a/src/game/hexWorld/ecs/systems/TileHighlightSystem.h +++ b/src/game/hexWorld/ecs/systems/TileHighlightSystem.h @@ -13,6 +13,7 @@ class Camera; class TileHighlightSystem { public: void update(EntityManager &entityManager, const MousePicker &picker, const Camera &camera, GameMode& gameMode); + void reset(EntityManager& entityManager); private: bool intersectTile(const glm::vec3 & rayOrigin, const glm::vec3 & rayDirection, glm::vec3 worldPos, float hexRadius, glm::vec3 &interectionPoint); };