1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-03-29 21:56:54 +02:00

Unified game controller input with keyboard/mouse

This commit is contained in:
Ivan Savenko 2024-04-30 13:36:29 +03:00
parent 9a71614588
commit 1dc27046ef
10 changed files with 202 additions and 403 deletions

@ -35,7 +35,6 @@ set(client_SRCS
eventsSDL/InputSourceText.cpp
eventsSDL/InputSourceTouch.cpp
eventsSDL/InputSourceGameController.cpp
eventsSDL/GameControllerConfig.cpp
gui/CGuiHandler.cpp
gui/CIntObject.cpp
@ -214,8 +213,7 @@ set(client_HEADERS
eventsSDL/InputSourceMouse.h
eventsSDL/InputSourceText.h
eventsSDL/InputSourceTouch.h
eventsSDL/InputSourceGameController.h
eventsSDL/GameControllerConfig.h
eventsSDL/InputSourceGameController.h
gui/CGuiHandler.h
gui/CIntObject.h

@ -1,212 +0,0 @@
/*
* GameControllerConfig.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include <SDL.h>
#include "StdInc.h"
#include "GameControllerConfig.h"
#include "../gui/CGuiHandler.h"
#include "../gui/ShortcutHandler.h"
GameControllerConfig::GameControllerConfig(): leftAxisType(AxisType::NONE), rightAxisType(AxisType::NONE)
{
load();
}
void GameControllerConfig::load()
{
const JsonNode config = JsonUtils::assembleFromFiles("config/shortcutsConfig");
for(auto const & entry : config["joystick"].Struct())
{
std::string configName = entry.first;
if(configName == "leftaxis")
leftAxisType = parseAxis(entry.first, entry.second);
else if (configName == "rightaxis")
rightAxisType = parseAxis(entry.first, entry.second);
else if (configName == "lefttrigger" || configName == "righttrigger")
parseTrigger(entry.first, entry.second);
else
parseButton(entry.first, entry.second);
}
}
AxisType GameControllerConfig::parseAxis(const std::string & key, const JsonNode & value)
{
if(!value.isString())
{
logGlobal->error("The value of joystick config key %s should be a string!", key);
return AxisType::NONE;
}
std::string featureName = value.String();
if(featureName == "cursorMotion")
return AxisType::CURSOR_MOTION;
else if(featureName == "mapScroll")
return AxisType::MAP_SCROLL;
else if(featureName != "")
logGlobal->error("Unknown value %s of joystick config key %s!", featureName, key);
return AxisType::NONE;
}
void GameControllerConfig::parseTrigger(const std::string & key, const JsonNode & value)
{
std::vector<std::string> operations = getOperations(key, value);
SDL_GameControllerAxis triggerAxis = key == "lefttrigger" ?
SDL_CONTROLLER_AXIS_TRIGGERLEFT : SDL_CONTROLLER_AXIS_TRIGGERRIGHT;
std::vector<EShortcut> shortcuts;
for(const auto & operation : operations)
{
if(operation == "mouseLeftClick")
{
leftClickTriggerSet.insert(triggerAxis);
}
else if(operation == "mouseRightClick")
{
rightClickTriggerSet.insert(triggerAxis);
}
else
{
EShortcut shortcut = GH.shortcuts().findShortcut(operation);
if(shortcut == EShortcut::NONE)
logGlobal->error("Shortcut %s in joystick config key %s is invalid.", operation, key);
else
shortcuts.push_back(shortcut);
}
}
if(!shortcuts.empty())
triggerShortcutsMap.emplace(triggerAxis, std::move(shortcuts));
}
void GameControllerConfig::parseButton(const std::string & key, const JsonNode & value)
{
std::vector<std::string> operations = getOperations(key, value);
SDL_GameControllerButton button = SDL_GameControllerGetButtonFromString(key.c_str());
if(button == SDL_CONTROLLER_BUTTON_INVALID)
{
logGlobal->error("Joystick config key %s is invalid.", key);
return;
}
std::vector<EShortcut> shortcuts;
for(const auto & operation : operations)
{
if(operation == "mouseLeftClick")
{
leftClickButtonSet.insert(button);
}
else if(operation == "mouseRightClick")
{
rightClickButtonSet.insert(button);
}
else
{
EShortcut shortcut = GH.shortcuts().findShortcut(operation);
if(shortcut == EShortcut::NONE)
logGlobal->error("Shortcut %s in joystick config key %s is invalid.", operation, key);
else
shortcuts.push_back(shortcut);
}
}
if(!shortcuts.empty())
buttonShortcutsMap.emplace(button, std::move(shortcuts));
}
const AxisType & GameControllerConfig::getLeftAxisType()
{
return leftAxisType;
}
const AxisType & GameControllerConfig::getRightAxisType()
{
return rightAxisType;
}
std::vector<std::string> GameControllerConfig::getOperations(const std::string & key, const JsonNode & value)
{
std::vector<std::string> operations;
if(value.isString())
{
operations.push_back(value.String());
}
else if(value.isVector())
{
for(auto const & entryVector : value.Vector())
{
if(!entryVector.isString())
logGlobal->error("The vector of joystick config key %s can not contain non-string element.", key);
else
operations.push_back(entryVector.String());
}
}
else
{
logGlobal->error("The value of joystick config key %s should be string or string vector.", key);
}
return operations;
}
bool GameControllerConfig::isLeftClickButton(int buttonValue)
{
SDL_GameControllerButton button = static_cast<SDL_GameControllerButton>(buttonValue);
return leftClickButtonSet.find(button) != leftClickButtonSet.end();
}
bool GameControllerConfig::isRightClickButton(int buttonValue)
{
SDL_GameControllerButton button = static_cast<SDL_GameControllerButton>(buttonValue);
return rightClickButtonSet.find(button) != rightClickButtonSet.end();
}
bool GameControllerConfig::isShortcutsButton(int buttonValue)
{
SDL_GameControllerButton button = static_cast<SDL_GameControllerButton>(buttonValue);
return buttonShortcutsMap.find(button) != buttonShortcutsMap.end();
}
const std::vector<EShortcut> & GameControllerConfig::getButtonShortcuts(int buttonValue)
{
SDL_GameControllerButton button = static_cast<SDL_GameControllerButton>(buttonValue);
auto it = buttonShortcutsMap.find(button);
if(it != buttonShortcutsMap.end())
return it->second;
static std::vector<EShortcut> emptyVec;
return emptyVec;
}
bool GameControllerConfig::isLeftClickTrigger(int axisValue)
{
SDL_GameControllerAxis axis = static_cast<SDL_GameControllerAxis>(axisValue);
return leftClickTriggerSet.find(axis) != leftClickTriggerSet.end();
}
bool GameControllerConfig::isRightClickTrigger(int axisValue)
{
SDL_GameControllerAxis axis = static_cast<SDL_GameControllerAxis>(axisValue);
return rightClickTriggerSet.find(axis) != rightClickTriggerSet.end();
}
bool GameControllerConfig::isShortcutsTrigger(int axisValue)
{
SDL_GameControllerAxis axis = static_cast<SDL_GameControllerAxis>(axisValue);
return triggerShortcutsMap.find(axis) != triggerShortcutsMap.end();
}
const std::vector<EShortcut> & GameControllerConfig::getTriggerShortcuts(int axisValue)
{
SDL_GameControllerAxis axis = static_cast<SDL_GameControllerAxis>(axisValue);
auto it = triggerShortcutsMap.find(axis);
if(it != triggerShortcutsMap.end())
return it->second;
static std::vector<EShortcut> emptyVec;
return emptyVec;
}

@ -1,59 +0,0 @@
/*
* GameControllerConfig.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include <SDL.h>
#include "../gui/Shortcut.h"
#include "../../lib/json/JsonUtils.h"
enum AxisType
{
CURSOR_MOTION,
MAP_SCROLL,
NONE
};
class GameControllerConfig {
using ButtonShortcutsMap = std::map<SDL_GameControllerButton, std::vector<EShortcut> >;
using TriggerShortcutsMap = std::map<SDL_GameControllerAxis, std::vector<EShortcut> >;
ButtonShortcutsMap buttonShortcutsMap;
TriggerShortcutsMap triggerShortcutsMap;
std::set<SDL_GameControllerButton> leftClickButtonSet;
std::set<SDL_GameControllerButton> rightClickButtonSet;
std::set<SDL_GameControllerAxis> leftClickTriggerSet;
std::set<SDL_GameControllerAxis> rightClickTriggerSet;
AxisType leftAxisType;
AxisType rightAxisType;
void load();
std::vector<std::string> getOperations(const std::string & key, const JsonNode & value);
AxisType parseAxis(const std::string & key, const JsonNode & value);
void parseTrigger(const std::string & key, const JsonNode & value);
void parseButton(const std::string & key, const JsonNode & value);
public:
GameControllerConfig();
~GameControllerConfig() = default;
const AxisType & getLeftAxisType();
const AxisType & getRightAxisType();
bool isLeftClickButton(int buttonValue);
bool isRightClickButton(int buttonValue);
bool isShortcutsButton(int buttonValue);
const std::vector<EShortcut> & getButtonShortcuts(int buttonValue);
bool isLeftClickTrigger(int axisValue);
bool isRightClickTrigger(int axisValue);
bool isShortcutsTrigger(int axisValue);
const std::vector<EShortcut> & getTriggerShortcuts(int axisValue);
};

@ -18,8 +18,6 @@
#include "../gui/EventDispatcher.h"
#include "../gui/ShortcutHandler.h"
void InputSourceGameController::gameControllerDeleter(SDL_GameController * gameController)
{
if(gameController)
@ -135,76 +133,56 @@ int InputSourceGameController::getRealAxisValue(int value)
return (value - base) * AXIS_MAX_ZOOM / (AXIS_MAX_ZOOM - AXIS_DEAD_ZOOM);
}
void InputSourceGameController::dispatchTriggerShortcuts(const std::vector<EShortcut> & shortcutsVector, int axisValue)
void InputSourceGameController::dispatchAxisShortcuts(const std::vector<EShortcut> & shortcutsVector, SDL_GameControllerAxis axisID, int axisValue)
{
if(axisValue >= TRIGGER_PRESS_THRESHOLD)
GH.events().dispatchShortcutPressed(shortcutsVector);
{
if (!pressedAxes.count(axisID))
{
GH.events().dispatchShortcutPressed(shortcutsVector);
pressedAxes.insert(axisID);
}
}
else
GH.events().dispatchShortcutReleased(shortcutsVector);
}
void InputSourceGameController::dispatchTriggerLeftClick(int axisValue)
{
const Point & position = GH.input().getCursorPosition();
if(axisValue >= TRIGGER_PRESS_THRESHOLD)
GH.events().dispatchMouseLeftButtonPressed(position, 0);
else
GH.events().dispatchMouseLeftButtonReleased(position, 0);
}
void InputSourceGameController::dispatchTriggerRightClick(int axisValue)
{
const Point & position = GH.input().getCursorPosition();
if(axisValue >= TRIGGER_PRESS_THRESHOLD)
GH.events().dispatchShowPopup(position, 0);
else
GH.events().dispatchClosePopup(position);
{
if (pressedAxes.count(axisID))
{
GH.events().dispatchShortcutReleased(shortcutsVector);
pressedAxes.erase(axisID);
}
}
}
void InputSourceGameController::handleEventAxisMotion(const SDL_ControllerAxisEvent & axis)
{
tryToConvertCursor();
if(axis.axis == SDL_CONTROLLER_AXIS_LEFTX)
SDL_GameControllerAxis axisID = static_cast<SDL_GameControllerAxis>(axis.axis);
std::string axisName = SDL_GameControllerGetStringForAxis(axisID);
auto axisActions = GH.shortcuts().translateJoystickAxis(axisName);
auto buttonActions = GH.shortcuts().translateJoystickButton(axisName);
for (auto const & action : axisActions)
{
if(config.getLeftAxisType() == AxisType::CURSOR_MOTION)
cursorAxisValueX = getRealAxisValue(axis.value);
else if(config.getLeftAxisType() == AxisType::MAP_SCROLL)
scrollAxisValueX = getRealAxisValue(axis.value);
}
else if(axis.axis == SDL_CONTROLLER_AXIS_LEFTY)
{
if(config.getLeftAxisType() == AxisType::CURSOR_MOTION)
cursorAxisValueY = getRealAxisValue(axis.value);
else if(config.getLeftAxisType() == AxisType::MAP_SCROLL)
scrollAxisValueY = getRealAxisValue(axis.value);
}
if(axis.axis == SDL_CONTROLLER_AXIS_RIGHTX)
{
if(config.getRightAxisType() == AxisType::CURSOR_MOTION)
cursorAxisValueX = getRealAxisValue(axis.value);
else if(config.getRightAxisType() == AxisType::MAP_SCROLL)
scrollAxisValueX = getRealAxisValue(axis.value);
}
else if(axis.axis == SDL_CONTROLLER_AXIS_RIGHTY)
{
if(config.getRightAxisType() == AxisType::CURSOR_MOTION)
cursorAxisValueY = getRealAxisValue(axis.value);
else if(config.getRightAxisType() == AxisType::MAP_SCROLL)
scrollAxisValueY = getRealAxisValue(axis.value);
}
else if(config.isLeftClickTrigger(axis.axis))
{
dispatchTriggerLeftClick(axis.value);
}
else if(config.isRightClickTrigger(axis.axis))
{
dispatchTriggerRightClick(axis.value);
}
else if(config.isShortcutsTrigger(axis.axis))
{
const auto & shortcutsVector = config.getTriggerShortcuts(axis.axis);
dispatchTriggerShortcuts(shortcutsVector, axis.value);
switch (action)
{
case EShortcut::MOUSE_CURSOR_X:
cursorAxisValueX = getRealAxisValue(axis.value);
break;
case EShortcut::MOUSE_CURSOR_Y:
cursorAxisValueY = getRealAxisValue(axis.value);
break;
case EShortcut::MOUSE_SWIPE_X:
scrollAxisValueX = getRealAxisValue(axis.value);
break;
case EShortcut::MOUSE_SWIPE_Y:
scrollAxisValueY = getRealAxisValue(axis.value);
break;
}
}
dispatchAxisShortcuts(buttonActions, axisID, axis.value);
}
void InputSourceGameController::tryToConvertCursor()
@ -222,42 +200,16 @@ void InputSourceGameController::tryToConvertCursor()
void InputSourceGameController::handleEventButtonDown(const SDL_ControllerButtonEvent & button)
{
const Point & position = GH.input().getCursorPosition();
if(config.isLeftClickButton(button.button))
{
GH.events().dispatchMouseLeftButtonPressed(position, 0);
}
if(config.isRightClickButton(button.button))
{
GH.events().dispatchShowPopup(position, 0);
}
if(config.isShortcutsButton(button.button))
{
const auto & shortcutsVector = config.getButtonShortcuts(button.button);
GH.events().dispatchShortcutPressed(shortcutsVector);
}
std::string buttonName = SDL_GameControllerGetStringForButton(static_cast<SDL_GameControllerButton>(button.button));
const auto & shortcutsVector = GH.shortcuts().translateJoystickButton(buttonName);
GH.events().dispatchShortcutPressed(shortcutsVector);
}
void InputSourceGameController::handleEventButtonUp(const SDL_ControllerButtonEvent & button)
{
const Point & position = GH.input().getCursorPosition();
if(config.isLeftClickButton(button.button))
{
GH.events().dispatchMouseLeftButtonReleased(position, 0);
}
if(config.isRightClickButton(button.button))
{
GH.events().dispatchClosePopup(position);
}
if(config.isShortcutsButton(button.button))
{
const auto & shortcutsVector = config.getButtonShortcuts(button.button);
GH.events().dispatchShortcutReleased(shortcutsVector);
}
std::string buttonName = SDL_GameControllerGetStringForButton(static_cast<SDL_GameControllerButton>(button.button));
const auto & shortcutsVector = GH.shortcuts().translateJoystickButton(buttonName);
GH.events().dispatchShortcutReleased(shortcutsVector);
}
void InputSourceGameController::doCursorMove(int deltaX, int deltaY)

@ -10,19 +10,24 @@
#pragma once
#include <SDL.h>
#include <SDL_events.h>
#include <SDL_gamecontroller.h>
#include "GameControllerConfig.h"
#include "../gui/Shortcut.h"
#include "../../lib/Point.h"
constexpr int AXIS_DEAD_ZOOM = 6000;
constexpr int AXIS_MAX_ZOOM = 32000;
constexpr int AXIS_MOVE_SPEED = 500;
constexpr int AXIS_CURSOR_MOVE_INTERVAL = 1000;
constexpr int TRIGGER_PRESS_THRESHOLD = 8000;
const int AXIS_DEAD_ZOOM = 6000;
const int AXIS_MAX_ZOOM = 32000;
const int AXIS_MOVE_SPEED = 500;
const int AXIS_CURSOR_MOVE_INTERVAL = 1000;
const int TRIGGER_PRESS_THRESHOLD = 8000;
enum class AxisType
{
CURSOR_MOTION,
MAP_SCROLL,
NONE
};
/// Class that handles game controller input from SDL events
class InputSourceGameController
@ -31,7 +36,8 @@ class InputSourceGameController
using GameControllerPtr = std::unique_ptr<SDL_GameController, decltype(&gameControllerDeleter)>;
std::map<int, GameControllerPtr> gameControllerMap;
GameControllerConfig config;
std::set<SDL_GameControllerAxis> pressedAxes;
long long lastCheckTime;
int cursorAxisValueX;
int cursorAxisValueY;
@ -49,9 +55,7 @@ class InputSourceGameController
void openGameController(int index);
int getJoystickIndex(SDL_GameController * controller);
int getRealAxisValue(int value);
void dispatchTriggerShortcuts(const std::vector<EShortcut> & shortcutsVector, int axisValue);
void dispatchTriggerLeftClick(int axisValue);
void dispatchTriggerRightClick(int axisValue);
void dispatchAxisShortcuts(const std::vector<EShortcut> & shortcutsVector, SDL_GameControllerAxis axisID, int axisValue);
void tryToConvertCursor();
void doCursorMove(int deltaX, int deltaY);
int getMoveDis(float planDis);

@ -15,6 +15,7 @@
#include "CGuiHandler.h"
#include "MouseButton.h"
#include "WindowHandler.h"
#include "gui/Shortcut.h"
#include "../../lib/Rect.h"
@ -74,6 +75,12 @@ void EventDispatcher::dispatchShortcutPressed(const std::vector<EShortcut> & sho
{
bool keysCaptured = false;
if (vstd::contains(shortcutsVector, EShortcut::MOUSE_LEFT))
dispatchMouseLeftButtonPressed(GH.getCursorPosition(), 0);
if (vstd::contains(shortcutsVector, EShortcut::MOUSE_RIGHT))
dispatchShowPopup(GH.getCursorPosition(), 0);
for(auto & i : keyinterested)
for(EShortcut shortcut : shortcutsVector)
if(i->captureThisKey(shortcut))
@ -97,6 +104,12 @@ void EventDispatcher::dispatchShortcutReleased(const std::vector<EShortcut> & sh
{
bool keysCaptured = false;
if (vstd::contains(shortcutsVector, EShortcut::MOUSE_LEFT))
dispatchMouseLeftButtonReleased(GH.getCursorPosition(), 0);
if (vstd::contains(shortcutsVector, EShortcut::MOUSE_RIGHT))
dispatchClosePopup(GH.getCursorPosition());
for(auto & i : keyinterested)
for(EShortcut shortcut : shortcutsVector)
if(i->captureThisKey(shortcut))

@ -13,6 +13,14 @@ enum class EShortcut
{
NONE,
// preudo-shortcuts that trigger mouse events
MOUSE_LEFT,
MOUSE_RIGHT,
MOUSE_CURSOR_X,
MOUSE_CURSOR_Y,
MOUSE_SWIPE_X,
MOUSE_SWIPE_Y,
// Global hotkeys that are available in multiple dialogs
GLOBAL_ACCEPT, // Return - Accept query
GLOBAL_CANCEL, // Escape - Cancel query

@ -19,7 +19,16 @@ ShortcutHandler::ShortcutHandler()
{
const JsonNode config = JsonUtils::assembleFromFiles("config/shortcutsConfig");
for (auto const & entry : config["keyboard"].Struct())
mappedKeyboardShortcuts = loadShortcuts(config["keyboard"]);
mappedJoystickShortcuts = loadShortcuts(config["joystickButtons"]);
mappedJoystickAxes = loadShortcuts(config["joystickAxes"]);
}
std::multimap<std::string, EShortcut> ShortcutHandler::loadShortcuts(const JsonNode & data) const
{
std::multimap<std::string, EShortcut> result;
for (auto const & entry : data.Struct())
{
std::string shortcutName = entry.first;
EShortcut shortcutID = findShortcut(shortcutName);
@ -32,20 +41,22 @@ ShortcutHandler::ShortcutHandler()
if (entry.second.isString())
{
mappedShortcuts.emplace(entry.second.String(), shortcutID);
result.emplace(entry.second.String(), shortcutID);
}
if (entry.second.isVector())
{
for (auto const & entryVector : entry.second.Vector())
mappedShortcuts.emplace(entryVector.String(), shortcutID);
result.emplace(entryVector.String(), shortcutID);
}
}
return result;
}
std::vector<EShortcut> ShortcutHandler::translateKeycode(const std::string & key) const
std::vector<EShortcut> ShortcutHandler::translateShortcut(const std::multimap<std::string, EShortcut> & options, const std::string & key) const
{
auto range = mappedShortcuts.equal_range(key);
auto range = options.equal_range(key);
// FIXME: some code expects calls to keyPressed / captureThisKey even without defined hotkeys
if (range.first == range.second)
@ -59,9 +70,30 @@ std::vector<EShortcut> ShortcutHandler::translateKeycode(const std::string & key
return result;
}
std::vector<EShortcut> ShortcutHandler::translateKeycode(const std::string & key) const
{
return translateShortcut(mappedKeyboardShortcuts, key);
}
std::vector<EShortcut> ShortcutHandler::translateJoystickButton(const std::string & key) const
{
return translateShortcut(mappedJoystickShortcuts, key);
}
std::vector<EShortcut> ShortcutHandler::translateJoystickAxis(const std::string & key) const
{
return translateShortcut(mappedJoystickAxes, key);
}
EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) const
{
static const std::map<std::string, EShortcut> shortcutNames = {
{"mouseClickLeft", EShortcut::MOUSE_LEFT },
{"mouseClickRight", EShortcut::MOUSE_RIGHT },
{"mouseCursorX", EShortcut::MOUSE_CURSOR_X, },
{"mouseCursorY", EShortcut::MOUSE_CURSOR_Y, },
{"mouseSwipeX", EShortcut::MOUSE_SWIPE_X, },
{"mouseSwipeY", EShortcut::MOUSE_SWIPE_Y, },
{"globalAccept", EShortcut::GLOBAL_ACCEPT },
{"globalCancel", EShortcut::GLOBAL_CANCEL },
{"globalReturn", EShortcut::GLOBAL_RETURN },

@ -12,15 +12,29 @@
enum class EShortcut;
VCMI_LIB_NAMESPACE_BEGIN
class JsonNode;
VCMI_LIB_NAMESPACE_END
class ShortcutHandler
{
std::multimap<std::string, EShortcut> mappedShortcuts;
std::multimap<std::string, EShortcut> mappedKeyboardShortcuts;
std::multimap<std::string, EShortcut> mappedJoystickShortcuts;
std::multimap<std::string, EShortcut> mappedJoystickAxes;
std::multimap<std::string, EShortcut> loadShortcuts(const JsonNode & data) const;
std::vector<EShortcut> translateShortcut(const std::multimap<std::string, EShortcut> & options, const std::string & key) const;
public:
ShortcutHandler();
/// returns list of shortcuts assigned to provided SDL keycode
std::vector<EShortcut> translateKeycode(const std::string & key) const;
std::vector<EShortcut> translateJoystickButton(const std::string & key) const;
std::vector<EShortcut> translateJoystickAxis(const std::string & key) const;
/// attempts to find shortcut by its unique identifier. Returns EShortcut::NONE on failure
EShortcut findShortcut(const std::string & identifier ) const;
};

@ -110,6 +110,7 @@
"battleConsoleDown": "Down",
"battleTacticsNext": "Space",
"battleTacticsEnd": [ "Return", "Keypad Enter"],
"battleToggleHeroesStats": [],
"battleSelectAction": "S",
"townOpenTavern": "T",
"townSwapArmies": "Space",
@ -137,24 +138,72 @@
"heroCostume8": "8",
"heroCostume9": "9"
},
"joystick": {
"leftaxis": "cursorMotion",
"rightaxis": "mapScroll",
"a": ["globalAccept", "globalReturn", "lobbyBeginStandardGame", "lobbyBeginCampaign", "lobbyLoadGame", "lobbySaveGame", "adventureViewSelected", "adventureExitWorldView", "battleTacticsEnd"],
"b": ["globalCancel", "globalReturn", "adventureExitWorldView"],
"x": "mouseLeftClick",
"y": "mouseRightClick",
"joystickAxes":
{
"mouseCursorX" : "leftx",
"mouseCursorY" : "lefty",
"mouseSwipeX" : "rightx",
"mouseSwipeY" : "righty"
},
"joystickButtons": {
"globalAccept" : "a",
"globalCancel" : "b",
"globalReturn" : [ "a", "b" ],
"lobbyBeginStandardGame" : "a",
"lobbyBeginCampaign" : "a",
"lobbyLoadGame" : "a",
"lobbySaveGame" : "a",
"adventureViewSelected" : "a",
"adventureExitWorldView" : [ "a", "b" ],
"battleTacticsEnd" : "a",
"mouseClickLeft": "x",
"mouseClickRight": "y",
"leftshoulder": ["adventureNextHero", "battleDefend"],
"rightshoulder": ["adventureNextTown", "battleWait"],
"lefttrigger": ["adventureVisitObject", "battleTacticsNext", "battleUseCreatureSpell"],
"righttrigger": ["adventureCastSpell", "battleCastSpell"],
"back": ["gameEndTurn", "battleAutocombatEnd"],
"start": ["globalOptions", "adventureGameOptions"],
"dpup": ["moveUp", "adventureViewWorld", "recruitmentUpgrade", "recruitmentUpgradeAll", "battleConsoleUp", "recruitmentMax"],
"dpdown": ["moveDown", "adventureKingdomOverview", "battleConsoleDown","recruitmentMin"],
"dpleft": ["moveLeft", "adventureViewScenario"],
"dpright": ["moveRight", "adventureThievesGuild"],
"leftstick" : ["adventureToggleMapLevel", "battleToggleHeroesStats"],
"rightstick": ["adventureToggleGrid", "battleToggleQueue"]
"adventureNextTown" : "rightshoulder",
"battleWait" : "rightshoulder",
"adventureVisitObject" : "lefttrigger",
"battleTacticsNext" : "lefttrigger",
"battleUseCreatureSpell" : "lefttrigger",
"adventureCastSpell" : "righttrigger",
"battleCastSpell" : "righttrigger",
"gameEndTurn" : "back",
"battleAutocombatEnd" : "back",
"globalOptions" : "start",
"adventureGameOptions" : "start",
"moveUp" : "dpup",
"adventureViewWorld" : "dpup",
"recruitmentUpgrade" : "dpup",
"recruitmentUpgradeAll" : "dpup",
"battleConsoleUp" : "dpup",
"recruitmentMax" : "dpup",
"moveDown" : "dpdown",
"adventureKingdomOverview" : "dpdown",
"battleConsoleDown" : "dpdown",
"recruitmentMin" : "dpdown",
"moveLeft" : "dpleft",
"adventureViewScenario" : "dpleft",
"moveRight" : "dpright",
"adventureThievesGuild" : "dpright",
"adventureToggleMapLevel" : "leftstick",
"battleToggleHeroesStats" : "leftstick",
"adventureToggleGrid" : "rightstick",
"battleToggleQueue" : "rightstick",
}
}