Vegastrike 0.5.1 rc1  1.0
Original sources for Vegastrike Evolved
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
basecomputer.cpp
Go to the documentation of this file.
1 // -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*-
2 /*
3  * Vega Strike
4  * Copyright (C) 2003 Mike Byron
5  *
6  * http://vegastrike.sourceforge.net/
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21  */
22 
23 #include "vegastrike.h"
24 #if defined (_WIN32) && !defined (__CYGWIN__) && !defined (__MINGW32__)
25 //For WIN32 debugging.
26 #include <crtdbg.h>
27 #endif
28 #include "basecomputer.h"
30 #include "savegame.h"
31 #include "universe_util.h"
32 #include "save_util.h"
33 #include <algorithm> //For std::sort.
34 #include <set>
35 #include "load_mission.h"
36 #include "cmd/planet_generic.h"
37 #include "cmd/unit_util.h"
38 #include "cmd/music.h"
39 #include "cmd/unit_const_cache.h"
40 #include "cmd/unit_factory.h"
41 #include "gui/modaldialog.h"
42 #include "main_loop.h" //For QuitNow().
43 #include "lin_time.h"
44 //FIXME mbyron -- Hack instead of reading XML.
45 #include "gui/newbutton.h"
46 #include "gui/staticdisplay.h"
47 #include "gui/textinputdisplay.h"
48 #include "gui/simplepicker.h"
49 #include "gui/groupcontrol.h"
50 #include "gui/scroller.h"
51 #include "networking/netclient.h"
52 #include "unit_xml.h"
53 #include "gfx/sprite.h"
54 #include "gfx/aux_texture.h"
55 #include "gamemenu.h" //network menu.
56 #include "audiolib.h"
57 
58 //for directory thing
59 #if defined (_WIN32) && !defined (__CYGWIN__)
60 #include <direct.h>
61 #include <config.h>
62 #include <string.h>
63 #ifndef NOMINMAX
64 #define NOMINMAX
65 #endif //tells VCC not to generate min/max macros
66 #include <windows.h>
67 #include <stdlib.h>
68 struct dirent
69 {
70  char d_name[1];
71 };
72 #else
73 #include <unistd.h>
74 #include <pwd.h>
75 #include <sys/types.h>
76 #include <dirent.h>
77 #endif
78 #include <sys/stat.h>
79 
80 //end for directory thing
81 extern const char *DamagedCategory;
82 
83 
84 int BaseComputer:: dirty = 0;
85 
86 static GFXColor UnsaturatedColor( float r, float g, float b, float a = 1.0f )
87 {
88  GFXColor ret( r, g, b, a );
89  return ret;
90 }
91 
93 extern std::string CurrentSaveGameName;
94 
95 std::vector< std::string >getWeapFilterVec()
96 {
97  std::vector< std::string >weapfiltervec;
99  weapfiltervec.push_back( "upgrades/Weapon" );
100  weapfiltervec.push_back( "SubUnits" );
101  weapfiltervec.push_back( "upgrades/Ammunition" );
102  return weapfiltervec;
103 }
104 
105 std::vector< std::string >weapfiltervec = getWeapFilterVec();
106 bool upgradeNotAddedToCargo( std::string category )
107 {
108  for (unsigned int i = 0; i < weapfiltervec.size(); ++i)
109  if (weapfiltervec[i].find( category ) == 0)
110  return true;
111  return false;
112 }
113 
114 extern vector< unsigned int >base_keyboard_queue;
115 std::string getDisplayCategory( const Cargo &cargo )
116 {
117  std::string::size_type where = cargo.GetDescription().find( "<" );
118  if (where != string::npos) {
119  std::string category = cargo.GetDescription().substr( where+1 );
120  where = category.find( ">" );
121  return category.substr( 0, where );
122  }
123  return cargo.category;
124 }
125 
126 //The separator used between categories in a category string.
127 static const char CATEGORY_SEP = '/';
128 //Tag that says this is a category not an item.
129 static const char CATEGORY_TAG = (-1);
130 
131 //Color of an item that there isn't enough money to buy.
132 //We read this out of the config file (or use a default).
133 static bool color_prohibited_upgrade_flag = false;
135 static bool color_insufficient_space_flag = false;
136 static bool color_insufficient_money_flag = false;
137 
139 {
140  static GFXColor NMC = getConfigColor( "no_money", GFXColor( 1, 1, .3, 1 ) );
141  return NMC; //Start out with bogus color.
142 }
143 
144 //Make the variable static, so it won't print so many annoying messages!
146 {
147  static GFXColor PU = getConfigColor( "prohibited_upgrade", GFXColor( 1, .1, 0, 1 ) );
148  return PU;
149 }
150 
152 {
153  static GFXColor DNC = getConfigColor( "downgrade_or_noncompatible", GFXColor( .75, .5, .5, 1 ) );
154  return DNC;
155 }
156 
158 {
159  static GFXColor NRFU = getConfigColor( "no_room_for_upgrade", GFXColor( 1, 0, 1, 1 ) );
160  return NRFU;
161 }
162 
164 {
165  static GFXColor IDC = getConfigColor( "upgrade_item_destroyed", GFXColor( 0.2, 0.2, 0.2, 1 ) );
166  return IDC;
167 }
168 
169 //Color of the text of a category.
171 {
172  static GFXColor CTC = getConfigColor( "base_category_color", GFXColor( 0, .75, 0, 1 ) );
173  return CTC;
174 }
176 {
177  static GFXColor MiC = getConfigColor( "base_mission_color", GFXColor( .66, .2, 0, 1 ) );
178  return MiC;
179 }
180 
181 //Space between mode buttons.
182 static const float MODE_BUTTON_SPACE = 0.03;
183 //Default color in CargoColor.
185 {
186  static GFXColor DuC = getConfigColor( "base_upgrade_color", GFXColor( 1, 1, 1, 1 ) );
187  return DuC;
188 }
189 
190 //MOUNT ENTRY COLORS
191 //Mount point that cannot be selected.
193 {
194  return GFXColor( 1, .7, .7 );
195 }
196 
197 //Empty mount point.
199 {
200  return GFXColor( .2, 1, .2 );
201 }
202 
203 //Mount point that contains weapon.
205 {
206  return GFXColor( 1, 1, 0 );
207 }
208 
209 //Some mission declarations.
210 //These should probably be in a header file somewhere.
211 static const char*const MISSION_SCRIPTS_LABEL = "mission_scripts";
212 static const char*const MISSION_NAMES_LABEL = "mission_names";
213 static const char*const MISSION_DESC_LABEL = "mission_descriptions";
214 
215 //Some new declarations.
216 //These should probably be in a header file somewhere.
217 static const char*const NEWS_NAME_LABEL = "news";
218 
219 //Some upgrade declarations.
220 //These should probably be in a header file somewhere.
221 
222 extern const Unit * makeFinalBlankUpgrade( string name, int faction );
223 extern int GetModeFromName( const char* ); //1=add, 2=mult, 0=neither.
224 extern Cargo * GetMasterPartList( const char *input_buffer );
225 extern Unit& GetUnitMasterPartList();
226 static const string LOAD_FAILED = "LOAD_FAILED";
227 
228 //Some ship dealer declarations.
229 //These should probably be in a header file somewhere.
230 extern void SwitchUnits( Unit *ol, Unit *nw );
231 extern void TerminateCurrentBase( void );
232 extern void CurrentBaseUnitSet( Unit *un );
233 //For ships stats.
234 extern string MakeUnitXMLPretty( std::string, Unit* );
235 extern float totalShieldEnergyCapacitance( const Shield &shield );
236 //For Options menu.
237 extern void RespawnNow( Cockpit *cockpit );
238 
239 //headers for functions used internally
240 //add to text a nicely-formated description of the unit and its subunits
241 void showUnitStats( Unit *playerUnit, string &text, int subunitlevel, int mode, Cargo &item );
242 //build the previous description for a ship purchase item
243 string buildShipDescription( Cargo &item, string &descriptiontexture );
244 //build the previous description from a cargo purchase item
245 string buildCargoDescription( const Cargo &item, BaseComputer &computer, float price );
246 //put in buffer a pretty prepresentation of the POSITIVE float f (ie 4,732.17)
247 void prettyPrintFloat( char *buffer, float f, int digitsBefore, int digitsAfter, int bufferLen = 128 );
248 string buildUpgradeDescription( Cargo &item );
249 int basecargoassets( Unit *base, string cargoname );
250 
251 //"Basic Repair" item that is added to Buy UPGRADE mode.
252 const string BASIC_REPAIR_NAME = "Basic Repair & Refuel";
253 
255 {
256  return GFXColor( 0, 1, 1 );
257 }
258 
259 const string BASIC_REPAIR_DESC =
260  "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. Your vessel will also be refuelled.";
261 //Repair price is a config variable.
262 
263 //Info about each mode.
264 struct ModeInfo
265 {
266  string title;
267  string button;
268  string command;
269  string groupId;
270  ModeInfo( string t = "", string b = "", string c = "", string g = "" ) :
271  title( t )
272  , button( b )
273  , command( c )
274  , groupId( g ) {}
275 };
276 
277 static const ModeInfo modeInfo[] = {
278  ModeInfo( "Cargo Dealer ", "Cargo", "CargoMode", "CargoGroup" ),
279  ModeInfo( "Ship Upgrades ", "Upgrades", "UpgradeMode", "UpgradeGroup" ),
280  ModeInfo( "New Ships ", "Ships", "ShipDealerMode", "ShipDealerGroup" ),
281  ModeInfo( "Missions BBS ", "Missions", "MissionsMode", "MissionsGroup" ),
282  ModeInfo( "GNN News ", "News", "NewsMode", "NewsGroup" ),
283  ModeInfo( "Info/Stats ", "Info", "InfoMode", "InfoGroup" ),
284  ModeInfo( "Load / Save ", "LoadSave", "LoadSaveMode", "LoadSaveGroup" ),
285  ModeInfo( "Network ", "Network", "NetworkMode", "NetworkGroup" )
286 };
287 
288 bool BaseComputer::actionDone( const EventCommandId &command, Control *control )
289 {
291  window()->close();
292  return true;
293 }
294 //Dispatch table for commands.
295 //Make an entry here for each command you want to handle.
296 //WARNING: The order of this table is important. There are multiple entries for
297 //some commands. Basically, you can make an entry for a particular control, and then
298 //later have an entry with an empty control id to cover the other cases.
299 template < >
301  BaseComputer::WctlTableEntry( "Picker::NewSelection",
302  "NewsPicker",
304  BaseComputer::WctlTableEntry( "Picker::NewSelection",
305  "LoadSavePicker",
307  BaseComputer::WctlTableEntry( "Picker::NewSelection",
308  "",
327 
346 
347  BaseComputer::WctlTableEntry( "", "", NULL )
348 };
349 
350 template<typename T> inline T mymin(T a, T b)
351 {
352  return (a<b) ? a : b;
353 }
354 
355 template<typename T> inline T mymax(T a, T b)
356 {
357  return (a>b) ? a : b;
358 }
359 
360 //Take underscores out of a string and capitalize letters after spaces.
361 static std::string beautify( const string &input )
362 {
363  std::string result;
364 
365  bool wordStart = true;
366  for (std::string::const_iterator i = input.begin(); i != input.end(); i++) {
367  if (*i == '_') {
368  //Turn this into a space, and make sure next letter is capitalized.
369  result += ' ';
370  wordStart = true;
371  } else if (wordStart) {
372  //Start or a word. Capitalize the character, and turn off start of word.
373  result += toupper( *i );
374  wordStart = false;
375  } else {
376  //Normal character in middle of word.
377  result += *i;
378  }
379  }
380  return result;
381 }
382 
383 //The "used" value of an item.
384 static double usedValue( double originalValue )
385 {
386  return .5*originalValue;
387 }
388 
389 extern float RepairPrice( float operational, float price );
390 
391 static float basicRepairPrice( void )
392 {
393  static const float price = XMLSupport::parse_float( vs_config->getVariable( "physics", "repair_price", "5000" ) );
394  return price*g_game.difficulty;
395 }
396 
397 static float SellPrice( float operational, float price )
398 {
399  return usedValue( price )-RepairPrice( operational, price );
400 }
401 extern const Unit * makeTemplateUpgrade( string name, int faction );
402 
403 //Ported from old code. Not sure what it does.
404 const Unit * getUnitFromUpgradeName( const string &upgradeName, int myUnitFaction = 0 );
405 
406 //Takes in a category of an upgrade or cargo and returns true if it is any type of mountable weapon.
407 extern bool isWeapon( std::string name );
408 
409 
410 
411 #define PRETTY_ADD( str, val, digits ) \
412  do { \
413  text += "#n#"; \
414  text += prefix; \
415  text += str; \
416  prettyPrintFloat( conversionBuffer, val, 0, digits ); \
417  text += conversionBuffer; \
418  } \
419  while (0)
420 
421 #define PRETTY_ADDN( str, val, digits ) \
422  do { \
423  text += str; \
424  prettyPrintFloat( conversionBuffer, val, 0, digits ); \
425  text += conversionBuffer; \
426  } \
427  while (0)
428 
429 #define PRETTY_ADDU( str, val, digits, unit ) \
430  do { \
431  text += "#n#"; \
432  text += prefix; \
433  text += str; \
434  prettyPrintFloat( conversionBuffer, val, 0, digits ); \
435  text += conversionBuffer; \
436  text += " "; \
437  text += unit; \
438  } \
439  while (0)
440 
441 
442 //CONSTRUCTOR.
443 BaseComputer::BaseComputer( Unit *player, Unit *base, const std::vector< DisplayMode > &modes ) :
444  m_displayModes( modes )
445  , m_player( player )
446  , m_base( base )
447  , m_currentDisplay( NULL_DISPLAY )
448  , m_selectedList( NULL )
449  , m_playingMuzak( false )
450 {
451  //Make sure we get this color loaded.
452  //Initialize mode group controls array.
453  for (int i = 0; i < DISPLAY_MODE_COUNT; i++)
454  m_modeGroups[i] = NULL;
455 }
456 
457 //Destructor.
459 {
460  m_player.SetUnit( NULL );
461  m_base.SetUnit( NULL );
462  //Delete any group controls that the window doesn't "own".
463  for (int i = 0; i < DISPLAY_MODE_COUNT; i++)
464  if (m_modeGroups[i] != NULL)
465  delete m_modeGroups[i];
466  //If we are playing muzak, stop it.
467  if (m_playingMuzak)
468  muzak->Skip();
469 }
470 
472 {
473  static bool use_faction_background =
474  XMLSupport::parse_bool( vs_config->getVariable( "graphics", "use_faction_gui_background_color", "true" ) );
475  static float faction_color_darkness =
476  XMLSupport::parse_float( vs_config->getVariable( "graphics", "base_faction_color_darkness", ".75" ) );
477  if (use_faction_background) {
478  int fac = m_base.GetUnit()->faction;
479  if (FactionUtil::GetFactionName( fac ) == "neutral")
481  const float *stuff = FactionUtil::GetSparkColor( fac );
482 
483  return GFXColor( stuff[0]*faction_color_darkness, stuff[1]*faction_color_darkness, stuff[2]*faction_color_darkness );
484  } else {
485  if (id == "CargoGroup")
486  return GFXColor( 0, 0, faction_color_darkness );
487  else if (id == "NewsGroup")
488  return GFXColor( faction_color_darkness, 0, faction_color_darkness );
489  else if (id == "UpgradeGroup")
490  return GFXColor( 0, faction_color_darkness, 0 );
491  else if (id == "InfoGroup")
492  return GFXColor( 0, faction_color_darkness, faction_color_darkness );
493  else if (id == "MissionsGroup")
494  return GFXColor( faction_color_darkness, 0, 0 );
495  else if (id == "ShipDealerGroup")
496  return GFXColor( faction_color_darkness, faction_color_darkness, 0 );
497  else if (id == "LoadSaveGroup")
498  return GFXColor( 0, faction_color_darkness, faction_color_darkness );
499  else if (id == "NetworkGroup")
500  return GFXColor( 0, faction_color_darkness, faction_color_darkness );
501  else
502  return GFXColor( 0, 0, 0 );
503  }
504 }
505 
506 //Hack that constructs controls in code.
508 {
509  if (m_displayModes.size() != 1 || m_displayModes[0] != NETWORK) {
510  //Base info title.
511  StaticDisplay *baseTitle = new StaticDisplay;
512  baseTitle->setRect( Rect( -.96, .76, 1.9, .08 ) );
513  baseTitle->setText( "ERROR" );
514  static GFXColor baseNameColor = getConfigColor( "base_name_color", GFXColor( .1, .8, .1 ) );
515  baseTitle->setTextColor( baseNameColor );
516  baseTitle->setColor( GUI_CLEAR );
517  baseTitle->setFont( Font( .07, 1.75 ) );
518  baseTitle->setId( "BaseInfoTitle" );
519  //Put it on the window.
520  window()->addControl( baseTitle );
521 
522  //Player info title.
523  StaticDisplay *playerTitle = new StaticDisplay;
524  static GFXColor basePlayerColor = getConfigColor( "base_player_color", GFXColor( .7, .7, .9 ) );
525  playerTitle->setRect( Rect( -.96, .69, 1.9, .07 ) );
526  playerTitle->setTextColor( basePlayerColor );
527  playerTitle->setColor( GUI_CLEAR );
528  playerTitle->setFont( Font( .06, BOLD_STROKE ) );
529  playerTitle->setId( "PlayerInfoTitle" );
530  //Put it on the window.
531  window()->addControl( playerTitle );
532 
533  static GFXColor saveLoadColor = getConfigColor( "base_save_load_color", GFXColor( .75, 0, 0 ) );
534  //Options button.
535  NewButton *options = new NewButton;
536  options->setRect( Rect( .64, .85, .32, .1 ) );
537  if (Network) {
538  options->setLabel( "Net Play" );
539  options->setCommand( "ShowNetworkMenu" );
540  } else {
541  options->setLabel( "Save/Load" );
542  options->setCommand( "ShowOptionsMenu" );
543  }
544  options->setColor( UnsaturatedColor( saveLoadColor.r, saveLoadColor.g, saveLoadColor.b, .25 ) );
545  options->setTextColor( GUI_OPAQUE_WHITE() );
546  options->setDownColor( UnsaturatedColor( saveLoadColor.r, saveLoadColor.g, saveLoadColor.b, .6 ) );
547  options->setDownTextColor( GUI_OPAQUE_BLACK() );
548  options->setHighlightColor( GFXColor( 0, 0, 1, .4 ) );
549  options->setFont( Font( .08 ) );
550  //Put the button on the window.
551  window()->addControl( options );
552  }
553  static GFXColor doneColor = getConfigColor( "base_done_color", GFXColor( .75, 0, 0 ) );
554  //Done button.
555  NewButton *done = new NewButton;
556  done->setRect( Rect( .74, .71, .22, .1 ) );
557  done->setLabel( "Done" );
558  done->setCommand( "DoneComputer" );
559  done->setColor( UnsaturatedColor( doneColor.r, doneColor.g, doneColor.b, .25 ) );
560  done->setTextColor( GUI_OPAQUE_WHITE() );
561  done->setDownColor( UnsaturatedColor( doneColor.r, doneColor.g, doneColor.b, .6 ) );
563  done->setHighlightColor( GFXColor( 0, 0, 1, .4 ) );
564  done->setFont( Font( .08, BOLD_STROKE ) );
565  window()->addControl( done );
566 
567  //Mode button.
568  NewButton *mode = new NewButton;
569  static GFXColor mode_color = getConfigColor( "base_mode_color", GFXColor( 0, .5, 0 ) );
570  mode->setRect( Rect( -.96, .86, .24, .09 ) );
571  mode->setLabel( "ERROR" );
572  mode->setColor( GFXColor( mode_color.r, mode_color.g, mode_color.b, .25 ) );
573  mode->setTextColor( GUI_OPAQUE_WHITE() );
574  mode->setDownColor( GFXColor( mode_color.r, mode_color.g, mode_color.b, .5 ) );
576  mode->setHighlightColor( GFXColor( mode_color.r, mode_color.g, mode_color.b, .4 ) );
577  mode->setFont( Font( .07, BOLD_STROKE ) );
578  mode->setId( "ModeButton" );
579  //Put the button on the window.
580  window()->addControl( mode );
581  {
582  //CARGO group control.
583  GroupControl *cargoGroup = new GroupControl;
584  cargoGroup->setId( "CargoGroup" );
585  window()->addControl( cargoGroup );
586  GFXColor color = getColorForGroup( "CargoGroup" );
587 
588  //Seller text display.
589  StaticDisplay *sellLabel = new StaticDisplay;
590  sellLabel->setRect( Rect( -.96, .56, .81, .1 ) );
591  sellLabel->setText( "Seller" );
592  sellLabel->setTextColor( GUI_OPAQUE_WHITE() );
593  sellLabel->setColor( GUI_CLEAR );
594  sellLabel->setFont( Font( .08, BOLD_STROKE ) );
595  sellLabel->setJustification( CENTER_JUSTIFY );
596  cargoGroup->addChild( sellLabel );
597 
598  //Player inventory text display.
599  StaticDisplay *inv = new StaticDisplay;
600  *inv = *sellLabel;
601  inv->setRect( Rect( .15, .56, .81, .1 ) );
602  inv->setText( "Inventory" );
603  cargoGroup->addChild( inv );
604 
605  //Total price text display.
606  StaticDisplay *totalPrice = new StaticDisplay;
607  totalPrice->setRect( Rect( -.2, .56, .4, .07 ) );
608  totalPrice->setTextColor( GUI_OPAQUE_WHITE() );
609  totalPrice->setColor( GUI_CLEAR );
610  totalPrice->setFont( Font( .06 ) );
611  totalPrice->setJustification( CENTER_JUSTIFY );
612  totalPrice->setId( "TotalPrice" );
613  cargoGroup->addChild( totalPrice );
614 
615  //"Max" text display.
616  StaticDisplay *maxForPlayer = new StaticDisplay;
617  maxForPlayer->setRect( Rect( -.14, .49, .28, .07 ) );
618  maxForPlayer->setTextColor( GUI_OPAQUE_WHITE() );
619  maxForPlayer->setColor( GUI_CLEAR );
620  maxForPlayer->setFont( Font( .06 ) );
621  maxForPlayer->setJustification( CENTER_JUSTIFY );
622  maxForPlayer->setId( "MaxQuantity" );
623  cargoGroup->addChild( maxForPlayer );
624 
625  //Scroller for seller.
626  Scroller *sellerScroller = new Scroller;
627  sellerScroller->setRect( Rect( -.20, -.4, .05, .95 ) );
628  sellerScroller->setColor( UnsaturatedColor( color.r, color.g, color.b, .1 ) );
629  sellerScroller->setThumbColor( UnsaturatedColor( color.r*.4, color.g*.4, color.b*.4 ), GUI_OPAQUE_WHITE() );
630  sellerScroller->setButtonColor( UnsaturatedColor( color.r*.4, color.g*.4, color.b*.4 ) );
631  sellerScroller->setTextColor( GUI_OPAQUE_WHITE() );
632  sellerScroller->setOutlineColor( GUI_OPAQUE_MEDIUM_GRAY() );
633 
634  //Seller picker.
635  SimplePicker *sellpick = new SimplePicker;
636  sellpick->setRect( Rect( -.96, -.4, .76, .95 ) );
637  sellpick->setColor( GFXColor( color.r, color.g, color.b, .1 ) );
639  sellpick->setTextColor( GUI_OPAQUE_WHITE() );
640  sellpick->setSelectionColor( UnsaturatedColor( 0, .6, 0, .8 ) );
641  sellpick->setHighlightColor( UnsaturatedColor( 0, .6, 0, .35 ) );
643  sellpick->setFont( Font( .07 ) );
644  sellpick->setTextMargins( Size( 0.02, 0.01 ) );
645  sellpick->setId( "BaseCargo" );
646  sellpick->setScroller( sellerScroller );
647  cargoGroup->addChild( sellpick );
648 
649  cargoGroup->addChild( sellerScroller ); //Want this "over" the picker.
650 
651  //Scroller for inventory.
652  Scroller *invScroller = new Scroller;
653  invScroller->setRect( Rect( .91, -.4, .05, .95 ) );
654  invScroller->setColor( UnsaturatedColor( color.r, color.g, color.b, .1 ) );
655  invScroller->setThumbColor( UnsaturatedColor( color.r*.4, color.g*.4, color.b*.4 ), GUI_OPAQUE_WHITE() );
656  invScroller->setButtonColor( UnsaturatedColor( color.r*.4, color.g*.4, color.b*.4 ) );
657  invScroller->setTextColor( GUI_OPAQUE_WHITE() );
658  invScroller->setOutlineColor( GUI_OPAQUE_MEDIUM_GRAY() );
659 
660  //Inventory picker.
661  SimplePicker *ipick = new SimplePicker;
662  ipick->setRect( Rect( .15, -.4, .76, .95 ) );
663  ipick->setColor( UnsaturatedColor( color.r, color.g, color.b, .1 ) );
665  ipick->setTextColor( GUI_OPAQUE_WHITE() );
666  ipick->setFont( Font( .07 ) );
667  ipick->setTextMargins( Size( 0.02, 0.01 ) );
668  ipick->setSelectionColor( UnsaturatedColor( 0, .6, 0, .8 ) );
669  ipick->setHighlightColor( UnsaturatedColor( 0, .6, 0, .35 ) );
671  ipick->setId( "PlayerCargo" );
672  ipick->setScroller( invScroller );
673  cargoGroup->addChild( ipick );
674 
675  cargoGroup->addChild( invScroller ); //Want this "over" the picker.
676 
677  //Buy button.
678  NewButton *buy = new NewButton;
679  buy->setRect( Rect( -.11, .3, .22, .13 ) );
680  buy->setColor( GFXColor( 0, 1, 1, .1 ) );
681  buy->setTextColor( GUI_OPAQUE_WHITE() );
682  buy->setDownColor( GFXColor( 0, 1, 1, .4 ) );
683  buy->setDownTextColor( GFXColor( .2, .2, .2 ) );
684  buy->setVariableBorderCycleTime( 1.0 );
685  buy->setBorderColor( GFXColor( .2, .2, .2 ) );
686  buy->setEndBorderColor( GFXColor( .4, .4, .4 ) );
687  buy->setShadowWidth( 2.0 );
688  buy->setFont( Font( .1, BOLD_STROKE ) );
689  buy->setId( "CommitAll" );
690  cargoGroup->addChild( buy );
691 
692  //"Buy 10" button.
693  NewButton *buy10 = new NewButton;
694  buy10->setRect( Rect( -.11, .1, .22, .1 ) );
695  buy10->setColor( GFXColor( 0, 1, 1, .1 ) );
696  buy10->setTextColor( GUI_OPAQUE_WHITE() );
697  buy10->setDownColor( GFXColor( 0, 1, 1, .4 ) );
698  buy10->setDownTextColor( GFXColor( .2, .2, .2 ) );
699  buy10->setVariableBorderCycleTime( 1.0 );
700  buy10->setBorderColor( GFXColor( .2, .2, .2 ) );
701  buy10->setEndBorderColor( GFXColor( .4, .4, .4 ) );
702  buy10->setShadowWidth( 2.0 );
703  buy10->setFont( Font( .08, BOLD_STROKE ) );
704  buy10->setId( "Commit10" );
705  cargoGroup->addChild( buy10 );
706 
707  //"Buy 1" button.
708  NewButton *buy1 = new NewButton;
709  buy1->setRect( Rect( -.11, -.1, .22, .1 ) );
710  buy1->setColor( GFXColor( 0, 1, 1, .1 ) );
711  buy1->setTextColor( GUI_OPAQUE_WHITE() );
712  buy1->setDownColor( GFXColor( 0, 1, 1, .4 ) );
713  buy1->setDownTextColor( GFXColor( .2, .2, .2 ) );
714  buy1->setVariableBorderCycleTime( 1.0 );
715  buy1->setBorderColor( GFXColor( .2, .2, .2 ) );
716  buy1->setEndBorderColor( GFXColor( .4, .4, .4 ) );
717  buy1->setShadowWidth( 2.0 );
718  buy1->setFont( Font( .08, BOLD_STROKE ) );
719  buy1->setId( "Commit" );
720  cargoGroup->addChild( buy1 );
721 
722  //Scroller for description.
723  Scroller *descScroller = new Scroller;
724  descScroller->setRect( Rect( .91, -.95, .05, .5 ) );
725  descScroller->setColor( UnsaturatedColor( color.r, color.g, color.b, .1 ) );
726  descScroller->setThumbColor( UnsaturatedColor( color.r*.4, color.g*.4, color.b*.4 ), GUI_OPAQUE_WHITE() );
727  descScroller->setButtonColor( UnsaturatedColor( color.r*.4, color.g*.4, color.b*.4 ) );
728  descScroller->setTextColor( GUI_OPAQUE_WHITE() );
729  descScroller->setOutlineColor( GUI_OPAQUE_MEDIUM_GRAY() );
730 
731  //Description box.
732  StaticDisplay *ms = new StaticDisplay;
733  StaticImageDisplay *picture = new StaticImageDisplay;
734  picture->setRect( Rect( -.96, -.45, .46*.75, -.47 ) );
735  picture->setTexture( "blackclear.png" );
736  picture->setId( "DescriptionImage" );
737  ms->setRect( Rect( -.6, -.95, 1.51, .5 ) );
738  ms->setColor( GFXColor( color.r, color.g, color.b, .1 ) );
740  ms->setFont( Font( .06 ) );
741  ms->setMultiLine( true );
743  ms->setTextMargins( Size( .02, .01 ) );
744  ms->setId( "Description" );
745  ms->setScroller( descScroller );
746  cargoGroup->addChild( ms );
747 
748  cargoGroup->addChild( descScroller ); //Want this "over" the description.
749  cargoGroup->addChild( picture );
750  }
751  {
752  //UPGRADE group control.
753  GroupControl *upgradeGroup = new GroupControl;
754  upgradeGroup->setId( "UpgradeGroup" );
755  window()->addControl( upgradeGroup );
756  GFXColor color = getColorForGroup( "UpgradeGroup" );
757 
758  //Seller text display.
759  StaticDisplay *sellLabel = new StaticDisplay;
760  sellLabel->setRect( Rect( -.96, .55, .81, .1 ) );
761  sellLabel->setText( "Available Upgrades" );
762  sellLabel->setTextColor( GUI_OPAQUE_WHITE() );
763  sellLabel->setColor( GUI_CLEAR );
764  sellLabel->setFont( Font( .07, BOLD_STROKE ) );
765  sellLabel->setJustification( CENTER_JUSTIFY );
766  upgradeGroup->addChild( sellLabel );
767 
768  //Player inventory text display.
769  StaticDisplay *inv = new StaticDisplay;
770  *inv = *sellLabel;
771  inv->setRect( Rect( .15, .55, .81, .1 ) );
772  inv->setText( "Improvements To Sell" );
773  upgradeGroup->addChild( inv );
774 
775  //Scroller for seller.
776  Scroller *sellerScroller = new Scroller;
777  sellerScroller->setRect( Rect( -.20, -.4, .05, .95 ) );
778  sellerScroller->setColor( UnsaturatedColor( color.r, color.g, color.b, .1 ) );
779  sellerScroller->setThumbColor( UnsaturatedColor( color.r*.4, color.g*.4, color.b*.4 ), GUI_OPAQUE_WHITE() );
780  sellerScroller->setButtonColor( UnsaturatedColor( color.r*.4, color.g*.4, color.b*.4 ) );
781  sellerScroller->setTextColor( GUI_OPAQUE_WHITE() );
782  sellerScroller->setOutlineColor( GUI_OPAQUE_MEDIUM_GRAY() );
783 
784  //Seller picker.
785  SimplePicker *sellpick = new SimplePicker;
786  sellpick->setRect( Rect( -.96, -.4, .76, .95 ) );
787  sellpick->setColor( UnsaturatedColor( color.r, color.g, color.b, .1 ) );
789  sellpick->setTextColor( GUI_OPAQUE_WHITE() );
790  sellpick->setFont( Font( .07 ) );
791  sellpick->setTextMargins( Size( 0.02, 0.01 ) );
792  sellpick->setSelectionColor( UnsaturatedColor( 0, .6, 0, .8 ) );
793  sellpick->setHighlightColor( UnsaturatedColor( 0, .6, 0, .35 ) );
795  sellpick->setId( "BaseUpgrades" );
796  sellpick->setScroller( sellerScroller );
797  upgradeGroup->addChild( sellpick );
798 
799  upgradeGroup->addChild( sellerScroller ); //Want this "over" the picker.
800 
801  //Scroller for inventory.
802  Scroller *invScroller = new Scroller;
803  invScroller->setRect( Rect( .91, -.4, .05, .95 ) );
804  invScroller->setColor( UnsaturatedColor( color.r, color.g, color.b, .1 ) );
805  invScroller->setThumbColor( UnsaturatedColor( color.r*.4, color.g*.4, color.b*.4 ), GUI_OPAQUE_WHITE() );
806  invScroller->setButtonColor( UnsaturatedColor( color.r*.4, color.g*.4, color.b*.4 ) );
807  invScroller->setTextColor( GUI_OPAQUE_WHITE() );
808  invScroller->setOutlineColor( GUI_OPAQUE_MEDIUM_GRAY() );
809 
810  //Inventory picker.
811  SimplePicker *ipick = new SimplePicker;
812  ipick->setRect( Rect( .15, -.4, .76, .95 ) );
813  ipick->setColor( GFXColor( color.r, color.g, color.b, .1 ) );
815  ipick->setTextColor( GUI_OPAQUE_WHITE() );
816  ipick->setSelectionColor( UnsaturatedColor( 0, .6, 0, .8 ) );
817  ipick->setHighlightColor( UnsaturatedColor( 0, .6, 0, .35 ) );
819  ipick->setFont( Font( .07 ) );
820  ipick->setTextMargins( Size( 0.02, 0.01 ) );
821  ipick->setId( "PlayerUpgrades" );
822  ipick->setScroller( invScroller );
823  upgradeGroup->addChild( ipick );
824 
825  upgradeGroup->addChild( invScroller ); //Want this "over" picker.
826 
827  //Buy button.
828  NewButton *buy = new NewButton;
829  buy->setRect( Rect( -.11, .2, .22, .12 ) );
830  buy->setColor( GFXColor( 0, 1, 1, .1 ) );
831  buy->setTextColor( GUI_OPAQUE_WHITE() );
832  buy->setDownColor( GFXColor( 0, 1, 1, .4 ) );
833  buy->setDownTextColor( GFXColor( .2, .2, .2 ) );
834  buy->setVariableBorderCycleTime( 1.0 );
835  buy->setBorderColor( GFXColor( .2, .2, .2 ) );
836  buy->setEndBorderColor( GFXColor( .4, .4, .4 ) );
837  buy->setShadowWidth( 2.0 );
838  buy->setFont( Font( .1, BOLD_STROKE ) );
839  buy->setId( "Commit" );
840  upgradeGroup->addChild( buy );
841 
842  //Fix button.
843  NewButton *fix = new NewButton;
844  fix->setRect( Rect( -.11, .0, .22, .12 ) );
845  fix->setColor( GFXColor( 0, 1, 1, .1 ) );
846  fix->setTextColor( GUI_OPAQUE_WHITE() );
847  fix->setDownColor( GFXColor( 0, 1, 1, .4 ) );
848  fix->setDownTextColor( GFXColor( .2, .2, .2 ) );
849  fix->setVariableBorderCycleTime( 1.0 );
850  fix->setBorderColor( GFXColor( .2, .2, .2 ) );
851  fix->setEndBorderColor( GFXColor( .4, .4, .4 ) );
852  fix->setShadowWidth( 2.0 );
853  fix->setFont( Font( .1, BOLD_STROKE ) );
854  fix->setId( "CommitFix" );
855  upgradeGroup->addChild( fix );
856 
857  //Scroller for description.
858  Scroller *descScroller = new Scroller;
859  descScroller->setRect( Rect( .91, -.95, .05, .5 ) );
860  descScroller->setColor( UnsaturatedColor( color.r, color.g, color.b, .1 ) );
861  descScroller->setThumbColor( UnsaturatedColor( color.r*.4, color.g*.4, color.b*.4 ), GUI_OPAQUE_WHITE() );
862  descScroller->setButtonColor( UnsaturatedColor( color.r*.4, color.g*.4, color.b*.4 ) );
863  descScroller->setTextColor( GUI_OPAQUE_WHITE() );
864  descScroller->setOutlineColor( GUI_OPAQUE_MEDIUM_GRAY() );
865 
866  //Description box.
867  StaticDisplay *ms = new StaticDisplay;
868  StaticImageDisplay *picture = new StaticImageDisplay;
869  picture->setRect( Rect( -.96, -.45, .46*.75, -.47 ) );
870  picture->setTexture( "blackclear.png" );
871  picture->setId( "DescriptionImage" );
872  ms->setRect( Rect( -.6, -.95, 1.51, .5 ) );
873 
874  ms->setColor( GFXColor( color.r, color.g, color.b, .1 ) );
876  ms->setFont( Font( .06 ) );
877  ms->setMultiLine( true );
879  ms->setTextMargins( Size( .02, .01 ) );
880  ms->setId( "Description" );
881  ms->setScroller( descScroller );
882  upgradeGroup->addChild( ms );
883 
884  upgradeGroup->addChild( descScroller ); //Want this "over" description box.
885  upgradeGroup->addChild( picture );
886  }
887  {
888  //NEWS group control.
889  GroupControl *newsGroup = new GroupControl;
890  newsGroup->setId( "NewsGroup" );
891  window()->addControl( newsGroup );
892  GFXColor color = getColorForGroup( "NewsGroup" );
893 
894  //Scroller for picker.
895  Scroller *pickScroller = new Scroller;
896  pickScroller->setRect( Rect( .91, 0, .05, .65 ) );
897  pickScroller->setColor( UnsaturatedColor( color.r, color.g, color.b, .1 ) );
898  pickScroller->setThumbColor( UnsaturatedColor( color.r*.4, color.g*.4, color.b*.4 ), GUI_OPAQUE_WHITE() );
899  pickScroller->setButtonColor( UnsaturatedColor( color.r*.4, color.g*.4, color.b*.4 ) );
900  pickScroller->setTextColor( GUI_OPAQUE_WHITE() );
901  pickScroller->setOutlineColor( GUI_OPAQUE_MEDIUM_GRAY() );
902 
903  //News picker.
904  SimplePicker *pick = new SimplePicker;
905  pick->setRect( Rect( -.96, 0, 1.87, .65 ) );
906  pick->setColor( GFXColor( color.r, color.g, color.b, .1 ) );
908  pick->setTextColor( GUI_OPAQUE_WHITE() );
909  pick->setSelectionColor( UnsaturatedColor( 0, .6, 0, .8 ) );
910  pick->setHighlightColor( UnsaturatedColor( 0, .6, 0, .35 ) );
912  pick->setFont( Font( .07 ) );
913  pick->setTextMargins( Size( 0.02, 0.01 ) );
914  pick->setId( "NewsPicker" );
915  pick->setScroller( pickScroller );
916  newsGroup->addChild( pick );
917 
918  newsGroup->addChild( pickScroller ); //Want scroller "over" picker.
919 
920  //Scroller for description.
921  Scroller *descScroller = new Scroller;
922  descScroller->setRect( Rect( .91, -.95, .05, .90 ) );
923  descScroller->setColor( UnsaturatedColor( color.r, color.g, color.b, .1 ) );
924  descScroller->setThumbColor( UnsaturatedColor( color.r*.4, color.g*.4, color.b*.4 ), GUI_OPAQUE_WHITE() );
925  descScroller->setButtonColor( UnsaturatedColor( color.r*.4, color.g*.4, color.b*.4 ) );
926  descScroller->setTextColor( GUI_OPAQUE_WHITE() );
927  descScroller->setOutlineColor( GUI_OPAQUE_MEDIUM_GRAY() );
928 
929  //Description box.
930  StaticDisplay *ms = new StaticDisplay;
931  ms->setRect( Rect( -.96, -.95, 1.87, .90 ) );
932  ms->setColor( GFXColor( color.r, color.g, color.b, .1 ) );
934  ms->setFont( Font( .07 ) );
935  ms->setMultiLine( true );
937  ms->setTextMargins( Size( .02, .01 ) );
938  ms->setId( "Description" );
939  ms->setScroller( descScroller );
940  newsGroup->addChild( ms );
941 
942  newsGroup->addChild( descScroller ); //Want scroller "over" description box.
943  }
944  {
945  GroupControl *loadSaveGroup = new GroupControl;
946  loadSaveGroup->setId( "LoadSaveGroup" );
947  window()->addControl( loadSaveGroup );
948  GFXColor color = getColorForGroup( "LoadSaveGroup" );
949  //Scroller for picker.
950  Scroller *pickScroller = new Scroller;
951  pickScroller->setRect( Rect( -.20, -.7, .05, 1.4 ) );
952  pickScroller->setColor( UnsaturatedColor( color.r, color.g, color.b, .1 ) );
953  pickScroller->setThumbColor( UnsaturatedColor( color.r*.4, color.g*.4, color.b*.4 ), GUI_OPAQUE_WHITE() );
954  pickScroller->setButtonColor( UnsaturatedColor( color.r*.4, color.g*.4, color.b*.4 ) );
955  pickScroller->setTextColor( GUI_OPAQUE_WHITE() );
956  pickScroller->setOutlineColor( GUI_OPAQUE_MEDIUM_GRAY() );
957 
958  //Save game picker.
959  SimplePicker *pick = new SimplePicker;
960  pick->setRect( Rect( -.96, -.7, .76, 1.4 ) );
961  pick->setColor( GFXColor( color.r, color.g, color.b, .1 ) );
963  pick->setTextColor( GUI_OPAQUE_WHITE() );
964  pick->setSelectionColor( UnsaturatedColor( 0, .6, 0, .8 ) );
965  pick->setHighlightColor( UnsaturatedColor( 0, .6, 0, .35 ) );
967  pick->setFont( Font( .07 ) );
968  pick->setTextMargins( Size( 0.02, 0.01 ) );
969  pick->setId( "LoadSavePicker" );
970  pick->setScroller( pickScroller );
971  loadSaveGroup->addChild( pick );
972 
973  loadSaveGroup->addChild( pickScroller ); //Want scroller "over" picker.
974 
975  //Scroller for description.
976  Scroller *descScroller = new Scroller;
977  descScroller->setRect( Rect( .91, -.7, .05, 1.4 ) );
978  descScroller->setColor( UnsaturatedColor( color.r, color.g, color.b, .1 ) );
979  descScroller->setThumbColor( UnsaturatedColor( color.r*.4, color.g*.4, color.b*.4 ), GUI_OPAQUE_WHITE() );
980  descScroller->setButtonColor( UnsaturatedColor( color.r*.4, color.g*.4, color.b*.4 ) );
981  descScroller->setTextColor( GUI_OPAQUE_WHITE() );
982  descScroller->setOutlineColor( GUI_OPAQUE_MEDIUM_GRAY() );
983 
984  //Description box.
985  StaticDisplay *ms = new StaticDisplay;
986  ms->setRect( Rect( .15, -.7, .76, 1.4 ) );
987  ms->setColor( GFXColor( color.r, color.g, color.b, .1 ) );
989  ms->setFont( Font( .07 ) );
990  ms->setMultiLine( true );
992  ms->setTextMargins( Size( .02, .01 ) );
993  ms->setId( "Description" );
994  ms->setScroller( descScroller );
995  loadSaveGroup->addChild( ms );
996 
997  loadSaveGroup->addChild( descScroller ); //Want scroller "over" description box.
998 
999  //Scroller for description.
1000  Scroller *inputTextScroller = new Scroller;
1001  inputTextScroller->setRect( Rect( .61, -0.95, .05, .2 ) );
1002  inputTextScroller->setColor( UnsaturatedColor( color.r, color.g, color.b, .1 ) );
1003  inputTextScroller->setThumbColor( UnsaturatedColor( color.r*.4, color.g*.4, color.b*.4 ), GUI_OPAQUE_WHITE() );
1004  inputTextScroller->setButtonColor( UnsaturatedColor( color.r*.4, color.g*.4, color.b*.4 ) );
1005  inputTextScroller->setTextColor( GUI_OPAQUE_WHITE() );
1006  inputTextScroller->setOutlineColor( GUI_OPAQUE_MEDIUM_GRAY() );
1007 
1008  //Description box.
1009  StaticDisplay *inputText = new TextInputDisplay( &base_keyboard_queue, "\x1b\n \t\r*?\\/|:<>\"^" );
1010  inputText->setRect( Rect( -.6, -.95, 1.21, .2 ) );
1011  inputText->setColor( GFXColor( color.r, color.g, color.b, .1 ) );
1012  inputText->setOutlineColor( GUI_OPAQUE_MEDIUM_GRAY() );
1013  inputText->setFont( Font( .07 ) );
1014  inputText->setMultiLine( true );
1015  inputText->setTextColor( GUI_OPAQUE_WHITE() );
1016  inputText->setTextMargins( Size( .02, .01 ) );
1017  inputText->setId( "InputText" );
1018  inputText->setScroller( inputTextScroller );
1019  loadSaveGroup->addChild( inputText );
1020 
1021  loadSaveGroup->addChild( inputTextScroller ); //Want scroller "over" description box.
1022  //Accept button.
1023  if (!Network) {
1024  //no save in network mode!
1025  NewButton *buy10 = new NewButton;
1026  buy10->setRect( Rect( -.11, 0, .22, .12 ) );
1027  buy10->setColor( GFXColor( 0, 1, 1, .1 ) );
1028  buy10->setTextColor( GUI_OPAQUE_WHITE() );
1029  buy10->setDownColor( GFXColor( 0, 1, 1, .4 ) );
1030  buy10->setDownTextColor( GFXColor( .2, .2, .2 ) );
1031  buy10->setVariableBorderCycleTime( 1.0 );
1032  buy10->setBorderColor( GFXColor( .2, .2, .2 ) );
1033  buy10->setEndBorderColor( GFXColor( .4, .4, .4 ) );
1034  buy10->setShadowWidth( 2.0 );
1035  buy10->setFont( Font( .08, BOLD_STROKE ) );
1036  buy10->setId( "Commit10" );
1037  buy10->setLabel( "Save" );
1038  buy10->setCommand( "Save" );
1039  loadSaveGroup->addChild( buy10 );
1040  }
1041  NewButton *accept = new NewButton;
1042  accept->setRect( Rect( -.11, -.2, .22, .12 ) );
1043  accept->setColor( GFXColor( 0, 1, 1, .1 ) );
1044  accept->setTextColor( GUI_OPAQUE_WHITE() );
1045  accept->setDownColor( GFXColor( 0, 1, 1, .4 ) );
1046  accept->setDownTextColor( GFXColor( .2, .2, .2 ) );
1047  accept->setVariableBorderCycleTime( 1.0 );
1048  accept->setBorderColor( GFXColor( .2, .2, .2 ) );
1049  accept->setEndBorderColor( GFXColor( .4, .4, .4 ) );
1050  accept->setShadowWidth( 2.0 );
1051  accept->setFont( Font( .08, BOLD_STROKE ) );
1052  accept->setId( "Commit" );
1053  accept->setLabel( "Load" );
1054  accept->setCommand( "Load" );
1055  loadSaveGroup->addChild( accept );
1056 
1057  NewButton *quit = new NewButton;
1058  quit->setRect( Rect( -.95, -.9, .3, .1 ) );
1059  quit->setColor( GFXColor( .8, 1, .1, .1 ) );
1060  quit->setTextColor( GUI_OPAQUE_WHITE() );
1061  quit->setDownColor( GFXColor( .8, 1, .1, .4 ) );
1062  quit->setDownTextColor( GFXColor( .2, .2, .2 ) );
1063  quit->setVariableBorderCycleTime( 1.0 );
1064  quit->setBorderColor( GFXColor( .5, .2, .2 ) );
1065  quit->setEndBorderColor( GFXColor( .7, .4, .4 ) );
1066  quit->setShadowWidth( 2.0 );
1067  quit->setFont( Font( .07, BOLD_STROKE ) );
1068  quit->setId( "CommitAll" );
1069  quit->setLabel( "Quit Game" );
1070  quit->setCommand( "Quit" );
1071  loadSaveGroup->addChild( quit );
1072 
1073  NewButton *net = new NewButton;
1074  net->setRect( Rect( .7, -.9, .25, .1 ) );
1075  net->setColor( GFXColor( 1, .5, .1, .1 ) );
1076  net->setTextColor( GUI_OPAQUE_WHITE() );
1077  net->setDownColor( GFXColor( 1, .5, .1, .4 ) );
1078  net->setDownTextColor( GFXColor( .2, .2, .2 ) );
1079  net->setVariableBorderCycleTime( 1.0 );
1080  net->setBorderColor( GFXColor( .2, .5, .2 ) );
1081  net->setEndBorderColor( GFXColor( .4, .7, .4 ) );
1082  net->setShadowWidth( 2.0 );
1083  net->setFont( Font( .07, 1 ) );
1084  net->setId( "CommitAll" );
1085  net->setLabel( "Net Play" );
1086  net->setCommand( "ShowNetworkMenu" );
1087  loadSaveGroup->addChild( net );
1088 
1089  NewButton *newgame = new NewButton;
1090  newgame->setRect( Rect( -.11, -.4, .22, .12 ) );
1091  newgame->setColor( GFXColor( 0, 1, 1, .1 ) );
1092  newgame->setTextColor( GUI_OPAQUE_WHITE() );
1093  newgame->setDownColor( GFXColor( 0, 1, 1, .4 ) );
1094  newgame->setDownTextColor( GFXColor( .2, .2, .2 ) );
1095  newgame->setVariableBorderCycleTime( 1.0 );
1096  newgame->setBorderColor( GFXColor( .2, .2, .2 ) );
1097  newgame->setEndBorderColor( GFXColor( .4, .4, .4 ) );
1098  newgame->setShadowWidth( 2.0 );
1099  newgame->setFont( Font( .08, BOLD_STROKE ) );
1100  newgame->setId( "NewGame" );
1101  newgame->setLabel( "New" );
1102  newgame->setCommand( "New" );
1103  loadSaveGroup->addChild( newgame );
1104  }
1105  {
1106  GroupControl *networkGroup = new GroupControl;
1107  networkGroup->setId( "NetworkGroup" );
1108  window()->addControl( networkGroup );
1109  GroupControl *netJoinGroup = new GroupControl;
1110  netJoinGroup->setId( "NetworkJoinGroup" );
1111  networkGroup->addChild( netJoinGroup );
1112  GroupControl *netStatGroup = new GroupControl;
1113  netStatGroup->setId( "NetworkStatGroup" );
1114  netStatGroup->setHidden( true );
1115  networkGroup->addChild( netStatGroup );
1116 
1117  GFXColor color = getColorForGroup( "NetworkGroup" );
1118 
1120  if (Network) {
1121  NetClient &nc = Network[0]; //Hack?: Assume 0th player.
1122  netStatGroup->setHidden( false );
1123  netJoinGroup->setHidden( true );
1124 
1125  std::string serverip;
1126  unsigned short serverport;
1127  nc.GetCurrentServerAddress( serverip, serverport );
1128 
1129  StaticDisplay *mplayTitle = new StaticDisplay;
1130  mplayTitle->setRect( Rect( -.7, .6, 1, .1 ) );
1131  mplayTitle->setText( "Independent Server IP Address:" );
1132  mplayTitle->setTextColor( GUI_OPAQUE_WHITE() );
1133  mplayTitle->setColor( GUI_CLEAR );
1134  mplayTitle->setFont( Font( .07, 2 ) );
1135  netStatGroup->addChild( mplayTitle );
1136 
1137  //Description box.
1138  StaticDisplay *serverInputText = new StaticDisplay;
1139  serverInputText->setRect( Rect( -.6, .5, 1.2, .1 ) );
1140  serverInputText->setColor( GUI_CLEAR );
1141  serverInputText->setFont( Font( .07 ) );
1142  serverInputText->setMultiLine( false );
1143  if (serverip == "localhost" || serverip == "127.0.0.1") {
1144  serverInputText->setText( "Locally Hosted Game" );
1145  serverInputText->setTextColor( GFXColor( .5, 1, .5 ) );
1146  } else if (serverip.substr( 0, 3 ) == "10." || serverip.substr( 0, 8 ) == "192.168."
1147  || serverip.substr( 0, 8 ) == "169.254." || serverip.find( '.' ) == std::string::npos) {
1148  serverInputText->setText( "LAN: "+serverip );
1149  serverInputText->setTextColor( GFXColor( 1, 1, .5 ) );
1150  } else {
1151  serverInputText->setText( serverip );
1152  serverInputText->setTextColor( GFXColor( 1, .8, .5 ) );
1153  }
1154  netStatGroup->addChild( serverInputText );
1155 
1156  mplayTitle = new StaticDisplay;
1157  mplayTitle->setRect( Rect( -.7, .4, 1, .1 ) );
1158  mplayTitle->setText( "Server Port: (default 6777)" );
1159  mplayTitle->setTextColor( GUI_OPAQUE_WHITE() );
1160  mplayTitle->setColor( GUI_CLEAR );
1161  mplayTitle->setFont( Font( .07, 2 ) );
1162  netStatGroup->addChild( mplayTitle );
1163 
1164  StaticDisplay *portInputText = new StaticDisplay;
1165  portInputText->setRect( Rect( -.6, .3, .4, .1 ) );
1166  portInputText->setColor( GUI_CLEAR );
1167  portInputText->setFont( Font( .07 ) );
1168  portInputText->setMultiLine( false );
1169  portInputText->setTextColor( GFXColor( .7, .7, 1 ) );
1170  {
1171  char portstr[15];
1172  sprintf( portstr, "%d", (int) (serverport) );
1173  portInputText->setText( portstr );
1174  }
1175  netStatGroup->addChild( portInputText );
1176 
1177  mplayTitle = new StaticDisplay;
1178  mplayTitle->setRect( Rect( -.7, .2, 1, .1 ) );
1179  mplayTitle->setText( "Account Server:" );
1180  mplayTitle->setTextColor( GUI_OPAQUE_WHITE() );
1181  mplayTitle->setColor( GUI_CLEAR );
1182  mplayTitle->setFont( Font( .07, 2 ) );
1183  netStatGroup->addChild( mplayTitle );
1184 
1185  StaticDisplay *acctserverInput = new StaticDisplay;
1186  acctserverInput->setRect( Rect( -.6, .1, 1.2, .1 ) );
1187  acctserverInput->setColor( GUI_CLEAR );
1188  acctserverInput->setFont( Font( .07 ) );
1189  acctserverInput->setMultiLine( false );
1190  acctserverInput->setTextColor( GFXColor( .9, 1, .6 ) );
1191 
1192  std::string acctserver;
1193  bool useacctserver = XMLSupport::parse_bool( vs_config->getVariable( "network", "use_account_server", "true" ) );
1194  if (useacctserver) {
1195  acctserver = vs_config->getVariable( "network", "account_server_url", "http://vegastrike.sourceforge.net/" );
1196  std::string::size_type s1, s3, q;
1197  s1 = acctserver.find( '/' );
1198  if (s1 != std::string::npos) {
1199  if (s1+1 < acctserver.length() && acctserver[s1+1] == '/') {
1200  s3 = acctserver.find( '/', s1+3 );
1201  if (s3 != std::string::npos) {
1202  std::string mod;
1203  q = acctserver.find( '?', s3 );
1204  if (q != std::string::npos)
1205  mod = "; "+acctserver.substr( q+1 );
1206  acctserver = acctserver.substr( s1+2, s3-s1-2 )+mod;
1207  }
1208  }
1209  }
1210  } else {
1211  acctserverInput->setTextColor( GFXColor( .7, 1, .6 ) );
1212  acctserver = "(Private Game)";
1213  }
1214  acctserverInput->setText( acctserver );
1215  netStatGroup->addChild( acctserverInput );
1216 
1217  mplayTitle = new StaticDisplay;
1218  mplayTitle->setRect( Rect( -.7, 0, .7, .1 ) );
1219  mplayTitle->setText( "Callsign:" );
1220  mplayTitle->setTextColor( GUI_OPAQUE_WHITE() );
1221  mplayTitle->setColor( GUI_CLEAR );
1222  mplayTitle->setFont( Font( .07, 2 ) );
1223  netStatGroup->addChild( mplayTitle );
1224 
1225  StaticDisplay *usernameInput = new StaticDisplay;
1226  usernameInput->setRect( Rect( -.6, -.1, .6, .1 ) );
1227  usernameInput->setColor( GUI_CLEAR );
1228  usernameInput->setFont( Font( .07 ) );
1229  usernameInput->setMultiLine( false );
1230  usernameInput->setTextColor( GFXColor( 1, .7, .9 ) );
1231  usernameInput->setText( nc.getCallsign() );
1232  netStatGroup->addChild( usernameInput );
1233 
1234  mplayTitle = new StaticDisplay;
1235  mplayTitle->setRect( Rect( 0, 0, .7, .1 ) );
1236  mplayTitle->setText( "Serial Number:" );
1237  mplayTitle->setTextColor( GUI_OPAQUE_WHITE() );
1238  mplayTitle->setColor( GUI_CLEAR );
1239  mplayTitle->setFont( Font( .07, 2 ) );
1240  netStatGroup->addChild( mplayTitle );
1241 
1242  StaticDisplay *currentSerial = new StaticDisplay;
1243  currentSerial->setRect( Rect( .1, -.1, .6, .1 ) );
1244  currentSerial->setColor( GUI_CLEAR );
1245  currentSerial->setFont( Font( .07 ) );
1246  currentSerial->setMultiLine( false );
1247  currentSerial->setTextColor( GFXColor( 1, .7, .9 ) );
1248  {
1249  char serial[15];
1250  sprintf( serial, "%d", (int) nc.serial );
1251  currentSerial->setText( serial );
1252  }
1253  netStatGroup->addChild( currentSerial );
1254 
1255  mplayTitle = new StaticDisplay;
1256  mplayTitle->setRect( Rect( -.7, -.2, .7, .1 ) );
1257  mplayTitle->setText( "Current Ship:" );
1258  mplayTitle->setTextColor( GUI_OPAQUE_WHITE() );
1259  mplayTitle->setColor( GUI_CLEAR );
1260  mplayTitle->setFont( Font( .07, 2 ) );
1261  netStatGroup->addChild( mplayTitle );
1262 
1263  StaticDisplay *currentFighter = new StaticDisplay;
1264  currentFighter->setRect( Rect( -.6, -.3, .6, .1 ) );
1265  currentFighter->setColor( GUI_CLEAR );
1266  currentFighter->setFont( Font( .07 ) );
1267  currentFighter->setMultiLine( false );
1268  currentFighter->setTextColor( GFXColor( 1, .7, .9 ) );
1269  Unit *player = nc.getUnit();
1270  if (player) {
1271  currentFighter->setText( player->getFullname() );
1272  } else {
1273  currentFighter->setTextColor( GFXColor( 1, .6, .6 ) );
1274  currentFighter->setText( "(Currently Destroyed)" );
1275  }
1276  netStatGroup->addChild( currentFighter );
1277 
1278  mplayTitle = new StaticDisplay;
1279  mplayTitle->setRect( Rect( 0, -.2, .7, .1 ) );
1280  mplayTitle->setText( "Faction:" );
1281  mplayTitle->setTextColor( GUI_OPAQUE_WHITE() );
1282  mplayTitle->setColor( GUI_CLEAR );
1283  mplayTitle->setFont( Font( .07, 2 ) );
1284  netStatGroup->addChild( mplayTitle );
1285 
1286  StaticDisplay *currentFaction = new StaticDisplay;
1287  currentFaction->setRect( Rect( .1, -.3, .6, .1 ) );
1288  currentFaction->setColor( GUI_CLEAR );
1289  currentFaction->setFont( Font( .07 ) );
1290  currentFaction->setMultiLine( false );
1291  if (player) {
1292  int fact = player->faction;
1293  const float *col = FactionUtil::GetSparkColor( fact );
1294  currentFaction->setTextColor( GFXColor( col[0], col[1], col[2] ) );
1295  currentFaction->setText( FactionUtil::GetFaction( fact ) );
1296  } else {
1297  currentFaction->setTextColor( GFXColor( 1, .6, .6 ) );
1298  currentFaction->setText( "(Currently Destroyed)" );
1299  }
1300  netStatGroup->addChild( currentFaction );
1301 
1302  NewButton *saveNetGame = new NewButton;
1303  saveNetGame->setRect( Rect( -.7, -.65, .4, .13 ) );
1304  saveNetGame->setColor( GFXColor( .2, 1, 0, .1 ) );
1305  saveNetGame->setTextColor( GUI_OPAQUE_WHITE() );
1306  saveNetGame->setDownColor( GFXColor( .2, 1, 0, .4 ) );
1307  saveNetGame->setDownTextColor( GFXColor( .2, .2, .2 ) );
1308  saveNetGame->setFont( Font( .07, 1 ) );
1309  saveNetGame->setCommand( "NetworkSaveGame" );
1310  saveNetGame->setLabel( "Save Progress" );
1311  netStatGroup->addChild( saveNetGame );
1312 
1313  NewButton *reloadNet = new NewButton;
1314  reloadNet->setRect( Rect( .3, -.65, .4, .13 ) );
1315  reloadNet->setColor( GFXColor( 1, .2, 0, .1 ) );
1316  reloadNet->setTextColor( GUI_OPAQUE_WHITE() );
1317  reloadNet->setDownColor( GFXColor( 1, .2, 0, .4 ) );
1318  reloadNet->setDownTextColor( GFXColor( .2, .2, .2 ) );
1319  reloadNet->setFont( Font( .07, 1 ) );
1320  reloadNet->setCommand( "NetworkDie" );
1321  reloadNet->setLabel( "Die and Reload" );
1322  netStatGroup->addChild( reloadNet );
1323 
1324  NewButton *hideNetStatus = new NewButton;
1325  hideNetStatus->setRect( Rect( -.2, -.65, .4, .13 ) );
1326  hideNetStatus->setColor( GFXColor( .2, .8, 1, .1 ) );
1327  hideNetStatus->setTextColor( GUI_OPAQUE_WHITE() );
1328  hideNetStatus->setDownColor( GFXColor( .2, .8, 1, .4 ) );
1329  hideNetStatus->setDownTextColor( GFXColor( .2, .2, .2 ) );
1330  hideNetStatus->setFont( Font( .07, 1 ) );
1331  hideNetStatus->setCommand( "HideNetworkStatus" );
1332  hideNetStatus->setLabel( "Join Another Server" );
1333  netStatGroup->addChild( hideNetStatus );
1334  }
1335  if (m_displayModes.size() != 1 || m_displayModes[0] != NETWORK) {
1336  NewButton *loadsave = new NewButton;
1337  loadsave->setRect( Rect( .7, -.9, .25, .1 ) );
1338  loadsave->setColor( GFXColor( 1, .5, .1, .1 ) );
1339  loadsave->setTextColor( GUI_OPAQUE_WHITE() );
1340  loadsave->setDownColor( GFXColor( 1, .5, .1, .4 ) );
1341  loadsave->setDownTextColor( GFXColor( .2, .2, .2 ) );
1342  loadsave->setVariableBorderCycleTime( 1.0 );
1343  loadsave->setBorderColor( GFXColor( .2, .5, .2 ) );
1344  loadsave->setEndBorderColor( GFXColor( .4, .7, .4 ) );
1345  loadsave->setShadowWidth( 2.0 );
1346  loadsave->setFont( Font( .07, 1 ) );
1347  loadsave->setId( "CommitAll" );
1348  if (Network != NULL)
1349  loadsave->setLabel( "Single Player" );
1350  else
1351  loadsave->setLabel( "Save/Load" );
1352  loadsave->setCommand( "ShowOptionsMenu" );
1353  networkGroup->addChild( loadsave );
1354  }
1355  if ( (m_displayModes.size() == 1 && m_displayModes[0] == NETWORK) || Network != NULL ) {
1356  NewButton *quit = new NewButton;
1357  quit->setRect( Rect( -.95, -.9, .3, .1 ) );
1358  quit->setColor( GFXColor( .8, 1, .1, .1 ) );
1359  quit->setTextColor( GUI_OPAQUE_WHITE() );
1360  quit->setDownColor( GFXColor( .8, 1, .1, .4 ) );
1361  quit->setDownTextColor( GFXColor( .2, .2, .2 ) );
1362  quit->setVariableBorderCycleTime( 1.0 );
1363  quit->setBorderColor( GFXColor( .5, .2, .2 ) );
1364  quit->setEndBorderColor( GFXColor( .7, .4, .4 ) );
1365  quit->setShadowWidth( 2.0 );
1366  quit->setFont( Font( .07, BOLD_STROKE ) );
1367  quit->setId( "CommitAll" );
1368  quit->setLabel( "Quit Game" );
1369  quit->setCommand( "Quit" );
1370  networkGroup->addChild( quit );
1371  }
1372  }
1373  {
1374  //MISSIONS group control.
1375  GroupControl *missionsGroup = new GroupControl;
1376  missionsGroup->setId( "MissionsGroup" );
1377  window()->addControl( missionsGroup );
1378  GFXColor color = getColorForGroup( "MissionsGroup" );
1379 
1380  //Scroller for picker.
1381  Scroller *pickScroller = new Scroller;
1382  pickScroller->setRect( Rect( -.20, -.8, .05, 1.45 ) );
1383  pickScroller->setColor( UnsaturatedColor( color.r, color.g, color.b, .1 ) );
1384  pickScroller->setThumbColor( UnsaturatedColor( color.r*.4, color.g*.4, color.b*.4 ), GUI_OPAQUE_WHITE() );
1385  pickScroller->setButtonColor( UnsaturatedColor( color.r*.4, color.g*.4, color.b*.4 ) );
1386  pickScroller->setTextColor( GUI_OPAQUE_WHITE() );
1387  pickScroller->setOutlineColor( GUI_OPAQUE_MEDIUM_GRAY() );
1388 
1389  //Picker.
1390  SimplePicker *pick = new SimplePicker;
1391  pick->setRect( Rect( -.96, -.8, .76, 1.45 ) );
1392  pick->setColor( GFXColor( color.r, color.g, color.b, .1 ) );
1394  pick->setTextColor( GUI_OPAQUE_WHITE() );
1395  pick->setSelectionColor( UnsaturatedColor( 0, .6, 0, .8 ) );
1396  pick->setHighlightColor( UnsaturatedColor( 0, .6, 0, .35 ) );
1398  pick->setFont( Font( .07 ) );
1399  pick->setTextMargins( Size( 0.02, 0.01 ) );
1400  pick->setId( "Missions" );
1401  pick->setScroller( pickScroller );
1402  missionsGroup->addChild( pick );
1403 
1404  missionsGroup->addChild( pickScroller ); //Want scroller "over" picker.
1405 
1406  //Scroller for description.
1407  Scroller *descScroller = new Scroller;
1408  descScroller->setRect( Rect( .91, -.8, .05, 1.45 ) );
1409  descScroller->setColor( UnsaturatedColor( color.r, color.g, color.b, .1 ) );
1410  descScroller->setThumbColor( UnsaturatedColor( color.r*.4, color.g*.4, color.b*.4 ), GUI_OPAQUE_WHITE() );
1411  descScroller->setButtonColor( UnsaturatedColor( color.r*.4, color.g*.4, color.b*.4 ) );
1412  descScroller->setTextColor( GUI_OPAQUE_WHITE() );
1413  descScroller->setOutlineColor( GUI_OPAQUE_MEDIUM_GRAY() );
1414 
1415  //Description box.
1416  StaticDisplay *ms = new StaticDisplay;
1417  ms->setRect( Rect( -.10, -.8, 1.01, 1.45 ) );
1418  ms->setColor( GFXColor( color.r, color.g, color.b, .1 ) );
1420  ms->setFont( Font( .06 ) );
1421  ms->setMultiLine( true );
1422  ms->setTextColor( GUI_OPAQUE_WHITE() );
1423  ms->setTextMargins( Size( .02, .01 ) );
1424  ms->setId( "Description" );
1425  ms->setScroller( descScroller );
1426  missionsGroup->addChild( ms );
1427 
1428  missionsGroup->addChild( descScroller ); //Want scroller "over" description box.
1429 
1430  //Accept button.
1431  NewButton *accept = new NewButton;
1432  accept->setRect( Rect( -.23, -.95, .22, .11 ) );
1433  accept->setColor( GFXColor( 0, 1, 1, .1 ) );
1434  accept->setTextColor( GUI_OPAQUE_WHITE() );
1435  accept->setDownColor( GFXColor( 0, 1, 1, .4 ) );
1436  accept->setDownTextColor( GFXColor( .2, .2, .2 ) );
1437  accept->setVariableBorderCycleTime( 1.0 );
1438  accept->setBorderColor( GFXColor( .2, .2, .2 ) );
1439  accept->setEndBorderColor( GFXColor( .4, .4, .4 ) );
1440  accept->setShadowWidth( 2.0 );
1441  accept->setFont( Font( .08, BOLD_STROKE ) );
1442  accept->setId( "Commit" );
1443  missionsGroup->addChild( accept );
1444  }
1445  {
1446  //SHIP_DEALER group control.
1447  GroupControl *shipDealerGroup = new GroupControl;
1448  shipDealerGroup->setId( "ShipDealerGroup" );
1449  window()->addControl( shipDealerGroup );
1450  GFXColor color = getColorForGroup( "ShipDealerGroup" );
1451 
1452  //Scroller for picker.
1453  Scroller *pickScroller = new Scroller;
1454  pickScroller->setRect( Rect( -.20, -.8, .05, 1.45 ) );
1455  pickScroller->setColor( UnsaturatedColor( color.r, color.g, color.b, .1 ) );
1456  pickScroller->setThumbColor( UnsaturatedColor( color.r*.4, color.g*.4, color.b*.4 ), GUI_OPAQUE_WHITE() );
1457  pickScroller->setButtonColor( UnsaturatedColor( color.r*.4, color.g*.4, color.b*.4 ) );
1458  pickScroller->setTextColor( GUI_OPAQUE_WHITE() );
1459  pickScroller->setOutlineColor( GUI_OPAQUE_MEDIUM_GRAY() );
1460 
1461  //Picker.
1462  SimplePicker *pick = new SimplePicker;
1463  pick->setRect( Rect( -.96, -.8, .76, 1.45 ) );
1464  pick->setColor( GFXColor( color.r, color.g, color.b, .1 ) );
1466  pick->setTextColor( GUI_OPAQUE_WHITE() );
1467  pick->setSelectionColor( UnsaturatedColor( 0, .6, 0, .8 ) );
1468  pick->setHighlightColor( UnsaturatedColor( 0, .6, 0, .35 ) );
1470  pick->setFont( Font( .07 ) );
1471  pick->setTextMargins( Size( 0.02, 0.01 ) );
1472  pick->setId( "Ships" );
1473  pick->setScroller( pickScroller );
1474  shipDealerGroup->addChild( pick );
1475 
1476  shipDealerGroup->addChild( pickScroller ); //Want scroller to be "over" picker.
1477 
1478  //Scroller for description.
1479  Scroller *descScroller = new Scroller;
1480  descScroller->setRect( Rect( .91, -.5, .05, 1.15 ) );
1481  descScroller->setColor( UnsaturatedColor( color.r, color.g, color.b, .1 ) );
1482  descScroller->setThumbColor( UnsaturatedColor( color.r*.4, color.g*.4, color.b*.4 ), GUI_OPAQUE_WHITE() );
1483  descScroller->setButtonColor( UnsaturatedColor( color.r*.4, color.g*.4, color.b*.4 ) );
1484  descScroller->setTextColor( GUI_OPAQUE_WHITE() );
1485  descScroller->setOutlineColor( GUI_OPAQUE_MEDIUM_GRAY() );
1486 
1487  //Description box.
1488  StaticDisplay *ms = new StaticDisplay;
1489  StaticImageDisplay *picture = new StaticImageDisplay;
1490  picture->setRect( Rect( -.10, -.51, .48*.75, -.48 ) );
1491  picture->setTexture( "blackclear.png" );
1492  picture->setId( "DescriptionImage" );
1493  ms->setRect( Rect( -.10, -.5, 1.01, 1.15 ) );
1494  ms->setColor( GFXColor( color.r, color.g, color.b, .1 ) );
1496  ms->setFont( Font( .06 ) );
1497  ms->setMultiLine( true );
1498  ms->setTextColor( GUI_OPAQUE_WHITE() );
1499  ms->setTextMargins( Size( .02, .01 ) );
1500  ms->setId( "Description" );
1501  ms->setScroller( descScroller );
1502  shipDealerGroup->addChild( ms );
1503 
1504  shipDealerGroup->addChild( descScroller ); //Want scroller "over" description box.
1505  shipDealerGroup->addChild( picture );
1506  //Buy button.
1507  NewButton *buy = new NewButton;
1508  buy->setRect( Rect( -.53, -.95, .22, .11 ) );
1509  buy->setColor( GFXColor( 0, 1, 1, .1 ) );
1510  buy->setTextColor( GUI_OPAQUE_WHITE() );
1511  buy->setDownColor( GFXColor( 0, 1, 1, .4 ) );
1512  buy->setDownTextColor( GFXColor( .2, .2, .2 ) );
1513  buy->setVariableBorderCycleTime( 1.0 );
1514  buy->setBorderColor( GFXColor( .2, .2, .2 ) );
1515  buy->setEndBorderColor( GFXColor( .4, .4, .4 ) );
1516  buy->setShadowWidth( 2.0 );
1517  buy->setFont( Font( .08, BOLD_STROKE ) );
1518  buy->setId( "Commit" );
1519  shipDealerGroup->addChild( buy );
1520  NewButton *sell = new NewButton;
1521  sell->setRect( Rect( -.23, -.95, .22, .11 ) );
1522  sell->setColor( GFXColor( 0, 1, 1, .1 ) );
1523  sell->setTextColor( GUI_OPAQUE_WHITE() );
1524  sell->setDownColor( GFXColor( 0, 1, 1, .4 ) );
1525  sell->setDownTextColor( GFXColor( .2, .2, .2 ) );
1526  sell->setVariableBorderCycleTime( 1.0 );
1527  sell->setBorderColor( GFXColor( .2, .2, .2 ) );
1528  sell->setEndBorderColor( GFXColor( .4, .4, .4 ) );
1529  sell->setShadowWidth( 2.0 );
1530  sell->setFont( Font( .08, BOLD_STROKE ) );
1531  sell->setId( "Commit10" );
1532  shipDealerGroup->addChild( sell );
1533  }
1534  {
1535  //INFO group control.
1536  GroupControl *infoGroup = new GroupControl;
1537  infoGroup->setId( "InfoGroup" );
1538  window()->addControl( infoGroup );
1539  GFXColor color = getColorForGroup( "InfoGroup" );
1540 
1541  //Player Info button.
1542  NewButton *playerInfo = new NewButton;
1543  playerInfo->setRect( Rect( -.40, .52, .27, .09 ) );
1544  playerInfo->setLabel( "Player Info" );
1545  static GFXColor pinfo_col = getConfigColor( "player_info", GFXColor( 0, .4, 0 ) );
1546  playerInfo->setCommand( "ShowPlayerInfo" );
1547 
1548  playerInfo->setColor( GFXColor( pinfo_col.r, pinfo_col.g, pinfo_col.b, .25 ) );
1549  playerInfo->setTextColor( GUI_OPAQUE_WHITE() );
1550  playerInfo->setDownColor( GFXColor( pinfo_col.r, pinfo_col.g, pinfo_col.b, .5 ) );
1551  playerInfo->setDownTextColor( GUI_OPAQUE_BLACK() );
1552  playerInfo->setHighlightColor( GFXColor( pinfo_col.r, pinfo_col.g, pinfo_col.b, .4 ) );
1553  playerInfo->setFont( Font( .07 ) );
1554  infoGroup->addChild( playerInfo );
1555 
1556  //Ship Stats button.
1557  NewButton *shipStats = new NewButton;
1558  shipStats->setRect( Rect( -.05, .52, .27, .09 ) );
1559  shipStats->setLabel( "Ship Stats" );
1560  shipStats->setCommand( "ShowShipStats" );
1561  shipStats->setColor( GFXColor( pinfo_col.r, pinfo_col.g, pinfo_col.b, .25 ) );
1562  shipStats->setTextColor( GUI_OPAQUE_WHITE() );
1563  shipStats->setDownColor( GFXColor( pinfo_col.r, pinfo_col.g, pinfo_col.b, .5 ) );
1564  shipStats->setDownTextColor( GUI_OPAQUE_BLACK() );
1565  shipStats->setHighlightColor( GFXColor( pinfo_col.r, pinfo_col.g, pinfo_col.b, .4 ) );
1566  shipStats->setFont( Font( .07 ) );
1567  infoGroup->addChild( shipStats );
1568 
1569  //Scroller for description.
1570  Scroller *descScroller = new Scroller;
1571  descScroller->setRect( Rect( .91, -.95, .05, 1.4 ) );
1572  descScroller->setColor( UnsaturatedColor( color.r, color.g, color.b, .1 ) );
1573  descScroller->setThumbColor( UnsaturatedColor( color.r*.4, color.g*.4, color.b*.4 ), GUI_OPAQUE_WHITE() );
1574  descScroller->setButtonColor( UnsaturatedColor( color.r*.4, color.g*.4, color.b*.4 ) );
1575  descScroller->setTextColor( GUI_OPAQUE_WHITE() );
1576  descScroller->setOutlineColor( GUI_OPAQUE_MEDIUM_GRAY() );
1577 
1578  //Description box.
1579  StaticDisplay *ms = new StaticDisplay;
1580  ms->setRect( Rect( -.96, -.95, 1.87, 1.4 ) );
1581  ms->setColor( GFXColor( color.r, color.g, color.b, .1 ) );
1583  ms->setFont( Font( .07 ) );
1584  ms->setMultiLine( true );
1585  ms->setTextColor( GUI_OPAQUE_WHITE() );
1586  ms->setTextMargins( Size( .02, .01 ) );
1587  ms->setId( "Description" );
1588  ms->setScroller( descScroller );
1589  infoGroup->addChild( ms );
1590 
1591  infoGroup->addChild( descScroller );
1592  }
1593 }
1594 
1595 //Create the controls that will be used for this window.
1597 {
1598  //Set up the window.
1599  window()->setFullScreen();
1600  window()->setColor( GUI_CLEAR );
1601  window()->setTexture( "basecomputer.png" );
1602 
1603  //Put all the controls in the window.
1605  //Take the mode group controls out of the window.
1606  for (int i = 0; i < DISPLAY_MODE_COUNT; i++) {
1607  Control *group = window()->findControlById( modeInfo[i].groupId );
1608  if (group) {
1609  window()->removeControlFromWindow( group );
1610  m_modeGroups[i] = group;
1611  }
1612  }
1614 }
1615 
1616 //Create the mode buttons.
1618 {
1619  NewButton *originalButton = static_cast< NewButton* > ( window()->findControlById( "ModeButton" ) );
1620  assert( originalButton != NULL );
1621  if (m_displayModes.size() > 1) {
1622  //Create a button for each display mode, copying the original button.
1623  Rect rect = originalButton->rect();
1624  for (unsigned int i = 0; i < m_displayModes.size(); i++) {
1625  DisplayMode mode = m_displayModes[i];
1626  NewButton *newButton = new NewButton( *originalButton );
1627  newButton->setRect( rect );
1628  newButton->setLabel( modeInfo[mode].button );
1629  newButton->setCommand( modeInfo[mode].command );
1630  window()->addControl( newButton );
1631  rect.origin.x += rect.size.width+MODE_BUTTON_SPACE;
1632  }
1633  }
1634  //Make sure this original doesn't show.
1635  originalButton->setHidden( true );
1636 }
1637 
1638 //Make sure the info in the transaction lists is gone.
1640 {
1643 }
1644 
1645 //Switch to the set of controls used for the specified mode.
1647 {
1648  if (m_currentDisplay != mode) {
1649  assert( m_modeGroups[mode] != NULL ); //We should have controls for this mode.
1650  if (mode == CARGO)
1651  window()->setTexture( "basecomputer_cargo.png" );
1652  if (mode == MISSIONS)
1653  window()->setTexture( "basecomputer_missions.png" );
1654  if (mode == UPGRADE)
1655  window()->setTexture( "basecomputer_upgrade.png" );
1656  if (mode == SHIP_DEALER)
1657  window()->setTexture( "basecomputer_ship_dealer.png" );
1658  if (mode == NEWS)
1659  window()->setTexture( "basecomputer_news.png" );
1660  if (mode == INFO)
1661  window()->setTexture( "basecomputer_info.png" );
1662  if (mode == LOADSAVE)
1663  window()->setTexture( "basecomputer_loadsave.png" );
1664  if (mode == NETWORK)
1665  window()->setTexture( "basecomputer_network.png" );
1666  if (m_currentDisplay != NULL_DISPLAY) {
1667  //Get the old controls out of the window.
1668  Control *oldControls = window()->findControlById( modeInfo[m_currentDisplay].groupId );
1669  if (oldControls)
1670  window()->removeControlFromWindow( oldControls );
1671  //We put this back in our table so that we "own" the controls.
1672  m_modeGroups[m_currentDisplay] = oldControls;
1673  //Stop playing muzak for the old mode.
1674  if (m_playingMuzak) {
1675  muzak->Skip();
1676  m_playingMuzak = false;
1677  }
1678  }
1679  m_currentDisplay = mode;
1680 
1681  window()->addControl( m_modeGroups[mode] );
1682  //Take this group out of our table because we don't own it anymore.
1683  m_modeGroups[mode] = NULL;
1684  }
1685 }
1686 
1687 //Change controls to CARGO mode.
1689 {
1690  if (m_currentDisplay != CARGO)
1694  return true;
1695 }
1696 
1698 {
1699  if (m_currentDisplay != LOADSAVE)
1702  return true;
1703 }
1704 
1706 {
1707  Control *group = window()->findControlById( "NetworkJoinGroup" );
1708  if (group) group->setHidden( show );
1709  group = window()->findControlById( "NetworkStatGroup" );
1710  if (group) group->setHidden( !show );
1711 }
1712 
1714 {
1715  showNetworkStatus( false );
1716  return true;
1717 }
1718 
1720 {
1721  if (m_currentDisplay != NETWORK)
1723  if (Network)
1724  showNetworkStatus( true );
1725  else
1726  showNetworkStatus( false );
1728  return true;
1729 }
1730 
1731 //Set up the window and get everything ready.
1733 {
1734  //Create a new window.
1735  Window *w = new Window;
1736  setWindow( w );
1737 
1738  //Read in the controls for all the modes.
1739  createControls();
1740 }
1741 
1742 //Open the window, etc.
1743 void BaseComputer::run( void )
1744 {
1745  //Simulate clicking the leftmost mode button.
1746  //We don't actually use the button because there isn't a button if there's only one mode.
1747  processWindowCommand( modeInfo[m_displayModes[0]].command, NULL );
1748 
1750 }
1751 
1752 //Redo the title strings for the display.
1754 {
1755  //Generic base title for the display.
1756  string baseTitle = modeInfo[m_currentDisplay].title;
1757 
1758  //Base name.
1759  Unit *baseUnit = m_base.GetUnit();
1760  string baseName;
1761  if (baseUnit) {
1762  if (baseUnit->isUnit() == PLANETPTR) {
1763  string temp = ( (Planet*) baseUnit )->getHumanReadablePlanetType()+" Planet";
1764  baseName = temp;
1765  } else {
1766  baseName = baseUnit->name;
1767  }
1768  }
1769  baseTitle += emergency_downgrade_mode;
1770  static bool includebasename =
1771  XMLSupport::parse_bool( vs_config->getVariable( "graphics", "include_base_name_on_dock", "true" ) );
1772  if (includebasename) {
1773  baseTitle += baseName;
1774 
1775  //Faction name for base.
1776  string baseFaction = FactionUtil::GetFactionName( baseUnit->faction );
1777  if ( !baseFaction.empty() )
1778  baseTitle += " ["+baseFaction+']';
1779  }
1780  //Set the string in the base title control.
1781  StaticDisplay *baseTitleDisplay = static_cast< StaticDisplay* > ( window()->findControlById( "BaseInfoTitle" ) );
1782  assert( baseTitleDisplay != NULL );
1783  baseTitleDisplay->setText( baseTitle );
1784 
1785  //Generic player title for display
1786  char playerTitle[256];
1787  playerTitle[0] = '\0'; //Start with an empty string.
1788 
1789  static bool showStardate =
1790  XMLSupport::parse_bool( vs_config->getVariable( "graphics", "show_stardate", "true" ) );
1791 
1792  //Credits the player has.
1793  const float playerCredits = _Universe->AccessCockpit()->credits;
1794  const char *stardate = _Universe->current_stardate.GetFullTrekDate().c_str();
1795  switch (m_currentDisplay)
1796  {
1797  default:
1798  if (showStardate) {
1799  sprintf( playerTitle, "Stardate: %s Credits: %.2f", stardate, playerCredits );
1800  } else {
1801  sprintf( playerTitle, "Credits: %.2f", playerCredits );
1802  }
1803  break;
1804  case MISSIONS:
1805  {
1806  const int count = guiMax( 0, active_missions.size()-1 );
1807  if (showStardate) {
1808  sprintf( playerTitle, "Stardate: %s Credits: %.2f Active missions: %d", stardate, playerCredits, count );
1809  } else {
1810  sprintf( playerTitle, "Credits: %.2f Active missions: %d", playerCredits, count );
1811  }
1812  break;
1813  }
1814  case UPGRADE:
1815  //Fall through.
1816  case CARGO:
1817  {
1818  Unit *playerUnit = m_player.GetUnit();
1819  if (playerUnit) {
1820  const float emptyVolume = m_currentDisplay
1821  == CARGO ? playerUnit->getEmptyCargoVolume() : playerUnit->getEmptyUpgradeVolume();
1822  const float volumeLeft = emptyVolume
1823  -( m_currentDisplay
1824  == CARGO ? playerUnit->getCargoVolume() : playerUnit->getUpgradeVolume() );
1825  if (showStardate) {
1826  sprintf( playerTitle, "Stardate: %s Credits: %.2f Space left: %.6g of %.6g cubic meters", stardate, playerCredits, volumeLeft, emptyVolume );
1827  } else {
1828  sprintf( playerTitle, "Credits: %.2f Space left: %.6g of %.6g cubic meters", playerCredits, volumeLeft, emptyVolume );
1829  }
1830  }
1831  break;
1832  }
1833  }
1834  //Set the string in the player title control.
1835  StaticDisplay *playerTitleDisplay = static_cast< StaticDisplay* > ( window()->findControlById( "PlayerInfoTitle" ) );
1836  assert( playerTitleDisplay != NULL );
1837  playerTitleDisplay->setText( playerTitle );
1838 }
1839 
1840 //Scroll to a specific item in a picker, and optionally select it.
1841 //Returns true if we selected an item.
1842 bool BaseComputer::scrollToItem( Picker *picker, const Cargo &item, bool select, bool skipFirstCategory )
1843 {
1844  PickerCells *cells = picker->cells();
1845  if (!cells) return false;
1846  PickerCell *categoryCell = NULL; //Need this if we can't find the item.
1847 
1848  //Walk through the category list(s).
1849  std::string category = getDisplayCategory( item );
1850  if (category.size() > 0) {
1851  //Make sure we have a category.
1852  string::size_type categoryStart = 0;
1853  if (skipFirstCategory) {
1854  //We need to skip the first category in the string.
1855  //Generally need to do this when there's a category level that's not in the UI, like
1856  //"upgrades" in the Upgrade UI.
1857  categoryStart = category.find( CATEGORY_SEP, 0 );
1858  if (categoryStart != string::npos) categoryStart++;
1859  }
1860  while (true) {
1861  //See if we have multiple categories left.
1862  const string::size_type categoryEnd = category.find( CATEGORY_SEP, categoryStart );
1863  const string currentCategory = category.substr( categoryStart, categoryEnd-categoryStart );
1864 
1865  PickerCell *cell = cells->cellWithId( currentCategory );
1866  if ( !cell || !cell->children() ) {
1867  //The category has no children, or we have no matching category. We are done.
1868  //WARNING: We return from here!
1869  picker->scrollToCell( categoryCell );
1870  return false;
1871  }
1872  //Found the category in the right place.
1873  categoryCell = cell;
1874  categoryCell->setHideChildren( false );
1875  picker->setMustRecalc();
1876  cells = categoryCell->children();
1877  //Next piece of the category string.
1878  if (categoryEnd == string::npos)
1879  break;
1880  categoryStart = categoryEnd+1;
1881  }
1882  }
1883  //We have the parent category, now we need the child itself.
1884  assert( cells != NULL );
1885  PickerCell *cell = cells->cellWithId( item.content );
1886  picker->setMustRecalc();
1887  if (!cell) {
1888  //Item is not here.
1889  int count = cells->count();
1890  if (count == 0) {
1891  //The category is empty.
1892  picker->scrollToCell( categoryCell );
1893  return false;
1894  }
1895  //Try to find the place where the item used to be.
1896  //We assume here that the list is sorted by id, which is the
1897  //original, un-beautified name.
1898  int i = 0;
1899  for (; i < count; i++)
1900  if ( item.content < cells->cellAt( i )->id() )
1901  break;
1902  if (i == count) i--;
1903  cell = cells->cellAt( i );
1904  assert( cell != NULL );
1905  //Drop through to get cell handled.
1906  }
1907  if (select) {
1908  picker->selectCell( cell, true );
1909  //This may not be a selectable cell.
1910  return picker->selectedCell() != NULL;
1911  } else {
1912  //Make sure we scroll it into view.
1913  //Since it's not selected, we assume it's in the "other" list and scroll
1914  //it into the middle.
1915  picker->scrollToCell( cell, true );
1916  return false;
1917  }
1918  //Shouldn't ever get here.
1919  assert( false );
1920  return false;
1921 }
1922 
1923 //Hide the controls that commit transactions.
1925 {
1926  //The three buy/sell buttons.
1927  NewButton *commitButton = static_cast< NewButton* > ( window()->findControlById( "Commit" ) );
1928  commitButton->setHidden( true );
1929  NewButton *commit10Button = static_cast< NewButton* > ( window()->findControlById( "Commit10" ) );
1930  if (commit10Button != NULL) commit10Button->setHidden( true );
1931  NewButton *commitAllButton = static_cast< NewButton* > ( window()->findControlById( "CommitAll" ) );
1932  if (commitAllButton != NULL) commitAllButton->setHidden( true );
1933  NewButton *commitFixButton = static_cast< NewButton* > ( window()->findControlById( "CommitFix" ) );
1934  if (commitFixButton != NULL) commitFixButton->setHidden( true );
1935  //The price and "max" displays.
1936  StaticDisplay *totalPrice = static_cast< StaticDisplay* > ( window()->findControlById( "TotalPrice" ) );
1937  if (totalPrice != NULL) totalPrice->setText( "" );
1938  StaticDisplay *maxForPlayer = static_cast< StaticDisplay* > ( window()->findControlById( "MaxQuantity" ) );
1939  if (maxForPlayer != NULL) maxForPlayer->setText( "" );
1940 }
1941 
1942 //Update the commit controls in the Cargo screen, since we have three of them.
1944 {
1945  if (trans == BUY_CARGO) {
1946  //"Buy 1" button.
1947  NewButton *commitButton = static_cast< NewButton* > ( window()->findControlById( "Commit" ) );
1948  assert( commitButton != NULL );
1949  commitButton->setHidden( false );
1950  commitButton->setLabel( "Buy 1" );
1951  commitButton->setCommand( "BuyCargo" );
1952 
1953  //"Buy 10" button.
1954  NewButton *commit10Button = static_cast< NewButton* > ( window()->findControlById( "Commit10" ) );
1955  assert( commit10Button != NULL );
1956  commit10Button->setHidden( false );
1957  commit10Button->setLabel( "Buy 10" );
1958  commit10Button->setCommand( "Buy10Cargo" );
1959 
1960  //"Buy All" button.
1961  NewButton *commitAllButton = static_cast< NewButton* > ( window()->findControlById( "CommitAll" ) );
1962  assert( commitAllButton != NULL );
1963  commitAllButton->setHidden( false );
1964  commitAllButton->setLabel( "Buy" );
1965  commitAllButton->setCommand( "BuyAllCargo" );
1966 
1967  const int maxQuantity = maxQuantityForPlayer( item, item.quantity );
1968 
1969  //Total price display.
1970  const double totalPrice = item.price*maxQuantity;
1971  char tempString[2048];
1972  sprintf( tempString, "Total: #b#%.2f#-b", totalPrice );
1973  StaticDisplay *totalDisplay = static_cast< StaticDisplay* > ( window()->findControlById( "TotalPrice" ) );
1974  assert( totalDisplay != NULL );
1975  totalDisplay->setText( tempString );
1976 
1977  //Limit if we have one.
1978  StaticDisplay *maxForPlayer = static_cast< StaticDisplay* > ( window()->findControlById( "MaxQuantity" ) );
1979  assert( maxForPlayer != NULL );
1980  if (maxQuantity >= item.quantity) {
1981  //No limits, so let's not mention anything.
1982  maxForPlayer->setText( "" );
1983  } else {
1984  char maxString[2048];
1985  sprintf( maxString, "Max: #b#%d#-b", maxQuantity );
1986  maxForPlayer->setText( maxString );
1987  }
1988  } else {
1989  assert( trans == SELL_CARGO );
1990 
1991  //"Sell" button.
1992  NewButton *commitButton = static_cast< NewButton* > ( window()->findControlById( "Commit" ) );
1993  assert( commitButton != NULL );
1994  commitButton->setHidden( false );
1995  commitButton->setLabel( item.mission ? "Dump 1" : "Sell 1" );
1996  commitButton->setCommand( "SellCargo" );
1997 
1998  //"Sell 10" button.
1999  NewButton *commit10Button = static_cast< NewButton* > ( window()->findControlById( "Commit10" ) );
2000  assert( commit10Button != NULL );
2001  commit10Button->setHidden( false );
2002  commit10Button->setLabel( item.mission ? "Dump 10" : "Sell 10" );
2003  commit10Button->setCommand( "Sell10Cargo" );
2004 
2005  //"Sell All" button.
2006  NewButton *commitAllButton = static_cast< NewButton* > ( window()->findControlById( "CommitAll" ) );
2007  assert( commitAllButton != NULL );
2008  commitAllButton->setHidden( false );
2009  commitAllButton->setLabel( item.mission ? "Dump" : "Sell" );
2010  commitAllButton->setCommand( "SellAllCargo" );
2011 
2012  //Total price display.
2013  const double totalPrice = item.price*item.quantity*(item.mission ? 0 : 1);
2014  char tempString[2048];
2015  sprintf( tempString, "Total: #b#%.2f#-b", totalPrice );
2016  StaticDisplay *totalDisplay = static_cast< StaticDisplay* > ( window()->findControlById( "TotalPrice" ) );
2017  assert( totalDisplay != NULL );
2018  totalDisplay->setText( tempString );
2019 
2020  //No limit.
2021  StaticDisplay *maxForPlayer = static_cast< StaticDisplay* > ( window()->findControlById( "MaxQuantity" ) );
2022  assert( maxForPlayer != NULL );
2023  maxForPlayer->setText( "" );
2024  }
2025 }
2026 
2027 //Update the commit controls in the Cargo screen, since we have three of them.
2029 {
2030  bool damaged_mode = false;
2031  if (trans == BUY_UPGRADE) {
2032  //base inventory
2033  NewButton *commitButton = static_cast< NewButton* > ( window()->findControlById( "Commit" ) );
2034  assert( commitButton != NULL );
2035  commitButton->setHidden( false );
2036  commitButton->setLabel( "Buy" );
2037  commitButton->setCommand( "BuyUpgrade" );
2038 
2039  NewButton *commitFixButton = static_cast< NewButton* > ( window()->findControlById( "CommitFix" ) );
2040  assert( commitButton != NULL );
2041  commitFixButton->setHidden( true );
2042  commitFixButton->setLabel( "Fix" );
2043  commitFixButton->setCommand( "FixUpgrade" );
2044  } else {
2045  //Sell Upgrade - Local Inventory
2046  NewButton *commitButton = static_cast< NewButton* > ( window()->findControlById( "Commit" ) );
2047  assert( commitButton != NULL );
2048  if ( m_player.GetUnit() ) {
2049  bool CanDoSell = true;
2050  Unit *player = m_player.GetUnit();
2051  unsigned int numc = player->numCargo();
2052  if ( !isWeapon( item.category ) ) {
2053  //weapons can always be sold
2054  for (unsigned int i = 0; i < numc; ++i) {
2055  Cargo *c = &player->GetCargo( i );
2056  if ( c->GetCategory().find( "upgrades/" ) == 0 && !isWeapon( c->category ) ) {
2057  float po = UnitUtil::PercentOperational( player, c->content, c->category, false );
2058  if (po > .02 && po < .98) {
2059  static bool must_fix_first =
2060  XMLSupport::parse_bool( vs_config->getVariable( "physics", "must_repair_to_sell", "true" ) );
2061 
2062  CanDoSell = (emergency_downgrade_mode.length() != 0 || must_fix_first == false);
2063  }
2064  }
2065  }
2066  }
2067  if (CanDoSell) {
2068  commitButton->setHidden( false );
2069  commitButton->setLabel( "Sell" );
2070  commitButton->setCommand( "SellUpgrade" );
2071  } else {
2072  damaged_mode = true;
2073  commitButton->setHidden( true );
2074  commitButton->setLabel( "Fix1st" );
2075  commitButton->setCommand( "" );
2076  }
2077  }
2078  NewButton *commitFixButton = static_cast< NewButton* > ( window()->findControlById( "CommitFix" ) );
2079  bool unhidden = true;
2080  if (m_player.GetUnit() && UnitUtil::PercentOperational( m_player.GetUnit(), item.content, item.category, false ) < 1) {
2081  if ( m_base.GetUnit() ) {
2083  item.content, item.category, false ),
2084  m_base.GetUnit()->PriceCargo( item.content ) )
2085  <= _Universe->AccessCockpit()->credits) {
2086  assert( commitFixButton != NULL );
2087  if (commitFixButton) {
2088  commitFixButton->setHidden( false );
2089  commitFixButton->setLabel( "Fix" );
2090  commitFixButton->setCommand( "FixUpgrade" );
2091  unhidden = false;
2092  }
2093  }
2094  }
2095  }
2096  if (unhidden && commitFixButton)
2097  commitFixButton->setHidden( true );
2098  }
2099  return damaged_mode;
2100 }
2101 
2102 
2103 //string buildShipDescription(Cargo &item,string & descriptiontexture); //Redundant definition
2104 //Update the controls when the selection for a transaction changes.
2106 {
2107  //Get the controls we need.
2108  NewButton *commitButton = static_cast< NewButton* > ( window()->findControlById( "Commit" ) );
2109  assert( commitButton != NULL );
2110  StaticDisplay *desc = static_cast< StaticDisplay* > ( window()->findControlById( "Description" ) );
2111  std::string descriptiontexture;
2112  assert( desc != NULL );
2113  if (!tlist) {
2114  //We have no selection. Turn off UI that commits a transaction.
2115  m_selectedList = NULL;
2117  desc->setText( "" );
2118  //Make sure there is no selection.
2121  return;
2122  }
2123  //We have a selection of some sort.
2124 
2125  //Set the button state.
2126  m_selectedList = tlist;
2127 
2128  //Clear selection from other list.
2130  if (otherList.picker) otherList.picker->selectCell( NULL );
2131  //They selected a cell that has a description.
2132  //The selected item.
2133  const PickerCell *cell = tlist->picker->selectedCell();
2134  assert( cell != NULL );
2135  Cargo &item = tlist->masterList[cell->tag()].cargo;
2136  bool damaged_mode = false;
2137  if ( !isTransactionOK( item, tlist->transaction ) ) {
2138  //We can't do the transaction. so hide the transaction button.
2139  //This is an odd state. We have a selection, but no transaction is possible.
2141  } else {
2142  //We can do the transaction.
2143  commitButton->setHidden( false );
2144  switch (tlist->transaction)
2145  {
2146  case BUY_CARGO:
2148  break;
2149  case BUY_UPGRADE:
2151  break;
2152  case BUY_SHIP:
2153  commitButton->setLabel( "Buy" );
2154  commitButton->setCommand( "BuyShip" );
2155  if (item.GetCategory().find( "My_Fleet" ) != string::npos) {
2156  //note can only sell it if you can afford to ship it over here.
2157  NewButton *commit10Button = static_cast< NewButton* > ( window()->findControlById( "Commit10" ) );
2158  assert( commit10Button != NULL );
2159  commit10Button->setHidden( false );
2160  commit10Button->setLabel( "Sell" );
2161  commit10Button->setCommand( "SellShip" );
2162  }
2163  break;
2164  case SELL_CARGO:
2166  break;
2167  case SELL_UPGRADE:
2168  damaged_mode = configureUpgradeCommitControls( item, SELL_UPGRADE );
2169  break;
2170  case ACCEPT_MISSION:
2171  if (item.GetCategory().find( "Active_Missions" ) != string::npos) {
2172  commitButton->setLabel( "Abort" );
2173  static bool allow_abort_mission =
2174  XMLSupport::parse_bool( vs_config->getVariable( "physics", "allow_mission_abort", "true" ) );
2175  if (allow_abort_mission == false)
2176  commitButton->setHidden( true );
2177  } else {
2178  commitButton->setLabel( "Accept" );
2179  }
2180  commitButton->setCommand( "AcceptMission" );
2181  break;
2182  default:
2183  assert( false ); //Missed enum in transaction.
2184  break;
2185  }
2186  }
2187  //The description string.
2188  char conversionBuffer[128];
2189  string text = "";
2190  string descString;
2191  string tailString;
2192  char tempString[2048];
2193  Unit *baseUnit = m_base.GetUnit();
2194  if (tlist->transaction != ACCEPT_MISSION) {
2195  //Do the money.
2196  switch (tlist->transaction)
2197  {
2198  case BUY_CARGO:
2199  if (item.GetDescription() == "" || item.GetDescription()[0] != '@') {
2200  buildShipDescription( item, descriptiontexture ); //Check for ship
2201  string temp = item.description; //do first, so can override default image, if so desired
2202  if ( ( string::npos != temp.find( '@' ) ) && ("" != descriptiontexture) ) //not already pic-annotated, has non-null ship pic
2203  temp = "@"+descriptiontexture+"@"+temp;
2204  item.description = temp;
2205  }
2206  if (item.GetCategory().find( "My_Fleet" ) != string::npos) {
2207  //This ship is in my fleet -- the price is just the transport cost to get it to
2208  //the current base. "Buying" this ship makes it my current ship.
2209  sprintf( tempString, "#b#Transport cost: %.2f#-b#n1.5#", item.price );
2210  } else {
2211  sprintf( tempString, "Price: #b#%.2f#-b#n#", baseUnit->PriceCargo( item.content ) );
2212  descString += tempString;
2213  sprintf( tempString, "Cargo volume: %.2f cubic meters; Mass: %.2f metric tons#n1.5#", item.volume, item.mass );
2214  }
2215  descString += tempString;
2216  tailString = buildCargoDescription( item, *this, item.price );
2217  break;
2218  case BUY_UPGRADE:
2219  if (item.content == BASIC_REPAIR_NAME) {
2220  //Basic repair is implemented entirely in this module.
2221  //PriceCargo() doesn't know about it.
2222  Unit *playerUnit = m_player.GetUnit();
2223  int multiplier = 1;
2224  if (playerUnit)
2225  multiplier = playerUnit->RepairCost();
2226  sprintf( tempString, "Price: #b#%.2f#-b#n1.5#", basicRepairPrice()*multiplier );
2227  } else {
2228  sprintf( tempString, "Price: #b#%.2f#-b#n1.5#", baseUnit->PriceCargo( item.content ) );
2229  }
2230  descString += tempString;
2231  if (item.GetDescription() == "" || item.GetDescription()[0] != '#')
2232  item.description = buildUpgradeDescription( item );
2233  break;
2234  case BUY_SHIP:
2235  if (item.GetCategory().find( "My_Fleet" ) == string::npos) {
2237  if (item.price < _Universe->AccessCockpit()->credits) {
2238  std::string tmp = item.GetContent().substr( 0, item.GetContent().find( "." ) );
2239  UniverseUtil::playSound( "sales/salespitch"+tmp+".wav", QVector( 0, 0, 0 ), Vector( 0, 0, 0 ) );
2240  } else {
2241  UniverseUtil::playSound( "sales/salespitchnotenoughmoney.wav", QVector( 0, 0, 0 ), Vector( 0, 0, 0 ) );
2242  }
2243  }
2244  if (item.description == "" || item.GetDescription()[0] != '@')
2245  item.description = buildShipDescription( item, descriptiontexture );
2246  if (item.GetCategory().find( "My_Fleet" ) != string::npos) {
2247  //This ship is in my fleet -- the price is just the transport cost to get it to
2248  //the current base. "Buying" this ship makes it my current ship.
2249  sprintf( tempString, "#b#Transport cost: %.2f#-b#n1.5#", item.price );
2250  } else {
2251  PRETTY_ADDN("", baseUnit->PriceCargo(item.content), 2);
2252  sprintf( tempString, "Price: #b#%s#-b#n#", text.c_str() );
2253  static bool printvolume =
2254  XMLSupport::parse_bool( vs_config->getVariable( "graphics", "base_print_cargo_volume", "true" ) );
2255  if (printvolume) {
2256  descString += tempString;
2257  sprintf( tempString, "Vessel volume: %.2f cubic meters; Mass: %.2f metric tons#n1.5#", item.volume, item.mass );
2258  }
2259  }
2260  descString += tempString;
2261  break;
2262  case SELL_CARGO:
2263  if (item.GetDescription() == "" || item.GetDescription()[0] != '@') {
2264  buildShipDescription( item, descriptiontexture ); //Check for ship
2265  string temp = item.description; //do first, so can override default image, if so desired
2266  if ( ( string::npos != temp.find( '@' ) ) && ("" != descriptiontexture) ) //not already pic-annotated, has non-null ship pic
2267  temp = "@"+descriptiontexture+"@"+item.description;
2268  item.description = temp;
2269  }
2270  if (item.mission)
2271  sprintf( tempString, "Destroy evidence of mission cargo. Credit received: 0.00." );
2272  else
2273  sprintf( tempString, "Value: #b#%.2f#-b, purchased for %.2f#n#",
2274  baseUnit->PriceCargo( item.content ), item.price );
2275  descString += tempString;
2276  sprintf( tempString, "Cargo volume: %.2f cubic meters; Mass: %.2f metric tons#n1.5#", item.volume, item.mass );
2277  descString += tempString;
2278  if (!item.mission)
2279  tailString = buildCargoDescription( item, *this, baseUnit->PriceCargo( item.content ) );
2280  break;
2281  case SELL_UPGRADE:
2282 
2283  //********************************************************************************************
2284  {
2285  double percent_working = m_player.GetUnit() ? UnitUtil::PercentOperational(
2286  m_player.GetUnit(), item.content, item.category, false ) : 0.0;
2287  if (percent_working < 1) {
2288  //IF DAMAGED
2289  sprintf( tempString, "Damaged and Used value: #b#%.2f#-b, purchased for %.2f#n1.5#",
2290  SellPrice( percent_working, baseUnit->PriceCargo( item.content ) ), item.price );
2291  descString += tempString;
2292 
2293  sprintf( tempString, "Percent Working: #b#%.2f#-b, Repair Cost: %.2f#n1.5#",
2294  percent_working*100, RepairPrice( percent_working, baseUnit->PriceCargo( item.content ) ) );
2295  descString += tempString;
2296  } else {
2297  sprintf( tempString, "Used value: #b#%.2f#-b, purchased for %.2f#n1.5#",
2298  usedValue( baseUnit->PriceCargo( item.content ) ), item.price );
2299  descString += tempString;
2300  }
2301  if (damaged_mode)
2302  descString +=
2303  "#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#";
2304  //********************************************************************************************
2305  if (item.GetDescription() == "" || item.GetDescription()[0] != '#')
2306  item.description = buildUpgradeDescription( item );
2307  break;
2308  }
2309  default:
2310  assert( false ); //Missed transaction enum in switch statement.
2311  break;
2312  }
2313  }
2314  //Description.
2315  descString += item.description;
2316  descString += tailString;
2317 
2318  //Change the description control.
2319  string::size_type pic;
2320  StaticImageDisplay *descimage = static_cast< StaticImageDisplay* > ( window()->findControlById( "DescriptionImage" ) );
2321  if ( ( pic = descString.find( "@" ) ) != string::npos ) {
2322  std::string texture = descString.substr( pic+1 );
2323  descString = descString.substr( 0, pic );
2324  string::size_type picend = texture.find( "@" );
2325  if (picend != string::npos) {
2326  descString += texture.substr( picend+1 );
2327  texture = texture.substr( 0, picend );
2328  }
2329  if (descimage)
2330  descimage->setTexture( texture );
2331  } else {
2332  if (descimage && descriptiontexture == "")
2333  descimage->setTexture( "blackclear.png" );
2334  else if (descimage)
2335  descimage->setTexture( descriptiontexture );
2336  }
2337  {
2338  pic = descString.find( "<" );
2339  if (pic != string::npos) {
2340  std::string tmp = descString.substr( pic+1 );
2341  descString = descString.substr( 0, pic );
2342  if ( ( pic = tmp.find( ">" ) ) != string::npos )
2343  descString += tmp.substr( pic+1 );
2344  }
2345  }
2346  desc->setText( descString );
2347 }
2348 
2349 //Something in a Picker was selected.
2351 {
2352  assert( control != NULL );
2353  Picker *picker = static_cast< Picker* > (control);
2354  PickerCell *cell = picker->selectedCell();
2355 
2356  //Figure out which transaction list we are using.
2357  assert( picker == m_transList1.picker || picker == m_transList2.picker );
2358  TransactionList *tlist = ( (picker == m_transList1.picker) ? &m_transList1 : &m_transList2 );
2359  if ( m_base.GetUnit() ) {
2360  if (!cell) {
2361  //The selection just got cleared.
2362  TransactionList &otherList = ( (&m_transList1 == tlist) ? m_transList2 : m_transList1 );
2363  if ( otherList.picker && otherList.picker->selectedCell() ) {
2364  //Special case. The other picker has a selection -- we are seeing the selection
2365  //cleared in this picker as result. Do nothing.
2366  } else {
2368  }
2369  } else if (cell->tag() == CATEGORY_TAG) {
2370  //They just selected a category. Clear the selection no matter what.
2372  } else {
2373  //Make the controls right for this item.
2375  }
2376  }
2377  return true;
2378 }
2379 
2380 bool UpgradeAllowed( const Cargo &item, Unit *playerUnit )
2381 {
2382  std::string prohibited_upgrades =
2384  playerUnit->faction ), "Prohibited_Upgrades" );
2385  while ( prohibited_upgrades.length() ) {
2386  std::string::size_type where = prohibited_upgrades.find( " " );
2387  if (where == string::npos) where = prohibited_upgrades.find( ";" );
2388  std::string prohibited_upgrade = prohibited_upgrades;
2389  if (where != string::npos) {
2390  prohibited_upgrade = prohibited_upgrades.substr( 0, where );
2391  prohibited_upgrades = prohibited_upgrades.substr( where+1 );
2392  } else {prohibited_upgrades = ""; } where = prohibited_upgrade.find( ":" );
2393  std::string content = prohibited_upgrade.substr( 0, where );
2394  int quantity = 0;
2395  if (where != string::npos) {
2396  std::string tmp = prohibited_upgrade.substr( where+1 );
2397  quantity = atoi( tmp.c_str() );
2398  }
2399  if ( item.content == content || ( 0 == string( item.category ).find( content ) ) ) {
2400  if (quantity == 0) {
2402  return false;
2403  }
2404  unsigned int i = 0;
2405  Cargo *numUpgrades = playerUnit->GetCargo( item.content, i );
2406  if (numUpgrades) {
2407  if (numUpgrades->quantity >= quantity) {
2409  return false;
2410  }
2411  }
2412  unsigned int limit = playerUnit->numCargo();
2413  int totalquant = 0;
2414  for (i = 0; i < limit; ++i) {
2415  numUpgrades = &( playerUnit->GetCargo( i ) );
2416  if ( numUpgrades && ( 0 == string( numUpgrades->category ).find( content ) ) )
2417  totalquant += numUpgrades->quantity;
2418  }
2419  if (totalquant >= quantity) {
2421  return false;
2422  }
2423  }
2424  }
2425  return true;
2426 }
2427 
2428 //Return whether or not the current item and quantity can be "transacted".
2429 bool BaseComputer::isTransactionOK( const Cargo &originalItem, TransactionType transType, int quantity )
2430 {
2431  if (originalItem.mission && transType != SELL_CARGO) {
2433  return false;
2434  }
2435  //Make sure we have somewhere to put stuff.
2436  Unit *playerUnit = m_player.GetUnit();
2437  if (!playerUnit) return false;
2438  Cockpit *cockpit = _Universe->isPlayerStarship( playerUnit );
2439  if (!cockpit) return false;
2440  //Need to fix item so there is only one for cost calculations.
2441  Cargo item = originalItem;
2442  item.quantity = quantity;
2443  Unit *baseUnit = m_base.GetUnit();
2444  bool havemoney = true;
2445  bool havespace = true;
2446  switch (transType)
2447  {
2448  case BUY_CARGO:
2449  //Enough credits and room for the item in the ship.
2450  havemoney = item.price*quantity <= cockpit->credits;
2451  havespace = playerUnit->CanAddCargo( item );
2452  if (havemoney && havespace) {
2453  return true;
2454  } else {
2455  if (!havemoney)
2457  if (!havespace)
2459  }
2460  break;
2461  case SELL_CARGO:
2462  //There is a base here, and it is willing to buy the item.
2463  if (!originalItem.mission) {
2464  if (baseUnit) {
2465  havespace = baseUnit->CanAddCargo( item );
2466  if (havespace)
2467  return true;
2468  else
2470  }
2471  } else {
2472  return true;
2473  }
2474  break;
2475  case BUY_SHIP:
2476  //Either you are buying this ship for your fleet, or you already own the
2477  //ship and it will be transported to you.
2478  if (baseUnit) {
2479  if (item.price*quantity <= cockpit->credits)
2480  return true;
2481  else
2483  }
2484  break;
2485  case ACCEPT_MISSION:
2486  //Make sure the player doesn't take too many missions.
2487  if ( item.GetCategory().find( "Active_Missions" ) != string::npos || active_missions.size() < UniverseUtil::maxMissions() )
2488  return true;
2489  break;
2490  case SELL_UPGRADE:
2491  if (baseUnit) {
2492  havespace = baseUnit->CanAddCargo( item );
2493  if (havespace)
2494  return true;
2495  else
2497  }
2498  case BUY_UPGRADE:
2499  //cargo.mission == true means you can't do the transaction.
2500  havemoney = item.price*quantity <= cockpit->credits;
2501  havespace = ( playerUnit->CanAddCargo( item ) || upgradeNotAddedToCargo( item.category ) );
2502  //UpgradeAllowed must be first -- short circuit && operator
2503  if (UpgradeAllowed( item, playerUnit ) && havemoney && havespace && !item.mission) {
2504  return true;
2505  } else {
2506  if (!havemoney)
2508  if (!havespace)
2510  }
2511  break;
2512  default:
2513  assert( false ); //Missed an enum in transaction switch statement.
2514  break;
2515  }
2516  return false;
2517 }
2518 
2519 //Create whatever cells are needed to add a category to the picker.
2521  const string &origCategory,
2522  bool skipFirstCategory )
2523 {
2524  string category = origCategory;
2525  if (skipFirstCategory) {
2526  //Skip over the first category piece.
2527  const string::size_type sepLoc = category.find( CATEGORY_SEP );
2528  if (sepLoc == string::npos)
2529  //No category separator. At most one category.
2530  category.erase( 0, category.size() );
2531  else
2532  //Skip the first piece.
2533  category = origCategory.substr( sepLoc+1 );
2534  }
2535  if (category.size() == 0)
2536  //No category at all.
2537  return NULL;
2538  //Create or reuse a cell for the first part of the category.
2539  const string::size_type loc = category.find( CATEGORY_SEP );
2540  const string currentCategory = category.substr( 0, loc );
2541  if (cells.count() > 0 && cells.cellAt( cells.count()-1 )->id() == currentCategory) {
2542  //Re-use the category we have.
2543  } else {
2544  //Need to make a new cell for this.
2545  cells.addCell( new SimplePickerCell( beautify( currentCategory ), currentCategory, CATEGORY_TEXT_COLOR(), CATEGORY_TAG ) );
2546  }
2547  SimplePickerCell *parentCell = static_cast< SimplePickerCell* > ( cells.cellAt( cells.count()-1 ) ); //Last cell in list.
2548  static bool showDefault = XMLSupport::parse_bool( vs_config->getVariable( "graphics", "open_picker_categories", "false" ) );
2549  parentCell->setHideChildren( !showDefault );
2550  if (loc == string::npos) {
2551  //This is a simple category -- we are done.
2552  return parentCell;
2553  } else {
2554  //The category string has more stuff in it -- finish it up.
2555  SimplePickerCells *childCellList = static_cast< SimplePickerCells* > ( parentCell->children() );
2556  if (!childCellList) {
2557  //If parent doesn't have room children, we create the room manually.
2558  parentCell->createEmptyChildList();
2559  childCellList = static_cast< SimplePickerCells* > ( parentCell->children() );
2560  }
2561  const string newCategory = category.substr( loc+1 );
2562  return createCategoryCell( *childCellList, newCategory, false );
2563  }
2564  assert( false );
2565  //Never get here.
2566 }
2567 
2568 //Load a picker with a list of items.
2570  SimplePicker &picker,
2571  TransactionType transType,
2572  bool skipFirstCategory )
2573 {
2574  //Make sure the transactionList has the correct info.
2575  tlist.picker = &picker;
2576  tlist.transaction = transType;
2577 
2578  //Make sure there is nothing old lying around in the picker.
2579  picker.clear();
2580 
2581  //Iterate through the list and load the picker from it.
2582  string currentCategory = "--ILLEGAL CATEGORY--"; //Current category we are adding cells to.
2583  SimplePickerCell *parentCell = NULL; //Place to add new items. NULL = Add to picker.
2584  for (size_t i = 0; i < tlist.masterList.size(); i++) {
2585  Cargo &item = tlist.masterList[i].cargo;
2586  std::string icategory = getDisplayCategory( item );
2587  if (icategory != currentCategory) {
2588  //Create new cell(s) for the new category.
2589  parentCell = createCategoryCell(
2590  *static_cast< SimplePickerCells* > ( picker.cells() ), icategory, skipFirstCategory );
2591  currentCategory = icategory;
2592  }
2593  //Construct the cell for this item.
2594  //JS_NUDGE -- this is where I'll need to do the upgrades colorations goop hooks
2595  const bool transOK = isTransactionOK( item, transType );
2596 
2597  string itemName = beautify( UniverseUtil::LookupUnitStat( item.content, "upgrades", "Name" ) );
2598  string originalName = itemName;
2599  if (itemName == "")
2600  itemName = beautify( item.content );
2601  if (item.quantity > 1)
2602  //If there is more than one item, show the number of items.
2603  itemName += " ("+tostring( item.quantity )+")";
2604 //*******************************************************************************
2605 
2606  //Clear color means use the text color in the picker.
2607  GFXColor bad_trans_color = NO_MONEY_COLOR();
2609  bad_trans_color = DOWNGRADE_OR_NONCOMPAT_COLOR();
2611  bad_trans_color = PROHIBITED_COLOR();
2613  bad_trans_color = NO_ROOM_COLOR();
2615  //Just in case we want to change the default reason for non-purchase
2616  bad_trans_color = NO_MONEY_COLOR();
2617  GFXColor base_color = (transOK ? (item.mission ? MISSION_COLOR() : GUI_CLEAR) : bad_trans_color);
2618  //Reset cause-color flags
2623  GFXColor final_color;
2624  if ( transType == SELL_UPGRADE && m_player.GetUnit() ) {
2625  //Adjust the base color if the item is 'damaged'
2626  double percent_working = UnitUtil::PercentOperational( m_player.GetUnit(), item.content, item.category, false );
2627 
2628  final_color = GFXColor(
2629  (1.0*percent_working)+( 1.0*(1.0-percent_working) ),
2630  (1.0*percent_working)+( 0.0*(1.0-percent_working) ),
2631  (0.0*percent_working)+( 0.0*(1.0-percent_working) ),
2632  (1.0*percent_working)+( 1.0*(1.0-percent_working) )
2633  );
2634  if (percent_working == 1.0) final_color = base_color; //working = normal color
2635  if (percent_working == 0.0) final_color = ITEM_DESTROYED_COLOR(); //dead = grey
2636  } else {
2637  final_color = base_color;
2638  }
2639  SimplePickerCell *cell = new SimplePickerCell( itemName, item.content, final_color, i );
2640 
2641  //*******************************************************************************
2642  //Add the cell.
2643  if (parentCell)
2644  parentCell->addChild( cell );
2645  else
2646  picker.addCell( cell );
2647  }
2648 }
2649 
2650 //Load the controls for the CARGO display.
2651 
2652 extern int SelectDockPort( Unit *utdw, Unit *parent );
2653 
2655 {
2656  //Make sure there's nothing in the transaction lists.
2658  static bool requireportforlaunch =
2659  XMLSupport::parse_bool( vs_config->getVariable( "physics", "cargo_wingmen_only_with_dockport", "false" ) );
2660  static bool portallowsupgrades =
2661  XMLSupport::parse_bool( vs_config->getVariable( "physics", "dockport_allows_ugrade_storage", "false" ) );
2662  //Set up the base dealer's transaction list. Note that you need a docking port to buy starships!
2663  vector< string >donttakethis;
2664  donttakethis.push_back( "missions" );
2665 //if no docking port OR no permission, no upgrades
2666  if ( (SelectDockPort( m_player.GetUnit(), m_player.GetUnit() ) < 0) || (!portallowsupgrades) )
2667  donttakethis.push_back( "upgrades" );
2668 //if no docking port AND no permission, no ships (different from above for VS compatibility)
2669  if ( (SelectDockPort( m_player.GetUnit(), m_player.GetUnit() ) < 0) && (requireportforlaunch) )
2670  donttakethis.push_back( "starships" );
2671  static bool starship_purchase = XMLSupport::parse_bool( vs_config->getVariable( "physics", "starships_as_cargo", "true" ) );
2672  if (!starship_purchase) {
2673  donttakethis.push_back( "starships" );
2674  donttakethis.push_back( "starship" );
2675  }
2676  loadMasterList( m_base.GetUnit(), vector< string > (), donttakethis, true, m_transList1 ); //Anything but a mission.
2677  SimplePicker *basePicker = static_cast< SimplePicker* > ( window()->findControlById( "BaseCargo" ) );
2678  assert( basePicker != NULL );
2679  loadListPicker( m_transList1, *basePicker, BUY_CARGO );
2680 
2681  //Set up the player's transaction list.
2682  loadMasterList( m_player.GetUnit(), vector< string > (), donttakethis, true, m_transList2 ); //Anything but a mission.
2683  SimplePicker *inventoryPicker = static_cast< SimplePicker* > ( window()->findControlById( "PlayerCargo" ) );
2684  assert( inventoryPicker != NULL );
2685  loadListPicker( m_transList2, *inventoryPicker, SELL_CARGO );
2686 
2687  //Make the title right.
2688  recalcTitle();
2689 }
2690 
2691 //Need this class to sort CargoColor's.
2693 {
2694 public:
2695  bool operator()( const CargoColor &a, const CargoColor &b )
2696  {
2697  std::string acategory( a.cargo.category );
2698  std::string bcategory( b.cargo.category );
2699  std::string::size_type aless = a.cargo.GetDescription().find( "<" );
2700  std::string::size_type agreater = a.cargo.GetDescription().find( ">" );
2701  std::string::size_type bless = b.cargo.GetDescription().find( "<" );
2702  std::string::size_type bgreater = b.cargo.GetDescription().find( ">" );
2703  if (aless != string::npos && agreater != string::npos)
2704  acategory = a.cargo.GetDescription().substr( aless+1, agreater );
2705  if (bless != string::npos && bgreater != string::npos)
2706  bcategory = b.cargo.GetDescription().substr( bless+1, bgreater );
2707  if (acategory == bcategory)
2708  return a.cargo < b.cargo;
2709  return acategory < bcategory;
2710  }
2711 };
2712 
2713 //Get a filtered list of items from a unit.
2715  const vector< string > &filtervec,
2716  const vector< string > &invfiltervec,
2717  bool removezero,
2718  TransactionList &tlist )
2719 {
2720  vector< CargoColor > *items = &tlist.masterList;
2721  for (size_t i = 0; i < un->numCargo(); i++) {
2722  bool filter = filtervec.empty();
2723  bool invfilter = true;
2724  size_t vecindex;
2725  for (vecindex = 0; !filter && ( vecindex < filtervec.size() ); vecindex++)
2726  if (un->GetCargo( i ).GetCategory().find( filtervec[vecindex] ) != string::npos)
2727  filter = true;
2728  for (vecindex = 0; invfilter && ( vecindex < invfiltervec.size() ); vecindex++)
2729  if (un->GetCargo( i ).GetCategory().find( invfiltervec[vecindex] ) != string::npos)
2730  invfilter = false;
2731  if (filter && invfilter) {
2732  if ( (!removezero) || un->GetCargo( i ).quantity > 0 ) {
2733  CargoColor col;
2734  col.cargo = un->GetCargo( i );
2735  if (col.cargo.category == "")
2736  col.cargo.category = "#c.5:1:.3#Uncategorized Cargo";
2737  items->push_back( col );
2738  }
2739  }
2740  }
2741  std::sort( items->begin(), items->end(), CargoColorSort() );
2742 }
2743 
2744 //Return a pointer to the selected item in the picker with the selection.
2746 {
2747  Cargo *result = NULL;
2748  if (m_selectedList) {
2749  assert( m_selectedList->picker );
2751  if (cell)
2752  result = &m_selectedList->masterList[cell->tag()].cargo;
2753  }
2754  return result;
2755 }
2756 
2757 //Update the transaction controls after a transaction.
2758 void BaseComputer::updateTransactionControls( const Cargo &item, bool skipFirstCategory )
2759 {
2760  //Go reselect the item.
2761  if (m_selectedList == NULL)
2762  return;
2763  const bool success = scrollToItem( m_selectedList->picker, item, true, skipFirstCategory );
2764  //And scroll to that item in the other picker, too.
2766  if (otherList.picker)
2767  //Scroll to the item in the other list, but don't select it.
2768  scrollToItem( otherList.picker, item, false, skipFirstCategory );
2769  if (success)
2770  //We selected the item successfully.
2772  else
2773  //Didn't find item. Clear the selection.
2775 }
2776 
2777 //The max number of a particular item this player can buy. Limits by price, cargo space, etc.
2778 int BaseComputer::maxQuantityForPlayer( const Cargo &item, int suggestedQuantity )
2779 {
2780  int result = 0;
2781 
2782  Unit *playerUnit = m_player.GetUnit();
2783  if (playerUnit) {
2784  //Limit by cargo capacity.
2785  const float volumeLeft = playerUnit->getEmptyCargoVolume()-playerUnit->getCargoVolume();
2786  result = (int) guiMin( suggestedQuantity, volumeLeft/item.volume );
2787 
2788  //Limit by price.
2789  const double credits = _Universe->AccessCockpit()->credits;
2790  result = (int) guiMin( result, credits/item.price );
2791  }
2792  return result;
2793 }
2794 
2795 static void eliminateZeroCargo( Unit *un )
2796 {
2797  for (int i = un->numCargo()-1; i >= 0; --i)
2798  if (un->GetCargo( i ).quantity == 0)
2799  un->RemoveCargo( i, 1, true );
2800 }
2801 
2803 {
2804  if ( m_player.GetUnit() ) {
2806  //Reload the UI -- inventory has changed. Because we reload the UI, we need to
2807  //find, select, and scroll to the thing we bought. The item might be gone from the
2808  //list (along with some categories) after the transaction.
2809  std::list< std::list< std::string > >list1save, list2save;
2810  if (m_transList1.picker)
2811  m_transList1.picker->saveOpenCategories( list1save );
2812  if (m_transList2.picker)
2813  m_transList2.picker->saveOpenCategories( list2save );
2814  processWindowCommand( modeInfo[m_currentDisplay].command, NULL );
2815  if (m_transList1.picker)
2817  if (m_transList2.picker)
2819  recalcTitle();
2820  }
2821 }
2822 
2824 {
2825  if ( ( !m_player.GetUnit() ) || m_player.GetUnit()->hull <= 0 ) {
2828  }
2829  if ( BaseComputer::dirty && m_player.GetUnit() ) {
2831  refresh();
2832  }
2833 }
2834 
2835 //Buy some items from the Cargo list. Use -1 for quantity to buy all of the item.
2836 bool BaseComputer::buySelectedCargo( int requestedQuantity )
2837 {
2838  Unit *playerUnit = m_player.GetUnit();
2839  Unit *baseUnit = m_base.GetUnit();
2840  if ( !(playerUnit && baseUnit) )
2841  return true;
2842  Cargo *item = selectedItem();
2843  if (item) {
2844  int quantity = (requestedQuantity <= 0 ? item->quantity : requestedQuantity);
2845  quantity = maxQuantityForPlayer( *item, quantity );
2846  playerUnit->BuyCargo( item->content, quantity, baseUnit, _Universe->AccessCockpit()->credits );
2847  eliminateZeroCargo( playerUnit );
2848  //Reload the UI -- inventory has changed. Because we reload the UI, we need to
2849  //find, select, and scroll to the thing we bought. The item might be gone from the
2850  //list (along with some categories) after the transaction.
2851  refresh(); //This will reload master lists.
2852  }
2853  return true;
2854 }
2855 
2856 //Buy an item from the cargo list.
2857 bool BaseComputer::buyCargo( const EventCommandId &command, Control *control )
2858 {
2859  return buySelectedCargo( 1 );
2860 }
2861 
2862 //Buy an item (quantity 10) from the cargo list.
2863 bool BaseComputer::buy10Cargo( const EventCommandId &command, Control *control )
2864 {
2865  return buySelectedCargo( 10 );
2866 }
2867 
2868 //Buy all of an item from the cargo list.
2869 bool BaseComputer::buyAllCargo( const EventCommandId &command, Control *control )
2870 {
2871  return buySelectedCargo( -1 );
2872 }
2873 
2874 //Sell some items from the Cargo list. Use -1 for quantity to buy all of the item.
2875 bool BaseComputer::sellSelectedCargo( int requestedQuantity )
2876 {
2877  Unit *playerUnit = m_player.GetUnit();
2878  Unit *baseUnit = m_base.GetUnit();
2879  if ( !(playerUnit && baseUnit) )
2880  return true;
2881  Cargo *item = selectedItem();
2882  if (item) {
2883  Cargo itemCopy = *item; //Not sure what "sold" has in it. Need copy of sold item.
2884  Cargo sold;
2885  const int quantity = (requestedQuantity <= 0 ? item->quantity : requestedQuantity);
2886  if (item->mission) {
2887  vector< Cargo >::iterator mycargo = std::find( playerUnit->pImage->cargo.begin(),
2888  playerUnit->pImage->cargo.end(), *item );
2889  if ( mycargo != playerUnit->pImage->cargo.end() )
2890  playerUnit->RemoveCargo( mycargo-playerUnit->pImage->cargo.begin(), quantity, true );
2891  } else {
2892  playerUnit->SellCargo( item->content, quantity, _Universe->AccessCockpit()->credits, sold, baseUnit );
2893  }
2894  eliminateZeroCargo( playerUnit );
2895  //Reload the UI -- inventory has changed. Because we reload the UI, we need to
2896  //find, select, and scroll to the thing we bought. The item might be gone from the
2897  //list (along with some categories) after the transaction.
2898  refresh(); //This will reload master lists.
2899  }
2900  return true;
2901 }
2902 
2903 //Sell an item from ship's cargo.
2904 bool BaseComputer::sellCargo( const EventCommandId &command, Control *control )
2905 {
2906  return sellSelectedCargo( 1 );
2907 }
2908 
2909 //Sell an item (quantity 10) from the cargo list.
2910 bool BaseComputer::sell10Cargo( const EventCommandId &command, Control *control )
2911 {
2912  return sellSelectedCargo( 10 );
2913 }
2914 
2915 //Sell all of an item from the cargo list.
2916 bool BaseComputer::sellAllCargo( const EventCommandId &command, Control *control )
2917 {
2918  return sellSelectedCargo( -1 );
2919 }
2920 
2921 //Change controls to NEWS mode.
2923 {
2924  if (m_currentDisplay != NEWS)
2926  loadNewsControls();
2927  return true;
2928 }
2929 
2930 //The selection in the News picker changed.
2932 {
2933  assert( control != NULL );
2934  Picker *picker = static_cast< Picker* > (control);
2935  PickerCell *cell = picker->selectedCell();
2936 
2937  StaticDisplay *desc = static_cast< StaticDisplay* > ( window()->findControlById( "Description" ) );
2938  assert( desc != NULL );
2939  if (cell == NULL)
2940  //No selection. Clear desc. (Not sure how this can happen, but it's easy to cover.)
2941  desc->setText( "" );
2942  else
2943  desc->setText( cell->text() );
2944  //Turn on some cool music.
2945  static string newssong = vs_config->getVariable( "audio", "newssong", "../music/news1.ogg" );
2946  muzak->GotoSong( newssong );
2947  m_playingMuzak = true;
2948  return true;
2949 }
2950 
2951 static std::string GarnerInfoFromSaveGame( const string &filename )
2952 {
2953  return UniverseUtil::getSaveInfo( filename, true );
2954 }
2955 
2956 //The selection in the News picker changed.
2958 {
2959  assert( control != NULL );
2960  Picker *picker = static_cast< Picker* > (control);
2961  PickerCell *cell = picker->selectedCell();
2962 
2963  StaticDisplay *desc = static_cast< StaticDisplay* > ( window()->findControlById( "Description" ) );
2964  StaticDisplay *inputbox = static_cast< StaticDisplay* > ( window()->findControlById( "InputText" ) );
2965  assert( desc != NULL );
2966  if (cell == NULL) {
2967  //No selection. Clear desc. (Not sure how this can happen, but it's easy to cover.)
2968  desc->setText( "" );
2969  } else {
2970  desc->setText( GarnerInfoFromSaveGame( cell->text() ) );
2971  if (inputbox != NULL)
2972  inputbox->setText( cell->text() );
2973  }
2974  return true;
2975 }
2976 
2977 //Load the controls for the News display.
2979 {
2980  SimplePicker *picker = static_cast< SimplePicker* > ( window()->findControlById( "NewsPicker" ) );
2981  assert( picker != NULL );
2982  picker->clear();
2983 
2984  //Load the picker.
2985  static const bool newsFromCargolist =
2986  XMLSupport::parse_bool( vs_config->getVariable( "cargo", "news_from_cargolist", "false" ) );
2987  if (newsFromCargolist && Network == NULL) {
2988  gameMessage last;
2989  int i = 0;
2990  vector< std::string >who;
2991  who.push_back( "news" );
2992  while ( ( mission->msgcenter->last( i++, last, who ) ) )
2993  picker->addCell( new SimplePickerCell( last.message ) );
2994  } else {
2995  //Get news from save game.
2996  Unit *playerUnit = m_player.GetUnit();
2997  if (playerUnit) {
2998  const int playerNum = UnitUtil::isPlayerStarship( playerUnit );
2999  int len = getSaveStringLength( playerNum, NEWS_NAME_LABEL );
3000  for (int i = len-1; i >= 0; i--)
3001  {
3002  picker->addCell( new SimplePickerCell( getSaveString( playerNum, NEWS_NAME_LABEL, i ) ) );
3003  }
3004  }
3005  }
3006  //Make sure the description is empty.
3007  StaticDisplay *desc = static_cast< StaticDisplay* > ( window()->findControlById( "Description" ) );
3008  assert( desc != NULL );
3009  desc->setText( "" );
3010 
3011  //Make the title right.
3012  recalcTitle();
3013 }
3014 
3015 #if defined (__APPLE__)
3016 static int nodirs( struct dirent *entry )
3017 #else
3018 static int nodirs( const struct dirent * entry )
3019 #endif
3020 {
3021 #if defined (_WIN32) || defined(__HAIKU__)
3022  //Have to check if we have the full path or just relative (which would be a problem)
3023  std::string tmp = VSFileSystem::homedir+"/save/"+entry->d_name;
3024  struct stat s;
3025  if (stat( tmp.c_str(), &s ) < 0)
3026  return string( entry->d_name ) != "." && string( entry->d_name ) != "..";
3027  if ( (s.st_mode&S_IFDIR) == 0 && string( entry->d_name ) != "." && string( entry->d_name ) != ".." )
3028  return 1;
3029 #else
3030  if (entry->d_type != DT_DIR && string( entry->d_name ) != "." && string( entry->d_name ) != "..")
3031  return 1;
3032 #endif
3033  return 0;
3034 }
3035 
3036 static int datesort( const void *v1, const void *v2 )
3037 {
3038  const struct dirent *d1 = *(const struct dirent**) v1;
3039  const struct dirent *d2 = *(const struct dirent**) v2;
3040  struct stat s1, s2;
3041  std::string tmp = VSFileSystem::homedir+"/save/"+d1->d_name;
3042  if ( stat( tmp.c_str(), &s1 ) )
3043  return 0;
3044  tmp = VSFileSystem::homedir+"/save/"+d2->d_name;
3045  if ( stat( tmp.c_str(), &s2 ) )
3046  return 0;
3047  return s1.st_mtime-s2.st_mtime;
3048 }
3049 
3050 #if (defined (__FREEBSD__)) || (defined (_WIN32) && !defined (__CYGWIN__ ) ) || (defined (__GLIBC_MINOR__) && __GLIBC_MINOR__ >= 10) || defined(__HAIKU__)
3051 typedef int (*scancompare)( const struct dirent **v1, const struct dirent **v2 );
3052 #else
3053 typedef int (*scancompare)( const void *v1, const void *v2 );
3054 #endif
3055 
3056 //Load the controls for the News display.
3058 {
3059  SimplePicker *picker = static_cast< SimplePicker* > ( window()->findControlById( "LoadSavePicker" ) );
3060  assert( picker != NULL );
3061  picker->clear();
3062 
3063  //Get news from save game.
3064  Unit *playerUnit = m_player.GetUnit();
3065  if (playerUnit) {
3066  struct dirent **dirlist;
3067  std::string savedir = VSFileSystem::homedir+"/save/";
3068  int ret = scandir( savedir.c_str(), &dirlist, nodirs, (scancompare)&datesort );
3069  while (ret-- > 0)
3070  picker->addCell( new SimplePickerCell( dirlist[ret]->d_name ) );
3071  }
3072  //Make sure the description is empty.
3073  StaticDisplay *desc = static_cast< StaticDisplay* > ( window()->findControlById( "Description" ) );
3074  assert( desc != NULL );
3075  desc->setText( "" );
3076 
3077  //Make the title right.
3078  recalcTitle();
3079 }
3080 
3081 //Change display mode to MISSIONS.
3083 {
3084  if (m_currentDisplay != MISSIONS)
3088  return true;
3089 }
3090 
3091 //Load a master list with missions.
3093 {
3094  //Make sure the list is clear.
3095  tlist.masterList.clear();
3096 
3097  Unit *unit = _Universe->AccessCockpit()->GetParent();
3098  int playerNum = UnitUtil::isPlayerStarship( unit );
3099  if (playerNum < 0) {
3100  VSFileSystem::vs_fprintf( stderr, "Docked ship not a player." );
3101  return;
3102  }
3103  //Number of strings to look at. And make sure they match!
3104  const size_t stringCount = getSaveStringLength( playerNum, MISSION_NAMES_LABEL );
3105  if (Network == NULL)
3106  //these aren't sent over the network.
3107  assert( stringCount == getSaveStringLength( playerNum, MISSION_SCRIPTS_LABEL ) );
3108  assert( stringCount == getSaveStringLength( playerNum, MISSION_DESC_LABEL ) );
3109  //Make sure we have different names for all the missions.
3110  //This changes the savegame -- it removes ambiguity for good.
3111  for (size_t current = 0; current < stringCount; current++) {
3112  const string currentName = getSaveString( playerNum, MISSION_NAMES_LABEL, current );
3113  size_t count = 1;
3114  //Check whether any after the current one have the same name.
3115  for (unsigned int check = current+1; check < stringCount; ++check) {
3116  const string checkName = getSaveString( playerNum, MISSION_NAMES_LABEL, check );
3117  if (check == current)
3118  {
3119  //Found identical names. Add a "count" at the end.
3120  putSaveString( playerNum, MISSION_NAMES_LABEL, current, checkName+"_"+tostring( static_cast<int>(count) ) );
3121  ++count;
3122  }
3123  }
3124  }
3125  //Create an entry for for each mission.
3126  for (size_t i = 0; i < stringCount; i++) {
3127  CargoColor c;
3128  //Take any categories out of the name and put them in the cargo.category.
3129  if (Network == NULL) {
3130  const string finalScript = getSaveString( playerNum, MISSION_SCRIPTS_LABEL, i );
3131  if (finalScript[0] == '#')
3132  continue; //Ignore any missions with comments. (those are fixer missions.)
3133  }
3134  const string originalName = getSaveString( playerNum, MISSION_NAMES_LABEL, i );
3135  const string::size_type lastCategorySep = originalName.rfind( CATEGORY_SEP );
3136  if (lastCategorySep != string::npos) {
3137  //We have a category.
3138  c.cargo.content = originalName.substr( lastCategorySep+1 );
3139  c.cargo.category = originalName.substr( 0, lastCategorySep );
3140  } else {
3141  //No category in name.
3142  c.cargo.content = originalName;
3143  c.cargo.category = "";
3144  }
3145  c.cargo.content = c.cargo.content;
3146  //Description gets name at the top.
3147  c.cargo.description = "#b#"+beautify( c.cargo.content )+":#-b#n1.75#"
3148  +getSaveString( playerNum, MISSION_DESC_LABEL, i );
3149 
3150  tlist.masterList.push_back( c );
3151  }
3152  //Sort the list. Better for display, easier to compile into categories, etc.
3153  std::sort( tlist.masterList.begin(), tlist.masterList.end(), CargoColorSort() );
3154  if ( active_missions.size() ) {
3155  for (unsigned int i = 1; i < active_missions.size(); ++i) {
3156  CargoColor amission;
3157  amission.cargo.content = XMLSupport::tostring( i )+" "+active_missions[i]->mission_name;
3158  amission.cargo.price = 0;
3159  amission.cargo.quantity = 1;
3160  amission.cargo.category = "Active_Missions";
3161  amission.cargo.description = "Objectives\\";
3162  for (unsigned int j = 0; j < active_missions[i]->objectives.size(); ++j)
3163  amission.cargo.description = amission.cargo.GetDescription()+active_missions[i]->objectives[j].objective+": "
3164  +XMLSupport::tostring( (int) (100
3165  *active_missions[i]->objectives[j].completeness) )
3166  +"%\\";
3167  amission.color = DEFAULT_UPGRADE_COLOR();
3168  tlist.masterList.push_back( amission );
3169  }
3170  }
3171 }
3172 
3173 //Load the controls for the MISSIONS display.
3175 {
3176  //Make sure there's nothing in the transaction lists.
3178 
3179  //Load up the list of missions.
3181  SimplePicker *picker = static_cast< SimplePicker* > ( window()->findControlById( "Missions" ) );
3182  assert( picker != NULL );
3184 
3185  //Make the title right.
3186  recalcTitle();
3187 }
3188 
3189 //Accept a mission.
3190 bool BaseComputer::acceptMission( const EventCommandId &command, Control *control )
3191 {
3192  Unit *playerUnit = m_player.GetUnit();
3193  Unit *baseUnit = m_base.GetUnit();
3194  if ( !(playerUnit && baseUnit) ) return true;
3195  Cargo *item = selectedItem();
3196  if ( !item || !isTransactionOK( *item, ACCEPT_MISSION ) ) {
3197  //Better reload the UI -- we shouldn't have gotten here.
3200  return true;
3201  }
3202  if (item->GetCategory().find( "Active_Missions" ) != string::npos) {
3203  unsigned int whichmission = atoi( item->GetContent().c_str() );
3204  if ( whichmission > 0 && whichmission < active_missions.size() ) {
3205  Mission *miss = active_missions[whichmission];
3206  if (Network == NULL) {
3207  miss->terminateMission();
3208  if (miss == mission)
3209  mission = active_missions[0];
3210  refresh();
3211  } else if ( m_player.GetUnit() ) {
3213  if (cp < 0) cp = 0;
3214  int num = miss->getPlayerMissionNumber();
3215 
3216  Network[cp].missionRequest( Subcmd::TerminateMission, string(), num );
3217  }
3218  return true;
3219  }
3220  return false;
3221  }
3222  const int playernum = UnitUtil::isPlayerStarship( playerUnit );
3223  const size_t stringCount = getSaveStringLength( playernum, MISSION_NAMES_LABEL );
3224  if (Network == NULL)
3225  assert( stringCount == getSaveStringLength( playernum, MISSION_SCRIPTS_LABEL ) );
3226  string qualifiedName;
3227  if ( item->GetCategory().empty() )
3228  qualifiedName = item->content;
3229  else
3230  qualifiedName = item->GetCategory()+CATEGORY_SEP+item->GetContent();
3231  string finalScript;
3232  for (size_t i = 0; i < stringCount; i++)
3233  if (getSaveString( playernum, MISSION_NAMES_LABEL, i ) == qualifiedName) {
3234  if (Network == NULL) {
3235  finalScript = getSaveString( playernum, MISSION_SCRIPTS_LABEL, i );
3236  eraseSaveString( playernum, MISSION_SCRIPTS_LABEL, i );
3237  eraseSaveString( playernum, MISSION_NAMES_LABEL, i );
3238  eraseSaveString( playernum, MISSION_DESC_LABEL, i );
3239  } else {
3240  finalScript = "#";
3241  }
3242  break;
3243  }
3244  if ( finalScript.empty() ) {
3245  return false;
3246  } else {
3247  if (Network == NULL) {
3248  LoadMission( ("#"+item->category).c_str(),
3249  finalScript, false );
3250  refresh();
3251  } else if ( m_player.GetUnit() ) {
3253  if (cp < 0) cp = 0;
3254  Network[cp].missionRequest( Subcmd::AcceptMission, qualifiedName, active_missions.size() );
3255  }
3256  }
3257  //We handled the command, whether we successfully accepted the mission or not.
3258  return true;
3259 }
3260 
3261 //Load the all the controls for the UPGRADE display.
3263 {
3264  //Make sure there's nothing in the transaction lists.
3266 
3267  //Load the controls.
3270 
3271  //Make the title right.
3272  recalcTitle();
3273 }
3274 
3275 //Load the BUY controls for the UPGRADE display.
3277 {
3278  Unit *playerUnit = m_player.GetUnit();
3279  Unit *baseUnit = m_base.GetUnit();
3280 
3281  TransactionList &tlist = m_transList1;
3282  tlist.masterList.clear(); //Just in case
3283 
3284  //Get all the upgrades.
3285  assert( equalColors( CargoColor().color, DEFAULT_UPGRADE_COLOR() ) );
3286  std::vector< std::string >filtervec;
3287  filtervec.push_back( "upgrades" );
3288  loadMasterList( baseUnit, filtervec, std::vector< std::string > (), true, tlist );
3289  playerUnit->FilterUpgradeList( tlist.masterList );
3290 
3291  //Mark all the upgrades that we can't do.
3292  //cargo.mission == true means we can't upgrade this.
3293  vector< CargoColor >::iterator iter;
3294  for (iter = tlist.masterList.begin(); iter != tlist.masterList.end(); iter++)
3295  iter->cargo.mission = ( !equalColors( iter->color, DEFAULT_UPGRADE_COLOR() ) );
3296  //Add Basic Repair.
3298  repair.cargo.content = BASIC_REPAIR_NAME;
3299  repair.cargo.price = basicRepairPrice()*playerUnit->RepairCost();
3301  tlist.masterList.push_back( repair );
3302 
3303  //Load the upgrade picker from the master tlist.
3304  SimplePicker *basePicker = static_cast< SimplePicker* > ( window()->findControlById( "BaseUpgrades" ) );
3305  assert( basePicker != NULL );
3306  loadListPicker( tlist, *basePicker, BUY_UPGRADE, true );
3307 
3308  //Fix the Basic Repair color.
3309  SimplePickerCells *baseCells = static_cast< SimplePickerCells* > ( basePicker->cells() );
3310  SimplePickerCell *repairCell = static_cast< SimplePickerCell* > ( baseCells->cellAt( baseCells->count()-1 ) );
3311  assert( repairCell->text() == BASIC_REPAIR_NAME );
3312  if ( isClear( repairCell->textColor() ) )
3313  //Have repair cell, and its color is normal.
3314  repairCell->setTextColor( BASIC_REPAIR_TEXT_COLOR() );
3315 }
3316 
3317 //Load the SELL controls for the UPGRADE display.
3319 {
3320  Unit *playerUnit = m_player.GetUnit();
3321  Unit *baseUnit = m_base.GetUnit();
3322  if ( !(playerUnit && baseUnit) )
3323  return;
3324  TransactionList &tlist = m_transList2;
3325  tlist.masterList.clear(); //Just in case
3326 
3327  //Get a list of upgrades on our ship we could sell.
3328  Unit *partListUnit = &GetUnitMasterPartList();
3329 
3330  loadMasterList( partListUnit, weapfiltervec, std::vector< std::string > (), false, tlist );
3332  playerUnit->FilterDowngradeList( tlist.masterList );
3333  static const bool clearDowngrades =
3334  XMLSupport::parse_bool( vs_config->getVariable( "physics", "only_show_best_downgrade", "true" ) );
3335  if (clearDowngrades) {
3336  std::set< std::string >downgradeMap = GetListOfDowngrades();
3337  for (unsigned int i = 0; i < tlist.masterList.size(); ++i)
3338  if ( downgradeMap.find( tlist.masterList[i].cargo.content ) == downgradeMap.end() ) {
3339  tlist.masterList.erase( tlist.masterList.begin()+i );
3340  i--;
3341  }
3342  }
3343  //Mark all the upgrades that we can't do.
3344  //cargo.mission == true means we can't upgrade this.
3345  vector< CargoColor >::iterator iter;
3346  for (iter = tlist.masterList.begin(); iter != tlist.masterList.end(); iter++)
3347  iter->cargo.mission = ( !equalColors( iter->color, DEFAULT_UPGRADE_COLOR() ) );
3348  std::vector< std::string >invplayerfiltervec = weapfiltervec;
3349  std::vector< string >playerfiltervec;
3350  playerfiltervec.push_back( "upgrades" );
3351  loadMasterList( playerUnit, playerfiltervec, invplayerfiltervec, false, tlist ); //Get upgrades, but not weapons.
3352 
3353  //Sort the tlist. Better for display, easier to compile into categories, etc.
3354  std::sort( tlist.masterList.begin(), tlist.masterList.end(), CargoColorSort() );
3355 
3356  //Load the upgrade picker form the master list.
3357  SimplePicker *basePicker = static_cast< SimplePicker* > ( window()->findControlById( "PlayerUpgrades" ) );
3358  assert( basePicker != NULL );
3359  loadListPicker( tlist, *basePicker, SELL_UPGRADE, true );
3360 }
3361 
3362 //Change display mode to UPGRADE.
3364 {
3365  Unit *playerUnit = m_player.GetUnit();
3366  Unit *baseUnit = m_base.GetUnit();
3367  if ( !(playerUnit && baseUnit) ) return true;
3368  if (m_currentDisplay != UPGRADE)
3372  return true;
3373 }
3374 
3375 //Actually do a repair operation.
3376 static void BasicRepair( Unit *parent )
3377 {
3378  if (parent) {
3379  int repairmultiplier = parent->RepairCost();
3380  if (UnitUtil::getCredits( parent ) < basicRepairPrice()*repairmultiplier)
3381  showAlert( "You don't have enough credits to repair your ship." );
3382  else if ( ( repairmultiplier = parent->RepairUpgrade() ) )
3383  UnitUtil::addCredits( parent, -basicRepairPrice()*repairmultiplier );
3384  else
3385  showAlert( "Your ship has no damage. No charge." );
3386  }
3387 }
3388 
3389 //The "Operation" classes deal with upgrades.
3390 //There are a number of potential questions that get asked, and a bunch of state that needs
3391 //to be maintained between the questions. Rather than cluttering up the main class with this
3392 //stuff, it's all declared internally here.
3393 
3394 //Base class for operation. Support functions and common data.
3395 //Should delete itself when done.
3397 {
3398 protected:
3400  m_parent( p )
3401  , m_newPart( NULL )
3402  , m_part()
3403  , m_selectedMount( 0 )
3404  , m_selectedTurret( 0 )
3405  , m_selectedItem() {};
3406  virtual ~UpgradeOperation( void ) {}
3407 
3408  bool commonInit( void ); //Initialization.
3409  void finish( void ); //Finish up -- destruct the object. MUST CALL THIS LAST.
3410  bool endInit( void ); //Finish initialization. Returns true if successful.
3411  bool gotSelectedMount( int index ); //We have the mount number. Returns true if the operation was completed.
3412  bool gotSelectedTurret( int index ); //We have the turret number. Returns true if the operation was completed.
3413  void updateUI( void ); //Make the UI right after we are done.
3414 
3415 //OVERRIDES FOR DERIVED CLASSES.
3416  virtual bool checkTransaction( void ) = 0; //Check, and verify user wants transaction.
3417  virtual void concludeTransaction( void ) = 0; //Finish the transaction.
3418  virtual void selectMount( void ) = 0; //Let the user pick a mount.
3419  virtual void showTurretPicker( void ); //Let the user pick a turret.
3420 
3421  virtual void modalDialogResult( //Dispatch to correct function after some modal UI.
3422  const std::string &id, int result, WindowController &controller );
3423 
3424  BaseComputer &m_parent; //Parent class that created us.
3425  const Unit *m_newPart;
3426  Cargo m_part; //Description of upgrade part.
3427  int m_selectedMount; //Which mount to use.
3428  int m_selectedTurret; //Which turret to use.
3429  Cargo m_selectedItem; //Selection from original UI.
3430 };
3431 
3432 //Buy an upgrade for our ship.
3434 {
3435 public:
3436  void start( void ); //Start the operation.
3437 
3439  , m_theTemplate( NULL )
3440  , m_addMultMode( 0 ) {};
3441 protected:
3442  virtual bool checkTransaction( void ); //Check, and verify user wants transaction.
3443  virtual void concludeTransaction( void ); //Finish the transaction.
3444  virtual void selectMount( void ); //Let the user pick a mount.
3445 
3446  virtual ~BuyUpgradeOperation( void ) {}
3447 
3450 };
3451 
3452 //Sell an upgrade from our ship.
3454 {
3455 public:
3456  void start( void ); //Start the operation.
3457 
3459  , m_downgradeLimiter( NULL ) {};
3460 protected:
3461  virtual bool checkTransaction( void ); //Check, and verify user wants transaction.
3462  virtual void concludeTransaction( void ); //Finish the transaction.
3463  virtual void selectMount( void ); //Let the user pick a mount.
3464 
3465  virtual ~SellUpgradeOperation( void ) {}
3466 
3468 };
3469 
3470 //Id's for callbacks.
3471 static const string GOT_MOUNT_ID = "GotMount";
3472 static const string GOT_TURRET_ID = "GotTurret";
3473 static const string CONFIRM_ID = "Confirm";
3474 
3475 //Some common initialization.
3477 {
3479  if (selectedItem) {
3481  return true;
3482  } else {
3483  return false;
3484  }
3485 }
3486 
3487 //Update the UI controls after a transaction has been concluded successfully.
3489 {
3490  m_parent.refresh();
3491 }
3492 
3493 //Finish this operation.
3495 {
3496  //Destruct us now.
3497  delete this;
3498 }
3499 
3500 //Finish initialization. Returns true if successful.
3502 {
3503  if ( m_parent.m_player.GetUnit() ) {
3504  m_newPart = getUnitFromUpgradeName( m_selectedItem.content, m_parent.m_player.GetUnit()->faction );
3505  if (m_newPart->name != LOAD_FAILED)
3506  selectMount();
3507  else
3508  return false;
3509  }
3510  return true;
3511 }
3512 
3513 //Let the user pick a turret.
3515 {
3516  Unit *playerUnit = m_parent.m_player.GetUnit();
3517  if (!playerUnit) {
3518  finish();
3519  return;
3520  }
3521  vector< string >mounts;
3522  for (un_iter unitIter = playerUnit->getSubUnits(); *unitIter != NULL; unitIter++)
3523  mounts.push_back( (*unitIter)->name );
3524  showListQuestion( "Select turret mount for your turret:", mounts, this, GOT_TURRET_ID );
3525 }
3526 
3527 //Got the mount number.
3529 {
3530  Unit *playerUnit = m_parent.m_player.GetUnit();
3531  if (index < 0 || !playerUnit) {
3532  //The user cancelled somehow.
3533  finish();
3534  return false; //kill the window.
3535  } else {
3536  m_selectedMount = index;
3537  if ( !( *m_newPart->viewSubUnits() ) ) {
3538  //Not a turret. Proceed with the transaction.
3539  return checkTransaction();
3540  } else {
3541  //Is a turret.
3542  if (*playerUnit->getSubUnits() != NULL) {
3543  //Need to get selected turret.
3544  showTurretPicker();
3545  return false;
3546  } else {
3547  //Ship can't take turrets.
3548  finish();
3549  showAlert( "Your ship does not support turrets." );
3550  return false; //kill the window.
3551  }
3552  }
3553  }
3554 }
3555 
3556 //Got the mount number.
3558 {
3559  if (index < 0) {
3560  //The user cancelled somehow.
3561  finish();
3562  return false; //kill the window.
3563  } else {
3564  m_selectedTurret = index;
3565  return checkTransaction();
3566  }
3567 }
3568 
3569 //Dispatch to correct function after some modal UI.
3570 void BaseComputer::UpgradeOperation::modalDialogResult( const std::string &id, int result, WindowController &controller )
3571 {
3572  if (id == GOT_MOUNT_ID) {
3573  //Got the selected mount from the user.
3574  gotSelectedMount( result );
3575  } else if (id == GOT_TURRET_ID) {
3576  //Got the selected turret from the user.
3577  gotSelectedTurret( result );
3578  } else if (id == CONFIRM_ID) {
3579  //User answered whether or not to conclude the transaction.
3580  if (result == YES_ANSWER)
3581  //User wants to do this.
3582  concludeTransaction();
3583  else
3584  //User doesn't want to do it. All done.
3585  finish();
3586  }
3587 }
3588 
3589 //Start the Buy Upgrade Operation.
3591 {
3592  Unit *playerUnit = m_parent.m_player.GetUnit();
3593  Unit *baseUnit = m_parent.m_base.GetUnit();
3594  if ( !( playerUnit && baseUnit && commonInit() ) ) {
3595  finish();
3596  return;
3597  }
3598  m_theTemplate = makeTemplateUpgrade( playerUnit->name.get(), playerUnit->faction );
3599 
3600  m_addMultMode = GetModeFromName( m_selectedItem.GetContent().c_str() ); //Whether the price is linear or geometric.
3601  unsigned int offset; //Temp. Not used.
3602  Cargo *part = baseUnit->GetCargo( m_selectedItem.content, offset ); //Whether the base has any of these.
3603  if (part && part->quantity > 0) {
3604  m_part = *part;
3605  endInit();
3606  } else {
3607  finish();
3608  //The object may be deleted now. Be careful here.
3609  }
3610 }
3611 
3612 //Custom class that handles picking a mount point.
3614 {
3615 public:
3616 //Process a command event from the window.
3617  virtual bool processWindowCommand( const EventCommandId &command, Control *control );
3618 };
3619 
3620 //Process a command from the window.
3622 {
3623  if (command == "Picker::NewSelection") {
3624  assert( control != NULL );
3625  Picker *picker = static_cast< Picker* > (control);
3626  PickerCell *cell = picker->selectedCell();
3627  if (cell && cell->tag() == 0)
3628  //An "unselectable" cell was selected. Turn the selection back off.
3629  picker->selectCell( NULL );
3630  return true;
3631  }
3632  //Only thing we care about is the selection changing.
3633  return ListQuestionDialog::processWindowCommand( command, control );
3634 }
3635 
3636 //Select the mount to use for selling.
3638 {
3639  if (m_newPart->GetNumMounts() <= 0) {
3640  //Part doesn't need a mount point.
3641  gotSelectedMount( 0 );
3642  return;
3643  }
3644  Unit *playerUnit = m_parent.m_player.GetUnit();
3645  if (!playerUnit) {
3646  finish();
3647  return;
3648  }
3649  //Create a custom list dialog to get the mount point.
3651  dialog->init( "Select mount for your item:" );
3652  dialog->setCallback( this, GOT_MOUNT_ID );
3653 
3654  //Fill the dialog picker with the mount points.
3655  SimplePicker *picker = static_cast< SimplePicker* > ( dialog->window()->findControlById( "Picker" ) );
3656  assert( picker != NULL );
3657  for (int i = 0; i < playerUnit->GetNumMounts(); i++) {
3658  //Mount is selectable if we can upgrade with the new part using that mount.
3659  double percent; //Temp. Not used.
3660  const bool selectable = playerUnit->canUpgrade( m_newPart,
3661  i,
3662  m_selectedTurret,
3663  m_addMultMode,
3664  false,
3665  percent,
3666  m_theTemplate );
3667 
3668  //Figure color and label based on weapon that is in the slot.
3669  GFXColor mountColor = MOUNT_POINT_NO_SELECT();
3670  string mountName;
3671  string ammoexp;
3672  if (playerUnit->mounts[i].status == Mount::ACTIVE || playerUnit->mounts[i].status == Mount::INACTIVE) {
3673  mountName = tostring( i+1 )+" "+playerUnit->mounts[i].type->weapon_name;
3674  ammoexp =
3675  (playerUnit->mounts[i].ammo == -1) ? string( "" ) : string( ( " ammo: "+tostring( playerUnit->mounts[i].ammo ) ) );
3676  mountName += ammoexp;
3677  mountColor = MOUNT_POINT_FULL();
3678  } else {
3679  const std::string temp = lookupMountSize( playerUnit->mounts[i].size );
3680  mountName = tostring( i+1 )+" (Empty) "+temp.c_str();
3681  mountColor = MOUNT_POINT_EMPTY();
3682  }
3683  //If the mount point won't work with the weapon, don't let user select it.
3684  if (!selectable) mountColor = MOUNT_POINT_NO_SELECT();
3685  //Now we add the cell. Note that "selectable" is stored in the tag property.
3686  picker->addCell( new SimplePickerCell( mountName, "", mountColor, (selectable ? 1 : 0) ) );
3687  }
3688  dialog->run();
3689 }
3690 
3691 //Check, and verify user wants Buy Upgrade transaction. Returns true if more input is required.
3693 {
3694  Unit *playerUnit = m_parent.m_player.GetUnit();
3695  if (!playerUnit) {
3696  finish();
3697  return false; //We want the window to die to avoid accessing of deleted memory.
3698  }
3699  double percent; //Temp. Not used.
3700  if ( playerUnit->canUpgrade( m_newPart, m_selectedMount, m_selectedTurret, m_addMultMode, false, percent,
3701  m_theTemplate ) ) {
3702  //We can buy the upgrade.
3703  concludeTransaction();
3704  return false;
3705  } else {
3706  showYesNoQuestion( "The item cannot fit the frame of your starship. Do you want to buy it anyway?",
3707  this, CONFIRM_ID );
3708  return true;
3709  }
3710 }
3711 
3712 //Finish the transaction.
3714 {
3715  Unit *playerUnit = m_parent.m_player.GetUnit();
3716  Unit *baseUnit = m_parent.m_base.GetUnit();
3717  if ( !(playerUnit && baseUnit) ) {
3718  finish();
3719  return;
3720  }
3721  //Get the upgrade percentage to calculate the full price.
3722  double percent;
3723  int numleft = basecargoassets( baseUnit, m_part.content );
3724  while ( numleft > 0
3725  && playerUnit->canUpgrade( m_newPart, m_selectedMount, m_selectedTurret, m_addMultMode, true, percent,
3726  m_theTemplate ) ) {
3727  const float price = m_part.price; //* (1-usedValue(percent));
3728  if (_Universe->AccessCockpit()->credits >= price) {
3729  //Have enough money. Buy it.
3730  _Universe->AccessCockpit()->credits -= price;
3731  if (Network)
3733  //Upgrade the ship.
3734  playerUnit->Upgrade( m_newPart, m_selectedMount, m_selectedTurret, m_addMultMode, true, percent, m_theTemplate );
3735  static bool allow_special_with_weapons =
3736  XMLSupport::parse_bool( vs_config->getVariable( "physics", "special_and_normal_gun_combo", "true" ) );
3737  if (!allow_special_with_weapons) {
3738  playerUnit->ToggleWeapon( false, /*backwards*/ true );
3739  playerUnit->ToggleWeapon( false, /*backwards*/ false );
3740  }
3741  //Remove the item from the base, since we bought it.
3742  unsigned int index;
3743  baseUnit->GetCargo( m_part.content, index );
3744  baseUnit->RemoveCargo( index, 1, false );
3745  } else {
3746  break;
3747  }
3748  if (m_newPart->mounts.size() == 0)
3749  break;
3750  else if (m_newPart->mounts[0].ammo <= 0)
3751  break;
3752  numleft = basecargoassets( baseUnit, m_part.content );
3753  }
3754  updateUI();
3755 
3756  finish();
3757 }
3758 
3759 int basecargoassets( Unit *baseUnit, string cargoname )
3760 {
3761  unsigned int dummy;
3762  Cargo *somecargo = baseUnit->GetCargo( cargoname, dummy );
3763  if (somecargo)
3764  return somecargo->quantity;
3765  else
3766  return 0;
3767 }
3768 
3769 //Start the Sell Upgrade Operation.
3771 {
3772  Unit *playerUnit = m_parent.m_player.GetUnit();
3773  if ( !( playerUnit && commonInit() ) ) {
3774  finish();
3775  return;
3776  }
3777  const string unitDir = GetUnitDir( playerUnit->name.get().c_str() );
3778  const string limiterName = unitDir+".blank";
3779  const int faction = playerUnit->faction;
3780 
3781  //Get the "limiter" for this operation. Stats can't decrease more than the blank ship.
3782  m_downgradeLimiter = makeFinalBlankUpgrade( playerUnit->name, faction );
3783 
3784  //If its limiter is not available, just assume that there are no limits.
3785 
3786  Cargo *part = GetMasterPartList( m_selectedItem.GetContent().c_str() );
3787  if (part) {
3788  m_part = *part;
3789  endInit();
3790  } else {
3791  finish();
3792  //The object may be deleted now. Be careful here.
3793  }
3794 }
3795 
3796 //Try to match a mounted waepon name with the cargo name.
3797 //Returns true if they are the same.
3798 static bool matchCargoToWeapon( const std::string &cargoName, const std::string &weaponName )
3799 {
3800  //Weapon names have capitalized words, and no spaces between the words.
3801  //Cargo names are lower-case, and have underscores between words.
3802  //Also, anything in the Ammo category ends with "_ammo" in cargo, and not in weapon.
3803  //We try to make a cargo name look like a weapon name, then match them.
3804 
3805  std::string convertedCargoName;
3806 
3807  //Take off "_ammo" if it's there.
3808  int end = cargoName.size();
3809  const string::size_type ammoOffset = cargoName.rfind( "_ammo" );
3810  if (ammoOffset != std::string::npos)
3811  end = ammoOffset;
3812  bool wordStart = true; //Start of word.
3813  for (int i = 0; i < end; i++) {
3814  const char c = cargoName[i];
3815  if (c == '_') {
3816  //Skip this, and make sure next letter is capitalized.
3817  wordStart = true;
3818  } else if (wordStart) {
3819  //Start or a word. Capitalize the character, and turn off start of word.
3820  convertedCargoName += toupper( c );
3821  wordStart = false;
3822  } else {
3823  //Normal character in middle of word.
3824  convertedCargoName += c;
3825  }
3826  }
3827  return strtoupper( convertedCargoName ) == strtoupper( weaponName );
3828 }
3829 
3830 //Select the mount to use for selling.
3832 {
3833  if (m_newPart->GetNumMounts() <= 0) {
3834  //Part doesn't need a mount point.
3835  gotSelectedMount( 0 );
3836  return;
3837  }
3838  Unit *playerUnit = m_parent.m_player.GetUnit();
3839  if (!playerUnit) {
3840  finish();
3841  return;
3842  }
3843  //Create a custom list dialog to get the mount point.
3845  dialog->init( "Select mount for your item:" );
3846  dialog->setCallback( this, GOT_MOUNT_ID );
3847 
3848  //Fill the dialog picker with the mount points.
3849  SimplePicker *picker = static_cast< SimplePicker* > ( dialog->window()->findControlById( "Picker" ) );
3850  assert( picker != NULL );
3851  int mount = -1; //The mount if there was only one.
3852  int selectableCount = 0;
3853  for (int i = 0; i < playerUnit->GetNumMounts(); i++) {
3854  //Whether or not the entry is selectable -- the same as the thing we are selling.
3855  bool selectable = false;
3856 
3857  //Get the name.
3858  string mountName;
3859  if (playerUnit->mounts[i].status == Mount::ACTIVE || playerUnit->mounts[i].status == Mount::INACTIVE) {
3860  //Something is mounted here.
3861  const std::string unitName = playerUnit->mounts[i].type->weapon_name;
3862  const Unit *partUnit = UnitConstCache::getCachedConst( StringIntKey( m_part.content, FactionUtil::GetUpgradeFaction() ) );
3863  string ammoexp;
3864  mountName = tostring( i+1 )+" "+unitName.c_str();
3865  ammoexp =
3866  (playerUnit->mounts[i].ammo == -1) ? string( "" ) : string( ( " ammo: "+tostring( playerUnit->mounts[i].ammo ) ) );
3867  mountName += ammoexp;
3868  if (partUnit) {
3869  if ( partUnit->GetNumMounts() ) {
3870  if (partUnit->mounts[0].type == playerUnit->mounts[i].type) {
3871  selectable = true;
3872  selectableCount++;
3873  mount = i;
3874  }
3875  }
3876  } else if ( matchCargoToWeapon( m_part.content, unitName ) ) {
3877  selectable = true;
3878  selectableCount++;
3879  mount = i;
3880  }
3881  } else {
3882  //Nothing at this mount point.
3883  const std::string temp = lookupMountSize( playerUnit->mounts[i].size );
3884  mountName = tostring( i+1 )+" (Empty) "+temp.c_str();
3885  }
3886  //Now we add the cell. Note that "selectable" is stored in the tag property.
3887  const GFXColor mountColor = ( selectable ? MOUNT_POINT_FULL() : MOUNT_POINT_NO_SELECT() );
3888  picker->addCell( new SimplePickerCell( mountName, "", mountColor, (selectable ? 1 : 0) ) );
3889  }
3890  assert( selectableCount > 0 ); //We should have found at least one unit mounted.
3891  if (selectableCount > 1) {
3892  //Need to have the user choose.
3893  dialog->run();
3894  } else {
3895  //Get rid of the dialog -- we only have one choice.
3896  delete dialog;
3897  gotSelectedMount( mount );
3898  }
3899 }
3900 
3901 //Check, and verify user wants Sell Upgrade transaction. Returns true if more input is required.
3903 {
3904  Unit *playerUnit = m_parent.m_player.GetUnit();
3905  if (!playerUnit) {
3906  finish();
3907  return false; //We want the window to die to avoid accessing of deleted memory.
3908  }
3909  double percent; //Temp. Not used.
3910  if ( playerUnit->canDowngrade( m_newPart, m_selectedMount, m_selectedTurret, percent, m_downgradeLimiter ) ) {
3911  //We can sell the upgrade.
3912  concludeTransaction();
3913  return false;
3914  } else {
3915  showYesNoQuestion( "You don't have exactly what you wish to sell. Continue?",
3916  this, CONFIRM_ID );
3917  return true;
3918  }
3919 }
3920 
3921 //Finish the transaction.
3923 {
3924  Unit *playerUnit = m_parent.m_player.GetUnit();
3925  Unit *baseUnit = m_parent.m_base.GetUnit();
3926  if ( !(playerUnit && baseUnit) ) {
3927  finish();
3928  return;
3929  }
3930  //Get the upgrade percentage to calculate the full price.
3931  double percent;
3932  playerUnit->canDowngrade( m_newPart, m_selectedMount, m_selectedTurret, percent, m_downgradeLimiter );
3933  const float price = m_part.price*usedValue( percent );
3934  //Adjust the money.
3935  if (!Network)
3936  _Universe->AccessCockpit()->credits += price;
3937  //Change the ship.
3938  if ( playerUnit->Downgrade( m_newPart, m_selectedMount, m_selectedTurret, percent, m_downgradeLimiter ) ) {
3939  //Remove the item from the ship, since we sold it, and add it to the base.
3940  m_part.quantity = 1;
3941  m_part.price = baseUnit->PriceCargo( m_part.content );
3942  baseUnit->AddCargo( m_part );
3943  }
3944  updateUI();
3945 
3946  finish();
3947 }
3948 
3949 extern int GetModeFromName( const char* );
3950 //Buy a ship upgrade.
3951 bool BaseComputer::buyUpgrade( const EventCommandId &command, Control *control )
3952 {
3953  //Take care of Basic Repair, which is implemented entirely in this module.
3954  Cargo *item = selectedItem();
3955  if (item) {
3956  Unit *playerUnit = m_player.GetUnit();
3957  if (item->content == BASIC_REPAIR_NAME) {
3958  if (playerUnit) {
3959  BasicRepair( playerUnit );
3960  if (m_selectedList == NULL)
3961  return true;
3962  refresh();
3963  m_transList1.picker->selectCell( NULL ); //Turn off selection.
3964  }
3965  return true;
3966  }
3967  if ( !isWeapon( item->category ) ) {
3968  if (playerUnit) {
3969  Unit *baseUnit = m_base.GetUnit();
3970  if (baseUnit) {
3971  const int quantity = 1;
3972  playerUnit->BuyCargo( item->content, quantity, baseUnit, _Universe->AccessCockpit()->credits );
3973  playerUnit->Upgrade( item->content, 0, 0, true, false );
3974  refresh();
3975  }
3976  }
3977  return true;
3978  }
3979  }
3980  //This complicated operation is done in a separate object.
3981  BuyUpgradeOperation *op = new BuyUpgradeOperation( *this );
3982  op->start();
3983 
3984  return true;
3985 }
3986 
3987 //Sell an upgrade on your ship.
3988 bool BaseComputer::sellUpgrade( const EventCommandId &command, Control *control )
3989 {
3990  Cargo *item = selectedItem();
3991  if (item) {
3992  if ( !isWeapon( item->category ) ) {
3993  Cargo sold;
3994  const int quantity = 1;
3995  Unit *playerUnit = m_player.GetUnit();
3996  Unit *baseUnit = m_base.GetUnit();
3997  if (baseUnit && playerUnit) {
3998  playerUnit->SellCargo( item->content, quantity, _Universe->AccessCockpit()->credits, sold, baseUnit );
3999  UnitUtil::RecomputeUnitUpgrades( playerUnit );
4000  refresh();
4001  }
4002  return true;
4003  }
4004  }
4005  //This complicated operation is done in a separate object.
4006  SellUpgradeOperation *op = new SellUpgradeOperation( *this );
4007  op->start();
4008 
4009  return true;
4010 }
4011 
4012 //Sell an upgrade on your ship.
4013 bool BaseComputer::fixUpgrade( const EventCommandId &command, Control *control )
4014 {
4015  Cargo *item = selectedItem();
4016  Unit *playerUnit = m_player.GetUnit();
4017  Unit *baseUnit = m_base.GetUnit();
4018  if (baseUnit && playerUnit && item) {
4019  float *credits = NULL;
4020  Cockpit *cp = _Universe->isPlayerStarship( playerUnit );
4021  if (cp)
4022  credits = &(cp->credits);
4023  if ( playerUnit->RepairUpgradeCargo( item, baseUnit, credits ) )
4024  if (UnitUtil::PercentOperational( playerUnit, item->content, "upgrades/", false ) < 1.0)
4025  emergency_downgrade_mode = "EMERGENCY MODE ";
4026  refresh();
4027  }
4028  return true;
4029 }
4030 
4031 //Change controls to SHIP_DEALER mode.
4033 {
4038  return true;
4039 }
4040 
4041 //Create a Cargo for the specified starship.
4042 Cargo CreateCargoForOwnerStarship( const Cockpit *cockpit, const Unit *base, int i )
4043 {
4044  Cargo cargo;
4045  cargo.quantity = 1;
4046  cargo.volume = 1;
4047  cargo.price = 0;
4048 
4049  string locationSystemName = cockpit->GetUnitSystemName(i);
4050  string locationBaseName = cockpit->GetUnitBaseName(i);
4051  string destinationSystemName = _Universe->activeStarSystem()->getFileName();
4052  string destinationBaseName = (base != NULL) ? Cockpit::MakeBaseName(base) : "";
4053 
4054  bool needsJumpTransport = (locationSystemName != destinationSystemName);
4055  bool needsInsysTransport = (locationBaseName != destinationBaseName);
4056 
4057  static const float shipping_price_base =
4058  XMLSupport::parse_float( vs_config->getVariable( "physics", "shipping_price_base", "0" ) );
4059  static const float shipping_price_insys =
4060  XMLSupport::parse_float( vs_config->getVariable( "physics", "shipping_price_insys", "1000" ) );
4061  static const float shipping_price_perjump =
4062  XMLSupport::parse_float( vs_config->getVariable( "physics", "shipping_price_perjump", "25000" ) );
4063 
4064  cargo.price = shipping_price_base;
4065  cargo.content = cockpit->GetUnitFileName(i);
4066  cargo.category = "starships/My_Fleet";
4067 
4068  if (needsJumpTransport) {
4069  vector< string > jumps;
4071  locationSystemName,
4072  destinationSystemName,
4073  jumps);
4074  VSFileSystem::vs_dprintf(3, "Player ship needs transport from %s to %s across %d systems",
4075  locationBaseName.c_str(),
4076  destinationSystemName.c_str(),
4077  jumps.size());
4078  cargo.price += shipping_price_perjump * (jumps.size() - 1);
4079  } else if (needsInsysTransport) {
4080  VSFileSystem::vs_dprintf(3, "Player ship needs insys transport from %s to %s",
4081  locationBaseName.c_str(),
4082  destinationBaseName.c_str());
4083  cargo.price += shipping_price_insys;
4084  }
4085 
4086  return cargo;
4087 }
4088 
4089 //Create a Cargo for an owned starship from the name.
4090 Cargo CreateCargoForOwnerStarshipName( const Cockpit *cockpit, const Unit *base, std::string name, int &index )
4091 {
4092  for (size_t i = 1, n = cockpit->GetNumUnits(); i < n; ++i) {
4093  if (cockpit->GetUnitFileName(i) == name) {
4094  index = i;
4095  return CreateCargoForOwnerStarship( cockpit, base, i );
4096  }
4097  }
4098  //Didn't find it.
4099  return Cargo();
4100 }
4101 
4102 void SwapInNewShipName( Cockpit *cockpit, Unit *base, const std::string &newFileName, int swappingShipsIndex )
4103 {
4104  Unit *parent = cockpit->GetParent();
4105  if (parent) {
4106  size_t putpos = (swappingShipsIndex >= 0) ? swappingShipsIndex : cockpit->GetNumUnits();
4107  cockpit->GetUnitFileName(putpos) = parent->name;
4108  cockpit->GetUnitSystemName(putpos) = _Universe->activeStarSystem()->getFileName();
4109  cockpit->GetUnitBaseName(putpos) = (base != NULL) ? Cockpit::MakeBaseName(base) : string("");
4110  if (swappingShipsIndex != -1) {
4111  for (size_t i = 1, n = cockpit->GetNumUnits(); i < n ; ++i)
4112  if (cockpit->GetUnitFileName(i) == newFileName) {
4113  cockpit->RemoveUnit(i);
4114  --i; //then ++;
4115  }
4116  }
4117  } else if (swappingShipsIndex != -1) {
4118  //if parent is dead
4119  cockpit->RemoveUnit(swappingShipsIndex);
4120  }
4121  cockpit->GetUnitFileName() = newFileName;
4122 }
4123 
4124 string buildShipDescription( Cargo &item, std::string &texturedescription )
4125 {
4126  //load the Unit
4127  string newModifications;
4128  if (item.GetCategory().find( "My_Fleet" ) != string::npos)
4129  //Player owns this starship.
4130  newModifications = _Universe->AccessCockpit()->GetUnitModifications();
4131  Flightgroup *flightGroup = new Flightgroup();
4132  int fgsNumber = 0;
4134  Unit *newPart = UnitFactory::createUnit( item.GetContent().c_str(), false, 0, newModifications,
4135  flightGroup, fgsNumber );
4137  string sHudImage;
4138  string sImage;
4139  if ( newPart->getHudImage() ) {
4140  if ( newPart->getHudImage()->getTexture() ) {
4141  sHudImage = newPart->getHudImage()->getTexture()->texfilename;
4142  string::size_type delim = sHudImage.find( '|' ); //cut off alpha texture
4143  if (delim != string::npos) {
4144  sImage = sHudImage.substr( delim+1 );
4145  sHudImage = sHudImage.substr( 0, delim-sImage.length() ); //assumes RGBname == Alphaname for ships
4146  }
4147  delim = sHudImage.rfind( '.' ); //cut off mangled base directory
4148  if (delim != string::npos)
4149  sHudImage = sHudImage.substr( delim+2 );
4150  texturedescription = "../units/"+sHudImage+"/"+sImage;
4151  }
4152  }
4153  std::string str;
4154  showUnitStats( newPart, str, 0, 0, item );
4155  delete newPart;
4156  if ( texturedescription != "" && ( string::npos == str.find( '@' ) ) )
4157  str = "@"+texturedescription+"@"+str;
4158  return str;
4159 }
4160 
4161 //UNDER CONSTRUCTION
4163 {
4164  //load the Unit
4165  string blnk; //modifications to an upgrade item???
4166  Flightgroup *flightGroup = new Flightgroup(); //sigh
4167  int fgsNumber = 0;
4169  Unit *newPart = UnitFactory::createUnit( item.GetContent().c_str(), false,
4170  FactionUtil::GetUpgradeFaction(), blnk, flightGroup, fgsNumber );
4172  string str = "";
4173  str += item.description;
4174  showUnitStats( newPart, str, 0, 1, item );
4175  delete newPart;
4176  return str;
4177 }
4178 
4179 class PriceSort {
4180  const vector<float> &price;
4181  bool reverse;
4182 
4183 public:
4184  PriceSort(const vector<float> &_price, bool _reverse)
4185  : price(_price)
4186  , reverse(_reverse)
4187  {
4188  }
4189 
4190  bool operator()(size_t a, size_t b)
4191  {
4192  if (reverse)
4193  return price[a] > price[b];
4194  else
4195  return price[a] < price[b];
4196  }
4197 };
4198 
4199 void trackPrice(int whichplayer, const Cargo &item, float price, const string &systemName, const string &baseName,
4200  /*out*/ vector<string> &highest, /*out*/ vector<string> &lowest)
4201 {
4202  static size_t toprank = (size_t)
4203  XMLSupport::parse_int( vs_config->getVariable( "general", "trade_interface_tracks_prices_toprank", "10" ) );
4204 
4205  VSFileSystem::vs_dprintf(1, "Ranking item %s/%s at %s/%s\n",
4206  item.category.get().c_str(), item.content.get().c_str(),
4207  systemName.c_str(), baseName.c_str());
4208  fflush(stderr);
4209 
4210  // Recorded prices are always sorted, so we first do a quick check to avoid
4211  // triggering savegame serialization without reason
4212  string itemkey = string(item.category) + "/" + item.content;
4213  string hilock = itemkey + "?hi?loc";
4214  string lolock = itemkey + "?lo?loc";
4215  string hipricek = itemkey + "?hi?pcs";
4216  string lopricek = itemkey + "?lo?pcs";
4217 
4218  // First record the given item's price and update the ranking lists
4219  {
4220  string locname = "#b#" + baseName + "#-b# in the #b#" + systemName + "#-b# system";
4221 
4222  {
4223  bool resort = false;
4224  {
4225  // Limited lifetime iterator (points to invalid data after save data manipulation)
4226  const vector<string> &recordedHighestLocs = getStringList(whichplayer, hilock);
4227  const vector<float> &recordedHighestPrices = getSaveData(whichplayer, hipricek);
4228  vector<string>::const_iterator prev = std::find(
4229  recordedHighestLocs.begin(), recordedHighestLocs.end(),
4230  locname);
4231 
4232  if (prev != recordedHighestLocs.end()) {
4233  size_t index = prev - recordedHighestLocs.begin();
4234  putSaveData(whichplayer, hipricek, index, price);
4235  resort = true;
4236  } else if (recordedHighestPrices.size() < toprank || recordedHighestPrices.back() < price) {
4237  // Track new top price
4238  pushSaveString(whichplayer, hilock, locname);
4239  pushSaveData(whichplayer, hipricek, price);
4240  resort = true;
4241  }
4242  }
4243 
4244  if (resort) {
4245  // Re-get lists, the ones we got earlier could be empty stubs
4246  const vector<string> &locs = getStringList(whichplayer, hilock);
4247  const vector<float> &prices = getSaveData(whichplayer, hipricek);
4248  vector<size_t> indices;
4249 
4250  indices.resize(mymin(prices.size(),locs.size()));
4251  { for (size_t i=0; i<indices.size(); ++i)
4252  indices[i] = i; }
4253 
4254  std::sort(indices.begin(), indices.end(), PriceSort(prices, true));
4255 
4256  vector<string> newlocs;
4257  vector<float> newprices;
4258 
4259  newlocs.reserve(indices.size());
4260  newprices.reserve(indices.size());
4261  { for (size_t i=0; i<indices.size() && i<toprank; ++i) {
4262  newlocs.push_back(locs[indices[i]]);
4263  newprices.push_back(prices[indices[i]]);
4264  } }
4265 
4266  // Save new rank list
4267  saveDataList(whichplayer, hipricek, newprices);
4268  saveStringList(whichplayer, hilock, newlocs);
4269  }
4270  }
4271 
4272  {
4273  bool resort = false;
4274  {
4275  // Limited lifetime iterator (points to invalid data after save data manipulation)
4276  const vector<string> &recordedLowestLocs = getStringList(whichplayer, lolock);
4277  const vector<float> &recordedLowestPrices = getSaveData(whichplayer, lopricek);
4278  vector<string>::const_iterator prev = std::find(
4279  recordedLowestLocs.begin(), recordedLowestLocs.end(),
4280  locname);
4281 
4282  if (prev != recordedLowestLocs.end()) {
4283  size_t index = prev - recordedLowestLocs.begin();
4284  putSaveData(whichplayer, lopricek, index, price);
4285  resort = true;
4286  } else if (recordedLowestPrices.size() < toprank || recordedLowestPrices.back() > price) {
4287  // Track new top price
4288  pushSaveString(whichplayer, lolock, locname);
4289  pushSaveData(whichplayer, lopricek, price);
4290  resort = true;
4291  }
4292  }
4293 
4294  if (resort) {
4295  // Re-get lists, the ones we got earlier could be empty stubs
4296  const vector<string> &locs = getStringList(whichplayer, lolock);
4297  const vector<float> &prices = getSaveData(whichplayer, lopricek);
4298  vector<size_t> indices;
4299 
4300  indices.resize(mymin(prices.size(),locs.size()));
4301  { for (size_t i=0; i<indices.size(); ++i)
4302  indices[i] = i; }
4303 
4304  std::sort(indices.begin(), indices.end(), PriceSort(prices, false));
4305 
4306  vector<string> newlocs;
4307  vector<float> newprices;
4308 
4309  newlocs.reserve(indices.size());
4310  newprices.reserve(indices.size());
4311  { for (size_t i=0; i<indices.size() && i<toprank; ++i) {
4312  newlocs.push_back(locs[indices[i]]);
4313  newprices.push_back(prices[indices[i]]);
4314  } }
4315 
4316  // Save new rank list
4317  saveDataList(whichplayer, lopricek, newprices);
4318  saveStringList(whichplayer, lolock, newlocs);
4319  }
4320  }
4321  }
4322 
4323  // Now build the top-ranking descriptions
4324  {
4325  const vector<string> &recordedHighestLocs = getStringList(whichplayer, hilock);
4326  const vector<string> &recordedLowestLocs = getStringList(whichplayer, lolock);
4327  const vector<float> &recordedHighestPrices = getSaveData(whichplayer, hipricek);
4328  const vector<float> &recordedLowestPrices = getSaveData(whichplayer, lopricek);
4329 
4330  string prefix = " ";
4331  char conversionBuffer[128];
4332 
4333  VSFileSystem::vs_dprintf(1,"Tracking data:\n");
4334  VSFileSystem::vs_dprintf(1," highest locs: (%d)\n", recordedHighestLocs.size());
4335  { for (size_t i=0; i < recordedHighestLocs.size(); ++i) {
4336  VSFileSystem::vs_dprintf(1, " %d : %s\n", i, recordedHighestLocs[i].c_str());
4337  } }
4338 
4339  VSFileSystem::vs_dprintf(1," highest prices: (%d)\n", recordedHighestPrices.size());
4340  { for (size_t i=0; i < recordedHighestPrices.size(); ++i) {
4341  VSFileSystem::vs_dprintf(1, " %d : %.2f\n", i, recordedHighestPrices[i]);
4342  } }
4343 
4344  VSFileSystem::vs_dprintf(1," loest locs: (%d)\n", recordedLowestLocs.size());
4345  { for (size_t i=0; i < recordedLowestLocs.size(); ++i) {
4346  VSFileSystem::vs_dprintf(1, " %d : %s\n", i, recordedLowestLocs[i].c_str());
4347  } }
4348 
4349  VSFileSystem::vs_dprintf(1," lowest prices: (%d)\n", recordedLowestPrices.size());
4350  { for (size_t i=0; i < recordedLowestPrices.size(); ++i) {
4351  VSFileSystem::vs_dprintf(1, " %d : %.2f\n", i, recordedLowestPrices[i]);
4352  } }
4353 
4354  fflush(stderr);
4355 
4356 
4357  highest.clear();
4358  highest.resize(recordedHighestPrices.size());
4359  { for (size_t i=0; i < recordedHighestPrices.size(); ++i) {
4360  string &text = highest[i];
4361  PRETTY_ADD( "", recordedHighestPrices[i], 2 );
4362  text += " (at " + recordedHighestLocs[i] + ")";
4363 
4364  VSFileSystem::vs_dprintf(1, "Highest item %s\n", text.c_str());
4365  } }
4366 
4367  lowest.clear();
4368  lowest.resize(recordedLowestPrices.size());
4369  { for (size_t i=0; i < recordedLowestPrices.size(); ++i) {
4370  string &text = lowest[i];
4371  PRETTY_ADD( "", recordedLowestPrices[i], 2 );
4372  text += " (at " + recordedLowestLocs[i] + ")";
4373 
4374  VSFileSystem::vs_dprintf(1, "Lowest item %s\n", text.c_str());
4375  } }
4376  }
4377 }
4378 
4379 string buildCargoDescription( const Cargo &item, BaseComputer &computer, float price )
4380 {
4381  static bool trackBestPrices =
4382  XMLSupport::parse_bool( vs_config->getVariable( "general", "trade_interface_tracks_prices", "true" ) );
4383 
4384  string desc;
4385 
4386  if (trackBestPrices && computer.m_base.GetUnit() != NULL) {
4387  int cp = _Universe->whichPlayerStarship( computer.m_player.GetUnit() );
4388  vector<string> highest, lowest;
4389 
4390  const string &baseName = (computer.m_base.GetUnit()->isUnit() == PLANETPTR) ?
4391  computer.m_base.GetUnit()->name.get()
4392  : computer.m_base.GetUnit()->getFullname();
4393 
4394  trackPrice(cp, item, price, UniverseUtil::getSystemName(), baseName, highest, lowest );
4395 
4396  if (highest.size()) {
4397  desc += "#n##n##b#Highest prices seen#-b#:";
4398  for (vector<string>::const_iterator i=highest.begin(); i!=highest.end(); ++i)
4399  desc += *i;
4400  }
4401 
4402  if (lowest.size()) {
4403  desc += "#n##n##b#Lowest prices seen#-b#:";
4404  for (vector<string>::const_iterator i=lowest.begin(); i!=lowest.end(); ++i)
4405  desc += *i;
4406  }
4407  }
4408 
4409  return desc;
4410 }
4411 
4412 //Load the controls for the SHIP_DEALER display.
4414 {
4415  //Make sure there's nothing in the transaction lists.
4417 
4418  //Set up the base dealer's transaction list.
4419  std::vector< std::string >filtervec;
4420  filtervec.push_back( "starships" );
4421  loadMasterList( m_base.GetUnit(), filtervec, std::vector< std::string > (), true, m_transList1 );
4422 
4423  //Add in the starships owned by this player.
4424  Cockpit *cockpit = _Universe->AccessCockpit();
4425  for (size_t i = 1, n = cockpit->GetNumUnits(); i < n; ++i) {
4426  CargoColor cargoColor;
4427  cargoColor.cargo = CreateCargoForOwnerStarship( cockpit, m_base.GetUnit(), i );
4428  m_transList1.masterList.push_back( cargoColor );
4429  }
4430  //remove the descriptions, we don't build them all here, it is a time consuming operation
4431  vector< CargoColor > *items = &m_transList1.masterList;
4432  for (vector< CargoColor >::iterator it = items->begin(); it != items->end(); it++)
4433  (*it).cargo.description = "";
4434  //Load the picker from the master list.
4435  SimplePicker *basePicker = static_cast< SimplePicker* > ( window()->findControlById( "Ships" ) );
4436  assert( basePicker != NULL );
4437  loadListPicker( m_transList1, *basePicker, BUY_SHIP, true );
4438 
4439  //Make the title right.
4440  recalcTitle();
4441 }
4442 
4443 bool sellShip( Unit *baseUnit, Unit *playerUnit, std::string shipname, BaseComputer *bcomputer )
4444 {
4445  Cockpit *cockpit = _Universe->isPlayerStarship( playerUnit );
4446  unsigned int tempInt = 1;
4447  Cargo *shipCargo = baseUnit->GetCargo( shipname, tempInt );
4448  if (shipCargo == NULL)
4449  shipCargo = UniverseUtil::GetMasterPartList()->GetCargo( shipname, tempInt );
4450  if (shipCargo) {
4451  //now we can actually do the selling
4452  for (size_t i = 1, n = cockpit->GetNumUnits(); i < n; ++i)
4453  if (cockpit->GetUnitFileName(i) == shipname) {
4454  if ( Network && !_Universe->netLocked() ) {
4455  Network[0].shipRequest( shipname, Subcmd::SellShip );
4456  return false;
4457  }
4458  float xtra = 0;
4459  if ( cockpit->GetUnitSystemName(i) == _Universe->activeStarSystem()->getFileName() ) {
4460  static const float shipping_price =
4461  XMLSupport::parse_float( vs_config->getVariable( "physics", "sellback_shipping_price", "6000" ) );
4462  xtra += shipping_price;
4463  }
4464  cockpit->RemoveUnit(i);
4465  static float shipSellback =
4466  XMLSupport::parse_float( vs_config->getVariable( "economics", "ship_sellback_price", ".5" ) );
4467  cockpit->credits += shipSellback*shipCargo->price; //sellback cost
4468  cockpit->credits -= xtra; //transportation cost
4469  break;
4470  }
4471  if (bcomputer) {
4472  bcomputer->loadShipDealerControls();
4473  bcomputer->updateTransactionControlsForSelection( NULL );
4474  }
4475  return true;
4476  }
4477  return false;
4478 }
4479 
4480 bool BaseComputer::sellShip( const EventCommandId &command, Control *control )
4481 {
4482  Unit *playerUnit = m_player.GetUnit();
4483  Unit *baseUnit = m_base.GetUnit();
4484  Cargo *item = selectedItem();
4485  Cockpit *cockpit = _Universe->isPlayerStarship( playerUnit );
4486  if ( !(playerUnit && baseUnit && item && cockpit) )
4487  return true;
4488  return ::sellShip( baseUnit, playerUnit, item->content, this );
4489 }
4490 
4491 bool buyShip( Unit *baseUnit,
4492  Unit *playerUnit,
4493  std::string content,
4494  bool myfleet,
4495  bool force_base_inventory,
4496  BaseComputer *bcomputer )
4497 {
4498  unsigned int tempInt; //Not used.
4499  Cargo *shipCargo = baseUnit->GetCargo( content, tempInt );
4500  if (shipCargo == NULL && force_base_inventory)
4501  shipCargo = UniverseUtil::GetMasterPartList()->GetCargo( content, tempInt );
4502  Cargo myFleetShipCargo;
4503  int swappingShipsIndex = -1;
4504  if (myfleet) {
4505  //Player owns this starship.
4506  shipCargo = &myFleetShipCargo;
4507  myFleetShipCargo = CreateCargoForOwnerStarshipName( _Universe->AccessCockpit(), baseUnit, content, swappingShipsIndex );
4508  if ( shipCargo->GetContent().empty() ) {
4509  //Something happened -- can't find ship by name.
4510  shipCargo = NULL;
4511  swappingShipsIndex = -1;
4512  }
4513  } else {
4514  Cockpit *cockpit = _Universe->AccessCockpit();
4515  for (size_t i = 1, n = cockpit->GetNumUnits(); i < n; ++i)
4516  if (cockpit->GetUnitFileName(i) == content)
4517  return false;
4518  //can't buy a ship you own
4519  }
4520  if (shipCargo) {
4521  if (shipCargo->price < _Universe->AccessCockpit()->credits) {
4522  if ( Network && !_Universe->netLocked() ) {
4523  Network[0].shipRequest( content, myfleet ? Subcmd::SwitchShip : Subcmd::BuyShip );
4524  return false;
4525  }
4526  Flightgroup *flightGroup = playerUnit->getFlightgroup();
4527  int fgsNumber = 0;
4528  if (flightGroup != NULL) {
4529  fgsNumber = flightGroup->nr_ships;
4530  flightGroup->nr_ships++;
4531  flightGroup->nr_ships_left++;
4532  }
4533  string newModifications;
4534  std::string tmpnam = CurrentSaveGameName;
4535  if (swappingShipsIndex != -1) {
4536  //if we're swapping not buying load the olde one
4537  newModifications = _Universe->AccessCockpit()->GetUnitModifications();
4539  }
4540  WriteSaveGame( _Universe->AccessCockpit(), true ); //oops saved game last time at wrong place
4542  UniverseUtil::playSound( "sales/salespitch"+content.substr( 0, content.find( "." ) )+"accept.wav", QVector( 0,
4543  0,
4544  0 ),
4545  Vector( 0, 0, 0 ) );
4546  Unit *newPart =
4547  UnitFactory::createUnit( content.c_str(),
4548  false,
4549  baseUnit->faction,
4550  newModifications,
4551  flightGroup,
4552  fgsNumber );
4553  CurrentSaveGameName = tmpnam;
4554  newPart->SetFaction( playerUnit->faction );
4555  if (newPart->name != LOAD_FAILED) {
4556  if (newPart->nummesh() > 0) {
4557  _Universe->AccessCockpit()->credits -= shipCargo->price;
4558  newPart->curr_physical_state = playerUnit->curr_physical_state;
4559  newPart->SetPosAndCumPos( UniverseUtil::SafeEntrancePoint( playerUnit->Position(), newPart->rSize() ) );
4560  newPart->prev_physical_state = playerUnit->prev_physical_state;
4561  _Universe->activeStarSystem()->AddUnit( newPart );
4562  SwapInNewShipName( _Universe->AccessCockpit(), baseUnit, content, swappingShipsIndex );
4563  for (int j = 0; j < 2; ++j) {
4564  for (int i = playerUnit->numCargo()-1; i >= 0; --i) {
4565  Cargo c = playerUnit->GetCargo( i );
4566  if ( (c.mission != 0 && j == 0)
4567  || (c.mission == 0 && j == 1 && (!myfleet || Network) && c.GetCategory().find( "upgrades" ) != 0) ) {
4568  for (int k = c.quantity; k > 0; --k) {
4569  c.quantity = k;
4570  if ( newPart->CanAddCargo( c ) ) {
4571  newPart->AddCargo( c );
4572  playerUnit->RemoveCargo( i, c.quantity, true );
4573  break;
4574  }
4575  }
4576  }
4577  }
4578  }
4579  WriteSaveGame( _Universe->AccessCockpit(), true ); //oops saved game last time at wrong place
4580 
4581  _Universe->AccessCockpit()->SetParent( newPart, content.c_str(),
4583  playerUnit->curr_physical_state.position ); //absolutely NO NO NO modifications...you got this baby clean off the slate
4584 
4585  //We now put the player in space.
4586  SwitchUnits( NULL, newPart );
4587  playerUnit->UnDock( baseUnit );
4588  if (bcomputer)
4589  bcomputer->m_player.SetUnit( newPart );
4591  if (baseUnit)
4592  newPart->ForceDock( baseUnit, 0 );
4593  CurrentBaseUnitSet( newPart );
4594  if (bcomputer)
4595  bcomputer->m_player.SetUnit( newPart );
4596  static bool persistent_missions_across_ship_switch =
4597  XMLSupport::parse_bool( vs_config->getVariable( "general", "persistent_mission_across_ship_switch",
4598  "true" ) );
4599  if (persistent_missions_across_ship_switch)
4601  newPart = NULL;
4602  playerUnit->Kill();
4603  if (bcomputer)
4604  bcomputer->window()->close();
4605  return true;
4606  }
4607  }
4608  newPart->Kill();
4609  newPart = NULL;
4610  }
4611  }
4612  return false;
4613 }
4614 
4615 //Buy ship from the base.
4616 bool BaseComputer::buyShip( const EventCommandId &command, Control *control )
4617 {
4618  Unit *playerUnit = m_player.GetUnit();
4619  Unit *baseUnit = m_base.GetUnit();
4620  Cargo *item = selectedItem();
4621  if ( !(playerUnit && baseUnit && item) )
4622  return true;
4623  ::buyShip( baseUnit, playerUnit, item->GetContent(), item->GetCategory().find( "My_Fleet" ) != string::npos, true, this ); //last was false BUCO
4624  return true;
4625 }
4626 
4627 //Change controls to INFO mode.
4629 {
4630  if (m_currentDisplay != INFO) {
4632  //Initialize description with player info.
4633  window()->sendCommand( "ShowPlayerInfo", NULL );
4634  recalcTitle();
4635  }
4636  return true;
4637 }
4638 
4639 //Faction colors 2-Sept-03. mbyron.
4640 /*
4641  * 0. r=0.5 g=0.5 b=1
4642  * 1. r=0 g=0 b=1
4643  * 2. r=0 g=1 b=0
4644  * 3. r=0.5 g=0.5 b=1
4645  * 4. r=0.75 g=0.5 b=0.25
4646  * 5. r=0 g=0.5 b=1
4647  * 6. r=0.5 g=0 b=1
4648  * 7. r=0.5 g=0.5 b=1
4649  * 8. r=0.5 g=0.5 b=1
4650  * 9. r=1 g=0.5 b=0
4651  * 10. r=0.4 g=0.2 b=0.7
4652  * 11. r=1 g=1 b=1
4653  * 12. r=0.5 g=0.5 b=1
4654  * 13. r=1 g=0 b=0
4655  * 14. r=0.5 g=0.5 b=1
4656  */
4657 
4658 //Given a faction number, return a PaintText color command for the faction.
4659 //This lightens up the faction colors to make them more easily seen on the dark background.
4660 static std::string factionColorTextString( int faction )
4661 {
4662  //The following gets the spark (faction) color.
4663  const float *spark = FactionUtil::GetSparkColor( faction );
4664 
4665  //This line puts the faction colors on the std out.
4666  //printf("%2d. r=%g g=%g b=%g\n", faction, spark[0], spark[1], spark[2]);
4667 
4668  //Brighten up the raw colors by multiplying each channel by 2/3, then adding back 1/3.
4669  //The darker colors are too hard to read.
4670  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 );
4671 
4672  return result;
4673 }
4674 
4675 //Show the player's basic information.
4676 bool BaseComputer::showPlayerInfo( const EventCommandId &command, Control *control )
4677 {
4678  //Heading.
4679  string text = "#b#Factions:#-b#n1.7#";
4680 
4681  //Number of kills for each faction.
4682  vector< float > *killList = &_Universe->AccessCockpit()->savegame->getMissionData( string( "kills" ) );
4683 
4684  //Make everything bold.
4685  text += "#b#";
4686 
4687  //A line for each faction.
4688  const size_t numFactions = FactionUtil::GetNumFactions();
4689  size_t i = 0;
4690  static string disallowedFactions = vs_config->getVariable( "graphics", "unprintable_factions", "" );
4691  int totkills = 0;
4692  size_t fac_loc_before = 0, fac_loc = 0, fac_loc_after = 0;
4693  for (; i < numFactions; i++) {
4695  float relation = 0;
4696  size_t upgrades = FactionUtil::GetUpgradeFaction();
4697  size_t planets = FactionUtil::GetPlanetFaction();
4698  static size_t privateer = FactionUtil::GetFactionIndex( "privateer" );
4699  size_t neutral = FactionUtil::GetNeutralFaction();
4700  if (i < killList->size() && i != upgrades && i != planets && i != neutral && i != privateer)
4701  totkills += (int) (*killList)[i];
4702  string factionname = FactionUtil::GetFactionName( i );
4703  fac_loc_after = 0;
4704  fac_loc = disallowedFactions.find( factionname, fac_loc_after );
4705  while (fac_loc != string::npos) {
4706  fac_loc_before = fac_loc-1;
4707  if (fac_loc_before < 0)
4708  fac_loc_before = 0;
4709  fac_loc_after = fac_loc+factionname.size();
4710  if ( (fac_loc == 0 || disallowedFactions[fac_loc_before] == ' '
4711  || disallowedFactions[fac_loc_before] == '\t')
4712  && (disallowedFactions[fac_loc_after] == ' ' || disallowedFactions[fac_loc_after] == '\t'
4713  || disallowedFactions[fac_loc_after] == '\0') )
4714  break;
4715  fac_loc = disallowedFactions.find( factionname, fac_loc_after );
4716  }
4717  if (fac_loc != string::npos)
4718  continue;
4719  if (currentplayer)
4720  relation = UnitUtil::getRelationFromFaction( currentplayer, i );
4721  if (relation < -1) relation = -1;
4722  if (relation > 1) relation = 1;
4723  const int percent = (int) (relation*100.0);
4724 
4725  //Faction name.
4726  text += factionColorTextString( i )+FactionUtil::GetFactionName( i )+":#-c ";
4727 
4728  //Relation color.
4729  float normRelation = (relation+1)/2; //Move relation value into 0-1 range.
4730  normRelation = guiMax( 0, guiMin( 1, normRelation ) ); //Make *sure* it's in the right range.
4731  text += colorsToCommandString( 1-normRelation, normRelation, guiMin( 1-normRelation, normRelation ) );
4732 
4733  //End the line.
4734  text += XMLSupport::tostring( percent )+"#-c";
4735  if ( i < killList->size() )
4736  text += ", kills: "+XMLSupport::tostring( (int) (*killList)[i] );
4737  text += "#n#";
4738  }
4739  //Total Kills if we have it.
4740  text += "#n##b#Total Kills: "+XMLSupport::tostring( totkills )+"#-b#";
4741  //Put this in the description.
4742  StaticDisplay *desc = static_cast< StaticDisplay* > ( window()->findControlById( "Description" ) );
4743  assert( desc != NULL );
4744  desc->setText( text );
4745 
4746  return true;
4747 }
4748 
4749 //does not work with negative numbers!!
4750 void prettyPrintFloat( char *buffer, float f, int digitsBefore, int digitsAfter, int bufferLen )
4751 {
4752  int bufferPos = 0;
4753  if ( !FINITE( f ) || ISNAN( f ) ) {
4754  buffer[0] = 'n';
4755  buffer[1] = '/';
4756  buffer[2] = 'a';
4757  buffer[3] = '\0';
4758  return;
4759  }
4760  if (f < 0) {
4761  buffer[0] = '-';
4762  bufferPos = 1;
4763  f = (-f);
4764  }
4765  float temp = f;
4766  int before = 0;
4767  while (temp >= 1.0f) {
4768  before++;
4769  temp /= 10.0f;
4770  }
4771  while (bufferPos < (bufferLen-4-digitsAfter) && before < digitsBefore) {
4772  buffer[bufferPos++] = '0';
4773  digitsBefore--;
4774  }
4775  if (before) {
4776  for (int p = before; bufferPos < (bufferLen-4-digitsAfter) && p > 0; p--) {
4777  temp = f;
4778  float substractor = 1;
4779  for (int i = 0; i < p-1; i++) {
4780  temp /= 10.0f;
4781  substractor *= 10.0;
4782  } //whe cant't cast to int before in case of overflow
4783  int digit = ( (int) temp )%10;
4784  buffer[bufferPos++] = '0'+digit;
4785  //reason for the folowing line: otherwise the "((int)temp)%10" may overflow when converting
4786  f = f-( (float) digit*substractor );
4787  if ( (p != 1) && (p%3 == 1) ) buffer[bufferPos++] = ' '; // thousand separator
4788  }
4789  } else {
4790  buffer[bufferPos++] = '0';
4791  }
4792  if (digitsAfter == 0) {
4793  buffer[bufferPos] = 0;
4794  return;
4795  }
4796 
4797  if (bufferPos < bufferLen)
4798  buffer[bufferPos++] = '.';
4799 
4800  temp = f;
4801  for (int i = 0; bufferPos < (bufferLen-1) && i < digitsAfter; i++) {
4802  temp *= 10;
4803  buffer[bufferPos++] = '0'+( ( (int) temp )%10 );
4804  }
4805  if (bufferPos < bufferLen)
4806  buffer[bufferPos] = 0;
4807  //printf("******************* %f is, I think %s\n",dbgval,buffer);
4808 }
4809 
4810 static const char *WeaponTypeStrings[] = {
4811  "UNKNOWN",
4812  "BEAM",
4813  "BALL",
4814  "BOLT",
4815  "PROJECTILE"
4816 };
4817 
4818 void showUnitStats( Unit *playerUnit, string &text, int subunitlevel, int mode, Cargo &item )
4819 {
4820  static Unit *blankUnit = UnitFactory::createUnit( "upgrading_dummy_unit", 1, FactionUtil::GetFactionIndex( "upgrades" ) );
4821  static float warpenratio = XMLSupport::parse_float( vs_config->getVariable( "physics", "warp_energy_multiplier", "0.12" ) );
4822  static float warpbleed = XMLSupport::parse_float( vs_config->getVariable( "physics", "warpbleed", "20" ) );
4823  static float shield_maintenance_cost =
4824  XMLSupport::parse_float( vs_config->getVariable( "physics", "shield_maintenance_charge", ".25" ) );
4825  static bool shields_require_power =
4826  XMLSupport::parse_bool( vs_config->getVariable( "physics", "shields_require_passive_recharge_maintenance", "true" ) );
4827  static float shieldenergycap =
4828  XMLSupport::parse_float( vs_config->getVariable( "physics", "shield_energy_capacitance", ".2" ) );
4829 
4830  float Wconv = warpenratio == 0.0 ? 0.0 : (1.0/warpenratio); //converts from reactor to warp energy scales
4831  char conversionBuffer[128];
4832  string prefix = "";
4833  for (int i = 0; i < subunitlevel; i++)
4834  prefix += " ";
4835  //get conversion factor for damage -> MJ; note that shield and reactor stats use a different constant.
4836  static float kj_per_unit_damage =
4837  XMLSupport::parse_float( vs_config->getVariable( "physics", "kilojoules_per_unit_damage", "5400" ) );
4838  float VSDM = kj_per_unit_damage/1000.0;
4839  float RSconverter = 100; //100MJ per reactor or shield recharge energy unit
4840  float totalWeaponEnergyUsage = 0;
4841  float totalWeaponDamage = 0;
4842  string MPLdesc = "";
4843  string statcolor = "#c.75:.9:1#";
4844  string substatcolor = "#c.675:.925:.825#";
4845  string expstatcolor = "#c.6:.7:.8#";
4846  string nametemp = "";
4847  string model = "";
4848  size_t nameindex = 0;
4849  int replacement_mode = -1;
4850  if (mode) {
4851  replacement_mode = GetModeFromName( item.GetContent().c_str() );
4852  MPLdesc += text;
4853  text = "";
4854  string nametemp = "";
4855  string model = "";
4856  if (item.content == BASIC_REPAIR_NAME) {
4857  text += MPLdesc;
4858  return;
4859  }
4860  nametemp = playerUnit->getFullname();
4861  if (nametemp == "") {
4862  const std::string &name = playerUnit->name.get();
4863  for (nameindex = 0; ( nameindex < name.size() ) && name[nameindex] != '.'; ++nameindex)
4864  nametemp += name[nameindex];
4865  }
4866  if ( nametemp.length() )
4867  nametemp[0] = toupper( nametemp[0] );
4868  nametemp = beautify( nametemp );
4869  text += statcolor+"Selected Part: #-c"+nametemp;
4870  if (item.mass == 1)
4871  PRETTY_ADDU( statcolor+"Mass: #-c", item.mass, 0, "metric ton." );
4872  else
4873  PRETTY_ADDU( statcolor+"Mass: #-c", item.mass, 1, "metric tons." );
4874  if (item.volume == 1) {
4875  PRETTY_ADDN( statcolor+" Space required: #-c", item.volume, 0 );
4876  text += " cubic meter.#n##n##c0:1:.5#"+prefix+"[DESCRIPTION]#n##-c";
4877  } else {
4878  PRETTY_ADDN( statcolor+" Space required: #-c", item.volume, 1 );
4879  text += " cubic meters.#n##n##c0:1:.5#"+prefix+"[DESCRIPTION]#n##-c";
4880  }
4881  text += MPLdesc;
4882  text += "#n#";
4883  text += "#n##c0:1:.5#[STATS]#n##-c";
4884  }
4885  if (!mode) {
4886  const std::string &name = playerUnit->name;
4887  for (nameindex = 0; ( nameindex < name.size() ) && name[nameindex] != '.'; nameindex++) {
4888  }
4889  nametemp = playerUnit->getFullname();
4890  if ( nametemp.length() )
4891  nametemp[0] = toupper( nametemp[0] );
4892  for (nameindex = nameindex+1; nameindex < name.size(); nameindex++)
4893  model += name[nameindex];
4894  if (model == "blank")
4895  model = "TEMPLATE--WARNING--BUG";
4896  else if (model == "")
4897  model = "Military Issue (equipped)";
4898  else if (model == "rg")
4899  model = "Regional Guard Issue (equipped)";
4900  else if (model == "milspec")
4901  model = "Military Spec.";
4902  else if (model == "rgspec")
4903  model = "Regional Guard Spec.";
4904  else if (model == "stock")
4905  model = "Stock";
4906  else if (model == "begin")
4907  model = "Stock(Refurbished)";
4908  else
4909  model = "Military Spec. Variant ("+model+")";
4910  Cargo *fullname = GetMasterPartList( playerUnit->name.get().c_str() );
4911  Cargo *milname = GetMasterPartList( nametemp.c_str() );
4912  Cargo *blankname = GetMasterPartList( (nametemp+".blank").c_str() );
4913  if ( !subunitlevel && (fullname || milname || blankname) ) {
4914  text += "#c0:1:.5#"+prefix+"[NOTES]#n##n##-c";
4915  if (fullname)
4916  text += fullname->GetDescription();
4917  else if (blankname)
4918  text += blankname->GetDescription();
4919  else if (milname)
4920  text += milname->GetDescription();
4921  text += "#n#";
4922  }
4923  text += "#n##c0:1:.5#"+prefix+"[GENERAL INFORMATION]#n##-c";
4924 
4925  text += "#n#"+prefix+statcolor+"Class: #-c"+nametemp+statcolor+" Model: #-c"+model;
4926  PRETTY_ADDU( statcolor+"Mass: #-c", playerUnit->GetMass(), 0, "metric tons" );
4927  //Irrelevant to player as is proportional to mass in our physics system.
4928  //PRETTY_ADDU("Moment of inertia: ",playerUnit->GetMoment(),2,"tons.m�");
4929  }
4930  if ( mode && replacement_mode == 2 && playerUnit->GetMass() != blankUnit->GetMass() )
4931  PRETTY_ADDU( statcolor+"Effective Mass reduced by: #-c", 100.0*( 1.0-playerUnit->GetMass() ), 0, "%" );
4932  if (!subunitlevel) {
4933  float vol[2];
4934  float bvol[2];
4935  const char *dvol[2] = {"Hold", "Upgrade"};
4936  vol[0] = playerUnit->getEmptyCargoVolume();
4937  vol[1] = playerUnit->getEmptyUpgradeVolume();
4938  bvol[0] = blankUnit->getEmptyCargoVolume();
4939  bvol[1] = blankUnit->getEmptyUpgradeVolume();
4940  for (int index = 0; index < 2; ++index) {
4941  if (!mode) {
4942  PRETTY_ADDU( statcolor+dvol[index]+" volume: #-c", vol[index], 0, "cubic meters" );
4943  } else if (bvol[index] != vol[index]) {
4944  switch (replacement_mode)
4945  {
4946  case 0: //Replacement or new Module
4947  PRETTY_ADDU( statcolor+"Changes "+dvol[index]+" Volume to: #-c", vol[index], 0, "cubic meters" );
4948  break;
4949  case 1: //Additive
4950  PRETTY_ADDU( statcolor+"Adds #-c", vol[index], 0, "cubic meters "+statcolor+"to "+dvol[index]+" Volume #-c" );
4951  break;
4952  case 2: //multiplicative
4953  PRETTY_ADDU( statcolor+"Increases "+dvol[index]+" Volume by #-c", 100.0*(vol[index]-1), 0, "%" );
4954  break;
4955  default: //Failure
4956  text += "Oh dear, this wasn't an upgrade. Please debug code.";
4957  break;
4958  }
4959  }
4960  }
4961  }
4962  //following lines somewhat borken in terms of semantics for quantity of fuel
4963  //and policy of upgrades to fuel
4964  if (!mode) {
4965  PRETTY_ADDU( statcolor+"Fuel capacity: #-c", playerUnit->FuelData(), 2, "metric tons of Lithium-6" );
4966  } else if ( blankUnit->FuelData() != playerUnit->FuelData() ) {
4967  switch (replacement_mode)
4968  {
4969  case 0: //Replacement or new Module
4970  break;
4971  case 1: //Additive
4972  PRETTY_ADDU( statcolor+"Adds #-c", playerUnit->FuelData(), 2, "metric tons of Lithium-6 " /*+statcolor+"to Fuel Capacity #-c"*/ );
4973  break;
4974  case 2: //multiplicative
4975  break;
4976  default: //Failure
4977  text += "Oh dear, this wasn't an upgrade. Please debug code.";
4978  break;
4979  }
4980  }
4981  const Unit::Computer uc = playerUnit->ViewComputerData();
4982  const Unit::Computer buc = blankUnit->ViewComputerData();
4983  if (!mode) {
4984  text += "#n##n#"+prefix+"#c0:1:.5#[FLIGHT CHARACTERISTICS]#n##-c";
4985  text += "#n#"+prefix+statcolor+"Turning response: #-c";
4986  }
4987  if (playerUnit->limits.yaw == playerUnit->limits.pitch && playerUnit->limits.yaw == playerUnit->limits.roll) {
4988  prettyPrintFloat( conversionBuffer, playerUnit->limits.yaw
4989  /( (playerUnit->GetMoment() != 0) ? playerUnit->GetMoment() : 1 ), 0, 4 );
4990  if (!mode) {
4991  text += conversionBuffer;
4992  text += " radians/second^2#n#"+expstatcolor+" (yaw, pitch, roll)#-c";
4993  } else if (playerUnit->limits.yaw != blankUnit->limits.yaw) {
4994  switch (replacement_mode)
4995  {
4996  case 0: //Replacement or new Module
4997  PRETTY_ADDU( statcolor+"#n#Installs maneuvering jets with turning response #-c",
4998  playerUnit->limits.yaw,
4999  0,
5000  " radians/second^2#n#"+statcolor+" (yaw, pitch, roll)#-c" );
5001  break;
5002  case 1: //Additive
5003  break;
5004  case 2: //multiplicative
5005  PRETTY_ADDU( statcolor+"#n#Increases turning response by #-c",
5006  100.0*( (playerUnit->limits.yaw*180/PI)-1 ),
5007  0,
5008  "%#n#"+statcolor+" (yaw, pitch, roll)#-c" );
5009  break;
5010  default: //Failure
5011  text += "Oh dear, this wasn't an upgrade. Please debug code.";
5012  break;
5013  }
5014  }
5015  } else {
5016  if (!mode) {
5017  float moment = (playerUnit->GetMoment() != 0) ? playerUnit->GetMoment() : 1;
5018  PRETTY_ADDN( substatcolor+" yaw #-c", playerUnit->limits.yaw/(moment), 4 );
5019  PRETTY_ADDN( substatcolor+" pitch #-c", playerUnit->limits.pitch/(moment), 4 );
5020  PRETTY_ADDN( substatcolor+" roll #-c", playerUnit->limits.roll/(moment), 4 );
5021  text += " radians/second^2";
5022  } else if (playerUnit->limits.yaw != blankUnit->limits.yaw || playerUnit->limits.pitch != blankUnit->limits.pitch
5023  || playerUnit->limits.roll != blankUnit->limits.roll) {
5024  switch (replacement_mode)
5025  {
5026  case 0: //Replacement or new Module
5027  text += "#n#Replaces existing maneuvering system with one rated at: #-c#n#";
5028  PRETTY_ADDN( substatcolor+"Yaw #-c", playerUnit->limits.yaw, 2 );
5029  PRETTY_ADDN( substatcolor+" Pitch #-c", playerUnit->limits.pitch, 2 );
5030  PRETTY_ADDN( substatcolor+" Roll #-c", playerUnit->limits.roll, 2 );
5031  text += " metric-ton*radians/second^2";
5032  break;
5033  case 1: //Additive
5034  text += "#n#Upgrades existing maneuvering system by the following amounts: #-c#n#";
5035  PRETTY_ADDN( substatcolor+"Yaw #-c", playerUnit->limits.yaw, 2 );
5036  PRETTY_ADDN( substatcolor+" Pitch #-c", playerUnit->limits.pitch, 2 );
5037  PRETTY_ADDN( substatcolor+" Roll #-c", playerUnit->limits.roll, 2 );
5038  text += " metric-ton*radians/second^2";
5039  break;
5040  case 2: //multiplicative
5041  text += "#n#Increases performance of existing maneuvering system by the following percentages: #-c#n#";
5042  PRETTY_ADDN( substatcolor+"Yaw #-c", 100.0*( (playerUnit->limits.yaw*180/PI)-1 ), 0 );
5043  PRETTY_ADDN( substatcolor+" Pitch #-c", 100.0*( (playerUnit->limits.pitch*180/PI)-1 ), 0 );
5044  PRETTY_ADDN( substatcolor+" Roll #-c", 100.0*( (playerUnit->limits.roll*180/PI)-1 ), 0 );
5045  break;
5046  default: //Failure
5047  text += "Oh dear, this wasn't an upgrade. Please debug code.";
5048  break;
5049  }
5050  }
5051  }
5052  if (!subunitlevel) {
5053  if ( !mode && (playerUnit->GetMass() != 0) ) {
5054  PRETTY_ADDU( statcolor+"Fore acceleration: #-c",
5055  playerUnit->limits.forward/( 9.8*playerUnit->GetMass() ), 2, "gravities" );
5056  PRETTY_ADDU( statcolor+"Aft acceleration: #-c",
5057  playerUnit->limits.retro/( 9.8*playerUnit->GetMass() ), 2, "gravities" );
5058  if (playerUnit->limits.lateral == playerUnit->limits.vertical) {
5059  PRETTY_ADDU( statcolor+"Orthogonal acceleration: #-c",
5060  playerUnit->limits.vertical/( 9.8*playerUnit->GetMass() ), 2, "gravities" );
5061  text += expstatcolor+"#n# (vertical and lateral axes)#-c";
5062  } else {
5063  PRETTY_ADDN( statcolor+" Lateral acceleration #-c", playerUnit->limits.lateral/( 9.8*playerUnit->GetMass() ), 2 );
5064  PRETTY_ADDN( statcolor+" Vertical acceleration #-c",
5065  playerUnit->limits.vertical/( 9.8*playerUnit->GetMass() ), 2 );
5066  text += " gravities";
5067  }
5068  PRETTY_ADDU( statcolor+"Forward acceleration with overthrust: #-c", playerUnit->limits.afterburn
5069  /( 9.8*playerUnit->GetMass() ), 2, "gravities" );
5070  text.append( "#n##n##c0:1:.5#"+prefix+"[GOVERNOR SETTINGS]#n##-c" );
5071  } else {
5072  switch (replacement_mode)
5073  {
5074  case 0: //Replacement or new Module
5075  if (playerUnit->limits.forward != blankUnit->limits.forward) {
5076  PRETTY_ADDU( statcolor+"Provides forward thrust rated at: #-c",
5077  playerUnit->limits.forward/1000.0,
5078  2,
5079  "MegaNewtons" );
5080  }
5081  if (playerUnit->limits.retro != blankUnit->limits.retro) {
5082  PRETTY_ADDU( statcolor+"Provides aftward thrust rated at: #-c",
5083  playerUnit->limits.retro/1000.0,
5084  2,
5085  "MegaNewtons" );
5086  }
5087  if (playerUnit->limits.vertical != blankUnit->limits.vertical) {
5088  PRETTY_ADDU( statcolor+"Provides vertical thrust rated at: #-c",
5089  playerUnit->limits.vertical/1000.0,
5090  2,
5091  "MegaNewtons" );
5092  }
5093  if (playerUnit->limits.lateral != blankUnit->limits.lateral) {
5094  PRETTY_ADDU( statcolor+"Provides lateral thrust rated at: #-c",
5095  playerUnit->limits.lateral/1000.0,
5096  2,
5097  "MegaNewtons" );
5098  }
5099  if (playerUnit->limits.afterburn != blankUnit->limits.afterburn) {
5100  PRETTY_ADDU( statcolor+"Overdrive thrust rated at: #-c",
5101  playerUnit->limits.afterburn/1000.0,
5102  2,
5103  "MegaNewtons" );
5104  }
5105  break;
5106  case 1: //Additive
5107  if (playerUnit->limits.forward != blankUnit->limits.forward) {
5108  PRETTY_ADDU( statcolor+"Increases forward thrust rating by: #-c",
5109  playerUnit->limits.forward/1000.0,
5110  2,
5111  "MegaNewtons" );
5112  }
5113  if (playerUnit->limits.retro != blankUnit->limits.retro) {
5114  PRETTY_ADDU( statcolor+"Increases aftward thrust rating by: #-c",
5115  playerUnit->limits.retro/1000.0,
5116  2,
5117  "MegaNewtons" );
5118  }
5119  if (playerUnit->limits.vertical != blankUnit->limits.vertical) {
5120  PRETTY_ADDU( statcolor+"Increases vertical thrust rating by: #-c",
5121  playerUnit->limits.vertical/1000.0,
5122  2,
5123  "MegaNewtons" );
5124  }
5125  if (playerUnit->limits.lateral != blankUnit->limits.lateral) {
5126  PRETTY_ADDU( statcolor+"Increases lateral thrust rating by: #-c",
5127  playerUnit->limits.lateral/1000.0,
5128  2,
5129  "MegaNewtons" );
5130  }
5131  if (playerUnit->limits.afterburn != blankUnit->limits.afterburn) {
5132  PRETTY_ADDU( statcolor+"Increases overdrive thrust rating by: #-c",
5133  playerUnit->limits.afterburn/1000.0,
5134  2,
5135  "MegaNewtons" );
5136  }
5137  break;
5138  case 2: //multiplicative
5139  if (playerUnit->limits.forward != blankUnit->limits.forward) {
5140  PRETTY_ADDU( statcolor+"Increases forward thrust rating by: #-c",
5141  (playerUnit->limits.forward-1)*100,
5142  0,
5143  "%" );
5144  }
5145  if (playerUnit->limits.retro != blankUnit->limits.retro) {
5146  PRETTY_ADDU( statcolor+"Increases aftward thrust rating by: #-c",
5147  (playerUnit->limits.retro-1)*100,
5148  0,
5149  "%" );
5150  }
5151  if (playerUnit->limits.vertical != blankUnit->limits.vertical) {
5152  PRETTY_ADDU( statcolor+"Increases vertical thrust rating by: #-c",
5153  (playerUnit->limits.vertical-1)*100,
5154  0,
5155  "%" );
5156  }
5157  if (playerUnit->limits.lateral != blankUnit->limits.lateral) {
5158  PRETTY_ADDU( statcolor+"Increases lateral thrust rating by: #-c",
5159  (playerUnit->limits.lateral-1)*100,
5160  0,
5161  "%" );
5162  }
5163  if (playerUnit->limits.afterburn != blankUnit->limits.afterburn)
5164  PRETTY_ADDU( statcolor+"Overdrive thrust rating by: #-c", (playerUnit->limits.afterburn-1)*100, 0, "%" );
5165  break;
5166  default: //Failure
5167  text += "Oh dear, this wasn't an upgrade. Please debug code.";
5168  break;
5169  }
5170  }
5171  static float non_combat_mode_mult =
5172  XMLSupport::parse_float( vs_config->getVariable( "physics", "combat_speed_boost", "100" ) );
5173  if (!mode) {
5174  PRETTY_ADDU( statcolor+"Max combat speed: #-c", uc.max_speed(), 0, "m/s" );
5175  PRETTY_ADDU( statcolor+"Max overdrive combat speed: #-c", uc.max_ab_speed(), 0, "m/s" );
5176  PRETTY_ADDU( statcolor+"Max non-combat speed: #-c", uc.max_speed()*non_combat_mode_mult, 0, "m/s" );
5177  } else {
5178  switch (replacement_mode)
5179  {
5180  case 0: //Replacement or new Module
5181  if ( uc.max_speed() != buc.max_speed() ) {
5182  PRETTY_ADDU( statcolor+"Sets max combat speed governor to: #-c", uc.max_speed(), 0, "m/s" );
5183  PRETTY_ADDU( statcolor+"Sets max non-combat speed governor to: #-c",
5184  uc.max_speed()*non_combat_mode_mult, 0, "m/s" );
5185  }
5186  if ( uc.max_ab_speed() != buc.max_ab_speed() )
5187  PRETTY_ADDU( statcolor+"Sets max overdrive combat speed governor to: #-c", uc.max_ab_speed(), 0, "m/s" );
5188  break;
5189  case 1: //Additive
5190  if ( uc.max_speed() != buc.max_speed() ) {
5191  PRETTY_ADDU( statcolor+"Increases max combat speed governor setting by: #-c", uc.max_speed(), 0, "m/s" );
5192  PRETTY_ADDU( statcolor+"Increases max non-combat speed governor setting by: #-c",
5193  uc.max_speed()*non_combat_mode_mult, 0, "m/s" );
5194  }
5195  if ( uc.max_ab_speed() != buc.max_ab_speed() )
5196  PRETTY_ADDU( statcolor+"Increases max overdrive combat speed governor setting by: #-c",
5197  uc.max_ab_speed(), 0, "m/s" );
5198  break;
5199  case 2: //multiplicative
5200  if ( uc.max_speed() != buc.max_speed() ) {
5201  PRETTY_ADDU( statcolor+"Increases max combat speed governor settings by: #-c",
5202  100.0*(uc.max_speed()-1), 0, "%" );
5203  PRETTY_ADDU( statcolor+"Increases max non-combat speed governor settings by: #-c",
5204  100.0*(uc.max_speed()-1), 0, "%" );
5205  }
5206  if ( uc.max_ab_speed() != buc.max_ab_speed() )
5207  PRETTY_ADDU( statcolor+"Increases max overdrive combat speed governor settings by: #-c",
5208  (uc.max_ab_speed()-1)*100, 0, "%" );
5209  break;
5210  default: //Failure
5211  text += "Oh dear, this wasn't an upgrade. Please debug code.";
5212  break;
5213  }
5214  }
5215  }
5216  if (!mode) {
5217  if (uc.max_yaw_right == uc.max_pitch_up && uc.max_yaw_right == uc.max_roll_right) {
5218  PRETTY_ADD( statcolor+"Max turn rate: #-c", uc.max_yaw_right, 2 );
5219  text += " radians/second "+expstatcolor+"(yaw, pitch, roll)#-c";
5220  } else {
5221  text += ("#n#"+prefix+statcolor+"Max turn rates:#-c");
5222  PRETTY_ADDU( substatcolor+" - yaw: #-c", uc.max_yaw_right, 2, "radians/second" );
5223  PRETTY_ADDU( substatcolor+" - pitch: #-c", uc.max_pitch_up, 2, "radians/second" );
5224  PRETTY_ADDU( substatcolor+" - roll: #-c", uc.max_roll_right, 2, "radians/second" );
5225  }
5226  text += "#n##n##c0:1:.5#"+prefix+"[TARGETTING SUBSYSTEM]#n##-c";
5227  } else if (uc.max_yaw_right != buc.max_yaw_right || uc.max_pitch_up != buc.max_pitch_up || uc.max_roll_right
5228  != buc.max_roll_right) {
5229  switch (replacement_mode)
5230  {
5231  case 0: //Replacement or new Module
5232  text += ("#n#"+prefix+"Governor settings for maximum turn rates set to: ");
5233  PRETTY_ADDN( substatcolor+" yaw #-c", uc.max_yaw_right, 2 );
5234  PRETTY_ADDN( substatcolor+" pitch #-c", uc.max_pitch_up, 2 );
5235  PRETTY_ADDN( substatcolor+" roll #-c", uc.max_roll_right, 2 );
5236  text += " radians/second";
5237  break;
5238  case 1: //Additive
5239  text += ("#n#"+prefix+"Governor settings for maximum turn rates increased by: ");
5240  PRETTY_ADDN( substatcolor+" yaw #-c", uc.max_yaw_right, 2 );
5241  PRETTY_ADDN( substatcolor+" pitch #-c", uc.max_pitch_up, 2 );
5242  PRETTY_ADDN( substatcolor+" roll #-c", uc.max_roll_right, 2 );
5243  text += " radians/second";
5244  break;
5245  case 2: //multiplicative
5246  text += ("#n#"+substatcolor+"Increases governor settings for maximum turn rates by: #-c");
5247  PRETTY_ADDN( substatcolor+" yaw #-c", 100.0*( (uc.max_yaw_right*180/PI)-1 ), 0 );
5248  PRETTY_ADDN( substatcolor+" pitch #-c", 100.0*( (uc.max_pitch_up*180/PI)-1 ), 0 );
5249  PRETTY_ADDN( substatcolor+" roll #-c", 100.0*( (uc.max_roll_right*180/PI)-1 ), 0 );
5250  text += " %";
5251  break;
5252  default: //Failure
5253  text += "Oh dear, this wasn't an upgrade. Please debug code.";
5254  break;
5255  }
5256  }
5257  if (!mode) {
5258  PRETTY_ADDU( statcolor+"Tracking range: #-c", uc.radar.maxrange/1000, 0, "km" );
5259  if ( (acos( uc.radar.maxcone )*360/PI) < 359 ) {
5260  PRETTY_ADDU( statcolor+"Tracking cone: #-c", acos( uc.radar.maxcone )*2, 2, "radians" );
5261  text += expstatcolor+"#n# (planar angle: 2 pi means full space)#-c";
5262  } else {
5263  text += "#n#"+prefix+statcolor+"Tracking cone: #-cOMNIDIRECTIONAL";
5264  }
5265  PRETTY_ADDU( statcolor+"Assisted targeting cone: #-c", acos( uc.radar.trackingcone )*2, 2, "radians" );
5266  PRETTY_ADDU( statcolor+"Missile locking cone: #-c", acos( uc.radar.lockcone )*2, 2, "radians" );
5267  if (!subunitlevel) {
5268  //Always zero PRETTY_ADDU("Minimum target size: ",uc.radar.mintargetsize,2,"m");
5269  text += "#n#"+prefix+statcolor+"ITTS (Intelligent Target Tracking System) support: #-c";
5270  if (uc.itts) text += "yes";
5271 
5272  else text += "no";
5273  text += "#n#"+prefix+statcolor+"AFHH (Advanced Flag & Hostility Heuristics) support: #-c";
5274  std::string afhh;
5275  if (uc.radar.UseFriendFoe())
5276  afhh += "friendly/hostile ";
5277  if (uc.radar.UseThreatAssessment())
5278  afhh += "threat ";
5279  if (afhh.empty())
5280  afhh = "no";
5281  text += afhh;
5282  }
5283  text.append( "#n##n##c0:1:.5#"+prefix+"[ENERGY SUBSYSTEM]#n##-c" );
5284  } else {
5285  switch (replacement_mode)
5286  {
5287  case 0: //Replacement or new Module
5288  if (uc.radar.maxrange != buc.radar.maxrange || uc.radar.maxcone != buc.radar.maxcone) {
5289  PRETTY_ADDU( statcolor+"Tracking range: #-c", uc.radar.maxrange/1000, 0, "km" );
5290  if ( (acos( uc.radar.maxcone )*360/PI) < 359 ) {
5291  PRETTY_ADDU( statcolor+"Tracking cone: #-c", acos( uc.radar.maxcone )*2, 2, "radians" );
5292  text += statcolor+" (planar angle: 2 pi means full space)#-c";
5293  } else {
5294  text += "#n#"+prefix+statcolor+"Tracking cone: #-cOMNIDIRECTIONAL";
5295  }
5296  PRETTY_ADDU( statcolor+"Assisted targeting cone: #-c", acos( uc.radar.trackingcone )*2, 2, "radians" );
5297  PRETTY_ADDU( statcolor+"Missile locking cone: #-c", acos( uc.radar.lockcone )*2, 2, "radians" );
5298  text += "#n#"+prefix+statcolor+"ITTS (Intelligent Target Tracking System) support: #-c";
5299  if (uc.itts) text += "yes";
5300 
5301  else text += "no";
5302  text += "#n#"+prefix+statcolor+"AFHH (Advanced Flag & Hostility Heuristics) support: #-c";
5303  std::string afhh;
5304  if (uc.radar.UseFriendFoe())
5305  afhh += "friendly/hostile ";
5306  if (uc.radar.UseThreatAssessment())
5307  afhh += "threat ";
5308  if (afhh.empty())
5309  afhh = "no";
5310  text += afhh;
5311  }
5312  break;
5313  case 1: //Additive
5314  break;
5315  case 2: //multiplicative
5316  break;
5317  default: //Failure
5318  text += "Oh dear, this wasn't an upgrade. Please debug code.";
5319  break;
5320  }
5321  }
5322  const Unit::UnitJump &uj = playerUnit->GetJumpStatus();
5323  const Unit::UnitJump &buj = blankUnit->GetJumpStatus();
5324  if (!mode) {
5325  float maxshield = totalShieldEnergyCapacitance( playerUnit->shield );
5326  if (shields_require_power)
5327  maxshield = 0;
5328  PRETTY_ADDU( statcolor+"Recharge: #-c", playerUnit->EnergyRechargeData()*RSconverter, 0, "MJ/s" );
5329  PRETTY_ADDU( statcolor+"Weapon capacitor bank storage: #-c",
5330  ( (playerUnit->MaxEnergyData()-maxshield)*RSconverter ), 0, "MJ" );
5331  //note: I found no function to get max warp energy, but since we're docked they are the same
5332  if (!subunitlevel) {
5333  PRETTY_ADDU( statcolor+"Warp capacitor bank storage: #-c", playerUnit->WarpCapData()*RSconverter*Wconv, 0, "MJ" );
5334 
5335  text += "#n##n##c0:1:.5#"+prefix+"[SPEC SUBSYSTEM]#n##-c";
5336 
5337  PRETTY_ADDU( statcolor+"Active SPEC Energy Requirements: #-c",
5338  uj.insysenergy*RSconverter*Wconv/warpbleed,
5339  0,
5340  "MJ/s" );
5341 
5342  text += "#n##n##c0:1:.5#"+prefix+"[JUMP SUBSYSTEM]#n##-c";
5343  if (uj.drive == -2) {
5344  text += "#n##c1:.3:.3#No outsystem jump drive present#-c"; //fixed??
5345  } else {
5346  PRETTY_ADDU( statcolor+"Energy cost for jumpnode travel: #-c", uj.energy*RSconverter*Wconv, 0, "MJ" );
5347  if (uj.delay)
5348  PRETTY_ADDU( statcolor+"Delay: #-c", uj.delay, 0, "seconds" );
5349  if (uj.damage > 0)
5350  PRETTY_ADDU( statcolor+"Damage to outsystem jump drive: #-c", uj.damage*VSDM, 0, "MJ" );
5351  if (playerUnit->WarpCapData() < uj.energy) {
5352  text += "#n##c1:.3:.3#"+prefix
5353  +
5354  "WARNING: Warp capacitor banks under capacity for jump: upgrade warp capacitance#-c";
5355  }
5356  }
5357  }
5358  } else {
5359  switch (replacement_mode)
5360  {
5361  case 0: //Replacement or new Module
5362  if ( playerUnit->EnergyRechargeData() != blankUnit->EnergyRechargeData() )
5363  PRETTY_ADDU( statcolor+"Installs reactor with recharge rate: #-c",
5364  playerUnit->EnergyRechargeData()*RSconverter, 0, "MJ/s" );
5365  if ( playerUnit->MaxEnergyData() != blankUnit->MaxEnergyData() )
5366  PRETTY_ADDU( statcolor+"Installs main capacitor bank with storage capacity: #-c",
5367  (playerUnit->MaxEnergyData()*RSconverter), 0, "MJ" );
5368  if ( playerUnit->GetWarpEnergy() != blankUnit->GetWarpEnergy() )
5369  PRETTY_ADDU( statcolor+"Installs warp capacitor bank with storage capacity: #-c",
5370  playerUnit->GetWarpEnergy()*RSconverter*Wconv, 0, "MJ" );
5371  if (buj.drive != uj.drive) {
5372  text += statcolor
5373  +
5374  "#n#Allows travel via Jump Points.#n#Consult your personal info screen for ship specific energy requirements. #-c";
5375  }
5376  break;
5377  case 1: //Additive
5378  if ( playerUnit->EnergyRechargeData() != blankUnit->EnergyRechargeData() )
5379  PRETTY_ADDU( statcolor+"Increases recharge rate by #-c",
5380  playerUnit->EnergyRechargeData()*RSconverter, 0, "MJ/s" );
5381  if ( playerUnit->MaxEnergyData() != blankUnit->MaxEnergyData() )
5382  PRETTY_ADDU( statcolor+"Adds #-c",
5383  (playerUnit->MaxEnergyData()*RSconverter), 0, "MJ of storage to main capacitor banks" );
5384  if ( playerUnit->GetWarpEnergy() != blankUnit->GetWarpEnergy() )
5385  PRETTY_ADDU( statcolor+"Adds #-c",
5386  playerUnit->GetWarpEnergy()*RSconverter*Wconv, 0, "MJ of storage to warp capacitor bank" );
5387  break;
5388  case 2: //multiplicative
5389  if ( playerUnit->EnergyRechargeData() != blankUnit->EnergyRechargeData() )
5390  PRETTY_ADDU( statcolor+"Increases reactor recharge rate by #-c",
5391  100.0*(playerUnit->EnergyRechargeData()-1), 0, "%" );
5392  if ( playerUnit->MaxEnergyData() != blankUnit->MaxEnergyData() )
5393  PRETTY_ADDU( statcolor+"Increases main capacitor bank storage by #-c",
5394  100.0*(playerUnit->MaxEnergyData()-1), 0, "%" );
5395  if ( playerUnit->GetWarpEnergy() != blankUnit->GetWarpEnergy() )
5396  PRETTY_ADDU( statcolor+"Increases warp capacitor bank storage by #-c",
5397  (playerUnit->GetWarpEnergy()-1)*100, 0, "%" );
5398  break;
5399  default: //Failure
5400  text += "Oh dear, this wasn't an upgrade. Please debug code.";
5401  break;
5402  }
5403  }
5404  if (!mode) {
5405  text += "#n##n##c0:1:.5#"+prefix+"[DURABILITY STATISTICS]#n##-c";
5406  text += "#n#"+prefix+statcolor+"Armor damage resistance:#-c";
5407  }
5408  if (mode && playerUnit->armor.frontlefttop != blankUnit->armor.frontlefttop) {
5409  switch (replacement_mode)
5410  {
5411  case 0: //Replacement or new Module
5412  text += "#n#"+prefix+statcolor+"Replaces existing armor, if any.#n#Armor damage resistance:#-c";
5413  break;
5414  case 1: //Additive
5415  text += "#n#"+prefix+statcolor+"Adds the following to armor damage resistance ratings:#-c";
5416  break;
5417  case 2: //multiplicative
5418  text += "#n#"+prefix+statcolor+"Armor damage resistance increased by following percentages:#-c";
5419  break;
5420  default: //Failure
5421  text += "Oh dear, this wasn't an upgrade. Please debug code.";
5422  break;
5423  }
5424  }
5425  if (!mode || playerUnit->armor.frontrighttop != blankUnit->armor.frontrighttop) {
5426  PRETTY_ADDU(
5427  substatcolor+" - Fore-starboard-high: #-c",
5428  (mode && replacement_mode
5429  == 2) ? 100.0*(playerUnit->armor.frontrighttop-1) : playerUnit->armor.frontrighttop*VSDM,
5430  0,
5431  (2 == replacement_mode) ? "%" : "MJ" );
5432  PRETTY_ADDU(
5433  substatcolor+" - Aft-starboard-high: #-c",
5434  (mode && replacement_mode
5435  == 2) ? 100.0*(playerUnit->armor.backrighttop-1) : playerUnit->armor.backrighttop*VSDM,
5436  0,
5437  (2 == replacement_mode) ? "%" : "MJ" );
5438  PRETTY_ADDU(
5439  substatcolor+" - Fore-port-high: #-c",
5440  (mode && replacement_mode
5441  == 2) ? 100.0*(playerUnit->armor.frontlefttop-1) : playerUnit->armor.frontlefttop*VSDM,
5442  0,
5443  (2 == replacement_mode) ? "%" : "MJ" );
5444  PRETTY_ADDU(
5445  substatcolor+" - Aft-port-high: #-c",
5446  (mode && replacement_mode
5447  == 2) ? 100.0*(playerUnit->armor.backlefttop-1) : playerUnit->armor.backlefttop*VSDM,
5448  0,
5449  (2 == replacement_mode) ? "%" : "MJ" );
5450  PRETTY_ADDU(
5451  substatcolor+" - Fore-starboard-low: #-c",
5452  (mode && replacement_mode
5453  == 2) ? 100.0*(playerUnit->armor.frontrightbottom-1) : playerUnit->armor.frontrightbottom*VSDM,
5454  0,
5455  (2 == replacement_mode) ? "%" : "MJ" );
5456  PRETTY_ADDU(
5457  substatcolor+" - Aft-starboard-low: #-c",
5458  (mode && replacement_mode
5459  == 2) ? 100.0*(playerUnit->armor.backrightbottom-1) : playerUnit->armor.backrightbottom*VSDM,
5460  0,
5461  (2 == replacement_mode) ? "%" : "MJ" );
5462  PRETTY_ADDU(
5463  substatcolor+" - Fore-port-low: #-c",
5464  (mode && replacement_mode
5465  == 2) ? 100.0*(playerUnit->armor.frontleftbottom-1) : playerUnit->armor.frontleftbottom*VSDM,
5466  0,
5467  (2 == replacement_mode) ? "%" : "MJ" );
5468  PRETTY_ADDU(
5469  substatcolor+" - Aft-port-low: #-c",
5470  (mode && replacement_mode
5471  == 2) ? 100.0*(playerUnit->armor.backleftbottom-1) : playerUnit->armor.backleftbottom*VSDM,
5472  0,
5473  (2 == replacement_mode) ? "%" : "MJ" );
5474  }
5475  if (!mode) {
5476  PRETTY_ADDU( statcolor+"Sustainable Hull Damage: #-c",
5477  playerUnit->GetHull()/( playerUnit->GetHullPercent() )*VSDM, 0, "MJ" );
5478  if ( 1 != playerUnit->GetHullPercent() ) {
5479  PRETTY_ADD( " Current condition: ", playerUnit->GetHullPercent()*100, 2 );
5480  text += "% of normal";
5481  }
5482  } else if ( playerUnit->GetHull() != blankUnit->GetHull() ) {
5483  switch (replacement_mode)
5484  {
5485  case 0: //Replacement or new Module
5486  PRETTY_ADDU( statcolor+"New Sustained Hull Damage Rating: #-c",
5487  playerUnit->GetHull()/( playerUnit->GetHullPercent() )*VSDM, 0, "MJ" );
5488  break;
5489  case 1: //Additive
5490  PRETTY_ADDU( statcolor+"Increases sustainable hull damage by #-c", playerUnit->GetHull()
5491  /( playerUnit->GetHullPercent() )*VSDM, 0, "MJ" );
5492  break;
5493  case 2: //multiplicative
5494  PRETTY_ADDU( statcolor+"Hull Strength increased by #-c", 100.0*(playerUnit->GetHull()-1), 0, "%" );
5495  break;
5496  default: //Failure
5497  text += "Oh dear, this wasn't an upgrade. Please debug code.";
5498  break;
5499  }
5500  }
5501  if (!mode) {
5502  if (playerUnit->shield.number) {
5503  PRETTY_ADD( statcolor+"Number of shield emitter facings: #-c", playerUnit->shield.number, 0 );
5504  text += "#n#"+prefix+statcolor+"Shield protection rating:#-c";
5505  } else {
5506  text += "#n#"+prefix+statcolor+"No shielding. #-c";
5507  }
5508  } else if ( playerUnit->shield.number
5509  && ( (playerUnit->shield.shield2fb.frontmax != blankUnit->shield.shield2fb.frontmax)
5510  || (playerUnit->shield.shield4fbrl.frontmax != blankUnit->shield.shield4fbrl.frontmax)
5511  || (playerUnit->shield.shield8.frontrightbottommax != blankUnit->shield.shield8.frontrightbottommax) ) ) {
5512  switch (replacement_mode)
5513  {
5514  case 0: //Replacement or new Module
5515  text += "#n#"+prefix+statcolor+"Installs shield with following protection ratings:#-c";
5516  break;
5517  case 1: //Additive
5518  text += "#n#"+prefix+statcolor+"Adds following amounts to shield protection ratings:#-c";
5519  break;
5520  case 2: //multiplicative
5521  text += "#n#"+prefix+statcolor+"Shield protection rating for each emitter increased by listed percentage:#-c";
5522  break;
5523  default: //Failure
5524  text += "Oh dear, this wasn't an upgrade. Please debug code.";
5525  break;
5526  }
5527  }
5528  switch (playerUnit->shield.number)
5529  {
5530  case 0:
5531  break;
5532  case 2:
5533  if (!mode || playerUnit->shield.shield2fb.frontmax != blankUnit->shield.shield2fb.frontmax) {
5534  PRETTY_ADDU(
5535  substatcolor+" - fore: #-c",
5536  (mode && replacement_mode == 2) ? ( 100.0
5537  *(playerUnit->shield.shield2fb.backmax
5538  -1) ) : playerUnit->shield.shield2fb.frontmax*VSDM,
5539  0,
5540  (2 == replacement_mode) ? "%" : "MJ" );
5541  PRETTY_ADDU(
5542  substatcolor+" - aft: #-c",
5543  (mode && replacement_mode == 2) ? ( 100.0
5544  *(playerUnit->shield.shield2fb.backmax
5545  -1) ) : playerUnit->shield.shield2fb.backmax*VSDM,
5546  0,
5547  (2 == replacement_mode) ? "%" : "MJ" );
5548  }
5549  break;
5550  case 4:
5551  if (!mode || playerUnit->shield.shield4fbrl.frontmax != blankUnit->shield.shield4fbrl.frontmax) {
5552  PRETTY_ADDU(
5553  substatcolor+" - fore: #-c",
5554  (mode && replacement_mode == 2) ? ( 100.0
5555  *(playerUnit->shield.shield4fbrl.frontmax
5556  -1) ) : playerUnit->shield.shield4fbrl.frontmax*VSDM,
5557  0,
5558  (2 == replacement_mode) ? "%" : "MJ" );
5559  PRETTY_ADDU(
5560  substatcolor+" - aft: #-c",
5561  (mode && replacement_mode == 2) ? ( 100.0
5562  *(playerUnit->shield.shield4fbrl.backmax
5563  -1) ) : playerUnit->shield.shield4fbrl.backmax*VSDM,
5564  0,
5565  (2 == replacement_mode) ? "%" : "MJ" );
5566  PRETTY_ADDU(
5567  substatcolor+" - port: #-c",
5568  (mode && replacement_mode == 2) ? ( 100.0
5569  *(playerUnit->shield.shield4fbrl.leftmax
5570  -1) ) : playerUnit->shield.shield4fbrl.leftmax*VSDM,
5571  0,
5572  (2 == replacement_mode) ? "%" : "MJ" );
5573  PRETTY_ADDU(
5574  substatcolor+" - starboard: #-c",
5575  (mode && replacement_mode == 2) ? ( 100.0
5576  *(playerUnit->shield.shield4fbrl.rightmax
5577  -1) ) : playerUnit->shield.shield4fbrl.rightmax*VSDM,
5578  0,
5579  (2 == replacement_mode) ? "%" : "MJ" );
5580  }
5581  break;
5582  case 8:
5583  if (!mode || playerUnit->shield.shield8.frontrightbottommax != blankUnit->shield.shield8.frontrightbottommax) {
5584  PRETTY_ADDU(
5585  statcolor+" - Fore-starboard-high: #-c",
5586  (mode && replacement_mode
5587  == 2) ? 100.0
5588  *(playerUnit->shield.shield8.frontrighttopmax-1) : playerUnit->shield.shield8.frontrighttopmax*VSDM,
5589  0,
5590  (2 == replacement_mode) ? "%" : "MJ" );
5591  PRETTY_ADDU(
5592  substatcolor+" - Aft-starboard-high: #-c",
5593  (mode && replacement_mode
5594  == 2) ? 100.0
5595  *(playerUnit->shield.shield8.backrighttopmax-1) : playerUnit->shield.shield8.backrighttopmax*VSDM,
5596  0,
5597  (2 == replacement_mode) ? "%" : "MJ" );
5598  PRETTY_ADDU(
5599  substatcolor+" - Fore-port-high: #-c",
5600  (mode && replacement_mode
5601  == 2) ? 100.0
5602  *(playerUnit->shield.shield8.frontlefttopmax-1) : playerUnit->shield.shield8.frontlefttopmax*VSDM,
5603  0,
5604  (2 == replacement_mode) ? "%" : "MJ" );
5605  PRETTY_ADDU(
5606  substatcolor+" - Aft-port-high: #-c",
5607  (mode && replacement_mode
5608  == 2) ? 100.0
5609  *(playerUnit->shield.shield8.backlefttopmax-1) : playerUnit->shield.shield8.backlefttopmax*VSDM,
5610  0,
5611  (2 == replacement_mode) ? "%" : "MJ" );
5612  PRETTY_ADDU(
5613  substatcolor+" - Fore-starboard-low: #-c",
5614  (mode && replacement_mode
5615  == 2) ? 100.0
5616  *(playerUnit->shield.shield8.frontrighttopmax-1) : playerUnit->shield.shield8.frontrightbottommax*VSDM,
5617  0,
5618  (2 == replacement_mode) ? "%" : "MJ" );
5619  PRETTY_ADDU(
5620  substatcolor+" - Aft-starboard-low: #-c",
5621  (mode && replacement_mode
5622  == 2) ? 100.0
5623  *(playerUnit->shield.shield8.backrighttopmax-1) : playerUnit->shield.shield8.backrightbottommax*VSDM,
5624  0,
5625  (2 == replacement_mode) ? "%" : "MJ" );
5626  PRETTY_ADDU(
5627  substatcolor+" - Fore-port-low: #-c",
5628  (mode && replacement_mode
5629  == 2) ? 100.0
5630  *(playerUnit->shield.shield8.frontlefttopmax-1) : playerUnit->shield.shield8.frontleftbottommax*VSDM,
5631  0,
5632  (2 == replacement_mode) ? "%" : "MJ" );
5633  PRETTY_ADDU(
5634  substatcolor+" - Aft-port-low: #-c",
5635  (mode && replacement_mode
5636  == 2) ? 100.0
5637  *(playerUnit->shield.shield8.backlefttopmax-1) : playerUnit->shield.shield8.backleftbottommax*VSDM,
5638  0,
5639  (2 == replacement_mode) ? "%" : "MJ" );
5640  }
5641  break;
5642  default:
5643  text += "#c1:.3:.3#Shield model unrecognized#-c";
5644  break;
5645  }
5646  if (!mode) {
5647  PRETTY_ADDU( statcolor+"Shield protection recharge speed: #-c", playerUnit->shield.recharge*VSDM, 0, "MJ/s" );
5648  } else if (playerUnit->shield.recharge != blankUnit->shield.recharge) {
5649  switch (replacement_mode)
5650  {
5651  case 0: //Replacement or new Module
5652  PRETTY_ADDU( statcolor+"Shield protection recharge speed set to: #-c", playerUnit->shield.recharge*VSDM, 0, "MJ/s" );
5653  break;
5654  case 1: //Additive
5655  PRETTY_ADDU( statcolor+"Increases shield protection recharge speed by #-c",
5656  playerUnit->shield.recharge*VSDM,
5657  0,
5658  "MJ/s" );
5659  break;
5660  case 2: //multiplicative
5661  PRETTY_ADDU( statcolor+"Shield protection recharge speed increased by #-c",
5662  100.0*(playerUnit->shield.recharge-1),
5663  0,
5664  "%" );
5665  break;
5666  default: //Failure
5667  text += "Oh dear, this wasn't an upgrade. Please debug code.";
5668  break;
5669  }
5670  }
5671  //cloaking device? If we don't have one, no need to mention it ever exists, right?
5672  if (playerUnit->cloaking != -1) {
5673  if (!mode) {
5674  PRETTY_ADDU( statcolor+"Cloaking device available, energy usage: #-c",
5675  playerUnit->pImage->cloakenergy*RSconverter*Wconv,
5676  0,
5677  "MJ/s" );
5678  } else {
5679  switch (replacement_mode)
5680  {
5681  case 0: //Replacement or new Module
5682  PRETTY_ADDU( statcolor+"Installs a cloaking device.#n# Activated energy usage: #-c",
5683  playerUnit->pImage->cloakenergy*RSconverter*Wconv,
5684  0,
5685  "MJ/s" );
5686  break;
5687  case 1: //Additive
5688  text += "#n#Additive Cloaking...Seems like a bug to me.#n#";
5689  break;
5690  case 2: //multiplicative
5691  text += "#n#Multiplicative Cloaking...Seems like a bug to me.#n#";
5692  break;
5693  default: //Failure
5694  text += "Oh dear, this wasn't an upgrade. Please debug code.";
5695  break;
5696  }
5697  }
5698  }
5699  bool anyweapons = false;
5700  if (!mode) {
5701  text += "#n##n##c0:1:.5#"+prefix+"[ARMAMENT]#n##-c";
5702  text += prefix+"MOUNTPOINT RATINGS:";
5703  }
5704  //let's go through all mountpoints
5705  {
5706  for (int i = 0; i < playerUnit->GetNumMounts(); i++) {
5707  if (!mode) {
5708  PRETTY_ADD( " #c0:1:.3#[#-c", i+1, 0 );
5709  text += "#c0:1:.3#]#-c #c0:1:1#"+lookupMountSize( playerUnit->mounts[i].size )+"#-c";
5710  }
5711  const weapon_info *wi = playerUnit->mounts[i].type;
5712  if (wi && wi->weapon_name != "")
5713  anyweapons = true;
5714  }
5715  }
5716  if (!mode)
5717  text += "#n#"+prefix+"MOUNTED:"; //need brace for namespace issues on VC++
5718  {
5719  if (anyweapons) {
5720  for (int i = 0; i < playerUnit->GetNumMounts(); i++) {
5721  const weapon_info *wi = playerUnit->mounts[i].type;
5722  if ( (!wi) || (wi->weapon_name == "") ) {
5723  continue;
5724  } else {
5725  if (!mode) {
5726  PRETTY_ADD( " #c0:1:.3#[#-c", i+1, 0 );
5727  text += "#c0:1:.3#]#-c ";
5728  }
5729  text += wi->weapon_name+": #c0:1:1#"+lookupMountSize( wi->size )+"#-c#c.9:.9:.5#"
5730  +WeaponTypeStrings[wi->type]+" #-c";
5731  if (wi->Damage < 0) {text += "#n#"+prefix+statcolor+" Damage:#-c special"; } else {
5732  PRETTY_ADDU( statcolor+" Damage: #-c",
5733  wi->Damage*VSDM,
5734  0,
5735  wi->type == weapon_info::BEAM ? "MJ/s" : "MJ" );
5736  }
5737  PRETTY_ADDU( statcolor+" Range: #-c", wi->Range, 0, "meters" );
5738  PRETTY_ADDU( statcolor+" Energy usage: #-c",
5739  wi->EnergyRate*RSconverter,
5740  0,
5741  wi->type == weapon_info::BEAM ? "MJ/s" : "MJ/shot" );
5742 
5743  PRETTY_ADDU( statcolor+" Refire delay: #-c", wi->Refire(), 2, "seconds" );
5744  if (wi->PhaseDamage > 0)
5745  PRETTY_ADDU( statcolor+" Phase damage: #-c", wi->PhaseDamage*VSDM, 2, "MJ" );
5746  //display info specific to some weapons type
5747  switch (wi->type)
5748  {
5749  case weapon_info::BALL: //may need ammo
5750  case weapon_info::BOLT:
5751  if (wi->Damage > 0)
5752  totalWeaponDamage += ( wi->Damage/wi->Refire() ); //damage per second
5753  PRETTY_ADDU( statcolor+" Exit velocity: #-c", wi->Speed, 0, "meters/second" );
5754  if ( ( 100000*(1.0-wi->Longrange)/(wi->Range) ) > 0.00001 ) {
5755  PRETTY_ADD( statcolor+" Range attenuation factor: #-c",
5756  100000*(1.0-wi->Longrange)/(wi->Range),
5757  2 );
5758  text += "% per km";
5759  }
5760  if ( playerUnit->mounts[i].ammo != -1 && (lookupMountSize( wi->size ) != "SPECIAL-MISSILE") )
5761  PRETTY_ADD( statcolor+" Rounds remaining: #-c", playerUnit->mounts[i].ammo, 0 );
5762  else if (lookupMountSize( wi->size ) == "SPECIAL-MISSILE")
5763  PRETTY_ADD( statcolor+" Rockets remaining: #-c", playerUnit->mounts[i].ammo, 0 );
5764  totalWeaponEnergyUsage += ( wi->EnergyRate/wi->Refire() );
5765  break;
5766  case weapon_info::PROJECTILE: //need ammo
5767  if (wi->LockTime > 0) {
5768  PRETTY_ADDU( statcolor+" 'Fire and Forget' lock time: #-c", wi->LockTime, 0, "seconds" );
5769  } else {
5770  text += "#n#";
5771  text += prefix;
5772  text += statcolor+" Missile Lock Type: #-c#c1:.3:.3#None.#-c Inertial Guidance Only";
5773  }
5774  PRETTY_ADD( statcolor+" Missiles remaining: #-c", playerUnit->mounts[i].ammo, 0 );
5775  totalWeaponEnergyUsage += ( wi->EnergyRate/wi->Refire() );
5776  break;
5777  case weapon_info::BEAM:
5778  if (wi->Damage > 0)
5779  totalWeaponDamage += wi->Damage;
5780  PRETTY_ADDU( statcolor+" Beam stability: #-c", wi->Stability, 2, "seconds" );
5781  if ( ( 100000*(1.0-wi->Longrange)/(wi->Range) ) > 0.00001 ) {
5782  PRETTY_ADD( statcolor+" Range attenuation factor: #-c",
5783  100000*(1.0-wi->Longrange)/(wi->Range),
5784  2 );
5785  text += "% per km";
5786  }
5787  totalWeaponEnergyUsage += wi->EnergyRate;
5788  break;
5789  default:
5790  break;
5791  }
5792  }
5793  }
5794  } else //end mountpoint list
5795  if (!mode) {
5796  text += "#n##c1:.3:.3#"+prefix+" NO MOUNTED WEAPONS#n##-c";
5797  }
5798  }
5799  if (mode)
5800  return;
5801  if (subunitlevel == 0 && mode == 0) {
5802  text += "#n##n##c0:1:.5#"+prefix+"[KEY FIGURES]#n##-c";
5803  float maxshield = totalShieldEnergyCapacitance( playerUnit->shield );
5804  if (shields_require_power)
5805  maxshield = 0;
5806  PRETTY_ADDU( statcolor+"Minimum time to reach full overthrust speed: #-c",
5807  playerUnit->GetMass()*uc.max_ab_speed()/playerUnit->limits.afterburn, 2, "seconds" );
5808  //reactor
5809  float avail = (playerUnit->MaxEnergyData()*RSconverter-maxshield*VSDM);
5810  float overhead =
5811  (shields_require_power) ? (playerUnit->shield.recharge/shieldenergycap*shield_maintenance_cost
5812  *playerUnit->shield.number*VSDM) : 0;
5813  float nrt = avail/(playerUnit->EnergyRechargeData()*RSconverter-overhead);
5814  PRETTY_ADDU( statcolor+"Reactor nominal replenish time: #-c", nrt, 2, "seconds" );
5815  //shield related stuff
5816  //code taken from RegenShields in unit_generic.cpp, so we're sure what we say here is correct.
5817  static float low_power_mode = XMLSupport::parse_float( vs_config->getVariable( "physics", "low_power_mode_energy", "10" ) );
5818  if (playerUnit->MaxEnergyData()-maxshield < low_power_mode) {
5819  text += "#n##c1:.3:.3#"+prefix
5820  +
5821  "WARNING: Capacitor banks are overdrawn: downgrade shield, upgrade reactor or purchase reactor capacitance!#-c";
5822  }
5823  if (uj.drive != -2 && playerUnit->WarpCapData() < uj.energy) {
5824  text += "#n##c1:.3:.3#"+prefix
5825  +
5826  "WARNING: Warp capacitor banks under capacity for jump: upgrade warp capacitance#-c";
5827  }
5828  if (playerUnit->shield.number) {
5829  if (playerUnit->shield.recharge*playerUnit->shield.number*VSDM/shieldenergycap > playerUnit->EnergyRechargeData()
5830  *RSconverter) {
5831  text += "#n##c1:1:.1#"+prefix+"WARNING: reactor recharge rate is less than combined shield recharge rate.#n#";
5832  text += "Your shields won't be able to regenerate at their optimal speed!#-c";
5833  }
5834  if (shields_require_power) {
5835  text += "#n#"+prefix+statcolor+"Reactor recharge slowdown caused by shield maintenance: #-c";
5836  float maint_draw_percent = playerUnit->shield.recharge*VSDM*100.0/shieldenergycap*shield_maintenance_cost
5837  *playerUnit->shield.number/(playerUnit->EnergyRechargeData()*RSconverter);
5838  sprintf( conversionBuffer, "%.2f", maint_draw_percent );
5839  text += conversionBuffer;
5840  text += " %.";
5841  if (maint_draw_percent > 60) {
5842  text += "#n##c1:1:.1#"+prefix
5843  +
5844  "WARNING: Reactor power is heavily consumed by passive shield maintenance: consider downgrading shield or upgrading reactor.#-c";
5845  } else if (maint_draw_percent > 95) {
5846  text += "#n##c1:.3:.3#"+prefix
5847  +
5848  "SEVERE WARNING: Reactor power is overdrawn! Unsustainable power is being consumed by passive shield maintenance: downgrade shield or upgrade reactor immediately!#-c";
5849  }
5850  }
5851  }
5852  totalWeaponEnergyUsage = totalWeaponEnergyUsage*RSconverter;
5853  PRETTY_ADDU( statcolor+"Combined weapon energy usage: #-c", totalWeaponEnergyUsage, 0, "MJ/s" );
5854  float maint_draw =
5855  (shields_require_power && playerUnit->shield.number) ? (playerUnit->shield.recharge*VSDM/shieldenergycap
5856  *shield_maintenance_cost*playerUnit->shield.number) : 0;
5857  if ( totalWeaponEnergyUsage < (playerUnit->EnergyRechargeData()*RSconverter-maint_draw) ) {
5858  //waouh, impressive...
5859  text += "#n##c0:1:.2#"+prefix+"Your reactor produces more energy than your weapons can use!#-c";
5860  } else {
5861  PRETTY_ADDU( statcolor+"Reactor energy depletion time if weapons in continuous use: #-c",
5862  (playerUnit->MaxEnergyData()
5863  *RSconverter)/( totalWeaponEnergyUsage-( (playerUnit->EnergyRechargeData()*RSconverter-maint_draw) ) ),
5864  2,
5865  "seconds" );
5866  }
5867  PRETTY_ADDU( statcolor+"Combined (non-missile) weapon damage: #-c", totalWeaponDamage*VSDM, 0, "MJ/s" );
5868  }
5869  if (!mode) {
5870  //handle SubUnits
5871  Unit *sub;
5872  int i = 1;
5873  for (un_iter ki = playerUnit->getSubUnits(); (sub=*ki)!=NULL; ++ki, ++i) {
5874  if (i == 1) text += "#n##n##c0:1:.5#"+prefix+"[SUB UNITS]#-c";
5875  PRETTY_ADD( "#n#"+prefix+"#c0:1:.2#[#-csub unit ", i, 0 );
5876  text += "#c0:1:.2#]#-c#n#";
5877  showUnitStats( sub, text, subunitlevel+1, 0, item );
5878  }
5879  }
5880 }
5881 
5882 //Show the stats on the player's current ship.
5883 bool BaseComputer::showShipStats( const EventCommandId &command, Control *control )
5884 {
5886  Unit *playerUnit = m_player.GetUnit();
5888  const string rawText = MakeUnitXMLPretty( playerUnit->WriteUnitString(), playerUnit );
5889 
5890  //Need to translate some characters to make it even prettier.
5891  string text;
5892  text = "";
5893  Cargo uninitcargo;
5894  showUnitStats( playerUnit, text, 0, 0, uninitcargo );
5895  //remove picture, if any
5896  string::size_type pic;
5897  if ( ( pic = text.find( "@" ) ) != string::npos ) {
5898  std::string texture = text.substr( pic+1 );
5899  text = text.substr( 0, pic );
5900  string::size_type picend = texture.find( "@" );
5901  if (picend != string::npos)
5902  text += texture.substr( picend+1 );
5903  } //picture removed
5904  text.append( "#n##n##c0:1:.5#[RAW DIAGNOSTIC OUTPUT]#n##-c" );
5905  bool inQuote = false;
5906  bool newLine = false;
5907  static bool showdiags = XMLSupport::parse_bool( vs_config->getVariable( "debug", "showdiagnostics", "false" ) );
5908  if (showdiags) {
5909  for (string::const_iterator i = rawText.begin(); i != rawText.end(); i++) {
5910  switch (*i)
5911  {
5912  case '\n':
5913  text.append( "#n#" );
5914  if (!newLine) {
5915  text.append( "#c0:1:.5#" );
5916  newLine = true;
5917  }
5918  break;
5919  case '"':
5920  if (!inQuote) {
5921  text.append( "#c1:.3:.3#" );
5922  inQuote = true;
5923  } else {
5924  text.append( "#-c" );
5925  inQuote = false;
5926  }
5927  //Delete these, so do nothing.
5928  break;
5929  case ' ':
5930  if (newLine) {
5931  newLine = false;
5932  text.append( "#-c" );
5933  }
5934  text += (*i);
5935  break;
5936  default:
5937  text += (*i);
5938  break;
5939  }
5940  }
5941  } else {
5942  text.append( "#n# #c1:.1:.1#SUPPRESSED #n##-c" );
5943  //Put this in the description.
5944  }
5945  StaticDisplay *desc = static_cast< StaticDisplay* > ( window()->findControlById( "Description" ) );
5946  assert( desc != NULL );
5947  desc->setText( text );
5948 
5949  return true;
5950 }
5951 
5952 namespace CockpitKeys
5953 {
5954 void QuitNow();
5955 }
5956 
5957 //Create the window and controls for the Options Menu.
5959 {
5960  Window *window = new Window;
5961  setWindow( window );
5962 
5963  window->setSizeAndCenter( Size( .9, .5 ) );
5964  window->setTexture( "basecomputer.png" );
5965  window->setColor( GFXColor( 0, 1, 0, .1 ) );
5966  window->setOutlineColor( GFXColor( .7, .7, .7 ) );
5967  window->setOutlineWidth( 2.0 );
5968  window->setController( this );
5969 
5970  //Information.
5971  StaticDisplay *text = new StaticDisplay;
5972  text->setRect( Rect( -.4, -.15, .8, .3 ) );
5973  text->setText( this->text );
5974  text->setTextColor( GFXColor( .7, 1, .4 ) );
5975  text->setMultiLine( true );
5976  text->setColor( GUI_CLEAR );
5977  text->setFont( Font( .07, 1.25 ) );
5978  text->setId( "Information" );
5979  //Put it on the window.
5980  window->addControl( text );
5981 
5982  //Save button.
5983  NewButton *cont = new NewButton;
5984  cont->setRect( Rect( .05, -.19, .30, .1 ) );
5985  cont->setLabel( type );
5986  cont->setCommand( type );
5987  cont->setColor( GFXColor( 1, .5, 0, .25 ) );
5988  cont->setTextColor( GUI_OPAQUE_WHITE() );
5989  cont->setDownColor( GFXColor( 1, .5, 0, .6 ) );
5991  cont->setHighlightColor( GFXColor( 0, 1, 0, .4 ) );
5992  cont->setFont( Font( .08, BOLD_STROKE ) );
5993  //Put the button on the window.
5994  window->addControl( cont );
5995 
5996  //Abort action button
5997  NewButton *resume = new NewButton;
5998  resume->setRect( Rect( -.35, -.20, .30, .12 ) );
5999  resume->setLabel( "Cancel" );
6000  resume->setCommand( "Window::Close" );
6001  resume->setColor( GFXColor( 0, 1, 0, .25 ) );
6002  resume->setTextColor( GUI_OPAQUE_WHITE() );
6003  resume->setDownColor( GFXColor( 0, 1, 0, .6 ) );
6004  resume->setDownTextColor( GUI_OPAQUE_BLACK() );
6005  resume->setHighlightColor( GFXColor( 0, 1, 0, .4 ) );
6006  resume->setFont( Font( .08, BOLD_STROKE ) );
6007  //Put the button on the window.
6008  window->addControl( resume );
6009 
6010  window->setModal( true );
6011 }
6012 
6013 //Process a command event from the Options Menu window.
6015 {
6016  if (command == "Save") {
6017  m_parent->actionConfirmedSaveGame();
6018  window()->close();
6019  } else if (command == "Load") {
6020  m_parent->actionConfirmedLoadGame();
6021  } else if (command == "Quit") {
6022  m_parent->actionConfirmedQuitGame();
6023  } else {
6024  //Not a command we know about.
6025  return WindowController::processWindowCommand( command, control );
6026  }
6027  return true;
6028 }
6029 
6030 //Show options.
6031 
6033 {
6035  return true;
6036 }
6037 
6038 bool BaseComputer::actionQuitGame( const EventCommandId &command, Control *control )
6039 {
6040  LoadSaveQuitConfirm *saver = new LoadSaveQuitConfirm( this, "Quit", "Are you sure that you want to quit?" );
6041  saver->init();
6042  saver->run();
6043  return true;
6044 }
6045 
6047 {
6048  Unit *player = m_player.GetUnit();
6049  if (player && player->name == "return_to_cockpit") {
6050  showAlert( "Return to a base to save." );
6051  return false; //should be false, but causes badness.
6052  }
6053  StaticDisplay *desc = static_cast< StaticDisplay* > ( window()->findControlById( "InputText" ) );
6054  if (desc) {
6055  std::string tmp = desc->text();
6058  if (err > Ok) {
6059  showAlert(
6060  "Could not create the saved game because it contains invalid characters or you do not have permissions or free space." );
6061  } else {
6062  fp.Close();
6063  if (tmp.length() > 0) {
6064  Cockpit *cockpit = player ? _Universe->isPlayerStarship( player ) : 0;
6065  if (player && cockpit) {
6067  WriteSaveGame( cockpit, false );
6069  showAlert( "Game saved successfully." );
6070  } else {
6071  showAlert( "Oops - unexpected error (player or cockpit is null)" );
6072  }
6073  } else {
6074  showAlert( "You Must Type In a Name To Save." );
6075  }
6076  }
6077  } else {
6078  showAlert( "Oops - unexpected error (desc control not found!)" );
6079  }
6080  return true;
6081 }
6082 
6083 bool BaseComputer::actionSaveGame( const EventCommandId &command, Control *control )
6084 {
6085  Unit *player = m_player.GetUnit();
6086  StaticDisplay *desc = static_cast< StaticDisplay* > ( window()->findControlById( "InputText" ) );
6087  bool ok = true;
6088  std::string tmp;
6089  if (desc) {
6090  tmp = desc->text();
6091  if (tmp.length() <= 0)
6092  ok = false;
6093  }
6094  if (player && ok) {
6095  Cockpit *cockpit = _Universe->isPlayerStarship( player );
6096  if (cockpit) {
6098  VSFileSystem::VSError err = fp.OpenReadOnly( tmp, SaveFile );
6099  if (err > Ok) {
6101  } else {
6102  fp.Close();
6103  if (string( "New_Game" ) != tmp) {
6104  LoadSaveQuitConfirm *saver = new LoadSaveQuitConfirm( this,
6105  "Save",
6106  "Do you want to overwrite your old saved game?" );
6107  saver->init();
6108  saver->run();
6109  } else {
6110  showAlert( "You may not save to the name New_Game." );
6111  }
6112  }
6113  }
6114  }
6115  if (!ok)
6116  showAlert( "You Must Type In a Name To Save." );
6117  return true;
6118 }
6119 
6121 {
6122  Unit *player = m_player.GetUnit();
6123  StaticDisplay *desc = static_cast< StaticDisplay* > ( window()->findControlById( "InputText" ) );
6124  if (desc) {
6125  std::string tmp = desc->text();
6126  if (tmp.length() > 0) {
6127  Cockpit *cockpit = player ? _Universe->isPlayerStarship( player ) : 0;
6128  if (player && cockpit) {
6130  UniverseUtil::showSplashMessage( "Loading saved game." );
6133  player->Kill();
6134  RespawnNow( cockpit );
6136  TerminateCurrentBase(); //BaseInterface::CurrentBase->Terminate();
6137  } else {
6138  showAlert( "Oops - unexpected error (player or cockpit is null)" );
6139  }
6140  } else {
6141  showAlert( "You Must Type In a Name To Load...." );
6142  }
6143  } else {
6144  showAlert( "Oops - unexpected error (desc control not found!)" );
6145  }
6146  return true;
6147 }
6148 
6149 bool BaseComputer::actionNewGame( const EventCommandId &command, Control *control )
6150 {
6151  StaticDisplay *desc = static_cast< StaticDisplay* > ( window()->findControlById( "InputText" ) );
6153  return this->actionLoadGame( command, control );
6154 }
6155 
6156 bool BaseComputer::actionLoadGame( const EventCommandId &command, Control *control )
6157 {
6158  Unit *player = m_player.GetUnit();
6159  StaticDisplay *desc = static_cast< StaticDisplay* > ( window()->findControlById( "InputText" ) );
6160  if (desc) {
6161  std::string tmp = desc->text();
6162  if (tmp.length() > 0) {
6163  if (player) {
6164  Cockpit *cockpit = _Universe->isPlayerStarship( player );
6165  if (cockpit) {
6166  LoadSaveQuitConfirm *saver = new LoadSaveQuitConfirm( this,
6167  "Load",
6168  "Are you sure that you want to load this game?" );
6169  saver->init();
6170  saver->run();
6171  return true;
6172  }
6173  }
6174  }
6175  }
6176  showAlert( "You Must Type In a Name To Load...." );
6177  return true;
6178 }
6179 
6181 {
6182 }
6183 
6185 {
6186  window()->findControlById( "MultiPlayerAccountServer" )->setHidden( true );
6187  window()->findControlById( "MultiPlayerHostPort" )->setHidden( false );
6188  return true;
6189 }
6190 
6192 {
6193  window()->findControlById( "MultiPlayerAccountServer" )->setHidden( false );
6194  window()->findControlById( "MultiPlayerHostPort" )->setHidden( true );
6195  return true;
6196 }
6197 
6198 bool BaseComputer::actionNetDie( const EventCommandId &command, Control *control )
6199 {
6200  if (Network != NULL) {
6202  nak->init();
6203  nak->run();
6204  }
6205  return true;
6206 }
6207 
6209 {
6210  if (Network != NULL) {
6212  nak->init();
6213  nak->run();
6214  }
6215  return true;
6216 }
6217 
6218 bool BaseComputer::actionJoinGame( const EventCommandId &command, Control *control )
6219 {
6221  if (Network != NULL) {
6222  //confirm.
6223  nak->init();
6224  nak->run();
6225  } else {
6226  nak->confirmedJoinGame();
6227  }
6228  return true;
6229 }
6230 //Process a command event from the Options Menu window.
6231