// IAEngine: 2D Game Engine by IA // Copyright (C) 2025 IASoft (PVT) LTD (oss@iasoft.dev) // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include #include namespace ia::iae { struct OnScreenGamepadState { Vec2 KnobPosition; Vec2 ThumbstickPosition{}; FLOAT32 ThumbstickRadius{}; } g_onScreenGamepadState{}; EXTERN SDL_Window *g_windowHandle; BOOL InputManager::s_keys[256]; BOOL InputManager::s_prevKeys[256]; Vec2 InputManager::s_pointerPosition{}; BOOL InputManager::s_pointerState{}; BOOL InputManager::s_pointerPrevState{}; InputManager::KeyboardGamePadMapping InputManager::s_keyboardGamePadMapping{}; BOOL InputManager::s_buttonA{}; BOOL InputManager::s_buttonB{}; BOOL InputManager::s_buttonC{}; BOOL InputManager::s_buttonD{}; INT16 InputManager::s_verticalAxis{}; INT16 InputManager::s_horizontalAxis{}; BOOL InputManager::s_onScreenGamePadEnabled{}; BOOL InputManager::s_keyboardGamePadEnabled{}; VOID InputManager::Initialize() { memset(s_keys, 0, sizeof(s_keys)); memset(s_prevKeys, 0, sizeof(s_prevKeys)); g_onScreenGamepadState.KnobPosition = {}; g_onScreenGamepadState.ThumbstickRadius = 64.0f; } VOID InputManager::Terminate() { } VOID InputManager::OnSDLEvent(IN SDL_Event *event) { s_pointerPrevState = s_pointerState; memcpy(s_prevKeys, s_keys, sizeof(s_prevKeys)); switch (event->type) { case SDL_EVENT_KEY_DOWN: s_keys[event->key.scancode] = true; break; case SDL_EVENT_KEY_UP: s_keys[event->key.scancode] = false; break; case SDL_EVENT_FINGER_MOTION: case SDL_EVENT_MOUSE_MOTION: s_pointerPosition = {event->motion.x, event->motion.y}; break; case SDL_EVENT_MOUSE_BUTTON_DOWN: case SDL_EVENT_FINGER_DOWN: s_pointerState = true; break; case SDL_EVENT_MOUSE_BUTTON_UP: case SDL_EVENT_FINGER_UP: s_pointerState = false; break; default: break; } } VOID InputManager::SwitchModeToText() { SDL_StartTextInput(g_windowHandle); } VOID InputManager::SwitchModeToAction() { SDL_StopTextInput(g_windowHandle); } BOOL InputManager::IsPointerDown() { return s_pointerState; } Vec2 InputManager::GetPointerPosition() { return s_pointerPosition; } BOOL InputManager::IsKeyDown(IN InputKey key) { return s_keys[(UINT8) key]; } BOOL InputManager::WasKeyPressed(IN InputKey key) { return !s_prevKeys[(UINT8) key] && s_keys[(UINT8) key]; } BOOL InputManager::WasKeyReleased(IN InputKey key) { return s_prevKeys[(UINT8) key] && !s_keys[(UINT8) key]; } VOID InputManager::SetupOnScreenGamePad() { // Initialize // Disable till manually enabled by calling Input_EnableOnScreenGamePad s_onScreenGamePadEnabled = false; } VOID InputManager::SetupKeyboardGamePad(IN CONST KeyboardGamePadMapping &mapping) { s_keyboardGamePadEnabled = true; s_keyboardGamePadMapping = mapping; } BOOL InputManager::GetButtonA() { return s_buttonA; } BOOL InputManager::GetButtonB() { return s_buttonB; } BOOL InputManager::GetButtonC() { return s_buttonC; } BOOL InputManager::GetButtonD() { return s_buttonD; } INT16 InputManager::GetVerticalAxis() { return s_verticalAxis; } INT16 InputManager::GetHorizontalAxis() { return s_horizontalAxis; } IVec2 InputManager::GetDirectionalInput() { return IVec2{GetHorizontalAxis(), GetVerticalAxis()}; } VOID InputManager::SetButtonA(IN BOOL value) { s_buttonA = value; } VOID InputManager::SetButtonB(IN BOOL value) { s_buttonB = value; } VOID InputManager::SetButtonC(IN BOOL value) { s_buttonC = value; } VOID InputManager::SetButtonD(IN BOOL value) { s_buttonD = value; } VOID InputManager::SetVerticalAxis(IN INT16 value) { s_verticalAxis = value; } VOID InputManager::SetHorizontalAxis(IN INT16 value) { s_horizontalAxis = value; } VOID InputManager::Draw() { if (s_onScreenGamePadEnabled) { g_onScreenGamepadState.ThumbstickPosition = Engine::CalculatePercentPosition({92.5f, 90.0f}) - Vec2{0, 32.0f}; Engine::SetRenderState_CameraRelative(false); Engine::SetRenderState_ColorOverlay({0x80, 0x80, 0x80, 0x80}); Engine::DrawCircle(g_onScreenGamepadState.ThumbstickPosition, 0, g_onScreenGamepadState.ThumbstickRadius, 0, 0xFF, 0); Engine::SetRenderState_ColorOverlay({0x20, 0x20, 0x20, 0xB0}); Engine::DrawCircle(g_onScreenGamepadState.ThumbstickPosition + g_onScreenGamepadState.KnobPosition, 0, 16, 0, 0xFF, 0); } } VOID InputManager::Update() { s_buttonA = false; s_buttonB = false; s_buttonC = false; s_buttonD = false; s_verticalAxis = 0; s_horizontalAxis = 0; if (s_onScreenGamePadEnabled) { STATIC CONSTEXPR INT16 DIRECTION_MAP_VERTICAL[] = {0, 1, 1, 1, 0, -1, -1, -1}; STATIC CONSTEXPR INT16 DIRECTION_MAP_HORIZONTAL[] = {1, 1, 0, -1, -1, -1, 0, 1}; if (Engine::Input_IsPointerDown(g_onScreenGamepadState.ThumbstickPosition, g_onScreenGamepadState.ThumbstickRadius)) g_onScreenGamepadState.KnobPosition = Engine::Input_GetPointerPosition() - g_onScreenGamepadState.ThumbstickPosition; else g_onScreenGamepadState.KnobPosition = {}; auto verticalAxis = abs(g_onScreenGamepadState.KnobPosition.y); auto horizontalAxis = abs(g_onScreenGamepadState.KnobPosition.x); verticalAxis = (verticalAxis > FLOAT32_EPSILON) ? g_onScreenGamepadState.KnobPosition.y / ((FLOAT32)g_onScreenGamepadState.ThumbstickRadius) : 0; horizontalAxis = (horizontalAxis > FLOAT32_EPSILON) ? g_onScreenGamepadState.KnobPosition.x / ((FLOAT32)g_onScreenGamepadState.ThumbstickRadius) : 0; if ((abs(verticalAxis) > FLOAT32_EPSILON) || (abs(horizontalAxis) > FLOAT32_EPSILON)) { auto angle = glm::degrees(atan2(verticalAxis, horizontalAxis)); if (angle < 0) angle += 360; const auto t = INT32((angle + 22.5) / 45) % 8; s_verticalAxis = DIRECTION_MAP_VERTICAL[t]; s_horizontalAxis = DIRECTION_MAP_HORIZONTAL[t]; } } if (s_keyboardGamePadEnabled) { s_buttonA |= IsKeyDown(s_keyboardGamePadMapping.ButtonA); s_buttonB |= IsKeyDown(s_keyboardGamePadMapping.ButtonB); s_buttonC |= IsKeyDown(s_keyboardGamePadMapping.ButtonC); s_buttonD |= IsKeyDown(s_keyboardGamePadMapping.ButtonD); s_verticalAxis += IsKeyDown(s_keyboardGamePadMapping.AxisDown) + IsKeyDown(s_keyboardGamePadMapping.AxisUp) * -1; s_horizontalAxis += IsKeyDown(s_keyboardGamePadMapping.AxisRight) + IsKeyDown(s_keyboardGamePadMapping.AxisLeft) * -1; } } BOOL InputManager::IsPointerDown(IN CONST Vec2 &start, IN CONST Vec2 &end) { if (!s_pointerState) return false; return (s_pointerPosition.x >= start.x) && (s_pointerPosition.x <= end.x) && (s_pointerPosition.y >= start.y) && (s_pointerPosition.y <= end.y); } BOOL InputManager::IsPointerDown(IN CONST Vec2 ¢er, IN FLOAT32 radius) { if (!s_pointerState) return false; const auto dx = s_pointerPosition.x - center.x; const auto dy = s_pointerPosition.y - center.y; const auto d = (dx * dx) + (dy * dy); return d <= (radius * radius); } BOOL InputManager::DidPointerClick(IN CONST Vec2 &start, IN CONST Vec2 &end) { return IsPointerDown(start, end) && !s_pointerPrevState; } BOOL InputManager::DidPointerClick(IN CONST Vec2 ¢er, IN FLOAT32 radius) { return IsPointerDown(center, radius) && !s_pointerPrevState; } } // namespace ia::iae namespace ia::iae { VOID Engine::Input_SwitchModeToText() { InputManager::SwitchModeToText(); } VOID Engine::Input_SwitchModeToAction() { InputManager::SwitchModeToAction(); } Vec2 Engine::Input_GetPointerPosition() { return InputManager::GetPointerPosition(); } BOOL Engine::Input_IsPointerDown() { return InputManager::IsPointerDown(); } BOOL Engine::Input_IsPointerDown(IN CONST Vec2 &start, IN CONST Vec2 &end) { return InputManager::IsPointerDown(start, end); } BOOL Engine::Input_DidPointerClick(IN CONST Vec2 &start, IN CONST Vec2 &end) { return InputManager::DidPointerClick(start, end); } BOOL Engine::Input_IsKeyDown(IN InputKey key) { return InputManager::IsKeyDown(key); } BOOL Engine::Input_WasKeyPressed(IN InputKey key) { return InputManager::WasKeyPressed(key); } BOOL Engine::Input_WasKeyReleased(IN InputKey key) { return InputManager::WasKeyReleased(key); } VOID Engine::Input_SetupOnScreenGamePad() { InputManager::SetupOnScreenGamePad(); } VOID Engine::Input_SetupKeyboardGamePad(IN InputKey axisLeft, IN InputKey axisRight, IN InputKey axisDown, IN InputKey axisUp, IN InputKey buttonA, IN InputKey buttonB, IN InputKey buttonC, IN InputKey buttonD) { InputManager::SetupKeyboardGamePad({axisLeft, axisRight, axisDown, axisUp, buttonA, buttonB, buttonC, buttonD}); } BOOL Engine::Input_GetButtonA() { return InputManager::GetButtonA(); } BOOL Engine::Input_GetButtonB() { return InputManager::GetButtonB(); } BOOL Engine::Input_GetButtonC() { return InputManager::GetButtonC(); } BOOL Engine::Input_GetButtonD() { return InputManager::GetButtonD(); } INT16 Engine::Input_GetVerticalAxis() { return InputManager::GetVerticalAxis(); } INT16 Engine::Input_GetHorizontalAxis() { return InputManager::GetHorizontalAxis(); } IVec2 Engine::Input_GetDirectionalInput() { return InputManager::GetDirectionalInput(); } VOID Engine::Input_SetButtonA(IN BOOL value) { InputManager::SetButtonA(value); } VOID Engine::Input_SetButtonB(IN BOOL value) { InputManager::SetButtonB(value); } VOID Engine::Input_SetButtonC(IN BOOL value) { InputManager::SetButtonC(value); } VOID Engine::Input_SetButtonD(IN BOOL value) { InputManager::SetButtonD(value); } VOID Engine::Input_SetVerticalAxis(IN INT16 value) { InputManager::SetVerticalAxis(value); } VOID Engine::Input_SetHorizontalAxis(IN INT16 value) { InputManager::SetHorizontalAxis(value); } BOOL Engine::Input_DidPointerClick(IN CONST Vec2 ¢er, IN FLOAT32 radius) { return InputManager::DidPointerClick(center, radius); } BOOL Engine::Input_IsPointerDown(IN CONST Vec2 ¢er, IN FLOAT32 radius) { return InputManager::IsPointerDown(center, radius); } VOID Engine::Input_EnableOnScreenGamePad() { InputManager::s_onScreenGamePadEnabled = true; } VOID Engine::Input_DisableOnScreenGamePad() { InputManager::s_onScreenGamePadEnabled = false; } } // namespace ia::iae