void ShipInfoDisplay::UpdateAttributes(const Ship &ship) { attributeLabels.clear(); attributeValues.clear(); attributesHeight = 10; const Outfit &attributes = ship.Attributes(); attributeLabels.push_back(string()); attributeValues.push_back(string()); attributesHeight += 10; attributeLabels.push_back("cost:"); attributeValues.push_back(Format::Number(ship.Cost())); attributesHeight += 20; attributeLabels.push_back(string()); attributeValues.push_back(string()); attributesHeight += 10; if(attributes.Get("shield generation")) { attributeLabels.push_back("shields charge / max:"); attributeValues.push_back(Format::Number(60. * attributes.Get("shield generation")) + " / " + Format::Number(attributes.Get("shields"))); } else { attributeLabels.push_back("shields:"); attributeValues.push_back(Format::Number(attributes.Get("shields"))); } attributesHeight += 20; if(attributes.Get("hull repair rate")) { attributeLabels.push_back("hull repair / max:"); attributeValues.push_back(Format::Number(60. * attributes.Get("hull repair rate")) + " / " + Format::Number(attributes.Get("hull"))); } else { attributeLabels.push_back("hull:"); attributeValues.push_back(Format::Number(attributes.Get("hull"))); } attributesHeight += 20; double emptyMass = attributes.Get("mass"); attributeLabels.push_back("mass with no cargo:"); attributeValues.push_back(Format::Number(emptyMass)); attributesHeight += 20; attributeLabels.push_back("cargo space:"); attributeValues.push_back(Format::Number(attributes.Get("cargo space"))); attributesHeight += 20; attributeLabels.push_back("required crew / bunks:"); attributeValues.push_back(Format::Number(ship.RequiredCrew()) + " / " + Format::Number(attributes.Get("bunks"))); attributesHeight += 20; attributeLabels.push_back("fuel capacity:"); attributeValues.push_back(Format::Number(attributes.Get("fuel capacity"))); attributesHeight += 20; double fullMass = emptyMass + attributes.Get("cargo space"); attributeLabels.push_back(string()); attributeValues.push_back(string()); attributesHeight += 10; attributeLabels.push_back((emptyMass == fullMass) ? "movement:" : "movement, full / no cargo:"); attributeValues.push_back(string()); attributesHeight += 20; attributeLabels.push_back("max speed:"); attributeValues.push_back(Format::Number(60. * attributes.Get("thrust") / attributes.Get("drag"))); attributesHeight += 20; attributeLabels.push_back("acceleration:"); if(emptyMass == fullMass) attributeValues.push_back(Format::Number(3600. * attributes.Get("thrust") / fullMass)); else attributeValues.push_back(Format::Number(3600. * attributes.Get("thrust") / fullMass) + " / " + Format::Number(3600. * attributes.Get("thrust") / emptyMass)); attributesHeight += 20; attributeLabels.push_back("turning:"); if(emptyMass == fullMass) attributeValues.push_back(Format::Number(60. * attributes.Get("turn") / fullMass)); else attributeValues.push_back(Format::Number(60. * attributes.Get("turn") / fullMass) + " / " + Format::Number(60. * attributes.Get("turn") / emptyMass)); attributesHeight += 20; // Find out how much outfit, engine, and weapon space the chassis has. map<string, double> chassis; static const string names[] = { "outfit space free:", "outfit space", " weapon capacity:", "weapon capacity", " engine capacity:", "engine capacity", "guns ports free:", "gun ports", "turret mounts free:", "turret mounts" }; static const int NAMES = sizeof(names) / sizeof(names[0]); for(int i = 1; i < NAMES; i += 2) chassis[names[i]] = attributes.Get(names[i]); for(const auto &it : ship.Outfits()) for(auto &cit : chassis) cit.second -= it.second * it.first->Get(cit.first); attributeLabels.push_back(string()); attributeValues.push_back(string()); attributesHeight += 10; for(int i = 0; i < NAMES; i += 2) { attributeLabels.push_back(names[i]); attributeValues.push_back(Format::Number(attributes.Get(names[i + 1])) + " / " + Format::Number(chassis[names[i + 1]])); attributesHeight += 20; } if(ship.DroneBaysFree()) { attributeLabels.push_back("drone bays:"); attributeValues.push_back(to_string(ship.DroneBaysFree())); attributesHeight += 20; } if(ship.FighterBaysFree()) { attributeLabels.push_back("fighter bays:"); attributeValues.push_back(to_string(ship.FighterBaysFree())); attributesHeight += 20; } tableLabels.clear(); energyTable.clear(); heatTable.clear(); // Skip a spacer and the table header. attributesHeight += 30; tableLabels.push_back("idle:"); energyTable.push_back(Format::Number(60. * attributes.Get("energy generation"))); heatTable.push_back(Format::Number( 60. * (attributes.Get("heat generation") - attributes.Get("cooling")))); attributesHeight += 20; tableLabels.push_back("moving:"); energyTable.push_back(Format::Number( -60. * (attributes.Get("thrusting energy") + attributes.Get("turning energy")))); heatTable.push_back(Format::Number( 60. * (attributes.Get("thrusting heat") + attributes.Get("turning heat")))); attributesHeight += 20; double firingEnergy = 0.; double firingHeat = 0.; for(const auto &it : ship.Outfits()) if(it.first->IsWeapon() && it.first->Reload()) { firingEnergy += it.second * it.first->FiringEnergy() / it.first->Reload(); firingHeat += it.second * it.first->FiringHeat() / it.first->Reload(); } tableLabels.push_back("firing:"); energyTable.push_back(Format::Number(-60. * firingEnergy)); heatTable.push_back(Format::Number(60. * firingHeat)); attributesHeight += 20; tableLabels.push_back("max:"); energyTable.push_back(Format::Number(attributes.Get("energy capacity"))); heatTable.push_back(Format::Number(60. * emptyMass * .1 * attributes.Get("heat dissipation"))); // Pad by 10 pixels on the top and bottom. attributesHeight += 30; }
// Load the cargo back into your ships. This may require selling excess, in // which case a message will be returned. void PlayerInfo::TakeOff() { shouldLaunch = false; // This can only be done while landed. if(!system || !planet) return; // Jobs are only available when you are landed. availableJobs.clear(); availableMissions.clear(); doneMissions.clear(); soldOutfits.clear(); // Special persons who appeared last time you left the planet, can appear // again. for(const auto &it : GameData::Persons()) it.second.GetShip()->SetSystem(nullptr); // Store the total cargo counts in case we need to adjust cost bases below. map<string, int> originalTotals = cargo.Commodities(); Ship *flagship = Flagship(); bool canRecharge = planet->HasSpaceport() && planet->CanUseServices(); for(const shared_ptr<Ship> &ship : ships) if(!ship->IsParked() && ship->GetSystem() == system && !ship->IsDisabled()) { if(canRecharge) ship->Recharge(); if(ship.get() != flagship) { ship->Cargo().SetBunks(ship->Attributes().Get("bunks") - ship->RequiredCrew()); cargo.TransferAll(&ship->Cargo()); } } if(flagship) { // Load up your flagship last, so that it will have space free for any // plunder that you happen to acquire. flagship->Cargo().SetBunks(flagship->Attributes().Get("bunks") - flagship->RequiredCrew()); cargo.TransferAll(&flagship->Cargo()); } if(cargo.Passengers() && ships.size()) { Ship &flagship = *ships.front(); int extra = min(cargo.Passengers(), flagship.Crew() - flagship.RequiredCrew()); if(extra) { flagship.AddCrew(-extra); Messages::Add("You fired " + to_string(extra) + " crew members to free up bunks for passengers."); flagship.Cargo().SetBunks(flagship.Attributes().Get("bunks") - flagship.Crew()); cargo.TransferAll(&flagship.Cargo()); } } if(ships.size()) { Ship &flagship = *ships.front(); int extra = flagship.Crew() + flagship.Cargo().Passengers() - flagship.Attributes().Get("bunks"); if(extra > 0) { flagship.AddCrew(-extra); Messages::Add("You fired " + to_string(extra) + " crew members because you have no bunks for them."); flagship.Cargo().SetBunks(flagship.Attributes().Get("bunks") - flagship.Crew()); } } // For each fighter and drone you own, try to find a ship that has a bay to // carry it in. Any excess ships will need to be sold. vector<shared_ptr<Ship>> fighters; vector<shared_ptr<Ship>> drones; for(shared_ptr<Ship> &ship : ships) { if(ship->IsParked() || ship->GetSystem() != system || ship->IsDisabled()) continue; bool fit = false; const string &category = ship->Attributes().Category(); if(category == "Fighter") { for(shared_ptr<Ship> &parent : ships) if(parent->GetSystem() == system && !parent->IsParked() && parent->FighterBaysFree()) { parent->AddFighter(ship); fit = true; break; } if(!fit) fighters.push_back(ship); } else if(category == "Drone") { for(shared_ptr<Ship> &parent : ships) if(parent->GetSystem() == system && !parent->IsParked() && parent->DroneBaysFree()) { parent->AddFighter(ship); fit = true; break; } if(!fit) drones.push_back(ship); } } if(!drones.empty() || !fighters.empty()) { // If your fleet contains more fighters or drones than you can carry, // some of them must be sold. ostringstream out; out << "Because none of your ships can carry them, you sold "; if(!fighters.empty() && !drones.empty()) out << fighters.size() << (fighters.size() == 1 ? " fighter and " : " fighters and ") << drones.size() << (drones.size() == 1 ? " drone" : " drones"); else if(fighters.size()) out << fighters.size() << (fighters.size() == 1 ? " fighter" : " fighters"); else out << drones.size() << (drones.size() == 1 ? " drone" : " drones"); int64_t income = 0; for(const shared_ptr<Ship> &ship : fighters) { auto it = find(ships.begin(), ships.end(), ship); if(it != ships.end()) { income += ship->Cost(); ships.erase(it); } } for(const shared_ptr<Ship> &ship : drones) { auto it = find(ships.begin(), ships.end(), ship); if(it != ships.end()) { income += ship->Cost(); ships.erase(it); } } out << ", earning " << income << " credits."; accounts.AddCredits(income); Messages::Add(out.str()); } // By now, all cargo should have been divvied up among your ships. So, any // mission cargo or passengers left behind cannot be carried, and those // missions have failed. vector<const Mission *> missionsToRemove; for(const auto &it : cargo.MissionCargo()) if(it.second) { Messages::Add("Mission \"" + it.first->Name() + "\" failed because you do not have space for the cargo."); missionsToRemove.push_back(it.first); } for(const auto &it : cargo.PassengerList()) if(it.second) { Messages::Add("Mission \"" + it.first->Name() + "\" failed because you do not have enough passenger bunks free."); missionsToRemove.push_back(it.first); } for(const Mission *mission : missionsToRemove) RemoveMission(Mission::FAIL, *mission, nullptr); // Any ordinary cargo left behind can be sold. int64_t sold = cargo.Used(); int64_t income = 0; int64_t totalBasis = 0; if(sold) for(const auto &commodity : cargo.Commodities()) { if(!commodity.second) continue; // Figure out how much income you get for selling this cargo. int64_t value = commodity.second * system->Trade(commodity.first); income += value; int original = originalTotals[commodity.first]; auto it = costBasis.find(commodity.first); if(!original || it == costBasis.end() || !it->second) continue; // Now, figure out how much of that income is profit by calculating // the cost basis for this cargo (which is just the total cost basis // multiplied by the percent of the cargo you are selling). int64_t basis = it->second * commodity.second / original; it->second -= basis; totalBasis += basis; } accounts.AddCredits(income); cargo.Clear(); if(sold) { // Report how much excess cargo was sold, and what profit you earned. ostringstream out; out << "You sold " << sold << " tons of excess cargo for " << income << " credits"; if(totalBasis && totalBasis != income) out << " (for a profit of " << (income - totalBasis) << " credits)."; else out << "."; Messages::Add(out.str()); } }