/* client/input.h This file is part of the Osirion project and is distributed under the terms and conditions of the GNU General Public License version 2 */ #include "auxiliary/functions.h" #include "audio/audio.h" #include "core/core.h" #include "client/client.h" #include "client/input.h" #include "client/chat.h" #include "client/console.h" #include "client/keyboard.h" #include "client/targets.h" #include "client/video.h" #include "render/camera.h" #include "math/functions.h" #include "render/text.h" #include "render/draw.h" #include "SDL/SDL.h" namespace client { core::Cvar *input_mousecontrol = 0; core::Cvar *input_keydelay = 0; core::Cvar *input_keyrepeat = 0; namespace input { //--- local variables --------------------------------------------- // keyboard instance Keyboard *keyboard = 0; // keyboard modifiers shift, ctrl, alt etc int keyboard_modifiers = 0; // local controls float local_direction = 0.0f; float local_pitch = 0.0f; float local_thrust = 0.0f; float local_roll = 0.0f; // last controlled entity unsigned int last_control = 0; // mouse cursor position int mouse_x = 0; int mouse_y = 0; float mouse_pitch = 0; float mouse_direction = 0; // true if the mouse is in the deadzone bool mouse_deadzone = false; // true if the mouse has control bool mouse_control_override = false; bool mouse_control = false; const float thruster_offset = 0.05f; int mouse_position_x() { return mouse_x; } int mouse_position_y() { return mouse_y; } //--- engine functions -------------------------------------------- void func_screenshot(std::string const & args) { video::screenshot(); } void func_ui_control(std::string const &args) { if (!core::localcontrol()) return; if (input_mousecontrol->value() > 0) { (*input_mousecontrol) = 0.0f; } else { (*input_mousecontrol) = 1.0f; } if (!input_mousecontrol->value()) { local_direction = 0.0f; local_pitch = 0.0f; local_roll = 0.0f; render::Camera::set_direction(0.0f); render::Camera::set_pitch(0.0f); } audio::play("ui/control"); } void func_ui_console(std::string const &args) { console()->toggle(); } void func_ui_chat(std::string const &args) { if (core::application()->connected()) { chat::toggle(); } } void func_view_next(std::string const &args) { if (core::application()->connected() && core::localcontrol()) { render::Camera::view_next(); local_roll = 0; local_pitch = 0; local_direction = 0; audio::play("ui/view"); } } void func_view_prev(std::string const &args) { if (core::application()->connected() && core::localcontrol()) { render::Camera::view_previous(); local_roll = 0; local_pitch = 0; local_direction = 0; audio::play("ui/view"); } } void func_list_actions(std::string const &args) { if (keyboard) keyboard->list_actions(); else con_warn << "Keyboard handler not installed!" << std::endl; } void func_list_keys(std::string const &args) { if (keyboard) { std::stringstream argstr(args); std::string keyname; if (argstr >> keyname) { aux::to_lowercase(keyname); keyboard->bind(keyname, ""); } else { keyboard->list_keys(); } } else { con_warn << "Keyboard handler not installed!" << std::endl; } } void func_list_binds(std::string const &args) { if (keyboard) { std::stringstream argstr(args); std::string keyname; if (argstr >> keyname) { aux::to_lowercase(keyname); keyboard->bind(keyname, ""); } else { keyboard->list_binds(); } } else { con_warn << "Keyboard handler not installed!" << std::endl; } } void func_bind(std::string const &args) { if (keyboard) { std::stringstream argstr(args); std::string keyname; if (argstr >> keyname) { aux::to_lowercase(keyname); if (args.size() > keyname.size()+1) { keyboard->bind(keyname, args.substr(keyname.size()+1)); } else { keyboard->bind(keyname, ""); } return; } else { core::Func *func = core::Func::find("bind"); con_print << "Usage: bind " << func->info() << std::endl; } } else { con_warn << "Keyboard handler not installed!" << std::endl; } } void func_unbind(std::string const &args) { if (keyboard) { std::stringstream argstr(args); std::string keyname; if (argstr >> keyname) { keyboard->unbind(keyname); return; } core::Func *func = core::Func::find("unbind"); con_print << "unbind " << func->info() << std::endl; } else { con_warn << "Keyboard handler not installed!" << std::endl; } } //--- input functions --------------------------------------------- void init() { con_print << "^BInitializing input..." << std::endl; keyboard = new Keyboard(); client::setkeyboardmode(false); SDL_ShowCursor(SDL_DISABLE); SDL_WM_GrabInput(SDL_GRAB_ON); // SDL_EnableUNICODE(1); input_mousecontrol = core::Cvar::get("input_mousecontrol", 1.0f, core::Cvar::Archive); input_mousecontrol->set_info("[bool] enable or disable mouse control"); input_keydelay = core::Cvar::get("input_keydelay", 250.0f, core::Cvar::Archive); input_keydelay->set_info("[int] keyboard delay time-out in milliseconds"); input_keyrepeat = core::Cvar::get("input_keyrepeat", 30.0f, core::Cvar::Archive); input_keyrepeat->set_info("[int] keyboard repeat time-out in milliseconds"); core::Func *func = 0; func = core::Func::add("ui_console", func_ui_console); func->set_info("toggle console on or off"); func = core::Func::add("ui_chat", func_ui_chat); func->set_info("toggle chatbox on or of"); func = core::Func::add("ui_control",func_ui_control); func->set_info("toggle mouse control"); func = core::Func::add("list_actions", func_list_actions); func->set_info("list key action names"); func = core::Func::add("list_keys", func_list_keys); func->set_info("list key names"); func = core::Func::add("list_binds",func_list_binds); func->set_info("list keyb binds"); func = core::Func::add("bind", (core::FuncPtr) func_bind); func->set_info("[key] [str] bind a command to a key"); func = core::Func::add("unbind", func_unbind); func->set_info("[key] unbind a key"); func = core::Func::add("unbindall", func_unbind); func->set_info("unbind all keys"); func = core::Func::add("view_next", func_view_next); func->set_info("switch to next view"); func = core::Func::add("view_prev", func_view_prev); func->set_info("switch to previous view"); func = core::Func::add("screenshot", func_screenshot); func->set_info("make a screenshot"); keyboard->load_binds(); } void shutdown() { con_print << "^BShutting down input..." << std::endl; core::Func::remove("list_actions"); core::Func::remove("list_keys"); core::Func::remove("list_binds"); core::Func::remove("bind"); core::Func::remove("unbind"); core::Func::remove("unbindall"); core::Func::remove("screenshot"); core::Func::remove("ui_console"); core::Func::remove("ui_control"); core::Func::remove("ui_chat"); core::Func::remove("ui_view"); keyboard->save_binds(); delete keyboard; keyboard = 0; SDL_ShowCursor(SDL_ENABLE); SDL_WM_GrabInput(SDL_GRAB_OFF); // SDL_DisableUNICODE(0); } void action_press(std::string const &action) { /* -- thruster ------------------------------------ */ if (action.compare("+thrustup") == 0) { local_thrust += thruster_offset; } else if (action.compare("+thrustdown") == 0) { local_thrust -= 2.0f * thruster_offset; /* -- mouse control ------------------------------- */ } else if (action.compare("+control") == 0) { mouse_control_override = true; /* -- directional control ------------------------- */ } else if (action.compare("+left") == 0) { local_direction = 1.0f; } else if (action.compare("+right") == 0) { local_direction = -1.0f; } else if (action.compare("+up") == 0) { local_pitch = 1.0f; } else if (action.compare("+down") == 0) { local_pitch = -1.0f; } else if (action.compare("+rollleft") == 0) { local_roll = 1.0f; } else if (action.compare("+rollright") == 0) { local_roll = -1.0f; /* -- camera control ------------------------------ */ } else if (action.compare("+camleft") == 0) { render::Camera::set_direction(1.0f); } else if (action.compare("+camright") == 0) { render::Camera::set_direction(-1.0f); } else if (action.compare("+camup") == 0) { render::Camera::set_pitch(1.0f); } else if (action.compare("+camdown") == 0) { render::Camera::set_pitch(-1.0f); } else con_warn << "Unknown action " << action << std::endl; } void action_release(std::string const &action) { /* -- thruster ------------------------------------ */ if (action.compare("+thrustup") == 0) { } else if (action.compare("+thrustdown") == 0) { /* -- mouse control ------------------------------- */ } else if (action.compare("+control") == 0) { mouse_control_override = false; if (!input_mousecontrol->value()) { local_direction = 0.0f; local_pitch = 0.0f; local_roll = 0.0f; render::Camera::set_direction(0.0f); render::Camera::set_pitch(0.0f); } /* -- directional control ------------------------- */ } else if (action.compare("+left") == 0) { local_direction = 0.0f; } else if (action.compare("+right") == 0) { local_direction = 0.0f; } else if (action.compare("+up") == 0) { local_pitch = 0.0f; } else if (action.compare("+down") == 0) { local_pitch = 0.0f; } else if (action.compare("+rollleft") == 0) { local_roll = 0.0f; } else if (action.compare("+rollright") == 0) { local_roll = 0.0f; /* -- camera control ------------------------------ */ } else if (action.compare("+camleft") == 0) { render::Camera::set_direction(0.0f); } else if (action.compare("+camright") == 0) { render::Camera::set_direction(0.0f); } else if (action.compare("+camup") == 0) { render::Camera::set_pitch(0.0f); } else if (action.compare("+camdown") == 0) { render::Camera::set_pitch(0.0f); } } Key::Modifier convert_SDL_modifier(int const sdlmodifier) { if (sdlmodifier & Key::Shift) return Key::Shift; else if (sdlmodifier & Key::Ctrl) return Key::Ctrl; else if (sdlmodifier & Key::Alt) return Key::Alt; else return Key::None; } void key_pressed(Key *key) { if (key->sym() == SDLK_ESCAPE) { if (chat::visible()) { chat::toggle(); } else { local_direction = 0.0f; local_pitch = 0.0f; local_roll = 0.0f; render::Camera::set_direction(0.0f); render::Camera::set_pitch(0.0f); console()->toggle(); } } else if (key->bind(Key::None).compare("ui_console") == 0) { local_direction = 0.0f; local_pitch = 0.0f; local_roll = 0.0f; render::Camera::set_direction(0.0f); render::Camera::set_pitch(0.0f); console()->toggle(); } else if (console()->visible()) { // send key events to the console if (key->sym() < 512) console()->keypressed(translate_keysym(key->sym(), keyboard_modifiers)); } else if (chat::visible()) { // send key events to the chat box if (key->sym() < 512) chat::keypressed(translate_keysym(key->sym(), keyboard_modifiers)); } else if (core::application()->connected() && core::localcontrol()) { if ((render::Camera::mode() == render::Camera::Free) && (key->sym() == 512 + SDL_BUTTON_LEFT) && targets::hover()) { // hovering target selected targets::select_target(targets::hover()); } else { char c = key->bind(convert_SDL_modifier(keyboard_modifiers)).c_str()[0]; if (c == '+') { // action bind action_press(key->bind(convert_SDL_modifier(keyboard_modifiers))); } else if (c) { // normal bind core::cmd() << key->bind(convert_SDL_modifier(keyboard_modifiers)) << "\n"; } } } else if (core::application()->connected()) { char c = key->bind(convert_SDL_modifier(keyboard_modifiers)).c_str()[0]; if (c && c != '+') { // normal bind core::cmd() << key->bind(convert_SDL_modifier(keyboard_modifiers)) << "\n"; } } } void key_released(Key *key) { if (core::application()->connected() && core::localcontrol() && !console()->visible() && !chat::visible()) { if ((render::Camera::mode() != render::Camera::Free) && (key->sym() == 512 + SDL_BUTTON_LEFT) && targets::hover()) { // hovering target selected targets::select_target(targets::hover()); } // the release event still must be processed as usual char c = 0; c = key->bind(Key::None).c_str()[0]; if (c == '+') { // action bind action_release(key->bind(Key::None)); } c = key->bind(Key::Shift).c_str()[0]; if (c == '+') { // action bind action_release(key->bind(Key::Shift)); } c = key->bind(Key::Ctrl).c_str()[0]; if (c == '+') { // action bind action_release(key->bind(Key::Ctrl)); } c = key->bind(Key::Alt).c_str()[0]; if (c == '+') { // action bind action_release(key->bind(Key::Alt)); } } } void reset() { local_direction = 0.0f; local_pitch = 0.0f; local_roll = 0.0f; if (core::localcontrol()) { local_thrust = core::localcontrol()->thrust(); last_control = core::localcontrol()->id(); } else { local_thrust = 0; last_control = 0; } mouse_pitch = 0.0f; mouse_direction = 0.0f; mouse_x = video::width / 2; mouse_y = video::height / 2; render::Camera::reset(); mouse_control_override = false; targets::reset(); render::reset(); for (Keyboard::iterator it = keyboard->begin(); it != keyboard->end(); it++) { Key *key = (*it).second; if (key) { key->pressed() = 0; key->lastpressed() = 0; } } } void frame(float seconds) { /* -- detect localcontrol() changes --------------- */ if (core::localcontrol() && (last_control != core::localcontrol()->id())) { local_direction = 0.0f; local_pitch = 0.0f; local_roll = 0.0f; local_thrust = core::localcontrol()->thrust(); last_control = core::localcontrol()->id(); mouse_pitch = 0.0f; mouse_direction = 0.0f; mouse_x = video::width / 2; mouse_y = video::height / 2; render::Camera::reset(); mouse_control_override = false; targets::reset(); render::reset(); } /* -- poll SDL keyboard events -------------------- */ SDL_Event event; Key *key = 0; bool pressed = false; while (SDL_PollEvent(&event)) { pressed = false; key = 0; switch (event.type) { case SDL_MOUSEMOTION: mouse_x = event.motion.x; mouse_y = event.motion.y; break; case SDL_MOUSEBUTTONDOWN: // override for gui mouse down key = keyboard->press(512 + event.button.button); pressed = true; break; case SDL_MOUSEBUTTONUP: key = keyboard->release(512 + event.button.button); pressed = false; break; case SDL_KEYDOWN: keyboard_modifiers = event.key.keysym.mod; key = keyboard->press(event.key.keysym.sym); pressed = true; break; case SDL_KEYUP: keyboard_modifiers = event.key.keysym.mod; key = keyboard->release(event.key.keysym.sym); pressed = false; break; case SDL_QUIT: core::application()->shutdown(); return; break; } if (key) { if (pressed) key_pressed(key); else key_released(key); } } /* -- handle key repeat --------------------------- */ float delay = 250.0f; // key delay time-out in milliseconds if (input_keydelay) { delay = input_keydelay->value(); math::clamp(delay, 50.0f, 500.0f); } float repeat = 30.0f; // key repeat time-out in milliseconds if (input_keyrepeat) { repeat = input_keyrepeat->value(); math::clamp(repeat, 10.0f, 250.0f); } if (repeat > delay) { float tmp = repeat; repeat = delay; delay = tmp; } if (input_keydelay) (*input_keydelay) = delay; if (input_keyrepeat) (*input_keyrepeat) = repeat; repeat /= 1000.0f; delay /= 1000.0f; for (Keyboard::iterator it = keyboard->begin(); it != keyboard->end(); it++) { key = (*it).second; if (key && key->sym() < 512 && key->pressed()) { while ((key->pressed()+delay < core::application()->time()) && (key->lastpressed()+repeat < core::application()->time())) { if (key->lastpressed() > key->pressed()) key->lastpressed() += repeat; else key->lastpressed() += delay; key_pressed(key); } } } /* -- process mouse movement ----------------------*/ mouse_control = false; mouse_deadzone = false; key->lastpressed() += repeat; if (core::application()->connected() && core::localcontrol()) { mouse_control = !console()->visible() && ((input_mousecontrol->value() > 0) || mouse_control_override); core::localcontrol()->set_autolevel(!mouse_control); if (mouse_control) { // the mouse will not react if it is in the deadzone int deadzone_size = 8; mouse_deadzone = true; // direction int l = mouse_x - (video::width >> 1); if (abs(l) < ( deadzone_size >> 1 )) { // dead zone mouse_direction = 0; } else { l = (mouse_x - deadzone_size) - ((video::width - deadzone_size) >> 1); mouse_direction = float (-l) / (float) ((video::width - deadzone_size) >> 1); mouse_deadzone = false; } // pitch int h = mouse_y - (video::height >> 1); if (abs(h) < ( deadzone_size >> 1 )) { // dead zone mouse_pitch = 0; } else { h = (mouse_y - deadzone_size) - ((video::height - deadzone_size) >> 1); mouse_pitch = float (-h) / (float) ((video::height - deadzone_size) >> 1); mouse_deadzone = false; } if ((render::Camera::mode() == render::Camera::Track) || (render::Camera::mode() == render::Camera::Cockpit)) { local_direction = mouse_direction * math::absf(mouse_direction); local_pitch = mouse_pitch * math::absf(mouse_pitch); } else if (render::Camera::mode() == render::Camera::Free) { // squared values to smoothen camera movement render::Camera::set_direction( -mouse_direction * math::absf(mouse_direction)); render::Camera::set_pitch(-mouse_pitch * math::absf(mouse_pitch)); } } math::clamp(local_direction, -1.0f, 1.0f); math::clamp(local_pitch, -1.0f, 1.0f); math::clamp(local_roll, -1.0f, 1.0f); math::clamp(local_thrust, 0.0f, 1.0f); if (local_thrust < 0.01f) local_thrust = 0.0f; else if (local_thrust > 0.99f) local_thrust = 1.0f; core::localcontrol()->set_thrust(local_thrust); core::localcontrol()->set_direction(local_direction); core::localcontrol()->set_pitch(local_pitch / render::Camera::aspect() ); core::localcontrol()->set_roll(local_roll); } } } // namespace input } // namespace client