/* 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 "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; namespace input { //--- local variables --------------------------------------------- Keyboard *keyboard = 0; //bool free_control = true; // 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; //--- 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_ui_view(std::string const &args) { if (core::application()->connected() && core::localcontrol()) { render::Camera::next_mode(); 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) { keyboard->list_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) { keyboard->list_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) { if (args.size() > keyname.size()+1) { keyboard->bind(keyname, args.substr(keyname.size()+1)); } else { keyboard->list_bind(keyname); } return; } core::Func *func = core::Func::find("bind"); con_print << "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", core::Cvar::Archive); input_mousecontrol->set_info("[bool] enable or disable mouse control"); 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_view", func_ui_view); func->set_info("switch camera view"); 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("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_EnableUNICODE(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); } } void frame(float seconds) { 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(); } 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: key = keyboard->press(event.key.keysym.sym); pressed = true; break; case SDL_KEYUP: key = keyboard->release(event.key.keysym.sym); pressed = false; break; case SDL_QUIT: core::application()->shutdown(); return; break; } if (key) { if (pressed) { /* if (keysym.mod & KMOD_NUM) numlock = true else numlock = false; if (keysym.mod & KMOD_CAPS) capslock = true; else capslock = false; if ((keysym.mod & KMOD_LSHIFT) || (keysym.mod & KMOD_RSHIFT)) capslock != capslock; */ // FIXME console key is always captured // FIXME ESC should escape to gui if ((key->sym() == SDLK_ESCAPE) || (key->bind(Key::None).compare("ui_console") == 0)) { console()->toggle(); local_direction = 0.0f; local_pitch = 0.0f; local_roll = 0.0f; render::Camera::set_direction(0.0f); render::Camera::set_pitch(0.0f); } else if (console()->visible()) { // send key events to the console if (event.type == SDL_KEYDOWN) console()->keypressed(translate_keysym(event.key.keysym)); } else if (chat::visible()) { // send key events to the chat box if (event.type == SDL_KEYDOWN) chat::keypressed(translate_keysym(event.key.keysym)); } else if (core::application()->connected() && core::localcontrol()) { if ((key->sym() == 512 + SDL_BUTTON_LEFT) && targets::hover()) { // hovering target selected targets::select_target(targets::hover()); } else { // FIXME modifier char c = key->bind(Key::None).c_str()[0]; if (c == '+') { // action bind action_press(key->bind(Key::None)); } else if (c) { // normal bind core::cmd() << key->bind(Key::None) << "\n"; } } } else if (core::application()->connected()) { // FIXME modifier char c = key->bind(Key::None).c_str()[0]; if (c && c != '+') { // normal bind core::cmd() << key->bind(Key::None) << "\n"; } } } else { if (core::application()->connected() && core::localcontrol() && !console()->visible() && !chat::visible()) { // FIXME modifier (note: mmmmm, modifier could be released by now) char c = key->bind(Key::None).c_str()[0]; if (c == '+') { // action bind action_release(key->bind(Key::None)); } } } } } mouse_control = false; mouse_deadzone = false; if (core::application()->connected() && core::localcontrol()) { mouse_control = !console()->visible() && ((input_mousecontrol->value() > 0) || mouse_control_override); 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)); } } else { /* const float MIN_DELTA = 10e-10; const float ANGLE_DELTA = 0.01f; math::Axis target_axis; // FIXME float cosangle; const math::Axis & entity_axis= core::localcontrol()->axis(); // auto-level: pitch local_pitch = 0; // project target_axis.up() into the plane with axis->left() normal math::Vector3f n = entity_axis.left(); math::Vector3f p = target_axis.up(); float u = p[0]*n[0] + p[1]*n[1] + p[2]*n[2] / (-n[0]*n[0] - n[1]*n[1] - n[2] * n[2]); p = target_axis.up() + u * n; float side = entity_axis.forward().x * p.x + entity_axis.forward().y * p.y + entity_axis.forward().z * p.z; if ((fabs(side) - MIN_DELTA > 0)) { cosangle = math::dotproduct(p, entity_axis.up()); if (fabs(cosangle) + ANGLE_DELTA < 1 ) { local_pitch = -3 * math::sgnf(side) * (1-cosangle); } } // auto-level: roll local_roll = 0; // project target_axis.up() into the plane with axis.forward() normal n = entity_axis.forward(); p = target_axis.up(); u = p[0]*n[0] + p[1]*n[1] + p[2]*n[2] / (-n[0]*n[0] - n[1]*n[1] - n[2] * n[2]); p = target_axis.up() + u * n; side = entity_axis.left().x * p.x + entity_axis.left().y * p.y + entity_axis.left().z * p.z; if ((fabs(side) - MIN_DELTA > 0)) { cosangle = math::dotproduct(p, entity_axis.up()); if (fabs(cosangle) + ANGLE_DELTA < 1 ) { local_roll = 3 * math::sgnf(side) * (1-cosangle); } } */ } 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