Adds initial ingame gamepad support to minetest.
Full Formspec support is not implemented yet and
can be added by a later change.
# Continuous forward movement (only used for testing).
continuous_forward (Continuous forward) bool false
+# Enable Joysticks
+enable_joysticks (Enable Joysticks) bool true
+
+# The time in seconds it takes between repeated events
+# when holding down a joystick button combination.
+repeat_joystick_button_time (Joystick button repetition invterval) float 0.17
+
+# The sensitivity of the joystick axes for moving the
+# ingame view frustum around.
+joystick_frustum_sensitivity (Joystick frustum sensitivity) float 170
+
# Key for moving the player forward.
# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3
keymap_forward (Forward key) key KEY_KEY_W
set(client_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/clientlauncher.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/joystick_controller.cpp
PARENT_SCOPE
)
#include "guiEngine.h"
#include "player.h"
#include "fontengine.h"
+#include "joystick_controller.h"
#include "clientlauncher.h"
/* mainmenumanager.h
#endif
/* show main menu */
- GUIEngine mymenu(device, guiroot, &g_menumgr, smgr, menudata, *kill);
+ GUIEngine mymenu(device, &input->joystick, guiroot,
+ &g_menumgr, smgr, menudata, *kill);
smgr->clear(); /* leave scene manager in a clean state */
}
device = createDeviceEx(params);
if (device) {
+ if (g_settings->getBool("enable_joysticks")) {
+ irr::core::array<irr::SJoystickInfo> infos;
+ std::vector<irr::SJoystickInfo> joystick_infos;
+ // Make sure this is called maximum once per
+ // irrlicht device, otherwise it will give you
+ // multiple events for the same joystick.
+ if (device->activateJoysticks(infos)) {
+ infostream << "Joystick support enabled" << std::endl;
+ joystick_infos.reserve(infos.size());
+ for (u32 i = 0; i < infos.size(); i++) {
+ joystick_infos.push_back(infos[i]);
+ }
+ } else {
+ errorstream << "Could not activate joystick support." << std::endl;
+ }
+ }
porting::initIrrlicht(device);
}
#define INPUT_HANDLER_H
#include "irrlichttypes_extrabloated.h"
+#include "joystick_controller.h"
class MyEventReceiver : public IEventReceiver
{
return true;
}
#endif
+
+ if (event.EventType == irr::EET_JOYSTICK_INPUT_EVENT) {
+ /* TODO add a check like:
+ if (event.JoystickEvent != joystick_we_listen_for)
+ return false;
+ */
+ return joystick->handleEvent(event.JoystickEvent);
+ }
// handle mouse events
if (event.EventType == irr::EET_MOUSE_INPUT_EVENT) {
if (noMenuActive() == false) {
s32 mouse_wheel;
+ JoystickController *joystick;
+
#ifdef HAVE_TOUCHSCREENGUI
TouchScreenGUI* m_touchscreengui;
#endif
m_receiver(receiver),
m_mousepos(0,0)
{
+ m_receiver->joystick = &joystick;
}
virtual bool isKeyDown(const KeyPress &keyCode)
{
void clear()
{
+ joystick.clear();
m_receiver->clearInput();
}
private:
--- /dev/null
+/*
+Minetest
+Copyright (C) 2016 est31, <MTest31@outlook.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "joystick_controller.h"
+#include "irrlichttypes_extrabloated.h"
+#include "keys.h"
+#include "settings.h"
+#include "gettime.h"
+
+bool JoystickButtonCmb::isTriggered(const irr::SEvent::SJoystickEvent &ev) const
+{
+ u32 buttons = ev.ButtonStates;
+
+ buttons &= filter_mask;
+ return buttons == compare_mask;
+}
+
+bool JoystickAxisCmb::isTriggered(const irr::SEvent::SJoystickEvent &ev) const
+{
+ s16 ax_val = ev.Axis[axis_to_compare];
+
+ return (ax_val * direction < 0) && (thresh * direction > ax_val * direction);
+}
+
+// spares many characters
+#define JLO_B_PB(A, B, C) jlo.button_keys.push_back(JoystickButtonCmb(A, B, C))
+#define JLO_A_PB(A, B, C, D) jlo.axis_keys.push_back(JoystickAxisCmb(A, B, C, D))
+
+static JoystickLayout create_default_layout()
+{
+ JoystickLayout jlo;
+
+ jlo.axes_dead_border = 1024;
+
+ const JoystickAxisLayout axes[JA_COUNT] = {
+ {0, 1}, // JA_SIDEWARD_MOVE
+ {1, 1}, // JA_FORWARD_MOVE
+ {3, 1}, // JA_FRUSTUM_HORIZONTAL
+ {4, 1}, // JA_FRUSTUM_VERTICAL
+ };
+ memcpy(jlo.axes, axes, sizeof(jlo.axes));
+
+ u32 sb = 1 << 7; // START button mask
+ u32 fb = 1 << 3; // FOUR button mask
+ u32 bm = sb | fb; // Mask for Both Modifiers
+
+ // The back button means "ESC".
+ JLO_B_PB(KeyType::ESC, 1 << 6, 1 << 6);
+
+ // The start button counts as modifier as well as use key.
+ // JLO_B_PB(KeyType::USE, sb, sb));
+
+ // Accessible without start modifier button pressed
+ // regardless whether four is pressed or not
+ JLO_B_PB(KeyType::SNEAK, sb | 1 << 2, 1 << 2);
+
+ // Accessible without four modifier button pressed
+ // regardless whether start is pressed or not
+ JLO_B_PB(KeyType::MOUSE_L, fb | 1 << 4, 1 << 4);
+ JLO_B_PB(KeyType::MOUSE_R, fb | 1 << 5, 1 << 5);
+
+ // Accessible without any modifier pressed
+ JLO_B_PB(KeyType::JUMP, bm | 1 << 0, 1 << 0);
+ JLO_B_PB(KeyType::SPECIAL1, bm | 1 << 1, 1 << 1);
+
+ // Accessible with start button not pressed, but four pressed
+ // TODO find usage for button 0
+ JLO_B_PB(KeyType::DROP, bm | 1 << 1, fb | 1 << 1);
+ JLO_B_PB(KeyType::SCROLL_UP, bm | 1 << 4, fb | 1 << 4);
+ JLO_B_PB(KeyType::SCROLL_DOWN,bm | 1 << 5, fb | 1 << 5);
+
+ // Accessible with start button and four pressed
+ // TODO find usage for buttons 0, 1 and 4, 5
+
+ // Now about the buttons simulated by the axes
+
+ // Movement buttons, important for vessels
+ JLO_A_PB(KeyType::FORWARD, 1, 1, 1024);
+ JLO_A_PB(KeyType::BACKWARD, 1, -1, 1024);
+ JLO_A_PB(KeyType::LEFT, 0, 1, 1024);
+ JLO_A_PB(KeyType::RIGHT, 0, -1, 1024);
+
+ // Scroll buttons
+ JLO_A_PB(KeyType::SCROLL_UP, 2, -1, 1024);
+ JLO_A_PB(KeyType::SCROLL_DOWN, 5, -1, 1024);
+
+ return jlo;
+}
+
+static const JoystickLayout default_layout = create_default_layout();
+
+JoystickController::JoystickController()
+{
+ m_layout = &default_layout;
+ doubling_dtime = g_settings->getFloat("repeat_joystick_button_time");
+
+ for (size_t i = 0; i < KeyType::INTERNAL_ENUM_COUNT; i++) {
+ m_past_pressed_time[i] = 0;
+ }
+ clear();
+}
+
+bool JoystickController::handleEvent(const irr::SEvent::SJoystickEvent &ev)
+{
+ m_internal_time = getTimeMs() / 1000.f;
+
+ std::bitset<KeyType::INTERNAL_ENUM_COUNT> keys_pressed;
+
+ // First generate a list of keys pressed
+
+ for (size_t i = 0; i < m_layout->button_keys.size(); i++) {
+ if (m_layout->button_keys[i].isTriggered(ev)) {
+ keys_pressed.set(m_layout->button_keys[i].key);
+ }
+ }
+
+ for (size_t i = 0; i < m_layout->axis_keys.size(); i++) {
+ if (m_layout->axis_keys[i].isTriggered(ev)) {
+ keys_pressed.set(m_layout->axis_keys[i].key);
+ }
+ }
+
+ // Then update the values
+
+ for (size_t i = 0; i < KeyType::INTERNAL_ENUM_COUNT; i++) {
+ if (keys_pressed[i]) {
+ if (!m_past_pressed_keys[i] &&
+ m_past_pressed_time[i] < m_internal_time - doubling_dtime) {
+ m_past_pressed_keys[i] = true;
+ m_past_pressed_time[i] = m_internal_time;
+ }
+ } else if (m_pressed_keys[i]) {
+ m_past_released_keys[i] = true;
+ }
+
+ m_pressed_keys[i] = keys_pressed[i];
+ }
+
+ for (size_t i = 0; i < JA_COUNT; i++) {
+ const JoystickAxisLayout &ax_la = m_layout->axes[i];
+ m_axes_vals[i] = ax_la.invert * ev.Axis[ax_la.axis_id];
+ }
+
+
+ return true;
+}
+
+void JoystickController::clear()
+{
+ m_pressed_keys.reset();
+ m_past_pressed_keys.reset();
+ m_past_released_keys.reset();
+ memset(m_axes_vals, 0, sizeof(m_axes_vals));
+}
+
+s16 JoystickController::getAxisWithoutDead(JoystickAxis axis)
+{
+ s16 v = m_axes_vals[axis];
+ if (((v > 0) && (v < m_layout->axes_dead_border)) ||
+ ((v < 0) && (v > -m_layout->axes_dead_border)))
+ return 0;
+ return v;
+}
--- /dev/null
+/*
+Minetest
+Copyright (C) 2016 est31, <MTest31@outlook.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef JOYSTICK_HEADER
+#define JOYSTICK_HEADER
+
+#include "irrlichttypes_extrabloated.h"
+#include "keys.h"
+#include <bitset>
+#include <vector>
+
+enum JoystickAxis {
+ JA_SIDEWARD_MOVE,
+ JA_FORWARD_MOVE,
+
+ JA_FRUSTUM_HORIZONTAL,
+ JA_FRUSTUM_VERTICAL,
+
+ // To know the count of enum values
+ JA_COUNT,
+};
+
+struct JoystickAxisLayout {
+ u16 axis_id;
+ // -1 if to invert, 1 if to keep it.
+ int invert;
+};
+
+
+struct JoystickCombination {
+
+ virtual bool isTriggered(const irr::SEvent::SJoystickEvent &ev) const=0;
+
+ GameKeyType key;
+};
+
+struct JoystickButtonCmb : public JoystickCombination {
+
+ JoystickButtonCmb() {}
+ JoystickButtonCmb(GameKeyType key, u32 filter_mask, u32 compare_mask) :
+ filter_mask(filter_mask),
+ compare_mask(compare_mask)
+ {
+ this->key = key;
+ }
+
+ virtual bool isTriggered(const irr::SEvent::SJoystickEvent &ev) const;
+
+ u32 filter_mask;
+ u32 compare_mask;
+};
+
+struct JoystickAxisCmb : public JoystickCombination {
+
+ JoystickAxisCmb() {}
+ JoystickAxisCmb(GameKeyType key, u16 axis_to_compare, int direction, s16 thresh) :
+ axis_to_compare(axis_to_compare),
+ direction(direction),
+ thresh(thresh)
+ {
+ this->key = key;
+ }
+
+ virtual bool isTriggered(const irr::SEvent::SJoystickEvent &ev) const;
+
+ u16 axis_to_compare;
+
+ // if -1, thresh must be smaller than the axis value in order to trigger
+ // if 1, thresh must be bigger than the axis value in order to trigger
+ int direction;
+ s16 thresh;
+};
+
+struct JoystickLayout {
+ std::vector<JoystickButtonCmb> button_keys;
+ std::vector<JoystickAxisCmb> axis_keys;
+ JoystickAxisLayout axes[JA_COUNT];
+ s16 axes_dead_border;
+};
+
+class JoystickController {
+
+public:
+ JoystickController();
+ bool handleEvent(const irr::SEvent::SJoystickEvent &ev);
+ void clear();
+
+ bool wasKeyDown(GameKeyType b)
+ {
+ bool r = m_past_pressed_keys[b];
+ m_past_pressed_keys[b] = false;
+ return r;
+ }
+ bool getWasKeyDown(GameKeyType b)
+ {
+ return m_past_pressed_keys[b];
+ }
+ void clearWasKeyDown(GameKeyType b)
+ {
+ m_past_pressed_keys[b] = false;
+ }
+
+ bool wasKeyReleased(GameKeyType b)
+ {
+ bool r = m_past_released_keys[b];
+ m_past_released_keys[b] = false;
+ return r;
+ }
+ bool getWasKeyReleased(GameKeyType b)
+ {
+ return m_past_pressed_keys[b];
+ }
+ void clearWasKeyReleased(GameKeyType b)
+ {
+ m_past_pressed_keys[b] = false;
+ }
+
+ bool isKeyDown(GameKeyType b)
+ {
+ return m_pressed_keys[b];
+ }
+
+ s16 getAxis(JoystickAxis axis)
+ {
+ return m_axes_vals[axis];
+ }
+
+ s16 getAxisWithoutDead(JoystickAxis axis);
+
+ f32 doubling_dtime;
+
+private:
+ const JoystickLayout *m_layout;
+
+ s16 m_axes_vals[JA_COUNT];
+
+ std::bitset<KeyType::INTERNAL_ENUM_COUNT> m_pressed_keys;
+
+ f32 m_internal_time;
+
+ f32 m_past_pressed_time[KeyType::INTERNAL_ENUM_COUNT];
+
+ std::bitset<KeyType::INTERNAL_ENUM_COUNT> m_past_pressed_keys;
+ std::bitset<KeyType::INTERNAL_ENUM_COUNT> m_past_released_keys;
+};
+
+#endif
DEBUG_STACKS,
+ // joystick specific keys
+ MOUSE_L,
+ MOUSE_R,
+ SCROLL_UP,
+ SCROLL_DOWN,
+
// Fake keycode for array size and internal checks
INTERNAL_ENUM_COUNT
PlayerControl controls = player->getPlayerControl();
bool walking = false;
- if(controls.up || controls.down || controls.left || controls.right)
+ if (controls.up || controls.down || controls.left || controls.right ||
+ controls.forw_move_joystick_axis != 0.f ||
+ controls.sidew_move_joystick_axis != 0.f)
walking = true;
f32 new_speed = player->local_animation_speed;
settings->setDefault("free_move", "false");
settings->setDefault("noclip", "false");
settings->setDefault("continuous_forward", "false");
+ settings->setDefault("enable_joysticks", "true");
+ settings->setDefault("repeat_joystick_button_time", "0.17");
+ settings->setDefault("joystick_frustum_sensitivity", "170");
settings->setDefault("cinematic", "false");
settings->setDefault("camera_smoothing", "0");
settings->setDefault("cinematic_camera_smoothing", "0.7");
#include "client.h"
#include "client/tile.h" // For TextureSource
#include "client/keys.h"
+#include "client/joystick_controller.h"
#include "clientmap.h"
#include "clouds.h"
#include "config.h"
static inline void create_formspec_menu(GUIFormSpecMenu **cur_formspec,
InventoryManager *invmgr, IGameDef *gamedef,
IWritableTextureSource *tsrc, IrrlichtDevice *device,
+ JoystickController *joystick,
IFormSource *fs_src, TextDest *txt_dest, Client *client)
{
if (*cur_formspec == 0) {
- *cur_formspec = new GUIFormSpecMenu(device, guiroot, -1, &g_menumgr,
- invmgr, gamedef, tsrc, fs_src, txt_dest, client);
+ *cur_formspec = new GUIFormSpecMenu(device, joystick,
+ guiroot, -1, &g_menumgr, invmgr, gamedef, tsrc,
+ fs_src, txt_dest, client);
(*cur_formspec)->doPause = false;
/*
static void show_deathscreen(GUIFormSpecMenu **cur_formspec,
InventoryManager *invmgr, IGameDef *gamedef,
- IWritableTextureSource *tsrc, IrrlichtDevice *device, Client *client)
+ IWritableTextureSource *tsrc, IrrlichtDevice *device,
+ JoystickController *joystick, Client *client)
{
std::string formspec =
std::string(FORMSPEC_VERSION_STRING) +
FormspecFormSource *fs_src = new FormspecFormSource(formspec);
LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_DEATH_SCREEN", client);
- create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device, fs_src, txt_dst, NULL);
+ create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device,
+ joystick, fs_src, txt_dst, NULL);
}
/******************************************************************************/
static void show_pause_menu(GUIFormSpecMenu **cur_formspec,
InventoryManager *invmgr, IGameDef *gamedef,
IWritableTextureSource *tsrc, IrrlichtDevice *device,
- bool singleplayermode)
+ JoystickController *joystick, bool singleplayermode)
{
#ifdef __ANDROID__
std::string control_text = strgettext("Default Controls:\n"
FormspecFormSource *fs_src = new FormspecFormSource(os.str());
LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_PAUSE_MENU");
- create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device, fs_src, txt_dst, NULL);
+ create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device,
+ joystick, fs_src, txt_dst, NULL);
std::string con("btn_continue");
(*cur_formspec)->setFocus(con);
(*cur_formspec)->doPause = true;
void decreaseViewRange(float *statustext_time);
void toggleFullViewRange(float *statustext_time);
- void updateCameraDirection(CameraOrientation *cam, VolatileRunFlags *flags);
+ void updateCameraDirection(CameraOrientation *cam, VolatileRunFlags *flags,
+ float dtime);
void updateCameraOrientation(CameraOrientation *cam,
- const VolatileRunFlags &flags);
+ const VolatileRunFlags &flags, float dtime);
void updatePlayerControl(const CameraOrientation &cam);
void step(f32 *dtime);
void processClientEvents(CameraOrientation *cam, float *damage_flash);
static void settingChangedCallback(const std::string &setting_name, void *data);
void readSettings();
- bool getLeftClicked()
+ inline bool getLeftClicked()
{
- return input->getLeftClicked();
+ return input->getLeftClicked() ||
+ input->joystick.getWasKeyDown(KeyType::MOUSE_L);
}
- bool getRightClicked()
+ inline bool getRightClicked()
{
- return input->getRightClicked();
+ return input->getRightClicked() ||
+ input->joystick.getWasKeyDown(KeyType::MOUSE_R);
}
- bool isLeftPressed()
+ inline bool isLeftPressed()
{
- return input->getLeftState();
+ return input->getLeftState() ||
+ input->joystick.isKeyDown(KeyType::MOUSE_L);
}
- bool isRightPressed()
+ inline bool isRightPressed()
{
- return input->getRightState();
+ return input->getRightState() ||
+ input->joystick.isKeyDown(KeyType::MOUSE_R);
}
- bool getLeftReleased()
+ inline bool getLeftReleased()
{
- return input->getLeftReleased();
+ return input->getLeftReleased() ||
+ input->joystick.wasKeyReleased(KeyType::MOUSE_L);
}
- bool isKeyDown(GameKeyType k)
+ inline bool isKeyDown(GameKeyType k)
{
- return input->isKeyDown(keycache.key[k]);
+ return input->isKeyDown(keycache.key[k]) || input->joystick.isKeyDown(k);
}
- bool wasKeyDown(GameKeyType k)
+ inline bool wasKeyDown(GameKeyType k)
{
- return input->wasKeyDown(keycache.key[k]);
+ return input->wasKeyDown(keycache.key[k]) || input->joystick.wasKeyDown(k);
}
#ifdef __ANDROID__
*/
bool m_cache_doubletap_jump;
bool m_cache_enable_clouds;
+ bool m_cache_enable_joysticks;
bool m_cache_enable_particles;
bool m_cache_enable_fog;
f32 m_cache_mouse_sensitivity;
+ f32 m_cache_joystick_frustum_sensitivity;
f32 m_repeat_right_click_time;
#ifdef __ANDROID__
&settingChangedCallback, this);
g_settings->registerChangedCallback("enable_clouds",
&settingChangedCallback, this);
+ g_settings->registerChangedCallback("doubletap_joysticks",
+ &settingChangedCallback, this);
g_settings->registerChangedCallback("enable_particles",
&settingChangedCallback, this);
g_settings->registerChangedCallback("enable_fog",
&settingChangedCallback, this);
g_settings->registerChangedCallback("mouse_sensitivity",
&settingChangedCallback, this);
+ g_settings->registerChangedCallback("joystick_frustum_sensitivity",
+ &settingChangedCallback, this);
g_settings->registerChangedCallback("repeat_rightclick_time",
&settingChangedCallback, this);
updateProfilers(runData, stats, draw_times, dtime);
processUserInput(&flags, &runData, dtime);
// Update camera before player movement to avoid camera lag of one frame
- updateCameraDirection(&cam_view_target, &flags);
+ updateCameraDirection(&cam_view_target, &flags, dtime);
float cam_smoothing = 0;
if (g_settings->getBool("cinematic"))
cam_smoothing = 1 - g_settings->getFloat("cinematic_camera_smoothing");
} else if (wasKeyDown(KeyType::ESC) || input->wasKeyDown(CancelKey)) {
if (!gui_chat_console->isOpenInhibited()) {
show_pause_menu(¤t_formspec, client, gamedef,
- texture_src, device, simple_singleplayer_mode);
+ texture_src, device, &input->joystick,
+ simple_singleplayer_mode);
}
} else if (wasKeyDown(KeyType::CHAT)) {
openConsole(0.2, L"");
u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE - 1,
player->hud_hotbar_itemcount - 1);
- if (wheel < 0)
+ s32 dir = wheel;
+
+ if (input->joystick.wasKeyDown(KeyType::SCROLL_DOWN)) {
+ dir = -1;
+ }
+
+ if (input->joystick.wasKeyDown(KeyType::SCROLL_UP)) {
+ dir = 1;
+ }
+
+ if (dir < 0)
*new_playeritem = *new_playeritem < max_item ? *new_playeritem + 1 : 0;
- else if (wheel > 0)
+ else if (dir > 0)
*new_playeritem = *new_playeritem > 0 ? *new_playeritem - 1 : max_item;
- // else wheel == 0
-
+ // else dir == 0
/* Item selection using keyboard
*/
TextDest *txt_dst = new TextDestPlayerInventory(client);
create_formspec_menu(¤t_formspec, client, gamedef, texture_src,
- device, fs_src, txt_dst, client);
+ device, &input->joystick, fs_src, txt_dst, client);
InventoryLocation inventoryloc;
inventoryloc.setCurrentPlayer();
void Game::updateCameraDirection(CameraOrientation *cam,
- VolatileRunFlags *flags)
+ VolatileRunFlags *flags, float dtime)
{
if ((device->isWindowActive() && noMenuActive()) || random_input) {
if (flags->first_loop_after_window_activation)
flags->first_loop_after_window_activation = false;
else
- updateCameraOrientation(cam, *flags);
+ updateCameraOrientation(cam, *flags, dtime);
input->setMousePos((driver->getScreenSize().Width / 2),
(driver->getScreenSize().Height / 2));
}
}
-
void Game::updateCameraOrientation(CameraOrientation *cam,
- const VolatileRunFlags &flags)
+ const VolatileRunFlags &flags, float dtime)
{
#ifdef HAVE_TOUCHSCREENGUI
if (g_touchscreengui) {
cam->camera_pitch = g_touchscreengui->getPitch();
} else {
#endif
+
s32 dx = input->getMousePos().X - (driver->getScreenSize().Width / 2);
s32 dy = input->getMousePos().Y - (driver->getScreenSize().Height / 2);
}
#endif
+ if (m_cache_enable_joysticks) {
+ f32 c = m_cache_joystick_frustum_sensitivity * (1.f / 32767.f) * dtime;
+ cam->camera_yaw -= input->joystick.getAxisWithoutDead(JA_FRUSTUM_HORIZONTAL) *
+ c;
+ cam->camera_pitch += input->joystick.getAxisWithoutDead(JA_FRUSTUM_VERTICAL) *
+ c;
+ }
+
cam->camera_pitch = rangelim(cam->camera_pitch, -89.5, 89.5);
}
{
//TimeTaker tt("update player control", NULL, PRECISION_NANO);
+ // DO NOT use the isKeyDown method for the forward, backward, left, right
+ // buttons, as the code that uses the controls needs to be able to
+ // distinguish between the two in order to know when to use joysticks.
+
PlayerControl control(
input->isKeyDown(keycache.key[KeyType::FORWARD]),
input->isKeyDown(keycache.key[KeyType::BACKWARD]),
input->isKeyDown(keycache.key[KeyType::LEFT]),
input->isKeyDown(keycache.key[KeyType::RIGHT]),
- input->isKeyDown(keycache.key[KeyType::JUMP]),
- input->isKeyDown(keycache.key[KeyType::SPECIAL1]),
- input->isKeyDown(keycache.key[KeyType::SNEAK]),
- input->getLeftState(),
- input->getRightState(),
+ isKeyDown(KeyType::JUMP),
+ isKeyDown(KeyType::SPECIAL1),
+ isKeyDown(KeyType::SNEAK),
+ isLeftPressed(),
+ isRightPressed(),
cam.camera_pitch,
- cam.camera_yaw
+ cam.camera_yaw,
+ input->joystick.getAxisWithoutDead(JA_SIDEWARD_MOVE),
+ input->joystick.getAxisWithoutDead(JA_FORWARD_MOVE)
);
u32 keypress_bits =
- ( (u32)(input->isKeyDown(keycache.key[KeyType::FORWARD]) & 0x1) << 0) |
- ( (u32)(input->isKeyDown(keycache.key[KeyType::BACKWARD]) & 0x1) << 1) |
- ( (u32)(input->isKeyDown(keycache.key[KeyType::LEFT]) & 0x1) << 2) |
- ( (u32)(input->isKeyDown(keycache.key[KeyType::RIGHT]) & 0x1) << 3) |
- ( (u32)(input->isKeyDown(keycache.key[KeyType::JUMP]) & 0x1) << 4) |
- ( (u32)(input->isKeyDown(keycache.key[KeyType::SPECIAL1]) & 0x1) << 5) |
- ( (u32)(input->isKeyDown(keycache.key[KeyType::SNEAK]) & 0x1) << 6) |
- ( (u32)(input->getLeftState() & 0x1) << 7) |
- ( (u32)(input->getRightState() & 0x1) << 8
+ ( (u32)(isKeyDown(KeyType::FORWARD) & 0x1) << 0) |
+ ( (u32)(isKeyDown(KeyType::BACKWARD) & 0x1) << 1) |
+ ( (u32)(isKeyDown(KeyType::LEFT) & 0x1) << 2) |
+ ( (u32)(isKeyDown(KeyType::RIGHT) & 0x1) << 3) |
+ ( (u32)(isKeyDown(KeyType::JUMP) & 0x1) << 4) |
+ ( (u32)(isKeyDown(KeyType::SPECIAL1) & 0x1) << 5) |
+ ( (u32)(isKeyDown(KeyType::SNEAK) & 0x1) << 6) |
+ ( (u32)(isLeftPressed() & 0x1) << 7) |
+ ( (u32)(isRightPressed() & 0x1) << 8
);
#ifdef ANDROID
cam->camera_pitch = event.player_force_move.pitch;
} else if (event.type == CE_DEATHSCREEN) {
show_deathscreen(¤t_formspec, client, gamedef, texture_src,
- device, client);
+ device, &input->joystick, client);
chat_backend->addMessage(L"", L"You died.");
new TextDestPlayerInventory(client, *(event.show_formspec.formname));
create_formspec_menu(¤t_formspec, client, gamedef,
- texture_src, device, fs_src, txt_dst, client);
+ texture_src, device, &input->joystick,
+ fs_src, txt_dst, client);
delete(event.show_formspec.formspec);
delete(event.show_formspec.formname);
input->resetLeftClicked();
input->resetRightClicked();
+ input->joystick.clearWasKeyDown(KeyType::MOUSE_L);
+ input->joystick.clearWasKeyDown(KeyType::MOUSE_R);
+
input->resetLeftReleased();
input->resetRightReleased();
+
+ input->joystick.clearWasKeyReleased(KeyType::MOUSE_L);
+ input->joystick.clearWasKeyReleased(KeyType::MOUSE_R);
}
TextDest *txt_dst = new TextDestNodeMetadata(nodepos, client);
create_formspec_menu(¤t_formspec, client, gamedef,
- texture_src, device, fs_src, txt_dst, client);
+ texture_src, device, &input->joystick, fs_src, txt_dst, client);
current_formspec->setFormSpec(meta->getString("formspec"), inventoryloc);
} else {
void Game::readSettings()
{
- m_cache_doubletap_jump = g_settings->getBool("doubletap_jump");
- m_cache_enable_clouds = g_settings->getBool("enable_clouds");
- m_cache_enable_particles = g_settings->getBool("enable_particles");
- m_cache_enable_fog = g_settings->getBool("enable_fog");
- m_cache_mouse_sensitivity = g_settings->getFloat("mouse_sensitivity");
- m_repeat_right_click_time = g_settings->getFloat("repeat_rightclick_time");
+ m_cache_doubletap_jump = g_settings->getBool("doubletap_jump");
+ m_cache_enable_clouds = g_settings->getBool("enable_clouds");
+ m_cache_enable_joysticks = g_settings->getBool("enable_joysticks");
+ m_cache_enable_particles = g_settings->getBool("enable_particles");
+ m_cache_enable_fog = g_settings->getBool("enable_fog");
+ m_cache_mouse_sensitivity = g_settings->getFloat("mouse_sensitivity");
+ m_cache_joystick_frustum_sensitivity = g_settings->getFloat("joystick_frustum_sensitivity");
+ m_repeat_right_click_time = g_settings->getFloat("repeat_rightclick_time");
m_cache_mouse_sensitivity = rangelim(m_cache_mouse_sensitivity, 0.001, 100.0);
}
#include "irrlichttypes_extrabloated.h"
#include <string>
#include "client/keys.h"
+#include "client/joystick_controller.h"
#include "keycode.h"
#include <list>
virtual void step(float dtime) {}
virtual void clear() {}
+
+ JoystickController joystick;
};
class ChatBackend; /* to avoid having to include chat.h */
/** GUIEngine */
/******************************************************************************/
GUIEngine::GUIEngine( irr::IrrlichtDevice* dev,
+ JoystickController *joystick,
gui::IGUIElement* parent,
IMenuManager *menumgr,
scene::ISceneManager* smgr,
/* Create menu */
m_menu = new GUIFormSpecMenu(m_device,
+ joystick,
m_parent,
-1,
m_menumanager,
* @param smgr scene manager to add scene elements to
* @param data struct to transfer data to main game handling
*/
- GUIEngine( irr::IrrlichtDevice* dev,
+ GUIEngine(irr::IrrlichtDevice* dev,
+ JoystickController *joystick,
gui::IGUIElement* parent,
IMenuManager *menumgr,
scene::ISceneManager* smgr,
}
GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev,
+ JoystickController *joystick,
gui::IGUIElement* parent, s32 id, IMenuManager *menumgr,
InventoryManager *invmgr, IGameDef *gamedef,
ISimpleTextureSource *tsrc, IFormSource* fsrc, TextDest* tdst,
m_text_dst(tdst),
m_formspec_version(0),
m_focused_element(""),
+ m_joystick(joystick),
m_font(NULL),
m_remap_dbl_click(remap_dbl_click)
#ifdef __ANDROID__
Draw static text elements
*/
for (u32 i = 0; i < m_static_texts.size(); i++) {
- const StaticTextSpec &spec = m_static_texts[i];
+ const StaticTextSpec &spec = m_static_texts[i];
core::rect<s32> rect = spec.rect;
if (spec.parent_button && spec.parent_button->isPressed()) {
#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
}
#endif
+ if (event.EventType == irr::EET_JOYSTICK_INPUT_EVENT) {
+ /* TODO add a check like:
+ if (event.JoystickEvent != joystick_we_listen_for)
+ return false;
+ */
+ bool handled = m_joystick->handleEvent(event.JoystickEvent);
+ if (handled) {
+ if (m_joystick->wasKeyDown(KeyType::ESC)) {
+ tryClose();
+ } else if (m_joystick->wasKeyDown(KeyType::JUMP)) {
+ if (m_allowclose) {
+ acceptInput(quit_mode_accept);
+ quitMenu();
+ }
+ }
+ }
+ return handled;
+ }
+
return false;
}
return false;
}
+void GUIFormSpecMenu::tryClose()
+{
+ if (m_allowclose) {
+ doPause = false;
+ acceptInput(quit_mode_cancel);
+ quitMenu();
+ } else {
+ m_text_dst->gotText(L"MenuQuit");
+ }
+}
+
bool GUIFormSpecMenu::OnEvent(const SEvent& event)
{
if (event.EventType==EET_KEY_INPUT_EVENT) {
KeyPress kp(event.KeyInput);
if (event.KeyInput.PressedDown && ( (kp == EscapeKey) ||
(kp == getKeySetting("keymap_inventory")) || (kp == CancelKey))) {
- if (m_allowclose) {
- doPause = false;
- acceptInput(quit_mode_cancel);
- quitMenu();
- } else {
- m_text_dst->gotText(L"MenuQuit");
- }
+ tryClose();
return true;
} else if (m_client != NULL && event.KeyInput.PressedDown &&
(kp == getKeySetting("keymap_screenshot"))) {
#include "modalMenu.h"
#include "guiTable.h"
#include "network/networkprotocol.h"
+#include "client/joystick_controller.h"
#include "util/string.h"
#include "util/enriched_string.h"
public:
GUIFormSpecMenu(irr::IrrlichtDevice* dev,
+ JoystickController *joystick,
gui::IGUIElement* parent, s32 id,
IMenuManager *menumgr,
InventoryManager *invmgr,
video::SColor m_default_tooltip_color;
private:
- IFormSource *m_form_src;
- TextDest *m_text_dst;
- unsigned int m_formspec_version;
- std::string m_focused_element;
+ IFormSource *m_form_src;
+ TextDest *m_text_dst;
+ unsigned int m_formspec_version;
+ std::string m_focused_element;
+ JoystickController *m_joystick;
typedef struct {
bool explicit_size;
bool parseSizeDirect(parserData* data, std::string element);
void parseScrollBar(parserData* data, std::string element);
+ void tryClose();
+
/**
* check if event is part of a double click
* @param event event to evaluate
speedH += move_direction;
}
}
- if(control.down)
- {
+ if (control.down) {
speedH -= move_direction;
}
- if(control.left)
- {
+ if (!control.up && !control.down) {
+ speedH -= move_direction *
+ (control.forw_move_joystick_axis / 32767.f);
+ }
+ if (control.left) {
speedH += move_direction.crossProduct(v3f(0,1,0));
}
- if(control.right)
- {
+ if (control.right) {
speedH += move_direction.crossProduct(v3f(0,-1,0));
}
+ if (!control.left && !control.right) {
+ speedH -= move_direction.crossProduct(v3f(0,1,0)) *
+ (control.sidew_move_joystick_axis / 32767.f);
+ }
if(control.jump)
{
if (free_move) {
RMB = false;
pitch = 0;
yaw = 0;
+ sidew_move_joystick_axis = .0f;
+ forw_move_joystick_axis = .0f;
}
PlayerControl(
bool a_up,
bool a_LMB,
bool a_RMB,
float a_pitch,
- float a_yaw
+ float a_yaw,
+ float a_sidew_move_joystick_axis,
+ float a_forw_move_joystick_axis
)
{
up = a_up;
RMB = a_RMB;
pitch = a_pitch;
yaw = a_yaw;
+ sidew_move_joystick_axis = a_sidew_move_joystick_axis;
+ forw_move_joystick_axis = a_forw_move_joystick_axis;
}
bool up;
bool down;
bool RMB;
float pitch;
float yaw;
+ float sidew_move_joystick_axis;
+ float forw_move_joystick_axis;
};
class Map;