/* 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 #include "math/mathlib.h" #include "sys/sys.h" #include "auxiliary/functions.h" #include "core/gameserver.h" #include "core/parser.h" #include "core/range.h" #include "core/descriptions.h" #include "filesystem/filesystem.h" #include "filesystem/inifile.h" #include "base/game.h" #include "base/cargo.h" #include "base/cargopod.h" #include "base/faction.h" #include "base/navpoint.h" #include "base/jumppoint.h" #include "base/npc.h" #include "base/patrol.h" #include "base/planet.h" #include "base/savegame.h" #include "base/spacemine.h" #include "base/station.h" #include "base/racetrack.h" #include "base/ship.h" #include "base/star.h" #include "base/template.h" #include "base/weapon.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_jumppointrange = 0; core::Cvar *Game::g_devel = 0; core::Cvar *Game::g_damping = 0; core::Cvar *Game::g_deplete = 0; core::Cvar *Game::g_cargoloss = 0; core::Module *factory() { return new Game(); } // a player joins the game void Game::func_join(core::Player *player, std::string const &args) { if (player->control()) return; std::string message("^B"); message.append(player->name()); message.append("^B joins the game."); core::server()->broadcast(message); // load existing player data core::server()->module()->player_load(player); if (!player->control()) { 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->set_dock(dock); player->set_view(dock); } ship->reset(); player->send("^BYou received " + aux::article(Default::shipmodel->name())); player->sound("game/buy-ship"); player->reputation().clear(); Faction::apply_default(player->reputation()); // reset player timestamps player->set_time_wasted(0); player->set_time_joined(); } player->set_dirty(); } // a player joins the spectators void Game::func_spectate(core::Player *player, std::string const &args) { if (!player->control()) return; core::server()->module()->player_save(player); 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 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()); if (ship->has_autopilot_flag(Ship::AutoPilotEnabled)) { ship->set_autopilot_target(0); ship->unset_autopilot_flag(Ship::AutoPilotEnabled); // TODO replace with "Canceled" voice player->send("Autopilot canceled"); } ship->func_impulse(); } // autopilot free flight void Game::func_freeflight(core::Player *player, std::string const &args) { if (!player->control()) return; if (player->control()->moduletype() != ship_enttype) return; Ship * ship = static_cast(player->control()); if (ship->has_autopilot_flag(Ship::AutoPilotEnabled)) { ship->set_autopilot_target(0); ship->unset_autopilot_flag(Ship::AutoPilotEnabled); // TODO replace with "Canceled" voice player->send("Autopilot canceled"); } } // autopilot goto void Game::func_target_goto(core::Player *player, core::Entity *entity) { if (!player->control()) return; if (player->control() == entity) return; if (player->control()->zone() != entity->zone()) return; if (player->control()->moduletype() != ship_enttype) return; Ship * ship = static_cast(player->control()); ship->set_autopilot_target(entity); ship->set_autopilot_flag(Ship::AutoPilotEnabled); ship->unset_autopilot_flag(Ship::AutoPilotDock); ship->unset_autopilot_flag(Ship::AutoPilotFormation); // TODO replace with "goto" voice player->send("Autopilot set to " + entity->name()); } // a player sends a docking request void Game::func_target_dock(core::Player *player, core::Entity *entity) { if (!player->control()) { return; } if (player->control() == entity) { return; } if (!entity->has_flag(core::Entity::Dockable)) { return; } if (player->control()->zone() != entity->zone()) { return; } if (player->control()->moduletype() != ship_enttype) { return; } if (player->reputation(entity->faction()) <= core::range::reputation_hostile) { // FIXME replace with "Denied" voice player->send("Dock denied"); return; } Ship * ship = static_cast(player->control()); ship->set_autopilot_target(entity); ship->set_autopilot_flag(Ship::AutoPilotEnabled); ship->set_autopilot_flag(Ship::AutoPilotDock); ship->unset_autopilot_flag(Ship::AutoPilotFormation); // TODO replace with "dock" voice player->send("Autopilot set to dock at " + entity->name()); } void Game::func_target_formation(core::Player *player, core::Entity *entity) { if (!player->control()) { return; } if (player->control() == entity) return; if (entity->type() != core::Entity::Controlable) { return; } if (player->control()->zone() != entity->zone()) { return; } if (player->control()->moduletype() != ship_enttype) { return; } Ship * ship = static_cast(player->control()); ship->set_autopilot_target(entity); ship->set_autopilot_flag(Ship::AutoPilotEnabled); ship->unset_autopilot_flag(Ship::AutoPilotDock); ship->set_autopilot_flag(Ship::AutoPilotFormation); // TODO replace with "formation" voice player->send("Autopilot set to formation with " + entity->name()); } // a player sends a standard hail void Game::func_target_hail(core::Player *player, core::Entity *entity) { // TODO spam protection if (!entity) return; if (!player->control()) return; if (player->control()->zone() != entity->zone()) return; if (player->mute()) { player->send("^WYou have been muted"); return; } core::Player *target = (entity->type() == core::Entity::Controlable ? static_cast(entity)->owner() : 0 ); if (!target) return; if (math::distance(player->control()->location(), entity->location()) > core::range::fxdistance) { player->send("^B" + target->name() + "^B is out of range"); } player->send("^BYou hail " + target->name()); player->sound("com/hail"); target->send("^B" + player->name() + "^B hails you"); target->sound("com/hail"); } // a player sends a trade request void Game::func_target_trade(core::Player *player, core::Entity *entity) { if (!entity) return; if (!player->control()) return; core::Player *target = (entity->type() == core::Entity::Controlable ? static_cast(entity)->owner() : 0 ); if ((!target) || (target == player)) return; if (entity != target->control()) return; if (target->control()->state() != core::Entity::Normal) return; if (player->control()->state() != core::Entity::Normal) return; player->send("^WTrade requests are not implemented at this time"); } // cheats void Game::func_give(core::Player *player, const std::string &args) { if (!Game::g_devel->value()) { player->send("Cheats disabled"); return; } if (!player->control()) { player->send("^WYou need to join the game first!"); return; } std::istringstream is(args); std::string str; is >> str; aux::to_label(str); if (str.compare("ship") == 0) { std::string labelstr; ShipModel *shipmodel = 0; if (!(is >> labelstr)) { player->send("Usage: give ship [string]"); } else { aux::to_label(labelstr); shipmodel = ShipModel::find(labelstr); } if (!shipmodel) { // enable rcon buffering sys::ConsoleInterface::instance()->set_rcon(true); ShipModel::list(); // disable rcon buffering sys::ConsoleInterface::instance()->set_rcon(false); while (sys::ConsoleInterface::instance()->rconbuf().size()) { player->send((*sys::ConsoleInterface::instance()->rconbuf().begin())); sys::ConsoleInterface::instance()->rconbuf().pop_front(); } player->send("Unknown ship type '" + labelstr + "'"); return; } // check if there's enough space available to transfer inventory if (shipmodel->maxcargo() < player->control()->inventory()->capacity_used()) { player->send("^WNot enough cargo space to transfer inventory!"); return; } assert(player->control()->moduletype() == ship_enttype); Ship * oldship = static_cast(player->control()); Ship * ship = new Ship(player, shipmodel); core::Entity *view = player->view(); if (view && (view == player->control())) { view = ship; } // transfer inventory for (core::Inventory::Items::iterator it = player->control()->inventory()->items().begin(); it != player->control()->inventory()->items().end(); it++) { core::Item *item = new core::Item(*(*it)); item->unset_flag(core::Item::Mounted); ship->inventory()->add(item); } ship->inventory()->set_dirty(); // FIME move this into a method in the Ship class ship->set_zone(player->control()->zone()); ship->get_location().assign(player->control()->location()); ship->set_dock(oldship->dock()); ship->set_spawn(oldship->spawn()); ship->get_axis().assign(player->control()->axis()); ship->set_thrust(player->control()->thrust()); ship->set_state(player->control()->state()); // remove the old ship from the physics simulation oldship->die(); oldship->reset(); // add the new ship to the physics simulation ship->reset(); //target_thrust is protected //ship->target_thrust = player->control()->target_thrust()); player->remove_asset(player->control()); player->set_control(ship); player->set_view(view); player->sound("game/buy-ship"); } else if (str.compare("credits") == 0) { long credits; if (!(is >> credits)) { player->send("Usage: give credits [int]"); return; } if (player->credits() + credits > 0 ) { player->set_credits(player->credits() + credits); } else { player->set_credits(0); } player->set_dirty(); player->sound("game/buy"); } else if (str.compare("cargo") == 0) { std::string labelstr; Cargo *cargo = 0; if (!(is >> labelstr)) { player->send("Usage: give cargo [string] [int]"); } else { aux::to_label(labelstr); cargo = Cargo::find(labelstr); } if (!cargo) { // enable rcon buffering sys::ConsoleInterface::instance()->set_rcon(true); Cargo::list(); // disable rcon buffering sys::ConsoleInterface::instance()->set_rcon(false); while (sys::ConsoleInterface::instance()->rconbuf().size()) { player->send((*sys::ConsoleInterface::instance()->rconbuf().begin())); sys::ConsoleInterface::instance()->rconbuf().pop_front(); } player->send("Unknown cargo type '" + labelstr + "'"); return; } else { int amount = 0; if (!(is >> amount)) { amount = 1; } else { if (!amount) return; } int max = 0; if (cargo->volume()) { max = (int)floorf(player->control()->inventory()->capacity_available() / cargo->volume()); } else { max = amount; if (max < 1) { max = 1; } } if (amount < 0) { amount = max; } if ((amount == 0) || (amount > max)) { player->send("^WNot enough cargo space available!"); return; } core::Item *item = player->control()->inventory()->find(cargo); if (!item) { item = new core::Item(cargo); player->control()->inventory()->add(item); } else { assert(item->info() == cargo); } item->inc_amount(amount); player->control()->inventory()->set_dirty(); player->control()->owner()->sound("game/buy"); } } else if (str.compare("weapon") == 0) { std::string labelstr; Weapon *weapon = 0; if (!(is >> labelstr)) { player->send("Usage: give weapon [string] [int]"); } else { aux::to_label(labelstr); weapon = Weapon::find(labelstr); } if (!weapon) { // enable rcon buffering sys::ConsoleInterface::instance()->set_rcon(true); Weapon::list(); // disable rcon buffering sys::ConsoleInterface::instance()->set_rcon(false); while (sys::ConsoleInterface::instance()->rconbuf().size()) { player->send((*sys::ConsoleInterface::instance()->rconbuf().begin())); sys::ConsoleInterface::instance()->rconbuf().pop_front(); } player->send("^WUnknown weapon type '" + labelstr + "'"); return; } else { int amount = 0; if (!(is >> amount)) { amount = 1; } else { if (!amount) return; } int max = 0; if (weapon->volume()) { max = (int)floorf(player->control()->inventory()->capacity_available() / weapon->volume()); } else { max = amount; if (max < 1) { max = 1; } } if (amount < 0) { switch (weapon->subtype()) { case Weapon::Mine: case Weapon::Ammo: amount = max; break; default: amount = 1; break; } } if ((amount == 0) || (amount > max)) { player->send("^WNot enough cargo space available!"); return; } core::Item *item = 0; switch (weapon->subtype()) { case Weapon::Ammo: case Weapon::Mine: item = player->control()->inventory()->find(weapon); if (!item) { item = new core::Item(weapon); item->set_flag(core::Item::Unrestricted); player->control()->inventory()->add(item); } else { assert(item->info() == weapon); } item->inc_amount(amount); break; case Weapon::Cannon: case Weapon::Turret: for (int n = 0; n < amount; n++) { item = new core::Item(weapon); item->set_flag(core::Item::Unique); item->set_flag(core::Item::Mountable); item->set_flag(core::Item::Unrestricted); item->set_amount(1); player->control()->inventory()->add(item); } break; } player->control()->inventory()->set_dirty(); player->sound("game/buy"); } } else if (str.compare("reputation") == 0) { std::string labelstr; Faction *faction = 0; if (!(is >> labelstr)) { player->send("Usage: give reputation [faction label] [value]"); return; } else { aux::to_label(labelstr); faction = Faction::find(labelstr); } if (labelstr.compare("default") == 0) { player->reputation().clear(); Faction::apply_default(player->reputation()); player->reputation().set_dirty(); player->send("Reputation reset to default"); return; } else if (!faction) { // enable rcon buffering sys::ConsoleInterface::instance()->set_rcon(true); Faction::list(); // disable rcon buffering sys::ConsoleInterface::instance()->set_rcon(false); while (sys::ConsoleInterface::instance()->rconbuf().size()) { player->send((*sys::ConsoleInterface::instance()->rconbuf().begin())); sys::ConsoleInterface::instance()->rconbuf().pop_front(); } player->send("^WUnknown faction '" + labelstr + "'"); return; } float reputation; if (!(is >> reputation)) { player->send("Usage: give reputation [faction] [value]"); return; } math::clamp(reputation, -100.0f, 100.0f); player->reputation().set_reputation(faction, reputation); player->reputation().set_dirty(); std::ostringstream msgstr; msgstr << "Reputation for faction '" << faction->label() << "' set to " << reputation; player->send(msgstr.str().c_str()); } else { player->send("Usage: give cargo [cargo label] [amount]"); player->send(" give credits [amount]"); player->send(" give reputation [faction label] [value]"); player->send(" give ship [ship label]"); player->send(" give weapon [weapon label] [amount]"); return; } } void Game::func_wingmen(core::Player *player, const std::string &args) { if (!Game::g_devel->value()) { player->send("Cheats disabled"); return; } if (!player->control()) { player->send("^WYou need to join the game first!"); return; } assert(player->control()->moduletype() == ship_enttype); Ship * ship = static_cast(player->control()); std::istringstream is(args); std::string str; is >> str; aux::to_label(str); if (str.compare("add") == 0) { Faction *faction = 0; int amount = 1; if (is >> str) { aux::to_label(str); faction = Faction::find(str); if (!faction) { player->send("^WUnknown faction '" + str + "'"); return; } if ( !(is >> amount)) { amount = 1; } } for (int count = 0; count < amount; ++count) { NPC *npc = NPC::add_wingman(ship); if (npc) { if (faction) { faction->apply(npc); player->send(faction->name() + " wingman standing by!"); } else { player->send("Wingman standing by!"); } } } } else if (str.compare("combat") == 0) { if (ship->has_autopilot_flag(Ship::AutoPilotCombat)) { ship->unset_autopilot_flag(Ship::AutoPilotCombat); player->send("Wingmen going into formation!"); } else { ship->set_autopilot_flag(Ship::AutoPilotCombat); player->send("Wingmen entering combat!"); } } else { player->send("Usage: wingmen add [faction label] [amount]"); player->send(" wingmen combat"); } } void Game::func_specs(core::Player *player, const std::string &args) { if (!Game::g_devel->value()) { player->send("Cheats disabled"); return; } if (!player->control()) { player->send("^WYou need to join the game first!"); return; } if (player->control()->moduletype() != ship_enttype) return; Ship * ship = static_cast(player->control()); std::istringstream is(args); std::string str; if (!(is >> str)) { // enable rcon buffering sys::ConsoleInterface::instance()->set_rcon(true); con_print << "^B" << ship->name() << std::endl; con_print << "Specifications:" << std::endl; con_print << " ^Nmass = ^B" << ship->mass() << std::endl; con_print << " ^Nradius = ^B" << ship->radius() << std::endl; con_print << " ^Ncargo = ^B" << ship->inventory()->capacity() << std::endl; con_print << " ^Narmor = ^B" << ship->maxarmor() << std::endl; con_print << "Engines:" << std::endl; con_print << " ^Ndamping = ^B" << ship->body()->getLinearDamping() << " " << ship->body()->getAngularDamping() << std::endl; con_print << " ^Nthrust = ^B" << ship->thrust_force() << std::endl; con_print << " ^Nimpulse = ^B" << ship->impulse_force() << std::endl; con_print << " ^Nstrafe = ^B" << ship->strafe_force() << std::endl; con_print << " ^Nturn = ^B" << ship->turn_force() << std::endl; con_print << " ^Nroll = ^B" << ship->roll_force() << std::endl; // disable rcon buffering sys::ConsoleInterface::instance()->set_rcon(false); while (sys::ConsoleInterface::instance()->rconbuf().size()) { player->send((*sys::ConsoleInterface::instance()->rconbuf().begin())); sys::ConsoleInterface::instance()->rconbuf().pop_front(); } } else { std::stringstream msgstr; float value; // specs label aux::to_label(str); if (str.compare("thrust") == 0) { if (is >> value) { ship->set_thrust_force(value); msgstr << "Ship thrust force set to " << value; } else { msgstr << "Ship thrust at " << ship->thrust_force(); } } else if (str.compare("impulse") == 0) { if (is >> value) { ship->set_impulse_force(value); msgstr << "Ship impulse force set to " << value; } else { msgstr << "Ship impulse force at " << ship->impulse_force(); } } else if (str.compare("strafe") == 0) { if (is >> value) { ship->set_strafe_force(value) ; msgstr << "Ship strafe force set to " << value; } else { msgstr << "Ship strafe force at " << ship->strafe_force(); } } else if (str.compare("turn") == 0) { if (is >> value) { ship->set_turn_force(value); msgstr << "Ship turn force set to " << value; } else { msgstr << "Ship turn force at " << ship->turn_force(); } } else if (str.compare("roll") == 0) { if (is >> value) { ship->set_roll_force(value); msgstr << "Ship roll force set to " << value; } else { msgstr << "Ship roll force at " << ship->roll_force(); } } else if (str.compare("damping") == 0) { if (is >> value) { float a; if (!(is >> a)) { a = value; } ship->body()->setDamping(value, a); msgstr << "Ship damping set to " << value << " " << a; } else { msgstr << "Ship damping at " << ship->body()->getLinearDamping() << " " << ship->body()->getAngularDamping(); } } else if (str.compare("mass") == 0) { msgstr << "Ship mass at " << ship->mass(); } else { msgstr << "^WUnknown ship engine specification '" << str << "'"; } player->send(msgstr.str()); } } // sell items on a base void Game::func_sell(core::Player *player, const std::string &args) { // must be joined to buy items if (!player->control()) { player->send("^WYou need to join the game first!"); return; } Ship *seller = static_cast(player->control()); // must be docked to buy items if ((seller->state() != core::Entity::Docked) || (!seller->dock())) { player->send("^WYou need to be docked!"); return; } core::Entity *buyer = seller->dock(); // can only buy at planets and stations if ((buyer->moduletype() != station_enttype) && (buyer->moduletype() != planet_enttype)) { player->send("^WCan not sell here"); return; } if (!seller->inventory() || !buyer->inventory()) { player->send("^WCan not sell here"); return; } core::Item *seller_item = 0; core::Item *buyer_item = 0; unsigned int id = 0; int amount = 0; std::istringstream is(args); if (is >> id >> amount) { seller_item = seller->inventory()->find(id); if (!seller_item) { std::stringstream msgstr; msgstr << "^WItem " << id << " not in inventory"; player->send(msgstr.str()); return; } if (amount == 0) { player->send("^WNo amount specified"); return; } if (seller_item->info()->type() == Cargo::infotype()) { // cargo has to be in demand buyer_item = buyer->inventory()->find(seller_item->info()); if (!buyer_item) { player->send("^W" + buyer->name() + " ^Bdoes not buy " + seller_item->info()->name()); return; } } else if (seller_item->info()->type() == Weapon::infotype()) { // weapons can be sold anywhere if (!seller_item->unique()) { buyer_item = buyer->inventory()->find(seller_item->info()); } if (!buyer_item) { buyer_item = new core::Item(seller_item->info()); buyer_item->set_flags(seller_item->flags()); buyer->inventory()->add(buyer_item); } } else { player->send("^W" + buyer->name() + " ^Bdoes not buy " + seller_item->info()->name()); return; } if (amount < 0) { amount = seller_item->amount(); } else if (amount > seller_item->amount()) { amount = seller_item->amount(); } int price = buyer_item->price(); seller_item->dec_amount(amount); seller->owner()->set_credits(seller->owner()->credits() + price * amount); seller->owner()->set_dirty(); seller->inventory()->set_dirty(); if (buyer_item->amount() >= 0) { buyer_item->inc_amount(amount); buyer->inventory()->set_dirty(); } // unmount if the item was mounted core::Slot *slot = (seller->slots() ? seller->slots()->find(seller_item) : 0); if (slot) { slot->set_item(0); slot->unset_flag(core::Slot::Active); slot->unset_flag(core::Slot::Mounted); seller_item->unset_flag(core::Item::Mounted); } // delete the item if the amount is 0 if (seller_item->amount() == 0) { seller->inventory()->erase(seller_item->id()); seller->inventory()->set_dirty(); seller_item = 0; } // send a cargo sold message std::stringstream msgstr; if (buyer_item->unique()) { msgstr << "^BSold " << buyer_item->info()->name(); } else { msgstr << "^BSold " << amount << " " << aux::plural("unit", amount) << " of " << buyer_item->info()->name() << " for " << price * amount << " credits"; } player->send(msgstr.str()); player->sound("game/buy"); } else { player->send("Usage: sell [id] [amount] sell item from inventory"); } } // buy items on a base void Game::func_buy(core::Player *player, const std::string &args) { // must be joined to buy items if (!player->control()) { player->send("^WYou need to join the game first!"); return; } Ship *ship = static_cast(player->control()); // must be docked to buy items if ((ship->state() != core::Entity::Docked) || (!ship->dock())) { player->send("^WYou need to be docked!"); return; } core::Entity *seller = ship->dock(); // can only buy at planets and stations if ((seller->moduletype() != station_enttype) && (seller->moduletype() != planet_enttype)) { player->send("^WCan not buy here"); return; } if (!ship->inventory() || !seller->inventory()) { player->send("^WCan not buy here"); return; } core::Item *item = 0; unsigned int id = 0; int amount = 0; std::istringstream is_int(args); if (is_int >> id >> amount) { // item id based buy item = seller->inventory()->find(id); if (!item) { std::stringstream msgstr; msgstr << "^WItem " << id << " not in seller inventory"; player->send(msgstr.str()); return; } if (item->info()->type() == ShipModel::infotype()) { ShipModel *shipmodel = ShipModel::find(item->info()->label()); if (shipmodel) { shipmodel->buy(player->control(), player->view()); } return; } else if ((item->info()->type() == Cargo::infotype()) || (item->info()->type() == Weapon::infotype())) { int price = item->price(); long cash = player->credits(); if (item->unique()) { amount = 1; } // check if the player has enough cash if (price > 0) { // negative amount means 'as much as possible' if (amount < 0) { amount = cash / price; } // maximum amount the player can afford if (cash < (amount * price)) { amount = cash / price; } if (amount < 1) { player->send("^WCan not afford transaction!"); return; } } // check cargo size - ignore zero volume cargo if (item->info()->volume() > 0 ) { // maximum cargo size if (amount * item->info()->volume() > ship->inventory()->capacity_available()) { amount = (int)floorf(ship->inventory()->capacity_available() / item->info()->volume()); } if (amount < 1) { player->send("^WNot enough cargo space available!"); return; } } if (amount <= 0) { // unlimited amount of zero-cost cargo player->send("^WUnlimited amounts of free items are not supported!"); return; } // if amount is set to -1. the base has an unlimited supply if (item->amount() == 0) { player->send("^W" + item->info()->name() + " not available!"); return; } else if (item->amount() > 0) { if (amount > item->amount()) { amount = item->amount(); } } // buyer is the player core::Item *buyer_item = 0; if (!item->unique()) { buyer_item = ship->inventory()->find(item->info()); } if (!buyer_item) { buyer_item = new core::Item(item->info()); buyer_item->set_flags(item->flags()); ship->inventory()->add(buyer_item); } // update buyer inventory buyer_item->inc_amount(amount); ship->inventory()->set_dirty(); player->set_credits(player->credits() - price * amount); player->set_dirty(); // update seller inventory if (item->amount() >= 0) { item->dec_amount(amount); seller->inventory()->set_dirty(); } if (item->info()->type() == Weapon::infotype()) { if (item->amount() == 0) { seller->inventory()->erase(item->id()); seller->inventory()->set_dirty(); } } // send a message to the player std::stringstream msgstr; if (buyer_item->unique()) { msgstr << "^BPurchased a " << buyer_item->info()->name(); } else { msgstr << "^BPurchased " << amount << " " << aux::plural("unit", amount) << " of " << buyer_item->info()->name() << " for " << price * amount << " credits"; } player->send(msgstr.str()); player->sound("game/buy"); } else { player->send("^WCan not buy " + item->info()->name()); } } else { std::istringstream is(args); std::string typestr; std::string labelstr; if (!(is >> typestr >> labelstr)) { player->send("Usage: buy ship [ship label] buy a ship from a base"); player->send("Usage: buy [id] [amount] buy from base inventory"); return; } else { aux::to_label(typestr); aux::to_label(labelstr); } if (typestr.compare("ship") == 0) { ShipModel *shipmodel = ShipModel::find(labelstr); if (shipmodel) { shipmodel->buy(player->control(), player->view()); } else { player->send("Unknown ship type '" + labelstr + "'"); } } else { player->send("Unknown item type '" + typestr + "'"); } } } // drop item requests void Game::func_drop(core::Player *player, const std::string &args) { // must be joined to buy items if (!player->control()) { player->send("^WYou need to join the game first!"); return; } Ship *ship = static_cast(player->control()); // cannot be docked to drop items if (ship->state() == core::Entity::Docked) { player->send("^WCan't drop items while docked!"); return; } // cannot drop items while jumping if ((ship->state() == core::Entity::Jump) || (ship->state() == core::Entity::JumpInitiate)) { player->send("^WCan not drop items while hyperspace jump drive is active"); return; } std::istringstream is(args); std::string labelstr; if (!(is >> labelstr)) { player->send("Usage: drop [label] drop an item"); return; } else { aux::to_label(labelstr); } if (labelstr.compare("mine") == 0) { // drop a mine // cannot drop items while jumping if ((ship->state() == core::Entity::Impulse) || (ship->state() == core::Entity::ImpulseInitiate)) { player->send("^WCan not drop mines on impulse speed"); return; } // find a mine in inventory const Weapon *weapon = 0; core::Item *item = 0; for (core::Inventory::Items::iterator it = ship->inventory()->items().begin(); (!item) && (it != ship->inventory()->items().end()); it++) { if ((*it)->info()->type() == Weapon::infotype()) { weapon = static_cast((*it)->info()); if (weapon->subtype() == Weapon::Mine) { item = (*it); } } } if (!item) { player->send("^WNo mines in inventory!"); return; } // create a spacemine entity using the Weapon instance as constructiong info SpaceMine *spacemine = new SpaceMine(weapon); spacemine->set_color(ship->color()); spacemine->set_owner_id(player->id()); spacemine->set_color_second(ship->color_second()); spacemine->set_location(ship->location() - ship->axis().forward() * (ship->radius() + spacemine->radius())); spacemine->set_axis(ship->axis()); spacemine->set_zone(ship->zone()); player->send(weapon->name() + " ejected"); player->sound("game/eject"); spacemine->reset(); // remove mine from inventory item->dec_amount(1); if (item->amount() == 0) { ship->inventory()->erase(item->id()); } ship->inventory()->set_dirty(); } } // eject item request void Game::func_eject(core::Player *player, const std::string &args) { if (!player->control()) { player->send("^WYou need to join the game first!"); return; } std::istringstream is(args); unsigned int id; if (!(is >> id)) { player->send("Usage: eject [id] [int] eject inventory item, amount"); return; } int amount = 0; if (!(is >> amount)) amount = -1; if (!amount) { return; } if (player->control()->moduletype() != ship_enttype) { return; } Ship * ship = static_cast(player->control()); // cannot eject while jumping if ((ship->state() == core::Entity::Jump) || (ship->state() == core::Entity::JumpInitiate)) { if (ship->owner()) { ship->owner()->send("^WCan not eject while hyperspace jump drive is active"); } return; } // find the item in the inventory core::Item *item = ship->inventory()->find(id); if (!item) { if (ship->owner()) { std::stringstream msgstr; msgstr << "^WItem " << id << " not in inventory"; ship->owner()->send(msgstr.str()); } return; } if (!item->amount()) { if (ship->owner()) { ship->owner()->send("^WYou do not own any " + item->info()->name()); } return; } ship->eject(item, amount, true); } // mount weapons into slots void Game::func_mount(core::Player *player, const std::string &args) { if (!player->control()) { return; } Ship *ship = static_cast(player->control()); if (!ship->slots()) { return; } std::istringstream is(args); unsigned int id = 0; if (!(is >> id)) { ship->owner()->send("Usage: mount [id] mount weapon with id into the first available slot"); return; } // find item to be mounted core::Item *item = ship->inventory()->find(id); if (!item) { if (ship->owner()) { std::stringstream msgstr; msgstr << "^WItem " << id << " not in inventory"; ship->owner()->send(msgstr.str()); } return; } // verify item is mountable if (item->info()->type() != Weapon::infotype()) { if (ship->owner()) { std::stringstream msgstr; msgstr << "^W" << item->info()->name() << " can not be mounted"; ship->owner()->send(msgstr.str()); } return; } const Weapon *weapon = static_cast(item->info()); if (!item->unique() || ((weapon->subtype() != Weapon::Cannon) && (weapon->subtype() != Weapon::Turret)) ) { if (ship->owner()) { std::stringstream msgstr; msgstr << "^W" << weapon->name() << " can not be mounted"; ship->owner()->send(msgstr.str()); } return; } if (item->has_flag(core::Item::Mounted)) { // unmount core::Slot *slot = ship->slots()->find(item); if (slot) { slot->set_item(0); slot->unset_flag(core::Slot::Active); slot->unset_flag(core::Slot::Mounted); item->unset_flag(core::Item::Mounted); ship->inventory()->set_dirty(); if (ship->owner()) { std::stringstream msgstr; msgstr << "^BUnmounted " << weapon->name(); ship->owner()->send(msgstr.str()); player->sound("game/unmount"); } } } else { // mount core::Slot *slot = 0; for(core::Slots::iterator it = ship->slots()->begin(); (!slot) && (it != ship->slots()->end()); ++it) { if (((*it)->type() == weapon->slot_type()) && !(*it)->has_flag(core::Slot::Mounted)) { slot = (*it); } } if (!slot) { if (ship->owner()) { std::stringstream msgstr; msgstr << "^WNo slot available to mount " << weapon->name(); ship->owner()->send(msgstr.str()); } return; } else { slot->set_item(item); slot->set_flag(core::Slot::Active); slot->set_flag(core::Slot::Mounted); item->set_flag(core::Item::Mounted); ship->inventory()->set_dirty(); if (ship->owner()) { std::stringstream msgstr; msgstr << "^BMounted " << weapon->name(); ship->owner()->send(msgstr.str()); player->sound("game/mount"); } } } } // unmount weapons from slots void Game::func_unmount(core::Player *player, const std::string &args) { } // beam in nearby cargo pods void Game::func_beam(core::Player *player, const std::string &args) { // 5km beam distance const float beam_range_squared = 50.0f * 50.0f; if (!player->control()) return; if (player->control()->state() != core::Entity::Normal) return; core::Zone *zone = player->control()->zone(); core::Inventory *inventory = player->control()->inventory(); // entity iterator for (core::Zone::Content::iterator eit = zone->content().begin(); eit != zone->content().end(); eit++) { // if the entity is a cargo pod and within beaming range if (((*eit)->moduletype() == cargopod_enttype) && (math::distancesquared(player->control()->location(), (*eit)->location()) <= beam_range_squared)) { core::Inventory *loot = (*eit)->inventory(); if (!inventory || !loot) continue; // item iterator int loot_left = 0; for (core::Inventory::Items::iterator iit = loot->items().begin(); iit != loot->items().end(); iit++) { core::Item *item = (*iit); int negotiated_amount = item->amount(); assert(item->info()); if (inventory->capacity_available() < negotiated_amount * item->info()->volume()) { negotiated_amount = (int) floorf( inventory->capacity_available() / item->info()->volume()); } if (negotiated_amount > 0) { core::Item *iteminv = 0; if (!item->has_flag(core::Item::Unique)) { iteminv = inventory->find(item->info()); } if (!iteminv) { iteminv = new core::Item(item->info()); iteminv->set_flags(item->flags()); iteminv->unset_flag(core::Item::Mounted); inventory->add(iteminv); } item->dec_amount(negotiated_amount); iteminv->inc_amount(negotiated_amount); // this will recalculate inventory capacity inventory->set_dirty(); loot->set_dirty(); std::stringstream msgstr; msgstr << "^BBeamed in " << negotiated_amount << " " << aux::plural("unit", negotiated_amount) << " of " << item->info()->name(); player->send(msgstr.str()); // TODO sound must be emitted at cargo pod location player->sound("game/beam"); } loot_left += item->amount(); } // if there's no loot left, the cargo pod will be destroyed if (!loot_left) { (*eit)->die(); } } } } // launch request void Game::func_launch(core::Player *player, std::string const &args) { if (!player->control()) { player->send("^WYou need to join the game first!"); return; } if (!player->view()) return; if (player->control()->state() != core::Entity::Docked) return; assert(player->view()->zone() == player->control()->zone()); assert(player->control()->moduletype() == ship_enttype); Ship *ship = static_cast(player->control()); if (ship->dock()->moduletype() == ship_enttype) { switch(static_cast(ship->dock())->state()) { case core::Entity::Normal: case core::Entity::Docked: break; case core::Entity::NoPower: player->send("^BCan not launch while carrier has no power!"); return; break; case core::Entity::ImpulseInitiate: case core::Entity::Impulse: player->send("^BCan not launch while carrier is using impulse engines!"); return; break; case core::Entity::JumpInitiate: case core::Entity::Jump: player->send("^BCan not launch while carrier is jumping through hyperspace!"); return; break; default: player->send("^BCan not launch from carrier!"); return; break; } } // force save, at this point, the ship is still docked core::server()->module()->player_save(player); player->send("^BLaunching from " + ship->dock()->name()); ship->launch(); player->set_view(0); } // respawn void Game::func_respawn(core::Player *player, std::string const &args) { Ship *ship = static_cast(player->control()); if (!ship) { func_join(player, args); return; } if (ship->state() != core::Entity::Destroyed) { return; } // restore armor ship->set_armor(ship->maxarmor()); core::Entity *spawn = ship->spawn(); if (!spawn) { ship->set_zone(Default::zone); spawn = ship->zone()->default_view(); } if (spawn && spawn->has_flag(core::Entity::Dockable)) { ship->set_dock(spawn); player->set_view(spawn); player->send("^BRespawning at " + spawn->name()); } else { ship->set_zone(Default::zone); ship->get_location().clear(); ship->get_axis().clear(); ship->set_state(core::Entity::Normal); player->set_view(0); player->send("^BRespawning"); } } // instantaniously goto a specified entity within the zone void Game::func_goto(core::Player *player, const std::string &args) { if (!g_devel->value()) { player->send("Cheats disabled"); return; } if (!player->control()) { player->send("^WYou need to join the game first!"); return; } if (!args.size()) { return; } core::Entity *dock = player->control()->zone()->search_entity(args); if (dock) { // if target is docked,goto the dock instead if (dock->moduletype() == ship_enttype) { Ship *othership = static_cast(dock); if ((othership->state() == core::Entity::Docked) && (othership->dock())) { player->send(othership->name() + " docked at " + othership->dock()->name()); dock = othership->dock(); } } Ship *ship = static_cast(player->control()); if (dock->type() == core::Entity::Globe) { // target is a planet: keep a safe distance ship->get_location().assign(dock->location() + (dock->axis().forward() * (PLANET_SAFE_DISTANCE + ship->radius() + dock->radius()))); ship->get_axis().assign(dock->axis()); ship->get_axis().change_direction(180.0f); } else if (dock->type() == core::Entity::Controlable) { // target is a controlable: appear above it ship->get_location().assign(dock->location() + (dock->axis().up() * (0.25f + ship->radius() + dock->radius()))); ship->get_axis().assign(dock->axis()); } else { // targe is something else, appear in front of it ship->get_location().assign(dock->location() + (dock->axis().forward() * (0.25f + ship->radius() + dock->radius()))); ship->get_axis().assign(dock->axis()); ship->get_axis().change_direction(180.0f); } ship->set_state(core::Entity::Normal); ship->nudge(); ship->reset(); player->set_view(0); player->send("Going to " + dock->name()); } else { player->send("Entity '" + args + "' not found"); } } // self-destruct void Game::func_explode(core::Player *player, const std::string &args) { if (!g_devel->value()) { player->send("Cheats disabled"); return; } if (!player->control()) { player->send("^WYou need to join the game first!"); return; } assert(player->control()->moduletype() == ship_enttype); Ship *ship = static_cast(player->control()); ship->explode(); } /* -- class Game -------------------------------------------------- */ Game::Game() : core::Module("Project::OSiRiON", true) { // clear defaults Default::clear(); // read factions.ini if (!Faction::init()) { abort(); return; } // read templates.ini if (!Template::init()) { abort(); return; } // read cargo.ini if (!Cargo::init()) { abort(); return; } // read weapons.ini if (!Weapon::init()) { abort(); return; } // read ships.ini if (!ShipModel::init()) { abort(); return; } // read world.ini and the zones it refers to if (!load_world()) { abort(); return; } // read game.ini if (!load_settings()) { abort(); return; } // add game functions core::Func *func = 0; func = core::Func::add("join", Game::func_join); func->set_info("join the game"); func = core::Func::add("spectate", Game::func_spectate); func->set_info("leave the game and spectate"); func = core::Func::add("buy", Game::func_buy); func->set_info("[string] [string] [int] buy an item: specify type, label and amount"); func = core::Func::add("sell", Game::func_sell); func->set_info("[string] [string] [int] sell an item: specify type, label and amount"); func = core::Func::add("drop", Game::func_drop); func->set_info("[label] drop an item and activate it"); func = core::Func::add("eject", Game::func_eject); func->set_info("[int] eject item with id from inventory"); func = core::Func::add("mount", Game::func_mount); func->set_info("[int] mount a weapon into the first available slot"); func = core::Func::add("beam", Game::func_beam); func->set_info("beam nearby cargo pods in"); func = core::Func::add("give", Game::func_give); func->set_info("cheat function to get items and ships"); func = core::Func::add("wingmen", Game::func_wingmen); func->set_info("cheat function for wingmen control"); func = core::Func::add("specs", Game::func_specs); func->set_info("change your current ship's engine specifications"); 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("toggle kinetic impulse drive"); func = core::Func::add("freeflight", Game::func_freeflight); func->set_info("disable autopilot, enable free flight"); func = core::Func::add("launch", Game::func_launch); func->set_info("launch to space when docked"); func = core::Func::add("respawn", Game::func_respawn); func->set_info("respawn when your ship has been destroyed"); func = core::Func::add("explode", Game::func_explode); func->set_info("self-destruct"); func = core::Func::add("goto", Game::func_goto); func->set_info("[string] goto to an entity within the zone"); func = core::Func::add("@goto", Game::func_target_goto); func->set_info("set autopilot to target"); func = core::Func::add("@dock", Game::func_target_dock); func->set_info("set autopilot to dock at target"); func = core::Func::add("@formation", Game::func_target_formation); func->set_info("set autopilot to formation flight with target"); func = core::Func::add("@hail", Game::func_target_hail); func->set_info("send a standard hail to target"); func = core::Func::add("@trade", Game::func_target_trade); func->set_info("send a trade request to target"); // add engine variables g_impulsespeed = core::Cvar::get("g_impulsespeed", "1500", core::Cvar::Game | core::Cvar::Archive); g_impulsespeed->set_info("[float] speed 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::Game | core::Cvar::Archive); g_devel->set_info("[bool] enable or disable developer mode"); g_damping = core::Cvar::get("g_damping", "0.1", core::Cvar::Game | core::Cvar::Archive); g_damping->set_info("[float] physics damping factor (0-1)"); g_deplete = core::Cvar::get("g_deplete", "60", core::Cvar::Game | core::Cvar::Archive); g_deplete->set_info("[int] number of seconds to deplete 1 unit of cargo from inventories"); g_cargoloss = core::Cvar::get("g_cargoloss", "100", core::Cvar::Game | core::Cvar::Archive); g_cargoloss->set_info("[int] percentage of cargo to drop on player death"); } Game::~Game() { g_impulsespeed = 0; g_jumppointrange = 0; g_devel = 0; g_damping = 0; g_deplete = 0; // clear defaults Default::clear(); // clear Cargo Cargo::done(); // clear Weapon Weapon::done(); // clear ShipModel ShipModel::done(); // clear Templates Template::done(); // clear Factions Faction::done(); } bool Game::load_world() { std::string inifilename("ini/world"); filesystem::IniFile inifile; inifile.open(inifilename); if (!inifile.is_open()) { con_error << "Could not open " << inifile.name() << std::endl; return false; } con_print << "^BLoading world..." << std::endl; core::Zone *zone = 0; std::string label; math::Color color; while (inifile.getline()) { if (inifile.got_section()) { zone = 0; if (inifile.got_section("world")) { continue; } else { inifile.unknown_section(); } } else if (inifile.got_key()) { if (inifile.in_section("world")) { if (inifile.got_key_label("zone", label)) { zone = new core::Zone(label); core::Zone::add(zone); } else { inifile.unknown_key(); } } } } inifile.close(); if (!core::Zone::registry().size()) { con_error << "No zones found!" << std::endl; return false; } con_debug << " " << inifile.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; // set zone defaults zone->set_color(0.5f); zone->set_ambient_color(0.1f); zone->set_sky("default"); std::string inifilename("ini/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; math::Color color; core::Entity *entity = 0; core::Inventory *inventory = 0; core::Item *item = 0; Station *station = 0; Planet *planet = 0; Star *star = 0; NavPoint *navpoint = 0; JumpPoint *jumppoint = 0; RaceTrack *racetrack = 0; CheckPoint *racetrack_checkpoint = 0; Patrol *patrol = 0; WayPoint *patrol_waypoint = 0; NPCType *patrol_npctype = 0; bool b; long l; std::string strval; math::Vector3f vectorval; while (zoneini.getline()) { if (zoneini.got_section()) { if (zoneini.got_section("zone")) { continue; } else if (zoneini.got_section("star")) { star = new Star(); entity = star; star->set_zone(zone); count ++; } else if (zoneini.got_section("navpoint")) { navpoint = new NavPoint(); entity = navpoint; navpoint->set_zone(zone); count ++; } else if (zoneini.got_section("jumpgate")) { jumppoint = new JumpGate(); entity = jumppoint; jumppoint->set_zone(zone); //jumppoint->set_radius(0); count ++; } else if (zoneini.got_section("jumppoint")) { jumppoint = new JumpPoint(); entity = jumppoint; jumppoint->set_zone(zone); count ++; } else if (zoneini.got_section("racetrack")) { racetrack = new RaceTrack(); entity = racetrack; racetrack->set_zone(zone); racetrack->set_radius(0); racetrack_checkpoint = 0; } else if (zoneini.got_section("checkpoint")) { racetrack_checkpoint = 0; if (!racetrack) { zoneini.unknown_error("checkpoint definition without racetrack"); } else { racetrack_checkpoint = new CheckPoint(racetrack); entity = racetrack_checkpoint; racetrack_checkpoint->set_radius(0); } } else if (zoneini.got_section("patrol")) { patrol = new Patrol(); entity = patrol; patrol->set_zone(zone); patrol_waypoint = 0; patrol_npctype = 0; } else if (zoneini.got_section("waypoint")) { patrol_waypoint = 0; patrol_npctype = 0; if (!entity || !patrol || (patrol != entity)) { zoneini.unknown_error("waypoint definition without patrol"); } else if ((entity->moduletype() != patrol_enttype)) { zoneini.unknown_error("waypoint definition for invalid entity type"); } else { patrol_waypoint = patrol->add_waypoint(); } } else if (zoneini.got_section("npc")) { patrol_waypoint = 0; patrol_npctype = 0; if (!entity || !patrol || (patrol != entity)) { zoneini.unknown_error("npc definition without patrol"); } else if ((entity->moduletype() != patrol_enttype)) { zoneini.unknown_error("npc definition for invalid entity type"); } else { patrol_npctype = patrol->add_npctype(); } } else if (zoneini.got_section("planet")) { planet = new Planet(); entity = planet; planet->set_zone(zone); count ++; } else if (zoneini.got_section("station")) { station = new Station(); entity = station; station->set_zone(zone); station->set_radius(0); count ++; } else if (zoneini.got_section("entity")) { entity = new core::Entity(); entity->set_zone(zone); entity->set_radius(0); count ++; } else if (zoneini.got_section("cargo")) { // new cargo trading definition for the current base item = 0; inventory = 0; if (!entity) { zoneini.unknown_error("cargo definition without entity"); } else if ((entity->moduletype() != planet_enttype) && (entity->moduletype() != station_enttype)) { zoneini.unknown_error("cargo definition for invalid entity type"); } else { inventory = entity->inventory(); if (!inventory) { inventory = entity->add_inventory(); } } } else if (zoneini.got_section("ship")) { // new ship trading definition for the current base item = 0; inventory = 0; if (!entity) { zoneini.unknown_error("ship definition without entity"); } else if ((entity->moduletype() != planet_enttype) && (entity->moduletype() != station_enttype)) { zoneini.unknown_error("ship definition for invalid entity type"); } else { inventory = entity->inventory(); if (!inventory) { inventory = entity->add_inventory(); } } } else if (zoneini.got_section("weapon")) { // new weapon trading definition for the current base item = 0; inventory = 0; if (!entity) { zoneini.unknown_error("weapon definition without entity"); } else if ((entity->moduletype() != planet_enttype) && (entity->moduletype() != station_enttype)) { zoneini.unknown_error("weapon definition for invalid entity type"); } else { inventory = entity->inventory(); if (!inventory) { inventory = entity->add_inventory(); } } } 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 if (zoneini.got_key_color("color", color)) { zone->set_color(color); continue; } else if (zoneini.got_key_label("faction", strval)) { Faction *faction = Faction::find(strval); if (!faction) { zoneini.unknown_error("unknown faction '" + strval + "'"); } else { zone->set_color(faction->color()); } } else if (zoneini.got_key_color("ambient", color)) { zone->set_ambient_color(color); continue; } else if (zoneini.got_key_vector3f("location", vectorval)) { zone->set_location(vectorval); continue; } else if (zoneini.got_key_bool("showonmap", b)) { if (b) { zone->unset_flag(core::Zone::Hidden); } else { zone->set_flag(core::Zone::Hidden); } continue; } else if (zoneini.got_key_string("info", strval)) { core::Info *info = core::Info::find(zone->info()); if (!info) { std::string labelstr(zone->label()); info = new core::Info(core::Zone::infotype(), labelstr.c_str()); zone->set_info(info); } info->add_text(strval); continue; } else { zoneini.unknown_key(); } } else if (zoneini.in_section("star")) { if (core::Parser::got_entity_key(zoneini, star)) { continue; } else { zoneini.unknown_key(); } } else if (zoneini.in_section("navpoint")) { if (core::Parser::got_entity_key(zoneini, navpoint)) { continue; } else if (zoneini.got_key_label("faction", strval)) { Faction *faction = Faction::find(strval); if (!faction) { zoneini.unknown_error("unknown faction '" + strval + "'"); } else { faction->apply(navpoint); } } else { zoneini.unknown_key(); } } else if (zoneini.in_section("jumppoint")) { if (core::Parser::got_entity_key(zoneini, jumppoint)) { continue; } else if (zoneini.got_key_string("target", strval)) { jumppoint->set_target_label(strval); continue; } else { zoneini.unknown_key(); } } else if (zoneini.in_section("jumpgate")) { if (core::Parser::got_entity_key(zoneini, jumppoint)) { continue; } else if (zoneini.got_key_label("faction", strval)) { Faction *faction = Faction::find(strval); if (!faction) { zoneini.unknown_error("unknown faction '" + strval + "'"); } else { faction->apply(jumppoint); } } else if (zoneini.got_key_label("template", strval)) { Template *entitytemplate = Template::find(strval); if (!entitytemplate) { zoneini.unknown_error("unknown template '" + strval + "'"); } else { entitytemplate->apply(jumppoint); } } else if (zoneini.got_key_string("target", strval)) { jumppoint->set_target_label(strval); continue; } else { zoneini.unknown_key(); } } else if (zoneini.in_section("patrol")) { if (core::Parser::got_entity_key(zoneini, patrol)) { continue; } else if (zoneini.got_key_label("profile", strval)) { if (strval.compare("convoy") == 0) { patrol->set_profile(Patrol::ProfileConvoy); } else if (strval.compare("patrol") == 0) { patrol->set_profile(Patrol::ProfilePatrol); } else { zoneini.unknown_error("unknown profile '" + strval + "'"); } } else if (zoneini.got_key_label("faction", strval)) { Faction *faction = Faction::find(strval); if (!faction) { zoneini.unknown_error("unknown faction '" + strval + "'"); } else { patrol->set_faction(faction); } } else { zoneini.unknown_key(); } } else if (zoneini.in_section("waypoint")) { if (!patrol || (patrol != entity) || !patrol_waypoint ) { continue; } else if (zoneini.got_key_string("target", strval)) { patrol_waypoint->set_target_label(strval); continue; } else if (zoneini.got_key_label("cargo", strval)) { Cargo *cargo = Cargo::find(strval); if (!cargo) { zoneini.unknown_error("unknown cargo '" + strval + "'"); } else { patrol_waypoint->set_cargo(cargo); } continue; } else if (zoneini.got_key_bool("dock", b)) { patrol_waypoint->set_dock(b); } else { zoneini.unknown_key(); } } else if (zoneini.in_section("npc")) { if (!patrol || (patrol != entity) || !patrol_npctype) { continue; } else if (zoneini.got_key_string("name", strval)) { aux::strip_quotes(strval); patrol_npctype->set_name(strval); } else if (zoneini.got_key_long("amount", l)) { patrol_npctype->set_amount(l); } else if (zoneini.got_key_bool("merchant", b)) { patrol_npctype->set_merchant(b); } else if (zoneini.got_key_label("ship", strval)) { ShipModel *shipmodel = ShipModel::find(strval); if (!shipmodel) { zoneini.unknown_error("unknown ship type '" + strval + "'"); } else { patrol_npctype->set_shipmodel(shipmodel); } } else if (zoneini.got_key_label("faction", strval)) { Faction *faction = Faction::find(strval); if (!faction) { zoneini.unknown_error("unknown faction '" + strval + "'"); } else { patrol_npctype->set_faction(faction); } } else if (zoneini.got_key_label("cannon", strval)) { Weapon *cannon = Weapon::find(strval); if (!cannon) { zoneini.unknown_error("unknown weapon type '" + strval + "'"); } else if (cannon->subtype() != Weapon::Cannon) { zoneini.unknown_error("weapon type '" + strval + "' is not a cannon"); } else { patrol_npctype->set_cannon(cannon); } } else if (zoneini.got_key_label("turret", strval)) { Weapon *turret = Weapon::find(strval); if (!turret) { zoneini.unknown_error("unknown weapon type '" + strval + "'"); } else if (turret->subtype() != Weapon::Turret) { zoneini.unknown_error("weapon type '" + strval + "' is not a turret"); } else { patrol_npctype->set_turret(turret); } } else { zoneini.unknown_key(); } } else if (zoneini.in_section("planet")) { if (core::Parser::got_entity_key(zoneini, planet)) { 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 if (zoneini.got_key_label("faction", strval)) { Faction *faction = Faction::find(strval); if (!faction) { zoneini.unknown_error("unknown faction '" + strval + "'"); } else { faction->apply(planet); } } else { zoneini.unknown_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 if (zoneini.got_key_label("faction", strval)) { Faction *faction = Faction::find(strval); if (!faction) { zoneini.unknown_error("unknown faction '" + strval + "'"); } else { faction->apply(station); } } else if (zoneini.got_key_label("template", strval)) { Template *entitytemplate = Template::find(strval); if (!entitytemplate) { zoneini.unknown_error("unknown template '" + strval + "'"); } else { entitytemplate->apply(station); } } else if (zoneini.got_key_label("ship", strval)) { ShipModel *shipmodel = ShipModel::find(strval); if (!shipmodel) { zoneini.unknown_error("unknown ship type '" + strval + "'"); } else { shipmodel->apply(station); } } else if (zoneini.got_key_label("cannon", strval)) { Weapon *cannon = Weapon::find(strval); if (!cannon) { zoneini.unknown_error("unknown weapon type '" + strval + "'"); } else if (cannon->subtype() != Weapon::Cannon) { zoneini.unknown_error("weapon type '" + strval + "' is not a cannon"); } else { station->set_cannon(cannon); } } else if (zoneini.got_key_label("turret", strval)) { Weapon *turret = Weapon::find(strval); if (!turret) { zoneini.unknown_error("unknown weapon type '" + strval + "'"); } else if (turret->subtype() != Weapon::Turret) { zoneini.unknown_error("weapon type '" + strval + "' is not a turret"); } else { station->set_turret(turret); } } else { zoneini.unknown_key(); } } else if (zoneini.in_section("racetrack")) { if (core::Parser::got_entity_key(zoneini, racetrack)) { continue; } else { zoneini.unknown_key(); } } else if (zoneini.in_section("checkpoint")) { if (!racetrack_checkpoint) { continue; } else if (core::Parser::got_entity_key(zoneini, racetrack_checkpoint)) { continue; } else { zoneini.unknown_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 if (zoneini.got_key_label("faction", strval)) { Faction *faction = Faction::find(strval); if (!faction) { zoneini.unknown_error("unknown faction '" + strval + "'"); } else { entity->set_color(faction->color()); entity->set_color_second(faction->color_second()); } } else if (zoneini.got_key_label("template", strval)) { Template *entitytemplate = Template::find(strval); if (!entitytemplate) { zoneini.unknown_error("unknown template '" + strval + "'"); } else { entitytemplate->apply(entity); } } else if (zoneini.got_key_label("ship", strval)) { ShipModel *shipmodel = ShipModel::find(strval); if (!shipmodel) { zoneini.unknown_error("unknown ship type '" + strval + "'"); } else { shipmodel->apply(entity); } } else if (zoneini.got_key_label("ship", strval)) { ShipModel *shipmodel = ShipModel::find(strval); if (!shipmodel) { zoneini.unknown_error("unknown ship type '" + strval + "'"); } else { // TODO apply ship model } } else { zoneini.unknown_key(); } } else if (zoneini.in_section("cargo")) { // cargo trade definition for a station or planet if (!entity || !inventory) { continue; } if (zoneini.got_key_label("label", strval)) { Cargo *cargo = Cargo::find(strval); if (cargo) { item = inventory->find(cargo); if (!item) { item = new core::Item(cargo); item->set_amount(-1); item->set_price(cargo->price()); inventory->add(item); } } else { zoneini.unknown_error("unknown cargo type '" + strval + "'"); } } else if (zoneini.got_key_long("price", l)) { if (item) { item->set_price(l); } } else if (zoneini.got_key_long("amount", l)) { if (item) { item->set_amount(l); } } else { zoneini.unknown_key(); } } else if (zoneini.in_section("weapon")) { // weapon trade definition for a station or planet if (!entity || !inventory) { continue; } if (zoneini.got_key_label("label", strval)) { Weapon *weapon = Weapon::find(strval); if (weapon) { item = inventory->find(weapon); if (!item) { item = new core::Item(weapon); item->set_amount(-1); item->set_price(weapon->price()); item->set_flag(core::Item::Unrestricted); switch (weapon->subtype()) { case Weapon::Cannon: item->set_flag(core::Item::Unique); item->set_flag(core::Item::Mountable); break; case Weapon::Turret: item->set_flag(core::Item::Unique); item->set_flag(core::Item::Mountable); break; default: break; } inventory->add(item); } } else { zoneini.unknown_error("unknown weapon type '" + strval + "'"); } } else if (zoneini.got_key_long("price", l)) { if (item) { item->set_price(l); } } else if (zoneini.got_key_long("amount", l)) { if (item) { item->set_amount(l); } } else { zoneini.unknown_key(); } } else if (zoneini.in_section("ship")) { // ship trade definition for a station or planet if (!entity || !inventory) { continue; } if (zoneini.got_key_label("label", strval)) { ShipModel *shipmodel= ShipModel::find(strval); if (shipmodel) { if (entity->moduletype() == patrol_enttype) { item = 0; } else { item = inventory->find(shipmodel); } if (!item) { item = new core::Item(shipmodel); item->set_amount(-1); item->set_price(shipmodel->price()); inventory->add(item); } } else { zoneini.unknown_error("unknown ship type '" + strval + "'"); } } else if (zoneini.got_key_long("price", l)) { if (item) { item->set_price(l); } } else { zoneini.unknown_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->entity_moduletypeid == patrol_enttype) { // validate jump gate Patrol *patrol = static_cast(entity); patrol->validate(); } else { if (entity->has_flag(core::Entity::Dockable)) { generate_entity_menus(entity); } } if (!entity->radius()) { if (entity->model()) { entity->set_radius(entity->model()->radius()); } else { entity->set_radius(0.25f); } //con_debug << " " << entity->label() << " radius set to " << entity->radius() << std::endl; } // initialize physics on planets and entities with a model if ((entity->entity_moduletypeid == star_enttype) ||(entity->entity_moduletypeid == planet_enttype) || (entity->model())) { entity->reset(); } if (entity->model()) { const float modelscale = entity->radius() / entity->model()->radius(); entity->add_slots(); entity->slots()->load(entity->model(), modelscale); } } return true; } bool Game::generate_entity_menus(core::Entity *entity) { using core::MenuDescription; using core::ButtonDescription; if ((entity->moduletype() != planet_enttype) && (entity->moduletype() != station_enttype)) { // not dockable return false; } MenuDescription *menu_dealer = 0; ButtonDescription *button = 0; // dockable entity // add main menu MenuDescription *menu_main = new MenuDescription(); menu_main->set_label("main"); menu_main->set_text("Launch area"); entity->add_menu(menu_main); // add launch button button = new ButtonDescription(); button->set_text("Launch"); button->set_command("launch", ButtonDescription::CommandGame); button->set_alignment(ButtonDescription::Center); menu_main->add_button(button); // add trade menus if (entity->inventory()) { entity->set_flag(core::Entity::KeepAlive); size_t nbcargo = 0; size_t nbweapon = 0; size_t nbships = 0; for (core::Inventory::Items::const_iterator it = entity->inventory()->items().begin(); it != entity->inventory()->items().end(); ++it) { core::Item *item = (*it); if (item->info()->type() == Cargo::infotype()) { nbcargo++; } else if (item->info()->type() == Weapon::infotype()) { nbweapon++; } else if (item->info()->type() == ShipModel::infotype()) { if (!menu_dealer) { menu_dealer = new MenuDescription(); menu_dealer->set_label("ships"); menu_dealer->set_text("Ship Dealer"); } button = new ButtonDescription(); button->set_text("buy " + item->info()->name()); std::ostringstream str(""); str << "buy " << item->info()->id(); button->set_command(str.str() , ButtonDescription::CommandMenu); button->set_info(item->info()); button->set_alignment(ButtonDescription::Left); menu_dealer->add_button(button); nbships++; } } if (nbcargo > 0) { con_debug << " " << entity->label() << " " << nbcargo << " cargo " << aux::plural("type", nbcargo) << std::endl; button = new ButtonDescription(); button->set_text("Trader"); button->set_command("trade cargo", ButtonDescription::CommandMenu); button->set_alignment(ButtonDescription::Center); menu_main->add_button(button); } if (nbweapon > 0) { con_debug << " " << entity->label() << " " << nbcargo << " weapon " << aux::plural("type", nbweapon) << std::endl; button = new ButtonDescription(); button->set_text("Weapon Dealer"); button->set_command("trade weapon", ButtonDescription::CommandMenu); button->set_alignment(ButtonDescription::Center); menu_main->add_button(button); } if (nbships > 0) { con_debug << " " << entity->label() << " " << nbships << " ship " << aux::plural("type", nbships) << std::endl; button = new ButtonDescription(); button->set_text("Return"); button->set_command("main", ButtonDescription::CommandMenu); 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("ships", ButtonDescription::CommandMenu); button->set_alignment(ButtonDescription::Center); menu_main->add_button(button); } } return true; } // load game defaults settings bool Game::load_settings() { filesystem::IniFile inifile; inifile.open("ini/game"); 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 if (inifile.got_section("cargo")) { 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_label("zone", str)) { Default::zone = core::Zone::find(str); } else if (inifile.got_key_label("ship", str)) { Default::shipmodel = ShipModel::find(str); } else { inifile.unknown_key(); } } } } 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; } for (core::GameInterface::Players::iterator it = core::game()->players().begin(); it != core::game()->players().end(); ++it) { core::Entity *target = 0; // set player autopilot target if ((*it)->control()) { assert ((*it)->control()->moduletype() == ship_enttype); Ship * ship = static_cast((*it)->control()); if (ship->state() != Ship::Docked) { target = ship->autopilot_target(); } } (*it)->set_autopilot_target(target); } } void Game::player_connect(core::Player *player) { std::string args; player->set_zone(Default::zone); player->set_view(0); // not docked // FIXME load player func_spectate(player, args); } void Game::player_disconnect(core::Player *player) { if (player->control()) { core::server()->module()->player_save(player); } } // load singleplayer savegame void Game::game_load(core::Player *player, filesystem::IniFile & inifile) { if (player->control()) return; if (core::server()->mode() != core::GameServer::SinglePlayer) { return; } // reset player timestamps player->set_time_wasted(0); player->set_time_joined(); SaveGame::load_game(player, inifile); } // load multiplayer savegame player data void Game::player_load(core::Player *player) { if (!player->guid().is_valid()) { return; } if (player->control()) return; if (core::server()->mode() != core::GameServer::MultiPlayer) { return; } // reset player timestamps player->set_time_wasted(0); player->set_time_joined(); std::string guid(player->guid().str()); std::string directory(guid.substr(0,4)); std::string filename; filename.append("players"); filename += '/'; filename.append(directory); filename += '/'; filename.append(guid); filesystem::IniFile inifile; inifile.open(filename); if (!inifile.is_open()) { return; } con_debug << "player " << player->id() << ": " << "loading data" << std::endl; SaveGame::load_game(player, inifile); inifile.close(); } void Game::game_save(core::Player *player, std::ostream & os) { if ((!player->control()) || (player->control()->moduletype() != ship_enttype)) { return; } if (core::server()->mode() == core::GameServer::SinglePlayer) { // save player data SaveGame::player_to_stream(player, os); } } void Game::player_save(core::Player *player) { if ((!player->control()) || (player->control()->moduletype() != ship_enttype)) { return; } if (core::server()->mode() == core::GameServer::SinglePlayer) { std::string command("savegame autosave AUTOSAVE"); core::CommandBuffer::exec(command); } else if (core::server()->mode() == core::GameServer::MultiPlayer) { if (!player->guid().is_valid()) { return; } con_debug << "player " << player->id() << ": " << "saving data" << std::endl; std::string guid(player->guid().str()); std::string directory(guid.substr(0,4)); // create players/ directory std::string filename(filesystem::writedir()); filename.append("players"); if (!sys::directory_exists(filename)) { sys::mkdir(filename); } filename += '/'; // second level directory filename.append(directory); if (!sys::directory_exists(filename)) { sys::mkdir(filename); } filename += '/'; // guid.ini filename.append(guid); filename.append(".ini"); std::ofstream ofs(filename.c_str()); if (!ofs.is_open()) { con_warn << "Could not write " << filename << std::endl; return; } // save header ofs << "; " << guid << ".ini" << std::endl; ofs << "; Project::OSiRiON player data" << std::endl; ofs << std::endl; // save player data SaveGame::player_to_stream(player, ofs); // close output stream ofs.close(); } } } // namespace game