/* client/client.cc 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 #include #include #include #include #include "audio/audio.h" #include "audio/sources.h" #include "client/client.h" #include "client/video.h" #include "client/input.h" #include "client/soundext.h" #include "client/savegamemenu.h" #include "core/core.h" #include "core/loader.h" #include "core/zone.h" #include "filesystem/filesystem.h" #include "render/render.h" #include "ui/ui.h" namespace client { core::Cvar *cl_framerate = 0; core::Cvar *cl_frametime = 0; Client app; //--- public ------------------------------------------------------ Client *client() { return &app; } void run(int count, char **arguments) { con_print << " command line "; for (int i = 0; i < count; i++) { con_print << arguments[i] << " "; } con_print << std::endl; app.init(count, arguments); app.run(); app.shutdown(); } //--- private ----------------------------------------------------- void Client::quit(int status) { SDL_Quit(); core::Application::quit(status); } void Client::init(int count, char **arguments) { client_mainwindow = 0; con_print << "^BInitializing client..." << std::endl; // initialize core core::Cvar::sv_private = core::Cvar::set("sv_private", "0"); core::Application::init(count, arguments); // engine variables cl_framerate = core::Cvar::get("cl_framerate", "125", core::Cvar::Archive); cl_framerate->set_info("[int] client framerate in frames/sec"); // player info variables core::Cvar *cvar = 0; cvar = core::Cvar::get("cl_name", "Player", core::Cvar::Archive | core::Cvar::Info); cvar->set_info("[str] player name"); cvar = core::Cvar::get("cl_color", "1.0 1.0 1.0", core::Cvar::Archive | core::Cvar::Info); cvar->set_info("[r g b] player primary color"); cvar = core::Cvar::get("cl_colorsecond", "1.0 1.0 1.0", core::Cvar::Archive | core::Cvar::Info); cvar->set_info("[r g b] player secondary color"); cvar = core::Cvar::get("rconpassword", "", core::Cvar::Archive | core::Cvar::Info); cvar->set_info("[string] password for remote console access"); snd_volume = core::Cvar::get("snd_volume", "0.8", core::Cvar::Archive); snd_volume->set_info("[float] master volume from 0 (mute) to 1 (max volume)"); snd_engines = core::Cvar::get("snd_engines", "1", core::Cvar::Archive); snd_engines->set_info("[bool] enable or disable engine sounds"); snd_doppler = core::Cvar::get("snd_doppler", "0", core::Cvar::Archive); snd_doppler->set_info("[bool] enable or disable doppler effect"); // initialize SDL, but do not initialize any subsystems SDL_Init(0); // initialize user interface ui::init(); client_mainwindow = new MainWindow(ui::root()); // FIXME needs to be a mainwindow child client_testmodelwindow = new TestModelWindow(ui::root()); client_testmodelwindow->hide(); // Initialize the video subsystem if (!video::init()) { quit(1); } // initialize audio audio::init(); // initialize input input::init(); // add engine functions core::Func *func = 0; func = core::Func::add("r_restart", Client::func_r_restart); func->set_info("restart render subsystem"); func = core::Func::add("snd_restart", Client::func_snd_restart); func->set_info("restart audio subsystem"); func = core::Func::add("list_ui", func_list_ui); func->set_info("list user interface widgets"); func = core::Func::add("list_menu", func_list_menu); func->set_info("list available menus"); func = core::Func::add("ui", func_ui); func->set_info("[command] user interface functions"); func = core::Func::add("ui_restart", func_ui_restart); func->set_info("reload user interface files"); func = core::Func::add("ui_console", func_ui_console); func->set_info("toggle console"); func = core::Func::add("ui_chat", func_ui_chat); func->set_info("toggle chat window"); func = core::Func::add("ui_chatbar", func_ui_chatbar); func->set_info("toggle chat bar"); func = core::Func::add("ui_inventory", func_ui_inventory); func->set_info("toggle inventory"); func = core::Func::add("ui_map", func_ui_map); func->set_info("toggle map"); func = core::Func::add("ui_menu", func_ui_menu); func->set_info("toggle main menu"); func = core::Func::add("testmodel", func_testmodel); func->set_info("[str] load and view a single 3D model"); func = core::Func::add("menu", func_menu); func->set_info("[command] menu functions"); func = core::Func::add("view", func_view); func->set_info("[command] view menu functions"); func = core::Func::add("loadgame", func_loadgame); func->set_info("[filename] load game"); func = core::Func::add("savegame", func_savegame); func->set_info("[filename] [description] save game"); previous_timestamp = 0; } void Client::run() { con_print << "^BRunning client..." << std::endl; // seed random generator unsigned int seed = (unsigned int) SDL_GetTicks(); srand(seed); // default framerate 125fps, 8 milliseconds unsigned long client_frame_lenght = 8; unsigned long client_current_timestamp = 0; while (true) { // current time in microseconds client_current_timestamp = timestamp(); // calculate the desired frame length if (cl_framerate->value() < 0) { (*cl_framerate) = 0.0f; } else if (cl_framerate->value() > 1000.0f) { (*cl_framerate) = 1000.0f; } if (cl_framerate->value()) { client_frame_lenght = (unsigned long) floorf(1000.0f / cl_framerate->value()); if (client_frame_lenght < 1) client_frame_lenght = 1; } else { client_frame_lenght = 1; } frame(); // sleep for the remainder of the frame const unsigned long elapsed = timestamp() - client_current_timestamp; if (elapsed < client_frame_lenght - 1) { sys::sleep (client_frame_lenght - elapsed -1); } while (timestamp() < client_current_timestamp + client_frame_lenght) { // busy waiting for the last microsecond } } } void Client::frame() { input::frame(); core::Application::frame(); if (!connected()) { const std::string module_label(core::Loader::label()); if (!ui::console()->visible()) { // load the intro if nothing is running if (load("intro")) { connect(""); if (module_label.size()) load(module_label); } else { ui::console()->toggle(); } } if (client_mainwindow->visible()) { client_mainwindow->hide(); } } else { if (client_mainwindow->hidden()) { client_mainwindow->show(); } if (ui::console()->visible()) { ui::console()->raise(); ui::console()->set_focus(); } } const float now = timestamp(); video::frame((float)(now - previous_timestamp) / 1000.0f); previous_timestamp = now; } void Client::shutdown() { con_print << "^BShutting down client..." << std::endl; if (connected()) disconnect(); core::Func::remove("r_restart"); core::Func::remove("snd_restart"); core::Func::remove("list_menu"); core::Func::remove("list_ui"); core::Func::remove("ui"); core::Func::remove("ui_restart"); core::Func::remove("ui_console"); core::Func::remove("ui_chat"); core::Func::remove("ui_chatbar"); core::Func::remove("ui_map"); core::Func::remove("ui_menu"); core::Func::remove("menu"); core::Func::remove("view"); audio::shutdown(); input::shutdown(); video::shutdown(); ui::shutdown(); core::Application::shutdown(); quit(0); } /* -- notifications from core::Application ------------------------- */ void Client::notify_connect() { video::set_loader_message(); video::frame_loader(); mainwindow()->clear(); } void Client::notify_disconnect() { mainwindow()->gamewindow()->hide(); mainwindow()->clear(); model::Material::clear(); audio::reset(); render::reset(); input::reset(); model::Material::init(); } void Client::notify_zonechange() { video::set_loader_message(); video::frame_loader(); // unload entity sounds for (core::Entity::Registry::iterator it = core::Entity::registry().begin(); it != core::Entity::registry().end(); it++) { core::Entity *entity = (*it).second; if (ext_sound(entity)) delete ext_sound(entity); } render::unload(); } void Client::notify_message(const char *message) { std::string text(message); notify_message(core::Message::Info, text); } void Client::notify_message(const std::string &message) { notify_message(core::Message::Info, message); } void Client::notify_message(const core::Message::Channel channel, const std::string &message) { switch (channel) { case core::Message::Info: // Info message break; case core::Message::RCon: // RCon message break; case core::Message::Public: // Public chat message audio::play("com/chat"); break; case core::Message::Local: // Local chat message break; case core::Message::Private: // Private chat message audio::play("com/priv"); break; case core::Message::Sound: // Sound event audio::play(message.c_str()); // a sound event prints nothing to the console return; default: break; } if (mainwindow()) { mainwindow()->event_text(message); } con_print << message << std::endl; } void Client::notify_loader(const std::string &message) { video::set_loader_message(message.c_str()); } //--- engine functions -------------------------------------------- void Client::func_snd_restart(std::string const &args) { // unload entity sounds for (core::Entity::Registry::iterator it = core::Entity::registry().begin(); it != core::Entity::registry().end(); it++) { core::Entity *entity = (*it).second; if (ext_sound(entity)) delete ext_sound(entity); } audio::reset(); } void Client::func_r_restart(std::string const &args) { video::restart(); video::set_loader_message(); video::frame_loader(); } /* ---- func_ui ---------------------------------------------------- */ void Client::func_ui_console(std::string const &args) { ui::console()->toggle(); } // list user interface widgets void Client::func_list_ui(std::string const &args) { if (ui::root()) { ui::root()->list(); } } void Client::func_ui_restart(std::string const &args) { if (ui::root()) { ui::root()->load_settings(); } } void Client::func_list_menu(std::string const &args) { if (ui::root()) { // FIXME ui::root()->list_menus(); } } // help for the global ui functions void Client::func_ui_help() { con_print << "^BUser interface functions" << std::endl; con_print << " ui help show this help" << std::endl; con_print << " ui debug toggle debug mode" << std::endl; con_print << " ui list list widgets" << std::endl; con_print << " ui visible list visible widgets" << std::endl; con_print << " ui restart reload user interface files" << std::endl; } // global ui functions void Client::func_ui(std::string const &args) { if (!ui::root()) { con_warn << "User Interface not available!" << std::endl; return; } if (!args.size()) { func_ui_help(); return; } std::stringstream argstr(args); std::string command; argstr >> command; aux::to_label(command); if (command.compare("help") == 0) { func_ui_help(); } else if (command.compare("debug") == 0) { ui::UI::ui_debug = !ui::UI::ui_debug; } else if (command.compare("list") == 0) { ui::root()->list(); } else if (command.compare("visible") == 0) { ui::root()->list_visible(); } else if (command.compare("restart") == 0) { ui::root()->load_settings(); } else { func_ui_help(); } } // help for the global menu functions void Client::func_menu_help() { con_print << "^Bmenu functions" << std::endl; con_print << "^RTODO" << std::endl; } void Client::show_menu(const char *menu) { client()->client_mainwindow->mainmenu()->show_menu(menu); } // global menu fuctions void Client::func_menu(std::string const &args) { if (!ui::root()) { con_warn << "User Interface not available!" << std::endl; return; } if (!args.size()) { func_menu_help(); return; } std::stringstream argstr(args); std::string command; argstr >> command; aux::to_label(command); if (command.compare("help") == 0) { func_menu_help(); } else if (command.compare("hide") == 0) { client()->client_mainwindow->mainmenu()->hide(); } else if (command.compare("close") == 0) { client()->client_mainwindow->mainmenu()->hide(); } else if (command.compare("default") == 0) { client()->client_mainwindow->mainmenu()->show_default(); } else if (command.compare("list") == 0) { // TODO client_mainwindow->mainmenu()->list(); } else { show_menu(command.c_str()); } } // used by keybinds to open the main menu void Client::func_ui_menu(std::string const &args) { if (client()->connected()) { if (client()->client_mainwindow->mainmenu()->visible()) { client()->client_mainwindow->mainmenu()->hide(); } else { // show the main menu on non-interactive modules if (!core::game()->interactive()) { show_menu("main"); } else { show_menu("game"); } } } else { con_print << "Not connected." << std::endl; } } // used by keybinds to open the chat view void Client::func_ui_chat(std::string const &args) { if (client()->connected() && client()->mainwindow()->gamewindow()->visible()) { client()->mainwindow()->gamewindow()->toggle_chat(); } } // used by keybinds to open the chat bar void Client::func_ui_chatbar(std::string const &args) { if (client()->connected() && client()->mainwindow()->gamewindow()->visible()) { client()->mainwindow()->gamewindow()->toggle_chatbar(); } } // used by keybinds to open the inventory view void Client::func_ui_inventory(std::string const &args) { if (client()->connected() && client()->mainwindow()->gamewindow()->visible()) { client()->mainwindow()->gamewindow()->toggle_inventory(); } } // used by keybinds to open the map view void Client::func_ui_map(std::string const &args) { if (client()->connected() && client()->mainwindow()->gamewindow()->visible()) { client()->mainwindow()->gamewindow()->toggle_map(); } } // used by views and keybinds and to open entity views void Client::func_view(std::string const &args) { if (client()->mainwindow()) { client()->mainwindow()->gamewindow()->show_menu(args); } } // open the testmodel window void Client::func_testmodel(std::string const &args) { // if testmodel is called from the command line, no module has been loaded yet if (!client()->connected()) { const std::string module_label(core::Loader::label()); // load the intro if nothing is running if (client()->load("intro")) { client()->connect(""); if (module_label.size()) client()->load(module_label); } } if (!client()->connected()) { client()->client_testmodelwindow->hide(); ui::console()->show(); return; } std::string modelname(args); aux::trim(modelname); if (!modelname.size()) { con_print << "usage: testmodel [model name]" << std::endl; } //video::set_loader_message(); //video::frame_loader(); client()->client_testmodelwindow->set_modelname(modelname); client()->client_testmodelwindow->raise(); // raise the window client()->client_testmodelwindow->show(); if (ui::console()->visible()) { ui::console()->raise(); ui::console()->set_focus(); } } // quick load void Client::func_savegame(std::string const &args) { std::stringstream argstr(args); std::string savename; std::string descr; argstr >> savename; aux::to_label(savename); if (!savename.size()) { savename.assign("quicksave"); descr.assign("QUICKSAVE"); } else { std::string word; while(argstr >> word) { if (descr.size()) { descr += ' '; } descr.append(word); } } SaveGameMenu::savegame(savename, descr); } // quik save void Client::func_loadgame(std::string const &args) { std::stringstream argstr(args); std::string savename; argstr >> savename; aux::to_label(savename); if (!savename.size()) { savename.assign("quicksave"); } SaveGameMenu::loadgame(savename); } } // namespace client