From 8ebb6c2cb37285ad98bd8427a7ed42399e3e9ab9 Mon Sep 17 00:00:00 2001 From: Deve Date: Sun, 21 May 2023 14:16:14 +0200 Subject: [PATCH] Add SDL game controller support (#105) --- LICENSE.txt | 1 + src/client/clientlauncher.cpp | 2 +- src/client/game.cpp | 32 ++- src/client/inputhandler.cpp | 33 ++- src/client/inputhandler.h | 30 +++ src/client/joystick_controller.cpp | 352 +++++++++++++++++++++++++++++ src/client/joystick_controller.h | 44 ++++ src/client/keycode.h | 2 + src/client/localplayer.cpp | 22 +- src/defaultsettings.cpp | 2 +- src/gui/modalMenu.cpp | 52 ++++- src/gui/modalMenu.h | 3 + textures/base/pack/cursor.png | Bin 0 -> 339 bytes 13 files changed, 558 insertions(+), 17 deletions(-) create mode 100644 textures/base/pack/cursor.png diff --git a/LICENSE.txt b/LICENSE.txt index 0b5d172c1..d26311175 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -25,6 +25,7 @@ MultiCraft Development Team: textures/base/pack/chat_btn.png textures/base/pack/crack_anylength_touch.png textures/base/pack/creative_bg.png + textures/base/pack/cursor.png textures/base/pack/error_screenshot.png textures/base/pack/desc_bg.png textures/base/pack/down_btn.png diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp index b87144382..d40fc3c34 100644 --- a/src/client/clientlauncher.cpp +++ b/src/client/clientlauncher.cpp @@ -558,7 +558,7 @@ void ClientLauncher::main_menu(MainMenuData *menudata) infostream << "Waited for other menus" << std::endl; // Cursor can be non-visible when coming from the game - RenderingEngine::get_raw_device()->getCursorControl()->setVisible(true); + input->setCursorVisible(true); /* show main menu */ GUIEngine mymenu(&input->joystick, guiroot, &g_menumgr, menudata, *kill); diff --git a/src/client/game.cpp b/src/client/game.cpp index 0b7a2dc1b..a05e3ebfe 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -1898,8 +1898,12 @@ void Game::processUserInput(f32 dtime) } #endif + bool doubletap_jump = m_cache_doubletap_jump; +#if defined(_IRR_COMPILE_WITH_SDL_DEVICE_) + doubletap_jump |= input->sdl_game_controller.isActive(); +#endif // Increase timer for double tap of "keymap_jump" - if (m_cache_doubletap_jump && runData.jump_timer <= 0.15f) + if (doubletap_jump && runData.jump_timer <= 0.15f) runData.jump_timer += dtime; processKeyInput(); @@ -2189,7 +2193,12 @@ void Game::toggleFreeMove() void Game::toggleFreeMoveAlt() { - if (m_cache_doubletap_jump && runData.jump_timer < 0.15f && + bool doubletap_jump = m_cache_doubletap_jump; +#if defined(_IRR_COMPILE_WITH_SDL_DEVICE_) + doubletap_jump |= input->sdl_game_controller.isActive(); +#endif + + if (doubletap_jump && runData.jump_timer < 0.15f && (!simple_singleplayer_mode || client->checkPrivilege("fly"))) toggleFreeMove(); @@ -2435,9 +2444,7 @@ void Game::updateCameraDirection(CameraOrientation *cam, float dtime) && !isMenuActive()) || input->isRandom()) { if (!input->isRandom()) { - // Mac OSX gets upset if this is set every frame - if (device->getCursorControl()->isVisible()) - device->getCursorControl()->setVisible(false); + input->setCursorVisible(false); } if (m_first_loop_after_window_activation) { @@ -2451,10 +2458,7 @@ void Game::updateCameraDirection(CameraOrientation *cam, float dtime) } else { - // Mac OSX gets upset if this is set every frame - if (!device->getCursorControl()->isVisible()) - device->getCursorControl()->setVisible(true); - + input->setCursorVisible(true); m_first_loop_after_window_activation = true; } @@ -2485,8 +2489,13 @@ void Game::updateCameraOrientation(CameraOrientation *cam, float dtime) if (m_cache_enable_joysticks) { f32 c = m_cache_joystick_frustum_sensitivity * (1.f / 32767.f) * dtime; +#if defined(_IRR_COMPILE_WITH_SDL_DEVICE_) + cam->camera_yaw -= input->sdl_game_controller.getCameraYaw() * c; + cam->camera_pitch += input->sdl_game_controller.getCameraPitch() * c; +#else cam->camera_yaw -= input->joystick.getAxisWithoutDead(JA_FRUSTUM_HORIZONTAL) * c; cam->camera_pitch += input->joystick.getAxisWithoutDead(JA_FRUSTUM_VERTICAL) * c; +#endif } cam->camera_pitch = rangelim(cam->camera_pitch, -89.5, 89.5); @@ -2514,8 +2523,13 @@ void Game::updatePlayerControl(const CameraOrientation &cam) isKeyDown(KeyType::PLACE), cam.camera_pitch, cam.camera_yaw, +#if defined(_IRR_COMPILE_WITH_SDL_DEVICE_) + input->sdl_game_controller.getMoveSideward(), + input->sdl_game_controller.getMoveForward() +#else input->joystick.getAxisWithoutDead(JA_SIDEWARD_MOVE), input->joystick.getAxisWithoutDead(JA_FORWARD_MOVE) +#endif ); u32 keypress_bits = ( diff --git a/src/client/inputhandler.cpp b/src/client/inputhandler.cpp index 6c3eebc6c..69ee3d8f8 100644 --- a/src/client/inputhandler.cpp +++ b/src/client/inputhandler.cpp @@ -23,12 +23,17 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "gui/guiChatConsole.h" #include "gui/mainmenumanager.h" #include "hud.h" +#include "settings.h" #ifdef __IOS__ #include "porting_ios.h" extern "C" void external_pause_game(); #endif +#if defined(_IRR_COMPILE_WITH_SDL_DEVICE_) +#include +#endif + void KeyCache::populate_nonchanging() { key[KeyType::ESC] = EscapeKey; @@ -103,19 +108,43 @@ void KeyCache::populate() bool MyEventReceiver::OnEvent(const SEvent &event) { +#if defined(_IRR_COMPILE_WITH_SDL_DEVICE_) + if (event.EventType == irr::EET_SDL_CONTROLLER_BUTTON_EVENT || + event.EventType == irr::EET_SDL_CONTROLLER_AXIS_EVENT) { + if (g_settings->getBool("enable_joysticks")) { + sdl_game_controller->translateEvent(event); + input->setCursorVisible(sdl_game_controller->isCursorVisible()); + } + } else if ((event.EventType == irr::EET_MOUSE_INPUT_EVENT && + event.MouseInput.Event == irr::EMIE_MOUSE_MOVED) || + event.EventType == irr::EET_TOUCH_INPUT_EVENT) { + if (!sdl_game_controller->isFakeEvent() && + sdl_game_controller->isActive()) { + sdl_game_controller->setActive(false); + input->setCursorVisible(sdl_game_controller->isCursorVisible()); + } + } +#endif + #ifdef HAVE_TOUCHSCREENGUI if (event.EventType == irr::EET_TOUCH_INPUT_EVENT) { TouchScreenGUI::setActive(true); if (m_touchscreengui && !isMenuActive()) m_touchscreengui->show(); - } else if (event.EventType == irr::EET_MOUSE_INPUT_EVENT && - event.MouseInput.Event == irr::EMIE_MOUSE_MOVED) { + } else if ((event.EventType == irr::EET_MOUSE_INPUT_EVENT && + event.MouseInput.Event == irr::EMIE_MOUSE_MOVED) || + sdl_game_controller->isActive()) { TouchScreenGUI::setActive(false); if (m_touchscreengui && !isMenuActive()) m_touchscreengui->hide(); } #endif +#if defined(_IRR_COMPILE_WITH_SDL_DEVICE_) + if (event.EventType == irr::EET_SDL_CONTROLLER_BUTTON_EVENT) + return true; +#endif + GUIChatConsole* chat_console = GUIChatConsole::getChatConsole(); if (chat_console && chat_console->isOpen()) { bool result = chat_console->preprocessEvent(event); diff --git a/src/client/inputhandler.h b/src/client/inputhandler.h index 2d84eab7d..acd06bb32 100644 --- a/src/client/inputhandler.h +++ b/src/client/inputhandler.h @@ -193,6 +193,11 @@ public: JoystickController *joystick = nullptr; +#if defined(_IRR_COMPILE_WITH_SDL_DEVICE_) + SDLGameController* sdl_game_controller = nullptr; + InputHandler* input = nullptr; +#endif + #ifdef HAVE_TOUCHSCREENGUI TouchScreenGUI *m_touchscreengui; #endif @@ -255,8 +260,13 @@ public: virtual void clear() {} + virtual void setCursorVisible(bool visible) {}; + JoystickController joystick; KeyCache keycache; +#if defined(_IRR_COMPILE_WITH_SDL_DEVICE_) + SDLGameController sdl_game_controller; +#endif }; /* Separated input handler @@ -268,6 +278,10 @@ public: RealInputHandler(MyEventReceiver *receiver) : m_receiver(receiver) { m_receiver->joystick = &joystick; +#if defined(_IRR_COMPILE_WITH_SDL_DEVICE_) + m_receiver->sdl_game_controller = &sdl_game_controller; + m_receiver->input = this; +#endif } virtual bool isKeyDown(GameKeyType k) { @@ -326,6 +340,22 @@ public: virtual s32 getMouseWheel() { return m_receiver->getMouseWheel(); } + virtual void setCursorVisible(bool visible) + { +#if defined(_IRR_COMPILE_WITH_SDL_DEVICE_) + sdl_game_controller.setCursorVisible(visible); + + if (sdl_game_controller.isActive()) + visible = false; +#endif + IrrlichtDevice *device = RenderingEngine::get_raw_device(); + + if (device->getCursorControl()) { + if (visible != device->getCursorControl()->isVisible()) + device->getCursorControl()->setVisible(visible); + } + } + void clear() { joystick.clear(); diff --git a/src/client/joystick_controller.cpp b/src/client/joystick_controller.cpp index f4668b249..711c015ff 100644 --- a/src/client/joystick_controller.cpp +++ b/src/client/joystick_controller.cpp @@ -20,9 +20,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "joystick_controller.h" #include "irrlichttypes_extrabloated.h" #include "keys.h" +#include "keycode.h" +#include "gui/mainmenumanager.h" #include "settings.h" #include "gettime.h" #include "porting.h" +#include "renderingengine.h" #include "util/string.h" bool JoystickButtonCmb::isTriggered(const irr::SEvent::SJoystickEvent &ev) const @@ -258,3 +261,352 @@ s16 JoystickController::getAxisWithoutDead(JoystickAxis axis) return 0; return v; } + +#if defined(_IRR_COMPILE_WITH_SDL_DEVICE_) +bool SDLGameController::m_active = false; +bool SDLGameController::m_cursor_visible = false; + +void SDLGameController::handleMouseMovement(int x, int y) +{ + IrrlichtDevice* device = RenderingEngine::get_raw_device(); + + bool changed = false; + + u32 current_time = device->getTimer()->getRealTime(); + v2s32 mouse_pos = device->getCursorControl()->getPosition(); + int deadzone = g_settings->getU16("joystick_deadzone"); + + if (x > deadzone || x < -deadzone) { + s32 dt = current_time - m_mouse_time; + + mouse_pos.X += (x * dt / 30000); + if (mouse_pos.X < 0) + mouse_pos.X = 0; + if (mouse_pos.X > device->getVideoDriver()->getScreenSize().Width) + mouse_pos.X = device->getVideoDriver()->getScreenSize().Width; + + changed = true; + } + + if (y > deadzone || y < -deadzone) { + s32 dt = current_time - m_mouse_time; + + mouse_pos.Y += (y * dt / 30000); + if (mouse_pos.Y < 0) + mouse_pos.Y = 0; + if (mouse_pos.Y > device->getVideoDriver()->getScreenSize().Height) + mouse_pos.Y = device->getVideoDriver()->getScreenSize().Height; + + changed = true; + } + + if (changed) { + SEvent translated_event; + translated_event.EventType = irr::EET_MOUSE_INPUT_EVENT; + translated_event.MouseInput.Event = irr::EMIE_MOUSE_MOVED; + translated_event.MouseInput.X = mouse_pos.X; + translated_event.MouseInput.Y = mouse_pos.Y; + translated_event.MouseInput.Control = false; + translated_event.MouseInput.Shift = false; + translated_event.MouseInput.ButtonStates = m_button_states; + + sendEvent(translated_event); + device->getCursorControl()->setPosition(mouse_pos.X, mouse_pos.Y); + } + + m_mouse_time = current_time; +} + +void SDLGameController::handleTriggerLeft(s16 value) +{ + IrrlichtDevice* device = RenderingEngine::get_raw_device(); + + int deadzone = g_settings->getU16("joystick_deadzone"); + + SEvent translated_event; + translated_event.EventType = irr::EET_KEY_INPUT_EVENT; + translated_event.KeyInput.Char = 0; + translated_event.KeyInput.Key = getKeySetting("keymap_hotbar_previous").getKeyCode(); + translated_event.KeyInput.Shift = false; + translated_event.KeyInput.Control = false; + + if (value <= deadzone && m_trigger_left_value > deadzone) { + translated_event.KeyInput.PressedDown = false; + sendEvent(translated_event); + } else if (value > deadzone && m_trigger_left_value <= deadzone) { + translated_event.KeyInput.PressedDown = true; + sendEvent(translated_event); + } + + m_trigger_left_value = value; +} + +void SDLGameController::handleTriggerRight(s16 value) +{ + IrrlichtDevice* device = RenderingEngine::get_raw_device(); + + int deadzone = g_settings->getU16("joystick_deadzone"); + + SEvent translated_event; + translated_event.EventType = irr::EET_KEY_INPUT_EVENT; + translated_event.KeyInput.Char = 0; + translated_event.KeyInput.Key = getKeySetting("keymap_hotbar_next").getKeyCode(); + translated_event.KeyInput.Shift = false; + translated_event.KeyInput.Control = false; + + if (value <= deadzone && m_trigger_right_value > deadzone) { + translated_event.KeyInput.PressedDown = false; + sendEvent(translated_event); + } else if (value > deadzone && m_trigger_right_value <= deadzone) { + translated_event.KeyInput.PressedDown = true; + sendEvent(translated_event); + } + + m_trigger_right_value = value; +} + +void SDLGameController::handleMouseClickLeft(bool pressed) +{ + IrrlichtDevice* device = RenderingEngine::get_raw_device(); + + v2s32 mouse_pos = device->getCursorControl()->getPosition(); + + if (pressed) + m_button_states |= irr::EMBSM_LEFT; + else + m_button_states &= ~irr::EMBSM_LEFT; + + SEvent translated_event; + translated_event.EventType = irr::EET_MOUSE_INPUT_EVENT; + translated_event.MouseInput.Event = pressed ? irr::EMIE_LMOUSE_PRESSED_DOWN : irr::EMIE_LMOUSE_LEFT_UP; + translated_event.MouseInput.X = mouse_pos.X; + translated_event.MouseInput.Y = mouse_pos.Y; + translated_event.MouseInput.Control = false; + translated_event.MouseInput.Shift = false; + translated_event.MouseInput.ButtonStates = m_button_states; + + sendEvent(translated_event); +} + +void SDLGameController::handleMouseClickRight(bool pressed) +{ + IrrlichtDevice* device = RenderingEngine::get_raw_device(); + + v2s32 mouse_pos = device->getCursorControl()->getPosition(); + + if (pressed) + m_button_states |= irr::EMBSM_RIGHT; + else + m_button_states &= ~irr::EMBSM_RIGHT; + + SEvent translated_event; + translated_event.EventType = irr::EET_MOUSE_INPUT_EVENT; + translated_event.MouseInput.Event = pressed ? irr::EMIE_RMOUSE_PRESSED_DOWN : irr::EMIE_RMOUSE_LEFT_UP; + translated_event.MouseInput.X = mouse_pos.X; + translated_event.MouseInput.Y = mouse_pos.Y; + translated_event.MouseInput.Control = false; + translated_event.MouseInput.Shift = false; + translated_event.MouseInput.ButtonStates = m_button_states; + + sendEvent(translated_event); +} + +void SDLGameController::handleButton(const SEvent &event) +{ + irr::EKEY_CODE key = KEY_UNKNOWN; + + switch (event.SDLControllerButtonEvent.Button) { + case SDL_CONTROLLER_BUTTON_A: + key = getKeySetting("keymap_jump").getKeyCode(); + break; + case SDL_CONTROLLER_BUTTON_B: + key = getKeySetting("keymap_sneak").getKeyCode(); + break; + case SDL_CONTROLLER_BUTTON_X: + key = getKeySetting("keymap_special1").getKeyCode(); + break; + case SDL_CONTROLLER_BUTTON_Y: + key = getKeySetting("keymap_minimap").getKeyCode(); + break; + case SDL_CONTROLLER_BUTTON_BACK: + key = getKeySetting("keymap_chat").getKeyCode(); + break; + case SDL_CONTROLLER_BUTTON_GUIDE: + break; + case SDL_CONTROLLER_BUTTON_START: + key = KEY_ESCAPE; + break; + case SDL_CONTROLLER_BUTTON_LEFTSTICK: + key = getKeySetting("keymap_camera_mode").getKeyCode(); + break; + case SDL_CONTROLLER_BUTTON_RIGHTSTICK: + key = KEY_LBUTTON; + break; + case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: + key = KEY_LBUTTON; + break; + case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: + key = KEY_RBUTTON; + break; + case SDL_CONTROLLER_BUTTON_DPAD_UP: + key = getKeySetting("keymap_rangeselect").getKeyCode(); + break; + case SDL_CONTROLLER_BUTTON_DPAD_DOWN: + key = getKeySetting("keymap_drop").getKeyCode(); + break; + case SDL_CONTROLLER_BUTTON_DPAD_LEFT: + key = KEY_ESCAPE; + break; + case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: + key = getKeySetting("keymap_inventory").getKeyCode(); + break; + case SDL_CONTROLLER_BUTTON_MISC1: + case SDL_CONTROLLER_BUTTON_PADDLE1: + case SDL_CONTROLLER_BUTTON_PADDLE2: + case SDL_CONTROLLER_BUTTON_PADDLE3: + case SDL_CONTROLLER_BUTTON_PADDLE4: + case SDL_CONTROLLER_BUTTON_TOUCHPAD: + break; + } + + if (key != KEY_UNKNOWN) { + SEvent translated_event; + translated_event.EventType = irr::EET_KEY_INPUT_EVENT; + translated_event.KeyInput.Char = 0; + translated_event.KeyInput.Key = key; + translated_event.KeyInput.PressedDown = event.SDLControllerButtonEvent.Pressed; + translated_event.KeyInput.Shift = false; + translated_event.KeyInput.Control = false; + + sendEvent(translated_event); + } +} + +void SDLGameController::handleButtonInMenu(const SEvent &event) +{ + irr::EKEY_CODE key = KEY_UNKNOWN; + + // Just used game mapping for escape key for now + switch (event.SDLControllerButtonEvent.Button) { + case SDL_CONTROLLER_BUTTON_A: + case SDL_CONTROLLER_BUTTON_B: + case SDL_CONTROLLER_BUTTON_X: + case SDL_CONTROLLER_BUTTON_Y: + case SDL_CONTROLLER_BUTTON_BACK: + case SDL_CONTROLLER_BUTTON_GUIDE: + break; + case SDL_CONTROLLER_BUTTON_START: + key = KEY_ESCAPE; + break; + case SDL_CONTROLLER_BUTTON_LEFTSTICK: + case SDL_CONTROLLER_BUTTON_RIGHTSTICK: + case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: + case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: + case SDL_CONTROLLER_BUTTON_DPAD_UP: + case SDL_CONTROLLER_BUTTON_DPAD_DOWN: + break; + case SDL_CONTROLLER_BUTTON_DPAD_LEFT: + key = KEY_ESCAPE; + break; + case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: + case SDL_CONTROLLER_BUTTON_MISC1: + case SDL_CONTROLLER_BUTTON_PADDLE1: + case SDL_CONTROLLER_BUTTON_PADDLE2: + case SDL_CONTROLLER_BUTTON_PADDLE3: + case SDL_CONTROLLER_BUTTON_PADDLE4: + case SDL_CONTROLLER_BUTTON_TOUCHPAD: + break; + } + + if (key != KEY_UNKNOWN) { + SEvent translated_event; + translated_event.EventType = irr::EET_KEY_INPUT_EVENT; + translated_event.KeyInput.Char = 0; + translated_event.KeyInput.Key = key; + translated_event.KeyInput.PressedDown = event.SDLControllerButtonEvent.Pressed; + translated_event.KeyInput.Shift = false; + translated_event.KeyInput.Control = false; + + sendEvent(translated_event); + } +} + +void SDLGameController::handlePlayerMovement(int x, int y) +{ + int deadzone = g_settings->getU16("joystick_deadzone"); + + m_move_sideward = x; + if (m_move_sideward < deadzone && m_move_sideward > -deadzone) + m_move_sideward = 0; + else + m_active = true; + + m_move_forward = y; + if (m_move_forward < deadzone && m_move_forward > -deadzone) + m_move_forward = 0; + else + m_active = true; +} + +void SDLGameController::handleCameraOrientation(int x, int y) +{ + int deadzone = g_settings->getU16("joystick_deadzone"); + + m_camera_yaw = x; + if (m_camera_yaw < deadzone && m_camera_yaw > -deadzone) + m_camera_yaw = 0; + else + m_active = true; + + m_camera_pitch = y; + if (m_camera_pitch < deadzone && m_camera_pitch > -deadzone) + m_camera_pitch = 0; + else + m_active = true; +} + +void SDLGameController::sendEvent(const SEvent &event) +{ + m_active = true; + m_is_fake_event = true; + IrrlichtDevice* device = RenderingEngine::get_raw_device(); + device->postEventFromUser(event); + m_is_fake_event = false; +} + +void SDLGameController::translateEvent(const SEvent &event) +{ + if (event.EventType == irr::EET_SDL_CONTROLLER_BUTTON_EVENT) { + if (isMenuActive()) { + if (event.SDLControllerButtonEvent.Button == SDL_CONTROLLER_BUTTON_LEFTSTICK || + event.SDLControllerButtonEvent.Button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) { + handleMouseClickLeft(event.SDLControllerButtonEvent.Pressed); + } else if (event.SDLControllerButtonEvent.Button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) { + handleMouseClickRight(event.SDLControllerButtonEvent.Pressed); + } else { + handleButtonInMenu(event); + } + } else { + if (event.SDLControllerButtonEvent.Button == SDL_CONTROLLER_BUTTON_RIGHTSTICK || + event.SDLControllerButtonEvent.Button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) { + handleMouseClickLeft(event.SDLControllerButtonEvent.Pressed); + } else if (event.SDLControllerButtonEvent.Button == SDL_CONTROLLER_BUTTON_LEFTSHOULDER) { + handleMouseClickRight(event.SDLControllerButtonEvent.Pressed); + } else { + handleButton(event); + } + } + } else if (event.EventType == irr::EET_SDL_CONTROLLER_AXIS_EVENT) { + const s16* value = event.SDLControllerAxisEvent.Value; + + if (isMenuActive()) { + handleMouseMovement(value[SDL_CONTROLLER_AXIS_LEFTX], value[SDL_CONTROLLER_AXIS_LEFTY]); + } else { + handleTriggerLeft(value[SDL_CONTROLLER_AXIS_TRIGGERLEFT]); + handleTriggerRight(value[SDL_CONTROLLER_AXIS_TRIGGERRIGHT]); + handlePlayerMovement(value[SDL_CONTROLLER_AXIS_LEFTX], value[SDL_CONTROLLER_AXIS_LEFTY]); + handleCameraOrientation(value[SDL_CONTROLLER_AXIS_RIGHTX], value[SDL_CONTROLLER_AXIS_RIGHTY]); + } + } +} +#endif diff --git a/src/client/joystick_controller.h b/src/client/joystick_controller.h index e69a7cdda..e13cff1f0 100644 --- a/src/client/joystick_controller.h +++ b/src/client/joystick_controller.h @@ -167,3 +167,47 @@ private: std::bitset m_past_keys_pressed; std::bitset m_keys_released; }; + +#if defined(_IRR_COMPILE_WITH_SDL_DEVICE_) +class SDLGameController +{ +private: + void handleMouseMovement(int x, int y); + void handleTriggerLeft(s16 value); + void handleTriggerRight(s16 value); + void handleMouseClickLeft(bool pressed); + void handleMouseClickRight(bool pressed); + void handleButton(const SEvent &event); + void handleButtonInMenu(const SEvent &event); + void handlePlayerMovement(int x, int y); + void handleCameraOrientation(int x, int y); + void sendEvent(const SEvent &event); + + int m_button_states = 0; + u32 m_mouse_time = 0; + s16 m_trigger_left_value = 0; + s16 m_trigger_right_value = 0; + s16 m_move_sideward = 0; + s16 m_move_forward = 0; + s16 m_camera_yaw = 0; + s16 m_camera_pitch = 0; + + static bool m_active; + static bool m_cursor_visible; + bool m_is_fake_event = false; + +public: + void translateEvent(const SEvent &event); + + s16 getMoveSideward() { return m_move_sideward; } + s16 getMoveForward() { return m_move_forward; } + s16 getCameraYaw() { return m_camera_yaw; } + s16 getCameraPitch() { return m_camera_pitch; } + + void setActive(bool value) { m_active = value; } + static bool isActive() { return m_active; } + void setCursorVisible(bool visible) { m_cursor_visible = visible; } + static bool isCursorVisible() { return m_cursor_visible; } + bool isFakeEvent() { return m_is_fake_event; } +}; +#endif diff --git a/src/client/keycode.h b/src/client/keycode.h index 4efe31d9b..34a12b83f 100644 --- a/src/client/keycode.h +++ b/src/client/keycode.h @@ -44,6 +44,8 @@ public: const char *sym() const; const char *name() const; + irr::EKEY_CODE getKeyCode() { return Key; } + protected: static bool valid_kcode(irr::EKEY_CODE k) { diff --git a/src/client/localplayer.cpp b/src/client/localplayer.cpp index 72d42b7a8..b58ad7fcc 100644 --- a/src/client/localplayer.cpp +++ b/src/client/localplayer.cpp @@ -27,6 +27,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "map.h" #include "client.h" #include "content_cao.h" +#include "client/joystick_controller.h" +#include "gui/touchscreengui.h" /* LocalPlayer @@ -293,7 +295,7 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, (touching_ground ? m_cao->getStepHeight() : (0.2f * BS)); #ifdef HAVE_TOUCHSCREENGUI - if (touching_ground) + if (TouchScreenGUI::isActive() && touching_ground) player_stepheight += (0.6f * BS); #endif @@ -926,7 +928,8 @@ void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d, float player_stepheight = touching_ground ? (BS * 0.6f) : (BS * 0.2f); #ifdef HAVE_TOUCHSCREENGUI - player_stepheight += (0.6 * BS); + if (TouchScreenGUI::isActive()) + player_stepheight += (0.6 * BS); #endif v3f accel_f; @@ -1159,8 +1162,21 @@ void LocalPlayer::handleAutojump(f32 dtime, Environment *env, const collisionMoveResult &result, const v3f &initial_position, const v3f &initial_speed, f32 pos_max_d) { +#ifdef HAVE_TOUCHSCREENGUI + // Touchscreen uses player_stepheight for autojump + if (TouchScreenGUI::isActive()) + return; +#endif + PlayerSettings &player_settings = getPlayerSettings(); - if (!player_settings.autojump) + bool autojump_enabled = player_settings.autojump; + +#if defined(_IRR_COMPILE_WITH_SDL_DEVICE_) + // Force autojump on gamepad + autojump_enabled |= SDLGameController::isActive(); +#endif + + if (!autojump_enabled) return; if (m_autojump) diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index abe8bfa73..11283ff0e 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -290,7 +290,7 @@ void set_default_settings() settings->setDefault("always_fly_fast", "true"); settings->setDefault("autojump", "false"); settings->setDefault("continuous_forward", "false"); - settings->setDefault("enable_joysticks", "false"); + settings->setDefault("enable_joysticks", "true"); settings->setDefault("joystick_id", "0"); settings->setDefault("joystick_type", ""); settings->setDefault("repeat_joystick_button_time", "0.17"); diff --git a/src/gui/modalMenu.cpp b/src/gui/modalMenu.cpp index 7b3c81819..955e73e26 100644 --- a/src/gui/modalMenu.cpp +++ b/src/gui/modalMenu.cpp @@ -20,13 +20,17 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "modalMenu.h" +#include "client/guiscalingfilter.h" +#include "client/joystick_controller.h" +#include "client/renderingengine.h" +#include "client/tile.h" +#include "filesys.h" #include "gettext.h" #include "porting.h" #include "settings.h" #ifdef HAVE_TOUCHSCREENGUI #include "touchscreengui.h" -#include "client/renderingengine.h" #endif #ifdef _IRR_COMPILE_WITH_SDL_DEVICE_ @@ -93,8 +97,54 @@ void GUIModalMenu::draw() } drawMenu(); + +#if defined(_IRR_COMPILE_WITH_SDL_DEVICE_) + if (SDLGameController::isActive() && SDLGameController::isCursorVisible()) + drawCursor(); +#endif } +#if defined(_IRR_COMPILE_WITH_SDL_DEVICE_) +void GUIModalMenu::drawCursor() +{ + video::IVideoDriver *driver = Environment->getVideoDriver(); + irr::IrrlichtDevice *device = RenderingEngine::get_raw_device(); + v2s32 pointer = device->getCursorControl()->getPosition(); + + v3f crosshair_color = g_settings->getV3F("crosshair_color"); + u32 cross_r = rangelim(myround(crosshair_color.X), 0, 255); + u32 cross_g = rangelim(myround(crosshair_color.Y), 0, 255); + u32 cross_b = rangelim(myround(crosshair_color.Z), 0, 255); + u32 cross_a = rangelim(g_settings->getS32("crosshair_alpha"), 0, 255); + video::SColor crosshair_argb = video::SColor(cross_a, cross_r, cross_g, cross_b); + + const int cursor_line_size = 16; + float hud_scaling = g_settings->getFloat("hud_scaling"); + float scale_factor = hud_scaling * RenderingEngine::getDisplayDensity(); + int cursor_size = (int)(cursor_line_size * scale_factor); + + std::string sprite_path = porting::path_share + DIR_DELIM + "textures" + + DIR_DELIM + "base" + DIR_DELIM + "pack" + DIR_DELIM + + "cursor.png"; + video::ITexture *cursor = driver->getTexture(sprite_path.c_str()); + + if (cursor) { + core::rect rect(pointer.X - cursor_size, pointer.Y - cursor_size, + pointer.X + cursor_size, pointer.Y + cursor_size); + video::SColor crosshair_color[] = {crosshair_argb, crosshair_argb, + crosshair_argb, crosshair_argb}; + draw2DImageFilterScaled(driver, cursor, rect, + core::rect({0, 0}, cursor->getOriginalSize()), + nullptr, crosshair_color, true); + } else { + driver->draw2DLine(pointer - v2s32(cursor_size, 0), + pointer + v2s32(cursor_size, 0), crosshair_argb); + driver->draw2DLine(pointer - v2s32(0, cursor_size), + pointer + v2s32(0, cursor_size), crosshair_argb); + } +} +#endif + /* This should be called when the menu wants to quit. diff --git a/src/gui/modalMenu.h b/src/gui/modalMenu.h index f7c7b5063..ddecb97f7 100644 --- a/src/gui/modalMenu.h +++ b/src/gui/modalMenu.h @@ -46,6 +46,9 @@ public: void allowFocusRemoval(bool allow); bool canTakeFocus(gui::IGUIElement *e); void draw(); +#if defined(_IRR_COMPILE_WITH_SDL_DEVICE_) + void drawCursor(); +#endif void quitMenu(); void removeChildren(); diff --git a/textures/base/pack/cursor.png b/textures/base/pack/cursor.png new file mode 100644 index 0000000000000000000000000000000000000000..ce5f8195219555b4f98ef696fda3f960bfa69662 GIT binary patch literal 339 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0J3?w7mbKU|ei2$Dv*Z=?j1DP>3S0*fd)-nHS z?X*WT*1TA$)5S5QBJS-q+rAb99@YmZc|GzP zCZ<)(6f&OSJ;Ca5>Pz&5*ZDT3bJTO44`=@r`KKo zFG3VTB{_Atj<79CxShM;y3?!1B?>nc?sCXIsFkl!y3gFVY-SrDhwx{cZw%c9>=s?O z_gzY8)?BvboWuda7itfaABnfdfBkPi?U~vKh8=7Vm0DLz=oLq>?+8dxJs8xywWCWk g%vi7dNA(BxB8wxN