Logo Search packages:      
Sourcecode: vegastrike version File versions  Download package

basecomputer.cpp

/* 
 * Vega Strike
 * Copyright (C) 2003 Mike Byron
 * 
 * http://vegastrike.sourceforge.net/
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#include "vegastrike.h"
#if defined( _WIN32) && !defined(__CYGWIN__) && !defined( __MINGW32__)
// For WIN32 debugging.
#include <crtdbg.h>
#endif
#include "basecomputer.h"

#include "savegame.h"
#include "universe_util.h"
#include <algorithm>                // For std::sort.
#include <set>
#include "load_mission.h"
#include "cmd/planet_generic.h"
#include "cmd/unit_util.h"
#include "cmd/music.h"
#include "cmd/unit_const_cache.h"
#include "cmd/unit_factory.h"
#include "gui/modaldialog.h"
#include "main_loop.h"              // For QuitNow().
#include "lin_time.h"
// FIXME mbyron -- Hack instead of reading XML.
#include "gui/newbutton.h"
#include "gui/staticdisplay.h"
#include "gui/textinputdisplay.h"
#include "gui/simplepicker.h"
#include "gui/groupcontrol.h"
#include "gui/scroller.h"
#include "unit_xml.h"
#include "gfx/sprite.h"
#include "gfx/aux_texture.h"
#include "audiolib.h"

//for directory thing
#if defined(_WIN32) && !defined(__CYGWIN__)
#include <direct.h>
#include <config.h>
#include <string.h>
#include <windows.h>
#include <stdlib.h>
struct dirent { char d_name[1]; };
#else
#include <unistd.h>
#include <pwd.h>
#include <sys/types.h>
#include <dirent.h>
#endif
#include <sys/stat.h>
//end for directory thing
extern const char * DamagedCategory;

static GFXColor UnsaturatedColor(float r, float g, float b, float a=1.0f) {
  GFXColor ret(r,g,b,a);
  return ret;
}
std::string emergency_downgrade_mode;
extern std::string CurrentSaveGameName;
std::vector<std::string> getWeapFilterVec() {
      std::vector<std::string> weapfiltervec;
      ///// FIXME: the following may change in the future if we ever redo the master part list.
      weapfiltervec.push_back("upgrades/Weapon");
      weapfiltervec.push_back("SubUnits");
      weapfiltervec.push_back("upgrades/Ammunition");
        return weapfiltervec;
}

std::vector<std::string>weapfiltervec=getWeapFilterVec();
bool upgradeNotAddedToCargo(std::string category) {
  for (unsigned int i=0;i<weapfiltervec.size();++i) {
    if (weapfiltervec[i].find(category)==0)
      return true;
  }
  return false;
}
extern vector<unsigned int > base_keyboard_queue;
std::string getDisplayCategory(const Cargo &cargo) {
  std::string::size_type where= cargo.description.find("<");
  if (where!=string::npos) {
    std::string category = cargo.description.substr(where+1);
    where=category.find(">");
    return category.substr(0,where);   
  }
  return cargo.category;
}     

using namespace std;


// The separator used between categories in a category string.
static const char CATEGORY_SEP = '/';
// Tag that says this is a category not an item.
static const char CATEGORY_TAG = (-1);

/*static GFXColor UnsaturatedColor(float r, float g, float b, float a=1.0f) {
  static float Saturation=XMLSupport::parse_float(vs_config->getVariable("graphics","base_saturation","1.0"));
  return GFXColor(r,g,b,a);
  }*/
// Color of an item that there isn't enough money to buy.
// We read this out of the config file (or use a default).
static GFXColor NO_MONEY_COLOR(){
  static GFXColor NO_MONEY_COLOR=getConfigColor("no_money",GFXColor(1,.3,.3,1));
  return  NO_MONEY_COLOR;        // Start out with bogus color.
}
// Color of the text of a category.
static GFXColor CATEGORY_TEXT_COLOR(){
  static GFXColor CTC=getConfigColor("base_category_color",GFXColor(0,.75,0,1));
  return CTC;
}
static GFXColor MISSION_COLOR(){
  static GFXColor MiC=getConfigColor("base_mission_color",GFXColor(.66,.2,0,1));
  return MiC;
}
// Space between mode buttons.
static const float MODE_BUTTON_SPACE = 0.03;
// Default color in CargoColor.
static GFXColor DEFAULT_UPGRADE_COLOR(){
  static GFXColor DuC=getConfigColor("base_upgrade_color",GFXColor(1,1,1,1));
  return DuC;
}

// MOUNT ENTRY COLORS
// Mount point that cannot be selected.
static GFXColor MOUNT_POINT_NO_SELECT() {
    return GFXColor(1,.7,.7);
}
// Empty mount point.
static GFXColor MOUNT_POINT_EMPTY () {
    return GFXColor(.2,1,.2);
}
// Mount point that contains weapon.
static GFXColor MOUNT_POINT_FULL() {
    return GFXColor(1,1,0);
}

// Some mission declarations.
// These should probably be in a header file somewhere.
static const char* const MISSION_SCRIPTS_LABEL = "mission_scripts";
static const char* const MISSION_NAMES_LABEL = "mission_names";
static const char* const MISSION_DESC_LABEL = "mission_descriptions";
extern unsigned int getSaveStringLength (int whichcp, string key);
extern unsigned int eraseSaveString (int whichcp, string key, unsigned int num);
extern std::string getSaveString (int whichcp, string key, unsigned int num);
extern void putSaveString (int whichcp, string key, unsigned int num,std::string s);

// Some new declarations.
// These should probably be in a header file somewhere.
static const char* const NEWS_NAME_LABEL = "news";

// Some upgrade declarations.
// These should probably be in a header file somewhere.
extern void RecomputeUnitUpgrades (Unit * un);
extern const Unit* makeFinalBlankUpgrade(string name, int faction);
extern int GetModeFromName(const char *);  // 1=add, 2=mult, 0=neither.
extern Cargo* GetMasterPartList(const char *input_buffer);
extern Unit& GetUnitMasterPartList();
extern void ClearDowngradeMap();
extern std::set<std::string> GetListOfDowngrades();
static const string LOAD_FAILED = "LOAD_FAILED";

// Some ship dealer declarations.
// These should probably be in a header file somewhere.
extern void SwitchUnits(Unit* ol, Unit* nw);
extern void TerminateCurrentBase(void);
extern void CurrentBaseUnitSet(Unit * un);
// For ships stats.
extern string MakeUnitXMLPretty(std::string, Unit*);
extern float totalShieldEnergyCapacitance (const Shield & shield); 
// For Options menu.
extern void RespawnNow(Cockpit* cockpit);


//headers for functions used internally
//add to text a nicely-formated description of the unit and its subunits
void showUnitStats(Unit * playerUnit,string &text,int subunitlevel,int mode, Cargo &item);
//build the previous description for a ship purchase item
string buildShipDescription(Cargo &item,string &descriptiontexture);
//build the previous description from a cargo purchase item
string buildCargoDescription(Cargo &item);
//put in buffer a pretty prepresentation of the POSITIVE float f (ie 4,732.17)
void prettyPrintFloat(char * buffer,float f, int digitsBefore, int digitsAfter);
string buildUpgradeDescription(Cargo &item);
int basecargoassets(Unit* base,string cargoname);

// "Basic Repair" item that is added to Buy UPGRADE mode.
const string BASIC_REPAIR_NAME = "Basic Repair";
const GFXColor BASIC_REPAIR_TEXT_COLOR() {
    return GFXColor(0,1,1);
}
const string BASIC_REPAIR_DESC = "Hire starship mechanics to examine and assess any wear and tear on your craft. They will replace any damaged components on your vessel with the standard components of the vessel you initially purchased.  Further upgrades above and beyond the original will not be replaced free of charge.  The total assessment and repair cost applies if any components are damaged or need servicing (fuel, wear and tear on jump drive, etc...) If such components are damaged you may save money by repairing them on your own.";
// Repair price is a config variable.

// Info about each mode.
struct ModeInfo {
      string title;
      string button;
      string command;
      string groupId;
      ModeInfo(string t="", string b="", string c="", string g="")
            : title(t), button(b), command(c), groupId(g) {
      }
};
static const ModeInfo modeInfo[] = {
    ModeInfo ( "Cargo Dealer  ", "Cargo", "CargoMode", "CargoGroup" ),
    ModeInfo ( "Ship Upgrades  ", "Upgrades", "UpgradeMode", "UpgradeGroup" ),
    ModeInfo ( "New Ships  ", "Ships", "ShipDealerMode", "ShipDealerGroup" ),
    ModeInfo ( "Missions BBS  ", "Missions", "MissionsMode", "MissionsGroup" ),
    ModeInfo ( "GNN News  ", "News", "NewsMode", "NewsGroup" ),
    ModeInfo ( "Info/Stats  ", "Info", "InfoMode", "InfoGroup" ),
      ModeInfo ( "Load / Save ", "LoadSave", "LoadSaveMode", "LoadSaveGroup" )
};

 bool BaseComputer::actionDone(const EventCommandId& command, Control* control) {
      AUDStopAllSounds();
      window()->close();
      return true;
}
// Dispatch table for commands.
// Make an entry here for each command you want to handle.
// WARNING:  The order of this table is important.  There are multiple entries for
//  some commands. Basically, you can make an entry for a particular control, and then
//  later have an entry with an empty control id to cover the other cases.
const BaseComputer::WctlTableEntry BaseComputer::WctlCommandTable[] = {
    BaseComputer::WctlTableEntry ( "Picker::NewSelection", "NewsPicker", &BaseComputer::newsPickerChangedSelection ),
    BaseComputer::WctlTableEntry ( "Picker::NewSelection", "LoadSavePicker", &BaseComputer::loadSavePickerChangedSelection ), 
    BaseComputer::WctlTableEntry ( "Picker::NewSelection", "", &BaseComputer::pickerChangedSelection ),
    BaseComputer::WctlTableEntry ( modeInfo[CARGO].command, "", &BaseComputer::changeToCargoMode ),
    BaseComputer::WctlTableEntry ( modeInfo[UPGRADE].command, "", &BaseComputer::changeToUpgradeMode ),
    BaseComputer::WctlTableEntry ( modeInfo[SHIP_DEALER].command, "", &BaseComputer::changeToShipDealerMode ),
    BaseComputer::WctlTableEntry ( modeInfo[NEWS].command, "", &BaseComputer::changeToNewsMode ),
    BaseComputer::WctlTableEntry ( modeInfo[MISSIONS].command, "", &BaseComputer::changeToMissionsMode ),
    BaseComputer::WctlTableEntry ( modeInfo[INFO].command, "", &BaseComputer::changeToInfoMode ),
      BaseComputer::WctlTableEntry ( modeInfo[LOADSAVE].command, "", &BaseComputer::changeToLoadSaveMode ),
    BaseComputer::WctlTableEntry ( "BuyCargo", "", &BaseComputer::buyCargo ),
    BaseComputer::WctlTableEntry ( "Buy10Cargo", "", &BaseComputer::buy10Cargo ),
    BaseComputer::WctlTableEntry ( "BuyAllCargo", "", &BaseComputer::buyAllCargo ),
    BaseComputer::WctlTableEntry ( "SellCargo", "", &BaseComputer::sellCargo ),
    BaseComputer::WctlTableEntry ( "Sell10Cargo", "", &BaseComputer::sell10Cargo ),
    BaseComputer::WctlTableEntry ( "SellAllCargo", "", &BaseComputer::sellAllCargo ),
    BaseComputer::WctlTableEntry ( "BuyUpgrade", "", &BaseComputer::buyUpgrade ),
    BaseComputer::WctlTableEntry ( "SellUpgrade", "", &BaseComputer::sellUpgrade ),
    BaseComputer::WctlTableEntry ( "FixUpgrade", "", &BaseComputer::fixUpgrade ),
    BaseComputer::WctlTableEntry ( "BuyShip", "", &BaseComputer::buyShip ),
    BaseComputer::WctlTableEntry ( "SellShip", "", &BaseComputer::sellShip ),
    BaseComputer::WctlTableEntry ( "AcceptMission", "", &BaseComputer::acceptMission ),
    BaseComputer::WctlTableEntry ( "ShowPlayerInfo", "", &BaseComputer::showPlayerInfo ),
    BaseComputer::WctlTableEntry ( "ShowShipStats", "", &BaseComputer::showShipStats ),
    BaseComputer::WctlTableEntry ( "ShowOptionsMenu", "", &BaseComputer::changeToLoadSaveMode ),
    BaseComputer::WctlTableEntry ( "Quit", "", &BaseComputer::actionQuitGame ),
    BaseComputer::WctlTableEntry ( "Load", "", &BaseComputer::actionLoadGame ),
    BaseComputer::WctlTableEntry ( "New", "", &BaseComputer::actionNewGame ),
    BaseComputer::WctlTableEntry ( "Save", "", &BaseComputer::actionSaveGame ),           
      BaseComputer::WctlTableEntry ( "DoneComputer", "", &BaseComputer::actionDone ),

    BaseComputer::WctlTableEntry ( "", "", NULL )
};

// Process a command from the window.
// This just dispatches to a handler.
bool BaseComputer::processWindowCommand(const EventCommandId& command, Control* control) {

    // Iterate through the dispatch table.
    for(const WctlTableEntry *p = &WctlCommandTable[0]; p->function ; p++) {
        if(p->command == command) {
            if(p->controlId.size() == 0 || p->controlId == control->id()) {
                // Found a handler for the command.
                return( (this->*(p->function))(command, control) );
            }
        }
    }

    // Let the base class have a try at the command first.
    if(WindowController::processWindowCommand(command, control)) {
        return true;
    }

    // Didn't find a handler.
    return false;
};


// Take underscores out of a string and capitalize letters after spaces.
static std::string beautify(const string &input) {
      std::string result;

      bool wordStart = true;
    for(std::string::const_iterator i=input.begin(); i!=input.end(); i++) {
            if(*i == '_') {
                  // Turn this into a space, and make sure next letter is capitalized.
                  result += ' ';
                  wordStart = true;
            } else if(wordStart) {
                  // Start or a word.  Capitalize the character, and turn off start of word.
                  result += toupper(*i);
                  wordStart = false;
            } else {
                  // Normal character in middle of word.
                  result += *i;
            }
    }
    return result;
}

// The "used" value of an item.
static double usedValue(double originalValue) {
  return .5*originalValue;
}

static float RepairPrice(float operational, float price) {
  return .5*price*(1-operational)*g_game.difficulty;
}
static float basicRepairPrice(void) {
    static const float price = XMLSupport::parse_float(vs_config->getVariable("physics","repair_price","5000"));
    return price*g_game.difficulty;
}

static float SellPrice(float operational, float price) {
  return usedValue(price)-RepairPrice(operational,price);
}
extern const Unit * makeTemplateUpgrade (string name, int faction);

// Ported from old code.  Not sure what it does.
const Unit* getUnitFromUpgradeName(const string& upgradeName, int myUnitFaction = 0);

// Lowerifies a string.
static std::string &tolower(std::string &loweritem) {
      for (unsigned int i=0;i<loweritem.size();i++) {
            loweritem[i]=tolower(loweritem[i]);
      }
      return loweritem;
}

// Takes in a category of an upgrade or cargo and returns true if it is any type of mountable weapon.
static bool isWeapon (std::string name) {
      if (name.find("Weapon")!=std::string::npos) {
            return true;
      }
      if (name.find("SubUnit")!=std::string::npos) {
            return true;
      }
      if (name.find("Ammunition")!=std::string::npos) {
            return true;
      }
      return false;
}
float PercentOperational (Unit * un, std::string name, std::string category="upgrades/") {
  if (category.find(DamagedCategory)==0) {
    return 0.0f;
  }else if (isWeapon(category)) {
    const Unit * upgrade=getUnitFromUpgradeName(name,un->faction); 
    if (upgrade->GetNumMounts()) {
      const Mount * mnt = &upgrade->mounts[0];
      unsigned int nummounts=un->GetNumMounts();
      for (unsigned int i=0;i<nummounts;++i) {
        if (mnt->type->weapon_name==un->mounts[i].type->weapon_name) {
          if (un->mounts[i].status==Mount::DESTROYED)
            return 0.0;
          if (un->mounts[i].functionality<1.0f){
            return un->mounts[i].functionality;
          }
        }
      }
    }
  }else if (name.find("add_")!=0&&name.find("mult_")!=0) {
    const Unit * upgrade=getUnitFromUpgradeName(name,un->faction);    
    double percent=0;
    if (un->canUpgrade(upgrade,-1,-1,0,true,percent,makeTemplateUpgrade(un->name,un->faction),false)) {
      if (percent)
        return percent;
      else return .5;
    }else if (percent>0) return percent;
  }
  return 1.0;
}

// CONSTRUCTOR.
BaseComputer::BaseComputer(Unit* player, Unit* base, const std::vector<DisplayMode>& modes)
    : 
    m_displayModes(modes),
    m_player(player), 
    m_base(base), 
    m_currentDisplay(NULL_DISPLAY),
    m_selectedList(NULL),
    m_playingMuzak(false)
{
    // Make sure we get this color loaded.

    // Initialize mode group controls array.
    for(int i=0; i<DISPLAY_MODE_COUNT; i++) {
        m_modeGroups[i] = NULL;
    }
}

// Destructor.
BaseComputer::~BaseComputer(void) {
    m_player.SetUnit(NULL);
    m_base.SetUnit(NULL);

    // Delete any group controls that the window doesn't "own".
    for(int i=0; i<DISPLAY_MODE_COUNT; i++) {
        if(m_modeGroups[i] != NULL) {
            delete m_modeGroups[i];
        }
    }

    // If we are playing muzak, stop it.
    if(m_playingMuzak) {
        muzak->Skip();
    }
}

GFXColor BaseComputer::getColorForGroup(std::string id) {
      static bool use_faction_background=XMLSupport::parse_bool(vs_config->getVariable("graphics","use_faction_gui_background_color","true"));
        static float faction_color_darkness=XMLSupport::parse_float(vs_config->getVariable("graphics","base_faction_color_darkness",".75"));
      if (use_faction_background) {
            int fac=m_base.GetUnit()->faction;
            if (FactionUtil::GetFactionName(fac)=="neutral") {
                  fac=FactionUtil::GetFactionIndex(_Universe->getGalaxyProperty(UniverseUtil::getSystemFile(),"faction"));
            }
            const float *stuff=FactionUtil::GetSparkColor(fac);

            return GFXColor(stuff[0]*faction_color_darkness,stuff[1]*faction_color_darkness,stuff[2]*faction_color_darkness);
      } else {
            if (id=="CargoGroup") {
                  return GFXColor(0,0,faction_color_darkness);
            } else if (id=="NewsGroup") {
                  return GFXColor(faction_color_darkness,0,faction_color_darkness);
            } else if (id=="UpgradeGroup") {
                  return GFXColor(0,faction_color_darkness,0);
            } else if (id=="InfoGroup") {
                  return GFXColor(0,faction_color_darkness,faction_color_darkness);
            } else if (id=="MissionsGroup") {
                  return GFXColor(faction_color_darkness,0,0);
            } else if (id=="ShipDealerGroup") {
                  return GFXColor(faction_color_darkness,faction_color_darkness,0);
            } else if (id=="LoadSaveGroup") {
                  return GFXColor(0,faction_color_darkness,faction_color_darkness);
            } else {
                  return GFXColor(0,0,0);
            }
      }
}

// Hack that constructs controls in code.
void BaseComputer::constructControls(void) {

    // Base info title.
    StaticDisplay* baseTitle = new StaticDisplay;
    baseTitle->setRect( Rect(-.96, .76, 1.9, .08) );
    baseTitle->setText("ERROR");
    static GFXColor baseNameColor=getConfigColor("base_name_color",GFXColor(.1,.8,.1));
    baseTitle->setTextColor(baseNameColor);
    baseTitle->setColor(GUI_CLEAR);
    baseTitle->setFont( Font(.07, 1.75) );
    baseTitle->setId("BaseInfoTitle");
    // Put it on the window.
    window()->addControl(baseTitle);

    // Player info title.
    StaticDisplay* playerTitle = new StaticDisplay;
    static GFXColor basePlayerColor=getConfigColor("base_player_color",GFXColor(.7,.7,.9));
    playerTitle->setRect( Rect(-.96, .69, 1.9, .07) );
    playerTitle->setTextColor(basePlayerColor);
    playerTitle->setColor(GUI_CLEAR);
    playerTitle->setFont( Font(.06, BOLD_STROKE) );
    playerTitle->setId("PlayerInfoTitle");
    // Put it on the window.
    window()->addControl(playerTitle);
    static GFXColor saveLoadColor=getConfigColor("base_save_load_color",GFXColor(.75,0,0));
    // Options button.
    NewButton* options = new NewButton;
    options->setRect( Rect(.64, .85, .32, .1) );
    options->setLabel("Save/Load");
    options->setCommand("ShowOptionsMenu");
    options->setColor( UnsaturatedColor(saveLoadColor.r,saveLoadColor.g,saveLoadColor.b,.25) );
    options->setTextColor( GUI_OPAQUE_WHITE() );
    options->setDownColor( UnsaturatedColor(saveLoadColor.r,saveLoadColor.g,saveLoadColor.b,.6) );
    options->setDownTextColor( GUI_OPAQUE_BLACK() );
    options->setHighlightColor( GFXColor(0,0,1,.4) );
    options->setFont(Font(.08));
    // Put the button on the window.
    window()->addControl(options);
    static GFXColor doneColor=getConfigColor("base_done_color",GFXColor(.75,0,0));
    // Done button.
    NewButton* done = new NewButton;
    done->setRect( Rect(.74, .71, .22, .1) );
    done->setLabel("Done");
    done->setCommand("DoneComputer");
    done->setColor( UnsaturatedColor(doneColor.r,doneColor.g,doneColor.b,.25) );
    done->setTextColor( GUI_OPAQUE_WHITE() );
    done->setDownColor( UnsaturatedColor(doneColor.r,doneColor.g,doneColor.b,.6) );
    done->setDownTextColor( GUI_OPAQUE_BLACK() );
    done->setHighlightColor( GFXColor(0,0,1,.4) );
    done->setFont(Font(.08, BOLD_STROKE));
    window()->addControl(done);

    // Mode button.
    NewButton* mode = new NewButton;
    static GFXColor mode_color=getConfigColor("base_mode_color",GFXColor(0,.5,0));
    mode->setRect( Rect(-.96, .86, .24, .09) );
    mode->setLabel("ERROR");
    mode->setColor( GFXColor(mode_color.r,mode_color.g,mode_color.b,.25) );
    mode->setTextColor( GUI_OPAQUE_WHITE() );
    mode->setDownColor( GFXColor(mode_color.r,mode_color.g,mode_color.b,.5) );
    mode->setDownTextColor( GUI_OPAQUE_BLACK() );
    mode->setHighlightColor( GFXColor(mode_color.r,mode_color.g,mode_color.b,.4) );
    mode->setFont(Font(.07,BOLD_STROKE));
    mode->setId("ModeButton");
    // Put the button on the window.
    window()->addControl(mode);

    {
        // CARGO group control.
        GroupControl* cargoGroup = new GroupControl;
        cargoGroup->setId("CargoGroup");
        window()->addControl(cargoGroup);
            GFXColor color=getColorForGroup("CargoGroup");

        // Seller text display.
        StaticDisplay* sellLabel = new StaticDisplay;
        sellLabel->setRect( Rect(-.96, .56, .81, .1) );
        sellLabel->setText("Seller");
        sellLabel->setTextColor(GUI_OPAQUE_WHITE());
        sellLabel->setColor(GUI_CLEAR);
        sellLabel->setFont( Font(.08, BOLD_STROKE) );
        sellLabel->setJustification(CENTER_JUSTIFY);
        cargoGroup->addChild(sellLabel);

        // Player inventory text display.
        StaticDisplay* inv = new StaticDisplay;
        *inv = *sellLabel;
        inv->setRect( Rect(.15, .56, .81, .1) );
        inv->setText("Inventory");
        cargoGroup->addChild(inv);

        // Total price text display.
        StaticDisplay* totalPrice = new StaticDisplay;
        totalPrice->setRect( Rect(-.2, .56, .4, .07) );
        totalPrice->setTextColor(GUI_OPAQUE_WHITE());
        totalPrice->setColor(GUI_CLEAR);
        totalPrice->setFont( Font(.06) );
        totalPrice->setJustification(CENTER_JUSTIFY);
        totalPrice->setId("TotalPrice");
        cargoGroup->addChild(totalPrice);

        // "Max" text display.
        StaticDisplay* maxForPlayer = new StaticDisplay;
        maxForPlayer->setRect( Rect(-.14, .49, .28, .07) );
        maxForPlayer->setTextColor(GUI_OPAQUE_WHITE());
        maxForPlayer->setColor(GUI_CLEAR);
        maxForPlayer->setFont( Font(.06) );
        maxForPlayer->setJustification(CENTER_JUSTIFY);
        maxForPlayer->setId("MaxQuantity");
        cargoGroup->addChild(maxForPlayer);

        // Scroller for seller.
        Scroller* sellerScroller = new Scroller;
        sellerScroller->setRect( Rect(-.20, -.4, .05, .95) );
        sellerScroller->setColor( UnsaturatedColor(color.r,color.g,color.b,.1) );
        sellerScroller->setThumbColor( UnsaturatedColor(color.r*.4,color.g*.4,color.b*.4), GUI_OPAQUE_WHITE() );
        sellerScroller->setButtonColor( UnsaturatedColor(color.r*.4,color.g*.4,color.b*.4) );
        sellerScroller->setTextColor(GUI_OPAQUE_WHITE());
            sellerScroller->setOutlineColor(GUI_OPAQUE_MEDIUM_GRAY());

        // Seller picker.
        SimplePicker* sellpick = new SimplePicker;
        sellpick->setRect( Rect(-.96, -.4, .76, .95) );
        sellpick->setColor( GFXColor(color.r,color.g,color.b,.1) );
            sellpick->setOutlineColor(GUI_OPAQUE_MEDIUM_GRAY());
        sellpick->setTextColor(GUI_OPAQUE_WHITE());
        sellpick->setSelectionColor(UnsaturatedColor(0,.6,0,.8));
        sellpick->setHighlightColor(UnsaturatedColor(0,.6,0,.35));
        sellpick->setHighlightTextColor(GUI_OPAQUE_WHITE());
        sellpick->setFont( Font(.07) );
        sellpick->setTextMargins(Size(0.02,0.01));
        sellpick->setId("BaseCargo");
        sellpick->setScroller(sellerScroller);
        cargoGroup->addChild(sellpick);

            cargoGroup->addChild(sellerScroller);           // Want this "over" the picker.

        // Scroller for inventory.
        Scroller* invScroller = new Scroller;
        invScroller->setRect( Rect(.91, -.4, .05, .95) );
        invScroller->setColor( UnsaturatedColor(color.r,color.g,color.b,.1) );
        invScroller->setThumbColor( UnsaturatedColor(color.r*.4,color.g*.4,color.b*.4), GUI_OPAQUE_WHITE() );
        invScroller->setButtonColor( UnsaturatedColor(color.r*.4,color.g*.4,color.b*.4) );
        invScroller->setTextColor(GUI_OPAQUE_WHITE());
            invScroller->setOutlineColor(GUI_OPAQUE_MEDIUM_GRAY());

        // Inventory picker.
        SimplePicker* ipick = new SimplePicker;
        ipick->setRect( Rect(.15, -.4, .76, .95) );
        ipick->setColor( UnsaturatedColor(color.r,color.g,color.b,.1) );
            ipick->setOutlineColor(GUI_OPAQUE_MEDIUM_GRAY());
        ipick->setTextColor(GUI_OPAQUE_WHITE());
        ipick->setFont( Font(.07) );
        ipick->setTextMargins(Size(0.02,0.01));
        ipick->setSelectionColor(UnsaturatedColor(0,.6,0,.8));
        ipick->setHighlightColor(UnsaturatedColor(0,.6,0,.35));
        ipick->setHighlightTextColor(GUI_OPAQUE_WHITE());
        ipick->setId("PlayerCargo");
        ipick->setScroller(invScroller);
        cargoGroup->addChild(ipick);

        cargoGroup->addChild(invScroller);            // Want this "over" the picker.

        // Buy button.
        NewButton* buy = new NewButton;
        buy->setRect( Rect(-.11, .3, .22, .13) );
        buy->setColor( GFXColor(0,1,1,.1) );
        buy->setTextColor(GUI_OPAQUE_WHITE());
            buy->setDownColor( GFXColor(0,1,1,.4) );
            buy->setDownTextColor( GFXColor(.2,.2,.2) );
            buy->setVariableBorderCycleTime(1.0);
            buy->setBorderColor( GFXColor(.2,.2,.2) );
            buy->setEndBorderColor( GFXColor(.4,.4,.4) );
            buy->setShadowWidth(2.0);
        buy->setFont(Font(.1, BOLD_STROKE));
        buy->setId("CommitAll");
        cargoGroup->addChild(buy);

        // "Buy 10" button.
        NewButton* buy10 = new NewButton;
        buy10->setRect( Rect(-.11, .1, .22, .1) );
        buy10->setColor( GFXColor(0,1,1,.1) );
        buy10->setTextColor(GUI_OPAQUE_WHITE());
            buy10->setDownColor( GFXColor(0,1,1,.4) );
            buy10->setDownTextColor( GFXColor(.2,.2,.2) );
            buy10->setVariableBorderCycleTime(1.0);
            buy10->setBorderColor( GFXColor(.2,.2,.2) );
            buy10->setEndBorderColor( GFXColor(.4,.4,.4) );
            buy10->setShadowWidth(2.0);
        buy10->setFont(Font(.08, BOLD_STROKE));
        buy10->setId("Commit10");
        cargoGroup->addChild(buy10);

        // "Buy 1" button.
        NewButton* buy1 = new NewButton;
        buy1->setRect( Rect(-.11, -.1, .22, .1) );
        buy1->setColor( GFXColor(0,1,1,.1) );
        buy1->setTextColor(GUI_OPAQUE_WHITE());
            buy1->setDownColor( GFXColor(0,1,1,.4) );
            buy1->setDownTextColor( GFXColor(.2,.2,.2) );
            buy1->setVariableBorderCycleTime(1.0);
            buy1->setBorderColor( GFXColor(.2,.2,.2) );
            buy1->setEndBorderColor( GFXColor(.4,.4,.4) );
            buy1->setShadowWidth(2.0);
        buy1->setFont(Font(.08, BOLD_STROKE));
        buy1->setId("Commit");
        cargoGroup->addChild(buy1);

        // Scroller for description.
        Scroller* descScroller = new Scroller;
        descScroller->setRect( Rect(.91, -.95, .05, .5) );
        descScroller->setColor( UnsaturatedColor(color.r,color.g,color.b,.1) );
        descScroller->setThumbColor( UnsaturatedColor(color.r*.4,color.g*.4,color.b*.4), GUI_OPAQUE_WHITE() );
        descScroller->setButtonColor( UnsaturatedColor(color.r*.4,color.g*.4,color.b*.4) );
        descScroller->setTextColor(GUI_OPAQUE_WHITE());
            descScroller->setOutlineColor(GUI_OPAQUE_MEDIUM_GRAY());

        // Description box.
        StaticDisplay* ms = new StaticDisplay;
        StaticImageDisplay * picture = new StaticImageDisplay;
        picture->setRect(Rect(-.96,-.45,.46*.75,-.47));
        picture->setTexture("blackclear.png");
        picture->setId("DescriptionImage");
        ms->setRect( Rect(-.6, -.95, 1.51, .5) );
        ms->setColor( GFXColor(color.r,color.g,color.b,.1) );
            ms->setOutlineColor(GUI_OPAQUE_MEDIUM_GRAY());
        ms->setFont( Font(.06) );
        ms->setMultiLine(true);
        ms->setTextColor(GUI_OPAQUE_WHITE());
        ms->setTextMargins(Size(.02,.01));
        ms->setId("Description");
        ms->setScroller(descScroller);
        cargoGroup->addChild(ms);

        cargoGroup->addChild(descScroller);           // Want this "over" the description.
        cargoGroup->addChild(picture);
    }

    {
        // UPGRADE group control.
        GroupControl* upgradeGroup = new GroupControl;
        upgradeGroup->setId("UpgradeGroup");
        window()->addControl(upgradeGroup);
            GFXColor color=getColorForGroup("UpgradeGroup");

        // Seller text display.
        StaticDisplay* sellLabel = new StaticDisplay;
        sellLabel->setRect( Rect(-.96, .55, .81, .1) );
        sellLabel->setText("Available Upgrades");
        sellLabel->setTextColor(GUI_OPAQUE_WHITE());
        sellLabel->setColor(GUI_CLEAR);
        sellLabel->setFont( Font(.07, BOLD_STROKE) );
        sellLabel->setJustification(CENTER_JUSTIFY);
        upgradeGroup->addChild(sellLabel);

        // Player inventory text display.
        StaticDisplay* inv = new StaticDisplay;
        *inv = *sellLabel;
        inv->setRect( Rect(.15, .55, .81, .1) );
        inv->setText("Improvements To Sell");
        upgradeGroup->addChild(inv);

        // Scroller for seller.
        Scroller* sellerScroller = new Scroller;
        sellerScroller->setRect( Rect(-.20, -.4, .05, .95) );
        sellerScroller->setColor( UnsaturatedColor(color.r,color.g,color.b,.1) );
        sellerScroller->setThumbColor( UnsaturatedColor(color.r*.4,color.g*.4,color.b*.4), GUI_OPAQUE_WHITE() );
        sellerScroller->setButtonColor( UnsaturatedColor(color.r*.4,color.g*.4,color.b*.4) );
        sellerScroller->setTextColor(GUI_OPAQUE_WHITE());
            sellerScroller->setOutlineColor(GUI_OPAQUE_MEDIUM_GRAY());

        // Seller picker.
        SimplePicker* sellpick = new SimplePicker;
        sellpick->setRect( Rect(-.96, -.4, .76, .95) );
        sellpick->setColor( UnsaturatedColor(color.r,color.g,color.b,.1) );
            sellpick->setOutlineColor(GUI_OPAQUE_MEDIUM_GRAY());
        sellpick->setTextColor(GUI_OPAQUE_WHITE());
        sellpick->setFont( Font(.07) );
        sellpick->setTextMargins(Size(0.02,0.01));
        sellpick->setSelectionColor(UnsaturatedColor(0,.6,0,.8));
        sellpick->setHighlightColor(UnsaturatedColor(0,.6,0,.35));
        sellpick->setHighlightTextColor(GUI_OPAQUE_WHITE());
        sellpick->setId("BaseUpgrades");
        sellpick->setScroller(sellerScroller);
        upgradeGroup->addChild(sellpick);

        upgradeGroup->addChild(sellerScroller);       // Want this "over" the picker.

        // Scroller for inventory.
        Scroller* invScroller = new Scroller;
        invScroller->setRect( Rect(.91, -.4, .05, .95) );
        invScroller->setColor( UnsaturatedColor(color.r,color.g,color.b,.1) );
        invScroller->setThumbColor( UnsaturatedColor(color.r*.4,color.g*.4,color.b*.4), GUI_OPAQUE_WHITE() );
        invScroller->setButtonColor( UnsaturatedColor(color.r*.4,color.g*.4,color.b*.4) );
        invScroller->setTextColor(GUI_OPAQUE_WHITE());
            invScroller->setOutlineColor(GUI_OPAQUE_MEDIUM_GRAY());

        // Inventory picker.
        SimplePicker* ipick = new SimplePicker;
        ipick->setRect( Rect(.15, -.4, .76, .95) );
        ipick->setColor( GFXColor(color.r,color.g,color.b,.1) );
            ipick->setOutlineColor(GUI_OPAQUE_MEDIUM_GRAY());
        ipick->setTextColor(GUI_OPAQUE_WHITE());
        ipick->setSelectionColor(UnsaturatedColor(0,.6,0,.8));
        ipick->setHighlightColor(UnsaturatedColor(0,.6,0,.35));
        ipick->setHighlightTextColor(GUI_OPAQUE_WHITE());
        ipick->setFont( Font(.07) );
        ipick->setTextMargins(Size(0.02,0.01));
        ipick->setId("PlayerUpgrades");
        ipick->setScroller(invScroller);
        upgradeGroup->addChild(ipick);

        upgradeGroup->addChild(invScroller);          // Want this "over" picker.

        // Buy button.
        NewButton* buy = new NewButton;
        buy->setRect( Rect(-.11, .2, .22, .12) );
        buy->setColor( GFXColor(0,1,1,.1) );
        buy->setTextColor(GUI_OPAQUE_WHITE());
            buy->setDownColor( GFXColor(0,1,1,.4) );
            buy->setDownTextColor( GFXColor(.2,.2,.2) );
            buy->setVariableBorderCycleTime(1.0);
            buy->setBorderColor( GFXColor(.2,.2,.2) );
            buy->setEndBorderColor( GFXColor(.4,.4,.4) );
            buy->setShadowWidth(2.0);
        buy->setFont(Font(.1, BOLD_STROKE));
        buy->setId("Commit");
        upgradeGroup->addChild(buy);


        // Fix button.
        NewButton* fix = new NewButton;
        fix->setRect( Rect(-.11, .0, .22, .12) );
        fix->setColor( GFXColor(0,1,1,.1) );
        fix->setTextColor(GUI_OPAQUE_WHITE());
            fix->setDownColor( GFXColor(0,1,1,.4) );
            fix->setDownTextColor( GFXColor(.2,.2,.2) );
            fix->setVariableBorderCycleTime(1.0);
            fix->setBorderColor( GFXColor(.2,.2,.2) );
            fix->setEndBorderColor( GFXColor(.4,.4,.4) );
            fix->setShadowWidth(2.0);
        fix->setFont(Font(.1, BOLD_STROKE));
        fix->setId("CommitFix");
        upgradeGroup->addChild(fix);


        // Scroller for description.
        Scroller* descScroller = new Scroller;
        descScroller->setRect( Rect(.91, -.95, .05, .5) );
        descScroller->setColor( UnsaturatedColor(color.r,color.g,color.b,.1) );
        descScroller->setThumbColor( UnsaturatedColor(color.r*.4,color.g*.4,color.b*.4), GUI_OPAQUE_WHITE() );
        descScroller->setButtonColor( UnsaturatedColor(color.r*.4,color.g*.4,color.b*.4) );
        descScroller->setTextColor(GUI_OPAQUE_WHITE());
            descScroller->setOutlineColor(GUI_OPAQUE_MEDIUM_GRAY());

        // Description box.
        StaticDisplay* ms = new StaticDisplay;
        StaticImageDisplay * picture = new StaticImageDisplay;
        picture->setRect(Rect(-.96,-.45,.46*.75,-.47));
        picture->setTexture("blackclear.png");
        picture->setId("DescriptionImage");
        ms->setRect( Rect(-.6, -.95, 1.51, .5) );

        ms->setColor( GFXColor(color.r,color.g,color.b,.1) );
            ms->setOutlineColor(GUI_OPAQUE_MEDIUM_GRAY());
        ms->setFont( Font(.06) );
        ms->setMultiLine(true);
        ms->setTextColor(GUI_OPAQUE_WHITE());
        ms->setTextMargins(Size(.02,.01));
        ms->setId("Description");
        ms->setScroller(descScroller);
        upgradeGroup->addChild(ms);

        upgradeGroup->addChild(descScroller);   // Want this "over" description box.
        upgradeGroup->addChild(picture);
    }

    {
        // NEWS group control.
        GroupControl* newsGroup = new GroupControl;
        newsGroup->setId("NewsGroup");
        window()->addControl(newsGroup);
            GFXColor color=getColorForGroup("NewsGroup");

        // Scroller for picker.
        Scroller* pickScroller = new Scroller;
        pickScroller->setRect( Rect(.91, 0, .05, .65) );
        pickScroller->setColor( UnsaturatedColor(color.r,color.g,color.b,.1) );
        pickScroller->setThumbColor( UnsaturatedColor(color.r*.4,color.g*.4,color.b*.4), GUI_OPAQUE_WHITE() );
        pickScroller->setButtonColor( UnsaturatedColor(color.r*.4,color.g*.4,color.b*.4) );
        pickScroller->setTextColor(GUI_OPAQUE_WHITE());
            pickScroller->setOutlineColor(GUI_OPAQUE_MEDIUM_GRAY());

        // News picker.
        SimplePicker* pick = new SimplePicker;
        pick->setRect( Rect(-.96, 0, 1.87, .65) );
        pick->setColor( GFXColor(color.r,color.g,color.b,.1) );
            pick->setOutlineColor(GUI_OPAQUE_MEDIUM_GRAY());
        pick->setTextColor(GUI_OPAQUE_WHITE());
        pick->setSelectionColor(UnsaturatedColor(0,.6,0,.8));
        pick->setHighlightColor(UnsaturatedColor(0,.6,0,.35));
        pick->setHighlightTextColor(GUI_OPAQUE_WHITE());
        pick->setFont( Font(.07) );
        pick->setTextMargins(Size(0.02,0.01));
        pick->setId("NewsPicker");
        pick->setScroller(pickScroller);
        newsGroup->addChild(pick);

        newsGroup->addChild(pickScroller);            // Want scroller "over" picker.

        // Scroller for description.
        Scroller* descScroller = new Scroller;
        descScroller->setRect( Rect(.91, -.95, .05, .90) );
        descScroller->setColor( UnsaturatedColor(color.r,color.g,color.b,.1) );
        descScroller->setThumbColor( UnsaturatedColor(color.r*.4,color.g*.4,color.b*.4), GUI_OPAQUE_WHITE() );
        descScroller->setButtonColor( UnsaturatedColor(color.r*.4,color.g*.4,color.b*.4) );
        descScroller->setTextColor(GUI_OPAQUE_WHITE());
            descScroller->setOutlineColor(GUI_OPAQUE_MEDIUM_GRAY());

        // Description box.
        StaticDisplay* ms = new StaticDisplay;
        ms->setRect( Rect(-.96, -.95, 1.87, .90) );
        ms->setColor( GFXColor(color.r,color.g,color.b,.1) );
            ms->setOutlineColor(GUI_OPAQUE_MEDIUM_GRAY());
        ms->setFont( Font(.07) );
        ms->setMultiLine(true);
        ms->setTextColor(GUI_OPAQUE_WHITE());
        ms->setTextMargins(Size(.02,.01));
        ms->setId("Description");
        ms->setScroller(descScroller);
        newsGroup->addChild(ms);

        newsGroup->addChild(descScroller);      // Want scroller "over" description box.
    }
      {
            GroupControl * loadSaveGroup = new GroupControl;
            loadSaveGroup->setId("LoadSaveGroup");
            window()->addControl(loadSaveGroup);
            GFXColor color=getColorForGroup("LoadSaveGroup");
        // Scroller for picker.
        Scroller* pickScroller = new Scroller;
        pickScroller->setRect( Rect(-.20, -.7, .05, 1.4) );
        pickScroller->setColor( UnsaturatedColor(color.r,color.g,color.b,.1) );
        pickScroller->setThumbColor( UnsaturatedColor(color.r*.4,color.g*.4,color.b*.4), GUI_OPAQUE_WHITE() );
        pickScroller->setButtonColor( UnsaturatedColor(color.r*.4,color.g*.4,color.b*.4) );
        pickScroller->setTextColor(GUI_OPAQUE_WHITE());
            pickScroller->setOutlineColor(GUI_OPAQUE_MEDIUM_GRAY());

        // Save game picker.
        SimplePicker* pick = new SimplePicker;
        pick->setRect( Rect(-.96, -.7, .76, 1.4) );
        pick->setColor( GFXColor(color.r,color.g,color.b,.1) );
            pick->setOutlineColor(GUI_OPAQUE_MEDIUM_GRAY());
        pick->setTextColor(GUI_OPAQUE_WHITE());
        pick->setSelectionColor(UnsaturatedColor(0,.6,0,.8));
        pick->setHighlightColor(UnsaturatedColor(0,.6,0,.35));
        pick->setHighlightTextColor(GUI_OPAQUE_WHITE());
        pick->setFont( Font(.07) );
        pick->setTextMargins(Size(0.02,0.01));
        pick->setId("LoadSavePicker");
        pick->setScroller(pickScroller);
        loadSaveGroup->addChild(pick);

        loadSaveGroup->addChild(pickScroller);        // Want scroller "over" picker.

        // Scroller for description.
        Scroller* descScroller = new Scroller;
        descScroller->setRect( Rect(.91, -.7, .05, 1.4) );
        descScroller->setColor( UnsaturatedColor(color.r,color.g,color.b,.1) );
        descScroller->setThumbColor( UnsaturatedColor(color.r*.4,color.g*.4,color.b*.4), GUI_OPAQUE_WHITE() );
        descScroller->setButtonColor( UnsaturatedColor(color.r*.4,color.g*.4,color.b*.4) );
        descScroller->setTextColor(GUI_OPAQUE_WHITE());
            descScroller->setOutlineColor(GUI_OPAQUE_MEDIUM_GRAY());

        // Description box.
        StaticDisplay* ms = new StaticDisplay;
        ms->setRect( Rect(.15, -.7, .76, 1.4) );
        ms->setColor( GFXColor(color.r,color.g,color.b,.1) );
            ms->setOutlineColor(GUI_OPAQUE_MEDIUM_GRAY());
        ms->setFont( Font(.07) );
        ms->setMultiLine(true);
        ms->setTextColor(GUI_OPAQUE_WHITE());
        ms->setTextMargins(Size(.02,.01));
        ms->setId("Description");
        ms->setScroller(descScroller);
        loadSaveGroup->addChild(ms);

        loadSaveGroup->addChild(descScroller);  // Want scroller "over" description box.



        // Scroller for description.
        Scroller* inputTextScroller = new Scroller;
        inputTextScroller->setRect( Rect(.91, -0.95, .05, .2) );
        inputTextScroller->setColor( UnsaturatedColor(color.r,color.g,color.b,.1) );
        inputTextScroller->setThumbColor( UnsaturatedColor(color.r*.4,color.g*.4,color.b*.4), GUI_OPAQUE_WHITE() );
        inputTextScroller->setButtonColor( UnsaturatedColor(color.r*.4,color.g*.4,color.b*.4) );
        inputTextScroller->setTextColor(GUI_OPAQUE_WHITE());
            inputTextScroller->setOutlineColor(GUI_OPAQUE_MEDIUM_GRAY());

        // Description box.
        StaticDisplay* inputText = new TextInputDisplay(&base_keyboard_queue,"\x1b\n \t\r*?\\/|:<>\"^");
        inputText->setRect( Rect(-.6, -.95, 1.51, .2) );
        inputText->setColor( GFXColor(color.r,color.g,color.b,.1) );
            inputText->setOutlineColor(GUI_OPAQUE_MEDIUM_GRAY());
        inputText->setFont( Font(.07) );
        inputText->setMultiLine(true);
        inputText->setTextColor(GUI_OPAQUE_WHITE());
        inputText->setTextMargins(Size(.02,.01));
        inputText->setId("InputText");
        inputText->setScroller(inputTextScroller);
        loadSaveGroup->addChild(inputText);

        loadSaveGroup->addChild(inputTextScroller);   // Want scroller "over" description box.


// Accept button.
        NewButton* buy10 = new NewButton;
        buy10->setRect( Rect(-.11, 0, .22, .12) );
        buy10->setColor( GFXColor(0,1,1,.1) );
        buy10->setTextColor(GUI_OPAQUE_WHITE());
            buy10->setDownColor( GFXColor(0,1,1,.4) );
            buy10->setDownTextColor( GFXColor(.2,.2,.2) );
            buy10->setVariableBorderCycleTime(1.0);
            buy10->setBorderColor( GFXColor(.2,.2,.2) );
            buy10->setEndBorderColor( GFXColor(.4,.4,.4) );
            buy10->setShadowWidth(2.0);
        buy10->setFont(Font(.08, BOLD_STROKE));
        buy10->setId("Commit10");
            buy10->setLabel("Save");
            buy10->setCommand("Save");
        loadSaveGroup->addChild(buy10);
            
        NewButton* accept = new NewButton;
        accept->setRect( Rect(-.11,-.2, .22, .12) );
        accept->setColor( GFXColor(0,1,1,.1) );
        accept->setTextColor(GUI_OPAQUE_WHITE());
            accept->setDownColor( GFXColor(0,1,1,.4) );
            accept->setDownTextColor( GFXColor(.2,.2,.2) );
            accept->setVariableBorderCycleTime(1.0);
            accept->setBorderColor( GFXColor(.2,.2,.2) );
            accept->setEndBorderColor( GFXColor(.4,.4,.4) );
            accept->setShadowWidth(2.0);
        accept->setFont(Font(.08, BOLD_STROKE));
        accept->setId("Commit");
            accept->setLabel("Load");
            accept->setCommand("Load");
            loadSaveGroup->addChild(accept);
        NewButton* quit = new NewButton;
        quit->setRect( Rect(-.95, -.9, .3, .1) );
        quit->setColor( GFXColor(.8,1,.1,.1) );
        quit->setTextColor(GUI_OPAQUE_WHITE());
            quit->setDownColor( GFXColor(.8,1,.1,.4) );
            quit->setDownTextColor( GFXColor(.2,.2,.2) );
            quit->setVariableBorderCycleTime(1.0);
            quit->setBorderColor( GFXColor(.5,.2,.2) );
            quit->setEndBorderColor( GFXColor(.7,.4,.4) );
            quit->setShadowWidth(2.0);
        quit->setFont(Font(.07, BOLD_STROKE));
        quit->setId("CommitAll");
            quit->setLabel("Quit Game");
            quit->setCommand("Quit");
        loadSaveGroup->addChild(quit);
        NewButton * newgame = new NewButton;
        newgame->setRect(Rect(-.11,-.4, .22, .12) );
        newgame->setColor( GFXColor(0,1,1,.1) );
        newgame->setTextColor(GUI_OPAQUE_WHITE());
        newgame->setDownColor( GFXColor(0,1,1,.4) );
        newgame->setDownTextColor( GFXColor(.2,.2,.2) );
        newgame->setVariableBorderCycleTime(1.0);
        newgame->setBorderColor( GFXColor(.2,.2,.2) );
        newgame->setEndBorderColor( GFXColor(.4,.4,.4) );
        newgame->setShadowWidth(2.0);
        newgame->setFont(Font(.08, BOLD_STROKE));
        newgame->setId("NewGame");
        newgame->setLabel("New");
        newgame->setCommand("New");
        loadSaveGroup->addChild(newgame);                   
            
      }
    {
        // MISSIONS group control.
        GroupControl* missionsGroup = new GroupControl;
        missionsGroup->setId("MissionsGroup");
        window()->addControl(missionsGroup);
            GFXColor color=getColorForGroup("MissionsGroup");

        // Scroller for picker.
        Scroller* pickScroller = new Scroller;
        pickScroller->setRect( Rect(-.20, -.8, .05, 1.45) );
        pickScroller->setColor( UnsaturatedColor(color.r,color.g,color.b,.1) );
        pickScroller->setThumbColor( UnsaturatedColor(color.r*.4,color.g*.4,color.b*.4), GUI_OPAQUE_WHITE() );
        pickScroller->setButtonColor( UnsaturatedColor(color.r*.4,color.g*.4,color.b*.4) );
        pickScroller->setTextColor(GUI_OPAQUE_WHITE());
            pickScroller->setOutlineColor(GUI_OPAQUE_MEDIUM_GRAY());

        // Picker.
        SimplePicker* pick = new SimplePicker;
        pick->setRect( Rect(-.96, -.8, .76, 1.45) );
        pick->setColor( GFXColor(color.r,color.g,color.b,.1) );
            pick->setOutlineColor(GUI_OPAQUE_MEDIUM_GRAY());
        pick->setTextColor(GUI_OPAQUE_WHITE());
        pick->setSelectionColor(UnsaturatedColor(0,.6,0,.8));
        pick->setHighlightColor(UnsaturatedColor(0,.6,0,.35));
        pick->setHighlightTextColor(GUI_OPAQUE_WHITE());
        pick->setFont( Font(.07) );
        pick->setTextMargins(Size(0.02,0.01));
        pick->setId("Missions");
        pick->setScroller(pickScroller);
        missionsGroup->addChild(pick);

        missionsGroup->addChild(pickScroller);        // Want scroller "over" picker.

        // Scroller for description.
        Scroller* descScroller = new Scroller;
        descScroller->setRect( Rect(.91, -.8, .05, 1.45) );
        descScroller->setColor( UnsaturatedColor(color.r,color.g,color.b,.1) );
        descScroller->setThumbColor( UnsaturatedColor(color.r*.4,color.g*.4,color.b*.4), GUI_OPAQUE_WHITE() );
        descScroller->setButtonColor( UnsaturatedColor(color.r*.4,color.g*.4,color.b*.4) );
        descScroller->setTextColor(GUI_OPAQUE_WHITE());
            descScroller->setOutlineColor(GUI_OPAQUE_MEDIUM_GRAY());

        // Description box.
        StaticDisplay* ms = new StaticDisplay;
        ms->setRect( Rect(-.10, -.8, 1.01, 1.45) );
        ms->setColor( GFXColor(color.r,color.g,color.b,.1) );
            ms->setOutlineColor(GUI_OPAQUE_MEDIUM_GRAY());
        ms->setFont( Font(.06) );
        ms->setMultiLine(true);
        ms->setTextColor(GUI_OPAQUE_WHITE());
        ms->setTextMargins(Size(.02,.01));
        ms->setId("Description");
        ms->setScroller(descScroller);
        missionsGroup->addChild(ms);

        missionsGroup->addChild(descScroller);        // Want scroller "over" description box.

        // Accept button.
        NewButton* accept = new NewButton;
        accept->setRect( Rect(-.23, -.95, .22, .11) );
        accept->setColor( GFXColor(0,1,1,.1) );
        accept->setTextColor(GUI_OPAQUE_WHITE());
            accept->setDownColor( GFXColor(0,1,1,.4) );
            accept->setDownTextColor( GFXColor(.2,.2,.2) );
            accept->setVariableBorderCycleTime(1.0);
            accept->setBorderColor( GFXColor(.2,.2,.2) );
            accept->setEndBorderColor( GFXColor(.4,.4,.4) );
            accept->setShadowWidth(2.0);
        accept->setFont(Font(.08, BOLD_STROKE));
        accept->setId("Commit");
        missionsGroup->addChild(accept);
      }
      {
        // SHIP_DEALER group control.
        GroupControl* shipDealerGroup = new GroupControl;
        shipDealerGroup->setId("ShipDealerGroup");
        window()->addControl(shipDealerGroup);
            GFXColor color=getColorForGroup("ShipDealerGroup");

        // Scroller for picker.
        Scroller* pickScroller = new Scroller;
        pickScroller->setRect( Rect(-.20, -.8, .05, 1.45) );
        pickScroller->setColor( UnsaturatedColor(color.r,color.g,color.b,.1) );
        pickScroller->setThumbColor( UnsaturatedColor(color.r*.4,color.g*.4,color.b*.4), GUI_OPAQUE_WHITE() );
        pickScroller->setButtonColor( UnsaturatedColor(color.r*.4,color.g*.4,color.b*.4) );
        pickScroller->setTextColor(GUI_OPAQUE_WHITE());
            pickScroller->setOutlineColor(GUI_OPAQUE_MEDIUM_GRAY());

        // Picker.
        SimplePicker* pick = new SimplePicker;
        pick->setRect( Rect(-.96, -.8, .76, 1.45) );
        pick->setColor( GFXColor(color.r,color.g,color.b,.1) );
            pick->setOutlineColor(GUI_OPAQUE_MEDIUM_GRAY());
        pick->setTextColor(GUI_OPAQUE_WHITE());
        pick->setSelectionColor(UnsaturatedColor(0,.6,0,.8));
        pick->setHighlightColor(UnsaturatedColor(0,.6,0,.35));
        pick->setHighlightTextColor(GUI_OPAQUE_WHITE());
        pick->setFont( Font(.07) );
        pick->setTextMargins(Size(0.02,0.01));
        pick->setId("Ships");
        pick->setScroller(pickScroller);
        shipDealerGroup->addChild(pick);

        shipDealerGroup->addChild(pickScroller);            // Want scroller to be "over" picker.

        // Scroller for description.
        Scroller* descScroller = new Scroller;
        descScroller->setRect( Rect(.91, -.5, .05, 1.15) );
        descScroller->setColor( UnsaturatedColor(color.r,color.g,color.b,.1) );
        descScroller->setThumbColor( UnsaturatedColor(color.r*.4,color.g*.4,color.b*.4), GUI_OPAQUE_WHITE() );
        descScroller->setButtonColor( UnsaturatedColor(color.r*.4,color.g*.4,color.b*.4) );
        descScroller->setTextColor(GUI_OPAQUE_WHITE());
            descScroller->setOutlineColor(GUI_OPAQUE_MEDIUM_GRAY());

        // Description box.
        StaticDisplay* ms = new StaticDisplay;
        StaticImageDisplay * picture = new StaticImageDisplay;
        picture->setRect(Rect(-.10,-.51,.48*.75,-.48));
        picture->setTexture("blackclear.png");
        picture->setId("DescriptionImage");
        ms->setRect( Rect(-.10, -.5, 1.01, 1.15) );
        ms->setColor( GFXColor(color.r,color.g,color.b,.1) );
            ms->setOutlineColor(GUI_OPAQUE_MEDIUM_GRAY());
        ms->setFont( Font(.06) );
        ms->setMultiLine(true);
        ms->setTextColor(GUI_OPAQUE_WHITE());
        ms->setTextMargins(Size(.02,.01));
        ms->setId("Description");
        ms->setScroller(descScroller);
        shipDealerGroup->addChild(ms);

        shipDealerGroup->addChild(descScroller);      // Want scroller "over" description box.
        shipDealerGroup->addChild(picture);
        // Buy button.
        NewButton* buy = new NewButton;
        buy->setRect( Rect(-.53, -.95, .22, .11) );
        buy->setColor( GFXColor(0,1,1,.1) );
        buy->setTextColor(GUI_OPAQUE_WHITE());
            buy->setDownColor( GFXColor(0,1,1,.4) );
            buy->setDownTextColor( GFXColor(.2,.2,.2) );
            buy->setVariableBorderCycleTime(1.0);
            buy->setBorderColor( GFXColor(.2,.2,.2) );
            buy->setEndBorderColor( GFXColor(.4,.4,.4) );
            buy->setShadowWidth(2.0);
        buy->setFont(Font(.08, BOLD_STROKE));
        buy->setId("Commit");
        shipDealerGroup->addChild(buy);
        NewButton* sell = new NewButton;
        sell->setRect( Rect(-.23, -.95, .22, .11) );
        sell->setColor( GFXColor(0,1,1,.1) );
        sell->setTextColor(GUI_OPAQUE_WHITE());
            sell->setDownColor( GFXColor(0,1,1,.4) );
            sell->setDownTextColor( GFXColor(.2,.2,.2) );
            sell->setVariableBorderCycleTime(1.0);
            sell->setBorderColor( GFXColor(.2,.2,.2) );
            sell->setEndBorderColor( GFXColor(.4,.4,.4) );
            sell->setShadowWidth(2.0);
        sell->setFont(Font(.08, BOLD_STROKE));
        sell->setId("Commit10");
        shipDealerGroup->addChild(sell);
    }

    {
        // INFO group control.
        GroupControl* infoGroup = new GroupControl;
        infoGroup->setId("InfoGroup");
        window()->addControl(infoGroup);
            GFXColor color=getColorForGroup("InfoGroup");

        // Player Info button.
        NewButton* playerInfo = new NewButton;
        playerInfo->setRect( Rect(-.40, .52, .27, .09) );
        playerInfo->setLabel("Player Info");
        static GFXColor pinfo_col=getConfigColor("player_info",GFXColor(0,.4,0));
        playerInfo->setCommand("ShowPlayerInfo");
        
            playerInfo->setColor( GFXColor(pinfo_col.r,pinfo_col.g,pinfo_col.b,.25) );
            playerInfo->setTextColor( GUI_OPAQUE_WHITE() );
            playerInfo->setDownColor( GFXColor(pinfo_col.r,pinfo_col.g,pinfo_col.b,.5) );
            playerInfo->setDownTextColor( GUI_OPAQUE_BLACK() );
            playerInfo->setHighlightColor( GFXColor(pinfo_col.r,pinfo_col.g,pinfo_col.b,.4) );
        playerInfo->setFont(Font(.07));
        infoGroup->addChild(playerInfo);

        // Ship Stats button.
        NewButton* shipStats = new NewButton;
        shipStats->setRect( Rect(-.05, .52, .27, .09) );
        shipStats->setLabel("Ship Stats");
        shipStats->setCommand("ShowShipStats");
            shipStats->setColor( GFXColor(pinfo_col.r,pinfo_col.g,pinfo_col.b,.25) );
            shipStats->setTextColor( GUI_OPAQUE_WHITE() );
            shipStats->setDownColor( GFXColor(pinfo_col.r,pinfo_col.g,pinfo_col.b,.5) );
            shipStats->setDownTextColor( GUI_OPAQUE_BLACK() );
            shipStats->setHighlightColor( GFXColor(pinfo_col.r,pinfo_col.g,pinfo_col.b,.4) );
        shipStats->setFont(Font(.07));
        infoGroup->addChild(shipStats);

        // Scroller for description.
        Scroller* descScroller = new Scroller;
        descScroller->setRect( Rect(.91, -.95, .05, 1.4) );
        descScroller->setColor( UnsaturatedColor(color.r,color.g,color.b,.1) );
        descScroller->setThumbColor( UnsaturatedColor(color.r*.4,color.g*.4,color.b*.4), GUI_OPAQUE_WHITE() );
        descScroller->setButtonColor( UnsaturatedColor(color.r*.4,color.g*.4,color.b*.4) );
        descScroller->setTextColor(GUI_OPAQUE_WHITE());
            descScroller->setOutlineColor(GUI_OPAQUE_MEDIUM_GRAY());

        // Description box.
        StaticDisplay* ms = new StaticDisplay;
        ms->setRect( Rect(-.96, -.95, 1.87, 1.4) );
        ms->setColor( GFXColor(color.r,color.g,color.b,.1) );
            ms->setOutlineColor(GUI_OPAQUE_MEDIUM_GRAY());
        ms->setFont( Font(.07) );
        ms->setMultiLine(true);
        ms->setTextColor(GUI_OPAQUE_WHITE());
        ms->setTextMargins(Size(.02,.01));
        ms->setId("Description");
        ms->setScroller(descScroller);
        infoGroup->addChild(ms);

        infoGroup->addChild(descScroller);
    }
}

// Create the controls that will be used for this window.
void BaseComputer::createControls(void) {
    // Set up the window.
    window()->setFullScreen();
    window()->setColor(GUI_CLEAR);
      window()->setTexture("basecomputer.png");

    // Put all the controls in the window.
    constructControls();

    // Take the mode group controls out of the window.
    for(int i=0; i<DISPLAY_MODE_COUNT; i++) {
        Control* group = window()->findControlById(modeInfo[i].groupId);
        if(group) {
            window()->removeControlFromWindow(group);
            m_modeGroups[i] = group;
        }
    }

    createModeButtons();
}

// Create the mode buttons.
void BaseComputer::createModeButtons(void) {
    NewButton* originalButton = static_cast<NewButton*>(window()->findControlById("ModeButton"));
    assert(originalButton != NULL);

    if(m_displayModes.size() > 1) {
        // Create a button for each display mode, copying the original button.
        Rect rect = originalButton->rect();
        for(unsigned int i=0; i<m_displayModes.size(); i++) {
            DisplayMode mode = m_displayModes[i];
            NewButton* newButton = new NewButton(*originalButton);
            newButton->setRect(rect);
            newButton->setLabel(modeInfo[mode].button);
            newButton->setCommand(modeInfo[mode].command);
            window()->addControl(newButton);
            rect.origin.x += rect.size.width + MODE_BUTTON_SPACE;
        }
    }

    // Make sure this original doesn't show.
    originalButton->setHidden(true);
}

// Make sure the info in the transaction lists is gone.
void BaseComputer::resetTransactionLists(void) {
    m_transList1 = TransactionList();
    m_transList2 = TransactionList();
}


// Switch to the set of controls used for the specified mode.
void BaseComputer::switchToControls(DisplayMode mode) {
    if(m_currentDisplay != mode) {
        assert(m_modeGroups[mode] != NULL);         // We should have controls for this mode.

        if(m_currentDisplay != NULL_DISPLAY) {
            // Get the old controls out of the window.
            Control* oldControls = window()->findControlById(modeInfo[m_currentDisplay].groupId);
            if(oldControls) {
                window()->removeControlFromWindow(oldControls);
            }
            // We put this back in our table so that we "own" the controls.
            m_modeGroups[m_currentDisplay] = oldControls;
            // Stop playing muzak for the old mode.
            if(m_playingMuzak) {
                muzak->Skip();
                m_playingMuzak = false;
            }
        }

        m_currentDisplay = mode;

        window()->addControl(m_modeGroups[mode]);
        // Take this group out of our table because we don't own it anymore.
        m_modeGroups[mode] = NULL;
    }
}

// Change controls to CARGO mode.
bool BaseComputer::changeToCargoMode(const EventCommandId& command, Control* control) {
    if(m_currentDisplay != CARGO) {
        switchToControls(CARGO);
        loadCargoControls();
        updateTransactionControlsForSelection(NULL);
    }
    return true;
}


bool BaseComputer::changeToLoadSaveMode(const EventCommandId& command, Control* control) {
    if(m_currentDisplay != LOADSAVE) {
        switchToControls(LOADSAVE);
        loadLoadSaveControls();
    }
    return true;
}


// Set up the window and get everything ready.
void BaseComputer::init(void) {
    // Create a new window.
    Window* w = new Window;
    setWindow(w);

    // Read in the controls for all the modes.
    createControls();
}

// Open the window, etc.
void BaseComputer::run(void) {
    // Simulate clicking the leftmost mode button.
    // We don't actually use the button because there isn't a button if there's only one mode.
    processWindowCommand(modeInfo[m_displayModes[0]].command, NULL);

    WindowController::run();
}

// Redo the title strings for the display.
void BaseComputer::recalcTitle() {
    // Generic base title for the display.
    string baseTitle = modeInfo[m_currentDisplay].title;

    // Base name.
    Unit* baseUnit = m_base.GetUnit();
    string baseName;
    if(baseUnit) {
        if(baseUnit->isUnit() == PLANETPTR) {
                  string temp = ((Planet*)baseUnit)->getHumanReadablePlanetType()+" Planet";
                  baseName = temp;
        } else {
                  baseName = baseUnit->name;
        }
    }
    baseTitle += emergency_downgrade_mode+baseName;

      // Faction name for base.
      string baseFaction = FactionUtil::GetFactionName(baseUnit->faction);
      if(!baseFaction.empty()) {
            baseTitle += " [" + baseFaction + ']';
      }

    // Set the string in the base title control.
    StaticDisplay* baseTitleDisplay = static_cast<StaticDisplay*>( window()->findControlById("BaseInfoTitle") );
    assert(baseTitleDisplay != NULL);
    baseTitleDisplay->setText(baseTitle);

      // Generic player title for display
      char playerTitle[256];
      playerTitle[0] = '\0';        // Start with an empty string.

    // Credits the player has.
      const float playerCredits = _Universe->AccessCockpit()->credits;

      switch(m_currentDisplay) {
            default:
                  sprintf(playerTitle, "Credits: %.2f", playerCredits);
                  break;
            case MISSIONS:
                  {
                        const int count = guiMax(0, active_missions.size() - 1);
                        sprintf(playerTitle, "Credits: %.2f,  Active missions: %d", playerCredits, count);
                  }
                  break;
            case UPGRADE:
                  // Fall through.
            case CARGO:
                  {
                        Unit* playerUnit = m_player.GetUnit();
                        if(playerUnit) {
                              const float emptyVolume = m_currentDisplay==CARGO?playerUnit->getEmptyCargoVolume():playerUnit->getEmptyUpgradeVolume();
                              const float volumeLeft = emptyVolume - (m_currentDisplay==CARGO?playerUnit->getCargoVolume():playerUnit->getUpgradeVolume());
                              sprintf(playerTitle, "Credits: %.2f,  Space left: %.6g of %.6g",
                                    playerCredits, volumeLeft, emptyVolume);
                        }
                  }
                  break;
      }

    // Set the string in the player title control.
    StaticDisplay* playerTitleDisplay = static_cast<StaticDisplay*>( window()->findControlById("PlayerInfoTitle") );
    assert(playerTitleDisplay != NULL);
    playerTitleDisplay->setText(playerTitle);
}

// Scroll to a specific item in a picker, and optionally select it.
//  Returns true if we selected an item.
bool BaseComputer::scrollToItem(Picker* picker, const Cargo& item, bool select, bool skipFirstCategory) {
    PickerCells* cells = picker->cells();
    if(!cells) return false;

    PickerCell* categoryCell = NULL;    // Need this if we can't find the item.

    // Walk through the category list(s).
    std::string category=getDisplayCategory(item);
    if(category.size() > 0 ) {     // Make sure we have a category.
        string::size_type categoryStart = 0;
        if(skipFirstCategory) {
            // We need to skip the first category in the string.
            // Generally need to do this when there's a category level that's not in the UI, like
            //  "upgrades" in the Upgrade UI.
            categoryStart = category.find(CATEGORY_SEP, 0);
            if(categoryStart != string::npos) categoryStart++;
        }
        while(true) {
            // See if we have multiple categories left.
            const string::size_type categoryEnd = category.find(CATEGORY_SEP, categoryStart);
            const string currentCategory = category.substr(categoryStart, categoryEnd-categoryStart);

            PickerCell* cell = cells->cellWithId(currentCategory);
            if(!cell || !cell->children()) {
                // The category has no children, or we have no matching category.  We are done.
                // WARNING:  We return from here!
                picker->scrollToCell(categoryCell);
                return false;
            }

            // Found the category in the right place.
            categoryCell = cell;
            categoryCell->setHideChildren(false);
            picker->setMustRecalc();
            cells = categoryCell->children();

            // Next piece of the category string.
            if(categoryEnd == string::npos) {
                break;
            }
            categoryStart = categoryEnd + 1;
        }
    }

    // We have the parent category, now we need the child itself.
    assert(cells != NULL);
    PickerCell* cell = cells->cellWithId(item.content);
    picker->setMustRecalc();
    if(!cell) {
        // Item is not here.
            int count = cells->count();
            if(count == 0) {
                  // The category is empty.
                  picker->scrollToCell(categoryCell);
                  return false;
            }

            // Try to find the place where the item used to be.
            // We assume here that the list is sorted by id, which is the
            //  original, un-beautified name.
            int i = 0;
            for(; i<count; i++) {
                  if(item.content < cells->cellAt(i)->id()) {
                        break;
                  }
            }
            if(i == count) i--;
            cell = cells->cellAt(i);
            assert(cell != NULL);
            // Drop through to get cell handled.
    }

    if(select) {
        picker->selectCell(cell, true);
            // This may not be a selectable cell.
            return (picker->selectedCell() != NULL);
      } else {
            // Make sure we scroll it into view.
            // Since it's not selected, we assume it's in the "other" list and scroll
            //  it into the middle.
            picker->scrollToCell(cell, true);
            return false;
      }

    // Shouldn't ever get here.
    assert(false);
    return false;
}

// Hide the controls that commit transactions.
void BaseComputer::hideCommitControls(void) {
      // The three buy/sell buttons.
    NewButton* commitButton = static_cast<NewButton*>( window()->findControlById("Commit") );
    commitButton->setHidden(true);
    NewButton* commit10Button = static_cast<NewButton*>( window()->findControlById("Commit10") );
    if(commit10Button != NULL) commit10Button->setHidden(true);
    NewButton* commitAllButton = static_cast<NewButton*>( window()->findControlById("CommitAll") );
    if(commitAllButton != NULL) commitAllButton->setHidden(true);

    NewButton* commitFixButton = static_cast<NewButton*>( window()->findControlById("CommitFix") );
    if(commitFixButton != NULL) commitFixButton->setHidden(true);

      // The price and "max" displays.
    StaticDisplay* totalPrice = static_cast<StaticDisplay*>( window()->findControlById("TotalPrice") );
      if(totalPrice != NULL) totalPrice->setText("");
    StaticDisplay* maxForPlayer = static_cast<StaticDisplay*>( window()->findControlById("MaxQuantity") );
      if(maxForPlayer != NULL) maxForPlayer->setText("");
}

// Update the commit controls in the Cargo screen, since we have three of them.
void BaseComputer::configureCargoCommitControls(const Cargo& item, TransactionType trans) {
      if(trans == BUY_CARGO) {
            // "Buy 1" button.
            NewButton* commitButton = static_cast<NewButton*>( window()->findControlById("Commit") );
            assert(commitButton != NULL);
            commitButton->setHidden(false);
            commitButton->setLabel("Buy 1");
            commitButton->setCommand("BuyCargo");

            // "Buy 10" button.
            NewButton* commit10Button = static_cast<NewButton*>( window()->findControlById("Commit10") );
            assert(commit10Button != NULL);
            commit10Button->setHidden(false);
            commit10Button->setLabel("Buy 10");
            commit10Button->setCommand("Buy10Cargo");

            // "Buy All" button.
            NewButton* commitAllButton = static_cast<NewButton*>( window()->findControlById("CommitAll") );
            assert(commitAllButton != NULL);
            commitAllButton->setHidden(false);
            commitAllButton->setLabel("Buy");
            commitAllButton->setCommand("BuyAllCargo");

            const int maxQuantity = maxQuantityForPlayer(item, item.quantity);

            // Total price display.
            const double totalPrice = item.price * maxQuantity;
            char tempString[2048];
            sprintf(tempString, "Total: #b#%.2f#-b", totalPrice);
            StaticDisplay* totalDisplay = static_cast<StaticDisplay*>( window()->findControlById("TotalPrice") );
            assert(totalDisplay != NULL);
            totalDisplay->setText(tempString);

            // Limit if we have one.
            StaticDisplay* maxForPlayer = static_cast<StaticDisplay*>( window()->findControlById("MaxQuantity") );
            assert(maxForPlayer != NULL);
            if(maxQuantity >= item.quantity) {
                  // No limits, so let's not mention anything.
                  maxForPlayer->setText("");
            } else {
                  char maxString[2048];
                  sprintf(maxString, "Max: #b#%d#-b", maxQuantity);
                  maxForPlayer->setText(maxString);
            }
      } else {
            assert(trans == SELL_CARGO);

            // "Sell" button.
            NewButton* commitButton = static_cast<NewButton*>( window()->findControlById("Commit") );
            assert(commitButton != NULL);
            commitButton->setHidden(false);
            commitButton->setLabel(item.mission?"Dump 1":"Sell 1");
            commitButton->setCommand("SellCargo");

            // "Sell 10" button.
            NewButton* commit10Button = static_cast<NewButton*>( window()->findControlById("Commit10") );
            assert(commit10Button != NULL);
            commit10Button->setHidden(false);
            commit10Button->setLabel(item.mission?"Dump 10":"Sell 10");
            commit10Button->setCommand("Sell10Cargo");

            // "Sell All" button.
            NewButton* commitAllButton = static_cast<NewButton*>( window()->findControlById("CommitAll") );
            assert(commitAllButton != NULL);
            commitAllButton->setHidden(false);
            commitAllButton->setLabel(item.mission?"Dump":"Sell");
            commitAllButton->setCommand("SellAllCargo");

            // Total price display.
            const double totalPrice = item.price * item.quantity*(item.mission?0:1);
            char tempString[2048];
            sprintf(tempString, "Total: #b#%.2f#-b", totalPrice);
            StaticDisplay* totalDisplay = static_cast<StaticDisplay*>( window()->findControlById("TotalPrice") );
            assert(totalDisplay != NULL);
            totalDisplay->setText(tempString);

            // No limit.
            StaticDisplay* maxForPlayer = static_cast<StaticDisplay*>( window()->findControlById("MaxQuantity") );
            assert(maxForPlayer != NULL);
            maxForPlayer->setText("");
      }
}

// Update the commit controls in the Cargo screen, since we have three of them.
bool BaseComputer::configureUpgradeCommitControls(const Cargo& item, TransactionType trans) {
        bool damaged_mode=false; 
      if(trans == BUY_UPGRADE)      //    base inventory
      {
            NewButton* commitButton = static_cast<NewButton*>( window()->findControlById("Commit") );
            assert(commitButton != NULL);
            commitButton->setHidden(false);
            commitButton->setLabel("Buy");
            commitButton->setCommand("BuyUpgrade");

            NewButton* commitFixButton = static_cast<NewButton*>( window()->findControlById("CommitFix") );
            assert(commitButton != NULL);
            commitFixButton->setHidden(true);
            commitFixButton->setLabel("Fix");
            commitFixButton->setCommand("FixUpgrade");
      }
      else  //    Sell Upgrade - Local Inventory
      {
            NewButton* commitButton = static_cast<NewButton*>( window()->findControlById("Commit") );
            assert(commitButton != NULL);

                if (m_player.GetUnit()) {
                  bool CanDoSell=true;
                  Unit* player = m_player.GetUnit();                  
                  unsigned int numc=player->numCargo();
                  if (!isWeapon(item.category)){//weapons can always be sold
                    for (unsigned int i=0;i<numc;++i) {
                      Cargo * c = &player->GetCargo(i);
                      if (c->category.find("upgrades/")==0&&!isWeapon(c->category)) {
                        float po=PercentOperational(player,c->content,c->category);
                        if (po>.02&&po<.98) {
                          static bool must_fix_first = XMLSupport::parse_bool(vs_config->getVariable("physics","must_repair_to_sell","true"));
                          
                          CanDoSell=(emergency_downgrade_mode.length()!=0||must_fix_first==false);
                        }
                      }
                    }
                  }
                  if (CanDoSell) {
                    commitButton->setHidden(false);
                    commitButton->setLabel("Sell");
                    commitButton->setCommand("SellUpgrade");
                  }else {
                    damaged_mode=true;
                    commitButton->setHidden(true);
                    commitButton->setLabel("Fix1st");
                    commitButton->setCommand("");
                  }
                }
                if (m_player.GetUnit()&&PercentOperational(m_player.GetUnit(), item.content,item.category)<1) {
                    NewButton* commitFixButton = static_cast<NewButton*>( window()->findControlById("CommitFix") );
                    if (m_base.GetUnit()) {
                      if (RepairPrice(PercentOperational(m_player.GetUnit(),
                                                         item.content, item.category),
                                      m_base.GetUnit()->PriceCargo(item.content))
                          <= _Universe->AccessCockpit()->credits) {
                                                         
                        assert(commitButton != NULL);
                        commitFixButton->setHidden(false);
                        commitFixButton->setLabel("Fix");
                        commitFixButton->setCommand("FixUpgrade");
                      }
                    }
                
                }
      }
        return damaged_mode;
}


/*
// Update the commit controls in the Upgrade screen, since we (will) have three of them.
void BaseComputer::configureUpgradeCommitControls(const Cargo& item, TransactionType trans) {
      // "Buy 1" button.
      NewButton* commitButton = static_cast<NewButton*>( window()->findControlById("Commit") );
      assert(commitButton != NULL);
      commitButton->setHidden(false);
      commitButton->setLabel("Buy 1");
      commitButton->setCommand("BuyCargo");

      // "Buy 10" button.
      NewButton* commit10Button = static_cast<NewButton*>( window()->findControlById("Commit10") );
      assert(commit10Button != NULL);
      commit10Button->setHidden(false);
      commit10Button->setLabel("Buy 10");
      commit10Button->setCommand("Buy10Cargo");

      // "Buy All" button.
      NewButton* commitAllButton = static_cast<NewButton*>( window()->findControlById("CommitAll") );
      assert(commitAllButton != NULL);
      commitAllButton->setHidden(false);
      commitAllButton->setLabel("Buy");
      commitAllButton->setCommand("BuyAllCargo");

      const int maxQuantity = maxQuantityForPlayer(item, item.quantity);

      // Total price display.
      const double totalPrice = item.price * maxQuantity;
      char tempString[2048];
      sprintf(tempString, "Total: #b#%.2f#-b", totalPrice);
      StaticDisplay* totalDisplay = static_cast<StaticDisplay*>( window()->findControlById("TotalPrice") );
      assert(totalDisplay != NULL);
      totalDisplay->setText(tempString);
      // Limit if we have one.
      StaticDisplay* maxForPlayer = static_cast<StaticDisplay*>( window()->findControlById("MaxQuantity") );
      assert(maxForPlayer != NULL);
      if(maxQuantity >= item.quantity) {
            // No limits, so let's not mention anything.
            maxForPlayer->setText("");
      } else {
            char maxString[2048];
            sprintf(maxString, "Max: #b#%d#-b", maxQuantity);
            maxForPlayer->setText(maxString);
      }
}
*/

string buildShipDescription(Cargo &item,string & descriptiontexture);
// Update the controls when the selection for a transaction changes.
void BaseComputer::updateTransactionControlsForSelection(TransactionList* tlist) {
    // Get the controls we need.
    NewButton* commitButton = static_cast<NewButton*>( window()->findControlById("Commit") );
    assert(commitButton != NULL);
    StaticDisplay* desc = static_cast<StaticDisplay*>( window()->findControlById("Description") );
    std::string descriptiontexture;
    assert(desc != NULL);

    if(!tlist) {
        // We have no selection.  Turn off UI that commits a transaction.
        m_selectedList = NULL;
            hideCommitControls();
        desc->setText("");
        // Make sure there is no selection.
        if(m_transList1.picker) m_transList1.picker->selectCell(NULL);
        if(m_transList2.picker) m_transList2.picker->selectCell(NULL);
        return;
    }

    // We have a selection of some sort.

    // Set the button state.
    m_selectedList = tlist;

    // Clear selection from other list.
    TransactionList& otherList = ( (&m_transList1==m_selectedList)? m_transList2 : m_transList1 );
    if(otherList.picker) otherList.picker->selectCell(NULL);

    // They selected a cell that has a description.
    // The selected item.
      const PickerCell* cell = tlist->picker->selectedCell();
      assert(cell != NULL);
    Cargo& item = tlist->masterList[cell->tag()].cargo;
    bool damaged_mode=false;
    if(!isTransactionOK(item, tlist->transaction)) {
        // We can't do the transaction. so hide the transaction button.
        // This is an odd state.  We have a selection, but no transaction is possible.
        hideCommitControls();
    } else {
        // We can do the transaction.
        commitButton->setHidden(false);
        switch(tlist->transaction) {
            case BUY_CARGO:
                        configureCargoCommitControls(item, BUY_CARGO);
                break;
            case BUY_UPGRADE:
                //commitButton->setLabel("Buy");
                //commitButton->setCommand("BuyUpgrade");
                        configureUpgradeCommitControls(item, BUY_UPGRADE);
                break;
            case BUY_SHIP:
                commitButton->setLabel("Buy");
                commitButton->setCommand("BuyShip");
                if(item.category.find("My_Fleet") != string::npos) {
                  //note can only sell it if you can afford to ship it over here.
                  NewButton* commit10Button = static_cast<NewButton*>( window()->findControlById("Commit10") );
                  assert(commit10Button != NULL);
                  commit10Button->setHidden(false);
                  commit10Button->setLabel("Sell");
                  commit10Button->setCommand("SellShip");

                }
                break;
            case SELL_CARGO:
                        configureCargoCommitControls(item, SELL_CARGO);
                break;
            case SELL_UPGRADE:
                //commitButton->setLabel("Sell");
                //commitButton->setCommand("SellUpgrade");
                        damaged_mode=configureUpgradeCommitControls(item, SELL_UPGRADE);
                break;
             case ACCEPT_MISSION:
               if (item.category.find("Active_Missions")!=string::npos) {
                 commitButton->setLabel("Abort");
               }else {
                 commitButton->setLabel("Accept");
               }
                commitButton->setCommand("AcceptMission");
                break;
           default:
                assert(false);      // Missed enum in transaction.
                break;
        }
    }

    // The description string.
    string descString;
    char tempString[2048];
    Unit* baseUnit = m_base.GetUnit();

    if(tlist->transaction == ACCEPT_MISSION) {
        descString = item.description;
    } else {
        // Do the money.
        switch(tlist->transaction) {
            case BUY_CARGO:
                        if (item.description==""||item.description[0]!='@'){
                              item.description=buildCargoDescription(item);
                        }
                if(item.category.find("My_Fleet") != string::npos) {
                    // This ship is in my fleet -- the price is just the transport cost to get it to
                    //  the current base.  "Buying" this ship makes it my current ship.
                    sprintf(tempString, "#b#Transport cost: %.2f#-b#n1.5#", item.price);
                } else {
                    sprintf(tempString, "Price: #b#%.2f#-b#n#", baseUnit->PriceCargo(item.content));
                    descString += tempString;
                    sprintf(tempString, "Cargo volume: %.2f;  Mass: %.2f#n1.5#", item.volume, item.mass);
                }
                descString += tempString;
                break;
            case BUY_UPGRADE:
                if(item.content == BASIC_REPAIR_NAME) {
                    // Basic repair is implemented entirely in this module.
                    // PriceCargo() doesn't know about it.
                              Unit * playerUnit = m_player.GetUnit();
                              int multiplier =1;
                              if (playerUnit) {
                                    multiplier = playerUnit->RepairCost();
                              }
                    sprintf(tempString, "Price: #b#%.2f#-b#n1.5#", basicRepairPrice()*multiplier);
                } else {
                    sprintf(tempString, "Price: #b#%.2f#-b#n1.5#", baseUnit->PriceCargo(item.content));
                }
                descString += tempString;
                        if(item.description==""||item.description[0]!='#'){
                              item.description=buildUpgradeDescription(item);
                        }
                break;
            case BUY_SHIP:
                if (item.category.find("My_Fleet")==string::npos) {
                  UniverseUtil::StopAllSounds();
                  if (item.price < _Universe->AccessCockpit()->credits) {
                    std::string tmp = item.content.substr(0,item.content.find("."));
                    UniverseUtil::playSound("sales/salespitch"+tmp+".wav",QVector(0,0,0),Vector(0,0,0));
                  }else {
                    UniverseUtil::playSound("sales/salespitchnotenoughmoney.wav",QVector(0,0,0),Vector(0,0,0));
                  }
                }
                if (item.description==""){
                  item.description=buildShipDescription(item,descriptiontexture);
                  
                }
                if(item.category.find("My_Fleet") != string::npos) {
                    // This ship is in my fleet -- the price is just the transport cost to get it to
                    //  the current base.  "Buying" this ship makes it my current ship.
                    sprintf(tempString, "#b#Transport cost: %.2f#-b#n1.5#", item.price);
                } else {
                    
                    sprintf(tempString, "Price: #b#%.2f#-b#n#", baseUnit->PriceCargo(item.content));
                    descString += tempString;
                    sprintf(tempString, "Cargo volume: %.2f;  Mass: %.2f#n1.5#", item.volume, item.mass);
                }
                descString += tempString;
                break;
            case SELL_CARGO:
              if (item.description==""||item.description[0]!='@'){
                item.description=buildCargoDescription(item);
              }
              if (item.mission) {
                sprintf(tempString, "Destroy evidence of mission cargo. Credit received: 0.00.");
              }else {
                sprintf(tempString, "Value: #b#%.2f#-b, purchased for %.2f#n#",
                        baseUnit->PriceCargo(item.content), item.price);
              }
              descString += tempString;
              sprintf(tempString, "Cargo volume: %.2f;  Mass: %.2f#n1.5#", item.volume, item.mass);
              descString += tempString;
              break;
            case SELL_UPGRADE:


//********************************************************************************************
              {
              double percent_working=m_player.GetUnit()?PercentOperational(m_player.GetUnit(),item.content,item.category):0.0;
              if(percent_working<1) //    IF DAMAGED
              {
                sprintf(tempString, "Damaged and Used value: #b#%.2f#-b, purchased for %.2f#n1.5#",
                        SellPrice(percent_working,baseUnit->PriceCargo(item.content)), item.price);
                descString += tempString;
                
                sprintf(tempString, "Percent Working: #b#%.2f#-b, Repair Cost: %.2f#n1.5#",
                        percent_working*100, RepairPrice(percent_working,baseUnit->PriceCargo(item.content)));
                descString += tempString;
              }
              else      
              {
                sprintf(tempString, "Used value: #b#%.2f#-b, purchased for %.2f#n1.5#",
                        usedValue(baseUnit->PriceCargo(item.content)), item.price);
                descString += tempString;
              }
                if (damaged_mode) {
                  descString+="#c1:0:0#Warning: #b#Because pieces of your ship are damaged, you will not be able to sell this item until you fix those damaged items in this column in order to allow the mechanics to remove this item.#-c#-b#n1.5#";
                }
//********************************************************************************************
                
                if(item.description==""||item.description[0]!='#'){
                  item.description=buildUpgradeDescription(item);
                }
              }
               
              break;
            default:
                assert(false);      // Missed transaction enum in switch statement.
                break;
        }

        // Description.
        descString += item.description;
    }

    // Change the description control.
    string::size_type pic;
    StaticImageDisplay* descimage = static_cast<StaticImageDisplay*>( window()->findControlById("DescriptionImage") );
    if ((pic=descString.find("@"))!=string::npos){
       std::string texture = descString.substr(pic+1);
       descString = descString.substr(0,pic);
       string::size_type picend = texture.find("@");
       if (picend!=string::npos) {
          descString+=texture.substr(picend+1);
          texture = texture.substr(0,picend);          
       }              
       if (descimage)
          descimage->setTexture(texture);
    }else {
      if (descimage&&descriptiontexture=="") 
        descimage->setTexture("blackclear.png");
      else if (descimage)
        descimage->setTexture(descriptiontexture);
    }
    {
      pic = descString.find("<");
      if (pic!=string::npos) {
        std::string tmp=descString.substr(pic+1);
        descString=descString.substr(0,pic);
        if ((pic=tmp.find(">"))!=string::npos) {
          descString+=tmp.substr(pic+1);
        }
      }
    }
    desc->setText(descString);
}

// Something in a Picker was selected.
bool BaseComputer::pickerChangedSelection(const EventCommandId& command, Control* control) {
    assert(control != NULL);
    Picker* picker = static_cast<Picker*>(control);
    PickerCell* cell = picker->selectedCell();

    // Figure out which transaction list we are using.
    assert(picker == m_transList1.picker || picker == m_transList2.picker);
    TransactionList* tlist = ((picker==m_transList1.picker)? &m_transList1 : &m_transList2);

    if(m_base.GetUnit()) {
        if(!cell) {
            // The selection just got cleared.
            TransactionList& otherList = ( (&m_transList1==tlist)? m_transList2 : m_transList1 );
            if(otherList.picker && otherList.picker->selectedCell()) {
                // Special case.  The other picker has a selection -- we are seeing the selection
                //  cleared in this picker as result.  Do nothing.
            } else {
                updateTransactionControlsForSelection(NULL);
            }
        } else if(cell->tag() == CATEGORY_TAG) {
            // They just selected a category.  Clear the selection no matter what.
            updateTransactionControlsForSelection(NULL);
        } else {
            // Make the controls right for this item.
            updateTransactionControlsForSelection(tlist);
        }
    }

    return true;
}
bool UpgradeAllowed (const Cargo& item, Unit * playerUnit) {
  std::string prohibited_upgrades=UniverseUtil::LookupUnitStat(playerUnit->name,FactionUtil::GetFactionName(playerUnit->faction),"Prohibited_Upgrades");
  while (prohibited_upgrades.length()) {
    std::string::size_type where=prohibited_upgrades.find(" ");
    if (where==string::npos) where=prohibited_upgrades.find(";");
    std::string prohibited_upgrade=prohibited_upgrades;
    if (where!=string::npos) {
      prohibited_upgrade=prohibited_upgrades.substr(0,where);
      prohibited_upgrades=prohibited_upgrades.substr(where+1);
    }else prohibited_upgrades="";
    where=prohibited_upgrade.find(":");
    std::string content = prohibited_upgrade.substr(0,where);
    int quantity=0;
    if (where!=string::npos) {
      std::string tmp = prohibited_upgrade.substr(where+1);      
      quantity = atoi(tmp.c_str());      
    }
    if (content==item.content||content==item.category) {
      if (quantity==0)
          return false;
      unsigned int i=0;
      Cargo * numUpgrades = playerUnit->GetCargo(item.content,i);
      if (numUpgrades) {
        if (numUpgrades->quantity>=quantity)
          return false;
      }            
    }
  }
  return true;
}
// Return whether or not the current item and quantity can be "transacted".
bool BaseComputer::isTransactionOK(const Cargo& originalItem, TransactionType transType, int quantity) {
    if(originalItem.mission&&transType!=SELL_CARGO)
        return false;
    // Make sure we have somewhere to put stuff.
    Unit* playerUnit = m_player.GetUnit();
    if(!playerUnit) return false;
    Cockpit* cockpit = _Universe->isPlayerStarship(playerUnit);
    if(!cockpit) return false;

    // Need to fix item so there is only one for cost calculations.
    Cargo item = originalItem;
    item.quantity = quantity;
    Unit* baseUnit = m_base.GetUnit();
    switch(transType) {
        case BUY_CARGO:
            // Enough credits and room for the item in the ship.
          if(item.price*quantity <= cockpit->credits && playerUnit->CanAddCargo(item)) {
              return true;
          }
            break;
        case SELL_CARGO:
            // There is a base here, and it is willing to buy the item.
          if (!originalItem.mission) {
            if(baseUnit && baseUnit->CanAddCargo(item)) {
              return true;
            }
          }else return true;
          break;
        case BUY_SHIP:
            // Either you are buying this ship for your fleet, or you already own the
            //  ship and it will be transported to you.
            if(baseUnit) {
                        if(item.price*quantity <= cockpit->credits) {
                              return true;
                        }
            }
            break;
        case ACCEPT_MISSION:
            // Make sure the player doesn't take too many missions.
                  if(item.category.find("Active_Missions")!=string::npos||active_missions.size() < UniverseUtil::maxMissions()) {
                        return true;
                  }
            break;
        case SELL_UPGRADE:
            if(baseUnit && baseUnit->CanAddCargo(item)) {
              return true;
            }
        case BUY_UPGRADE:
            // cargo.mission == true means you can't do the transaction.
            if(item.price*quantity <= cockpit->credits && (playerUnit->CanAddCargo(item)||upgradeNotAddedToCargo(item.category))&&UpgradeAllowed(item,playerUnit)&&!item.mission) {
                return true;
            }
            break;
        default:
            assert(false);          // Missed an enum in transaction switch statement.
            break;
        }

    return false;
}

// Create whatever cells are needed to add a category to the picker.
SimplePickerCell* BaseComputer::createCategoryCell(SimplePickerCells& cells, const string& origCategory,
                                                   bool skipFirstCategory) {
    string category = origCategory;
    if(skipFirstCategory) {
        // Skip over the first category piece.
        const string::size_type sepLoc = category.find(CATEGORY_SEP);
        if(sepLoc == string::npos) {
            // No category separator.  At most one category.
            category.erase(0, category.size());
        } else {
            // Skip the first piece.
            category = origCategory.substr(sepLoc+1);
        }
    }

    if(category.size() == 0) {
        // No category at all.
        return NULL;
    }
    // Create or reuse a cell for the first part of the category.
    const string::size_type loc = category.find(CATEGORY_SEP);
    const string currentCategory = category.substr(0, loc);
    if(cells.count() > 0 && cells.cellAt(cells.count()-1)->id() == currentCategory) {
        // Re-use the category we have.
    } else {
        // Need to make a new cell for this.
        cells.addCell(new SimplePickerCell(beautify(currentCategory), currentCategory, CATEGORY_TEXT_COLOR(), CATEGORY_TAG));
    }

    SimplePickerCell* parentCell = static_cast<SimplePickerCell*>(cells.cellAt(cells.count()-1));   // Last cell in list.
    if(loc == string::npos) {
        // This is a simple category -- we are done.
        return parentCell;
    } else {
        // The category string has more stuff in it -- finish it up.
        SimplePickerCells* childCellList = static_cast<SimplePickerCells*>( parentCell->children() );
        if(!childCellList) {
            // If parent doesn't have room children, we create the room manually.
            parentCell->createEmptyChildList();
            childCellList = static_cast<SimplePickerCells*>( parentCell->children() );
        }
        const string newCategory = category.substr(loc+1);
        return createCategoryCell(*childCellList, newCategory, false);
    }

    assert(false);
    // Never get here.
}

// Load a picker with a list of items.
void BaseComputer::loadListPicker(TransactionList& tlist, SimplePicker& picker, TransactionType transType,
                              bool skipFirstCategory) {
    // Make sure the transactionList has the correct info.
    tlist.picker = &picker;
    tlist.transaction = transType;

    // Make sure there is nothing old lying around in the picker.
    picker.clear();
    
    // Iterate through the list and load the picker from it.
    string currentCategory = "--ILLEGAL CATEGORY--";    // Current category we are adding cells to.
    SimplePickerCell* parentCell = NULL;                // Place to add new items.  NULL = Add to picker.
      for(int i=0; i<tlist.masterList.size(); i++) {
        Cargo& item = tlist.masterList[i].cargo;
        std::string icategory = getDisplayCategory(item);


          if(icategory != currentCategory) {
            // Create new cell(s) for the new category.
            parentCell = createCategoryCell(*static_cast<SimplePickerCells*>(picker.cells()), icategory, skipFirstCategory);
            currentCategory = icategory;
        }

        // Construct the cell for this item.
        const bool transOK = isTransactionOK(item, transType);
            
            string itemName = beautify(UniverseUtil::LookupUnitStat(item.content,"upgrades","Name"));
            string originalName = itemName;
            
            if(itemName==""){
              itemName=beautify(item.content); 
            }
            
          if (item.quantity > 1) {
            // If there is more than one item, show the number of items.
            itemName += " (" + tostring(item.quantity) + ")";
              }
      

//*******************************************************************************
        // Clear color means use the text color in the picker.
            GFXColor base_color = (transOK? (item.mission?MISSION_COLOR():GUI_CLEAR) : NO_MONEY_COLOR());
            GFXColor final_color;

            if(transType == SELL_UPGRADE&&m_player.GetUnit())
            {
                  //Adjust the base color if the item is 'damaged'
                  double percent_working = PercentOperational(m_player.GetUnit(),item.content,item.category);;

                  //Unit* playerUnit = m_player.GetUnit();
                  //Unit* upgradeUnit = getUnitFromUpgradeName(originalName, playerUnit->faction);
                  //playerUnit->canUpgrade(upgradeUnit,0,0,0,false,percent_working,NULL,false);

                  //UnitConstCache::getCachedConst(StringIntKey(itemName, FactionUtil::GetFaction("upgrades")))

                  //Unit* playerUnit = m_parent.m_player.GetUnit();
                  //if(!playerUnit) {percent_working = 1.0;}
                  //else
                  //{
                  //    playerUnit->canUpgrade(m_newPart, m_selectedMount, m_selectedTurret, m_addMultMode, false, percent_working, m_theTemplate);
                  //}

                  final_color = GFXColor(
                        (1.0*percent_working)+(1.0*(1.0-percent_working)),
                        (1.0*percent_working)+(0.0*(1.0-percent_working)),
                        (0.0*percent_working)+(0.0*(1.0-percent_working)),
                        (1.0*percent_working)+(1.0*(1.0-percent_working))
                        );

                  if(percent_working == 1.0){final_color = base_color;} //    working = normal color
                  if(percent_working == 0.0){final_color = GFXColor(0.2,0.2,0.2);}  //    dead = grey
            }
            else
                  final_color = base_color;

        //SimplePickerCell cell(itemName, item.content, (transOK? (item.mission?MISSION_COLOR():GUI_CLEAR) : NO_MONEY_COLOR()), i);
        SimplePickerCell *cell = new SimplePickerCell(itemName, item.content, final_color, i);

      //    cell.textColor(
      //          GFXColor(1,0,0)
//*******************************************************************************
        // Add the cell.
        if(parentCell) {
            parentCell->addChild(cell);
        } else {
            picker.addCell(cell);
        }
    }
}


// Load the controls for the CARGO display.
void BaseComputer::loadCargoControls(void) {
    // Make sure there's nothing in the transaction lists.
    resetTransactionLists();

    // Set up the base dealer's transaction list.
      vector<string> donttakethis;
      donttakethis.push_back("missions");
      donttakethis.push_back("upgrades");
    loadMasterList(m_base.GetUnit(), vector<string>(),donttakethis, true, m_transList1); // Anything but a mission.
    SimplePicker* basePicker = static_cast<SimplePicker*>( window()->findControlById("BaseCargo") );
    assert(basePicker != NULL);
    loadListPicker(m_transList1, *basePicker, BUY_CARGO);

    // Set up the player's transaction list.
    loadMasterList(m_player.GetUnit(),vector<string>(),donttakethis, true, m_transList2); // Anything but a mission.
    SimplePicker* inventoryPicker = static_cast<SimplePicker*>( window()->findControlById("PlayerCargo") );
    assert(inventoryPicker != NULL);
    loadListPicker(m_transList2, *inventoryPicker, SELL_CARGO);

    // Make the title right.
    recalcTitle();
}
// Need this class to sort CargoColor's.
class CargoColorSort {
public:
    bool operator () (const CargoColor & a, const CargoColor&b) {
      return( a.cargo < b.cargo );
    }
};


// Get a filtered list of items from a unit.
void BaseComputer::loadMasterList(Unit *un, const vector<string>& filtervec, const vector<string> &invfiltervec, bool removezero, TransactionList& tlist){
    vector<CargoColor>* items = &tlist.masterList;
    for (unsigned int i=0;i<un->numCargo();i++) {
          bool filter = filtervec.empty();
          bool invfilter = true;
            int vecindex;
        for (vecindex=0;vecindex<filtervec.size();vecindex++) {
                  if (un->GetCargo(i).category.find(filtervec[vecindex])!=string::npos) {
                        filter = true;
                        break;
                  }
            }
        for (vecindex=0;vecindex<invfiltervec.size();vecindex++) {
                  if (un->GetCargo(i).category.find(invfiltervec[vecindex])!=string::npos) {
                        invfilter = false;
                        break;
                  }
            }
            if (filter&&invfilter) {
            if ((!removezero)||un->GetCargo(i).quantity>0) {
                CargoColor col;
                col.cargo=un->GetCargo(i);
                        if (col.cargo.category=="")
                              col.cargo.category="#c.5:1:.3#Uncategorized Cargo";
                items->push_back (col);
//                if (!un->GetCargo(i).mission) {
//                }
                  }
            }
      }
      std::sort(items->begin(),items->end(),CargoColorSort());
}

// Return a pointer to the selected item in the picker with the selection.
Cargo* BaseComputer::selectedItem(void) {
    Cargo* result = NULL;
    if(m_selectedList) {
        assert(m_selectedList->picker);
        PickerCell* cell = m_selectedList->picker->selectedCell();
        if(cell) {
            result = &m_selectedList->masterList[cell->tag()].cargo;
        }
    }

    return result;
}

// Update the transaction controls after a transaction.
void BaseComputer::updateTransactionControls(const Cargo& item, bool skipFirstCategory) {
    // Go reselect the item.
    assert(m_selectedList != NULL);
    const bool success = scrollToItem(m_selectedList->picker, item, true, skipFirstCategory);
    // And scroll to that item in the other picker, too.
    TransactionList& otherList = ( (&m_transList1==m_selectedList)? m_transList2 : m_transList1 );
    if(otherList.picker) {
        // Scroll to the item in the other list, but don't select it.
        scrollToItem(otherList.picker, item, false, skipFirstCategory);
    }

    if(success) {
        // We selected the item successfully.
        updateTransactionControlsForSelection(m_selectedList);
    } else {
        // Didn't find item.  Clear the selection.
        updateTransactionControlsForSelection(NULL);
    }
}

// The max number of a particular item this player can buy.  Limits by price, cargo space, etc.
int BaseComputer::maxQuantityForPlayer(const Cargo& item, int suggestedQuantity) {
      int result = 0;

    Unit* playerUnit = m_player.GetUnit();
      if(playerUnit) {
            // Limit by cargo capacity.
            const float volumeLeft = playerUnit->getEmptyCargoVolume() - playerUnit->getCargoVolume();
            result = (int)guiMin(suggestedQuantity, volumeLeft/item.volume);

            // Limit by price.
            const double credits = _Universe->AccessCockpit()->credits;
            result = (int)guiMin(result, credits / item.price);
      }

      return result;
}
static void eliminateZeroCargo(Unit * un) {
  for (int i=un->numCargo()-1;i>=0;--i) {
    if (un->GetCargo(i).quantity==0)
      un->RemoveCargo(i,1,true);
  }
  
}
// Buy some items from the Cargo list.  Use -1 for quantity to buy all of the item.
bool BaseComputer::buySelectedCargo(int requestedQuantity) {
    Unit* playerUnit = m_player.GetUnit();
    Unit* baseUnit = m_base.GetUnit();
    if(!(playerUnit && baseUnit)) {
        return true;
    }

    Cargo* item = selectedItem();
    if(item) {
        Cargo itemCopy = *item;     // Copy this because we reload master list before we need it.
            int quantity = (requestedQuantity <= 0? item->quantity : requestedQuantity);
            quantity = maxQuantityForPlayer(*item, quantity);
        playerUnit->BuyCargo(item->content, quantity, baseUnit, _Universe->AccessCockpit()->credits);
        eliminateZeroCargo(playerUnit);
        // Reload the UI -- inventory has changed.  Because we reload the UI, we need to 
        //  find, select, and scroll to the thing we bought.  The item might be gone from the
        //  list (along with some categories) after the transaction.
        loadCargoControls();        // This will reload master lists.
        updateTransactionControls(itemCopy);
    }

    return true;
}

// Buy an item from the cargo list.
bool BaseComputer::buyCargo(const EventCommandId& command, Control* control) {
      return buySelectedCargo(1);
}

// Buy an item (quantity 10) from the cargo list.
bool BaseComputer::buy10Cargo(const EventCommandId& command, Control* control) {
      return buySelectedCargo(10);
}

// Buy all of an item from the cargo list.
bool BaseComputer::buyAllCargo(const EventCommandId& command, Control* control) {
      return buySelectedCargo(-1);
}

// Sell some items from the Cargo list.  Use -1 for quantity to buy all of the item.
bool BaseComputer::sellSelectedCargo(int requestedQuantity) {
    Unit* playerUnit = m_player.GetUnit();
    Unit* baseUnit = m_base.GetUnit();
    if(!(playerUnit && baseUnit)) {
        return true;
    }

    Cargo* item = selectedItem();
    if(item) {
        Cargo itemCopy = *item;     // Not sure what "sold" has in it.  Need copy of sold item.
        Cargo sold;
        const int quantity = (requestedQuantity <= 0? item->quantity : requestedQuantity);
        if (item->mission) {
          vector <Cargo>::iterator mycargo = std::find (playerUnit->image->cargo.begin(),playerUnit->image->cargo.end(),*item);
          if (mycargo!=playerUnit->image->cargo.end()) {
            playerUnit->RemoveCargo(mycargo-playerUnit->image->cargo.begin(),quantity,true);
          }
        }else {
          playerUnit->SellCargo(item->content, quantity, _Universe->AccessCockpit()->credits, sold, baseUnit);
        }
        eliminateZeroCargo(playerUnit);
        // Reload the UI -- inventory has changed.  Because we reload the UI, we need to 
        //  find, select, and scroll to the thing we bought.  The item might be gone from the
        //  list (along with some categories) after the transaction.
        loadCargoControls();
        updateTransactionControls(itemCopy);
    }

    return true;
}

// Sell an item from ship's cargo.
bool BaseComputer::sellCargo(const EventCommandId& command, Control* control) {
      return sellSelectedCargo(1);
}

// Sell an item (quantity 10) from the cargo list.
bool BaseComputer::sell10Cargo(const EventCommandId& command, Control* control) {
      return sellSelectedCargo(10);
}

// Sell all of an item from the cargo list.
bool BaseComputer::sellAllCargo(const EventCommandId& command, Control* control) {
      return sellSelectedCargo(-1);
}

// Change controls to NEWS mode.
bool BaseComputer::changeToNewsMode(const EventCommandId& command, Control* control) {
    if(m_currentDisplay != NEWS) {
        switchToControls(NEWS);
        loadNewsControls();
    }
    return true;
}

// The selection in the News picker changed.
bool BaseComputer::newsPickerChangedSelection(const EventCommandId& command, Control* control) {
    assert(control != NULL);
    Picker* picker = static_cast<Picker*>(control);
    PickerCell* cell = picker->selectedCell();

    StaticDisplay* desc = static_cast<StaticDisplay*>( window()->findControlById("Description") );
    assert(desc != NULL);

    if(cell == NULL) {
        // No selection.  Clear desc.  (Not sure how this can happen, but it's easy to cover.)
        desc->setText("");
    } else {
        desc->setText(cell->text());
    }
//    if (!m_playingMuzak) {
            // Turn on some cool music.
            static string newssong=vs_config->getVariable("audio","newssong","../music/news1.ogg");
            muzak->GotoSong(newssong);
            m_playingMuzak = true;
//    }
    return true;
}
static std::string simplePrettySystem(std::string system) {
  std::string::size_type where=system.find("/");
  return std::string("Sec:")+system.substr(0,where)+" Sys:"+(where==string::npos?system:system.substr(where+1));
}
static std::string simplePrettyShip(std::string ship) {
  if (ship.length()>0) {
    ship[0]=toupper(ship[0]);
  }
  std::string::size_type where = ship.find(".");
  if (where!=string::npos) {
    ship=ship.substr(0,where);
    ship="Refurbished "+ship;
  }
  return ship;
}
static std::string GarnerInfoFromSaveGame(string text) {
  static SaveGame savegame("");
  std::string system;
  QVector pos(0,0,0);
  bool updatepos=false;
  float creds;
  vector<std::string> Ships;
  std::string sillytemp=CurrentSaveGameName;
  CurrentSaveGameName=text;
  savegame.SetStarSystem("");
  savegame.ParseSaveGame(text,system,"",pos,updatepos,creds,Ships,_Universe->CurrentCockpit(),"",true,false);
  CurrentSaveGameName=sillytemp;
  text="Savegame: "+text+"#n#_________________#n#";  
  text+="Credits: "+tostring((unsigned int)creds)+"."+tostring(((unsigned int)(creds*100))%100)+"#n#";
  text+=simplePrettySystem(system)+"#n#";
  if (Ships.size()) {
    text+="Starship: "+simplePrettyShip(Ships[0])+"#n#";
    if (Ships.size()>2){
      text+="Fleet:#n#";
      for (int i=2;i<Ships.size();i+=2){
        text+=" "+simplePrettyShip(Ships[i-1])+"#n#  Located At:#n#  "+simplePrettySystem(Ships[i])+"#n#";
      }
    }
  }
  static string campaign_score=vs_config->getVariable("physics","campaigns","privateer_campaign vegastrike_campaign");
  string score=campaign_score;
  bool hit=false;
  string::size_type where;
  while (score.length()) {
    where=score.find(" ");
    string var = score.substr(0,where);
    if (where!=string::npos) {
      score = score.substr(where+1);
    }else score="";
    unsigned int curscore=savegame.getMissionData(var).size()+savegame.getMissionStringData(var).size();
    if (curscore>0) {
      hit =true;
      if (var.length()>0)
        var[0]=toupper(var[0]);
      text+=var.substr(0,var.find("_"))+" Campaign Score: "+tostring(curscore)+"#n#";
    }
  }
  if (!hit) {
    text+="Campaign Score: 0#n#";
  }
  return text;
}
// The selection in the News picker changed.
bool BaseComputer::loadSavePickerChangedSelection(const EventCommandId& command, Control* control) {
    assert(control != NULL);
    Picker* picker = static_cast<Picker*>(control);
    PickerCell* cell = picker->selectedCell();

    StaticDisplay* desc = static_cast<StaticDisplay*>( window()->findControlById("Description") );
      StaticDisplay* inputbox = static_cast<StaticDisplay*>( window()->findControlById("InputText") );
    assert(desc != NULL);

    if(cell == NULL) {
        // No selection.  Clear desc.  (Not sure how this can happen, but it's easy to cover.)
        desc->setText("");
    } else {
      desc->setText(GarnerInfoFromSaveGame(cell->text()));
      if (inputbox!=NULL)
        inputbox->setText(cell->text());
      
    }



      return true;
}

// Load the controls for the News display.
void BaseComputer::loadNewsControls(void) {
    SimplePicker* picker = static_cast<SimplePicker*>( window()->findControlById("NewsPicker") );
    assert(picker != NULL);
    picker->clear();

    // Load the picker.
    static const bool newsFromCargolist = XMLSupport::parse_bool(vs_config->getVariable("cargo","news_from_cargolist","false"));
    if(newsFromCargolist) {
        gameMessage last;
        int i = 0;
        vector<std::string> who;
        who.push_back("news");
        while((mission->msgcenter->last(i++, last, who))) {
            picker->addCell(new SimplePickerCell(last.message));
        }
    } else {
        // Get news from save game.
        Unit* playerUnit = m_player.GetUnit();
        if(playerUnit) {
            const int playerNum=UnitUtil::isPlayerStarship(playerUnit);
            int len = getSaveStringLength(playerNum, NEWS_NAME_LABEL);
            for (int i=len-1; i>=0; i--) {
                picker->addCell(new SimplePickerCell(getSaveString(playerNum, NEWS_NAME_LABEL, i)));
            }
        }
    }

    // Make sure the description is empty.
    StaticDisplay* desc = static_cast<StaticDisplay*>( window()->findControlById("Description") );
    assert(desc != NULL);
    desc->setText("");

    // Make the title right.
    recalcTitle();
}
#if defined (__FreeBSD__) || defined(__APPLE__)
static int  nodirs( struct dirent * entry)
#else
static int  nodirs( const struct dirent * entry)
#endif      
{
#if defined(_WIN32)
      // Have to check if we have the full path or just relative (which would be a problem)
      struct stat s;
      std::string tmp=VSFileSystem::homedir+"/save/"+entry->d_name;
      if( stat( tmp.c_str(), &s)<0)
            return string( entry->d_name)!="." && string( entry->d_name)!="..";
      if( (s.st_mode & S_IFDIR)==0 && string( entry->d_name)!="." && string( entry->d_name)!="..")
      {
            return 1;
      }
#else
      if( entry->d_type!=DT_DIR && string( entry->d_name)!="." && string( entry->d_name)!="..")
            return 1;
#endif
      return 0;
}


// Load the controls for the News display.
void BaseComputer::loadLoadSaveControls(void) {
    SimplePicker* picker = static_cast<SimplePicker*>( window()->findControlById("LoadSavePicker") );
    assert(picker != NULL);
    picker->clear();

      // Get news from save game.
      Unit* playerUnit = m_player.GetUnit();
      if(playerUnit) {
            const int playerNum=UnitUtil::isPlayerStarship(playerUnit);
            struct dirent ** dirlist;
            std::string savedir = VSFileSystem::homedir+"/save/";
            int ret = scandir (savedir.c_str(),&dirlist,nodirs,0);
            while( ret-->0) {
                  picker->addCell(new SimplePickerCell(dirlist[ret]->d_name));
            }           
      }

    // Make sure the description is empty.
    StaticDisplay* desc = static_cast<StaticDisplay*>( window()->findControlById("Description") );
    assert(desc != NULL);
    desc->setText("");

    // Make the title right.
    recalcTitle();
}


// Change display mode to MISSIONS.
bool BaseComputer::changeToMissionsMode(const EventCommandId& command, Control* control) {
    if(m_currentDisplay != MISSIONS) {
        switchToControls(MISSIONS);
        loadMissionsControls();
        updateTransactionControlsForSelection(NULL);
    }
    return true;
}


// Load a master list with missions.
void BaseComputer::loadMissionsMasterList(TransactionList& tlist) {
    // Make sure the list is clear.
    tlist.masterList.clear();

    Unit* unit = _Universe->AccessCockpit()->GetParent();
    int playerNum = UnitUtil::isPlayerStarship(unit);
    if(playerNum < 0) {
        VSFileSystem::vs_fprintf(stderr,"Docked ship not a player.");
        return;
    }

    // Number of strings to look at.  And make sure they match!
    const int stringCount = getSaveStringLength(playerNum, MISSION_SCRIPTS_LABEL);
    assert(stringCount == getSaveStringLength(playerNum, MISSION_NAMES_LABEL));
    assert(stringCount == getSaveStringLength(playerNum, MISSION_DESC_LABEL));

    // Make sure we have different names for all the missions.
    // This changes the savegame -- it removes ambiguity for good.
    for(int current=0; current<stringCount; current++) {
        const string currentName = getSaveString(playerNum, MISSION_NAMES_LABEL, current);
        int count = 1;
        // Check whether any after the current one have the same name.
        for(unsigned int check=current+1; check<stringCount; ++check) {
            const string checkName = getSaveString(playerNum, MISSION_NAMES_LABEL, check);
            if(check == current) {
                // Found identical names.  Add a "count" at the end. 
                putSaveString(playerNum, MISSION_NAMES_LABEL, current, checkName+"_"+tostring(count++));
            }
        }
    }

    // Create an entry for for each mission.
    for(int i=0; i<stringCount; i++) {
        CargoColor c;

        // Take any categories out of the name and put them in the cargo.category.
        const string finalScript = getSaveString(playerNum, MISSION_SCRIPTS_LABEL, i);
        if (finalScript[0]=='#') {
          continue; // Ignore any missions with comments. (those are fixer missions.)
        }
            
        const string originalName = getSaveString(playerNum, MISSION_NAMES_LABEL, i);
        const string::size_type lastCategorySep = originalName.rfind(CATEGORY_SEP);
        if(lastCategorySep != string::npos) {
            // We have a category.
            c.cargo.content = originalName.substr(lastCategorySep + 1);
            c.cargo.category = originalName.substr(0, lastCategorySep);
        } else {
            // No category in name.
            c.cargo.content = originalName;
            c.cargo.category = "";
        }
        c.cargo.content=c.cargo.content;
        // Description gets name at the top.
        c.cargo.description = "#b#" + beautify(c.cargo.content) + ":#-b#n1.75#" + 
            getSaveString(playerNum, MISSION_DESC_LABEL, i);

        tlist.masterList.push_back(c);
    }

    // Sort the list.  Better for display, easier to compile into categories, etc.
    std::sort(tlist.masterList.begin(), tlist.masterList.end(), CargoColorSort());
    if (active_missions.size()) {
      for (unsigned int i=1;i<active_missions.size();++i){
        CargoColor amission;
        amission.cargo.content=XMLSupport::tostring(i)+" "+active_missions[i]->mission_name;
        amission.cargo.price=0;
        amission.cargo.quantity=1;
        amission.cargo.category="Active_Missions";
        amission.cargo.description="Objectives\\";
        for (unsigned int j=0;j<active_missions[i]->objectives.size();++j) {
          amission.cargo.description+=active_missions[i]->objectives[j].objective+": "+XMLSupport::tostring((int)(100*active_missions[i]->objectives[j].completeness))+"%\\";          
        }
        amission.color=DEFAULT_UPGRADE_COLOR();
        tlist.masterList.push_back(amission);
      }
    }
}

// Load the controls for the MISSIONS display.
void BaseComputer::loadMissionsControls(void) {
    // Make sure there's nothing in the transaction lists.
    resetTransactionLists();

    // Load up the list of missions.
    loadMissionsMasterList(m_transList1);
    SimplePicker* picker = static_cast<SimplePicker*>( window()->findControlById("Missions") );
    assert(picker != NULL);
    loadListPicker(m_transList1, *picker, ACCEPT_MISSION);

    // Make the title right.
    recalcTitle();
}

// Accept a mission.
bool BaseComputer::acceptMission(const EventCommandId& command, Control* control) {
    Unit* playerUnit = m_player.GetUnit();
    Unit* baseUnit = m_base.GetUnit();
    if(!(playerUnit && baseUnit)) return true;

    Cargo* item = selectedItem();

    if(!item || !isTransactionOK(*item, ACCEPT_MISSION)) {
        // Better reload the UI -- we shouldn't have gotten here.
        loadMissionsControls();
        updateTransactionControlsForSelection(NULL);
        return true;
    }
    if (item->category.find("Active_Missions")!=string::npos) {
      unsigned int whichmission=atoi(item->content.c_str());
      if (whichmission>0&&whichmission<active_missions.size()) {
        Mission * tmp=active_missions[whichmission];
        active_missions[whichmission]->terminateMission();        
        if (tmp==mission)
          mission=active_missions[0];
        Cargo itemCopy = *item;
        loadMissionsControls();
        updateTransactionControls(itemCopy);
        return true;
      }      
      return false;
    }
    const int playernum = UnitUtil::isPlayerStarship(playerUnit);
    const int stringCount = getSaveStringLength(playernum, MISSION_NAMES_LABEL);
    assert(stringCount == getSaveStringLength(playernum, MISSION_SCRIPTS_LABEL));
    string qualifiedName;
    if(item->category.empty()) {
        qualifiedName = item->content;
    } else {
        qualifiedName = item->category + CATEGORY_SEP + item->content;
    }
    string finalScript;
    for (unsigned int i=0; i<stringCount; i++) {
      if (getSaveString(playernum, MISSION_NAMES_LABEL, i) == qualifiedName) {
          finalScript = getSaveString(playernum, MISSION_SCRIPTS_LABEL, i);
          eraseSaveString(playernum, MISSION_SCRIPTS_LABEL, i);
          eraseSaveString(playernum, MISSION_NAMES_LABEL, i);
          eraseSaveString(playernum, MISSION_DESC_LABEL, i);
          break;
      }
    }
    if(finalScript.empty()) {
      return false;
    } else {
        LoadMission("", finalScript, false);
      if(active_missions.size() > 0) {
            // Give the mission a name.
            active_missions.back()->mission_name = item->category;
      }

        // Reload the UI.
        Cargo itemCopy = *item;
        loadMissionsControls();
        updateTransactionControls(itemCopy);
    }

    // We handled the command, whether we successfully accepted the mission or not.
    return true;
}

// Load the all the controls for the UPGRADE display.
void BaseComputer::loadUpgradeControls(void) {
    // Make sure there's nothing in the transaction lists.
    resetTransactionLists();

    // Load the controls.
    loadBuyUpgradeControls();
    loadSellUpgradeControls();
  
    // Make the title right.
    recalcTitle();
}

// Load the BUY controls for the UPGRADE display.
void BaseComputer::loadBuyUpgradeControls(void) {
    Unit* playerUnit = m_player.GetUnit();
    Unit* baseUnit = m_base.GetUnit();

    TransactionList& tlist = m_transList1;
      tlist.masterList.clear(); // Just in case

    // Get all the upgrades.
    assert( equalColors(CargoColor().color, DEFAULT_UPGRADE_COLOR()) );
      std::vector<std::string> filtervec;
      filtervec.push_back("upgrades");
    loadMasterList(baseUnit, filtervec, std::vector<std::string>(), true, tlist);
    playerUnit->FilterUpgradeList(tlist.masterList);

    // Mark all the upgrades that we can't do.
    // cargo.mission == true means we can't upgrade this.
    vector<CargoColor>::iterator iter;
    for(iter=tlist.masterList.begin(); iter!=tlist.masterList.end(); iter++) {
        iter->cargo.mission = ( !equalColors(iter->color, DEFAULT_UPGRADE_COLOR()) );
    }

    // Add Basic Repair.
    CargoColor repair;
    repair.cargo.content = BASIC_REPAIR_NAME;
    repair.cargo.price = basicRepairPrice()*playerUnit->RepairCost();
    repair.cargo.description = BASIC_REPAIR_DESC;
    tlist.masterList.push_back(repair);

    // Load the upgrade picker from the master tlist.
    SimplePicker* basePicker = static_cast<SimplePicker*>( window()->findControlById("BaseUpgrades") );
    assert(basePicker != NULL);
    loadListPicker(tlist, *basePicker, BUY_UPGRADE, true);

    // Fix the Basic Repair color.
    SimplePickerCells* baseCells = static_cast<SimplePickerCells*>(basePicker->cells());
    SimplePickerCell* repairCell = static_cast<SimplePickerCell*>(baseCells->cellAt(baseCells->count()-1));
    assert(repairCell->text() == BASIC_REPAIR_NAME);
    if(isClear(repairCell->textColor())) {
        // Have repair cell, and its color is normal.
        repairCell->setTextColor(BASIC_REPAIR_TEXT_COLOR());
    }
}

// Load the SELL controls for the UPGRADE display.
void BaseComputer::loadSellUpgradeControls(void) {
    Unit* playerUnit = m_player.GetUnit();
    Unit* baseUnit = m_base.GetUnit();
    if(!(playerUnit && baseUnit)) {
        return;
    }

    TransactionList& tlist = m_transList2;
      tlist.masterList.clear(); // Just in case

    // Get a list of upgrades on our ship we could sell.
    Unit* partListUnit = &GetUnitMasterPartList();


    loadMasterList(partListUnit, weapfiltervec, std::vector<std::string>(), false, tlist);
    ClearDowngradeMap();
    playerUnit->FilterDowngradeList(tlist.masterList);
    static const bool clearDowngrades = XMLSupport::parse_bool(vs_config->getVariable("physics","only_show_best_downgrade","true"));
    if (clearDowngrades) {
        std::set<std::string> downgradeMap = GetListOfDowngrades();
        for (unsigned int i=0;i<tlist.masterList.size();++i) {
            if (downgradeMap.find(tlist.masterList[i].cargo.content)==downgradeMap.end()) {
                tlist.masterList.erase(tlist.masterList.begin()+i);
                i--;
            }
        }
    }

    // Mark all the upgrades that we can't do.
    // cargo.mission == true means we can't upgrade this.
    vector<CargoColor>::iterator iter;
    for(iter=tlist.masterList.begin(); iter!=tlist.masterList.end(); iter++) {
        iter->cargo.mission = ( !equalColors(iter->color, DEFAULT_UPGRADE_COLOR()) );
    }

      std::vector<std::string> invplayerfiltervec=weapfiltervec;
      //invplayerfiltervec.push_back("Damaged");
      std::vector<string> playerfiltervec;
      playerfiltervec.push_back("upgrades");
      loadMasterList(playerUnit, playerfiltervec, invplayerfiltervec, false, tlist); // Get upgrades, but not weapons.
      
    // Sort the tlist.  Better for display, easier to compile into categories, etc.
    std::sort(tlist.masterList.begin(), tlist.masterList.end(), CargoColorSort());

    // Load the upgrade picker form the master list.
    SimplePicker* basePicker = static_cast<SimplePicker*>( window()->findControlById("PlayerUpgrades") );
    assert(basePicker != NULL);
    loadListPicker(tlist, *basePicker, SELL_UPGRADE, true);
}


// Change display mode to UPGRADE.
bool BaseComputer::changeToUpgradeMode(const EventCommandId& command, Control* control) {
    Unit* playerUnit = m_player.GetUnit();
    Unit* baseUnit = m_base.GetUnit();
    if(!(playerUnit && baseUnit)) return true;

    if(m_currentDisplay != UPGRADE) {
        switchToControls(UPGRADE);
        loadUpgradeControls();
        updateTransactionControlsForSelection(NULL);
    }
    return true;
}

// Actually do a repair operation.
static void BasicRepair(Unit* parent) {
    if (parent) {
            int repairmultiplier=parent->RepairCost();
        if(UnitUtil::getCredits(parent) < basicRepairPrice()*repairmultiplier) {
            showAlert("You don't have enough credits to repair your ship.");
        } else if((repairmultiplier=parent->RepairUpgrade())) {
            UnitUtil::addCredits(parent, -basicRepairPrice()*repairmultiplier);
        } else {
            showAlert("Your ship has no damage.  No charge.");
        }
    }
}


// The "Operation" classes deal with upgrades.
// There are a number of potential questions that get asked, and a bunch of state that needs
//  to be maintained between the questions.  Rather than cluttering up the main class with this
//  stuff, it's all declared internally here.

// Base class for operation.  Support functions and common data.
// Should delete itself when done.
class BaseComputer::UpgradeOperation : public ModalDialogCallback
{
protected:
    UpgradeOperation(BaseComputer& p)
        : m_parent(p), m_newPart(NULL), m_part(), m_selectedMount(0), m_selectedTurret(0), m_selectedItem() {};
    virtual ~UpgradeOperation(void) {};

    bool commonInit(void);              // Initialization.
    void finish(void);                  // Finish up -- destruct the object.  MUST CALL THIS LAST.
    bool endInit(void);                 // Finish initialization.  Returns true if successful.
    bool gotSelectedMount(int index);   // We have the mount number.  Returns true if the operation was completed.
    bool gotSelectedTurret(int index);  // We have the turret number.  Returns true if the operation was completed.
    void updateUI(void);                // Make the UI right after we are done.

    // OVERRIDES FOR DERIVED CLASSES.
    virtual bool checkTransaction(void) = 0;    // Check, and verify user wants transaction.
    virtual void concludeTransaction(void) = 0; // Finish the transaction.
    virtual void selectMount(void) = 0;               // Let the user pick a mount.
    virtual void showTurretPicker(void);        // Let the user pick a turret.

    virtual void modalDialogResult( // Dispatch to correct function after some modal UI.
        const std::string& id,
        int result,
        WindowController& controller
        );

    BaseComputer& m_parent;         // Parent class that created us.
    const Unit* m_newPart;
    Cargo m_part;                   // Description of upgrade part.
    int m_selectedMount;            // Which mount to use.
    int m_selectedTurret;           // Which turret to use.
    Cargo m_selectedItem;           // Selection from original UI.
};

// Buy an upgrade for our ship.
class BaseComputer::BuyUpgradeOperation : public BaseComputer::UpgradeOperation
{
public:
    void start(void);               // Start the operation.

    BuyUpgradeOperation(BaseComputer& p) : UpgradeOperation(p),  m_theTemplate(NULL), m_addMultMode(0) {};
protected:
    virtual bool checkTransaction(void);        // Check, and verify user wants transaction.
    virtual void concludeTransaction(void);     // Finish the transaction.
    virtual void selectMount(void);                   // Let the user pick a mount.

    virtual ~BuyUpgradeOperation(void) {};

    const Unit* m_theTemplate;
    int m_addMultMode;
};

// Sell an upgrade from our ship.
class BaseComputer::SellUpgradeOperation : public BaseComputer::UpgradeOperation
{
public:
    void start(void);               // Start the operation.

    SellUpgradeOperation(BaseComputer& p) : UpgradeOperation(p), m_downgradeLimiter(NULL) {};
protected:
    virtual bool checkTransaction(void);        // Check, and verify user wants transaction.
    virtual void concludeTransaction(void);     // Finish the transaction.
    virtual void selectMount(void);                   // Let the user pick a mount.

    virtual ~SellUpgradeOperation(void) {};

    const Unit* m_downgradeLimiter;
};

// Id's for callbacks.
static const string GOT_MOUNT_ID = "GotMount";
static const string GOT_TURRET_ID = "GotTurret";
static const string CONFIRM_ID = "Confirm";

// Some common initialization.
bool BaseComputer::UpgradeOperation::commonInit(void) {
    Cargo* selectedItem = m_parent.selectedItem();
    if (selectedItem) {
      m_selectedItem = *selectedItem;
      return true;
    }else return false;
}

// Update the UI controls after a transaction has been concluded successfully.
void BaseComputer::UpgradeOperation::updateUI(void) {
    m_parent.loadUpgradeControls();
    m_parent.updateTransactionControls(m_selectedItem, true);
}

// Finish this operation.
void BaseComputer::UpgradeOperation::finish(void) {
    // Destruct us now.
    delete this;
}

// Finish initialization.  Returns true if successful.
bool BaseComputer::UpgradeOperation::endInit(void) {
    if(m_parent.m_player.GetUnit()) {
        m_newPart = getUnitFromUpgradeName(m_selectedItem.content, m_parent.m_player.GetUnit()->faction);
        if(m_newPart->name != LOAD_FAILED) {
                  selectMount();
        } else {
            return false;
        }
    }

    return true;
}

// Let the user pick a turret.
void BaseComputer::UpgradeOperation::showTurretPicker(void) {
    Unit* playerUnit = m_parent.m_player.GetUnit();
    if(!playerUnit) {
        finish();
        return;
    }

    vector<string> mounts;
    for(un_iter unitIter = playerUnit->getSubUnits(); *unitIter!=NULL; unitIter++) {
        mounts.push_back((*unitIter)->name);
    }

    showListQuestion("Select turret mount for your turret:", mounts, this, GOT_TURRET_ID);
}

// Got the mount number.
bool BaseComputer::UpgradeOperation::gotSelectedMount(int index) {
    Unit* playerUnit = m_parent.m_player.GetUnit();
    if(index < 0 || !playerUnit) {
        // The user cancelled somehow.
        finish();
            return false; // kill the window.
    } else {
        m_selectedMount = index;
        if(m_newPart->viewSubUnits().current() == NULL) {
            // Not a turret.  Proceed with the transaction.
            return checkTransaction();
            } else {
                  // Is a turret.
                  if(*playerUnit->getSubUnits() != NULL) {
                        // Need to get selected turret.
                        showTurretPicker();
                        return false;
                  } else {
                        // Ship can't take turrets.
                        finish();
                        showAlert("Your ship does not support turrets.");
                        return false; // kill the window.
                  }
            }
    }
}

// Got the mount number.
bool BaseComputer::UpgradeOperation::gotSelectedTurret(int index) {
    if(index < 0) {
        // The user cancelled somehow.
        finish();
            return false; // kill the window.
    } else {
        m_selectedTurret = index;
        return checkTransaction();
    }
}

// Dispatch to correct function after some modal UI.
void BaseComputer::UpgradeOperation::modalDialogResult(
    const std::string& id, int result, WindowController& controller) {
    if(id == GOT_MOUNT_ID) {
        // Got the selected mount from the user.
        gotSelectedMount(result);
    } else if(id == GOT_TURRET_ID) {
        // Got the selected turret from the user.
            gotSelectedTurret(result);
    } else if(id == CONFIRM_ID) {
        // User answered whether or not to conclude the transaction.
        if(result == YES_ANSWER) {
            // User wants to do this.
            concludeTransaction();
        } else {
            // User doesn't want to do it.  All done.
            finish();
        }
    }
}



// Start the Buy Upgrade Operation.
void BaseComputer::BuyUpgradeOperation::start(void) {

    Unit* playerUnit = m_parent.m_player.GetUnit();
    Unit* baseUnit = m_parent.m_base.GetUnit();
    if(!(playerUnit && baseUnit&&commonInit())) {
        finish();
        return;
    }

    const string unitDir = GetUnitDir(playerUnit->name.c_str());
    const string templateName = unitDir + ".template";
    const int faction = playerUnit->faction;

    // Get the "limiter" for the upgrade.  Stats can't increase more than this.
    m_theTemplate = UnitConstCache::getCachedConst(StringIntKey(templateName,faction));
    if (!m_theTemplate) {
        m_theTemplate = UnitConstCache::setCachedConst(StringIntKey(templateName,faction),UnitFactory::createUnit(templateName.c_str(),true,faction));
    }
    if (m_theTemplate->name != LOAD_FAILED) {
        m_addMultMode = GetModeFromName(m_selectedItem.content.c_str());   // Whether the price is linear or geometric.
        unsigned int offset;                // Temp.  Not used.
        Cargo* part = baseUnit->GetCargo(m_selectedItem.content, offset);    // Whether the base has any of these.
        if(part && part->quantity > 0) {
            m_part = *part;
            endInit();
        } else {
            finish();
        }
    } else {
        finish();
    }

    // The object may be deleted now. Be careful here.
}

// Custom class that handles picking a mount point.
class UpgradeOperationMountDialog : public ListQuestionDialog
{
public:
    // Process a command event from the window.
    virtual bool processWindowCommand(const EventCommandId& command, Control* control);
};

// Process a command from the window.
bool UpgradeOperationMountDialog::processWindowCommand(const EventCommandId& command, Control* control) {
    if(command == "Picker::NewSelection") {
            assert(control != NULL);
            Picker* picker = static_cast<Picker*>(control);
            PickerCell* cell = picker->selectedCell();
            if(cell && cell->tag()==0) {
                  // An "unselectable" cell was selected.  Turn the selection back off.
                  picker->selectCell(NULL);
            }
            return true;
    }

      // Only thing we care about is the selection changing.
      return ListQuestionDialog::processWindowCommand(command, control);
}

// Select the mount to use for selling.
void BaseComputer::BuyUpgradeOperation::selectMount(void) {
    if(m_newPart->GetNumMounts() <= 0) {
            // Part doesn't need a mount point.
        gotSelectedMount(0);
            return;
    }

      Unit* playerUnit = m_parent.m_player.GetUnit();
    if(!playerUnit) {
        finish();
        return;
    }

      // Create a custom list dialog to get the mount point.
      UpgradeOperationMountDialog* dialog = new UpgradeOperationMountDialog;
      dialog->init("Select mount for your item:");
      dialog->setCallback(this, GOT_MOUNT_ID);

    // Fill the dialog picker with the mount points.
    SimplePicker* picker = static_cast<SimplePicker*>( dialog->window()->findControlById("Picker") );
    assert(picker != NULL);
    for(int i=0; i<playerUnit->GetNumMounts(); i++) {
            // Mount is selectable if we can upgrade with the new part using that mount.
            double percent;         // Temp.  Not used.
            const bool selectable = playerUnit->canUpgrade(m_newPart, i, m_selectedTurret, m_addMultMode, false, percent, m_theTemplate);
        
            // Figure color and label based on weapon that is in the slot.
            GFXColor mountColor = MOUNT_POINT_NO_SELECT();
            string mountName;
            string ammoexp;
            if(playerUnit->mounts[i].status==Mount::ACTIVE || playerUnit->mounts[i].status==Mount::INACTIVE) {
                  mountName=tostring(i+1)+" "+ playerUnit->mounts[i].type->weapon_name;
                  ammoexp=(playerUnit->mounts[i].ammo==-1)?string(""):string((" ammo: "+tostring(playerUnit->mounts[i].ammo)));
                  mountName+=ammoexp;
                  mountColor = MOUNT_POINT_FULL();
        } else {
                  const std::string temp = lookupMountSize(playerUnit->mounts[i].size);
                  mountName=tostring(i+1)+" (Empty) "+ temp.c_str();
                  mountColor = MOUNT_POINT_EMPTY();
        }

            // If the mount point won't work with the weapon, don't let user select it.
            if(!selectable) mountColor = MOUNT_POINT_NO_SELECT();

            // Now we add the cell.  Note that "selectable" is stored in the tag property.
            picker->addCell(new SimplePickerCell(mountName, "", mountColor, (selectable?1:0)));
    }

      dialog->run();
}

// Check, and verify user wants Buy Upgrade transaction.  Returns true if more input is required.
bool BaseComputer::BuyUpgradeOperation::checkTransaction(void) {
    Unit* playerUnit = m_parent.m_player.GetUnit();
    if(!playerUnit) {
        finish();
        return false; // We want the window to die to avoid accessing of deleted memory. 
    }

    double percent;         // Temp.  Not used.
    if(playerUnit->canUpgrade(m_newPart, m_selectedMount, m_selectedTurret, m_addMultMode, false, percent, m_theTemplate) ) {
        // We can buy the upgrade.
        concludeTransaction();
            return false;
    } else {
        showYesNoQuestion("The item cannot fit the frame of your starship.  Do you want to buy it anyway?",
            this, CONFIRM_ID);
            return true;
    }
}
 
// Finish the transaction.
void BaseComputer::BuyUpgradeOperation::concludeTransaction(void) {
    Unit* playerUnit = m_parent.m_player.GetUnit();
    Unit* baseUnit = m_parent.m_base.GetUnit();
    if(!(playerUnit && baseUnit)) {
        finish();
        return;
    }

   // Get the upgrade percentage to calculate the full price.
    double percent;
      int numleft = basecargoassets(baseUnit,m_part.content);
      while(numleft>0&&playerUnit->canUpgrade(m_newPart, m_selectedMount, m_selectedTurret, m_addMultMode, true, percent, m_theTemplate)){
      const float price = m_part.price;// * (1-usedValue(percent));
      if (_Universe->AccessCockpit()->credits >= price) {
          // Have enough money.  Buy it.
          _Universe->AccessCockpit()->credits -= price;
          // Upgrade the ship.
          playerUnit->Upgrade(m_newPart, m_selectedMount, m_selectedTurret, m_addMultMode, true, percent, m_theTemplate);
          // Remove the item from the base, since we bought it.
          unsigned int index;
          baseUnit->GetCargo(m_part.content, index);
          baseUnit->RemoveCargo(index, 1, false);
        } else {
            break;
        }
          if(m_newPart->mounts.size()==0) {
            break;
          }else if(m_newPart->mounts[0].ammo<=0){
            break;
        }
        numleft = basecargoassets(baseUnit,m_part.content);
      }

    updateUI();

    finish();
}

int basecargoassets(Unit* baseUnit, string cargoname){
  unsigned int dummy;
  Cargo * somecargo = baseUnit->GetCargo(cargoname,dummy);
  if (somecargo){
      return somecargo->quantity;
  } else {
      return 0;
  }
}

// Start the Sell Upgrade Operation.
void BaseComputer::SellUpgradeOperation::start(void) {

    Unit* playerUnit = m_parent.m_player.GetUnit();
    if(!(playerUnit&&commonInit())) {
        finish();
        return;
    }

    const string unitDir = GetUnitDir(playerUnit->name.c_str());
    const string limiterName = unitDir + ".blank";
    const int faction = playerUnit->faction;

    // Get the "limiter" for this operation.  Stats can't decrease more than the blank ship.
    m_downgradeLimiter = makeFinalBlankUpgrade(playerUnit->name,faction);
    if(m_downgradeLimiter->name != LOAD_FAILED) {
        Cargo* part = GetMasterPartList(m_selectedItem.content.c_str());
        if(part) {
            m_part = *part;
            endInit();
        } else {
            finish();
        }
    } else {
        finish();
    }
    // The object may be deleted now. Be careful here.
}

// Try to match a mounted waepon name with the cargo name.
// Returns true if they are the same.
static bool matchCargoToWeapon(const std::string& cargoName, const std::string& weaponName) {
      // Weapon names have capitalized words, and no spaces between the words.
      // Cargo names are lower-case, and have underscores between words.
      // Also, anything in the Ammo category ends with "_ammo" in cargo, and not in weapon.
      // We try to make a cargo name look like a weapon name, then match them.

      std::string convertedCargoName;

      // Take off "_ammo" if it's there.
      int end = cargoName.size();
      const string::size_type ammoOffset = cargoName.rfind("_ammo");
      if(ammoOffset != std::string::npos) {
            end = ammoOffset;
      }

      bool wordStart = true;        // Start of word.
      for(int i=0; i<end; i++) {
            const char c = cargoName[i];
            if(c == '_') {
                  // Skip this, and make sure next letter is capitalized.
                  wordStart = true;
            } else if(wordStart) {
                  // Start or a word.  Capitalize the character, and turn off start of word.
                  convertedCargoName += toupper(c);
                  wordStart = false;
            } else {
                  // Normal character in middle of word.
                  convertedCargoName += c;
            }
    }

      return (strtoupper(convertedCargoName) == strtoupper(weaponName));
}

// Select the mount to use for selling.
void BaseComputer::SellUpgradeOperation::selectMount(void) {
    if(m_newPart->GetNumMounts() <= 0) {
            // Part doesn't need a mount point.
        gotSelectedMount(0);
            return;
    }

      Unit* playerUnit = m_parent.m_player.GetUnit();
    if(!playerUnit) {
        finish();
        return;
    }

      // Create a custom list dialog to get the mount point.
      UpgradeOperationMountDialog* dialog = new UpgradeOperationMountDialog;
      dialog->init("Select mount for your item:");
      dialog->setCallback(this, GOT_MOUNT_ID);

    // Fill the dialog picker with the mount points.
    SimplePicker* picker = static_cast<SimplePicker*>( dialog->window()->findControlById("Picker") );
    assert(picker != NULL);
      int mount = -1;               // The mount if there was only one.
      int selectableCount = 0;
    for(int i=0; i<playerUnit->GetNumMounts(); i++) {
            // Whether or not the entry is selectable -- the same as the thing we are selling.
            bool selectable = false;

            // Get the name.
            string mountName;
            if(playerUnit->mounts[i].status==Mount::ACTIVE || playerUnit->mounts[i].status==Mount::INACTIVE) {
                  // Something is mounted here.
                  const std::string unitName = playerUnit->mounts[i].type->weapon_name;
                  const Unit* partUnit = UnitConstCache::getCachedConst(StringIntKey(m_part.content, FactionUtil::GetFaction("upgrades")));
                  string ammoexp;
                  mountName=tostring(i+1)+" "+ unitName.c_str();
                  ammoexp=(playerUnit->mounts[i].ammo==-1)?string(""):string((" ammo: "+tostring(playerUnit->mounts[i].ammo)));
                  mountName+=ammoexp;
                  if (partUnit) {
                        if (partUnit->GetNumMounts()) {
                              if (partUnit->mounts[0].type==playerUnit->mounts[i].type) {
                                    selectable = true;
                                    selectableCount++;
                                    mount = i;
                              }
                        }
                  }else {
                        if(matchCargoToWeapon(m_part.content, unitName)) {
                              selectable = true;
                              selectableCount++;
                              mount = i;
                        }
                  }
        } else {
                  // Nothing at this mount point.
                  const std::string temp = lookupMountSize(playerUnit->mounts[i].size);
                  mountName=tostring(i+1)+" (Empty) "+ temp.c_str();
           }

            // Now we add the cell.  Note that "selectable" is stored in the tag property.
            const GFXColor mountColor = (selectable? MOUNT_POINT_FULL():MOUNT_POINT_NO_SELECT());
            picker->addCell(new SimplePickerCell(mountName, "", mountColor, (selectable?1:0)));
    }

      assert(selectableCount > 0);        // We should have found at least one unit mounted.
      if(selectableCount > 1) {
            // Need to have the user choose.
            dialog->run();
      } else {
            // Get rid of the dialog -- we only have one choice.
            delete dialog;
        gotSelectedMount(mount);
      }
}

// Check, and verify user wants Sell Upgrade transaction.  Returns true if more input is required.
bool BaseComputer::SellUpgradeOperation::checkTransaction(void) {
    Unit* playerUnit = m_parent.m_player.GetUnit();
    if(!playerUnit) {
        finish();
        return false; // We want the window to die to avoid accessing of deleted memory. 
    }

    double percent;         // Temp.  Not used.
    if( playerUnit->canDowngrade(m_newPart, m_selectedMount, m_selectedTurret, percent, m_downgradeLimiter) ) {
        // We can sell the upgrade.
        concludeTransaction();
            return false;
    } else {
        showYesNoQuestion("You don't have exactly what you wish to sell.  Continue?",
            this, CONFIRM_ID);
            return true;
    }
}
 
// Finish the transaction.
void BaseComputer::SellUpgradeOperation::concludeTransaction(void) {
    Unit* playerUnit = m_parent.m_player.GetUnit();
    Unit* baseUnit = m_parent.m_base.GetUnit();
    if(!(playerUnit && baseUnit)) {
        finish();
        return;
    }

    // Get the upgrade percentage to calculate the full price.
    double percent;
    playerUnit->canDowngrade(m_newPart, m_selectedMount, m_selectedTurret, percent, m_downgradeLimiter);
    const float price = m_part.price * usedValue(percent);
    // Adjust the money.
    _Universe->AccessCockpit()->credits += price;
    // Change the ship.
    if(playerUnit->Downgrade(m_newPart, m_selectedMount, m_selectedTurret, percent, m_downgradeLimiter)) {
        // Remove the item from the ship, since we sold it, and add it to the base.
        m_part.quantity = 1;
        m_part.price = baseUnit->PriceCargo(m_part.content);
        baseUnit->AddCargo(m_part);
    }

    updateUI();

    finish();
}

extern int GetModeFromName(const char *);
// Buy a ship upgrade.
bool BaseComputer::buyUpgrade(const EventCommandId& command, Control* control) {
    // Take care of Basic Repair, which is implemented entirely in this module.
    Cargo* item = selectedItem();
      if (item) {
            Unit * playerUnit = m_player.GetUnit();
            if(item->content == BASIC_REPAIR_NAME) {
                  if (playerUnit) {
                        Cargo itemCopy = *item;     // Copy this because we reload master list before we need it.
                        BasicRepair(playerUnit);
                        assert(m_selectedList != NULL);                       
                        loadUpgradeControls();
                        updateTransactionControls(itemCopy, true);
                        m_selectedList->picker->selectCell(NULL);       // Turn off selection.
                  }
                  return true;
            }
            if (!isWeapon(item->category)) {
                  if (playerUnit) {
                        Unit * baseUnit = m_base.GetUnit();
                        if (baseUnit) {
                              Cargo itemCopy = *item;     // Copy this because we reload master list before we need it.
                              const int quantity=1;
                              playerUnit->BuyCargo(item->content, quantity, baseUnit, _Universe->AccessCockpit()->credits);
                              playerUnit->Upgrade(item->content,0,0,true,false);
                              /*
                                        const Unit * upgrade=getUnitFromUpgradeName(item->content,playerUnit->faction);                                            
                                        double percentage=0;
                                        playerUnit->Upgrade(upgrade,0,0,GetModeFromName(item->content.c_str()),true,percentage,makeTemplateUpgrade(playerUnit->name,playerUnit->faction));                                        
                              */
                              //RecomputeUnitUpgrades(playerUnit); //Narfed damage
                              loadUpgradeControls();
                              updateTransactionControls(itemCopy, true);
                        }
                  }
                  return true;
            }
      }

    // This complicated operation is done in a separate object.
    BuyUpgradeOperation* op = new BuyUpgradeOperation(*this);
    op->start();

    return true;
}

// Sell an upgrade on your ship.
bool BaseComputer::sellUpgrade(const EventCommandId& command, Control* control) {
      Cargo* item = selectedItem();
      if (item) {
            if (!isWeapon(item->category)) {
                  Cargo sold;
                  const int quantity=1;
                  Unit * playerUnit = m_player.GetUnit();
                  Unit * baseUnit = m_base.GetUnit();
                  if (baseUnit&&playerUnit) {
                        Cargo itemCopy = *item;     // Copy this because we reload master list before we need it.
                        playerUnit->SellCargo(item->content, quantity, _Universe->AccessCockpit()->credits, sold, baseUnit);
                        RecomputeUnitUpgrades(playerUnit);
                        loadUpgradeControls();
                        updateTransactionControls(itemCopy, true);                        
                  }
                  return true;
            }
      }
    // This complicated operation is done in a separate object.
    SellUpgradeOperation* op = new SellUpgradeOperation(*this);
    op->start();

    return true;
}



// Sell an upgrade on your ship.
bool BaseComputer::fixUpgrade(const EventCommandId& command, Control* control) {
      Cargo* item = selectedItem();
        Unit * playerUnit = m_player.GetUnit();
        Unit * baseUnit = m_base.GetUnit();
      if (baseUnit&&playerUnit&&item) {
            if (isWeapon(item->category)) {
                const Unit * upgrade=getUnitFromUpgradeName(item->content,playerUnit->faction);    
                    if (upgrade->GetNumMounts()) {
                      const Mount * mnt = &upgrade->mounts[0];
                      unsigned int nummounts=playerUnit->GetNumMounts();
                      for (unsigned int i=0;i<nummounts;++i) {
                        if (mnt->type->weapon_name==playerUnit->mounts[i].type->weapon_name) {
                          bool complete=false;
                          if (playerUnit->mounts[i].status==Mount::DESTROYED){
                            playerUnit->mounts[i].status=Mount::INACTIVE;
                            complete=true;
                          }
                          if (playerUnit->mounts[i].functionality<1.0f){
                            playerUnit->mounts[i].functionality=1.0f;
                            complete=true;
                          }
                          if (playerUnit->mounts[i].maxfunctionality<1.0f){
                            playerUnit->mounts[i].maxfunctionality=1.0f;
                            complete=true;
                          }
                          if (complete) break;
                        }
                      }
                      Cargo itemCopy=*item;
                      loadUpgradeControls();
                      updateTransactionControls(itemCopy, true);

                    }                
                }else {
                  Cargo sold;
                  const int quantity=1;
                        bool notadditive=(item->content.find("add_")!=0&&item->content.find("mult_")!=0);
                  if (notadditive||item->category.find(DamagedCategory)==0) {
                        Cargo itemCopy = *item;     // Copy this because we reload master list before we need it.
                                
                        //playerUnit->SellCargo(item->content, quantity, _Universe->AccessCockpit()->credits, sold, baseUnit);
                        //RecomputeUnitUpgrades(playerUnit);
                                const Unit * un=  getUnitFromUpgradeName(item->content,playerUnit->faction);
                                if (un) {
                                  double percentage = PercentOperational(playerUnit,item->content,item->category);
                                  double price = RepairPrice(percentage,baseUnit->PriceCargo(item->content));
                                  if (price<=_Universe->AccessCockpit()->credits) {
                                    _Universe->AccessCockpit()->credits-=price;                                    
                                    if (notadditive)
                                      playerUnit->Upgrade(un,0,0,0,true,percentage,makeTemplateUpgrade(playerUnit->name,playerUnit->faction));
                                    if (item->category.find(DamagedCategory)==0) {
                                      unsigned int where;
                                      Cargo * c=playerUnit->GetCargo(item->content,where);
                                      if (c) c->category="upgrades/"+c->category.substr(strlen(DamagedCategory));
                                      
                                    }
                                    if (PercentOperational(playerUnit,item->content)<1.0) {
                                      emergency_downgrade_mode="EMERGENCY MODE ";
                                    }
                                  }
                                  
                                  loadUpgradeControls();
                                  updateTransactionControls(itemCopy, true);                    
                                }
                  }
                  return true;
            }
      }

    return true;
}

// Change controls to SHIP_DEALER mode.
bool BaseComputer::changeToShipDealerMode(const EventCommandId& command, Control* control) {
    if(m_currentDisplay != SHIP_DEALER) {
        switchToControls(SHIP_DEALER);
        loadShipDealerControls();
        updateTransactionControlsForSelection(NULL);
    }
    return true;
}

// Create a Cargo for the specified starship.
Cargo CreateCargoForOwnerStarship(Cockpit* cockpit, int i) {
    Cargo cargo;
    cargo.quantity = 1;
    cargo.volume = 1;
    cargo.price = 0;

    bool needsTransport = true;

    if(i+1 < cockpit->unitfilename.size()) {
        if(cockpit->unitfilename[i+1] == _Universe->activeStarSystem()->getFileName()) {
            // Ship is in this system -- doesn't need transport.
            needsTransport = false;
        }
    }

    if(needsTransport) {
        static const float shipping_price = XMLSupport::parse_float (vs_config->getVariable ("physics","shipping_price","6000"));
        cargo.price = shipping_price;
    }

    cargo.content = cockpit->unitfilename[i];
    cargo.category = "starships/My_Fleet"; 

    return cargo;
}

// Create a Cargo for an owned starship from the name.
Cargo CreateCargoForOwnerStarshipName(Cockpit* cockpit, std::string name, int& index) {
    for(int i=1; i < cockpit->unitfilename.size(); i+=2) {
        if(cockpit->unitfilename[i]==name) {
            index = i;
            return CreateCargoForOwnerStarship(cockpit, i);
        }
    }

  // Didn't find it.
  return Cargo();
}


void SwapInNewShipName(Cockpit* cockpit, const std::string& newFileName, int swappingShipsIndex) {
    Unit* parent = cockpit->GetParent();
    if (parent) {
        if (swappingShipsIndex != -1) {
            while (cockpit->unitfilename.size() <= swappingShipsIndex+1) {
                cockpit->unitfilename.push_back("");
            } 
            cockpit->unitfilename[swappingShipsIndex] = parent->name;
            cockpit->unitfilename[swappingShipsIndex+1] = _Universe->activeStarSystem()->getFileName();
            for(int i=1; i < cockpit->unitfilename.size();i+=2) {
                if(cockpit->unitfilename[i] == newFileName) {
                    cockpit->unitfilename.erase(cockpit->unitfilename.begin()+i);
                    if (i<cockpit->unitfilename.size()) {
                        cockpit->unitfilename.erase(cockpit->unitfilename.begin()+i);//get rid of system
                    }
                    i -= 2;//then +=2;
                }
            }
        } else {
            cockpit->unitfilename.push_back(parent->name);
            cockpit->unitfilename.push_back(_Universe->activeStarSystem()->getFileName());
        }
    } else if (swappingShipsIndex != -1) {//if parent is dead
        if (cockpit->unitfilename.size() > swappingShipsIndex) { //erase the ship we have
            cockpit->unitfilename.erase(cockpit->unitfilename.begin()+swappingShipsIndex);
        }
        if (cockpit->unitfilename.size() > swappingShipsIndex) {
            cockpit->unitfilename.erase(cockpit->unitfilename.begin()+swappingShipsIndex);
        }
    }
    cockpit->unitfilename.front() = newFileName;
}


string buildShipDescription(Cargo &item,std::string & texturedescription) {
      //load the Unit
            string newModifications;
            if(item.category.find("My_Fleet") != string::npos) {
            // Player owns this starship.
            newModifications = _Universe->AccessCockpit()->GetUnitModifications();
            }
            Flightgroup* flightGroup=new Flightgroup();
            int fgsNumber=0;
            current_unit_load_mode=NO_MESH;
            Unit* newPart = UnitFactory::createUnit(item.content.c_str(), false, 0, newModifications,
                                    flightGroup,fgsNumber);
            current_unit_load_mode=DEFAULT;
      string hudimage;
        if (newPart->getHudImage()) {
           if (newPart->getHudImage()->getTexture()) {
              hudimage = newPart->getHudImage()->getTexture()->texfilename;
              string::size_type doublepng = hudimage.find(".png");
              if (doublepng==string::npos) doublepng=hudimage.find(".jpg");
              if (doublepng!=string::npos) {
                 std::string shipname= hudimage.substr(doublepng+4);
                 if (shipname.find(".png")!=string::npos||shipname.find(".jpg")!=string::npos) {
                    hudimage = hudimage.substr(0,doublepng+4-shipname.length());
                    string shipnoblank = item.content.substr(0,item.content.find("."));
                    string::size_type ship = hudimage.rfind(shipnoblank);
                    if (ship!=string::npos) {
                       texturedescription="../units/"+shipnoblank+"/"+shipname;
                    }else {
                       texturedescription=shipname;
                    }                    
                 }
              }else {
                 texturedescription = hudimage.substr(hudimage.find(item.content));
                 
              }
           }
        }
        std::string str;
      showUnitStats(newPart,str,0,0,item);
      delete newPart;
        if (texturedescription!="")
           str+="@"+texturedescription+"@";
      return str; 
}

//UNDER CONSTRUCTION
string buildUpgradeDescription(Cargo &item) {
      //load the Unit
    string blnk; //modifications to an upgrade item???
    Flightgroup* flightGroup=new Flightgroup();//sigh
    int fgsNumber=0;
      current_unit_load_mode=NO_MESH;
    Unit* newPart = UnitFactory::createUnit(item.content.c_str(), false, FactionUtil::GetFaction("upgrades"),blnk,flightGroup,fgsNumber);
      current_unit_load_mode=DEFAULT;
      string str="";
      str+=item.description;
      showUnitStats(newPart,str,0,1,item);
      delete newPart;
      return str; 
}


string buildCargoDescription(Cargo &item){
      //load the Unit
    string blnk; //modifications to an upgrade item???
    /*
    Flightgroup* flightGroup=new Flightgroup();//sigh
    int fgsNumber=0;
      current_unit_load_mode=NO_MESH;
    Unit* newPart = UnitFactory::createUnit(item.content.c_str(), false, FactionUtil::GetFaction("upgrades"),blnk,flightGroup,fgsNumber);
      current_unit_load_mode=DEFAULT;
      string str="";
      string hudimage;
    string texturedescription;
      if(newPart->getHudImage()) {
      if(newPart->getHudImage()->getTexture()) {
        hudimage = newPart->getHudImage()->getTexture()->texfilename;
        string::size_type doublepng = hudimage.find(".png");
        if(doublepng==string::npos) doublepng=hudimage.find(".jpg");
        if(doublepng!=string::npos) {
          std::string shipname= hudimage.substr(doublepng+4);
          if(shipname.find(".png")!=string::npos||shipname.find(".jpg")!=string::npos) {
            hudimage = hudimage.substr(0,doublepng+4-shipname.length());
            string shipnoblank = item.content.substr(0,item.content.find("."));
            string::size_type ship = hudimage.rfind(shipnoblank);
            if(ship!=string::npos) {
              texturedescription="../units/"+shipnoblank+"/"+shipname;
            }else{
              texturedescription=shipname;
            }                    
              }
            }else {
          texturedescription = hudimage.substr(hudimage.find(item.content));
            }
        }
      }
      delete newPart;
    if(texturedescription!=""){
      str+="@"+texturedescription+"@";
    }

    
    str+=item.description;
    return str;
    */
      return item.description;
}
// Load the controls for the SHIP_DEALER display.
void BaseComputer::loadShipDealerControls(void) {
    // Make sure there's nothing in the transaction lists.
    resetTransactionLists();

    // Set up the base dealer's transaction list.
      std::vector<std::string> filtervec;
      filtervec.push_back("starships");
    loadMasterList(m_base.GetUnit(), filtervec, std::vector<std::string>(), true, m_transList1);
    
    // Add in the starships owned by this player.
    Cockpit* cockpit = _Universe->AccessCockpit();
    for (int i=1; i<cockpit->unitfilename.size(); i+=2) {
        CargoColor cargoColor;
        cargoColor.cargo=CreateCargoForOwnerStarship(cockpit, i);
        m_transList1.masterList.push_back(cargoColor);
    }
    
    //remove the descriptions, we don't build them all here, it is a time consuming operation
    vector<CargoColor>* items = &m_transList1.masterList;
    for (vector<CargoColor>::iterator it=items->begin();it!=items->end();it++) {
      (*it).cargo.description="";
            
      }


    // Load the picker from the master list.
    SimplePicker* basePicker = static_cast<SimplePicker*>( window()->findControlById("Ships") );
    assert(basePicker != NULL);
    loadListPicker(m_transList1, *basePicker, BUY_SHIP, true);

    // Make the title right.
    recalcTitle();
}
bool BaseComputer::sellShip(const EventCommandId& command, Control* control) {
    Unit* playerUnit = m_player.GetUnit();
    Cockpit * cockpit = _Universe->isPlayerStarship(playerUnit);
    Unit* baseUnit = m_base.GetUnit();
    Cargo* item = selectedItem();
    if(!(playerUnit && baseUnit && item && cockpit)) {
        return true;
    }
    unsigned int tempInt=1;
    Cargo* shipCargo = baseUnit->GetCargo(item->content, tempInt);    
    if (shipCargo==NULL) {
      shipCargo=UniverseUtil::GetMasterPartList()->GetCargo(item->content,tempInt);
    }
    if (shipCargo) {
      //now we can actually do the selling
      for(int i=1; i < cockpit->unitfilename.size(); i+=2) {      
        if (cockpit->unitfilename[i]==item->content) {
          cockpit->unitfilename.erase(cockpit->unitfilename.begin()+i);
          cockpit->unitfilename.erase(cockpit->unitfilename.begin()+i);
          static float shipSellback=XMLSupport::parse_float(vs_config->getVariable("economics","ship_sellback_price",".5"));
          cockpit->credits+=shipSellback*shipCargo->price;// sellback cost
          cockpit->credits-=item->price;//transportation cost
          break;
        }
      }
      loadShipDealerControls();
      updateTransactionControlsForSelection(NULL);
      return true;
    }
    return false;
}
// Buy ship from the base.
bool BaseComputer::buyShip(const EventCommandId& command, Control* control) {
    Unit* playerUnit = m_player.GetUnit();
    Unit* baseUnit = m_base.GetUnit();
    Cargo* item = selectedItem();
    if(!(playerUnit && baseUnit && item)) {
        return true;
    }

    unsigned int tempInt;           // Not used.
    Cargo* shipCargo = baseUnit->GetCargo(item->content, tempInt);
    Cargo myFleetShipCargo;
    int swappingShipsIndex = -1;
    if(item->category.find("My_Fleet") != string::npos) {
        // Player owns this starship.
        shipCargo = &myFleetShipCargo;
        myFleetShipCargo = CreateCargoForOwnerStarshipName(_Universe->AccessCockpit(), item->content, swappingShipsIndex);
        if(shipCargo->content.empty()) {
            // Something happened -- can't find ship by name.
            shipCargo = NULL;
            swappingShipsIndex = -1;
        }
    }

    if(shipCargo) {
        if (shipCargo->price < _Universe->AccessCockpit()->credits) {
            Flightgroup* flightGroup = playerUnit->getFlightgroup();
            int fgsNumber = 0;
            if (flightGroup != NULL) {
                fgsNumber = flightGroup->nr_ships;
                flightGroup->nr_ships++;
                flightGroup->nr_ships_left++;
            }
            string newModifications;
            std::string tmpnam=CurrentSaveGameName;
            if(swappingShipsIndex != -1) {//if we're swapping not buying load the olde one
                newModifications = _Universe->AccessCockpit()->GetUnitModifications();
                CurrentSaveGameName="~"+CurrentSaveGameName;
            }
            WriteSaveGame(_Universe->AccessCockpit(), true);//oops saved game last time at wrong place
            UniverseUtil::StopAllSounds();
            UniverseUtil::playSound("sales/salespitch"+item->content.substr(0,item->content.find("."))+"accept.wav",QVector(0,0,0),Vector(0,0,0));
            Unit* newPart = 
               UnitFactory::createUnit(item->content.c_str(), 
                                       false, 
                                       baseUnit->faction, 
                                       newModifications,
                                       flightGroup,
                                       fgsNumber);
            CurrentSaveGameName=tmpnam;
            newPart->SetFaction(playerUnit->faction);
            if (newPart->name != LOAD_FAILED) {
                if (newPart->nummesh() > 0) {
                    _Universe->AccessCockpit()->credits -= shipCargo->price;
                    newPart->curr_physical_state = playerUnit->curr_physical_state;
                    newPart->SetPosAndCumPos(UniverseUtil::SafeEntrancePoint(playerUnit->Position(),newPart->rSize()));
                    newPart->prev_physical_state = playerUnit->prev_physical_state;
                    _Universe->activeStarSystem()->AddUnit(newPart);
                    SwapInNewShipName(_Universe->AccessCockpit(), item->content, swappingShipsIndex);
                    for (int j=0;j<2;++j) {
                      for (int i=playerUnit->numCargo()-1;i>=0;--i) {
                        Cargo c = playerUnit->GetCargo(i);
                        if ((c.mission!=0&&j==0)||(c.mission==0&&j==1&&c.category.find("upgrades")!=0)) {
                          for (int k=c.quantity;k>0;--k) {
                            c.quantity=k;
                            if (newPart->CanAddCargo(c)) {
                              newPart->AddCargo(c);
                              playerUnit->RemoveCargo(i,c.quantity,true);
                              break;
                            }
                          }
                        }
                      }
                    }                    
                    WriteSaveGame(_Universe->AccessCockpit(), true);//oops saved game last time at wrong place

                    _Universe->AccessCockpit()->SetParent(newPart, item->content.c_str(), _Universe->AccessCockpit()->GetUnitModifications().c_str(),
                        playerUnit->curr_physical_state.position);//absolutely NO NO NO modifications...you got this baby clean off the slate

                    // We now put the player in space.
                    SwitchUnits(NULL, newPart);
                    playerUnit->UnDock(baseUnit);
                    m_player.SetUnit(newPart);
                    WriteSaveGame(_Universe->AccessCockpit(), true);
                              if (baseUnit)
                                    newPart->ForceDock(baseUnit,0);
                              CurrentBaseUnitSet(newPart);
//                            if (BaseInterface::CurrentBase)
//                                  BaseInterface::CurrentBase->caller.SetUnit(newPart);
                              m_player.SetUnit(newPart);
                    static bool persistent_missions_across_ship_switch=XMLSupport::parse_bool(vs_config->getVariable("general","persistent_mission_across_ship_switch","true"));
                    if (persistent_missions_across_ship_switch){
                      for (int i=active_missions.size()-1;i>0;--i){// don't terminate zeroth mission
                        if (active_missions[i]->player_num==_Universe->CurrentCockpit())
                          active_missions[i]->terminateMission();
                      }
                    
                      _Universe->AccessCockpit()->savegame->LoadSavedMissions();
                    }
                    newPart=NULL;
                    playerUnit->Kill();
                    window()->close();
//                    TerminateCurrentBase();  //BaseInterface::CurrentBase->Terminate();
                              
                    return true;
                }
            }
            newPart->Kill();
            newPart = NULL;
        }
    }

    return true;
}



// Change controls to INFO mode.
bool BaseComputer::changeToInfoMode(const EventCommandId& command, Control* control) {
    if(m_currentDisplay != INFO) {
        switchToControls(INFO);
        // Initialize description with player info.
        window()->sendCommand("ShowPlayerInfo", NULL);
            recalcTitle();
    }
    return true;
}

// Faction colors 2-Sept-03.  mbyron.
/*
 0. r=0.5 g=0.5 b=1
 1. r=0 g=0 b=1
 2. r=0 g=1 b=0
 3. r=0.5 g=0.5 b=1
 4. r=0.75 g=0.5 b=0.25
 5. r=0 g=0.5 b=1
 6. r=0.5 g=0 b=1
 7. r=0.5 g=0.5 b=1
 8. r=0.5 g=0.5 b=1
 9. r=1 g=0.5 b=0
10. r=0.4 g=0.2 b=0.7
11. r=1 g=1 b=1
12. r=0.5 g=0.5 b=1
13. r=1 g=0 b=0
14. r=0.5 g=0.5 b=1
*/

// Given a faction number, return a PaintText color command for the faction.
// This lightens up the faction colors to make them more easily seen on the dark background.
static std::string factionColorTextString(int faction) {
            // The following gets the spark (faction) color.
            const float *spark = FactionUtil::GetSparkColor(faction);

            // This line puts the faction colors on the std out.
            // printf("%2d. r=%g g=%g b=%g\n", faction, spark[0], spark[1], spark[2]);
            
            // Brighten up the raw colors by multiplying each channel by 2/3, then adding back 1/3.
            // The darker colors are too hard to read.
            std::string result = colorsToCommandString(spark[0]/1.5+1.0/3, spark[1]/1.5+1.0/3, spark[2]/1.5+1.0/3);

            return result;
}

// Show the player's basic information.
bool BaseComputer::showPlayerInfo(const EventCommandId& command, Control* control) {
    // Heading.
    string text = "#b#Factions:#-b#n1.7#";

    // Number of kills for each faction.
    vector<float>* killList = &_Universe->AccessCockpit()->savegame->getMissionData(string("kills"));

      // Make everything bold.
      text += "#b#";

    // A line for each faction.
    const int numFactions = FactionUtil::GetNumFactions();
    int i = 0;
    static string disallowedFactions=vs_config->getVariable("graphics","unprintable_factions","");
    for(; i<numFactions; i++) {
            Unit *currentplayer=UniverseUtil::getPlayerX(UniverseUtil::getCurrentPlayer());
            float relation=0;
                if (disallowedFactions.find(FactionUtil::GetFactionName(i))!=string::npos) {
                  continue;                 
                }

            if (currentplayer) {
              relation = FactionUtil::GetIntRelation(i, currentplayer->faction );
            }
                if (relation<-1) relation=-1;
                if (relation>1) relation=1;
//        relation = relation * 0.5;
//        relation = relation + 0.5;
                const int percent = (int)(relation * 100.0);

            // Faction name.
            text += factionColorTextString(i) + FactionUtil::GetFactionName(i) + ":#-c  ";

            // Relation color.
            float normRelation = ( relation + 1 ) / 2;                  // Move relation value into 0-1 range.
            normRelation = guiMax(0, guiMin(1, normRelation));    // Make *sure* it's in the right range.
            text += colorsToCommandString(1-normRelation, normRelation, guiMin(1-normRelation, normRelation));

            // End the line.
            text += XMLSupport::tostring(percent) + "#-c";
        if (i < killList->size()) {
            text += ", kills: " + XMLSupport::tostring((int)(*killList)[i]);
        }
        text += "#n#";
    }

    // Total Kills if we have it.
    if (i < killList->size()) {
        text += "#n##b#Total Kills: " + XMLSupport::tostring((int)(*killList)[i]) + "#-b#";                                         
    }
    // Put this in the description.
    StaticDisplay* desc = static_cast<StaticDisplay*>( window()->findControlById("Description") );
    assert(desc != NULL);
    desc->setText(text);

    return true;
}



//does not work with negative numbers!!
void prettyPrintFloat(char * buffer,float f, int digitsBefore, int digitsAfter) {
      
      float dbgval=f;
      int bufferPos=0;
      if (!FINITE(f)) {
            buffer[0]='n';
            buffer[1]='/';
            buffer[2]='a';
            buffer[3]='\0';
            return;
      }
      if (f<0) {
            buffer[0]='-';
            bufferPos=1;
            f=(-f);
      }
      float temp=f;
      int before=0;
      while (temp>=1.0f) {
            before++;
            temp/=10.0f;
            }
      //printf("before=%i\n",before);
      while (before<digitsBefore) {
            buffer[bufferPos++]='0';
            digitsBefore--;
      }
      if (before) {
            for (int p=before;p>0;p--) {
            temp=f;
            float substractor=1;
            for (int i=0;i<p-1;i++) { temp/=10.0f;  substractor*=10.0 ; }//whe cant't cast to int before in case of overflow
            int digit= ((int)temp)%10 ;
            buffer[bufferPos++]='0' + digit;
            //reason for the folowing line: otherwise the  "((int)temp)%10" may overflow when converting 
            f=f-((float)digit*substractor); 
            if ( (p!=1) && (p%3 ==1) ) buffer[bufferPos++]=',';         
            }
      
      }
      else {
            buffer[bufferPos++]='0';
      }
      if (digitsAfter==0) {
            buffer[bufferPos]=0;
            return;
      }
      buffer[bufferPos++]='.';
      temp=f;
      for (int i=0;i<digitsAfter;i++) {
            temp*=10;
            buffer[bufferPos++]='0'+ (  ((int)temp)%10 );
      }
      buffer[bufferPos]=0;
      //printf("******************* %f is, I think %s\n",dbgval,buffer);
}

#define PRETTY_ADD(str,val,digits) \
      text+="#n#";  \
      text+=prefix; \
      text+=str; \
      prettyPrintFloat(conversionBuffer,val,0,digits); \
      text+=conversionBuffer;

#define PRETTY_ADDN(str,val,digits) \
      text+=str; \
      prettyPrintFloat(conversionBuffer,val,0,digits); \
      text+=conversionBuffer;

#define PRETTY_ADDU(str,val,digits,unit) \
      text+="#n#";  \
      text+=prefix; \
      text+=str; \
      prettyPrintFloat(conversionBuffer,val,0,digits); \
      text+=conversionBuffer; \
      text+=" "; \
      text+=unit;



static const char *WeaponTypeStrings[]= {
      "UNKNOWN",
      "BEAM",
      "BALL",
      "BOLT",
      "PROJECTILE"
      };

void showUnitStats(Unit * playerUnit,string &text,int subunitlevel, int mode, Cargo & item) {
      static Unit* blankUnit = UnitFactory::createUnit("blank",1,FactionUtil::GetFactionIndex("upgrades"));
      static float game_speed = XMLSupport::parse_float (vs_config->getVariable("physics","game_speed","1"));
      static float game_accel = XMLSupport::parse_float (vs_config->getVariable("physics","game_accel","1"));
      static float warpenratio = XMLSupport::parse_float (vs_config->getVariable("physics","warp_energy_multiplier","0.12"));
      static float warpbleed = XMLSupport::parse_float (vs_config->getVariable("physics","warpbleed","20"));
    static float shield_maintenance_cost=XMLSupport::parse_float(vs_config->getVariable("physics","shield_maintenance_charge",".25"));
    static bool shields_require_power=XMLSupport::parse_bool(vs_config->getVariable ("physics","shields_require_passive_recharge_maintenance","true"));
      static float shieldenergycap = XMLSupport::parse_float(vs_config->getVariable ("physics","shield_energy_capacitance",".2"));

      float Wconv= warpenratio==0.0?0.0:(1.0/warpenratio); // converts from reactor to warp energy scales
      char conversionBuffer[2048];
      string prefix="";
      for (int i=0;i<subunitlevel;i++) prefix+="   ";
      //get conversion factor for damage -> MJ; note that shield and reactor stats use a different constant.
      float VSDM = XMLSupport::parse_float (vs_config->getVariable ("physics","kilojoules_per_unit_damage","5400"))/1000.0;
      float RSconverter = 100; // 100MJ per reactor or shield recharge energy unit
      float totalWeaponEnergyUsage=0;
      float totalWeaponDamage=0;
      string MPLdesc="";
      string statcolor="#c.75:.9:1#";
      string nametemp="";
      string model="";
      int nameindex=0;
      int replacement_mode=-1;
      if(mode){
            replacement_mode=GetModeFromName(item.content.c_str());
            MPLdesc+=text;
            text="";
            string statcolor="#c.75:.9:1#";
            string nametemp="";
            string model="";
            if(item.content == BASIC_REPAIR_NAME){
                  text+=MPLdesc;
                  return;
            }
            nametemp=playerUnit->getFullname();
            if(nametemp==""){
              for(nameindex=0; (nameindex<playerUnit->name.size())&&playerUnit->name[nameindex]!='.';nameindex++){
                    nametemp+=playerUnit->name[nameindex];
              }
            }
            if (nametemp.length()){
                  nametemp[0]=toupper(nametemp[0]);
            }
            nametemp=beautify(nametemp);
            text+=statcolor+"Selected Part: #-c"+nametemp;
            if(item.mass==1){
                  PRETTY_ADDU(statcolor+"Mass: #-c",item.mass,0,"Metric ton.");
            } else {
                  PRETTY_ADDU(statcolor+"Mass: #-c",item.mass,1,"Metric tons.");
            } 
            if(item.volume==1){
                  PRETTY_ADDN(statcolor+"  Space Required: #-c",item.volume,0);
                  text+=" Cubic Meter.#n##n##c0:1:.5#"+prefix+"[DESCRIPTION]#n##-c";
            }else{
                  PRETTY_ADDN(statcolor+"  Space Required: #-c",item.volume,1);
                  text+=" Cubic Meters.#n##n##c0:1:.5#"+prefix+"[DESCRIPTION]#n##-c";
            }
            text+=MPLdesc;
            text+="#n#";
            text+="#n##c0:1:.5#[STATS]#n##-c";
            
      }

      if(!mode){
            for(nameindex=0; (nameindex<playerUnit->name.size())&&playerUnit->name[nameindex]!='.';nameindex++){
                  //nametemp+=playerUnit->name[nameindex];
            }
            nametemp=playerUnit->getFullname();
            if (nametemp.length()){
                  nametemp[0]=toupper(nametemp[0]);
            }
            for(nameindex=nameindex+1;nameindex<playerUnit->name.size();nameindex++){
                  model+=playerUnit->name[nameindex];
            }

            if(model=="blank"){
                  model="Stock";
            }else if (model==""){
                  model="Military Spec.";
            }else if (model=="begin"){
                  model="Stock(Refurbished)";
            } else {
                  model="Military Spec. Variant ("+model+")";
            }
            Cargo * fullname = GetMasterPartList(playerUnit->name.c_str());
            Cargo * milname = GetMasterPartList(nametemp.c_str());
            Cargo * blankname = GetMasterPartList((nametemp+".blank").c_str());
            if(!subunitlevel && (fullname || milname || blankname)){
                  text+="#c0:1:.5#"+prefix+"[NOTES]#n##n##-c";
                  if(fullname){
                        text+=fullname->GetDescription();
                  }else if(blankname){
                        text+=blankname->GetDescription();
                  }else if(milname){
                        text+=milname->GetDescription();
                  }
                  text+="#n#";
            }
            text+="#n##c0:1:.5#"+prefix+"[GENERAL INFORMATION]#n##-c";
            text+= "#n#"+prefix+statcolor+"Class: #-c"+nametemp+statcolor+"  Model: #-c"+model;
            /*  Flightgroup name for unsold or player ships not very important

            text+= " " + playerUnit->getFullname();
            Flightgroup *fg = playerUnit->getFlightgroup();
            if (fg && fg->name!="") {
            text+= " Designation: " +fg->name+ " "+ XMLSupport::tostring (playerUnit->getFgSubnumber());
            }
            */    
            PRETTY_ADDU(statcolor+"Mass: #-c",playerUnit->GetMass(),0,"metric tons");
            // Irrelevant to player as is proportional to mass in our physics system.
            // PRETTY_ADDU("Moment of inertia: ",playerUnit->GetMoment(),2,"tons.mē");
      
      }     
      
      if(mode&&replacement_mode==2&&playerUnit->GetMass()!=blankUnit->GetMass()){
            PRETTY_ADDU(statcolor+"Effective Mass reduced by: #-c",100.0*(1.0-playerUnit->GetMass()),0,"%");
      }

      if(!subunitlevel){
          float vol[2];
          float bvol[2];
          const char * dvol[2]={"Hold","Upgrade"};
          vol[0]=playerUnit->getEmptyCargoVolume();
          vol[1]=playerUnit->getEmptyUpgradeVolume();
          bvol[0]=blankUnit->getEmptyCargoVolume();
          bvol[1]=blankUnit->getEmptyUpgradeVolume();
          for (int index=0;index<2;++index) {
            if(!mode){
                  PRETTY_ADDU(statcolor+" "+dvol[index]+" volume: #-c",vol[index],0,"cubic meters");
            }else{
                  if(bvol[index]!=vol[index]){
                        switch(replacement_mode){
                        case 0: // Replacement or new Module
                              PRETTY_ADDU(statcolor+"Changes "+dvol[index]+" Volume to: #-c",vol[index],0,"cubic meters");
                              break;
                        case 1: // Additive
                              PRETTY_ADDU(statcolor+"Adds #-c",vol[index],0,"cubic meters "+statcolor+"to "+dvol[index]+" Volume #-c");
                              break;
                        case 2: // multiplicative
                              PRETTY_ADDU(statcolor+"Increases "+dvol[index]+" Volume by #-c",100.0*(vol[index]-1),0,"%");
                              break;
                        default: // Failure 
                              text+="Oh dear, this wasn't an upgrade. Please debug code.";
                              break;
                        }
                  }
            }
          }
      }
      
      
      //following lines somewhat borken in terms of semantics for quantity of fuel
      //and policy of upgrades to fuel
      
      if(!mode){
            PRETTY_ADDU(statcolor+"Fuel Capacity: #-c",playerUnit->FuelData(),2,"Metric Tons of Lithium-6");
      }else{
            if(blankUnit->FuelData()!=playerUnit->FuelData()){
                  switch(replacement_mode){
                  case 0: // Replacement or new Module
                        //PRETTY_ADDU(statcolor+"Changes Fuel Capacity to: #-c",playerUnit->FuelData()/1000.0f,0,"Standard Fuel Units");
                        break;
                  case 1: // Additive
                        PRETTY_ADDU(statcolor+"Adds #-c",playerUnit->FuelData(),2,"Metric Tons of Lithium-6 "/*+statcolor+"to Fuel Capacity #-c"*/);
                        break;
                  case 2: // multiplicative
                        //PRETTY_ADDU(statcolor+"Increases Fuel Capacity by #-c",100.0*(playerUnit->FuelData -1),0,"%");
                        break;
                  default: // Failure 
                        text+="Oh dear, this wasn't an upgrade. Please debug code.";
                        break;
                  }
            }
      }

      
      
      
      const Unit::Computer uc=playerUnit->ViewComputerData();
      const Unit::Computer buc=blankUnit->ViewComputerData();
    if(!mode){
            text+="#n##n#"+prefix+"#c0:1:.5#[FLIGHT CHARACTERISTICS]#n##-c";
      text+="#n#"+prefix+statcolor+"Turning Response: #-c";
      }
    if (playerUnit->limits.yaw==playerUnit->limits.pitch &&playerUnit->limits.yaw==playerUnit->limits.roll) {
            prettyPrintFloat(conversionBuffer,playerUnit->limits.yaw/((playerUnit->GetMoment()!=0)?playerUnit->GetMoment():1),0,4);
            if(!mode){
                  text+=conversionBuffer;
                  text+=" radians/second^2 "+statcolor+"(yaw, pitch, roll)#-c";
            } else {
                  if(playerUnit->limits.yaw!=blankUnit->limits.yaw){
                        switch(replacement_mode){
                        case 0: // Replacement or new Module
                              PRETTY_ADDU(statcolor+"#n#Installs maneuvering jets with turning response #-c",playerUnit->limits.yaw,0," radians/second^2 "+statcolor+"(yaw, pitch, roll)#-c");
                              break;
                        case 1: // Additive
                              break;
                        case 2: // multiplicative
                              PRETTY_ADDU(statcolor+"#n#Increases turning response by #-c",100.0*((playerUnit->limits.yaw*180/PI)-1),0,"% "+statcolor+"(yaw, pitch, roll)#-c");
                              break;
                        default: // Failure 
                              text+="Oh dear, this wasn't an upgrade. Please debug code.";
                              break;
                        }
                        
                  }
            }
      } else {
            if(!mode){
                  float moment = (playerUnit->GetMoment()!=0)?playerUnit->GetMoment():1;
                  PRETTY_ADDN(statcolor+" yaw #-c",playerUnit->limits.yaw/(moment),4);
                  PRETTY_ADDN(statcolor+"  pitch #-c",playerUnit->limits.pitch/(moment),4);
                  PRETTY_ADDN(statcolor+"  roll #-c",playerUnit->limits.roll/(moment),4);
                  text+=" radians/second^2";
            } else {
                  if(playerUnit->limits.yaw!=blankUnit->limits.yaw||playerUnit->limits.pitch!=blankUnit->limits.pitch||playerUnit->limits.roll!=blankUnit->limits.roll){
                        switch(replacement_mode){
                        case 0: // Replacement or new Module
                              text+="#n#Replaces existing maneuvering system with one rated at: #-c#n#";
                              PRETTY_ADDN(statcolor+"Yaw #-c",playerUnit->limits.yaw,2);
                              PRETTY_ADDN(statcolor+"  Pitch #-c",playerUnit->limits.pitch,2);
                              PRETTY_ADDN(statcolor+"  Roll #-c",playerUnit->limits.roll,2);
                              text+=" metric-ton*radians/second^2";
                              break;
                        case 1: // Additive
                              text+="#n#Upgrades existing maneuvering system by the following amounts: #-c#n#";
                              PRETTY_ADDN(statcolor+"Yaw #-c",playerUnit->limits.yaw,2);
                              PRETTY_ADDN(statcolor+"  Pitch #-c",playerUnit->limits.pitch,2);
                              PRETTY_ADDN(statcolor+"  Roll #-c",playerUnit->limits.roll,2);
                              text+=" metric-ton*radians/second^2";
                              break;
                        case 2: // multiplicative
                              text+="#n#Increases performance of existing maneuvering system by the following percentages: #-c#n#";
                              PRETTY_ADDN(statcolor+"Yaw #-c",100.0*((playerUnit->limits.yaw*180/PI)-1),0);
                              PRETTY_ADDN(statcolor+"  Pitch #-c",100.0*((playerUnit->limits.pitch*180/PI)-1),0);
                              PRETTY_ADDN(statcolor+"  Roll #-c",100.0*((playerUnit->limits.roll*180/PI)-1),0);
                              break;
                        default: // Failure 
                              text+="Oh dear, this wasn't an upgrade. Please debug code.";
                              break;
                        }
                  }
            }
      }
      
      if(!subunitlevel){
            if(!mode&&(playerUnit->GetMass()!=0)){
                  PRETTY_ADDU(statcolor+"Fore acceleration: #-c",playerUnit->limits.forward/(9.8*playerUnit->GetMass()),2,"gravities");
                  PRETTY_ADDU(statcolor+"Aft acceleration: #-c",playerUnit->limits.retro/(9.8*playerUnit->GetMass()),2,"gravities")
                  if (playerUnit->limits.lateral==playerUnit->limits.vertical) {
                        PRETTY_ADDU(statcolor+"Orthogonal acceleration: #-c",playerUnit->limits.vertical/(9.8*playerUnit->GetMass()),2,"gravities");
                        text+=statcolor+" (vertical and lateral axes)#-c";
            }else {
                        PRETTY_ADDN(statcolor+" Lateral acceleration #-c",playerUnit->limits.lateral/(9.8*playerUnit->GetMass()),2);
                        PRETTY_ADDN(statcolor+" Vertical acceleration #-c",playerUnit->limits.vertical/(9.8*playerUnit->GetMass()),2);
                        text+=" gravities";
            }
                  PRETTY_ADDU(statcolor+"Forward acceleration with Afterburner: #-c",playerUnit->limits.afterburn/(9.8*playerUnit->GetMass()),2,"gravities");
            text.append("#n##n##c0:1:.5#"+prefix+"[GOVERNOR SETTINGS]#n##-c");
            } else {
                  switch(replacement_mode){
                        case 0: // Replacement or new Module
                              if(playerUnit->limits.forward!=blankUnit->limits.forward){
                                    PRETTY_ADDU(statcolor+"Provides forward thrust rated at: #-c",playerUnit->limits.forward/1000.0,2,"MegaNewtons");
                              }
                              if(playerUnit->limits.retro!=blankUnit->limits.retro){
                                    PRETTY_ADDU(statcolor+"Provides aftward thrust rated at: #-c",playerUnit->limits.retro/1000.0,2,"MegaNewtons");
                              }
                              if(playerUnit->limits.vertical!=blankUnit->limits.vertical){
                                    PRETTY_ADDU(statcolor+"Provides vertical thrust rated at: #-c",playerUnit->limits.vertical/1000.0,2,"MegaNewtons");
                              }
                              if(playerUnit->limits.lateral!=blankUnit->limits.lateral){
                                    PRETTY_ADDU(statcolor+"Provides lateral thrust rated at: #-c",playerUnit->limits.lateral/1000.0,2,"MegaNewtons");
                              }
                              if(playerUnit->limits.afterburn!=blankUnit->limits.afterburn){
                                    PRETTY_ADDU(statcolor+"Afterburner thrust rated at: #-c",playerUnit->limits.afterburn/1000.0,2,"MegaNewtons");
                              }
                              break;
                        case 1: // Additive
                              if(playerUnit->limits.forward!=blankUnit->limits.forward){
                                    PRETTY_ADDU(statcolor+"Increases forward thrust rating by: #-c",playerUnit->limits.forward/1000.0,2,"MegaNewtons");
                              }
                              if(playerUnit->limits.retro!=blankUnit->limits.retro){
                                    PRETTY_ADDU(statcolor+"Increases aftward thrust rating by: #-c",playerUnit->limits.retro/1000.0,2,"MegaNewtons");
                              }
                              if(playerUnit->limits.vertical!=blankUnit->limits.vertical){
                                    PRETTY_ADDU(statcolor+"Increases vertical thrust rating by: #-c",playerUnit->limits.vertical/1000.0,2,"MegaNewtons");
                              }
                              if(playerUnit->limits.lateral!=blankUnit->limits.lateral){
                                    PRETTY_ADDU(statcolor+"Increases lateral thrust rating by: #-c",playerUnit->limits.lateral/1000.0,2,"MegaNewtons");
                              }
                              if(playerUnit->limits.afterburn!=blankUnit->limits.afterburn){
                                    PRETTY_ADDU(statcolor+"Increases afterburner thrust rating by: #-c",playerUnit->limits.afterburn/1000.0,2,"MegaNewtons");
                              }
                              break;
                        case 2: // multiplicative
                              if(playerUnit->limits.forward!=blankUnit->limits.forward){
                                    PRETTY_ADDU(statcolor+"Increases forward thrust rating by: #-c",(playerUnit->limits.forward-1)*100,0,"%");
                              }
                              if(playerUnit->limits.retro!=blankUnit->limits.retro){
                                    PRETTY_ADDU(statcolor+"Increases aftward thrust rating by: #-c",(playerUnit->limits.retro-1)*100,0,"%");
                              }
                              if(playerUnit->limits.vertical!=blankUnit->limits.vertical){
                                    PRETTY_ADDU(statcolor+"Increases vertical thrust rating by: #-c",(playerUnit->limits.vertical-1)*100,0,"%");
                              }
                              if(playerUnit->limits.lateral!=blankUnit->limits.lateral){
                                    PRETTY_ADDU(statcolor+"Increases lateral thrust rating by: #-c",(playerUnit->limits.lateral-1)*100,0,"%");
                              }
                              if(playerUnit->limits.afterburn!=blankUnit->limits.afterburn){
                                    PRETTY_ADDU(statcolor+"Afterburner thrust rating by: #-c",(playerUnit->limits.afterburn-1)*100,0,"%");
                              }
                              break;
                        default: // Failure 
                              text+="Oh dear, this wasn't an upgrade. Please debug code.";
                              break;
                  }
            }
            static float non_combat_mode_mult = XMLSupport::parse_float (vs_config->getVariable ("physics","combat_speed_boost","100"));
            if(!mode){
                  PRETTY_ADDU(statcolor+"Max combat speed: #-c",uc.max_speed()*3.6,0,"km/h");
                  PRETTY_ADDU(statcolor+"Max afterburner combat speed: #-c",uc.max_ab_speed()*3.6,0,"km/h");
                  PRETTY_ADDU(statcolor+"Max non-combat speed: #-c",uc.max_speed()*3.6*non_combat_mode_mult,0,"km/h");
            } else {
                  switch(replacement_mode){
                        case 0: // Replacement or new Module
                              if(uc.max_speed()!=buc.max_speed()){
                                    PRETTY_ADDU(statcolor+"Sets max combat speed governor to: #-c",uc.max_speed()*3.6,0,"km/h");
                                    PRETTY_ADDU(statcolor+"Sets max non-combat speed governor to: #-c",uc.max_speed()*3.6*non_combat_mode_mult,0,"km/h");
                              }
                              if(uc.max_ab_speed()!=buc.max_ab_speed()){
                                    PRETTY_ADDU(statcolor+"Sets max afterburner combat speed governor to: #-c",uc.max_ab_speed()*3.6,0,"km/h");
                              }
                              break;
                        case 1: // Additive
                              if(uc.max_speed()!=buc.max_speed()){
                                    PRETTY_ADDU(statcolor+"Increases max combat speed governor setting by: #-c",uc.max_speed()*3.6,0,"km/h");
                                    PRETTY_ADDU(statcolor+"Increases max non-combat speed governor setting by: #-c",uc.max_speed()*3.6*non_combat_mode_mult,0,"km/h");
                              }
                              if(uc.max_ab_speed()!=buc.max_ab_speed()){
                                    PRETTY_ADDU(statcolor+"Increases max afterburner combat speed governor setting by: #-c",uc.max_ab_speed()*3.6,0,"km/h");
                              }
                              break;
                        case 2: // multiplicative
                              if(uc.max_speed()!=buc.max_speed()){
                                    PRETTY_ADDU(statcolor+"Increases max combat speed governor settings by: #-c",100.0*(uc.max_speed()-1),0,"%");
                                    PRETTY_ADDU(statcolor+"Increases max non-combat speed governor settings by: #-c",100.0*(uc.max_speed()-1),0,"%");
                              }
                              if(uc.max_ab_speed()!=buc.max_ab_speed()){
                                    PRETTY_ADDU(statcolor+"Increases max afterburner combat speed governor settings by: #-c",(uc.max_ab_speed()-1)*100,0,"%");
                              }
                              break;
                        default: // Failure 
                              text+="Oh dear, this wasn't an upgrade. Please debug code.";
                              break;
                        }
            }
      }

      if(!mode){
            if (uc.max_yaw_right==uc.max_pitch_up &&uc.max_yaw_right==uc.max_roll_right) {
                  PRETTY_ADD(statcolor+"Max turn rate: #-c",uc.max_yaw_right,2);
                  text+=" Radians/second "+statcolor+"(yaw, pitch, roll)#-c";
                  }
            else {
                  text+=("#n#"+prefix+statcolor+"Max turn rates:#-c ");
                  PRETTY_ADDN(statcolor+"  yaw #-c",uc.max_yaw_right,2);
                  PRETTY_ADDN(statcolor+"  pitch #-c",uc.max_pitch_up,2);
                  PRETTY_ADDN(statcolor+"  roll #-c",uc.max_roll_right,2);
                  text+=" Radians/second";
            }

            text+="#n##n##c0:1:.5#"+prefix+"[TARGETTING SUBSYSTEM]#n##-c";
      } else {
            if(uc.max_yaw_right!=buc.max_yaw_right||uc.max_pitch_up!=buc.max_pitch_up||uc.max_roll_right!=buc.max_roll_right){
                  switch(replacement_mode){
                        case 0: // Replacement or new Module
                              text+=("#n#"+prefix+"Governor settings for maximum turn rates set to: ");
                              PRETTY_ADDN(statcolor+"  yaw #-c",uc.max_yaw_right,2);
                              PRETTY_ADDN(statcolor+"  pitch #-c",uc.max_pitch_up,2);
                              PRETTY_ADDN(statcolor+"  roll #-c",uc.max_roll_right,2);
                              text+=" Radians/second";
                              break;
                        case 1: // Additive
                              text+=("#n#"+prefix+"Governor settings for maximum turn rates increased by: ");
                              PRETTY_ADDN(statcolor+"  yaw #-c",uc.max_yaw_right,2);
                              PRETTY_ADDN(statcolor+"  pitch #-c",uc.max_pitch_up,2);
                              PRETTY_ADDN(statcolor+"  roll #-c",uc.max_roll_right,2);
                              text+=" Radians/second";
                              break;
                        case 2: // multiplicative
                              text+=("#n#"+statcolor+"Increases governor settings for maximum turn rates by: #-c");
                              PRETTY_ADDN(statcolor+"  yaw #-c",100.0*((uc.max_yaw_right*180/PI)-1),0);
                              PRETTY_ADDN(statcolor+"  pitch #-c",100.0*((uc.max_pitch_up*180/PI)-1),0);
                              PRETTY_ADDN(statcolor+"  roll #-c",100.0*((uc.max_roll_right*180/PI)-1),0);
                              text+=" %";
                              break;
                        default: // Failure 
                              text+="Oh dear, this wasn't an upgrade. Please debug code.";
                              break;
                  }
            }
      }
      
      
      if(!mode){
            PRETTY_ADDU(statcolor+"Tracking range: #-c",uc.radar.maxrange/1000,0,"km");
            if((acos(uc.radar.maxcone)*360/PI)<359){
                  PRETTY_ADDU(statcolor+"Tracking cone: #-c",acos(uc.radar.maxcone)*2,2,"Radians");
                  text+=statcolor+" (planar angle: 2 pi means full space)#-c";
            } else {
                  text+="#n#"+prefix+statcolor+"Tracking cone: #-cOMNIDIRECTIONAL";
            }
            PRETTY_ADDU(statcolor+"Assisted targeting cone: #-c",acos(uc.radar.trackingcone)*2,2,"Radians");
            PRETTY_ADDU(statcolor+"Missile locking cone: #-c",acos(uc.radar.lockcone)*2,2,"Radians");
            if(!subunitlevel){
                  // Always zero PRETTY_ADDU("Minimum target size: ",uc.radar.mintargetsize,2,"m");
                  text+="#n#"+prefix+statcolor+"ITTS (Intelligent Target Tracking System) support: #-c";
                  if (uc.itts) text+="yes"; else text+="no";
                  text+="#n#"+prefix+statcolor+"AFHH (Advanced Flag & Hostility Heuristics) support: #-c";
                  if (uc.radar.color) text+="yes"; else text+="no";
            }

            text.append("#n##n##c0:1:.5#"+prefix+"[ENERGY SUBSYSTEM]#n##-c");
      } else {
            switch(replacement_mode){
                  case 0: // Replacement or new Module
                        if(uc.radar.maxrange!=buc.radar.maxrange||uc.radar.maxcone!=buc.radar.maxcone){
                              PRETTY_ADDU(statcolor+"Tracking range: #-c",uc.radar.maxrange/1000,0,"km");
                              if((acos(uc.radar.maxcone)*360/PI)<359){
                              PRETTY_ADDU(statcolor+"Tracking cone: #-c",acos(uc.radar.maxcone)*2,2,"Radians");
                              text+=statcolor+" (planar angle: 2 pi means full space)#-c";
                              } else {
                                    text+="#n#"+prefix+statcolor+"Tracking cone: #-cOMNIDIRECTIONAL";
                              }
                              PRETTY_ADDU(statcolor+"Assisted targeting cone: #-c",acos(uc.radar.trackingcone)*2,2,"Radians");
                              PRETTY_ADDU(statcolor+"Missile locking cone: #-c",acos(uc.radar.lockcone)*2,2,"Radians");
                              text+="#n#"+prefix+statcolor+"ITTS (Intelligent Target Tracking System) support: #-c";
                              if (uc.itts) text+="yes"; else text+="no";
                              text+="#n#"+prefix+statcolor+"AFHH (Advanced Flag & Hostility Heuristics) support: #-c";
                              if (uc.radar.color) text+="yes"; else text+="no";
                        }
                        break;
                  case 1: // Additive
                        break;
                  case 2: // multiplicative
                        break;
                  default: // Failure 
                        text+="Oh dear, this wasn't an upgrade. Please debug code.";
                        break;
            }
      }
      const Unit::UnitJump uj = playerUnit->GetJumpStatus();
      const Unit::UnitJump buj = blankUnit->GetJumpStatus();
      if(!mode){
            float maxshield=totalShieldEnergyCapacitance(playerUnit->shield);
            if(shields_require_power){
                  maxshield=0;
            }
            PRETTY_ADDU(statcolor+"Recharge: #-c",playerUnit->EnergyRechargeData()*RSconverter,0,"MJ/s");
            PRETTY_ADDU(statcolor+"Weapon capacitor bank storage: #-c",((playerUnit->MaxEnergyData()-maxshield)*RSconverter),0,"MJ");
            //note: I found no function to get max warp energy, but since we're docked they are the same
            if(!subunitlevel){
                  PRETTY_ADDU(statcolor+"Warp capacitor bank storage: #-c",playerUnit->GetWarpEnergy()*RSconverter*Wconv,0,"MJ");

                  text+="#n##n##c0:1:.5#"+prefix+"[SPEC SUBSYSTEM]#n##-c";

                  PRETTY_ADDU(statcolor+"Active SPEC Energy Requirements: #-c",uj.insysenergy*RSconverter*Wconv/warpbleed,0,"MJ/s");

            text+="#n##n##c0:1:.5#"+prefix+"[JUMP SUBSYSTEM]#n##-c";
                  
                  if (uj.drive==-2) {
                        text+="#n##c1:.3:.3#No outsystem jump drive present#-c"; // fixed??
                  } else {
                        PRETTY_ADDU(statcolor+"Energy cost for jumpnode travel: #-c",uj.energy*RSconverter*Wconv,0,"MJ");
                        if (uj.delay) {
                              PRETTY_ADDU(statcolor+"Delay: #-c",uj.delay,0,"seconds");
                        }     
                        if (uj.damage>0) {
                              PRETTY_ADDU(statcolor+"Damage to outsystem jump drive: #-c",uj.damage*VSDM,0,"MJ");
                        }
                  }
            }
      } else {
            switch(replacement_mode){
                  case 0: // Replacement or new Module
                        if(playerUnit->EnergyRechargeData()!=blankUnit->EnergyRechargeData()){
                              PRETTY_ADDU(statcolor+"Installs reactor with recharge rate: #-c",playerUnit->EnergyRechargeData()*RSconverter,0,"MJ/s");
                        }
                        if(playerUnit->MaxEnergyData()!=blankUnit->MaxEnergyData()){
                              PRETTY_ADDU(statcolor+"Installs main capacitor bank with storage capacity: #-c",(playerUnit->MaxEnergyData()*RSconverter),0,"MJ");
                        }
                        if(playerUnit->GetWarpEnergy()!=blankUnit->GetWarpEnergy()){
                              PRETTY_ADDU(statcolor+"Installs warp capacitor bank with storage capacity: #-c",playerUnit->GetWarpEnergy()*RSconverter*Wconv,0,"MJ");
                        }
                        if(buj.drive!=uj.drive){
                              text+=statcolor+"#n#Allows travel via Jump Points.#n#Consult your personal info screen for ship specific energy requirements. #-c";
                              //PRETTY_ADDU(statcolor+"Energy cost for jumpnode travel: #-c",uj.energy*RSconverter*Wconv,0,"MJ");
                        }
                        break;
                  case 1: // Additive
                        if(playerUnit->EnergyRechargeData()!=blankUnit->EnergyRechargeData()){
                              PRETTY_ADDU(statcolor+"Increases recharge rate by #-c",playerUnit->EnergyRechargeData()*RSconverter,0,"MJ/s");
                        }
                        if(playerUnit->MaxEnergyData()!=blankUnit->MaxEnergyData()){
                              PRETTY_ADDU(statcolor+"Adds #-c",(playerUnit->MaxEnergyData()*RSconverter),0,"MJ of storage to main capacitor banks");
                        }
                        if(playerUnit->GetWarpEnergy()!=blankUnit->GetWarpEnergy()){
                              PRETTY_ADDU(statcolor+"Adds #-c",playerUnit->GetWarpEnergy()*RSconverter*Wconv,0,"MJ of storage to warp capacitor bank");
                        }
                        break;
                  case 2: // multiplicative
                        if(playerUnit->EnergyRechargeData()!=blankUnit->EnergyRechargeData()){
                              PRETTY_ADDU(statcolor+"Increases reactor recharge rate by #-c",100.0*(playerUnit->EnergyRechargeData()-1),0,"%");
                        }
                        if(playerUnit->MaxEnergyData()!=blankUnit->MaxEnergyData()){
                              PRETTY_ADDU(statcolor+"Increases main capacitor bank storage by #-c",100.0*(playerUnit->MaxEnergyData()-1),0,"%");
                        }
                        if(playerUnit->GetWarpEnergy()!=blankUnit->GetWarpEnergy()){
                              PRETTY_ADDU(statcolor+"Increases warp capacitor bank storage by #-c",(playerUnit->GetWarpEnergy()-1)*100,0,"%");
                        }
                        break;
                  default: // Failure 
                        text+="Oh dear, this wasn't an upgrade. Please debug code.";
                        break;
            }
      }
      
      if(!mode){
            text+="#n##n##c0:1:.5#"+prefix+"[DURABILITY STATISTICS]#n##-c";
            text+="#n#"+prefix+statcolor+"Armor damage resistance:#-c";
      }
      if(mode&&playerUnit->armor.frontlefttop!=blankUnit->armor.frontlefttop){
            switch(replacement_mode){
                  case 0: // Replacement or new Module
                        text+="#n#"+prefix+statcolor+"Replaces existing armor, if any.#n#Armor damage resistance:#-c";
                        break;
                  case 1: // Additive
                        text+="#n#"+prefix+statcolor+"Adds the following to armor damage resistance ratings:#-c";
                        break;
                  case 2: // multiplicative
                        text+="#n#"+prefix+statcolor+"Armor damage resistance increased by following percentages:#-c";
                        break;
                  default: // Failure 
                        text+="Oh dear, this wasn't an upgrade. Please debug code.";
                        break;
            }
      }
      if(!mode||playerUnit->armor.frontrighttop!=blankUnit->armor.frontrighttop){
            PRETTY_ADDU(statcolor+"  Fore-starboard-high - #-c",(mode&&replacement_mode==2)?100.0*(playerUnit->armor.frontrighttop-1):playerUnit->armor.frontrighttop*VSDM,0,"MJ");
            PRETTY_ADDU(statcolor+"  Aft-starboard-high - #-c",(mode&&replacement_mode==2)?100.0*(playerUnit->armor.backrighttop-1):playerUnit->armor.backrighttop*VSDM,0,"MJ"); 
            PRETTY_ADDU(statcolor+"  Fore-port-high - #-c",(mode&&replacement_mode==2)?100.0*(playerUnit->armor.frontlefttop-1):playerUnit->armor.frontlefttop*VSDM,0,"MJ");
            PRETTY_ADDU(statcolor+"  Aft-port-high - #-c",(mode&&replacement_mode==2)?100.0*(playerUnit->armor.backlefttop-1):playerUnit->armor.backlefttop*VSDM,0,"MJ");
            PRETTY_ADDU(statcolor+"  Fore-starboard-low - #-c",(mode&&replacement_mode==2)?100.0*(playerUnit->armor.frontrightbottom-1):playerUnit->armor.frontrightbottom*VSDM,0,"MJ");
            PRETTY_ADDU(statcolor+"  Aft-starboard-low - #-c",(mode&&replacement_mode==2)?100.0*(playerUnit->armor.backrightbottom-1):playerUnit->armor.backrightbottom*VSDM,0,"MJ"); 
            PRETTY_ADDU(statcolor+"  Fore-port-low - #-c",(mode&&replacement_mode==2)?100.0*(playerUnit->armor.frontleftbottom-1):playerUnit->armor.frontleftbottom*VSDM,0,"MJ");
            PRETTY_ADDU(statcolor+"  Aft-port-low - #-c",(mode&&replacement_mode==2)?100.0*(playerUnit->armor.backleftbottom-1):playerUnit->armor.backleftbottom*VSDM,0,"MJ");
      }
      if(!mode){
            PRETTY_ADDU(statcolor+"Sustainable Hull Damage: #-c",playerUnit->GetHull()/(playerUnit->GetHullPercent())*VSDM,0,"MJ");
            if (1!=playerUnit->GetHullPercent()) {
                  PRETTY_ADD("  Current condition: ",playerUnit->GetHullPercent()*100,2);
                  text+="% of normal";
            }     
      }else{
            if(playerUnit->GetHull()!=blankUnit->GetHull()){
                  switch(replacement_mode){
                        case 0: // Replacement or new Module
                              PRETTY_ADDU(statcolor+"New Sustained Hull Damage Rating: #-c",playerUnit->GetHull()/(playerUnit->GetHullPercent())*VSDM,0,"MJ");
                              break;
                        case 1: // Additive
                              PRETTY_ADDU(statcolor+"Increases sustainable hull damage by #-c",playerUnit->GetHull()/(playerUnit->GetHullPercent())*VSDM,0,"MJ");
                              break;
                        case 2: // multiplicative
                              PRETTY_ADDU(statcolor+"Hull Strength increased by #-c",100.0*(playerUnit->GetHull()-1),0,"%");                    
                              break;
                        default: // Failure 
                              text+="Oh dear, this wasn't an upgrade. Please debug code.";
                              break;
                  }     
            }
      }
      if(!mode){
            PRETTY_ADD(statcolor+"Number of shield emitter facings: #-c",playerUnit->shield.number,0);
            text+="#n#"+prefix+statcolor+"Shield protection rating:#-c";
      } else {
            if(playerUnit->shield.shield2fb.frontmax!=blankUnit->shield.shield2fb.frontmax||playerUnit->shield.shield4fbrl.frontmax!=blankUnit->shield.shield4fbrl.frontmax||playerUnit->shield.shield8.frontrightbottommax!=blankUnit->shield.shield8.frontrightbottommax){
                  switch(replacement_mode){
                        case 0: // Replacement or new Module
                              text+="#n#"+prefix+statcolor+"Installs shield with following protection ratings:#-c";
                              break;
                        case 1: // Additive
                              text+="#n#"+prefix+statcolor+"Adds following amounts to shield protection ratings:#-c";
                              break;
                        case 2: // multiplicative
                              text+="#n#"+prefix+statcolor+"Shield protection rating for each emitter increased by listed percentage:#-c";
                              break;
                        default: // Failure 
                              text+="Oh dear, this wasn't an upgrade. Please debug code.";
                              break;
                  }
            }
      }

      switch (playerUnit->shield.number) {
            case 2:
                  if(!mode||playerUnit->shield.shield2fb.frontmax!=blankUnit->shield.shield2fb.frontmax){
                        PRETTY_ADDU(statcolor+"  fore - #-c",(mode&&replacement_mode==2)?(100.0*(playerUnit->shield.shield2fb.backmax-1)):playerUnit->shield.shield2fb.frontmax*VSDM,0, "MJ");
                        PRETTY_ADDU(statcolor+"  aft - #-c",(mode&&replacement_mode==2)?(100.0*(playerUnit->shield.shield2fb.backmax-1)):playerUnit->shield.shield2fb.backmax*VSDM,0, "MJ");
                  }
                  break;
            case 4:
                  if(!mode||playerUnit->shield.shield4fbrl.frontmax!=blankUnit->shield.shield4fbrl.frontmax){
                        PRETTY_ADDU(statcolor+"  fore - #-c",(mode&&replacement_mode==2)?(100.0*(playerUnit->shield.shield4fbrl.frontmax-1)):playerUnit->shield.shield4fbrl.frontmax*VSDM,0,"MJ");
                        PRETTY_ADDU(statcolor+"  aft - #-c",(mode&&replacement_mode==2)?(100.0*(playerUnit->shield.shield4fbrl.backmax-1)):playerUnit->shield.shield4fbrl.backmax*VSDM,0,"MJ");
                        PRETTY_ADDU(statcolor+"  port - #-c",(mode&&replacement_mode==2)?(100.0*(playerUnit->shield.shield4fbrl.leftmax-1)):playerUnit->shield.shield4fbrl.leftmax*VSDM,0,"MJ");
                        PRETTY_ADDU(statcolor+"  starboard - #-c",(mode&&replacement_mode==2)?(100.0*(playerUnit->shield.shield4fbrl.rightmax-1)):playerUnit->shield.shield4fbrl.rightmax*VSDM,0,"MJ");
                  }
                  break;
            case 8:
                  if(!mode||playerUnit->shield.shield8.frontrightbottommax!=blankUnit->shield.shield8.frontrightbottommax){
                        PRETTY_ADDU(statcolor+"  Fore-starboard-high - #-c",(mode&&replacement_mode==2)?100.0*(playerUnit->shield.shield8.frontrighttopmax-1):playerUnit->shield.shield8.frontrighttopmax*VSDM,0,"MJ");
            PRETTY_ADDU(statcolor+"  Aft-starboard-high - #-c",(mode&&replacement_mode==2)?100.0*(playerUnit->shield.shield8.backrighttopmax-1):playerUnit->shield.shield8.backrighttopmax*VSDM,0,"MJ"); 
            PRETTY_ADDU(statcolor+"  Fore-port-high - #-c",(mode&&replacement_mode==2)?100.0*(playerUnit->shield.shield8.frontlefttopmax-1):playerUnit->shield.shield8.frontlefttopmax*VSDM,0,"MJ");
            PRETTY_ADDU(statcolor+"  Aft-port-high - #-c",(mode&&replacement_mode==2)?100.0*(playerUnit->shield.shield8.backlefttopmax-1):playerUnit->shield.shield8.backlefttopmax*VSDM,0,"MJ");
            PRETTY_ADDU(statcolor+"  Fore-starboard-low - #-c",(mode&&replacement_mode==2)?100.0*(playerUnit->shield.shield8.frontrighttopmax-1):playerUnit->shield.shield8.frontrightbottommax*VSDM,0,"MJ");
            PRETTY_ADDU(statcolor+"  Aft-starboard-low - #-c",(mode&&replacement_mode==2)?100.0*(playerUnit->shield.shield8.backrighttopmax-1):playerUnit->shield.shield8.backrightbottommax*VSDM,0,"MJ"); 
            PRETTY_ADDU(statcolor+"  Fore-port-low - #-c",(mode&&replacement_mode==2)?100.0*(playerUnit->shield.shield8.frontlefttopmax-1):playerUnit->shield.shield8.frontleftbottommax*VSDM,0,"MJ");
            PRETTY_ADDU(statcolor+"  Aft-port-low - #-c",(mode&&replacement_mode==2)?100.0*(playerUnit->shield.shield8.backlefttopmax-1):playerUnit->shield.shield8.backleftbottommax*VSDM,0,"MJ");
                  }
                  break;
            default:
                  text+="#c1:.3:.3#Shield model unrecognized#-c";
                  break;
      }
      
      if(!mode){
            PRETTY_ADDU(statcolor+"Shield protection recharge speed: #-c",playerUnit->shield.recharge*VSDM,0,"MJ/s"); 
      }else{
            if(playerUnit->shield.recharge!=blankUnit->shield.recharge){
                  switch(replacement_mode){
                        case 0: // Replacement or new Module
                              PRETTY_ADDU(statcolor+"Shield protection recharge speed set to: #-c",playerUnit->shield.recharge*VSDM,0,"MJ/s"); 
                              break;
                        case 1: // Additive
                              PRETTY_ADDU(statcolor+"Increases shield protection recharge speed by #-c",playerUnit->shield.recharge*VSDM,0,"MJ/s"); 
                              break;
                        case 2: // multiplicative
                              PRETTY_ADDU(statcolor+"Shield protection recharge speed increased by #-c",100.0*(playerUnit->shield.recharge-1),0,"%"); 
                              break;
                        default: // Failure 
                              text+="Oh dear, this wasn't an upgrade. Please debug code.";
                        break;
                  }
            }
      }
      //cloaking device? If we don't have one, no need to mention it ever exists, right?
      if( playerUnit->cloaking!=-1) {
            if(!mode){
                  PRETTY_ADDU(statcolor+"Cloaking device available, energy usage: #-c",playerUnit->image->cloakenergy*RSconverter*Wconv,0,"MJ/s");
            } else {
                  switch(replacement_mode){
                        case 0: // Replacement or new Module
                              PRETTY_ADDU(statcolor+"Installs a cloaking device.#n#  Activated energy usage: #-c",playerUnit->image->cloakenergy*RSconverter*Wconv,0,"MJ/s");
                              break;
                        case 1: // Additive
                              text+="#n#Additive Cloaking...Seems like a bug to me.#n#";
                              break;
                        case 2: // multiplicative
                              text+="#n#Multiplicative Cloaking...Seems like a bug to me.#n#";
                              break;
                        default: // Failure 
                              text+="Oh dear, this wasn't an upgrade. Please debug code.";
                              break;
                  }     
            }
      }

      
      bool anyweapons=false;
      if(!mode){
            text+="#n##n##c0:1:.5#"+prefix+"[ARMAMENT]#n##-c";
            text+=prefix+"MOUNTPOINT RATINGS:";
      }
                  //let's go through all mountpoints
      {     for(int i=0;i<playerUnit->GetNumMounts();i++){
                  if(!mode){
                        PRETTY_ADD(" #c0:1:.3#<#-c",i+1,0);
                        text+="#c0:1:.3#>#-c #c0:1:1#"+lookupMountSize(playerUnit->mounts[i].size)+"#-c";
                  }
                  const weapon_info * wi=playerUnit->mounts[i].type;
                  if(wi&&wi->weapon_name!=""){
                        anyweapons=true;
                  }
            }
      }
      if(!mode){
            text+="#n#"+prefix+"MOUNTED:"; // need brace for namespace issues on VC++
      }
      { if(anyweapons){
            for (int i=0;i<playerUnit->GetNumMounts();i++) {
            const weapon_info * wi=playerUnit->mounts[i].type;
            if ( (!wi) ||  (wi->weapon_name=="") ) {
                  continue;
            } else {
                  //let's display some weapon information
            //    int s=1; //??
            //    int off=0;//??
                  if(!mode){
                        PRETTY_ADD("  #c0:1:.3#<#-c",i+1,0);
                        text+="#c0:1:.3#>#-c ";
                  }
                  text+=wi->weapon_name+": #c0:1:1#"+lookupMountSize(wi->size)+"#-c#c.9:.9:.5#"+WeaponTypeStrings[wi->type]+" #-c";
                  if (wi->Damage<0) text+="#n#"+prefix+statcolor+"   Damage:#-c special";
                  else {
                        PRETTY_ADDU(statcolor+"   Damage: #-c",wi->Damage*VSDM,0,wi->type==weapon_info::BEAM?"MJ/s":"MJ");
                  }
                  PRETTY_ADDU(statcolor+"   Range: #-c",wi->Range,0,"meters");
                  PRETTY_ADDU(statcolor+"   Energy usage: #-c",wi->EnergyRate*RSconverter,0,wi->type==weapon_info::BEAM?"MJ/s":"MJ/shot");
                  
                  PRETTY_ADDU(statcolor+"   Refire delay: #-c",wi->Refire,2,"seconds");
                  if (wi->PhaseDamage>0) {
                        PRETTY_ADDU(statcolor+"   Phase damage: #-c",wi->PhaseDamage*VSDM,2,"MJ");
                  }
                  
                  
                  /*I don't see the point in displaying those
                  PRETTY_ADD("   Pulse speed: ",wi->PulseSpeed,2);
                  PRETTY_ADD("   Radial speed: ",wi->RadialSpeed,2);
                  PRETTY_ADD("   Radius: ",wi->Radius,2);
                  PRETTY_ADD("   Length: ",wi->Length,2);               
                  */

                  //display info specific to some weapons type                
                  switch ( wi->type) {
                        case weapon_info::BALL: //may need ammo
                        case weapon_info::BOLT:
                              if (wi->Damage>0){
                                    totalWeaponDamage+=(wi->Damage/wi->Refire); //damage per second
                              }
                              PRETTY_ADDU(statcolor+"   Exit velocity: #-c",wi->Speed,0,"meters/second");
                              if((100000*(1.0 - wi->Longrange)/(wi->Range))>0.00001){
                                    PRETTY_ADD(statcolor+"   Range attenuation factor: #-c",100000*(1.0 - wi->Longrange)/(wi->Range),2);
                                    text+="% per km";
                              }
                              if(playerUnit->mounts[i].ammo!=-1&&(lookupMountSize(wi->size)!="SPECIAL-MISSILE")){
                                    PRETTY_ADD(statcolor+"   Rounds remaining: #-c",playerUnit->mounts[i].ammo,0);
                              } else {
                                    if(lookupMountSize(wi->size)=="SPECIAL-MISSILE"){
                                          PRETTY_ADD(statcolor+"   Rockets remaining: #-c",playerUnit->mounts[i].ammo,0);
                                    }
                              }
                              totalWeaponEnergyUsage+=(wi->EnergyRate/wi->Refire);
                              break;
                        case weapon_info::PROJECTILE: //need ammo
                              if(wi->LockTime > 0){
                                    PRETTY_ADDU(statcolor+"   'Fire and Forget' lock time: #-c",wi->LockTime,0,"seconds");
                              } else {
                                    text+="#n#";
                                    text+=prefix;
                                    text+=statcolor+"   Missile Lock Type: #-c#c1:.3:.3#None.#-c Inertial Guidance Only";
                              }
                              PRETTY_ADD(statcolor+"   Missiles remaining: #-c",playerUnit->mounts[i].ammo,0);
                              totalWeaponEnergyUsage+=(wi->EnergyRate/wi->Refire);
                              break;
                        case weapon_info::BEAM:
                              if (wi->Damage>0)
                                    totalWeaponDamage+=wi->Damage;
                              PRETTY_ADDU(statcolor+"   Beam stability: #-c",wi->Stability,2,"seconds");
                              if((100000*(1.0 - wi->Longrange)/(wi->Range))>0.00001){
                                    PRETTY_ADD(statcolor+"   Range attenuation factor: #-c",100000*(1.0 - wi->Longrange)/(wi->Range),2);
                                    text+="% per km";
                              }
                              totalWeaponEnergyUsage+=wi->EnergyRate;
                              break;
                        default:
                              break;
                  }
            }
                  
      }}else{           //end mountpoint list
            if(!mode){
                  text+="#n##c1:.3:.3#"+prefix+"  NO MOUNTED WEAPONS#n##-c";
            }
      }
      }
      if(mode){
            return;
      }
      if (subunitlevel==0 && mode==0) {
            text+="#n##n##c0:1:.5#"+prefix+"[KEY FIGURES]#n##-c";
            float maxshield=totalShieldEnergyCapacitance(playerUnit->shield);
            if(shields_require_power){
                  maxshield=0;
            }
            PRETTY_ADDU(statcolor+"Minimum time to reach full afterburner speed: #-c",playerUnit->GetMass()*uc.max_ab_speed()/playerUnit->limits.afterburn,2,"seconds");
            //reactor
            float avail=(playerUnit->MaxEnergyData()*RSconverter-maxshield*VSDM);
            float overhead=(shields_require_power)?(playerUnit->shield.recharge/shieldenergycap*shield_maintenance_cost*playerUnit->shield.number*VSDM):0;
            float nrt=avail/(playerUnit->EnergyRechargeData()*RSconverter-overhead);
            PRETTY_ADDU(statcolor+"Reactor nominal replenish time: #-c",nrt,2,"seconds");
            //shield related stuff
            //code taken from RegenShields in unit_generic.cpp, so we're sure what we say here is correct.
            static float low_power_mode = XMLSupport::parse_float(vs_config->getVariable("physics","low_power_mode_energy","10"));
            if (playerUnit->MaxEnergyData()-maxshield<low_power_mode) {
            text+="#n##c1:.3:.3#"+prefix+"WARNING: Capacitor banks are overdrawn: downgrade shield, upgrade reactor or purchase reactor capacitance!#-c";
            }
            else {
            /*    PRETTY_ADDU("Power balance will make your reactor never recharge above ",(playerUnit->MaxEnergyData()-maxshield)*100.0/playerUnit->MaxEnergyData(),0,"% of it's max capacity");*/
            }
            
            if (playerUnit->shield.recharge*playerUnit->shield.number*VSDM/shieldenergycap>playerUnit->EnergyRechargeData()*RSconverter) {
                  text+="#n##c1:1:.1#"+prefix+"WARNING: reactor recharge rate is less than combined shield recharge rate.#n#";
                  text+="Your shields won't be able to regenerate at their optimal speed!#-c";
            }
            if(shields_require_power){
                  text+="#n#"+prefix+statcolor+"Reactor recharge slowdown caused by shield maintenance: #-c";
                  float maint_draw_percent=playerUnit->shield.recharge*VSDM*100.0/shieldenergycap*shield_maintenance_cost*playerUnit->shield.number/(playerUnit->EnergyRechargeData()*RSconverter);
                  sprintf(conversionBuffer,"%.2f",maint_draw_percent);
                  text+=conversionBuffer;
                  text+=" %.";
                  if (maint_draw_percent>60) {
                  text+="#n##c1:1:.1#"+prefix+"WARNING: Reactor power is heavily consumed by passive shield maintenance: consider downgrading shield or upgrading reactor.#-c";
                  } else if(maint_draw_percent>95){
                    text+="#n##c1:.3:.3#"+prefix+"SEVERE WARNING: Reactor power is overdrawn! Unsustainable power is being consumed by passive shield maintenance: downgrade shield or upgrade reactor immediately!#-c";
                  }
            }
            totalWeaponEnergyUsage=totalWeaponEnergyUsage*RSconverter;
            float maint_draw=(shields_require_power)?(playerUnit->shield.recharge*VSDM/shieldenergycap*shield_maintenance_cost*playerUnit->shield.number):0;
            PRETTY_ADDU(statcolor+"Combined weapon energy usage: #-c",totalWeaponEnergyUsage,0,"MJ/s");
            if (totalWeaponEnergyUsage<(playerUnit->EnergyRechargeData()*RSconverter-maint_draw)) {
                  //waouh, impressive...
                  text+="#n##c0:1:.2#"+prefix+"Your reactor produces more energy than your weapons can use!#-c";
            }
            else {
                  PRETTY_ADDU(statcolor+"Reactor energy depletion time if weapons in continuous use: #-c",(playerUnit->MaxEnergyData()*RSconverter)/(totalWeaponEnergyUsage-((playerUnit->EnergyRechargeData()*RSconverter-maint_draw))),2,"seconds");      
            }
      PRETTY_ADDU(statcolor+"Combined (non-missile) weapon damage: #-c",totalWeaponDamage*VSDM,0,"MJ/s");
      }
      
      if(!mode){
            //handle SubUnits
            un_iter ki=playerUnit->getSubUnits(); //can't use kiter, MakeUnitXMLPretty takes non-const Unit
            {     int i=1;
            Unit *sub=ki.current();
            while (sub) {
                  if (i==1) text+="#n##n##c0:1:.5#"+prefix+"[SUB UNITS]#-c";
                  PRETTY_ADD("#n#"+prefix+"#c0:1:.2#<#-csub unit ",i,0);
                  text+="#c0:1:.2#>#-c#n#";
                  showUnitStats(sub,text,subunitlevel+1,0,item);
                  i++;
                  sub=ki.next();
            }}
      }
}


// Show the stats on the player's current ship.
bool BaseComputer::showShipStats(const EventCommandId& command, Control* control) {
      current_unit_load_mode=NO_MESH;
      Unit* playerUnit = m_player.GetUnit();
      current_unit_load_mode=DEFAULT;
      const string rawText = MakeUnitXMLPretty(playerUnit->WriteUnitString(), playerUnit);

      // Need to translate some characters to make it even prettier.
      string text;
      text="";
      Cargo uninitcargo;
      showUnitStats(playerUnit,text,0,0,uninitcargo);
      text.append("#n##n##c0:1:.5#[RAW DIAGNOSTIC OUTPUT]#n##-c");
      bool inQuote = false;
      bool newLine = false;
      static bool showdiags = XMLSupport::parse_bool (vs_config->getVariable("debug","showdiagnostics","false"));
      if(showdiags){
            for(string::const_iterator i=rawText.begin(); i!=rawText.end(); i++) {
                  switch(*i) {
                        case '\n':
                              text.append("#n#");
                              if (!newLine) {
                                    text.append("#c0:1:.5#");
                                    newLine = true;
                              }
                              break;
                        case '"':
                              if (!inQuote) {
                                    text.append("#c1:.3:.3#");
                                    inQuote=true;
                              } else {
                                    text.append("#-c");
                                    inQuote=false;
                              }
                              // Delete these, so do nothing.
                              break;
                        case ' ':
                              if (newLine) {
                                    newLine=false;
                                    text.append("#-c");
                              }
                              text+=(*i);
                              break;
                        default:
                              text+=(*i);
                              break;
                  }
            }
      }
      else{
            text.append("#n# #c1:.1:.1#SUPPRESSED #n##-c");
      }
    // Put this in the description.
    StaticDisplay* desc = static_cast<StaticDisplay*>( window()->findControlById("Description") );
    assert(desc != NULL);
    desc->setText(text);

    return true;
}




namespace CockpitKeys {
      void QuitNow();
}

// Create the window and controls for the Options Menu.
void BaseComputer::LoadSaveQuitConfirm::init(void) {
      Window* window = new Window;
      setWindow(window);
      
      window->setSizeAndCenter(Size(.9,.5));
      window->setTexture("basecomputer.png");
      window->setColor( GFXColor(0,1,0,.1) );
      window->setOutlineColor( GFXColor(.7,.7,.7) );
      window->setOutlineWidth(2.0);
      window->setController(this);

      // Information.
      StaticDisplay* text = new StaticDisplay;
      text->setRect( Rect(-.4, -.15, .8, .3) );
      text->setText(this->text);
      text->setTextColor(GFXColor(.7,1,.4));
      text->setMultiLine(true);
      text->setColor(GUI_CLEAR);
      text->setFont( Font(.07, 1.25) );
      text->setId("Information");
      // Put it on the window.
      window->addControl(text);

      // Save button.
      NewButton* cont = new NewButton;
      cont->setRect( Rect(.05, -.19, .30, .1) );
      cont->setLabel(type);
      cont->setCommand(type);
      cont->setColor( GFXColor(1,.5,0,.25) );
      cont->setTextColor( GUI_OPAQUE_WHITE() );
      cont->setDownColor( GFXColor(1,.5,0,.6) );
      cont->setDownTextColor( GUI_OPAQUE_BLACK() );
      cont->setHighlightColor( GFXColor(0,1,0,.4) );
      cont->setFont(Font(.08, BOLD_STROKE));
      // Put the button on the window.
      window->addControl(cont);

      // Abort action button
      NewButton* resume = new NewButton;
      resume->setRect( Rect(-.35, -.20, .30, .12) );
      resume->setLabel("Cancel");
      resume->setCommand("Window::Close");
      resume->setColor( GFXColor(0,1,0,.25) );
      resume->setTextColor( GUI_OPAQUE_WHITE() );
      resume->setDownColor( GFXColor(0,1,0,.6) );
      resume->setDownTextColor( GUI_OPAQUE_BLACK() );
      resume->setHighlightColor( GFXColor(0,1,0,.4) );
      resume->setFont(Font(.08, BOLD_STROKE));
      // Put the button on the window.
      window->addControl(resume);

      window->setModal(true);
}

// Process a command event from the Options Menu window.
bool BaseComputer::LoadSaveQuitConfirm::processWindowCommand(const EventCommandId& command, Control* control) {
      if(command == "Save") {
            m_parent->actionConfirmedSaveGame();
                window()->close();
      } else if(command == "Load") {
            m_parent->actionConfirmedLoadGame();
      } else if(command == "Quit") {
            m_parent->actionConfirmedQuitGame();
      } else {
            // Not a command we know about.
            return WindowController::processWindowCommand(command, control);
      }

      return true;
}

// Show options.

bool BaseComputer::actionConfirmedQuitGame() {
      CockpitKeys::QuitNow();
      return true;
}
bool BaseComputer::actionQuitGame(const EventCommandId& command, Control* control) {
      LoadSaveQuitConfirm *saver= new LoadSaveQuitConfirm(this,"Quit","Are you sure that you want to quit?");
      saver->init();
      saver->run();
      return true;
}

bool BaseComputer::actionConfirmedSaveGame() {
      Unit* player = m_player.GetUnit();
      StaticDisplay* desc = static_cast<StaticDisplay*>( window()->findControlById("InputText") );
      bool ok=true;
      if (desc) {    
            std::string tmp = desc->text();
            VSFileSystem::VSFile fp;
            VSFileSystem::VSError err = fp.OpenCreateWrite(tmp,SaveFile);
            if (err>Ok) {
                  showAlert ("Could not create the saved game because it contains invalid characters or you do not have permissions or free space.");
                  ok=false;
                  return true;
            }else {
                  fp.Close();
                  if (tmp.length()>0) {
                        CurrentSaveGameName=tmp;
                  }else {
                        ok=false;
                  }
            }
      }
      if(player&&ok) {
            Cockpit* cockpit = _Universe->isPlayerStarship(player);
            if(cockpit) {
                  WriteSaveGame(cockpit, false);
                  loadLoadSaveControls();             
                  showAlert("Game saved successfully.");
            }
      }
      if (!ok) {
            showAlert ("You Must Type In a Name To Save.");
      }
      return true;
}
bool BaseComputer::actionSaveGame(const EventCommandId& command, Control* control) {
      Unit* player = m_player.GetUnit();
      StaticDisplay* desc = static_cast<StaticDisplay*>( window()->findControlById("InputText") );
      bool ok=true;
      std::string tmp;
      if (desc) {
            tmp = desc->text();
            if (tmp.length()<=0) {
                  ok=false;
            }
      }
      if(player&&ok) {
            Cockpit* cockpit = _Universe->isPlayerStarship(player);
            if(cockpit) {
                  VSFileSystem::VSFile fp;
                  VSFileSystem::VSError err = fp.OpenReadOnly(tmp,SaveFile);
                  if (err>Ok) {
                        actionConfirmedSaveGame();
                  } else {
                        fp.Close();
                                if (string("New_Game")!=tmp) {
                                  LoadSaveQuitConfirm *saver= new LoadSaveQuitConfirm(this,"Save","Do you want to overwrite your old saved game?");
                                  saver->init();
                                  saver->run();
                                } else {
                                  showAlert("You may not save to the name New_Game.");
                                }
                  }
            }
      }
      if (!ok) {
            showAlert ("You Must Type In a Name To Save.");
      }
      return true;
}

bool BaseComputer::actionConfirmedLoadGame() {
            Unit* player = m_player.GetUnit();
            StaticDisplay* desc = static_cast<StaticDisplay*>( window()->findControlById("InputText") );
            if (desc) {
                  std::string tmp = desc->text();
                  if (tmp.length()>0) {
                        CurrentSaveGameName=tmp;
                        if(player) {
                              Cockpit* cockpit = _Universe->isPlayerStarship(player);
                              if(cockpit) {
                                    player->Kill();
                                    RespawnNow(cockpit);
                                    globalWindowManager().shutDown();
                                    TerminateCurrentBase();  //BaseInterface::CurrentBase->Terminate();
                              }
                        }                                         
                  }else {
                        showAlert ("You Must Type In a Name To Load....");
                  }
            }           
            return true;
}
bool BaseComputer::actionNewGame(const EventCommandId& command, Control* control) {
  StaticDisplay* desc = static_cast<StaticDisplay*>( window()->findControlById("InputText") );  
  desc->setText("New_Game");
  return this->actionLoadGame(command,control);
}
bool BaseComputer::actionLoadGame(const EventCommandId& command, Control* control) {
            Unit* player = m_player.GetUnit();
            StaticDisplay* desc = static_cast<StaticDisplay*>( window()->findControlById("InputText") );
            if (desc) {
                  std::string tmp = desc->text();
                  if (tmp.length()>0) {
                        CurrentSaveGameName=tmp;      
                        if(player) {
                              Cockpit* cockpit = _Universe->isPlayerStarship(player);
                              if(cockpit) {
                                    LoadSaveQuitConfirm *saver= new LoadSaveQuitConfirm(this,"Load","Are you sure that you want to load this game?");
                                    saver->init();
                                    saver->run();
                                    return true;
                              }
                        }
                  }
            }
            showAlert ("You Must Type In a Name To Load....");
            return true;
}

// Process a command event from the Options Menu window.

Generated by  Doxygen 1.6.0   Back to index