void ShipInfoDisplay::DrawAttributes(const Point &topLeft) const { Point point = Draw(topLeft, attributeLabels, attributeValues); // Get standard colors to draw with. Color labelColor = *GameData::Colors().Get("medium"); Color valueColor = *GameData::Colors().Get("bright"); Table table; table.AddColumn(10, Table::LEFT); table.AddColumn(WIDTH - 90, Table::RIGHT); table.AddColumn(WIDTH - 10, Table::RIGHT); table.DrawAt(point); table.DrawGap(10.); table.Advance(); table.Draw("energy", labelColor); table.Draw("heat", labelColor); for(unsigned i = 0; i < tableLabels.size(); ++i) { table.Draw(tableLabels[i], labelColor); table.Draw(energyTable[i], valueColor); table.Draw(heatTable[i], valueColor); } }
void ShipInfoPanel::DrawShipStats(const Rectangle &bounds) { // Check that the specified area is big enough. if(bounds.Width() < WIDTH) return; // Colors to draw with. Color dim = *GameData::Colors().Get("medium"); Color bright = *GameData::Colors().Get("bright"); const Ship &ship = **shipIt; const Font &font = FontSet::Get(14); // Table attributes. Table table; table.AddColumn(0, Table::LEFT); table.AddColumn(WIDTH - 20, Table::RIGHT); table.SetUnderline(0, WIDTH - 20); table.DrawAt(bounds.TopLeft() + Point(10., 8.)); // Draw the ship information. table.Draw("ship:", dim); table.Draw(font.TruncateMiddle(ship.Name(), WIDTH - 50), bright); table.Draw("model:", dim); table.Draw(ship.ModelName(), bright); info.DrawAttributes(table.GetRowBounds().TopLeft() - Point(10., 10.)); }
Point ItemInfoDisplay::Draw(Point point, const vector<string> &labels, const vector<string> &values) { // Add ten pixels of padding at the top. point.Y() += 10.; // Get standard colors to draw with. Color labelColor = *GameData::Colors().Get("medium"); Color valueColor = *GameData::Colors().Get("bright"); Table table; // Use 10-pixel margins on both sides. table.AddColumn(10, Table::LEFT); table.AddColumn(WIDTH - 10, Table::RIGHT); table.DrawAt(point); for(unsigned i = 0; i < labels.size() && i < values.size(); ++i) { if(labels[i].empty()) { table.DrawGap(10); continue; } table.Draw(labels[i], values[i].empty() ? valueColor : labelColor); table.Draw(values[i], valueColor); } return table.GetPoint(); }
void PreferencesPanel::DrawPlugins() { const Color &back = *GameData::Colors().Get("faint"); const Color &medium = *GameData::Colors().Get("medium"); const Color &bright = *GameData::Colors().Get("bright"); Table table; table.AddColumn(-115, Table::LEFT); table.SetUnderline(-120, 120); int firstY = -238; table.DrawAt(Point(-130, firstY)); table.DrawUnderline(medium); table.Draw("Installed plugins:", bright); table.DrawGap(5); const int MAX_TEXT_WIDTH = 230; const Font &font = FontSet::Get(14); for(const pair<string, string> &plugin : GameData::PluginAboutText()) { pluginZones.emplace_back(table.GetCenterPoint(), table.GetRowSize(), plugin.first); bool isSelected = (plugin.first == selectedPlugin); if(isSelected || plugin.first == hoverPlugin) table.DrawHighlight(back); table.Draw(font.TruncateMiddle(plugin.first, MAX_TEXT_WIDTH), isSelected ? bright : medium); if(isSelected) { const Sprite *sprite = SpriteSet::Get(plugin.first); Point top(15., firstY); if(sprite) { Point center(130., top.Y() + .5 * sprite->Height()); SpriteShader::Draw(sprite, center); top.Y() += sprite->Height() + 10.; } WrappedText wrap(font); wrap.SetWrapWidth(MAX_TEXT_WIDTH); static const string EMPTY = "(No description given.)"; wrap.Wrap(plugin.second.empty() ? EMPTY : plugin.second); wrap.Draw(top, medium); } } }
void BankPanel::Draw() const { Table table; table.AddColumn(TYPE_X, Table::LEFT); table.AddColumn(PRINCIPAL_X, Table::LEFT); table.AddColumn(INTEREST_X, Table::LEFT); table.AddColumn(TERM_X, Table::LEFT); table.AddColumn(PAYMENT_X, Table::LEFT); table.AddColumn(170, Table::RIGHT); table.SetHighlight(-300, 180); table.DrawAt(Point(0., FIRST_Y)); Color back = *GameData::Colors().Get("faint"); Color unselected = *GameData::Colors().Get("medium"); Color selected = *GameData::Colors().Get("bright"); table.DrawUnderline(unselected); table.SetColor(selected); for(int i = 0; i < 6; ++i) table.Draw(HEADING[i]); table.DrawGap(5); // Figure out the total payments and principal (other than salaries). This // is in case there are more mortgages than can be displayed. int otherPrincipal = 0; int otherPayment = 0; for(const Mortgage &mortgage : player.Accounts().Mortgages()) { otherPrincipal += mortgage.Principal(); otherPayment += mortgage.Payment(); } int totalPayment = otherPayment; // Check if salaries need to be drawn. int salaries = player.Salaries(); int row = 0; for(const Mortgage &mortgage : player.Accounts().Mortgages()) { if(row == selectedRow) { table.DrawHighlight(back); table.SetColor(selected); } else table.SetColor(unselected); // There is room for seven rows if including salaries, or 8 if not. if(row == (6 + !salaries) && otherPrincipal != mortgage.Principal()) { table.Draw("Other", unselected); table.Draw(otherPrincipal); table.Advance(2); table.Draw(otherPayment); } else { table.Draw(mortgage.Type()); table.Draw(mortgage.Principal()); table.Draw(mortgage.Interest()); table.Draw(mortgage.Term()); table.Draw(mortgage.Payment()); otherPrincipal -= mortgage.Principal(); otherPayment -= mortgage.Payment(); } table.Draw("[pay extra]"); ++row; // Draw no more than 8 rows, counting the salaries row if any. if(row == 7 + !salaries) break; } table.SetColor(unselected); // Draw the salaries, if necessary. if(salaries) { totalPayment += salaries; table.Draw("Crew Salaries", unselected); table.Advance(3); table.Draw(salaries); table.Advance(); } table.Advance(3); table.Draw("total:", selected); table.Draw(totalPayment, unselected); table.Advance(); table.DrawAt(Point(0., FIRST_Y + 210.)); string credit = "Your credit score is " + to_string(player.Accounts().CreditScore()) + "."; table.Draw(credit); table.Advance(5); string amount; if(!qualify) amount = "You do not qualify for further loans at this time."; else amount = "You qualify for a new loan of up to " + Format::Number(qualify) + " credits."; bool isSelected = qualify && (selectedRow > (6 + !salaries) || static_cast<unsigned>(selectedRow) >= player.Accounts().Mortgages().size()); if(isSelected) table.DrawHighlight(back); table.Draw(amount, unselected); if(qualify) { table.Advance(4); table.Draw("[apply]", selected); } const Interface *interface = GameData::Interfaces().Get("bank"); Information info; for(const Mortgage &mortgage : player.Accounts().Mortgages()) if(mortgage.Principal() <= player.Accounts().Credits()) info.SetCondition("can pay"); interface->Draw(info); }
void InfoPanel::DrawInfo() const { Color dim = *GameData::Colors().Get("medium"); Color bright = *GameData::Colors().Get("bright"); Color elsewhere = *GameData::Colors().Get("dim"); Color dead(.4, 0., 0., 0.); const Font &font = FontSet::Get(14); // Player info. Table table; table.AddColumn(0, Table::LEFT); table.AddColumn(230, Table::RIGHT); table.SetUnderline(0, 230); table.DrawAt(Point(-490., -282.)); table.Draw("player:", dim); table.Draw(player.FirstName() + " " + player.LastName(), bright); table.Draw("net worth:", dim); table.Draw(Format::Number(player.Accounts().NetWorth()) + " credits", bright); // Determine the player's combat rating. table.DrawGap(10); size_t ratingLevel = 0; auto it = player.Conditions().find("combat rating"); if(it != player.Conditions().end() && it->second > 0) { ratingLevel = log(it->second); ratingLevel = min(ratingLevel, RATINGS.size() - 1); } table.DrawUnderline(dim); table.Draw("combat rating:", bright); table.Advance(); table.DrawGap(5); table.Draw(RATINGS[ratingLevel], dim); table.Draw("(" + to_string(ratingLevel) + ")", dim); auto salary = Match(player, "salary: ", ""); sort(salary.begin(), salary.end()); DrawList(salary, table, "salary:", 4); auto tribute = Match(player, "tribute: ", ""); sort(tribute.begin(), tribute.end()); DrawList(tribute, table, "tribute:", 4); int maxRows = static_cast<int>(250. - 30. - table.GetPoint().Y()) / 20; auto licenses = Match(player, "license: ", " License"); DrawList(licenses, table, "licenses:", maxRows, false); // Fleet listing. Point pos = Point(-240., -280.); font.Draw("ship", pos + Point(0., 0.), bright); font.Draw("model", pos + Point(220., 0.), bright); font.Draw("system", pos + Point(350., 0.), bright); font.Draw("shields", pos + Point(550. - font.Width("shields"), 0.), bright); font.Draw("hull", pos + Point(610. - font.Width("hull"), 0.), bright); font.Draw("fuel", pos + Point(670. - font.Width("fuel"), 0.), bright); font.Draw("crew", pos + Point(730. - font.Width("crew"), 0.), bright); FillShader::Fill(pos + Point(365., 15.), Point(730., 1.), dim); if(!player.Ships().size()) return; int lastIndex = player.Ships().size() - 1; const Ship *flagship = player.Flagship(); pos.Y() += 5.; int index = scroll; auto sit = player.Ships().begin() + scroll; for( ; sit < player.Ships().end(); ++sit) { const shared_ptr<Ship> &ship = *sit; pos.Y() += 20.; if(pos.Y() >= 260.) break; bool isElsewhere = (ship->GetSystem() != player.GetSystem()); isElsewhere |= (ship->CanBeCarried() && player.GetSystem()); bool isDead = ship->IsDestroyed() || ship->IsDisabled(); bool isHovered = (index == hover); const Color &color = isDead ? dead : isElsewhere ? elsewhere : isHovered ? bright : dim; font.Draw(ship->Name(), pos + Point(10. * ship->CanBeCarried(), 0.), color); font.Draw(ship->ModelName(), pos + Point(220., 0.), color); const System *system = ship->GetSystem(); if(system) font.Draw(system->Name(), pos + Point(350., 0.), color); string shields = to_string(static_cast<int>(100. * max(0., ship->Shields()))) + "%"; font.Draw(shields, pos + Point(550. - font.Width(shields), 0.), color); string hull = to_string(static_cast<int>(100. * max(0., ship->Hull()))) + "%"; font.Draw(hull, pos + Point(610. - font.Width(hull), 0.), color); string fuel = to_string(static_cast<int>( ship->Attributes().Get("fuel capacity") * ship->Fuel())); font.Draw(fuel, pos + Point(670. - font.Width(fuel), 0.), color); string crew = ship->IsParked() ? "Parked" : to_string(ship.get() == flagship ? ship->Crew() : ship->RequiredCrew()); font.Draw(crew, pos + Point(730. - font.Width(crew), 0.), color); if(index < lastIndex || selected < 0) zones.emplace_back(pos + Point(365, font.Height() / 2), Point(730, 20), index); else { int height = 280. - pos.Y(); zones.emplace_back(pos + Point(365, height / 2), Point(730, height), index); } ++index; } // Re-ordering ships in your fleet. if(selected >= 0) { const string &name = player.Ships()[selected]->Name(); Point pos(hoverPoint.X() - .5 * font.Width(name), hoverPoint.Y()); font.Draw(name, pos + Point(1., 1.), Color(0., 1.)); font.Draw(name, pos, bright); } }
void PreferencesPanel::DrawSettings() { const Color &back = *GameData::Colors().Get("faint"); const Color &dim = *GameData::Colors().Get("dim"); const Color &medium = *GameData::Colors().Get("medium"); const Color &bright = *GameData::Colors().Get("bright"); Table table; table.AddColumn(-115, Table::LEFT); table.AddColumn(115, Table::RIGHT); table.SetUnderline(-120, 120); int firstY = -248; table.DrawAt(Point(-130, firstY)); static const string SETTINGS[] = { "Display", ZOOM_FACTOR, VIEW_ZOOM_FACTOR, "Show status overlays", "Highlight player's flagship", "Rotate flagship in HUD", "Show planet labels", "Show mini-map", "", "AI", "Automatic aiming", "Automatic firing", EXPEND_AMMO, TURRET_TRACKING, "", "Performance", "Show CPU / GPU load", "Render motion blur", "Reduce large graphics", "Draw background haze", "Show hyperspace flash", "\n", "Other", "Clickable radar display", "Hide unexplored map regions", REACTIVATE_HELP, "Rehire extra crew when lost", SCROLL_SPEED, "Show escort systems on map", "Warning siren" }; bool isCategory = true; for(const string &setting : SETTINGS) { // Check if this is a category break or column break. if(setting.empty() || setting == "\n") { isCategory = true; if(!setting.empty()) table.DrawAt(Point(130, firstY)); continue; } if(isCategory) { isCategory = false; table.DrawGap(10); table.DrawUnderline(medium); table.Draw(setting, bright); table.Advance(); table.DrawGap(5); continue; } // Record where this setting is displayed, so the user can click on it. prefZones.emplace_back(table.GetCenterPoint(), table.GetRowSize(), setting); // Get the "on / off" text for this setting. bool isOn = Preferences::Has(setting); string text; if(setting == ZOOM_FACTOR) { isOn = true; text = to_string(Screen::Zoom()); } else if(setting == VIEW_ZOOM_FACTOR) { isOn = true; text = to_string(static_cast<int>(100. * Preferences::ViewZoom())); } else if(setting == EXPEND_AMMO) text = Preferences::AmmoUsage(); else if(setting == TURRET_TRACKING) { isOn = true; text = Preferences::Has(FOCUS_PREFERENCE) ? "focused" : "opportunistic"; } else if(setting == REACTIVATE_HELP) { // Check how many help messages have been displayed. const map<string, string> &help = GameData::HelpTemplates(); int shown = 0; for(const auto &it : help) shown += Preferences::Has("help: " + it.first); // Don't count the "basic help" messages in the total. if(shown) text = to_string(shown) + " / " + to_string(help.size() - 2); else { isOn = true; text = "done"; } } else if(setting == SCROLL_SPEED) { isOn = true; text = to_string(Preferences::ScrollSpeed()); } else text = isOn ? "on" : "off"; if(setting == hoverPreference) table.DrawHighlight(back); table.Draw(setting, isOn ? medium : dim); table.Draw(text, isOn ? bright : medium); } }
void PreferencesPanel::DrawControls() { const Color &back = *GameData::Colors().Get("faint"); const Color &dim = *GameData::Colors().Get("dim"); const Color &medium = *GameData::Colors().Get("medium"); const Color &bright = *GameData::Colors().Get("bright"); // Check for conflicts. Color red(.3, 0., 0., .3); Table table; table.AddColumn(-115, Table::LEFT); table.AddColumn(115, Table::RIGHT); table.SetUnderline(-120, 120); int firstY = -248; table.DrawAt(Point(-130, firstY)); static const string CATEGORIES[] = { "Navigation", "Weapons", "Targeting", "Menus", "Fleet" }; const string *category = CATEGORIES; static const Command COMMANDS[] = { Command::NONE, Command::FORWARD, Command::LEFT, Command::RIGHT, Command::BACK, Command::AFTERBURNER, Command::LAND, Command::JUMP, Command::NONE, Command::PRIMARY, Command::SELECT, Command::SECONDARY, Command::CLOAK, Command::NONE, Command::NEAREST, Command::TARGET, Command::HAIL, Command::BOARD, Command::SCAN, Command::NONE, Command::MENU, Command::MAP, Command::INFO, Command::FULLSCREEN, Command::NONE, Command::DEPLOY, Command::FIGHT, Command::GATHER, Command::HOLD, Command::AMMO }; static const Command *BREAK = &COMMANDS[19]; for(const Command &command : COMMANDS) { // The "BREAK" line is where to go to the next column. if(&command == BREAK) table.DrawAt(Point(130, firstY)); if(!command) { table.DrawGap(10); table.DrawUnderline(medium); if(category != end(CATEGORIES)) table.Draw(*category++, bright); else table.Advance(); table.Draw("Key", bright); table.DrawGap(5); } else { int index = zones.size(); // Mark conflicts. bool isConflicted = command.HasConflict(); bool isEditing = (index == editing); if(isConflicted || isEditing) { table.SetHighlight(66, 120); table.DrawHighlight(isEditing ? dim: red); } // Mark the selected row. bool isHovering = (index == hover && !isEditing); if(!isHovering && index == selected) { table.SetHighlight(-120, 64); table.DrawHighlight(back); } // Highlight whichever row the mouse hovers over. table.SetHighlight(-120, 120); if(isHovering) table.DrawHighlight(back); zones.emplace_back(table.GetCenterPoint(), table.GetRowSize(), command); table.Draw(command.Description(), medium); table.Draw(command.KeyName(), isEditing ? bright : medium); } } Table shiftTable; shiftTable.AddColumn(125, Table::RIGHT); shiftTable.SetUnderline(0, 130); shiftTable.DrawAt(Point(-400, 52)); shiftTable.DrawUnderline(medium); shiftTable.Draw("With <shift> key", bright); shiftTable.DrawGap(5); shiftTable.Draw("Select nearest ship", medium); shiftTable.Draw("Select next escort", medium); shiftTable.Draw("Talk to planet", medium); shiftTable.Draw("Board disabled escort", medium); }
void ShipInfoPanel::DrawCargo(const Rectangle &bounds) { Color dim = *GameData::Colors().Get("medium"); Color bright = *GameData::Colors().Get("bright"); Color backColor = *GameData::Colors().Get("faint"); const Ship &ship = **shipIt; // Cargo list. const CargoHold &cargo = (player.Cargo().Used() ? player.Cargo() : ship.Cargo()); Table table; table.AddColumn(0, Table::LEFT); table.AddColumn(WIDTH - 20, Table::RIGHT); table.SetUnderline(-5, WIDTH - 15); table.DrawAt(bounds.TopLeft() + Point(10., 8.)); double endY = bounds.Bottom() - 30. * (cargo.Passengers() != 0); bool hasSpace = (table.GetRowBounds().Bottom() < endY); if((cargo.CommoditiesSize() || cargo.HasOutfits() || cargo.MissionCargoSize()) && hasSpace) { table.Draw("Cargo", bright); table.Advance(); hasSpace = (table.GetRowBounds().Bottom() < endY); } if(cargo.CommoditiesSize() && hasSpace) { for(const auto &it : cargo.Commodities()) { if(!it.second) continue; commodityZones.emplace_back(table.GetCenterPoint(), table.GetRowSize(), it.first); if(it.first == selectedCommodity) table.DrawHighlight(backColor); table.Draw(it.first, dim); table.Draw(to_string(it.second), bright); // Truncate the list if there is not enough space. if(table.GetRowBounds().Bottom() >= endY) { hasSpace = false; break; } } table.DrawGap(10.); } if(cargo.HasOutfits() && hasSpace) { for(const auto &it : cargo.Outfits()) { if(!it.second) continue; plunderZones.emplace_back(table.GetCenterPoint(), table.GetRowSize(), it.first); if(it.first == selectedPlunder) table.DrawHighlight(backColor); // For outfits, show how many of them you have and their total mass. bool isSingular = (it.second == 1 || it.first->Get("installable") < 0.); string name = (isSingular ? it.first->Name() : it.first->PluralName()); if(!isSingular) name += " (" + to_string(it.second) + "x)"; table.Draw(name, dim); double mass = it.first->Mass() * it.second; table.Draw(Format::Number(mass), bright); // Truncate the list if there is not enough space. if(table.GetRowBounds().Bottom() >= endY) { hasSpace = false; break; } } table.DrawGap(10.); } if(cargo.HasMissionCargo() && hasSpace) { for(const auto &it : cargo.MissionCargo()) { // Capitalize the name of the cargo. table.Draw(Format::Capitalize(it.first->Cargo()), dim); table.Draw(to_string(it.second), bright); // Truncate the list if there is not enough space. if(table.GetRowBounds().Bottom() >= endY) break; } table.DrawGap(10.); } if(cargo.Passengers() && endY >= bounds.Top()) { table.DrawAt(Point(bounds.Left(), endY) + Point(10., 8.)); table.Draw("passengers:", dim); table.Draw(to_string(cargo.Passengers()), bright); } }
void ShipInfoPanel::DrawOutfits(const Rectangle &bounds, Rectangle &cargoBounds) { // Check that the specified area is big enough. if(bounds.Width() < WIDTH) return; // Colors to draw with. Color dim = *GameData::Colors().Get("medium"); Color bright = *GameData::Colors().Get("bright"); const Ship &ship = **shipIt; // Table attributes. Table table; table.AddColumn(0, Table::LEFT); table.AddColumn(WIDTH - 20, Table::RIGHT); table.SetUnderline(0, WIDTH - 20); Point start = bounds.TopLeft() + Point(10., 8.); table.DrawAt(start); // Draw the outfits in the same order used in the outfitter. for(const string &category : Outfit::CATEGORIES) { auto it = outfits.find(category); if(it == outfits.end()) continue; // Skip to the next column if there is not space for this category label // plus at least one outfit. if(table.GetRowBounds().Bottom() + 40. > bounds.Bottom()) { start += Point(WIDTH, 0.); if(start.X() + WIDTH - 20 > bounds.Right()) break; table.DrawAt(start); } // Draw the category label. table.Draw(category, bright); table.Advance(); for(const Outfit *outfit : it->second) { // Check if we've gone below the bottom of the bounds. if(table.GetRowBounds().Bottom() > bounds.Bottom()) { start += Point(WIDTH, 0.); if(start.X() + WIDTH - 20 > bounds.Right()) break; table.DrawAt(start); table.Draw(category, bright); table.Advance(); } // Draw the outfit name and count. table.Draw(outfit->Name(), dim); string number = to_string(ship.OutfitCount(outfit)); table.Draw(number, bright); } // Add an extra gap in between categories. table.DrawGap(10.); } // Check if this information spilled over into the cargo column. if(table.GetPoint().X() >= cargoBounds.Left()) { double startY = table.GetRowBounds().Top() - 8.; cargoBounds = Rectangle::WithCorners( Point(cargoBounds.Left(), startY), Point(cargoBounds.Right(), max(startY, cargoBounds.Bottom()))); } }