diff --git a/GameMods.hpp b/GameMods.hpp index f3b2234c9..af788fb83 100644 --- a/GameMods.hpp +++ b/GameMods.hpp @@ -39,7 +39,6 @@ #define ENH_USE_OWN_AO // Use own ambient occlusion engine - looks pretty much the same except it fixes the corners #define ENH_ADD_OPTIONS_PAUSE // Add an 'options' button in the pause menu #define ENH_ALLOW_SAND_GRAVITY // Allow sand to fall. -#define ENH_USE_GUI_SCALE_2 // Use a 2x GUI scale instead of 3x. Looks better on PC #define ENH_ALLOW_SCROLL_WHEEL // Allow use of the scroll wheel to change selected inventory slots #define ENH_3D_INVENTORY_TILES // Uses 3D rendered inventory tiles, use with ENH_SHADE_HELD_TILES to render correctly. #define ENH_MENU_BACKGROUND // Renders a spinning panorama (if it's available) in the background of the main menu diff --git a/game/assets/materials/ui.material b/game/assets/materials/ui.material index 73b1858d0..7c086d6ab 100644 --- a/game/assets/materials/ui.material +++ b/game/assets/materials/ui.material @@ -35,7 +35,7 @@ "states": ["DisableCulling"] }, - "ui_text:ui_texture_and_color": { + "ui_text:ui_textured_and_glcolor": { "fragmentShader": "shaders/text.fragment" }, diff --git a/platforms/input/xinput/GameControllerHandler_xinput.cpp b/platforms/input/xinput/GameControllerHandler_xinput.cpp index 02701c6f2..ea7eb7e87 100644 --- a/platforms/input/xinput/GameControllerHandler_xinput.cpp +++ b/platforms/input/xinput/GameControllerHandler_xinput.cpp @@ -42,11 +42,6 @@ void GameControllerHandler_xinput::_initButtonMap() m[XINPUT_GAMEPAD_START] = GameController::BUTTON_START; } -Keyboard::KeyState _getKeyState(bool value) -{ - return value ? Keyboard::DOWN : Keyboard::UP; -} - void GameControllerHandler_xinput::_processButton(GameController::ID controllerId, const XINPUT_STATE& state, GameController::NativeButtonID nativeBtn, GameController::EngineButtonID engineBtn, bool& joinGameAlreadyFired) { bool bButtonPressed = (state.Gamepad.wButtons & nativeBtn) != 0; @@ -63,8 +58,7 @@ void GameControllerHandler_xinput::_processButton(GameController::ID controllerI joinGameAlreadyFired = true; } - // @TODO: should call GameControllerManager::feedButton() instead - Keyboard::feed(_getKeyState(bButtonPressed), engineBtn); + GameControllerManager::feedButton(btnState, engineBtn); lastBtnState = btnState; } @@ -95,9 +89,9 @@ void GameControllerHandler_xinput::refresh() { XINPUT_STATE& inputState = m_inputStates.m_inputState[id]; bool joinGameAlreadyFired = false; - for (ButtonIDMap::const_iterator it = m_buttonIdMap.begin(); it != m_buttonIdMap.end(); it++) + for (ButtonIDMap::iterator it = m_buttonIdMap.begin(); it != m_buttonIdMap.end(); it++) { - _processButton(id, inputState, it->first, it->second, joinGameAlreadyFired); + _processButton(id, inputState, it.key(), it.value(), joinGameAlreadyFired); } GameControllerManager::feedTrigger(1, (float)inputState.Gamepad.bLeftTrigger / 255.0f); diff --git a/platforms/sdl/base/AppPlatform_sdl.cpp b/platforms/sdl/base/AppPlatform_sdl.cpp index ecacb41fb..52ee56012 100644 --- a/platforms/sdl/base/AppPlatform_sdl.cpp +++ b/platforms/sdl/base/AppPlatform_sdl.cpp @@ -309,10 +309,39 @@ void AppPlatform_sdl::handleKeyEvent(const SDL_Event& event) return _handleKeyEvent(key, state); } +static GameController::EngineButtonID _getEngineButton(uint8_t button) +{ + switch (button) + { + case SDL_CONTROLLER_BUTTON_A: return GameController::BUTTON_A; + case SDL_CONTROLLER_BUTTON_B: return GameController::BUTTON_B; + case SDL_CONTROLLER_BUTTON_X: return GameController::BUTTON_X; + case SDL_CONTROLLER_BUTTON_Y: return GameController::BUTTON_Y; + case SDL_CONTROLLER_BUTTON_DPAD_UP: return GameController::BUTTON_DPAD_UP; + case SDL_CONTROLLER_BUTTON_DPAD_DOWN: return GameController::BUTTON_DPAD_DOWN; + case SDL_CONTROLLER_BUTTON_DPAD_LEFT: return GameController::BUTTON_DPAD_LEFT; + case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: return GameController::BUTTON_DPAD_RIGHT; + case SDL_CONTROLLER_BUTTON_LEFTSTICK: return GameController::BUTTON_LEFTSTICK; + case SDL_CONTROLLER_BUTTON_RIGHTSTICK: return GameController::BUTTON_RIGHTSTICK; + case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: return GameController::BUTTON_LEFTSHOULDER; + case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: return GameController::BUTTON_RIGHTSHOULDER; + case SDL_CONTROLLER_BUTTON_BACK: return GameController::BUTTON_BACK; + case SDL_CONTROLLER_BUTTON_START: return GameController::BUTTON_START; + case SDL_CONTROLLER_BUTTON_GUIDE: return GameController::BUTTON_GUIDE; + case SDL_CONTROLLER_BUTTON_MISC1: return GameController::BUTTON_MISC1; + case SDL_CONTROLLER_BUTTON_PADDLE1: return GameController::BUTTON_PADDLE1; + case SDL_CONTROLLER_BUTTON_PADDLE2: return GameController::BUTTON_PADDLE2; + case SDL_CONTROLLER_BUTTON_PADDLE3: return GameController::BUTTON_PADDLE3; + case SDL_CONTROLLER_BUTTON_PADDLE4: return GameController::BUTTON_PADDLE4; + case SDL_CONTROLLER_BUTTON_TOUCHPAD: return GameController::BUTTON_TOUCHPAD; + default: return GameController::BUTTON_NONE; + } +} + void AppPlatform_sdl::handleControllerButtonEvent(SDL_JoystickID controllerIndex, uint8_t button, uint8_t state) { // Normal Key Press - Keyboard::feed(GetKeyState(state), button); + GameControllerManager::feedButton(state == SDL_PRESSED ? GameController::BTN_STATE_DOWN : GameController::BTN_STATE_UP, _getEngineButton(button)); } void AppPlatform_sdl::handleControllerAxisEvent(SDL_JoystickID controllerIndex, uint8_t axis, int16_t value) diff --git a/platforms/sdl/base/AppPlatform_sdl.hpp b/platforms/sdl/base/AppPlatform_sdl.hpp index c93d4f4b2..9b6c8bfc2 100644 --- a/platforms/sdl/base/AppPlatform_sdl.hpp +++ b/platforms/sdl/base/AppPlatform_sdl.hpp @@ -8,6 +8,7 @@ #include "client/player/input/Mouse.hpp" #include "client/player/input/Keyboard.hpp" +#include "client/player/input/GameController.hpp" #include "common/Logger.hpp" class AppPlatform_sdl : public AppPlatform diff --git a/platforms/sdl/sdl2/main.cpp b/platforms/sdl/sdl2/main.cpp index 68c341b10..280a86b72 100644 --- a/platforms/sdl/sdl2/main.cpp +++ b/platforms/sdl/sdl2/main.cpp @@ -233,7 +233,8 @@ static void handle_events() float x = event.button.x * scale; float y = event.button.y * scale; Mouse::feed(type, state, x, y); - Multitouch::feed(type, state, x, y, 0); + if (g_pAppPlatform->isTouchscreen()) + Multitouch::feed(type, state, x, y, 0); } break; } @@ -244,7 +245,8 @@ static void handle_events() float scale = g_fPointToPixelScale; float x = event.motion.x * scale; float y = event.motion.y * scale; - Multitouch::feed(MOUSE_BUTTON_NONE, false, x, y, 0); + if (g_pAppPlatform->isTouchscreen()) + Multitouch::feed(MOUSE_BUTTON_NONE, false, x, y, 0); Mouse::feed(MOUSE_BUTTON_NONE, false, x, y); g_pAppPlatform->setMouseDiff(event.motion.xrel * scale, event.motion.yrel * scale); } diff --git a/platforms/windows/main.cpp b/platforms/windows/main.cpp index 00f703ea1..4848500a9 100644 --- a/platforms/windows/main.cpp +++ b/platforms/windows/main.cpp @@ -61,7 +61,8 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam) posY = Mouse::getY(); } Mouse::feed(buttonType, buttonState, posX, posY); - Multitouch::feed(buttonType, buttonState, posX, posY, 0); + if (g_AppPlatform.isTouchscreen()) + Multitouch::feed(buttonType, buttonState, posX, posY, 0); break; } diff --git a/platforms/windows/projects/Common/Common.vcxproj b/platforms/windows/projects/Common/Common.vcxproj index a497af7e0..9ee2aa6ef 100644 --- a/platforms/windows/projects/Common/Common.vcxproj +++ b/platforms/windows/projects/Common/Common.vcxproj @@ -115,6 +115,7 @@ + diff --git a/platforms/windows/projects/Common/Common.vcxproj.filters b/platforms/windows/projects/Common/Common.vcxproj.filters index 56d68fb15..fdd8109f3 100644 --- a/platforms/windows/projects/Common/Common.vcxproj.filters +++ b/platforms/windows/projects/Common/Common.vcxproj.filters @@ -127,5 +127,8 @@ Header Files\Threading + + Header Files\Utility + \ No newline at end of file diff --git a/source/client/app/Minecraft.cpp b/source/client/app/Minecraft.cpp index 27a162a15..888bb09a9 100644 --- a/source/client/app/Minecraft.cpp +++ b/source/client/app/Minecraft.cpp @@ -181,14 +181,21 @@ void Minecraft::_reloadInput() if (isTouchscreen()) { m_pInputHolder = new TouchInputHolder(this, getOptions()); + + if (m_bGrabbedMouse) + platform()->setMouseGrabbed(false); } else if (useController()) { m_pInputHolder = new CustomInputHolder( new ControllerMoveInput(getOptions()), new ControllerTurnInput(), - new ControllerBuildInput() + new ControllerBuildInput(), + true ); + + if (m_bGrabbedMouse) + platform()->setMouseGrabbed(false); } else { @@ -197,6 +204,9 @@ void Minecraft::_reloadInput() new MouseTurnInput(this), new MouseBuildInput() ); + + if (m_bGrabbedMouse) + platform()->setMouseGrabbed(true); } m_mouseHandler.setTurnInput(m_pInputHolder->getTurnInput()); @@ -262,7 +272,6 @@ void Minecraft::recenterMouse() void Minecraft::setScreen(Screen* pScreen) { - float lastScale = getBestScaleForThisScreenSize(Minecraft::width, Minecraft::height); #ifndef ORIGINAL_CODE if (pScreen == nullptr && !isLevelGenerated()) { @@ -277,13 +286,15 @@ void Minecraft::setScreen(Screen* pScreen) return; } - if (pScreen && pScreen->isErrorScreen()) + if (pScreen && (pScreen->isErrorScreen() || !pScreen->validate(this))) { // not in original delete pScreen; return; } + float lastScale = getBestScaleForThisScreenSize(Minecraft::width, Minecraft::height); + if (m_pScreen) { m_pScreen->removed(); @@ -361,17 +372,17 @@ bool Minecraft::isOnlineClient() const bool Minecraft::isTouchscreen() const { - return m_bIsTouchscreen; + return IInputHolder::activeType == IInputHolder::TOUCHSCREEN; } bool Minecraft::useSplitControls() const { - return !m_bIsTouchscreen || getOptions()->m_splitControls.get(); + return !isTouchscreen() || getOptions()->m_splitControls.get(); } bool Minecraft::useController() const { - return m_pPlatform->hasGamepad() && getOptions()->m_bUseController.get(); + return m_pPlatform->hasGamepad() && (IInputHolder::activeType == IInputHolder::CONTROLLER || getOptions()->m_bUseController.get()); } void Minecraft::setGameMode(GameType gameType) @@ -505,6 +516,9 @@ void Minecraft::handleBuildAction(const BuildActionIntention& action) void Minecraft::tickInput() { + if (!m_pInputHolder->allowsType(IInputHolder::activeType)) + _reloadInput(); + if (m_pScreen) { if (!m_pScreen->m_bPassEvents) @@ -550,93 +564,131 @@ void Minecraft::tickInput() #endif } - while (Keyboard::next()) + if (useController()) { - int keyCode = Keyboard::getEventKey(); - bool bPressed = Keyboard::getEventKeyState() == 1; + //Clearing, so the events don't accumulate + Keyboard::reset(); - m_pLocalPlayer->m_pMoveInput->setKey(keyCode, bPressed); - - if (bPressed) + while (GameControllerManager::next()) { - m_pGui->handleKeyPressed(keyCode); + GameController::EngineButtonID button = GameControllerManager::getEventButton(); + bool bPressed = GameControllerManager::getEventButtonState() == GameController::BTN_STATE_DOWN; - for (int i = 0; i < m_pGui->getNumUsableSlots(); i++) - { - if (getOptions()->isKey(eKeyMappingIndex(KM_SLOT_1 + i), keyCode)) - m_pLocalPlayer->m_pInventory->selectSlot(i); - } + if (bPressed) + m_pGui->handleControlPressed(ControlBind(-1, button)); - if (getOptions()->isKey(KM_TOGGLE3RD, keyCode)) + for (size_t i = 0; i < KM_COUNT; ++i) { - getOptions()->m_thirdPerson.toggle(); + eControlMappingIndex ctrl = (eControlMappingIndex)i; + if (!getOptions()->getControl(ctrl).isButton(button)) continue; + + m_pLocalPlayer->m_pMoveInput->setKey(ctrl, bPressed); + if (bPressed) + getOptions()->getControlMapping(ctrl).pressed(); + else + getOptions()->getControlMapping(ctrl).reset(); } - else if (getOptions()->isKey(KM_MENU_PAUSE, keyCode)) + } + } + else + { + //Clearing, so the events don't accumulate + GameControllerManager::clear(); + while (Keyboard::next()) + { + int keyCode = Keyboard::getEventKey(); + bool bPressed = Keyboard::getEventKeyState() == Keyboard::DOWN; + + if (bPressed) + m_pGui->handleControlPressed(ControlBind(keyCode, GameController::BUTTON_NONE)); + + for (size_t i = 0; i < KM_COUNT; ++i) { - handleBack(false); + eControlMappingIndex ctrl = (eControlMappingIndex)i; + if (!getOptions()->isKey(ctrl, keyCode)) continue; + + m_pLocalPlayer->m_pMoveInput->setKey(ctrl, bPressed); + if (bPressed) + getOptions()->getControlMapping(ctrl).pressed(); + else + getOptions()->getControlMapping(ctrl).reset(); } - else if (getOptions()->isKey(KM_DROP, keyCode)) + + if (getOptions()->field_19) + continue; + + // @TODO: Replace with KeyboardBuildInput + if (getTimeMs() - field_2B4 <= 200) { - ItemStack& item = m_pLocalPlayer->m_pInventory->getSelected(); - if (!item.isEmpty()) + if (getOptions()->isKey(KM_DESTROY, keyCode) && bPressed) { - ItemStack itemDrop(item); - itemDrop.m_count = 1; - - if (m_pLocalPlayer->isSurvival()) - item.shrink(1); + BuildActionIntention intention(BuildActionIntention::KEY_DESTROY); + handleBuildAction(intention); + } - m_pLocalPlayer->drop(itemDrop); + if (getOptions()->isKey(KM_PLACE, keyCode) && bPressed) + { + BuildActionIntention intention(BuildActionIntention::KEY_USE); + handleBuildAction(intention); } } - else if (getOptions()->isKey(KM_TOGGLEGUI, keyCode)) - { - getOptions()->m_hideGui.toggle(); - } - else if (getOptions()->isKey(KM_TOGGLEDEBUG, keyCode)) - { - getOptions()->m_debugText.toggle(); - } -#ifdef ENH_ALLOW_AO_TOGGLE - else if (getOptions()->isKey(KM_TOGGLEAO, keyCode)) - { - // Toggle ambient occlusion. - getOptions()->m_ambientOcclusion.toggle(); - Minecraft::useAmbientOcclusion = getOptions()->m_ambientOcclusion.get(); - m_pLevelRenderer->allChanged(); - } -#endif } + } - if (getOptions()->field_19) - continue; + for (int i = 0; i < m_pGui->getNumUsableSlots(); i++) + { + while (getOptions()->getControlMapping(eControlMappingIndex(KM_SLOT_1 + i)).consume()) + m_pLocalPlayer->m_pInventory->selectSlot(i); + } - // @TODO: Replace with KeyboardBuildInput - if (!useController() && getTimeMs() - field_2B4 <= 200) + while (getOptions()->getControlMapping(KM_TOGGLE3RD).consume()) + { + getOptions()->m_thirdPerson.toggle(); + } + + while (getOptions()->getControlMapping(KM_MENU_PAUSE).consume()) + { + handleBack(false); + } + + while (getOptions()->getControlMapping(KM_DROP).consume()) + { + ItemStack& item = m_pLocalPlayer->m_pInventory->getSelected(); + if (!item.isEmpty()) { - if (getOptions()->getKey(KM_DESTROY) == keyCode && bPressed) - { - BuildActionIntention intention(BuildActionIntention::KEY_DESTROY); - handleBuildAction(intention); - } + ItemStack itemDrop(item); + itemDrop.m_count = 1; - if (getOptions()->getKey(KM_PLACE) == keyCode && bPressed) - { - BuildActionIntention intention(BuildActionIntention::KEY_USE); - handleBuildAction(intention); - } + item.shrink(1); + + m_pLocalPlayer->drop(itemDrop); } } + while (getOptions()->getControlMapping(KM_TOGGLEGUI).consume()) + { + getOptions()->m_hideGui.toggle(); + } + + while (getOptions()->getControlMapping(KM_TOGGLEDEBUG).consume()) + { + getOptions()->m_debugText.toggle(); + } +#ifdef ENH_ALLOW_AO_TOGGLE + while (getOptions()->getControlMapping(KM_TOGGLEAO).consume()) + { + // Toggle ambient occlusion. + getOptions()->m_ambientOcclusion.toggle(); + m_pLevelRenderer->allChanged(); + } +#endif + BuildActionIntention bai; IBuildInput* buildInput = m_pInputHolder->getBuildInput(); if (buildInput && buildInput->tickBuild(m_pLocalPlayer, &bai)) handleBuildAction(bai); field_2B4 = getTimeMs(); - - Keyboard::reset(); - Mouse::reset(); } void Minecraft::tickMouse() @@ -844,9 +896,15 @@ void Minecraft::tick() } if (m_pScreen) + { + m_pScreen->validate(this); m_pScreen->tick(); + } Multitouch::reset(); + + // Actually pause the game, because fuck bedrock edition + m_bIsGamePaused = (!isOnline() || (m_pLevel && m_pLevel->m_players.size() == 1)) && (m_pScreen && m_pScreen->isPauseScreen()); } } @@ -1001,13 +1059,12 @@ void Minecraft::prepareLevel(const std::string& unused) void Minecraft::sizeUpdate(int newWidth, int newHeight) { // re-calculate the GUI scale. - Gui::GuiScale = getBestScaleForThisScreenSize(newWidth, newHeight) / getRenderScaleMultiplier(); + Gui::GuiScale = getBestScaleForThisScreenSize(newWidth, newHeight) / getRenderScaleMultiplier(); // The ceil gives an extra pixel to the screen's width and height, in case the GUI scale doesn't // divide evenly into width or height, so that none of the game screen is uncovered. + Gui::GuiWidth = ceilf(Minecraft::width * Gui::GuiScale); Gui::GuiHeight = ceilf(Minecraft::height * Gui::GuiScale); - Gui::GuiScale = float(Gui::GuiHeight) / height; - Gui::GuiWidth = ceilf(Minecraft::width * Gui::GuiScale); if (m_pScreen) m_pScreen->setSize( @@ -1038,20 +1095,14 @@ float Minecraft::getBestScaleForThisScreenSize(int width, int height) else if (m_pOptions->getUiTheme() == UI_CONSOLE) return Screen::GetConsoleScale(height); -#if MC_PLATFORM_XBOX -#define USE_JAVA_SCREEN_SCALING -#endif -#ifdef USE_JAVA_SCREEN_SCALING - // @HACK: the scaling code for Java/Pocket Screens when using the Console theme is pretty broken - if (m_pOptions->getUiTheme() != UI_CONSOLE) + if (getUiTheme() == UI_JAVA) { int scale; for (scale = 1; width / (scale + 1) >= 320 && height / (scale + 1) >= 240; ++scale) { } - return scale; + return 1.0f / scale; } -#endif if (height > 1800) return 1.0f / 8.0f; @@ -1153,11 +1204,6 @@ bool Minecraft::pauseGame() { if (isGamePaused() || m_pScreen) return false; - if (!isOnline() || m_pLevel->m_players.size() == 1) - { - // Actually pause the game, because fuck bedrock edition - m_bIsGamePaused = true; - } m_pLevel->savePlayerData(); getScreenChooser()->pushPauseScreen(); diff --git a/source/client/app/NinecraftApp.cpp b/source/client/app/NinecraftApp.cpp index 1c7bd839c..f0c610914 100644 --- a/source/client/app/NinecraftApp.cpp +++ b/source/client/app/NinecraftApp.cpp @@ -102,7 +102,13 @@ void NinecraftApp::_initRenderMaterials() void NinecraftApp::_initInput() { m_bIsTouchscreen = platform()->isTouchscreen(); - getOptions()->m_bUseController.set(platform()->hasGamepad()); + + //If someone has a gamepad connected, certainly they want to use it + if (platform()->hasGamepad()) + IInputHolder::activeType = IInputHolder::CONTROLLER; + else if (platform()->isTouchscreen()) + IInputHolder::activeType = IInputHolder::TOUCHSCREEN; + getOptions()->loadControls(); _reloadInput(); } @@ -365,13 +371,10 @@ void NinecraftApp::update() Multitouch::commit(); - if (getOptions()->m_bUseController.get()) + GameControllerHandler* pControllerHandler = platform()->getGameControllerHandler(); + if (pControllerHandler) { - GameControllerHandler* pControllerHandler = platform()->getGameControllerHandler(); - if (pControllerHandler) - { - pControllerHandler->refresh(); - } + pControllerHandler->refresh(); } Minecraft::update(); diff --git a/source/client/gui/Gui.cpp b/source/client/gui/Gui.cpp index 38ccbf65c..944f4b1bf 100644 --- a/source/client/gui/Gui.cpp +++ b/source/client/gui/Gui.cpp @@ -366,11 +366,11 @@ void Gui::handleScrollWheel(bool down) m_pMinecraft->m_pLocalPlayer->m_pInventory->selectSlot(slot); } -void Gui::handleKeyPressed(int keyCode) +void Gui::handleControlPressed(const ControlBind& bind) { Options* options = m_pMinecraft->getOptions(); - if (options->isKey(KM_INVENTORY, keyCode)) + if (options->isControl(KM_INVENTORY, bind)) { if (m_pMinecraft->m_pGameMode->isSurvivalType()) m_pMinecraft->setScreen(new InventoryScreen(m_pMinecraft->m_pLocalPlayer)); @@ -379,8 +379,8 @@ void Gui::handleKeyPressed(int keyCode) return; } - bool slotL = options->isKey(KM_SLOT_L, keyCode); - bool slotR = options->isKey(KM_SLOT_R, keyCode); + bool slotL = options->isControl(KM_SLOT_L, bind); + bool slotR = options->isControl(KM_SLOT_R, bind); if (slotL || slotR) { int maxItems = getNumSlots() - 1; @@ -405,10 +405,10 @@ void Gui::handleKeyPressed(int keyCode) return; } - if (options->isKey(KM_CHAT, keyCode) || options->isKey(KM_CHAT_CMD, keyCode)) + if (options->isControl(KM_CHAT, bind) || options->isControl(KM_CHAT_CMD, bind)) { if (!m_pMinecraft->m_pScreen) - m_pMinecraft->setScreen(new ChatScreen(m_pMinecraft->getOptions()->isKey(KM_CHAT_CMD, keyCode))); + m_pMinecraft->setScreen(new ChatScreen(m_pMinecraft->getOptions()->isControl(KM_CHAT_CMD, bind))); } } diff --git a/source/client/gui/Gui.hpp b/source/client/gui/Gui.hpp index 9957d2f65..8d6167b34 100644 --- a/source/client/gui/Gui.hpp +++ b/source/client/gui/Gui.hpp @@ -65,7 +65,7 @@ class Gui : public GuiComponent bool isInside(int mx, int my); void handleClick(int id, int mx, int my); void handleScrollWheel(bool down); - void handleKeyPressed(int keyCode); + void handleControlPressed(const ControlBind&); void renderMessages(bool bShowAll); void renderHearts(bool topLeft); void renderArmor(bool topLeft); diff --git a/source/client/gui/GuiElement.cpp b/source/client/gui/GuiElement.cpp index 2ddae5136..67e2a20fd 100644 --- a/source/client/gui/GuiElement.cpp +++ b/source/client/gui/GuiElement.cpp @@ -63,7 +63,7 @@ bool GuiElement::areaNavigation(Minecraft* pMinecraft, AreaNavigation::Direction return false; } -void GuiElement::handleButtonPress(Minecraft* pMinecraft, int key) +void GuiElement::handleControlPress(Minecraft* pMinecraft, const ControlBind& key) { } diff --git a/source/client/gui/GuiElement.hpp b/source/client/gui/GuiElement.hpp index 8ac2bc45c..95072c66c 100644 --- a/source/client/gui/GuiElement.hpp +++ b/source/client/gui/GuiElement.hpp @@ -42,7 +42,7 @@ class GuiElement : public GuiComponent virtual bool pointerPressed(Minecraft* pMinecraft, const MenuPointer& pointer); virtual bool pointerReleased(Minecraft* pMinecraft, const MenuPointer& pointer); virtual bool areaNavigation(Minecraft* pMinecraft, AreaNavigation::Direction); - virtual void handleButtonPress(Minecraft* pMinecraft, int key); + virtual void handleControlPress(Minecraft* pMinecraft, const ControlBind& key); virtual void handleTextChar(Minecraft* pMinecraft, int chr); virtual void handleClipboardPaste(const std::string& content); virtual void handleScroll(float force); diff --git a/source/client/gui/Screen.cpp b/source/client/gui/Screen.cpp index 5c029da2b..6611bbab2 100644 --- a/source/client/gui/Screen.cpp +++ b/source/client/gui/Screen.cpp @@ -47,7 +47,9 @@ Screen::Screen() m_bRenderPointer = false; m_lastTimeMoved = 0; m_cursorTick = 0; - m_uiTheme = UI_GENERIC; + m_themeSelection = UI_GENERIC; + m_uiTheme = UI_POCKET; + m_bUniversalUiTheme = false; } Screen::~Screen() @@ -56,7 +58,7 @@ Screen::~Screen() m_elements.clear(); } -void Screen::controllerEvent(GameController::StickID stickID, double deltaTime) +void Screen::controllerStickEvent(GameController::StickID stickID, double deltaTime) { // @TODO: this probably shouldn't be here GameController::StickEvent event; @@ -139,10 +141,10 @@ void Screen::init(Minecraft* pMinecraft, int width, int height) m_pFont = pMinecraft->m_pFont; // Apply UI theme to current Screen based on user preference - UITheme userTheme = m_pMinecraft->getOptions()->getUiTheme(); + UITheme userTheme = pMinecraft->getOptions()->getUiTheme(); // We don't bother applying the console theme automatically for generic screens because this completely fucks scaling - if (m_uiTheme == UI_UNIVERSAL || (m_uiTheme == UI_GENERIC && userTheme != UI_CONSOLE)) - m_uiTheme = m_pMinecraft->getOptions()->getUiTheme(); + if (m_themeSelection == UI_UNIVERSAL || (m_themeSelection == UI_GENERIC && userTheme != UI_CONSOLE)) + m_uiTheme = userTheme; setSize(width, height); initMenuPointer(); @@ -150,44 +152,44 @@ void Screen::init(Minecraft* pMinecraft, int width, int height) m_bLastPointerPressedState = false; } -void Screen::keyPressed(int key) +void Screen::controlPressed(const ControlBind& bind) { Options& options = *m_pMinecraft->getOptions(); GuiElement* element = _getSelectedElement(); - if (options.isKey(KM_MENU_CANCEL, key)) + if (options.isControl(KM_MENU_CANCEL, bind)) { m_pMinecraft->handleBack(false); } - if (m_pMinecraft->getOptions()->isKey(KM_MENU_TAB_LEFT, key)) + if (options.isControl(KM_MENU_TAB_LEFT, bind)) { prevTab(); } - if (m_pMinecraft->getOptions()->isKey(KM_MENU_TAB_RIGHT, key)) + if (options.isControl(KM_MENU_TAB_RIGHT, bind)) { nextTab(); } if (doElementTabbing()) { - if (options.isKey(KM_MENU_DOWN, key)) + if (options.isControl(KM_MENU_DOWN, bind)) { _areaNavigation(AreaNavigation::DOWN); } - else if (options.isKey(KM_MENU_UP, key)) + else if (options.isControl(KM_MENU_UP, bind)) { _areaNavigation(AreaNavigation::UP); } - else if (options.isKey(KM_MENU_RIGHT, key)) + else if (options.isControl(KM_MENU_RIGHT, bind)) { _areaNavigation(AreaNavigation::RIGHT); } - else if (options.isKey(KM_MENU_LEFT, key)) + else if (options.isControl(KM_MENU_LEFT, bind)) { _areaNavigation(AreaNavigation::LEFT); } - else if (options.isKey(KM_MENU_OK, key)) + else if (options.isControl(KM_MENU_OK, bind)) { if (element && element->isEnabled()) { @@ -204,7 +206,7 @@ void Screen::keyPressed(int key) if (element && element->isEnabled()) { - element->handleButtonPress(m_pMinecraft, key); + element->handleControlPress(m_pMinecraft, bind); } } @@ -255,6 +257,11 @@ void Screen::handleKeyboardClosed() } } +bool Screen::validate(Minecraft*) +{ + return true; +} + static const char* g_panoramaList[] = { "gui/background/panorama_0.png", @@ -530,11 +537,14 @@ void Screen::selectElement(GuiElement* element) bool Screen::_areaNavigation(AreaNavigation::Direction dir) { - if (!m_pSelectedElement) return false; + if (m_pSelectedElement && m_pSelectedElement->areaNavigation(m_pMinecraft, dir)) return true; - if (m_pSelectedElement->areaNavigation(m_pMinecraft, dir)) return true; - - if (selectElementById(Navigation(this).navigateCyclic(dir, m_pSelectedElement->m_xPos + m_pSelectedElement->m_width / 2, m_pSelectedElement->m_yPos + m_pSelectedElement->m_height / 2))) + if ((m_pSelectedElement && selectElementById(Navigation(this).navigateCyclic(dir, + m_pSelectedElement->m_xPos + m_pSelectedElement->m_width / 2, + m_pSelectedElement->m_yPos + m_pSelectedElement->m_height / 2))) || + (!m_pSelectedElement && selectElementById(Navigation(this).navigate(dir, + m_width / 2, + m_height / 2, true)))) { _playSelectSound(); return true; @@ -723,7 +733,14 @@ void Screen::updateEvents() if (_useController()) { checkForPointerEvent(MOUSE_BUTTON_LEFT); - controllerEvent(); + + while(GameControllerManager::next()) + controllerEvent(); + + _processControllerDirection(1); + _processControllerDirection(2); + + controllerStickEvent(2); } } @@ -758,15 +775,26 @@ void Screen::keyboardEvent() } if (Keyboard::getEventKeyState()) - keyPressed(Keyboard::getEventKey()); + controlPressed(ControlBind(Keyboard::getEventKey(), GameController::BUTTON_NONE)); } void Screen::controllerEvent() { - _processControllerDirection(1); - _processControllerDirection(2); + // Ugly hack x2 + if (!doElementTabbing()) + { + if (GameControllerManager::getEventButtonState() && m_pMinecraft->getOptions()->getControl(KM_MENU_OK).isButton(GameControllerManager::getEventButton())) + { + m_menuPointer.isPressed = true; + } + else + { + m_menuPointer.isPressed = false; + } + } - controllerEvent(2); + if (GameControllerManager::getEventButtonState()) + controlPressed(ControlBind(-1, GameControllerManager::getEventButton())); } void Screen::checkForPointerEvent(MouseButtonType button) diff --git a/source/client/gui/Screen.hpp b/source/client/gui/Screen.hpp index a936e10ed..316d2d59b 100644 --- a/source/client/gui/Screen.hpp +++ b/source/client/gui/Screen.hpp @@ -76,7 +76,7 @@ class Screen : public GuiComponent int getYOffset(); unsigned int getCursorMoveThrottle() const { return 65; } bool doElementTabbing() const; - void controllerEvent(GameController::StickID stickId, double deltaTime = 0.0); + void controllerStickEvent(GameController::StickID stickId, double deltaTime = 0.0); protected: virtual bool _areaNavigation(AreaNavigation::Direction); @@ -117,13 +117,14 @@ class Screen : public GuiComponent virtual void onTextBoxUpdated(int id) {}; virtual void pointerPressed(const MenuPointer& pointer, MouseButtonType btn); virtual void pointerReleased(const MenuPointer& pointer, MouseButtonType btn); - virtual void keyPressed(int); + virtual void controlPressed(const ControlBind&); virtual void handleTextChar(char); virtual void keyboardTextPaste(const std::string& text); virtual float getScale(int width, int height); static float GetConsoleScale(int height); virtual void setTextboxText(const std::string& text); virtual void handleKeyboardClosed(); + virtual bool validate(Minecraft*); // ported from 0.8 virtual void renderMenuBackground(float f); @@ -152,16 +153,25 @@ class Screen : public GuiComponent Screen* m_pScreen; }; + enum ThemeSelection + { + UI_SPECIFIC, // The Screen handles a specific UI Theme + UI_GENERIC, // The Screen is a Java / Pocket mix + UI_UNIVERSAL // The Screen automatically handles all UI themes + }; + int m_width; int m_height; bool m_bPassEvents; //@NOTE: This should be enabled only if the the actual screen handles the deletion of the previous screen, otherwise, there will be a memory leak! bool m_bDeletePrevious; + bool m_bUniversalUiTheme; Minecraft* m_pMinecraft; GuiElementList m_elements; GuiElement* m_pSelectedElement; Font* m_pFont; GuiElement* m_pClickedElement; + ThemeSelection m_themeSelection; UITheme m_uiTheme; #ifndef ORIGINAL_CODE diff --git a/source/client/gui/ScreenChooser.cpp b/source/client/gui/ScreenChooser.cpp index d2620083f..f48be2e22 100644 --- a/source/client/gui/ScreenChooser.cpp +++ b/source/client/gui/ScreenChooser.cpp @@ -6,7 +6,6 @@ #include "screens/inventory/CraftingScreen.hpp" #include "screens/inventory/ChestScreen.hpp" #include "screens/OptionsScreen.hpp" -#include "screens/OptionsScreen_Console.hpp" #include "screens/CreateWorldScreen.hpp" #include "screens/ProgressScreen.hpp" #include "screens/CreditsScreen.hpp" diff --git a/source/client/gui/VerticalLayout.cpp b/source/client/gui/VerticalLayout.cpp index b85fc8847..d5e4795e1 100644 --- a/source/client/gui/VerticalLayout.cpp +++ b/source/client/gui/VerticalLayout.cpp @@ -26,6 +26,11 @@ GuiElement* VerticalLayout::getElement(ID index) const return nullptr; } +bool VerticalLayout::isLastIn(AreaNavigation::Direction dir) +{ + return m_pSelectedElement && Navigation(this).navigate(dir, m_pSelectedElement->m_xPos + m_pSelectedElement->m_width / 2, m_pSelectedElement->m_yPos + m_pSelectedElement->m_height / 2) < 0; +} + bool VerticalLayout::selectElementById(ID id, bool sound) { GuiElement* element = getElement(id); @@ -99,7 +104,7 @@ void VerticalLayout::organize() } if (isSelected() && !m_pSelectedElement && m_pScreen->_useController()) - selectElementById(0, false); + startNavigation(); } void VerticalLayout::clear() @@ -115,18 +120,24 @@ void VerticalLayout::clear() } } +void VerticalLayout::startNavigation() +{ + AreaNavigation::ID id = Navigation(this).navigate(AreaNavigation::DOWN, m_pScreen->m_width / 2, 0); + if (id >= 0) + selectElementById(m_scrollAmount + id); +} + bool VerticalLayout::areaNavigation(Minecraft* mc, AreaNavigation::Direction dir) { if (m_pSelectedElement && m_pSelectedElement->areaNavigation(mc, dir)) return true; if (dir == AreaNavigation::DOWN) { - GuiElement* element = m_pSelectedElement; - if (element && isBottomElement(*element)) + if (isLastIn(dir)) { - if (m_bCyclic && !m_bCanScrollDown && m_scrollAmount > 0) + if (m_bCyclic && !m_bCanScrollDown) { - int x = element->m_xPos + element->m_width / 2; + int x = m_pSelectedElement->m_xPos + m_pSelectedElement->m_width / 2; updateScroll(0); AreaNavigation::ID id = Navigation(this).navigate(dir, x, 0, true); if (id >= 0) @@ -134,21 +145,28 @@ bool VerticalLayout::areaNavigation(Minecraft* mc, AreaNavigation::Direction dir return true; } - handleScroll(false); - areaNavigation(dir, element == getElement(ID(m_elements.size() - 1))); - return true; + if (m_bCanScrollDown) + { + GuiElement* oldElement = m_pSelectedElement; + while (oldElement == m_pSelectedElement) + { + handleScroll(false); + areaNavigation(dir, false); + } + + return true; + } } areaNavigation(dir); return true; } else if (dir == AreaNavigation::UP) { - GuiElement* element = m_pSelectedElement; - if (element && isTopElement(*element)) + if (isLastIn(dir)) { if (m_bCyclic && !m_scrollAmount) { - int x = element->m_xPos + element->m_width / 2; + int x = m_pSelectedElement->m_xPos + m_pSelectedElement->m_width / 2; while (m_bCanScrollDown) { updateScroll(m_scrollAmount + 1); @@ -189,7 +207,7 @@ void VerticalLayout::setSelected(bool b) GuiElement::setSelected(b); if (b && !m_pSelectedElement && m_pScreen->_useController()) - selectElementById(0, false); + startNavigation(); if (!b) selectElement(nullptr); @@ -284,5 +302,5 @@ bool VerticalLayout::Navigation::next(int& x, int& y, bool cycle) bool VerticalLayout::Navigation::isValid(ID id) { - return m_pLayout->m_pSelectedElement->getId() != (m_pLayout->m_scrollAmount + id); + return !m_pLayout->m_pSelectedElement || m_pLayout->m_pSelectedElement->getId() != (m_pLayout->m_scrollAmount + id); } \ No newline at end of file diff --git a/source/client/gui/VerticalLayout.hpp b/source/client/gui/VerticalLayout.hpp index d3dd64699..78d0673b5 100644 --- a/source/client/gui/VerticalLayout.hpp +++ b/source/client/gui/VerticalLayout.hpp @@ -12,6 +12,7 @@ class VerticalLayout : public GuiElement ~VerticalLayout(); GuiElement* getElement(ID) const; + bool isLastIn(AreaNavigation::Direction dir); bool isTopElement(GuiElement& element) const { return element.m_yPos == m_yPos; }; bool isBottomElement(GuiElement& element) const { return element.m_yPos == m_bottom; }; bool selectElementById(ID, bool sound = true); @@ -21,6 +22,7 @@ class VerticalLayout : public GuiElement void organize(); void clear(); + void startNavigation(); bool areaNavigation(Minecraft*, AreaNavigation::Direction) override; void areaNavigation(AreaNavigation::Direction, bool cyclic = false); void setSelected(bool); diff --git a/source/client/gui/components/OptionList.cpp b/source/client/gui/components/OptionList.cpp index 062f32f5e..fdbcfeebd 100644 --- a/source/client/gui/components/OptionList.cpp +++ b/source/client/gui/components/OptionList.cpp @@ -173,7 +173,8 @@ void OptionList::initControlsMenu() if (!m_pMinecraft->isTouchscreen()) m_items[idxSplit]->setEnabled(false); - m_items[idxController]->setEnabled(false); + if (!m_pMinecraft->platform()->hasGamepad()) + m_items[idxController]->setEnabled(false); } void OptionList::initVideoMenu() diff --git a/source/client/gui/components/TextBox.cpp b/source/client/gui/components/TextBox.cpp index f18371498..2351c501f 100644 --- a/source/client/gui/components/TextBox.cpp +++ b/source/client/gui/components/TextBox.cpp @@ -205,26 +205,26 @@ char TextBox::guessCharFromKey(int key) { #endif -void TextBox::handleButtonPress(Minecraft* pMinecraft, int key) +void TextBox::handleControlPress(Minecraft* pMinecraft, const ControlBind& bind) { Options& options = *pMinecraft->getOptions(); if (!hasFocus()) { - if (options.isKey(KM_MENU_OK, key)) + if (options.isControl(KM_MENU_OK, bind)) setFocused(true); return; } #ifndef HANDLE_CHARS_SEPARATELY - char guess = guessCharFromKey(key); + char guess = guessCharFromKey(bind.keyId); if (guess != '\0') { handleTextChar(guess); return; } #endif - switch (key) { + switch (bind.keyId) { case AKEYCODE_DEL: { // handled elsewhere, do not dupe diff --git a/source/client/gui/components/TextBox.hpp b/source/client/gui/components/TextBox.hpp index 59c02b0b1..ae2de1eec 100644 --- a/source/client/gui/components/TextBox.hpp +++ b/source/client/gui/components/TextBox.hpp @@ -40,7 +40,7 @@ class TextBox : public GuiElement public: void init(Font* pFont); bool pointerPressed(Minecraft* pMinecraft, const MenuPointer& pointer) override; - void handleButtonPress(Minecraft* pMinecraft, int key) override; + void handleControlPress(Minecraft* pMinecraft, const ControlBind&) override; void handleTextChar(Minecraft* pMinecraft, int chr) override; void handleClipboardPaste(const std::string& text) override; void render(Minecraft* pMinecraft, const MenuPointer& pointer) override; diff --git a/source/client/gui/components/TickBox.cpp b/source/client/gui/components/TickBox.cpp index 2bce1cf86..a4451515d 100644 --- a/source/client/gui/components/TickBox.cpp +++ b/source/client/gui/components/TickBox.cpp @@ -48,11 +48,9 @@ void TickBox::render(Minecraft* mc, const MenuPointer& pointer) if (!mc->m_pScreen->doElementTabbing()) setSelected(isHovered(mc, pointer)); - if (!isEnabled()) - currentShaderColor.a *= 0.5f; - Color unselectedColor = Color::TEXT_GREY; - unselectedColor.a = currentShaderColor.a; + if (!isEnabled()) + unselectedColor.a = 0.5f; mc->m_pFont->drawScalable( getMessage(), m_xPos + C_TICKBOX_SIZE + 5, @@ -64,7 +62,10 @@ void TickBox::render(Minecraft* mc, const MenuPointer& pointer) getMessage(), m_xPos + C_TICKBOX_SIZE + 4, m_yPos + m_height / 2 - 9, - Color(204, 196, 13, currentShaderColor.a)); + Color(204, 196, 13, unselectedColor.a)); + + if (!isEnabled()) + currentShaderColor.a *= 0.5f; blitSprite(*mc->m_pTextures, isSelected() ? "gui/console/Graphics/Tickbox_Over.png" : "gui/console/Graphics/Tickbox_Norm.png", m_xPos, m_yPos + (m_height - C_TICKBOX_SIZE) / 2, C_TICKBOX_SIZE, C_TICKBOX_SIZE, &m_materials.ui_textured_and_glcolor); diff --git a/source/client/gui/screens/ChatScreen.cpp b/source/client/gui/screens/ChatScreen.cpp index 748b0aeac..042775d3e 100644 --- a/source/client/gui/screens/ChatScreen.cpp +++ b/source/client/gui/screens/ChatScreen.cpp @@ -58,15 +58,15 @@ void ChatScreen::render(float f) Screen::render(f); } -void ChatScreen::keyPressed(int keyCode) +void ChatScreen::controlPressed(const ControlBind& bind) { if (!_useController()) { - if (m_pMinecraft->getOptions()->isKey(KM_MENU_OK, keyCode)) + if (m_pMinecraft->getOptions()->isControl(KM_MENU_OK, bind)) sendMessageAndExit(); } - Screen::keyPressed(keyCode); + Screen::controlPressed(bind); } void ChatScreen::handleKeyboardClosed() @@ -77,6 +77,11 @@ void ChatScreen::handleKeyboardClosed() Screen::handleKeyboardClosed(); } +bool ChatScreen::isPauseScreen() +{ + return false; +} + void ChatScreen::sendMessageAndExit() { m_pMinecraft->sendMessage(m_textChat.getText()); diff --git a/source/client/gui/screens/ChatScreen.hpp b/source/client/gui/screens/ChatScreen.hpp index 2be599ab8..df2f8e56d 100644 --- a/source/client/gui/screens/ChatScreen.hpp +++ b/source/client/gui/screens/ChatScreen.hpp @@ -24,8 +24,9 @@ class ChatScreen : public Screen void init() override; void removed() override; void render(float f) override; - void keyPressed(int keyCode) override; + void controlPressed(const ControlBind& bind) override; void handleKeyboardClosed() override; + bool isPauseScreen() override; void sendMessageAndExit(); diff --git a/source/client/gui/screens/CreditsScreen.cpp b/source/client/gui/screens/CreditsScreen.cpp index b5bfb604d..a3afde9ea 100644 --- a/source/client/gui/screens/CreditsScreen.cpp +++ b/source/client/gui/screens/CreditsScreen.cpp @@ -53,9 +53,9 @@ bool CreditsScreen::isInGameScreen() return true; } -void CreditsScreen::keyPressed(int code) +void CreditsScreen::controlPressed(const ControlBind& bind) { - Screen::keyPressed(code); + Screen::controlPressed(bind); } void CreditsScreen::tick() diff --git a/source/client/gui/screens/CreditsScreen.hpp b/source/client/gui/screens/CreditsScreen.hpp index 27185894e..e922449fc 100644 --- a/source/client/gui/screens/CreditsScreen.hpp +++ b/source/client/gui/screens/CreditsScreen.hpp @@ -15,7 +15,7 @@ class CreditsScreen : public Screen public: void init() override; bool isInGameScreen() override; - void keyPressed(int code) override; + void controlPressed(const ControlBind&) override; void tick() override; void render(float f) override; bool handleBackEvent(bool b) override; diff --git a/source/client/gui/screens/IngameBlockSelectionScreen.cpp b/source/client/gui/screens/IngameBlockSelectionScreen.cpp index 1875b5ba9..27e7124ae 100644 --- a/source/client/gui/screens/IngameBlockSelectionScreen.cpp +++ b/source/client/gui/screens/IngameBlockSelectionScreen.cpp @@ -337,18 +337,23 @@ void IngameBlockSelectionScreen::removed() m_pMinecraft->m_pGui->inventoryUpdated(); } -void IngameBlockSelectionScreen::keyPressed(int keyCode) +void IngameBlockSelectionScreen::controlPressed(const ControlBind& bind) { - if (!_useController() && m_pMinecraft->getOptions()->isKey(KM_INVENTORY, keyCode)) + if (!_useController() && m_pMinecraft->getOptions()->isControl(KM_INVENTORY, bind)) { m_pMinecraft->handleBack(false); } else { - Screen::keyPressed(keyCode); + Screen::controlPressed(bind); } } +bool IngameBlockSelectionScreen::isPauseScreen() +{ + return false; +} + void IngameBlockSelectionScreen::selectSlotAndClose() { Inventory* pInv = getInventory(); diff --git a/source/client/gui/screens/IngameBlockSelectionScreen.hpp b/source/client/gui/screens/IngameBlockSelectionScreen.hpp index 097af0f64..45b14dcbc 100644 --- a/source/client/gui/screens/IngameBlockSelectionScreen.hpp +++ b/source/client/gui/screens/IngameBlockSelectionScreen.hpp @@ -40,7 +40,8 @@ class IngameBlockSelectionScreen : public Screen void pointerPressed(const MenuPointer& pointer, MouseButtonType btn) override; void pointerReleased(const MenuPointer& pointer, MouseButtonType btn) override; void removed() override; - void keyPressed(int key) override; + void controlPressed(const ControlBind&) override; + bool isPauseScreen() override; private: SlotID m_selectedSlot; diff --git a/source/client/gui/screens/OptionsScreen_Console.cpp b/source/client/gui/screens/OptionsScreen_Console.cpp index 934283210..b829f07e4 100644 --- a/source/client/gui/screens/OptionsScreen_Console.cpp +++ b/source/client/gui/screens/OptionsScreen_Console.cpp @@ -35,7 +35,7 @@ void OptionsScreen_Console::_buttonClicked(Button* btn) void OptionsScreen_Console::init() { - Button* layoutButtons[] = {&m_btnHowToPlay, &m_btnControls, &m_btnSettings, &m_btnCredits, &m_btnResetToDefaults}; + Button* layoutButtons[] = { &m_btnHowToPlay, &m_btnControls, &m_btnSettings, &m_btnCredits, &m_btnResetToDefaults }; int buttonsWidth = 450; int buttonsHeight = 40; @@ -70,6 +70,16 @@ bool OptionsScreen_Console::handleBackEvent(bool b) return true; } +bool OptionsScreen_Console::validate(Minecraft* mc) +{ + if (mc->getOptions()->getUiTheme() != UI_CONSOLE) + { + mc->getScreenChooser()->pushOptionsScreen(m_pParent); + return false; + } + return true; +} + #define HEADER(text) do { m_layout.m_elements.push_back(new OptionHeader_Console(text)); currentIndex++; } while (0) #define OPTION(name) do { options.name.addGuiElement(m_layout.m_elements, m_uiTheme); currentIndex++; } while (0) @@ -134,6 +144,7 @@ OptionHeader_Console::OptionHeader_Console(const std::string& text) : m_text(text) { m_height = 22; + setNavigable(false); } void OptionHeader_Console::render(Minecraft* pMinecraft, const MenuPointer& pointer) diff --git a/source/client/gui/screens/OptionsScreen_Console.hpp b/source/client/gui/screens/OptionsScreen_Console.hpp index 3a7a3cbe7..52f4db0ee 100644 --- a/source/client/gui/screens/OptionsScreen_Console.hpp +++ b/source/client/gui/screens/OptionsScreen_Console.hpp @@ -32,6 +32,7 @@ class OptionsScreen_Console : public Screen void init() override; void render(float) override; bool handleBackEvent(bool) override; + bool validate(Minecraft*) override; private: Screen* m_pParent; diff --git a/source/client/gui/screens/PanelScreen_Console.cpp b/source/client/gui/screens/PanelScreen_Console.cpp index d70b231be..6be5760fc 100644 --- a/source/client/gui/screens/PanelScreen_Console.cpp +++ b/source/client/gui/screens/PanelScreen_Console.cpp @@ -6,6 +6,7 @@ PanelScreen_Console::PanelScreen_Console(Screen* parent) : m_pParent(parent) , m_layout(this) { + m_themeSelection = UI_SPECIFIC; m_uiTheme = UI_CONSOLE; m_bDeletePrevious = false; } diff --git a/source/client/gui/screens/PauseScreen.cpp b/source/client/gui/screens/PauseScreen.cpp index 0700e676c..440ea537f 100644 --- a/source/client/gui/screens/PauseScreen.cpp +++ b/source/client/gui/screens/PauseScreen.cpp @@ -119,3 +119,13 @@ void PauseScreen::_buttonClicked(Button* pButton) } #endif } + +bool PauseScreen::validate(Minecraft* mc) +{ + if (mc->getOptions()->getUiTheme() == UI_CONSOLE) + { + mc->getScreenChooser()->pushPauseScreen(); + return false; + } + return true; +} diff --git a/source/client/gui/screens/PauseScreen.hpp b/source/client/gui/screens/PauseScreen.hpp index 80a94bee6..8c69621a8 100644 --- a/source/client/gui/screens/PauseScreen.hpp +++ b/source/client/gui/screens/PauseScreen.hpp @@ -15,10 +15,11 @@ class PauseScreen : public Screen { public: PauseScreen(); - virtual void init() override; - virtual void tick() override; - virtual void render(float f) override; - virtual void _buttonClicked(Button*) override; + void init() override; + void tick() override; + void render(float f) override; + void _buttonClicked(Button*) override; + bool validate(Minecraft*) override; void updateServerVisibilityText(); diff --git a/source/client/gui/screens/PauseScreen_Console.cpp b/source/client/gui/screens/PauseScreen_Console.cpp index 9c20d057d..2e5380c1c 100644 --- a/source/client/gui/screens/PauseScreen_Console.cpp +++ b/source/client/gui/screens/PauseScreen_Console.cpp @@ -14,6 +14,7 @@ PauseScreen_Console::PauseScreen_Console() : m_btnLeaderboards.setEnabled(false); m_btnAchievements.setEnabled(false); + m_themeSelection = UI_SPECIFIC; m_uiTheme = UI_CONSOLE; } @@ -54,4 +55,14 @@ void PauseScreen_Console::_buttonClicked(Button* btn) m_pMinecraft->m_pLevel->saveGame(); // Minecraft auto-saves automatically when we hit the pause screen else if (btn->getId() == m_btnExitGame.getId()) m_pMinecraft->leaveGame(false); -} \ No newline at end of file +} + +bool PauseScreen_Console::validate(Minecraft* mc) +{ + if (mc->getOptions()->getUiTheme() != UI_CONSOLE) + { + mc->getScreenChooser()->pushPauseScreen(); + return false; + } + return true; +} diff --git a/source/client/gui/screens/PauseScreen_Console.hpp b/source/client/gui/screens/PauseScreen_Console.hpp index 018fef1e8..8243c5be9 100644 --- a/source/client/gui/screens/PauseScreen_Console.hpp +++ b/source/client/gui/screens/PauseScreen_Console.hpp @@ -10,6 +10,7 @@ class PauseScreen_Console : public Screen void init() override; void render(float) override; void _buttonClicked(Button*) override; + bool validate(Minecraft*) override; private: Button m_btnResume; diff --git a/source/client/gui/screens/SelectWorldScreen.cpp b/source/client/gui/screens/SelectWorldScreen.cpp index 18c54f512..daf86b280 100644 --- a/source/client/gui/screens/SelectWorldScreen.cpp +++ b/source/client/gui/screens/SelectWorldScreen.cpp @@ -76,23 +76,24 @@ bool SelectWorldScreen::isInGameScreen() return true; } -void SelectWorldScreen::keyPressed(int code) +void SelectWorldScreen::controlPressed(const ControlBind& bind) { + Options& options = *m_pMinecraft->getOptions(); #ifndef ORIGINAL_CODE - if (m_pMinecraft->getOptions()->getKey(KM_MENU_OK) == code) + if (options.isControl(KM_MENU_OK, bind)) m_pWorldSelectionList->selectItem(m_pWorldSelectionList->getItemAtPosition(m_width / 2, m_height / 2), false); #endif if (m_btnWorld.isSelected()) { - if (m_pMinecraft->getOptions()->getKey(KM_LEFT) == code) + if (options.isControl(KM_LEFT, bind)) m_pWorldSelectionList->stepLeft(); - if (m_pMinecraft->getOptions()->getKey(KM_RIGHT) == code) + if (options.isControl(KM_RIGHT, bind)) m_pWorldSelectionList->stepRight(); } - Screen::keyPressed(code); + Screen::controlPressed(bind); } static char g_SelectWorldFilterArray[] = { '/','\n','\r','\x09','\0','\xC','`','?','*','\\','<','>','|','"',':'}; diff --git a/source/client/gui/screens/SelectWorldScreen.hpp b/source/client/gui/screens/SelectWorldScreen.hpp index 7d1937175..b0b722ffb 100644 --- a/source/client/gui/screens/SelectWorldScreen.hpp +++ b/source/client/gui/screens/SelectWorldScreen.hpp @@ -24,7 +24,7 @@ class SelectWorldScreen : public Screen public: void init() override; bool isInGameScreen() override; - void keyPressed(int code) override; + void controlPressed(const ControlBind&) override; void tick() override; void render(float f) override; bool handleBackEvent(bool b) override; diff --git a/source/client/gui/screens/StartMenuScreen.cpp b/source/client/gui/screens/StartMenuScreen.cpp index 4d2dc03ac..292a3ff9c 100644 --- a/source/client/gui/screens/StartMenuScreen.cpp +++ b/source/client/gui/screens/StartMenuScreen.cpp @@ -238,4 +238,14 @@ bool StartMenuScreen::handleBackEvent(bool b) m_pMinecraft->quit(); } return true; -} \ No newline at end of file +} + +bool StartMenuScreen::validate(Minecraft* mc) +{ + if (mc->getOptions()->getUiTheme() == UI_CONSOLE) + { + mc->getScreenChooser()->pushStartScreen(); + return false; + } + return true; +} diff --git a/source/client/gui/screens/StartMenuScreen.hpp b/source/client/gui/screens/StartMenuScreen.hpp index f924d935f..9a44c74d3 100644 --- a/source/client/gui/screens/StartMenuScreen.hpp +++ b/source/client/gui/screens/StartMenuScreen.hpp @@ -33,6 +33,7 @@ class StartMenuScreen : public Screen void drawSplash(); bool handleBackEvent(bool b) override; + bool validate(Minecraft*) override; protected: Button m_startButton; diff --git a/source/client/gui/screens/StartMenuScreen_Console.cpp b/source/client/gui/screens/StartMenuScreen_Console.cpp index f67c0e40d..ce84afc66 100644 --- a/source/client/gui/screens/StartMenuScreen_Console.cpp +++ b/source/client/gui/screens/StartMenuScreen_Console.cpp @@ -17,6 +17,7 @@ StartMenuScreen_Console::StartMenuScreen_Console() : m_btnAchievements.setEnabled(false); m_btnDownload.setEnabled(false); + m_themeSelection = UI_SPECIFIC; m_uiTheme = UI_CONSOLE; m_splash = SplashManager::singleton().getSplash(); @@ -72,4 +73,14 @@ void StartMenuScreen_Console::_buttonClicked(Button* btn) m_pMinecraft->getScreenChooser()->pushOptionsScreen(this); else if (btn->getId() == m_btnExitGame.getId()) m_pMinecraft->quit(); -} \ No newline at end of file +} + +bool StartMenuScreen_Console::validate(Minecraft* mc) +{ + if (mc->getOptions()->getUiTheme() != UI_CONSOLE) + { + mc->getScreenChooser()->pushStartScreen(); + return false; + } + return true; +} diff --git a/source/client/gui/screens/StartMenuScreen_Console.hpp b/source/client/gui/screens/StartMenuScreen_Console.hpp index 715122f1f..100472c68 100644 --- a/source/client/gui/screens/StartMenuScreen_Console.hpp +++ b/source/client/gui/screens/StartMenuScreen_Console.hpp @@ -10,6 +10,7 @@ class StartMenuScreen_Console : public Screen void init() override; void render(float) override; void _buttonClicked(Button*) override; + bool validate(Minecraft*) override; private: Button m_btnPlayGame; diff --git a/source/client/gui/screens/inventory/ContainerScreen.cpp b/source/client/gui/screens/inventory/ContainerScreen.cpp index c751f60af..77cde805a 100644 --- a/source/client/gui/screens/inventory/ContainerScreen.cpp +++ b/source/client/gui/screens/inventory/ContainerScreen.cpp @@ -13,7 +13,7 @@ ContainerScreen::ContainerScreen(ContainerMenu* menu) : m_topPos(0), m_timeSlotDragged(0) { - m_uiTheme = UI_UNIVERSAL; + m_themeSelection = UI_UNIVERSAL; m_bRenderPointer = true; } @@ -212,16 +212,17 @@ void ContainerScreen::render(float partialTicks) if (!name.empty()) { int w = m_pFont->width(name); - int tx = m_menuPointer.x - m_leftPos + 12; - int ty = m_menuPointer.y - m_topPos - 12; if (m_uiTheme == UI_CONSOLE) { - blitNineSlice(*m_pMinecraft->m_pTextures, ScreenRenderer::POINTER_TEXT_PANEL_SLICES, tx - 6, ty - 6, w * 2 + 12, 28, 8); - MatrixStack::Ref tooltipMatrix = MatrixStack::World.push(); - m_pFont->drawScalable(name, tx, ty, -1); + int tx = m_menuPointer.x - m_leftPos + 12; + int ty = m_menuPointer.y - m_topPos - 28; + blitNineSlice(*m_pMinecraft->m_pTextures, ScreenRenderer::POINTER_TEXT_PANEL_SLICES, tx - 6, ty - 10, w * 2 + 12, 33, 8); + m_pFont->drawScalableShadow(name, tx, ty, -1); } else { + int tx = m_menuPointer.x - m_leftPos + 12; + int ty = m_menuPointer.y - m_topPos - 12; fillGradient(tx - 3, ty - 3, tx + w + 3, ty + 8 + 3, 0xC0000000, 0xC0000000); m_pFont->drawShadow(name, tx, ty, -1); } @@ -285,33 +286,34 @@ void ContainerScreen::slotClicked(const MenuPointer& pointer, MouseButtonType bu slotClicked(pointer, button, m_pMinecraft->m_pPlatform->shiftPressed()); } -void ContainerScreen::keyPressed(int keyCode) +void ContainerScreen::controlPressed(const ControlBind& bind) { - if (!_useController() && m_pMinecraft->getOptions()->isKey(KM_INVENTORY, keyCode)) + Options& options = *m_pMinecraft->getOptions(); + if (!_useController() && options.isControl(KM_INVENTORY, bind)) { m_pMinecraft->handleBack(false); } - else if (m_pMinecraft->getOptions()->isKey(KM_CONTAINER_QUICKMOVE, keyCode) && _useController()) + else if (options.isControl(KM_CONTAINER_QUICKMOVE, bind) && _useController()) { slotClicked(m_menuPointer, MOUSE_BUTTON_LEFT, true); } - else if (m_pMinecraft->getOptions()->isKey(KM_CONTAINER_SPLIT, keyCode) && _useController()) + else if (options.isControl(KM_CONTAINER_SPLIT, bind) && _useController()) { slotClicked(m_menuPointer, MOUSE_BUTTON_RIGHT, false); } else { if (_useController() && - ((m_pMinecraft->getOptions()->isKey(KM_MENU_UP, keyCode) && _selectSlotInDirection(AreaNavigation::UP)) || - (m_pMinecraft->getOptions()->isKey(KM_MENU_DOWN, keyCode) && _selectSlotInDirection(AreaNavigation::DOWN)) || - (m_pMinecraft->getOptions()->isKey(KM_MENU_RIGHT, keyCode) && _selectSlotInDirection(AreaNavigation::RIGHT)) || - (m_pMinecraft->getOptions()->isKey(KM_MENU_LEFT, keyCode) && _selectSlotInDirection(AreaNavigation::LEFT)))) + ((options.isControl(KM_MENU_UP, bind) && _selectSlotInDirection(AreaNavigation::UP)) || + (options.isControl(KM_MENU_DOWN, bind) && _selectSlotInDirection(AreaNavigation::DOWN)) || + (options.isControl(KM_MENU_RIGHT, bind) && _selectSlotInDirection(AreaNavigation::RIGHT)) || + (options.isControl(KM_MENU_LEFT, bind) && _selectSlotInDirection(AreaNavigation::LEFT)))) { _playSelectSound(); return; } - Screen::keyPressed(keyCode); + Screen::controlPressed(bind); } } diff --git a/source/client/gui/screens/inventory/ContainerScreen.hpp b/source/client/gui/screens/inventory/ContainerScreen.hpp index 274120153..b8de9a5e1 100644 --- a/source/client/gui/screens/inventory/ContainerScreen.hpp +++ b/source/client/gui/screens/inventory/ContainerScreen.hpp @@ -65,7 +65,7 @@ class ContainerScreen : public Screen void pointerPressed(const MenuPointer& pointer, MouseButtonType button) override; void pointerReleased(const MenuPointer& pointer, MouseButtonType button) override; void handlePointerPressed(bool isPressed) override; - void keyPressed(int key) override; + void controlPressed(const ControlBind&) override; const SlotDisplay& getSlotDisplay(const Slot&) const; diff --git a/source/client/options/Options.cpp b/source/client/options/Options.cpp index 4aea60bbc..98ccf792b 100644 --- a/source/client/options/Options.cpp +++ b/source/client/options/Options.cpp @@ -152,9 +152,9 @@ void Options::_load() { std::string key = strings[i], value = strings[i + 1]; - std::map::iterator opt = m_options.find(key); - if (opt != m_options.end()) - opt->second->load(value); + HashMap::iterator it = m_options.find(key); + if (it != m_options.end()) + it.value()->load(value); else if (key == "misc_oldtitle") logo3d = !readBool(value); else if (key == "gfx_resourcepacks") @@ -404,11 +404,11 @@ std::vector Options::getOptionStrings() #define SO(optname, value) do { vec.push_back(optname); vec.push_back(value); } while (0) std::stringstream ss; - for (std::map::iterator it = m_options.begin(); it != m_options.end(); ++it) + for (HashMap::iterator it = m_options.begin(); it != m_options.end(); ++it) { ss.str(""); - it->second->save(ss); - SO(it->first, ss.str()); + it.value()->save(ss); + SO(it.key(), ss.str()); } SO("gfx_resourcepacks", savePackArray(m_resourcePacks)); @@ -418,7 +418,7 @@ std::vector Options::getOptionStrings() void Options::loadControls() { // Win32 key codes are being used by default -#define KM(idx, name, code) m_keyMappings[idx] = KeyMapping(name, code) +#define KM(idx, name, code) m_controlMappings[idx] = ControlMapping(name, code) KM(KM_FORWARD, "key.forward", 'W'); KM(KM_LEFT, "key.left", 'A'); KM(KM_BACKWARD, "key.back", 'S'); @@ -465,7 +465,7 @@ void Options::loadControls() // @TODO: These should **really** not be defined in here. How about AppPlatform? -#define KM(idx,code) m_keyMappings[idx].value = code +#define KM(idx,code) m_controlMappings[idx].bind.keyId = code #ifdef USE_SDL KM(KM_FORWARD, SDLVK_w); KM(KM_LEFT, SDLVK_a); @@ -577,64 +577,37 @@ void Options::loadControls() #endif #undef KM - if (m_bUseController.get()) - { -#define KM(idx,code) m_keyMappings[idx].value = code -#ifdef USE_SDL - KM(KM_TOGGLEDEBUG, SDL_CONTROLLER_BUTTON_GUIDE); - KM(KM_JUMP, SDL_CONTROLLER_BUTTON_A); - KM(KM_MENU_UP, SDL_CONTROLLER_BUTTON_DPAD_UP); - KM(KM_MENU_DOWN, SDL_CONTROLLER_BUTTON_DPAD_DOWN); - KM(KM_MENU_LEFT, SDL_CONTROLLER_BUTTON_DPAD_LEFT); - KM(KM_MENU_RIGHT, SDL_CONTROLLER_BUTTON_DPAD_RIGHT); - KM(KM_MENU_TAB_LEFT, SDL_CONTROLLER_BUTTON_LEFTSHOULDER); - KM(KM_MENU_TAB_RIGHT, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER); - KM(KM_MENU_OK, SDL_CONTROLLER_BUTTON_A); - KM(KM_MENU_CANCEL, SDL_CONTROLLER_BUTTON_B); - KM(KM_DROP, SDL_CONTROLLER_BUTTON_B); - KM(KM_CHAT, SDL_CONTROLLER_BUTTON_BACK); - KM(KM_INVENTORY, SDL_CONTROLLER_BUTTON_Y); - KM(KM_SNEAK, SDL_CONTROLLER_BUTTON_RIGHTSTICK); - KM(KM_CONTAINER_QUICKMOVE, SDL_CONTROLLER_BUTTON_Y); - KM(KM_CONTAINER_SPLIT, SDL_CONTROLLER_BUTTON_X); - KM(KM_TOGGLE3RD, SDL_CONTROLLER_BUTTON_LEFTSTICK); - KM(KM_SLOT_L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER); - KM(KM_SLOT_R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER); - KM(KM_FLY_UP, SDL_CONTROLLER_BUTTON_A); - KM(KM_FLY_DOWN, SDL_CONTROLLER_BUTTON_RIGHTSTICK); -#else - KM(KM_TOGGLEDEBUG, GameController::BUTTON_GUIDE); - KM(KM_JUMP, GameController::BUTTON_A); - KM(KM_MENU_UP, GameController::BUTTON_DPAD_UP); - KM(KM_MENU_DOWN, GameController::BUTTON_DPAD_DOWN); - KM(KM_MENU_LEFT, GameController::BUTTON_DPAD_LEFT); - KM(KM_MENU_RIGHT, GameController::BUTTON_DPAD_RIGHT); - KM(KM_MENU_TAB_LEFT, GameController::BUTTON_LEFTSHOULDER); - KM(KM_MENU_TAB_RIGHT, GameController::BUTTON_RIGHTSHOULDER); - KM(KM_MENU_OK, GameController::BUTTON_A); - KM(KM_MENU_CANCEL, GameController::BUTTON_B); - KM(KM_MENU_PAUSE, GameController::BUTTON_START); - KM(KM_DROP, GameController::BUTTON_B); - KM(KM_CHAT, GameController::BUTTON_BACK); - KM(KM_INVENTORY, GameController::BUTTON_Y); - KM(KM_SNEAK, GameController::BUTTON_RIGHTSTICK); - KM(KM_CONTAINER_QUICKMOVE, GameController::BUTTON_Y); - KM(KM_CONTAINER_SPLIT, GameController::BUTTON_X); - KM(KM_TOGGLE3RD, GameController::BUTTON_LEFTSTICK); - KM(KM_SLOT_L, GameController::BUTTON_LEFTSHOULDER); - KM(KM_SLOT_R, GameController::BUTTON_RIGHTSHOULDER); - KM(KM_FLY_UP, GameController::BUTTON_A); - KM(KM_FLY_DOWN, GameController::BUTTON_RIGHTSTICK); -#endif -#undef KM - } +#define BTN(idx,code) m_controlMappings[idx].bind.buttonId = code + BTN(KM_TOGGLEDEBUG, GameController::BUTTON_GUIDE); + BTN(KM_JUMP, GameController::BUTTON_A); + BTN(KM_MENU_UP, GameController::BUTTON_DPAD_UP); + BTN(KM_MENU_DOWN, GameController::BUTTON_DPAD_DOWN); + BTN(KM_MENU_LEFT, GameController::BUTTON_DPAD_LEFT); + BTN(KM_MENU_RIGHT, GameController::BUTTON_DPAD_RIGHT); + BTN(KM_MENU_TAB_LEFT, GameController::BUTTON_LEFTSHOULDER); + BTN(KM_MENU_TAB_RIGHT, GameController::BUTTON_RIGHTSHOULDER); + BTN(KM_MENU_OK, GameController::BUTTON_A); + BTN(KM_MENU_CANCEL, GameController::BUTTON_B); + BTN(KM_MENU_PAUSE, GameController::BUTTON_START); + BTN(KM_DROP, GameController::BUTTON_B); + BTN(KM_CHAT, GameController::BUTTON_BACK); + BTN(KM_INVENTORY, GameController::BUTTON_Y); + BTN(KM_SNEAK, GameController::BUTTON_RIGHTSTICK); + BTN(KM_CONTAINER_QUICKMOVE, GameController::BUTTON_Y); + BTN(KM_CONTAINER_SPLIT, GameController::BUTTON_X); + BTN(KM_TOGGLE3RD, GameController::BUTTON_LEFTSTICK); + BTN(KM_SLOT_L, GameController::BUTTON_LEFTSHOULDER); + BTN(KM_SLOT_R, GameController::BUTTON_RIGHTSHOULDER); + BTN(KM_FLY_UP, GameController::BUTTON_A); + BTN(KM_FLY_DOWN, GameController::BUTTON_RIGHTSTICK); +#undef BTN } void Options::reset() { - for (std::map::iterator it = m_options.begin(); it != m_options.end(); ++it) + for (HashMap::iterator it = m_options.begin(); it != m_options.end(); ++it) { - it->second->reset(); + it.value()->reset(); } } diff --git a/source/client/options/Options.hpp b/source/client/options/Options.hpp index 0e1e3faeb..adeba0084 100644 --- a/source/client/options/Options.hpp +++ b/source/client/options/Options.hpp @@ -15,10 +15,12 @@ #include #include +#include "common/utility/HashMap.hpp" #include "common/threading/AsyncTask.hpp" +#include "client/player/input/GameController.hpp" #include "client/resources/ResourcePackManager.hpp" -enum eKeyMappingIndex +enum eControlMappingIndex { KM_FORWARD, KM_LEFT, @@ -65,23 +67,51 @@ enum eKeyMappingIndex KM_COUNT }; -struct KeyMapping +struct ControlBind +{ + //@TODO: Replace this with an universal key + int keyId; + GameController::EngineButtonID buttonId; + + ControlBind() : keyId(-1), buttonId(GameController::BUTTON_NONE) {} + ControlBind(int key, GameController::EngineButtonID button) : keyId(key), buttonId(button) {} + + bool isKey(int key) const { return keyId >= 0 && key == keyId; } + bool isButton(GameController::EngineButtonID button) const { return buttonId > GameController::BUTTON_NONE && button == buttonId; } + bool operator==(const ControlBind& other) const + { + return isKey(other.keyId) || isButton(other.buttonId); + } +}; + +struct ControlMapping { std::string key; - int value; + ControlBind bind; + int timesPressed; - KeyMapping() : value(-1) {} // key is automatically clear when constructed - KeyMapping(const char* keyName, int keyCode) : key(keyName), value(keyCode) {} + ControlMapping() : timesPressed(0) {} // key is automatically clear when constructed + ControlMapping(const char* keyName, int keyCode) : key(keyName), timesPressed(0) + { + bind.keyId = keyCode; + } + + void pressed() { ++timesPressed; } + void reset() { timesPressed = 0; } + bool consume() + { + if (timesPressed == 0) return false; + + --timesPressed; + return true; + } }; enum UITheme { UI_POCKET, UI_JAVA, - UI_CONSOLE, - - UI_GENERIC, // The Screen is a Java / Pocket mix - UI_UNIVERSAL // The Screen automatically handles all UI themes + UI_CONSOLE }; enum LogoType @@ -330,8 +360,6 @@ class HUDSizeOption : public MinMaxOption class Options { -public: - struct KeyBind; private: static bool _hasResourcePack(const ResourcePack& pack, ResourcePackStack& packs); static void _tryAddResourcePack(const std::string& name, ResourcePackStack& packs); @@ -368,8 +396,12 @@ class Options const AsyncTask& save(); std::vector getOptionStrings(); - int getKey(eKeyMappingIndex idx) const { return m_keyMappings[idx].value; } - bool isKey(eKeyMappingIndex idx, int keyCode) const { return getKey(idx) == keyCode; } + int getKey(eControlMappingIndex idx) const { return m_controlMappings[idx].bind.keyId; } + bool isKey(eControlMappingIndex idx, int keyCode) const { return getKey(idx) == keyCode; } + + ControlMapping& getControlMapping(eControlMappingIndex idx) { return m_controlMappings[idx]; } + const ControlBind& getControl(eControlMappingIndex idx) const { return m_controlMappings[idx].bind; } + bool isControl(eControlMappingIndex idx, const ControlBind& bind) const { return m_controlMappings[idx].bind == bind; } void loadControls(); void reset(); @@ -380,10 +412,10 @@ class Options private: Minecraft* m_pMinecraft; - std::map m_options; + HashMap m_options; AsyncTask m_saveTask; std::string m_filePath; - KeyMapping m_keyMappings[KM_COUNT]; + ControlMapping m_controlMappings[KM_COUNT]; public: friend class BoolOption; diff --git a/source/client/player/input/ControllerMoveInput.cpp b/source/client/player/input/ControllerMoveInput.cpp index cf813abe8..2da8e9475 100644 --- a/source/client/player/input/ControllerMoveInput.cpp +++ b/source/client/player/input/ControllerMoveInput.cpp @@ -43,9 +43,15 @@ void ControllerMoveInput::tick(Player* player) IMoveInput::tick(player); } -void ControllerMoveInput::setKey(int eventKey, bool eventKeyState) +void ControllerMoveInput::setKey(eControlMappingIndex ctrl, bool eventKeyState) { - KeyboardInput::setKey(eventKey, eventKeyState); + if (ctrl == KM_SNEAK) + { + if (eventKeyState) + KeyboardInput::setKey(ctrl, m_keys[INPUT_SNEAK] ^ 1); + } + else + KeyboardInput::setKey(ctrl, eventKeyState); //this->m_culledEntities = m_pOptions[36] == eventKey && eventKeyState; //this->field_21 = m_pOptions[34] == eventKey && eventKeyState; } diff --git a/source/client/player/input/ControllerMoveInput.hpp b/source/client/player/input/ControllerMoveInput.hpp index bfdaa8369..62e073982 100644 --- a/source/client/player/input/ControllerMoveInput.hpp +++ b/source/client/player/input/ControllerMoveInput.hpp @@ -12,7 +12,7 @@ class ControllerMoveInput : public KeyboardInput ControllerMoveInput(Options *options); void tick(Player *player) override; - void setKey(int eventKey, bool eventKeyState) override; + void setKey(eControlMappingIndex, bool eventKeyState) override; void releaseAllKeys() override; }; diff --git a/source/client/player/input/CustomInputHolder.cpp b/source/client/player/input/CustomInputHolder.cpp index c4dacfd4a..8f3d78600 100644 --- a/source/client/player/input/CustomInputHolder.cpp +++ b/source/client/player/input/CustomInputHolder.cpp @@ -8,11 +8,18 @@ #include "CustomInputHolder.hpp" -CustomInputHolder::CustomInputHolder(IMoveInput* pMoveInput, ITurnInput* pTurnInput, IBuildInput* pBuildInput) +CustomInputHolder::CustomInputHolder(IMoveInput* pMoveInput, ITurnInput* pTurnInput, IBuildInput* pBuildInput, bool isController) : + m_bIsController(isController) { setInputs(pMoveInput, pTurnInput, pBuildInput); } +bool CustomInputHolder::allowsType(Type type) const +{ + if (m_bIsController) return type == CONTROLLER; + return IInputHolder::allowsType(type); +} + IMoveInput* CustomInputHolder::getMoveInput() { return m_pMoveInput; diff --git a/source/client/player/input/CustomInputHolder.hpp b/source/client/player/input/CustomInputHolder.hpp index 3f4d2cb49..af49844cf 100644 --- a/source/client/player/input/CustomInputHolder.hpp +++ b/source/client/player/input/CustomInputHolder.hpp @@ -13,8 +13,9 @@ class CustomInputHolder : public IInputHolder { public: - CustomInputHolder(IMoveInput*, ITurnInput*, IBuildInput*); + CustomInputHolder(IMoveInput*, ITurnInput*, IBuildInput*, bool isController = false); + bool allowsType(Type) const override; IMoveInput* getMoveInput() override; ITurnInput* getTurnInput() override; IBuildInput* getBuildInput() override; @@ -26,5 +27,6 @@ class CustomInputHolder : public IInputHolder IMoveInput* m_pMoveInput; ITurnInput* m_pTurnInput; IBuildInput* m_pBuildInput; + bool m_bIsController; }; diff --git a/source/client/player/input/GameController.hpp b/source/client/player/input/GameController.hpp index f65ccf4f7..127ad9677 100644 --- a/source/client/player/input/GameController.hpp +++ b/source/client/player/input/GameController.hpp @@ -29,6 +29,7 @@ class GameController }; enum EngineButtonID { + BUTTON_NONE = -1, BUTTON_A, BUTTON_B, BUTTON_X, diff --git a/source/client/player/input/GameControllerHandler.hpp b/source/client/player/input/GameControllerHandler.hpp index 9b9064a99..4dc1e53a3 100644 --- a/source/client/player/input/GameControllerHandler.hpp +++ b/source/client/player/input/GameControllerHandler.hpp @@ -1,7 +1,6 @@ #pragma once -#include - +#include "common/utility/HashMap.hpp" #include "client/app/AppPlatform.hpp" #include "world/phys/Vec2.hpp" #include "GameController.hpp" @@ -11,8 +10,8 @@ class GameControllerHandler { public: - typedef std::map ButtonIDMap; - typedef std::map ButtonStateLookup; + typedef HashMap ButtonIDMap; + typedef HashMap ButtonStateLookup; public: GameControllerHandler(); diff --git a/source/client/player/input/GameControllerManager.cpp b/source/client/player/input/GameControllerManager.cpp index 404e57f2b..6580508db 100644 --- a/source/client/player/input/GameControllerManager.cpp +++ b/source/client/player/input/GameControllerManager.cpp @@ -9,6 +9,7 @@ #include #include "GameControllerManager.hpp" +#include "IInputHolder.hpp" const float GameControllerManager::DIRECTION_X_THRESHOLD = 0.3f; const float GameControllerManager::DIRECTION_Y_THRESHOLD = 0.3f; @@ -17,6 +18,9 @@ const float GameControllerManager::DIRECTION_Y_THRESHOLD = 0.3f; // 0.3051f float GameControllerManager::_deadzonesX[][2] = { DEADZONE(0.3f), DEADZONE(0.3f) }; // Java: { DEADZONE(0.15f), DEADZONE(0.20f) }; float GameControllerManager::_deadzonesY[][2] = { DEADZONE(0.3f), DEADZONE(0.3f) }; // Java: { DEADZONE(0.15f), DEADZONE(0.20f) }; +std::vector GameControllerManager::_inputs; +GameController::ButtonState GameControllerManager::_states[]; +size_t GameControllerManager::_index = -1; bool GameControllerManager::isTouchedValues[] = { false, false }; float GameControllerManager::stickValuesX[] = { 0.0f, 0.0f }; @@ -24,6 +28,55 @@ float GameControllerManager::stickValuesY[] = { 0.0f, 0.0f }; float GameControllerManager::triggerValues[] = { 0.0f, 0.0f }; bool GameControllerManager::inReset = true; +void GameControllerManager::feedButton(GameController::ButtonState state, GameController::EngineButtonID button) +{ + // Prevent Crashes + if (button >= GameController::BUTTON_MAX || button < 0) { + return; + } + + IInputHolder::activeType = IInputHolder::CONTROLLER; + + GameController::ButtonEvent event; + event.state = state; + event.id = button; + event.bAllowRemapping = false; + + _inputs.push_back(event); + + _states[button] = state; +} + +bool GameControllerManager::next() +{ + if ((size_t)_index + 1 >= _inputs.size()) + { + if (!_inputs.empty()) clear(); + return false; + } + + _index++; + return true; +} + +GameController::EngineButtonID GameControllerManager::getEventButton() +{ + return _inputs[_index].id; +} + +GameController::ButtonState GameControllerManager::getEventButtonState() +{ + return _inputs[_index].state; +} + +bool GameControllerManager::isButtonDown(GameController::EngineButtonID button) +{ + if (button < 0 || button >= GameController::BUTTON_MAX) + return false; + + return _states[button] == GameController::BTN_STATE_DOWN; +} + bool GameControllerManager::isValidStick(GameController::StickID stickId) { // We have 2 'sticks' on the Xperia Play @@ -63,6 +116,9 @@ void GameControllerManager::feedStickX(GameController::StickID stickId, bool tou stickValuesX[index] = x; // LCE has a deadzone of 20000, ~0.3f on 360 inReset = false; + + if (x != 0.0f) + IInputHolder::activeType = IInputHolder::CONTROLLER; } void GameControllerManager::feedStickY(GameController::StickID stickId, bool touched, float y) @@ -83,6 +139,9 @@ void GameControllerManager::feedStickY(GameController::StickID stickId, bool tou stickValuesY[index] = y; // LCE has a deadzone of 20000, ~0.3f on 360 inReset = false; + + if (y != 0.0f) + IInputHolder::activeType = IInputHolder::CONTROLLER; } void GameControllerManager::feedStick(GameController::StickID stickId, bool touched, float x, float y) @@ -192,8 +251,15 @@ float GameControllerManager::getPressure(int triggerNo) return isValidTrigger(triggerNo) ? triggerValues[triggerNo - 1] : 0; } +void GameControllerManager::clear() +{ + _index = -1; + _inputs.clear(); +} + void GameControllerManager::reset() { + clear(); feedStick(1, 0, 0.0f, 0.0f); feedStick(2, 0, 0.0f, 0.0f); feedTrigger(1, 0.0f); diff --git a/source/client/player/input/GameControllerManager.hpp b/source/client/player/input/GameControllerManager.hpp index 346aba1ea..38c1ee497 100644 --- a/source/client/player/input/GameControllerManager.hpp +++ b/source/client/player/input/GameControllerManager.hpp @@ -19,8 +19,16 @@ class GameControllerManager static const float DIRECTION_Y_THRESHOLD; static float _deadzonesX[2][2], _deadzonesY[2][2]; + static std::vector _inputs; + static GameController::ButtonState _states[GameController::BUTTON_MAX]; + static size_t _index; public: + static void feedButton(GameController::ButtonState, GameController::EngineButtonID); + static bool next(); + static GameController::EngineButtonID getEventButton(); + static GameController::ButtonState getEventButtonState(); + static bool isButtonDown(GameController::EngineButtonID); static bool isValidStick(GameController::StickID stickId); static float linearTransform(float, float, float, bool); // SDL2 feeds controller stick update events one axis at a time @@ -39,6 +47,7 @@ class GameControllerManager static bool isValidTrigger(int triggerNo); static void feedTrigger(int triggerNo, float x); static float getPressure(int triggerNo); + static void clear(); static void reset(); static bool isReset() { return inReset; } diff --git a/source/client/player/input/IInputHolder.cpp b/source/client/player/input/IInputHolder.cpp index 4d017b5d5..69f1a2d64 100644 --- a/source/client/player/input/IInputHolder.cpp +++ b/source/client/player/input/IInputHolder.cpp @@ -9,6 +9,8 @@ #include "IInputHolder.hpp" #include "Mouse.hpp" +IInputHolder::Type IInputHolder::activeType = KEYBOARD; + IInputHolder::IInputHolder() : m_feedbackX(0), m_feedbackY(0) @@ -32,3 +34,8 @@ void IInputHolder::setScreenSize(int width, int height) getTurnInput()->setScreenSize(width, height); getBuildInput()->setScreenSize(width, height); } + +bool IInputHolder::allowsType(Type type) const +{ + return type == KEYBOARD || type == MOUSE; +} diff --git a/source/client/player/input/IInputHolder.hpp b/source/client/player/input/IInputHolder.hpp index 233d97dce..0aa9448c3 100644 --- a/source/client/player/input/IInputHolder.hpp +++ b/source/client/player/input/IInputHolder.hpp @@ -15,10 +15,19 @@ class IInputHolder { public: + enum Type + { + KEYBOARD, + MOUSE, + CONTROLLER, + TOUCHSCREEN + }; + IInputHolder(); virtual ~IInputHolder(); virtual bool allowPicking(); virtual void setScreenSize(int width, int height); + virtual bool allowsType(Type) const; virtual IMoveInput* getMoveInput() = 0; virtual ITurnInput* getTurnInput() = 0; virtual IBuildInput* getBuildInput() = 0; @@ -30,5 +39,9 @@ class IInputHolder float m_feedbackX; float m_feedbackY; float m_feedbackAlpha; + +public: + static Type activeType; + static Type lastType; }; diff --git a/source/client/player/input/IMoveInput.cpp b/source/client/player/input/IMoveInput.cpp index 7b646287f..385213791 100644 --- a/source/client/player/input/IMoveInput.cpp +++ b/source/client/player/input/IMoveInput.cpp @@ -30,7 +30,7 @@ void IMoveInput::render(float f) { } -void IMoveInput::setKey(int eventKey, bool eventKeyState) +void IMoveInput::setKey(eControlMappingIndex, bool eventKeyState) { } diff --git a/source/client/player/input/IMoveInput.hpp b/source/client/player/input/IMoveInput.hpp index bcde29324..862b1cb56 100644 --- a/source/client/player/input/IMoveInput.hpp +++ b/source/client/player/input/IMoveInput.hpp @@ -10,6 +10,8 @@ #include "compat/LegacyCPP.hpp" +#include "client/options/Options.hpp" + class Player; enum @@ -32,7 +34,7 @@ class IMoveInput virtual void releaseAllKeys(); virtual void render(float f); - virtual void setKey(int eventKey, bool eventKeyState); + virtual void setKey(eControlMappingIndex, bool eventKeyState); virtual void setScreenSize(int width, int height); virtual void tick(Player*); diff --git a/source/client/player/input/Keyboard.cpp b/source/client/player/input/Keyboard.cpp index 562003650..2fefc279b 100644 --- a/source/client/player/input/Keyboard.cpp +++ b/source/client/player/input/Keyboard.cpp @@ -8,6 +8,7 @@ #include #include "Keyboard.hpp" +#include "IInputHolder.hpp" #include "GameMods.hpp" @@ -22,6 +23,8 @@ void Keyboard::feed(KeyState state, int key) return; } + IInputHolder::activeType = IInputHolder::KEYBOARD; + _inputs.push_back(KeyboardAction(key, state)); _states[key] = state; @@ -30,7 +33,10 @@ void Keyboard::feed(KeyState state, int key) bool Keyboard::next() { if ((size_t)_index + 1 >= _inputs.size()) + { + if (!_inputs.empty()) reset(); return false; + } _index++; return true; diff --git a/source/client/player/input/KeyboardInput.cpp b/source/client/player/input/KeyboardInput.cpp index 739a8106f..ff9bb2040 100644 --- a/source/client/player/input/KeyboardInput.cpp +++ b/source/client/player/input/KeyboardInput.cpp @@ -29,19 +29,19 @@ void KeyboardInput::releaseAllKeys() m_keys[i] = false; } -void KeyboardInput::setKey(int eventKey, bool eventKeyState) +void KeyboardInput::setKey(eControlMappingIndex ctrl, bool eventKeyState) { - int index = -1; - - if (m_pOptions->getKey(KM_FORWARD) == eventKey) index = INPUT_FORWARD; - else if (m_pOptions->getKey(KM_BACKWARD) == eventKey) index = INPUT_BACKWARD; - else if (m_pOptions->getKey(KM_LEFT) == eventKey) index = INPUT_LEFT; - else if (m_pOptions->getKey(KM_RIGHT) == eventKey) index = INPUT_RIGHT; - else if (m_pOptions->getKey(KM_JUMP) == eventKey) index = INPUT_JUMP; - else if (m_pOptions->getKey(KM_SNEAK) == eventKey) index = INPUT_SNEAK; - - if (index == -1) - return; + int index; + switch (ctrl) + { + case KM_FORWARD: index = INPUT_FORWARD; break; + case KM_BACKWARD: index = INPUT_BACKWARD; break; + case KM_LEFT: index = INPUT_LEFT; break; + case KM_RIGHT: index = INPUT_RIGHT; break; + case KM_JUMP: index = INPUT_JUMP; break; + case KM_SNEAK: index = INPUT_SNEAK; break; + default: index = -1; return; + } m_keys[index] = eventKeyState; } diff --git a/source/client/player/input/KeyboardInput.hpp b/source/client/player/input/KeyboardInput.hpp index 156af6b70..ae09499a4 100644 --- a/source/client/player/input/KeyboardInput.hpp +++ b/source/client/player/input/KeyboardInput.hpp @@ -20,7 +20,7 @@ class KeyboardInput : public IMoveInput KeyboardInput(Options*); void releaseAllKeys() override; - void setKey(int eventKey, bool eventKeyState) override; + void setKey(eControlMappingIndex, bool eventKeyState) override; void tick(Player*) override; public: diff --git a/source/client/player/input/MouseDevice.cpp b/source/client/player/input/MouseDevice.cpp index 393f7f214..450e451e6 100644 --- a/source/client/player/input/MouseDevice.cpp +++ b/source/client/player/input/MouseDevice.cpp @@ -8,6 +8,7 @@ #include #include "MouseDevice.hpp" +#include "IInputHolder.hpp" MouseDevice::MouseDevice() { @@ -24,7 +25,10 @@ MouseDevice::MouseDevice() void MouseDevice::feed(MouseButtonType buttonType, bool buttonState, int posX, int posY) { if (buttonType != MOUSE_BUTTON_NONE) + { _inputs.push_back(MouseAction(buttonType, buttonState, posX, posY, 0)); + IInputHolder::activeType = IInputHolder::MOUSE; + } // Make sure button type is valid if (buttonType < MOUSE_BUTTON_COUNT) @@ -53,7 +57,10 @@ short MouseDevice::getY() bool MouseDevice::next() { if ((size_t)_index + 1 >= _inputs.size()) + { + if (!_inputs.empty()) reset(); return false; + } _index++; return true; diff --git a/source/client/player/input/Multitouch.cpp b/source/client/player/input/Multitouch.cpp index 3ba22d8d3..81dd970e9 100644 --- a/source/client/player/input/Multitouch.cpp +++ b/source/client/player/input/Multitouch.cpp @@ -8,6 +8,7 @@ #include #include "Multitouch.hpp" +#include "IInputHolder.hpp" int Multitouch::_activePointerCount; int Multitouch::_activePointerList[MAX_TOUCHES]; @@ -50,6 +51,7 @@ void Multitouch::feed(MouseButtonType a1, bool a2, int a3, int a4, int fingerId) MouseDevice* pDevice = g(fingerId); pDevice->feed(a1, a2, a3, a4); + IInputHolder::activeType = IInputHolder::TOUCHSCREEN; if (a1 != MOUSE_BUTTON_NONE) { diff --git a/source/client/player/input/TouchInputHolder.cpp b/source/client/player/input/TouchInputHolder.cpp index 95f7873fe..06709abd8 100644 --- a/source/client/player/input/TouchInputHolder.cpp +++ b/source/client/player/input/TouchInputHolder.cpp @@ -39,6 +39,11 @@ bool TouchInputHolder::allowPicking() return false; } +bool TouchInputHolder::allowsType(Type type) const +{ + return type == TOUCHSCREEN; +} + IMoveInput* TouchInputHolder::getMoveInput() { return &m_touchScreenInput; diff --git a/source/client/player/input/TouchInputHolder.hpp b/source/client/player/input/TouchInputHolder.hpp index 36ba4944d..f3c383497 100644 --- a/source/client/player/input/TouchInputHolder.hpp +++ b/source/client/player/input/TouchInputHolder.hpp @@ -20,6 +20,7 @@ class TouchInputHolder : public IInputHolder public: TouchInputHolder(Minecraft*, Options*); bool allowPicking() override; + bool allowsType(Type) const override; IMoveInput* getMoveInput() override; ITurnInput* getTurnInput() override; IBuildInput* getBuildInput() override; diff --git a/source/client/player/input/TouchscreenInput_TestFps.cpp b/source/client/player/input/TouchscreenInput_TestFps.cpp index af67bd4f8..38f2d7bf7 100644 --- a/source/client/player/input/TouchscreenInput_TestFps.cpp +++ b/source/client/player/input/TouchscreenInput_TestFps.cpp @@ -48,7 +48,7 @@ void TouchscreenInput_TestFps::releaseAllKeys() field_6C[i] = false; } -void TouchscreenInput_TestFps::setKey(int eventKey, bool eventKeyState) +void TouchscreenInput_TestFps::setKey(eControlMappingIndex, bool eventKeyState) { } diff --git a/source/client/player/input/TouchscreenInput_TestFps.hpp b/source/client/player/input/TouchscreenInput_TestFps.hpp index 25ac08599..d19f93a98 100644 --- a/source/client/player/input/TouchscreenInput_TestFps.hpp +++ b/source/client/player/input/TouchscreenInput_TestFps.hpp @@ -24,7 +24,7 @@ class TouchscreenInput_TestFps : public IMoveInput, public GuiComponent // IMoveInput void releaseAllKeys() override; - void setKey(int eventKey, bool eventKeyState) override; + void setKey(eControlMappingIndex, bool eventKeyState) override; void setScreenSize(int width, int height) override; void tick(Player*) override; void render(float f) override; diff --git a/source/client/renderer/Font.cpp b/source/client/renderer/Font.cpp index 6ec81196f..4712bf336 100644 --- a/source/client/renderer/Font.cpp +++ b/source/client/renderer/Font.cpp @@ -8,7 +8,6 @@ #include "Font.hpp" #include "client/renderer/renderer/RenderMaterialGroup.hpp" -#include "client/renderer/renderer/Tesselator.hpp" #include "renderer/ShaderConstants.hpp" #include "renderer/MatrixStack.hpp" #include @@ -33,7 +32,7 @@ void Font::init(Options* pOpts) TextureData* pTexture = m_pTextures->getTextureData(m_fileName, true); if (!pTexture) return; - for (int i = 0; i < 256; i++) // character number + for (int i = 0; i < FONT_CHARS_AMOUNT; i++) // character number { // note: the 'widthMax' behavior is assumed. It might not be like that exactly int widthMax = 0; @@ -64,6 +63,10 @@ void Font::init(Options* pOpts) m_charWidthInt[i] = widthMax + 2; m_charWidthFloat[i] = float (widthMax) + 2; + Tesselator& t = Tesselator::instance; + t.begin(4); + buildChar(i, 0, 0); + m_charMeshes[i] = t.end(); } } @@ -165,7 +168,49 @@ void Font::drawWordWrap(const std::vector& lines, int x, int y, con void Font::draw(const std::string& str, int x, int y, const Color& color, bool bShadow) { - drawSlow(str, x, y, color, bShadow); + if (str.empty()) return; + + if (bShadow) + { + currentShaderDarkColor = Color(0.25f, 0.25f, 0.25f); + } + else + { + currentShaderDarkColor = Color::WHITE; + } + + m_pTextures->loadAndBindTexture(m_fileName); + + Color finalColor = color; + // For hex colors which don't specify an alpha + if (finalColor.a == 0.0f) + finalColor.a = 1.0f; + + MatrixStack::Ref mtx = MatrixStack::World.push(); + mtx->translate(Vec3(x, y, 0.0f)); + + currentShaderColor = finalColor; + float xOff = 0.0f; + + for (size_t i = 0; i < str.size(); i++) + { + if (str[i] == '\n') + { + mtx->translate(Vec3(0.0f, 12.0f, 0.0f)); + xOff = 0.0f; + continue; + } + + uint8_t x = uint8_t(str[i]); + + MatrixStack::Ref xmtx = MatrixStack::World.push(); + xmtx->translate(Vec3(xOff, 0.0f, 0.0f)); + m_charMeshes[x].render(m_materials.ui_text); + + xOff += m_charWidthFloat[x]; + } + + currentShaderColor = Color::WHITE; } void Font::drawSlow(const std::string& str, int x, int y, const Color& color, bool bShadow) @@ -188,18 +233,13 @@ void Font::drawSlow(const std::string& str, int x, int y, const Color& color, bo if (finalColor.a == 0.0f) finalColor.a = 1.0f; -#ifndef FEATURE_GFX_SHADERS - finalColor *= currentShaderDarkColor; -#endif - MatrixStack::Ref mtx = MatrixStack::World.push(); mtx->translate(Vec3(x, y, 0.0f)); + currentShaderColor = finalColor; Tesselator& t = Tesselator::instance; t.begin(4 * str.size()); - t.color(finalColor); - float cXPos = 0.0f, cYPos = 0.0f; for (size_t i = 0; i < str.size(); i++) @@ -219,6 +259,8 @@ void Font::drawSlow(const std::string& str, int x, int y, const Color& color, bo } t.draw(m_materials.ui_text); + + currentShaderColor = Color::WHITE; } void Font::onGraphicsReset() diff --git a/source/client/renderer/Font.hpp b/source/client/renderer/Font.hpp index 92020c250..7823b5233 100644 --- a/source/client/renderer/Font.hpp +++ b/source/client/renderer/Font.hpp @@ -11,6 +11,9 @@ #include "Textures.hpp" #include "client/options/Options.hpp" #include "renderer/MaterialPtr.hpp" +#include "client/renderer/renderer/Tesselator.hpp" + +#define FONT_CHARS_AMOUNT (256) class Font { @@ -47,8 +50,9 @@ class Font private: int field_0; - int m_charWidthInt[256]; - float m_charWidthFloat[256]; + int m_charWidthInt[FONT_CHARS_AMOUNT]; + float m_charWidthFloat[FONT_CHARS_AMOUNT]; + mce::Mesh m_charMeshes[FONT_CHARS_AMOUNT]; // huge gap, don't know why it's there... std::string m_fileName; Options* m_pOptions; diff --git a/source/client/renderer/GameRenderer.cpp b/source/client/renderer/GameRenderer.cpp index d71fd3d4e..96d8b0280 100644 --- a/source/client/renderer/GameRenderer.cpp +++ b/source/client/renderer/GameRenderer.cpp @@ -657,7 +657,7 @@ void GameRenderer::render(const Timer& timer) { if (m_pMinecraft->m_pScreen) { - m_pMinecraft->m_pScreen->controllerEvent(1, timer.m_deltaTime); + m_pMinecraft->m_pScreen->controllerStickEvent(1, timer.m_deltaTime); } } else diff --git a/source/common/utility/HashMap.hpp b/source/common/utility/HashMap.hpp new file mode 100644 index 000000000..b9ea7800e --- /dev/null +++ b/source/common/utility/HashMap.hpp @@ -0,0 +1,414 @@ +#pragma once + +#include +#include +#include +#include +#include + +template +struct HashMapEntry +{ + std::pair pair; + bool bOccupied; + bool bDeleted; + + HashMapEntry() : bOccupied(false), bDeleted(false) {} +}; + +template +struct HashFunction +{ + size_t operator()(const TKey& key) const; +}; + +template<> +struct HashFunction +{ + size_t operator()(const int8_t& key) const { return key; } +}; + +template<> +struct HashFunction +{ + size_t operator()(const uint8_t& key) const { return key; } +}; + +template<> +struct HashFunction +{ + size_t operator()(const int16_t& key) const { return key; } +}; + +template<> +struct HashFunction +{ + size_t operator()(const uint16_t& key) const { return key; } +}; + +template<> +struct HashFunction +{ + size_t operator()(const int32_t& key) const { return key; } +}; + +template<> +struct HashFunction +{ + size_t operator()(const uint32_t& key) const { return key; } +}; + +template<> +struct HashFunction +{ + size_t operator()(const int64_t& key) const { return key; } +}; + +template<> +struct HashFunction +{ + size_t operator()(const uint64_t& key) const { return key; } +}; + +template<> +struct HashFunction +{ + size_t operator()(const std::string& key) const + { + size_t hash = 5381; + for (size_t i = 0; i < key.length(); i++) + hash = ((hash << 5) + hash) + key[i]; + return hash; + } +}; + +template<> +struct HashFunction +{ + size_t operator()(const char* key) const + { + size_t hash = 5381; + while (*key) + hash = ((hash << 5) + hash) + size_t(*key++); + return hash; + } +}; + +template > +class HashMap +{ +private: + size_t _findSlot(const TKey& key) const + { + size_t hash = m_HashFunc(key); + size_t slot = hash % capacity(); + size_t originalSlot = slot; + + while (m_entries[slot].bOccupied) + { + if (!m_entries[slot].bDeleted && _keysEqual(m_entries[slot].pair.first, key)) + return slot; + + slot = (slot + 1) % capacity(); + + if (slot == originalSlot) + break; + } + + return capacity(); + } + + size_t _findSlotForInsert(const TKey& key) const + { + size_t hash = m_HashFunc(key); + size_t slot = hash % capacity(); + size_t firstDeleted = capacity(); + size_t originalSlot = slot; + + while (m_entries[slot].bOccupied) + { + if (m_entries[slot].bDeleted) + { + if (firstDeleted == capacity()) + firstDeleted = slot; + } + else if (_keysEqual(m_entries[slot].pair.first, key)) + { + return slot; + } + + slot = (slot + 1) % capacity(); + + if (slot == originalSlot) + break; + } + + if (firstDeleted != capacity()) + return firstDeleted; + + return slot; + } + + void _resize(size_t newCapacity) + { + std::vector > oldEntries = m_entries; + + m_entries.clear(); + m_entries.resize(newCapacity); + m_size = 0; + + for (size_t i = 0; i < oldEntries.size(); i++) + { + if (oldEntries[i].bOccupied && !oldEntries[i].bDeleted) + insert(oldEntries[i].pair.first, oldEntries[i].pair.second); + } + } + + bool _keysEqual(const TKey& a, const TKey& b) const { return a == b; } + +public: + HashMap() : m_size(0) + { + _resize(DEFAULT_CAPACITY); + } + + HashMap(size_t initialCapacity) : + m_size(0) + { + _resize(initialCapacity); + } + + bool insert(const TKey& key, const TValue& value) + { + if ((m_size * 100) / capacity() >= MAX_LOAD_FACTOR_PERCENT) + _resize(capacity() * 2); + + size_t slot = _findSlotForInsert(key); + + if (m_entries[slot].bOccupied && !m_entries[slot].bDeleted) + { + m_entries[slot].pair.second = value; + return false; + } + + m_entries[slot].pair.first = key; + m_entries[slot].pair.second = value; + m_entries[slot].bOccupied = true; + m_entries[slot].bDeleted = false; + m_size++; + + return true; + } + + bool remove(const TKey& key) + { + return erase(find(key)) != end(); + } + + TValue* get(const TKey& key) + { + if (m_size == 0) + return NULL; + + size_t slot = _findSlot(key); + + if (slot == capacity()) + return NULL; + + return &m_entries[slot].pair.second; + } + + const TValue* get(const TKey& key) const + { + if (m_size == 0) + return NULL; + + size_t slot = _findSlot(key); + + if (slot == capacity()) + return NULL; + + return &m_entries[slot].pair.second; + } + + TValue& operator[](const TKey& key) + { + TValue* pValue = get(key); + if (pValue) + return *pValue; + + insert(key, TValue()); + return *get(key); + } + + void clear() + { + for (size_t i = 0; i < capacity(); ++i) + { + m_entries[i] = HashMapEntry(); + } + m_size = 0; + } + + size_t size() const { return m_size; } + size_t capacity() const { return m_entries.size(); } + bool empty() const { return m_size == 0; } + + class iterator + { + private: + void _findNextOccupied() + { + while (m_index < m_entries->size() && (!m_entries->at(m_index).bOccupied || m_entries->at(m_index).bDeleted)) + m_index++; + } + + public: + iterator(std::vector >* entries, size_t index) : + m_entries(entries), + m_index(index) + { + if (m_index < m_entries->size() && (!m_entries->at(m_index).bOccupied || m_entries->at(m_index).bDeleted)) + _findNextOccupied(); + } + + iterator& operator++() + { + m_index++; + _findNextOccupied(); + return *this; + } + + iterator operator++(int) + { + iterator it = *this; + ++(*this); + return it; + } + + bool operator==(const iterator& other) const { return m_entries == other.m_entries && m_index == other.m_index; } + bool operator!=(const iterator& other) const { return !(*this == other); } + + TKey& key() { return m_entries->at(m_index).pair.first; } + TValue& value() { return m_entries->at(m_index).pair.second; } + std::pair* operator->() { return &m_entries->at(m_index); } + + private: + std::vector >* m_entries; + size_t m_index; + + friend class HashMap; + }; + + iterator find(const TKey& key) + { + if (m_size == 0) + return end(); + + size_t slot = _findSlot(key); + + if (slot == capacity()) + return end(); + + return iterator(&m_entries, slot); + } + + iterator begin() { return iterator(&m_entries, 0); } + iterator end() { return iterator(&m_entries, capacity()); } + + iterator erase(iterator it) + { + if (it == end()) + return it; + + size_t slot = it.m_index; + if (slot < capacity() && + m_entries[slot].bOccupied && + !m_entries[slot].bDeleted) + { + m_entries[slot].pair = std::pair(); + m_entries[slot].bDeleted = true; + m_size--; + } + + return ++it; + } + + iterator erase(iterator first, iterator last) + { + while (first != last) + first = erase(first); + + return first; + } + + class const_iterator + { + private: + void _findNextOccupied() + { + while (m_index < m_entries->size() && (!m_entries->at(m_index).bOccupied || m_entries->at(m_index).bDeleted)) + m_index++; + } + + public: + const_iterator(const std::vector >* entries, size_t index) : + m_entries(entries), + m_index(index) + { + if (m_index < entries->size() && (!m_entries->at(m_index).bOccupied || m_entries->at(m_index).bDeleted)) + _findNextOccupied(); + } + + const_iterator& operator++() + { + m_index++; + _findNextOccupied(); + return *this; + } + + const_iterator operator++(int) + { + const_iterator it = *this; + ++(*this); + return it; + } + + bool operator==(const const_iterator& other) const { return m_entries == other.m_entries && m_index == other.m_index; } + bool operator!=(const const_iterator& other) const { return !(*this == other); } + + const TKey& key() { return m_entries->at(m_index).pair.first; } + const TValue& value() { return m_entries->at(m_index).pair.second; } + const std::pair* operator->() { return &m_entries->at(m_index); } + + private: + const std::vector >* m_entries; + size_t m_index; + + friend class HashMap; + }; + + const_iterator find(const TKey& key) const + { + if (m_size == 0) + return end(); + + size_t slot = _findSlot(key); + + if (slot == capacity()) + return end(); + + return const_iterator(&m_entries, slot); + } + + const_iterator begin() const { return const_iterator(&m_entries, 0); } + const_iterator end() const { return const_iterator(&m_entries, capacity()); } + +private: + std::vector > m_entries; + size_t m_size; + THash m_HashFunc; + + static const size_t DEFAULT_CAPACITY = 16; + static const size_t MAX_LOAD_FACTOR_PERCENT = 75; +}; \ No newline at end of file diff --git a/source/nbt/CompoundTag.cpp b/source/nbt/CompoundTag.cpp index 398ae945b..74b50795d 100644 --- a/source/nbt/CompoundTag.cpp +++ b/source/nbt/CompoundTag.cpp @@ -3,6 +3,7 @@ #include "CompoundTag.hpp" #include "common/Util.hpp" +#include "common/Logger.hpp" CompoundTag::CompoundTag() { @@ -11,9 +12,9 @@ CompoundTag::CompoundTag() void CompoundTag::write(IDataOutput& dos) const { - for (std::map::const_iterator it = m_tags.begin(); it != m_tags.end(); it++) + for (NamedTagMap::const_iterator it = m_tags.begin(); it != m_tags.end(); it++) { - writeNamedTag(it->first, *it->second, dos); + writeNamedTag(it.key(), *it.value(), dos); } dos.writeInt8(TAG_TYPE_END); @@ -144,15 +145,15 @@ bool CompoundTag::contains(const std::string& name, Tag::Type type) const const Tag* CompoundTag::get(const std::string& name) const { - std::map::const_iterator it = m_tags.find(name); - if (it != m_tags.end()) return it->second; + NamedTagMap::const_iterator it = m_tags.find(name); + if (it != m_tags.end()) return it.value(); return nullptr; } Tag* CompoundTag::get(const std::string& name) { - std::map::iterator it = m_tags.find(name); - if (it != m_tags.end()) return it->second; + NamedTagMap::iterator it = m_tags.find(name); + if (it != m_tags.end()) return it.value(); return nullptr; } @@ -300,7 +301,7 @@ CompoundTag* CompoundTag::uniqueClone() const for (NamedTagMap::const_iterator it = m_tags.begin(); it != m_tags.end(); it++) { - newTag->put(it->first, it->second->copy()); + newTag->put(it.key(), it.value()->copy()); } return newTag; @@ -308,12 +309,12 @@ CompoundTag* CompoundTag::uniqueClone() const bool CompoundTag::remove(const std::string& name) { - std::map::iterator it = m_tags.find(name); + NamedTagMap::iterator it = m_tags.find(name); if (it == m_tags.end()) return false; - delete it->second; - m_tags.erase(it); + delete it.value(); + m_tags.remove(name); return true; } @@ -322,9 +323,9 @@ void CompoundTag::deleteChildren() { if (!m_bLeak) { - for (std::map::iterator it = m_tags.begin(); it != m_tags.end(); it++) + for (NamedTagMap::iterator it = m_tags.begin(); it != m_tags.end(); it++) { - Tag* tag = it->second; + Tag* tag = it.value(); tag->deleteChildren(); delete tag; } @@ -338,17 +339,13 @@ bool CompoundTag::operator==(const Tag& other) const const CompoundTag& other2 = (const CompoundTag&)(other); if (getId() == other2.getId() && m_tags.size() == other2.m_tags.size()) { - for (std::map::const_iterator it = m_tags.begin(); it != m_tags.end(); it++) + for (NamedTagMap::const_iterator it = m_tags.begin(); it != m_tags.end(); it++) { - std::pair pair = *it, pair2; - std::map::const_iterator it2 = other2.m_tags.find(pair.first); + NamedTagMap::const_iterator it2 = other2.m_tags.find(it.key()); if (it2 == other2.m_tags.end()) return false; // Failed to find tag in other by name - pair2 = *it2; - Tag *tag = pair.second, *tag2 = pair2.second; - - if (tag != tag2) + if (it.value() != it2.value()) return false; } diff --git a/source/nbt/CompoundTag.hpp b/source/nbt/CompoundTag.hpp index 4ed865255..6b02c3165 100644 --- a/source/nbt/CompoundTag.hpp +++ b/source/nbt/CompoundTag.hpp @@ -14,11 +14,12 @@ #include "StringTag.hpp" #include "FloatTag.hpp" #include "DoubleTag.hpp" +#include "common/utility/HashMap.hpp" class CompoundTag : public Tag { public: - typedef std::map NamedTagMap; + typedef HashMap NamedTagMap; public: CompoundTag(); diff --git a/source/world/entity/Arrow.hpp b/source/world/entity/Arrow.hpp index 5a08fc1da..83b90f6ad 100644 --- a/source/world/entity/Arrow.hpp +++ b/source/world/entity/Arrow.hpp @@ -21,7 +21,7 @@ class Arrow : public Entity public: void shoot(float x, float y, float z, float speed, float r) { shoot(Vec3(x, y, z), speed, r); }; - void shoot(Vec3 pos, float speed, float r); + void shoot(Vec3, float speed, float r); void lerpMotion(float x, float y, float z) { lerpMotion(Vec3(x, y, z)); }; void lerpMotion(const Vec3& vel) override; diff --git a/source/world/entity/Skeleton.cpp b/source/world/entity/Skeleton.cpp index 84ce64d2d..5858a3eea 100644 --- a/source/world/entity/Skeleton.cpp +++ b/source/world/entity/Skeleton.cpp @@ -33,7 +33,7 @@ void Skeleton::checkHurtTarget(Entity* ent, float f) { Arrow* arrow = new Arrow(m_pLevel, this); arrow->m_pos.y += 1; - float var8 = ent->m_pos.y - 0.2f - arrow->m_pos.y; + float var8 = ent->m_pos.y + ent->getHeadHeight() - 0.2f - arrow->m_pos.y; float var10 = Mth::sqrt(delta_x * delta_x + delta_z * delta_z) * 0.2f; m_pLevel->playSound(this, "random.bow", 1.0f, 1.0f / (m_random.nextFloat() * 0.4f + 0.8f)); m_pLevel->addEntity(arrow); diff --git a/source/world/inventory/ContainerMenu.cpp b/source/world/inventory/ContainerMenu.cpp index 8c083c908..78efea533 100644 --- a/source/world/inventory/ContainerMenu.cpp +++ b/source/world/inventory/ContainerMenu.cpp @@ -258,7 +258,9 @@ ItemStack ContainerMenu::clicked(int slotIndex, MouseButtonType mouseButton, boo { if (carried.m_count <= slot->getMaxStackSize()) { - std::swap(carried, slotItem); + ItemStack oldSlotItem = slotItem; + slot->set(carried); + inv->setCarried(oldSlotItem); } } else if (slotItem.getId() == carried.getId()) diff --git a/source/world/item/ItemStack.cpp b/source/world/item/ItemStack.cpp index 0ced312cf..de3a56dd0 100644 --- a/source/world/item/ItemStack.cpp +++ b/source/world/item/ItemStack.cpp @@ -160,8 +160,8 @@ CompoundTag* ItemStack::getNetworkUserData() const CompoundTag::NamedTagMap& tags = m_userData->rawView(); for (CompoundTag::NamedTagMap::iterator it = tags.begin(); it != tags.end(); it++) { - const std::string& name = it->first; - const Tag* tag = it->second; + const std::string& name = it.key(); + const Tag* tag = it.value(); if (!tag) continue; if (name == TAG_REPAIR_COST) diff --git a/source/world/level/Level.cpp b/source/world/level/Level.cpp index e91a3c8f2..e68c42ece 100644 --- a/source/world/level/Level.cpp +++ b/source/world/level/Level.cpp @@ -322,10 +322,10 @@ Entity* Level::getEntity(Entity::ID id) const unsigned int Level::getEntityCount(const EntityCategories& category) const { EntityCategories::CategoriesMask mask = category.getCategoryMask(); - std::map::const_iterator it = m_entityCountsByCategory.find(mask); + HashMap::const_iterator it = m_entityCountsByCategory.find(mask); if (it == m_entityCountsByCategory.end()) return 0; - return it->second; + return it.value(); } const EntityVector* Level::getAllEntities() const diff --git a/source/world/level/Level.hpp b/source/world/level/Level.hpp index 993830cf6..998e28748 100644 --- a/source/world/level/Level.hpp +++ b/source/world/level/Level.hpp @@ -9,12 +9,12 @@ #pragma once #include -#include #ifndef _USE_MATH_DEFINES #define _USE_MATH_DEFINES #endif #include +#include "common/utility/HashMap.hpp" #include "client/renderer/LightUpdate.hpp" #include "world/tile/Tile.hpp" #include "world/entity/Entity.hpp" @@ -236,6 +236,6 @@ class Level : public LevelSource PathFinder* m_pPathFinder; MobSpawner* m_pMobSpawner; - std::map m_entityCountsByCategory; + HashMap m_entityCountsByCategory; };