From f5d7038d69645eb7cc25ade6508a4144b17f2357 Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Sat, 18 Apr 2020 17:58:00 -0400 Subject: [PATCH 1/7] Refactor to InputFrame 2.0 Significantly refactored the Input class, added Input::Manager to handle most duties of the Input system. Replaced the legacy KeyBindings system with the new InputBindings (namespace subject to change) system for handling key state and key chords - among other improvements, there is no longer a hardcoded dependency on Pi::input anymore, as well as storing the binding state in the binding itself, completely decoupling InputBindings from the input system that handles input events and significantly reducing the complexity of the binding system. Input::Manager now is totally responsible for generating binding events, removing the need for WorldView::HandleSDLEvent and its ilk - interested parties can simply bind to the onPressed event and they will only be notified when the binding in question is part of an active input frame and nothing else has grabbed the input event, vastly simplifying control flow. Among other effects, this almost totally eradicates the need for Pi::IsConsoleActive(). The lua Console widget is slightly broken as a result of this change, it doesn't handle activation/deactivation state changes properly. The solution to this seems to simply be a direct rewrite in PiGui, which I don't expect to cause too many issues. I've added a CMake option to use LLD to speed up incremental build link times; this seems to be somewhat platform dependent and is not implemented for windows at this time. Serialization of key bindings to the config file is totally completed, although support for a graphical method of binding multi-key chords is not anticipated for this pull request. --- CMakeLists.txt | 4 + data/pigui/views/game.lua | 2 +- src/GameConfig.cpp | 1 - src/Input.cpp | 710 +++++++++++++++++++-------- src/Input.h | 172 ++++--- src/InputBindings.cpp | 380 +++++++++++++++ src/InputBindings.h | 216 +++++++++ src/KeyBindings.cpp | 712 ---------------------------- src/KeyBindings.h | 229 --------- src/ModelViewer.cpp | 60 +-- src/ModelViewer.h | 26 +- src/Pi.cpp | 36 +- src/Pi.h | 7 +- src/Player.cpp | 1 - src/SectorView.cpp | 96 ++-- src/SectorView.h | 16 +- src/ShipCpanelMultiFuncDisplays.cpp | 8 +- src/SystemView.cpp | 22 +- src/SystemView.h | 12 +- src/UIView.h | 8 +- src/WorldView.cpp | 59 ++- src/WorldView.h | 19 +- src/core/GuiApplication.cpp | 6 +- src/core/GuiApplication.h | 6 +- src/lua/LuaConsole.cpp | 47 +- src/lua/LuaConsole.h | 4 + src/lua/LuaEngine.cpp | 1 - src/lua/LuaInput.cpp | 90 ++-- src/lua/LuaPiGui.cpp | 5 + src/ship/PlayerShipController.cpp | 79 +-- src/ship/PlayerShipController.h | 43 +- src/ship/Propulsion.cpp | 23 +- src/ship/ShipViewController.cpp | 82 ++-- src/ship/ShipViewController.h | 14 +- 34 files changed, 1653 insertions(+), 1543 deletions(-) create mode 100644 src/InputBindings.cpp create mode 100644 src/InputBindings.h delete mode 100644 src/KeyBindings.cpp delete mode 100644 src/KeyBindings.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ab20bf1e..cba9a4cff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,6 +49,7 @@ if (APPLE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-gnu") endif(APPLE) +option(USE_LLD_LINKER "Use the LLVM lld linker instead of gcc's linker" OFF) if (CMAKE_COMPILER_IS_GNUCXX) if (NOT IS_TRAVIS) add_compile_options( @@ -63,6 +64,9 @@ if (CMAKE_COMPILER_IS_GNUCXX) -Wno-implicit-fallthrough ) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive") + if (USE_LLD_LINKER) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fuse-ld=lld") + endif(USE_LLD_LINKER) set(CMAKE_CXX_FLAGS_DEBUG "-g -Og") endif (CMAKE_COMPILER_IS_GNUCXX) diff --git a/data/pigui/views/game.lua b/data/pigui/views/game.lua index 737f7f958..0e10fe1d7 100644 --- a/data/pigui/views/game.lua +++ b/data/pigui/views/game.lua @@ -298,7 +298,7 @@ ui.registerHandler('game', function(delta_t) if not ui.optionsWindow.isOpen then Game.SetTimeAcceleration("paused") ui.optionsWindow:open() - Input.DisableBindings() + Input.EnableBindings(false) else ui.optionsWindow:close() if not ui.optionsWindow.isOpen then diff --git a/src/GameConfig.cpp b/src/GameConfig.cpp index 8a30af55f..eb0e0777f 100644 --- a/src/GameConfig.cpp +++ b/src/GameConfig.cpp @@ -3,7 +3,6 @@ #include "GameConfig.h" #include "FileSystem.h" -#include "KeyBindings.h" GameConfig::GameConfig(const map_string &override_) { diff --git a/src/Input.cpp b/src/Input.cpp index 1bcf8a918..265d27471 100644 --- a/src/Input.cpp +++ b/src/Input.cpp @@ -3,43 +3,383 @@ #include "Input.h" #include "GameConfig.h" +#include "InputBindings.h" #include "Pi.h" #include "SDL.h" +#include "SDL_events.h" +#include "SDL_joystick.h" #include "ui/Context.h" #include +#include +#include -Input::Input(IniConfig *config) : +using namespace Input; + +namespace Input { + std::vector> *m_registrations; + + std::vector> &GetBindingRegistration() + { + return *m_registrations; + } + + bool AddBindingRegistrar(sigc::slot &&fn) + { + static std::vector> registrations; + m_registrations = ®istrations; + + registrations.push_back(fn); + return true; + } +} // namespace Input + +/* + + STATIC JOYSTICK HANDLING + +*/ + +namespace Input { + std::map m_joysticks; + + InputBindings::Action nullAction; + InputBindings::Axis nullAxis; +} // namespace Input + +std::string Input::JoystickName(int joystick) +{ + return std::string(SDL_JoystickName(m_joysticks[joystick].joystick)); +} + +std::string Input::JoystickGUIDString(int joystick) +{ + const int guidBufferLen = 33; // as documented by SDL + char guidBuffer[guidBufferLen]; + + SDL_JoystickGetGUIDString(m_joysticks[joystick].guid, guidBuffer, guidBufferLen); + return std::string(guidBuffer); +} + +// conveniance version of JoystickFromGUID below that handles the string mangling. +int Input::JoystickFromGUIDString(const std::string &guid) +{ + return JoystickFromGUIDString(guid.c_str()); +} + +// conveniance version of JoystickFromGUID below that handles the string mangling. +int Input::JoystickFromGUIDString(const char *guid) +{ + return JoystickFromGUID(SDL_JoystickGetGUIDFromString(guid)); +} + +// return the internal ID of the stated joystick guid. +// returns -1 if we couldn't find the joystick in question. +int Input::JoystickFromGUID(SDL_JoystickGUID guid) +{ + const int guidLength = 16; // as defined + for (auto pair : m_joysticks) { + JoystickInfo &state = pair.second; + if (0 == memcmp(state.guid.data, guid.data, guidLength)) { + return static_cast(pair.first); + } + } + return -1; +} + +SDL_JoystickGUID Input::JoystickGUID(int joystick) +{ + return m_joysticks[joystick].guid; +} + +void Input::InitJoysticks() +{ + SDL_Init(SDL_INIT_JOYSTICK); + + int joy_count = SDL_NumJoysticks(); + Output("Initializing joystick subsystem.\n"); + for (int n = 0; n < joy_count; n++) { + JoystickInfo state; + + state.joystick = SDL_JoystickOpen(n); + if (!state.joystick) { + Warning("SDL_JoystickOpen(%i): %s\n", n, SDL_GetError()); + continue; + } + + state.guid = SDL_JoystickGetGUID(state.joystick); + state.axes.resize(SDL_JoystickNumAxes(state.joystick)); + state.buttons.resize(SDL_JoystickNumButtons(state.joystick)); + state.hats.resize(SDL_JoystickNumHats(state.joystick)); + + std::array joystickGUIDName; + SDL_JoystickGetGUIDString(state.guid, joystickGUIDName.data(), joystickGUIDName.size()); + Output("Found joystick '%s' (GUID: %s)\n", SDL_JoystickName(state.joystick), joystickGUIDName.data()); + Output(" - %ld axes, %ld buttons, %ld hats\n", state.axes.size(), state.buttons.size(), state.hats.size()); + + SDL_JoystickID joyID = SDL_JoystickInstanceID(state.joystick); + m_joysticks[joyID] = state; + } +} + +std::map &Input::GetJoysticks() +{ + return m_joysticks; +} + +/* + + INPUT MANAGER INITIALIZATION + +*/ + +Manager::Manager(IniConfig *config) : m_config(config), keyModState(0), mouseButton(), mouseMotion(), m_capturingMouse(false), joystickEnabled(true), - mouseYInvert(false) + mouseYInvert(false), + m_enableBindings(true) { joystickEnabled = (m_config->Int("EnableJoystick")) ? true : false; mouseYInvert = (m_config->Int("InvertMouseY")) ? true : false; - InitJoysticks(); + Input::InitJoysticks(); } -void Input::InitGame() +void Manager::InitGame() { //reset input states keyState.clear(); keyModState = 0; mouseButton.fill(0); mouseMotion.fill(0); - for (std::map::iterator stick = joysticks.begin(); stick != joysticks.end(); ++stick) { - JoystickState &state = stick->second; + + // Force a rebuild of key chords and modifier state + m_frameListChanged = true; + + for (auto &pair : Input::GetJoysticks()) { + JoystickInfo &state = pair.second; std::fill(state.buttons.begin(), state.buttons.end(), false); std::fill(state.hats.begin(), state.hats.end(), 0); - std::fill(state.axes.begin(), state.axes.end(), 0.f); + std::fill(state.axes.begin(), state.axes.end(), JoystickInfo::Axis{ 0.0f, 0.0f }); } } -void Input::NewFrame() +/* + + BINDING AND INPUT FRAME HANDLING + +*/ + +InputBindings::Action *InputFrame::AddAction(std::string id) +{ + auto *action = manager->GetActionBinding(id); + if (!action) + throw std::runtime_error("Adding unknown action binding to InputFrame, id: " + id); + + actions.push_back(action); + return action; +} + +InputBindings::Axis *InputFrame::AddAxis(std::string id) +{ + auto *axis = manager->GetAxisBinding(id); + if (!axis) + throw std::runtime_error("Adding unknown axis binding to an InputFrame, id: " + id); + + axes.push_back(axis); + return axis; +} + +bool Manager::PushInputFrame(InputFrame *frame) +{ + if (HasInputFrame(frame)) { + return false; + } + + m_inputFrames.push_back(frame); + frame->active = true; + frame->onFrameAdded.emit(frame); + m_frameListChanged = true; + + return true; +} + +InputFrame *Manager::PopInputFrame() +{ + if (m_inputFrames.size() > 0) { + auto frame = m_inputFrames.back(); + m_inputFrames.pop_back(); + frame->active = false; + frame->onFrameRemoved.emit(frame); + m_frameListChanged = true; + + return frame; + } + + return nullptr; +} + +void Manager::RemoveInputFrame(InputFrame *frame) +{ + auto it = std::find(m_inputFrames.begin(), m_inputFrames.end(), frame); + if (it != m_inputFrames.end()) { + m_inputFrames.erase(it); + + // When an input frame is removed, its actions and axes are no longer active. + for (auto *action : frame->actions) { + if (action->m_active) { + action->m_active = false; + action->onReleased.emit(); + } + } + + for (auto *axis : frame->axes) { + if (axis->m_value != 0.0) { + axis->m_value = 0.0; + axis->onAxisValue.emit(0.0); + } + } + + frame->active = false; + frame->onFrameRemoved.emit(frame); + m_frameListChanged = true; + } +} + +InputBindings::Action *Manager::AddActionBinding(std::string id, BindingGroup *group, InputBindings::Action &&binding) +{ + // throw an error if we attempt to bind an action onto an already-bound axis in the same group. + if (group->bindings.count(id) && group->bindings[id] != BindingGroup::ENTRY_ACTION) + Error("Attempt to bind already-registered axis %s as an action.\n", id.c_str()); + + group->bindings[id] = BindingGroup::ENTRY_ACTION; + + // Load from the config + std::string config_str = m_config->String(id.c_str()); + if (!config_str.empty()) { + nonstd::string_view str(config_str); + str >> binding; + } + + return &(actionBindings[id] = binding); +} + +InputBindings::Axis *Manager::AddAxisBinding(std::string id, BindingGroup *group, InputBindings::Axis &&binding) +{ + // throw an error if we attempt to bind an axis onto an already-bound action in the same group. + if (group->bindings.count(id) && group->bindings[id] != BindingGroup::ENTRY_AXIS) + Error("Attempt to bind already-registered action %s as an axis.\n", id.c_str()); + + group->bindings[id] = BindingGroup::ENTRY_AXIS; + + // Load from the config + std::string config_str = m_config->String(id.c_str()); + if (!config_str.empty()) { + nonstd::string_view str(config_str); + str >> binding; + } + + return &(axisBindings[id] = binding); +} + +InputBindings::Action *Manager::GetActionBinding(std::string id) +{ + return actionBindings.count(id) ? &actionBindings[id] : &Input::nullAction; +} + +InputBindings::Axis *Manager::GetAxisBinding(std::string id) +{ + return axisBindings.count(id) ? &axisBindings[id] : &Input::nullAxis; +} + +/* + + STATE MANAGEMENT + +*/ + +bool Manager::GetBindingState(InputBindings::KeyBinding &key) +{ + using Type = InputBindings::KeyBinding::Type; + + switch (key.type) { + case Type::Disabled: + return false; + case Type::KeyboardKey: + return KeyState(key.keycode); + case Type::JoystickButton: + return JoystickButtonState(key.joystick.id, key.joystick.button); + case Type::JoystickHat: + return (JoystickHatState(key.joystick.id, key.joystick.hat) & key.joystick.button) == key.joystick.button; + case Type::MouseButton: + return MouseButtonState(key.mouse.button); + default: + return false; + } +} + +float Manager::GetAxisState(InputBindings::JoyAxis &axis) +{ + if (axis.direction == 0) + return 0.0; // disabled + + return JoystickAxisState(axis.joystickId, axis.axis) * float(axis.direction); +} + +bool Manager::GetModifierState(InputBindings::KeyChord *chord) +{ + bool mod1 = chord->modifier1.Enabled() ? m_modifiers[chord->modifier1] : true; + bool mod2 = chord->modifier2.Enabled() ? m_modifiers[chord->modifier2] : true; + return mod1 && mod2; +} + +int Manager::JoystickButtonState(int joystick, int button) +{ + if (!joystickEnabled) return 0; + if (joystick < 0 || joystick >= int(GetJoysticks().size())) + return 0; + + if (button < 0 || button >= int(GetJoysticks()[joystick].buttons.size())) + return 0; + + return GetJoysticks()[joystick].buttons[button]; +} + +int Manager::JoystickHatState(int joystick, int hat) +{ + if (!joystickEnabled) return 0; + if (joystick < 0 || joystick >= int(GetJoysticks().size())) + return 0; + + if (hat < 0 || hat >= int(GetJoysticks()[joystick].hats.size())) + return 0; + + return GetJoysticks()[joystick].hats[hat]; +} + +float Manager::JoystickAxisState(int joystick, int axis) +{ + if (!joystickEnabled) return 0; + if (joystick < 0 || joystick >= int(GetJoysticks().size())) + return 0; + + if (axis < 0 || axis >= int(GetJoysticks()[joystick].axes.size())) + return 0; + + return GetJoysticks()[joystick].axes[axis].value; +} + +/* + + FRAME AND EVENT HANDLING + +*/ + +void Manager::NewFrame() { mouseMotion.fill(0); mouseWheel = 0; @@ -56,94 +396,89 @@ void Input::NewFrame() break; } } -} -InputResponse Input::InputFrame::ProcessSDLEvent(SDL_Event &event) -{ - bool matched = false; - - for (KeyBindings::ActionBinding *action : actions) { - auto resp = action->CheckSDLEventAndDispatch(&event); - if (resp == RESPONSE_MATCHED) return resp; - matched = matched || resp > RESPONSE_NOMATCH; - } - - for (KeyBindings::AxisBinding *axis : axes) { - auto resp = axis->CheckSDLEventAndDispatch(&event); - if (resp == RESPONSE_MATCHED) return resp; - matched = matched || resp > RESPONSE_NOMATCH; - } - - return matched ? RESPONSE_PASSTHROUGH : RESPONSE_NOMATCH; -} - -bool Input::PushInputFrame(Input::InputFrame *frame) -{ - if (HasInputFrame(frame)) { - return false; - } - - inputFrames.push_back(frame); - frame->active = true; - frame->onFrameAdded(); - return true; -} - -Input::InputFrame *Input::PopInputFrame() -{ - if (inputFrames.size() > 0) { - auto frame = inputFrames.back(); - inputFrames.pop_back(); - frame->active = false; - frame->onFrameRemoved(); - return frame; - } - - return nullptr; -} - -void Input::RemoveInputFrame(Input::InputFrame *frame) -{ - auto it = std::find(inputFrames.begin(), inputFrames.end(), frame); - if (it != inputFrames.end()) { - inputFrames.erase(it); - frame->active = false; - frame->onFrameRemoved(); + if (m_frameListChanged) { + RebuildInputFrames(); } } -KeyBindings::ActionBinding *Input::AddActionBinding(std::string id, BindingGroup *group, KeyBindings::ActionBinding binding) +template +struct reverse_iter { + using iterator = typename T::reverse_iterator; + using const_iterator = typename T::const_reverse_iterator; + reverse_iter(T &obj) : + m_obj(obj) {} + + iterator begin() { return m_obj.rbegin(); } + iterator end() { return m_obj.rend(); } + const_iterator begin() const { return m_obj.crbegin(); } + const_iterator end() const { return m_obj.crend(); } + +private: + T &m_obj; +}; + +void Manager::RebuildInputFrames() { - // throw an error if we attempt to bind an action onto an already-bound axis in the same group. - if (group->bindings.count(id) && group->bindings[id] != BindingGroup::ENTRY_ACTION) - Error("Attempt to bind already-registered axis %s as an action.\n", id.c_str()); + // Reset the list of active chords. + m_chords.clear(); + m_activeActions.clear(); + m_activeAxes.clear(); - group->bindings[id] = BindingGroup::ENTRY_ACTION; + for (const auto *frame : reverse_iter(m_inputFrames)) { - // Load from the config - std::string config_str = m_config->String(id.c_str()); - if (config_str.length() > 0) binding.SetFromString(config_str); + // Push all enabled key chords onto the key chord stack. + for (auto *action : frame->actions) { + if (!action->Enabled()) + continue; - return &(actionBindings[id] = binding); + m_activeActions.push_back(action); + assert(m_activeActions.back() == action); + if (action->binding.Enabled()) + m_chords.push_back(&action->binding); + + if (action->binding2.Enabled()) + m_chords.push_back(&action->binding2); + } + + for (auto *axis : frame->axes) { + if (!axis->Enabled()) + continue; + + m_activeAxes.push_back(axis); + if (axis->positive.Enabled()) + m_chords.push_back(&axis->positive); + + if (axis->negative.Enabled()) + m_chords.push_back(&axis->negative); + } + + // If we have a modal frame, it prevents input from passing through it to frames below + if (frame->modal) { // modal frame blocks all inputs below it + break; + } + } + + // Group all chords with the same number of modifiers together, in descending order. + std::sort(m_chords.begin(), m_chords.end(), [](const InputBindings::KeyChord *a, const InputBindings::KeyChord *b) { return *a < *b; }); + + // Reinitialize the modifier list, preserving key state. + m_modifiers.clear(); + for (auto *chord : m_chords) { + m_modifiers.emplace(chord->modifier1, GetBindingState(chord->modifier1)); + m_modifiers.emplace(chord->modifier2, GetBindingState(chord->modifier2)); + } } -KeyBindings::AxisBinding *Input::AddAxisBinding(std::string id, BindingGroup *group, KeyBindings::AxisBinding binding) +static int8_t keys_in_chord(InputBindings::KeyChord *chord) { - // throw an error if we attempt to bind an axis onto an already-bound action in the same group. - if (group->bindings.count(id) && group->bindings[id] != BindingGroup::ENTRY_AXIS) - Error("Attempt to bind already-registered action %s as an axis.\n", id.c_str()); - - group->bindings[id] = BindingGroup::ENTRY_AXIS; - - // Load from the config - std::string config_str = m_config->String(id.c_str()); - if (config_str.length() > 0) binding.SetFromString(config_str); - - return &(axisBindings[id] = binding); + return chord->activator.Enabled() + chord->modifier1.Enabled() + chord->modifier2.Enabled(); } -void Input::HandleSDLEvent(SDL_Event &event) +void Manager::HandleSDLEvent(SDL_Event &event) { + using namespace InputBindings; + switch (event.type) { case SDL_KEYDOWN: // Set key state to "just pressed" @@ -180,145 +515,136 @@ void Input::HandleSDLEvent(SDL_Event &event) mouseMotion[1] += event.motion.yrel; break; case SDL_JOYAXISMOTION: - if (!joysticks[event.jaxis.which].joystick) + if (!GetJoysticks()[event.jaxis.which].joystick) break; if (event.jaxis.value == -32768) - joysticks[event.jaxis.which].axes[event.jaxis.axis] = 1.f; + GetJoysticks()[event.jaxis.which].axes[event.jaxis.axis].value = 1.f; else - joysticks[event.jaxis.which].axes[event.jaxis.axis] = -event.jaxis.value / 32767.f; + GetJoysticks()[event.jaxis.which].axes[event.jaxis.axis].value = -event.jaxis.value / 32767.f; break; case SDL_JOYBUTTONUP: case SDL_JOYBUTTONDOWN: - if (!joysticks[event.jaxis.which].joystick) + if (!GetJoysticks()[event.jaxis.which].joystick) break; - joysticks[event.jbutton.which].buttons[event.jbutton.button] = event.jbutton.state != 0; + GetJoysticks()[event.jbutton.which].buttons[event.jbutton.button] = event.jbutton.state != 0; break; case SDL_JOYHATMOTION: - if (!joysticks[event.jaxis.which].joystick) + if (!GetJoysticks()[event.jaxis.which].joystick) break; - joysticks[event.jhat.which].hats[event.jhat.hat] = event.jhat.value; + GetJoysticks()[event.jhat.which].hats[event.jhat.hat] = event.jhat.value; break; + default: + // Don't process non-input events any further. + return; } - for (auto it = inputFrames.rbegin(); it != inputFrames.rend(); it++) { - auto *inputFrame = *it; - auto resp = inputFrame->ProcessSDLEvent(event); - if (resp == RESPONSE_MATCHED) break; + // if bindings are disabled, don't process the event any further + if (!m_enableBindings) + return; + + // Update the modifier status from this event + for (auto &pair : m_modifiers) { + auto r = pair.first.Matches(event); + if (r != Response::Ignored) { + pair.second = r == Response::Pressed ? true : false; + } } -} -void Input::InitJoysticks() -{ - SDL_Init(SDL_INIT_JOYSTICK); - - int joy_count = SDL_NumJoysticks(); - Output("Initializing joystick subsystem.\n"); - for (int n = 0; n < joy_count; n++) { - JoystickState state; - - state.joystick = SDL_JoystickOpen(n); - if (!state.joystick) { - Warning("SDL_JoystickOpen(%i): %s\n", n, SDL_GetError()); + // If the event matches one of the key chords we care about, update that chord + int num_keys_in_chord = 0; + for (auto *chord : m_chords) { + Response activator = chord->activator.Matches(event); + if (activator == Response::Ignored) continue; - } - state.guid = SDL_JoystickGetGUID(state.joystick); - state.axes.resize(SDL_JoystickNumAxes(state.joystick)); - state.buttons.resize(SDL_JoystickNumButtons(state.joystick)); - state.hats.resize(SDL_JoystickNumHats(state.joystick)); + if (chord->IsActive()) { + // Another press event came in for a key that's currently pressed right now. + // This should be sufficiently rare that it won't be happening. + if (activator == Response::Pressed) + break; + else { // clear the active state, continue processing the release event + chord->m_active = false; + // in case of a press-release sequence in the same frame, make sure to properly send updates. + chord->m_queuedEvents |= 2; + } + } else { + // Key-release event for a non-active chord. Don't handle it, but pass it on so + // another key chord (with fewer / different modifiers) can handle it. + if (activator == Response::Released) + continue; - std::array joystickGUIDName; - SDL_JoystickGetGUIDString(state.guid, joystickGUIDName.data(), joystickGUIDName.size()); - Output("Found joystick '%s' (GUID: %s)\n", SDL_JoystickName(state.joystick), joystickGUIDName.data()); - Output(" - %ld axes, %ld buttons, %ld hats\n", state.axes.size(), state.buttons.size(), state.hats.size()); + // Break here to prevent CTRL+ALT+X from activating +X / +X / +X + // when there's a CTRL+ALT+X binding + if (keys_in_chord(chord) < num_keys_in_chord) + break; - SDL_JoystickID joyID = SDL_JoystickInstanceID(state.joystick); - joysticks[joyID] = state; - } -} - -std::string Input::JoystickName(int joystick) -{ - return std::string(SDL_JoystickName(joysticks[joystick].joystick)); -} - -std::string Input::JoystickGUIDString(int joystick) -{ - const int guidBufferLen = 33; // as documented by SDL - char guidBuffer[guidBufferLen]; - - SDL_JoystickGetGUIDString(joysticks[joystick].guid, guidBuffer, guidBufferLen); - return std::string(guidBuffer); -} - -// conveniance version of JoystickFromGUID below that handles the string mangling. -int Input::JoystickFromGUIDString(const std::string &guid) -{ - return JoystickFromGUIDString(guid.c_str()); -} - -// conveniance version of JoystickFromGUID below that handles the string mangling. -int Input::JoystickFromGUIDString(const char *guid) -{ - return JoystickFromGUID(SDL_JoystickGetGUIDFromString(guid)); -} - -// return the internal ID of the stated joystick guid. -// returns -1 if we couldn't find the joystick in question. -int Input::JoystickFromGUID(SDL_JoystickGUID guid) -{ - const int guidLength = 16; // as defined - for (std::map::iterator stick = joysticks.begin(); stick != joysticks.end(); ++stick) { - JoystickState &state = stick->second; - if (0 == memcmp(state.guid.data, guid.data, guidLength)) { - return static_cast(stick->first); + bool mod1 = chord->modifier1.Enabled() ? m_modifiers[chord->modifier1] : true; + bool mod2 = chord->modifier2.Enabled() ? m_modifiers[chord->modifier2] : true; + if (mod1 && mod2) { + // Modifiers are pressed, we can activate the chord. + chord->m_active = true; + // in the case of a press-release in the same frame, make sure to properly send updates + chord->m_queuedEvents |= 1; + // all copies of this chord should be notified, but don't propagate to chords with fewer modifiers + num_keys_in_chord = keys_in_chord(chord); + } } } - return -1; } -SDL_JoystickGUID Input::JoystickGUID(int joystick) +void Manager::DispatchEvents() { - return joysticks[joystick].guid; + // Chords which have had their modifier keys released this frame get updated all at once + for (auto *chord : m_chords) { + if (chord->IsActive() && !GetModifierState(chord)) { + chord->m_active = false; + chord->m_queuedEvents |= 2; + } + } + + for (auto *action : m_activeActions) { + // if we have queued events for this binding, make sure to + uint8_t queued = action->binding.m_queuedEvents | action->binding2.m_queuedEvents; + if (queued) { + bool wasActive = action->m_active; + action->m_active = action->binding.IsActive() || action->binding2.IsActive(); + + // if at least one of the bindings was pressed this frame and the action was not + // previously active, call the pressed event + if (queued & 1 && !wasActive) + action->onPressed.emit(); + + // if at least one of the bindings was released this frame but are not pressed currently, + // call the released event + if (queued & 2 && !action->m_active) + action->onReleased.emit(); + + // clear queued events + action->binding.m_queuedEvents = action->binding2.m_queuedEvents = 0; + } + } + + for (auto *axis : m_activeAxes) { + float value = GetAxisState(axis->axis); + + value += axis->positive.IsActive(); + value -= axis->negative.IsActive(); + + value = Clamp(value, -1.0f, 1.0f); + if (value != 0.0 || axis->m_value != 0.0) { + axis->m_value = value; + axis->onAxisValue.emit(value); + } + } } -int Input::JoystickButtonState(int joystick, int button) -{ - if (!joystickEnabled) return 0; - if (joystick < 0 || joystick >= int(joysticks.size())) - return 0; +/* - if (button < 0 || button >= int(joysticks[joystick].buttons.size())) - return 0; + INPUT DEVICE MANAGEMENT ROUTINES - return joysticks[joystick].buttons[button]; -} +*/ -int Input::JoystickHatState(int joystick, int hat) -{ - if (!joystickEnabled) return 0; - if (joystick < 0 || joystick >= int(joysticks.size())) - return 0; - - if (hat < 0 || hat >= int(joysticks[joystick].hats.size())) - return 0; - - return joysticks[joystick].hats[hat]; -} - -float Input::JoystickAxisState(int joystick, int axis) -{ - if (!joystickEnabled) return 0; - if (joystick < 0 || joystick >= int(joysticks.size())) - return 0; - - if (axis < 0 || axis >= int(joysticks[joystick].axes.size())) - return 0; - - return joysticks[joystick].axes[axis]; -} - -void Input::SetCapturingMouse(bool grabbed) +void Manager::SetCapturingMouse(bool grabbed) { // early-out to avoid changing (possibly) expensive WM state if (grabbed == m_capturingMouse) diff --git a/src/Input.h b/src/Input.h index 424ee8fb8..dfa512fc6 100644 --- a/src/Input.h +++ b/src/Input.h @@ -4,7 +4,7 @@ #ifndef INPUT_H #define INPUT_H -#include "KeyBindings.h" +#include "InputBindings.h" #include "utils.h" #include @@ -12,17 +12,23 @@ class IniConfig; -class Input { -public: - Input(IniConfig *config); - void InitGame(); - void HandleSDLEvent(SDL_Event &ev); - void NewFrame(); +// Macro to simplify registering input bindings in the codebase +// TODO: evaluate if registering key bindings via lua / json file works better +#define REGISTER_INPUT_BINDING(name) \ + namespace name##Input \ + { \ + void Register(Input::Manager *input); \ + bool name##Registered = Input::AddBindingRegistrar(&Register); \ + } \ + void name##Input::Register(Input::Manager *input) + +namespace Input { + class Manager; // The Page->Group->Binding system serves as a thin veneer for the UI to make // sane reasonings about how to structure the Options dialog. struct BindingGroup { - enum EntryType { + enum EntryType : uint8_t { ENTRY_ACTION, ENTRY_AXIS }; @@ -36,28 +42,93 @@ public: std::map groups; }; - BindingPage *GetBindingPage(std::string id) { return &bindingPages[id]; } - std::map GetBindingPages() { return bindingPages; } - struct InputFrame { - std::vector actions; - std::vector axes; + using Axis = InputBindings::Axis; + using Action = InputBindings::Action; - bool active; + InputFrame(Input::Manager *man, bool modal = false) : + manager(man), + modal(modal) + {} + + std::vector actions; + std::vector axes; + + // Must set this to a valid Input::Manager instance before using AddAction / AddAxis + Manager *manager = nullptr; + bool active = false; + bool modal = false; // Call this at startup to register all the bindings associated with the frame. virtual void RegisterBindings(){}; // Called when the frame is added to the stack. - virtual void onFrameAdded(){}; + sigc::signal onFrameAdded; // Called when the frame is removed from the stack. - virtual void onFrameRemoved(){}; + sigc::signal onFrameRemoved; - // Check the event against all the inputs in this frame. - InputResponse ProcessSDLEvent(SDL_Event &event); + Action *AddAction(std::string id); + Axis *AddAxis(std::string id); }; + struct JoystickInfo { + struct Axis { + float value; + float deadzone; + }; + + SDL_Joystick *joystick; + SDL_JoystickGUID guid; + + std::vector buttons; + std::vector hats; + + std::vector axes; + }; + + void InitJoysticks(); + std::map &GetJoysticks(); + + // User display name for the joystick from the API/OS. + std::string JoystickName(int joystick); + // fetch the GUID for the named joystick + SDL_JoystickGUID JoystickGUID(int joystick); + std::string JoystickGUIDString(int joystick); + + // reverse map a JoystickGUID to the actual internal ID. + int JoystickFromGUIDString(const std::string &guid); + int JoystickFromGUIDString(const char *guid); + int JoystickFromGUID(SDL_JoystickGUID guid); + + // We use SDL's joystick IDs because they're stable enough for the job. + inline int JoystickFromID(SDL_JoystickID id) { return id; } + + // An adapter to decouple input frame creation from input binding registration. + // The functions registered via AddBindingRegistrar should be thread-safe and + // should not depend on anything but the manager object being passed in. + // The registrars are guaranteed to be called after static initialization has finished. + std::vector> &GetBindingRegistration(); + bool AddBindingRegistrar(sigc::slot &&fn); +} // namespace Input + +class Input::Manager { +public: + Manager(IniConfig *config); + void InitGame(); + + // Call this at the start of a frame, before passing SDL events in + void NewFrame(); + + // Call once per SDL event, handles updating all internal state + void HandleSDLEvent(SDL_Event &ev); + + // Call immediately after processing events, dispatches events to Action / Axis bindings. + void DispatchEvents(); + + BindingPage *GetBindingPage(std::string id) { return &bindingPages[id]; } + std::map GetBindingPages() { return bindingPages; } + // Pushes an InputFrame onto the input stack. bool PushInputFrame(InputFrame *frame); @@ -65,32 +136,33 @@ public: InputFrame *PopInputFrame(); // Get a read-only list of input frames. - const std::vector &GetInputFrames() { return inputFrames; } + const std::vector &GetInputFrames() { return m_inputFrames; } // Check if a specific input frame is currently on the stack. bool HasInputFrame(InputFrame *frame) { - return std::count(inputFrames.begin(), inputFrames.end(), frame) > 0; + return std::count(m_inputFrames.begin(), m_inputFrames.end(), frame) > 0; } // Remove an arbitrary input frame from the input stack. void RemoveInputFrame(InputFrame *frame); + // Inform the input system that a binding or frame was changed this frame. + void MarkBindingsDirty() { m_frameListChanged = true; } + // Creates a new action binding, copying the provided binding. // The returned binding pointer points to the actual binding. - KeyBindings::ActionBinding *AddActionBinding(std::string id, BindingGroup *group, KeyBindings::ActionBinding binding); - KeyBindings::ActionBinding *GetActionBinding(std::string id) - { - return actionBindings.count(id) ? &actionBindings[id] : nullptr; - } + InputBindings::Action *AddActionBinding(std::string id, BindingGroup *group, InputBindings::Action &&binding); + InputBindings::Action *GetActionBinding(std::string id); // Creates a new axis binding, copying the provided binding. // The returned binding pointer points to the actual binding. - KeyBindings::AxisBinding *AddAxisBinding(std::string id, BindingGroup *group, KeyBindings::AxisBinding binding); - KeyBindings::AxisBinding *GetAxisBinding(std::string id) - { - return axisBindings.count(id) ? &axisBindings[id] : nullptr; - } + InputBindings::Axis *AddAxisBinding(std::string id, BindingGroup *group, InputBindings::Axis &&binding); + InputBindings::Axis *GetAxisBinding(std::string id); + + // Call EnableBindings() to temporarily disable handling input bindings while + // you're recording a new input binding or are in a modal window. + void EnableBindings(bool enabled) { m_enableBindings = enabled; } bool KeyState(SDL_Keycode k) { return IsKeyDown(k); } @@ -112,26 +184,6 @@ public: bool IsJoystickEnabled() { return joystickEnabled; } void SetJoystickEnabled(bool state) { joystickEnabled = state; } - struct JoystickState { - SDL_Joystick *joystick; - SDL_JoystickGUID guid; - std::vector buttons; - std::vector hats; - std::vector axes; - }; - std::map GetJoysticksState() { return joysticks; } - - // User display name for the joystick from the API/OS. - std::string JoystickName(int joystick); - // fetch the GUID for the named joystick - SDL_JoystickGUID JoystickGUID(int joystick); - std::string JoystickGUIDString(int joystick); - - // reverse map a JoystickGUID to the actual internal ID. - int JoystickFromGUIDString(const std::string &guid); - int JoystickFromGUIDString(const char *guid); - int JoystickFromGUID(SDL_JoystickGUID guid); - void SetMouseYInvert(bool state) { mouseYInvert = state; } bool IsMouseYInvert() { return mouseYInvert; } @@ -162,7 +214,10 @@ public: sigc::signal onMouseWheel; private: - void InitJoysticks(); + void RebuildInputFrames(); + bool GetModifierState(InputBindings::KeyChord *key); + bool GetBindingState(InputBindings::KeyBinding &key); + float GetAxisState(InputBindings::JoyAxis &axis); IniConfig *m_config; @@ -175,13 +230,20 @@ private: bool joystickEnabled; bool mouseYInvert; - std::map joysticks; std::map bindingPages; - std::map actionBindings; - std::map axisBindings; + std::map actionBindings; + std::map axisBindings; + bool m_enableBindings; - std::vector inputFrames; + std::vector m_inputFrames; + bool m_frameListChanged; + + std::vector m_activeActions; + std::vector m_activeAxes; + + std::map m_modifiers; + std::vector m_chords; }; #endif diff --git a/src/InputBindings.cpp b/src/InputBindings.cpp new file mode 100644 index 000000000..e6153ffd0 --- /dev/null +++ b/src/InputBindings.cpp @@ -0,0 +1,380 @@ +// Copyright © 2008-2020 Pioneer Developers. See AUTHORS.txt for details +// Licensed under the terms of the GPL v3. See licenses/GPL-3.txt + +#include "InputBindings.h" +#include "Input.h" +#include "SDL_events.h" +#include "SDL_joystick.h" +#include "utils.h" + +#include + +using namespace InputBindings; + +namespace Input { + int JoystickFromGUIDString(const std::string &guid); + std::string JoystickGUIDString(int joystickID); +} // namespace Input + +// Helper function to early-out in Matches() +Response MatchType(const KeyBinding &k, const SDL_Event &ev) +{ + using Type = KeyBinding::Type; + + bool p = false; + bool r = false; + + switch (k.type) { + case Type::Disabled: + return Response::Ignored; + case Type::KeyboardKey: + p = ev.type == SDL_KEYDOWN; + r = ev.type == SDL_KEYUP; + break; + case Type::JoystickButton: + p = ev.type == SDL_JOYBUTTONDOWN; + r = ev.type == SDL_JOYBUTTONUP; + break; + case Type::JoystickHat: + if (ev.type == SDL_JOYHATMOTION) { + p = (ev.jhat.value & k.joystick.button) == k.joystick.button; + r = !p; + } + break; + case Type::MouseButton: + p = ev.type == SDL_MOUSEBUTTONDOWN; + r = ev.type == SDL_MOUSEBUTTONUP; + break; + } + + return p ? Response::Pressed : r ? Response::Released : Response::Ignored; +} + +Response KeyBinding::Matches(const SDL_Event &ev) const +{ + Response r = MatchType(*this, ev); + if (r == Response::Ignored) + return r; + + if (type == Type::KeyboardKey) { + return (ev.key.keysym.sym == keycode) ? r : Response::Ignored; + } else if (type == Type::JoystickButton) { + bool cond = (joystick.id == ev.jbutton.which && joystick.button == ev.jbutton.button); + return cond ? r : Response::Ignored; + } else if (type == Type::JoystickHat) { + bool cond = (joystick.id == ev.jhat.which && joystick.hat == ev.jhat.hat); + return cond ? r : Response::Ignored; + } else if (type == Type::MouseButton) { + return (mouse.button == ev.button.button) ? r : Response::Ignored; + } + + return Response::Ignored; +} + +bool KeyBinding::operator==(const KeyBinding &rhs) const +{ + if (type != rhs.type) + return false; + + if (type == Type::KeyboardKey) + return keycode == rhs.keycode; + else if (type == Type::JoystickButton) + return joystick.id == rhs.joystick.id && joystick.button == rhs.joystick.button; + else if (type == Type::JoystickHat) + return joystick.id == rhs.joystick.id && joystick.hat == rhs.joystick.hat && joystick.button == rhs.joystick.button; + else if (type == Type::MouseButton) + return mouse.button == rhs.mouse.button; + else + return true; +} + +#define ret_if_different(val) \ + if (val != rhs.val) return val < rhs.val +bool KeyBinding::operator<(const KeyBinding &rhs) const +{ + ret_if_different(type); + + switch (type) { + case Type::KeyboardKey: + ret_if_different(keycode); + break; + case Type::JoystickButton: + ret_if_different(joystick.id); + ret_if_different(joystick.button); + break; + case Type::JoystickHat: + ret_if_different(joystick.id); + ret_if_different(joystick.hat); + ret_if_different(joystick.button); + break; + case Type::MouseButton: + ret_if_different(mouse.button); + break; + default: + break; + } + + return this < &rhs; +} +#undef ret_if_different + +Action &Action::operator=(const Action &rhs) +{ + binding = rhs.binding; + binding2 = rhs.binding2; + return *this; +} + +Axis &Axis::operator=(const Axis &rhs) +{ + axis = rhs.axis; + positive = rhs.positive; + negative = rhs.negative; + return *this; +} + +// ============================================================================ +// +// Config Loading +// +// ============================================================================ + +using smatch = std::match_results; +static std::regex disabled_matcher("^disabled", std::regex::icase); + +// Handle the fiddly bits of matching a regex and advancing the beginning of a string +bool consumeMatch(nonstd::string_view &str, smatch &match_results, std::regex ®) +{ + if (!std::regex_search(str.cbegin(), str.cend(), match_results, reg)) + return false; + + str.remove_prefix(std::distance(str.cbegin(), match_results[0].second)); + return true; +} + +// Key54 | JoyGUID/B3 JoyGUID/H4/2 | Mouse5 +// Less awful than iostreams, but still not elegant. That's C++ for you. +// TODO: save joystick id->GUID mapping separately in the config file and +// don't write them here to save space +nonstd::string_view &InputBindings::operator>>(nonstd::string_view &str, KeyBinding &out) +{ + static std::regex key_matcher("^Key(\\d+)"); + static std::regex joystick_matcher("^Joy([^/]{32})"); + static std::regex joystick_button("^/B(\\d+)"); + static std::regex joystick_hat("^/H(\\d+)/(\\d+)"); + static std::regex mouse_matcher("^Mouse(\\d+)"); + + const auto begin = str.cbegin(); + const auto end = str.cend(); + + // match_results[0].second should always point to the character after the + // parsed key binding unless the value present could not be parsed. + smatch match_results; + if (std::regex_search(begin, end, match_results, key_matcher)) { + out.type = KeyBinding::Type::KeyboardKey; + out.keycode = std::stoi(match_results[1]); + } else if (std::regex_search(begin, end, match_results, joystick_matcher)) { + out.joystick.id = Input::JoystickFromGUIDString(match_results[1]); + const auto start = match_results[0].second; + if (std::regex_search(start, end, match_results, joystick_button)) { + out.type = KeyBinding::Type::JoystickButton; + out.joystick.button = std::stoi(match_results[1]); + out.joystick.hat = 0; + } else if (std::regex_search(start, end, match_results, joystick_hat)) { + out.type = KeyBinding::Type::JoystickHat; + out.joystick.hat = std::stoi(match_results[1]); + out.joystick.button = std::stoi(match_results[2]); + } + } else if (std::regex_search(begin, end, match_results, mouse_matcher)) { + out.type = KeyBinding::Type::MouseButton; + out.mouse.button = std::stoi(match_results[1]); + } else { + out.type = KeyBinding::Type::Disabled; + // consume the disabled text if present. + std::regex_search(begin, end, match_results, disabled_matcher); + } + + // return a string view containing the rest of the string + if (!match_results.empty()) + str.remove_prefix(std::distance(begin, match_results[0].second)); + + return str; +} + +// Serialize a KeyBinding into the output stream. +// Writes nothing if the binding is disabled +std::ostream &InputBindings::operator<<(std::ostream &str, KeyBinding &in) +{ + switch (in.type) { + case KeyBinding::Type::KeyboardKey: + return str << "Key" << in.keycode; + case KeyBinding::Type::JoystickButton: + return str << "Joy" << Input::JoystickGUIDString(in.joystick.id) + << "/B" << int(in.joystick.button); + case KeyBinding::Type::JoystickHat: + return str << "Joy" << Input::JoystickGUIDString(in.joystick.id) + << "/H" << int(in.joystick.hat) << "/" << int(in.joystick.button); + case KeyBinding::Type::MouseButton: + return str << "Mouse" << int(in.mouse.button); + default: + return str; + } +} + +// Match [-]JoyGUID/A4 +nonstd::string_view &InputBindings::operator>>(nonstd::string_view &str, JoyAxis &out) +{ + static std::regex joy_matcher("^Joy([^/]{32})/A(\\d+)"); + auto begin = str.cbegin(); + + bool reverse = !str.empty() && str[0] == '-'; + if (reverse) + ++begin; + + smatch match_results; + if (std::regex_search(begin, str.cend(), match_results, joy_matcher)) { + out.joystickId = Input::JoystickFromGUIDString(match_results[1]); + out.axis = std::stoi(match_results[2]); + out.direction = reverse ? -1 : 1; + } else { + std::regex_search(str.cbegin(), str.cend(), match_results, std::regex("^disabled")); + out.direction = 0; + } + + if (!match_results.empty()) + str.remove_prefix(std::distance(str.cbegin(), match_results[0].second)); + + return str; +} + +// [-]JoyGUID/A4 +std::ostream &InputBindings::operator<<(std::ostream &str, JoyAxis &in) +{ + if (!in.Enabled()) + return str << "disabled"; + + return str << (in.direction < 0.0 ? "-Joy" : "Joy") + << Input::JoystickGUIDString(in.joystickId) + << "/A" << int(in.axis); +} + +// find a close paren, copy str into ret str, and return retstr +// (for one-line failure case returns) +nonstd::string_view &findCloseParen(nonstd::string_view &str, nonstd::string_view &retstr, smatch &match_results) +{ + if (std::regex_search(str.cbegin(), str.cend(), match_results, std::regex("\\)"))) + str.remove_prefix(std::distance(str.cbegin(), match_results[0].second)); + + retstr = str; + return retstr; +} + +// Parse KeyChord(Key53 + JoyGUID/B3 + Mouse1) | KeyChord(Mouse5) +nonstd::string_view &InputBindings::operator>>(nonstd::string_view &str, KeyChord &out) +{ + static std::regex key_chord("^KeyChord\\(\\s*"); + static std::regex plus_sign("^\\s*\\+\\s*"); + smatch match_results; + + // Early-out for disabled key chord + if (consumeMatch(str, match_results, disabled_matcher)) + return str; + + // make a copy of the string view so we can nondestructively consume matches. + nonstd::string_view iterstr = str; + + // ensure we read the KeyChord( opening + if (!consumeMatch(iterstr, match_results, key_chord)) + return str; + + // read the activator KeyBinding + iterstr >> out.activator; + + // if the activator is disabled, early-out here. + // if we don't have a following plus sign, discard everything to the next close-paren + if (!out.activator.Enabled() || !consumeMatch(iterstr, match_results, plus_sign)) + return findCloseParen(iterstr, str, match_results); + + // read the first modifier + iterstr >> out.modifier1; + + // ditto for the second modifier + if (!out.modifier1.Enabled() || !consumeMatch(iterstr, match_results, plus_sign)) + return findCloseParen(iterstr, str, match_results); + + iterstr >> out.modifier2; + + return findCloseParen(iterstr, str, match_results); +} + +// KeyChord(Key54 + JoyGUID/B4 + JoyGUID/H1/3) +std::ostream &InputBindings::operator<<(std::ostream &str, KeyChord &in) +{ + if (!in.Enabled()) + return str << "disabled"; + + str << "KeyChord(" << in.activator; + if (in.modifier1.Enabled()) { + str << " + " << in.modifier1; + if (in.modifier2.Enabled()) + str << " + " << in.modifier2; + } + str << ")"; + + return str; +} + +nonstd::string_view &InputBindings::operator>>(nonstd::string_view &str, Axis &out) +{ + static std::regex input_axis("^InputAxis\\(\\s*"); + static std::regex comma_sep("^\\s*,\\s*"); + smatch match_results; + + auto iterstr = str; + if (!consumeMatch(iterstr, match_results, input_axis)) + return str; + iterstr >> out.axis; + + if (!consumeMatch(iterstr, match_results, comma_sep)) + return findCloseParen(iterstr, str, match_results); + + iterstr >> out.negative; + + if (!consumeMatch(iterstr, match_results, comma_sep)) + return findCloseParen(iterstr, str, match_results); + iterstr >> out.positive; + + return findCloseParen(iterstr, str, match_results); +} + +// InputAxis(-JoyGUID/A3, KeyChord(Key32), KeyChord(Mouse5 + JoyGUID/H1/1)) +// InputAxis(disabled, disabled, disabled) +std::ostream &InputBindings::operator<<(std::ostream &str, Axis &in) +{ + return str << "InputAxis(" << in.axis << ", " << in.negative << ", " << in.positive << ")"; +} + +nonstd::string_view &InputBindings::operator>>(nonstd::string_view &str, Action &out) +{ + static std::regex input_action("^InputAction\\(\\s*"); + static std::regex comma_sep("^\\s*,\\s*"); + smatch match_results; + + auto iterstr = str; + if (!consumeMatch(iterstr, match_results, input_action)) + return str; + iterstr >> out.binding; + + if (!consumeMatch(iterstr, match_results, comma_sep)) + return findCloseParen(iterstr, str, match_results); + iterstr >> out.binding2; + + return findCloseParen(iterstr, str, match_results); +} + +// InputAction(KeyChord(Key53 + Mouse4), KeyChord(Mouse5 + JoyGUID/H1/1)) +// InputAction(disabled, disabled) +std::ostream &InputBindings::operator<<(std::ostream &str, Action &in) +{ + return str << "InputAction(" << in.binding << ", " << in.binding2 << ")"; +} diff --git a/src/InputBindings.h b/src/InputBindings.h new file mode 100644 index 000000000..729153092 --- /dev/null +++ b/src/InputBindings.h @@ -0,0 +1,216 @@ +// Copyright © 2008-2020 Pioneer Developers. See AUTHORS.txt for details +// Licensed under the terms of the GPL v3. See licenses/GPL-3.txt + +#pragma once + +#include "DeleteEmitter.h" +#include "nonstd/string_view.hpp" +#include +#include +#include +#include +#include + +namespace InputBindings { + enum class Response { + Ignored = 0, + Pressed, + Released + }; + + struct KeyBinding { + enum class Type : uint8_t { + Disabled, + KeyboardKey, + JoystickButton, + JoystickHat, + MouseButton + }; + + Type type = Type::Disabled; + union { + SDL_Keycode keycode = 0; + struct { + // 65536 possible IDs should be more than plenty, even with lots of hot-plug noise. + uint16_t id; + // if type = JoystickHat, this is the hat direction; otherwise it's the button index + uint8_t button; + uint8_t hat; + } joystick; + struct { + uint8_t button; + } mouse; + }; + + KeyBinding() = default; + + KeyBinding(SDL_Keycode k) : + type(Type::KeyboardKey), keycode(k) {} + + static KeyBinding JoystickButton(uint8_t joystickID, uint8_t button) + { + KeyBinding t; + t.type = Type::JoystickButton; + t.joystick = { joystickID, button, 0 }; + return t; + } + + static KeyBinding JoystickHat(uint8_t joystickID, uint8_t hat, uint8_t direction) + { + KeyBinding t; + t.type = Type::JoystickHat; + t.joystick = { joystickID, direction, hat }; + return t; + } + + static KeyBinding MouseButton(uint8_t mouseButton) + { + KeyBinding t; + t.type = Type::MouseButton; + t.mouse.button = mouseButton; + return t; + } + + static KeyBinding FromEvent(SDL_Event &event); + + bool Enabled() const { return type != Type::Disabled; } + Response Matches(const SDL_Event &ev) const; + + bool operator==(const KeyBinding &rhs) const; + bool operator<(const KeyBinding &rhs) const; + + // serialization + friend nonstd::string_view &operator>>(nonstd::string_view &, KeyBinding &); + friend std::ostream &operator<<(std::ostream &, KeyBinding &); + }; + + struct JoyAxis { + uint8_t joystickId; + uint8_t axis; + int8_t direction; // if 0, the axis is disabled + + bool Enabled() const { return direction != 0; } + + bool operator==(const JoyAxis &rhs) const + { + if (!direction && !rhs.direction) + return true; + + return joystickId == rhs.joystickId && axis == rhs.axis && direction == rhs.direction; + } + + // serialization + friend nonstd::string_view &operator>>(nonstd::string_view &, JoyAxis &); + friend std::ostream &operator<<(std::ostream &, JoyAxis &); + }; + + struct KeyChord { + KeyBinding activator; + KeyBinding modifier1; + KeyBinding modifier2; + + KeyChord() = default; + KeyChord(KeyBinding a, KeyBinding m1 = {}, KeyBinding m2 = {}) : + activator(a), + modifier1(m1), + modifier2(m2) + {} + + bool IsActive() const { return Enabled() && m_active; } + bool Enabled() const { return activator.Enabled(); } + + bool operator==(const KeyChord &rhs) const + { + return activator == rhs.activator && modifier1 == rhs.modifier1 && modifier2 == rhs.modifier2; + } + bool operator!=(const KeyChord &rhs) const { return !(*this == rhs); } + + // Groups chords by number of modifiers in descending order + bool operator<(const KeyChord &rhs) const + { + return (modifier2.Enabled() && !rhs.modifier2.Enabled()) || (modifier1.Enabled() && !rhs.modifier2.Enabled()); + } + + bool m_active = false; + uint8_t m_queuedEvents = 0; + + // serialization + friend nonstd::string_view &operator>>(nonstd::string_view &, KeyChord &); + friend std::ostream &operator<<(std::ostream &, KeyChord &); + }; + + struct Action : public DeleteEmitter { + KeyChord binding; + KeyChord binding2; + + Action() = default; + Action(KeyChord b1, KeyChord b2 = {}) : + binding(b1), + binding2(b2) + {} + + // NOTE: sigc::signals cannot be copied, this function is for convenience to copy bindings only + Action &operator=(const Action &rhs); + + bool IsActive() { return m_active; } + bool Enabled() { return binding.Enabled() || binding2.Enabled(); } + + bool m_active = false; + + sigc::signal onPressed; + sigc::signal onReleased; + + // serialization + friend nonstd::string_view &operator>>(nonstd::string_view &, Action &); + friend std::ostream &operator<<(std::ostream &, Action &); + }; + + struct Axis : public DeleteEmitter { + JoyAxis axis; + KeyChord positive; + KeyChord negative; + + Axis() = default; + Axis(JoyAxis a, KeyChord p = {}, KeyChord n = {}) : + axis(a), + positive(p), + negative(n) + {} + + Axis(KeyChord p, KeyChord n = {}) : + axis{}, + positive(p), + negative(n) + {} + + // NOTE: sigc::signals cannot be copied, this function is for convenience to copy bindings only + Axis &operator=(const Axis &rhs); + + bool IsActive() { return m_value != 0.0 || positive.IsActive() || negative.IsActive(); } + float GetValue() { return m_value; } + bool Enabled() { return axis.Enabled() || positive.Enabled() || negative.Enabled(); } + + float m_value; + + sigc::signal onAxisValue; + + // serialization + friend nonstd::string_view &operator>>(nonstd::string_view &, Axis &); + friend std::ostream &operator<<(std::ostream &, Axis &); + }; + + nonstd::string_view &operator>>(nonstd::string_view &, KeyBinding &); + std::ostream &operator<<(std::ostream &, KeyBinding &); + + nonstd::string_view &operator>>(nonstd::string_view &, JoyAxis &); + std::ostream &operator<<(std::ostream &, JoyAxis &); + + nonstd::string_view &operator>>(nonstd::string_view &, KeyChord &); + std::ostream &operator<<(std::ostream &, KeyChord &); + + nonstd::string_view &operator>>(nonstd::string_view &, Action &); + std::ostream &operator<<(std::ostream &, Action &); + + nonstd::string_view &operator>>(nonstd::string_view &, Axis &); + std::ostream &operator<<(std::ostream &, Axis &); +}; // namespace InputBindings diff --git a/src/KeyBindings.cpp b/src/KeyBindings.cpp deleted file mode 100644 index 1e284cae2..000000000 --- a/src/KeyBindings.cpp +++ /dev/null @@ -1,712 +0,0 @@ -// Copyright © 2008-2020 Pioneer Developers. See AUTHORS.txt for details -// Licensed under the terms of the GPL v3. See licenses/GPL-3.txt - -#include "KeyBindings.h" -#include "GameConfig.h" -#include "Input.h" -#include "Lang.h" -#include "Pi.h" -#include "StringF.h" - -#include -#include - -static bool m_disableBindings = 0; - -namespace KeyBindings { - -#define KEY_BINDING(name, a, b, c, d) ActionBinding name; -#define AXIS_BINDING(name, a, b, c) JoyAxisBinding name; -#include "KeyBindings.inc.h" - -// create the BindingPrototype sets for use by the UI -#define BINDING_PAGE(name) const BindingPrototype BINDING_PROTOS_##name[] = { -#define BINDING_PAGE_END() \ - { \ - 0, 0, 0, 0 \ - } \ - } \ - ; -#define BINDING_GROUP(ui_name) \ - { ui_name, 0, 0, 0 }, -#define KEY_BINDING(name, config_name, ui_name, def1, def2) \ - { ui_name, config_name, &KeyBindings::name, 0 }, -#define AXIS_BINDING(name, config_name, ui_name, default_value) \ - { ui_name, config_name, 0, &KeyBindings::name }, -#include "KeyBindings.inc.h" - - // static binding object lists for use by the dispatch function - static ActionBinding *const s_KeyBindings[] = { -#define KEY_BINDING(name, a, b, c, d) &KeyBindings::name, -#include "KeyBindings.inc.h" - 0 - }; - - bool KeyBinding::IsActive() const - { - if (type == BINDING_DISABLED) { - return false; - } else if (type == KEYBOARD_KEY) { - if (!Pi::input->KeyState(u.keyboard.key)) - return false; - if (u.keyboard.mod == KMOD_NONE) - return true; - else { - int mod = Pi::input->KeyModState(); - if (mod & KMOD_CTRL) { - mod |= KMOD_CTRL; - } - if (mod & KMOD_SHIFT) { - mod |= KMOD_SHIFT; - } - if (mod & KMOD_ALT) { - mod |= KMOD_ALT; - } - if (mod & KMOD_GUI) { - mod |= KMOD_GUI; - } - return ((mod & u.keyboard.mod) == u.keyboard.mod); - } - } else if (type == JOYSTICK_BUTTON) { - return Pi::input->JoystickButtonState(u.joystickButton.joystick, u.joystickButton.button) != 0; - } else if (type == JOYSTICK_HAT) { - // SDL_HAT generates diagonal directions by ORing two cardinal directions. - int hatState = Pi::input->JoystickHatState(u.joystickHat.joystick, u.joystickHat.hat); - return (hatState & u.joystickHat.direction) == u.joystickHat.direction; - } else - abort(); - - return false; - } - - bool KeyBinding::Matches(const SDL_Keysym *sym) const - { - int mod = sym->mod; - if (mod & KMOD_CTRL) { - mod |= KMOD_CTRL; - } - if (mod & KMOD_SHIFT) { - mod |= KMOD_SHIFT; - } - if (mod & KMOD_ALT) { - mod |= KMOD_ALT; - } - if (mod & KMOD_GUI) { - mod |= KMOD_GUI; - } - return (type == KEYBOARD_KEY) && - (sym->sym == u.keyboard.key) && - ((mod & u.keyboard.mod) == u.keyboard.mod); - } - - bool KeyBinding::Matches(const SDL_JoyButtonEvent *joy) const - { - return (type == JOYSTICK_BUTTON) && - (joy->which == u.joystickButton.joystick) && - (joy->button == u.joystickButton.button); - } - - bool KeyBinding::Matches(const SDL_JoyHatEvent *joy) const - { - return (type == JOYSTICK_HAT) && - (joy->which == u.joystickHat.joystick) && - (joy->hat == u.joystickHat.hat) && - (joy->value == u.joystickHat.direction); - } - - std::string KeyBinding::Description() const - { - std::ostringstream oss; - - if (type == BINDING_DISABLED) { - // blank - } else if (type == KEYBOARD_KEY) { - if (u.keyboard.mod & KMOD_SHIFT) oss << Lang::SHIFT << " + "; - if (u.keyboard.mod & KMOD_CTRL) oss << Lang::CTRL << " + "; - if (u.keyboard.mod & KMOD_ALT) oss << Lang::ALT << " + "; - if (u.keyboard.mod & KMOD_GUI) oss << Lang::META << " + "; - oss << SDL_GetKeyName(u.keyboard.key); - } else if (type == JOYSTICK_BUTTON) { - oss << Pi::input->JoystickName(u.joystickButton.joystick); - oss << Lang::BUTTON << int(u.joystickButton.button); - } else if (type == JOYSTICK_HAT) { - oss << Pi::input->JoystickName(u.joystickHat.joystick); - oss << Lang::HAT << int(u.joystickHat.hat); - oss << Lang::DIRECTION << int(u.joystickHat.direction); - } else - assert(0 && "invalid key binding type"); - - return oss.str(); - } - - /** - * In a C string pointed to by the string pointer pointed to by p, scan for - * the character token tok, copying the bytes on the way to bufOut which is at most buflen long. - * - * returns true on success, returns false if the end of the input string was reached, - * or the buffer would be overfilled without encountering the token. - * - * upon return, the pointer pointed to by p will refer to the character AFTER the tok. - */ - static bool ReadToTok(char tok, const char **p, char *bufOut, size_t buflen) - { - unsigned int idx; - for (idx = 0; idx < buflen; idx++) { - if (**p == '\0' || **p == tok) { - break; - } - bufOut[idx] = *((*p)++); - } - // if, after that, we're not pointing at the tok, we must have hit - // the terminal or run out of buffer. - if (**p != tok) { - return false; - } - // otherwise, skip over the tok. - (*p)++; - // if there is sufficient space in the buffer, NUL terminate. - if (idx < buflen) { - bufOut[idx] = '\0'; - } - return true; - } - - /** - * Example strings: - * Key55 - * Joy{uuid}/Button2 - * Joy{uuid}/Hat0Dir3 - */ - bool KeyBinding::FromString(const char *str, KeyBinding &kb) - { - const char *digits = "1234567890"; - const char *p = str; - - if (strcmp(p, "disabled") == 0) { - kb.Clear(); - } else if (strncmp(p, "Key", 3) == 0) { - kb.type = KEYBOARD_KEY; - p += 3; - - kb.u.keyboard.key = SDL_Keycode(atoi(p)); - p += strspn(p, digits); - - if (strncmp(p, "Mod", 3) == 0) { - p += 3; - kb.u.keyboard.mod = SDL_Keymod(atoi(p)); - } else { - kb.u.keyboard.mod = KMOD_NONE; - } - } else if (strncmp(p, "Joy", 3) == 0) { - p += 3; - - const int JoyUUIDLength = 33; - char joyUUIDBuf[JoyUUIDLength]; - - // read the UUID - if (!ReadToTok('/', &p, joyUUIDBuf, JoyUUIDLength)) { - return false; - } - // force terminate - joyUUIDBuf[JoyUUIDLength - 1] = '\0'; - // now, locate the internal ID. - int joy = Pi::input->JoystickFromGUIDString(joyUUIDBuf); - if (joy == -1) { - return false; - } - if (strncmp(p, "Button", 6) == 0) { - p += 6; - kb.type = JOYSTICK_BUTTON; - kb.u.joystickButton.joystick = joy; - kb.u.joystickButton.button = atoi(p); - } else if (strncmp(p, "Hat", 3) == 0) { - p += 3; - kb.type = JOYSTICK_HAT; - kb.u.joystickHat.joystick = joy; - kb.u.joystickHat.hat = atoi(p); - p += strspn(p, digits); - - if (strncmp(p, "Dir", 3) != 0) - return false; - - p += 3; - kb.u.joystickHat.direction = atoi(p); - } else - return false; - } - - return true; - } - - KeyBinding KeyBinding::FromString(const char *str) - { - KeyBinding kb; - if (!KeyBinding::FromString(str, kb)) - kb.Clear(); - return kb; - } - - std::ostream &operator<<(std::ostream &oss, const KeyBinding &kb) - { - if (kb.type == BINDING_DISABLED) { - oss << "disabled"; - } else if (kb.type == KEYBOARD_KEY) { - oss << "Key" << int(kb.u.keyboard.key); - if (kb.u.keyboard.mod != 0) { - oss << "Mod" << int(kb.u.keyboard.mod); - } - } else if (kb.type == JOYSTICK_BUTTON) { - oss << "Joy" << Pi::input->JoystickGUIDString(kb.u.joystickButton.joystick); - oss << "/Button" << int(kb.u.joystickButton.button); - } else if (kb.type == JOYSTICK_HAT) { - oss << "Joy" << Pi::input->JoystickGUIDString(kb.u.joystickButton.joystick); - oss << "/Hat" << int(kb.u.joystickHat.hat); - oss << "Dir" << int(kb.u.joystickHat.direction); - } else { - assert(0 && "KeyBinding type field is invalid"); - } - return oss; - } - - std::string KeyBinding::ToString() const - { - std::ostringstream oss; - oss << *this; - return oss.str(); - } - - KeyBinding KeyBinding::FromKeyMod(SDL_Keycode key, SDL_Keymod mod) - { - KeyBinding kb; - kb.type = KEYBOARD_KEY; - kb.u.keyboard.key = key; - // expand the modifier to cover both left & right variants - int imod = mod; - if (imod & KMOD_CTRL) { - imod |= KMOD_CTRL; - } - if (imod & KMOD_SHIFT) { - imod |= KMOD_SHIFT; - } - if (imod & KMOD_ALT) { - imod |= KMOD_ALT; - } - if (imod & KMOD_GUI) { - imod |= KMOD_GUI; - } - kb.u.keyboard.mod = static_cast(imod); - return kb; - } - - KeyBinding KeyBinding::FromJoystickButton(Uint8 joystick, Uint8 button) - { - KeyBinding kb; - kb.type = JOYSTICK_BUTTON; - kb.u.joystickButton.joystick = joystick; - kb.u.joystickButton.button = button; - return kb; - } - - KeyBinding KeyBinding::FromJoystickHat(Uint8 joystick, Uint8 hat, Uint8 direction) - { - KeyBinding kb; - kb.type = JOYSTICK_HAT; - kb.u.joystickHat.joystick = joystick; - kb.u.joystickHat.hat = hat; - kb.u.joystickHat.direction = direction; - return kb; - } - - void ActionBinding::SetFromString(const char *str) - { - const size_t BUF_SIZE = 64; - const size_t len = strlen(str); - if (len >= BUF_SIZE) { - Output("invalid ActionBinding string\n"); - binding1 = KeyBinding::FromString(str); - binding2.Clear(); - } else { - const char *sep = strchr(str, ','); - if (sep) { - char buf[BUF_SIZE]; - const size_t len1 = sep - str; - const size_t len2 = len - len1 - 1; - memcpy(buf, str, len1); - buf[len1] = '\0'; - binding1 = KeyBinding::FromString(buf); - memcpy(buf, sep + 1, len2); - buf[len2] = '\0'; - binding2 = KeyBinding::FromString(buf); - } else { - binding1 = KeyBinding::FromString(str); - binding2.Clear(); - } - } - } - - std::string ActionBinding::ToString() const - { - std::ostringstream oss; - if (binding1.Enabled() && binding2.Enabled()) { - oss << binding1 << "," << binding2; - } else if (binding1.Enabled()) { - oss << binding1; - } else if (binding2.Enabled()) { - oss << binding2; - } else { - oss << "disabled"; - } - return oss.str(); - } - - bool ActionBinding::IsActive() const - { - return binding1.IsActive() || binding2.IsActive(); - } - - bool ActionBinding::Matches(const SDL_Keysym *sym) const - { - return binding1.Matches(sym) || binding2.Matches(sym); - } - - InputResponse ActionBinding::CheckSDLEventAndDispatch(const SDL_Event *event) - { - if (m_disableBindings) return RESPONSE_NOMATCH; - switch (event->type) { - case SDL_KEYDOWN: - case SDL_KEYUP: { - if (Matches(&event->key.keysym)) { - if (event->key.state == SDL_PRESSED) - onPress.emit(); - else if (event->key.state == SDL_RELEASED) - onRelease.emit(); - return RESPONSE_MATCHED; - } - break; - } - case SDL_JOYBUTTONDOWN: - case SDL_JOYBUTTONUP: { - if (binding1.Matches(&event->jbutton) || binding2.Matches(&event->jbutton)) { - if (event->jbutton.state == SDL_PRESSED) - onPress.emit(); - else if (event->jbutton.state == SDL_RELEASED) - onRelease.emit(); - return RESPONSE_MATCHED; - } - break; - } - case SDL_JOYHATMOTION: { - if (binding1.Matches(&event->jhat) || binding2.Matches(&event->jhat)) { - onPress.emit(); - // XXX to emit onRelease, we need to have access to the state of the joystick hat prior to this event, - // so that we can detect the case of switching from a direction that matches the binding to some other direction - return RESPONSE_MATCHED; - } - break; - } - default: break; - } - return RESPONSE_NOMATCH; - } - - bool JoyAxisBinding::IsActive() const - { - // If the stick is within the deadzone, it's not active. - return fabs(Pi::input->JoystickAxisState(joystick, axis)) > deadzone; - } - - float JoyAxisBinding::GetValue() const - { - if (!Enabled()) return 0.0f; - - const float o_val = Pi::input->JoystickAxisState(joystick, axis); - - // Deadzone with normalisation - float value = fabs(o_val); - if (value < deadzone) { - return 0.0f; - } else { - // subtract deadzone and re-normalise to full range - value = (value - deadzone) / (1.0f - deadzone); - } - - // Apply sensitivity scaling and clamp. - value = fmax(fmin(value * sensitivity, 1.0f), 0.0f); - - value = copysign(value, o_val); - - // Invert as necessary. - return direction == POSITIVE ? value : 0.0f - value; - } - - bool JoyAxisBinding::Matches(const SDL_Event *event) const - { - if (event->type != SDL_JOYAXISMOTION) return false; - return event->jaxis.which == joystick && event->jaxis.axis == axis; - } - - std::string JoyAxisBinding::Description() const - { - if (!Enabled()) return std::string(); - - const char *axis_names[] = { Lang::X, Lang::Y, Lang::Z }; - std::ostringstream ossaxisnum; - ossaxisnum << int(axis); - - return stringf(Lang::JOY_AXIS, - formatarg("sign", direction == KeyBindings::NEGATIVE ? "-" : ""), // no + sign if positive - formatarg("signp", direction == KeyBindings::NEGATIVE ? "-" : "+"), // optional with + sign - formatarg("joynum", joystick), - formatarg("joyname", Pi::input->JoystickName(joystick)), - formatarg("axis", axis < 3 ? axis_names[axis] : ossaxisnum.str())); - } - - bool JoyAxisBinding::FromString(const char *str, JoyAxisBinding &ab) - { - if (strcmp(str, "disabled") == 0) { - ab.Clear(); - return true; - } - - const char *p = str; - - if (p[0] == '-') { - ab.direction = NEGATIVE; - p++; - } else - ab.direction = POSITIVE; - - if (strncmp(p, "Joy", 3) != 0) - return false; - p += 3; - - const int JoyUUIDLength = 33; - char joyUUIDBuf[JoyUUIDLength]; - - // read the UUID - if (!ReadToTok('/', &p, joyUUIDBuf, JoyUUIDLength)) { - return false; - } - // force terminate - joyUUIDBuf[JoyUUIDLength - 1] = '\0'; - // now, map the GUID to a joystick number - const int joystick = Pi::input->JoystickFromGUIDString(joyUUIDBuf); - if (joystick == -1) { - return false; - } - // found a joystick - assert(joystick < 256); - ab.joystick = Uint8(joystick); - - if (strncmp(p, "Axis", 4) != 0) - return false; - - p += 4; - ab.axis = atoi(p); - - // Skip past the axis integer - if (!(p = strstr(p, "/DZ"))) - return true; // deadzone is optional - - p += 3; - ab.deadzone = atof(p); - - // Skip past the deadzone float - if (!(p = strstr(p, "/E"))) - return true; // sensitivity is optional - - p += 2; - ab.sensitivity = atof(p); - - return true; - } - - JoyAxisBinding JoyAxisBinding::FromString(const char *str) - { - JoyAxisBinding ab; - if (!JoyAxisBinding::FromString(str, ab)) - ab.Clear(); - return ab; - } - - std::string JoyAxisBinding::ToString() const - { - std::ostringstream oss; - if (Enabled()) { - if (direction == NEGATIVE) - oss << '-'; - - oss << "Joy"; - oss << Pi::input->JoystickGUIDString(joystick); - oss << "/Axis"; - oss << int(axis); - oss << "/DZ" << deadzone; - oss << "/E" << sensitivity; - } else { - oss << "disabled"; - } - return oss.str(); - } - - void AxisBinding::SetFromString(const std::string str) - { - size_t ofs = 0; - size_t nextpos = str.find(','); - if (nextpos == std::string::npos) return; - - if (str.substr(ofs, 8) != "disabled") - axis = JoyAxisBinding::FromString(str.substr(0, nextpos).c_str()); - - ofs = nextpos + 1; - nextpos = str.find(',', ofs); - if (str.substr(ofs, 8) != "disabled") - positive = KeyBinding::FromString(str.substr(ofs, nextpos - ofs).c_str()); - - ofs = nextpos + 1; - if (str.substr(ofs, 8) != "disabled") - negative = KeyBinding::FromString(str.substr(ofs).c_str()); - } - - std::string AxisBinding::ToString() const - { - std::ostringstream oss; - oss << axis.ToString() << ','; - oss << positive.ToString() << ','; - oss << negative.ToString(); - return oss.str(); - } - - bool AxisBinding::IsActive() const - { - return axis.IsActive() || positive.IsActive() || negative.IsActive(); - } - - float AxisBinding::GetValue() const - { - // Holding the positive and negative keys cancel each other out, - float value = 0.0f; - value += positive.IsActive() ? 1.0 : 0.0; - value -= negative.IsActive() ? 1.0 : 0.0; - - // And input on the axis device supercedes both of them. - return axis.IsActive() ? axis.GetValue() : value; - } - - InputResponse AxisBinding::CheckSDLEventAndDispatch(const SDL_Event *event) - { - if (m_disableBindings) return RESPONSE_NOMATCH; - float value = GetValue(); - switch (event->type) { - case SDL_KEYDOWN: - case SDL_KEYUP: { - if (positive.Matches(&event->key.keysym) && negative.Matches(&event->key.keysym)) { - onAxis.emit(value); - return RESPONSE_MATCHED; - } - break; - } - case SDL_JOYBUTTONDOWN: - case SDL_JOYBUTTONUP: { - if (positive.Matches(&event->jbutton) || negative.Matches(&event->jbutton)) { - onAxis.emit(value); - return RESPONSE_MATCHED; - } - break; - } - case SDL_JOYHATMOTION: { - if (positive.Matches(&event->jhat) || positive.Matches(&event->jhat)) { - onAxis.emit(value); - // XXX to emit onRelease, we need to have access to the state of the joystick hat prior to this event, - // so that we can detect the case of switching from a direction that matches the binding to some other direction - return RESPONSE_MATCHED; - } - break; - } - case SDL_JOYAXISMOTION: { - if (axis.Matches(event)) { - onAxis.emit(value); - return RESPONSE_MATCHED; - } - } - default: break; - } - - return RESPONSE_NOMATCH; - } - - void DispatchSDLEvent(const SDL_Event *event) - { - switch (event->type) { - case SDL_KEYDOWN: - case SDL_KEYUP: - case SDL_JOYBUTTONDOWN: - case SDL_JOYBUTTONUP: - case SDL_JOYHATMOTION: - break; - default: return; - } - - // simplest possible approach here: just check each binding and dispatch if it matches - for (ActionBinding *const *binding = s_KeyBindings; *binding; ++binding) { - (*binding)->CheckSDLEventAndDispatch(event); - } - } - - void InitKeyBinding(ActionBinding &kb, const std::string &bindName, Uint32 defaultKey1, Uint32 defaultKey2) - { - std::string keyName = Pi::config->String(bindName.c_str()); - if (keyName.length() == 0) { - if (defaultKey1 && defaultKey2) { - keyName = stringf("Key%0{u},Key%1{u}", defaultKey1, defaultKey2); - } else if (defaultKey1 || defaultKey2) { - Uint32 k = (defaultKey1 | defaultKey2); // only one of them is non-zero, so this gets the non-zero value - keyName = stringf("Key%0{u}", k); - } - Pi::config->SetString(bindName.c_str(), keyName.c_str()); - } - - // set the binding from the configured or default value - kb.SetFromString(keyName.c_str()); - } - - void InitAxisBinding(JoyAxisBinding &ab, const std::string &bindName, const std::string &defaultAxis) - { - std::string axisName = Pi::config->String(bindName.c_str()); - if (axisName.length() == 0) { - axisName = defaultAxis; - Pi::config->SetString(bindName.c_str(), axisName.c_str()); - } - - // set the binding from the configured or default value - if (!JoyAxisBinding::FromString(axisName.c_str(), ab)) { - Output("invalid axis binding '%s' in config file for %s\n", axisName.c_str(), bindName.c_str()); - ab.Clear(); - } - } - - void UpdateBindings() - { -#define KEY_BINDING(name, config_name, b, default_value_1, default_value_2) \ - InitKeyBinding(KeyBindings::name, config_name, default_value_1, default_value_2); -#define AXIS_BINDING(name, config_name, b, default_value) \ - InitAxisBinding(KeyBindings::name, config_name, default_value); -#include "KeyBindings.inc.h" - } - - void InitBindings() - { - UpdateBindings(); - Pi::config->Save(); - } - - void EnableBindings() - { - m_disableBindings = 0; - } - - void DisableBindings() - { - m_disableBindings = 1; - } - -} // namespace KeyBindings diff --git a/src/KeyBindings.h b/src/KeyBindings.h deleted file mode 100644 index 82374e7bb..000000000 --- a/src/KeyBindings.h +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright © 2008-2020 Pioneer Developers. See AUTHORS.txt for details -// Licensed under the terms of the GPL v3. See licenses/GPL-3.txt - -#ifndef KEYBINDINGS_H -#define KEYBINDINGS_H - -#include "libs.h" -#include - -enum InputResponse { - // None of the inputs match the event. - RESPONSE_NOMATCH = 0, - // An input matched, but won't consume the event. - RESPONSE_PASSTHROUGH, - // An input matched and consumed the event. - RESPONSE_MATCHED -}; - -namespace KeyBindings { - enum Type { - BINDING_DISABLED, - KEYBOARD_KEY, - JOYSTICK_BUTTON, - JOYSTICK_HAT, - MOUSE_BUTTON // TODO: implementme! - }; - - struct KeyBinding { - public: - // constructors - static bool FromString(const char *str, KeyBinding &binding); - static KeyBinding FromString(const char *str); - static KeyBinding FromKeyMod(SDL_Keycode key, SDL_Keymod mod); - static KeyBinding FromJoystickButton(Uint8 joystick, Uint8 button); - static KeyBinding FromJoystickHat(Uint8 joystick, Uint8 hat, Uint8 direction); - - KeyBinding() : - type(BINDING_DISABLED) - { - u.keyboard.key = SDLK_UNKNOWN; - u.keyboard.mod = KMOD_NONE; - } - KeyBinding(SDL_Keycode key, SDL_Keymod mod = KMOD_NONE) : - type(KEYBOARD_KEY) - { - u.keyboard.key = key; - u.keyboard.mod = mod; - } - - std::string ToString() const; // for serialisation - std::string Description() const; // for display to the user - - bool IsActive() const; - bool Matches(const SDL_Keysym *sym) const; - bool Matches(const SDL_JoyButtonEvent *joy) const; - bool Matches(const SDL_JoyHatEvent *joy) const; - - void Clear() { memset(this, 0, sizeof(*this)); } - - bool Enabled() const { return (type != BINDING_DISABLED); } - - friend std::ostream &operator<<(std::ostream &oss, const KeyBinding &kb); - - private: - Type type; - union { - struct { - SDL_Keycode key; - SDL_Keymod mod; - } keyboard; - - struct { - Uint8 joystick; - Uint8 button; - } joystickButton; - - struct { - Uint8 joystick; - Uint8 hat; - Uint8 direction; - } joystickHat; - - /* TODO: implement binding mouse buttons. - struct { - Uint8 button; - // TODO: implement binding multiple clicks as their own action. - Uint8 clicks; - } mouseButton; - */ - } u; - }; - - struct ActionBinding { - KeyBinding binding1; - KeyBinding binding2; - - sigc::signal onPress; - sigc::signal onRelease; - - ActionBinding() {} - ActionBinding(KeyBinding b1, KeyBinding b2 = KeyBinding()) : - binding1(b1), - binding2(b2) {} - // This constructor is just a programmer shortcut. - ActionBinding(SDL_Keycode k1, SDL_Keycode k2 = SDLK_UNKNOWN) - { - binding1 = KeyBinding(k1); - if (k2 != SDLK_UNKNOWN) binding2 = KeyBinding(k2); - } - - void SetFromString(const char *str); - void SetFromString(const std::string str) { return SetFromString(str.c_str()); } - std::string ToString() const; - - bool IsActive() const; - InputResponse CheckSDLEventAndDispatch(const SDL_Event *event); - - bool Matches(const SDL_Keysym *sym) const; - }; - - enum AxisDirection { - POSITIVE, - NEGATIVE - }; - - struct JoyAxisBinding { - public: - JoyAxisBinding() : - joystick(JOYSTICK_DISABLED), - axis(0), - direction(POSITIVE), - deadzone(0.0f), - sensitivity(1.0f) {} - JoyAxisBinding(Uint8 joystick_, Uint8 axis_, AxisDirection direction_, float deadzone_ = 0.0f, float sensitivity_ = 1.0f) : - joystick(joystick_), - axis(axis_), - direction(direction_), - deadzone(deadzone_), - sensitivity(sensitivity_) {} - - float GetValue() const; - std::string Description() const; - - void Clear() - { - joystick = JOYSTICK_DISABLED; - axis = 0; - direction = POSITIVE; - deadzone = 0.0f; - sensitivity = 1.0f; - } - - bool Enabled() const { return (joystick != JOYSTICK_DISABLED); } - - static bool FromString(const char *str, JoyAxisBinding &binding); - static JoyAxisBinding FromString(const char *str); - std::string ToString() const; - - bool Matches(const SDL_Event *event) const; - bool IsActive() const; - - bool IsInverted() { return direction == NEGATIVE; } - AxisDirection GetDirection() { return direction; } - void SetDirection(AxisDirection dir) { direction = dir; } - - float GetDeadzone() { return deadzone; } - void SetDeadzone(float dz) { deadzone = dz; } - - float GetSensitivity() { return sensitivity; } - void SetSensitivity(float sens) { sensitivity = sens; } - - private: - enum { JOYSTICK_DISABLED = Uint8(-1) }; - Uint8 joystick; - Uint8 axis; - AxisDirection direction; - float deadzone; - float sensitivity; - }; - - struct AxisBinding { - JoyAxisBinding axis; - KeyBinding positive; - KeyBinding negative; - - AxisBinding() {} - AxisBinding(JoyAxisBinding ax, KeyBinding pos = KeyBinding(), KeyBinding neg = KeyBinding()) : - axis(ax), - positive(pos), - negative(neg) {} - // This constructor is just a programmer shortcut. - AxisBinding(SDL_Keycode k1, SDL_Keycode k2) : - positive(KeyBinding(k1)), - negative(KeyBinding(k2)) {} - - sigc::signal onAxis; - - void SetFromString(const char *str) { return SetFromString(std::string(str)); } - void SetFromString(const std::string str); - std::string ToString() const; - - bool IsActive() const; - float GetValue() const; - InputResponse CheckSDLEventAndDispatch(const SDL_Event *event); - }; - - struct BindingPrototype { - const char *label, *function; - ActionBinding *kb; - JoyAxisBinding *ab; - }; - - void InitBindings(); - void UpdateBindings(); - void EnableBindings(); - void DisableBindings(); - - void DispatchSDLEvent(const SDL_Event *event); - -#define KEY_BINDING(name, a, b, c, d) extern ActionBinding name; -#define AXIS_BINDING(name, a, b, c) extern JoyAxisBinding name; -#include "KeyBindings.inc.h" - -#define BINDING_PAGE(name) extern const BindingPrototype BINDING_PROTOS_##name[]; -#include "KeyBindings.inc.h" - -} // namespace KeyBindings - -#endif /* KEYBINDINGS_H */ diff --git a/src/ModelViewer.cpp b/src/ModelViewer.cpp index 2a10965e8..52a8b0a38 100644 --- a/src/ModelViewer.cpp +++ b/src/ModelViewer.cpp @@ -5,7 +5,6 @@ #include "FileSystem.h" #include "GameConfig.h" #include "GameSaveError.h" -#include "KeyBindings.h" #include "ModManager.h" #include "PngWriter.h" #include "SDL_keycode.h" @@ -122,12 +121,7 @@ void ModelViewerApp::Startup() auto *renderer = StartupRenderer(config.get()); - // FIXME MAJOR FIXME: Action / Axis bindings depend on Pi::input to get their data. - // This is OBVIOUSLY suboptimal, and *must* be redesigned. - // Either make Input a singleton (lots of function overhead when polling axes) - // or cache input state on the binding itself (probably the best option) - - Pi::input = StartupInput(config.get()); + StartupInput(config.get()); StartupPiGui(); NavLights::Init(renderer); @@ -171,6 +165,7 @@ void ModelViewerApp::PostUpdate() ModelViewer::ModelViewer(ModelViewerApp *app, LuaManager *lm) : m_input(app->GetInput()), m_pigui(app->GetPiGui()), + m_bindings(m_input), m_logWindowSize(350.0f, 500.0f), m_animWindowSize(0.0f, 150.0f), m_colors({ Color(255, 0, 0), @@ -609,14 +604,20 @@ void ModelViewer::SetupAxes() auto *page = m_input->GetBindingPage("ModelViewer"); auto *group = page->GetBindingGroup("View"); -#define AXIS(name, axis, positive, negative) m_input->AddAxisBinding(name, group, KeyBindings::AxisBinding(axis, positive, negative)) -#define ACTION(name, b1, b2) m_input->AddActionBinding(name, group, KeyBindings::ActionBinding(b1, b2)) + // Don't add this to REGISTER_INPUT_BINDING because these bindings aren't used by the game +#define AXIS(val, name, axis, positive, negative) \ + m_input->AddAxisBinding(name, group, InputBindings::Axis(axis, { positive }, { negative })); \ + m_bindings.val = m_bindings.AddAxis(name) - m_zoomAxis = AXIS("BindZoomAxis", {}, SDLK_EQUALS, SDLK_MINUS); +#define ACTION(val, name, b1, b2) \ + m_input->AddActionBinding(name, group, InputBindings::Action({ b1 }, { b2 })); \ + m_bindings.val = m_bindings.AddAction(name) - m_moveForward = AXIS("BindMoveForward", {}, SDLK_w, SDLK_s); - m_moveLeft = AXIS("BindMoveLeft", {}, SDLK_a, SDLK_d); - m_moveUp = AXIS("BindMoveUp", {}, SDLK_q, SDLK_e); + AXIS(zoomAxis, "BindZoomAxis", {}, SDLK_EQUALS, SDLK_MINUS); + + AXIS(moveForward, "BindMoveForward", {}, SDLK_w, SDLK_s); + AXIS(moveLeft, "BindMoveLeft", {}, SDLK_a, SDLK_d); + AXIS(moveUp, "BindMoveUp", {}, SDLK_q, SDLK_e); // Like Blender, but a bit different because we like that // 1 - front (+ctrl back) @@ -624,23 +625,28 @@ void ModelViewer::SetupAxes() // 3 - left (+ctrl right) // 2,4,6,8 incrementally rotate - m_viewFront = ACTION("BindViewFront", SDLK_KP_1, SDLK_m); - m_viewFront->onPress.connect([=]() { + ACTION(viewFront, "BindViewFront", SDLK_KP_1, SDLK_m); + m_bindings.viewFront->onPressed.connect([=]() { this->ChangeCameraPreset(m_input->KeyModState() & KMOD_CTRL ? CameraPreset::Back : CameraPreset::Front); }); - m_viewLeft = ACTION("BindViewLeft", SDLK_KP_3, SDLK_PERIOD); - m_viewLeft->onPress.connect([=]() { + ACTION(viewLeft, "BindViewLeft", SDLK_KP_3, SDLK_PERIOD); + m_bindings.viewLeft->onPressed.connect([=]() { this->ChangeCameraPreset(m_input->KeyModState() & KMOD_CTRL ? CameraPreset::Right : CameraPreset::Left); }); - m_viewTop = ACTION("BindViewTop", SDLK_KP_7, SDLK_u); - m_viewTop->onPress.connect([=]() { + ACTION(viewTop, "BindViewTop", SDLK_KP_7, SDLK_u); + m_bindings.viewTop->onPressed.connect([=]() { this->ChangeCameraPreset(m_input->KeyModState() & KMOD_CTRL ? CameraPreset::Bottom : CameraPreset::Top); }); - m_rotateViewLeft = AXIS("BindRotateViewLeft", {}, SDLK_KP_6, SDLK_KP_4); - m_rotateViewUp = AXIS("BindRotateViewUp", {}, SDLK_KP_8, SDLK_KP_2); + AXIS(rotateViewLeft, "BindRotateViewLeft", {}, SDLK_KP_6, SDLK_KP_4); + AXIS(rotateViewUp, "BindRotateViewUp", {}, SDLK_KP_8, SDLK_KP_2); + +#undef AXIS +#undef ACTION + + m_input->PushInputFrame(&m_bindings); } void ModelViewer::HandleInput() @@ -1165,14 +1171,14 @@ void ModelViewer::UpdateCamera(float deltaTime) } vector3f motion( - m_moveLeft->GetValue(), - m_moveUp->GetValue(), - m_moveForward->GetValue()); + m_bindings.moveLeft->GetValue(), + m_bindings.moveUp->GetValue(), + m_bindings.moveForward->GetValue()); m_viewPos += m_viewRot * motion; } else { //zoom - m_zoom += m_zoomAxis->GetValue() * BASE_ZOOM_RATE; + m_zoom += m_bindings.zoomAxis->GetValue() * BASE_ZOOM_RATE; //zoom with mouse wheel int mouseWheel = m_input->GetMouseWheel(); @@ -1187,8 +1193,8 @@ void ModelViewer::UpdateCamera(float deltaTime) if (m_input->IsKeyDown(SDLK_LEFT)) m_rotY += rotateRate; if (m_input->IsKeyDown(SDLK_RIGHT)) m_rotY -= rotateRate; - m_rotX += rotateRate * m_rotateViewLeft->GetValue(); - m_rotY += rotateRate * -m_rotateViewUp->GetValue(); + m_rotX += rotateRate * m_bindings.rotateViewLeft->GetValue(); + m_rotY += rotateRate * -m_bindings.rotateViewUp->GetValue(); //mouse rotate when right button held if (rightMouseDown) { diff --git a/src/ModelViewer.h b/src/ModelViewer.h index 6b06c0caa..75df7c3a0 100644 --- a/src/ModelViewer.h +++ b/src/ModelViewer.h @@ -19,7 +19,6 @@ #include -class Input; class ModelViewer; class ModelViewerApp : public GuiApplication { @@ -42,7 +41,6 @@ protected: private: std::string m_modelName; - std::unique_ptr m_input; std::shared_ptr m_modelViewer; }; @@ -132,20 +130,24 @@ private: }; private: - Input *m_input; + Input::Manager *m_input; PiGui::Instance *m_pigui; - KeyBindings::AxisBinding *m_moveForward; - KeyBindings::AxisBinding *m_moveLeft; - KeyBindings::AxisBinding *m_moveUp; - KeyBindings::AxisBinding *m_zoomAxis; + struct Inputs : Input::InputFrame { + using InputFrame::InputFrame; - KeyBindings::AxisBinding *m_rotateViewLeft; - KeyBindings::AxisBinding *m_rotateViewUp; + Axis *moveForward; + Axis *moveLeft; + Axis *moveUp; + Axis *zoomAxis; - KeyBindings::ActionBinding *m_viewTop; - KeyBindings::ActionBinding *m_viewLeft; - KeyBindings::ActionBinding *m_viewFront; + Axis *rotateViewLeft; + Axis *rotateViewUp; + + Action *viewTop; + Action *viewLeft; + Action *viewFront; + } m_bindings; vector2f m_windowSize; vector2f m_logWindowSize; diff --git a/src/Pi.cpp b/src/Pi.cpp index df0c7d22c..f473f4e7e 100644 --- a/src/Pi.cpp +++ b/src/Pi.cpp @@ -1,6 +1,7 @@ // Copyright © 2008-2020 Pioneer Developers. See AUTHORS.txt for details // Licensed under the terms of the GPL v3. See licenses/GPL-3.txt +#include "Input.h" #include "buildopts.h" #include "Pi.h" @@ -17,7 +18,6 @@ #include "GameLog.h" #include "GameSaveError.h" #include "Intro.h" -#include "KeyBindings.h" #include "Lang.h" #include "Missile.h" #include "ModManager.h" @@ -106,7 +106,7 @@ LuaNameGen *Pi::luaNameGen; #ifdef ENABLE_SERVER_AGENT ServerAgent *Pi::serverAgent; #endif -Input *Pi::input; +Input::Manager *Pi::input; Player *Pi::player; View *Pi::currentView; TransferPlanner *Pi::planner; @@ -323,17 +323,6 @@ void TestGPUJobsSupport() } } -// TODO: make this a part of the class and/or improve the mechanism -void RegisterInputBindings() -{ - PlayerShipController::RegisterInputBindings(); - - ShipViewController::InputBindings.RegisterBindings(); - - WorldView::RegisterInputBindings(); - SectorView::InputBindings.RegisterBindings(); -} - void Pi::App::Startup() { PROFILE_SCOPED() @@ -380,11 +369,11 @@ void Pi::App::Startup() Pi::input = StartupInput(config); Pi::input->onKeyPress.connect(sigc::ptr_fun(&Pi::HandleKeyDown)); - // we can only do bindings once joysticks are initialised. - if (!m_noGui) // This re-saves the config file. With no GUI we want to allow multiple instances in parallel. - KeyBindings::InitBindings(); - - RegisterInputBindings(); + // Register all C++-side input bindings. + // TODO: handle registering Lua input bindings in the startup phase. + for (auto ®istrar : Input::GetBindingRegistration()) { + registrar(Pi::input); + } Pi::pigui = StartupPiGui(); @@ -613,7 +602,7 @@ void LoadStep::Start() AddStep("PostLoad", []() { Pi::luaConsole = new LuaConsole(); - KeyBindings::toggleLuaConsole.onPress.connect(sigc::mem_fun(Pi::luaConsole, &LuaConsole::Toggle)); + Pi::luaConsole->SetupBindings(); Pi::planner = new TransferPlanner(); @@ -843,13 +832,8 @@ bool Pi::App::HandleEvent(SDL_Event &event) return true; bool consoleActive = Pi::IsConsoleActive(); - if (!consoleActive) { - KeyBindings::DispatchSDLEvent(&event); - if (currentView) - currentView->HandleSDLEvent(event); - } else { - KeyBindings::toggleLuaConsole.CheckSDLEventAndDispatch(&event); - } + if (!consoleActive && currentView) + currentView->HandleSDLEvent(event); if (consoleActive != Pi::IsConsoleActive()) { skipTextInput = true; diff --git a/src/Pi.h b/src/Pi.h index df07a93a9..af4d8c700 100644 --- a/src/Pi.h +++ b/src/Pi.h @@ -16,6 +16,10 @@ #include #include +namespace Input { + class Manager; +} //namespace Input + namespace PiGui { class Instance; } //namespace PiGui @@ -23,7 +27,6 @@ namespace PiGui { class Game; class GameConfig; -class Input; class Intro; class LuaConsole; class LuaNameGen; @@ -207,7 +210,7 @@ public: static void RequestProfileFrame(const std::string &profilePath = ""); - static Input *input; + static Input::Manager *input; static Player *player; static TransferPlanner *planner; static LuaConsole *luaConsole; diff --git a/src/Player.cpp b/src/Player.cpp index b4d13d648..59a894738 100644 --- a/src/Player.cpp +++ b/src/Player.cpp @@ -8,7 +8,6 @@ #include "GameConfig.h" #include "GameLog.h" #include "HyperspaceCloud.h" -#include "KeyBindings.h" #include "Lang.h" #include "Pi.h" #include "SectorView.h" diff --git a/src/SectorView.cpp b/src/SectorView.cpp index e6f900453..3a252de7e 100644 --- a/src/SectorView.cpp +++ b/src/SectorView.cpp @@ -23,6 +23,7 @@ #include "gui/Gui.h" #include "lua/LuaConstants.h" #include "lua/LuaObject.h" +#include "sigc++/functors/mem_fun.h" #include "utils.h" #include #include @@ -46,8 +47,56 @@ enum DetailSelection { static const float ZOOM_SPEED = 15; static const float WHEEL_SENSITIVITY = .03f; // Should be a variable in user settings. +REGISTER_INPUT_BINDING(SectorView) +{ + using namespace InputBindings; + auto *mapView = input->GetBindingPage("MapControls"); + Input::BindingGroup *group; + +#define BINDING_GROUP(n) group = mapView->GetBindingGroup(#n); +#define KEY_BINDING(name, k1, k2) \ + input->AddActionBinding(name, group, InputBindings::Action({ k1 }, { k2 })); +#define AXIS_BINDING(name, k1, k2) \ + input->AddAxisBinding(name, group, InputBindings::Axis({}, { k1 }, { k2 })); + + BINDING_GROUP(GeneralViewControls) + KEY_BINDING("BindResetOrientationAndZoom", SDLK_t, 0) + AXIS_BINDING("BindMapViewYaw", SDLK_KP_4, SDLK_KP_6) + AXIS_BINDING("BindMapViewPitch", SDLK_KP_8, SDLK_KP_2) + AXIS_BINDING("BindViewZoom", SDLK_KP_PLUS, SDLK_KP_MINUS) + AXIS_BINDING("BindMapViewMoveUp", SDLK_r, SDLK_f) + AXIS_BINDING("BindMapViewMoveLeft", SDLK_a, SDLK_d) + AXIS_BINDING("BindMapViewMoveForward", SDLK_w, SDLK_s) + + BINDING_GROUP(SectorMapViewControls) + KEY_BINDING("BindMapToggleSelectionFollowView", SDLK_RETURN, SDLK_KP_ENTER) + KEY_BINDING("BindMapWarpToCurrentSystem", SDLK_c, 0) + KEY_BINDING("BindMapWarpToSelectedSystem", SDLK_g, 0) + +#undef KEY_BINDING +#undef AXIS_BINDING +#undef BINDING_GROUP +} + +void SectorView::InputBinding::RegisterBindings() +{ + mapViewReset = AddAction("BindResetOrientationAndZoom"); + mapToggleSelectionFollowView = AddAction("BindMapToggleSelectionFollowView"); + mapWarpToCurrent = AddAction("BindMapWarpToCurrentSystem"); + mapWarpToSelected = AddAction("BindMapWarpToSelectedSystem"); + + mapViewMoveForward = AddAxis("BindMapViewMoveForward"); + mapViewMoveLeft = AddAxis("BindMapViewMoveLeft"); + mapViewMoveUp = AddAxis("BindMapViewMoveUp"); + + mapViewYaw = AddAxis("BindMapViewYaw"); + mapViewPitch = AddAxis("BindMapViewPitch"); + mapViewZoom = AddAxis("BindMapViewZoom"); +} + SectorView::SectorView(Game *game) : UIView(), + InputBindings(Pi::input), m_galaxy(game->GetGalaxy()) { InitDefaults(); @@ -80,6 +129,7 @@ SectorView::SectorView(Game *game) : SectorView::SectorView(const Json &jsonObj, Game *game) : UIView(), + InputBindings(Pi::input), m_galaxy(game->GetGalaxy()) { InitDefaults(); @@ -130,6 +180,7 @@ void SectorView::InitDefaults() m_cacheYMax = 0; m_sectorCache = m_galaxy->NewSectorSlaveCache(); + InputBindings.RegisterBindings(); m_drawRouteLines = true; // where should this go?! m_route = std::vector(); @@ -139,19 +190,19 @@ void SectorView::InitObject() { // single keystroke handlers m_onToggleSelectionFollowView = - InputBindings.mapToggleSelectionFollowView->onPress.connect([&]() { + InputBindings.mapToggleSelectionFollowView->onPressed.connect([&]() { m_automaticSystemSelection = !m_automaticSystemSelection; }); m_onWarpToCurrent = - InputBindings.mapWarpToCurrent->onPress.connect([&]() { + InputBindings.mapWarpToCurrent->onPressed.connect([&]() { GotoSystem(m_current); }); m_onWarpToSelected = - InputBindings.mapWarpToSelected->onPress.connect([&]() { + InputBindings.mapWarpToSelected->onPressed.connect([&]() { GotoSystem(m_selected); }); m_onViewReset = - InputBindings.mapViewReset->onPress.connect([&]() { + InputBindings.mapViewReset->onPressed.connect([&]() { while (m_rotZ < -180.0f) m_rotZ += 360.0f; while (m_rotZ > 180.0f) @@ -1313,43 +1364,6 @@ std::vector SectorView::GetNearbyStarSystemsByName(std::string patte return result; } -SectorView::InputBinding SectorView::InputBindings; - -void SectorView::InputBinding::RegisterBindings() -{ - using namespace KeyBindings; - Input::BindingPage *page = Pi::input->GetBindingPage("MapControls"); - Input::BindingGroup *group; - -#define BINDING_GROUP(n) group = page->GetBindingGroup(#n); -#define KEY_BINDING(n, id, k1, k2) \ - n = \ - Pi::input->AddActionBinding(id, group, ActionBinding(k1, k2)); \ - actions.push_back(n); -#define AXIS_BINDING(n, id, k1, k2) \ - n = \ - Pi::input->AddAxisBinding(id, group, AxisBinding(k1, k2)); \ - axes.push_back(n); - - BINDING_GROUP(GeneralViewControls) - KEY_BINDING(mapViewReset, "ResetOrientationAndZoom", SDLK_t, 0) - AXIS_BINDING(mapViewYaw, "BindMapViewYaw", SDLK_KP_4, SDLK_KP_6) - AXIS_BINDING(mapViewPitch, "BindMapViewPitch", SDLK_KP_8, SDLK_KP_2) - AXIS_BINDING(mapViewZoom, "BindViewZoom", SDLK_KP_PLUS, SDLK_KP_MINUS) - AXIS_BINDING(mapViewMoveUp, "BindMapViewMoveUp", SDLK_r, SDLK_f) - AXIS_BINDING(mapViewMoveLeft, "BindMapViewMoveLeft", SDLK_a, SDLK_d) - AXIS_BINDING(mapViewMoveForward, "BindMapViewMoveForward", SDLK_w, SDLK_s) - - BINDING_GROUP(SectorMapViewControls) - KEY_BINDING(mapToggleSelectionFollowView, "MapToggleSelectionFollowView", SDLK_RETURN, SDLK_KP_ENTER) - KEY_BINDING(mapWarpToCurrent, "MapWarpToCurrentSystem", SDLK_c, 0) - KEY_BINDING(mapWarpToSelected, "MapWarpToSelectedSystem", SDLK_g, 0) - -#undef BINDING_GROUP -#undef KEY_BINDING -#undef AXIS_BINDING -} - void SectorView::SetFactionVisible(const Faction *faction, bool visible) { if (visible) diff --git a/src/SectorView.h b/src/SectorView.h index 4fafcf85e..d56d67847 100644 --- a/src/SectorView.h +++ b/src/SectorView.h @@ -4,6 +4,7 @@ #ifndef _SECTORVIEW_H #define _SECTORVIEW_H +#include "Input.h" #include "UIView.h" #include "Input.h" #include "galaxy/Sector.h" @@ -76,9 +77,12 @@ public: const std::string AutoRoute(const SystemPath &start, const SystemPath &target, std::vector &outRoute) const; void SetDrawRouteLines(bool value) { m_drawRouteLines = value; } - static struct InputBinding : public Input::InputFrame { - using Action = KeyBindings::ActionBinding; - using Axis = KeyBindings::AxisBinding; +protected: + void OnSwitchTo() override; + void OnSwitchFrom() override; + + struct InputBinding : public Input::InputFrame { + using InputFrame::InputFrame; Action *mapToggleSelectionFollowView; Action *mapWarpToCurrent; @@ -91,13 +95,10 @@ public: Axis *mapViewYaw; Axis *mapViewPitch; Axis *mapViewZoom; + void RegisterBindings() override; } InputBindings; -protected: - void OnSwitchTo() override; - void OnSwitchFrom() override; - private: void InitDefaults(); void InitObject(); @@ -135,7 +136,6 @@ private: void SetSelected(const SystemPath &path); void MouseWheel(bool up); - void OnKeyPressed(SDL_Keysym *keysym); RefCountedPtr m_galaxy; diff --git a/src/ShipCpanelMultiFuncDisplays.cpp b/src/ShipCpanelMultiFuncDisplays.cpp index 7854f4aa7..8690df729 100644 --- a/src/ShipCpanelMultiFuncDisplays.cpp +++ b/src/ShipCpanelMultiFuncDisplays.cpp @@ -5,7 +5,6 @@ #include "Game.h" #include "GameSaveError.h" #include "HyperspaceCloud.h" -#include "KeyBindings.h" #include "Lang.h" #include "Missile.h" #include "Pi.h" @@ -73,7 +72,10 @@ void RadarWidget::InitObject() { InitScaling(); +#if 0 // TODO: KeyBindings has been removed, reimplement this when switching to PiGui. m_toggleScanModeConnection = KeyBindings::toggleScanMode.onPress.connect(sigc::mem_fun(this, &RadarWidget::ToggleMode)); +#endif + m_lastRange = RADAR_RANGE_MAX * 100.0f; // force regen GenerateBaseGeometry(); @@ -99,6 +101,7 @@ void RadarWidget::GetSizeRequested(float size[2]) size[1] = 62; } +#if 0 // See previous notice void RadarWidget::ToggleMode() { if (IsVisible() && Pi::game->GetTimeAccel() != Game::TIMEACCEL_PAUSED) { @@ -108,6 +111,7 @@ void RadarWidget::ToggleMode() m_mode = RADAR_MODE_AUTO; } } +#endif void RadarWidget::Draw() { @@ -256,6 +260,7 @@ void RadarWidget::Update() m_contacts.push_back(c); } +#if 0 // TODO: KeyBindings has been removed, translate this into PiGui and the new InputBindings system. if (KeyBindings::increaseScanRange.IsActive()) { if (m_mode == RADAR_MODE_AUTO) { m_manualRange = m_targetRange; @@ -271,6 +276,7 @@ void RadarWidget::Update() m_manualRange = m_currentRange; m_manualRange = Clamp(m_manualRange * 0.95f, RADAR_RANGE_MIN, RADAR_RANGE_MAX); } +#endif if (m_mode == RADAR_MODE_AUTO) { switch (range_type) { diff --git a/src/SystemView.cpp b/src/SystemView.cpp index ffffdcaf4..8834a04a8 100644 --- a/src/SystemView.cpp +++ b/src/SystemView.cpp @@ -226,8 +226,16 @@ vector3d TransferPlanner::GetPosition() const { return m_position; } void TransferPlanner::SetPosition(const vector3d &position) { m_position = position; } +void SystemView::InputBindings::RegisterBindings() +{ + mapViewPitch = AddAxis("BindMapViewPitch"); + mapViewYaw = AddAxis("BindMapViewYaw"); + mapViewZoom = AddAxis("BindMapViewZoom"); +} + SystemView::SystemView(Game *game) : UIView(), + m_input(Pi::input), m_game(game), m_showL4L5(LAG_OFF), m_shipDrawing(OFF), @@ -240,6 +248,8 @@ SystemView::SystemView(Game *game) : m_zoom = 1.0f / float(AU); SetTransparency(true); + m_input.RegisterBindings(); + Graphics::RenderStateDesc rsd; m_lineState = Pi::renderer->CreateRenderState(rsd); //m_renderer not set yet @@ -615,12 +625,12 @@ void SystemView::Update() } // camera control signals from devices, sent to the SectorView - if (SectorView::InputBindings.mapViewZoom->IsActive()) - m_zoomTo *= pow(ZOOM_IN_SPEED * 0.006 + 1, SectorView::InputBindings.mapViewZoom->GetValue()); - if (SectorView::InputBindings.mapViewYaw->IsActive()) - m_rot_y_to += SectorView::InputBindings.mapViewYaw->GetValue() * ft * 60; - if (SectorView::InputBindings.mapViewPitch->IsActive()) - m_rot_x_to += SectorView::InputBindings.mapViewPitch->GetValue() * ft * 60; + if (m_input.mapViewZoom->IsActive()) + m_zoomTo *= pow(ZOOM_IN_SPEED * 0.006 + 1, m_input.mapViewZoom->GetValue()); + if (m_input.mapViewYaw->IsActive()) + m_rot_y_to += m_input.mapViewYaw->GetValue() * ft * 60; + if (m_input.mapViewPitch->IsActive()) + m_rot_x_to += m_input.mapViewPitch->GetValue() * ft * 60; m_rot_x_to = Clamp(m_rot_x_to, -80.0f, 80.0f); diff --git a/src/SystemView.h b/src/SystemView.h index 58321bfdc..50918c158 100644 --- a/src/SystemView.h +++ b/src/SystemView.h @@ -6,12 +6,13 @@ #include "Color.h" #include "DeleteEmitter.h" +#include "Frame.h" +#include "Input.h" #include "UIView.h" #include "graphics/Drawables.h" #include "matrix4x4.h" #include "vector3.h" #include "enum_table.h" -#include "Frame.h" class StarSystem; class SystemBody; @@ -150,6 +151,15 @@ public: void SetColor(ColorIndex color_index, Color* color_value) { svColor[color_index] = *color_value; } private: + struct InputBindings : public Input::InputFrame { + using InputFrame::InputFrame; + void RegisterBindings() override; + + Axis *mapViewPitch; + Axis *mapViewYaw; + Axis *mapViewZoom; + } m_input; + bool m_rotateWithMouseButton = false; bool m_rotateView = false; bool m_zoomView = false; diff --git a/src/UIView.h b/src/UIView.h index 22b15a26f..41258658a 100644 --- a/src/UIView.h +++ b/src/UIView.h @@ -20,14 +20,14 @@ public: UIView() : m_templateName(0) {} - virtual void Update() {} - virtual void Draw3D() {} + virtual void Update() override {} + virtual void Draw3D() override {} const char *GetTemplateName() { return m_templateName; } protected: virtual void BuildUI(UI::Single *container); - virtual void OnSwitchTo(); - virtual void OnSwitchFrom(); + virtual void OnSwitchTo() override; + virtual void OnSwitchFrom() override; UI::Widget *BuildTemplateUI(); diff --git a/src/WorldView.cpp b/src/WorldView.cpp index 5ec237730..b9134dd9a 100644 --- a/src/WorldView.cpp +++ b/src/WorldView.cpp @@ -35,16 +35,35 @@ namespace { static const Color red(255, 0, 0, 128); } // namespace +REGISTER_INPUT_BINDING(WorldView) +{ + using namespace InputBindings; + Input::BindingGroup *group = input->GetBindingPage("General")->GetBindingGroup("Miscellaneous"); + + input->AddActionBinding("BindToggleHudMode", group, Action({ SDLK_TAB })); + input->AddActionBinding("BindIncreaseTimeAcceleration", group, Action({ SDLK_PAGEUP })); + input->AddActionBinding("BindDecreaseTimeAcceleration", group, Action({ SDLK_PAGEDOWN })); +} + +void WorldView::InputBinding::RegisterBindings() +{ + toggleHudMode = AddAction("BindToggleHudMode"); + increaseTimeAcceleration = AddAction("BindIncreaseTimeAcceleration"); + decreaseTimeAcceleration = AddAction("BindDecreaseTimeAcceleration"); +} + WorldView::WorldView(Game *game) : PiGuiView("WorldView"), - m_game(game) + m_game(game), + InputBindings(Pi::input) { InitObject(); } WorldView::WorldView(const Json &jsonObj, Game *game) : PiGuiView("WorldView"), - m_game(game) + m_game(game), + InputBindings(Pi::input) { if (!jsonObj["world_view"].is_object()) throw SavedGameCorruptException(); Json worldViewObj = jsonObj["world_view"]; @@ -54,26 +73,6 @@ WorldView::WorldView(const Json &jsonObj, Game *game) : shipView->LoadFromJson(worldViewObj); } -WorldView::InputBinding WorldView::InputBindings; - -void WorldView::RegisterInputBindings() -{ - using namespace KeyBindings; - Input::BindingPage *page = Pi::input->GetBindingPage("General"); - Input::BindingGroup *group; - -#define BINDING_GROUP(n) group = page->GetBindingGroup(#n); -#define KEY_BINDING(n, id, k1, k2) InputBindings.n = Pi::input->AddActionBinding(id, group, \ - ActionBinding(k1, k2)); -#define AXIS_BINDING(n, id, k1, k2) InputBindings.n = Pi::input->AddAxisBinding(id, group, \ - AxisBinding(k1, k2)); - - BINDING_GROUP(Miscellaneous) - KEY_BINDING(toggleHudMode, "BindToggleHudMode", SDLK_TAB, 0) - KEY_BINDING(increaseTimeAcceleration, "BindIncreaseTimeAcceleration", SDLK_PAGEUP, 0) - KEY_BINDING(decreaseTimeAcceleration, "BindDecreaseTimeAcceleration", SDLK_PAGEDOWN, 0) -} - void WorldView::InitObject() { m_labelsOn = true; @@ -104,13 +103,14 @@ void WorldView::InitObject() m_cameraContext.Reset(new CameraContext(Graphics::GetScreenWidth(), Graphics::GetScreenHeight(), fovY, znear, zfar)); m_camera.reset(new Camera(m_cameraContext, Pi::renderer)); + InputBindings.RegisterBindings(); shipView.reset(new ShipViewController(this)); shipView->Init(); SetViewController(shipView.get()); - m_onToggleHudModeCon = InputBindings.toggleHudMode->onPress.connect(sigc::mem_fun(this, &WorldView::OnToggleLabels)); - m_onIncTimeAccelCon = InputBindings.increaseTimeAcceleration->onPress.connect(sigc::mem_fun(this, &WorldView::OnRequestTimeAccelInc)); - m_onDecTimeAccelCon = InputBindings.decreaseTimeAcceleration->onPress.connect(sigc::mem_fun(this, &WorldView::OnRequestTimeAccelDec)); + m_onToggleHudModeCon = InputBindings.toggleHudMode->onPressed.connect(sigc::mem_fun(this, &WorldView::OnToggleLabels)); + m_onIncTimeAccelCon = InputBindings.increaseTimeAcceleration->onPressed.connect(sigc::mem_fun(this, &WorldView::OnRequestTimeAccelInc)); + m_onDecTimeAccelCon = InputBindings.decreaseTimeAcceleration->onPressed.connect(sigc::mem_fun(this, &WorldView::OnRequestTimeAccelDec)); } WorldView::~WorldView() @@ -239,12 +239,14 @@ void WorldView::OnSwitchTo() { if (m_viewController) m_viewController->Activated(); + Pi::input->PushInputFrame(&InputBindings); } void WorldView::OnSwitchFrom() { if (m_viewController) m_viewController->Deactivated(); + Pi::input->RemoveInputFrame(&InputBindings); Pi::DrawGUI = true; } @@ -749,10 +751,3 @@ vector3d WorldView::GetTargetIndicatorScreenPosition(const Body *body) const pos += body->GetInterpOrientRelTo(m_cameraContext->GetCameraFrame()) * body->GetTargetIndicatorPosition(); return WorldSpaceToScreenSpace(pos); } - -void WorldView::HandleSDLEvent(SDL_Event &event) -{ - InputBindings.toggleHudMode->CheckSDLEventAndDispatch(&event); - InputBindings.increaseTimeAcceleration->CheckSDLEventAndDispatch(&event); - InputBindings.decreaseTimeAcceleration->CheckSDLEventAndDispatch(&event); -} diff --git a/src/WorldView.h b/src/WorldView.h index f7a886017..7e8aaa5a2 100644 --- a/src/WorldView.h +++ b/src/WorldView.h @@ -30,11 +30,6 @@ namespace Gui { class TexturedQuad; } -namespace KeyBindings { - struct ActionBinding; - struct AxisBinding; -} // namespace KeyBindings - namespace UI { class Widget; class Single; @@ -53,7 +48,6 @@ public: void Draw3D() override; void Draw() override; void SaveToJson(Json &jsonObj) override; - void HandleSDLEvent(SDL_Event &event) override; RefCountedPtr GetCameraContext() const { return m_cameraContext; } @@ -143,13 +137,14 @@ private: Graphics::Drawables::Line3D m_edgeMarker; Graphics::Drawables::Lines m_indicator; - static struct InputBinding { - typedef KeyBindings::ActionBinding ActionBinding; - typedef KeyBindings::AxisBinding AxisBinding; + struct InputBinding : public Input::InputFrame { + using InputFrame::InputFrame; - ActionBinding *toggleHudMode; - ActionBinding *increaseTimeAcceleration; - ActionBinding *decreaseTimeAcceleration; + Action *toggleHudMode; + Action *increaseTimeAcceleration; + Action *decreaseTimeAcceleration; + + void RegisterBindings() override; } InputBindings; }; diff --git a/src/core/GuiApplication.cpp b/src/core/GuiApplication.cpp index 26490bd62..c0d76604e 100644 --- a/src/core/GuiApplication.cpp +++ b/src/core/GuiApplication.cpp @@ -148,6 +148,8 @@ void GuiApplication::HandleEvents() m_input->HandleSDLEvent(event); } + + m_input->DispatchEvents(); } Graphics::Renderer *GuiApplication::StartupRenderer(IniConfig *config, bool hidden) @@ -196,9 +198,9 @@ void GuiApplication::ShutdownRenderer() SDL_QuitSubSystem(SDL_INIT_VIDEO); } -Input *GuiApplication::StartupInput(IniConfig *config) +Input::Manager *GuiApplication::StartupInput(IniConfig *config) { - m_input.reset(new Input(config)); + m_input.reset(new Input::Manager(config)); return m_input.get(); } diff --git a/src/core/GuiApplication.h b/src/core/GuiApplication.h index bccd1b6ab..1bb8a2c48 100644 --- a/src/core/GuiApplication.h +++ b/src/core/GuiApplication.h @@ -22,7 +22,7 @@ public: {} Graphics::Renderer *GetRenderer() { return m_renderer.get(); } - Input *GetInput() { return m_input.get(); } + Input::Manager *GetInput() { return m_input.get(); } PiGui::Instance *GetPiGui() { return m_pigui.Get(); } protected: @@ -35,7 +35,7 @@ protected: Graphics::Renderer *StartupRenderer(IniConfig *config, bool hidden = false); // Call this from your Startup() method - Input *StartupInput(IniConfig *config); + Input::Manager *StartupInput(IniConfig *config); // Call this from your Startup() method PiGui::Instance *StartupPiGui(); @@ -70,7 +70,7 @@ private: Graphics::RenderTarget *CreateRenderTarget(const Graphics::Settings &settings); RefCountedPtr m_pigui; - std::unique_ptr m_input; + std::unique_ptr m_input; std::string m_applicationTitle; diff --git a/src/lua/LuaConsole.cpp b/src/lua/LuaConsole.cpp index 57ab3443a..ec2fef81d 100644 --- a/src/lua/LuaConsole.cpp +++ b/src/lua/LuaConsole.cpp @@ -2,14 +2,15 @@ // Licensed under the terms of the GPL v3. See licenses/GPL-3.txt #include "LuaMetaType.h" +#include "SDL_keycode.h" #include "buildopts.h" #include "FileSystem.h" -#include "KeyBindings.h" #include "LuaConsole.h" #include "LuaManager.h" #include "LuaUtils.h" #include "Pi.h" +#include "sigc++/functors/mem_fun.h" #include "text/TextSupport.h" #include "text/TextureFont.h" #include "ui/Context.h" @@ -42,6 +43,7 @@ static const char CONSOLE_CHUNK_NAME[] = "console"; LuaConsole::LuaConsole() : m_active(false), + m_inputFrame(Pi::input), m_precompletionStatement(), m_completionList() #ifdef REMOTE_LUA_REPL @@ -69,14 +71,31 @@ LuaConsole::LuaConsole() : RegisterAutoexec(); } +REGISTER_INPUT_BINDING(LuaConsole) +{ + auto *group = Pi::input->GetBindingPage("General")->GetBindingGroup("Miscellaneous"); + input->AddActionBinding("BindToggleLuaConsole", group, InputBindings::Action({ SDLK_BACKQUOTE })); +} + +void LuaConsole::SetupBindings() +{ + toggleLuaConsole = m_inputFrame.AddAction("BindToggleLuaConsole"); + toggleLuaConsole->onPressed.connect(sigc::mem_fun(this, &LuaConsole::Toggle)); + Pi::input->PushInputFrame(&m_inputFrame); +} + void LuaConsole::Toggle() { - if (m_active) + Pi::input->RemoveInputFrame(&m_inputFrame); + if (m_active) { Pi::ui->DropLayer(); - else { + m_inputFrame.modal = false; + } else { Pi::ui->NewLayer()->SetInnerWidget(m_container.Get()); Pi::ui->SelectWidget(m_entry); + m_inputFrame.modal = true; } + Pi::input->PushInputFrame(&m_inputFrame); m_active = !m_active; } @@ -133,11 +152,11 @@ static int console_autoexec(lua_State *l) } // set the chunk's _ENV (globals) var - lua_insert(l, -2); // _ENV, code + lua_insert(l, -2); // _ENV, code lua_setupvalue(l, -2, 1); // code lua_pushcfunction(l, &capture_traceback); // traceback, code - lua_insert(l, -2); // code, traceback + lua_insert(l, -2); // code, traceback ret = lua_pcall(l, 0, 0, -2); if (ret != LUA_OK) { @@ -162,11 +181,11 @@ void LuaConsole::RegisterAutoexec() Output("console.lua:\nProblem when registering the autoexec script.\n"); return; } - lua_getfield(L, -1, "Register"); // Register, Event - lua_pushstring(L, "onGameStart"); // "onGameStart", Register, Event - lua_pushlightuserdata(L, this); // console, "onGameStart", Register, Event + lua_getfield(L, -1, "Register"); // Register, Event + lua_pushstring(L, "onGameStart"); // "onGameStart", Register, Event + lua_pushlightuserdata(L, this); // console, "onGameStart", Register, Event lua_pushcclosure(L, console_autoexec, 1); // autoexec, "onGameStart", Register, Event - lua_call(L, 2, 0); // Event + lua_call(L, 2, 0); // Event lua_pop(L, 1); LUA_DEBUG_END(L, 0); @@ -180,7 +199,7 @@ bool LuaConsole::OnKeyDown(const UI::KeyboardEvent &event) switch (event.keysym.sym) { case SDLK_ESCAPE: { // pressing the ESC key will drop our layer, but we still have to make sure we are marked as not active anymore - m_active = false; + Toggle(); break; } case SDLK_UP: @@ -284,14 +303,14 @@ void LuaConsole::UpdateCompletion(const std::string &statement) } else if (expect_symbolname) // Wrong syntax. return; if (*r_str_it != '.' && (!chunks.empty() || *r_str_it != ':')) { // We are out of the expression. - current_begin = r_str_it.base(); // Flag the symbol marking the beginning of the expression. + current_begin = r_str_it.base(); // Flag the symbol marking the beginning of the expression. break; } expect_symbolname = true; // We hit a separator, there should be a symbol name before it. chunks.push(std::string(r_str_it.base(), current_end)); - if (*r_str_it == ':') // If it is a colon, we know chunks is empty so it is incomplete. - method = true; // it must mean that we want to call a method. + if (*r_str_it == ':') // If it is a colon, we know chunks is empty so it is incomplete. + method = true; // it must mean that we want to call a method. current_end = (r_str_it + 1).base(); // +1 in order to point on the CURRENT character. } if (expect_symbolname) // Again, a symbol was expected when we broke out of the loop. @@ -315,7 +334,7 @@ void LuaConsole::UpdateCompletion(const std::string &statement) lua_gettable(l, -2); chunks.pop(); } - + LuaMetaTypeBase::GetNames(m_completionList, chunks.top(), method); if (!m_completionList.empty()) { std::sort(m_completionList.begin(), m_completionList.end()); diff --git a/src/lua/LuaConsole.h b/src/lua/LuaConsole.h index 80939bb14..ca3ee6f01 100644 --- a/src/lua/LuaConsole.h +++ b/src/lua/LuaConsole.h @@ -4,6 +4,7 @@ #ifndef _LUACONSOLE_H #define _LUACONSOLE_H +#include "Input.h" #include "LuaManager.h" #include "RefCounted.h" #include "ui/Widget.h" @@ -20,6 +21,7 @@ public: LuaConsole(); virtual ~LuaConsole(); + void SetupBindings(); void Toggle(); bool IsActive() const { return m_active; } @@ -51,6 +53,8 @@ private: #endif bool m_active; + Input::InputFrame m_inputFrame; + InputBindings::Action *toggleLuaConsole; RefCountedPtr m_container; UI::MultiLineText *m_output; diff --git a/src/lua/LuaEngine.cpp b/src/lua/LuaEngine.cpp index 9a3a92a5b..232be63ed 100644 --- a/src/lua/LuaEngine.cpp +++ b/src/lua/LuaEngine.cpp @@ -9,7 +9,6 @@ #include "Game.h" #include "GameConfig.h" #include "Intro.h" -#include "KeyBindings.h" #include "Lang.h" #include "LuaColor.h" #include "LuaConstants.h" diff --git a/src/lua/LuaInput.cpp b/src/lua/LuaInput.cpp index eb70a850b..919c031c2 100644 --- a/src/lua/LuaInput.cpp +++ b/src/lua/LuaInput.cpp @@ -4,11 +4,14 @@ #include "LuaInput.h" #include "GameConfig.h" #include "Input.h" -#include "KeyBindings.h" +#include "InputBindings.h" #include "Lang.h" #include "LuaObject.h" #include "LuaUtils.h" +#include "LuaWrappable.h" #include "Pi.h" +#include "src/lua.h" + /* * Interface: Input * @@ -28,21 +31,12 @@ static void setup_binding_table(lua_State *l, const char *id, const char *type) lua_setfield(l, -2, "type"); } -static void push_key_binding(lua_State *l, KeyBindings::KeyBinding *kb, const char *binding, const char *description) -{ - if (kb->Enabled()) { - lua_pushstring(l, kb->ToString().c_str()); - lua_setfield(l, -2, binding); - lua_pushstring(l, kb->Description().c_str()); - lua_setfield(l, -2, description); - } -} /* - * Function: GetBindings + * Function: GetBindingPages * * Get a table listing all the current key and axis bindings. * - * > bindings = Input.GetBindings() + * > bindings = Input.GetBindingPages() * * Returns: * @@ -53,24 +47,17 @@ static void push_key_binding(lua_State *l, KeyBindings::KeyBinding *kb, const ch * > bindings = { * > { -- a page * > id = 'CONTROLS', -- the translation key of the page's label + * > type = 'page', * > { -- a group * > id = 'Miscellaneous', -- the translation key of the name of the group + * > type = 'group', * > { -- a binding * > type = 'action', -- the type of binding; can be 'action' or 'axis' - * > id = 'BindToggleLuaConsole', -- the internal ID of the binding; used as a translation key and passed to Input.SetKeyBinding - * > binding1 = 'Key96', -- the first bound key or axis (value stored in config file) - * > bindingDescription1 = '`', -- display text for the first bound key or axis - * > binding2 = 'Key96', -- the second bound key or axis (value stored in config file) - * > bindingDescription2 = '`', -- display text for the second bound key or axis + * > id = 'BindToggleLuaConsole' -- the internal ID of the binding; used as a translation key and passed to Input.SetKeyBinding * > }, * > { -- an axis binding * > type = 'axis', - * > id = 'BindAxisPitch', - * > axis = 'Joy[UUID]/Axis3/DZ0.0/E1.0', -- The joystick binding (value stored in the config file) - * > positive = 'Key96', -- the key bound to the positive half of the axis - * > positiveDescription = '`', -- as normal for key bindings - * > negative = 'Key96', -- the key bound to the negative half of the axis - * > negativeDescription = '`', -- as normal for key bindings + * > id = 'BindAxisPitch' * > } * > -- ... more bindings * > }, @@ -87,12 +74,12 @@ static void push_key_binding(lua_State *l, KeyBindings::KeyBinding *kb, const ch * * permanent */ -static int l_input_get_bindings(lua_State *l) +static int l_input_get_binding_pages(lua_State *l) { LUA_DEBUG_START(l); lua_newtable(l); // [-1] bindings - using namespace KeyBindings; + using namespace InputBindings; int page_idx = 1; for (auto page : Pi::input->GetBindingPages()) { @@ -108,26 +95,9 @@ static int l_input_get_bindings(lua_State *l) for (auto type : group.second.bindings) { lua_pushunsigned(l, binding_idx++); if (type.second == Input::BindingGroup::EntryType::ENTRY_ACTION) { - ActionBinding *ab = Pi::input->GetActionBinding(type.first); - if (!ab) continue; // Should never happen, but include it here for future proofing. setup_binding_table(l, type.first.c_str(), "action"); - - push_key_binding(l, &ab->binding1, "binding1", "bindingDescription1"); - push_key_binding(l, &ab->binding2, "binding2", "bindingDescription2"); } else { - AxisBinding *ab = Pi::input->GetAxisBinding(type.first); - if (!ab) continue; // Should never happen, but include it here for future proofing. setup_binding_table(l, type.first.c_str(), "axis"); - - if (ab->axis.Enabled()) { - lua_pushstring(l, ab->axis.ToString().c_str()); - lua_setfield(l, -2, "axis"); - lua_pushstring(l, ab->axis.Description().c_str()); - lua_setfield(l, -2, "axisDescription"); - } - - push_key_binding(l, &ab->positive, "positive", "positiveDescription"); - push_key_binding(l, &ab->negative, "negative", "negativeDescription"); } // [-3] group, [-2] idx, [-1] binding @@ -138,7 +108,7 @@ static int l_input_get_bindings(lua_State *l) lua_settable(l, -3); } - // [-3] bindings, [-2] idx, [-1] group + // [-3] bindings, [-2] idx, [-1] page lua_settable(l, -3); } @@ -148,16 +118,12 @@ static int l_input_get_bindings(lua_State *l) static int l_input_enable_bindings(lua_State *l) { - KeyBindings::EnableBindings(); - return 0; -} - -static int l_input_disable_bindings(lua_State *l) -{ - KeyBindings::DisableBindings(); + bool enable = lua_gettop(l) > 0 ? lua_toboolean(l, 1) : true; + Pi::input->EnableBindings(enable); return 0; } +#if 0 static int l_input_set_action_binding(lua_State *l) { const char *binding_id = luaL_checkstring(l, 1); @@ -217,10 +183,11 @@ static int l_input_set_axis_binding(lua_State *l) Pi::config->Save(); return 0; } +#endif static int l_input_get_mouse_y_inverted(lua_State *l) { - lua_pushboolean(l, Pi::config->Int("InvertMouseY") != 0); + lua_pushboolean(l, Pi::input->IsMouseYInvert()); return 1; } @@ -237,7 +204,7 @@ static int l_input_set_mouse_y_inverted(lua_State *l) static int l_input_get_joystick_enabled(lua_State *l) { - lua_pushboolean(l, Pi::config->Int("EnableJoystick") != 0); + lua_pushboolean(l, Pi::input->IsJoystickEnabled()); return 1; } @@ -252,6 +219,14 @@ static int l_input_set_joystick_enabled(lua_State *l) return 0; } +static void pi_lua_generic_push(lua_State *l, InputBindings::Action *action) +{ +} + +static void pi_lua_generic_push(lua_State *l, InputBindings::Axis *axis) +{ +} + void LuaInput::Register() { lua_State *l = Lua::manager->GetLuaState(); @@ -260,10 +235,13 @@ void LuaInput::Register() static const luaL_Reg l_methods[] = { { "EnableBindings", l_input_enable_bindings }, - { "DisableBindings", l_input_disable_bindings }, - { "GetBindings", l_input_get_bindings }, - { "SetActionBinding", l_input_set_action_binding }, - { "SetAxisBinding", l_input_set_axis_binding }, + { "GetBindingPages", l_input_get_binding_pages }, +#if 0 // FIXME: actually implement these! + { "GetActionBinding", l_input_get_action_binding }, + { "AddActionBinding", l_input_add_action_binding }, + { "GetAxisBinding", l_input_get_axis_binding }, + { "AddAxisBinding", l_input_add_axis_binding }, +#endif { "GetMouseYInverted", l_input_get_mouse_y_inverted }, { "SetMouseYInverted", l_input_set_mouse_y_inverted }, { "GetJoystickEnabled", l_input_get_joystick_enabled }, diff --git a/src/lua/LuaPiGui.cpp b/src/lua/LuaPiGui.cpp index ea5e4d6b2..c9c199699 100644 --- a/src/lua/LuaPiGui.cpp +++ b/src/lua/LuaPiGui.cpp @@ -1044,6 +1044,7 @@ static int l_pigui_get_axisbinding(lua_State *l) // otherwise actually check the joystick +#if 0 // FIXME implement lua joystick binding auto joysticks = Pi::input->GetJoysticksState(); for (auto js : joysticks) { @@ -1056,6 +1057,7 @@ static int l_pigui_get_axisbinding(lua_State *l) } if (binding.compare("")) break; } +#endif if (!binding.compare("")) lua_pushnil(l); @@ -1099,6 +1101,7 @@ static int l_pigui_get_keybinding(lua_State *l) if (io.KeySuper) mod |= KMOD_GUI; } +#if 0 // FIXME: actually implement axis binding // Check joysticks if no keys are held down if (Pi::input->IsJoystickEnabled() && (key == 0 || (key >= SDLK_LCTRL && key <= SDLK_RGUI))) { auto joysticks = Pi::input->GetJoysticksState(); @@ -1134,6 +1137,8 @@ static int l_pigui_get_keybinding(lua_State *l) binding = "Key" + std::to_string(key); if (mod > 0) binding += "Mod" + std::to_string(mod); } +#endif + if (!binding.compare("")) lua_pushnil(l); else diff --git a/src/ship/PlayerShipController.cpp b/src/ship/PlayerShipController.cpp index 2a3c19a80..324e5519c 100644 --- a/src/ship/PlayerShipController.cpp +++ b/src/ship/PlayerShipController.cpp @@ -6,7 +6,7 @@ #include "Game.h" #include "GameConfig.h" #include "GameSaveError.h" -#include "KeyBindings.h" +#include "Input.h" #include "Pi.h" #include "Player.h" #include "Ship.h" @@ -17,8 +17,37 @@ #include +REGISTER_INPUT_BINDING(PlayerShipController) +{ + using namespace InputBindings; + auto controlsPage = Pi::input->GetBindingPage("ShipControls"); + + auto weaponsGroup = controlsPage->GetBindingGroup("Weapons"); + input->AddActionBinding("BindTargetObject", weaponsGroup, Action({ SDLK_y })); + input->AddActionBinding("BindPrimaryFire", weaponsGroup, Action({ SDLK_SPACE })); + input->AddActionBinding("BindSecondaryFire", weaponsGroup, Action({ SDLK_m })); + + auto flightGroup = controlsPage->GetBindingGroup("ShipOrient"); + input->AddAxisBinding("BindAxisPitch", flightGroup, Axis({}, { SDLK_k }, { SDLK_i })); + input->AddAxisBinding("BindAxisYaw", flightGroup, Axis({}, { SDLK_j }, { SDLK_l })); + input->AddAxisBinding("BindAxisRoll", flightGroup, Axis({}, { SDLK_u }, { SDLK_o })); + input->AddActionBinding("BindKillRot", flightGroup, Action({ SDLK_p }, { SDLK_x })); + input->AddActionBinding("BindToggleRotationDamping", flightGroup, Action({ SDLK_v })); + + auto thrustGroup = controlsPage->GetBindingGroup("ManualControl"); + input->AddAxisBinding("BindAxisThrustForward", thrustGroup, Axis({}, { SDLK_w }, { SDLK_s })); + input->AddAxisBinding("BindAxisThrustUp", thrustGroup, Axis({}, { SDLK_r }, { SDLK_f })); + input->AddAxisBinding("BindAxisThrustLeft", thrustGroup, Axis({}, { SDLK_a }, { SDLK_d })); + input->AddActionBinding("BindThrustLowPower", thrustGroup, Action({ SDLK_LSHIFT })); + + auto speedGroup = controlsPage->GetBindingGroup("SpeedControl"); + input->AddAxisBinding("BindSpeedControl", speedGroup, Axis({}, { SDLK_RETURN }, { SDLK_RSHIFT })); + input->AddActionBinding("BindToggleSetSpeed", speedGroup, Action({ SDLK_v })); +} + PlayerShipController::PlayerShipController() : ShipController(), + InputBindings(Pi::input), m_combatTarget(0), m_navTarget(0), m_setSpeedTarget(0), @@ -44,48 +73,44 @@ PlayerShipController::PlayerShipController() : "You must call PlayerShipController::RegisterInputBindings before initializing a PlayerShipController"); } - m_connRotationDampingToggleKey = InputBindings.toggleRotationDamping->onPress.connect( + InputBindings.RegisterBindings(); + Pi::input->PushInputFrame(&InputBindings); + + m_connRotationDampingToggleKey = InputBindings.toggleRotationDamping->onPressed.connect( sigc::mem_fun(this, &PlayerShipController::ToggleRotationDamping)); - m_fireMissileKey = InputBindings.secondaryFire->onPress.connect( + m_fireMissileKey = InputBindings.secondaryFire->onPressed.connect( sigc::mem_fun(this, &PlayerShipController::FireMissile)); - m_setSpeedMode = InputBindings.toggleSetSpeed->onPress.connect( + m_setSpeedMode = InputBindings.toggleSetSpeed->onPressed.connect( sigc::mem_fun(this, &PlayerShipController::ToggleSetSpeedMode)); } -PlayerShipController::InputBinding PlayerShipController::InputBindings; - -void PlayerShipController::RegisterInputBindings() +void PlayerShipController::InputBinding::RegisterBindings() { - using namespace KeyBindings; - auto controlsPage = Pi::input->GetBindingPage("ShipControls"); + targetObject = AddAction("BindTargetObject"); + primaryFire = AddAction("BindPrimaryFire"); + secondaryFire = AddAction("BindSecondaryFire"); - auto weaponsGroup = controlsPage->GetBindingGroup("Weapons"); - InputBindings.targetObject = Pi::input->AddActionBinding("BindTargetObject", weaponsGroup, ActionBinding(SDLK_y)); - InputBindings.primaryFire = Pi::input->AddActionBinding("BindPrimaryFire", weaponsGroup, ActionBinding(SDLK_SPACE)); - InputBindings.secondaryFire = Pi::input->AddActionBinding("BindSecondaryFire", weaponsGroup, ActionBinding(SDLK_m)); + pitch = AddAxis("BindAxisPitch"); + yaw = AddAxis("BindAxisYaw"); + roll = AddAxis("BindAxisRoll"); - auto flightGroup = controlsPage->GetBindingGroup("ShipOrient"); - InputBindings.pitch = Pi::input->AddAxisBinding("BindAxisPitch", flightGroup, AxisBinding(SDLK_k, SDLK_i)); - InputBindings.yaw = Pi::input->AddAxisBinding("BindAxisYaw", flightGroup, AxisBinding(SDLK_j, SDLK_l)); - InputBindings.roll = Pi::input->AddAxisBinding("BindAxisRoll", flightGroup, AxisBinding(SDLK_u, SDLK_o)); - InputBindings.killRot = Pi::input->AddActionBinding("BindKillRot", flightGroup, ActionBinding(SDLK_p, SDLK_x)); - InputBindings.toggleRotationDamping = Pi::input->AddActionBinding("BindToggleRotationDamping", flightGroup, ActionBinding(SDLK_v)); + killRot = AddAction("BindKillRot"); + toggleRotationDamping = AddAction("BindToggleRotationDamping"); - auto thrustGroup = controlsPage->GetBindingGroup("ManualControl"); - InputBindings.thrustForward = Pi::input->AddAxisBinding("BindAxisThrustForward", thrustGroup, AxisBinding(SDLK_w, SDLK_s)); - InputBindings.thrustUp = Pi::input->AddAxisBinding("BindAxisThrustUp", thrustGroup, AxisBinding(SDLK_r, SDLK_f)); - InputBindings.thrustLeft = Pi::input->AddAxisBinding("BindAxisThrustLeft", thrustGroup, AxisBinding(SDLK_a, SDLK_d)); - InputBindings.thrustLowPower = Pi::input->AddActionBinding("BindThrustLowPower", thrustGroup, ActionBinding(SDLK_LSHIFT)); + thrustForward = AddAxis("BindAxisThrustForward"); + thrustLeft = AddAxis("BindAxisThrustLeft"); + thrustUp = AddAxis("BindAxisThrustUp"); + thrustLowPower = AddAction("BindThrustLowPower"); - auto speedGroup = controlsPage->GetBindingGroup("SpeedControl"); - InputBindings.speedControl = Pi::input->AddAxisBinding("BindSpeedControl", speedGroup, AxisBinding(SDLK_RETURN, SDLK_RSHIFT)); - InputBindings.toggleSetSpeed = Pi::input->AddActionBinding("BindToggleSetSpeed", speedGroup, ActionBinding(SDLK_v)); + speedControl = AddAxis("BindAxisSpeedControl"); + toggleSetSpeed = AddAction("BindToggleSetSpeed"); } PlayerShipController::~PlayerShipController() { + Pi::input->RemoveInputFrame(&InputBindings); m_connRotationDampingToggleKey.disconnect(); m_fireMissileKey.disconnect(); } diff --git a/src/ship/PlayerShipController.h b/src/ship/PlayerShipController.h index cabdc45e5..55209d382 100644 --- a/src/ship/PlayerShipController.h +++ b/src/ship/PlayerShipController.h @@ -6,17 +6,12 @@ #include "Input.h" #include "ShipController.h" -namespace KeyBindings { - struct ActionBinding; - struct AxisBinding; -} // namespace KeyBindings - // autopilot AI + input class PlayerShipController : public ShipController { public: PlayerShipController(); ~PlayerShipController(); - static void RegisterInputBindings(); + Type GetType() override { return PLAYER; } void SaveToJson(Json &jsonObj, Space *s) override; void LoadFromJson(const Json &jsonObj) override; @@ -58,32 +53,32 @@ public: sigc::signal onChangeFlightControlState; private: - static struct InputBinding : public Input::InputFrame { - // We create a local alias for ease of typing these bindings. - typedef KeyBindings::AxisBinding AxisBinding; - typedef KeyBindings::ActionBinding ActionBinding; + struct InputBinding : public Input::InputFrame { + using InputFrame::InputFrame; // Weapons - ActionBinding *targetObject; - ActionBinding *primaryFire; - ActionBinding *secondaryFire; + Action *targetObject; + Action *primaryFire; + Action *secondaryFire; // Flight - AxisBinding *pitch; - AxisBinding *yaw; - AxisBinding *roll; - ActionBinding *killRot; - ActionBinding *toggleRotationDamping; + Axis *pitch; + Axis *yaw; + Axis *roll; + Action *killRot; + Action *toggleRotationDamping; // Manual Control - AxisBinding *thrustForward; - AxisBinding *thrustUp; - AxisBinding *thrustLeft; - ActionBinding *thrustLowPower; + Axis *thrustForward; + Axis *thrustUp; + Axis *thrustLeft; + Action *thrustLowPower; // Speed Control - AxisBinding *speedControl; - ActionBinding *toggleSetSpeed; + Axis *speedControl; + Action *toggleSetSpeed; + + void RegisterBindings() override; } InputBindings; // FIXME: separate the propusion controller from the input system, pass in wanted velocity correction directly. diff --git a/src/ship/Propulsion.cpp b/src/ship/Propulsion.cpp index 41d5e1ff9..5e0d4ad93 100644 --- a/src/ship/Propulsion.cpp +++ b/src/ship/Propulsion.cpp @@ -7,6 +7,7 @@ #include "GameSaveError.h" #include "Object.h" // <- here only for comment in AIFaceDirection (line 320) #include "Pi.h" +#include "Player.h" #include "PlayerShipController.h" void Propulsion::SaveToJson(Json &jsonObj, Space *space) @@ -364,7 +365,7 @@ vector3d Propulsion::AIChangeVelDir(const vector3d &reqdiffvel) if (fabs(diffvel.y) > maxFA.y) diffvel *= maxFA.y / fabs(diffvel.y); if (fabs(diffvel.z) > maxFA.z) diffvel *= maxFA.z / fabs(diffvel.z); - AIChangeVelBy(diffvel); // should always return true because it's already capped? + AIChangeVelBy(diffvel); // should always return true because it's already capped? return m_dBody->GetOrient() * (reqdiffvel - diffvel); // should be remaining diffvel to correct } @@ -385,19 +386,19 @@ double Propulsion::AIFaceUpdir(const vector3d &updir, double av) double frameAccel = maxAccel * Pi::game->GetTimeStep(); vector3d uphead = updir * m_dBody->GetOrient(); // create desired object-space updir - if (uphead.z > 0.99999) return 0; // bail out if facing updir + if (uphead.z > 0.99999) return 0; // bail out if facing updir uphead.z = 0; uphead = uphead.Normalized(); // only care about roll axis double ang = 0.0, dav = 0.0; if (uphead.y < 0.99999999) { - ang = acos(Clamp(uphead.y, -1.0, 1.0)); // scalar angle from head to curhead + ang = acos(Clamp(uphead.y, -1.0, 1.0)); // scalar angle from head to curhead double iangvel = av + calc_ivel_pos(ang, 0.0, maxAccel); // ideal angvel at current time dav = uphead.x > 0 ? -iangvel : iangvel; } double cav = (m_dBody->GetAngVelocity() * m_dBody->GetOrient()).z; // current obj-rel angvel - double diff = (dav - cav) / frameAccel; // find diff between current & desired angvel + double diff = (dav - cav) / frameAccel; // find diff between current & desired angvel SetAngThrusterState(2, diff); return ang; @@ -413,11 +414,11 @@ double Propulsion::AIFaceDirection(const vector3d &dir, double av) double maxAccel = m_angThrust / m_dBody->GetAngularInertia(); // should probably be in stats anyway vector3d head = (dir * m_dBody->GetOrient()).Normalized(); // create desired object-space heading - vector3d dav(0.0, 0.0, 0.0); // desired angular velocity + vector3d dav(0.0, 0.0, 0.0); // desired angular velocity double ang = 0.0; if (head.z > -0.99999999) { - ang = acos(Clamp(-head.z, -1.0, 1.0)); // scalar angle from head to curhead + ang = acos(Clamp(-head.z, -1.0, 1.0)); // scalar angle from head to curhead double iangvel = av + calc_ivel_pos(ang, 0.0, maxAccel); // ideal angvel at current time // Normalize (head.x, head.y) to give desired angvel direction @@ -431,13 +432,17 @@ double Propulsion::AIFaceDirection(const vector3d &dir, double av) vector3d diff = is_zero_exact(frameAccel) ? vector3d(0.0) : (dav - cav) / frameAccel; // find diff between current & desired angvel // If the player is pressing a roll key, don't override roll. - // XXX this really shouldn't be here. a better way would be to have a + // HACK this really shouldn't be here. a better way would be to have a // field in Ship describing the wanted angvel adjustment from input. the // baseclass version in Ship would always be 0. the version in Player // would be constructed from user input. that adjustment could then be // considered by this method when computing the required change - if (m_dBody->IsType(Object::PLAYER) && (PlayerShipController::InputBindings.roll->IsActive())) - diff.z = GetAngThrusterState().z; + if (m_dBody->IsType(Object::PLAYER)) { + auto *playerController = static_cast(m_dBody)->GetPlayerController(); + if (playerController->InputBindings.roll->IsActive()) + diff.z = GetAngThrusterState().z; + } + SetAngThrusterState(diff); return ang; } diff --git a/src/ship/ShipViewController.cpp b/src/ship/ShipViewController.cpp index e4b4deacb..94173fd07 100644 --- a/src/ship/ShipViewController.cpp +++ b/src/ship/ShipViewController.cpp @@ -5,6 +5,7 @@ #include "CameraController.h" #include "GameSaveError.h" +#include "Input.h" #include "WorldView.h" #include "Pi.h" @@ -17,48 +18,59 @@ namespace { static const float WHEEL_SENSITIVITY = .05f; // Should be a variable in user settings. } // namespace -ShipViewController::InputBinding ShipViewController::InputBindings; +REGISTER_INPUT_BINDING(ShipViewController) +{ + using namespace InputBindings; + + Input::BindingGroup *group = input->GetBindingPage("ShipView")->GetBindingGroup("GeneralViewControls"); + + input->AddAxisBinding("BindCameraRoll", group, Axis({}, { SDLK_KP_1 }, { SDLK_KP_3 })); + input->AddAxisBinding("BindCameraPitch", group, Axis({}, { SDLK_KP_2 }, { SDLK_KP_8 })); + input->AddAxisBinding("BindCameraYaw", group, Axis({}, { SDLK_KP_4 }, { SDLK_KP_6 })); + input->AddAxisBinding("BindViewZoom", group, Axis({}, { SDLK_EQUALS }, { SDLK_MINUS })); + + input->AddAxisBinding("BindLookYaw", group, Axis()); + input->AddAxisBinding("BindLookPitch", group, Axis()); + + input->AddActionBinding("BindFrontCamera", group, Action({ SDLK_KP_8 }, { SDLK_UP })); + input->AddActionBinding("BindRearCamera", group, Action({ SDLK_KP_2 }, { SDLK_DOWN })); + input->AddActionBinding("BindLeftCamera", group, Action({ SDLK_KP_4 }, { SDLK_LEFT })); + input->AddActionBinding("BindRightCamera", group, Action({ SDLK_KP_6 }, { SDLK_RIGHT })); + input->AddActionBinding("BindTopCamera", group, Action({ SDLK_KP_9 })); + input->AddActionBinding("BindBottomCamera", group, Action({ SDLK_KP_3 })); + + input->AddActionBinding("BindCycleCameraMode", group, Action({ SDLK_F1, SDLK_LCTRL })); + input->AddActionBinding("BindResetCamera", group, Action({ SDLK_HOME })); +} void ShipViewController::InputBinding::RegisterBindings() { - using namespace KeyBindings; + cameraRoll = AddAxis("BindCameraRoll"); + cameraPitch = AddAxis("BindCameraPitch"); + cameraYaw = AddAxis("BindCameraYaw"); + cameraZoom = AddAxis("BindViewZoom"); - Input::BindingPage *page = Pi::input->GetBindingPage("ShipView"); - Input::BindingGroup *group; + lookYaw = AddAxis("BindLookYaw"); + lookPitch = AddAxis("BindLookPitch"); -#define BINDING_GROUP(n) group = page->GetBindingGroup(#n); -#define KEY_BINDING(n, id, k1, k2) \ - n = \ - Pi::input->AddActionBinding(id, group, ActionBinding(k1, k2)); \ - actions.push_back(n); -#define AXIS_BINDING(n, id, k1, k2) \ - n = \ - Pi::input->AddAxisBinding(id, group, AxisBinding(k1, k2)); \ - axes.push_back(n); + frontCamera = AddAction("BindFrontCamera"); + rearCamera = AddAction("BindRearCamera"); + leftCamera = AddAction("BindLeftCamera"); + rightCamera = AddAction("BindRightCamera"); + topCamera = AddAction("BindTopCamera"); + bottomCamera = AddAction("BindBottomCamera"); - BINDING_GROUP(GeneralViewControls) - KEY_BINDING(cycleCameraMode, "BindCycleCameraMode", SDLK_F1, 0) + cycleCameraMode = AddAction("BindCycleCameraMode"); + resetCamera = AddAction("BindResetCamera"); +} - AXIS_BINDING(cameraRoll, "BindCameraRoll", SDLK_KP_1, SDLK_KP_3) - AXIS_BINDING(cameraPitch, "BindCameraPitch", SDLK_KP_2, SDLK_KP_8) - AXIS_BINDING(cameraYaw, "BindCameraYaw", SDLK_KP_4, SDLK_KP_6) - AXIS_BINDING(cameraZoom, "BindViewZoom", SDLK_EQUALS, SDLK_MINUS) - - AXIS_BINDING(lookYaw, "BindLookYaw", 0, 0); - AXIS_BINDING(lookPitch, "BindLookPitch", 0, 0); - - KEY_BINDING(frontCamera, "BindFrontCamera", SDLK_KP_8, SDLK_UP) - KEY_BINDING(rearCamera, "BindRearCamera", SDLK_KP_2, SDLK_DOWN) - KEY_BINDING(leftCamera, "BindLeftCamera", SDLK_KP_4, SDLK_LEFT) - KEY_BINDING(rightCamera, "BindRightCamera", SDLK_KP_6, SDLK_RIGHT) - KEY_BINDING(topCamera, "BindTopCamera", SDLK_KP_9, 0) - KEY_BINDING(bottomCamera, "BindBottomCamera", SDLK_KP_3, 0) - - KEY_BINDING(resetCamera, "BindResetCamera", SDLK_HOME, 0) - -#undef BINDING_GROUP -#undef KEY_BINDING -#undef AXIS_BINDING +ShipViewController::ShipViewController(WorldView *v) : + ViewController(v), + m_camType(CAM_INTERNAL), + headtracker_input_priority(false), + InputBindings(Pi::input) +{ + InputBindings.RegisterBindings(); } void ShipViewController::LoadFromJson(const Json &jsonObj) diff --git a/src/ship/ShipViewController.h b/src/ship/ShipViewController.h index 7cb9284ad..915c5fd9d 100644 --- a/src/ship/ShipViewController.h +++ b/src/ship/ShipViewController.h @@ -5,16 +5,13 @@ #include "CameraController.h" #include "Input.h" -#include "KeyBindings.h" +#include "InputBindings.h" #include "ViewController.h" #include "utils.h" class ShipViewController : public ViewController { public: - ShipViewController(WorldView *v) : - ViewController(v), - m_camType(CAM_INTERNAL), - headtracker_input_priority(false) {} + ShipViewController(WorldView *v); void Update() override; void Activated() override; @@ -63,9 +60,8 @@ public: void LoadFromJson(const Json &jsonObj); void SaveToJson(Json &jsonObj); - static struct InputBinding : public Input::InputFrame { - using Action = KeyBindings::ActionBinding; - using Axis = KeyBindings::AxisBinding; + struct InputBinding : public Input::InputFrame { + using InputFrame::InputFrame; Axis *cameraYaw; Axis *cameraPitch; @@ -85,6 +81,6 @@ public: Action *cycleCameraMode; Action *resetCamera; - virtual void RegisterBindings(); + void RegisterBindings() override; } InputBindings; }; From 4db4d5cb119a79bf35c9d5b763617ae1a9b9bce3 Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Wed, 11 Nov 2020 18:32:04 -0500 Subject: [PATCH 2/7] Expose InputFrame 2.0 to Lua Querying bindings is now exposed to Lua, with the added side effect of slightly simplifying our binding menu control flow. I've refactored the existing binding window to support the new API, although it's a faint shadow of what's possible under the new system. Further work (in another PR) will be required to support binding of multiple key chords from the GUI; this may well be lumped in with the rewrite of the Lua console. --- data/pigui/modules/settings-window.lua | 163 +++++------ src/lua/LuaInput.cpp | 358 ++++++++++++++++++++----- src/lua/LuaInput.h | 12 + src/lua/LuaMetaType.h | 14 +- src/lua/LuaObject.cpp | 1 + src/lua/LuaObject.h | 3 +- src/lua/LuaPiGui.cpp | 158 ++++++----- src/lua/LuaPiGuiInternal.h | 1 + src/lua/LuaSectorView.cpp | 1 + 9 files changed, 470 insertions(+), 241 deletions(-) diff --git a/data/pigui/modules/settings-window.lua b/data/pigui/modules/settings-window.lua index 006002a97..cc66a4487 100644 --- a/data/pigui/modules/settings-window.lua +++ b/data/pigui/modules/settings-window.lua @@ -15,12 +15,46 @@ local linput = Lang.GetResource("input-core") local ui = require 'pigui' local ModalWindow = require 'pigui.libs.modal-win' +local function l18n_key_from_id(str) + return str:gsub("([^A-Z0-9_])([A-Z0-9])", "%1_%2"):upper() +end + -- convert an axis binding style ID to a translation resource identifier local function localize_binding_id(str) -- TODO: avoid reading lines from the "Core" resource (lc) -- it's here to reuse old strings (keyboard bindings for maps in KeyBindings.inc.h) local jsonIndex = str:gsub("([^A-Z0-9_])([A-Z0-9])", "%1_%2"):upper() - return rawget(linput, jsonIndex) or rawget(lc, jsonIndex) or error("NO_JSON: " .. jsonIndex) + return rawget(linput, jsonIndex) or rawget(lc, jsonIndex) or '[NO_JSON] '..jsonIndex +end + +local function get_binding_desc(bind) + if not bind then return end + local axis_names = { lc.X, lc.Y, lc.Z } + if bind.key then + return Input.GetKeyName(bind.key) + elseif bind.joystick and bind.hat then + return Input.GetJoystickName(bind.joystick) .. lc.HAT .. bind.hat .. lc.DIRECTION .. bind.direction + elseif bind.joystick and bind.button then + return Input.GetJoystickName(bind.joystick) .. lc.BUTTON .. bind.button + elseif bind.joystick and bind.axis then + return (bind.direction < 0 and "-" or "") .. Input.GetJoystickName(bind.joystick) .. ' AXIS ' .. (axis_names[bind.axis + 1] or tostring(bind.axis)) + elseif bind.mouse then + return "MOUSE" .. bind.mouse -- FIXME + end +end + +-- Get a localized name for a key chord to display on a binding +local function get_chord_desc(chord) + if not chord.enabled then return "" end + + local str = get_binding_desc(chord.activator) + local mod1 = chord.modifier1 + local mod2 = chord.modifier2 + + if mod1 then str = str .. " + " .. get_binding_desc(mod1) end + if mod2 then str = str .. " + " .. get_binding_desc(mod2) end + + return str end local player = nil @@ -41,7 +75,7 @@ local optionsWinSize = Vector2(ui.screenWidth * 0.4, ui.screenHeight * 0.6) local showTab = 'video' local binding_pages -local keyCaptureId +local keyCaptureBind local keyCaptureNum local function combo(label, selected, items, tooltip) @@ -278,66 +312,46 @@ end local captureBindingWindow captureBindingWindow = ModalWindow.New("CaptureBinding", function() - local info - - for _,page in pairs(binding_pages) do - for _,group in pairs(page) do - if group.id then - for _,i in pairs(group) do - if i.id == keyCaptureId then - info = i - end - end - end - end - end + local info = keyCaptureBind ui.text(localize_binding_id(info.id)) ui.text(lui.PRESS_A_KEY_OR_CONTROLLER_BUTTON) - if info.type == 'action' then - local desc - if keyCaptureNum == 1 then desc = info.bindingDescription1 - else desc = info.bindingDescription2 end - desc = desc or '' - ui.text(desc) + if info.type == 'Action' then + local desc = keyCaptureNum == 1 and info.binding or info.binding2 + ui.text(desc.enabled and get_chord_desc(desc) or '') - local bindingKey = Engine.pigui.GetKeyBinding() - local setBinding = false - if(bindingKey and keyCaptureNum==1 and bindingKey~=info.binding1) or (bindingKey and keyCaptureNum==2 and bindingKey~=info.binding2) then setBinding = true end - - if setBinding and keyCaptureNum == 1 then Input.SetActionBinding(info.id, bindingKey, info.binding2) - elseif setBinding and keyCaptureNum==2 then Input.SetActionBinding(info.id, info.binding1, bindingKey) + local set, bindingKey = Engine.pigui.GetKeyBinding() + if set then + if keyCaptureNum == 1 then info.binding = bindingKey + else info.binding2 = bindingKey end end - elseif info.type == 'axis' then + elseif info.type == 'Axis' then local desc - if keyCaptureNum == 1 then desc = info.axisDescription - elseif keyCaptureNum == 2 then desc = info.positiveDescription - else desc = info.negativeDescription end - desc = desc or '' + if keyCaptureNum == 1 then + desc = get_binding_desc(info.axis) or '' + else + desc = keyCaptureNum == 2 and info.positive or info.negative + desc = desc.enabled and get_chord_desc(desc) or '' + end ui.text(desc) if keyCaptureNum == 1 then - local bindingAxis = Engine.pigui.GetAxisBinding() - - if bindingAxis and bindingAxis~=info.axis then - Input.SetAxisBinding(info.id, bindingAxis, info.positive, info.negative) - end + local set, bindingAxis = Engine.pigui.GetAxisBinding() + if set then info.axis = bindingAxis end elseif keyCaptureNum == 2 then - local bindingKey = Engine.pigui.GetKeyBinding() - - if bindingKey and bindingKey ~= info.positive then - Input.SetAxisBinding(info.id, info.axis, bindingKey, info.negative) - end + local set, bindingKey = Engine.pigui.GetKeyBinding() + if set then info.positive = bindingKey end else - local bindingKey = Engine.pigui.GetKeyBinding() - if bindingKey and bindingKey ~= info.negative then - Input.SetAxisBinding(info.id, info.axis, info.positive, bindingKey) - end + local set, bindingKey = Engine.pigui.GetKeyBinding() + if set then info.negative = bindingKey end end end - optionTextButton(lui.OK, nil, true, function() captureBindingWindow:close() end) + optionTextButton(lui.OK, nil, true, function() + Input.SaveBinding(info) + captureBindingWindow:close() + end) end, function (self, drawPopupFn) ui.setNextWindowPosCenter('Always') ui.withStyleColorsAndVars({["PopupBg"] = Color(20, 20, 80, 230)}, {WindowBorderSize = 1}, drawPopupFn) @@ -394,22 +408,24 @@ local function showLanguageOptions() end local function actionBinding(info) - local bindings = { info.binding1, info.binding2 } - local descs = { info.bindingDescription1, info.bindingDescription2 } + local descs = { + get_chord_desc(info.binding), + get_chord_desc(info.binding2) + } if (ui.collapsingHeader(localize_binding_id(info.id), {})) then ui.columns(3,"##bindings",false) ui.nextColumn() ui.text(linput.TEXT_BINDING) bindingTextButton((descs[1] or '')..'##'..info.id..'1', (descs[1] or ''), true, function() - keyCaptureId = info.id + keyCaptureBind = info keyCaptureNum = 1 captureBindingWindow:open() end) ui.nextColumn() ui.text(linput.TEXT_ALT_BINDING) bindingTextButton((descs[2] or '')..'##'..info.id..'2', (descs[2] or ''), true, function() - keyCaptureId = info.id + keyCaptureBind = info keyCaptureNum = 2 captureBindingWindow:open() end) @@ -418,51 +434,38 @@ local function actionBinding(info) end local function axisBinding(info) - local bindings = { info.axis, info.positive, info.negative } - local descs = { info.axisDescription, info.positiveDescription, info.negativeDescription } + local axis, positive, negative = info.axis, info.positive, info.negative + local descs = { get_binding_desc(axis), get_chord_desc(positive), get_chord_desc(negative) } + if (ui.collapsingHeader(localize_binding_id(info.id), {})) then ui.columns(3,"##axisjoybindings",false) ui.text("Axis:") ui.nextColumn() bindingTextButton((descs[1] or '')..'##'..info.id..'axis', (descs[1] or ''), true, function() - keyCaptureId = info.id + keyCaptureBind = info keyCaptureNum = 1 captureBindingWindow:open() end) ui.nextColumn() - if info.axis then - local c, inverted, deadzone, sensitivity = nil, info.axis:sub(1,1) == "-", - tonumber(info.axis:match"/DZ(%d+%.%d*)" or 0) * 100, - tonumber(info.axis:match"/E(%d+%.%d*)" or 1) * 100 - local axis = info.axis:match("Joy[0-9a-f]+/Axis%d+") - local function set_axis() - local _ax = (inverted and "-" or "") .. axis .. "/DZ" .. deadzone / 100.0 .. "/E" .. sensitivity / 100.0 - Input.SetAxisBinding(info.id, _ax, info.positive, info.negative) - end + if axis then + local c, inverted = nil, axis.direction < 0 c,inverted = ui.checkbox("Inverted##"..info.id, inverted, linput.TEXT_INVERT_AXIS) - set_axis() - ui.nextColumn() - ui.nextColumn() - c, deadzone = slider("Deadzone##"..info.id, deadzone, 0, 100, linput.TEXT_AXIS_DEADZONE) - set_axis() - ui.nextColumn() - c, sensitivity = slider("Sensitivity##"..info.id, sensitivity, 0, 100, linput.TEXT_AXIS_SENSITIVITY) - set_axis() + if c then axis.direction = inverted and -1 or 1; info.axis = axis end end + -- new row ui.nextColumn() - ui.columns(3,"##axiskeybindings",false) ui.text(linput.TEXT_KEY_BINDINGS) ui.nextColumn() ui.text(linput.TEXT_KEY_POSITIVE) bindingTextButton((descs[2] or '')..'##'..info.id..'positive', (descs[2] or ''), true, function() - keyCaptureId = info.id + keyCaptureBind = info keyCaptureNum = 2 captureBindingWindow:open() end) ui.nextColumn() ui.text(linput.TEXT_KEY_NEGATIVE) bindingTextButton((descs[3] or '')..'##'..info.id..'negative', (descs[3] or ''), true, function() - keyCaptureId = info.id + keyCaptureBind = info keyCaptureNum = 3 captureBindingWindow:open() end) @@ -475,7 +478,7 @@ local function showControlsOptions() local mouseYInvert = Input.GetMouseYInverted() local joystickEnabled = Input.GetJoystickEnabled() - binding_pages = Input.GetBindings() + binding_pages = Input.GetBindingPages() local c c,mouseYInvert = checkbox(lui.INVERT_MOUSE_Y, mouseYInvert) @@ -497,11 +500,11 @@ local function showControlsOptions() ui.text(localize_binding_id("Group" .. group.id)) end) ui.separator() - for _,info in ipairs(group) do - if info.type == 'action' then - actionBinding(info) - elseif info.type == 'axis' then - axisBinding(info) + for _,binding in ipairs(group) do + if binding.type == 'Action' then + actionBinding(binding) + elseif binding.type == 'Axis' then + axisBinding(binding) end end end diff --git a/src/lua/LuaInput.cpp b/src/lua/LuaInput.cpp index 919c031c2..c0a7819fd 100644 --- a/src/lua/LuaInput.cpp +++ b/src/lua/LuaInput.cpp @@ -6,11 +6,112 @@ #include "Input.h" #include "InputBindings.h" #include "Lang.h" +#include "LuaMetaType.h" #include "LuaObject.h" #include "LuaUtils.h" #include "LuaWrappable.h" #include "Pi.h" -#include "src/lua.h" + +#include "SDL_keyboard.h" +#include + +using namespace InputBindings; + +// Proxy object for an action binding +// TODO: push the actual action pointer to lua +struct LuaInputAction : public LuaWrappable { + LuaInputAction(std::string _id) : + id(_id) {} + std::string id; + + const char *getType() const { return "Action"; } + KeyChord getBinding() const { return getAction()->binding; } + KeyChord getBinding2() const { return getAction()->binding2; } + + void setBinding(KeyChord &bind) + { + auto *action = getAction(); + if (action->binding != bind) { + action->binding = bind; + Pi::input->MarkBindingsDirty(); + } + } + + void setBinding2(KeyChord &bind) + { + auto *action = getAction(); + if (action->binding2 != bind) { + action->binding2 = bind; + Pi::input->MarkBindingsDirty(); + } + } + + Action *getAction() const { return Pi::input->GetActionBinding(id); } +}; + +// Proxy object for an axis binding +// TODO: push the actual axis pointer to lua +struct LuaInputAxis : public LuaWrappable { + LuaInputAxis(std::string _id) : + id(_id) {} + std::string id; + + const char *getType() const { return "Axis"; } + JoyAxis getAxisBinding() const { return getAxis()->axis; } + KeyChord getNegative() const { return getAxis()->negative; } + KeyChord getPositive() const { return getAxis()->positive; } + + void setAxisBinding(JoyAxis &axis) + { + getAxis()->axis = axis; + Pi::input->MarkBindingsDirty(); + } + + void setNegative(KeyChord &chord) + { + auto axis = getAxis(); + if (axis->negative != chord) { + axis->negative = chord; + Pi::input->MarkBindingsDirty(); + } + } + + void setPositive(KeyChord &chord) + { + auto axis = getAxis(); + if (axis->positive != chord) { + axis->positive = chord; + Pi::input->MarkBindingsDirty(); + } + } + + Axis *getAxis() const { return Pi::input->GetAxisBinding(id); } +}; + +template +static int lua_deleter(lua_State *l) +{ + T *ref = LuaPull(l, 1); + ref->~T(); + return 0; +} + +#define GENERIC_COPY_OBJ_DEF(Typename) \ + template <> \ + const char *LuaObject::s_type = #Typename; \ + inline void pi_lua_generic_pull(lua_State *l, int index, Typename &out) \ + { \ + assert(l == Lua::manager->GetLuaState()); \ + out = *LuaObject::CheckFromLua(index); \ + } \ + inline void pi_lua_generic_push(lua_State *l, const Typename &value) \ + { \ + assert(l == Lua::manager->GetLuaState()); \ + LuaObject::PushToLua(value); \ + } + +GENERIC_COPY_OBJ_DEF(LuaInputAction) +GENERIC_COPY_OBJ_DEF(LuaInputAxis) /* * Interface: Input @@ -51,13 +152,18 @@ static void setup_binding_table(lua_State *l, const char *id, const char *type) * > { -- a group * > id = 'Miscellaneous', -- the translation key of the name of the group * > type = 'group', - * > { -- a binding - * > type = 'action', -- the type of binding; can be 'action' or 'axis' - * > id = 'BindToggleLuaConsole' -- the internal ID of the binding; used as a translation key and passed to Input.SetKeyBinding + * > { -- a LuaInputAction binding + * > type = 'Action', -- the type of binding; can be 'Action' or 'Axis' + * > id = 'BindToggleLuaConsole', -- the internal ID of the binding; used as a translation key and passed to Input.SetKeyBinding + * > binding = { enabled = true, activator = { key = 93 }, modifier1 = nil, modifier2 = nil }, + * > binding2 = { enabled = true, activator = { key = 104 }, modifier1 = { joystick = 2, button = 7 }, modifier2 = nil } * > }, - * > { -- an axis binding - * > type = 'axis', - * > id = 'BindAxisPitch' + * > { -- a LuaInputAxis binding + * > type = 'Axis', + * > id = 'BindAxisPitch', + * > axis = { joystick = 2, axis = 1 }, + * > negative = { enabled = false }, + * > positive = { enabled = false } * > } * > -- ... more bindings * > }, @@ -94,11 +200,10 @@ static int l_input_get_binding_pages(lua_State *l) int binding_idx = 1; for (auto type : group.second.bindings) { lua_pushunsigned(l, binding_idx++); - if (type.second == Input::BindingGroup::EntryType::ENTRY_ACTION) { - setup_binding_table(l, type.first.c_str(), "action"); - } else { - setup_binding_table(l, type.first.c_str(), "axis"); - } + if (type.second == Input::BindingGroup::EntryType::ENTRY_ACTION) + LuaPush(l, LuaInputAction{ type.first }); + else + LuaPush(l, LuaInputAxis{ type.first }); // [-3] group, [-2] idx, [-1] binding lua_settable(l, -3); @@ -123,67 +228,64 @@ static int l_input_enable_bindings(lua_State *l) return 0; } -#if 0 -static int l_input_set_action_binding(lua_State *l) +static int l_input_save_binding(lua_State *l) { - const char *binding_id = luaL_checkstring(l, 1); - const char *binding_config_1 = lua_tostring(l, 2); - const char *binding_config_2 = lua_tostring(l, 3); - KeyBindings::ActionBinding *action = Pi::input->GetActionBinding(binding_id); + LuaInputAction *action = LuaObject::GetFromLua(1); + LuaInputAxis *axis = LuaObject::GetFromLua(1); + + std::ostringstream buffer; + if (action) { + buffer << *action->getAction(); + Pi::config->SetString(action->id, buffer.str()); + Pi::config->Save(); + } else if (axis) { + buffer << *axis->getAxis(); + Pi::config->SetString(axis->id, buffer.str()); + Pi::config->Save(); + } - KeyBindings::KeyBinding kb1, kb2; - if (binding_config_1) { - if (!KeyBindings::KeyBinding::FromString(binding_config_1, kb1)) - return luaL_error(l, "invalid first key binding given to Input.SetKeyBinding"); - } else - kb1.Clear(); - if (binding_config_2) { - if (!KeyBindings::KeyBinding::FromString(binding_config_2, kb2)) - return luaL_error(l, "invalid second key binding given to Input.SetKeyBinding"); - } else - kb2.Clear(); - action->binding1 = kb1; - action->binding2 = kb2; - Pi::config->SetString(binding_id, action->ToString()); - Pi::config->Save(); return 0; } -static int l_input_set_axis_binding(lua_State *l) +static int l_input_get_key_name(lua_State *l) { - const char *binding_id = luaL_checkstring(l, 1); - const char *binding_config_axis = lua_tostring(l, 2); - const char *binding_config_positive = lua_tostring(l, 3); - const char *binding_config_negative = lua_tostring(l, 4); - KeyBindings::AxisBinding *binding = Pi::input->GetAxisBinding(binding_id); - - KeyBindings::JoyAxisBinding ab; - if (binding_config_axis) { - if (!KeyBindings::JoyAxisBinding::FromString(binding_config_axis, ab)) - return luaL_error(l, "invalid axis binding given to Input.SetKeyBinding"); - } else - ab.Clear(); - - KeyBindings::KeyBinding kb1, kb2; - if (binding_config_positive) { - if (!KeyBindings::KeyBinding::FromString(binding_config_positive, kb1)) - return luaL_error(l, "invalid first key binding given to Input.SetKeyBinding"); - } else - kb1.Clear(); - if (binding_config_negative) { - if (!KeyBindings::KeyBinding::FromString(binding_config_negative, kb2)) - return luaL_error(l, "invalid second key binding given to Input.SetKeyBinding"); - } else - kb2.Clear(); - - binding->axis = ab; - binding->positive = kb1; - binding->negative = kb2; - Pi::config->SetString(binding_id, binding->ToString()); - Pi::config->Save(); - return 0; + auto name = LuaPull(l, 1); + lua_pushstring(l, SDL_GetKeyName(name)); + return 1; +} + +static int l_input_get_joystick_name(lua_State *l) +{ + auto joystick = LuaPull(l, 1); + lua_pushstring(l, Input::JoystickName(joystick).c_str()); + return 1; +} + +static int l_input_get_action_binding(lua_State *l) +{ + std::string id = luaL_checkstring(l, 1); + auto *binding = Pi::input->GetActionBinding(id); + if (!binding) { + lua_pushnil(l); + return 1; + } + + LuaPush(l, LuaInputAction{ id }); + return 1; +} + +static int l_input_get_axis_binding(lua_State *l) +{ + std::string id = luaL_checkstring(l, 1); + auto *binding = Pi::input->GetAxisBinding(id); + if (!binding) { + lua_pushnil(l); + return 1; + } + + LuaPush(l, LuaInputAxis{ id }); + return 1; } -#endif static int l_input_get_mouse_y_inverted(lua_State *l) { @@ -219,14 +321,105 @@ static int l_input_set_joystick_enabled(lua_State *l) return 0; } -static void pi_lua_generic_push(lua_State *l, InputBindings::Action *action) +void pi_lua_generic_push(lua_State *l, InputBindings::JoyAxis axis) { + if (!axis.Enabled()) { + lua_pushnil(l); + return; + } + + LuaTable axisTab(l); + axisTab.Set("joystick", axis.joystickId); + axisTab.Set("axis", axis.axis); + axisTab.Set("direction", axis.direction); } -static void pi_lua_generic_push(lua_State *l, InputBindings::Axis *axis) +void pi_lua_generic_pull(lua_State *l, int index, InputBindings::JoyAxis &out) { + if (!lua_istable(l, index)) + return; + + LuaTable axisTab(l, index); + out.joystickId = axisTab.Get("joystick"); + out.axis = axisTab.Get("axis"); + out.direction = axisTab.Get("direction"); } +void pi_lua_generic_push(lua_State *l, InputBindings::KeyBinding bind) +{ + if (bind.type == KeyBinding::Type::Disabled) { + lua_pushnil(l); + return; + } + + LuaTable bindingTable(l); + if (bind.type == KeyBinding::Type::KeyboardKey) { + bindingTable.Set("key", bind.keycode); + } else if (bind.type == KeyBinding::Type::JoystickButton) { + bindingTable.Set("joystick", bind.joystick.id); + bindingTable.Set("button", bind.joystick.button); + } else if (bind.type == KeyBinding::Type::JoystickHat) { + bindingTable.Set("joystick", bind.joystick.id); + bindingTable.Set("hat", bind.joystick.hat); + bindingTable.Set("dir", bind.joystick.button); + } else { + bindingTable.Set("mouse", bind.mouse.button); + } +} + +void pi_lua_generic_pull(lua_State *l, int index, InputBindings::KeyBinding &out) +{ + out.type = KeyBinding::Type::Disabled; + if (!lua_istable(l, index)) + return; + + LuaTable bindingTable(l, index); + if (bindingTable.Get("key")) { + out.type = KeyBinding::Type::KeyboardKey; + out.keycode = bindingTable.Get("key"); + } else if (bindingTable.Get("button")) { + out.type = KeyBinding::Type::JoystickButton; + out.joystick.id = bindingTable.Get("joystick"); + out.joystick.button = bindingTable.Get("button"); + } else if (bindingTable.Get("hat")) { + out.type = KeyBinding::Type::JoystickHat; + out.joystick.id = bindingTable.Get("joystick"); + out.joystick.hat = bindingTable.Get("hat"); + out.joystick.button = bindingTable.Get("dir"); + } else if (bindingTable.Get("mouse")) { + out.type = KeyBinding::Type::MouseButton; + out.mouse.button = bindingTable.Get("mouse"); + } +} + +void pi_lua_generic_push(lua_State *l, InputBindings::KeyChord chord) +{ + LuaTable chordTab(l); + chordTab.Set("enabled", chord.Enabled()); + if (chord.activator.Enabled()) + chordTab.Set("activator", chord.activator); + + if (chord.modifier1.Enabled()) + chordTab.Set("modifier1", chord.modifier1); + + if (chord.modifier2.Enabled()) + chordTab.Set("modifier2", chord.modifier2); +} + +void pi_lua_generic_pull(lua_State *l, int index, InputBindings::KeyChord &out) +{ + if (!lua_istable(l, index)) return; + LuaTable chordTab(l, index); + if (!chordTab.Get("enabled")) + return; + + out.activator = chordTab.Get("activator"); + out.modifier1 = chordTab.Get("modifier1"); + out.modifier2 = chordTab.Get("modifier2"); +} + +static LuaMetaType s_inputActionBinding("LuaInputAction"); +static LuaMetaType s_inputAxisBinding("LuaInputAxis"); void LuaInput::Register() { lua_State *l = Lua::manager->GetLuaState(); @@ -236,12 +429,11 @@ void LuaInput::Register() static const luaL_Reg l_methods[] = { { "EnableBindings", l_input_enable_bindings }, { "GetBindingPages", l_input_get_binding_pages }, -#if 0 // FIXME: actually implement these! { "GetActionBinding", l_input_get_action_binding }, - { "AddActionBinding", l_input_add_action_binding }, { "GetAxisBinding", l_input_get_axis_binding }, - { "AddAxisBinding", l_input_add_axis_binding }, -#endif + { "GetKeyName", l_input_get_key_name }, + { "GetJoystickName", l_input_get_joystick_name }, + { "SaveBinding", l_input_save_binding }, { "GetMouseYInverted", l_input_get_mouse_y_inverted }, { "SetMouseYInverted", l_input_set_mouse_y_inverted }, { "GetJoystickEnabled", l_input_get_joystick_enabled }, @@ -258,5 +450,27 @@ void LuaInput::Register() lua_setfield(l, -2, "Input"); lua_pop(l, 1); + s_inputActionBinding.CreateMetaType(l); + s_inputActionBinding.StartRecording() + .AddMember("id", &LuaInputAction::id) + .AddMember("type", &LuaInputAction::getType) + .AddMember("binding", &LuaInputAction::getBinding, &LuaInputAction::setBinding) + .AddMember("binding2", &LuaInputAction::getBinding2, &LuaInputAction::setBinding2); + + lua_pushcfunction(l, lua_deleter); + lua_setfield(l, -2, "__gc"); + s_inputActionBinding.StopRecording(); + + s_inputAxisBinding.CreateMetaType(l); + s_inputAxisBinding.StartRecording() + .AddMember("id", &LuaInputAxis::id) + .AddMember("type", &LuaInputAxis::getType) + .AddMember("axis", &LuaInputAxis::getAxisBinding, &LuaInputAxis::setAxisBinding) + .AddMember("positive", &LuaInputAxis::getPositive, &LuaInputAxis::setPositive) + .AddMember("negative", &LuaInputAxis::getNegative, &LuaInputAxis::setNegative); + lua_pushcfunction(l, lua_deleter); + lua_setfield(l, -2, "__gc"); + s_inputAxisBinding.StopRecording(); + LUA_DEBUG_END(l, 0); } diff --git a/src/lua/LuaInput.h b/src/lua/LuaInput.h index e983a9c8c..def3520a4 100644 --- a/src/lua/LuaInput.h +++ b/src/lua/LuaInput.h @@ -4,8 +4,20 @@ #ifndef LUAINPUT_H #define LUAINPUT_H +struct lua_State; + +namespace InputBindings { + struct KeyChord; + struct JoyAxis; +} // namespace InputBindings + namespace LuaInput { void Register(); } +void pi_lua_generic_pull(lua_State *l, int index, InputBindings::KeyChord &out); +void pi_lua_generic_pull(lua_State *l, int index, InputBindings::JoyAxis &out); +void pi_lua_generic_push(lua_State *l, InputBindings::KeyChord inChord); +void pi_lua_generic_push(lua_State *l, InputBindings::JoyAxis inAxis); + #endif diff --git a/src/lua/LuaMetaType.h b/src/lua/LuaMetaType.h index e9a66f85f..c7a5865d2 100644 --- a/src/lua/LuaMetaType.h +++ b/src/lua/LuaMetaType.h @@ -119,7 +119,7 @@ protected: return getter(L, ptr); } - template + template static int getter_member_fn_wrapper_(lua_State *L) { T *ptr = LuaPull(L, 1); @@ -133,7 +133,7 @@ protected: return luaL_error(L, "Invalid number of arguments for property getter/setter %s", name); if (lua_gettop(L) > 1) { - auto &setter = PullPointerToMember>(L, lua_upvalueindex(3)); + auto &setter = PullPointerToMember>(L, lua_upvalueindex(3)); if (setter != nullptr) { pi_lua_multiple_call(L, 1, ptr, setter); return 0; @@ -366,16 +366,16 @@ public: // Magic to allow binding a const function to Lua. Take care to ensure that you do not // push a const object to lua, or this code will become undefined behavior. - template - LuaMetaType &AddMember(const char *name, const_member_function getter, member_function setter = nullptr) + template + LuaMetaType &AddMember(const char *name, const_member_function getter, member_function setter = nullptr) { return AddMember(name, reinterpret_cast>(getter), setter); } // Bind a pseudo-member to Lua via a member-function getter and setter. // The parameter will automatically be pulled from Lua and passed to the setter. - template - LuaMetaType &AddMember(const char *name, member_function getter, member_function setter = nullptr) + template + LuaMetaType &AddMember(const char *name, member_function getter, member_function setter = nullptr) { lua_State *L = m_lua; GetAttrTable(L, m_index); @@ -383,7 +383,7 @@ public: lua_pushstring(L, (m_typeName + "." + name).c_str()); PushPointerToMember(L, getter); PushPointerToMember(L, setter); - lua_pushcclosure(L, &getter_member_fn_wrapper_, 3); + lua_pushcclosure(L, &getter_member_fn_wrapper_, 3); if (m_protected) lua_pushcclosure(L, &secure_trampoline, 1); diff --git a/src/lua/LuaObject.cpp b/src/lua/LuaObject.cpp index 32950ef29..83a409071 100644 --- a/src/lua/LuaObject.cpp +++ b/src/lua/LuaObject.cpp @@ -3,6 +3,7 @@ #include "LuaObject.h" #include "Json.h" +#include "LuaMetaType.h" #include "LuaUtils.h" #include "PropertiedObject.h" #include "PropertyMap.h" diff --git a/src/lua/LuaObject.h b/src/lua/LuaObject.h index aab4ddc1d..fdbd3afe0 100644 --- a/src/lua/LuaObject.h +++ b/src/lua/LuaObject.h @@ -6,7 +6,6 @@ #include "DeleteEmitter.h" #include "Lua.h" -#include "LuaMetaType.h" #include "LuaPushPull.h" #include "LuaRef.h" #include "LuaUtils.h" @@ -100,6 +99,7 @@ struct SerializerPair { }; class PropertyMap; +class LuaMetaTypeBase; // wrapper baseclass, and extra bits for getting at certain parts of the // LuaObject layer @@ -269,7 +269,6 @@ private: // initial lua type string. defined in a specialisation in the appropriate // .cpp file static const char *s_type; - static LuaMetaType s_metaType; }; // wrapper for a "core" object - one owned by c++ (eg Body). diff --git a/src/lua/LuaPiGui.cpp b/src/lua/LuaPiGui.cpp index c9c199699..62ee96e0b 100644 --- a/src/lua/LuaPiGui.cpp +++ b/src/lua/LuaPiGui.cpp @@ -1,12 +1,14 @@ // Copyright © 2008-2020 Pioneer Developers. See AUTHORS.txt for details // Licensed under the terms of the GPL v3. See licenses/GPL-3.txt +#include "Input.h" #include "LuaPiGuiInternal.h" #include "EnumStrings.h" #include "Game.h" #include "LuaColor.h" #include "LuaConstants.h" +#include "LuaInput.h" #include "LuaUtils.h" #include "LuaVector.h" #include "LuaVector2.h" @@ -1026,59 +1028,92 @@ static int l_pigui_text_colored(lua_State *l) static int l_pigui_get_axisbinding(lua_State *l) { PROFILE_SCOPED() - std::string binding = ""; if (!Pi::input->IsJoystickEnabled()) { lua_pushnil(l); return 1; } - ImGuiIO io = ImGui::GetIO(); - // Escape is used to clear an existing binding // io.KeysDown uses scancodes, but we want to use keycodes. - if (io.KeysDown[SDL_GetScancodeFromKey(SDLK_ESCAPE)]) { - binding = "disabled"; - LuaPush(l, binding); - return 1; + if (ImGui::GetIO().KeysDown[SDL_GetScancodeFromKey(SDLK_ESCAPE)]) { + LuaPush(l, true); + lua_pushnil(l); + return 2; } // otherwise actually check the joystick + const auto &joysticks = Input::GetJoysticks(); + InputBindings::JoyAxis binding = {}; -#if 0 // FIXME implement lua joystick binding - auto joysticks = Pi::input->GetJoysticksState(); + for (const auto &js : joysticks) { + const auto &axes = js.second.axes; + for (size_t axis_num = 0; axis_num < axes.size(); axis_num++) { + float val = axes[axis_num].value; + if (std::abs(val) > 0.25) { + binding.axis = axis_num; + binding.joystickId = js.first; + binding.direction = val > 0.0 ? 1 : -1; + } + } + if (binding.Enabled()) + break; + } - for (auto js : joysticks) { - std::vector axes = js.second.axes; - for (size_t a = 0; a < axes.size(); a++) { - if (axes[a] > 0.25 || axes[a] < -0.25) { - binding = "Joy" + Pi::input->JoystickGUIDString(js.first) + "/Axis" + std::to_string(a); + if (!binding.Enabled()) { + lua_pushnil(l); + return 1; + } + + LuaPush(l, true); + LuaPush(l, binding); + return 2; +} + +// FIXME: at the moment this just grabs the first button that is pressed +static InputBindings::KeyBinding get_joy_button() +{ + auto &joysticks = Input::GetJoysticks(); + + for (const auto &js : joysticks) { + const auto &buttons = js.second.buttons; + const auto &hats = js.second.hats; + for (size_t b = 0; b < buttons.size(); b++) { + if (buttons[b]) + return InputBindings::KeyBinding::JoystickButton(js.first, b); + } + for (size_t h = 0; h < hats.size(); h++) { + if (hats[h]) { + int hatDir = hats[h]; + switch (hatDir) { + case SDL_HAT_LEFT: + case SDL_HAT_RIGHT: + case SDL_HAT_UP: + case SDL_HAT_DOWN: + return InputBindings::KeyBinding::JoystickHat(js.first, h, hatDir); + default: + continue; + } break; } } - if (binding.compare("")) break; } -#endif - if (!binding.compare("")) - lua_pushnil(l); - else - LuaPush(l, binding); - return 1; + return InputBindings::KeyBinding{}; } +// FIXME: implement an "input binding mode" which listens to events from Pi::input to build input chords static int l_pigui_get_keybinding(lua_State *l) { PROFILE_SCOPED() - ImGuiIO io = ImGui::GetIO(); + + InputBindings::KeyChord binding; int key = 0; - int mod = 0; - - std::string binding; + // FIXME: support key chording instead of scanning the list of currently held keys! // pick the first key that's currently held down // should there be a priority? for (int i = 0; i < 512; i++) { - if (io.KeysDown[i]) { + if (ImGui::GetIO().KeysDown[i]) { // io.KeysDown uses scancodes, but we need keycodes. key = SDL_GetKeyFromScancode(static_cast(i)); break; @@ -1087,63 +1122,26 @@ static int l_pigui_get_keybinding(lua_State *l) // Escape is used to clear an existing binding if (key == SDLK_ESCAPE) { - binding = "disabled"; - LuaPush(l, binding); + LuaPush(l, true); + lua_pushnil(l); + return 2; + } + + // Check joysticks if no keys are held down + if (Pi::input->IsJoystickEnabled() && (key == 0 || (key >= SDLK_LCTRL && key <= SDLK_RGUI))) { + binding.activator = get_joy_button(); + } else if (key != 0) { + binding.activator = InputBindings::KeyBinding(key); + } + + if (!binding.Enabled()) { + lua_pushnil(l); return 1; } - // No modifier if the key is a modifier - // These are all in a continous range - if (!(key >= SDLK_LCTRL && key <= SDLK_RGUI)) { - if (io.KeyAlt) mod |= KMOD_ALT; - if (io.KeyShift) mod |= KMOD_SHIFT; - if (io.KeyCtrl) mod |= KMOD_CTRL; - if (io.KeySuper) mod |= KMOD_GUI; - } - -#if 0 // FIXME: actually implement axis binding - // Check joysticks if no keys are held down - if (Pi::input->IsJoystickEnabled() && (key == 0 || (key >= SDLK_LCTRL && key <= SDLK_RGUI))) { - auto joysticks = Pi::input->GetJoysticksState(); - - for (auto js : joysticks) { - std::vector buttons = js.second.buttons; - for (size_t b = 0; b < buttons.size(); b++) { - if (buttons[b]) { - binding = "Joy" + Pi::input->JoystickGUIDString(js.first) + "/Button" + std::to_string(b); - break; - } - } - for (size_t h = 0; h < js.second.hats.size(); h++) { - if (js.second.hats[h]) { - int hatDir = js.second.hats[h]; - switch (hatDir) { - case SDL_HAT_LEFT: - case SDL_HAT_RIGHT: - case SDL_HAT_UP: - case SDL_HAT_DOWN: - binding = "Joy" + Pi::input->JoystickGUIDString(js.first) + "/Hat" + std::to_string(h) + "Dir" + std::to_string(js.second.hats[h]); - break; - default: - continue; - } - break; - } - } - if (binding.compare("")) break; - } - } else if (key != 0) { - // hard coding is bad, but is instantiating a keybinding every frame worse? - binding = "Key" + std::to_string(key); - if (mod > 0) binding += "Mod" + std::to_string(mod); - } -#endif - - if (!binding.compare("")) - lua_pushnil(l); - else - LuaPush(l, binding); - return 1; + LuaPush(l, true); + LuaPush(l, binding); + return 2; } static int l_pigui_add_text(lua_State *l) diff --git a/src/lua/LuaPiGuiInternal.h b/src/lua/LuaPiGuiInternal.h index 5b2e9e626..aa3083c38 100644 --- a/src/lua/LuaPiGuiInternal.h +++ b/src/lua/LuaPiGuiInternal.h @@ -6,6 +6,7 @@ #include "LuaObject.h" #include "LuaPushPull.h" +#include "LuaTable.h" #include "vector2.h" #include "vector3.h" diff --git a/src/lua/LuaSectorView.cpp b/src/lua/LuaSectorView.cpp index 7b8e50083..919b3659f 100644 --- a/src/lua/LuaSectorView.cpp +++ b/src/lua/LuaSectorView.cpp @@ -2,6 +2,7 @@ // Licensed under the terms of the GPL v3. See licenses/GPL-3.txt #include "Game.h" +#include "LuaMetaType.h" #include "LuaObject.h" #include "LuaVector.h" #include "SectorView.h" From ec7cb6d33e7c95addc425ced1def707f9db40a3f Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Wed, 11 Nov 2020 18:34:40 -0500 Subject: [PATCH 3/7] Move translations into input-core Move one legacy translation into ui-core Now bindings without an i18n key will display [NO_JSON] BIND_KEY_NAME instead of crashing. --- data/lang/core/ar.json | 28 ------- data/lang/core/bg.json | 28 ------- data/lang/core/ca.json | 28 ------- data/lang/core/cs.json | 28 ------- data/lang/core/da.json | 28 ------- data/lang/core/de.json | 28 ------- data/lang/core/el.json | 28 ------- data/lang/core/en.json | 100 ------------------------- data/lang/core/eo.json | 28 ------- data/lang/core/es.json | 28 ------- data/lang/core/fr.json | 28 ------- data/lang/core/ga.json | 28 ------- data/lang/core/gd.json | 28 ------- data/lang/core/hr.json | 28 ------- data/lang/core/hu.json | 28 ------- data/lang/core/id.json | 28 ------- data/lang/core/it.json | 28 ------- data/lang/core/lt.json | 28 ------- data/lang/core/nb.json | 28 ------- data/lang/core/nl.json | 28 ------- data/lang/core/pl.json | 28 ------- data/lang/core/pt.json | 28 ------- data/lang/core/pt_BR.json | 28 ------- data/lang/core/ro.json | 28 ------- data/lang/core/ru.json | 28 ------- data/lang/core/sv.json | 28 ------- data/lang/core/tr.json | 28 ------- data/lang/core/zh.json | 28 ------- data/lang/input-core/ar.json | 24 ++++++ data/lang/input-core/bg.json | 24 ++++++ data/lang/input-core/ca.json | 24 ++++++ data/lang/input-core/cs.json | 24 ++++++ data/lang/input-core/da.json | 24 ++++++ data/lang/input-core/de.json | 24 ++++++ data/lang/input-core/el.json | 24 ++++++ data/lang/input-core/en.json | 64 ++++++++++++++++ data/lang/input-core/eo.json | 24 ++++++ data/lang/input-core/es.json | 24 ++++++ data/lang/input-core/fr.json | 24 ++++++ data/lang/input-core/ga.json | 24 ++++++ data/lang/input-core/gd.json | 24 ++++++ data/lang/input-core/hr.json | 24 ++++++ data/lang/input-core/hu.json | 24 ++++++ data/lang/input-core/id.json | 24 ++++++ data/lang/input-core/it.json | 24 ++++++ data/lang/input-core/lt.json | 24 ++++++ data/lang/input-core/nb.json | 24 ++++++ data/lang/input-core/nl.json | 24 ++++++ data/lang/input-core/pl.json | 24 ++++++ data/lang/input-core/pt.json | 24 ++++++ data/lang/input-core/pt_BR.json | 24 ++++++ data/lang/input-core/ro.json | 24 ++++++ data/lang/input-core/ru.json | 24 ++++++ data/lang/input-core/sv.json | 24 ++++++ data/lang/input-core/tr.json | 24 ++++++ data/lang/input-core/zh.json | 24 ++++++ data/lang/ui-core/ar.json | 4 + data/lang/ui-core/bg.json | 4 + data/lang/ui-core/ca.json | 4 + data/lang/ui-core/cs.json | 4 + data/lang/ui-core/da.json | 4 + data/lang/ui-core/de.json | 4 + data/lang/ui-core/el.json | 4 + data/lang/ui-core/en.json | 4 + data/lang/ui-core/eo.json | 4 + data/lang/ui-core/es.json | 4 + data/lang/ui-core/fr.json | 4 + data/lang/ui-core/ga.json | 4 + data/lang/ui-core/gd.json | 4 + data/lang/ui-core/hr.json | 4 + data/lang/ui-core/hu.json | 4 + data/lang/ui-core/id.json | 4 + data/lang/ui-core/it.json | 4 + data/lang/ui-core/lt.json | 4 + data/lang/ui-core/nb.json | 4 + data/lang/ui-core/nl.json | 4 + data/lang/ui-core/pl.json | 4 + data/lang/ui-core/pt.json | 4 + data/lang/ui-core/pt_BR.json | 4 + data/lang/ui-core/ro.json | 4 + data/lang/ui-core/ru.json | 4 + data/lang/ui-core/sv.json | 4 + data/lang/ui-core/tr.json | 4 + data/lang/ui-core/zh.json | 4 + data/pigui/modules/map-sector-view.lua | 2 +- data/pigui/modules/settings-window.lua | 19 +++-- data/pigui/modules/system-view-ui.lua | 2 +- scripts/canonicalise_translations.py | 4 +- src/KeyBindings.inc.h | 56 -------------- src/LangStrings.inc.h | 25 ------- 90 files changed, 839 insertions(+), 949 deletions(-) delete mode 100644 src/KeyBindings.inc.h diff --git a/data/lang/core/ar.json b/data/lang/core/ar.json index adbff6999..57badb5f7 100644 --- a/data/lang/core/ar.json +++ b/data/lang/core/ar.json @@ -103,10 +103,6 @@ "description": "", "message": "بقايا جسم كوكب بني قزم شبه ممتازة" }, - "BUTTON": { - "description": "", - "message": "\" زر \"" - }, "CAMERA_BOTTOM_VIEW": { "description": "", "message": "الكاميرا السفلية" @@ -327,10 +323,6 @@ "description": "", "message": "مكان الوصول" }, - "DIRECTION": { - "description": "Direction", - "message": "\" Dir \"" - }, "DISTANCE_LY": { "description": "", "message": "البعد: %distance{f.2} سنة ضوئية" @@ -495,10 +487,6 @@ "description": "", "message": "Entirely Capitalist - no government welfare provision" }, - "HAT": { - "description": "Hat switch: Is the top button on a joystick", - "message": "\" قبعة\"" - }, "HEADING_LOCK_ANTINORMAL": { "description": "Autopilot direction (perpendicular, down), relative to plane of orbit", "message": "Hold {target} anti-normal" @@ -1231,10 +1219,6 @@ "description": "", "message": "اعادة" }, - "RESET_ORIENTATION_AND_ZOOM": { - "description": "", - "message": "اعادة التوجية و التكبير" - }, "ROBOTS": { "description": "", "message": "الي" @@ -1871,14 +1855,6 @@ "description": "", "message": "\" لا يوجد غلاف جوي\"" }, - "X": { - "description": "keybinding: x axis", - "message": "X" - }, - "Y": { - "description": "keybinding: y axis", - "message": "Y" - }, "YAW": { "description": "", "message": "انعراج" @@ -1895,10 +1871,6 @@ "description": "", "message": "مستعمرة زراعة صغيرة" }, - "Z": { - "description": "keybinding: z axis", - "message": "Z" - }, "ZOOM_IN": { "description": "", "message": "تقريب" diff --git a/data/lang/core/bg.json b/data/lang/core/bg.json index 1b09107e7..0b719f83c 100644 --- a/data/lang/core/bg.json +++ b/data/lang/core/bg.json @@ -103,10 +103,6 @@ "description": "", "message": "Кафяво джудже" }, - "BUTTON": { - "description": "", - "message": "\" Бутон \"" - }, "CAMERA_BOTTOM_VIEW": { "description": "", "message": "Долна камера" @@ -327,10 +323,6 @@ "description": "", "message": "Дестинация" }, - "DIRECTION": { - "description": "Direction", - "message": "\" Пос \"" - }, "DISTANCE_LY": { "description": "", "message": "Разстояние: %distance{f.2} сг" @@ -495,10 +487,6 @@ "description": "", "message": "Изцяло капиталистическа - без намеса от правителството" }, - "HAT": { - "description": "Hat switch: Is the top button on a joystick", - "message": "\" Hat\"" - }, "HEADING_LOCK_ANTINORMAL": { "description": "Autopilot direction (perpendicular, down), relative to plane of orbit", "message": "Задръж {target} анти-нормално" @@ -1231,10 +1219,6 @@ "description": "", "message": "Нулиране" }, - "RESET_ORIENTATION_AND_ZOOM": { - "description": "", - "message": "Нулирай Ориентацията и Мащабирането" - }, "ROBOTS": { "description": "", "message": "Роботи" @@ -1871,14 +1855,6 @@ "description": "", "message": "\" незначителна атмосфера\"" }, - "X": { - "description": "keybinding: x axis", - "message": "X" - }, - "Y": { - "description": "keybinding: y axis", - "message": "Y" - }, "YAW": { "description": "", "message": "Отклонение" @@ -1895,10 +1871,6 @@ "description": "", "message": "Млада фермерска колония." }, - "Z": { - "description": "keybinding: z axis", - "message": "Z" - }, "ZOOM_IN": { "description": "", "message": "Приближи" diff --git a/data/lang/core/ca.json b/data/lang/core/ca.json index bdc770047..f545ab87f 100644 --- a/data/lang/core/ca.json +++ b/data/lang/core/ca.json @@ -103,10 +103,6 @@ "description": "", "message": "Nan marró (objecte subestel·lar)" }, - "BUTTON": { - "description": "", - "message": "\" botó \"" - }, "CAMERA_BOTTOM_VIEW": { "description": "", "message": "Càmera inferior" @@ -327,10 +323,6 @@ "description": "", "message": "Destinació" }, - "DIRECTION": { - "description": "Direction", - "message": "\" Dir \"" - }, "DISTANCE_LY": { "description": "", "message": "Distància: %distance{f.2} al" @@ -495,10 +487,6 @@ "description": "", "message": "Capitalisme salvatge. Cap prestació social del govern." }, - "HAT": { - "description": "Hat switch: Is the top button on a joystick", - "message": "\" Bolet\"" - }, "HEADING_LOCK_ANTINORMAL": { "description": "Autopilot direction (perpendicular, down), relative to plane of orbit", "message": "Orientació anti-normal respecte a {target}" @@ -1231,10 +1219,6 @@ "description": "", "message": "Reiniciar" }, - "RESET_ORIENTATION_AND_ZOOM": { - "description": "", - "message": "Reiniciar orientació i zoom" - }, "ROBOTS": { "description": "", "message": "Robots" @@ -1871,14 +1855,6 @@ "description": "", "message": "\" sense atmosfera significativa\"" }, - "X": { - "description": "keybinding: x axis", - "message": "X" - }, - "Y": { - "description": "keybinding: y axis", - "message": "Y" - }, "YAW": { "description": "", "message": "Guinyada" @@ -1895,10 +1871,6 @@ "description": "", "message": "Colònia agrícola recent." }, - "Z": { - "description": "keybinding: z axis", - "message": "Z" - }, "ZOOM_IN": { "description": "", "message": "Apropar" diff --git a/data/lang/core/cs.json b/data/lang/core/cs.json index 0905ad0b1..f9a5efad9 100644 --- a/data/lang/core/cs.json +++ b/data/lang/core/cs.json @@ -103,10 +103,6 @@ "description": "", "message": "Hnědý trpaslík (podhvězda)" }, - "BUTTON": { - "description": "", - "message": "\" klávesa \"" - }, "CAMERA_BOTTOM_VIEW": { "description": "", "message": "Pohled zdola" @@ -327,10 +323,6 @@ "description": "", "message": "Destinace" }, - "DIRECTION": { - "description": "Direction", - "message": "\" směr \"" - }, "DISTANCE_LY": { "description": "", "message": "Vzdálenost: %distance{f.2} ly" @@ -495,10 +487,6 @@ "description": "", "message": "Naprostý kapitalismus - žádný státní sociální systém" }, - "HAT": { - "description": "Hat switch: Is the top button on a joystick", - "message": "\" klobouk\"" - }, "HEADING_LOCK_ANTINORMAL": { "description": "Autopilot direction (perpendicular, down), relative to plane of orbit", "message": "Hold {target} anti-normal" @@ -1231,10 +1219,6 @@ "description": "", "message": "Reset" }, - "RESET_ORIENTATION_AND_ZOOM": { - "description": "", - "message": "Reset orientace a zoomu" - }, "ROBOTS": { "description": "", "message": "Roboti" @@ -1871,14 +1855,6 @@ "description": "", "message": "\" bez výrazné atmosféry\"" }, - "X": { - "description": "keybinding: x axis", - "message": "X" - }, - "Y": { - "description": "keybinding: y axis", - "message": "Y" - }, "YAW": { "description": "", "message": "Zatáčení" @@ -1895,10 +1871,6 @@ "description": "", "message": "Nová zemědělská kolonie." }, - "Z": { - "description": "keybinding: z axis", - "message": "Z" - }, "ZOOM_IN": { "description": "", "message": "Zvětšit" diff --git a/data/lang/core/da.json b/data/lang/core/da.json index 94ef55bc2..5f6f7b26a 100644 --- a/data/lang/core/da.json +++ b/data/lang/core/da.json @@ -103,10 +103,6 @@ "description": "", "message": "Brun dværg sub-stjerneobjekt" }, - "BUTTON": { - "description": "", - "message": "\" Knap \"" - }, "CAMERA_BOTTOM_VIEW": { "description": "", "message": "Underkamera" @@ -327,10 +323,6 @@ "description": "", "message": "Destinatio" }, - "DIRECTION": { - "description": "Direction", - "message": "\" Retning \"" - }, "DISTANCE_LY": { "description": "", "message": "Afstand: %distance{f.2} ly" @@ -495,10 +487,6 @@ "description": "", "message": "Ren kapitalisme - ingen offentlige velfærdsydelser" }, - "HAT": { - "description": "Hat switch: Is the top button on a joystick", - "message": "\" Hat\"" - }, "HEADING_LOCK_ANTINORMAL": { "description": "Autopilot direction (perpendicular, down), relative to plane of orbit", "message": "Hold {target} anti-normal" @@ -1231,10 +1219,6 @@ "description": "", "message": "Nulstil" }, - "RESET_ORIENTATION_AND_ZOOM": { - "description": "", - "message": "Nulstil Synspunkt og Zoom" - }, "ROBOTS": { "description": "", "message": "Robotter" @@ -1871,14 +1855,6 @@ "description": "", "message": "\" uden nogen betydningsfuld atmosfære\"" }, - "X": { - "description": "keybinding: x axis", - "message": "X" - }, - "Y": { - "description": "keybinding: y axis", - "message": "Y" - }, "YAW": { "description": "", "message": "Sideror" @@ -1895,10 +1871,6 @@ "description": "", "message": "Ung landbrugskoloni." }, - "Z": { - "description": "keybinding: z axis", - "message": "Z" - }, "ZOOM_IN": { "description": "", "message": "Zoom ind" diff --git a/data/lang/core/de.json b/data/lang/core/de.json index 4bd81ea5f..d3641a552 100644 --- a/data/lang/core/de.json +++ b/data/lang/core/de.json @@ -103,10 +103,6 @@ "description": "", "message": "Brauner Zwerg (substellares Objekt)" }, - "BUTTON": { - "description": "", - "message": "\"-Taste \"" - }, "CAMERA_BOTTOM_VIEW": { "description": "", "message": "Untere Ansicht" @@ -327,10 +323,6 @@ "description": "", "message": "Reiseziel" }, - "DIRECTION": { - "description": "Direction", - "message": "\"-Richtung \"" - }, "DISTANCE_LY": { "description": "", "message": "Entfernung: %distance{f.2} Lj" @@ -495,10 +487,6 @@ "description": "", "message": "Rein kapitalistisch – kein staatliches Sozialsystem" }, - "HAT": { - "description": "Hat switch: Is the top button on a joystick", - "message": "\"-Hat\"" - }, "HEADING_LOCK_ANTINORMAL": { "description": "Autopilot direction (perpendicular, down), relative to plane of orbit", "message": "{target} anti-normal halten" @@ -1231,10 +1219,6 @@ "description": "", "message": "Zurücksetzen" }, - "RESET_ORIENTATION_AND_ZOOM": { - "description": "", - "message": "Ansicht und Zoom zurücksetzen" - }, "ROBOTS": { "description": "", "message": "Roboter" @@ -1871,14 +1855,6 @@ "description": "", "message": "\" ohne signifikante Atmosphäre\"" }, - "X": { - "description": "keybinding: x axis", - "message": "X" - }, - "Y": { - "description": "keybinding: y axis", - "message": "Y" - }, "YAW": { "description": "", "message": "Gieren" @@ -1895,10 +1871,6 @@ "description": "", "message": "Neu gegründete landwirtschaftliche Kolonie." }, - "Z": { - "description": "keybinding: z axis", - "message": "Z" - }, "ZOOM_IN": { "description": "", "message": "Vergrößern" diff --git a/data/lang/core/el.json b/data/lang/core/el.json index 5b226c777..36d3287be 100644 --- a/data/lang/core/el.json +++ b/data/lang/core/el.json @@ -103,10 +103,6 @@ "description": "", "message": "Brown dwarf sub-stellar object" }, - "BUTTON": { - "description": "", - "message": "\" Button \"" - }, "CAMERA_BOTTOM_VIEW": { "description": "", "message": "Bottom camera" @@ -327,10 +323,6 @@ "description": "", "message": "Destination" }, - "DIRECTION": { - "description": "Direction", - "message": "\" Dir \"" - }, "DISTANCE_LY": { "description": "", "message": "Distance: %distance{f.2} ly" @@ -495,10 +487,6 @@ "description": "", "message": "Entirely Capitalist - no government welfare provision" }, - "HAT": { - "description": "Hat switch: Is the top button on a joystick", - "message": "\" Hat\"" - }, "HEADING_LOCK_ANTINORMAL": { "description": "Autopilot direction (perpendicular, down), relative to plane of orbit", "message": "Hold {target} anti-normal" @@ -1231,10 +1219,6 @@ "description": "", "message": "Reset" }, - "RESET_ORIENTATION_AND_ZOOM": { - "description": "", - "message": "Reset Orientation and Zoom" - }, "ROBOTS": { "description": "", "message": "Ρομπότ" @@ -1871,14 +1855,6 @@ "description": "", "message": "\" with no significant atmosphere\"" }, - "X": { - "description": "keybinding: x axis", - "message": "X" - }, - "Y": { - "description": "keybinding: y axis", - "message": "Y" - }, "YAW": { "description": "", "message": "Yaw" @@ -1895,10 +1871,6 @@ "description": "", "message": "Young farming colony." }, - "Z": { - "description": "keybinding: z axis", - "message": "Z" - }, "ZOOM_IN": { "description": "", "message": "Zoom in" diff --git a/data/lang/core/en.json b/data/lang/core/en.json index 6f22a90f2..1aa64278a 100644 --- a/data/lang/core/en.json +++ b/data/lang/core/en.json @@ -103,10 +103,6 @@ "description": "", "message": "Brown dwarf sub-stellar object" }, - "BUTTON": { - "description": "", - "message": "\" Button \"" - }, "CAMERA_BOTTOM_VIEW": { "description": "", "message": "Bottom camera" @@ -327,10 +323,6 @@ "description": "", "message": "Destination" }, - "DIRECTION": { - "description": "Direction", - "message": "\" Dir \"" - }, "DISTANCE_LY": { "description": "", "message": "Distance: %distance{f.2} ly" @@ -495,10 +487,6 @@ "description": "", "message": "Entirely Capitalist - no government welfare provision" }, - "HAT": { - "description": "Hat switch: Is the top button on a joystick", - "message": "\" Hat\"" - }, "HEADING_LOCK_ANTINORMAL": { "description": "Autopilot direction (perpendicular, down), relative to plane of orbit", "message": "Hold {target} anti-normal" @@ -651,10 +639,6 @@ "description": "", "message": "Joystick input" }, - "JOY_AXIS": { - "description": "Regarding joystick", - "message": "%{sign}%joyname %axis Axis" - }, "L4L5_DISPLAY_MODE_TOGGLE": { "description": "Either show L4/L5 points or don't", "message": "Switch between Lagrange L4/L5 point display modes." @@ -751,70 +735,6 @@ "description": "", "message": "Manual control mode" }, - "MAP_LOCK_HYPERSPACE_TARGET": { - "description": "", - "message": "Lock Hyperspace Target" - }, - "MAP_TOGGLE_INFO_PANEL": { - "description": "", - "message": "Toggle Info Panel" - }, - "MAP_TOGGLE_SELECTION_FOLLOW_VIEW": { - "description": "", - "message": "Toggle Selection Following View" - }, - "MAP_VIEW_ROTATE_DOWN": { - "description": "", - "message": "Rotate View Down" - }, - "MAP_VIEW_ROTATE_LEFT": { - "description": "", - "message": "Rotate View Left" - }, - "MAP_VIEW_ROTATE_RIGHT": { - "description": "", - "message": "Rotate View Right" - }, - "MAP_VIEW_ROTATE_UP": { - "description": "", - "message": "Rotate View Up" - }, - "MAP_VIEW_SHIFT_BACKWARD": { - "description": "", - "message": "Shift View Backwards" - }, - "MAP_VIEW_SHIFT_DOWN": { - "description": "", - "message": "Shift View Down" - }, - "MAP_VIEW_SHIFT_FORWARD": { - "description": "", - "message": "Shift View Forwards" - }, - "MAP_VIEW_SHIFT_LEFT": { - "description": "", - "message": "Shift View Left" - }, - "MAP_VIEW_SHIFT_RIGHT": { - "description": "", - "message": "Shift View Right" - }, - "MAP_VIEW_SHIFT_UP": { - "description": "", - "message": "Shift View Up" - }, - "MAP_WARP_TO_CURRENT_SYSTEM": { - "description": "", - "message": "Warp to Current System" - }, - "MAP_WARP_TO_HYPERSPACE_TARGET": { - "description": "", - "message": "Warp to Hyperspace Target" - }, - "MAP_WARP_TO_SELECTED_SYSTEM": { - "description": "", - "message": "Warp to Selected System" - }, "MASS": { "description": "", "message": "Mass" @@ -1231,10 +1151,6 @@ "description": "", "message": "Reset" }, - "RESET_ORIENTATION_AND_ZOOM": { - "description": "", - "message": "Reset Orientation and Zoom" - }, "ROBOTS": { "description": "", "message": "Robots" @@ -1707,10 +1623,6 @@ "description": "", "message": "Toggle HUD mode" }, - "TOGGLE_LUA_CONSOLE": { - "description": "", - "message": "Toggle Lua console" - }, "TOGGLE_RADAR_MODE": { "description": "", "message": "Toggle radar mode" @@ -1871,14 +1783,6 @@ "description": "", "message": "\" with no significant atmosphere\"" }, - "X": { - "description": "keybinding: x axis", - "message": "X" - }, - "Y": { - "description": "keybinding: y axis", - "message": "Y" - }, "YAW": { "description": "", "message": "Yaw" @@ -1895,10 +1799,6 @@ "description": "", "message": "Young farming colony." }, - "Z": { - "description": "keybinding: z axis", - "message": "Z" - }, "ZOOM_IN": { "description": "", "message": "Zoom in" diff --git a/data/lang/core/eo.json b/data/lang/core/eo.json index d06f197ef..81d08cd8b 100644 --- a/data/lang/core/eo.json +++ b/data/lang/core/eo.json @@ -103,10 +103,6 @@ "description": "", "message": "Brown dwarf sub-stellar object" }, - "BUTTON": { - "description": "", - "message": "\" Button \"" - }, "CAMERA_BOTTOM_VIEW": { "description": "", "message": "Bottom camera" @@ -327,10 +323,6 @@ "description": "", "message": "Destination" }, - "DIRECTION": { - "description": "Direction", - "message": "\" Dir \"" - }, "DISTANCE_LY": { "description": "", "message": "Distance: %distance{f.2} ly" @@ -495,10 +487,6 @@ "description": "", "message": "Entirely Capitalist - no government welfare provision" }, - "HAT": { - "description": "Hat switch: Is the top button on a joystick", - "message": "\" Hat\"" - }, "HEADING_LOCK_ANTINORMAL": { "description": "Autopilot direction (perpendicular, down), relative to plane of orbit", "message": "Hold {target} anti-normal" @@ -1231,10 +1219,6 @@ "description": "", "message": "Reset" }, - "RESET_ORIENTATION_AND_ZOOM": { - "description": "", - "message": "Reset Orientation and Zoom" - }, "ROBOTS": { "description": "", "message": "Robots" @@ -1871,14 +1855,6 @@ "description": "", "message": "\" with no significant atmosphere\"" }, - "X": { - "description": "keybinding: x axis", - "message": "X" - }, - "Y": { - "description": "keybinding: y axis", - "message": "Y" - }, "YAW": { "description": "", "message": "Yaw" @@ -1895,10 +1871,6 @@ "description": "", "message": "Young farming colony." }, - "Z": { - "description": "keybinding: z axis", - "message": "Z" - }, "ZOOM_IN": { "description": "", "message": "Zoom in" diff --git a/data/lang/core/es.json b/data/lang/core/es.json index d985d7359..889aa0b73 100644 --- a/data/lang/core/es.json +++ b/data/lang/core/es.json @@ -103,10 +103,6 @@ "description": "", "message": "Enana marrón (objeto subestelar)" }, - "BUTTON": { - "description": "", - "message": "\" Botón \"" - }, "CAMERA_BOTTOM_VIEW": { "description": "", "message": "Cámara inferior" @@ -327,10 +323,6 @@ "description": "", "message": "Destino" }, - "DIRECTION": { - "description": "Direction", - "message": "\" Dir \"" - }, "DISTANCE_LY": { "description": "", "message": "Distancia: %distance{f.2} AL" @@ -495,10 +487,6 @@ "description": "", "message": "Capitalista puro - sin intervencion del estado" }, - "HAT": { - "description": "Hat switch: Is the top button on a joystick", - "message": "\" Seta\"" - }, "HEADING_LOCK_ANTINORMAL": { "description": "Autopilot direction (perpendicular, down), relative to plane of orbit", "message": "Fijar anti-normal con {target}" @@ -1231,10 +1219,6 @@ "description": "", "message": "Reiniciar" }, - "RESET_ORIENTATION_AND_ZOOM": { - "description": "", - "message": "Reiniciar orientación y zoom" - }, "ROBOTS": { "description": "", "message": "Robots" @@ -1871,14 +1855,6 @@ "description": "", "message": "\" sin atmósfera significativa\"" }, - "X": { - "description": "keybinding: x axis", - "message": "X" - }, - "Y": { - "description": "keybinding: y axis", - "message": "Y" - }, "YAW": { "description": "", "message": "Guiñada" @@ -1895,10 +1871,6 @@ "description": "", "message": "Colonia agrícola joven." }, - "Z": { - "description": "keybinding: z axis", - "message": "Z" - }, "ZOOM_IN": { "description": "", "message": "Acercar" diff --git a/data/lang/core/fr.json b/data/lang/core/fr.json index 89cfc6341..cecbd1f7c 100644 --- a/data/lang/core/fr.json +++ b/data/lang/core/fr.json @@ -103,10 +103,6 @@ "description": "", "message": "Naine brune sous-stellaire." }, - "BUTTON": { - "description": "", - "message": "\"bouton\"" - }, "CAMERA_BOTTOM_VIEW": { "description": "", "message": "Vue Inférieure" @@ -327,10 +323,6 @@ "description": "", "message": "Destination" }, - "DIRECTION": { - "description": "Direction", - "message": "\" Doc \"" - }, "DISTANCE_LY": { "description": "", "message": "Distance: %distance{f.2} al" @@ -495,10 +487,6 @@ "description": "", "message": "Entièrement capitaliste - pas de gouvernement réel" }, - "HAT": { - "description": "Hat switch: Is the top button on a joystick", - "message": "\"Couv\"" - }, "HEADING_LOCK_ANTINORMAL": { "description": "Autopilot direction (perpendicular, down), relative to plane of orbit", "message": "Maintenir {target} en direction anti-normale" @@ -1231,10 +1219,6 @@ "description": "", "message": "Reset" }, - "RESET_ORIENTATION_AND_ZOOM": { - "description": "", - "message": "Réinitialise l'orientation et le zoom" - }, "ROBOTS": { "description": "", "message": "Robots" @@ -1871,14 +1855,6 @@ "description": "", "message": "\" sans atmosphère significative\"" }, - "X": { - "description": "keybinding: x axis", - "message": "X" - }, - "Y": { - "description": "keybinding: y axis", - "message": "Y" - }, "YAW": { "description": "", "message": "Lacets" @@ -1895,10 +1871,6 @@ "description": "", "message": "Jeune colonie agricole." }, - "Z": { - "description": "keybinding: z axis", - "message": "Z" - }, "ZOOM_IN": { "description": "", "message": "Zoom avant" diff --git a/data/lang/core/ga.json b/data/lang/core/ga.json index dd8d5a508..faec46cc1 100644 --- a/data/lang/core/ga.json +++ b/data/lang/core/ga.json @@ -103,10 +103,6 @@ "description": "", "message": "Abhac donn (rinn fo-réaltach)" }, - "BUTTON": { - "description": "", - "message": "\" Cnaipe \"" - }, "CAMERA_BOTTOM_VIEW": { "description": "", "message": "Ceamara íochtarach" @@ -327,10 +323,6 @@ "description": "", "message": "Ceann scríbe" }, - "DIRECTION": { - "description": "Direction", - "message": "\" Treo \"" - }, "DISTANCE_LY": { "description": "", "message": "Fad: %distance{f.2} sbh" @@ -495,10 +487,6 @@ "description": "", "message": "Caipitlíoch amach is amach - gan aon chóras leasa shóisialaigh" }, - "HAT": { - "description": "Hat switch: Is the top button on a joystick", - "message": "\" Hata\"" - }, "HEADING_LOCK_ANTINORMAL": { "description": "Autopilot direction (perpendicular, down), relative to plane of orbit", "message": "Hold {target} anti-normal" @@ -1231,10 +1219,6 @@ "description": "", "message": "Athshocraigh" }, - "RESET_ORIENTATION_AND_ZOOM": { - "description": "", - "message": "Athshocraigh an Treoshuíomh agus an Zúmáil" - }, "ROBOTS": { "description": "", "message": "Róbait" @@ -1871,14 +1855,6 @@ "description": "", "message": "\" gan atmaisféar arbh fhiú trácht air\"" }, - "X": { - "description": "keybinding: x axis", - "message": "X" - }, - "Y": { - "description": "keybinding: y axis", - "message": "Y" - }, "YAW": { "description": "", "message": "Luascáil" @@ -1895,10 +1871,6 @@ "description": "", "message": "Coilíneacht óg feirmeoireachta." }, - "Z": { - "description": "keybinding: z axis", - "message": "Z" - }, "ZOOM_IN": { "description": "", "message": "Zúmáil isteach" diff --git a/data/lang/core/gd.json b/data/lang/core/gd.json index 564096cea..d3570154f 100644 --- a/data/lang/core/gd.json +++ b/data/lang/core/gd.json @@ -103,10 +103,6 @@ "description": "", "message": "Troich dhonn - nì fo-reultach" }, - "BUTTON": { - "description": "", - "message": "\" Putan \"" - }, "CAMERA_BOTTOM_VIEW": { "description": "", "message": "Camara a' bhuinn" @@ -327,10 +323,6 @@ "description": "", "message": "Ceann-uidhe" }, - "DIRECTION": { - "description": "Direction", - "message": "\"Comhair\"" - }, "DISTANCE_LY": { "description": "", "message": "Astar: %distance{f.2} bl-shol" @@ -495,10 +487,6 @@ "description": "", "message": "Calpach gu tur - gun riaghaltas no sochair" }, - "HAT": { - "description": "Hat switch: Is the top button on a joystick", - "message": "\" Ad\"" - }, "HEADING_LOCK_ANTINORMAL": { "description": "Autopilot direction (perpendicular, down), relative to plane of orbit", "message": "Cum {target} gu h-an àbhaisteach" @@ -1231,10 +1219,6 @@ "description": "", "message": "Ath-shuidhich" }, - "RESET_ORIENTATION_AND_ZOOM": { - "description": "", - "message": "Ath-shuidhich a' chomhair agus an sùm" - }, "ROBOTS": { "description": "", "message": "Robotairean" @@ -1871,14 +1855,6 @@ "description": "", "message": "\" gun àile ceart\"" }, - "X": { - "description": "keybinding: x axis", - "message": "X" - }, - "Y": { - "description": "keybinding: y axis", - "message": "Y" - }, "YAW": { "description": "", "message": "Claonadh" @@ -1895,10 +1871,6 @@ "description": "", "message": "Tuineachadh àiteachais òg." }, - "Z": { - "description": "keybinding: z axis", - "message": "Z" - }, "ZOOM_IN": { "description": "", "message": "Sùm a-steach" diff --git a/data/lang/core/hr.json b/data/lang/core/hr.json index 1bf8d6cc2..68b8f8e1f 100644 --- a/data/lang/core/hr.json +++ b/data/lang/core/hr.json @@ -103,10 +103,6 @@ "description": "", "message": "Smeđi patuljak pod-zvijezdani objekt" }, - "BUTTON": { - "description": "", - "message": "\" Tipka \"" - }, "CAMERA_BOTTOM_VIEW": { "description": "", "message": "Bottom camera" @@ -327,10 +323,6 @@ "description": "", "message": "Cilj" }, - "DIRECTION": { - "description": "Direction", - "message": "\" Smjer \"" - }, "DISTANCE_LY": { "description": "", "message": "Udaljenost: %distance{f.2} sg" @@ -495,10 +487,6 @@ "description": "", "message": "Potpuni kapitalizam - bez vladinih socijalnih davanja" }, - "HAT": { - "description": "Hat switch: Is the top button on a joystick", - "message": "\" Hat\"" - }, "HEADING_LOCK_ANTINORMAL": { "description": "Autopilot direction (perpendicular, down), relative to plane of orbit", "message": "Hold {target} anti-normal" @@ -1231,10 +1219,6 @@ "description": "", "message": "Reset" }, - "RESET_ORIENTATION_AND_ZOOM": { - "description": "", - "message": "Reset Orientation and Zoom" - }, "ROBOTS": { "description": "", "message": "Roboti" @@ -1871,14 +1855,6 @@ "description": "", "message": "\" sa beznačajnom atmosferom\"" }, - "X": { - "description": "keybinding: x axis", - "message": "X" - }, - "Y": { - "description": "keybinding: y axis", - "message": "Y" - }, "YAW": { "description": "", "message": "Skretanje" @@ -1895,10 +1871,6 @@ "description": "", "message": "Mlada poljoprivredna kolonija." }, - "Z": { - "description": "keybinding: z axis", - "message": "Z" - }, "ZOOM_IN": { "description": "", "message": "Povećaj" diff --git a/data/lang/core/hu.json b/data/lang/core/hu.json index d74b8193b..0f31d9bec 100644 --- a/data/lang/core/hu.json +++ b/data/lang/core/hu.json @@ -103,10 +103,6 @@ "description": "", "message": "Barna törpe objektum" }, - "BUTTON": { - "description": "", - "message": "\" Gomb \"" - }, "CAMERA_BOTTOM_VIEW": { "description": "", "message": "Alsó kamera" @@ -327,10 +323,6 @@ "description": "", "message": "Célállomás" }, - "DIRECTION": { - "description": "Direction", - "message": "\" Irány \"" - }, "DISTANCE_LY": { "description": "", "message": "Távolság: %distance{f.2} fényév" @@ -495,10 +487,6 @@ "description": "", "message": "Teljesen kapitalista - kormányintézkedések nélkül" }, - "HAT": { - "description": "Hat switch: Is the top button on a joystick", - "message": "\" Hat\"" - }, "HEADING_LOCK_ANTINORMAL": { "description": "Autopilot direction (perpendicular, down), relative to plane of orbit", "message": "Antinormál irány tartása" @@ -1231,10 +1219,6 @@ "description": "", "message": "Alaphelyzet" }, - "RESET_ORIENTATION_AND_ZOOM": { - "description": "", - "message": "Nézet alaphelyzetbe" - }, "ROBOTS": { "description": "", "message": "Robotok" @@ -1871,14 +1855,6 @@ "description": "", "message": "\" számottevő atmoszféra nélkül\"" }, - "X": { - "description": "keybinding: x axis", - "message": "X" - }, - "Y": { - "description": "keybinding: y axis", - "message": "Y" - }, "YAW": { "description": "", "message": "Kitér" @@ -1895,10 +1871,6 @@ "description": "", "message": "Fiatal farmkolónia" }, - "Z": { - "description": "keybinding: z axis", - "message": "Z" - }, "ZOOM_IN": { "description": "", "message": "Közelítés" diff --git a/data/lang/core/id.json b/data/lang/core/id.json index 72a50ec31..5d24896bd 100644 --- a/data/lang/core/id.json +++ b/data/lang/core/id.json @@ -103,10 +103,6 @@ "description": "", "message": "Brown dwarf sub-stellar object" }, - "BUTTON": { - "description": "", - "message": "\" Button \"" - }, "CAMERA_BOTTOM_VIEW": { "description": "", "message": "Kamera bawah" @@ -327,10 +323,6 @@ "description": "", "message": "Tujuan" }, - "DIRECTION": { - "description": "Direction", - "message": "\" Arah \"" - }, "DISTANCE_LY": { "description": "", "message": "Distance: %distance{f.2} ly" @@ -495,10 +487,6 @@ "description": "", "message": "Entirely Capitalist - no government welfare provision" }, - "HAT": { - "description": "Hat switch: Is the top button on a joystick", - "message": "\" Hat\"" - }, "HEADING_LOCK_ANTINORMAL": { "description": "Autopilot direction (perpendicular, down), relative to plane of orbit", "message": "Hold {target} anti-normal" @@ -1231,10 +1219,6 @@ "description": "", "message": "Reset" }, - "RESET_ORIENTATION_AND_ZOOM": { - "description": "", - "message": "Reset Orientation and Zoom" - }, "ROBOTS": { "description": "", "message": "Robot" @@ -1871,14 +1855,6 @@ "description": "", "message": "\" tanpa atmosfer signifikan\"" }, - "X": { - "description": "keybinding: x axis", - "message": "X" - }, - "Y": { - "description": "keybinding: y axis", - "message": "Y" - }, "YAW": { "description": "", "message": "Yaw" @@ -1895,10 +1871,6 @@ "description": "", "message": "Koloni pertanian muda." }, - "Z": { - "description": "keybinding: z axis", - "message": "Z" - }, "ZOOM_IN": { "description": "", "message": "Zoom in" diff --git a/data/lang/core/it.json b/data/lang/core/it.json index 46fc807bf..8977a5105 100644 --- a/data/lang/core/it.json +++ b/data/lang/core/it.json @@ -103,10 +103,6 @@ "description": "", "message": "Nana Bruna (oggetto substellare)" }, - "BUTTON": { - "description": "", - "message": "\" Pulsante \"" - }, "CAMERA_BOTTOM_VIEW": { "description": "", "message": "Camera inferiore" @@ -327,10 +323,6 @@ "description": "", "message": "Destinazione" }, - "DIRECTION": { - "description": "Direction", - "message": "\" Dir \"" - }, "DISTANCE_LY": { "description": "", "message": "Distanza: %distance{f.2} AL" @@ -495,10 +487,6 @@ "description": "", "message": "Capitalismo perfetto - nessun intervento dello Stato in economia" }, - "HAT": { - "description": "Hat switch: Is the top button on a joystick", - "message": "\" Hat\"" - }, "HEADING_LOCK_ANTINORMAL": { "description": "Autopilot direction (perpendicular, down), relative to plane of orbit", "message": "Mantieni {target} anti-normale" @@ -1231,10 +1219,6 @@ "description": "", "message": "Azzera Zoom" }, - "RESET_ORIENTATION_AND_ZOOM": { - "description": "", - "message": "Reimposta Orientamento e Zoom" - }, "ROBOTS": { "description": "", "message": "Robot" @@ -1871,14 +1855,6 @@ "description": "", "message": "\" con atmosfera insignificante\"" }, - "X": { - "description": "keybinding: x axis", - "message": "X" - }, - "Y": { - "description": "keybinding: y axis", - "message": "Y" - }, "YAW": { "description": "", "message": "Imbardata" @@ -1895,10 +1871,6 @@ "description": "", "message": "Giovane colonia agricola." }, - "Z": { - "description": "keybinding: z axis", - "message": "Z" - }, "ZOOM_IN": { "description": "", "message": "Zoom avanti" diff --git a/data/lang/core/lt.json b/data/lang/core/lt.json index 805d53f86..413481cb1 100644 --- a/data/lang/core/lt.json +++ b/data/lang/core/lt.json @@ -103,10 +103,6 @@ "description": "", "message": "Rudasis nykštukas - subžvaigždinis objektas" }, - "BUTTON": { - "description": "", - "message": "\" Mygtukas \"" - }, "CAMERA_BOTTOM_VIEW": { "description": "", "message": "Kamera iš apačios" @@ -327,10 +323,6 @@ "description": "", "message": "Paskirties vieta" }, - "DIRECTION": { - "description": "Direction", - "message": "\" Katalogas \"" - }, "DISTANCE_LY": { "description": "", "message": "Atstumas: %distance{f.2} šviesm." @@ -495,10 +487,6 @@ "description": "", "message": "Visiškai kapitalistinė - nėra valstybinės socialinės paramos" }, - "HAT": { - "description": "Hat switch: Is the top button on a joystick", - "message": "\" Kepurė \"" - }, "HEADING_LOCK_ANTINORMAL": { "description": "Autopilot direction (perpendicular, down), relative to plane of orbit", "message": "Hold {target} anti-normal" @@ -1231,10 +1219,6 @@ "description": "", "message": "Perkrauti" }, - "RESET_ORIENTATION_AND_ZOOM": { - "description": "", - "message": "Atitaisyti kryptį ir priartinimą" - }, "ROBOTS": { "description": "", "message": "Robotai" @@ -1871,14 +1855,6 @@ "description": "", "message": "\" neturi reikšmingos atmosferos\"" }, - "X": { - "description": "keybinding: x axis", - "message": "X" - }, - "Y": { - "description": "keybinding: y axis", - "message": "Y" - }, "YAW": { "description": "", "message": "Nuokrypis" @@ -1895,10 +1871,6 @@ "description": "", "message": "Jauna žemės ūkio kolonija." }, - "Z": { - "description": "keybinding: z axis", - "message": "Z" - }, "ZOOM_IN": { "description": "", "message": "Priartinti" diff --git a/data/lang/core/nb.json b/data/lang/core/nb.json index 172c47079..b62351979 100644 --- a/data/lang/core/nb.json +++ b/data/lang/core/nb.json @@ -103,10 +103,6 @@ "description": "", "message": "Brown dwarf sub-stellar object" }, - "BUTTON": { - "description": "", - "message": "\" Button \"" - }, "CAMERA_BOTTOM_VIEW": { "description": "", "message": "Bottom camera" @@ -327,10 +323,6 @@ "description": "", "message": "Destinasjon" }, - "DIRECTION": { - "description": "Direction", - "message": "\" Dir \"" - }, "DISTANCE_LY": { "description": "", "message": "Distance: %distance{f.2} ly" @@ -495,10 +487,6 @@ "description": "", "message": "Entirely Capitalist - no government welfare provision" }, - "HAT": { - "description": "Hat switch: Is the top button on a joystick", - "message": "\" Hat\"" - }, "HEADING_LOCK_ANTINORMAL": { "description": "Autopilot direction (perpendicular, down), relative to plane of orbit", "message": "Hold {target} anti-normal" @@ -1231,10 +1219,6 @@ "description": "", "message": "Reset" }, - "RESET_ORIENTATION_AND_ZOOM": { - "description": "", - "message": "Reset Orientation and Zoom" - }, "ROBOTS": { "description": "", "message": "Roboter" @@ -1871,14 +1855,6 @@ "description": "", "message": "\" with no significant atmosphere\"" }, - "X": { - "description": "keybinding: x axis", - "message": "X" - }, - "Y": { - "description": "keybinding: y axis", - "message": "Y" - }, "YAW": { "description": "", "message": "Yaw" @@ -1895,10 +1871,6 @@ "description": "", "message": "Young farming colony." }, - "Z": { - "description": "keybinding: z axis", - "message": "Z" - }, "ZOOM_IN": { "description": "", "message": "Zoom in" diff --git a/data/lang/core/nl.json b/data/lang/core/nl.json index 8c38a3ec3..40e87e6d9 100644 --- a/data/lang/core/nl.json +++ b/data/lang/core/nl.json @@ -103,10 +103,6 @@ "description": "", "message": "Bruine dwerg, sub-stellair object" }, - "BUTTON": { - "description": "", - "message": "\" Knop \"" - }, "CAMERA_BOTTOM_VIEW": { "description": "", "message": "Onderste camera" @@ -327,10 +323,6 @@ "description": "", "message": "Bestemming" }, - "DIRECTION": { - "description": "Direction", - "message": "\" Richting \"" - }, "DISTANCE_LY": { "description": "", "message": "Afstand: %distance{f.2} lj" @@ -495,10 +487,6 @@ "description": "", "message": "Volkomen kapitalistisch - geen overheids-gesubsidieerde welzijnsvoorzieningen" }, - "HAT": { - "description": "Hat switch: Is the top button on a joystick", - "message": "\" Hoed\"" - }, "HEADING_LOCK_ANTINORMAL": { "description": "Autopilot direction (perpendicular, down), relative to plane of orbit", "message": "Behoud {target} anti-normaal" @@ -1231,10 +1219,6 @@ "description": "", "message": "Reset" }, - "RESET_ORIENTATION_AND_ZOOM": { - "description": "", - "message": "Herinstellen Oriëntatie en Zoom" - }, "ROBOTS": { "description": "", "message": "Robots" @@ -1871,14 +1855,6 @@ "description": "", "message": "\" zonder significante atmosfeer\"" }, - "X": { - "description": "keybinding: x axis", - "message": "X" - }, - "Y": { - "description": "keybinding: y axis", - "message": "Y" - }, "YAW": { "description": "", "message": "Gier" @@ -1895,10 +1871,6 @@ "description": "", "message": "Jonge agrarische kolonie." }, - "Z": { - "description": "keybinding: z axis", - "message": "Z" - }, "ZOOM_IN": { "description": "", "message": "Zoom in" diff --git a/data/lang/core/pl.json b/data/lang/core/pl.json index 9e3a10314..51d2b9563 100644 --- a/data/lang/core/pl.json +++ b/data/lang/core/pl.json @@ -103,10 +103,6 @@ "description": "", "message": "Brązowy karzeł" }, - "BUTTON": { - "description": "", - "message": "\" przycisk \"" - }, "CAMERA_BOTTOM_VIEW": { "description": "", "message": "Dolna kamera" @@ -327,10 +323,6 @@ "description": "", "message": "Punkt docelowy" }, - "DIRECTION": { - "description": "Direction", - "message": "\" Dir \"" - }, "DISTANCE_LY": { "description": "", "message": "Dystans: %distance{f.2} lś" @@ -495,10 +487,6 @@ "description": "", "message": "Całkowity Kapitalizm - bez opieki społecznej" }, - "HAT": { - "description": "Hat switch: Is the top button on a joystick", - "message": "\" Hat\"" - }, "HEADING_LOCK_ANTINORMAL": { "description": "Autopilot direction (perpendicular, down), relative to plane of orbit", "message": "Utrzymuj się wzdłuż antynormalnej do {target}" @@ -1231,10 +1219,6 @@ "description": "", "message": "Reset" }, - "RESET_ORIENTATION_AND_ZOOM": { - "description": "", - "message": "Reset orientacji i zbliżenia" - }, "ROBOTS": { "description": "", "message": "Roboty" @@ -1871,14 +1855,6 @@ "description": "", "message": "\" bez znaczącej atmosfery\"" }, - "X": { - "description": "keybinding: x axis", - "message": "X" - }, - "Y": { - "description": "keybinding: y axis", - "message": "Y" - }, "YAW": { "description": "", "message": "Odchylenie poziome" @@ -1895,10 +1871,6 @@ "description": "", "message": "Młoda kolonia rolnicza." }, - "Z": { - "description": "keybinding: z axis", - "message": "Z" - }, "ZOOM_IN": { "description": "", "message": "Zbliż" diff --git a/data/lang/core/pt.json b/data/lang/core/pt.json index a738639ad..57cc6e6f1 100644 --- a/data/lang/core/pt.json +++ b/data/lang/core/pt.json @@ -103,10 +103,6 @@ "description": "", "message": "Objeto sub-estelar anã castanha" }, - "BUTTON": { - "description": "", - "message": "\" Botão \"" - }, "CAMERA_BOTTOM_VIEW": { "description": "", "message": "Câmara inferior" @@ -327,10 +323,6 @@ "description": "", "message": "Destino" }, - "DIRECTION": { - "description": "Direction", - "message": "\" Dir \"" - }, "DISTANCE_LY": { "description": "", "message": "Distância: %distance{f.2} ly" @@ -495,10 +487,6 @@ "description": "", "message": "Completamente Capitalista - não há segurança social do estado" }, - "HAT": { - "description": "Hat switch: Is the top button on a joystick", - "message": "\" Hat\"" - }, "HEADING_LOCK_ANTINORMAL": { "description": "Autopilot direction (perpendicular, down), relative to plane of orbit", "message": "Hold {target} anti-normal" @@ -1231,10 +1219,6 @@ "description": "", "message": "Reiniciar" }, - "RESET_ORIENTATION_AND_ZOOM": { - "description": "", - "message": "Reset Orientation and Zoom" - }, "ROBOTS": { "description": "", "message": "Robôs" @@ -1871,14 +1855,6 @@ "description": "", "message": "\" sem atmosfera significativa\"" }, - "X": { - "description": "keybinding: x axis", - "message": "X" - }, - "Y": { - "description": "keybinding: y axis", - "message": "Y" - }, "YAW": { "description": "", "message": "Yaw" @@ -1895,10 +1871,6 @@ "description": "", "message": "Colónia agrícola jovem." }, - "Z": { - "description": "keybinding: z axis", - "message": "Z" - }, "ZOOM_IN": { "description": "", "message": "Zoom in" diff --git a/data/lang/core/pt_BR.json b/data/lang/core/pt_BR.json index 255488053..19ffc5fbd 100644 --- a/data/lang/core/pt_BR.json +++ b/data/lang/core/pt_BR.json @@ -103,10 +103,6 @@ "description": "", "message": "Objeto sub-estelar anã marrom" }, - "BUTTON": { - "description": "", - "message": "\" Botão \"" - }, "CAMERA_BOTTOM_VIEW": { "description": "", "message": "Câmera inferior" @@ -327,10 +323,6 @@ "description": "", "message": "Destino" }, - "DIRECTION": { - "description": "Direction", - "message": "\" Dir \"" - }, "DISTANCE_LY": { "description": "", "message": "Distância: %distance{f.2} al" @@ -495,10 +487,6 @@ "description": "", "message": "Completamente Capitalista - governo não faz provisões para o bem-estar" }, - "HAT": { - "description": "Hat switch: Is the top button on a joystick", - "message": "\" Chapéu\"" - }, "HEADING_LOCK_ANTINORMAL": { "description": "Autopilot direction (perpendicular, down), relative to plane of orbit", "message": "Hold {target} anti-normal" @@ -1231,10 +1219,6 @@ "description": "", "message": "Reset" }, - "RESET_ORIENTATION_AND_ZOOM": { - "description": "", - "message": "Reiniciar Orientação e Zoom" - }, "ROBOTS": { "description": "", "message": "Robôs" @@ -1871,14 +1855,6 @@ "description": "", "message": "\" sem atmosfera significativa\"" }, - "X": { - "description": "keybinding: x axis", - "message": "X" - }, - "Y": { - "description": "keybinding: y axis", - "message": "Y" - }, "YAW": { "description": "", "message": "Guinada" @@ -1895,10 +1871,6 @@ "description": "", "message": "Jovem colônia agricultora." }, - "Z": { - "description": "keybinding: z axis", - "message": "Z" - }, "ZOOM_IN": { "description": "", "message": "Aproximar zoom" diff --git a/data/lang/core/ro.json b/data/lang/core/ro.json index 5ff6372e5..b5d5b20a0 100644 --- a/data/lang/core/ro.json +++ b/data/lang/core/ro.json @@ -103,10 +103,6 @@ "description": "", "message": "Obiect sub-stelar pitică cenușie" }, - "BUTTON": { - "description": "", - "message": "\" Buton \"" - }, "CAMERA_BOTTOM_VIEW": { "description": "", "message": "Vedere de jos" @@ -327,10 +323,6 @@ "description": "", "message": "Destinație" }, - "DIRECTION": { - "description": "Direction", - "message": "\" Dir \"" - }, "DISTANCE_LY": { "description": "", "message": "Distanța: %distance{f.2} al" @@ -495,10 +487,6 @@ "description": "", "message": "Complet Capitalist - nici o dispoziție pentru prosperitatea guvernului" }, - "HAT": { - "description": "Hat switch: Is the top button on a joystick", - "message": "\" Buton vizualizare\"" - }, "HEADING_LOCK_ANTINORMAL": { "description": "Autopilot direction (perpendicular, down), relative to plane of orbit", "message": "Menține {target} înclinația anti-normală" @@ -1231,10 +1219,6 @@ "description": "", "message": "Resetare" }, - "RESET_ORIENTATION_AND_ZOOM": { - "description": "", - "message": "Resetează Orientare și Focalizare" - }, "ROBOTS": { "description": "", "message": "Roboți" @@ -1871,14 +1855,6 @@ "description": "", "message": "\" fără atmosferă semnificativă\"" }, - "X": { - "description": "keybinding: x axis", - "message": "X" - }, - "Y": { - "description": "keybinding: y axis", - "message": "Y" - }, "YAW": { "description": "", "message": "Girare" @@ -1895,10 +1871,6 @@ "description": "", "message": "Colonie proaspătă de agricultură." }, - "Z": { - "description": "keybinding: z axis", - "message": "Z" - }, "ZOOM_IN": { "description": "", "message": "Focalizează" diff --git a/data/lang/core/ru.json b/data/lang/core/ru.json index 96b51faab..7a122f8c6 100644 --- a/data/lang/core/ru.json +++ b/data/lang/core/ru.json @@ -103,10 +103,6 @@ "description": "", "message": "Коричневый карлик - субзвёздный объект" }, - "BUTTON": { - "description": "", - "message": "\" Кнопка \"" - }, "CAMERA_BOTTOM_VIEW": { "description": "", "message": "Нижний экран" @@ -327,10 +323,6 @@ "description": "", "message": "Пункт назначения" }, - "DIRECTION": { - "description": "Direction", - "message": "\" Напр. \"" - }, "DISTANCE_LY": { "description": "", "message": "Расстояние: %distance{f.2} СвЛ" @@ -495,10 +487,6 @@ "description": "", "message": "Ультракапитализм - отсутствие соцгарантий." }, - "HAT": { - "description": "Hat switch: Is the top button on a joystick", - "message": "\" Hat\"" - }, "HEADING_LOCK_ANTINORMAL": { "description": "Autopilot direction (perpendicular, down), relative to plane of orbit", "message": "Удерживать {target} против нормали" @@ -1231,10 +1219,6 @@ "description": "", "message": "Сброс" }, - "RESET_ORIENTATION_AND_ZOOM": { - "description": "", - "message": "Камера по умолчанию" - }, "ROBOTS": { "description": "", "message": "Роботы" @@ -1871,14 +1855,6 @@ "description": "", "message": "\" с разрежённой атмосферой\"" }, - "X": { - "description": "keybinding: x axis", - "message": "X" - }, - "Y": { - "description": "keybinding: y axis", - "message": "Y" - }, "YAW": { "description": "", "message": "Управление рысканьем" @@ -1895,10 +1871,6 @@ "description": "", "message": "Молодая сельскохозяйственная колония." }, - "Z": { - "description": "keybinding: z axis", - "message": "Z" - }, "ZOOM_IN": { "description": "", "message": "Приблизить камеру" diff --git a/data/lang/core/sv.json b/data/lang/core/sv.json index 97081059d..045c1fce4 100644 --- a/data/lang/core/sv.json +++ b/data/lang/core/sv.json @@ -103,10 +103,6 @@ "description": "", "message": "Brunt dvärgstjärnobjekt" }, - "BUTTON": { - "description": "", - "message": "\" Knapp \"" - }, "CAMERA_BOTTOM_VIEW": { "description": "", "message": "Nedre kamera" @@ -327,10 +323,6 @@ "description": "", "message": "Destination" }, - "DIRECTION": { - "description": "Direction", - "message": "\" Dir \"" - }, "DISTANCE_LY": { "description": "", "message": "Distans: %distance{f.2} ly" @@ -495,10 +487,6 @@ "description": "", "message": "Helt kapitalistiskt - ingen välfärd tillhandahållen från regeringen" }, - "HAT": { - "description": "Hat switch: Is the top button on a joystick", - "message": "\" Hatt\"" - }, "HEADING_LOCK_ANTINORMAL": { "description": "Autopilot direction (perpendicular, down), relative to plane of orbit", "message": "Hold {target} anti-normal" @@ -1231,10 +1219,6 @@ "description": "", "message": "Återställ" }, - "RESET_ORIENTATION_AND_ZOOM": { - "description": "", - "message": "Återställ orientering och zoom" - }, "ROBOTS": { "description": "", "message": "Robotar" @@ -1871,14 +1855,6 @@ "description": "", "message": "\" utan någon nämnbar atmosfär\"" }, - "X": { - "description": "keybinding: x axis", - "message": "X" - }, - "Y": { - "description": "keybinding: y axis", - "message": "Y" - }, "YAW": { "description": "", "message": "Gira" @@ -1895,10 +1871,6 @@ "description": "", "message": "Ung jordbrukskoloni." }, - "Z": { - "description": "keybinding: z axis", - "message": "Z" - }, "ZOOM_IN": { "description": "", "message": "Zooma in" diff --git a/data/lang/core/tr.json b/data/lang/core/tr.json index c9f253b60..42a53f9cc 100644 --- a/data/lang/core/tr.json +++ b/data/lang/core/tr.json @@ -103,10 +103,6 @@ "description": "", "message": "Kahverengi cüce alt-yıldız cismi" }, - "BUTTON": { - "description": "", - "message": "\" Düğme \"" - }, "CAMERA_BOTTOM_VIEW": { "description": "", "message": "Alt kamera" @@ -327,10 +323,6 @@ "description": "", "message": "Hedef" }, - "DIRECTION": { - "description": "Direction", - "message": "\" Yön \"" - }, "DISTANCE_LY": { "description": "", "message": "Uzaklık: %distance{f.2} Iy" @@ -495,10 +487,6 @@ "description": "", "message": "Tamamen Kapitalist - yönetim mal varlığını düzenlemiyor" }, - "HAT": { - "description": "Hat switch: Is the top button on a joystick", - "message": "\" Üst düğme\"" - }, "HEADING_LOCK_ANTINORMAL": { "description": "Autopilot direction (perpendicular, down), relative to plane of orbit", "message": "Ters-dikey yönelimde {target} sabitle" @@ -1231,10 +1219,6 @@ "description": "", "message": "Yeniden başlat" }, - "RESET_ORIENTATION_AND_ZOOM": { - "description": "", - "message": "Yönelim ve Yakınlaştırmayı Sıfırla" - }, "ROBOTS": { "description": "", "message": "Robotlar" @@ -1871,14 +1855,6 @@ "description": "", "message": "\" belli bir atmosferi yok\"" }, - "X": { - "description": "keybinding: x axis", - "message": "X" - }, - "Y": { - "description": "keybinding: y axis", - "message": "Y" - }, "YAW": { "description": "", "message": "Sapma" @@ -1895,10 +1871,6 @@ "description": "", "message": "Genç tarım kolonisi." }, - "Z": { - "description": "keybinding: z axis", - "message": "Z" - }, "ZOOM_IN": { "description": "", "message": "Yakınlaştır" diff --git a/data/lang/core/zh.json b/data/lang/core/zh.json index 6c495dc18..e6254a380 100644 --- a/data/lang/core/zh.json +++ b/data/lang/core/zh.json @@ -103,10 +103,6 @@ "description": "", "message": "褐矮星亚恒星体" }, - "BUTTON": { - "description": "", - "message": "\"按钮\"" - }, "CAMERA_BOTTOM_VIEW": { "description": "", "message": "底部摄像机" @@ -327,10 +323,6 @@ "description": "", "message": "目的地" }, - "DIRECTION": { - "description": "Direction", - "message": "\"目录\"" - }, "DISTANCE_LY": { "description": "", "message": "距离: %distance{f.2} 光年" @@ -495,10 +487,6 @@ "description": "", "message": "完全资本主义 - 政府不提供福利" }, - "HAT": { - "description": "Hat switch: Is the top button on a joystick", - "message": "\"帽子\"" - }, "HEADING_LOCK_ANTINORMAL": { "description": "Autopilot direction (perpendicular, down), relative to plane of orbit", "message": "保持{target}反法线" @@ -1231,10 +1219,6 @@ "description": "", "message": "重置" }, - "RESET_ORIENTATION_AND_ZOOM": { - "description": "", - "message": "重置方向和缩放" - }, "ROBOTS": { "description": "", "message": "机器人" @@ -1871,14 +1855,6 @@ "description": "", "message": "\"没有明显大气\"" }, - "X": { - "description": "keybinding: x axis", - "message": "X" - }, - "Y": { - "description": "keybinding: y axis", - "message": "Y" - }, "YAW": { "description": "", "message": "偏航" @@ -1895,10 +1871,6 @@ "description": "", "message": "新兴的农业殖民地." }, - "Z": { - "description": "keybinding: z axis", - "message": "Z" - }, "ZOOM_IN": { "description": "", "message": "放大" diff --git a/data/lang/input-core/ar.json b/data/lang/input-core/ar.json index a513b9fda..321c8d0f3 100644 --- a/data/lang/input-core/ar.json +++ b/data/lang/input-core/ar.json @@ -143,6 +143,14 @@ "description": "Descriptive name for the View Zoom axis", "message": "View Zoom" }, + "BUTTON": { + "description": "", + "message": "\" زر \"" + }, + "DIRECTION": { + "description": "Direction", + "message": "\" Dir \"" + }, "GROUP_GENERAL_VIEW_CONTROLS": { "description": "Header for the GeneralViewControls input group.", "message": "تحكم الروئية الرئيسي" @@ -170,6 +178,10 @@ "description": "Header for the Weapons input group.", "message": "اسلحة" }, + "HAT": { + "description": "Hat switch: Is the top button on a joystick", + "message": "\" قبعة\"" + }, "PAGE_GENERAL": { "description": "Header for the General input page.", "message": "General Controls" @@ -217,5 +229,17 @@ "TEXT_KEY_POSITIVE": { "description": "Indicates the positive half of a key-binding pair.", "message": "Positive:" + }, + "X": { + "description": "keybinding: x axis", + "message": "X" + }, + "Y": { + "description": "keybinding: y axis", + "message": "Y" + }, + "Z": { + "description": "keybinding: z axis", + "message": "Z" } } diff --git a/data/lang/input-core/bg.json b/data/lang/input-core/bg.json index 38dbe20e8..4ad678d7d 100644 --- a/data/lang/input-core/bg.json +++ b/data/lang/input-core/bg.json @@ -143,6 +143,14 @@ "description": "Descriptive name for the View Zoom axis", "message": "Увеличение на Изглед" }, + "BUTTON": { + "description": "", + "message": "\" Бутон \"" + }, + "DIRECTION": { + "description": "Direction", + "message": "\" Пос \"" + }, "GROUP_GENERAL_VIEW_CONTROLS": { "description": "Header for the GeneralViewControls input group.", "message": "Общи Контроли за Изглед" @@ -170,6 +178,10 @@ "description": "Header for the Weapons input group.", "message": "Оръжия" }, + "HAT": { + "description": "Hat switch: Is the top button on a joystick", + "message": "\" Hat\"" + }, "PAGE_GENERAL": { "description": "Header for the General input page.", "message": "Общи Контроли" @@ -217,5 +229,17 @@ "TEXT_KEY_POSITIVE": { "description": "Indicates the positive half of a key-binding pair.", "message": "Увеличение:" + }, + "X": { + "description": "keybinding: x axis", + "message": "X" + }, + "Y": { + "description": "keybinding: y axis", + "message": "Y" + }, + "Z": { + "description": "keybinding: z axis", + "message": "Z" } } diff --git a/data/lang/input-core/ca.json b/data/lang/input-core/ca.json index b3827cf46..8524bc35b 100644 --- a/data/lang/input-core/ca.json +++ b/data/lang/input-core/ca.json @@ -143,6 +143,14 @@ "description": "Descriptive name for the View Zoom axis", "message": "View Zoom" }, + "BUTTON": { + "description": "", + "message": "\" botó \"" + }, + "DIRECTION": { + "description": "Direction", + "message": "\" Dir \"" + }, "GROUP_GENERAL_VIEW_CONTROLS": { "description": "Header for the GeneralViewControls input group.", "message": "Controls de vista general" @@ -170,6 +178,10 @@ "description": "Header for the Weapons input group.", "message": "Armes" }, + "HAT": { + "description": "Hat switch: Is the top button on a joystick", + "message": "\" Bolet\"" + }, "PAGE_GENERAL": { "description": "Header for the General input page.", "message": "General Controls" @@ -217,5 +229,17 @@ "TEXT_KEY_POSITIVE": { "description": "Indicates the positive half of a key-binding pair.", "message": "Positive:" + }, + "X": { + "description": "keybinding: x axis", + "message": "X" + }, + "Y": { + "description": "keybinding: y axis", + "message": "Y" + }, + "Z": { + "description": "keybinding: z axis", + "message": "Z" } } diff --git a/data/lang/input-core/cs.json b/data/lang/input-core/cs.json index ca942826a..2f253d957 100644 --- a/data/lang/input-core/cs.json +++ b/data/lang/input-core/cs.json @@ -143,6 +143,14 @@ "description": "Descriptive name for the View Zoom axis", "message": "View Zoom" }, + "BUTTON": { + "description": "", + "message": "\" klávesa \"" + }, + "DIRECTION": { + "description": "Direction", + "message": "\" směr \"" + }, "GROUP_GENERAL_VIEW_CONTROLS": { "description": "Header for the GeneralViewControls input group.", "message": "Ovládací prvky celkového pohledu" @@ -170,6 +178,10 @@ "description": "Header for the Weapons input group.", "message": "Zbraně" }, + "HAT": { + "description": "Hat switch: Is the top button on a joystick", + "message": "\" klobouk\"" + }, "PAGE_GENERAL": { "description": "Header for the General input page.", "message": "General Controls" @@ -217,5 +229,17 @@ "TEXT_KEY_POSITIVE": { "description": "Indicates the positive half of a key-binding pair.", "message": "Positive:" + }, + "X": { + "description": "keybinding: x axis", + "message": "X" + }, + "Y": { + "description": "keybinding: y axis", + "message": "Y" + }, + "Z": { + "description": "keybinding: z axis", + "message": "Z" } } diff --git a/data/lang/input-core/da.json b/data/lang/input-core/da.json index 1a9b9ab3a..c77ea0708 100644 --- a/data/lang/input-core/da.json +++ b/data/lang/input-core/da.json @@ -143,6 +143,14 @@ "description": "Descriptive name for the View Zoom axis", "message": "View Zoom" }, + "BUTTON": { + "description": "", + "message": "\" Knap \"" + }, + "DIRECTION": { + "description": "Direction", + "message": "\" Retning \"" + }, "GROUP_GENERAL_VIEW_CONTROLS": { "description": "Header for the GeneralViewControls input group.", "message": "Generel synspunktsstyring" @@ -170,6 +178,10 @@ "description": "Header for the Weapons input group.", "message": "Våben" }, + "HAT": { + "description": "Hat switch: Is the top button on a joystick", + "message": "\" Hat\"" + }, "PAGE_GENERAL": { "description": "Header for the General input page.", "message": "General Controls" @@ -217,5 +229,17 @@ "TEXT_KEY_POSITIVE": { "description": "Indicates the positive half of a key-binding pair.", "message": "Positive:" + }, + "X": { + "description": "keybinding: x axis", + "message": "X" + }, + "Y": { + "description": "keybinding: y axis", + "message": "Y" + }, + "Z": { + "description": "keybinding: z axis", + "message": "Z" } } diff --git a/data/lang/input-core/de.json b/data/lang/input-core/de.json index 5c72cb399..efb48050a 100644 --- a/data/lang/input-core/de.json +++ b/data/lang/input-core/de.json @@ -143,6 +143,14 @@ "description": "Descriptive name for the View Zoom axis", "message": "Ansicht zoomen" }, + "BUTTON": { + "description": "", + "message": "\"-Taste \"" + }, + "DIRECTION": { + "description": "Direction", + "message": "\"-Richtung \"" + }, "GROUP_GENERAL_VIEW_CONTROLS": { "description": "Header for the GeneralViewControls input group.", "message": "Allgemeine Ansichtssteuerung" @@ -170,6 +178,10 @@ "description": "Header for the Weapons input group.", "message": "Waffen" }, + "HAT": { + "description": "Hat switch: Is the top button on a joystick", + "message": "\"-Hat\"" + }, "PAGE_GENERAL": { "description": "Header for the General input page.", "message": "Allgemeine Steuerung" @@ -217,5 +229,17 @@ "TEXT_KEY_POSITIVE": { "description": "Indicates the positive half of a key-binding pair.", "message": "Positiv:" + }, + "X": { + "description": "keybinding: x axis", + "message": "X" + }, + "Y": { + "description": "keybinding: y axis", + "message": "Y" + }, + "Z": { + "description": "keybinding: z axis", + "message": "Z" } } diff --git a/data/lang/input-core/el.json b/data/lang/input-core/el.json index fbe4db443..b05d3d86e 100644 --- a/data/lang/input-core/el.json +++ b/data/lang/input-core/el.json @@ -143,6 +143,14 @@ "description": "Descriptive name for the View Zoom axis", "message": "View Zoom" }, + "BUTTON": { + "description": "", + "message": "\" Button \"" + }, + "DIRECTION": { + "description": "Direction", + "message": "\" Dir \"" + }, "GROUP_GENERAL_VIEW_CONTROLS": { "description": "Header for the GeneralViewControls input group.", "message": "General View Controls" @@ -170,6 +178,10 @@ "description": "Header for the Weapons input group.", "message": "Weapons" }, + "HAT": { + "description": "Hat switch: Is the top button on a joystick", + "message": "\" Hat\"" + }, "PAGE_GENERAL": { "description": "Header for the General input page.", "message": "General Controls" @@ -217,5 +229,17 @@ "TEXT_KEY_POSITIVE": { "description": "Indicates the positive half of a key-binding pair.", "message": "Positive:" + }, + "X": { + "description": "keybinding: x axis", + "message": "X" + }, + "Y": { + "description": "keybinding: y axis", + "message": "Y" + }, + "Z": { + "description": "keybinding: z axis", + "message": "Z" } } diff --git a/data/lang/input-core/en.json b/data/lang/input-core/en.json index fbe4db443..14c7ec499 100644 --- a/data/lang/input-core/en.json +++ b/data/lang/input-core/en.json @@ -1,4 +1,8 @@ { + "AXIS": { + "description": "Cardinal name used to compose Joystick Button ", + "message": " Axis " + }, "BIND_AXIS_PITCH": { "description": "Descriptive name for the Pitch axis.", "message": "Pitch" @@ -91,6 +95,18 @@ "description": "Axis binding", "message": "Map Yaw" }, + "BIND_MAP_WARP_TO_CURRENT_SYSTEM": { + "description": "Descriptive name for the MapWarpToCurrentSystem action.", + "message": "Warp to Current System" + }, + "BIND_MAP_WARP_TO_SELECTED_SYSTEM": { + "description": "Descriptive name for the MapWarpToSelectedSystem action.", + "message": "Warp to Selected System" + }, + "BIND_MAP_TOGGLE_SELECTION_FOLLOW_VIEW": { + "description": "Descriptive name for the MapToggleSelectionFollowView action.", + "message": "Toggle Selection Following View" + }, "BIND_PRIMARY_FIRE": { "description": "Descriptive name for the PrimaryFire action.", "message": "Primary Fire" @@ -103,6 +119,10 @@ "description": "Descriptive name for the ResetCamera action.", "message": "Reset Camera View to Default" }, + "BIND_RESET_ORIENTATION_AND_ZOOM": { + "description": "Descriptive name for the ResetOrientationAndZoom action.", + "message": "Reset Orientation and Zoom" + }, "BIND_RIGHT_CAMERA": { "description": "Descriptive name for the RightCamera action.", "message": "Switch to Right Camera" @@ -127,6 +147,10 @@ "description": "Descriptive name for the ToggleHudMode action.", "message": "Toggle HUD Mode" }, + "BIND_TOGGLE_LUA_CONSOLE": { + "description": "Descriptive name for the ToggleLuaConsole action.", + "message": "Toggle Lua console" + }, "BIND_TOGGLE_ROTATION_DAMPING": { "description": "Descriptive name for the ToggleRotationDamping action.", "message": "Toggle Rotation Damping" @@ -143,6 +167,14 @@ "description": "Descriptive name for the View Zoom axis", "message": "View Zoom" }, + "BUTTON": { + "description": "", + "message": "\" Button \"" + }, + "DIRECTION": { + "description": "Direction", + "message": "\" Dir \"" + }, "GROUP_GENERAL_VIEW_CONTROLS": { "description": "Header for the GeneralViewControls input group.", "message": "General View Controls" @@ -170,6 +202,26 @@ "description": "Header for the Weapons input group.", "message": "Weapons" }, + "HAT": { + "description": "Hat switch: Is the top button on a joystick", + "message": "\" Hat\"" + }, + "MOUSE": { + "description": "A computer mouse, used to create the name for a mouse button binding", + "message": "Mouse " + }, + "MOUSE_LMB": { + "description": "An acronym for the left mouse button", + "message": "LMB" + }, + "MOUSE_MMB": { + "description": "An acronym for the middle mouse button", + "message": "MMB" + }, + "MOUSE_RMB": { + "description": "An acronym for the right mouse button", + "message": "RMB" + }, "PAGE_GENERAL": { "description": "Header for the General input page.", "message": "General Controls" @@ -217,5 +269,17 @@ "TEXT_KEY_POSITIVE": { "description": "Indicates the positive half of a key-binding pair.", "message": "Positive:" + }, + "X": { + "description": "keybinding: x axis", + "message": "X" + }, + "Y": { + "description": "keybinding: y axis", + "message": "Y" + }, + "Z": { + "description": "keybinding: z axis", + "message": "Z" } } diff --git a/data/lang/input-core/eo.json b/data/lang/input-core/eo.json index fbe4db443..b05d3d86e 100644 --- a/data/lang/input-core/eo.json +++ b/data/lang/input-core/eo.json @@ -143,6 +143,14 @@ "description": "Descriptive name for the View Zoom axis", "message": "View Zoom" }, + "BUTTON": { + "description": "", + "message": "\" Button \"" + }, + "DIRECTION": { + "description": "Direction", + "message": "\" Dir \"" + }, "GROUP_GENERAL_VIEW_CONTROLS": { "description": "Header for the GeneralViewControls input group.", "message": "General View Controls" @@ -170,6 +178,10 @@ "description": "Header for the Weapons input group.", "message": "Weapons" }, + "HAT": { + "description": "Hat switch: Is the top button on a joystick", + "message": "\" Hat\"" + }, "PAGE_GENERAL": { "description": "Header for the General input page.", "message": "General Controls" @@ -217,5 +229,17 @@ "TEXT_KEY_POSITIVE": { "description": "Indicates the positive half of a key-binding pair.", "message": "Positive:" + }, + "X": { + "description": "keybinding: x axis", + "message": "X" + }, + "Y": { + "description": "keybinding: y axis", + "message": "Y" + }, + "Z": { + "description": "keybinding: z axis", + "message": "Z" } } diff --git a/data/lang/input-core/es.json b/data/lang/input-core/es.json index e2431f8bc..c694ebe13 100644 --- a/data/lang/input-core/es.json +++ b/data/lang/input-core/es.json @@ -143,6 +143,14 @@ "description": "Descriptive name for the View Zoom axis", "message": "Zoom" }, + "BUTTON": { + "description": "", + "message": "\" Botón \"" + }, + "DIRECTION": { + "description": "Direction", + "message": "\" Dir \"" + }, "GROUP_GENERAL_VIEW_CONTROLS": { "description": "Header for the GeneralViewControls input group.", "message": "Vista General" @@ -170,6 +178,10 @@ "description": "Header for the Weapons input group.", "message": "Armamento" }, + "HAT": { + "description": "Hat switch: Is the top button on a joystick", + "message": "\" Seta\"" + }, "PAGE_GENERAL": { "description": "Header for the General input page.", "message": "Controles Generales" @@ -217,5 +229,17 @@ "TEXT_KEY_POSITIVE": { "description": "Indicates the positive half of a key-binding pair.", "message": "Positivo:" + }, + "X": { + "description": "keybinding: x axis", + "message": "X" + }, + "Y": { + "description": "keybinding: y axis", + "message": "Y" + }, + "Z": { + "description": "keybinding: z axis", + "message": "Z" } } diff --git a/data/lang/input-core/fr.json b/data/lang/input-core/fr.json index 3e65f4739..1d4e05a89 100644 --- a/data/lang/input-core/fr.json +++ b/data/lang/input-core/fr.json @@ -143,6 +143,14 @@ "description": "Descriptive name for the View Zoom axis", "message": "Zoom vue" }, + "BUTTON": { + "description": "", + "message": "\"bouton\"" + }, + "DIRECTION": { + "description": "Direction", + "message": "\" Doc \"" + }, "GROUP_GENERAL_VIEW_CONTROLS": { "description": "Header for the GeneralViewControls input group.", "message": "Contrôles de vue généraux" @@ -170,6 +178,10 @@ "description": "Header for the Weapons input group.", "message": "Armes" }, + "HAT": { + "description": "Hat switch: Is the top button on a joystick", + "message": "\"Couv\"" + }, "PAGE_GENERAL": { "description": "Header for the General input page.", "message": "Contrôles généraux" @@ -217,5 +229,17 @@ "TEXT_KEY_POSITIVE": { "description": "Indicates the positive half of a key-binding pair.", "message": "Positif:" + }, + "X": { + "description": "keybinding: x axis", + "message": "X" + }, + "Y": { + "description": "keybinding: y axis", + "message": "Y" + }, + "Z": { + "description": "keybinding: z axis", + "message": "Z" } } diff --git a/data/lang/input-core/ga.json b/data/lang/input-core/ga.json index 14b4f97d7..dc3718371 100644 --- a/data/lang/input-core/ga.json +++ b/data/lang/input-core/ga.json @@ -143,6 +143,14 @@ "description": "Descriptive name for the View Zoom axis", "message": "View Zoom" }, + "BUTTON": { + "description": "", + "message": "\" Cnaipe \"" + }, + "DIRECTION": { + "description": "Direction", + "message": "\" Treo \"" + }, "GROUP_GENERAL_VIEW_CONTROLS": { "description": "Header for the GeneralViewControls input group.", "message": "Rialtáin Ghinearálta Radhairc" @@ -170,6 +178,10 @@ "description": "Header for the Weapons input group.", "message": "Airm" }, + "HAT": { + "description": "Hat switch: Is the top button on a joystick", + "message": "\" Hata\"" + }, "PAGE_GENERAL": { "description": "Header for the General input page.", "message": "General Controls" @@ -217,5 +229,17 @@ "TEXT_KEY_POSITIVE": { "description": "Indicates the positive half of a key-binding pair.", "message": "Positive:" + }, + "X": { + "description": "keybinding: x axis", + "message": "X" + }, + "Y": { + "description": "keybinding: y axis", + "message": "Y" + }, + "Z": { + "description": "keybinding: z axis", + "message": "Z" } } diff --git a/data/lang/input-core/gd.json b/data/lang/input-core/gd.json index 252bbab5d..6173cfbb2 100644 --- a/data/lang/input-core/gd.json +++ b/data/lang/input-core/gd.json @@ -143,6 +143,14 @@ "description": "Descriptive name for the View Zoom axis", "message": "View Zoom" }, + "BUTTON": { + "description": "", + "message": "\" Putan \"" + }, + "DIRECTION": { + "description": "Direction", + "message": "\"Comhair\"" + }, "GROUP_GENERAL_VIEW_CONTROLS": { "description": "Header for the GeneralViewControls input group.", "message": "Uidheaman-smachd coitcheann" @@ -170,6 +178,10 @@ "description": "Header for the Weapons input group.", "message": "Airm" }, + "HAT": { + "description": "Hat switch: Is the top button on a joystick", + "message": "\" Ad\"" + }, "PAGE_GENERAL": { "description": "Header for the General input page.", "message": "General Controls" @@ -217,5 +229,17 @@ "TEXT_KEY_POSITIVE": { "description": "Indicates the positive half of a key-binding pair.", "message": "Positive:" + }, + "X": { + "description": "keybinding: x axis", + "message": "X" + }, + "Y": { + "description": "keybinding: y axis", + "message": "Y" + }, + "Z": { + "description": "keybinding: z axis", + "message": "Z" } } diff --git a/data/lang/input-core/hr.json b/data/lang/input-core/hr.json index 43cb4b1b7..1375503bc 100644 --- a/data/lang/input-core/hr.json +++ b/data/lang/input-core/hr.json @@ -143,6 +143,14 @@ "description": "Descriptive name for the View Zoom axis", "message": "View Zoom" }, + "BUTTON": { + "description": "", + "message": "\" Tipka \"" + }, + "DIRECTION": { + "description": "Direction", + "message": "\" Smjer \"" + }, "GROUP_GENERAL_VIEW_CONTROLS": { "description": "Header for the GeneralViewControls input group.", "message": "General View Controls" @@ -170,6 +178,10 @@ "description": "Header for the Weapons input group.", "message": "Oružja" }, + "HAT": { + "description": "Hat switch: Is the top button on a joystick", + "message": "\" Hat\"" + }, "PAGE_GENERAL": { "description": "Header for the General input page.", "message": "General Controls" @@ -217,5 +229,17 @@ "TEXT_KEY_POSITIVE": { "description": "Indicates the positive half of a key-binding pair.", "message": "Positive:" + }, + "X": { + "description": "keybinding: x axis", + "message": "X" + }, + "Y": { + "description": "keybinding: y axis", + "message": "Y" + }, + "Z": { + "description": "keybinding: z axis", + "message": "Z" } } diff --git a/data/lang/input-core/hu.json b/data/lang/input-core/hu.json index 8da2af099..af43d3f2a 100644 --- a/data/lang/input-core/hu.json +++ b/data/lang/input-core/hu.json @@ -143,6 +143,14 @@ "description": "Descriptive name for the View Zoom axis", "message": "Zoom" }, + "BUTTON": { + "description": "", + "message": "\" Gomb \"" + }, + "DIRECTION": { + "description": "Direction", + "message": "\" Irány \"" + }, "GROUP_GENERAL_VIEW_CONTROLS": { "description": "Header for the GeneralViewControls input group.", "message": "Általános nézet vezérlők" @@ -170,6 +178,10 @@ "description": "Header for the Weapons input group.", "message": "Fegyverek" }, + "HAT": { + "description": "Hat switch: Is the top button on a joystick", + "message": "\" Hat\"" + }, "PAGE_GENERAL": { "description": "Header for the General input page.", "message": "Általános" @@ -217,5 +229,17 @@ "TEXT_KEY_POSITIVE": { "description": "Indicates the positive half of a key-binding pair.", "message": "Pozitív:" + }, + "X": { + "description": "keybinding: x axis", + "message": "X" + }, + "Y": { + "description": "keybinding: y axis", + "message": "Y" + }, + "Z": { + "description": "keybinding: z axis", + "message": "Z" } } diff --git a/data/lang/input-core/id.json b/data/lang/input-core/id.json index 00856e913..143a675aa 100644 --- a/data/lang/input-core/id.json +++ b/data/lang/input-core/id.json @@ -143,6 +143,14 @@ "description": "Descriptive name for the View Zoom axis", "message": "View Zoom" }, + "BUTTON": { + "description": "", + "message": "\" Button \"" + }, + "DIRECTION": { + "description": "Direction", + "message": "\" Arah \"" + }, "GROUP_GENERAL_VIEW_CONTROLS": { "description": "Header for the GeneralViewControls input group.", "message": "General View Controls" @@ -170,6 +178,10 @@ "description": "Header for the Weapons input group.", "message": "Senjata" }, + "HAT": { + "description": "Hat switch: Is the top button on a joystick", + "message": "\" Hat\"" + }, "PAGE_GENERAL": { "description": "Header for the General input page.", "message": "General Controls" @@ -217,5 +229,17 @@ "TEXT_KEY_POSITIVE": { "description": "Indicates the positive half of a key-binding pair.", "message": "Positive:" + }, + "X": { + "description": "keybinding: x axis", + "message": "X" + }, + "Y": { + "description": "keybinding: y axis", + "message": "Y" + }, + "Z": { + "description": "keybinding: z axis", + "message": "Z" } } diff --git a/data/lang/input-core/it.json b/data/lang/input-core/it.json index e618d415c..457420f88 100644 --- a/data/lang/input-core/it.json +++ b/data/lang/input-core/it.json @@ -143,6 +143,14 @@ "description": "Descriptive name for the View Zoom axis", "message": "Zoom Vista" }, + "BUTTON": { + "description": "", + "message": "\" Pulsante \"" + }, + "DIRECTION": { + "description": "Direction", + "message": "\" Dir \"" + }, "GROUP_GENERAL_VIEW_CONTROLS": { "description": "Header for the GeneralViewControls input group.", "message": "Controlli Generali della Visuale" @@ -170,6 +178,10 @@ "description": "Header for the Weapons input group.", "message": "Armamenti" }, + "HAT": { + "description": "Hat switch: Is the top button on a joystick", + "message": "\" Hat\"" + }, "PAGE_GENERAL": { "description": "Header for the General input page.", "message": "Controlli Generici" @@ -217,5 +229,17 @@ "TEXT_KEY_POSITIVE": { "description": "Indicates the positive half of a key-binding pair.", "message": "Positivo:" + }, + "X": { + "description": "keybinding: x axis", + "message": "X" + }, + "Y": { + "description": "keybinding: y axis", + "message": "Y" + }, + "Z": { + "description": "keybinding: z axis", + "message": "Z" } } diff --git a/data/lang/input-core/lt.json b/data/lang/input-core/lt.json index a1c410e9e..486779f9e 100644 --- a/data/lang/input-core/lt.json +++ b/data/lang/input-core/lt.json @@ -143,6 +143,14 @@ "description": "Descriptive name for the View Zoom axis", "message": "View Zoom" }, + "BUTTON": { + "description": "", + "message": "\" Mygtukas \"" + }, + "DIRECTION": { + "description": "Direction", + "message": "\" Katalogas \"" + }, "GROUP_GENERAL_VIEW_CONTROLS": { "description": "Header for the GeneralViewControls input group.", "message": "Bendras Vaizdo Valdymas" @@ -170,6 +178,10 @@ "description": "Header for the Weapons input group.", "message": "Ginklai" }, + "HAT": { + "description": "Hat switch: Is the top button on a joystick", + "message": "\" Kepurė \"" + }, "PAGE_GENERAL": { "description": "Header for the General input page.", "message": "General Controls" @@ -217,5 +229,17 @@ "TEXT_KEY_POSITIVE": { "description": "Indicates the positive half of a key-binding pair.", "message": "Positive:" + }, + "X": { + "description": "keybinding: x axis", + "message": "X" + }, + "Y": { + "description": "keybinding: y axis", + "message": "Y" + }, + "Z": { + "description": "keybinding: z axis", + "message": "Z" } } diff --git a/data/lang/input-core/nb.json b/data/lang/input-core/nb.json index 20e3424b0..00a4b63a9 100644 --- a/data/lang/input-core/nb.json +++ b/data/lang/input-core/nb.json @@ -143,6 +143,14 @@ "description": "Descriptive name for the View Zoom axis", "message": "View Zoom" }, + "BUTTON": { + "description": "", + "message": "\" Button \"" + }, + "DIRECTION": { + "description": "Direction", + "message": "\" Dir \"" + }, "GROUP_GENERAL_VIEW_CONTROLS": { "description": "Header for the GeneralViewControls input group.", "message": "General View Controls" @@ -170,6 +178,10 @@ "description": "Header for the Weapons input group.", "message": "Weapons" }, + "HAT": { + "description": "Hat switch: Is the top button on a joystick", + "message": "\" Hat\"" + }, "PAGE_GENERAL": { "description": "Header for the General input page.", "message": "General Controls" @@ -217,5 +229,17 @@ "TEXT_KEY_POSITIVE": { "description": "Indicates the positive half of a key-binding pair.", "message": "Positive:" + }, + "X": { + "description": "keybinding: x axis", + "message": "X" + }, + "Y": { + "description": "keybinding: y axis", + "message": "Y" + }, + "Z": { + "description": "keybinding: z axis", + "message": "Z" } } diff --git a/data/lang/input-core/nl.json b/data/lang/input-core/nl.json index 1fbe89bb7..4026f182b 100644 --- a/data/lang/input-core/nl.json +++ b/data/lang/input-core/nl.json @@ -143,6 +143,14 @@ "description": "Descriptive name for the View Zoom axis", "message": "View Zoom" }, + "BUTTON": { + "description": "", + "message": "\" Knop \"" + }, + "DIRECTION": { + "description": "Direction", + "message": "\" Richting \"" + }, "GROUP_GENERAL_VIEW_CONTROLS": { "description": "Header for the GeneralViewControls input group.", "message": "Algemeen zicht beheer" @@ -170,6 +178,10 @@ "description": "Header for the Weapons input group.", "message": "Wapens" }, + "HAT": { + "description": "Hat switch: Is the top button on a joystick", + "message": "\" Hoed\"" + }, "PAGE_GENERAL": { "description": "Header for the General input page.", "message": "General Controls" @@ -217,5 +229,17 @@ "TEXT_KEY_POSITIVE": { "description": "Indicates the positive half of a key-binding pair.", "message": "Positive:" + }, + "X": { + "description": "keybinding: x axis", + "message": "X" + }, + "Y": { + "description": "keybinding: y axis", + "message": "Y" + }, + "Z": { + "description": "keybinding: z axis", + "message": "Z" } } diff --git a/data/lang/input-core/pl.json b/data/lang/input-core/pl.json index 61112f329..397fc5ef8 100644 --- a/data/lang/input-core/pl.json +++ b/data/lang/input-core/pl.json @@ -143,6 +143,14 @@ "description": "Descriptive name for the View Zoom axis", "message": "Powiększ widok" }, + "BUTTON": { + "description": "", + "message": "\" przycisk \"" + }, + "DIRECTION": { + "description": "Direction", + "message": "\" Dir \"" + }, "GROUP_GENERAL_VIEW_CONTROLS": { "description": "Header for the GeneralViewControls input group.", "message": "Kontrolki widoku głównego" @@ -170,6 +178,10 @@ "description": "Header for the Weapons input group.", "message": "Bronie" }, + "HAT": { + "description": "Hat switch: Is the top button on a joystick", + "message": "\" Hat\"" + }, "PAGE_GENERAL": { "description": "Header for the General input page.", "message": "Kontrolki ogólne" @@ -217,5 +229,17 @@ "TEXT_KEY_POSITIVE": { "description": "Indicates the positive half of a key-binding pair.", "message": "Positive:" + }, + "X": { + "description": "keybinding: x axis", + "message": "X" + }, + "Y": { + "description": "keybinding: y axis", + "message": "Y" + }, + "Z": { + "description": "keybinding: z axis", + "message": "Z" } } diff --git a/data/lang/input-core/pt.json b/data/lang/input-core/pt.json index 56fabf448..527e32449 100644 --- a/data/lang/input-core/pt.json +++ b/data/lang/input-core/pt.json @@ -143,6 +143,14 @@ "description": "Descriptive name for the View Zoom axis", "message": "View Zoom" }, + "BUTTON": { + "description": "", + "message": "\" Botão \"" + }, + "DIRECTION": { + "description": "Direction", + "message": "\" Dir \"" + }, "GROUP_GENERAL_VIEW_CONTROLS": { "description": "Header for the GeneralViewControls input group.", "message": "General View Controls" @@ -170,6 +178,10 @@ "description": "Header for the Weapons input group.", "message": "Armas" }, + "HAT": { + "description": "Hat switch: Is the top button on a joystick", + "message": "\" Hat\"" + }, "PAGE_GENERAL": { "description": "Header for the General input page.", "message": "General Controls" @@ -217,5 +229,17 @@ "TEXT_KEY_POSITIVE": { "description": "Indicates the positive half of a key-binding pair.", "message": "Positive:" + }, + "X": { + "description": "keybinding: x axis", + "message": "X" + }, + "Y": { + "description": "keybinding: y axis", + "message": "Y" + }, + "Z": { + "description": "keybinding: z axis", + "message": "Z" } } diff --git a/data/lang/input-core/pt_BR.json b/data/lang/input-core/pt_BR.json index cb3746b22..ecfc5c444 100644 --- a/data/lang/input-core/pt_BR.json +++ b/data/lang/input-core/pt_BR.json @@ -143,6 +143,14 @@ "description": "Descriptive name for the View Zoom axis", "message": "View Zoom" }, + "BUTTON": { + "description": "", + "message": "\" Botão \"" + }, + "DIRECTION": { + "description": "Direction", + "message": "\" Dir \"" + }, "GROUP_GENERAL_VIEW_CONTROLS": { "description": "Header for the GeneralViewControls input group.", "message": "Controles de vista geral" @@ -170,6 +178,10 @@ "description": "Header for the Weapons input group.", "message": "Armas" }, + "HAT": { + "description": "Hat switch: Is the top button on a joystick", + "message": "\" Chapéu\"" + }, "PAGE_GENERAL": { "description": "Header for the General input page.", "message": "General Controls" @@ -217,5 +229,17 @@ "TEXT_KEY_POSITIVE": { "description": "Indicates the positive half of a key-binding pair.", "message": "Positive:" + }, + "X": { + "description": "keybinding: x axis", + "message": "X" + }, + "Y": { + "description": "keybinding: y axis", + "message": "Y" + }, + "Z": { + "description": "keybinding: z axis", + "message": "Z" } } diff --git a/data/lang/input-core/ro.json b/data/lang/input-core/ro.json index c7b661783..2351b0a9b 100644 --- a/data/lang/input-core/ro.json +++ b/data/lang/input-core/ro.json @@ -143,6 +143,14 @@ "description": "Descriptive name for the View Zoom axis", "message": "View Zoom" }, + "BUTTON": { + "description": "", + "message": "\" Buton \"" + }, + "DIRECTION": { + "description": "Direction", + "message": "\" Dir \"" + }, "GROUP_GENERAL_VIEW_CONTROLS": { "description": "Header for the GeneralViewControls input group.", "message": "Controale Generale Vizionare" @@ -170,6 +178,10 @@ "description": "Header for the Weapons input group.", "message": "Arme" }, + "HAT": { + "description": "Hat switch: Is the top button on a joystick", + "message": "\" Buton vizualizare\"" + }, "PAGE_GENERAL": { "description": "Header for the General input page.", "message": "General Controls" @@ -217,5 +229,17 @@ "TEXT_KEY_POSITIVE": { "description": "Indicates the positive half of a key-binding pair.", "message": "Positive:" + }, + "X": { + "description": "keybinding: x axis", + "message": "X" + }, + "Y": { + "description": "keybinding: y axis", + "message": "Y" + }, + "Z": { + "description": "keybinding: z axis", + "message": "Z" } } diff --git a/data/lang/input-core/ru.json b/data/lang/input-core/ru.json index d0c7c7aa2..bcd90b4cf 100644 --- a/data/lang/input-core/ru.json +++ b/data/lang/input-core/ru.json @@ -143,6 +143,14 @@ "description": "Descriptive name for the View Zoom axis", "message": "View Zoom" }, + "BUTTON": { + "description": "", + "message": "\" Кнопка \"" + }, + "DIRECTION": { + "description": "Direction", + "message": "\" Напр. \"" + }, "GROUP_GENERAL_VIEW_CONTROLS": { "description": "Header for the GeneralViewControls input group.", "message": "Управление камерой" @@ -170,6 +178,10 @@ "description": "Header for the Weapons input group.", "message": "Управление оружием:" }, + "HAT": { + "description": "Hat switch: Is the top button on a joystick", + "message": "\" Hat\"" + }, "PAGE_GENERAL": { "description": "Header for the General input page.", "message": "General Controls" @@ -217,5 +229,17 @@ "TEXT_KEY_POSITIVE": { "description": "Indicates the positive half of a key-binding pair.", "message": "Positive:" + }, + "X": { + "description": "keybinding: x axis", + "message": "X" + }, + "Y": { + "description": "keybinding: y axis", + "message": "Y" + }, + "Z": { + "description": "keybinding: z axis", + "message": "Z" } } diff --git a/data/lang/input-core/sv.json b/data/lang/input-core/sv.json index 227684ef0..0aecf83ff 100644 --- a/data/lang/input-core/sv.json +++ b/data/lang/input-core/sv.json @@ -143,6 +143,14 @@ "description": "Descriptive name for the View Zoom axis", "message": "Zoom-vy" }, + "BUTTON": { + "description": "", + "message": "\" Knapp \"" + }, + "DIRECTION": { + "description": "Direction", + "message": "\" Dir \"" + }, "GROUP_GENERAL_VIEW_CONTROLS": { "description": "Header for the GeneralViewControls input group.", "message": "Generella vykontroller" @@ -170,6 +178,10 @@ "description": "Header for the Weapons input group.", "message": "Vapen" }, + "HAT": { + "description": "Hat switch: Is the top button on a joystick", + "message": "\" Hatt\"" + }, "PAGE_GENERAL": { "description": "Header for the General input page.", "message": "Generella kontroller" @@ -217,5 +229,17 @@ "TEXT_KEY_POSITIVE": { "description": "Indicates the positive half of a key-binding pair.", "message": "Positiv:" + }, + "X": { + "description": "keybinding: x axis", + "message": "X" + }, + "Y": { + "description": "keybinding: y axis", + "message": "Y" + }, + "Z": { + "description": "keybinding: z axis", + "message": "Z" } } diff --git a/data/lang/input-core/tr.json b/data/lang/input-core/tr.json index 8688d71f8..986a3ac1c 100644 --- a/data/lang/input-core/tr.json +++ b/data/lang/input-core/tr.json @@ -143,6 +143,14 @@ "description": "Descriptive name for the View Zoom axis", "message": "Görüntü Yakınlaştırma" }, + "BUTTON": { + "description": "", + "message": "\" Düğme \"" + }, + "DIRECTION": { + "description": "Direction", + "message": "\" Yön \"" + }, "GROUP_GENERAL_VIEW_CONTROLS": { "description": "Header for the GeneralViewControls input group.", "message": "Genel Görünüm Kontrolleri" @@ -170,6 +178,10 @@ "description": "Header for the Weapons input group.", "message": "Silahlar" }, + "HAT": { + "description": "Hat switch: Is the top button on a joystick", + "message": "\" Üst düğme\"" + }, "PAGE_GENERAL": { "description": "Header for the General input page.", "message": "Genel Kontroller" @@ -217,5 +229,17 @@ "TEXT_KEY_POSITIVE": { "description": "Indicates the positive half of a key-binding pair.", "message": "Pozitif:" + }, + "X": { + "description": "keybinding: x axis", + "message": "X" + }, + "Y": { + "description": "keybinding: y axis", + "message": "Y" + }, + "Z": { + "description": "keybinding: z axis", + "message": "Z" } } diff --git a/data/lang/input-core/zh.json b/data/lang/input-core/zh.json index 156d3304f..fd3b7c16d 100644 --- a/data/lang/input-core/zh.json +++ b/data/lang/input-core/zh.json @@ -143,6 +143,14 @@ "description": "Descriptive name for the View Zoom axis", "message": "视图缩放" }, + "BUTTON": { + "description": "", + "message": "\"按钮\"" + }, + "DIRECTION": { + "description": "Direction", + "message": "\"目录\"" + }, "GROUP_GENERAL_VIEW_CONTROLS": { "description": "Header for the GeneralViewControls input group.", "message": "通用视角控制" @@ -170,6 +178,10 @@ "description": "Header for the Weapons input group.", "message": "武器" }, + "HAT": { + "description": "Hat switch: Is the top button on a joystick", + "message": "\"帽子\"" + }, "PAGE_GENERAL": { "description": "Header for the General input page.", "message": "常规控制" @@ -217,5 +229,17 @@ "TEXT_KEY_POSITIVE": { "description": "Indicates the positive half of a key-binding pair.", "message": "正面" + }, + "X": { + "description": "keybinding: x axis", + "message": "X" + }, + "Y": { + "description": "keybinding: y axis", + "message": "Y" + }, + "Z": { + "description": "keybinding: z axis", + "message": "Z" } } diff --git a/data/lang/ui-core/ar.json b/data/lang/ui-core/ar.json index 368943640..2b878361a 100644 --- a/data/lang/ui-core/ar.json +++ b/data/lang/ui-core/ar.json @@ -1643,6 +1643,10 @@ "description": "", "message": "اعادة" }, + "RESET_ORIENTATION_AND_ZOOM": { + "description": "", + "message": "اعادة التوجية و التكبير" + }, "RETURN_TO_GAME": { "description": "", "message": "Return to game" diff --git a/data/lang/ui-core/bg.json b/data/lang/ui-core/bg.json index c0aaff87d..719f7aa7b 100644 --- a/data/lang/ui-core/bg.json +++ b/data/lang/ui-core/bg.json @@ -1643,6 +1643,10 @@ "description": "", "message": "Нулиране" }, + "RESET_ORIENTATION_AND_ZOOM": { + "description": "", + "message": "Нулирай Ориентацията и Мащабирането" + }, "RETURN_TO_GAME": { "description": "", "message": "Върни се в играта" diff --git a/data/lang/ui-core/ca.json b/data/lang/ui-core/ca.json index c4adeb6ad..2f8d25167 100644 --- a/data/lang/ui-core/ca.json +++ b/data/lang/ui-core/ca.json @@ -1643,6 +1643,10 @@ "description": "", "message": "Reset" }, + "RESET_ORIENTATION_AND_ZOOM": { + "description": "", + "message": "Reiniciar orientació i zoom" + }, "RETURN_TO_GAME": { "description": "", "message": "Return to game" diff --git a/data/lang/ui-core/cs.json b/data/lang/ui-core/cs.json index 20e42a53d..864229f19 100644 --- a/data/lang/ui-core/cs.json +++ b/data/lang/ui-core/cs.json @@ -1643,6 +1643,10 @@ "description": "", "message": "Reset" }, + "RESET_ORIENTATION_AND_ZOOM": { + "description": "", + "message": "Reset orientace a zoomu" + }, "RETURN_TO_GAME": { "description": "", "message": "Návrat do hry" diff --git a/data/lang/ui-core/da.json b/data/lang/ui-core/da.json index 3f00d0f1b..c331aa850 100644 --- a/data/lang/ui-core/da.json +++ b/data/lang/ui-core/da.json @@ -1643,6 +1643,10 @@ "description": "", "message": "Nulstil" }, + "RESET_ORIENTATION_AND_ZOOM": { + "description": "", + "message": "Nulstil Synspunkt og Zoom" + }, "RETURN_TO_GAME": { "description": "", "message": "Return to game" diff --git a/data/lang/ui-core/de.json b/data/lang/ui-core/de.json index dd0e36925..3f89ad613 100644 --- a/data/lang/ui-core/de.json +++ b/data/lang/ui-core/de.json @@ -1643,6 +1643,10 @@ "description": "", "message": "Zurücksetzen" }, + "RESET_ORIENTATION_AND_ZOOM": { + "description": "", + "message": "Ansicht und Zoom zurücksetzen" + }, "RETURN_TO_GAME": { "description": "", "message": "Zurück zum Spiel" diff --git a/data/lang/ui-core/el.json b/data/lang/ui-core/el.json index 9bd34af8e..8c50d600b 100644 --- a/data/lang/ui-core/el.json +++ b/data/lang/ui-core/el.json @@ -1643,6 +1643,10 @@ "description": "", "message": "Reset" }, + "RESET_ORIENTATION_AND_ZOOM": { + "description": "", + "message": "Reset Orientation and Zoom" + }, "RETURN_TO_GAME": { "description": "", "message": "Return to game" diff --git a/data/lang/ui-core/en.json b/data/lang/ui-core/en.json index 7ebda1f3b..b303dfa8f 100644 --- a/data/lang/ui-core/en.json +++ b/data/lang/ui-core/en.json @@ -1643,6 +1643,10 @@ "description": "", "message": "Reset" }, + "RESET_ORIENTATION_AND_ZOOM": { + "description": "", + "message": "Reset Orientation and Zoom" + }, "RETURN_TO_GAME": { "description": "", "message": "Return to game" diff --git a/data/lang/ui-core/eo.json b/data/lang/ui-core/eo.json index 7ebda1f3b..b303dfa8f 100644 --- a/data/lang/ui-core/eo.json +++ b/data/lang/ui-core/eo.json @@ -1643,6 +1643,10 @@ "description": "", "message": "Reset" }, + "RESET_ORIENTATION_AND_ZOOM": { + "description": "", + "message": "Reset Orientation and Zoom" + }, "RETURN_TO_GAME": { "description": "", "message": "Return to game" diff --git a/data/lang/ui-core/es.json b/data/lang/ui-core/es.json index ead936ba0..5be99fcb9 100644 --- a/data/lang/ui-core/es.json +++ b/data/lang/ui-core/es.json @@ -1643,6 +1643,10 @@ "description": "", "message": "Reiniciar" }, + "RESET_ORIENTATION_AND_ZOOM": { + "description": "", + "message": "Reiniciar orientación y zoom" + }, "RETURN_TO_GAME": { "description": "", "message": "Volver al juego" diff --git a/data/lang/ui-core/fr.json b/data/lang/ui-core/fr.json index e0aa8c018..a9ea06308 100644 --- a/data/lang/ui-core/fr.json +++ b/data/lang/ui-core/fr.json @@ -1643,6 +1643,10 @@ "description": "", "message": "Reset" }, + "RESET_ORIENTATION_AND_ZOOM": { + "description": "", + "message": "Réinitialise l'orientation et le zoom" + }, "RETURN_TO_GAME": { "description": "", "message": "Reprendre la partie" diff --git a/data/lang/ui-core/ga.json b/data/lang/ui-core/ga.json index d62b72165..8849e275a 100644 --- a/data/lang/ui-core/ga.json +++ b/data/lang/ui-core/ga.json @@ -1643,6 +1643,10 @@ "description": "", "message": "Athshocraigh" }, + "RESET_ORIENTATION_AND_ZOOM": { + "description": "", + "message": "Athshocraigh an Treoshuíomh agus an Zúmáil" + }, "RETURN_TO_GAME": { "description": "", "message": "Fill ar an gcluiche" diff --git a/data/lang/ui-core/gd.json b/data/lang/ui-core/gd.json index 293d3f3a8..2ade0a0ce 100644 --- a/data/lang/ui-core/gd.json +++ b/data/lang/ui-core/gd.json @@ -1643,6 +1643,10 @@ "description": "", "message": "Ath-shuidhich" }, + "RESET_ORIENTATION_AND_ZOOM": { + "description": "", + "message": "Ath-shuidhich a' chomhair agus an sùm" + }, "RETURN_TO_GAME": { "description": "", "message": "Till dhan gheama" diff --git a/data/lang/ui-core/hr.json b/data/lang/ui-core/hr.json index a80022a1d..5961fd796 100644 --- a/data/lang/ui-core/hr.json +++ b/data/lang/ui-core/hr.json @@ -1643,6 +1643,10 @@ "description": "", "message": "Reset" }, + "RESET_ORIENTATION_AND_ZOOM": { + "description": "", + "message": "Reset Orientation and Zoom" + }, "RETURN_TO_GAME": { "description": "", "message": "Return to game" diff --git a/data/lang/ui-core/hu.json b/data/lang/ui-core/hu.json index 850a7ba78..740b9038e 100644 --- a/data/lang/ui-core/hu.json +++ b/data/lang/ui-core/hu.json @@ -1643,6 +1643,10 @@ "description": "", "message": "Alaphelyzet" }, + "RESET_ORIENTATION_AND_ZOOM": { + "description": "", + "message": "Nézet alaphelyzetbe" + }, "RETURN_TO_GAME": { "description": "", "message": "Vissza" diff --git a/data/lang/ui-core/id.json b/data/lang/ui-core/id.json index d879b5576..af7bbd2e3 100644 --- a/data/lang/ui-core/id.json +++ b/data/lang/ui-core/id.json @@ -1643,6 +1643,10 @@ "description": "", "message": "Reset" }, + "RESET_ORIENTATION_AND_ZOOM": { + "description": "", + "message": "Reset Orientation and Zoom" + }, "RETURN_TO_GAME": { "description": "", "message": "Return to game" diff --git a/data/lang/ui-core/it.json b/data/lang/ui-core/it.json index afac993d4..98b120c6f 100644 --- a/data/lang/ui-core/it.json +++ b/data/lang/ui-core/it.json @@ -1643,6 +1643,10 @@ "description": "", "message": "Azzera" }, + "RESET_ORIENTATION_AND_ZOOM": { + "description": "", + "message": "Reimposta Orientamento e Zoom" + }, "RETURN_TO_GAME": { "description": "", "message": "Torna al gioco" diff --git a/data/lang/ui-core/lt.json b/data/lang/ui-core/lt.json index 86d6b7442..dc3b302bc 100644 --- a/data/lang/ui-core/lt.json +++ b/data/lang/ui-core/lt.json @@ -1643,6 +1643,10 @@ "description": "", "message": "Perkrauti" }, + "RESET_ORIENTATION_AND_ZOOM": { + "description": "", + "message": "Atitaisyti kryptį ir priartinimą" + }, "RETURN_TO_GAME": { "description": "", "message": "Grįžti į žaidimą" diff --git a/data/lang/ui-core/nb.json b/data/lang/ui-core/nb.json index bafa71622..fbb30e71b 100644 --- a/data/lang/ui-core/nb.json +++ b/data/lang/ui-core/nb.json @@ -1643,6 +1643,10 @@ "description": "", "message": "Reset" }, + "RESET_ORIENTATION_AND_ZOOM": { + "description": "", + "message": "Reset Orientation and Zoom" + }, "RETURN_TO_GAME": { "description": "", "message": "Return to game" diff --git a/data/lang/ui-core/nl.json b/data/lang/ui-core/nl.json index 77c8e9e65..61184ac12 100644 --- a/data/lang/ui-core/nl.json +++ b/data/lang/ui-core/nl.json @@ -1643,6 +1643,10 @@ "description": "", "message": "Reset" }, + "RESET_ORIENTATION_AND_ZOOM": { + "description": "", + "message": "Herinstellen Oriëntatie en Zoom" + }, "RETURN_TO_GAME": { "description": "", "message": "Terug naar spel" diff --git a/data/lang/ui-core/pl.json b/data/lang/ui-core/pl.json index fdf1611ed..85f80b603 100644 --- a/data/lang/ui-core/pl.json +++ b/data/lang/ui-core/pl.json @@ -1643,6 +1643,10 @@ "description": "", "message": "Reset" }, + "RESET_ORIENTATION_AND_ZOOM": { + "description": "", + "message": "Reset orientacji i zbliżenia" + }, "RETURN_TO_GAME": { "description": "", "message": "Powrót do gry" diff --git a/data/lang/ui-core/pt.json b/data/lang/ui-core/pt.json index 92c5a7723..d8373f389 100644 --- a/data/lang/ui-core/pt.json +++ b/data/lang/ui-core/pt.json @@ -1643,6 +1643,10 @@ "description": "", "message": "Reiniciar" }, + "RESET_ORIENTATION_AND_ZOOM": { + "description": "", + "message": "Reset Orientation and Zoom" + }, "RETURN_TO_GAME": { "description": "", "message": "Return to game" diff --git a/data/lang/ui-core/pt_BR.json b/data/lang/ui-core/pt_BR.json index 999920a97..1a2b139a9 100644 --- a/data/lang/ui-core/pt_BR.json +++ b/data/lang/ui-core/pt_BR.json @@ -1643,6 +1643,10 @@ "description": "", "message": "Reset" }, + "RESET_ORIENTATION_AND_ZOOM": { + "description": "", + "message": "Reiniciar Orientação e Zoom" + }, "RETURN_TO_GAME": { "description": "", "message": "Voltar ao jogo" diff --git a/data/lang/ui-core/ro.json b/data/lang/ui-core/ro.json index 021b76030..33fcbbb67 100644 --- a/data/lang/ui-core/ro.json +++ b/data/lang/ui-core/ro.json @@ -1643,6 +1643,10 @@ "description": "", "message": "Resetare" }, + "RESET_ORIENTATION_AND_ZOOM": { + "description": "", + "message": "Resetează Orientare și Focalizare" + }, "RETURN_TO_GAME": { "description": "", "message": "Întoarcere în joc" diff --git a/data/lang/ui-core/ru.json b/data/lang/ui-core/ru.json index dcd492b9d..b5b2fd04b 100644 --- a/data/lang/ui-core/ru.json +++ b/data/lang/ui-core/ru.json @@ -1643,6 +1643,10 @@ "description": "", "message": "Сброс" }, + "RESET_ORIENTATION_AND_ZOOM": { + "description": "", + "message": "Камера по умолчанию" + }, "RETURN_TO_GAME": { "description": "", "message": "Вернуться к игре" diff --git a/data/lang/ui-core/sv.json b/data/lang/ui-core/sv.json index 53ca77462..5051b742c 100644 --- a/data/lang/ui-core/sv.json +++ b/data/lang/ui-core/sv.json @@ -1643,6 +1643,10 @@ "description": "", "message": "Återställ" }, + "RESET_ORIENTATION_AND_ZOOM": { + "description": "", + "message": "Återställ orientering och zoom" + }, "RETURN_TO_GAME": { "description": "", "message": "Återgå till spelet" diff --git a/data/lang/ui-core/tr.json b/data/lang/ui-core/tr.json index 764b41669..127672112 100644 --- a/data/lang/ui-core/tr.json +++ b/data/lang/ui-core/tr.json @@ -1643,6 +1643,10 @@ "description": "", "message": "Yeniden başlat" }, + "RESET_ORIENTATION_AND_ZOOM": { + "description": "", + "message": "Yönelim ve Yakınlaştırmayı Sıfırla" + }, "RETURN_TO_GAME": { "description": "", "message": "Oyuna dön" diff --git a/data/lang/ui-core/zh.json b/data/lang/ui-core/zh.json index 2d4447304..5780f4d56 100644 --- a/data/lang/ui-core/zh.json +++ b/data/lang/ui-core/zh.json @@ -1643,6 +1643,10 @@ "description": "", "message": "重置" }, + "RESET_ORIENTATION_AND_ZOOM": { + "description": "", + "message": "重置方向和缩放" + }, "RETURN_TO_GAME": { "description": "", "message": "返回游戏" diff --git a/data/pigui/modules/map-sector-view.lua b/data/pigui/modules/map-sector-view.lua index 81c347ae1..4c0212575 100644 --- a/data/pigui/modules/map-sector-view.lua +++ b/data/pigui/modules/map-sector-view.lua @@ -276,7 +276,7 @@ end function Windows.edgeButtons.Show() -- view control buttons - if mainMenuButton(icons.reset_view, lc.RESET_ORIENTATION_AND_ZOOM) then + if mainMenuButton(icons.reset_view, lui.RESET_ORIENTATION_AND_ZOOM) then sectorView:ResetView() end mainMenuButton(icons.rotate_view, lui.ROTATE_VIEW) diff --git a/data/pigui/modules/settings-window.lua b/data/pigui/modules/settings-window.lua index cc66a4487..1951c5d01 100644 --- a/data/pigui/modules/settings-window.lua +++ b/data/pigui/modules/settings-window.lua @@ -21,25 +21,24 @@ end -- convert an axis binding style ID to a translation resource identifier local function localize_binding_id(str) - -- TODO: avoid reading lines from the "Core" resource (lc) - -- it's here to reuse old strings (keyboard bindings for maps in KeyBindings.inc.h) local jsonIndex = str:gsub("([^A-Z0-9_])([A-Z0-9])", "%1_%2"):upper() - return rawget(linput, jsonIndex) or rawget(lc, jsonIndex) or '[NO_JSON] '..jsonIndex + return rawget(linput, jsonIndex) or '[NO_JSON] '..jsonIndex end local function get_binding_desc(bind) if not bind then return end - local axis_names = { lc.X, lc.Y, lc.Z } + local axis_names = { linput.X, linput.Y, linput.Z } + local mouse_names = { linput.MOUSE_LMB, linput.MOUSE_MMB, linput.MOUSE_RMB } if bind.key then return Input.GetKeyName(bind.key) elseif bind.joystick and bind.hat then - return Input.GetJoystickName(bind.joystick) .. lc.HAT .. bind.hat .. lc.DIRECTION .. bind.direction + return Input.GetJoystickName(bind.joystick) .. linput.HAT .. bind.hat .. linput.DIRECTION .. bind.dir elseif bind.joystick and bind.button then - return Input.GetJoystickName(bind.joystick) .. lc.BUTTON .. bind.button + return Input.GetJoystickName(bind.joystick) .. linput.BUTTON .. bind.button elseif bind.joystick and bind.axis then - return (bind.direction < 0 and "-" or "") .. Input.GetJoystickName(bind.joystick) .. ' AXIS ' .. (axis_names[bind.axis + 1] or tostring(bind.axis)) + return (bind.direction < 0 and "-" or "") .. Input.GetJoystickName(bind.joystick) .. linput.AXIS .. (axis_names[bind.axis + 1] or tostring(bind.axis)) elseif bind.mouse then - return "MOUSE" .. bind.mouse -- FIXME + return linput.MOUSE .. (mouse_names[bind.mouse + 1] or tostring(bind.mouse)) end end @@ -493,8 +492,10 @@ local function showControlsOptions() ui.text(localize_binding_id("Page" .. page.id)) end) ui.separator() + Engine.pigui.PushID(page.id) for _,group in ipairs(page) do if group.id then + Engine.pigui.PushID(group.id) if _ > 1 then ui.text '' end ui.withFont(pionillium.medium.name, bindingGroupFontSize, function() ui.text(localize_binding_id("Group" .. group.id)) @@ -507,8 +508,10 @@ local function showControlsOptions() axisBinding(binding) end end + Engine.pigui.PopID() end end + Engine.pigui.PopID() end end diff --git a/data/pigui/modules/system-view-ui.lua b/data/pigui/modules/system-view-ui.lua index c8187eccf..0f20d92b6 100644 --- a/data/pigui/modules/system-view-ui.lua +++ b/data/pigui/modules/system-view-ui.lua @@ -181,7 +181,7 @@ local Windows = { function Windows.edgeButtons.Show() -- view control buttons - if ui.coloredSelectedIconButton(icons.reset_view, mainButtonSize, false, mainButtonFramePadding, svColor.BUTTON_ACTIVE, svColor.BUTTON_INK, lc.RESET_ORIENTATION_AND_ZOOM) then + if ui.coloredSelectedIconButton(icons.reset_view, mainButtonSize, false, mainButtonFramePadding, svColor.BUTTON_ACTIVE, svColor.BUTTON_INK, luc.RESET_ORIENTATION_AND_ZOOM) then systemView:SetVisibility("RESET_VIEW") end ui.coloredSelectedIconButton(icons.rotate_view, mainButtonSize, false, mainButtonFramePadding, svColor.BUTTON_ACTIVE, svColor.BUTTON_INK, luc.ROTATE_VIEW) diff --git a/scripts/canonicalise_translations.py b/scripts/canonicalise_translations.py index 742715157..a8709a17b 100755 --- a/scripts/canonicalise_translations.py +++ b/scripts/canonicalise_translations.py @@ -15,8 +15,8 @@ def write_translation_file(path, data): with open(path, 'w', encoding='utf-8') as fl: json.dump(data, fl, ensure_ascii=False, - indent=3, - separators=(',',' : '), + indent=2, + separators=(',',': '), sort_keys=True) fl.write('\n') diff --git a/src/KeyBindings.inc.h b/src/KeyBindings.inc.h deleted file mode 100644 index f3d288d93..000000000 --- a/src/KeyBindings.inc.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright © 2008-2020 Pioneer Developers. See AUTHORS.txt for details -// Licensed under the terms of the GPL v3. See licenses/GPL-3.txt - -#ifndef KEY_BINDING -#define KEY_BINDING(name, config_name, ui_name, default_value_1, default_value_2) -#endif -#ifndef AXIS_BINDING -#define AXIS_BINDING(name, config_name, ui_name, default_value) -#endif -#ifndef BINDING_GROUP -#define BINDING_GROUP(ui_name) -#endif -#ifndef BINDING_PAGE -#define BINDING_PAGE(ui_name) -#endif -#ifndef BINDING_PAGE_END -#define BINDING_PAGE_END() -#endif - -BINDING_PAGE(CONTROLS) -BINDING_GROUP(Lang::MISCELLANEOUS) -KEY_BINDING(toggleLuaConsole, "BindToggleLuaConsole", Lang::TOGGLE_LUA_CONSOLE, SDLK_BACKQUOTE, 0) - -BINDING_GROUP(Lang::RADAR_CONTROL) -KEY_BINDING(toggleScanMode, "BindToggleScanMode", Lang::TOGGLE_RADAR_MODE, SDLK_BACKSLASH, 0) -KEY_BINDING(increaseScanRange, "BindIncreaseScanRange", Lang::INCREASE_RADAR_RANGE, SDLK_RIGHTBRACKET, 0) -KEY_BINDING(decreaseScanRange, "BindDecreaseScanRange", Lang::DECREASE_RADAR_RANGE, SDLK_LEFTBRACKET, 0) - -BINDING_PAGE_END() - -BINDING_PAGE(VIEW) - -BINDING_GROUP(Lang::INTERNAL_VIEW) -KEY_BINDING(frontCamera, "BindFrontCamera", Lang::CAMERA_FRONT_VIEW, SDLK_KP_8, SDLK_UP) -KEY_BINDING(rearCamera, "BindRearCamera", Lang::CAMERA_REAR_VIEW, SDLK_KP_2, SDLK_DOWN) -KEY_BINDING(leftCamera, "BindLeftCamera", Lang::CAMERA_LEFT_VIEW, SDLK_KP_4, SDLK_LEFT) -KEY_BINDING(rightCamera, "BindRightCamera", Lang::CAMERA_RIGHT_VIEW, SDLK_KP_6, SDLK_RIGHT) -KEY_BINDING(topCamera, "BindTopCamera", Lang::CAMERA_TOP_VIEW, SDLK_KP_9, 0) -KEY_BINDING(bottomCamera, "BindBottomCamera", Lang::CAMERA_BOTTOM_VIEW, SDLK_KP_3, 0) - -BINDING_GROUP(Lang::EXTERNAL_VIEW) -KEY_BINDING(cameraRollLeft, "BindCameraRollLeft", Lang::ROLL_LEFT, SDLK_KP_1, 0) -KEY_BINDING(cameraRollRight, "BindCameraRollRight", Lang::ROLL_RIGHT, SDLK_KP_3, 0) -KEY_BINDING(cameraRotateDown, "BindCameraRotateDown", Lang::ROTATE_DOWN, SDLK_KP_2, SDLK_DOWN) -KEY_BINDING(cameraRotateUp, "BindCameraRotateUp", Lang::ROTATE_UP, SDLK_KP_8, SDLK_UP) -KEY_BINDING(cameraRotateLeft, "BindCameraRotateLeft", Lang::ROTATE_LEFT, SDLK_KP_4, SDLK_LEFT) -KEY_BINDING(cameraRotateRight, "BindCameraRotateRight", Lang::ROTATE_RIGHT, SDLK_KP_6, SDLK_RIGHT) -KEY_BINDING(resetCamera, "BindResetCamera", Lang::RESET, SDLK_HOME, 0) - -BINDING_PAGE_END() - -#undef KEY_BINDING -#undef AXIS_BINDING -#undef BINDING_GROUP -#undef BINDING_PAGE -#undef BINDING_PAGE_END diff --git a/src/LangStrings.inc.h b/src/LangStrings.inc.h index 09938bcbb..9d74bfe1b 100644 --- a/src/LangStrings.inc.h +++ b/src/LangStrings.inc.h @@ -72,13 +72,6 @@ DECLARE_STRING(CTRL) DECLARE_STRING(ALT) DECLARE_STRING(META) DECLARE_STRING(JOY) -DECLARE_STRING(BUTTON) -DECLARE_STRING(HAT) -DECLARE_STRING(DIRECTION) -DECLARE_STRING(X) -DECLARE_STRING(Y) -DECLARE_STRING(Z) -DECLARE_STRING(JOY_AXIS) DECLARE_STRING(WEAPONS) DECLARE_STRING(TARGET_OBJECT_IN_SIGHTS) DECLARE_STRING(FIRE_LASER) @@ -105,7 +98,6 @@ DECLARE_STRING(RADAR_CONTROL) DECLARE_STRING(TOGGLE_RADAR_MODE) DECLARE_STRING(INCREASE_RADAR_RANGE) DECLARE_STRING(DECREASE_RADAR_RANGE) -DECLARE_STRING(TOGGLE_LUA_CONSOLE) DECLARE_STRING(TOGGLE_ROTATION_DAMPING) DECLARE_STRING(TOGGLE_HUD_MODE) DECLARE_STRING(JOYSTICK_INPUT) @@ -170,23 +162,6 @@ DECLARE_STRING(SIDEREAL_VIEW) DECLARE_STRING(FLYBY_VIEW) DECLARE_STRING(SECTOR_MAP_VIEW) DECLARE_STRING(SEARCH_MAP) -DECLARE_STRING(MAP_LOCK_HYPERSPACE_TARGET) -DECLARE_STRING(MAP_TOGGLE_INFO_PANEL) -DECLARE_STRING(MAP_TOGGLE_SELECTION_FOLLOW_VIEW) -DECLARE_STRING(MAP_WARP_TO_CURRENT_SYSTEM) -DECLARE_STRING(MAP_WARP_TO_SELECTED_SYSTEM) -DECLARE_STRING(MAP_WARP_TO_HYPERSPACE_TARGET) -DECLARE_STRING(MAP_VIEW_SHIFT_FORWARD) -DECLARE_STRING(MAP_VIEW_SHIFT_BACKWARD) -DECLARE_STRING(MAP_VIEW_SHIFT_LEFT) -DECLARE_STRING(MAP_VIEW_SHIFT_RIGHT) -DECLARE_STRING(MAP_VIEW_SHIFT_UP) -DECLARE_STRING(MAP_VIEW_SHIFT_DOWN) -DECLARE_STRING(MAP_VIEW_ROTATE_LEFT) -DECLARE_STRING(MAP_VIEW_ROTATE_RIGHT) -DECLARE_STRING(MAP_VIEW_ROTATE_UP) -DECLARE_STRING(MAP_VIEW_ROTATE_DOWN) -DECLARE_STRING(RESET_ORIENTATION_AND_ZOOM) DECLARE_STRING(ROTATE_UP) DECLARE_STRING(ROTATE_DOWN) DECLARE_STRING(ROTATE_LEFT) From a91eed678ca505b6942ecdf1ea6438f81e5f8429 Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Sat, 12 Sep 2020 18:18:56 -0400 Subject: [PATCH 4/7] Refactor IniConfig, Input optionally saves config IniConfig::Save() writes the config to the file it was Read() from. GameConfig is reduced to defaults only Several Input methods now maintain config state as well as input class state Set default screen resolution to 1280x720 This is an opinionated default, but given that >90% of our userbase has a widescreen monitor (and we're getting rid of the 800x600 unit metric) it's time to move on. Fix missing config file preventing saving config --- src/GameConfig.cpp | 16 +++------------- src/GameConfig.h | 3 --- src/Input.cpp | 18 ++++++++++++++++++ src/Input.h | 8 ++++++-- src/core/IniConfig.cpp | 41 +++++++++++++++++++++++++++++++++-------- src/core/IniConfig.h | 20 ++++++++++++++++---- 6 files changed, 76 insertions(+), 30 deletions(-) diff --git a/src/GameConfig.cpp b/src/GameConfig.cpp index eb0e0777f..664370543 100644 --- a/src/GameConfig.cpp +++ b/src/GameConfig.cpp @@ -12,8 +12,8 @@ GameConfig::GameConfig(const map_string &override_) map["AMD_MESA_HACKS"] = "0"; map["DisableSound"] = "0"; map["StartFullscreen"] = "0"; - map["ScrWidth"] = "800"; - map["ScrHeight"] = "600"; + map["ScrWidth"] = "1280"; + map["ScrHeight"] = "720"; map["UIScaleFactor"] = "1"; map["DetailCities"] = "1"; map["DetailPlanets"] = "1"; @@ -50,7 +50,7 @@ GameConfig::GameConfig(const map_string &override_) map["EnableGPUJobs"] = "1"; map["GL3ForwardCompatible"] = "1"; - Load(); + Read(FileSystem::userFiles, "config.ini"); for (auto i = override_.begin(); i != override_.end(); ++i) { const std::string &key = (*i).first; @@ -58,13 +58,3 @@ GameConfig::GameConfig(const map_string &override_) map[key] = val; } } - -void GameConfig::Load() -{ - Read(FileSystem::userFiles, "config.ini"); -} - -bool GameConfig::Save() -{ - return Write(FileSystem::userFiles, "config.ini"); -} diff --git a/src/GameConfig.h b/src/GameConfig.h index 401bff3ac..3e0646a15 100644 --- a/src/GameConfig.h +++ b/src/GameConfig.h @@ -10,9 +10,6 @@ class GameConfig : public IniConfig { public: typedef std::map map_string; GameConfig(const map_string &override_ = map_string()); - - void Load(); - bool Save(); }; #endif diff --git a/src/Input.cpp b/src/Input.cpp index 265d27471..5d5a39ea8 100644 --- a/src/Input.cpp +++ b/src/Input.cpp @@ -373,6 +373,24 @@ float Manager::JoystickAxisState(int joystick, int axis) return GetJoysticks()[joystick].axes[axis].value; } +void Manager::SetJoystickEnabled(bool state) +{ + joystickEnabled = state; + if (m_enableConfigSaving) { + m_config->SetInt("EnableJoystick", joystickEnabled); + m_config->Save(); + } +} + +void Manager::SetMouseYInvert(bool state) +{ + mouseYInvert = state; + if (m_enableConfigSaving) { + m_config->SetInt("InvertMouseY", mouseYInvert); + m_config->Save(); + } +} + /* FRAME AND EVENT HANDLING diff --git a/src/Input.h b/src/Input.h index dfa512fc6..8bd917638 100644 --- a/src/Input.h +++ b/src/Input.h @@ -126,6 +126,9 @@ public: // Call immediately after processing events, dispatches events to Action / Axis bindings. void DispatchEvents(); + // When enable is false, this prevents the input system from writing to the config file. + void EnableConfigSaving(bool enable) { m_enableConfigSaving = enable; } + BindingPage *GetBindingPage(std::string id) { return &bindingPages[id]; } std::map GetBindingPages() { return bindingPages; } @@ -182,10 +185,10 @@ public: float JoystickAxisState(int joystick, int axis); bool IsJoystickEnabled() { return joystickEnabled; } - void SetJoystickEnabled(bool state) { joystickEnabled = state; } + void SetJoystickEnabled(bool state); - void SetMouseYInvert(bool state) { mouseYInvert = state; } bool IsMouseYInvert() { return mouseYInvert; } + void SetMouseYInvert(bool state); int MouseButtonState(int button) { return mouseButton[button]; } void SetMouseButtonState(int button, bool state) { mouseButton[button] = state; } @@ -220,6 +223,7 @@ private: float GetAxisState(InputBindings::JoyAxis &axis); IniConfig *m_config; + bool m_enableConfigSaving; std::map keyState; int keyModState; diff --git a/src/core/IniConfig.cpp b/src/core/IniConfig.cpp index e6aa9ba13..aeec562cb 100644 --- a/src/core/IniConfig.cpp +++ b/src/core/IniConfig.cpp @@ -30,9 +30,9 @@ void IniConfig::SetString(const std::string §ion, const std::string &key, co int IniConfig::Int(const std::string §ion, const std::string &key, int defval) const { - SectionMapType::const_iterator secIt = m_map.find(section); + const auto secIt = m_map.find(section); if (secIt == m_map.end()) return defval; - MapType::const_iterator it = secIt->second.find(key); + const auto it = secIt->second.find(key); if (it == secIt->second.end()) return defval; const StringRange val = StringRange(it->second.c_str(), it->second.size()).StripSpace(); @@ -45,9 +45,9 @@ int IniConfig::Int(const std::string §ion, const std::string &key, int defva float IniConfig::Float(const std::string §ion, const std::string &key, float defval) const { - SectionMapType::const_iterator secIt = m_map.find(section); + const auto secIt = m_map.find(section); if (secIt == m_map.end()) return defval; - MapType::const_iterator it = secIt->second.find(key); + const auto it = secIt->second.find(key); if (it == secIt->second.end()) return defval; const StringRange val = StringRange(it->second.c_str(), it->second.size()).StripSpace(); @@ -60,17 +60,32 @@ float IniConfig::Float(const std::string §ion, const std::string &key, float std::string IniConfig::String(const std::string §ion, const std::string &key, const std::string &defval) const { - SectionMapType::const_iterator secIt = m_map.find(section); + const auto secIt = m_map.find(section); if (secIt == m_map.end()) return defval; - MapType::const_iterator it = secIt->second.find(key); + const auto it = secIt->second.find(key); if (it == secIt->second.end()) return defval; return it->second; } void IniConfig::Read(FileSystem::FileSource &fs, const std::string &path) { + // FIXME: add a mechanism to determine if a FileSource is suitable for writing + // We use dynamic_cast here as a very simple hack because IniConfig::Read isn't + // intended to be called very often. + auto *sourceFs = dynamic_cast(&fs); + if (sourceFs != nullptr) { + m_fs = sourceFs; + m_path = path; + } else { + m_fs = nullptr; + m_path.clear(); + } + RefCountedPtr data = fs.ReadFile(path); - if (data) Read(*data); + if (!data) + return; + + Read(*data); } void IniConfig::Read(const FileSystem::FileData &data) @@ -123,7 +138,7 @@ bool IniConfig::Write(FileSystem::FileSourceFS &fs, const std::string &path) Output("Could not write config file '%s'\n", FileSystem::JoinPath(fs.GetRoot(), path).c_str()); return false; } - for (SectionMapType::const_iterator secIt = m_map.begin(); secIt != m_map.end(); ++secIt) { + for (auto secIt = m_map.cbegin(); secIt != m_map.cend(); ++secIt) { const MapType &map = secIt->second; if (map.empty()) continue; @@ -140,3 +155,13 @@ bool IniConfig::Write(FileSystem::FileSourceFS &fs, const std::string &path) fclose(f); return true; } + +bool IniConfig::Save() +{ + if (!m_fs || m_path.empty()) { + Output("Attempted to write uninitialized IniConfig. Did you forget to Read() first?"); + return false; + } + + return Write(*m_fs, m_path); +} diff --git a/src/core/IniConfig.h b/src/core/IniConfig.h index 4135364e8..f958e3fd5 100644 --- a/src/core/IniConfig.h +++ b/src/core/IniConfig.h @@ -15,12 +15,19 @@ namespace FileSystem { class IniConfig { public: - IniConfig() {} + IniConfig() = default; + // Read from a file on disk. If fs is a FileSourceFS, enables in-place saving + // of the IniConfig. void Read(FileSystem::FileSource &fs, const std::string &path); - void Read(const FileSystem::FileData &data); + + // Write to a file on disk. bool Write(FileSystem::FileSourceFS &fs, const std::string &path); + // If a previous call to Read() was made using a writable file source, + // save the IniConfig in-place to that file. + bool Save(); + void SetInt(const std::string §ion, const std::string &key, int val); void SetFloat(const std::string §ion, const std::string &key, float val); void SetString(const std::string §ion, const std::string &key, const std::string &val); @@ -39,21 +46,26 @@ public: bool HasSection(const std::string §ion) const { - SectionMapType::const_iterator it = m_map.find(section); + const auto it = m_map.find(section); return (it != m_map.end()) && (!it->second.empty()); } bool HasEntry(const std::string §ion, const std::string &key) const { - SectionMapType::const_iterator it = m_map.find(section); + const auto it = m_map.find(section); return (it != m_map.end()) && it->second.count(key); } bool HasEntry(const std::string &key) const { return HasEntry("", key); } protected: + void Read(const FileSystem::FileData &data); + typedef std::map MapType; typedef std::map SectionMapType; SectionMapType m_map; + + FileSystem::FileSourceFS *m_fs = nullptr; + std::string m_path; }; #endif /* _INICONFIG_H */ From e0451219d845d11734628d8c98cd189d21c91525 Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Wed, 21 Oct 2020 15:50:49 -0400 Subject: [PATCH 5/7] Move reverse-container implementation to utils.h Fix unused variable warnings. Add SmoothEasing function - A variable-exponent easing function for input and friends - It's not as performant as the optimized InOut*Easing variants so use sparingly - Based on the mentioned blog and reverse-engineering the Godot implementation --- src/AnimationCurves.h | 27 +++++++++++++++++++++++++++ src/Input.cpp | 18 +----------------- src/ModelViewer.cpp | 29 ----------------------------- src/utils.cpp | 24 ++++++++++++++++++++++++ src/utils.h | 29 +++++++++++++++++++++++++++++ 5 files changed, 81 insertions(+), 46 deletions(-) diff --git a/src/AnimationCurves.h b/src/AnimationCurves.h index 9c18a6bf3..39245a4e2 100644 --- a/src/AnimationCurves.h +++ b/src/AnimationCurves.h @@ -58,6 +58,33 @@ namespace AnimationCurves { return 0.5 * (1.0 - std::cos(p * M_PI)); } + // Based on http://blog.moagrius.com/actionscript/jsas-understanding-easing/ + // and observations from Godot Engine and Star Citizen + // This supports four different easing functions encoded in a single float: + // e == 1.0|-1.0: linear easing, returns p + // e > 1.0: in-quadratic, in-cubic etc. for e = 2.0, e = 3.0 ... + // e < 1.0: out-quadratic, out-cubic etc. for e = 0.5, e = 0.33_ ... + // e == 0.0: returns zero + // e > -1.0: reverse inout-quadratic, inout-cubic for e = -0.5, e = -0.33_ ... + // e < -1.0: inout-quadratic, inout-cubic etc. for e = -2.0, e = -3.0 ... + inline float SmoothEasing(double p, double e) + { + // e > 0.0 = ease in or out / e < 0.0 = ease in-out + if (e > 0.0) { + // e < 1.0 = ease out / e > 1.0 = ease in + return e < 1.0 ? (1.0 - pow(1.0 - p, 1.0 / e)) : (pow(p, e)); + } else if (e < 0) { + // Ease in-out at arbirary exponents (the e term is negated to get positive exponent) + float m = (p - 0.5) * 2.0; + float t = p * 2.0; + if (t < 1) + return pow(t, -e) * 0.5; + else + return (1.0 - pow(1.0 - m, -e)) * 0.5 + 0.5; + } else // completely flat at 0.0 + return 0.0; + } + } // namespace AnimationCurves #endif diff --git a/src/Input.cpp b/src/Input.cpp index 5d5a39ea8..c3bfb17b1 100644 --- a/src/Input.cpp +++ b/src/Input.cpp @@ -420,22 +420,6 @@ void Manager::NewFrame() } } -template -struct reverse_iter { - using iterator = typename T::reverse_iterator; - using const_iterator = typename T::const_reverse_iterator; - reverse_iter(T &obj) : - m_obj(obj) {} - - iterator begin() { return m_obj.rbegin(); } - iterator end() { return m_obj.rend(); } - const_iterator begin() const { return m_obj.crbegin(); } - const_iterator end() const { return m_obj.crend(); } - -private: - T &m_obj; -}; - void Manager::RebuildInputFrames() { // Reset the list of active chords. @@ -443,7 +427,7 @@ void Manager::RebuildInputFrames() m_activeActions.clear(); m_activeAxes.clear(); - for (const auto *frame : reverse_iter(m_inputFrames)) { + for (const auto *frame : reverse_container(m_inputFrames)) { // Push all enabled key chords onto the key chord stack. for (auto *action : frame->actions) { diff --git a/src/ModelViewer.cpp b/src/ModelViewer.cpp index 52a8b0a38..cd0bc4263 100644 --- a/src/ModelViewer.cpp +++ b/src/ModelViewer.cpp @@ -67,35 +67,6 @@ namespace { } } // namespace -// An adaptor for automagic reverse range-for iteration of containers -// One might be able to specialize this for raw arrays, but that's beyond the -// point of its use-case. -// One might also point out that this is surely more work to code than simply -// writing an explicit iterator loop, to which I say: bah humbug! -template -struct reverse_container_t { - using iterator = std::reverse_iterator; - using const_iterator = std::reverse_iterator; - - using value_type = typename std::remove_reference::type; - - reverse_container_t(value_type &ref) : - ref(ref) {} - - iterator begin() { return iterator(ref.end()); } - const_iterator begin() const { return const_iterator(ref.cend()); } - - iterator end() { return iterator(ref.begin()); } - const_iterator end() const { return const_iterator(ref.cbegin()); } - -private: - value_type &ref; -}; - -// Use this function for automatic template parameter deduction -template -reverse_container_t reverse_container(T &ref) { return reverse_container_t(ref); } - namespace ImGui { bool ColorEdit3(const char *label, Color &color) { diff --git a/src/utils.cpp b/src/utils.cpp index 7bf0a7ffa..e94098037 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -279,12 +279,14 @@ void Vector3fToStr(const vector3f &val, char *out, size_t size) #ifdef USE_HEX_FLOATS const int amt = std::sprintf(out, "%a,%a,%a", val.x, val.y, val.z); assert(static_cast(amt) <= size); + (void)amt; #else fu32 a(val.x); fu32 b(val.y); fu32 c(val.z); const int amt = sprintf(out, "(%" PRIu32 ",%" PRIu32 ",%" PRIu32 ")", a.u, b.u, c.u); assert(static_cast(amt) <= size); + (void)amt; #endif } @@ -295,12 +297,14 @@ void Vector3dToStr(const vector3d &val, char *out, size_t size) #ifdef USE_HEX_FLOATS const int amt = std::sprintf(out, "%la,%la,%la", val.x, val.y, val.z); assert(static_cast(amt) <= size); + (void)amt; #else fu64 a(val.x); fu64 b(val.y); fu64 c(val.z); const int amt = sprintf(out, "(%" PRIu64 ",%" PRIu64 ",%" PRIu64 ")", a.u, b.u, c.u); assert(static_cast(amt) <= size); + (void)amt; #endif } @@ -311,6 +315,7 @@ void Matrix3x3fToStr(const matrix3x3f &val, char *out, size_t size) #ifdef USE_HEX_FLOATS const int amt = std::sprintf(out, "%a,%a,%a,%a,%a,%a,%a,%a,%a", val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7], val[8]); assert(static_cast(amt) <= size); + (void)amt; #else fu32 fuvals[9]; for (int i = 0; i < 9; i++) @@ -323,6 +328,7 @@ void Matrix3x3fToStr(const matrix3x3f &val, char *out, size_t size) fuvals[3].u, fuvals[4].u, fuvals[5].u, fuvals[6].u, fuvals[7].u, fuvals[8].u); assert(static_cast(amt) <= size); + (void)amt; #endif } @@ -333,6 +339,7 @@ void Matrix3x3dToStr(const matrix3x3d &val, char *out, size_t size) #ifdef USE_HEX_FLOATS const int amt = std::sprintf(out, "%a,%a,%a,%a,%a,%a,%a,%a,%a", val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7], val[8]); assert(static_cast(amt) <= size); + (void)amt; #else fu64 fuvals[9]; for (int i = 0; i < 9; i++) @@ -345,6 +352,7 @@ void Matrix3x3dToStr(const matrix3x3d &val, char *out, size_t size) fuvals[3].u, fuvals[4].u, fuvals[5].u, fuvals[6].u, fuvals[7].u, fuvals[8].u); assert(static_cast(amt) <= size); + (void)amt; #endif } @@ -355,6 +363,7 @@ void Matrix4x4fToStr(const matrix4x4f &val, char *out, size_t size) #ifdef USE_HEX_FLOATS const int amt = std::sprintf(out, "%a,%a,%a,%a,%a,%a,%a,%a,%a,%a,%a,%a,%a,%a,%a,%a", val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7], val[8], val[9], val[10], val[11], val[12], val[13], val[14], val[15]); assert(static_cast(amt) <= size); + (void)amt; #else fu32 fuvals[16]; for (int i = 0; i < 16; i++) @@ -369,6 +378,7 @@ void Matrix4x4fToStr(const matrix4x4f &val, char *out, size_t size) fuvals[8].u, fuvals[9].u, fuvals[10].u, fuvals[11].u, fuvals[12].u, fuvals[13].u, fuvals[14].u, fuvals[15].u); assert(static_cast(amt) <= size); + (void)amt; #endif } @@ -379,6 +389,7 @@ void Matrix4x4dToStr(const matrix4x4d &val, char *out, size_t size) #ifdef USE_HEX_FLOATS const int amt = std::sprintf(out, "%a,%a,%a,%a,%a,%a,%a,%a,%a,%a,%a,%a,%a,%a,%a,%a", val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7], val[8], val[9], val[10], val[11], val[12], val[13], val[14], val[15]); assert(static_cast(amt) <= size); + (void)amt; #else fu64 fuvals[16]; for (int i = 0; i < 16; i++) @@ -393,6 +404,7 @@ void Matrix4x4dToStr(const matrix4x4d &val, char *out, size_t size) fuvals[8].u, fuvals[9].u, fuvals[10].u, fuvals[11].u, fuvals[12].u, fuvals[13].u, fuvals[14].u, fuvals[15].u); assert(static_cast(amt) <= size); + (void)amt; #endif } @@ -447,6 +459,7 @@ float StrToFloat(const std::string &str) fu32 uval; const int amt = sscanf(str.c_str(), "%" SCNu32, &uval.u); assert(amt == 1); + (void)amt; return uval.f; #endif } @@ -464,6 +477,7 @@ double StrToDouble(const std::string &str) static_assert(sizeof(long long) == sizeof(uint64_t), "long long isn't equal in size to uint64_t"); fu64 uval; const int amt = sscanf(str.c_str(), "%" SCNu64, &uval.u); + (void)amt; assert(amt == 1); return uval.d; #endif @@ -495,10 +509,12 @@ void StrToVector3f(const char *str, vector3f &val) #ifdef USE_HEX_FLOATS const int amt = std::sscanf(str, "%a,%a,%a", &val.x, &val.y, &val.z); assert(amt == 3); + (void)amt; #else fu32 a, b, c; const int amt = std::sscanf(str, "(%" SCNu32 ",%" SCNu32 ",%" SCNu32 ")", &a.u, &b.u, &c.u); assert(amt == 3); + (void)amt; val.x = a.f; val.y = b.f; val.z = c.f; @@ -511,10 +527,12 @@ void StrToVector3d(const char *str, vector3d &val) #ifdef USE_HEX_FLOATS const int amt = std::sscanf(str, "%la,%la,%la", &val.x, &val.y, &val.z); assert(amt == 3); + (void)amt; #else fu64 a, b, c; const int amt = std::sscanf(str, "(%" SCNu64 ",%" SCNu64 ",%" SCNu64 ")", &a.u, &b.u, &c.u); assert(amt == 3); + (void)amt; val.x = a.d; val.y = b.d; val.z = c.d; @@ -527,6 +545,7 @@ void StrToMatrix3x3f(const char *str, matrix3x3f &val) #ifdef USE_HEX_FLOATS const int amt = std::sscanf(str, "%a,%a,%a,%a,%a,%a,%a,%a,%a", &val[0], &val[1], &val[2], &val[3], &val[4], &val[5], &val[6], &val[7], &val[8]); assert(amt == 9); + (void)amt; #else fu32 fu[9]; const int amt = std::sscanf(str, "(%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ")", @@ -534,6 +553,7 @@ void StrToMatrix3x3f(const char *str, matrix3x3f &val) &fu[3].u, &fu[4].u, &fu[5].u, &fu[6].u, &fu[7].u, &fu[8].u); assert(amt == 9); + (void)amt; for (int i = 0; i < 9; i++) val[i] = fu[i].f; #endif @@ -545,6 +565,7 @@ void StrToMatrix3x3d(const char *str, matrix3x3d &val) #ifdef USE_HEX_FLOATS const int amt = std::sscanf(str, "%la,%la,%la,%la,%la,%la,%la,%la,%la", &val[0], &val[1], &val[2], &val[3], &val[4], &val[5], &val[6], &val[7], &val[8]); assert(amt == 9); + (void)amt; #else fu64 fu[9]; const int amt = std::sscanf(str, "(%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ")", @@ -552,6 +573,7 @@ void StrToMatrix3x3d(const char *str, matrix3x3d &val) &fu[3].u, &fu[4].u, &fu[5].u, &fu[6].u, &fu[7].u, &fu[8].u); assert(amt == 9); + (void)amt; for (int i = 0; i < 9; i++) val[i] = fu[i].d; #endif @@ -571,6 +593,7 @@ void StrToMatrix4x4f(const char *str, matrix4x4f &val) &fu[8].u, &fu[9].u, &fu[10].u, &fu[11].u, &fu[12].u, &fu[13].u, &fu[14].u, &fu[15].u); assert(amt == 16); + (void)amt; for (int i = 0; i < 16; i++) val[i] = fu[i].f; #endif @@ -590,6 +613,7 @@ void StrToMatrix4x4d(const char *str, matrix4x4d &val) &fu[8].u, &fu[9].u, &fu[10].u, &fu[11].u, &fu[12].u, &fu[13].u, &fu[14].u, &fu[15].u); assert(amt == 16); + (void)amt; for (int i = 0; i < 16; i++) val[i] = fu[i].d; #endif diff --git a/src/utils.h b/src/utils.h index dfe5ae227..4e24eb288 100644 --- a/src/utils.h +++ b/src/utils.h @@ -269,6 +269,35 @@ static inline Uint32 ceil_pow2(Uint32 v) return v; } +// An adaptor for automagic reverse range-for iteration of containers +// One might be able to specialize this for raw arrays, but that's beyond the +// point of its use-case. +// One might also point out that this is surely more work to code than simply +// writing an explicit iterator loop, to which I say: bah humbug! +template +struct reverse_container_t { + using iterator = typename T::reverse_iterator; + using const_iterator = typename T::const_reverse_iterator; + + using value_type = typename std::remove_reference::type; + + reverse_container_t(value_type &ref) : + ref(ref) {} + + iterator begin() { return ref.rbegin(); } + const_iterator begin() const { return ref.crbegin(); } + + iterator end() { return ref.rend(); } + const_iterator end() const { return ref.crend(); } + +private: + value_type &ref; +}; + +// Use this function for automatic template parameter deduction +template +reverse_container_t reverse_container(T &ref) { return reverse_container_t(ref); } + void hexdump(const unsigned char *buf, int bufsz); #endif /* _UTILS_H */ From d3501e015ba9ab26f7134ed3f4c7f14977c11631 Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Mon, 9 Nov 2020 00:02:27 -0500 Subject: [PATCH 6/7] Add deadzone, sensitivity, and half-axis mode Restores joystick deadzone settings and adds user-set sensitivity curves and half-axis mode This functionality has not yet been exposed to Lua / PiGui and must be configured in config.ini --- src/Input.cpp | 70 ++++++++++++++++++++++++++++++++++++++++++++------- src/Input.h | 9 ++++--- 2 files changed, 67 insertions(+), 12 deletions(-) diff --git a/src/Input.cpp b/src/Input.cpp index c3bfb17b1..a097b9bd7 100644 --- a/src/Input.cpp +++ b/src/Input.cpp @@ -2,6 +2,7 @@ // Licensed under the terms of the GPL v3. See licenses/GPL-3.txt #include "Input.h" +#include "AnimationCurves.h" #include "GameConfig.h" #include "InputBindings.h" #include "Pi.h" @@ -11,6 +12,7 @@ #include "ui/Context.h" #include +#include #include #include @@ -49,7 +51,7 @@ namespace Input { std::string Input::JoystickName(int joystick) { - return std::string(SDL_JoystickName(m_joysticks[joystick].joystick)); + return m_joysticks[joystick].name; } std::string Input::JoystickGUIDString(int joystick) @@ -92,12 +94,30 @@ SDL_JoystickGUID Input::JoystickGUID(int joystick) return m_joysticks[joystick].guid; } -void Input::InitJoysticks() +static std::string saveAxisConfig(const Input::JoystickInfo::Axis &axis) +{ + return fmt::format("DZ{:.1f} CV{:.1f}{}", axis.deadzone, axis.curve, (axis.zeroToOne ? " Half" : "")); +} + +static void loadAxisConfig(const std::string &str, Input::JoystickInfo::Axis &outAxis) +{ + std::regex matcher("DZ([\\d\\.]+)\\s*(?:CV(-?[\\d\\.]+))?\\s*(Half)?", std::regex::icase); + std::smatch match_results; + if (std::regex_search(str, match_results, matcher)) { + outAxis.deadzone = std::stof(match_results[1].str()); + outAxis.curve = match_results[2].matched ? std::stof(match_results[2].str()) : 1.0; + outAxis.zeroToOne = match_results[3].matched; + } + outAxis.value = 0; +} + +void Input::InitJoysticks(IniConfig *config) { SDL_Init(SDL_INIT_JOYSTICK); int joy_count = SDL_NumJoysticks(); Output("Initializing joystick subsystem.\n"); + for (int n = 0; n < joy_count; n++) { JoystickInfo state; @@ -107,6 +127,7 @@ void Input::InitJoysticks() continue; } + state.name = SDL_JoystickName(state.joystick); state.guid = SDL_JoystickGetGUID(state.joystick); state.axes.resize(SDL_JoystickNumAxes(state.joystick)); state.buttons.resize(SDL_JoystickNumButtons(state.joystick)); @@ -117,9 +138,26 @@ void Input::InitJoysticks() Output("Found joystick '%s' (GUID: %s)\n", SDL_JoystickName(state.joystick), joystickGUIDName.data()); Output(" - %ld axes, %ld buttons, %ld hats\n", state.axes.size(), state.buttons.size(), state.hats.size()); + std::string joystickName = "Joystick." + std::string(joystickGUIDName.data()); + config->SetString(joystickName, "Name", state.name); + + for (size_t i = 0; i < state.axes.size(); i++) { + std::string axisName = "Axis" + std::to_string(i); + if (!config->HasEntry(joystickName, axisName)) { + config->SetString(joystickName, axisName, saveAxisConfig(state.axes[i])); + continue; + } + + loadAxisConfig(config->String(joystickName, axisName, ""), state.axes[i]); + Output(" - axis %ld: deadzone %.2f, curve: %.2f, half-axis mode: %b\n", + i, state.axes[i].deadzone, state.axes[i].curve, state.axes[i].zeroToOne); + } + SDL_JoystickID joyID = SDL_JoystickInstanceID(state.joystick); m_joysticks[joyID] = state; } + + config->Save(); } std::map &Input::GetJoysticks() @@ -146,7 +184,7 @@ Manager::Manager(IniConfig *config) : joystickEnabled = (m_config->Int("EnableJoystick")) ? true : false; mouseYInvert = (m_config->Int("InvertMouseY")) ? true : false; - Input::InitJoysticks(); + Input::InitJoysticks(m_config); } void Manager::InitGame() @@ -164,7 +202,9 @@ void Manager::InitGame() JoystickInfo &state = pair.second; std::fill(state.buttons.begin(), state.buttons.end(), false); std::fill(state.hats.begin(), state.hats.end(), 0); - std::fill(state.axes.begin(), state.axes.end(), JoystickInfo::Axis{ 0.0f, 0.0f }); + for (auto &ax : state.axes) { + ax.value = 0.0; + } } } @@ -477,6 +517,16 @@ static int8_t keys_in_chord(InputBindings::KeyChord *chord) return chord->activator.Enabled() + chord->modifier1.Enabled() + chord->modifier2.Enabled(); } +static float applyDeadzoneAndCurve(const JoystickInfo::Axis &axis, float value) +{ + float absVal = std::fabs(value); + float sign = value < 0.0 ? 1.0 : -1.0; + if (absVal < axis.deadzone) return 0.0f; + // renormalize value to 0..1 after deadzone + absVal = (absVal - axis.deadzone) * (1.0 / (1.0 - axis.deadzone)); + return AnimationCurves::SmoothEasing(absVal, axis.curve) * sign; +} + void Manager::HandleSDLEvent(SDL_Event &event) { using namespace InputBindings; @@ -516,14 +566,16 @@ void Manager::HandleSDLEvent(SDL_Event &event) mouseMotion[0] += event.motion.xrel; mouseMotion[1] += event.motion.yrel; break; - case SDL_JOYAXISMOTION: + case SDL_JOYAXISMOTION: { if (!GetJoysticks()[event.jaxis.which].joystick) break; - if (event.jaxis.value == -32768) - GetJoysticks()[event.jaxis.which].axes[event.jaxis.axis].value = 1.f; + auto &axis = GetJoysticks()[event.jaxis.which].axes[event.jaxis.axis]; + if (axis.zeroToOne) + // assume -32768 == 0.0 in half-axis mode (this is true for most controllers) + axis.value = applyDeadzoneAndCurve(axis, (event.jaxis.value + 32768) / 65535.f); else - GetJoysticks()[event.jaxis.which].axes[event.jaxis.axis].value = -event.jaxis.value / 32767.f; - break; + axis.value = applyDeadzoneAndCurve(axis, (event.jaxis.value == -32768 ? -1.f : event.jaxis.value / 32767.f)); + } break; case SDL_JOYBUTTONUP: case SDL_JOYBUTTONDOWN: if (!GetJoysticks()[event.jaxis.which].joystick) diff --git a/src/Input.h b/src/Input.h index 8bd917638..57ce431a1 100644 --- a/src/Input.h +++ b/src/Input.h @@ -74,12 +74,15 @@ namespace Input { struct JoystickInfo { struct Axis { - float value; - float deadzone; + float value = 0.0; + float deadzone = 0.0; + float curve = 1.0; + bool zeroToOne = false; }; SDL_Joystick *joystick; SDL_JoystickGUID guid; + std::string name; std::vector buttons; std::vector hats; @@ -87,7 +90,7 @@ namespace Input { std::vector axes; }; - void InitJoysticks(); + void InitJoysticks(IniConfig *config); std::map &GetJoysticks(); // User display name for the joystick from the API/OS. From 74fdc0e98350e83d16b01e09ec1762cfa4747ed7 Mon Sep 17 00:00:00 2001 From: Webster Sheets Date: Wed, 11 Nov 2020 18:52:33 -0500 Subject: [PATCH 7/7] Fix Economy assert in debug mode --- src/galaxy/Economy.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/galaxy/Economy.cpp b/src/galaxy/Economy.cpp index ffd9dc4b6..51a62b778 100644 --- a/src/galaxy/Economy.cpp +++ b/src/galaxy/Economy.cpp @@ -106,8 +106,8 @@ namespace GalacticEconomy { out.price = j["price"]; - const Json &legality = j["default_legality"]; - if (legality.is_array()) { + if (j.count("default_legality") && j["default_legality"].is_array()) { + const Json &legality = j["default_legality"]; out.default_legality = fixed(legality[0].get(), legality[1].get()); } else { out.default_legality = fixed(1, 1);