/* base/game.cc This file is part of the Osirion project and is distributed under the terms of the GNU General Public License version 2 */ #include #include #include #include "auxiliary/functions.h" #include "core/gameserver.h" #include "core/parser.h" #include "core/descriptions.h" #include "filesystem/filesystem.h" #include "filesystem/inifile.h" #include "base/game.h" #include "base/collision.h" #include "base/navpoint.h" #include "base/jumppoint.h" #include "base/planet.h" #include "base/station.h" #include "base/racetrack.h" #include "base/ship.h" #include "base/star.h" #include "math/mathlib.h" #include "sys/sys.h" namespace game { /* -- class Default ----------- ----------------------------------- */ // default player settings core::Zone *Default::zone = 0; core::Entity *Default::view = 0; ShipModel *Default::shipmodel = 0; long Default::credits = 0; void Default::clear() { zone = 0; view = 0; shipmodel = 0; credits = 0; } /* -- class Game static members ----------------------------------- */ // game variables core::Cvar *Game::g_impulsespeed = 0; core::Cvar *Game::g_impulseacceleration = 0; core::Cvar *Game::g_jumppointrange = 0; core::Cvar *Game::g_devel = 0; core::Module *factory() { return new Game(); } // list the ship model registry void Game::func_list_ship(std::string const &args) { ShipModel *shipmodel = ShipModel::search(args); if (shipmodel) shipmodel->print(); else ShipModel::list(); } // a player joins the game void Game::func_join(core::Player *player, std::string const &args) { if (player->control()) return; player->set_credits(Default::credits); Ship *ship = new Ship(player, Default::shipmodel); ship->set_zone(player->zone()); player->set_control(ship); core::Entity *dock = ship->zone()->default_view(); if (dock) { ship->entity_location.assign(dock->location() + (dock->axis().forward() * ((ship->radius()+ dock->radius())*2.0f))); ship->entity_axis.assign(dock->axis()); ship->set_eventstate(core::Entity::Docked); player->set_view(dock); } std::string message("^B"); message.append(player->name()); message.append("^B joins the game."); core::server()->broadcast(message); player->send("^BYou received " + aux::article(Default::shipmodel->name())); player->sound("game/buy-ship"); player->set_dirty(); } // a player joins the spectators void Game::func_spectate(core::Player *player, std::string const &args) { if (!player->control()) return; std::string message("^B"); message.append(player->name()); message.append("^B spectates."); core::server()->broadcast(message); if (player->control()) { player->remove_asset(player->control()); } if (!player->zone()) player->set_zone(Default::zone); player->set_view(0); } // a player sends standard hails void Game::func_hail(core::Player *player, std::string const &args) { if (player->mute()) { player->send("^WYou have been muted"); return; } std::string target; std::istringstream is(args); if (!(is >> target)) { player->send("Usage: hail [player]"); return; } core::Player *targetplayer = core::server()->find_player(target); if (!targetplayer) { player->send("Player " + target + "^N not found"); return; } player->send("^BYou hail " + targetplayer->name()); player->sound("com/hail"); targetplayer->send("^B" + player->name() + "^B hails you"); targetplayer->sound("com/hail"); } // a player actives the hyperspace jump drive on his ship void Game::func_jump(core::Player *player, std::string const &args) { if (!player->control()) return; if (!player->control()->moduletype() == ship_enttype) return; Ship * ship = static_cast(player->control()); ship->func_jump(args); } // a player actives the kinetic impulse drive on his ship void Game::func_impulse(core::Player *player, std::string const &args) { if (!player->control()) return; if (!player->control()->moduletype() == ship_enttype) return; Ship * ship = static_cast(player->control()); ship->func_impulse(); } // a player sends a docking request void Game::func_dock(core::Player *player, core::Entity *entity) { if (!player->control()) return; if (player->control()->zone() != entity->zone()) return; if ((entity->flags() & core::Entity::Dockable) == 0) return; if (player->control()->eventstate() != core::Entity::Normal) return; if ((entity->flags() & core::Entity::Dockable) == core::Entity::Dockable) { entity->dock(player->control()); } } // launch request void Game::func_launch(core::Player *player, std::string const &args) { if (!player->control()) return; if (!player->view()) return; if (player->control()->eventstate() != core::Entity::Docked) return; assert(player->view()->zone() == player->control()->zone()); core::Entity *dock = player->view(); player->control()->entity_location.assign(dock->location() + (dock->axis().forward() * (player->control()->radius()+ dock->radius())*2.0f)); player->control()->entity_axis.assign(dock->axis()); player->control()->set_eventstate(core::Entity::Normal); player->set_view(0); player->send("^BLaunching from " + dock->name()); } // instantaniously goto a specified entity within the zone void Game::func_goto(core::Player *player, const std::string &args) { if (!args.size()) return; if (!g_devel->value()) { player->send("Cheats disabled"); return; } if (!player->control()) return; core::Entity *dock = player->control()->zone()->search_entity(args); if (dock) { player->control()->entity_location.assign(dock->location() + (dock->axis().forward() * (player->control()->radius()+dock->radius())*2.0f)); player->control()->entity_axis.assign(dock->axis()); player->control()->entity_axis.change_direction(180.0f); player->control()->set_eventstate(core::Entity::Normal); player->set_view(0); player->send("Going to " + dock->name()); } else { player->send("Entity '" + args + "' not found"); } } /* -- class Game -------------------------------------------------- */ Game::Game() : core::Module("Project::OSiRiON", true) { Default::clear(); ShipModel::clear(); if (!load_ships()) { abort(); return; } if (!load_world()) { abort(); return; } if (!load_player()) { abort(); return; } // add engine functions core::Func *func = 0; func = core::Func::add("list_ship", Game::func_list_ship); func->set_info("[string] list ship statistics"); func = core::Func::add("join", Game::func_join); func->set_info("join the game"); func = core::Func::add("hail", Game::func_hail); func->set_info("send a standard hail"); func = core::Func::add("spectate", Game::func_spectate); func->set_info("leave the game and spectate"); func = core::Func::add("buy", ShipDealer::func_buy); func->set_info("buy a ship"); func = core::Func::add("jump", Game::func_jump); func->set_info("[string] activate or deactivate hyperspace jump drive"); func = core::Func::add("impulse", Game::func_impulse); func->set_info("activate are deactive kinetic impulse drive"); func = core::Func::add("launch", Game::func_launch); func->set_info("launch to space when docked"); func = core::Func::add("goto", Game::func_goto); func->set_info("[string] goto to an entity within the zone"); func = core::Func::add("@dock", Game::func_dock); func->set_info("dock with target object"); // add engine variables g_impulsespeed = core::Cvar::get("g_impulsespeed", "15", core::Cvar::Game | core::Cvar::Archive); g_impulsespeed->set_info("[float] speed of the impulse drive"); g_impulseacceleration = core::Cvar::get("g_impulseacceleration", "5", core::Cvar::Game | core::Cvar::Archive); g_impulseacceleration->set_info("[float] acceleration of the impulse drive"); g_jumppointrange = core::Cvar::get("g_jumppointrange", "512", core::Cvar::Game | core::Cvar::Archive); g_jumppointrange->set_info("[float] jumppoint range"); g_devel = core::Cvar::get("g_devel", "0", core::Cvar::Archive); g_devel->set_info("[bool] enable or disable developer mode"); } Game::~Game() { g_impulsespeed = 0; // game functions are automaticly removed // remove engine core functions core::Func::remove("list_ship"); ShipModel::clear(); } bool Game::load_world() { std::string inifilename("world"); filesystem::IniFile worldini; worldini.open(inifilename); if (!worldini.is_open()) { con_error << "Could not open " << worldini.name() << std::endl; return false; } con_print << "^BLoading world..." << std::endl; core::Zone *zone = 0; std::string label; while (worldini.getline()) { if (worldini.got_section()) { if (worldini.got_section("world")) { continue; } else { worldini.unknown_section(); } } else if (worldini.got_key()) { if (worldini.in_section("world")) { if (worldini.got_key_string("zone", label)) { aux::to_label(label); zone = new core::Zone(label); core::Zone::add(zone); } else { worldini.unkown_key(); } } } } worldini.close(); if (!core::Zone::registry().size()) { con_error << "No zones found!" << std::endl; return false; } con_debug << " " << worldini.name() << " " << core::Zone::registry().size() << " zones" << std::endl; for (core::Zone::Registry::iterator it = core::Zone::registry().begin(); it != core::Zone::registry().end(); it++) { if (!load_zone((*it).second)) { return false; } } for (core::Zone::Registry::iterator it = core::Zone::registry().begin(); it != core::Zone::registry().end(); it++) { if (!validate_zone((*it).second)) { return false; } } return true; } bool Game::load_zone(core::Zone *zone) { using math::Vector3f; using math::Color; std::string inifilename("zones/"); inifilename.append(zone->label()); filesystem::IniFile zoneini; zoneini.open(inifilename); if (!zoneini.is_open()) { con_error << "Could not open " << zoneini.name() << std::endl; return false; } con_print << "^BLoading zone " << zone->label() << "..." << std::endl; size_t count = 0; Station *station = 0; Planet *planet = 0; Star *star = 0; NavPoint *navpoint = 0; JumpPoint *jumppoint = 0; RaceTrack *racetrack = 0; CheckPoint *checkpoint = 0; core::Entity *entity = 0; bool b; std::string strval; // set th default sky zone->set_sky("sky"); while (zoneini.getline()) { if (zoneini.got_section()) { if (zoneini.got_section("zone")) { continue; } else if (zoneini.got_section("star")) { star = new Star(); star->set_zone(zone); count ++; } else if (zoneini.got_section("navpoint")) { navpoint = new NavPoint(); navpoint->set_zone(zone); count ++; } else if (zoneini.got_section("jumpgate")) { jumppoint = new JumpGate(); jumppoint->set_zone(zone); count ++; } else if (zoneini.got_section("jumppoint")) { jumppoint = new JumpPoint(); jumppoint->set_zone(zone); count ++; } else if(zoneini.got_section("racetrack")) { racetrack = new RaceTrack(); racetrack->set_zone(zone); } else if(zoneini.got_section("checkpoint")) { checkpoint = new CheckPoint(racetrack); if (!racetrack) { con_warn << zoneini.name() << " checkpoint without racetrack at line " << zoneini.line() << std::endl; } } else if (zoneini.got_section("planet")) { planet = new Planet(); planet->set_zone(zone); count ++; } else if (zoneini.got_section("station")) { station = new Station(); station->set_zone(zone); count ++; } else if (zoneini.got_section("entity")) { entity = new core::Entity(); entity->set_flag(core::Entity::Static); entity->set_zone(zone); count ++; } else { zoneini.unknown_section(); } } else if (zoneini.got_key()) { if (zoneini.in_section("zone")) { if (zoneini.got_key_string("name", strval)) { aux::strip_quotes(strval); zone->set_name(strval); continue; } else if (zoneini.got_key_string("sky", strval)) { zone->set_sky(strval); continue; } else { zoneini.unkown_key(); } } else if (zoneini.in_section("star")) { if (core::Parser::got_entity_key(zoneini, star)) { continue; } else if (zoneini.got_key_string("texture", star->entity_texture)) { continue; } else { zoneini.unkown_key(); } } else if (zoneini.in_section("navpoint")) { if (core::Parser::got_entity_key(zoneini, navpoint)) { continue; } else { zoneini.unkown_key(); } } else if (zoneini.in_section("jumppoint") || zoneini.in_section("jumpgate")) { if (core::Parser::got_entity_key(zoneini, jumppoint)) { continue; } else if (zoneini.got_key_string("target", strval)) { jumppoint->set_targetlabel(strval); continue; } else { zoneini.unkown_key(); } } else if (zoneini.in_section("planet")) { if (core::Parser::got_entity_key(zoneini, planet)) { continue; } else if (zoneini.got_key_string("texture", planet->entity_texture)) { continue; } else if (zoneini.got_key_float("rotationspeed", planet->entity_rotationspeed)) { continue; } else if (zoneini.got_key_bool("dock", b)) { if (b) { planet->set_flag(core::Entity::Dockable); } else { planet->unset_flag(core::Entity::Dockable); } } else if (zoneini.got_key_bool("default", b)) { if (b) { zone->set_default_view(planet); } continue; } else { zoneini.unkown_key(); } } else if (zoneini.in_section("station")) { if (core::Parser::got_entity_key(zoneini, station)) { continue; } else if (zoneini.got_key_bool("default", b)) { if (b) { zone->set_default_view(station); } } else { zoneini.unkown_key(); } } else if (zoneini.in_section("racetrack")) { if (core::Parser::got_entity_key(zoneini, racetrack)) { continue; } else { zoneini.unkown_key(); } } else if (zoneini.in_section("checkpoint")) { if (core::Parser::got_entity_key(zoneini, checkpoint)) { continue; } else { zoneini.unkown_key(); } } else if (zoneini.in_section("entity")) { if (core::Parser::got_entity_key(zoneini, entity)) { continue; } else if (zoneini.got_key_bool("default", b)) { if (b) { zone->set_default_view(entity); } } else { zoneini.unkown_key(); } } } } zoneini.close(); con_debug << " " << zoneini.name() << " " << zone->content().size() << " entities" << std::endl; return true; } bool Game::validate_zone(core::Zone *zone) { con_print << "^BValidating zone " << zone->label() << "..." << std::endl; for (core::Zone::Content::iterator it = zone->content().begin(); it != zone->content().end(); it++) { core::Entity *entity = (*it); if (entity->entity_moduletypeid == jumppoint_enttype) { // validate jump points JumpPoint *jumppoint = static_cast(entity); jumppoint->validate(); } else if (entity->entity_moduletypeid == jumpgate_enttype) { // validate jump gate JumpGate *jumpgate = static_cast(entity); jumpgate->validate(); } else { if ((entity->flags() & core::Entity::Dockable) == core::Entity::Dockable) load_menus(entity, "zones/" + zone->label() + "/" + entity->label()); } } return true; } bool Game::load_menus(core::Entity *entity, const std::string &menufilename) { using core::MenuDescription; using core::ButtonDescription; if ((entity->moduletype() != planet_enttype) && (entity->moduletype() != station_enttype)) { return false; } filesystem::IniFile inifile; std::string strval; MenuDescription *menu_dealer = 0; ButtonDescription *button = 0; ShipDealer *shipdealer = 0; inifile.open(menufilename); if (inifile.is_open()) { while (inifile.getline()) { if (inifile.got_section()) { if (inifile.got_section("dealer")) { // dealer menu if (!menu_dealer) { menu_dealer = new MenuDescription(); menu_dealer->set_label("dealer"); menu_dealer->set_text("Ship dealer"); shipdealer = new ShipDealer(); if (entity->moduletype() == planet_enttype) { static_cast(entity)->set_shipdealer(shipdealer); } else if (entity->moduletype() == station_enttype) { static_cast(entity)->set_shipdealer(shipdealer); } } } else { inifile.unknown_section(); } } else if (inifile.got_key()) { if (inifile.in_section("dealer")) { if (inifile.got_key_string("ship", strval)) { aux::to_label(strval); ShipModel *model = shipdealer->add(strval); if (model) { button = new ButtonDescription(); button->set_text("buy " + model->name()); button->set_command("buy " + model->label()); button->set_modelname(model->modelname()); button->set_alignment(ButtonDescription::Left); menu_dealer->add_button(button); } } else { inifile.unkown_key(); } } } } } MenuDescription *menu_main = new MenuDescription(); menu_main->set_label("main"); menu_main->set_text("Launch area"); entity->add_menu(menu_main); button = new ButtonDescription(); button->set_text("Launch"); button->set_command("launch"); button->set_alignment(ButtonDescription::Center); menu_main->add_button(button); if (menu_dealer) { button = new ButtonDescription(); button->set_text("Return"); button->set_command("menu view main"); button->set_alignment(ButtonDescription::Center); menu_dealer->add_button(button); entity->add_menu(menu_dealer); button = new ButtonDescription(); button->set_text("Ship dealer"); button->set_command("menu view dealer"); button->set_alignment(ButtonDescription::Center); menu_main->add_button(button); } if (inifile.is_open()) { size_t n = entity->menus().size(); con_debug << " " << inifile.name() << " " << n << " " << aux::plural("menu", n) << std::endl; inifile.close(); } return true; } // read ship model specifications bool Game::load_ships() { using math::Vector3f; using math::Color; filesystem::IniFile shipsini; shipsini.open("ships"); if (!shipsini.is_open()) { con_error << "Could not open " << shipsini.name() << "!" << std::endl; return false; } ShipModel *shipmodel = 0; std::string label; long l; float f; while (shipsini.getline()) { if (shipsini.got_key()) { if (shipsini.section().compare("ship") == 0) { if (shipsini.got_key_string("label", label)) { aux::to_label(label); shipmodel->shipmodel_label.assign(label); ShipModel::add(shipmodel); continue; } else if (shipsini.got_key_string("name",shipmodel->shipmodel_name)) { continue; } else if (shipsini.got_key_string("model", shipmodel->shipmodel_modelname)) { continue; } else if (shipsini.got_key_long("price", l)) { shipmodel->set_price(l); continue; } else if (shipsini.got_key_float("cargo", f)) { shipmodel->set_maxcargo(f); continue; } else if (shipsini.got_key_bool("jumpdrive", shipmodel->shipmodel_jumpdrive)) { continue; } else if (shipsini.got_key_float("acceleration",f)) { shipmodel->set_acceleration(f); continue; } else if (shipsini.got_key_float("maxspeed", f)) { shipmodel->set_maxspeed(f); continue; } else if (shipsini.got_key_float("turnspeed", f)) { math::clamp(f, 0.0f, 90.0f); shipmodel->set_turnspeed(f); continue; } else { shipsini.unkown_key(); } } } else if (shipsini.got_section("ship")) { if (shipmodel && !ShipModel::find(shipmodel)) delete shipmodel; shipmodel = new ShipModel(); if (!Default::shipmodel) Default::shipmodel = shipmodel; } else if (shipsini.got_section()) { shipsini.unknown_section(); } } shipsini.close(); if (shipmodel && !ShipModel::find(shipmodel)) delete shipmodel; con_debug << " " << shipsini.name() << " " << ShipModel::registry.size() << " ship models" << std::endl; return true; } // load default player settings bool Game::load_player() { Default::clear(); filesystem::IniFile inifile; inifile.open("player"); if (!inifile.is_open()) { con_error << "Could not open " << inifile.name() << "!" << std::endl; return false; } long l; std::string str; while (inifile.getline()) { if (inifile.got_section()) { if (inifile.got_section("player")) { continue; } else { inifile.unknown_section(); } } else if (inifile.got_key()) { if (inifile.in_section("player")) { if (inifile.got_key_long("credits", l)) { Default::credits = l; } else if (inifile.got_key_string("zone", str)) { aux::to_label(str); Default::zone = core::Zone::find(str); } else if (inifile.got_key_string("ship", str)) { aux::to_label(str); Default::shipmodel = ShipModel::find(str); } } } } inifile.close(); if (!Default::zone) { con_error << "No default zone found!\n"; return false; } if (!Default::zone->default_view()) { con_error << "Zone '" << Default::zone->label() << "' has no default view!\n"; return false; } Default::view = Default::zone->default_view(); if (!Default::shipmodel) { con_error << "No default ship model found!\n"; return false; } return true; } void Game::frame(float seconds) { if (!running()) return; // TODO check Module::frame() is execute before are Entity::frame() // collision Collision::frame(seconds); } void Game::player_connect(core::Player *player) { std::string args; player->set_zone(Default::zone); player->set_view(0); // not docked func_spectate(player, args); } void Game::player_disconnect(core::Player *player) { player->remove_asset(player->control()); } } // namespace game